controller.dart 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423
  1. import 'dart:async';
  2. import 'package:flyinsono/architecture/utils/prompt_box.dart';
  3. import 'package:flyinsono/lab/architecture/task_center.dart';
  4. import 'package:flyinsono/lab/color/lab_colors.dart';
  5. import 'package:flyinsono/lab/components/classify_data_selector/classify_data_selector.dart';
  6. import 'package:flyinsono/lab/components/lab_dialog.dart';
  7. import 'package:flyinsono/lab/manager/interfaces/analysis.dart';
  8. import 'package:flyinsono/lab/manager/interfaces/project.dart';
  9. import 'package:flyinsono/lab/manager/interfaces/task.dart';
  10. import 'package:flyinsono/lab/manager/page_manager.dart';
  11. import 'package:flyinsono/lab/mixin/tab_hook_mixin.dart';
  12. import 'package:flyinsono/lab/pages/lab_data/controller.dart';
  13. import 'package:flyinsono/lab/router/lab_route_names.dart';
  14. import 'package:flyinsono/managers/interfaces/entities/task_info.dart';
  15. import 'package:flyinsono/managers/interfaces/remedical.dart';
  16. import 'package:flyinsono/pages/schedule/schedule_notification/schedule_notification_controller.dart';
  17. import 'package:get/get.dart';
  18. import 'package:fis_jsonrpc/rpc.dart';
  19. import 'package:intl/intl.dart';
  20. import 'index.dart';
  21. import 'package:fis_common/index.dart';
  22. class LabTaskController extends GetxController with TabHookMixin {
  23. LabTaskController({required this.tabHashCode});
  24. @override
  25. final int tabHashCode;
  26. static const taskPageId = "task_page_id"; // 页面id
  27. static const headerPanelId = "header_panel_id"; // 头部面板
  28. static const taskListId = "task_list_id"; // 任务列表
  29. static const taskDetailListId = "task_detail_list_id"; // 任务详情
  30. final state = LabTaskState();
  31. final serverTaskManager = Get.find<IServerTaskManager>();
  32. /// 项目接口
  33. final projectManager = Get.find<IProjectManager>();
  34. // 任务数据总览
  35. List<ClassifyData<TaskBaseDTO>> taskStatusClassifyData = [];
  36. //任务分类(进行中、已完成)
  37. int selectedClassifyIndex = 0;
  38. //分类中选中的位置
  39. int selectedTaskIndex = 0;
  40. // 任务详情列表(deepcopy)
  41. List<TaskDetail> taskDetailList = [];
  42. // 当前不存在任何任务
  43. bool noProcessingTask = false;
  44. ClassifyData<TaskBaseDTO> get selectedClassify =>
  45. taskStatusClassifyData.isNotEmpty
  46. ? taskStatusClassifyData[selectedClassifyIndex]
  47. : ClassifyData<TaskBaseDTO>(
  48. classifyName: "",
  49. data: [],
  50. );
  51. ///当前选中的任务
  52. TaskBaseDTO? get currentSelectedTask => selectedClassify.data.isNotEmpty
  53. ? selectedClassify.data[selectedTaskIndex]
  54. : null;
  55. // 轮询定时器(用于获取当前选中的任务详情进度)
  56. Timer? timer;
  57. TaskInfo currentTaskInfo = TaskInfo(
  58. complateCount: 0,
  59. elapsedTime: '',
  60. startTime: '',
  61. title: '',
  62. totalCount: 0,
  63. patientCode: "");
  64. void onTaskClick(int classifyIndex, int itemIndex) {
  65. selectedClassifyIndex = classifyIndex;
  66. selectedTaskIndex = itemIndex;
  67. _updateTaskDetailList();
  68. if (currentSelectedTask?.status == VTaskStatus.Processing ||
  69. currentSelectedTask?.status == VTaskStatus.Unhandled) {
  70. startUpdateTimer();
  71. }
  72. update([taskListId]);
  73. }
  74. // 开启定时器
  75. void startUpdateTimer() {
  76. if (timer != null) {
  77. return;
  78. }
  79. timer = Timer.periodic(
  80. Duration(milliseconds: 1000),
  81. (timer) {
  82. print("定时器执行 $tabHashCode");
  83. ///如果是未完成的状态才需要更新状态
  84. if (currentTaskInfo.status.index < VTaskStatus.Completed.index) {
  85. _updateTaskDetailList();
  86. } else {
  87. updateTaskList();
  88. cancelTimer();
  89. }
  90. },
  91. );
  92. }
  93. // 销毁定时器
  94. void cancelTimer() {
  95. timer?.cancel();
  96. timer = null;
  97. }
  98. /// 更新任务列表总览
  99. void updateTaskList() async {
  100. List<TaskBaseDTO> processingTasks = localTaskCenter.getTaskList();
  101. for (int i = 0; i < processingTasks.length; i++) {
  102. if ([VTaskStatus.Completed, VTaskStatus.Canceled]
  103. .contains(processingTasks[i].status)) {
  104. processingTasks.removeAt(i);
  105. }
  106. }
  107. List<TaskBaseDTO> serverProjects =
  108. await serverTaskManager.getTaskList(status: VTaskStatus.Processing);
  109. ///若Server任务和本地任务有相同的,以本地任务为准
  110. serverProjects = serverProjects
  111. .where((element) => !processingTasks.any((e) => e.code == element.code))
  112. .toList();
  113. processingTasks.addAll(serverProjects);
  114. processingTasks = updateTaskName(processingTasks);
  115. processingTasks.sort((a, b) {
  116. // 比较两个TaskBaseDTO对象的createTime属性
  117. if (a.createTime == null && b.createTime == null) {
  118. return 0; // 都是null,视为相等
  119. } else if (a.createTime == null) {
  120. return 1; // null的排在后面
  121. } else if (b.createTime == null) {
  122. return -1; // null的排在后面
  123. } else {
  124. return b.createTime!.compareTo(a.createTime!); // 按照时间降序排列,最近的时间排在最前面
  125. }
  126. });
  127. List<TaskBaseDTO> completeTasks =
  128. await serverTaskManager.getTaskList(status: VTaskStatus.Completed);
  129. completeTasks = updateTaskName(completeTasks);
  130. taskStatusClassifyData = [
  131. ClassifyData(
  132. classifyName: "进行中(${processingTasks.length})",
  133. data: processingTasks),
  134. ClassifyData(
  135. classifyName: "已完成(${completeTasks.length})", data: completeTasks),
  136. ];
  137. _updateTaskDetailList();
  138. update([taskListId]);
  139. if (processingTasks.isEmpty) {
  140. noProcessingTask = true;
  141. update([taskPageId]);
  142. }
  143. }
  144. List<TaskBaseDTO> updateTaskName(List<TaskBaseDTO> sourceTasks) {
  145. for (TaskBaseDTO p in sourceTasks) {
  146. if (p.taskName.isNullOrEmpty) {
  147. p.taskName = _getTaskNameByTaskType(p.taskType);
  148. var createTime = p.createTime?.toLocal();
  149. if (createTime != null) {
  150. DateFormat formatter = DateFormat('yyyy-MM-dd-HH:mm');
  151. String formattedDate = formatter.format(createTime);
  152. p.taskName = "${p.taskName}-$formattedDate";
  153. }
  154. }
  155. }
  156. return sourceTasks;
  157. }
  158. // 查看已完成的任务详情
  159. Future<void> viewTaskDetail(TaskDetail task, String? patientCode) async {
  160. print("查看任务详情");
  161. String taskCode = task.taskCode ?? '';
  162. TaskDTO? taskInfo =
  163. await Get.find<IServerTaskManager>().getTaskDetail(taskCode);
  164. if (taskInfo == null) {
  165. PromptBox.toast("获取任务失败");
  166. return;
  167. }
  168. var remedicalCode = taskInfo.businessCode ?? '';
  169. RemedicalInfoDTO imageInfo =
  170. await Get.find<IRemedicalManager>().findRemedicalByCode(remedicalCode);
  171. String recordCode = imageInfo.recordCode ?? '';
  172. String projectCode = imageInfo.projectCode ?? '';
  173. PageCollection<ResearchProjectDataDTO>? researchProjectData =
  174. await projectManager.getResearchProjectDataPagesAsync(
  175. projectCode: projectCode,
  176. keyWord: "",
  177. pageSize: 1,
  178. pageIndex: 1,
  179. sort: 0,
  180. );
  181. print("recordCode:$recordCode");
  182. PageManager.ins.openTab(
  183. LabRouteNames.Data,
  184. args: LabDataActiveArguments(
  185. patientCode: patientCode ?? '',
  186. projectCode: projectCode,
  187. ),
  188. );
  189. // PageManager.ins.openTab(
  190. // LabRouteNames.Image,
  191. // args: LabImageActiveArguments(
  192. // patient: selectedPatient,
  193. // record: selectedRecord,
  194. // images: imageList,
  195. // selectedImageIndex: index,
  196. // patientType: patientType,
  197. // ),
  198. // );
  199. }
  200. @override
  201. void onSleep() {
  202. cancelTimer();
  203. serverTaskManager.onReceiveNotification
  204. .removeListener(onReceiveNotification);
  205. }
  206. @override
  207. void onActive(arguments) {
  208. startUpdateTimer();
  209. updateTaskList();
  210. }
  211. /// 在 widget 内存中分配后立即调用。
  212. @override
  213. void onInit() {
  214. super.onInit();
  215. }
  216. /// 在 onInit() 之后调用 1 帧。这是进入的理想场所
  217. @override
  218. void onReady() {
  219. super.onReady();
  220. startUpdateTimer();
  221. updateTaskList();
  222. serverTaskManager.onReceiveNotification.addListener(onReceiveNotification);
  223. }
  224. /// 在 [onDelete] 方法之前调用。
  225. @override
  226. void onClose() {
  227. super.onClose();
  228. cancelTimer();
  229. serverTaskManager.onReceiveNotification
  230. .removeListener(onReceiveNotification);
  231. }
  232. /// dispose 释放内存
  233. @override
  234. void dispose() {
  235. super.dispose();
  236. cancelTimer();
  237. }
  238. Future<void> _updateTaskDetailList() async {
  239. String currentSelectedTaskId = currentSelectedTask?.code ?? "";
  240. if (currentSelectedTaskId.isEmpty) {
  241. return;
  242. }
  243. List<TaskDetail> localTasks =
  244. localTaskCenter.getLocalTasks(currentSelectedTaskId);
  245. if (localTasks.isNotEmpty) {
  246. taskDetailList = localTasks;
  247. currentTaskInfo = localTaskCenter.getTaskInfo(currentSelectedTaskId)!;
  248. if ([VTaskStatus.Completed, VTaskStatus.Canceled]
  249. .contains(currentTaskInfo.status)) {
  250. var targetTask = localTasks
  251. .firstWhereOrNull((element) => element.id == currentSelectedTaskId);
  252. if (targetTask != null) {
  253. localTasks.remove(targetTask);
  254. }
  255. taskDetailList = localTasks;
  256. updateTaskList();
  257. }
  258. if (localTasks.isNotEmpty) {
  259. noProcessingTask = false;
  260. }
  261. }
  262. if (localTasks.isEmpty) {
  263. MainTaskDTO? serverTaskInfo = await Get.find<IServerTaskManager>()
  264. .getMainTaskDetailAsync(currentSelectedTaskId);
  265. if (serverTaskInfo != null) {
  266. taskDetailList = [];
  267. for (TaskDetail task in serverTaskInfo.taskDetailList ?? []) {
  268. if (task.name.isNullOrEmpty) {
  269. task.name = task.taskCode;
  270. }
  271. if (task.description.isNullOrEmpty) {
  272. task.description =
  273. task.rate.toStringAsFixed(2).replaceAll(RegExp(r'\.00$'), '') +
  274. "%";
  275. }
  276. taskDetailList.add(task);
  277. }
  278. taskDetailList = serverTaskInfo.taskDetailList ?? [];
  279. if (taskDetailList.isNotEmpty) {
  280. noProcessingTask = false;
  281. }
  282. int complateCount = taskDetailList
  283. .where((element) => element.status == VTaskStatus.Completed)
  284. .length;
  285. DateFormat formatter = DateFormat('yyyy-MM-dd HH:mm:ss');
  286. var startTime = serverTaskInfo.createTime?.toLocal() ?? DateTime.now();
  287. String formattedDate = formatter.format(startTime);
  288. Duration difference;
  289. var endTime = serverTaskInfo.endTime?.toLocal();
  290. if (endTime == null || endTime.year == 1) {
  291. difference = DateTime.now().difference(startTime);
  292. } else {
  293. difference = endTime.difference(startTime);
  294. }
  295. // 提取分钟和秒钟
  296. int minutes = difference.inMinutes;
  297. int seconds = difference.inSeconds % 60;
  298. String elapsedTime = '${minutes}min${seconds}s';
  299. currentTaskInfo = TaskInfo(
  300. complateCount: complateCount,
  301. elapsedTime: elapsedTime,
  302. startTime: formattedDate,
  303. status: serverTaskInfo.status,
  304. title: _getTaskNameByTaskType(serverTaskInfo.taskType),
  305. totalCount: taskDetailList.length,
  306. patientCode: serverTaskInfo.patientCode);
  307. }
  308. }
  309. update([taskPageId, taskDetailListId, headerPanelId]);
  310. }
  311. String _getTaskNameByTaskType(VTaskType status) {
  312. switch (status) {
  313. case VTaskType.Analysis:
  314. return "URM 分析";
  315. case VTaskType.DeviceUpload:
  316. return "上传";
  317. case VTaskType.UserExport:
  318. return "数据导出";
  319. case VTaskType.UserImport:
  320. return "数据导入";
  321. case VTaskType.URMSaveVideo:
  322. return "视频保存";
  323. default:
  324. return "";
  325. }
  326. }
  327. Future<void> cancelCurrentTask() async {
  328. cancelTimer();
  329. String currentSelectedTaskId = currentSelectedTask?.code ?? "";
  330. if (currentSelectedTaskId.isEmpty) {
  331. return;
  332. }
  333. if (currentSelectedTask?.taskType == VTaskType.UserExport ||
  334. currentSelectedTask?.taskType == VTaskType.UserImport) {
  335. TaskInfo? task = localTaskCenter.getTaskInfo(currentSelectedTaskId);
  336. if (task != null) {
  337. localTaskCenter.canCelTask(currentSelectedTaskId);
  338. } else {
  339. bool result = await Get.find<IServerTaskManager>()
  340. .canCelTasks(currentSelectedTaskId, taskDetailList);
  341. if (result) {
  342. PromptBox.toast("取消成功");
  343. }
  344. }
  345. } else {
  346. if (taskDetailList.any((element) => element.rate == 100)) {
  347. PromptBox.toast("当前没有可以取消的任务");
  348. } else {
  349. var dialogResult = await Get.dialog(
  350. LabDialog(
  351. title: "提示",
  352. content: "处理中的任务无法取消,是否取消待开始的任务?",
  353. ),
  354. barrierColor: LabColors.base100.withOpacity(0.1),
  355. );
  356. if (dialogResult) {
  357. bool result = await Get.find<IURMFunctionManager>()
  358. .cancelAnalysis(currentSelectedTaskId);
  359. if (result) {
  360. PromptBox.toast("取消成功");
  361. }
  362. }
  363. }
  364. }
  365. updateTaskList();
  366. Future.delayed(Duration(milliseconds: 1000), () {
  367. updateTaskList();
  368. });
  369. //3秒是壳子通讯超时时间
  370. Future.delayed(Duration(milliseconds: 3100), () {
  371. updateTaskList();
  372. });
  373. }
  374. void onReceiveNotification(Object sender, ApplicationNotification e) {
  375. String currentSelectedTaskId = currentSelectedTask?.code ?? "";
  376. print("通知接收:${e.code}");
  377. if (e.code == currentSelectedTaskId) {
  378. Future.delayed(Duration(milliseconds: 800), () {
  379. updateTaskList();
  380. });
  381. }
  382. }
  383. }