import 'dart:convert'; import 'dart:io'; import 'package:fis_jsonrpc/rpc.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:uuid/uuid.dart'; import 'package:vitalapp/architecture/utils/prompt_box.dart'; import 'package:vitalapp/architecture/utils/upload.dart'; import 'package:vitalapp/components/alert_dialog.dart'; import 'package:vitalapp/database/db.dart'; import 'package:vitalapp/database/entities/defines.dart'; import 'package:vitalapp/database/entities/diagnosis.dart'; import 'package:vitalapp/global.dart'; import 'package:vitalapp/managers/interfaces/exam.dart'; import 'package:vitalapp/managers/interfaces/patient.dart'; import 'package:vitalapp/managers/interfaces/record_data_cache.dart'; import 'package:vitalapp/rpc.dart'; import 'package:vnote_device_plugin/consts/types.dart'; import 'package:vitalapp/architecture/defines.dart'; import 'package:vitalapp/architecture/storage/text_storage.dart'; import 'package:vitalapp/managers/interfaces/cachedRecord.dart'; import 'package:vitalapp/managers/interfaces/device.dart'; import 'package:vitalapp/managers/interfaces/diagnosis.dart'; import 'package:vitalapp/managers/interfaces/models/device.dart'; import 'package:vitalapp/pages/medical/models/item.dart'; import 'package:vitalapp/pages/medical/state.dart'; import 'package:vitalapp/store/store.dart'; import 'package:vnote_device_plugin/events/event_type.dart'; import 'package:fis_common/logger/logger.dart'; import 'package:vitalapp/architecture/storage/storage.dart'; class MedicalController extends FControllerBase { String patientCode = ''; Map diagnosisDataValue = {}; final _patientManager = Get.find(); final _examManager = Get.find(); String appDataId = ""; final state = MedicalState(); final recordDataCacheManager = Get.find(); static final typeConvertMap = { DeviceTypes.TEMP: "Temp", DeviceTypes.WEIGHT: "BMI", DeviceTypes.SPO2: "SpO2", DeviceTypes.NIBP: "NIBP", DeviceTypes.SUGAR: "GLU", DeviceTypes.URINE: "Urine", DeviceTypes.IC_READER: "ICReader", DeviceTypes.HEART: "HEART", DeviceTypes.TWELVEHEART: "Twelveheart", }; final changePatient = FEventHandler(); final _diagnosisManager = Get.find(); final _cachedRecordManager = Get.find(); final _deviceManager = Get.find(); final _medicalMenus = [ MedicalItem(key: DeviceTypes.TEMP, diagnosticItem: '体温'), MedicalItem(key: DeviceTypes.SUGAR, diagnosticItem: '血糖'), MedicalItem(key: DeviceTypes.NIBP, diagnosticItem: '血压'), MedicalItem(key: DeviceTypes.SPO2, diagnosticItem: '血氧'), MedicalItem(key: DeviceTypes.WEIGHT, diagnosticItem: 'BMI'), MedicalItem(key: DeviceTypes.WAIST, diagnosticItem: '腰臀比'), MedicalItem(key: DeviceTypes.URINE, diagnosticItem: '尿常规'), MedicalItem(key: DeviceTypes.HEART, diagnosticItem: '心电'), MedicalItem(key: DeviceTypes.TWELVEHEART, diagnosticItem: '十二导心电'), ]; @override void onInit() async { setBusy('Loading...'); await initData(); await getAccessTypes(); state.currentTab = DeviceTypes.TEMP; //等数据加载完成之后在切换到体温页面 busy = false; logger.i('MedicalController init end'); super.onInit(); } Future initData() async { patientCode = Store.user.currentSelectPatientInfo?.code ?? ''; if (patientCode.isNotEmpty) { logger.i( 'MedicalController initData patientName:${Store.user.currentSelectPatientInfo?.patientName} patientCode:${Store.user.currentSelectPatientInfo?.code}'); var cachedAppDataId = await readCachedAppDataId(); if (cachedAppDataId != null) { appDataId = cachedAppDataId; } else { await saveCachedAppDataId(); } await initReadCached(); } } Future getAccessTypes() async { List medicalItemList = []; List accessTypes = await _deviceManager.getAccessTypes(); for (var element in _medicalMenus) { if (accessTypes.contains(element.key)) { medicalItemList.add(element); } } state.medicalMenuList = medicalItemList; } Future getDevice(String type) async { List devices = await _deviceManager.getDeviceList(); return devices.firstWhereOrNull((element) => element.type == type); } Future initReadCached() async { if (patientCode.isNotEmpty) { logger .i('MedicalController initReadCached fail,patientCod e:$patientCode'); TextStorage cachedRecord = TextStorage( fileName: 'JKJC', directory: "patient/$patientCode", ); String? value = await cachedRecord.read(); if (value == null) { diagnosisDataValue = {}; Store.resident.handleSaveMedicalData(jsonEncode(diagnosisDataValue)); return; } Store.resident.handleSaveMedicalData(value); if (kIsWeb) { diagnosisDataValue = {}; } else { diagnosisDataValue = jsonDecode(value); } } } Future saveCachedAppDataId() async { appDataId = const Uuid().v4().replaceAll('-', ''); TextStorage cachedRecord = TextStorage( fileName: 'appDataId', directory: "patient/$patientCode", ); Get.back(); return cachedRecord.save(appDataId); } Future readCachedAppDataId() async { TextStorage cachedRecord = TextStorage( fileName: 'appDataId', directory: "patient/$patientCode", ); return cachedRecord.read(); } Future saveCachedRecord() async { if (patientCode.isEmpty) { logger.i( 'MedicalController saveCachedRecord fail,patientCode:$patientCode'); return false; } Store.resident.handleSaveMedicalData(jsonEncode(diagnosisDataValue)); TextStorage cachedRecord = TextStorage( fileName: 'JKJC', directory: "patient/$patientCode", ); return cachedRecord.save(jsonEncode(diagnosisDataValue)); } Future deleteDirectory() async { TextStorage cachedRecord = TextStorage( fileName: 'JKJC', directory: "patient/$patientCode", ); return cachedRecord.deleteDirectory(); } Future submitDiagnosis( Map submitDiagnosisDataValue, ) async { try { List diagnosisItems = await recordDataCacheManager .verifyDiagnosisDataList(submitDiagnosisDataValue); state.currentTab = '-1'; await recordDataCacheManager.saveRecordData( appDataId, patientCode, submitDiagnosisDataValue, ); if (kIsOnline) { busy = true; diagnosisItems = await recordDataCacheManager .convertDiagnosisDataToList(submitDiagnosisDataValue); bool result; result = await _diagnosisManager.syncPatientAndDiagnosisData( SyncPatientAndDiagnosisDataRequest( patientCode: patientCode, patientName: Store.user.currentSelectPatientInfo?.patientName ?? '', patientAddress: Store.user.currentSelectPatientInfo?.patientAddress ?? '', patientGender: Store.user.currentSelectPatientInfo?.patientGender ?? GenderEnum.Unknown, permanentResidenceAddress: Store.user.currentSelectPatientInfo?.permanentResidenceAddress, phone: Store.user.currentSelectPatientInfo?.phone, cardNo: Store.user.currentSelectPatientInfo?.cardNo, nationality: Store.user.currentSelectPatientInfo?.nationality, birthday: Store.user.currentSelectPatientInfo?.birthday, crowdLabels: Store.user.currentSelectPatientInfo?.crowdLabels, cardType: Store.user.currentSelectPatientInfo?.cardType ?? CardTypeEnum.Identity, contractedDoctor: Store.user.currentSelectPatientInfo?.contractedDoctor, appDataId: appDataId, diagnosisTime: DateTime.now(), token: Store.user.token, diagnosisItems: diagnosisItems, ), ); // print(SyncPatientAndDiagnosisDataRequest( // patientCode: patientCode, // patientName: Store.user.currentSelectPatientInfo?.patientName ?? '', // patientAddress: // Store.user.currentSelectPatientInfo?.patientAddress ?? '', // patientGender: Store.user.currentSelectPatientInfo?.patientGender ?? // GenderEnum.Unknown, // permanentResidenceAddress: // Store.user.currentSelectPatientInfo?.permanentResidenceAddress, // phone: Store.user.currentSelectPatientInfo?.phone, // cardNo: Store.user.currentSelectPatientInfo?.cardNo, // nationality: Store.user.currentSelectPatientInfo?.nationality, // birthday: Store.user.currentSelectPatientInfo?.birthday, // crowdLabels: Store.user.currentSelectPatientInfo?.crowdLabels, // cardType: Store.user.currentSelectPatientInfo?.cardType ?? // CardTypeEnum.Identity, // contractedDoctor: // Store.user.currentSelectPatientInfo?.contractedDoctor, // appDataId: appDataId, // diagnosisTime: DateTime.now(), // token: Store.user.token, // diagnosisItems: diagnosisItems, // ).toJson()); busy = false; if (result) { //如果在线,则提交历史数据 var submitSuccess = await _submitHistory(); if (submitSuccess) { logger.i('当前数据提交成功,且历史记录:$patientCode 提交成功'); } recordDataCacheManager.recordSyncStateChange(appDataId); PromptBox.toast('提交成功'); await initRecordDataState(); ///提交之后,测试结果清空 diagnosisDataValue.clear(); } else { PromptBox.toast('提交失败'); logger.i('提交失败:$patientCode'); } state.currentTab = state.medicalMenuList[0].key; } else { Future.delayed(const Duration(milliseconds: 10), () { state.currentTab = state.medicalMenuList[0].key; }); PromptBox.toast("已缓存至本地"); logger.i('已缓存至本地:$patientCode'); await initRecordDataState(); ///提交之后,测试结果清空 diagnosisDataValue.clear(); state.currentTab = state.medicalMenuList[0].key; Get.back(); } } catch (err) { busy = false; state.currentTab = state.medicalMenuList[0].key; logger.e('submitDiagnosis error: ${err.toString()}'); } } Future initRecordDataState() async { await saveCachedAppDataId(); await deleteDirectory(); await initReadCached(); } Future createDiagnosis() async { busy = true; try { if (Store.user.teamName.isEmpty) { PromptBox.toast('未设置团队无法提交检测数据'); return; } if (patientCode.isEmpty || patientCode != Store.user.currentSelectPatientInfo?.code) { initData(); } Map submitDiagnosisDataValue = Map.from(diagnosisDataValue); //确定提交值不会发生变更 List diagnosisItems = await recordDataCacheManager .verifyDiagnosisDataList(submitDiagnosisDataValue); logger.i('submitDiagnosis diagnosisItems.leng:${diagnosisItems.length}'); if (diagnosisItems.isEmpty) { if (patientCode.isNotEmpty && kIsOnline) { bool submitHistory = await _submitHistory(); if (submitHistory) { PromptBox.toast('提交成功'); return; } } PromptBox.toast('不能提交空数据'); return; } if (state.medicalMenuList.length > submitDiagnosisDataValue.length) { logger.i( 'state.medicalMenuList.length:${state.medicalMenuList.length},copiedDiagnosisDataValue.length:${diagnosisDataValue.length}'); Get.dialog( VAlertDialog( title: '提示', content: Container( margin: const EdgeInsets.only(bottom: 20), child: const Text( '当前检测项目未完成,请确定是否提交本次检测', style: TextStyle(fontSize: 20), textAlign: TextAlign.center, ), ), showCancel: true, onConfirm: () { Get.back(); submitDiagnosis(submitDiagnosisDataValue); }, onCanceled: () {}, ), ); return; } else { ///如果已完成所有检查,则直接提交 submitDiagnosis(submitDiagnosisDataValue); } } catch (e) { logger.e('MedicalController createDiagnosis ex:', e); } finally { busy = false; } } @override void dispose() { print('MedicalController dispose'); super.dispose(); } /// 【TODO 接口需要变更】 心电 Future createHeart( String physicalExamNumber, String? keyValue, ) async { Map input = diagnosisDataValue; for (var entry in input.entries) { var key = entry.key; var value = entry.value; if (value != null) { Store.app.setBusy("提交中"); if (['Heart', 'TwelveHeart'].contains(key) && value is Map) { value = await uploadData(value); } Store.app.busy = false; // diagnosisItems.add( // DiagnosisItem( // key: key, // diagnosisData: jsonEncode(value), // ), // ); } print('$key: $value'); } Map output = {}; input.forEach((key, value) { value.forEach((innerKey, innerValue) { output[innerKey] = innerValue; }); }); var result = await _examManager.createExam(CreateExamRequest( key: keyValue ?? "HEIBasic", examData: jsonEncode(output), physicalExamNumber: physicalExamNumber, )); if (result == true) { Get.back(); } } bool isUploaded(String url) { return url.startsWith('https://') || url.startsWith('http://'); } Future uploadData(Map data) async { if (data['ECG_POINT'] != null && !isUploaded(data['ECG_POINT'])) { File ecgPointFile = await rpc.storage.writeStringToFile(data['ECG_POINT']); String? ecgPointUrl = await rpc.storage.uploadFile(ecgPointFile); data['ECG_POINT'] = ecgPointUrl ?? ''; // ... 上传点集 } if (data['ECG'] != null && !isUploaded(data['ECG'])) { // ... 上传图片 /// 图片地址 final imageFile = UploadUtils.convertBase64ToXFile(data['ECG']); String? imageUrl = await rpc.storage.upload(imageFile!); data['ECG'] = imageUrl ?? ''; } if (data['ECG_POINT12'] != null && !isUploaded(data['ECG_POINT12'])) { File ecgPointFile = await rpc.storage.writeStringToFile(data['ECG_POINT12']); String? ecgPointUrl = await rpc.storage.uploadFile(ecgPointFile); data['ECG_POINT12'] = ecgPointUrl ?? ''; // ... 上传点集 } if (data['ECG12'] != null && !isUploaded(data['ECG12'])) { // ... 上传图片 final imageFile = UploadUtils.convertBase64ToXFile(data['ECG12']); String? imageUrl = await rpc.storage.upload(imageFile!, fileType: "jpg"); data['ECG12'] = imageUrl ?? ''; } return data; } /// 体检 检查提交 Future createCheckup( String? physicalExamNumber, String? keyValue, ) async { Map input = diagnosisDataValue; Map output = {}; input.forEach((key, value) { value.forEach((innerKey, innerValue) { output[innerKey] = innerValue; }); }); var result = await _examManager.createExam(CreateExamRequest( key: keyValue ?? "HEIBasic", examData: jsonEncode(output), physicalExamNumber: physicalExamNumber, )); if (result == true) { Get.back(); } print(result); } ///提交历史记录 Future _submitHistory() async { var existDatas = await recordDataCacheManager.getNoSubmitRecords(patientCode); var historyDatas = existDatas.where((element) => element.code != appDataId); if (historyDatas.isEmpty) { return false; } for (DiagnosisEntity data in historyDatas) { try { Map jsonData = jsonDecode(data.dataJson); var diagnosisItems = await recordDataCacheManager.convertDiagnosisDataToList(jsonData); SubmitDiagnosisRequest submitDiagnosisRequest = SubmitDiagnosisRequest( appDataId: data.code, patientCode: data.patientCode, diagnosisItems: diagnosisItems, diagnosisTime: data.updateTime ?? data.createTime, ); bool submitResult; if (kIsOnline) { PatientDTO? patientInfo = await _patientManager.getDetail( data.patientCode, ); if (patientInfo == null) { ///如果Server没有,则从本地读取 var patientEntity = await db.repositories.patient.singleByCode(data.patientCode); if (patientEntity != null) { patientInfo = PatientDTO.fromJson(jsonDecode(patientEntity.dataJson)); } } submitResult = await _diagnosisManager.syncPatientAndDiagnosisData( SyncPatientAndDiagnosisDataRequest( token: Store.user.token, patientCode: data.patientCode, patientName: patientInfo?.patientName ?? '', patientAddress: patientInfo?.patientAddress ?? '', patientGender: patientInfo?.patientGender ?? GenderEnum.Unknown, permanentResidenceAddress: patientInfo?.permanentResidenceAddress, phone: patientInfo?.phone, cardNo: patientInfo?.cardNo, nationality: patientInfo?.nationality, birthday: patientInfo?.birthday, crowdLabels: patientInfo?.crowdLabels, cardType: patientInfo?.cardType ?? CardTypeEnum.Identity, contractedDoctor: patientInfo?.contractedDoctor, appDataId: data.code, diagnosisTime: data.updateTime ?? data.createTime, diagnosisItems: diagnosisItems, ), ); } else { submitResult = await _diagnosisManager .submitDiagnosisAsync(submitDiagnosisRequest); } OfflineDataSyncState state; if (submitResult) { state = OfflineDataSyncState.success; logger.i('提交历史记录:$patientCode 成功'); } else { state = OfflineDataSyncState.fail; logger.i('提交历史记录:$patientCode 失败'); } recordDataCacheManager.recordSyncStateChange(data.code, state: state); } catch (e) { logger.e('MedicalController _submitHistory ex:', e); } } return true; } }