import 'dart:convert'; import 'package:fis_jsonrpc/rpc.dart'; import 'package:get/get.dart'; import 'package:vital_local_database/core/interface/queryable.dart'; import 'package:vitalapp/database/db.dart'; import 'package:vitalapp/database/entities/defines.dart'; import 'package:vitalapp/database/entities/diagnosis.dart'; import 'package:vitalapp/database/entities/exam.dart'; import 'package:vitalapp/database/entities/followup.dart'; import 'package:vitalapp/database/entities/patient.dart'; import 'package:vitalapp/managers/adapters/offline/patient.dart'; import 'package:vitalapp/managers/interfaces/models/common.dart'; import 'package:vitalapp/managers/interfaces/patient.dart'; import 'package:vitalapp/rpc.dart'; import 'package:vitalapp/store/store.dart'; import 'interfaces/data_sync.dart'; import 'package:fis_common/logger/logger.dart'; import 'interfaces/record_data_cache.dart'; class DataSyncManager implements IDataSyncManager { static const tcmKey = "HEITCMC"; String get userCode => Store.user.userCode!; final _patientManager = Get.find(); @override Future> getPatientPagedList( int pageIndex, { int pageSize = 50, List? syncStates, bool isReturnCountOnly = false, }) async { try { var query = db.repositories.patient.queryable.where((x) { final list = []; list.add(x.isValid.equals(true)); list.add(x.userCode.equals(Store.user.userCode)); //添加用户code if (syncStates != null && syncStates.isNotEmpty) { if (syncStates.length == 1) { list.add(x.overallSyncState.equals(syncStates[0])); } else { list.add(x.overallSyncState.inEqueals(syncStates)); } } return list; }); List states = syncStates ?? []; if (states.isEmpty) { states.add(OfflineDataSyncState.wait); states.add(OfflineDataSyncState.fail); states.add(OfflineDataSyncState.success); } // updateTime 比 createTime 大于一秒以上,说明有操作而更新过数据 final successWhere = "overallSyncState=${OfflineDataSyncState.success.index} AND julianday(updateTime) > julianday(createTime, '+1 second')"; if (states.length == 1) { if (states.first == OfflineDataSyncState.success) { query = query.whereCustom(" AND $successWhere"); } else { query = query.and((x) => x.overallSyncState.equals(states.first)); } } else { if (states.contains(OfflineDataSyncState.success)) { final noralStateValue = states .where((e) => e != OfflineDataSyncState.success) .map((e) => e.index) .join(','); query = query.whereCustom( " AND ( overallSyncState IN ($noralStateValue) OR ( $successWhere ) ) ", ); } else { query.and((x) => x.overallSyncState.inEqueals(states)); } } final count = await query.count(); if (isReturnCountOnly) { // 只需要返回数量 return PagedDataCollection( totalCount: count, pageIndex: pageIndex, pageSize: pageSize, data: [], ); } final offset = pageSize * (pageIndex - 1); final entities = await query.offset(offset).limit(pageSize).toList(); return PagedDataCollection( totalCount: count, pageIndex: pageIndex, pageSize: pageSize, data: entities, ); } catch (e) { logger.e("DataSyncManager getPatientPagedList error.", e); return PagedDataCollection( totalCount: 0, pageIndex: pageIndex, pageSize: pageSize, data: [], ); } } @override Future> getPatientWaitUploadAllList() async { final waitList = await db.repositories.patient.queryable.where((x) { final list = []; list.add(x.isValid.equals(true)); list.add(x.userCode.equals(Store.user.userCode)); list.add(x.overallSyncState.equals(OfflineDataSyncState.wait)); return list; }).toList(); final failList = await db.repositories.patient.queryable.where((x) { final list = []; list.add(x.isValid.equals(true)); list.add(x.userCode.equals(Store.user.userCode)); list.add(x.overallSyncState.equals(OfflineDataSyncState.fail)); return list; }).toList(); // TODO: 暂时未支持 Where in ,暂时先分开查 return [...waitList, ...failList]; } @override Future syncPatientAllData(String patientCode) async { PatientEntity? patientEntity; try { patientEntity = await db.repositories.patient .singleByCodeWithUserCode(patientCode, Store.user.userCode!); } catch (e) { logger.e( "DataSyncManager.syncPatientAllData db.singleByCodeWithUserCode-$patientCode error.", e); } if (patientEntity == null) { return false; } bool isSyncCompleted = false; try { final patientResult = await syncPatient(patientEntity); if (patientResult) { if (patientEntity.diagnosisCount > 0) { final diagnosisSyncCount = await syncPatientDiagnosis(patientCode); patientEntity.diagnosisCount -= diagnosisSyncCount; } if (patientEntity.gxyFollowUpCount > 0) { final followUpSyncCount = await _syncPatientFollowUpWithKey(patientCode, "GXY"); patientEntity.gxyFollowUpCount -= followUpSyncCount; } if (patientEntity.tnbFollowUpCount > 0) { final followUpSyncCount = await _syncPatientFollowUpWithKey(patientCode, "TNB"); patientEntity.tnbFollowUpCount -= followUpSyncCount; } if (patientEntity.examCount > 0) { final count = await syncPatientExam(patientCode); // patientEntity.followUpCount -= count; } if (patientEntity.tcmConsitutionCount > 0) { final count = await syncPatientTCMConsitution(patientCode); patientEntity.tcmConsitutionCount -= count; } } isSyncCompleted = checkPatientSyncCompleted(patientEntity); if (isSyncCompleted) { patientEntity.overallSyncState = OfflineDataSyncState.success; } else { patientEntity.overallSyncState = OfflineDataSyncState.fail; } } catch (e) { logger.e( "DataSyncManager syncPatientAllData[during sync each] error.", e); // TODO: 后续可加上失败原因字段 patientEntity.overallSyncState = OfflineDataSyncState.fail; return false; } // 更新居民记录 try { final ret = await db.repositories.patient.update(patientEntity); return isSyncCompleted && ret > 0; } catch (e) { logger.e( "DataSyncManager syncPatientAllData[during update overall state] error.", e); return false; } } @override bool checkPatientSyncCompleted(PatientEntity entity) { if (entity.examCount > 0) return false; if (entity.tcmConsitutionCount > 0) return false; if (entity.diagnosisCount > 0) return false; if (entity.gxyFollowUpCount > 0) return false; if (entity.tnbFollowUpCount > 0) return false; if (entity.syncState != OfflineDataSyncState.success) return false; return true; } @override Future syncPatient(PatientEntity entity) async { logger.i("DataSyncManager start sync patient info..."); logger.i("Patient info: ${entity.name}|${entity.code}."); bool syncResult = false; try { final jsonMap = jsonDecode(entity.dataJson); final dto = PatientDTO.fromJson(jsonMap); if (entity.syncType == OfflineDataSyncType.create) { final request = PatientDtoConverter.dto2Create(dto); request.token = Store.user.token; final code = await rpc.vitalPatient.createPatientAsync(request); syncResult = code.isNotEmpty; if (syncResult) { entity.code = code; } } else { final request = PatientDtoConverter.dto2Update(dto); request.token = Store.user.token; syncResult = await rpc.patient.updatePatientAsync(request); } if (syncResult) { syncResult = await _syncPatientExt(entity); if (!syncResult) { logger.i("DataSyncManager sync patient ext info fail."); } } else { logger.i("DataSyncManager sync patient base info fail."); } entity.syncState = syncResult ? OfflineDataSyncState.success : OfflineDataSyncState.fail; await db.repositories.patient.update(entity); } catch (e) { logger.e("DataSyncManager sync patient error.", e); syncResult = false; } logger.i("DataSyncManager stop sync patient info."); return syncResult; } @override Future syncPatientDiagnosis(String patientCode) async { logger.i("DataSyncManager start sync diagnosis..."); final list = await db.repositories.diagnosis .getNotUploadedListByPatientCode(patientCode, userCode); logger.i("DataSyncManager diagnosis total count: ${list.length}."); int count = 0; try { for (var entity in list) { final success = await _syncSingleDiagnosis(entity); if (success) { count++; } } } catch (e) { logger.e("DataSyncManager sync diagnosis error.", e); } logger.i("DataSyncManager stop sync diagnosis; uploaded count: $count."); return count; } @override Future syncPatientFollowUp(String patientCode) async { logger.i("DataSyncManager start sync followup..."); final list = await db.repositories.followUp .queryAllListByPatient(patientCode, userCode); logger.i("DataSyncManager followup total count: ${list.length}."); int count = 0; try { for (var entity in list) { final success = await _syncSingleFollowUp(entity); if (success) { count++; } } } catch (e) { logger.e("DataSyncManager sync followup error.", e); } logger.i("DataSyncManager stop sync followup; uploaded count: $count."); return count; } @override Future syncPatientExam(String patientCode) async { logger.i("DataSyncManager start sync exam..."); final list = await db.repositories.exam.queryPatientAllList( patientCode, userCode, keys: ["Unset"]); //TODO: 这里请指定Key集合 logger.i("DataSyncManager exam total count: ${list.length}."); int count = 0; try { for (var entity in list) { final success = await _syncSingleExam(entity); if (success) { count++; } } } catch (e) { logger.e("DataSyncManager sync exam error.", e); } logger.i("DataSyncManager stop sync exam; uploaded count: $count."); return count; } @override Future syncPatientTCMConsitution(String patientCode) async { logger.i("DataSyncManager start sync TCMConsitution..."); final list = await db.repositories.exam .queryPatientAllList(patientCode, userCode, keys: [tcmKey]); logger.i("DataSyncManager TCMConsitution total count: ${list.length}."); int count = 0; try { for (var entity in list) { final success = await _syncSingleExam(entity); if (success) { count++; } } } catch (e) { logger.e("DataSyncManager sync TCMConsitution error.", e); } logger .i("DataSyncManager stop sync TCMConsitution; uploaded count: $count."); return count; } Future _syncPatientFollowUpWithKey( String patientCode, String key) async { logger.i("DataSyncManager start sync followup..."); final list = await db.repositories.followUp .queryAllListByPatient(patientCode, userCode, key); logger.i("DataSyncManager followup total count: ${list.length}."); int count = 0; try { for (var entity in list) { final success = await _syncSingleFollowUp(entity); if (success) { count++; } } } catch (e) { logger.e("DataSyncManager sync followup error.", e); } logger.i("DataSyncManager stop sync followup; uploaded count: $count."); return count; } Future _syncSingleFollowUp(FollowUpEntity entity) async { bool result = false; try { if (entity.syncType == OfflineDataSyncType.create) { final request = CreateFollowUpRequest( token: Store.user.token, key: entity.typeKey, patientCode: entity.patientCode, templateCode: entity.templateCode, followUpData: entity.dataJson, followUpTime: entity.followUpTime, nextFollowUpTime: entity.nextFollowUpTime, followUpMode: entity.mode, followUpPhotos: entity.followUpPhtots, ); final code = await rpc.vitalFollowUp.createFollowUpAsync(request); result = code.isNotEmpty; if (result) { entity.code = code; // 更新真实Code } } else { final request = UpdateFollowUpRequest( token: Store.user.token, key: entity.typeKey, followUpData: entity.dataJson, followUpTime: entity.followUpTime, nextFollowUpTime: entity.nextFollowUpTime, followUpMode: entity.mode, code: entity.code, followUpPhotos: entity.followUpPhtots, ); result = await rpc.vitalFollowUp.updateFollowUpAsync(request); } if (result) { entity.syncState = OfflineDataSyncState.success; } else { entity.syncState = OfflineDataSyncState.fail; } final updateRows = await db.repositories.followUp.update(entity); result = updateRows > 0; } catch (e) { logger.e( "DataSyncManager_syncSingleFollowUp error. id: ${entity.id}.", e); } return result; } /// 根据身份证号获取居民信息 Future _getPatientByID(String idNum, [bool isValidOperationDoctor = true]) async { final patient = await _patientManager.getDetail( idNum, isValidOperationDoctor: isValidOperationDoctor, ); return patient; } Future _syncSingleDiagnosis(DiagnosisEntity entity) async { final currPatient = await _getPatientByID(entity.patientCode); if (currPatient == null) { return false; } bool result = false; try { final valuesMap = jsonDecode(entity.dataJson); List diagnosisItems = await Get.find() .verifyDiagnosisDataList(valuesMap); // 目前不存在更新 final request = SyncPatientAndDiagnosisDataRequest( token: Store.user.token, patientCode: currPatient.code, patientName: currPatient.patientName ?? '', patientAddress: currPatient.patientAddress ?? '', patientGender: currPatient.patientGender, permanentResidenceAddress: currPatient.permanentResidenceAddress, phone: currPatient.phone, cardNo: currPatient.cardNo, nationality: currPatient.nationality, birthday: currPatient.birthday, crowdLabels: currPatient.crowdLabels, cardType: currPatient.cardType, contractedDoctor: currPatient.contractedDoctor, appDataId: entity.code, diagnosisTime: DateTime.now(), diagnosisItems: diagnosisItems, ); result = await rpc.vitalDiagnosis.syncPatientAndDiagnosisDataAsync(request); if (result) { entity.syncState = OfflineDataSyncState.success; } else { entity.syncState = OfflineDataSyncState.fail; } final updateRows = await db.repositories.diagnosis.update(entity); result = updateRows > 0; } catch (e) { logger.e( "DataSyncManager_syncSingleDiagnosis error. id: ${entity.id}.", e); } return result; } Future _syncSingleExam(ExamEntity entity) async { bool result = false; try { if (entity.syncType == OfflineDataSyncType.create) { final code = await _createExam(entity); result = code.isNotEmpty; if (result) { entity.code = code; // 更新真实Code } } else { result = await _updateExam(entity); } if (result) { entity.syncState = OfflineDataSyncState.success; } else { entity.syncState = OfflineDataSyncState.fail; } final updateRows = await db.repositories.exam.update(entity); result = updateRows > 0; } catch (e) { logger.e("DataSyncManager_syncSingleExam error. id: ${entity.id}.", e); } return result; } Future _createExam(ExamEntity entity) async { final code = entity.code.startsWith("mock") ? null : entity.code; final request = CreateExamRequest( code: code, batchNumber: entity.batchNumber, key: entity.templateKey, patientCode: entity.patientCode, examData: entity.dataJson, templateCode: entity.templateCode, physicalExamNumber: "", token: Store.user.token, ); final result = await rpc.vitalExam.createExamAsync(request); return result; } Future _updateExam(ExamEntity entity) async { // 中医体质 和 普通体检 更新通道不同 if (entity.templateKey == tcmKey) { final request = UpdateExamRequest( token: Store.user.token, key: entity.templateKey, examData: entity.dataJson, code: entity.code, ); final result = await rpc.vitalExam.updateExamAsync(request); return result; } else { final code = await _createExam(entity); return code.isNotEmpty; } } Future _syncPatientExt(PatientEntity entity) async { if (entity.extJson == null || entity.extJson!.isEmpty) { return true; } if (entity.extCode == null || entity.extCode!.startsWith("mock")) { final extCode = await rpc.vitalPatientExtension.createPatientExtensionAsync( CreatePatientExtensionRequest( token: Store.user.token, key: "PatientHealthInfo", patientCode: entity.code, extensionData: entity.extJson, ), ); if (extCode.isNotEmpty) { entity.extCode = extCode; return true; } else { return false; } } else { final result = await rpc.vitalPatientExtension.updatePatientExtensionAsync( UpdatePatientExtensionRequest( code: entity.extCode, token: Store.user.token, key: "PatientHealthInfo", patientCode: entity.code, extensionData: entity.extJson, ), ); return result; } } }