Compare commits

...

714 Commits

Author SHA1 Message Date
he b143ffbcfc 角色禁用不发邮件
continuous-integration/drone/push Build is passing Details
2025-06-20 16:57:59 +08:00
he 1d8be8c04b Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-06-20 16:46:25 +08:00
he 139e1f8186 退出项目不发邮件 2025-06-20 16:46:24 +08:00
hang 81cb43d3a3 修改PD 逻辑
continuous-integration/drone/push Build is passing Details
2025-06-20 16:21:28 +08:00
hang c2db7a2c61 修改项目邮件配置
continuous-integration/drone/push Build is passing Details
2025-06-20 14:46:02 +08:00
hang 8cd19b929c Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-06-20 14:25:43 +08:00
hang c62e7b13a9 修改搜索查询 2025-06-20 14:25:38 +08:00
he fc81738176 修改文档发布发邮件
continuous-integration/drone/push Build is passing Details
2025-06-20 14:10:33 +08:00
he b1e1840c87 修改阅片期计划的排序
continuous-integration/drone/push Build is passing Details
2025-06-20 14:03:37 +08:00
he b31d7ee762 阅片期计划可以选择基线
continuous-integration/drone/push Build is passing Details
2025-06-20 13:55:34 +08:00
he fb0c3879cf 邮件发送
continuous-integration/drone/push Build is passing Details
2025-06-20 11:53:25 +08:00
he 5ffe788a3b Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-06-20 09:31:57 +08:00
he 056573874a 修改受试者内随机 2025-06-20 09:31:55 +08:00
hang c959a47fa6 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-06-19 17:35:27 +08:00
hang 41fe678708 CDISC导表库bug 2025-06-19 17:35:24 +08:00
he e7bbf09a50 修改稽查的单位翻译
continuous-integration/drone/push Build is passing Details
2025-06-19 16:33:21 +08:00
he 3bd16d7fce 邮件修改
continuous-integration/drone/push Build is passing Details
2025-06-19 16:18:01 +08:00
he db1d1b0f14 修改
continuous-integration/drone/push Build is passing Details
2025-06-19 15:08:24 +08:00
he ef980fdc68 邮件修改
continuous-integration/drone/push Build is passing Details
2025-06-19 14:31:41 +08:00
he 4de186a36b Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-06-19 14:16:54 +08:00
he 0dd73991f0 邮件修改 2025-06-19 14:16:52 +08:00
hang 27e8e0e79c 修改子项排序
continuous-integration/drone/push Build is passing Details
2025-06-19 13:32:34 +08:00
hang fa53d6e88e 修改标准查询条件
continuous-integration/drone/push Build is passing Details
2025-06-19 11:37:54 +08:00
hang f3fc3fa4bf 修改邮件配置
continuous-integration/drone/push Build is passing Details
2025-06-19 11:26:17 +08:00
hang 476f692355 禁用人员系统查看文档
continuous-integration/drone/push Build is passing Details
2025-06-19 10:32:11 +08:00
hang 41b2a638fe 完全随机触发系统盲态名称
continuous-integration/drone/push Build is passing Details
2025-06-19 10:21:33 +08:00
hang c3631eee51 修改有序阅片生成任务bug
continuous-integration/drone/push Build is passing Details
2025-06-18 18:06:16 +08:00
hang 3ed74a578e 修改邮件配置为适配多标准
continuous-integration/drone/push Build is passing Details
2025-06-18 17:14:13 +08:00
hang 918728a42e 修改日志保留天数
continuous-integration/drone/push Build is passing Details
2025-06-18 13:57:50 +08:00
hang a8c0bef99e 关键图像-加上条件,已签名的才导出
continuous-integration/drone/push Build is passing Details
2025-06-18 13:05:53 +08:00
hang b3468a6d1b 修改影像下载bug
continuous-integration/drone/push Build is passing Details
2025-06-18 10:40:14 +08:00
hang dba8d3c964 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-06-18 10:33:29 +08:00
hang 8f5b024d3b 修改导表bug 2025-06-18 10:33:28 +08:00
he ea836d9349 修改阅片期计划生成裁判的逻辑
continuous-integration/drone/push Build is passing Details
2025-06-17 16:45:09 +08:00
he c32878911d 生成阅片期计划的时候 产生裁判 2025-06-17 16:45:07 +08:00
hang 0c4bd680dd 增加影像统计导出
continuous-integration/drone/push Build is passing Details
2025-06-17 16:30:13 +08:00
hang 90cd6ad0eb 真实随机阅片导表-1
continuous-integration/drone/push Build is passing Details
2025-06-17 09:59:22 +08:00
hang 013561541c 随机阅片导表
continuous-integration/drone/push Build is running Details
2025-06-17 09:58:53 +08:00
he f1f584c337 修改阅片期选择访视下拉框的数据
continuous-integration/drone/push Build is passing Details
2025-06-16 16:46:05 +08:00
he c443969f18 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-06-16 16:05:33 +08:00
hang 01621511f1 随机阅片排序-5
continuous-integration/drone/push Build is running Details
2025-06-16 16:03:25 +08:00
he 0740b38b10 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8 2025-06-16 15:59:03 +08:00
he ebd70e2202 生成了裁判任务 阅片期不让删除和修改 2025-06-16 15:59:00 +08:00
hang 80c026d5b2 修改CDISC 导出bug
continuous-integration/drone/push Build is passing Details
2025-06-16 15:58:33 +08:00
hang 8e2fbf06c5 随机阅片-NextTask-5
continuous-integration/drone/push Build is passing Details
2025-06-16 15:18:49 +08:00
hang daee20e309 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-06-16 15:00:48 +08:00
hang 3ba7481df8 末次访视的展示+ 展示基线选择阅片期 2025-06-16 15:00:37 +08:00
he 1e299e3f9e 添加阅片期 产生裁判
continuous-integration/drone/push Build is running Details
2025-06-16 14:59:23 +08:00
he 05cc46a6d6 修改标记信息
continuous-integration/drone/push Build is passing Details
2025-06-13 16:01:23 +08:00
he f1b943b6b0 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-06-13 14:40:47 +08:00
he 9f00c1c49b 修改阅片标记信息 2025-06-13 14:40:47 +08:00
hang b1fd623065 随机阅片NextTask-4
continuous-integration/drone/push Build is passing Details
2025-06-13 14:33:15 +08:00
hang 7683866718 随机阅片序号3
continuous-integration/drone/push Build is passing Details
2025-06-13 11:49:14 +08:00
hang d58395c25e 随机阅片序号2
continuous-integration/drone/push Build is passing Details
2025-06-13 11:38:09 +08:00
hang 15dd21451e 随机阅片随机序号
continuous-integration/drone/push Build is passing Details
2025-06-13 10:46:29 +08:00
hang 483c7c68e8 修改重置阅片稽查3
continuous-integration/drone/push Build is passing Details
2025-06-13 09:53:59 +08:00
hang 738ad29c0b 修改阅片重置稽查
continuous-integration/drone/push Build is passing Details
2025-06-12 17:55:02 +08:00
hang f87b57ecc5 修改重置阅片稽查
continuous-integration/drone/push Build is passing Details
2025-06-12 17:40:50 +08:00
hang fccd704659 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-06-12 16:36:37 +08:00
hang e5b41735ec 确认浏览临床数据 2025-06-12 16:36:29 +08:00
he 07b496eb67 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-06-11 15:26:20 +08:00
he 762e46480c 删除标记 2025-06-11 15:26:18 +08:00
hang a7ad22ed28 修改关键图下载
continuous-integration/drone/push Build is passing Details
2025-06-11 14:35:56 +08:00
hang 8610a627ea 修改导出影响接口
continuous-integration/drone/push Build is passing Details
2025-06-11 14:03:50 +08:00
hang 302658d467 修改影响导出列表
continuous-integration/drone/push Build is passing Details
2025-06-11 13:46:13 +08:00
hang 97c882b0b2 肿瘤学稽查修改
continuous-integration/drone/push Build is passing Details
2025-06-11 09:33:24 +08:00
hang d711bb4b7f Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-06-11 09:27:24 +08:00
hang 1a4ca6e36b 增加进入阅片中稽查 2025-06-11 09:27:18 +08:00
he aaac85b7b2 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-06-10 17:16:59 +08:00
he cb3e4bc671 添加数据 2025-06-10 17:16:58 +08:00
hang 9f187e4b36 稽查 申请PM 重阅,有SPM 判断
continuous-integration/drone/push Build is passing Details
2025-06-10 15:11:41 +08:00
hang 663ec11c8d 稽查修改,区分标识
continuous-integration/drone/push Build is passing Details
2025-06-10 13:44:27 +08:00
he 122447a621 修改
continuous-integration/drone/push Build is passing Details
2025-06-10 10:31:09 +08:00
he 31bd3c646b 修改
continuous-integration/drone/push Build is passing Details
2025-06-10 10:11:16 +08:00
he f53fc409cb 修改
continuous-integration/drone/push Build is passing Details
2025-06-10 10:06:11 +08:00
he 7f4357de68 修改
continuous-integration/drone/push Build is passing Details
2025-06-10 09:37:26 +08:00
he 23fa689498 修改
continuous-integration/drone/push Build is passing Details
2025-06-10 09:31:55 +08:00
he e564debbab Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-06-10 09:13:49 +08:00
he 04aa2c79f6 修改 2025-06-10 09:13:48 +08:00
hang 68d66523e8 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-06-09 17:38:44 +08:00
hang 549f394d43 增加稽查标识 2025-06-09 17:38:41 +08:00
he 5a83bf9221 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-06-09 17:01:32 +08:00
he 65bffa26b7 修改定时发送 2025-06-09 17:01:30 +08:00
hang 39a6f6a9dc Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-06-09 15:26:22 +08:00
hang fc02428435 导表修改 2025-06-09 15:26:20 +08:00
he 4262189508 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-06-09 15:21:34 +08:00
he 6dd6a5f696 修改 2025-06-09 15:21:33 +08:00
hang e03f2e35b5 修改到处临床数据bug
continuous-integration/drone/push Build is running Details
2025-06-09 15:20:02 +08:00
hang ea4a3db145 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-06-09 14:57:02 +08:00
hang f4ac12d4f8 修改导表+界面展示 2025-06-09 14:57:01 +08:00
he a2afe4f6e4 修改
continuous-integration/drone/push Build is passing Details
2025-06-09 14:44:17 +08:00
he ef31afa1d8 修改
continuous-integration/drone/push Build is passing Details
2025-06-09 14:14:41 +08:00
he d9fda60b99 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-06-09 13:39:48 +08:00
he 6508698bda 添加字段 2025-06-09 13:39:47 +08:00
hang 734675a1a5 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-06-09 10:31:06 +08:00
hang d2697d5133 检查部位、检查技术、检查名称在确认提交时,增加校验 2025-06-09 10:31:02 +08:00
he 1c7f4eac90 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-06-09 10:19:03 +08:00
he 6dcaa953d4 添加历史记录 2025-06-09 10:19:00 +08:00
hang 1f67d24abc 修改部位使用
continuous-integration/drone/push Build is passing Details
2025-06-06 14:24:25 +08:00
hang c7a310c1ad Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-06-06 11:12:51 +08:00
hang 578aaa467c 邮件修改+影像导出 2025-06-06 11:12:46 +08:00
he 82a7c4de29 报错修改
continuous-integration/drone/push Build is passing Details
2025-06-05 16:19:59 +08:00
he fe4df21a68 稽查添加
continuous-integration/drone/push Build is passing Details
2025-06-05 15:19:39 +08:00
he 9cb6fb2a92 添加验证
continuous-integration/drone/push Build is passing Details
2025-06-05 14:52:07 +08:00
he 37cfe07ea6 添加字段
continuous-integration/drone/push Build is passing Details
2025-06-05 09:20:47 +08:00
he 52ffdac948 修改
continuous-integration/drone/push Build is passing Details
2025-06-04 17:09:23 +08:00
he 83a15a0b47 修改
continuous-integration/drone/push Build is passing Details
2025-06-04 16:14:24 +08:00
he 1952a108a9 修改
continuous-integration/drone/push Build is passing Details
2025-06-04 16:00:32 +08:00
he ccff36507f 修改
continuous-integration/drone/push Build is passing Details
2025-06-04 15:51:27 +08:00
he 285fd0e1bb 修改映射
continuous-integration/drone/push Build is passing Details
2025-06-04 15:17:58 +08:00
he 918aafa7e5 测量值可以为空
continuous-integration/drone/push Build is passing Details
2025-06-04 14:24:26 +08:00
he 60accb2f87 修改
continuous-integration/drone/push Build is passing Details
2025-06-04 14:21:56 +08:00
he 018c9afee0 添加表
continuous-integration/drone/push Build is passing Details
2025-06-04 14:13:38 +08:00
he 1b9351b23f Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-06-04 13:36:31 +08:00
he 86ea3373fc 修改 2025-06-04 13:36:30 +08:00
hang dd3b43da7b Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-06-04 13:15:22 +08:00
hang 3151e42e78 访视裁判,裁判选择标记bug 2025-06-04 13:15:21 +08:00
he 5d8f94ef65 修改
continuous-integration/drone/push Build is passing Details
2025-06-04 10:51:32 +08:00
he 26acc94172 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is running Details
2025-06-04 10:50:28 +08:00
he 5c663ec3e9 添加获取模块类型列表 2025-06-04 10:50:28 +08:00
hang 5c0917f14e Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-06-04 10:20:52 +08:00
hang 74230da400 修改CDISC 多表导出 2025-06-04 10:20:47 +08:00
he df104ec527 验证修改
continuous-integration/drone/push Build is passing Details
2025-06-04 09:42:00 +08:00
he 5cdef2051a 修改
continuous-integration/drone/push Build is passing Details
2025-06-03 17:21:07 +08:00
he 470d05131e 修改
continuous-integration/drone/push Build is passing Details
2025-06-03 16:57:04 +08:00
he 5e046b847d Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-06-03 16:12:11 +08:00
he 8b6fcf9ae1 添加字段 2025-06-03 16:12:09 +08:00
hang 4dfa1e9812 表格问题竖列留底
continuous-integration/drone/push Build is passing Details
2025-06-03 16:02:45 +08:00
he a05cb71e0d 修改
continuous-integration/drone/push Build is passing Details
2025-06-03 13:21:56 +08:00
he 137b18446c 修改
continuous-integration/drone/push Build is passing Details
2025-06-03 12:46:51 +08:00
he e44478dafe 修改
continuous-integration/drone/push Build is passing Details
2025-06-03 11:45:57 +08:00
he 0a5a36adb3 添加表
continuous-integration/drone/push Build is passing Details
2025-06-03 10:43:31 +08:00
he 91e4553795 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-05-30 09:03:29 +08:00
he f504cfee9e 修改 2025-05-30 09:03:26 +08:00
hang 2e7e3acf68 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-05-29 17:56:20 +08:00
hang c00b98daf8 心动超声后端处理下载,上传,预备 2025-05-29 17:56:19 +08:00
he 235f420e97 添加字段
continuous-integration/drone/push Build is passing Details
2025-05-29 11:37:35 +08:00
he da15b352a1 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-05-29 11:30:28 +08:00
he 9857b1ae92 添加发送邮件功能 2025-05-29 11:30:24 +08:00
hang 9e83f8df36 返回EnrollId
continuous-integration/drone/push Build is passing Details
2025-05-28 11:35:05 +08:00
he ae431df9b5 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-05-27 13:30:14 +08:00
he e5592c5bfa 修改 2025-05-27 13:30:11 +08:00
hang 1b2bca9f1e Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-05-27 13:15:41 +08:00
hang 4235e6025a 下载项目影像修改 2025-05-27 13:15:38 +08:00
he 209ce439c2 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-05-27 10:27:36 +08:00
he eabf8f4532 添加字段 2025-05-27 10:27:33 +08:00
hang 07ddb7b234 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-05-26 16:37:44 +08:00
hang 1f157bd1a2 项目下载影像 2025-05-26 16:37:41 +08:00
he 0c28f85f49 修改按照标记时间排序
continuous-integration/drone/push Build is passing Details
2025-05-26 14:11:54 +08:00
hang 44536b803f 裁判阅片明细表bug
continuous-integration/drone/push Build is passing Details
2025-05-23 19:13:23 +08:00
hang 750c31a159 修改导出bug
continuous-integration/drone/push Build is passing Details
2025-05-23 19:00:33 +08:00
hang 8325ccad72 返回系统简称配置
continuous-integration/drone/push Build is passing Details
2025-05-22 17:40:30 +08:00
hang c27c4ce617 生产删除影像
continuous-integration/drone/push Build is passing Details
2025-05-22 17:16:35 +08:00
hang b9ecabf915 稽查退回修改
continuous-integration/drone/push Build is passing Details
2025-05-22 16:36:12 +08:00
hang 4f13a98eff 修改退回
continuous-integration/drone/push Build is passing Details
2025-05-22 15:45:26 +08:00
hang 2a4e4e66fa 阅片跟踪页面,返回该任务退回原因字
continuous-integration/drone/push Build is passing Details
2025-05-22 15:23:59 +08:00
hang b72cc5cbc0 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-05-22 15:19:30 +08:00
hang 04f91560b8 pm设置回退原因 2025-05-22 15:19:27 +08:00
he 87af2edf17 修改领取
continuous-integration/drone/push Build is passing Details
2025-05-22 13:41:38 +08:00
he 856977612d Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-05-22 11:07:00 +08:00
he d1ac17cee1 修改退回的时候不让领取 2025-05-22 11:06:59 +08:00
hang 9748e70cdf 申请退回领取限制
continuous-integration/drone/push Build is passing Details
2025-05-22 11:02:05 +08:00
hang 5ba0a9bbd0 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-05-21 15:15:23 +08:00
hang d8ea1e8f94 修改IR 申请,PM 同意发送邮件 2025-05-21 15:15:21 +08:00
he 806d663cfb Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-05-21 14:05:17 +08:00
he 7ab1728bc7 修改关键序列排序 2025-05-21 14:05:16 +08:00
hang 32e64eaeff MR MRI 统一,认为是一致的
continuous-integration/drone/push Build is passing Details
2025-05-20 15:24:20 +08:00
he 7b6efd52bb 修改国际化
continuous-integration/drone/push Build is passing Details
2025-05-20 14:30:19 +08:00
he 7467f2ae11 修改
continuous-integration/drone/push Build is passing Details
2025-05-20 10:28:42 +08:00
he c498ffb21b 修改
continuous-integration/drone/push Build is passing Details
2025-05-19 17:15:15 +08:00
he 1dadf7b8b6 修改
continuous-integration/drone/push Build is passing Details
2025-05-19 16:42:33 +08:00
he 793cf1b570 修改
continuous-integration/drone/push Build is passing Details
2025-05-19 16:22:43 +08:00
he 5a8f564b74 修改
continuous-integration/drone/push Build is passing Details
2025-05-19 15:58:27 +08:00
he e24c9e6ac2 代码修改
continuous-integration/drone/push Build is passing Details
2025-05-19 13:54:29 +08:00
he 814f6fd83c 修改发送邮件逻辑
continuous-integration/drone/push Build is passing Details
2025-05-19 13:47:17 +08:00
hang d34be9b07d 修改获取授权
continuous-integration/drone/push Build is passing Details
2025-05-09 09:28:53 +08:00
hang e205ea0018 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-05-09 09:09:11 +08:00
hang ba9d3a30b9 增加老的获取token的接口 2025-05-09 09:09:10 +08:00
he 10cf908775 修改
continuous-integration/drone/push Build is passing Details
2025-05-08 14:49:57 +08:00
he ee9ad472f0 修改
continuous-integration/drone/push Build is passing Details
2025-05-08 13:57:16 +08:00
he 1cdfc5f2b5 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-05-08 13:41:47 +08:00
he 9e9f07e4fd 修改 2025-05-08 13:41:46 +08:00
hang a0294e6bd6 修改触发逻辑
continuous-integration/drone/push Build is passing Details
2025-05-08 13:16:43 +08:00
hang 55fdf431e0 修改触发邮件逻辑
continuous-integration/drone/push Build is passing Details
2025-05-08 13:12:39 +08:00
hang 9f1110f91a Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-05-08 11:44:42 +08:00
hang d76b24bc25 驳回修改 2025-05-08 11:44:35 +08:00
he 0787875b3b 修改
continuous-integration/drone/push Build is passing Details
2025-05-08 09:50:03 +08:00
he b33aca3230 合并
continuous-integration/drone/push Build is passing Details
2025-05-07 15:47:09 +08:00
he 0b291e8b21 修改邮件 2025-05-07 15:46:32 +08:00
hang 0cf7e5fddd 增加备注
continuous-integration/drone/push Build is passing Details
2025-05-07 15:25:56 +08:00
hang 95f708da58 修改邮件配置
continuous-integration/drone/push Build is passing Details
2025-05-07 14:53:49 +08:00
hang ce47776f19 统一修改邮件配置
continuous-integration/drone/push Build is passing Details
2025-05-07 14:47:22 +08:00
hang 7fff48b07a 修改邮件配置
continuous-integration/drone/push Build is passing Details
2025-05-07 14:17:16 +08:00
hang 4688cb388e 修改触发
continuous-integration/drone/push Build is passing Details
2025-05-07 13:51:32 +08:00
he 21e1a7ab01 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is failing Details
2025-05-07 13:39:52 +08:00
he 5952af85f1 修改 2025-05-07 13:39:51 +08:00
hang 5e193db33b 修改中心调研邮件
continuous-integration/drone/push Build is running Details
2025-05-07 13:38:52 +08:00
he 30bcfcc675 修改
continuous-integration/drone/push Build is passing Details
2025-05-07 13:34:49 +08:00
he 514764ee6e 邮件修改
continuous-integration/drone/push Build is passing Details
2025-05-07 13:16:35 +08:00
he 685269eebf 修改
continuous-integration/drone/push Build is passing Details
2025-05-07 10:25:56 +08:00
he 276ad83080 修改
continuous-integration/drone/push Build is passing Details
2025-05-07 10:21:30 +08:00
he 7a318eae45 代码修改
continuous-integration/drone/push Build is passing Details
2025-05-06 17:08:04 +08:00
hang 0769f8fe05 增加额外参数
continuous-integration/drone/push Build is passing Details
2025-05-06 16:35:05 +08:00
hang 03ac25835e 修改增加TrialId
continuous-integration/drone/push Build is running Details
2025-05-06 16:33:41 +08:00
he 29c3888108 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-05-06 15:34:22 +08:00
he 91951be107 邮件发送修改 2025-05-06 15:34:20 +08:00
hang 935e46a8f1 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-05-06 14:19:08 +08:00
hang 1345808019 修改过滤项目参与人员 2025-05-06 14:19:07 +08:00
he 400d0d3336 修改
continuous-integration/drone/push Build is passing Details
2025-05-06 14:12:57 +08:00
he ca0fd77176 邮件修改
continuous-integration/drone/push Build is passing Details
2025-05-06 13:57:31 +08:00
he f7cf6d9669 邮件修改
continuous-integration/drone/push Build is passing Details
2025-05-06 13:39:49 +08:00
he 7f19944005 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-05-06 10:49:43 +08:00
he e8aec5f04c 添加邮件枚举 2025-05-06 10:49:42 +08:00
hang 0afbd24923 增加备注
continuous-integration/drone/push Build is passing Details
2025-05-06 10:30:48 +08:00
hang 932772f8c8 修改阅片人筛选事件触发
continuous-integration/drone/push Build is passing Details
2025-05-06 10:26:39 +08:00
he b7a803678c Revert "修改稽查"
continuous-integration/drone/push Build is passing Details
This reverts commit 5064630d1f.
2025-04-30 09:57:13 +08:00
he e99360bf5b Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-04-29 17:56:16 +08:00
he 5064630d1f 修改稽查 2025-04-29 17:56:14 +08:00
hang 4034da47f2 测量数据里面带任务Id 需要替换
continuous-integration/drone/push Build is passing Details
2025-04-28 18:03:18 +08:00
hang 381b1965cf Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-04-27 16:24:26 +08:00
hang d5e747becd 修改导出 2025-04-27 16:24:24 +08:00
he 758273d362 修改
continuous-integration/drone/push Build is passing Details
2025-04-27 13:08:22 +08:00
he 1560a0278e 修改
continuous-integration/drone/push Build is passing Details
2025-04-27 13:05:31 +08:00
he 64c275a91c 修改
continuous-integration/drone/push Build is passing Details
2025-04-27 10:18:31 +08:00
he 05189b4e5a 修改稽查
continuous-integration/drone/push Build is passing Details
2025-04-25 17:19:12 +08:00
hang 80aaac997d 没有收件人,默认不发送
continuous-integration/drone/push Build is passing Details
2025-04-25 10:01:32 +08:00
hang c560921092 修改配置
continuous-integration/drone/push Build is passing Details
2025-04-25 09:46:41 +08:00
hang 81cd9aa6b7 修改邮件自动发送配置
continuous-integration/drone/push Build is passing Details
2025-04-25 09:20:06 +08:00
hang 4bfc6dd295 调研表发送邮件修改
continuous-integration/drone/push Build is passing Details
2025-04-24 17:29:22 +08:00
hang a8464364c1 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-04-24 17:22:54 +08:00
hang e333368528 修改邮件发送配置 2025-04-24 17:22:52 +08:00
he 58c1c25fc2 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-04-24 15:53:26 +08:00
he 3045b0ec48 修改 2025-04-24 15:53:22 +08:00
hang 2d50037c03 外部人员 不能看未签名的文档
continuous-integration/drone/push Build is passing Details
2025-04-24 15:18:55 +08:00
he 6a1cd07951 修改
continuous-integration/drone/push Build is passing Details
2025-04-24 14:55:50 +08:00
he c7103654e3 修改
continuous-integration/drone/push Build is passing Details
2025-04-24 10:02:39 +08:00
he 4e03071c4c 修改
continuous-integration/drone/push Build is passing Details
2025-04-24 09:55:49 +08:00
he bf9e8a6758 修改
continuous-integration/drone/push Build is passing Details
2025-04-24 09:36:57 +08:00
he 00c6705161 修改
continuous-integration/drone/push Build is passing Details
2025-04-24 09:31:56 +08:00
he 209d932757 修改
continuous-integration/drone/push Build is passing Details
2025-04-23 16:25:00 +08:00
he e954ea8538 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-04-23 13:56:38 +08:00
he f79a8b4cfb 复制修改 2025-04-23 13:56:36 +08:00
hang 8fc01e2509 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-04-23 13:30:14 +08:00
hang 232e607b8c 增加系统文档导出 2025-04-23 13:30:09 +08:00
he bdeb7c464e 修改
continuous-integration/drone/push Build is passing Details
2025-04-23 10:55:08 +08:00
he 3668079479 修改
continuous-integration/drone/push Build is passing Details
2025-04-23 10:48:16 +08:00
he c115e9453b 修改
continuous-integration/drone/push Build is passing Details
2025-04-23 10:03:24 +08:00
he f157a0f473 登录修改
continuous-integration/drone/push Build is passing Details
2025-04-23 09:54:00 +08:00
he c2252b4e9f 修改
continuous-integration/drone/push Build is passing Details
2025-04-22 17:53:55 +08:00
he b0554119f1 修改
continuous-integration/drone/push Build is passing Details
2025-04-22 13:56:05 +08:00
he 37387692dd 修改
continuous-integration/drone/push Build is passing Details
2025-04-22 13:20:02 +08:00
he 970c41a064 修改
continuous-integration/drone/push Build is passing Details
2025-04-22 11:41:24 +08:00
he 67a3fe0f1b 修改
continuous-integration/drone/push Build is running Details
2025-04-22 11:40:28 +08:00
he 15108156c2 拷贝问题修改
continuous-integration/drone/push Build is passing Details
2025-04-22 11:33:38 +08:00
he 1a2c51da12 修改
continuous-integration/drone/push Build is passing Details
2025-04-22 09:40:16 +08:00
he 3d10482da8 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-04-22 09:29:36 +08:00
he 3abc8787c6 修改 2025-04-22 09:29:32 +08:00
hang df346d31e6 编译错误
continuous-integration/drone/push Build is passing Details
2025-04-22 09:24:30 +08:00
hang a8babb53f2 修改时区测试
continuous-integration/drone/push Build is passing Details
2025-04-22 09:20:49 +08:00
hang 3c1cc92e1b Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-04-21 17:00:28 +08:00
hang 99ee8c4757 增加邮件事件 2025-04-21 17:00:26 +08:00
he fc81d6da22 修改
continuous-integration/drone/push Build is passing Details
2025-04-21 14:50:58 +08:00
he 47edcbd761 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-04-21 14:26:24 +08:00
he a499971e91 修改 2025-04-21 14:26:23 +08:00
he afd34c7c9b 修改
continuous-integration/drone/push Build is running Details
2025-04-21 14:15:21 +08:00
hang 529c646277 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-04-21 14:15:20 +08:00
hang b8f03153ca 修改反了 2025-04-21 14:15:19 +08:00
he a14df73879 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-04-21 14:05:51 +08:00
he cb9141c1ce 修改 2025-04-21 14:05:50 +08:00
hang ff2461dafc 查询启用的角色
continuous-integration/drone/push Build is passing Details
2025-04-21 14:02:39 +08:00
he 779af013e6 修改
continuous-integration/drone/push Build is passing Details
2025-04-21 13:37:50 +08:00
he 75fb357cc5 修改
continuous-integration/drone/push Build is passing Details
2025-04-21 13:24:24 +08:00
he 89eb1032cf 修改
continuous-integration/drone/push Build is passing Details
2025-04-21 11:28:45 +08:00
he eca9c36110 修改
continuous-integration/drone/push Build is passing Details
2025-04-21 11:09:38 +08:00
he 2430651ed4 修改
continuous-integration/drone/push Build is passing Details
2025-04-18 14:13:12 +08:00
he 87d658031b 修改
continuous-integration/drone/push Build is passing Details
2025-04-18 14:10:46 +08:00
he 34b90538ab 修改
continuous-integration/drone/push Build is passing Details
2025-04-18 13:36:53 +08:00
he d2571d5f79 修改
continuous-integration/drone/push Build is passing Details
2025-04-18 11:41:47 +08:00
he 1688d7c934 修改
continuous-integration/drone/push Build is passing Details
2025-04-18 10:24:38 +08:00
he 9ab5dc7d02 修改
continuous-integration/drone/push Build is passing Details
2025-04-17 17:24:15 +08:00
he 97cdec61be 修改
continuous-integration/drone/push Build is passing Details
2025-04-17 17:21:23 +08:00
he c0d6eb3203 修改
continuous-integration/drone/push Build is passing Details
2025-04-17 15:33:44 +08:00
he d125ff6324 x修改
continuous-integration/drone/push Build is passing Details
2025-04-16 13:28:00 +08:00
he 1b830b386e 修改
continuous-integration/drone/push Build is passing Details
2025-04-16 11:30:27 +08:00
he ee83909123 修改
continuous-integration/drone/push Build is passing Details
2025-04-16 10:26:10 +08:00
he 844e27e94e 修改
continuous-integration/drone/push Build is passing Details
2025-04-15 17:55:16 +08:00
he 6fded9f028 修改
continuous-integration/drone/push Build is passing Details
2025-04-15 17:23:45 +08:00
he b5103f1940 修改
continuous-integration/drone/push Build is passing Details
2025-04-15 16:54:38 +08:00
he 8bf54dfc1b 修改
continuous-integration/drone/push Build is running Details
2025-04-15 16:54:24 +08:00
he 1069292c54 修改
continuous-integration/drone/push Build is passing Details
2025-04-15 15:39:59 +08:00
he 88c6f574ec 修改
continuous-integration/drone/push Build is passing Details
2025-04-15 15:36:23 +08:00
he f5f6d3b6ac Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-04-15 15:16:11 +08:00
he 38f937ea9e 查询修改 2025-04-15 15:16:08 +08:00
hang 2913eb67f4 修改删除的文件统计数字
continuous-integration/drone/push Build is running Details
2025-04-15 15:14:37 +08:00
hang a0ceec28e7 修改删除统计文件数字
continuous-integration/drone/push Build is passing Details
2025-04-15 14:54:32 +08:00
hang b77a50caf6 非dicom 删除上传
continuous-integration/drone/push Build is passing Details
2025-04-15 14:33:03 +08:00
hang e17fc6bce9 修改删除
continuous-integration/drone/push Build is passing Details
2025-04-15 13:20:55 +08:00
hang 0970e2807b 修改非dicom 删除
continuous-integration/drone/push Build is passing Details
2025-04-15 11:37:00 +08:00
hang a64d26513a Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-04-15 11:33:24 +08:00
hang 73e80f2571 修改非dicom 删除 2025-04-15 11:33:23 +08:00
hang 770f361d04 修改非dicom 删除
continuous-integration/drone/push Build is passing Details
2025-04-15 11:05:22 +08:00
hang 7476472071 软删除修改
continuous-integration/drone/push Build is passing Details
2025-04-15 10:54:28 +08:00
hang 1fff2f7256 修改删除
continuous-integration/drone/push Build is passing Details
2025-04-15 09:13:56 +08:00
he 1e4044488d Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-04-14 14:39:42 +08:00
he 1e1822151b 修改 2025-04-14 14:39:40 +08:00
hang c37b6c4c84 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-04-14 14:23:49 +08:00
hang 8f65b9e60e 修改检查列表查询 2025-04-14 14:23:47 +08:00
he a8a9a09f88 修改
continuous-integration/drone/push Build is passing Details
2025-04-14 14:11:06 +08:00
he 7526ca2905 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is running Details
2025-04-14 14:10:23 +08:00
he fd83c3e7a7 修改 2025-04-14 14:10:23 +08:00
hang a3c575857a Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-04-14 14:04:53 +08:00
hang c292f6ce44 修改质控删除bug 测试 2025-04-14 14:04:51 +08:00
he 84a7d75174 修改
continuous-integration/drone/push Build is running Details
2025-04-14 14:03:54 +08:00
he 048afdcc7b Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-04-14 13:56:54 +08:00
he 06e319cf09 代码修改 2025-04-14 13:56:52 +08:00
hang cee0730e03 升级nuget包
continuous-integration/drone/push Build is passing Details
2025-04-14 13:54:27 +08:00
he adaa672185 修改
continuous-integration/drone/push Build is passing Details
2025-04-14 11:20:59 +08:00
he b6b5d3e4c9 代码修改
continuous-integration/drone/push Build is passing Details
2025-04-14 11:03:01 +08:00
he 6ac057a0c7 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-04-14 10:28:33 +08:00
he 6607a0ff8f 修改复制 2025-04-14 10:28:30 +08:00
hang 0374551655 修改限制
continuous-integration/drone/push Build is passing Details
2025-04-11 17:43:07 +08:00
hang be9f0075e3 增加申请限制
continuous-integration/drone/push Build is passing Details
2025-04-11 16:57:39 +08:00
hang 2cc004746e 增加限制
continuous-integration/drone/push Build is passing Details
2025-04-11 16:32:00 +08:00
hang b1a7ac1d28 修改映射字段
continuous-integration/drone/push Build is passing Details
2025-04-11 15:05:18 +08:00
hang 5a31e16c65 修改限制
continuous-integration/drone/push Build is passing Details
2025-04-11 14:06:53 +08:00
hang 0b56836097 升级普通组件包
continuous-integration/drone/push Build is passing Details
2025-04-11 13:40:07 +08:00
hang a4a11aaaec 修改查询条件
continuous-integration/drone/push Build is passing Details
2025-04-11 11:36:29 +08:00
hang ae1b2523b1 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-04-11 10:21:45 +08:00
hang 87f9d08c74 添加冗余字段 2025-04-11 10:21:42 +08:00
he 81abd9622f 修改病灶验证
continuous-integration/drone/push Build is passing Details
2025-04-11 10:10:46 +08:00
he faf87f5b86 修改记录
continuous-integration/drone/push Build is passing Details
2025-04-10 17:30:09 +08:00
he 66dc231850 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-04-10 14:01:07 +08:00
he 34ff47bc1c 修改 2025-04-10 14:01:06 +08:00
hang 842b7117a2 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-04-10 11:50:23 +08:00
hang 6543aba53f 文档签署 2025-04-10 11:50:20 +08:00
he 06cee8926e 修改
continuous-integration/drone/push Build is passing Details
2025-04-10 11:18:36 +08:00
he a35fe11f5b 修改
continuous-integration/drone/push Build is passing Details
2025-04-10 11:14:00 +08:00
he cc13544968 修改
continuous-integration/drone/push Build is passing Details
2025-04-10 10:40:05 +08:00
he 19ee511d8d Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-04-10 10:32:18 +08:00
he 0b47d6ca09 新增文件夹 2025-04-10 10:32:15 +08:00
hang 00193bb4bc 修改统计数字
continuous-integration/drone/push Build is passing Details
2025-04-10 10:05:15 +08:00
hang 4379fcbdb3 增加字段
continuous-integration/drone/push Build is passing Details
2025-04-10 09:49:38 +08:00
hang 8464625bbf 修改统计数字
continuous-integration/drone/push Build is passing Details
2025-04-10 09:31:36 +08:00
hang e88f949cb7 增加新的提示
continuous-integration/drone/push Build is passing Details
2025-04-09 17:46:23 +08:00
hang 4fdb0737fc 修改查询条件
continuous-integration/drone/push Build is passing Details
2025-04-09 17:07:18 +08:00
hang 04be8cf642 修改访视查询下拉框
continuous-integration/drone/push Build is passing Details
2025-04-09 17:03:17 +08:00
hang 089ecbf144 增加访视提交时间
continuous-integration/drone/push Build is passing Details
2025-04-09 16:48:22 +08:00
hang b95e97a5df 增加用户列表接口
continuous-integration/drone/push Build is passing Details
2025-04-09 16:45:52 +08:00
hang e84710c4aa 修改查询
continuous-integration/drone/push Build is passing Details
2025-04-09 15:20:40 +08:00
hang f549d1fca9 删除部分迁移文件
continuous-integration/drone/push Build is passing Details
2025-04-09 15:15:26 +08:00
hang 2c72dc000f Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-04-09 15:09:54 +08:00
hang 2ceddc1340 修改统计 2025-04-09 15:09:53 +08:00
he f118c48d0e Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-04-09 15:03:58 +08:00
he a3a1236d5b 修改 2025-04-09 15:03:57 +08:00
hang a7848727ee Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-04-09 11:27:21 +08:00
hang 5787c36687 修改验证 2025-04-09 11:27:17 +08:00
he 4e66ea43d7 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-04-09 10:59:42 +08:00
he 68d69181a9 修改 2025-04-09 10:59:39 +08:00
hang b879b3f2c7 国际化修改
continuous-integration/drone/push Build is passing Details
2025-04-08 15:20:09 +08:00
hang 4fd77ce70d 影像回退逻辑增加
continuous-integration/drone/push Build is passing Details
2025-04-08 14:50:45 +08:00
he 8213c6d10f 测试肿瘤学评估
continuous-integration/drone/push Build is passing Details
2025-04-08 09:31:36 +08:00
he b79b4567b4 修改稽查文档
continuous-integration/drone/push Build is passing Details
2025-04-07 17:42:58 +08:00
he 8c5b759a92 同步修改
continuous-integration/drone/push Build is passing Details
2025-04-07 17:25:37 +08:00
he ab1e33a799 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-04-07 16:29:32 +08:00
he 824b65d5ee 同步系统标准修改 2025-04-07 16:29:31 +08:00
hang c68004b804 维护用户登录区域
continuous-integration/drone/push Build is passing Details
2025-04-07 15:45:43 +08:00
hang d5cd8dc338 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-04-07 15:24:08 +08:00
hang 1fef5509d1 修改解析IP区域位置的库 以及异地登录判断 2025-04-07 15:24:05 +08:00
he 06d12ce709 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-04-07 15:12:49 +08:00
he ad7ac80444 复制稽查文档 2025-04-07 15:11:49 +08:00
hang 29d0ebdb7c 账户日志的操作类型支持多选
continuous-integration/drone/push Build is passing Details
2025-04-07 14:31:06 +08:00
hang 9bcaa07c1a 测试新的解析IP的库
continuous-integration/drone/push Build is passing Details
2025-04-07 14:08:30 +08:00
he bf5f56f5f2 修改查询
continuous-integration/drone/push Build is passing Details
2025-04-07 11:06:11 +08:00
he c1c2b59641 修改
continuous-integration/drone/push Build is passing Details
2025-04-03 17:31:51 +08:00
he 33820c2acb 修改
continuous-integration/drone/push Build is passing Details
2025-04-03 17:26:19 +08:00
he 9814f63246 修改
continuous-integration/drone/push Build is passing Details
2025-04-03 16:27:06 +08:00
he 5e6c018199 修改
continuous-integration/drone/push Build is passing Details
2025-04-03 16:10:11 +08:00
he b24751287c 修改
continuous-integration/drone/push Build is running Details
2025-04-03 16:07:48 +08:00
he 759cc6c1be 修改
continuous-integration/drone/push Build is passing Details
2025-04-03 15:29:22 +08:00
he 1a929ce2ca 修改
continuous-integration/drone/push Build is passing Details
2025-04-03 15:10:04 +08:00
he 1e4571a814 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-04-03 14:31:25 +08:00
he 848670d128 修改 2025-04-03 14:31:23 +08:00
hang 6d9b5fdb2d 测试项目降级为8版本
continuous-integration/drone/push Build is passing Details
2025-04-03 14:26:11 +08:00
hang ab00d64725 移除之前的废弃包换用微软新包
continuous-integration/drone/push Build is running Details
2025-04-03 14:23:42 +08:00
hang 5e36054ee9 删除logdashboard 升级下fo-dicom 常见包
continuous-integration/drone/push Build is passing Details
2025-04-03 14:17:24 +08:00
hang 2b11042f35 升级fo-dicom
continuous-integration/drone/push Build is passing Details
2025-04-03 14:04:28 +08:00
hang e9e5075c1c 验证环境生成缩略图测试ok提交
continuous-integration/drone/push Build is passing Details
2025-04-03 13:56:54 +08:00
hang 9149dd6cf6 Merge branch 'Uat_IRC_Net8' into Test_IRC_Net8 2025-04-03 11:30:53 +08:00
hang 24bfb4920c IQC回退到CRC 上传修改
continuous-integration/drone/push Build is passing Details
2025-04-03 11:29:13 +08:00
he 553ad4760c 修改
continuous-integration/drone/push Build is passing Details
2025-04-02 15:55:12 +08:00
he 5acd56689b 修改
continuous-integration/drone/push Build is running Details
2025-04-02 15:52:07 +08:00
hang 5f0643d4d4 修改swagger 展示 2025-04-02 14:03:36 +08:00
he 17e4ed38be 修改
continuous-integration/drone/push Build is passing Details
2025-04-02 13:46:08 +08:00
he c49f16b055 修改
continuous-integration/drone/push Build is passing Details
2025-04-02 13:44:36 +08:00
he aa42d64cac 修改
continuous-integration/drone/push Build is passing Details
2025-04-01 18:05:58 +08:00
he 8f0ef86ab9 修改
continuous-integration/drone/push Build is passing Details
2025-04-01 17:55:48 +08:00
he c17b2f6bac 修改
continuous-integration/drone/push Build is running Details
2025-04-01 17:53:13 +08:00
he be498f2329 修改
continuous-integration/drone/push Build is passing Details
2025-04-01 16:54:23 +08:00
he fea3d8d415 修改
continuous-integration/drone/push Build is passing Details
2025-04-01 16:39:28 +08:00
he bffc8626c7 修改
continuous-integration/drone/push Build is passing Details
2025-04-01 16:31:47 +08:00
he 1efe1228ac 修改
continuous-integration/drone/push Build is passing Details
2025-04-01 16:21:50 +08:00
he f900b811c7 修改
continuous-integration/drone/push Build is passing Details
2025-04-01 16:18:10 +08:00
he a5f7e03f6c 修改
continuous-integration/drone/push Build is passing Details
2025-04-01 16:05:13 +08:00
he 7aba4adc14 修改
continuous-integration/drone/push Build is passing Details
2025-04-01 15:52:37 +08:00
he 12c8c641ea 查询修改
continuous-integration/drone/push Build is passing Details
2025-04-01 15:09:47 +08:00
he 977adf3153 修改项目下拉查询
continuous-integration/drone/push Build is passing Details
2025-04-01 13:10:30 +08:00
he 409b76df26 修改
continuous-integration/drone/push Build is passing Details
2025-03-31 17:48:30 +08:00
he 2d9ebe7094 修改
continuous-integration/drone/push Build is passing Details
2025-03-31 15:26:13 +08:00
he 71e797cf7d 修改
continuous-integration/drone/push Build is passing Details
2025-03-31 14:06:17 +08:00
he f781027388 修改 2025-03-31 14:01:29 +08:00
he 56e316e6a1 修改
continuous-integration/drone/push Build is passing Details
2025-03-31 11:51:19 +08:00
he edef3e1990 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-03-31 11:09:24 +08:00
he d6d0965467 整体肿瘤评估 计算修改 2025-03-31 11:09:22 +08:00
hang 62f888710c 修改阅片人统计查询
continuous-integration/drone/push Build is passing Details
2025-03-28 14:55:59 +08:00
he 58e8c2392f 添加MRecisthhc
continuous-integration/drone/push Build is passing Details
2025-03-28 11:12:09 +08:00
he 0e1310324d 修改
continuous-integration/drone/push Build is passing Details
2025-03-28 09:48:46 +08:00
he efcda61104 修改
continuous-integration/drone/push Build is passing Details
2025-03-27 17:11:00 +08:00
he 97133a5554 文档修改
continuous-integration/drone/push Build is passing Details
2025-03-27 16:29:55 +08:00
he 577ed42f97 修改文件路径
continuous-integration/drone/push Build is passing Details
2025-03-27 16:19:08 +08:00
he 8f704c4617 修改
continuous-integration/drone/push Build is passing Details
2025-03-27 16:03:12 +08:00
he a03d7f55c7 稽查文档
continuous-integration/drone/push Build is passing Details
2025-03-27 15:48:33 +08:00
he 2bbc101f95 修改
continuous-integration/drone/push Build is passing Details
2025-03-27 11:17:59 +08:00
he 2b11e9165f 修改
continuous-integration/drone/push Build is passing Details
2025-03-27 11:08:06 +08:00
he b25dec68cb 修改项目下拉列表
continuous-integration/drone/push Build is passing Details
2025-03-27 10:55:45 +08:00
hang 2f57238f66 修改swagger 展示
continuous-integration/drone/push Build is passing Details
2025-03-26 14:22:26 +08:00
hang 8239061a84 修改异地登录bug 设置最后一次登录的iP
continuous-integration/drone/push Build is passing Details
2025-03-26 11:11:34 +08:00
hang a43ebc016a 修改异地登录ip逻辑
continuous-integration/drone/push Build is passing Details
2025-03-25 17:49:13 +08:00
hang c036aab9c4 修改refer
continuous-integration/drone/push Build is passing Details
2025-03-24 17:13:43 +08:00
hang dd6419dc44 撤回修改
continuous-integration/drone/push Build is passing Details
2025-03-24 17:13:21 +08:00
hang 54a9cfbb8e 修改refer
continuous-integration/drone/push Build is running Details
2025-03-24 17:12:04 +08:00
hang 2eb85b6b71 Merge branch 'Test_IRC_Net8' into Uat_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-03-24 16:59:47 +08:00
hang 712f1ceb61 修改非dicom 检查
continuous-integration/drone/push Build is passing Details
2025-03-24 16:59:11 +08:00
he adf873639c 修改稽查报错问题
continuous-integration/drone/push Build is passing Details
2025-03-24 16:46:33 +08:00
hang 4e9460e7da 为cdn 部署取项目Id 修改
continuous-integration/drone/push Build is passing Details
2025-03-24 15:17:44 +08:00
hang 5481eac988 增加环境变量
continuous-integration/drone/push Build is passing Details
2025-03-24 11:20:03 +08:00
he b7c9abca02 拷贝表单
continuous-integration/drone/push Build is passing Details
2025-03-20 17:37:18 +08:00
he 447d3fc36f Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-03-20 14:37:26 +08:00
he 71702b2abb 修改显示检查名称 2025-03-20 14:37:25 +08:00
hang 2ffb24d630 维护oss 数据大小
continuous-integration/drone/push Build is passing Details
2025-03-20 13:15:50 +08:00
he 81ecd9ff7f Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-03-20 09:38:24 +08:00
he ebe012ca67 修改 2025-03-20 09:38:22 +08:00
hang 805176c3ea 修改编译错误
continuous-integration/drone/push Build is running Details
2025-03-20 09:35:34 +08:00
hang f99cab6983 获取文件大小 oss
continuous-integration/drone/push Build is passing Details
2025-03-19 17:16:39 +08:00
he c94d800646 移动文件
continuous-integration/drone/push Build is passing Details
2025-03-19 15:47:31 +08:00
he 0cbc345f49 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-03-18 15:20:07 +08:00
he d34f3f1576 修改 2025-03-18 15:20:05 +08:00
hang 73f5e67e3c 切换账户排序修改
continuous-integration/drone/push Build is passing Details
2025-03-18 13:55:30 +08:00
he 5cf8c7a898 查询撤销
continuous-integration/drone/push Build is passing Details
2025-03-18 10:33:43 +08:00
he 11c9cebf47 接口修改
continuous-integration/drone/push Build is passing Details
2025-03-18 10:08:53 +08:00
he bd9362fb25 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-03-18 09:43:03 +08:00
he 3aa588f125 修改 2025-03-18 09:43:01 +08:00
hang 6d33a2c1b0 修改角色排序
continuous-integration/drone/push Build is passing Details
2025-03-18 09:33:38 +08:00
he 9e4cf1d1db 修改
continuous-integration/drone/push Build is passing Details
2025-03-17 16:44:43 +08:00
he ae38c84ef7 修改
continuous-integration/drone/push Build is passing Details
2025-03-17 16:30:27 +08:00
he 6306c6752a 修改
continuous-integration/drone/push Build is passing Details
2025-03-17 16:23:38 +08:00
he 5490630922 修改
continuous-integration/drone/push Build is passing Details
2025-03-17 15:31:53 +08:00
he d1ea886e76 修改排序
continuous-integration/drone/push Build is passing Details
2025-03-17 14:48:16 +08:00
he 7dc1205bb6 修改
continuous-integration/drone/push Build is passing Details
2025-03-17 14:14:01 +08:00
he 15b094a422 修改
continuous-integration/drone/push Build is passing Details
2025-03-17 13:52:23 +08:00
he 78c46572a2 修改路径
continuous-integration/drone/push Build is passing Details
2025-03-17 11:21:56 +08:00
he abeda7820d 修改文件路径
continuous-integration/drone/push Build is passing Details
2025-03-17 10:50:21 +08:00
he 2dac67618c 修改
continuous-integration/drone/push Build is passing Details
2025-03-14 17:10:22 +08:00
he bb9b1283bf 修改
continuous-integration/drone/push Build is passing Details
2025-03-14 16:47:39 +08:00
he b9458ecb38 修改
continuous-integration/drone/push Build is passing Details
2025-03-14 16:12:42 +08:00
he b205757f8f 修改
continuous-integration/drone/push Build is passing Details
2025-03-14 16:01:41 +08:00
he 0b937663ff 修改
continuous-integration/drone/push Build is passing Details
2025-03-14 15:11:21 +08:00
he 7a75d107dc 修改
continuous-integration/drone/push Build is passing Details
2025-03-14 14:50:12 +08:00
he 0f682dd184 修改
continuous-integration/drone/push Build is passing Details
2025-03-14 14:40:14 +08:00
he c2360dd519 修改
continuous-integration/drone/push Build is passing Details
2025-03-14 14:25:02 +08:00
he f9c2ac030a 修改
continuous-integration/drone/push Build is passing Details
2025-03-14 13:29:01 +08:00
he 7ce2386aea 修改
continuous-integration/drone/push Build is passing Details
2025-03-14 12:52:14 +08:00
he 67b226ee44 修改
continuous-integration/drone/push Build is passing Details
2025-03-14 11:45:20 +08:00
he 70c7f8a740 查询修改
continuous-integration/drone/push Build is passing Details
2025-03-14 10:20:55 +08:00
he e409aa3c05 修改
continuous-integration/drone/push Build is passing Details
2025-03-13 16:59:47 +08:00
he a3b5129ee2 修改
continuous-integration/drone/push Build is passing Details
2025-03-13 16:43:19 +08:00
he 57cd0281fa 查询条件修改
continuous-integration/drone/push Build is passing Details
2025-03-13 16:33:53 +08:00
he ba9d3abc52 修改
continuous-integration/drone/push Build is passing Details
2025-03-13 16:16:30 +08:00
he b9bc1f6da0 入项记录上传与修改
continuous-integration/drone/push Build is passing Details
2025-03-13 15:43:53 +08:00
he 08f1998e61 修改
continuous-integration/drone/push Build is passing Details
2025-03-13 15:22:40 +08:00
he bc19ead2f5 添加optuserType
continuous-integration/drone/push Build is passing Details
2025-03-13 14:36:37 +08:00
he f9327c56db Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-03-13 10:42:59 +08:00
he 17f3d821ea 修改 2025-03-13 10:42:57 +08:00
hang 02f3e97654 修改质控看到的列表
continuous-integration/drone/push Build is passing Details
2025-03-12 18:05:34 +08:00
he 94bba7837c 国际化修改
continuous-integration/drone/push Build is passing Details
2025-03-12 11:12:35 +08:00
he 626fcb9cf3 修改
continuous-integration/drone/push Build is passing Details
2025-03-12 09:37:36 +08:00
he bf92165432 修改
continuous-integration/drone/push Build is running Details
2025-03-12 09:36:55 +08:00
he 92e99edbbd 修改项目稽查
continuous-integration/drone/push Build is passing Details
2025-03-11 16:59:34 +08:00
he 94507c7887 修改稽查
continuous-integration/drone/push Build is passing Details
2025-03-11 16:41:36 +08:00
he 0cce75befa Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-03-11 16:36:34 +08:00
he 96469c28d3 修改 2025-03-11 16:36:33 +08:00
hang a12db2637e Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-03-11 15:37:18 +08:00
hang 0a406da3a4 修改过滤 2025-03-11 15:37:14 +08:00
he 6271dd4144 修改中心排序
continuous-integration/drone/push Build is passing Details
2025-03-11 15:12:57 +08:00
he 4182809657 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-03-10 17:40:03 +08:00
he ab6ec064fa 修改重复验证的问题 2025-03-10 17:40:03 +08:00
hang d921a48a09 阿里云返回oss 预览地址
continuous-integration/drone/push Build is passing Details
2025-03-10 17:35:30 +08:00
he 0f03dbc75b 修改
continuous-integration/drone/push Build is passing Details
2025-03-10 14:45:03 +08:00
he e5fa3a09b3 修改查询
continuous-integration/drone/push Build is passing Details
2025-03-10 14:17:22 +08:00
he 4cdfb60fdf 添加阅片人接口
continuous-integration/drone/push Build is passing Details
2025-03-10 13:46:12 +08:00
he 200bdaccc4 修改
continuous-integration/drone/push Build is passing Details
2025-03-10 11:38:10 +08:00
he 17a35e0fdb 修改
continuous-integration/drone/push Build is passing Details
2025-03-10 11:13:43 +08:00
he 28f2bf1629 修改
continuous-integration/drone/push Build is passing Details
2025-03-10 11:06:53 +08:00
he bec04ab795 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-03-10 10:56:12 +08:00
he 54cefab633 修改 2025-03-10 10:56:07 +08:00
hang 2918c0e93f 文件级别设置阅片过滤删除
continuous-integration/drone/push Build is passing Details
2025-03-07 15:54:05 +08:00
hang 92c495ed07 非dicom 预览阅片
continuous-integration/drone/push Build is passing Details
2025-03-07 15:41:46 +08:00
hang d9d807e3d1 修改dicom 只有一个序列设置删除 检查被过滤
continuous-integration/drone/push Build is passing Details
2025-03-07 10:35:18 +08:00
hang 959b49a2cc 非dicom 预览阅片影像 过滤是否阅片
continuous-integration/drone/push Build is passing Details
2025-03-06 16:46:17 +08:00
hang 17f131cf63 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-03-06 15:48:11 +08:00
hang 4ef67beafc 修改非dicom 过滤文件是否阅片,是否删除 2025-03-06 15:48:10 +08:00
he 826a04ef31 修改
continuous-integration/drone/push Build is passing Details
2025-03-06 14:38:12 +08:00
he 104b1208ed 修改
continuous-integration/drone/push Build is passing Details
2025-03-06 14:34:47 +08:00
he 8b3040c24d Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-03-06 13:12:10 +08:00
he 153dbd2016 添加排序 2025-03-06 13:12:09 +08:00
hang 91d5cbce16 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-03-06 11:06:25 +08:00
hang 7218427ec2 用户参与项目,返回授权 和禁用时间 2025-03-06 11:06:24 +08:00
he 5d91ae8157 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-03-06 10:47:56 +08:00
he 0f61d746e5 修改 2025-03-06 10:47:54 +08:00
hang e931e740e6 非dicom 文件修改列表预览可以查看删除
continuous-integration/drone/push Build is passing Details
2025-03-05 15:57:03 +08:00
hang 38917e05ab Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-03-05 15:21:05 +08:00
hang dd33f7e086 修改忽略过滤器 2025-03-05 15:21:04 +08:00
he 43d4d679b5 Merge branch 'Uat_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Uat_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-03-05 13:13:39 +08:00
he e02fa28cd0 修改非Dicom排序 2025-03-05 13:13:37 +08:00
he 055e085d2a 修改非Dicom排序
continuous-integration/drone/push Build is passing Details
2025-03-05 13:12:48 +08:00
he caf1fb6b32 修改
continuous-integration/drone/push Build is passing Details
2025-03-05 11:41:36 +08:00
he 6b049e68cd Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-03-05 11:24:53 +08:00
he 2a4360cc64 修改数据库结构 2025-03-05 11:24:50 +08:00
hang 8efc254f3c Merge branch 'Uat_IRC_Net8' into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-03-05 10:43:11 +08:00
hang f89b3daf50 qc 序列层级返回更新时间
continuous-integration/drone/push Build is passing Details
2025-03-05 10:42:58 +08:00
hang 29f6196698 合并
continuous-integration/drone/push Build is passing Details
2025-03-05 09:47:41 +08:00
hang 09ff2468e9 Merge branch 'Uat_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Uat_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-03-05 09:44:36 +08:00
hang 1867277e1b 修改非dicom 检查编号重复问题 2025-03-05 09:44:34 +08:00
he 053a09a5b2 Merge branch 'Uat_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Uat_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-03-05 09:40:03 +08:00
he fae86f0836 修改非dicom 影像 排序 先按检查时间,再按创建时间排序 2025-03-05 09:39:45 +08:00
he 2e539dfa71 修改非dicom 影像 排序 先按检查时间,再按创建时间排序
continuous-integration/drone/push Build is passing Details
2025-03-05 09:31:37 +08:00
he a9efd85aec 修改
continuous-integration/drone/push Build is passing Details
2025-03-04 17:58:33 +08:00
he 09e70b9f21 修改
continuous-integration/drone/push Build is passing Details
2025-03-04 17:48:33 +08:00
he 2d184e0f08 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-03-04 16:39:44 +08:00
he 43d6c7d8db 查询条件修改 2025-03-04 16:39:42 +08:00
hang 19487acc64 Merge branch 'Uat_IRC_Net8' into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-03-04 15:20:27 +08:00
hang 336e7768b8 时间拼写错误
continuous-integration/drone/push Build is passing Details
2025-03-04 15:20:06 +08:00
hang 9b49dc41c8 Merge branch 'Uat_IRC_Net8' into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-03-04 11:20:51 +08:00
hang 74f896cf00 Merge branch 'Uat_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Uat_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-03-04 11:20:22 +08:00
hang 10b0bde1d0 检查列表增加时间 2025-03-04 11:20:21 +08:00
he 411b5d5f8f 培训记录修改
continuous-integration/drone/push Build is passing Details
2025-03-04 10:45:29 +08:00
he b6da27ac07 修改
continuous-integration/drone/push Build is passing Details
2025-03-03 17:20:38 +08:00
he a6b81b038f 修改
continuous-integration/drone/push Build is passing Details
2025-03-03 16:49:43 +08:00
he e2c80babfa 修改
continuous-integration/drone/push Build is passing Details
2025-03-03 16:34:58 +08:00
he 01b9f28e7b 批量添加
continuous-integration/drone/push Build is passing Details
2025-03-03 16:08:50 +08:00
he 2c9fad60fb 修改
continuous-integration/drone/push Build is passing Details
2025-03-03 16:03:04 +08:00
he 398baea876 字段修改
continuous-integration/drone/push Build is passing Details
2025-03-03 13:56:01 +08:00
he aec94d0a46 数据库修改
continuous-integration/drone/push Build is passing Details
2025-03-03 13:43:34 +08:00
he 4b69e45d44 实体修改
continuous-integration/drone/push Build is passing Details
2025-03-03 13:18:11 +08:00
he 12f2c2becb 修改
continuous-integration/drone/push Build is passing Details
2025-03-03 11:06:44 +08:00
he 699f79278b 修改
continuous-integration/drone/push Build is passing Details
2025-02-28 17:55:45 +08:00
he 88e07d28fd Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-02-28 16:35:03 +08:00
he 226ba06bf3 修改 2025-02-28 16:35:02 +08:00
hang c1667fc48b Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-02-28 11:29:15 +08:00
hang f900e675da 清理项目删除项目参与用户 2025-02-28 11:29:14 +08:00
he ee51b9eade 添加 ImageFormatList
continuous-integration/drone/push Build is passing Details
2025-02-27 17:19:53 +08:00
he bf62ee92dd 添加 ImageFormatList
continuous-integration/drone/push Build is passing Details
2025-02-27 17:19:35 +08:00
he 78532803bc 影像质疑超限天数修改
continuous-integration/drone/push Build is passing Details
2025-02-27 17:12:28 +08:00
he 86ea3ebc32 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-02-27 17:12:01 +08:00
he 01aba3f3e0 影像质疑超限天数修改 2025-02-27 17:11:59 +08:00
hang 6dc1ef5728 增加查询配置
continuous-integration/drone/push Build is passing Details
2025-02-27 16:15:11 +08:00
hang 02e5d770db 增加查询配置
continuous-integration/drone/push Build is passing Details
2025-02-27 16:14:46 +08:00
hang f8c3704c87 合并修改
continuous-integration/drone/push Build is passing Details
2025-02-27 15:39:48 +08:00
hang 4c21459e00 修改StudyName 存放位置
continuous-integration/drone/push Build is passing Details
2025-02-27 15:38:51 +08:00
he cdbbe460ab 修改实体
continuous-integration/drone/push Build is passing Details
2025-02-27 14:31:38 +08:00
he 45da1645e0 Merge branch 'Uat_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Uat_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-02-27 14:31:02 +08:00
he 9a1f4697c2 修改实体 2025-02-27 14:31:01 +08:00
hang 217b8713ff 合并到测试环境
continuous-integration/drone/push Build is passing Details
2025-02-27 13:17:08 +08:00
hang 0998f047e4 Merge branch 'Uat_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Uat_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-02-27 13:05:10 +08:00
hang 47755bc2aa 检查 返回配置字段 2025-02-27 13:05:09 +08:00
he 72efd36649 实体修改
continuous-integration/drone/push Build is running Details
2025-02-27 13:05:00 +08:00
he 553a8d392f 查询返回检查名称
continuous-integration/drone/push Build is passing Details
2025-02-27 11:33:31 +08:00
he f050416265 数据库文件
continuous-integration/drone/push Build is passing Details
2025-02-27 11:21:32 +08:00
he a7d1a92a01 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-02-27 11:17:15 +08:00
he bf9ce9b213 影像配置数据库字段添加 2025-02-27 11:17:13 +08:00
he a9c7c79fb5 影像配置数据库字段添加
continuous-integration/drone/push Build is passing Details
2025-02-27 11:16:53 +08:00
hang dc3a830909 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-02-27 10:49:59 +08:00
hang 171bfde031 质控删除文件 2025-02-27 10:49:58 +08:00
he f471ce1535 修改
continuous-integration/drone/push Build is passing Details
2025-02-26 13:37:30 +08:00
hang 56170b73ed 修改非dicom 阅片
continuous-integration/drone/push Build is passing Details
2025-02-26 13:25:56 +08:00
he 2a9c1ec45c Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-02-26 09:46:28 +08:00
he 3ccb3ff4de 授权修改 2025-02-26 09:46:26 +08:00
hang 6c078806cd 修改token 过期时间
continuous-integration/drone/push Build is passing Details
2025-02-25 16:36:18 +08:00
hang c2f768bef8 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-02-25 16:11:49 +08:00
hang 1b7bb5a4fe 修改链接过期+ 删除oss 数据ok 2025-02-25 16:11:47 +08:00
he dfe6d13182 修改
continuous-integration/drone/push Build is passing Details
2025-02-25 15:51:16 +08:00
he 4c04718db4 修改
continuous-integration/drone/push Build is passing Details
2025-02-25 15:23:42 +08:00
he 64d803cbc0 修改
continuous-integration/drone/push Build is passing Details
2025-02-25 15:01:57 +08:00
he 54ccae5762 修改
continuous-integration/drone/push Build is passing Details
2025-02-25 14:48:05 +08:00
he 8d6bf0c7b1 修改
continuous-integration/drone/push Build is passing Details
2025-02-25 14:26:21 +08:00
he 8496e6bcd7 修改
continuous-integration/drone/push Build is running Details
2025-02-25 14:24:44 +08:00
he 7770fc264b Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-02-25 14:07:26 +08:00
he 51f21805be 非Dicom 2025-02-25 14:07:25 +08:00
hang 434e49ea85 修改用户来源为null bug
continuous-integration/drone/push Build is passing Details
2025-02-25 11:31:19 +08:00
hang 468f7c7070 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is running Details
2025-02-25 11:29:52 +08:00
hang 4577db7582 修改bug 2025-02-25 11:29:51 +08:00
he cc17816dae Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-02-25 11:12:44 +08:00
he 172cecebe4 修改 2025-02-25 11:12:42 +08:00
hang 7913acdf70 修改运维增加字段
continuous-integration/drone/push Build is passing Details
2025-02-25 11:08:37 +08:00
hang e9f57b5001 临时注释提交,手动维护
continuous-integration/drone/push Build is passing Details
2025-02-25 11:02:52 +08:00
he b8c0bd7082 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-02-25 10:38:14 +08:00
he 6200ec1ba7 代码提交 2025-02-25 10:38:11 +08:00
hang 73912fd875 修改列表
continuous-integration/drone/push Build is passing Details
2025-02-25 10:26:55 +08:00
hang 812035686e 修改用户参与项目接口
continuous-integration/drone/push Build is passing Details
2025-02-25 09:57:45 +08:00
hang 1f8caa4616 合并
continuous-integration/drone/push Build is passing Details
2025-02-24 17:21:28 +08:00
hang 4a1e0a87ba 修改管理用户类型过滤 2025-02-24 17:21:09 +08:00
he 337bda45fd 修改
continuous-integration/drone/push Build is passing Details
2025-02-24 17:17:25 +08:00
he c2412bfb4b 复制系统到项目
continuous-integration/drone/push Build is passing Details
2025-02-24 16:54:05 +08:00
he cff6b87daa Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-02-24 16:15:00 +08:00
he 2f93a490f0 查询项目信息 2025-02-24 16:14:59 +08:00
hang 1856ef9483 修改合并
continuous-integration/drone/push Build is passing Details
2025-02-24 16:09:42 +08:00
hang b45602556c 用户创建来源维护,以及用户参与项目列表修改 2025-02-24 16:07:52 +08:00
he b5326d4928 修改
continuous-integration/drone/push Build is passing Details
2025-02-24 13:45:55 +08:00
he 8bbc8c449f 合并
continuous-integration/drone/push Build is running Details
2025-02-24 13:44:57 +08:00
he dcd6375d7d 系统配置 2025-02-24 13:43:22 +08:00
hang 8b53517f2c 角色修改
continuous-integration/drone/push Build is running Details
2025-02-24 13:43:21 +08:00
hang 1c3ddcf1cd 修改邮箱登录bug
continuous-integration/drone/push Build is passing Details
2025-02-24 13:32:54 +08:00
hang fc4ef6b939 账号初始化链接过期+初始化不允许忘记密码修改密码
continuous-integration/drone/push Build is passing Details
2025-02-24 13:24:41 +08:00
hang 6d009c4f86 增加备注
continuous-integration/drone/push Build is passing Details
2025-02-24 11:32:26 +08:00
hang f995187925 修改文件类型基础代码
continuous-integration/drone/push Build is passing Details
2025-02-24 11:25:03 +08:00
hang 67a6a5f861 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-02-21 17:08:35 +08:00
hang 6bfa4da72d 文档服务初步提交 2025-02-21 17:08:33 +08:00
he 9797c94aa8 修改
continuous-integration/drone/push Build is passing Details
2025-02-21 14:46:10 +08:00
hang 5dd2dfd17b 增加项目文档新表
continuous-integration/drone/push Build is passing Details
2025-02-21 14:20:39 +08:00
hang 5791aca9fd 最后一次登录时间 和ip 记录
continuous-integration/drone/push Build is passing Details
2025-02-20 15:26:36 +08:00
hang f61ce70b82 修改项目培训统计数字
continuous-integration/drone/push Build is passing Details
2025-02-20 15:05:13 +08:00
hang c8091d3215 清理签名文档的表
continuous-integration/drone/push Build is passing Details
2025-02-20 13:47:09 +08:00
hang 7153d810aa 删除用户角色表部分字段,修改统计
continuous-integration/drone/push Build is passing Details
2025-02-20 13:08:06 +08:00
hang 68053d24b6 项目创建账户的时候,没有用户名,但是目标用户Id 有值,导致查询出来有名字
continuous-integration/drone/push Build is passing Details
2025-02-19 14:20:06 +08:00
hang a49b4c8ef7 修改冲突
continuous-integration/drone/push Build is passing Details
2025-02-19 10:13:37 +08:00
hang c6c036c152 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-02-19 09:39:21 +08:00
hang db0d087396 修改国际化bug 2025-02-19 09:39:20 +08:00
he 263f43b1a9 项目下拉修改
continuous-integration/drone/push Build is passing Details
2025-02-18 13:35:22 +08:00
hang 21626cda9d 维护历史退出人员数据
continuous-integration/drone/push Build is passing Details
2025-02-18 13:15:05 +08:00
he 77d920a2ef Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-02-18 10:40:31 +08:00
he bbf7af8405 项目筛选修改 2025-02-18 10:40:29 +08:00
hang d0bbc58b20 Merge branch 'Uat_IRC_Net8' into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-02-17 17:15:44 +08:00
hang fed2cbe043 修改 创建账号仅仅记录一条
continuous-integration/drone/push Build is passing Details
2025-02-17 17:15:25 +08:00
hang 4d4bb08423 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-02-17 15:13:02 +08:00
hang dd09c78cd2 项目退出人员维护数据 2025-02-17 15:12:59 +08:00
hang 771091a557 IR 反馈bug 重新设置状态
continuous-integration/drone/push Build is passing Details
2025-02-17 14:16:34 +08:00
he 8ce1bdf21c Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-02-17 10:06:06 +08:00
he 679cc26225 状态修改 2025-02-17 10:06:04 +08:00
hang ff5be827d0 Merge branch 'Uat_IRC_Net8' into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-02-17 09:28:02 +08:00
hang 4555997957 修改文档查询条件问题
continuous-integration/drone/push Build is passing Details
2025-02-17 09:22:58 +08:00
hang 3b43bee0f1 x
continuous-integration/drone/push Build is passing Details
2025-02-14 17:30:21 +08:00
hang 3677a30bab 修改日志角色查询
continuous-integration/drone/push Build is passing Details
2025-02-14 17:19:50 +08:00
hang 2e8b2a24ae 合并冲突
continuous-integration/drone/push Build is passing Details
2025-02-14 16:51:09 +08:00
hang 26500aa1cf 修改日志
continuous-integration/drone/push Build is passing Details
2025-02-14 16:48:52 +08:00
hang adfd6b50c1 修改登录日志
continuous-integration/drone/push Build is failing Details
2025-02-14 16:39:38 +08:00
hang b58bdc184e Merge branch 'Uat_IRC_Net8' into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-02-14 16:15:37 +08:00
hang 580eea7a61 修改稽查查询条件bug
continuous-integration/drone/push Build is passing Details
2025-02-14 16:15:21 +08:00
hang da27b65e56 用户登录日志 项目参与用户修改
continuous-integration/drone/push Build is passing Details
2025-02-14 16:11:48 +08:00
hang bb493fe827 修改日志,和稽查查询 2025-02-14 16:02:49 +08:00
hang 655fe18138 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-02-14 16:00:48 +08:00
hang 9d70d61013 修改日志,和稽查查询 2025-02-14 16:00:46 +08:00
he 68452beba0 添加是否存在未处理反馈的状态
continuous-integration/drone/push Build is passing Details
2025-02-14 15:40:34 +08:00
he 409e9e9bc0 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-02-14 15:18:12 +08:00
he f241f2cd2b 去除掉已经停止的项目 2025-02-14 15:18:10 +08:00
hang 2ea90a66ae Merge branch 'Uat_IRC_Net8' into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-02-14 14:18:51 +08:00
hang c2c8f65aaa 修改质控问题答案排序
continuous-integration/drone/push Build is passing Details
2025-02-14 14:07:33 +08:00
hang a578be8336 Merge branch 'Uat_IRC_Net8' into Test_IRC_Net8 2025-02-14 13:27:52 +08:00
hang 45b469682a 影像质控,质控人看到的是过滤了删除的,其他人看到的是原始的
continuous-integration/drone/push Build is passing Details
2025-02-14 13:09:26 +08:00
hang 1e9f4d890f 质控预览再次修改0214
continuous-integration/drone/push Build is passing Details
2025-02-14 10:54:00 +08:00
hang 44f74fd9e6 修改 预览根据质控状态过滤
continuous-integration/drone/push Build is passing Details
2025-02-13 18:01:17 +08:00
hang f1c8415899 修改阅片预览影像(同时标记了删除 和阅片)
continuous-integration/drone/push Build is passing Details
2025-02-13 17:11:21 +08:00
hang 823ada5673 合并
continuous-integration/drone/push Build is passing Details
2025-02-13 16:53:27 +08:00
hang 55f146a44a 修改序列上的Instance 数量
continuous-integration/drone/push Build is passing Details
2025-02-13 16:52:13 +08:00
hang 5fe972bd99 修改质控预览影像 uat 修改
continuous-integration/drone/push Build is passing Details
2025-02-13 16:43:59 +08:00
hang 0578263e10 修改质控预览影像 uat 修改
continuous-integration/drone/push Build is passing Details
2025-02-13 16:40:34 +08:00
hang 9316c0814f 合并
continuous-integration/drone/push Build is passing Details
2025-02-13 16:16:18 +08:00
hang 8bdd9e29ab 修改阅片看到得instance 序列
continuous-integration/drone/push Build is failing Details
2025-02-13 16:13:52 +08:00
hang ae14a8a530 修改预览不阅片影像数字
continuous-integration/drone/push Build is passing Details
2025-02-13 15:53:02 +08:00
hang 2a170cc97e 修改预览不阅片影像数字
continuous-integration/drone/push Build is passing Details
2025-02-13 15:52:44 +08:00
hang c109e14a93 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-02-13 14:01:02 +08:00
hang 240d406a1a Merge branch 'Uat_IRC_Net8' into Test_IRC_Net8 2025-02-13 14:00:53 +08:00
hang b6cf988058 导表 裁判选择原因bug
continuous-integration/drone/push Build is passing Details
2025-02-13 14:00:34 +08:00
he c16153fa19 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-02-13 13:12:11 +08:00
he a0797a99ff 修改 2025-02-13 13:12:09 +08:00
hang 376cfa3167 合并bug
continuous-integration/drone/push Build is passing Details
2025-02-13 11:06:32 +08:00
hang 14690dc3cc 合并修改
continuous-integration/drone/push Build is passing Details
2025-02-13 10:59:23 +08:00
hang 6dd0831a66 质控预览 增加是否阅片 是否删除 第二次提交
continuous-integration/drone/push Build is passing Details
2025-02-13 10:40:18 +08:00
hang a150156810 修改配置文件
continuous-integration/drone/push Build is passing Details
2025-02-13 10:21:57 +08:00
hang d61fba9d95 Uat 影像阅片初步修改 2025-02-13 10:21:49 +08:00
hang 7cb8d04ca9 Uat 影像阅片初步修改
continuous-integration/drone/push Build is passing Details
2025-02-13 10:17:01 +08:00
he a2fc2c5075 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-02-12 16:52:43 +08:00
he 1ed8788c83 修改邮箱 2025-02-12 16:52:39 +08:00
hang 14e95fd303 修改配置文件
continuous-integration/drone/push Build is passing Details
2025-02-12 14:01:53 +08:00
hang 3dca5aa06f Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-02-11 15:45:54 +08:00
hang faea3a4f86 肿瘤学阅片结果导出 2025-02-11 15:45:50 +08:00
he a5dcad1767 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-02-11 14:38:09 +08:00
he 6754121b17 肿瘤学修改 2025-02-11 14:38:07 +08:00
hang 131425774a 修改MFA 日志
continuous-integration/drone/push Build is passing Details
2025-02-10 15:08:14 +08:00
hang 585e4cdc1b 修改一致性核查对话判断
continuous-integration/drone/push Build is passing Details
2025-02-10 14:54:04 +08:00
hang 12ddeba2a3 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-02-10 09:25:38 +08:00
hang ab0c256e0b 修改国际化返回更多信息 2025-02-10 09:25:37 +08:00
hang 8a24b33757 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-02-08 15:58:17 +08:00
hang 8f6e2e342a 肿瘤评估标准-Id匹配合并去重bug 2025-02-08 15:58:12 +08:00
he 4dfeb653b1 修改逻辑
continuous-integration/drone/push Build is passing Details
2025-02-08 15:43:11 +08:00
he 670a754eb2 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-02-08 11:06:54 +08:00
he 3f46103dc3 修改 2025-02-08 11:06:53 +08:00
hang 6c2901daca Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details
2025-02-08 10:54:45 +08:00
hang a73e5d4133 增加一致性核查排序 以及一致性核查国际化调整填充公司简称 2025-02-08 10:54:44 +08:00
he b067cb66f3 接口修改
continuous-integration/drone/push Build is running Details
2025-02-08 10:54:06 +08:00
he f1185758d6 修改领取规则
continuous-integration/drone/push Build is passing Details
2025-02-08 10:48:00 +08:00
he 0d4a0adf00 添加IsPACSConnect
continuous-integration/drone/push Build is passing Details
2025-02-08 10:23:22 +08:00
he 20f6f95b89 Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is running Details
2025-02-08 10:20:18 +08:00
he 7e7c186784 临床数据修改 2025-02-08 10:20:15 +08:00
hang 70e0a1325a HIR 推送影像bug 修改到IRC 测试
continuous-integration/drone/push Build is passing Details
2025-02-08 09:33:29 +08:00
he 91471dee13 修改配置
continuous-integration/drone/push Build is passing Details
2025-02-07 15:03:14 +08:00
383 changed files with 355987 additions and 939322 deletions

View File

@ -0,0 +1,37 @@
using IRaCIS.Core.Infrastructure.Extention;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Localization;
using Newtonsoft.Json;
namespace IRaCIS.Core.SCP.Filter
{
public class ModelActionFilter : ActionFilterAttribute, IActionFilter
{
public IStringLocalizer _localizer;
public ModelActionFilter(IStringLocalizer localizer)
{
_localizer = localizer;
}
public override void OnActionExecuting(ActionExecutingContext context)
{
if (!context.ModelState.IsValid)
{
var validationErrors = context.ModelState
.Keys
.SelectMany(k => context.ModelState[k]!.Errors)
.Select(e => e.ErrorMessage)
.ToArray();
//---提供给接口的参数无效。
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["ModelAction_InvalidAPIParameter"] + JsonConvert.SerializeObject( validationErrors)));
}
}
}
}

View File

@ -0,0 +1,61 @@
using IRaCIS.Core.Infrastructure;
using IRaCIS.Core.Infrastructure.Extention;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
namespace IRaCIS.Core.SCP.Filter
{
public class ProjectExceptionFilter : Attribute, IExceptionFilter
{
private readonly ILogger<ProjectExceptionFilter> _logger;
public IStringLocalizer _localizer;
public ProjectExceptionFilter(IStringLocalizer localizer, ILogger<ProjectExceptionFilter> logger)
{
_logger = logger;
_localizer = localizer;
}
public void OnException(ExceptionContext context)
{
//context.ExceptionHandled;//记录当前这个异常是否已经被处理过了
if (!context.ExceptionHandled)
{
if (context.Exception.GetType().Name == "DbUpdateConcurrencyException")
{
//---并发更新,当前不允许该操作
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["ProjectException_ConcurrentUpdateNotAllowed"] + context.Exception.Message));
}
if (context.Exception.GetType() == typeof(BusinessValidationFailedException))
{
var error = context.Exception as BusinessValidationFailedException;
context.Result = new JsonResult(ResponseOutput.NotOk(context.Exception.Message, error!.Code));
}
else if(context.Exception.GetType() == typeof(QueryBusinessObjectNotExistException))
{
context.Result = new JsonResult(ResponseOutput.NotOk( context.Exception.Message, ApiResponseCodeEnum.DataNotExist));
}
else
{
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["Project_ExceptionContactDeveloper"] + (context.Exception.InnerException is null ? (context.Exception.Message /*+ context.Exception.StackTrace*/)
: (context.Exception.InnerException?.Message /*+ context.Exception.InnerException?.StackTrace*/)), ApiResponseCodeEnum.ProgramException));
}
_logger.LogError(context.Exception.InnerException is null ? (context.Exception.Message + context.Exception.StackTrace) : (context.Exception.InnerException?.Message + context.Exception.InnerException?.StackTrace));
}
else
{
//继续
}
context.ExceptionHandled = true;//标记当前异常已经被处理过了
}
}
}

View File

@ -0,0 +1,120 @@
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc;
using IRaCIS.Core.Domain.Share;
using IRaCIS.Core.Infrastructure.Extention;
namespace IRaCIS.Core.Application.Service.BusinessFilter
{
/// <summary>
/// 统一返回前端数据包装之前在控制器包装现在修改为动态Api 在ResultFilter这里包装减少重复冗余代码
/// by zhouhang 2021.09.12 周末
/// </summary>
public class UnifiedApiResultFilter : Attribute, IAsyncResultFilter
{
/// <summary>
/// 异步版本
/// </summary>
/// <param name="context"></param>
/// <param name="next"></param>
/// <returns></returns>
public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next)
{
if (context.Result is ObjectResult objectResult)
{
var statusCode = objectResult.StatusCode ?? context.HttpContext.Response.StatusCode;
//是200 并且没有包装 那么包装结果
if (statusCode == 200 && !(objectResult.Value is IResponseOutput))
{
//if (objectResult.Value == null)
//{
// var apiResponse = ResponseOutput.DBNotExist();
// objectResult.Value = apiResponse;
// objectResult.DeclaredType = apiResponse.GetType();
//}
//else
//{
var type = objectResult.Value?.GetType();
if ( type!=null&& type.IsGenericType&&(type.GetGenericTypeDefinition()==typeof(ValueTuple<,>)|| type.GetGenericTypeDefinition()==typeof(Tuple<,>)))
{
//报错
//var tuple = (object, object))objectResult.Value;
//var (val1, val2) = ((dynamic, dynamic))objectResult.Value;
//var apiResponse = ResponseOutput.Ok(val1, val2);
//OK
var tuple = (dynamic)objectResult.Value;
var apiResponse = ResponseOutput.Ok(tuple.Item1, tuple.Item2);
objectResult.Value = apiResponse;
objectResult.DeclaredType = apiResponse.GetType();
}
else
{
var apiResponse = ResponseOutput.Ok(objectResult.Value);
objectResult.Value = apiResponse;
objectResult.DeclaredType = apiResponse.GetType();
}
//}
}
//如果不是200 是IResponseOutput 不处理
else if (statusCode != 200 && (objectResult.Value is IResponseOutput))
{
}
else if(statusCode != 200&&!(objectResult.Value is IResponseOutput))
{
//---程序错误,请联系开发人员。
var apiResponse = ResponseOutput.NotOk(I18n.T("UnifiedAPI_ProgramError"));
objectResult.Value = apiResponse;
objectResult.DeclaredType = apiResponse.GetType();
}
}
await next.Invoke();
}
public static bool IsTupleType(Type type, bool checkBaseTypes = false)
{
if (type == null)
throw new ArgumentNullException(nameof(type));
if (type == typeof(Tuple))
return true;
while (type != null)
{
if (type.IsGenericType)
{
var genType = type.GetGenericTypeDefinition();
if (genType == typeof(Tuple<>)
|| genType == typeof(Tuple<,>)
|| genType == typeof(Tuple<,>))
return true;
}
if (!checkBaseTypes)
break;
type = type.BaseType;
}
return false;
}
}
}

View File

@ -0,0 +1,57 @@
using Autofac;
using IRaCIS.Core.Infra.EFCore;
using Microsoft.AspNetCore.Http;
using Panda.DynamicWebApi;
using System;
using System.Linq;
using System.Reflection;
using IRaCIS.Core.Domain.Models;
using IRaCIS.Core.Domain.Share;
using IRaCIS.Core.Application.Service;
using AutoMapper;
using IRaCIS.Core.SCP.Service;
namespace IRaCIS.Core.SCP
{
// ReSharper disable once IdentifierTypo
public class AutofacModuleSetup : Autofac.Module
{
protected override void Load(ContainerBuilder containerBuilder)
{
#region byzhouhang 20210917 此处注册泛型仓储 可以减少Domain层 和Infra.EFcore 两层 空的仓储接口定义和 仓储文件定义
containerBuilder.RegisterGeneric(typeof(Repository<>))
.As(typeof(IRepository<>)).InstancePerLifetimeScope();//注册泛型仓储
containerBuilder.RegisterType<Repository>().As<IRepository>().InstancePerLifetimeScope();
#endregion
#region 指定控制器也由autofac 来进行实例获取 https://www.cnblogs.com/xwhqwer/p/15320838.html
//获取所有控制器类型并使用属性注入
containerBuilder.RegisterAssemblyTypes(typeof(BaseService).Assembly)
.Where(type => typeof(IDynamicWebApi).IsAssignableFrom(type))
.PropertiesAutowired();
#endregion
Assembly application = Assembly.LoadFrom(AppDomain.CurrentDomain.BaseDirectory + typeof(BaseService).Assembly.GetName().Name+".dll");
containerBuilder.RegisterAssemblyTypes(application).Where(t => t.FullName.Contains("Service"))
.PropertiesAutowired().AsImplementedInterfaces();
//containerBuilder.RegisterType<HttpContextAccessor>().As<IHttpContextAccessor>().SingleInstance();
//containerBuilder.RegisterType<UserInfo>().As<IUserInfo>().InstancePerLifetimeScope();
}
}
}

View File

@ -0,0 +1,64 @@
using EntityFramework.Exceptions.SqlServer;
using IRaCIS.Core.Domain.Share;
using IRaCIS.Core.Infra.EFCore;
using Medallion.Threading;
using Medallion.Threading.SqlServer;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace IRaCIS.Core.SCP
{
public static class EFSetup
{
public static void AddEFSetup( this IServiceCollection services, IConfiguration configuration)
{
services.AddHttpContextAccessor();
services.AddScoped<IUserInfo, UserInfo>();
services.AddScoped<ISaveChangesInterceptor, AuditEntityInterceptor>();
//这个注入没有成功--注入是没问题的构造函数也只是支持参数就好错在注入的地方不能写DbContext
//Web程序中通过重用池中DbContext实例可提高高并发场景下的吞吐量 这在概念上类似于ADO.NET Provider原生的连接池操作方式具有节省DbContext实例化成本的优点
services.AddDbContext<IRaCISDBContext>((sp, options) =>
{
// 在控制台
//public static readonly ILoggerFactory MyLoggerFactory = LoggerFactory.Create(builder => { builder.AddConsole(); });
var logFactory = LoggerFactory.Create(builder => { builder.AddDebug(); });
options.UseSqlServer(configuration.GetSection("ConnectionStrings:RemoteNew").Value,
contextOptionsBuilder => contextOptionsBuilder.EnableRetryOnFailure());
options.UseLoggerFactory(logFactory);
options.UseExceptionProcessor();
options.EnableSensitiveDataLogging();
options.AddInterceptors(new QueryWithNoLockDbCommandInterceptor());
options.AddInterceptors(sp.GetServices<ISaveChangesInterceptor>());
options.UseProjectables();
});
//// Register an additional context factory as a Scoped service, which gets a pooled context from the Singleton factory we registered above,
//services.AddScoped<IRaCISDBScopedFactory>();
//// Finally, arrange for a context to get injected from our Scoped factory:
//services.AddScoped(sp => sp.GetRequiredService<IRaCISDBScopedFactory>().CreateDbContext());
//注意区分 easy caching 也有 IDistributedLockProvider
services.AddSingleton<IDistributedLockProvider>(sp =>
{
//var connection = ConnectionMultiplexer.Connect(configuration["Redis:Configuration"]!);
return new SqlDistributedSynchronizationProvider(configuration.GetSection("ConnectionStrings:RemoteNew").Value);
});
}
}
}

View File

@ -0,0 +1,59 @@

using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using System;
namespace IRaCIS.Core.SCP
{
public static class NewtonsoftJsonSetup
{
public static void AddNewtonsoftJsonSetup(this IMvcBuilder builder, IServiceCollection services)
{
services.AddHttpContextAccessor();
services.AddScoped<IOSSService,OSSService>();
builder.AddNewtonsoftJson(options =>
{
//options.SerializerSettings.PreserveReferencesHandling = PreserveReferencesHandling.Objects;
// 忽略循环引用
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
//options.SerializerSettings.TypeNameHandling = TypeNameHandling.All;
//处理返回给前端 可空类型 给出默认值 比如in? 为null 设置 默认值0
options.SerializerSettings.ContractResolver = new NullToEmptyStringResolver(); //new DefaultContractResolver();// new NullToEmptyStringResolver();
// 设置时间格式
options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss";
options.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.RoundtripKind;
//options.SerializerSettings.Converters.Add(new JSONCustomDateConverter()) ;
//options.SerializerSettings.Converters.Add(services.BuildServiceProvider().GetService<JSONTimeZoneConverter>());
})
.AddControllersAsServices()//动态webApi属性注入需要
.ConfigureApiBehaviorOptions(o =>
{
o.SuppressModelStateInvalidFilter = true; //自己写验证
});
Newtonsoft.Json.JsonSerializerSettings setting = new Newtonsoft.Json.JsonSerializerSettings();
JsonConvert.DefaultSettings = new Func<JsonSerializerSettings>(() =>
{
//日期类型默认格式化处理
setting.DateFormatString = "yyyy-MM-dd HH:mm:ss";
setting.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
return setting;
});
}
}
}

View File

@ -0,0 +1,36 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using System;
using System.Collections.Generic;
using System.Linq;
namespace IRaCIS.Core.SCP
{
public class NullToEmptyStringResolver : DefaultContractResolver
{
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
IList<JsonProperty> properties = base.CreateProperties(type, memberSerialization);
var list= type.GetProperties()
.Select(p =>
{
var jp = base.CreateProperty(p, memberSerialization);
jp.ValueProvider = new NullToEmptyStringValueProvider(p);
return jp;
}).ToList();
var uu = list.Select(t => t.PropertyName).ToList();
//获取复杂对象属性
properties = properties.TakeWhile(t => !uu.Contains(t.PropertyName)).ToList();
list.AddRange(properties);
return list;
}
}
}

View File

@ -0,0 +1,42 @@
using System;
using System.Reflection;
using Newtonsoft.Json.Serialization;
namespace IRaCIS.Core.SCP
{
public class NullToEmptyStringValueProvider : IValueProvider
{
PropertyInfo _MemberInfo;
public NullToEmptyStringValueProvider(PropertyInfo memberInfo)
{
_MemberInfo = memberInfo;
}
public object GetValue(object target)
{
object result = _MemberInfo.GetValue(target);
if (_MemberInfo.PropertyType == typeof(string) && result == null) result = "";
else if (_MemberInfo.PropertyType == typeof(String[]) && result == null) result = new string[] { };
//else if (_MemberInfo.PropertyType == typeof(Nullable<Int32>) && result == null) result = 0;
else if (_MemberInfo.PropertyType == typeof(Nullable<Decimal>) && result == null) result = 0.00M;
return result;
}
public void SetValue(object target, object value)
{
if(_MemberInfo.PropertyType == typeof(string))
{
//去掉前后空格
_MemberInfo.SetValue(target, value==null?string.Empty: value.ToString()==string.Empty? value:value.ToString().Trim());
}
else
{
_MemberInfo.SetValue(target, value);
}
}
}
}

View File

@ -0,0 +1,45 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.11" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2" />
<PackageReference Include="AlibabaCloud.SDK.Sts20150401" Version="1.1.5" />
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="10.0.0" />
<PackageReference Include="Aliyun.OSS.SDK.NetCore" Version="2.14.1" />
<PackageReference Include="AWSSDK.S3" Version="3.7.416.8" />
<PackageReference Include="AWSSDK.SecurityToken" Version="3.7.401.81" />
<PackageReference Include="DistributedLock.Core" Version="1.0.8" />
<PackageReference Include="DistributedLock.SqlServer" Version="1.0.6" />
<PackageReference Include="fo-dicom" Version="5.2.1" />
<PackageReference Include="fo-dicom.Codecs" Version="5.16.1" />
<PackageReference Include="fo-dicom.Imaging.ImageSharp" Version="5.2.1" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.10" />
<PackageReference Include="AutoMapper" Version="13.0.1" />
<PackageReference Include="Minio" Version="6.0.4" />
<PackageReference Include="My.Extensions.Localization.Json" Version="3.3.0">
<TreatAsUsed>true</TreatAsUsed>
</PackageReference>
<PackageReference Include="Panda.DynamicWebApi" Version="1.2.2" />
<PackageReference Include="Serilog.Enrichers.ClientInfo" Version="2.1.2" />
<PackageReference Include="Serilog.Extensions.Hosting" Version="9.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="8.1.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\IRaCIS.Core.Infra.EFCore\IRaCIS.Core.Infra.EFCore.csproj" />
<ProjectReference Include="..\IRaCIS.Core.Infrastructure\IRaCIS.Core.Infrastructure.csproj" />
</ItemGroup>
<ItemGroup>
<Folder Include="Helper\" />
</ItemGroup>
</Project>

186
IRC.Core.Dicom/Program.cs Normal file
View File

@ -0,0 +1,186 @@
using Autofac;
using Autofac.Extensions.DependencyInjection;
using AutoMapper.EquivalencyExpression;
using FellowOakDicom;
using FellowOakDicom.Imaging;
using FellowOakDicom.Imaging.NativeCodec;
using FellowOakDicom.Network;
using IRaCIS.Core.Infra.EFCore;
using IRaCIS.Core.SCP;
using IRaCIS.Core.SCP.Filter;
using IRaCIS.Core.SCP.Service;
using MassTransit;
using MassTransit.NewIdProviders;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.Extensions.DependencyInjection;
using Panda.DynamicWebApi;
using Serilog;
using Serilog.Events;
using System.Runtime.InteropServices;
//以配置文件为准,否则 从url中取环境值(服务以命令行传递参数启动,配置文件配置了就不需要传递环境参数)
var config = new ConfigurationBuilder()
.AddEnvironmentVariables()
.Build();
var enviromentName = config["ASPNETCORE_ENVIRONMENT"];
var builder = WebApplication.CreateBuilder(new WebApplicationOptions
{
EnvironmentName = enviromentName
});
#region 主机配置
NewId.SetProcessIdProvider(new CurrentProcessIdProvider());
builder.Configuration.AddJsonFile("appsettings.json", false, true)
.AddJsonFile($"appsettings.{enviromentName}.json", false, true);
builder.Host
.UseServiceProviderFactory(new AutofacServiceProviderFactory())
.ConfigureContainer<ContainerBuilder>(containerBuilder =>
{
containerBuilder.RegisterModule<AutofacModuleSetup>();
})
.UseSerilog();
#endregion
#region 配置服务
var _configuration = builder.Configuration;
//健康检查
builder.Services.AddHealthChecks();
//本地化
builder.Services.AddJsonLocalization(options => options.ResourcesPath = "Resources");
// 异常、参数统一验证过滤器、Json序列化配置、字符串参数绑型统一Trim()
builder.Services.AddControllers(options =>
{
options.Filters.Add<ModelActionFilter>();
options.Filters.Add<ProjectExceptionFilter>();
options.Filters.Add<UnitOfWorkFilter>();
})
.AddNewtonsoftJsonSetup(builder.Services); // NewtonsoftJson 序列化 处理
builder.Services.AddOptions().Configure<AliyunOSSOptions>(_configuration.GetSection("AliyunOSS"));
builder.Services.AddOptions().Configure<ObjectStoreServiceOptions>(_configuration.GetSection("ObjectStoreService"));
builder.Services.AddOptions().Configure<DicomSCPServiceOption>(_configuration.GetSection("DicomSCPServiceConfig"));
//动态WebApi + UnifiedApiResultFilter 省掉控制器代码
//动态webApi 目前存在的唯一小坑是生成api上服务上的动态代理AOP失效 间接掉用不影响
builder.Services
.AddDynamicWebApi(dynamicWebApiOption =>
{
//默认是 api
dynamicWebApiOption.DefaultApiPrefix = "";
//首字母小写
dynamicWebApiOption.GetRestFulActionName = (actionName) => char.ToLower(actionName[0]) + actionName.Substring(1);
//删除 Service后缀
dynamicWebApiOption.RemoveControllerPostfixes.Add("Service");
});
//AutoMapper
builder.Services.AddAutoMapper(automapper =>
{
automapper.AddCollectionMappers();
}, typeof(BaseService).Assembly);
//EF ORM QueryWithNoLock
builder.Services.AddEFSetup(_configuration);
builder.Services.AddMediator(cfg =>
{
});
//转发头设置 获取真实IP
builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders =
ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
});
builder.Services.AddFellowOakDicom().AddTranscoderManager<NativeTranscoderManager>()
//.AddTranscoderManager<FellowOakDicom.Imaging.NativeCodec.NativeTranscoderManager>()
.AddImageManager<ImageSharpImageManager>();
#endregion
// Add services to the container.
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// Configure the HTTP request pipeline.
//if (app.Environment.IsDevelopment())
//{
app.UseSwagger();
app.UseSwaggerUI();
//}
app.UseAuthorization();
app.MapControllers();
#region 日志
Log.Logger = new LoggerConfiguration()
//.MinimumLevel.Information()
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
.WriteTo.Console()
.WriteTo.File($"{AppContext.BaseDirectory}Serilogs/.log", rollingInterval: RollingInterval.Day)
.CreateLogger();
#endregion
#region 运行环境 部署平台
Log.Logger.Warning($"当前环境:{enviromentName}");
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
Log.Logger.Warning($"当前部署平台环境windows");
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
Log.Logger.Warning($"当前部署平台环境linux");
}
else
{
Log.Logger.Warning($"当前部署平台环境OSX or FreeBSD");
}
#endregion
DicomSetupBuilder.UseServiceProvider(app.Services);
var logger = app.Services.GetService<Microsoft.Extensions.Logging.ILogger<Program>>();
var server = DicomServerFactory.Create<CStoreSCPService>(_configuration.GetSection("DicomSCPServiceConfig").GetValue<int>("ServerPort"), userState: app.Services, logger: logger);
app.Run();

View File

@ -0,0 +1,31 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:11224",
"sslPort": 0
}
},
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "swagger",
"applicationUrl": "http://localhost:5127",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

View File

@ -0,0 +1,111 @@
using AutoMapper;
using IRaCIS.Core.Application.Service.BusinessFilter;
using IRaCIS.Core.Infra.EFCore;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Localization;
using Panda.DynamicWebApi;
using Panda.DynamicWebApi.Attributes;
using System.Diagnostics.CodeAnalysis;
using IRaCIS.Core.Domain.Share;
using IRaCIS.Core.Infrastructure.Extention;
using IRaCIS.Core.Domain.Models;
namespace IRaCIS.Core.SCP.Service
{
#pragma warning disable CS8618
#region 非泛型版本
[Authorize, DynamicWebApi, UnifiedApiResultFilter]
public class BaseService : IBaseService, IDynamicWebApi
{
public IMapper _mapper { get; set; }
public IUserInfo _userInfo { get; set; }
public IStringLocalizer _localizer { get; set; }
public IWebHostEnvironment _hostEnvironment { get; set; }
public static IResponseOutput Null404NotFound<TEntity>(TEntity? businessObject) where TEntity : class
{
return new ResponseOutput<string>()
.NotOk($"The query object {typeof(TEntity).Name} does not exist , or was deleted by someone else, or an incorrect parameter query caused", code: ApiResponseCodeEnum.DataNotExist);
}
}
public interface IBaseService
{
[MemberNotNull(nameof(_mapper))]
public IMapper _mapper { get; set; }
[MemberNotNull(nameof(_userInfo))]
public IUserInfo _userInfo { get; set; }
[MemberNotNull(nameof(_localizer))]
public IStringLocalizer _localizer { get; set; }
[MemberNotNull(nameof(_hostEnvironment))]
public IWebHostEnvironment _hostEnvironment { get; set; }
}
#endregion
#region 泛型版本测试
public interface IBaseServiceTest<T> where T : Entity
{
[MemberNotNull(nameof(_mapper))]
public IMapper _mapper { get; set; }
[MemberNotNull(nameof(_userInfo))]
public IUserInfo _userInfo { get; set; }
[MemberNotNull(nameof(_localizer))]
public IStringLocalizer _localizer { get; set; }
}
[Authorize, DynamicWebApi, UnifiedApiResultFilter]
public class BaseServiceTest<T> : IBaseServiceTest<T>, IDynamicWebApi where T : Entity
{
public IMapper _mapper { get; set; }
public IUserInfo _userInfo { get; set; }
public IStringLocalizer _localizer { get; set; }
public static IResponseOutput Null404NotFound<TEntity>(TEntity? businessObject) where TEntity : class
{
return new ResponseOutput<string>()
.NotOk($"The query object {typeof(TEntity).Name} does not exist , or was deleted by someone else, or an incorrect parameter query caused", code: ApiResponseCodeEnum.DataNotExist);
}
}
#endregion
}

View File

@ -0,0 +1,376 @@
using FellowOakDicom.Network;
using FellowOakDicom;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using IRaCIS.Core.SCP.Service;
using IRaCIS.Core.Domain.Models;
using IRaCIS.Core.Infra.EFCore;
using Medallion.Threading;
using IRaCIS.Core.Domain.Share;
using Serilog;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion.Internal;
using Microsoft.Extensions.Options;
using System.Data;
using FellowOakDicom.Imaging;
using SharpCompress.Common;
using SixLabors.ImageSharp.Formats.Jpeg;
using IRaCIS.Core.Infrastructure;
namespace IRaCIS.Core.SCP.Service
{
public class DicomSCPServiceOption
{
public List<string> CalledAEList { get; set; }
public string ServerPort { get; set; }
}
public class CStoreSCPService : DicomService, IDicomServiceProvider, IDicomCStoreProvider, IDicomCEchoProvider
{
private IServiceProvider _serviceProvider { get; set; }
private List<Guid> _SCPStudyIdList { get; set; } = new List<Guid>();
private SCPImageUpload _upload { get; set; }
private Guid _trialId { get; set; }
private Guid _trialSiteId { get; set; }
private static readonly DicomTransferSyntax[] _acceptedTransferSyntaxes = new DicomTransferSyntax[]
{
DicomTransferSyntax.ExplicitVRLittleEndian,
DicomTransferSyntax.ExplicitVRBigEndian,
DicomTransferSyntax.ImplicitVRLittleEndian
};
private static readonly DicomTransferSyntax[] _acceptedImageTransferSyntaxes = new DicomTransferSyntax[]
{
// Lossless
DicomTransferSyntax.JPEGLSLossless, //1.2.840.10008.1.2.4.80
DicomTransferSyntax.JPEG2000Lossless, //1.2.840.10008.1.2.4.90
DicomTransferSyntax.JPEGProcess14SV1, //1.2.840.10008.1.2.4.70
DicomTransferSyntax.JPEGProcess14, //1.2.840.10008.1.2.4.57 JPEG Lossless, Non-Hierarchical (Process 14)
DicomTransferSyntax.RLELossless, //1.2.840.10008.1.2.5
// Lossy
DicomTransferSyntax.JPEGLSNearLossless,//1.2.840.10008.1.2.4.81"
DicomTransferSyntax.JPEG2000Lossy, //1.2.840.10008.1.2.4.91
DicomTransferSyntax.JPEGProcess1, //1.2.840.10008.1.2.4.50
DicomTransferSyntax.JPEGProcess2_4, //1.2.840.10008.1.2.4.51
// Uncompressed
DicomTransferSyntax.ExplicitVRLittleEndian, //1.2.840.10008.1.2.1
DicomTransferSyntax.ExplicitVRBigEndian, //1.2.840.10008.1.2.2
DicomTransferSyntax.ImplicitVRLittleEndian //1.2.840.10008.1.2
};
public CStoreSCPService(INetworkStream stream, Encoding fallbackEncoding, Microsoft.Extensions.Logging.ILogger log, DicomServiceDependencies dependencies, IServiceProvider injectServiceProvider)
: base(stream, fallbackEncoding, log, dependencies)
{
_serviceProvider = injectServiceProvider.CreateScope().ServiceProvider;
}
public Task OnReceiveAssociationRequestAsync(DicomAssociation association)
{
_upload = new SCPImageUpload() { StartTime = DateTime.Now, CallingAE = association.CallingAE, CalledAE = association.CalledAE, CallingAEIP = association.RemoteHost };
Log.Logger.Warning($"接收到来自{association.CallingAE}的连接");
//_serviceProvider = (IServiceProvider)this.UserState;
var _trialDicomAERepository = _serviceProvider.GetService<IRepository<TrialDicomAE>>();
var trialDicomAEList = _trialDicomAERepository.Select(t => new { t.CalledAE, t.TrialId }).ToList();
var trialCalledAEList = trialDicomAEList.Select(t => t.CalledAE).ToList();
Log.Logger.Information("当前系统配置:", string.Join('|', trialDicomAEList));
var findCalledAE = trialDicomAEList.Where(t => t.CalledAE == association.CalledAE).FirstOrDefault();
var isCanReceiveIamge = false;
if (findCalledAE != null)
{
_trialId = findCalledAE.TrialId;
var _trialSiteDicomAERepository = _serviceProvider.GetService<IRepository<TrialSiteDicomAE>>();
var findTrialSiteAE = _trialSiteDicomAERepository.Where(t => t.CallingAE == association.CallingAE && t.TrialId==_trialId).FirstOrDefault();
if (findTrialSiteAE != null)
{
_trialSiteId = findTrialSiteAE.TrialSiteId;
isCanReceiveIamge = true;
}
}
if (association.CallingAE == "test-callingAE")
{
isCanReceiveIamge = true;
}
if (!trialCalledAEList.Contains(association.CalledAE) || isCanReceiveIamge == false)
{
Log.Logger.Warning($"拒绝CallingAE:{association.CallingAE} CalledAE:{association.CalledAE}的连接");
return SendAssociationRejectAsync(
DicomRejectResult.Permanent,
DicomRejectSource.ServiceUser,
DicomRejectReason.CalledAENotRecognized);
}
foreach (var pc in association.PresentationContexts)
{
if (pc.AbstractSyntax == DicomUID.Verification)
{
pc.AcceptTransferSyntaxes(_acceptedTransferSyntaxes);
}
else if (pc.AbstractSyntax.StorageCategory != DicomStorageCategory.None)
{
pc.AcceptTransferSyntaxes(_acceptedImageTransferSyntaxes);
}
}
return SendAssociationAcceptAsync(association);
}
public async Task OnReceiveAssociationReleaseRequestAsync()
{
await DataMaintenanceAsaync();
//记录监控
var _SCPImageUploadRepository = _serviceProvider.GetService<IRepository<SCPImageUpload>>();
_upload.EndTime = DateTime.Now;
_upload.StudyCount = _SCPStudyIdList.Count;
_upload.TrialId = _trialId;
_upload.TrialSiteId = _trialSiteId;
await _SCPImageUploadRepository.AddAsync(_upload, true);
var _studyRepository = _serviceProvider.GetService<IRepository<SCPStudy>>();
//将检查设置为传输结束
await _studyRepository.BatchUpdateNoTrackingAsync(t => _SCPStudyIdList.Contains(t.Id), u => new SCPStudy() { IsUploadFinished = true });
await _studyRepository.SaveChangesAndClearAllTrackingAsync();
await SendAssociationReleaseResponseAsync();
}
private async Task DataMaintenanceAsaync()
{
Log.Logger.Warning($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE}传输结束开始维护数据处理检查Modality");
//处理检查Modality
var _dictionaryRepository = _serviceProvider.GetService<IRepository<Dictionary>>();
var _seriesRepository = _serviceProvider.GetService<IRepository<SCPSeries>>();
var _studyRepository = _serviceProvider.GetService<IRepository<SCPStudy>>();
var dicModalityList = _dictionaryRepository.Where(t => t.Code == "Modality").SelectMany(t => t.ChildList.Select(c => c.Value)).ToList();
var seriesModalityList = _seriesRepository.Where(t => _SCPStudyIdList.Contains(t.StudyId)).Select(t => new { SCPStudyId = t.StudyId, t.Modality }).ToList();
foreach (var g in seriesModalityList.GroupBy(t => t.SCPStudyId))
{
var modality = string.Join('、', g.Select(t => t.Modality).Distinct().ToList());
//特殊逻辑
var modalityForEdit = dicModalityList.Contains(modality) ? modality : String.Empty;
if (modality == "MR")
{
modalityForEdit = "MRI";
}
if (modality == "PT")
{
modalityForEdit = "PET";
}
if (modality == "PT、CT" || modality == "CT、PT")
{
modalityForEdit = "PET-CT";
}
await _studyRepository.BatchUpdateNoTrackingAsync(t => t.Id == g.Key, u => new SCPStudy() { Modalities = modality, ModalityForEdit = modalityForEdit });
}
Log.Logger.Warning($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE}维护数据结束");
}
public void OnReceiveAbort(DicomAbortSource source, DicomAbortReason reason)
{
Log.Logger.Warning($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE}接收中断,中断原因:{source.ToString() + reason.ToString()}");
/* nothing to do here */
}
public async void OnConnectionClosed(Exception exception)
{
/* nothing to do here */
//奇怪的bug 上传的时候用王捷修改的影像会关闭重新连接导致检查id 丢失,然后状态不一致
if (exception == null)
{
//var _studyRepository = _serviceProvider.GetService<IRepository<SCPStudy>>();
////将检查设置为传输结束
//await _studyRepository.BatchUpdateNoTrackingAsync(t => _SCPStudyIdList.Contains(t.Id), u => new SCPStudy() { IsUploadFinished = true });
//await _studyRepository.SaveChangesAndClearAllTrackingAsync();
}
Log.Logger.Warning($"连接关闭 {exception?.Message} {exception?.InnerException?.Message}");
}
public async Task<DicomCStoreResponse> OnCStoreRequestAsync(DicomCStoreRequest request)
{
string studyInstanceUid = request.Dataset.GetString(DicomTag.StudyInstanceUID);
string seriesInstanceUid = request.Dataset.GetString(DicomTag.SeriesInstanceUID);
string sopInstanceUid = request.Dataset.GetString(DicomTag.SOPInstanceUID);
//Guid studyId = IdentifierHelper.CreateGuid(studyInstanceUid, trialId.ToString());
Guid seriesId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid, _trialId.ToString());
Guid instanceId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid, sopInstanceUid, _trialId.ToString());
var ossService = _serviceProvider.GetService<IOSSService>();
var dicomArchiveService = _serviceProvider.GetService<IDicomArchiveService>();
var _seriesRepository = _serviceProvider.GetService<IRepository<SCPSeries>>();
var _distributedLockProvider = _serviceProvider.GetService<IDistributedLockProvider>();
var storeRelativePath = string.Empty;
var ossFolderPath = $"{_trialId}/Image/PACS/{_trialSiteId}/{studyInstanceUid}";
long fileSize = 0;
try
{
using (MemoryStream ms = new MemoryStream())
{
await request.File.SaveAsync(ms);
//irc 从路径最后一截取Guid
storeRelativePath = await ossService.UploadToOSSAsync(ms, ossFolderPath, instanceId.ToString(), false);
fileSize = ms.Length;
}
Log.Logger.Information($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE} {request.SOPInstanceUID} 上传完成 ");
}
catch (Exception ec)
{
Log.Logger.Warning($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE} 上传异常 {ec.Message}");
}
var @lock = _distributedLockProvider.CreateLock($"{studyInstanceUid}");
using (await @lock.AcquireAsync())
{
try
{
var scpStudyId = await dicomArchiveService.ArchiveDicomFileAsync(request.Dataset, _trialId, _trialSiteId, storeRelativePath, Association.CallingAE, Association.CalledAE,fileSize);
if (!_SCPStudyIdList.Contains(scpStudyId))
{
_SCPStudyIdList.Add(scpStudyId);
}
var series = await _seriesRepository.FirstOrDefaultAsync(t => t.Id == seriesId);
//没有缩略图
if (series != null && string.IsNullOrEmpty(series.ImageResizePath))
{
// 生成缩略图
using (var memoryStream = new MemoryStream())
{
DicomImage image = new DicomImage(request.Dataset);
var sharpimage = image.RenderImage().AsSharpImage();
sharpimage.Save(memoryStream, new JpegEncoder());
// 上传缩略图到 OSS
var seriesPath = await ossService.UploadToOSSAsync(memoryStream, ossFolderPath, seriesId.ToString() + ".preview.jpg", false);
Console.WriteLine(seriesPath + " Id: " + seriesId);
series.ImageResizePath = seriesPath;
}
}
await _seriesRepository.SaveChangesAsync();
}
catch (Exception ex)
{
Log.Logger.Warning($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE} 传输处理异常:{ex.ToString()}");
}
}
//监控信息设置
_upload.FileCount++;
_upload.FileSize = _upload.FileSize + fileSize;
return new DicomCStoreResponse(request, DicomStatus.Success);
}
public Task OnCStoreRequestExceptionAsync(string tempFileName, Exception e)
{
// let library handle logging and error response
return Task.CompletedTask;
}
public Task<DicomCEchoResponse> OnCEchoRequestAsync(DicomCEchoRequest request)
{
return Task.FromResult(new DicomCEchoResponse(request, DicomStatus.Success));
}
}
}

View File

@ -0,0 +1,356 @@
using IRaCIS.Core.Domain.Share;
using System.Text;
using Microsoft.AspNetCore.Hosting;
using IRaCIS.Core.Infrastructure;
using Medallion.Threading;
using FellowOakDicom;
using FellowOakDicom.Imaging.Codec;
using System.Data;
using IRaCIS.Core.Domain.Models;
using FellowOakDicom.Network;
using IRaCIS.Core.SCP.Service;
using IRaCIS.Core.Infra.EFCore;
using MassTransit;
using System.Runtime.Intrinsics.X86;
using Serilog.Sinks.File;
namespace IRaCIS.Core.SCP.Service
{
public class DicomArchiveService : BaseService, IDicomArchiveService
{
private readonly IRepository<SCPPatient> _patientRepository;
private readonly IRepository<SCPStudy> _studyRepository;
private readonly IRepository<SCPSeries> _seriesRepository;
private readonly IRepository<SCPInstance> _instanceRepository;
private readonly IRepository<Dictionary> _dictionaryRepository;
private readonly IDistributedLockProvider _distributedLockProvider;
private List<Guid> _instanceIdList = new List<Guid>();
public DicomArchiveService(IRepository<SCPPatient> patientRepository, IRepository<SCPStudy> studyRepository,
IRepository<SCPSeries> seriesRepository,
IRepository<SCPInstance> instanceRepository,
IRepository<Dictionary> dictionaryRepository,
IDistributedLockProvider distributedLockProvider)
{
_distributedLockProvider = distributedLockProvider;
_studyRepository = studyRepository;
_patientRepository = patientRepository;
_seriesRepository = seriesRepository;
_instanceRepository = instanceRepository;
_dictionaryRepository = dictionaryRepository;
}
/// <summary>
/// 单个文件接收 归档
/// </summary>
/// <param name="dataset"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public async Task<Guid> ArchiveDicomFileAsync(DicomDataset dataset, Guid trialId, Guid trialSiteId, string fileRelativePath, string callingAE, string calledAE,long fileSize)
{
string studyInstanceUid = dataset.GetString(DicomTag.StudyInstanceUID);
string seriesInstanceUid = dataset.GetString(DicomTag.SeriesInstanceUID);
string sopInstanceUid = dataset.GetString(DicomTag.SOPInstanceUID);
string patientIdStr = dataset.GetSingleValueOrDefault(DicomTag.PatientID,string.Empty);
//Guid patientId= IdentifierHelper.CreateGuid(patientIdStr);
Guid studyId = IdentifierHelper.CreateGuid(studyInstanceUid,trialId.ToString());
Guid seriesId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid, trialId.ToString());
Guid instanceId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid, sopInstanceUid, trialId.ToString());
var isStudyNeedAdd = false;
var isSeriesNeedAdd = false;
var isInstanceNeedAdd = false;
var isPatientNeedAdd = false;
//var @lock = _distributedLockProvider.CreateLock($"{studyInstanceUid}");
//using (@lock.Acquire())
{
var findPatient = await _patientRepository.FirstOrDefaultAsync(t => t.PatientIdStr == patientIdStr && t.TrialSiteId==trialSiteId );
var findStudy = await _studyRepository.FirstOrDefaultAsync(t=>t.Id== studyId);
var findSerice = await _seriesRepository.FirstOrDefaultAsync(t => t.Id == seriesId);
var findInstance = await _instanceRepository.FirstOrDefaultAsync(t => t.Id == instanceId);
DateTime? studyTime = dataset.GetSingleValueOrDefault(DicomTag.StudyDate, string.Empty) == string.Empty ? null : dataset.GetSingleValue<DateTime>(DicomTag.StudyDate).Add(dataset.GetSingleValueOrDefault(DicomTag.StudyTime, string.Empty) == string.Empty ? TimeSpan.Zero : dataset.GetSingleValue<DateTime>(DicomTag.StudyTime).TimeOfDay);
//先传输了修改了患者编号的又传输了没有修改患者编号的导致后传输的没有修改患者编号的下面的检查为0
if (findPatient == null && findStudy==null)
{
isPatientNeedAdd = true;
findPatient = new SCPPatient()
{
Id = NewId.NextSequentialGuid(),
TrialId=trialId,
TrialSiteId=trialSiteId,
PatientIdStr = dataset.GetSingleValueOrDefault(DicomTag.PatientID, string.Empty),
PatientName = dataset.GetSingleValueOrDefault(DicomTag.PatientName, string.Empty),
PatientAge = dataset.GetSingleValueOrDefault(DicomTag.PatientAge, string.Empty),
PatientSex = dataset.GetSingleValueOrDefault(DicomTag.PatientSex, string.Empty),
PatientBirthDate = dataset.GetSingleValueOrDefault(DicomTag.PatientBirthDate, string.Empty),
EarliestStudyTime = studyTime,
LatestStudyTime = studyTime,
LatestPushTime = DateTime.Now,
};
if (findPatient.PatientBirthDate.Length == 8)
{
var birthDateStr = $"{findPatient.PatientBirthDate[0]}{findPatient.PatientBirthDate[1]}{findPatient.PatientBirthDate[2]}{findPatient.PatientBirthDate[3]}-{findPatient.PatientBirthDate[4]}{findPatient.PatientBirthDate[5]}-{findPatient.PatientBirthDate[6]}{findPatient.PatientBirthDate[7]}";
var yearStr = $"{findPatient.PatientBirthDate[0]}{findPatient.PatientBirthDate[1]}{findPatient.PatientBirthDate[2]}{findPatient.PatientBirthDate[3]}";
int year = 0;
var canParse = int.TryParse(yearStr, out year);
if (canParse && year > 1900)
{
findPatient.PatientBirthDate = birthDateStr;
DateTime birthDate;
if (findPatient.PatientAge == string.Empty && studyTime.HasValue && DateTime.TryParse(findPatient.PatientBirthDate,out birthDate))
{
var patientAge = studyTime.Value.Year - birthDate.Year;
// 如果生日还未到,年龄减去一岁
if (studyTime.Value < birthDate.AddYears(patientAge))
{
patientAge--;
}
findPatient.PatientAge = patientAge.ToString();
}
}
else
{
findPatient.PatientBirthDate = string.Empty;
}
}
}
else
{
if (studyTime < findPatient.EarliestStudyTime)
{
findPatient.EarliestStudyTime = studyTime;
}
if (studyTime > findPatient.LatestStudyTime)
{
findPatient.LatestStudyTime = studyTime;
}
findPatient.LatestPushTime = DateTime.Now;
}
if (findStudy == null)
{
isStudyNeedAdd = true;
findStudy = new SCPStudy
{
CalledAE = calledAE,
CallingAE = callingAE,
PatientId = findPatient.Id,
Id = studyId,
TrialId = trialId,
TrialSiteId = trialSiteId,
StudyInstanceUid = studyInstanceUid,
StudyTime = studyTime,
Modalities = dataset.GetSingleValueOrDefault(DicomTag.Modality, string.Empty),
//ModalityForEdit = modalityForEdit,
Description = dataset.GetSingleValueOrDefault(DicomTag.StudyDescription, string.Empty),
InstitutionName = dataset.GetSingleValueOrDefault(DicomTag.InstitutionName, string.Empty),
PatientIdStr = dataset.GetSingleValueOrDefault(DicomTag.PatientID, string.Empty),
PatientName = dataset.GetSingleValueOrDefault(DicomTag.PatientName, string.Empty),
PatientAge = dataset.GetSingleValueOrDefault(DicomTag.PatientAge, string.Empty),
PatientSex = dataset.GetSingleValueOrDefault(DicomTag.PatientSex, string.Empty),
BodyPartExamined = dataset.GetSingleValueOrDefault(DicomTag.BodyPartExamined, string.Empty),
StudyId = dataset.GetSingleValueOrDefault(DicomTag.StudyID, string.Empty),
AccessionNumber = dataset.GetSingleValueOrDefault(DicomTag.AccessionNumber, string.Empty),
//需要特殊处理
PatientBirthDate = dataset.GetSingleValueOrDefault(DicomTag.PatientBirthDate, string.Empty),
AcquisitionTime = dataset.GetSingleValueOrDefault(DicomTag.AcquisitionTime, string.Empty),
AcquisitionNumber = dataset.GetSingleValueOrDefault(DicomTag.AcquisitionNumber, string.Empty),
TriggerTime = dataset.GetSingleValueOrDefault(DicomTag.TriggerTime, string.Empty),
//IsDoubleReview = addtionalInfo.IsDoubleReview,
SeriesCount = 0,
InstanceCount = 0
};
if (findStudy.PatientBirthDate.Length == 8)
{
findStudy.PatientBirthDate = $"{findStudy.PatientBirthDate[0]}{findStudy.PatientBirthDate[1]}{findStudy.PatientBirthDate[2]}{findStudy.PatientBirthDate[3]}-{findStudy.PatientBirthDate[4]}{findStudy.PatientBirthDate[5]}-{findStudy.PatientBirthDate[6]}{findStudy.PatientBirthDate[7]}";
}
}
if (findSerice == null)
{
isSeriesNeedAdd = true;
findSerice = new SCPSeries
{
Id = seriesId,
StudyId = findStudy.Id,
StudyInstanceUid = findStudy.StudyInstanceUid,
SeriesInstanceUid = seriesInstanceUid,
SeriesNumber = dataset.GetSingleValueOrDefault(DicomTag.SeriesNumber, 1),
//SeriesTime = dataset.GetSingleValueOrDefault(DicomTag.SeriesDate, DateTime.Now).Add(dataset.GetSingleValueOrDefault(DicomTag.SeriesTime, DateTime.Now).TimeOfDay),
//SeriesTime = DateTime.TryParse(dataset.GetSingleValue<string>(DicomTag.SeriesDate) + dataset.GetSingleValue<string>(DicomTag.SeriesTime), out DateTime dt) ? dt : null,
SeriesTime = dataset.GetSingleValueOrDefault(DicomTag.SeriesDate, string.Empty) == string.Empty ? null : dataset.GetSingleValue<DateTime>(DicomTag.SeriesDate).Add(dataset.GetSingleValueOrDefault(DicomTag.SeriesTime, string.Empty) == string.Empty ? TimeSpan.Zero : dataset.GetSingleValue<DateTime>(DicomTag.SeriesTime).TimeOfDay),
Modality = dataset.GetSingleValueOrDefault(DicomTag.Modality, string.Empty),
Description = dataset.GetSingleValueOrDefault(DicomTag.SeriesDescription, string.Empty),
SliceThickness = dataset.GetSingleValueOrDefault(DicomTag.SliceThickness, string.Empty),
ImagePositionPatient = dataset.GetSingleValueOrDefault(DicomTag.ImagePositionPatient, string.Empty),
ImageOrientationPatient = dataset.GetSingleValueOrDefault(DicomTag.ImageOrientationPatient, string.Empty),
BodyPartExamined = dataset.GetSingleValueOrDefault(DicomTag.BodyPartExamined, string.Empty),
SequenceName = dataset.GetSingleValueOrDefault(DicomTag.SequenceName, string.Empty),
ProtocolName = dataset.GetSingleValueOrDefault(DicomTag.ProtocolName, string.Empty),
ImagerPixelSpacing = dataset.GetSingleValueOrDefault(DicomTag.ImagerPixelSpacing, string.Empty),
AcquisitionTime = dataset.GetSingleValueOrDefault(DicomTag.AcquisitionTime, string.Empty),
AcquisitionNumber = dataset.GetSingleValueOrDefault(DicomTag.AcquisitionNumber, string.Empty),
TriggerTime = dataset.GetSingleValueOrDefault(DicomTag.TriggerTime, string.Empty),
InstanceCount = 0
};
++findStudy.SeriesCount;
}
if (findInstance == null)
{
isInstanceNeedAdd = true;
findInstance = new SCPInstance
{
Id = instanceId,
StudyId = findStudy.Id,
SeriesId = findSerice.Id,
StudyInstanceUid = findStudy.StudyInstanceUid,
SeriesInstanceUid = findSerice.SeriesInstanceUid,
SopInstanceUid = sopInstanceUid,
InstanceNumber = dataset.GetSingleValueOrDefault(DicomTag.InstanceNumber, 1),
InstanceTime = dataset.GetSingleValueOrDefault(DicomTag.ContentDate, string.Empty) == string.Empty ? null : dataset.GetSingleValue<DateTime>(DicomTag.ContentDate).Add(dataset.GetSingleValueOrDefault(DicomTag.ContentTime, string.Empty) == string.Empty ? TimeSpan.Zero : dataset.GetSingleValue<DateTime>(DicomTag.ContentTime).TimeOfDay),
//InstanceTime = DateTime.TryParse(dataset.GetSingleValue<string>(DicomTag.ContentDate) + dataset.GetSingleValue<string>(DicomTag.ContentTime), out DateTime dt) ? dt : null,
//InstanceTime = dataset.GetSingleValueOrDefault(DicomTag.ContentDate,(DateTime?)null)?.Add(dataset.GetSingleValueOrDefault(DicomTag.ContentTime, TimeSpan.Zero)),
//dataset.GetSingleValueOrDefault(DicomTag.ContentDate,DateTime.Now);//, DicomTag.ContentTime)
CPIStatus = false,
ImageRows = dataset.GetSingleValueOrDefault(DicomTag.Rows, 0),
ImageColumns = dataset.GetSingleValueOrDefault(DicomTag.Columns, 0),
SliceLocation = dataset.GetSingleValueOrDefault(DicomTag.SliceLocation, 0),
SliceThickness = dataset.GetSingleValueOrDefault(DicomTag.SliceThickness, string.Empty),
NumberOfFrames = dataset.GetSingleValueOrDefault(DicomTag.NumberOfFrames, 0),
PixelSpacing = dataset.GetSingleValueOrDefault(DicomTag.PixelSpacing, string.Empty),
ImagerPixelSpacing = dataset.GetSingleValueOrDefault(DicomTag.ImagerPixelSpacing, string.Empty),
FrameOfReferenceUID = dataset.GetSingleValueOrDefault(DicomTag.FrameOfReferenceUID, string.Empty),
WindowCenter = dataset.GetSingleValueOrDefault(DicomTag.WindowCenter, string.Empty),
WindowWidth = dataset.GetSingleValueOrDefault(DicomTag.WindowWidth, string.Empty),
Path = fileRelativePath,
FileSize= fileSize,
};
++findStudy.InstanceCount;
++findSerice.InstanceCount;
}
if (isPatientNeedAdd)
{
var ss = await _patientRepository.AddAsync(findPatient);
}
if (isStudyNeedAdd)
{
var dd = await _studyRepository.AddAsync(findStudy);
}
else
{
await _studyRepository.BatchUpdateNoTrackingAsync(t => t.Id == findStudy.Id, t => new SCPStudy() { IsUploadFinished = false });
}
if (isSeriesNeedAdd)
{
await _seriesRepository.AddAsync(findSerice);
}
if (isInstanceNeedAdd)
{
await _instanceRepository.AddAsync(findInstance);
}
else
{
await _instanceRepository.BatchUpdateNoTrackingAsync(t => t.Id == instanceId, u => new SCPInstance() { Path = fileRelativePath,FileSize=fileSize });
}
await _studyRepository.SaveChangesAsync();
return findStudy.Id;
}
}
// 从DICOM文件中获取使用的字符集
private string GetEncodingVaulueFromDicomFile(DicomDataset dataset, DicomTag dicomTag)
{
// 获取DICOM文件的特定元素通常用于指示使用的字符集
var charset = dataset.GetSingleValueOrDefault(DicomTag.SpecificCharacterSet, string.Empty);
var dicomEncoding = DicomEncoding.GetEncoding(charset);
var dicomStringElement = dataset.GetDicomItem<DicomStringElement>(dicomTag);
var bytes = dicomStringElement.Buffer.Data;
return dicomEncoding.GetString(bytes);
//// 从DICOM文件中获取使用的字符集
//string filePath = "C:\\Users\\hang\\Documents\\WeChat Files\\wxid_r2imdzb7j3q922\\FileStorage\\File\\2024-05\\1.2.840.113619.2.80.169103990.5390.1271401378.4.dcm";
//DicomFile dicomFile = DicomFile.Open(filePath);
//// 获取DICOM文件的特定元素通常用于指示使用的字符集
//var charset = dicomFile.Dataset.GetSingleValueOrDefault(DicomTag.SpecificCharacterSet, string.Empty);
//var dicomEncoding = DicomEncoding.GetEncoding(charset);
//var value = dicomFile.Dataset.GetSingleValueOrDefault(DicomTag.PatientName, string.Empty);
//var dicomStringElement = dicomFile.Dataset.GetDicomItem<DicomStringElement>(DicomTag.PatientName);
//var bytes = dicomStringElement.Buffer.Data;
//var aa= dicomEncoding.GetString(bytes);
}
}
}

View File

@ -0,0 +1,11 @@
using FellowOakDicom;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion.Internal;
namespace IRaCIS.Core.SCP.Service
{
public interface IDicomArchiveService
{
Task<Guid> ArchiveDicomFileAsync(DicomDataset dicomDataset,Guid trialId,Guid trialSiteId, string fileRelativePath,string callingAE,string calledAE,long fileSize);
}
}

View File

@ -0,0 +1,770 @@
using AlibabaCloud.SDK.Sts20150401;
using Aliyun.OSS;
using Amazon;
using Amazon.Runtime;
using Amazon.S3;
using Amazon.S3.Model;
using Amazon.SecurityToken;
using Amazon.SecurityToken.Model;
using IRaCIS.Core.Infrastructure;
using IRaCIS.Core.Infrastructure.NewtonsoftJson;
using MassTransit;
using Microsoft.Extensions.Options;
using Minio;
using Minio.DataModel.Args;
using System.Reactive.Linq;
using System.Runtime.InteropServices;
namespace IRaCIS.Core.SCP;
#region 绑定和返回模型
[LowerCamelCaseJson]
public class MinIOOptions : AWSOptions
{
public int Port { get; set; }
}
public class AWSOptions
{
public string EndPoint { get; set; }
public bool UseSSL { get; set; }
public string AccessKeyId { get; set; }
public string RoleArn { get; set; }
public string SecretAccessKey { get; set; }
public string BucketName { get; set; }
public string ViewEndpoint { get; set; }
public int DurationSeconds { get; set; }
public string Region { get; set; }
}
public class AliyunOSSOptions
{
public string RegionId { get; set; }
public string AccessKeyId { get; set; }
public string AccessKeySecret { get; set; }
public string InternalEndpoint { get; set; }
public string EndPoint { get; set; }
public string BucketName { get; set; }
public string RoleArn { get; set; }
public string Region { get; set; }
public string ViewEndpoint { get; set; }
public int DurationSeconds { get; set; }
}
public class ObjectStoreServiceOptions
{
public string ObjectStoreUse { get; set; }
public AliyunOSSOptions AliyunOSS { get; set; }
public MinIOOptions MinIO { get; set; }
public AWSOptions AWS { get; set; }
}
public class ObjectStoreDTO
{
public string ObjectStoreUse { get; set; }
public AliyunOSSTempToken AliyunOSS { get; set; }
public MinIOOptions MinIO { get; set; }
public AWSTempToken AWS { get; set; }
}
[LowerCamelCaseJson]
public class AliyunOSSTempToken
{
public string AccessKeyId { get; set; }
public string AccessKeySecret { get; set; }
public string EndPoint { get; set; }
public string BucketName { get; set; }
public string Region { get; set; }
public string ViewEndpoint { get; set; }
public string SecurityToken { get; set; }
public DateTime Expiration { get; set; }
}
[LowerCamelCaseJson]
public class AWSTempToken
{
public string Region { get; set; }
public string SessionToken { get; set; }
public string EndPoint { get; set; }
public string AccessKeyId { get; set; }
public string SecretAccessKey { get; set; }
public string BucketName { get; set; }
public string ViewEndpoint { get; set; }
public DateTime Expiration { get; set; }
}
public enum ObjectStoreUse
{
AliyunOSS = 0,
MinIO = 1,
AWS = 2,
}
#endregion
// aws 参考链接 https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/dotnetv3/S3/S3_Basics
public interface IOSSService
{
public Task<string> UploadToOSSAsync(Stream fileStream, string oosFolderPath, string fileRealName, bool isFileNameAddGuid = true);
public Task<string> UploadToOSSAsync(string localFilePath, string oosFolderPath, bool isFileNameAddGuid = true);
public Task DownLoadFromOSSAsync(string ossRelativePath, string localFilePath);
public ObjectStoreServiceOptions ObjectStoreServiceOptions { get; set; }
public Task<string> GetSignedUrl(string ossRelativePath);
public Task DeleteFromPrefix(string prefix);
public ObjectStoreDTO GetObjectStoreTempToken();
}
public class OSSService : IOSSService
{
public ObjectStoreServiceOptions ObjectStoreServiceOptions { get; set; }
private AliyunOSSTempToken AliyunOSSTempToken { get; set; }
private AWSTempToken AWSTempToken { get; set; }
public OSSService(IOptionsMonitor<ObjectStoreServiceOptions> options)
{
ObjectStoreServiceOptions = options.CurrentValue;
}
/// <summary>
/// oosFolderPath 不要 "/ "开头 应该: TempFolder/ChildFolder
/// </summary>
/// <param name="fileStream"></param>
/// <param name="oosFolderPath"></param>
/// <param name="fileRealName"></param>
/// <param name="isFileNameAddGuid"></param>
/// <returns></returns>
public async Task<string> UploadToOSSAsync(Stream fileStream, string oosFolderPath, string fileRealName, bool isFileNameAddGuid = true)
{
GetObjectStoreTempToken();
var ossRelativePath = isFileNameAddGuid ? $"{oosFolderPath}/{Guid.NewGuid()}_{fileRealName}" : $"{oosFolderPath}/{fileRealName}";
try
{
using (var memoryStream = new MemoryStream())
{
fileStream.Seek(0, SeekOrigin.Begin);
fileStream.CopyTo(memoryStream);
memoryStream.Seek(0, SeekOrigin.Begin);
if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS")
{
var aliConfig = ObjectStoreServiceOptions.AliyunOSS;
var _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.EndPoint : aliConfig.InternalEndpoint, AliyunOSSTempToken.AccessKeyId, AliyunOSSTempToken.AccessKeySecret, AliyunOSSTempToken.SecurityToken);
// 上传文件
var result = _ossClient.PutObject(aliConfig.BucketName, ossRelativePath, memoryStream);
}
else if (ObjectStoreServiceOptions.ObjectStoreUse == "MinIO")
{
var minIOConfig = ObjectStoreServiceOptions.MinIO;
var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.EndPoint}:{minIOConfig.Port}")
.WithCredentials(minIOConfig.AccessKeyId, minIOConfig.SecretAccessKey).WithSSL(minIOConfig.UseSSL)
.Build();
var putObjectArgs = new PutObjectArgs()
.WithBucket(minIOConfig.BucketName)
.WithObject(ossRelativePath)
.WithStreamData(memoryStream)
.WithObjectSize(memoryStream.Length);
await minioClient.PutObjectAsync(putObjectArgs);
}
else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS")
{
var awsConfig = ObjectStoreServiceOptions.AWS;
var credentials = new SessionAWSCredentials(AWSTempToken.AccessKeyId, AWSTempToken.SecretAccessKey, AWSTempToken.SessionToken);
//提供awsEndPoint域名进行访问配置
var clientConfig = new AmazonS3Config
{
RegionEndpoint = RegionEndpoint.USEast1,
UseHttp = true,
};
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
var putObjectRequest = new Amazon.S3.Model.PutObjectRequest()
{
BucketName = awsConfig.BucketName,
InputStream = memoryStream,
Key = ossRelativePath,
};
await amazonS3Client.PutObjectAsync(putObjectRequest);
}
else
{
throw new BusinessValidationFailedException("未定义的存储介质类型");
}
}
}
catch (Exception ex)
{
throw new BusinessValidationFailedException($"上传发生异常:{ex.Message}");
}
return "/" + ossRelativePath;
}
/// <summary>
/// oosFolderPath 不要 "/ "开头 应该: TempFolder/ChildFolder
/// </summary>
/// <param name="localFilePath"></param>
/// <param name="oosFolderPath"></param>
/// <param name="isFileNameAddGuid"></param>
/// <returns></returns>
/// <exception cref="BusinessValidationFailedException"></exception>
public async Task<string> UploadToOSSAsync(string localFilePath, string oosFolderPath, bool isFileNameAddGuid = true)
{
GetObjectStoreTempToken();
var localFileName = Path.GetFileName(localFilePath);
var ossRelativePath = isFileNameAddGuid ? $"{oosFolderPath}/{Guid.NewGuid()}_{localFileName}" : $"{oosFolderPath}/{localFileName}";
if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS")
{
var aliConfig = ObjectStoreServiceOptions.AliyunOSS;
var _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.EndPoint : aliConfig.InternalEndpoint, AliyunOSSTempToken.AccessKeyId, AliyunOSSTempToken.AccessKeySecret, AliyunOSSTempToken.SecurityToken);
// 上传文件
var result = _ossClient.PutObject(aliConfig.BucketName, ossRelativePath, localFilePath);
}
else if (ObjectStoreServiceOptions.ObjectStoreUse == "MinIO")
{
var minIOConfig = ObjectStoreServiceOptions.MinIO;
var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.EndPoint}:{minIOConfig.Port}")
.WithCredentials(minIOConfig.AccessKeyId, minIOConfig.SecretAccessKey).WithSSL(minIOConfig.UseSSL)
.Build();
var putObjectArgs = new PutObjectArgs()
.WithBucket(minIOConfig.BucketName)
.WithObject(ossRelativePath)
.WithFileName(localFilePath);
await minioClient.PutObjectAsync(putObjectArgs);
}
else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS")
{
var awsConfig = ObjectStoreServiceOptions.AWS;
// 提供awsAccessKeyId和awsSecretAccessKey构造凭证
var credentials = new SessionAWSCredentials(AWSTempToken.AccessKeyId, AWSTempToken.SecretAccessKey, AWSTempToken.SessionToken);
//提供awsEndPoint域名进行访问配置
var clientConfig = new AmazonS3Config
{
RegionEndpoint = RegionEndpoint.USEast1,
UseHttp = true,
};
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
var putObjectRequest = new Amazon.S3.Model.PutObjectRequest()
{
BucketName = awsConfig.BucketName,
FilePath = localFilePath,
Key = ossRelativePath,
};
await amazonS3Client.PutObjectAsync(putObjectRequest);
}
else
{
throw new BusinessValidationFailedException("未定义的存储介质类型");
}
return "/" + ossRelativePath;
}
public async Task DownLoadFromOSSAsync(string ossRelativePath, string localFilePath)
{
GetObjectStoreTempToken();
ossRelativePath = ossRelativePath.TrimStart('/');
try
{
if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS")
{
var aliConfig = ObjectStoreServiceOptions.AliyunOSS;
var _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.EndPoint : aliConfig.InternalEndpoint, AliyunOSSTempToken.AccessKeyId, AliyunOSSTempToken.AccessKeySecret, AliyunOSSTempToken.SecurityToken);
// 上传文件
var result = _ossClient.GetObject(aliConfig.BucketName, ossRelativePath);
// 将下载的文件流保存到本地文件
using (var fs = File.OpenWrite(localFilePath))
{
result.Content.CopyTo(fs);
fs.Close();
}
}
else if (ObjectStoreServiceOptions.ObjectStoreUse == "MinIO")
{
var minIOConfig = ObjectStoreServiceOptions.MinIO;
var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.EndPoint}:{minIOConfig.Port}")
.WithCredentials(minIOConfig.AccessKeyId, minIOConfig.SecretAccessKey).WithSSL(minIOConfig.UseSSL)
.Build();
var getObjectArgs = new GetObjectArgs()
.WithBucket(minIOConfig.BucketName)
.WithObject(ossRelativePath)
.WithFile(localFilePath);
await minioClient.GetObjectAsync(getObjectArgs);
}
else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS")
{
var awsConfig = ObjectStoreServiceOptions.AWS;
// 提供awsAccessKeyId和awsSecretAccessKey构造凭证
var credentials = new SessionAWSCredentials(AWSTempToken.AccessKeyId, AWSTempToken.SecretAccessKey, AWSTempToken.SessionToken);
//提供awsEndPoint域名进行访问配置
var clientConfig = new AmazonS3Config
{
RegionEndpoint = RegionEndpoint.USEast1,
UseHttp = true,
};
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
var getObjectArgs = new Amazon.S3.Model.GetObjectRequest()
{
BucketName = awsConfig.BucketName,
Key = ossRelativePath,
};
await (await amazonS3Client.GetObjectAsync(getObjectArgs)).WriteResponseStreamToFileAsync(localFilePath, true, CancellationToken.None);
}
else
{
throw new BusinessValidationFailedException("未定义的存储介质类型");
}
}
catch (Exception ex)
{
throw new BusinessValidationFailedException("oss下载失败!" + ex.Message);
}
}
public async Task<string> GetSignedUrl(string ossRelativePath)
{
GetObjectStoreTempToken();
ossRelativePath = ossRelativePath.TrimStart('/');
try
{
if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS")
{
var aliConfig = ObjectStoreServiceOptions.AliyunOSS;
var _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.EndPoint : aliConfig.InternalEndpoint, AliyunOSSTempToken.AccessKeyId, AliyunOSSTempToken.AccessKeySecret, AliyunOSSTempToken.SecurityToken);
// 生成签名URL。
var req = new GeneratePresignedUriRequest(aliConfig.BucketName, ossRelativePath, SignHttpMethod.Get)
{
// 设置签名URL过期时间默认值为3600秒。
Expiration = DateTime.Now.AddHours(1),
};
var uri = _ossClient.GeneratePresignedUri(req);
return uri.PathAndQuery;
}
else if (ObjectStoreServiceOptions.ObjectStoreUse == "MinIO")
{
var minIOConfig = ObjectStoreServiceOptions.MinIO;
var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.EndPoint}:{minIOConfig.Port}")
.WithCredentials(minIOConfig.AccessKeyId, minIOConfig.SecretAccessKey).WithSSL(minIOConfig.UseSSL)
.Build();
var args = new PresignedGetObjectArgs()
.WithBucket(minIOConfig.BucketName)
.WithObject(ossRelativePath)
.WithExpiry(3600)
/*.WithHeaders(reqParams)*/;
var presignedUrl = await minioClient.PresignedGetObjectAsync(args);
Uri uri = new Uri(presignedUrl);
string relativePath = uri.PathAndQuery;
return relativePath;
}
else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS")
{
var awsConfig = ObjectStoreServiceOptions.AWS;
// 提供awsAccessKeyId和awsSecretAccessKey构造凭证
var credentials = new SessionAWSCredentials(AWSTempToken.AccessKeyId, AWSTempToken.SecretAccessKey, AWSTempToken.SessionToken);
//提供awsEndPoint域名进行访问配置
var clientConfig = new AmazonS3Config
{
RegionEndpoint = RegionEndpoint.USEast1,
UseHttp = true,
};
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
var presignedUrl = await amazonS3Client.GetPreSignedURLAsync(new GetPreSignedUrlRequest()
{
BucketName = awsConfig.BucketName,
Key = ossRelativePath,
Expires = DateTime.UtcNow.AddMinutes(120)
});
Uri uri = new Uri(presignedUrl);
string relativePath = uri.PathAndQuery;
return relativePath;
}
else
{
throw new BusinessValidationFailedException("未定义的存储介质类型");
}
}
catch (Exception ex)
{
throw new BusinessValidationFailedException("oss授权url失败!" + ex.Message);
}
}
/// <summary>
/// 删除某个目录的文件
/// </summary>
/// <param name="prefix"></param>
/// <returns></returns>
public async Task DeleteFromPrefix(string prefix)
{
GetObjectStoreTempToken();
if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS")
{
var aliConfig = ObjectStoreServiceOptions.AliyunOSS;
var _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.EndPoint : aliConfig.InternalEndpoint, AliyunOSSTempToken.AccessKeyId, AliyunOSSTempToken.AccessKeySecret, AliyunOSSTempToken.SecurityToken);
try
{
ObjectListing objectListing = null;
string nextMarker = null;
do
{
// 使用 prefix 模拟目录结构,设置 MaxKeys 和 NextMarker
objectListing = _ossClient.ListObjects(new Aliyun.OSS.ListObjectsRequest(aliConfig.BucketName)
{
Prefix = prefix,
MaxKeys = 1000,
Marker = nextMarker
});
List<string> keys = objectListing.ObjectSummaries.Select(t => t.Key).ToList();
// 删除获取到的文件
if (keys.Count > 0)
{
_ossClient.DeleteObjects(new Aliyun.OSS.DeleteObjectsRequest(aliConfig.BucketName, keys, false));
}
// 设置 NextMarker 以获取下一页的数据
nextMarker = objectListing.NextMarker;
} while (objectListing.IsTruncated);
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
}
else if (ObjectStoreServiceOptions.ObjectStoreUse == "MinIO")
{
var minIOConfig = ObjectStoreServiceOptions.MinIO;
var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.EndPoint}:{minIOConfig.Port}")
.WithCredentials(minIOConfig.AccessKeyId, minIOConfig.SecretAccessKey).WithSSL(minIOConfig.UseSSL)
.Build();
var listArgs = new ListObjectsArgs().WithBucket(minIOConfig.BucketName).WithPrefix(prefix).WithRecursive(true);
// 创建一个空列表用于存储对象键
var objects = new List<string>();
// 使用 await foreach 来异步迭代对象列表
await foreach (var item in minioClient.ListObjectsEnumAsync(listArgs))
{
objects.Add(item.Key);
}
if (objects.Count > 0)
{
var objArgs = new RemoveObjectsArgs()
.WithBucket(minIOConfig.BucketName)
.WithObjects(objects);
// 删除对象
await minioClient.RemoveObjectsAsync(objArgs);
}
}
else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS")
{
var awsConfig = ObjectStoreServiceOptions.AWS;
// 提供awsAccessKeyId和awsSecretAccessKey构造凭证
var credentials = new SessionAWSCredentials(AWSTempToken.AccessKeyId, AWSTempToken.SecretAccessKey, AWSTempToken.SessionToken);
//提供awsEndPoint域名进行访问配置
var clientConfig = new AmazonS3Config
{
RegionEndpoint = RegionEndpoint.USEast1,
UseHttp = true,
};
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
// 列出指定前缀下的所有对象
var listObjectsRequest = new ListObjectsV2Request
{
BucketName = awsConfig.BucketName,
Prefix = prefix
};
var listObjectsResponse = await amazonS3Client.ListObjectsV2Async(listObjectsRequest);
if (listObjectsResponse.S3Objects.Count > 0)
{
// 准备删除请求
var deleteObjectsRequest = new Amazon.S3.Model.DeleteObjectsRequest
{
BucketName = awsConfig.BucketName,
Objects = new List<KeyVersion>()
};
foreach (var s3Object in listObjectsResponse.S3Objects)
{
deleteObjectsRequest.Objects.Add(new KeyVersion
{
Key = s3Object.Key
});
}
// 批量删除对象
var deleteObjectsResponse = await amazonS3Client.DeleteObjectsAsync(deleteObjectsRequest);
}
}
else
{
throw new BusinessValidationFailedException("未定义的存储介质类型");
}
}
public ObjectStoreDTO GetObjectStoreTempToken()
{
var ossOptions = ObjectStoreServiceOptions.AliyunOSS;
if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS")
{
var client = new Client(new AlibabaCloud.OpenApiClient.Models.Config()
{
AccessKeyId = ossOptions.AccessKeyId,
AccessKeySecret = ossOptions.AccessKeySecret,
//AccessKeyId = "LTAI5tJV76pYX5yPg1N9QVE8",
//AccessKeySecret = "roRNLa9YG1of4pYruJGCNKBXEWTAWa",
Endpoint = "sts.cn-hangzhou.aliyuncs.com"
});
var assumeRoleRequest = new AlibabaCloud.SDK.Sts20150401.Models.AssumeRoleRequest();
// 将<YOUR_ROLE_SESSION_NAME>设置为自定义的会话名称例如oss-role-session。
assumeRoleRequest.RoleSessionName = $"session-name-{NewId.NextGuid()}";
// 将<YOUR_ROLE_ARN>替换为拥有上传文件到指定OSS Bucket权限的RAM角色的ARN。
assumeRoleRequest.RoleArn = ossOptions.RoleArn;
//assumeRoleRequest.RoleArn = "acs:ram::1899121822495495:role/webdirect";
assumeRoleRequest.DurationSeconds = ossOptions.DurationSeconds;
var runtime = new AlibabaCloud.TeaUtil.Models.RuntimeOptions();
var response = client.AssumeRoleWithOptions(assumeRoleRequest, runtime);
var credentials = response.Body.Credentials;
var tempToken = new AliyunOSSTempToken()
{
AccessKeyId = credentials.AccessKeyId,
AccessKeySecret = credentials.AccessKeySecret,
//转为服务器时区,最后统一转为客户端时区
Expiration = TimeZoneInfo.ConvertTimeFromUtc(DateTime.Parse(credentials.Expiration), TimeZoneInfo.Local),
SecurityToken = credentials.SecurityToken,
Region = ossOptions.Region,
BucketName = ossOptions.BucketName,
EndPoint = ossOptions.EndPoint,
ViewEndpoint = ossOptions.ViewEndpoint,
};
AliyunOSSTempToken = tempToken;
return new ObjectStoreDTO() { ObjectStoreUse = ObjectStoreServiceOptions.ObjectStoreUse, AliyunOSS = tempToken };
}
else if (ObjectStoreServiceOptions.ObjectStoreUse == "MinIO")
{
return new ObjectStoreDTO() { ObjectStoreUse = ObjectStoreServiceOptions.ObjectStoreUse, MinIO = ObjectStoreServiceOptions.MinIO };
}
else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS")
{
var awsOptions = ObjectStoreServiceOptions.AWS;
//aws 临时凭证
// 创建 STS 客户端
var stsClient = new AmazonSecurityTokenServiceClient(awsOptions.AccessKeyId, awsOptions.SecretAccessKey);
// 使用 AssumeRole 请求临时凭证
var assumeRoleRequest = new AssumeRoleRequest
{
RoleArn = awsOptions.RoleArn, // 角色 ARN
RoleSessionName = $"session-name-{NewId.NextGuid()}",
DurationSeconds = awsOptions.DurationSeconds // 临时凭证有效期
};
var assumeRoleResponse = stsClient.AssumeRoleAsync(assumeRoleRequest).Result;
var credentials = assumeRoleResponse.Credentials;
var tempToken = new AWSTempToken()
{
AccessKeyId = credentials.AccessKeyId,
SecretAccessKey = credentials.SecretAccessKey,
SessionToken = credentials.SessionToken,
Expiration = credentials.Expiration,
Region = awsOptions.Region,
BucketName = awsOptions.BucketName,
EndPoint = awsOptions.EndPoint,
ViewEndpoint = awsOptions.ViewEndpoint,
};
AWSTempToken = tempToken;
return new ObjectStoreDTO() { ObjectStoreUse = ObjectStoreServiceOptions.ObjectStoreUse, AWS = tempToken };
}
else
{
throw new BusinessValidationFailedException("未定义的存储介质类型");
}
}
}

View File

@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}

View File

@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}

View File

@ -7,28 +7,28 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AlibabaCloud.SDK.Sts20150401" Version="1.1.4" />
<PackageReference Include="AlibabaCloud.SDK.Sts20150401" Version="1.1.5" />
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="10.0.0" />
<PackageReference Include="Aliyun.OSS.SDK.NetCore" Version="2.14.1" />
<PackageReference Include="AWSSDK.S3" Version="3.7.405" />
<PackageReference Include="AWSSDK.SecurityToken" Version="3.7.400.36" />
<PackageReference Include="DistributedLock.Core" Version="1.0.7" />
<PackageReference Include="DistributedLock.SqlServer" Version="1.0.5" />
<PackageReference Include="fo-dicom" Version="5.1.3" />
<PackageReference Include="fo-dicom.Codecs" Version="5.15.1" />
<PackageReference Include="fo-dicom.Imaging.ImageSharp" Version="5.1.3" />
<PackageReference Include="AWSSDK.S3" Version="3.7.416.8" />
<PackageReference Include="AWSSDK.SecurityToken" Version="3.7.401.81" />
<PackageReference Include="DistributedLock.Core" Version="1.0.8" />
<PackageReference Include="DistributedLock.SqlServer" Version="1.0.6" />
<PackageReference Include="fo-dicom" Version="5.2.1" />
<PackageReference Include="fo-dicom.Codecs" Version="5.16.1" />
<PackageReference Include="fo-dicom.Imaging.ImageSharp" Version="5.2.1" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.10" />
<PackageReference Include="AutoMapper" Version="13.0.1" />
<PackageReference Include="Minio" Version="6.0.3" />
<PackageReference Include="Minio" Version="6.0.4" />
<PackageReference Include="My.Extensions.Localization.Json" Version="3.3.0">
<TreatAsUsed>true</TreatAsUsed>
</PackageReference>
<PackageReference Include="Panda.DynamicWebApi" Version="1.2.2" />
<PackageReference Include="Serilog.Enrichers.ClientInfo" Version="2.1.2" />
<PackageReference Include="Serilog.Extensions.Hosting" Version="8.0.0" />
<PackageReference Include="Serilog.Extensions.Hosting" Version="9.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.9.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="8.1.1" />
</ItemGroup>
<ItemGroup>

View File

@ -126,16 +126,22 @@ builder.Services.Configure<ForwardedHeadersOptions>(options =>
ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
});
builder.Services.AddFellowOakDicom().AddTranscoderManager<NativeTranscoderManager>()
//.AddTranscoderManager<FellowOakDicom.Imaging.NativeCodec.NativeTranscoderManager>()
.AddImageManager<ImageSharpImageManager>();
//Dicom影像渲染图片 跨平台
//builder.Services.AddDicomSetup();
new DicomSetupBuilder()
.RegisterServices(s =>
s.AddFellowOakDicom()
.AddTranscoderManager<NativeTranscoderManager>()
//.AddTranscoderManager<FellowOakDicom.Imaging.NativeCodec.NativeTranscoderManager>()
.AddImageManager<ImageSharpImageManager>())
.SkipValidation()
.Build();
//new DicomSetupBuilder()
// .RegisterServices(s =>
// s.AddFellowOakDicom()
// .AddTranscoderManager<NativeTranscoderManager>()
// //.AddTranscoderManager<FellowOakDicom.Imaging.NativeCodec.NativeTranscoderManager>()
// .AddImageManager<ImageSharpImageManager>())
// .SkipValidation()
// .Build();
@ -211,6 +217,8 @@ else
#endregion
DicomSetupBuilder.UseServiceProvider(app.Services);
var logger = app.Services.GetService<Microsoft.Extensions.Logging.ILogger<Program>>();
var server = DicomServerFactory.Create<CStoreSCPService>(_configuration.GetSection("DicomSCPServiceConfig").GetValue<int>("ServerPort"), userState: app.Services,logger: logger);

View File

@ -76,9 +76,10 @@ namespace IRaCIS.Core.SCP.Service
};
public CStoreSCPService(INetworkStream stream, Encoding fallbackEncoding, Microsoft.Extensions.Logging.ILogger log, DicomServiceDependencies dependencies)
public CStoreSCPService(INetworkStream stream, Encoding fallbackEncoding, Microsoft.Extensions.Logging.ILogger log, DicomServiceDependencies dependencies, IServiceProvider injectServiceProvider)
: base(stream, fallbackEncoding, log, dependencies)
{
_serviceProvider = injectServiceProvider.CreateScope().ServiceProvider;
}
@ -92,9 +93,7 @@ namespace IRaCIS.Core.SCP.Service
Log.Logger.Warning($"接收到来自{association.CallingAE}的连接");
_serviceProvider = (IServiceProvider)this.UserState;
//_serviceProvider = (IServiceProvider)this.UserState;
var _trialDicomAERepository = _serviceProvider.GetService<IRepository<TrialDicomAE>>();

View File

@ -19,6 +19,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IRaCIS.Core.Infrastructure"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IRC.Core.SCP", "IRC.Core.SCP\IRC.Core.SCP.csproj", "{ECD08F47-DC1A-484E-BB91-6CDDC8823CC5}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IRC.Core.Dicom", "IRC.Core.Dicom\IRC.Core.Dicom.csproj", "{0545F0A5-D97B-4A47-92A6-A8A02A181322}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -57,6 +59,10 @@ Global
{ECD08F47-DC1A-484E-BB91-6CDDC8823CC5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{ECD08F47-DC1A-484E-BB91-6CDDC8823CC5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{ECD08F47-DC1A-484E-BB91-6CDDC8823CC5}.Release|Any CPU.Build.0 = Release|Any CPU
{0545F0A5-D97B-4A47-92A6-A8A02A181322}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0545F0A5-D97B-4A47-92A6-A8A02A181322}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0545F0A5-D97B-4A47-92A6-A8A02A181322}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0545F0A5-D97B-4A47-92A6-A8A02A181322}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@ -1,4 +1,5 @@
using Amazon.Auth.AccessControlPolicy;
using AlibabaCloud.SDK.Sts20150401;
using Amazon.Auth.AccessControlPolicy;
using Amazon.SecurityToken;
using AutoMapper;
using Azure.Core;
@ -21,10 +22,12 @@ using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Org.BouncyCastle.Tls;
using RestSharp;
using RestSharp.Authenticators;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Net.Http;
@ -181,11 +184,74 @@ namespace IRaCIS.Api.Controllers
}
#region 老项目依赖
[HttpGet("user/GenerateSTS")]
public IResponseOutput GenerateSTS([FromServices] IOptionsMonitor<ObjectStoreServiceOptions> options)
{
var ossOptions = options.CurrentValue.AliyunOSS;
var client = new Client(new AlibabaCloud.OpenApiClient.Models.Config()
{
AccessKeyId = ossOptions.AccessKeyId,
AccessKeySecret = ossOptions.AccessKeySecret,
Endpoint = "sts.cn-hangzhou.aliyuncs.com"
});
var assumeRoleRequest = new AlibabaCloud.SDK.Sts20150401.Models.AssumeRoleRequest();
// 将<YOUR_ROLE_SESSION_NAME>设置为自定义的会话名称例如oss-role-session。
assumeRoleRequest.RoleSessionName = $"session-name-{NewId.NextGuid()}";
// 将<YOUR_ROLE_ARN>替换为拥有上传文件到指定OSS Bucket权限的RAM角色的ARN。
assumeRoleRequest.RoleArn = ossOptions.RoleArn;
//assumeRoleRequest.RoleArn = "acs:ram::1899121822495495:role/webdirect";
assumeRoleRequest.DurationSeconds = ossOptions.DurationSeconds;
var runtime = new AlibabaCloud.TeaUtil.Models.RuntimeOptions();
var response = client.AssumeRoleWithOptions(assumeRoleRequest, runtime);
var credentials = response.Body.Credentials;
var tempToken = new AliyunOSSTempToken()
{
AccessKeyId = credentials.AccessKeyId,
AccessKeySecret = credentials.AccessKeySecret,
//转为服务器时区,最后统一转为客户端时区
Expiration = TimeZoneInfo.ConvertTimeFromUtc(DateTime.Parse(credentials.Expiration), TimeZoneInfo.Local),
SecurityToken = credentials.SecurityToken,
Region = ossOptions.Region,
BucketName = ossOptions.BucketName,
EndPoint = ossOptions.EndPoint,
ViewEndpoint = ossOptions.ViewEndpoint,
PreviewEndpoint = ossOptions.PreviewEndpoint
};
// 返回STS令牌信息给前端
var stsToken = new
{
AccessKeyId = credentials.AccessKeyId,
AccessKeySecret = credentials.AccessKeySecret,
SecurityToken = credentials.SecurityToken,
Expiration = credentials.Expiration,
Region = ossOptions.Region,
BucketName = ossOptions.BucketName,
ViewEndpoint = ossOptions.ViewEndpoint,
};
return ResponseOutput.Ok(stsToken);
}
#endregion
[HttpGet("User/UserRedirect")]
[AllowAnonymous]
public async Task<IActionResult> UserRedirect([FromServices] IRepository<IdentityUser> _useRepository, string url, [FromServices] ILogger<ExtraController> _logger)
public async Task<IActionResult> UserRedirect([FromServices] IRepository<IdentityUser> _useRepository, string url, [FromServices] ILogger<ExtraController> _logger, [FromServices] ITokenService _tokenService)
{
var decodeUrl = System.Web.HttpUtility.UrlDecode(url);
@ -200,10 +266,22 @@ namespace IRaCIS.Api.Controllers
var errorUrl = domainStrList[0] + "//" + domainStrList[2] + "/error";
if (!await _useRepository.AnyAsync(t => t.Id == Guid.Parse(userId) && t.EmailToken == token && t.IsFirstAdd))
if (lang == "zh")
{
decodeUrl = errorUrl + $"?lang={lang}&ErrorMessage={System.Web.HttpUtility.UrlEncode(lang == "zh" ? "" : "ErrorThe initialization link has expired.")} ";
CultureInfo.CurrentCulture = new CultureInfo(StaticData.CultureInfo.zh_CN);
CultureInfo.CurrentUICulture = new CultureInfo(StaticData.CultureInfo.zh_CN);
}
else
{
CultureInfo.CurrentCulture = new CultureInfo(StaticData.CultureInfo.en_US);
CultureInfo.CurrentUICulture = new CultureInfo(StaticData.CultureInfo.en_US);
}
var isExpire = _tokenService.IsTokenExpired(token);
if (!await _useRepository.AnyAsync(t => t.Id == Guid.Parse(userId) && t.EmailToken == token && t.IsFirstAdd) || isExpire)
{
decodeUrl = errorUrl + $"?lang={lang}&ErrorMessage={System.Web.HttpUtility.UrlEncode(I18n.T("UserRedirect_InitializationLinkExpire"))} ";
}
return Redirect(decodeUrl);

View File

@ -527,7 +527,7 @@ namespace IRaCIS.Core.API.Controllers
}
var uploadFinishedTime = DateTime.Now;
var noneDicomStudy = await _noneDicomStudyRepository.FirstOrDefaultAsync((t => t.Id == noneDicomStudyId));
var noneDicomStudy = await _noneDicomStudyRepository.FirstOrDefaultAsync(t => t.Id == noneDicomStudyId,true);
noneDicomStudy.FileCount = noneDicomStudy.FileCount + (incommand.VisitTaskId != null ? 0 : incommand.UploadedFileList.Count);

View File

@ -20,6 +20,8 @@ namespace IRaCIS.Core.API.HostService;
public class HangfireHostService(IRecurringMessageScheduler _recurringMessageScheduler,
IRepository<TrialEmailNoticeConfig> _trialEmailNoticeConfigRepository,
IRepository<EmailNoticeConfig> _emailNoticeConfigrepository,
IMediator _mediator,
ILogger<HangfireHostService> _logger) : IHostedService
{
@ -55,6 +57,20 @@ public class HangfireHostService(IRecurringMessageScheduler _recurringMessageSch
}
// 系统邮件定时任务
var systemTaskInfoList = await _emailNoticeConfigrepository.Where(t => t.EmailCron != string.Empty && t.IsAutoSend)
.Select(t => new { t.Id, t.Code, t.EmailCron, t.BusinessScenarioEnum, })
.ToListAsync();
foreach (var task in systemTaskInfoList)
{
//利用主键作为任务Id
var jobId = $"{task.Id}_({task.BusinessScenarioEnum})";
HangfireJobHelper.AddOrUpdateTimingCronJob(jobId, task.BusinessScenarioEnum, task.EmailCron);
}
//await _recurringMessageScheduler.ScheduleRecurringPublish(new QCImageQuestionSchedule() { CronExpression = "0/3 * * * * ? " }, new MasstransiTestCommand { value = "message at " + DateTime.Now.ToString() });

View File

@ -69,24 +69,23 @@
<ItemGroup>
<PackageReference Include="AspNetCoreRateLimit" Version="5.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.14" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.10">
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.15">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="ConfigMapFileProvider" Version="2.0.1" />
<PackageReference Include="Hangfire.AspNetCore" Version="1.8.14" />
<PackageReference Include="Hangfire.AspNetCore" Version="1.8.18" />
<PackageReference Include="Hangfire.Dashboard.BasicAuthorization" Version="1.0.2" />
<PackageReference Include="Hangfire.InMemory" Version="1.0.0" />
<PackageReference Include="Hangfire.SqlServer" Version="1.8.14" />
<PackageReference Include="Invio.Extensions.Authentication.JwtBearer" Version="2.0.1" />
<PackageReference Include="LogDashboard" Version="1.4.8" />
<PackageReference Include="Hangfire.SqlServer" Version="1.8.18" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.10" />
<PackageReference Include="Serilog.AspNetCore" Version="8.0.3" />
<PackageReference Include="Serilog.AspNetCore" Version="9.0.0" />
<PackageReference Include="Serilog.Formatting.Compact" Version="3.0.0" />
<PackageReference Include="Serilog.Sinks.Email" Version="4.0.0" />
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="6.9.0" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="8.1.1" />
</ItemGroup>
<ItemGroup>
@ -114,6 +113,12 @@
</Content>
</ItemGroup>
<ItemGroup>
<None Update="Resources\GeoLite2-City.mmdb">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
<ProjectExtensions>
<VisualStudio>
<UserProperties properties_4launchsettings_1json__JsonSchema="" />

View File

@ -7,7 +7,6 @@ using IRaCIS.Core.Application.Service;
using IRaCIS.Core.Application.Service.BusinessFilter;
using IRaCIS.Core.Infra.EFCore;
using IRaCIS.Core.Infrastructure.Extention;
using LogDashboard;
using MassTransit;
using MassTransit.NewIdProviders;
using Microsoft.AspNetCore.Builder;
@ -37,6 +36,11 @@ var config = new ConfigurationBuilder()
var enviromentName = config["ASPNETCORE_ENVIRONMENT"];
var openSwaggerStr = config["ASPNETCORE_OpenSwagger"];
var isOpenSwagger= openSwaggerStr == null|| openSwaggerStr?.ToLower()=="true";
if (string.IsNullOrWhiteSpace(enviromentName))
{
@ -111,8 +115,13 @@ builder.Services.AddAutoMapperSetup();
builder.Services.AddEFSetup(_configuration, enviromentName);
//Http 响应压缩
builder.Services.AddResponseCompressionSetup();
//Swagger Api 文档
builder.Services.AddSwaggerSetup();
if (isOpenSwagger)
{
//Swagger Api 文档
builder.Services.AddSwaggerSetup();
}
//JWT Token 验证
builder.Services.AddJWTAuthSetup(_configuration);
@ -126,7 +135,7 @@ builder.Services.AddFusionCache();
builder.Services.AddhangfireSetup(_configuration);
//Serilog 日志可视化 LogDashboard日志
builder.Services.AddLogDashboardSetup();
//builder.Services.AddLogDashboardSetup();
//Dicom影像渲染图片 跨平台
builder.Services.AddDicomSetup();
@ -211,13 +220,18 @@ app.UseResponseCompression();
app.UseStaticFiles();
//LogDashboard
app.UseLogDashboard("/LogDashboard");
//app.UseLogDashboard("/LogDashboard");
//hangfire
app.UseHangfireConfig(env);
// Swagger
SwaggerSetup.Configure(app, env);
if (isOpenSwagger)
{
SwaggerSetup.Configure(app, env);
}
//serilog 记录请求的用户信息
app.UseSerilogConfig(env);

View File

@ -20,7 +20,8 @@
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Test_IRC"
"ASPNETCORE_ENVIRONMENT": "Test_IRC",
"ASPNETCORE_OpenSwagger": "true"
},
"applicationUrl": "http://localhost:6100"
},

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 MiB

View File

@ -1,15 +1,15 @@
using LogDashboard;
using LogDashboard.Authorization;
//using LogDashboard;
//using LogDashboard.Authorization;
namespace IRaCIS.Core.API.Filter
{
//namespace IRaCIS.Core.API.Filter
//{
public class LogDashBoardAuthFilter : ILogDashboardAuthorizationFilter
{
//在此可以利用 本系统的UerTypeEnum 判断
public bool Authorization(LogDashboardContext context)
{
return context.HttpContext.User.Identity.IsAuthenticated;
}
}
}
// public class LogDashBoardAuthFilter : ILogDashboardAuthorizationFilter
// {
// //在此可以利用 本系统的UerTypeEnum 判断
// public bool Authorization(LogDashboardContext context)
// {
// return context.HttpContext.User.Identity.IsAuthenticated;
// }
// }
//}

View File

@ -1,25 +1,25 @@

using LogDashboard;
using Microsoft.Extensions.DependencyInjection;
//using LogDashboard;
//using Microsoft.Extensions.DependencyInjection;
namespace IRaCIS.Core.API
{
public static class LogDashboardSetup
{
public static void AddLogDashboardSetup(this IServiceCollection services)
{
//IIS 配置虚拟路径部署会出现IIS静态文件404
services.AddLogDashboard(opt =>
{
//opt.PathMatch = "/api/LogDashboard";
opt.PathMatch = "/LogDashboard";
//namespace IRaCIS.Core.API
//{
// public static class LogDashboardSetup
// {
// public static void AddLogDashboardSetup(this IServiceCollection services)
// {
// //IIS 配置虚拟路径部署会出现IIS静态文件404
// services.AddLogDashboard(opt =>
// {
// //opt.PathMatch = "/api/LogDashboard";
// opt.PathMatch = "/LogDashboard";
//opt.AddAuthorizationFilter(new LogDashboardBasicAuthFilter("admin", "zhizhun2018"));
// //opt.AddAuthorizationFilter(new LogDashboardBasicAuthFilter("admin", "zhizhun2018"));
//opt.AddAuthorizationFilter(new LogDashBoardAuthFilter());
// //opt.AddAuthorizationFilter(new LogDashBoardAuthFilter());
});
// });
}
}
}
// }
// }
//}

View File

@ -31,7 +31,7 @@ namespace IRaCIS.Core.API
.Enrich.FromLogContext()
.Filter.ByExcluding(logEvent => logEvent.Properties.ContainsKey("RequestPath") && logEvent.Properties["RequestPath"].ToString().Contains("/health"))
.WriteTo.Console()
.WriteTo.File($"{AppContext.BaseDirectory}Serilogs/.log", rollingInterval: RollingInterval.Day);
.WriteTo.File($"{AppContext.BaseDirectory}Serilogs/.log", rollingInterval: RollingInterval.Day,retainedFileCountLimit:60);
#region 根据环境配置是否打开错误发送邮件通知

View File

@ -16,27 +16,30 @@ namespace IRaCIS.Core.API;
public enum SwaggerVersion
{
[Description("医生模块")]
[Description("文件记录(FileRecord)")]
FileRecord = -1,
[Description("医生模块(Reviewer)")]
Reviewer = 1,
[Description("项目模块")]
[Description("项目模块(Trial)")]
Trial = 2,
[Description("入组模块")]
[Description("入组模块(Enroll)")]
Enroll = 3,
[Description("工作量模块")]
[Description("工作量模块(Workload)")]
Workload = 4,
[Description("通用信息获取")]
[Description("通用信息获取(Common)")]
Common = 5,
[Description("机构信息模块")]
[Description("机构信息模块(Institution)")]
Institution = 6,
[Description("统计模块")]
[Description("统计模块(DashboardStatistics)")]
DashboardStatistics = 7,
[Description("财务模块")]
[Description("财务模块(Financial)")]
Financial = 8,
[Description("管理模块")]
[Description("管理模块(Management)")]
Management =9,
[Description("影像模块")]
[Description("影像模块(Image)")]
Image =10,
[Description("读片模块")]
[Description("读片模块(Reading)")]
Reading =11
};

View File

@ -7,8 +7,8 @@
}
},
"ConnectionStrings": {
"RemoteNew": "Server=47.117.164.182,1434;Database=Event_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
"Hangfire": "Server=47.117.164.182,1434;Database=Event_IRC_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
"RemoteNew": "Server=101.132.253.119,1435;Database=irc_Prpd_bak;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
"Hangfire": "Server=101.132.253.119,1435;Database=irc_Hangfire_bak;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
},
"ObjectStoreService": {
"ObjectStoreUse": "AliyunOSS",
@ -52,7 +52,10 @@
"IsNeedChangePassWord": true,
"ChangePassWordDays": 90
"ChangePassWordDays": 90,
// 1 Elevate 2 Extensive
"TemplateType": 2
},
"SystemEmailSendConfig": {
"Port": 465,

View File

@ -7,10 +7,10 @@
}
},
"ConnectionStrings": {
//"RemoteNew": "Server=101.132.193.237,1434;Database=Prod_IRC;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true",
//"Hangfire": "Server=101.132.193.237,1434;Database=Prod_IRC_Hangfire;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true"
"RemoteNew": "Server=prod_mssql_standard,1433;Database=Prod_IRC;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true",
"Hangfire": "Server=prod_mssql_standard,1433;Database=Prod_IRC_Hangfire;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true"
"RemoteNew": "Server=101.132.193.237,1434;Database=Prod_IRC;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true",
"Hangfire": "Server=101.132.193.237,1434;Database=Prod_IRC_Hangfire;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true"
//"RemoteNew": "Server=prod_mssql_standard,1433;Database=Prod_IRC;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true",
//"Hangfire": "Server=prod_mssql_standard,1433;Database=Prod_IRC_Hangfire;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true"
},
"ObjectStoreService": {
"ObjectStoreUse": "AliyunOSS",
@ -43,7 +43,7 @@
"OpenLoginLimit": true,
"LoginMaxFailCount": 5,
"LoginFailLockMinutes": 30,
"AutoLoginOutMinutes": 360,
"AutoLoginOutMinutes": 120,
"OpenLoginMFA": false,
@ -51,7 +51,9 @@
"ReadingRestTimeMin": 10,
"IsNeedChangePassWord": true,
"ChangePassWordDays": 90
"ChangePassWordDays": 90,
// 1 Elevate 2 Extensive
"TemplateType": 2
},
"SystemEmailSendConfig": {
@ -61,6 +63,7 @@
"FromName": "irc",
"AuthorizationCode": "ExtImg@2022",
"SiteUrl": "http://irc.extimaging.com/login",
"SystemShortName": "IRC",
"OrganizationName": "Extlmaging",
"OrganizationNameCN": "Extlmaging",
"CompanyName": "Extensive Imaging",

View File

@ -23,9 +23,11 @@
"AccessKeySecret": "FLizxkHsMm4CGYHtkV8E3PNJJZU7oV",
"RoleArn": "acs:ram::1899121822495495:role/dev-oss-access",
"BucketName": "zy-irc-test-store",
"ViewEndpoint": "https://zy-irc-test-store.oss-cn-shanghai.aliyuncs.com",
//"ViewEndpoint": "https://zy-irc-test-store.oss-cn-shanghai.aliyuncs.com",
"ViewEndpoint": "https://zy-irc-test-dev-cache.oss-cn-shanghai.aliyuncs.com",
"Region": "oss-cn-shanghai",
"DurationSeconds": 7200
"DurationSeconds": 7200,
"PreviewEndpoint": "https://test-oss.test.extimaging.com"
},
"MinIO": {
"EndPoint": "hir-oss.test.extimaging.com",
@ -67,6 +69,8 @@
"IsNeedChangePassWord": true,
"ChangePassWordDays": 90,
// 1 Elevate 2 Extensive
"TemplateType": 2,
"OpenTrialRelationDelete": true,
@ -81,6 +85,7 @@
"AuthorizationCode": "SHzyyl2021",
"SiteUrl": "http://irc.test.extimaging.com/login",
"SystemShortName": "IRC",
"OrganizationName": "Extlmaging",
"OrganizationNameCN": "Extlmaging",
"CompanyName": "Extensive Imaging",

View File

@ -54,7 +54,8 @@
"IsNeedChangePassWord": true,
"ChangePassWordDays": 90,
// 1 Elevate 2 Extensive
"TemplateType": 1,
"OpenTrialRelationDelete": false
@ -67,6 +68,7 @@
"FromName": "LiLi",
"AuthorizationCode": "Q#669869497420ul",
"SystemShortName": "LiLi",
"OrganizationName": "Elevate Imaging",
"OrganizationNameCN": "Elevate Imaging",
"CompanyName": "Elevate Imaging Inc.",

View File

@ -64,7 +64,8 @@
"IsNeedChangePassWord": true,
"ChangePassWordDays": 90,
// 1 Elevate 2 Extensive
"TemplateType": 1,
"OpenLoginMFA": true
},
@ -75,6 +76,7 @@
"FromName": "LiLi",
"AuthorizationCode": "Q#669869497420ul",
"SystemShortName": "LiLi",
"OrganizationName": "Elevate Imaging",
"OrganizationNameCN": "Elevate Imaging",
"CompanyName": "Elevate Imaging Inc.",

View File

@ -63,7 +63,9 @@
"ReadingRestTimeMin": 10,
"IsNeedChangePassWord": true,
"ChangePassWordDays": 90
"ChangePassWordDays": 90,
// 1 Elevate 2 Extensive
"TemplateType": 1
},
"SystemEmailSendConfig": {
@ -73,6 +75,7 @@
"FromName": "LiLi",
"AuthorizationCode": "Q#669869497420ul",
"SystemShortName": "LiLi",
"OrganizationName": "Elevate Imaging",
"OrganizationNameCN": "Elevate Imaging",
"CompanyName": "Elevate Imaging Inc.",

View File

@ -62,7 +62,7 @@
"LoginMaxFailCount": 5,
"LoginFailLockMinutes": 30,
"AutoLoginOutMinutes": 60,
"AutoLoginOutMinutes": 120,
"OpenLoginMFA": false,
@ -70,7 +70,9 @@
"ReadingRestTimeMin": 10,
"IsNeedChangePassWord": true,
"ChangePassWordDays": 90
"ChangePassWordDays": 90,
// 1 Elevate 2 Extensive
"TemplateType": 2
},
"SystemEmailSendConfig": {
@ -80,6 +82,8 @@
"FromName": "UAT_IRC",
"AuthorizationCode": "SHzyyl2021",
"SiteUrl": "http://irc.uat.extimaging.com/login",
"SystemShortName": "IRC",
"OrganizationName": "Extlmaging",
"OrganizationNameCN": "Extlmaging",
"CompanyName": "Extensive Imaging",

View File

@ -3,7 +3,7 @@
"SecurityKey": "ShangHaiZhanYing_SecurityKey_SHzyyl@2021",
"Issuer": "Extimaging",
"Audience": "EICS",
"TokenExpireDays": "7"
"TokenExpireMinute": "10080"//7
},
"IpRateLimiting": {
"EnableEndpointRateLimiting": true,

View File

@ -23,7 +23,7 @@ namespace IRaCIS.Core.Application.Auth
/// <summary>
/// 过期时间
/// </summary>
public int TokenExpireDays { get; set; }
public int TokenExpireMinute { get; set; }
//public Dictionary<string, object> Claims { get; set; }

View File

@ -9,6 +9,8 @@ namespace IRaCIS.Core.Application.Auth
public interface ITokenService
{
string GetToken(UserTokenInfo user);
bool IsTokenExpired(string token);
}
@ -47,13 +49,27 @@ namespace IRaCIS.Core.Application.Auth
signingCredentials: _jwtSetting.Credentials,
claims: claims,
notBefore: DateTime.Now,
expires: DateTime.Now.AddDays(_jwtSetting.TokenExpireDays)
expires: DateTime.Now.AddMinutes(_jwtSetting.TokenExpireMinute)
);
string jwtToken = new JwtSecurityTokenHandler().WriteToken(token);
return jwtToken;
}
public bool IsTokenExpired(string token)
{
var handler = new JwtSecurityTokenHandler();
try
{
var jwtToken = handler.ReadJwtToken(token);
return jwtToken.ValidTo < DateTime.UtcNow;
}
catch
{
return true; // 无效 Token 也视为已过期
}
}
}

View File

@ -34,6 +34,8 @@ public class ServiceVerifyConfigOption
[Description("修改密码的天数")]
public int ChangePassWordDays { get; set; }
public SysTemplateType TemplateType { get; set; }
public string ThirdPdfUrl { get; set; }
}
@ -51,6 +53,8 @@ public class SystemEmailSendConfig
public string SiteUrl { get; set; } = string.Empty;
public string SystemShortName { get; set; } = string.Empty;
public string OrganizationName { get; set; } = string.Empty;
public string OrganizationNameCN { get; set; } = string.Empty;
@ -71,6 +75,7 @@ public class SystemEmailSendConfig
public class SystemEmailSendConfigView
{
public string SystemShortName { get; set; } = string.Empty;
public string CompanyName { get; set; } = string.Empty;
public string CompanyNameCN { get; set; } = string.Empty;

View File

@ -11,6 +11,12 @@ public static class SendEmailHelper
public static async Task SendEmailAsync(MimeMessage messageToSend, SystemEmailSendConfig _systemEmailConfig, EventHandler<MessageSentEventArgs>? messageSentSuccess = null)
{
//没有收件人 那么不发送
if (messageToSend.To.Count == 0)
{
return;
}
try
{
using (var smtp = new MailKit.Net.Smtp.SmtpClient())

View File

@ -243,6 +243,7 @@ public static class ExcelExportHelper
}
public class DynamicColumnConfig
{
/// <summary>
@ -260,7 +261,9 @@ public static class ExcelExportHelper
/// </summary>
public int TempalteLastColumnIndex { get; set; }
public List<string> CDISCList { get; set; } = new List<string>();
public bool IsCDISCExport { get; set; }=false;
//public List<string> CDISCList { get; set; } = new List<string>();
/// <summary>
/// 动态的列名 如果Id 重复那么就按照名称填充否则就按照Id 填充列数据
@ -300,6 +303,19 @@ public static class ExcelExportHelper
{
public Guid Id { get; set; }
public string Name { get; set; }
public string CDISCCode { get; set; }
public override bool Equals(object obj)
{
if (obj is not ColumItem other) return false;
return Id == other.Id && Name == other.Name;
}
public override int GetHashCode()
{
return HashCode.Combine(Id, Name);
}
}
public List<string> ColumnIdList => ColumnIdNameList == null ? new List<string>() : ColumnIdNameList.Select(t => t.Id.ToString()).ToList();
@ -478,7 +494,9 @@ public static class ExcelExportHelper
if (dynamicColumnConfig != null)
{
var isCdics = dynamicColumnConfig.CDISCList.Count > 0;
//var isCdics = dynamicColumnConfig.CDISCList.Count > 0;
var isCdics = dynamicColumnConfig.IsCDISCExport;
var sheet = workbook.GetSheetAt(0);
@ -533,9 +551,9 @@ public static class ExcelExportHelper
}
//创建新的列
for (int i = originTotalEndIndex; i < originTotalEndIndex + needAddCount; i++)
for (int i = originRemoveEndIndex; i < originRemoveEndIndex + needAddCount; i++)
{
titelRow.CreateCell(i + 1);
templateRow.CreateCell(i + 1);
@ -554,6 +572,11 @@ public static class ExcelExportHelper
titelRow.GetCell(i).SetCellValue(titelRow.GetCell(i - gap).StringCellValue);
templateRow.GetCell(i).SetCellValue(templateRow.GetCell(i - gap).StringCellValue);
if (isCdics)
{
cdicsRow.GetCell(i).SetCellValue(cdicsRow.GetCell(i - gap).StringCellValue);
}
}
@ -562,6 +585,7 @@ public static class ExcelExportHelper
for (int i = dynamicColunmStartIndex; i < dynamicColunmStartIndex + needAddCount; i++)
{
var name = dynamicColumnConfig.ColumnIdNameList[i - dynamicColunmStartIndex].Name;
titelRow.GetCell(i).SetCellValue(name);
@ -569,13 +593,15 @@ public static class ExcelExportHelper
if (isCdics)
{
var cdicsCode = dynamicColumnConfig.CDISCList[i - dynamicColunmStartIndex];
var cdicsCode = dynamicColumnConfig.ColumnIdNameList[i - dynamicColunmStartIndex].CDISCCode;
cdicsRow.GetCell(i).SetCellValue(cdicsCode);
}
}
}
using (var memoryStream2 = new MemoryStream())
{
workbook.Write(memoryStream2, true);
@ -598,8 +624,11 @@ public static class ExcelExportHelper
IgnoreTemplateParameterMissing = true,
};
//await MiniExcel.SaveAsByTemplateAsync("testmini.xlsx", templateStream.ToArray(), translateData);
await MiniExcel.SaveAsByTemplateAsync(memoryStream, templateStream.ToArray(), translateData, config);
memoryStream.Seek(0, SeekOrigin.Begin);
if (dynamicColumnConfig != null)
@ -896,7 +925,7 @@ public static class ExcelExportHelper
{
var name = dynamicColumnConfig.ColumnIdNameList[i - dynamicColunmStartIndex].Name;
var cdicsCode = dynamicColumnConfig.CDISCList[i - dynamicColunmStartIndex];
var cdicsCode = dynamicColumnConfig.ColumnIdNameList[i - dynamicColunmStartIndex].CDISCCode;
cdicsRow.GetCell(i).SetCellValue(cdicsCode);
titelRow.GetCell(i).SetCellValue(name);

View File

@ -69,6 +69,15 @@ public static class FileStoreHelper
return rootFolder;
}
public static string GetDonwnloadImageFolder(IWebHostEnvironment _hostEnvironment)
{
var rootPath = GetIRaCISRootPath(_hostEnvironment);
var rootFolder = Path.Combine(rootPath, StaticData.Folder.DownloadIamgeFolder);
return rootFolder;
}
//根据相对路径 获取具体文件物理地址
public static string GetPhysicalFilePath(IWebHostEnvironment _hostEnvironment, string relativePath)
{

View File

@ -20,12 +20,12 @@ namespace IRaCIS.Core.Application.Helper
//添加 或者更新定时任务 Id 要唯一标识一个定义任务
public static void AddOrUpdateCronJob<T>(string jobId, Expression<Action<T>> methodCall, string cron, string queueName = "default")
{
RecurringJob.AddOrUpdate<T>(jobId, queueName, methodCall, cron);
RecurringJob.AddOrUpdate<T>(jobId, queueName, methodCall, cron, new RecurringJobOptions() { TimeZone = TimeZoneInfo.Local });
}
public static void AddOrUpdateInitCronJob<T>(string jobId, Expression<Action<T>> methodCall, string cron)
{
RecurringJob.AddOrUpdate<T>(jobId, "sys_init", methodCall, cron);
RecurringJob.AddOrUpdate<T>(jobId, "sys_init", methodCall, cron, new RecurringJobOptions() { TimeZone = TimeZoneInfo.Local });
}
public static void RemoveCronJob(string jobId)
@ -73,17 +73,36 @@ namespace IRaCIS.Core.Application.Helper
case EmailBusinessScenario.QCToCRCImageQuestion:
HangfireJobHelper.AddOrUpdateCronJob<IMediator>(jobId, t => t.Send(new QCImageQuestionRecurringEvent() { TrialId = trialId }, default), emailCron);
break;
//加急阅片 10分钟
//加急阅片 10分钟
case EmailBusinessScenario.ExpeditedReading:
HangfireJobHelper.AddOrUpdateCronJob<IMediator>(jobId, t => t.Send(new UrgentIRUnReadTaskRecurringEvent() { TrialId = trialId }, default), emailCron);
break;
default:
break;
}
}
public static void AddOrUpdateTimingCronJob(string jobId, EmailBusinessScenario businessScenario, string emailCron)
{
switch (businessScenario)
{
case EmailBusinessScenario.GeneralTraining_ExpirationNotification:
HangfireJobHelper.AddOrUpdateCronJob<IMediator>(jobId, t => t.Send(new SystemDocumentErverDayEvent() { }, default), emailCron);
break;
case EmailBusinessScenario.TrialTraining_ExpirationNotification:
Console.WriteLine("更新项目到期job");
HangfireJobHelper.AddOrUpdateCronJob<IMediator>(jobId, t => t.Send(new TrialDocumentErverDayEvent() { }, default), emailCron);
break;
default:
break;
}
}
}
}

View File

@ -0,0 +1,42 @@
using IRaCIS.Core.Domain.Models;
using MaxMind.GeoIP2;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace IRaCIS.Core.Application.Helper
{
public static class IPCityHelper
{
public static string GetCityResponse(string ip)
{
var path = Path.Combine(AppContext.BaseDirectory, StaticData.Folder.Resources, "GeoLite2-City.mmdb");
try
{
using (var reader = new DatabaseReader(path))
{
var city = reader.City(ip);
//Console.WriteLine(city.Country.IsoCode); // 'US' 'CN'
//Console.WriteLine(city.Country.Name); // 'United States' 'China'
////Console.WriteLine(city.Country.Names["zh-CN"]); // '美国'
//Console.WriteLine(city.MostSpecificSubdivision.Name); // 'Minnesota' 'Hubei'
//Console.WriteLine(city.City.Name); // 'Minneapolis' 'WUHan'
return $"{city.Country.Name} | {city.MostSpecificSubdivision.Name} | {city.City.Name}";
}
}
catch (Exception ex)
{
return $"UN | UN | {ip}";
}
}
}
}

View File

@ -1,5 +1,6 @@
using AlibabaCloud.SDK.Sts20150401;
using Aliyun.OSS;
using Aliyun.OSS.Common;
using Amazon;
using Amazon.Runtime;
using Amazon.S3;
@ -9,9 +10,12 @@ using Amazon.SecurityToken.Model;
using IRaCIS.Core.Infrastructure;
using IRaCIS.Core.Infrastructure.NewtonsoftJson;
using MassTransit;
using MassTransit.Caching.Internals;
using Microsoft.Extensions.Options;
using Minio;
using Minio.DataModel;
using Minio.DataModel.Args;
using Minio.Exceptions;
using System.Reactive.Linq;
using System.Runtime.InteropServices;
@ -59,7 +63,7 @@ public class AliyunOSSOptions
public int DurationSeconds { get; set; }
public string PreviewEndpoint { get; set; }
}
@ -105,6 +109,7 @@ public class AliyunOSSTempToken
public string SecurityToken { get; set; }
public DateTime Expiration { get; set; }
public string PreviewEndpoint { get; set; }
}
@ -143,11 +148,17 @@ public interface IOSSService
public Task<string> GetSignedUrl(string ossRelativePath);
public Task DeleteFromPrefix(string prefix);
public Task DeleteFromPrefix(string prefix, bool isCache = false);
public Task DeleteObjects(List<string> objectKeys);
List<string> GetRootFolderNames();
public ObjectStoreDTO GetObjectStoreTempToken();
public Task MoveObject(string sourcePath, string destPath, bool overwrite = true);
public Task<long> GetObjectSizeAsync(string sourcePath);
}
@ -349,7 +360,11 @@ public class OSSService : IOSSService
public async Task DownLoadFromOSSAsync(string ossRelativePath, string localFilePath)
{
GetObjectStoreTempToken();
if (isFirstCall)
{
GetObjectStoreTempToken();
isFirstCall = false;
}
ossRelativePath = ossRelativePath.TrimStart('/');
try
@ -528,6 +543,185 @@ public class OSSService : IOSSService
}
/// <summary>
/// 移动OSS文件到新路径
/// </summary>
/// <param name="sourcePath">原文件路径格式bucket/key</param>
/// <param name="destPath">新文件路径格式bucket/key</param>
/// <param name="overwrite">是否覆盖已存在的目标文件默认true</param>
public async Task MoveObject(string sourcePath, string destPath, bool overwrite = true)
{
GetObjectStoreTempToken();
switch (ObjectStoreServiceOptions.ObjectStoreUse)
{
case "AliyunOSS":
#region 阿里云
var aliConfig = ObjectStoreServiceOptions.AliyunOSS;
var client = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.EndPoint : aliConfig.InternalEndpoint, AliyunOSSTempToken.AccessKeyId, AliyunOSSTempToken.AccessKeySecret, AliyunOSSTempToken.SecurityToken);
if (sourcePath.StartsWith("/"))
{
sourcePath = sourcePath.Substring(1);
}
if (destPath.StartsWith("/"))
{
destPath = destPath.Substring(1);
}
var sourceBucket = aliConfig.BucketName;
var sourceKey = sourcePath;
var destBucket = aliConfig.BucketName;
var destKey = destPath;
try
{
// 检查目标是否存在(当不允许覆盖时)
if (!overwrite && client.DoesObjectExist(destBucket, destKey))
{
throw new InvalidOperationException("File Exist");
}
//var copyRequest = new Aliyun.OSS.CopyObjectRequest(sourceBucket, sourceKey, sourceBucket, destKey);
//var result = client.CopyObject(copyRequest);
//// 2. 删除原文件(可选,根据是否需要保留原文件)
//client.DeleteObject(sourceBucket, sourceKey);
// 执行复制
var copyRequestAli = new Aliyun.OSS.CopyObjectRequest(
sourceBucket, sourceKey,
destBucket, destKey);
// 保持原文件元数据
copyRequestAli.NewObjectMetadata = new ObjectMetadata
{
ContentType = client.GetObjectMetadata(sourceBucket, sourceKey).ContentType
};
var result = client.CopyObject(copyRequestAli);
// 删除原文件(仅在复制成功后)
client.DeleteObject(sourceBucket, sourceKey);
}
catch (OssException ex)
{
throw new Exception($"[{ex.ErrorCode}] {ex.Message}", ex);
}
#endregion
break;
case "MinIO":
#region MinIO
var minIOConfig = ObjectStoreServiceOptions.MinIO;
var minioClient = new MinioClient()
.WithEndpoint($"{minIOConfig.EndPoint}:{minIOConfig.Port}")
.WithCredentials(minIOConfig.AccessKeyId, minIOConfig.SecretAccessKey)
.WithSSL(minIOConfig.UseSSL)
.Build();
// 定义源路径和目标路径
string destinationKey = "b路径/文件名";
try
{
// 1. 复制文件到新路径[2,5](@ref)
using (var memoryStream = new MemoryStream())
{
// 下载源文件流
await minioClient.GetObjectAsync(new GetObjectArgs()
.WithBucket(minIOConfig.BucketName)
.WithObject(sourcePath)
.WithCallbackStream(stream => stream.CopyTo(memoryStream)));
memoryStream.Position = 0; // 重置流位置
// 上传到新路径
await minioClient.PutObjectAsync(new PutObjectArgs()
.WithBucket(minIOConfig.BucketName)
.WithObject(destinationKey)
.WithStreamData(memoryStream)
.WithObjectSize(memoryStream.Length));
}
// 2. 删除原文件[1,6](@ref)
await minioClient.RemoveObjectAsync(new RemoveObjectArgs()
.WithBucket(minIOConfig.BucketName)
.WithObject(sourcePath));
}
catch (MinioException ex)
{
// 处理异常(例如:记录日志或抛出)
throw new Exception();
}
#endregion
break;
case "AWS":
#region AWS
var awsConfig = ObjectStoreServiceOptions.AWS;
var credentials = new SessionAWSCredentials(
AWSTempToken.AccessKeyId,
AWSTempToken.SecretAccessKey,
AWSTempToken.SessionToken
);
var clientConfig = new AmazonS3Config
{
RegionEndpoint = RegionEndpoint.USEast1,
UseHttp = true,
};
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
// 定义原路径和目标路径
// 1. 复制对象到新路径
var copyRequest = new Amazon.S3.Model.CopyObjectRequest
{
SourceBucket = awsConfig.BucketName,
SourceKey = sourcePath,
DestinationBucket = awsConfig.BucketName,
DestinationKey = destPath
};
try
{
// 执行复制操作
await amazonS3Client.CopyObjectAsync(copyRequest);
// 2. 删除原对象
var deleteRequest = new Amazon.S3.Model.DeleteObjectRequest
{
BucketName = awsConfig.BucketName,
Key = sourcePath
};
await amazonS3Client.DeleteObjectAsync(deleteRequest);
}
catch (AmazonS3Exception ex)
{
Console.WriteLine($"ERROR: {ex.Message}");
// 可根据异常类型细化处理(如文件不存在、权限问题等)
}
#endregion
break;
default:
throw new BusinessValidationFailedException("ERROR");
}
}
/// <summary>
/// 获取所有根目录名称
/// </summary>
@ -550,9 +744,9 @@ public class OSSService : IOSSService
do
{
// 列出根目录下的对象和文件夹
objectListing = _ossClient.ListObjects(new Aliyun.OSS.ListObjectsRequest(aliConfig.BucketName)
objectListing = _ossClient.ListObjects(new Aliyun.OSS.ListObjectsRequest(aliConfig.BucketName)
{
MaxKeys = 1000,
Marker = nextMarker,
Delimiter = "/" // 使用分隔符来模拟文件夹
@ -582,7 +776,7 @@ public class OSSService : IOSSService
/// </summary>
/// <param name="prefix"></param>
/// <returns></returns>
public async Task DeleteFromPrefix(string prefix)
public async Task DeleteFromPrefix(string prefix, bool isCache = false)
{
GetObjectStoreTempToken();
@ -592,6 +786,21 @@ public class OSSService : IOSSService
var _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.EndPoint : aliConfig.InternalEndpoint, AliyunOSSTempToken.AccessKeyId, AliyunOSSTempToken.AccessKeySecret, AliyunOSSTempToken.SecurityToken);
var bucketName = string.Empty;
if (isCache)
{
Uri uri = new Uri(aliConfig.ViewEndpoint);
string host = uri.Host; // 获取 "zy-irc-test-dev-cache.oss-cn-shanghai.aliyuncs.com"
string[] parts = host.Split('.');
bucketName = parts[0];
}
else
{
bucketName = aliConfig.BucketName;
}
try
{
@ -600,7 +809,7 @@ public class OSSService : IOSSService
do
{
// 使用 prefix 模拟目录结构,设置 MaxKeys 和 NextMarker
objectListing = _ossClient.ListObjects(new Aliyun.OSS.ListObjectsRequest(aliConfig.BucketName)
objectListing = _ossClient.ListObjects(new Aliyun.OSS.ListObjectsRequest(bucketName)
{
Prefix = prefix,
MaxKeys = 1000,
@ -612,7 +821,7 @@ public class OSSService : IOSSService
// 删除获取到的文件
if (keys.Count > 0)
{
_ossClient.DeleteObjects(new Aliyun.OSS.DeleteObjectsRequest(aliConfig.BucketName, keys, false));
_ossClient.DeleteObjects(new Aliyun.OSS.DeleteObjectsRequest(bucketName, keys, false));
}
// 设置 NextMarker 以获取下一页的数据
@ -669,7 +878,6 @@ public class OSSService : IOSSService
var awsConfig = ObjectStoreServiceOptions.AWS;
// 提供awsAccessKeyId和awsSecretAccessKey构造凭证
var credentials = new SessionAWSCredentials(AWSTempToken.AccessKeyId, AWSTempToken.SecretAccessKey, AWSTempToken.SessionToken);
@ -721,6 +929,159 @@ public class OSSService : IOSSService
}
}
public async Task DeleteObjects(List<string> objectKeys)
{
GetObjectStoreTempToken();
if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS")
{
var aliConfig = ObjectStoreServiceOptions.AliyunOSS;
var _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.EndPoint : aliConfig.InternalEndpoint, AliyunOSSTempToken.AccessKeyId, AliyunOSSTempToken.AccessKeySecret, AliyunOSSTempToken.SecurityToken);
if (objectKeys.Count > 0)
{
var result = _ossClient.DeleteObjects(new Aliyun.OSS.DeleteObjectsRequest(aliConfig.BucketName, objectKeys, false));
}
}
else if (ObjectStoreServiceOptions.ObjectStoreUse == "MinIO")
{
var minIOConfig = ObjectStoreServiceOptions.MinIO;
var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.EndPoint}:{minIOConfig.Port}")
.WithCredentials(minIOConfig.AccessKeyId, minIOConfig.SecretAccessKey).WithSSL(minIOConfig.UseSSL)
.Build();
if (objectKeys.Count > 0)
{
var objArgs = new RemoveObjectsArgs()
.WithBucket(minIOConfig.BucketName)
.WithObjects(objectKeys);
// 删除对象
await minioClient.RemoveObjectsAsync(objArgs);
}
}
else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS")
{
var awsConfig = ObjectStoreServiceOptions.AWS;
// 提供awsAccessKeyId和awsSecretAccessKey构造凭证
var credentials = new SessionAWSCredentials(AWSTempToken.AccessKeyId, AWSTempToken.SecretAccessKey, AWSTempToken.SessionToken);
//提供awsEndPoint域名进行访问配置
var clientConfig = new AmazonS3Config
{
RegionEndpoint = RegionEndpoint.USEast1,
UseHttp = true,
};
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
if (objectKeys.Count > 0)
{
// 准备删除请求
var deleteObjectsRequest = new Amazon.S3.Model.DeleteObjectsRequest
{
BucketName = awsConfig.BucketName,
Objects = objectKeys.Select(t => new KeyVersion() { Key = t }).ToList()
};
// 批量删除对象
var deleteObjectsResponse = await amazonS3Client.DeleteObjectsAsync(deleteObjectsRequest);
}
}
else
{
throw new BusinessValidationFailedException("未定义的存储介质类型");
}
}
private bool isFirstCall = true;
public async Task<long> GetObjectSizeAsync(string sourcePath)
{
if (isFirstCall)
{
GetObjectStoreTempToken();
isFirstCall = false;
}
var objectkey = sourcePath.Trim('/');
if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS")
{
var aliConfig = ObjectStoreServiceOptions.AliyunOSS;
var _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.EndPoint : aliConfig.InternalEndpoint, AliyunOSSTempToken.AccessKeyId, AliyunOSSTempToken.AccessKeySecret, AliyunOSSTempToken.SecurityToken);
var metadata = _ossClient.GetObjectMetadata(aliConfig.BucketName, objectkey);
long fileSize = metadata.ContentLength; // 文件大小(字节)
return fileSize;
}
else if (ObjectStoreServiceOptions.ObjectStoreUse == "MinIO")
{
var minIOConfig = ObjectStoreServiceOptions.MinIO;
var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.EndPoint}:{minIOConfig.Port}")
.WithCredentials(minIOConfig.AccessKeyId, minIOConfig.SecretAccessKey).WithSSL(minIOConfig.UseSSL)
.Build();
var stat = await minioClient.StatObjectAsync(new Minio.DataModel.Args.StatObjectArgs()
.WithBucket(minIOConfig.BucketName)
.WithObject(objectkey));
return stat.Size; // 文件大小(字节)
}
else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS")
{
var awsConfig = ObjectStoreServiceOptions.AWS;
// 提供awsAccessKeyId和awsSecretAccessKey构造凭证
var credentials = new SessionAWSCredentials(AWSTempToken.AccessKeyId, AWSTempToken.SecretAccessKey, AWSTempToken.SessionToken);
//提供awsEndPoint域名进行访问配置
var clientConfig = new AmazonS3Config
{
RegionEndpoint = RegionEndpoint.USEast1,
UseHttp = true,
};
var request = new Amazon.S3.Model.GetObjectMetadataRequest
{
BucketName = awsConfig.BucketName,
Key = objectkey
};
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
var response = await amazonS3Client.GetObjectMetadataAsync(request);
long fileSize = response.ContentLength; // 文件大小(字节)
return fileSize;
}
else
{
throw new BusinessValidationFailedException("未定义的存储介质类型");
}
}
public ObjectStoreDTO GetObjectStoreTempToken()
{
@ -764,6 +1125,7 @@ public class OSSService : IOSSService
BucketName = ossOptions.BucketName,
EndPoint = ossOptions.EndPoint,
ViewEndpoint = ossOptions.ViewEndpoint,
PreviewEndpoint = ossOptions.PreviewEndpoint
};

View File

@ -15,14 +15,20 @@ public static class ImageHelper
fileStorePath = fileStorePath ?? filePath + ".preview.jpeg";
using (var image = SixLabors.ImageSharp.Image.Load(filePath))
{
// 读取 DICOM 文件
var dicomImage = new DicomImage(filePath);
// 渲染 DICOM 图像到 ImageSharp 格式
using (var image = dicomImage.RenderImage().AsSharpImage())
{
// 生成缩略图(调整大小)
image.Mutate(x => x.Resize(500, 500));
image.Save(fileStorePath);
// 保存缩略图为 JPEG
image.Save(fileStorePath, new JpegEncoder());
}
}

View File

@ -33,37 +33,38 @@
<ItemGroup>
<PackageReference Include="IdentityModel.OidcClient" Version="6.0.0" />
<PackageReference Include="AlibabaCloud.SDK.Sts20150401" Version="1.1.4" />
<PackageReference Include="AWSSDK.SecurityToken" Version="3.7.400.36" />
<PackageReference Include="AlibabaCloud.SDK.Sts20150401" Version="1.1.5" />
<PackageReference Include="AWSSDK.SecurityToken" Version="3.7.401.81" />
<PackageReference Include="Aliyun.OSS.SDK.NetCore" Version="2.14.1" />
<PackageReference Include="AWSSDK.S3" Version="3.7.405" />
<PackageReference Include="DocX" Version="3.0.1" />
<PackageReference Include="AWSSDK.S3" Version="3.7.416.8" />
<PackageReference Include="DocX" Version="4.0.25105.5786" />
<PackageReference Include="FreeSpire.Doc" Version="12.2.0" />
<PackageReference Include="Hangfire.Core" Version="1.8.14" />
<PackageReference Include="Hangfire.Core" Version="1.8.18" />
<PackageReference Include="ExcelDataReader" Version="3.7.0" />
<PackageReference Include="ExcelDataReader.DataSet" Version="3.7.0" />
<PackageReference Include="DistributedLock.Redis" Version="1.0.3" />
<PackageReference Include="DistributedLock.SqlServer" Version="1.0.5" />
<PackageReference Include="fo-dicom" Version="5.1.3" />
<PackageReference Include="fo-dicom.Imaging.ImageSharp" Version="5.1.3" />
<PackageReference Include="fo-dicom.Codecs" Version="5.15.1" />
<PackageReference Include="DistributedLock.SqlServer" Version="1.0.6" />
<PackageReference Include="fo-dicom" Version="5.2.1" />
<PackageReference Include="fo-dicom.Imaging.ImageSharp" Version="5.2.1" />
<PackageReference Include="fo-dicom.Codecs" Version="5.16.1" />
<PackageReference Include="IP2Region.Net" Version="2.0.2" />
<PackageReference Include="MailKit" Version="4.7.1.1" />
<PackageReference Include="MailKit" Version="4.11.0" />
<PackageReference Include="Masa.Contrib.Service.MinimalAPIs" Version="1.0.0" />
<PackageReference Include="MaxMind.GeoIP2" Version="5.2.0" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.10" />
<PackageReference Include="MimeKit" Version="4.7.1" />
<PackageReference Include="MiniExcel" Version="1.34.2" />
<PackageReference Include="Minio" Version="6.0.3" />
<PackageReference Include="MiniWord" Version="0.8.0" />
<PackageReference Include="MimeKit" Version="4.11.0" />
<PackageReference Include="MiniExcel" Version="1.41.2" />
<PackageReference Include="Minio" Version="6.0.4" />
<PackageReference Include="MiniWord" Version="0.9.2" />
<PackageReference Include="My.Extensions.Localization.Json" Version="3.3.0">
<TreatAsUsed>true</TreatAsUsed>
</PackageReference>
<PackageReference Include="NPOI" Version="2.7.1" />
<PackageReference Include="NPOI" Version="2.7.3" />
<PackageReference Include="Panda.DynamicWebApi" Version="1.2.2" />
<PackageReference Include="RestSharp" Version="112.1.0" />
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.5" />
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.7" />
<PackageReference Include="Swashbuckle.AspNetCore.Filters" Version="8.0.2" />
<PackageReference Include="ZiggyCreatures.FusionCache" Version="1.4.0" />
<PackageReference Include="ZiggyCreatures.FusionCache" Version="2.1.0" />
</ItemGroup>
<ItemGroup>

File diff suppressed because it is too large Load Diff

View File

@ -14,19 +14,20 @@ namespace IRaCIS.Core.Application.MassTransit.Consumer;
public static class CommonEmailHelper
{
public static async Task<EmailNoticeConfig> GetEmailSubejctAndHtmlInfoAndBuildAsync(IRepository<EmailNoticeConfig> _emailNoticeConfigrepository, EmailBusinessScenario scenario, MimeMessage messageToSend,
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailFunc)
/// <summary>
/// 系统默认邮件 + 项目默认邮件 (不用添加到项目邮件配置中,才发送)
/// </summary>
/// <param name="configInfo"></param>
/// <param name="messageToSend"></param>
/// <param name="emailFunc"></param>
/// <returns></returns>
/// <exception cref="BusinessValidationFailedException"></exception>
public static async Task<EmailNoticeConfig> GetEmailSubejctAndHtmlInfoAndBuildAsync(EmailNoticeConfig configInfo, MimeMessage messageToSend,
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailFunc)
{
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
var configInfo = await _emailNoticeConfigrepository.Where(t => t.BusinessScenarioEnum == scenario).FirstOrDefaultAsync();
if (configInfo == null)
{
throw new BusinessValidationFailedException("系统未找到当前场景邮件配置信息,请联系运维人员核查");
}
var (topicStr, htmlBodyStr) = isEn_US ? (configInfo.EmailTopic, configInfo.EmailHtmlContent) : (configInfo.EmailTopicCN, configInfo.EmailHtmlContentCN);
try
@ -36,8 +37,9 @@ public static class CommonEmailHelper
}
catch (Exception ex)
{
//"邮件模板内容有误,填充内容出现问题,请联系运维人员核查"
throw new BusinessValidationFailedException("邮件模板内容有误,填充内容出现问题,请联系运维人员核查");
throw new BusinessValidationFailedException(I18n.T("CommonEmail_ConfigError"));
}
@ -52,6 +54,49 @@ public static class CommonEmailHelper
return configInfo;
}
/// <summary>
/// 项目手动邮件 (需要添加到项目邮件配置中,才发送)
/// </summary>
/// <param name="configInfo"></param>
/// <param name="messageToSend"></param>
/// <param name="emailFunc"></param>
/// <returns></returns>
/// <exception cref="BusinessValidationFailedException"></exception>
public static async Task<TrialEmailNoticeConfig> GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(TrialEmailNoticeConfig configInfo, MimeMessage messageToSend,
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailFunc)
{
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
var (topicStr, htmlBodyStr) = isEn_US ? (configInfo.EmailTopic, configInfo.EmailHtmlContent) : (configInfo.EmailTopicCN, configInfo.EmailHtmlContentCN);
try
{
//每个场景修改主题 和body的逻辑不一样
(topicStr, htmlBodyStr) = emailFunc((topicStr, htmlBodyStr));
}
catch (Exception ex)
{
//"邮件模板内容有误,填充内容出现问题,请联系运维人员核查"
throw new BusinessValidationFailedException(I18n.T("CommonEmail_ConfigError"));
}
messageToSend.Subject = topicStr;
var builder = new BodyBuilder();
builder.HtmlBody = htmlBodyStr;
messageToSend.Body = builder.ToMessageBody();
return configInfo;
}
public static string ReplaceCompanyName(SystemEmailSendConfig _systemEmailConfig, string needDealtxt)
{
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
@ -74,12 +119,13 @@ public static class CommonEmailHelper
var enumValueList = inDto.DictionaryList.Select(x => x.EnumValue).ToList();
var dicList =await inDto.DictionaryRepository.Where(x => dictionaryCodelist.Contains(x.Parent.Code) && enumValueList.Contains(x.Code)).Select(x=>new DictionaryData() {
DictionaryCode=x.Parent.Code,
EnumValue=x.Code,
Value=x.Value,
ValueCN=x.ValueCN
var dicList = await inDto.DictionaryRepository.Where(x => dictionaryCodelist.Contains(x.Parent.Code) && enumValueList.Contains(x.Code)).Select(x => new DictionaryData()
{
DictionaryCode = x.Parent.Code,
EnumValue = x.Code,
Value = x.Value,
ValueCN = x.ValueCN
}).ToListAsync();
@ -88,7 +134,7 @@ public static class CommonEmailHelper
inDto.DictionaryList.ForEach(x =>
{
var dic = dicList.Where(y => y.EnumValue == x.EnumValue && y.DictionaryCode == x.DictionaryCode).FirstOrDefault();
var dic = dicList.Where(y => y.EnumValue == x.EnumValue && y.DictionaryCode.Equals(x.DictionaryCode,StringComparison.OrdinalIgnoreCase)).FirstOrDefault();
result.Add(dic == null ? string.Empty : (inDto.IsEn_US ? dic.Value : dic.ValueCN));
});

View File

@ -1,9 +1,12 @@
using AutoMapper;
using IRaCIS.Application.Interfaces;
using IRaCIS.Core.Application.Contracts.DTO;
using IRaCIS.Core.Application.MassTransit.Command;
using IRaCIS.Core.Domain.Share;
using MassTransit;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using System.Text;
@ -21,6 +24,9 @@ namespace IRaCIS.Core.Application.MassTransit.Consumer
private readonly IRepository<NoneDicomStudy> _noneDicomStudyRepository;
public IStringLocalizer _localizer { get; set; }
private readonly SystemEmailSendConfig _systemEmailConfig;
private readonly IRepository<Dictionary> _dictionaryRepository;
/// <summary>
/// 构造函数注入
/// </summary>
@ -28,7 +34,7 @@ namespace IRaCIS.Core.Application.MassTransit.Consumer
public ConsistencyCheckConsumer(IRepository<DicomStudy> studyRepository, IUserInfo userInfo,
IRepository<Subject> subjectRepository, IRepository<SubjectVisit> subjectVisitRepository,
IRepository<TrialSite> trialSiteRepository, IRepository<NoneDicomStudy> noneDicomStudyRepository,
IMapper mapper, IStringLocalizer localizer)
IMapper mapper, IStringLocalizer localizer, IOptionsMonitor<SystemEmailSendConfig> systemEmailConfig, IRepository<Dictionary> dictionaryRepository)
{
_noneDicomStudyRepository = noneDicomStudyRepository;
_studyRepository = studyRepository;
@ -38,6 +44,9 @@ namespace IRaCIS.Core.Application.MassTransit.Consumer
_trialSiteRepository = trialSiteRepository;
_mapper = mapper;
_localizer = localizer;
_systemEmailConfig = systemEmailConfig.CurrentValue;
_dictionaryRepository = dictionaryRepository;
}
@ -47,6 +56,9 @@ namespace IRaCIS.Core.Application.MassTransit.Consumer
var trialId = context.Message.TrialId;
//系统定义MRI 到底是MR 还是MRI
var mriModality = await _dictionaryRepository.Where(t => t.Parent.Code == "Modality").Where(t => t.Code == "MRI").Select(t => t.Value).FirstNotNullAsync();
//处理Excel大小写
context.Message.ETCList.ForEach(t => { t.Modality = t.Modality.ToUpper().Trim(); t.StudyDate = Convert.ToDateTime(t.StudyDate).ToString("yyyy-MM-dd"); t.SiteCode = t.SiteCode.ToUpper().Trim(); t.VisitName = t.VisitName.ToUpper().Trim(); t.SubjectCode = t.SubjectCode.ToUpper().Trim(); });
var etcList = context.Message.ETCList;
@ -115,6 +127,16 @@ namespace IRaCIS.Core.Application.MassTransit.Consumer
var etcVisitStudyList = etcList.Where(t => t.SubjectCode == sv.SubjectCode && t.SiteCode == sv.SiteCode && t.VisitName == sv.VisitName).ToList();
//以我们系统数据库为准 判断是MR 还是MRI, 把Excel 里的MR 或者MRI 处理成一致MR MRI 是一致的
foreach (var item in etcVisitStudyList)
{
if (item.Modality == "MR" || item.Modality == "MRI")
{
item.Modality = mriModality;
}
}
StringBuilder dialogMsg = new StringBuilder();
//---您好,根据本系统自动识别该受试者当前访视在IRC系统中已提交的影像检查情况如下
@ -186,7 +208,7 @@ namespace IRaCIS.Core.Application.MassTransit.Consumer
{
dialogMsg.AppendLine($"<br/>");
//---核对EDC数据完全一致, 审核通过
dialogMsg.AppendLine(_localizer["ConsistencyVerification_EDCA"]);
dialogMsg.AppendLine(_localizer["ConsistencyVerification_EDCA", _systemEmailConfig.SystemShortName]);
// dialogMsg.AppendLine(@$"<br>说明:为高效解决/处理以上全部质疑问题麻烦您准确核实实际影像检查情况。请注意影像日期与实际检查的日期可能会不一致部分检查如PET -CT)可能同时存在多种模态影像。准确核实后,请回复该访视正确的影像检查情况。");
@ -196,7 +218,7 @@ namespace IRaCIS.Core.Application.MassTransit.Consumer
dbSV.CheckChallengeState = CheckChanllengeTypeEnum.Closed;
//---核对EDC数据完全一致
dbSV.CheckResult = _localizer["ConsistencyVerification_EDCB"];
dbSV.CheckResult = _localizer["ConsistencyVerification_EDCB", _systemEmailConfig.SystemShortName];
//---自动核查通过
dbSV.ManualPassReason = _localizer["ConsistencyVerification_Auto"];
@ -224,7 +246,7 @@ namespace IRaCIS.Core.Application.MassTransit.Consumer
{
num++;
//影像检查(EDC 缺少) ConsistencyVerification_EdcL
dialogMsg.AppendLine($"<br/><div style='text-indent: 20px;'>{num}. {_localizer["ConsistencyVerification_EdcL", item.StudyDate, item.Modality]}</div>");
dialogMsg.AppendLine($"<br/><div style='text-indent: 20px;'>{num}. {_localizer["ConsistencyVerification_EdcL", item.StudyDate, item.Modality/*, _systemEmailConfig.SystemShortName*/]}</div>");
}
@ -232,15 +254,15 @@ namespace IRaCIS.Core.Application.MassTransit.Consumer
{
num++;
// 影像检查(IRC 缺少)
dialogMsg.AppendLine($"<br/><div style='text-indent: 20px;'>{num}. {_localizer["ConsistencyVerification_IrcLi", item.StudyDate, item.Modality]}</div>");
dialogMsg.AppendLine($"<br/><div style='text-indent: 20px;'>{num}. {_localizer["ConsistencyVerification_IrcLi", item.StudyDate, item.Modality, _systemEmailConfig.SystemShortName]}</div>");
}
dialogMsg.AppendLine($"<br/>");
dialogMsg.AppendLine(@$"<div>{_localizer["ConsistencyVerification_Desc"]}</div>");
dbSV.CheckResult = _localizer["ConsistencyVerification_Conf"] +
String.Join(" | ", dbExceptExcel.Select(t => $"{_localizer["ConsistencyVerification_EdcL", t.StudyDate, t.Modality]}")) + " | "
+ String.Join(" | ", excelExceptDB.Select(t => $"{_localizer["ConsistencyVerification_IrcLi", t.StudyDate, t.Modality]}"));
dbSV.CheckResult =
String.Join(" | ", dbExceptExcel.Select(t => $"{_localizer["ConsistencyVerification_EdcL", t.StudyDate, t.Modality/*, _systemEmailConfig.SystemShortName*/]}")) + " | "
+ String.Join(" | ", excelExceptDB.Select(t => $"{_localizer["ConsistencyVerification_IrcLi", t.StudyDate, t.Modality, _systemEmailConfig.SystemShortName]}"));
//新增一致性核查质疑记录

View File

@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace IRaCIS.Core.Application.MassTransit.Consumer.Dto
{
public class SendImageReuploadEmailInDto
{
public EmailNoticeConfig EmailNoticeConfig { get; set; }
public EmailBusinessScenario Scenario { get; set; }
public SubjectVisit SubjectVisit { get; set; }
public Guid TrialId { get; set; }
public List<UserTypeEnum> UserTypes { get; set; } = null;
public ImageBackStateEnum ImageBackStateEnum { get; set; } = ImageBackStateEnum.None;
public RequestBackStateEnum RequestBackStateEnum { get; set; } = RequestBackStateEnum.NotRequest;
public ReReadingApplyState ReReadingApplyState { get; set; } = ReReadingApplyState.Default;
}
}

View File

@ -0,0 +1,522 @@
using IRaCIS.Core.Application.Helper;
using IRaCIS.Core.Application.MassTransit.Command;
using IRaCIS.Core.Application.MassTransit.Consumer.Dto;
using IRaCIS.Core.Application.Service.Reading.Dto;
using IRaCIS.Core.Domain;
using IRaCIS.Core.Domain._DomainEvent;
using IRaCIS.Core.Domain.BaseModel;
using IRaCIS.Core.Domain.Models;
using IRaCIS.Core.Domain.Share;
using IRaCIS.Core.Infra.EFCore.Common;
using MassTransit;
using MassTransit.Mediator;
using MassTransit.Scheduling;
using Medallion.Threading;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.StaticFiles;
using Microsoft.Extensions.Options;
using MimeKit;
using Newtonsoft.Json;
using Panda.DynamicWebApi.Attributes;
using RestSharp;
using SkiaSharp;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace IRaCIS.Core.Application.MassTransit.Consumer;
/// <summary>
/// 影像重传和阅片人筛选相关邮件发送
/// </summary>
public class ImageConsumer(
IRepository<UserRole> _userRoleRepository,
IRepository<TrialUserRole> _trialUseRoleRepository,
IRepository<SubjectVisit> _subjectVisitRepository,
IRepository<Trial> _trialRepository,
IRepository<Enroll> _enrollRepository,
IRepository<VisitTask> _visitTaskRepository,
IRepository<ReadModule> _readModuleRepository,
IRepository<EmailNoticeUserType> _emailNoticeUserTypeRepository,
IRepository<TrialSite> _trialSiteRepository,
IRepository<Dictionary> _dictionaryRepository,
IRepository<EmailNoticeConfig> _emailNoticeConfigrepository,
IOptionsMonitor<SystemEmailSendConfig> systemEmailConfig) :
IConsumer<DirectApplyReupdloadEvent>,
IConsumer<CheckReuploadEvent>,
IConsumer<QCChanllengeReuploadEvent>,
IConsumer<HaveReadVisitTaskReReading>,
IConsumer<ReviewerPMApplyEvent>,
IConsumer<ReviewerSPMApprovedEvent>
{
private readonly SystemEmailSendConfig _systemEmailConfig = systemEmailConfig.CurrentValue;
/// <summary>
/// 处理直接申请流程的重传事件
/// </summary>
public async Task Consume(ConsumeContext<DirectApplyReupdloadEvent> context)
{
Console.WriteLine("发送【影像重传-直接申请流程】邮件!!!");
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
var subjectVisitId = context.Message.SubjectVisitId;
var subjectVisit = await _subjectVisitRepository.Where(x => x.Id == subjectVisitId).Include(x => x.Subject).FirstNotNullAsync();
var trialId = subjectVisit.TrialId;
List<UserTypeEnum> userTypes = null;
// 根据申请角色和状态确定邮件场景
EmailBusinessScenario scenario;
if (context.Message.ImageBackState == ImageBackStateEnum.None)
{
switch (context.Message.ApplyUserRole)
{
case ImageBackApplyEnum.IQCRequestBack:
scenario = EmailBusinessScenario.ImageQC_IQCApplyRe_Uploading; // 64
break;
case ImageBackApplyEnum.CRCRequestBack:
scenario = EmailBusinessScenario.ImageUploadCRCApplyRe_Uploading; // 65
break;
default:
scenario = EmailBusinessScenario.ImageQC_IQCApplyRe_Uploading; // 64
break;
}
}
else
{
scenario = EmailBusinessScenario.ImageReUpload_PMApprovalRe_Uploading; // 66
if (context.Message.ApplyUserRole == ImageBackApplyEnum.IQCRequestBack)
{
userTypes = new List<UserTypeEnum>() { UserTypeEnum.IQC, UserTypeEnum.ClinicalResearchCoordinator };
}
else if (context.Message.ApplyUserRole == ImageBackApplyEnum.CRCRequestBack)
{
userTypes = new List<UserTypeEnum>() { UserTypeEnum.ClinicalResearchCoordinator };
}
}
var emailConfig = _emailNoticeConfigrepository.Where(t => t.BusinessScenarioEnum == scenario && t.IsEnable && t.IsAutoSend).FirstOrDefault();
if (emailConfig != null)
{
var inDto = new SendImageReuploadEmailInDto
{
EmailNoticeConfig = emailConfig,
Scenario = scenario,
SubjectVisit = subjectVisit,
TrialId = trialId,
UserTypes = userTypes,
ImageBackStateEnum = context.Message.ImageBackState
};
await SendImageReuploadEmail(inDto);
}
}
/// <summary>
/// 处理一致性核查申请重传事件
/// </summary>
public async Task Consume(ConsumeContext<CheckReuploadEvent> context)
{
Console.WriteLine("发送【一致性核查-影像重传】邮件!!!");
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
var subjectVisitId = context.Message.SubjectVisitId;
var subjectVisit = await _subjectVisitRepository.Where(x => x.Id == subjectVisitId).Include(x => x.Subject).FirstNotNullAsync();
var trialId = subjectVisit.TrialId;
// 根据状态确定邮件场景
EmailBusinessScenario scenario;
switch (context.Message.RequestBackState)
{
case RequestBackStateEnum.CRC_RequestBack:
scenario = EmailBusinessScenario.DataReconciliation_CRCApplyRe_Uploading; // 62
break;
case RequestBackStateEnum.PM_AgressBack:
case RequestBackStateEnum.PM_NotAgree:
scenario = EmailBusinessScenario.DataReconciliation_PMApprovalRe_Uploading; // 63
break;
default:
return; // 其他状态不发送邮件
}
var emailConfig = _emailNoticeConfigrepository.Where(t => t.BusinessScenarioEnum == scenario && t.IsEnable && t.IsAutoSend).FirstOrDefault();
if (emailConfig != null)
{
var inDto = new SendImageReuploadEmailInDto
{
EmailNoticeConfig = emailConfig,
Scenario = scenario,
SubjectVisit = subjectVisit,
TrialId = trialId,
RequestBackStateEnum = context.Message.RequestBackState,
};
await SendImageReuploadEmail(inDto);
}
}
/// <summary>
/// 处理质疑CRC申请重传事件
/// </summary>
public async Task Consume(ConsumeContext<QCChanllengeReuploadEvent> context)
{
Console.WriteLine("发送【影像质疑-影像重传】邮件!!!");
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
var subjectVisitId = context.Message.SubjectVisitId;
var subjectVisit = await _subjectVisitRepository.Where(x => x.Id == subjectVisitId).Include(x => x.Subject).FirstNotNullAsync();
var trialId = subjectVisit.TrialId;
// 根据状态确定邮件场景
EmailBusinessScenario scenario;
switch (context.Message.ReuploadEnum)
{
case QCChanllengeReuploadEnum.CRCRequestReupload:
scenario = EmailBusinessScenario.ImageQueryCRCApplyRe_Uploading; // 60
break;
case QCChanllengeReuploadEnum.QCAgreeUpload:
scenario = EmailBusinessScenario.ImageQuery_IQCApprovalRe_Uploading; // 61
break;
default:
return; // 其他状态不发送邮件
}
var emailConfig = _emailNoticeConfigrepository.Where(t => t.BusinessScenarioEnum == scenario && t.IsEnable && t.IsAutoSend).FirstOrDefault();
if (emailConfig != null)
{
var inDto = new SendImageReuploadEmailInDto
{
EmailNoticeConfig = emailConfig,
Scenario = scenario,
SubjectVisit = subjectVisit,
TrialId = trialId
};
await SendImageReuploadEmail(inDto);
}
}
/// <summary>
/// 处理已阅任务重阅申请事件
/// </summary>
public async Task Consume(ConsumeContext<HaveReadVisitTaskReReading> context)
{
Console.WriteLine("发送【重阅-影像重传】邮件!!!");
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
var visitTaskId = context.Message.VisitTaskId;
// 获取VisitTask关联的SubjectVisit
var visitTask = await _visitTaskRepository.Where(x => x.Id == visitTaskId).Include(x => x.Subject).Include(x => x.SourceSubjectVisit).FirstNotNullAsync();
var subjectVisit = new SubjectVisit() { };
if (visitTask.SourceSubjectVisitId != null)
{
subjectVisit = visitTask.SourceSubjectVisit;
}
else if (visitTask.SouceReadModuleId != null)
{
var readModule = await _readModuleRepository.Where(x => x.Id == visitTask.SouceReadModuleId).Include(x => x.SubjectVisit).FirstOrDefaultAsync();
subjectVisit = readModule.SubjectVisit;
}
var trialId = subjectVisit.TrialId;
// 根据状态确定邮件场景
EmailBusinessScenario scenario;
switch (context.Message.ReReadingApplyState)
{
case ReReadingApplyState.TrialGroupHaveApplyed:
scenario = EmailBusinessScenario.ReadTracking_PMApplyRe_Reading; // 67
break;
case ReReadingApplyState.Agree:
case ReReadingApplyState.Reject:
scenario = EmailBusinessScenario.Re_ReadApproval_SPMCPMApprovalRe_Reading; // 68
break;
default:
return; // 其他状态不发送邮件
}
var emailConfig = _emailNoticeConfigrepository.Where(t => t.BusinessScenarioEnum == scenario && t.IsEnable && t.IsAutoSend).FirstOrDefault();
if (emailConfig != null)
{
var inDto = new SendImageReuploadEmailInDto
{
EmailNoticeConfig = emailConfig,
Scenario = scenario,
SubjectVisit = subjectVisit,
TrialId = trialId,
ReReadingApplyState= context.Message.ReReadingApplyState,
};
await SendImageReuploadEmail(inDto);
}
}
/// <summary>
/// 发送影像重传相关邮件的通用方法
/// </summary>
private async Task SendImageReuploadEmail(SendImageReuploadEmailInDto inDto)
{
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
inDto.SubjectVisit = await _subjectVisitRepository.Where(x => x.Id == inDto.SubjectVisit.Id).Include(x => x.Subject).FirstNotNullAsync();
// 获取项目信息
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == inDto.TrialId);
// 根据不同场景获取不同角色的用户
var trialUser = await _trialUseRoleRepository.Where(x => x.TrialId == inDto.TrialId && !x.TrialUser.IsDeleted).Include(x => x.UserRole).Select(x => x.UserRole).ToListAsync();
// 根据场景确定收件人
List<UserRole> toUserList = new List<UserRole>();
List<UserRole> ccUserList = new List<UserRole>();
var emailNoticeUserList = await _emailNoticeUserTypeRepository.Where(x => x.EmailNoticeConfigId == inDto.EmailNoticeConfig.Id).ToListAsync();
var userTypeEnum = emailNoticeUserList.Select(x => x.UserType).ToList();
if (inDto.UserTypes != null)
{
userTypeEnum = inDto.UserTypes;
}
var crcUserList = new List<UserRole>() { };
if (userTypeEnum.Contains(UserTypeEnum.ClinicalResearchCoordinator))
{
crcUserList = await _trialSiteRepository.Where(x => x.Id == inDto.SubjectVisit.TrialSiteId).SelectMany(x => x.CRCUserList.Select(y => y.UserRole)).ToListAsync();
}
if (inDto.UserTypes == null)
{
var toList = emailNoticeUserList.Where(x => x.EmailUserType == EmailUserType.To && x.UserType != UserTypeEnum.ClinicalResearchCoordinator).Select(x => x.UserType).ToList();
toUserList = trialUser.Where(x => toList.Contains(x.UserTypeEnum)).ToList();
if (emailNoticeUserList.Any(x => x.EmailUserType == EmailUserType.To && x.UserType == UserTypeEnum.ClinicalResearchCoordinator))
{
toUserList.AddRange(crcUserList);
}
}
else
{
var toList = inDto.UserTypes.Where(x => x != UserTypeEnum.ClinicalResearchCoordinator).ToList();
toUserList = trialUser.Where(x => toList.Contains(x.UserTypeEnum)).ToList();
if (inDto.UserTypes.Any(x => x == UserTypeEnum.ClinicalResearchCoordinator))
{
toUserList.AddRange(crcUserList);
}
}
var ccList = emailNoticeUserList.Where(x => x.EmailUserType == EmailUserType.Copy && x.UserType != UserTypeEnum.ClinicalResearchCoordinator).Select(x => x.UserType).ToList();
ccUserList = trialUser.Where(x => ccList.Contains(x.UserTypeEnum)).ToList();
if (emailNoticeUserList.Any(x => x.EmailUserType == EmailUserType.Copy && x.UserType == UserTypeEnum.ClinicalResearchCoordinator))
{
ccUserList.AddRange(crcUserList);
}
// 如果没有收件人,则不发送邮件
if (toUserList.Count == 0)
{
return;
}
List<DictionaryDto> dictionaryDtos = new List<DictionaryDto>() {
new DictionaryDto (){DictionaryCode= "YesOrNo",EnumValue=inDto.SubjectVisit.IsUrgent.ToString().ToLower(), }, //是否加急
};
switch (inDto.Scenario)
{
case EmailBusinessScenario.Re_ReadApproval_SPMCPMApprovalRe_Reading:
dictionaryDtos.Add(new DictionaryDto() { DictionaryCode = "ReReadingApplyState", EnumValue = inDto.ReReadingApplyState.GetEnumInt(), });
break;
case EmailBusinessScenario.DataReconciliation_PMApprovalRe_Uploading:
dictionaryDtos.Add(new DictionaryDto() { DictionaryCode = "RequestBackState", EnumValue = inDto.RequestBackStateEnum.GetEnumInt(), });
break;
default:
dictionaryDtos.Add(new DictionaryDto() { DictionaryCode = "ImageBackStateEnum", EnumValue = inDto.ImageBackStateEnum.GetEnumInt(), });//审批结果
break;
}
var dictionValue = await CommonEmailHelper.TranslationDictionary(new TranslationDictionaryDto()
{
DictionaryRepository = _dictionaryRepository,
IsEn_US = isEn_US,
DictionaryList = dictionaryDtos
});
foreach (var userinfo in toUserList)
{
var messageToSend = new MimeMessage();
// 发件地址
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
// 添加抄送
foreach (var ccUser in ccUserList)
{
messageToSend.Cc.Add(new MailboxAddress(String.Empty, ccUser.EMail));
}
// 格式化邮件内容
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
{
var subjectCode = inDto.SubjectVisit.Subject.Code;
var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo, subjectCode, inDto.SubjectVisit.VisitName);
var htmlBodyStr = string.Format(
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
userinfo.FullName, // 用户名 {0}
trialInfo.ExperimentName, // 项目 {1}
subjectCode, // 受试者 {2}
inDto.SubjectVisit.VisitName, // 访视 {3}
dictionValue[0], // 是否加急 {4}
dictionValue[1], // 审批结果 {5}
_systemEmailConfig.SiteUrl // 链接 {6}
);
return (topicStr, htmlBodyStr);
};
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(inDto.EmailNoticeConfig, messageToSend, emailConfigFunc);
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
}
}
/// <summary>
/// 处理PM申请阅片人筛选事件
/// </summary>
public async Task Consume(ConsumeContext<ReviewerPMApplyEvent> context)
{
Console.WriteLine("发送【阅片人筛选-PM申请审批】邮件");
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
// 获取项目信息
var enrollIdList = context.Message.EnrollIdList;
if (enrollIdList == null || !enrollIdList.Any())
{
return;
}
// 获取第一个Enroll的TrialId
var enrollId = enrollIdList.First();
var enroll = await _enrollRepository.Where(x => x.Id == enrollId).FirstNotNullAsync();
var trialId = enroll.TrialId;
// 设置邮件场景
var scenario = EmailBusinessScenario.ReviewerSelection_PMApplyApproving; // 69
var emailConfig = _emailNoticeConfigrepository.Where(t => t.BusinessScenarioEnum == scenario && t.IsEnable && t.IsAutoSend).FirstOrDefault();
if (emailConfig != null)
{
await SendReviewerSelectionEmail(emailConfig, trialId, enrollIdList);
}
}
/// <summary>
/// 处理SPM审批阅片人筛选事件
/// </summary>
public async Task Consume(ConsumeContext<ReviewerSPMApprovedEvent> context)
{
Console.WriteLine("发送【阅片人筛选-SPM审批】邮件");
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
// 获取项目信息
var enrollIdList = context.Message.EnrollIdList;
if (enrollIdList == null || !enrollIdList.Any())
{
return;
}
// 获取第一个Enroll的TrialId
var enrollId = enrollIdList.First();
var enroll = await _enrollRepository.Where(x => x.Id == enrollId).FirstNotNullAsync();
var trialId = enroll.TrialId;
// 设置邮件场景
var scenario = EmailBusinessScenario.ReviewerSelection_SPMCPMApproval; // 70
var emailConfig = _emailNoticeConfigrepository.Where(t => t.BusinessScenarioEnum == scenario && t.IsEnable && t.IsAutoSend).FirstOrDefault();
if (emailConfig != null)
{
await SendReviewerSelectionEmail(emailConfig, trialId, enrollIdList);
}
}
/// <summary>
/// 发送阅片人筛选相关邮件的通用方法
/// </summary>
private async Task SendReviewerSelectionEmail(EmailNoticeConfig emailNoticeConfig, Guid trialId, List<Guid> enrollIdList)
{
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
// 获取项目信息
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == trialId);
// 根据不同场景获取不同角色的用户
var trialUser = await _trialUseRoleRepository.Where(x => x.TrialId == trialId && !x.TrialUser.IsDeleted).Include(x => x.UserRole).Select(x => x.UserRole).ToListAsync();
// 根据场景确定收件人
List<UserRole> toUserList = new List<UserRole>();
List<UserRole> ccUserList = new List<UserRole>();
var emailNoticeUserList = await _emailNoticeUserTypeRepository.Where(x => x.EmailNoticeConfigId == emailNoticeConfig.Id).ToListAsync();
var toList = emailNoticeUserList.Where(x => x.EmailUserType == EmailUserType.To).Select(x => x.UserType).ToList();
toUserList = trialUser.Where(x => toList.Contains(x.UserTypeEnum)).ToList();
var ccList = emailNoticeUserList.Where(x => x.EmailUserType == EmailUserType.Copy && x.UserType != UserTypeEnum.ClinicalResearchCoordinator).Select(x => x.UserType).ToList();
ccUserList = trialUser.Where(x => ccList.Contains(x.UserTypeEnum)).ToList();
// 如果没有收件人,则不发送邮件
if (toUserList.Count == 0)
{
return;
}
foreach (var userinfo in toUserList)
{
var messageToSend = new MimeMessage();
// 发件地址
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
// 添加抄送
foreach (var ccUser in ccUserList)
{
messageToSend.Cc.Add(new MailboxAddress(String.Empty, ccUser.EMail));
}
// 格式化邮件内容
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
{
var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo);
var htmlBodyStr = string.Format(
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
userinfo.FullName, // 用户名 {0}
trialInfo.ExperimentName, // 项目 {1}
"", // 受试者 {2} - 阅片人筛选不涉及受试者
"", // 访视 {3} - 阅片人筛选不涉及访视
"", // 是否加急 {4}
"", // 预留 {5}
_systemEmailConfig.SiteUrl // 链接 {6}
);
return (topicStr, htmlBodyStr);
};
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(emailNoticeConfig, messageToSend, emailConfigFunc);
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
}
}
}

View File

@ -29,7 +29,7 @@ public class UrgentMedicalReviewAddedEventConsumer(
IRepository<ReadingQuestionCriterionTrial> _readingQuestionCriterionTrialRepository,
IRepository<VisitTask> _visitTaskRepository,
IRepository<Dictionary> _dictionaryRepository,
IRepository<EmailNoticeConfig> _emailNoticeConfigrepository,
IRepository<TrialEmailNoticeConfig> _trialEmailNoticeConfigrepository,
IOptionsMonitor<SystemEmailSendConfig> systemEmailConfig
) : IConsumer<UrgentMedicalReviewAddedEvent>
{
@ -48,52 +48,62 @@ public class UrgentMedicalReviewAddedEventConsumer(
var medicalReviewId = context.Message.MedicalReviewId;
var medicalReview = await _taskMedicalReviewRepository.Where(x => x.Id == medicalReviewId).Include(x => x.VisitTask).Include(x=>x.MedicalManagerUser).FirstOrDefaultAsync();
var medicalReview = await _taskMedicalReviewRepository.Where(x => x.Id == medicalReviewId).Include(x => x.VisitTask).Include(x => x.MedicalManagerUser).FirstOrDefaultAsync();
if (medicalReview.MedicalManagerUserId == null)
{
Console.WriteLine("MIMId为空 ID"+ medicalReviewId);
Console.WriteLine("MIMId为空 ID" + medicalReviewId);
return;
}
var criterion = await _readingQuestionCriterionTrialRepository.FirstOrDefaultAsync(x => x.Id == medicalReview.VisitTask.TrialReadingCriterionId);
var userinfo = await _userRoleRepository.Where(x => x.Id == medicalReview.MedicalManagerUserId).FirstOrDefaultAsync();
var taskInfo = await _visitTaskRepository.Where(x => x.Id == medicalReview.VisitTaskId).Include(x=>x.SourceSubjectVisit).Include(x => x.ReadModule).Include(x => x.Subject).FirstNotNullAsync();
var trialId = medicalReview.TrialId;
var scenario = context.Message.IsPD ? EmailBusinessScenario.PDVerification_MedicalQC : EmailBusinessScenario.EligibilityVerification_MedicalQC;
var visitid = taskInfo.SourceSubjectVisit == null ? taskInfo.ReadModule.SubjectVisitId : taskInfo.SourceSubjectVisitId;
var trialEmailConfig = _trialEmailNoticeConfigrepository.Where(t => t.TrialId == trialId && t.BusinessScenarioEnum == scenario && t.IsAutoSend && t.IsEnable).FirstOrDefault();
var subjectVisit = await _subjectVisitRepository.Where(x => x.Id == visitid).FirstOrDefaultAsync();
var messageToSend = new MimeMessage();
//发件地址
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == medicalReview.TrialId);
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
var dictionValue = await CommonEmailHelper.TranslationDictionary(new TranslationDictionaryDto()
if (trialEmailConfig != null)
{
DictionaryRepository = _dictionaryRepository,
IsEn_US = isEn_US,
var criterion = await _readingQuestionCriterionTrialRepository.FirstOrDefaultAsync(x => x.Id == medicalReview.VisitTask.TrialReadingCriterionId);
var userinfo = await _userRoleRepository.Where(x => x.Id == medicalReview.MedicalManagerUserId).FirstOrDefaultAsync();
var taskInfo = await _visitTaskRepository.Where(x => x.Id == medicalReview.VisitTaskId).Include(x => x.SourceSubjectVisit).Include(x => x.ReadModule).Include(x => x.Subject).FirstNotNullAsync();
DictionaryList = new List<DictionaryDto>()
{
var visitid = taskInfo.SourceSubjectVisit == null ? taskInfo.ReadModule.SubjectVisitId : taskInfo.SourceSubjectVisitId;
var subjectVisit = await _subjectVisitRepository.Where(x => x.Id == visitid).FirstOrDefaultAsync();
var messageToSend = new MimeMessage();
//发件地址
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == medicalReview.TrialId);
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
var dictionValue = await CommonEmailHelper.TranslationDictionary(new TranslationDictionaryDto()
{
DictionaryRepository = _dictionaryRepository,
IsEn_US = isEn_US,
DictionaryList = new List<DictionaryDto>()
{
new DictionaryDto (){DictionaryCode= "ReadingCategory",EnumValue=taskInfo.ReadingCategory.GetEnumInt(), },
new DictionaryDto (){DictionaryCode= "MedicalReviewDoctorUserIdea",EnumValue=medicalReview.DoctorUserIdeaEnum.GetEnumInt(), },
new DictionaryDto (){DictionaryCode= "MedicalReviewAuditState",EnumValue=medicalReview.AuditState.GetEnumInt(), },
new DictionaryDto (){DictionaryCode= "YesOrNo",EnumValue=taskInfo.IsUrgent.ToString().ToLower(), }
}
}) ;
});
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
{
@ -107,7 +117,7 @@ public class UrgentMedicalReviewAddedEventConsumer(
subjectName, // 受试者 {3}
taskInfo.TaskBlindName, // 访视 {4}
dictionValue[0], // 任务类型 {5}
//dictionValue[1], // 阅片人是否同意 {6}
//dictionValue[1], // 阅片人是否同意 {6}
dictionValue[2], // 审核状态 {6}
dictionValue[3], // 是否加急 {7}
_systemEmailConfig.SiteUrl // 链接 {8}
@ -116,13 +126,12 @@ public class UrgentMedicalReviewAddedEventConsumer(
return (topicStr, htmlBodyStr);
};
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(_emailNoticeConfigrepository,
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc);
context.Message.IsPD ? EmailBusinessScenario.PDVerification_MedicalQC : EmailBusinessScenario.EligibilityVerification_MedicalQC,
messageToSend, emailConfigFunc);
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
}
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
}
}
@ -135,7 +144,7 @@ public class UrgentIRRepliedMedicalReviewConsumer(
IRepository<ReadingQuestionCriterionTrial> _readingQuestionCriterionTrialRepository,
IRepository<VisitTask> _visitTaskRepository,
IRepository<Dictionary> _dictionaryRepository,
IRepository<EmailNoticeConfig> _emailNoticeConfigrepository,
IRepository<TrialEmailNoticeConfig> _trialEmailNoticeConfigrepository,
IOptionsMonitor<SystemEmailSendConfig> systemEmailConfig) : IConsumer<UrgentIRRepliedMedicalReview>
{
private readonly SystemEmailSendConfig _systemEmailConfig = systemEmailConfig.CurrentValue;
@ -151,79 +160,85 @@ public class UrgentIRRepliedMedicalReviewConsumer(
var medicalReview = await _taskMedicalReviewRepository.Where(x => x.Id == medicalReviewId).Include(x => x.VisitTask).Include(x => x.MedicalManagerUser).FirstOrDefaultAsync();
if (medicalReview.MedicalManagerUserId == null)
{
Console.WriteLine("MIMId为空 ID" + medicalReviewId);
return;
}
var criterion = await _readingQuestionCriterionTrialRepository.FirstOrDefaultAsync(x => x.Id == medicalReview.VisitTask.TrialReadingCriterionId);
var userinfo = await _userRoleRepository.Where(x => x.Id == medicalReview.MedicalManagerUserId).FirstOrDefaultAsync();
var trialId = medicalReview.TrialId;
var taskInfo = await _visitTaskRepository.Where(x => x.Id == medicalReview.VisitTaskId).Include(x => x.SourceSubjectVisit).Include(x => x.ReadModule).Include(x => x.Subject).FirstNotNullAsync();
var scenario = context.Message.IsPD ? EmailBusinessScenario.PDVerification_ExpeditedMedicalQCResponse : EmailBusinessScenario.EligibilityVerification_ExpeditedMedicalQCResponse;
var trialEmailConfig = _trialEmailNoticeConfigrepository.Where(t => t.TrialId == trialId && t.BusinessScenarioEnum == scenario && t.IsAutoSend && t.IsEnable).FirstOrDefault();
var visitid = taskInfo.SourceSubjectVisit == null ? taskInfo.ReadModule.SubjectVisitId : taskInfo.SourceSubjectVisitId;
var subjectVisit = await _subjectVisitRepository.Where(x => x.Id == visitid).FirstOrDefaultAsync();
var messageToSend = new MimeMessage();
//发件地址
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == medicalReview.TrialId);
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
var dictionValue = await CommonEmailHelper.TranslationDictionary(new TranslationDictionaryDto()
if (trialEmailConfig != null)
{
DictionaryRepository = _dictionaryRepository,
IsEn_US = isEn_US,
var criterion = await _readingQuestionCriterionTrialRepository.FirstOrDefaultAsync(x => x.Id == medicalReview.VisitTask.TrialReadingCriterionId);
var userinfo = await _userRoleRepository.Where(x => x.Id == medicalReview.MedicalManagerUserId).FirstOrDefaultAsync();
var taskInfo = await _visitTaskRepository.Where(x => x.Id == medicalReview.VisitTaskId).Include(x => x.SourceSubjectVisit).Include(x => x.ReadModule).Include(x => x.Subject).FirstNotNullAsync();
DictionaryList = new List<DictionaryDto>()
var visitid = taskInfo.SourceSubjectVisit == null ? taskInfo.ReadModule.SubjectVisitId : taskInfo.SourceSubjectVisitId;
var subjectVisit = await _subjectVisitRepository.Where(x => x.Id == visitid).FirstOrDefaultAsync();
var messageToSend = new MimeMessage();
//发件地址
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == medicalReview.TrialId);
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
var dictionValue = await CommonEmailHelper.TranslationDictionary(new TranslationDictionaryDto()
{
DictionaryRepository = _dictionaryRepository,
IsEn_US = isEn_US,
DictionaryList = new List<DictionaryDto>()
{
new DictionaryDto (){DictionaryCode= "ReadingCategory",EnumValue=taskInfo.ReadingCategory.GetEnumInt(), },
new DictionaryDto (){DictionaryCode= "MedicalReviewDoctorUserIdea",EnumValue=medicalReview.DoctorUserIdeaEnum.GetEnumInt(), },
new DictionaryDto (){DictionaryCode= "MedicalReviewAuditState",EnumValue=medicalReview.AuditState.GetEnumInt(), },
new DictionaryDto (){DictionaryCode= "YesOrNo",EnumValue=taskInfo.IsUrgent.ToString().ToLower(), }
}
});
});
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
{
var subjectCode = taskInfo.BlindSubjectCode.IsNullOrEmpty() ? taskInfo.Subject.Code : taskInfo.BlindSubjectCode;
var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo, subjectCode, taskInfo.TaskBlindName);
var htmlBodyStr = string.Format(
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
userinfo.FullName, // 用户名 {0}
trialInfo.ExperimentName, // 项目 {1}
criterion.CriterionName, // 阅片标准 {2}
subjectCode, // 受试者 {3}
taskInfo.TaskBlindName, // 访视 {4}
dictionValue[0], // 任务类型 {5}
dictionValue[1], // 阅片人是否同意 {6}
dictionValue[2], // 审核状态 {7}
dictionValue[3], // 是否加急 {8}
_systemEmailConfig.SiteUrl // 链接 {9}
);
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
{
var subjectCode = taskInfo.BlindSubjectCode.IsNullOrEmpty() ? taskInfo.Subject.Code : taskInfo.BlindSubjectCode;
var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo, subjectCode, taskInfo.TaskBlindName);
var htmlBodyStr = string.Format(
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
userinfo.FullName, // 用户名 {0}
trialInfo.ExperimentName, // 项目 {1}
criterion.CriterionName, // 阅片标准 {2}
subjectCode, // 受试者 {3}
taskInfo.TaskBlindName, // 访视 {4}
dictionValue[0], // 任务类型 {5}
dictionValue[1], // 阅片人是否同意 {6}
dictionValue[2], // 审核状态 {7}
dictionValue[3], // 是否加急 {8}
_systemEmailConfig.SiteUrl // 链接 {9}
);
return (topicStr, htmlBodyStr);
};
return (topicStr, htmlBodyStr);
};
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc);
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(_emailNoticeConfigrepository,
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
}
context.Message.IsPD ? EmailBusinessScenario.PDVerification_ExpeditedMedicalQCResponse : EmailBusinessScenario.EligibilityVerification_ExpeditedMedicalQCResponse,
messageToSend, emailConfigFunc);
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
}
}
@ -238,7 +253,7 @@ public class UrgentMIMRepliedMedicalReviewConsumer(
IRepository<ReadingQuestionCriterionTrial> _readingQuestionCriterionTrialRepository,
IRepository<VisitTask> _visitTaskRepository,
IRepository<Dictionary> _dictionaryRepository,
IRepository<EmailNoticeConfig> _emailNoticeConfigrepository,
IRepository<TrialEmailNoticeConfig> _trialEmailNoticeConfigrepository,
IOptionsMonitor<SystemEmailSendConfig> systemEmailConfig) : IConsumer<UrgentMIMRepliedMedicalReview>
{
@ -254,91 +269,93 @@ public class UrgentMIMRepliedMedicalReviewConsumer(
Console.WriteLine("发送(022) 【加急医学反馈】邮件!!!");
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
var medicalReviewId = context.Message.MedicalReviewId;
var medicalReview = await _taskMedicalReviewRepository.Where(x => x.Id == medicalReviewId).Include(x => x.VisitTask).Include(x => x.MedicalManagerUser).FirstOrDefaultAsync();
if (medicalReview.MedicalManagerUserId == null)
{
Console.WriteLine("MIMId为空 ID" + medicalReviewId);
return;
}
var criterion = await _readingQuestionCriterionTrialRepository.FirstOrDefaultAsync(x => x.Id == medicalReview.VisitTask.TrialReadingCriterionId);
var taskInfo = await _visitTaskRepository.Where(x => x.Id == medicalReview.VisitTaskId).Include(x => x.SourceSubjectVisit).Include(x => x.ReadModule).Include(x => x.Subject).FirstNotNullAsync();
var userinfo = await _userRoleRepository.Where(x => x.Id == taskInfo.DoctorUserId).FirstOrDefaultAsync();
var trialId = medicalReview.TrialId;
var scenario = EmailBusinessScenario.ExpeditedMedicalQCToIR;
var trialEmailConfig = _trialEmailNoticeConfigrepository.Where(t => t.TrialId == trialId && t.BusinessScenarioEnum == scenario && t.IsAutoSend && t.IsEnable).FirstOrDefault();
var visitid = taskInfo.SourceSubjectVisit == null ? taskInfo.ReadModule.SubjectVisitId : taskInfo.SourceSubjectVisitId;
var subjectVisit = await _subjectVisitRepository.Where(x => x.Id == visitid).FirstOrDefaultAsync();
var messageToSend = new MimeMessage();
//发件地址
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == medicalReview.TrialId);
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
var dictionValue = await CommonEmailHelper.TranslationDictionary(new TranslationDictionaryDto()
if (trialEmailConfig != null)
{
DictionaryRepository = _dictionaryRepository,
IsEn_US = isEn_US,
var criterion = await _readingQuestionCriterionTrialRepository.FirstOrDefaultAsync(x => x.Id == medicalReview.VisitTask.TrialReadingCriterionId);
var taskInfo = await _visitTaskRepository.Where(x => x.Id == medicalReview.VisitTaskId).Include(x => x.SourceSubjectVisit).Include(x => x.ReadModule).Include(x => x.Subject).FirstNotNullAsync();
var userinfo = await _userRoleRepository.Where(x => x.Id == taskInfo.DoctorUserId).FirstOrDefaultAsync();
DictionaryList = new List<DictionaryDto>()
var visitid = taskInfo.SourceSubjectVisit == null ? taskInfo.ReadModule.SubjectVisitId : taskInfo.SourceSubjectVisitId;
var subjectVisit = await _subjectVisitRepository.Where(x => x.Id == visitid).FirstOrDefaultAsync();
var messageToSend = new MimeMessage();
//发件地址
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == medicalReview.TrialId);
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
var dictionValue = await CommonEmailHelper.TranslationDictionary(new TranslationDictionaryDto()
{
DictionaryRepository = _dictionaryRepository,
IsEn_US = isEn_US,
DictionaryList = new List<DictionaryDto>()
{
new DictionaryDto (){DictionaryCode= "ReadingCategory",EnumValue=taskInfo.ReadingCategory.GetEnumInt(), },
new DictionaryDto (){DictionaryCode= "IsPass",EnumValue=medicalReview.IsHaveQuestion.ToString().ToLower(), },// 审核结论
new DictionaryDto (){DictionaryCode= "AuditAdvice",EnumValue=medicalReview.AuditAdviceEnum.GetEnumInt(), },// 审核建议
new DictionaryDto (){DictionaryCode= "YesOrNo",EnumValue=taskInfo.IsUrgent.ToString().ToLower(), }, //是否加急
new DictionaryDto (){DictionaryCode= "YesOrNo",EnumValue=taskInfo.IsUrgent.ToString().ToLower(), }, //是否加急
}
});
});
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
{
var subjectCode = taskInfo.BlindSubjectCode.IsNullOrEmpty() ? taskInfo.Subject.Code : taskInfo.BlindSubjectCode;
var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo, subjectCode, taskInfo.TaskBlindName);
var htmlBodyStr = string.Format(
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
userinfo.FullName, // 用户名 {0}
trialInfo.ExperimentName, // 项目 {1}
subjectCode, // 受试者 {2}
taskInfo.TaskBlindName, // 访视 {3}
criterion.CriterionName, // 阅片标准 {4}
dictionValue[0], // 任务类型 {5}
dictionValue[1], // 审核结论 {6}
dictionValue[2], // 医学审核建议 {7}
dictionValue[3], // 是否加急 {8}
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
{
var subjectCode = taskInfo.BlindSubjectCode.IsNullOrEmpty() ? taskInfo.Subject.Code : taskInfo.BlindSubjectCode;
var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo, subjectCode, taskInfo.TaskBlindName);
var htmlBodyStr = string.Format(
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
userinfo.FullName, // 用户名 {0}
trialInfo.ExperimentName, // 项目 {1}
subjectCode, // 受试者 {2}
taskInfo.TaskBlindName, // 访视 {3}
criterion.CriterionName, // 阅片标准 {4}
dictionValue[0], // 任务类型 {5}
dictionValue[1], // 审核结论 {6}
dictionValue[2], // 医学审核建议 {7}
dictionValue[3], // 是否加急 {8}
_systemEmailConfig.SiteUrl // 链接 {9}
);
_systemEmailConfig.SiteUrl // 链接 {9}
);
return (topicStr, htmlBodyStr);
};
return (topicStr, htmlBodyStr);
};
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(_emailNoticeConfigrepository,
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc);
EmailBusinessScenario.ExpeditedMedicalQCToIR,
messageToSend, emailConfigFunc);
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
}
}
}
@ -354,7 +371,7 @@ public class UrgentIRApplyedReReadingConsumer(
IRepository<ReadingQuestionCriterionTrial> _readingQuestionCriterionTrialRepository,
IRepository<VisitTask> _visitTaskRepository,
IRepository<Dictionary> _dictionaryRepository,
IRepository<EmailNoticeConfig> _emailNoticeConfigrepository,
IRepository<TrialEmailNoticeConfig> _trialEmailNoticeConfigrepository,
IOptionsMonitor<SystemEmailSendConfig> systemEmailConfig) : IConsumer<UrgentApplyedReReading>
{
@ -367,82 +384,90 @@ public class UrgentIRApplyedReReadingConsumer(
var visitTaskId = context.Message.VisitTaskId;
var taskInfo = await _visitTaskRepository.Where(x => x.Id == visitTaskId).Include(x => x.SourceSubjectVisit).Include(x => x.ReadModule).Include(x => x.Subject).FirstNotNullAsync();
var criterion = await _readingQuestionCriterionTrialRepository.FirstOrDefaultAsync(x => x.Id == taskInfo.TrialReadingCriterionId);
var doctorInfo = await _userRoleRepository.Where(x => x.Id == taskInfo.DoctorUserId).FirstOrDefaultAsync();
var trialId=taskInfo.TrialId;
var trialUser = await _trialUseRoleRepository.Where(x => x.TrialId == taskInfo.TrialId).Include(x => x.UserRole).Select(x=>x.UserRole).ToListAsync();
var scenario = context.Message.ReReadingApplyState == ReReadingApplyState.TrialGroupHaveApplyed ? EmailBusinessScenario.ReReadFromPMApproval : EmailBusinessScenario.ReReadFromIRApproval;
var userinfoList = trialUser.Where(x => x.UserTypeEnum == UserTypeEnum.ProjectManager||x.UserTypeEnum==UserTypeEnum.APM).ToList();
if (context.Message.ReReadingApplyState == ReReadingApplyState.TrialGroupHaveApplyed)
var trialEmailConfig = _trialEmailNoticeConfigrepository.Where(t => t.TrialId == trialId && t.BusinessScenarioEnum == scenario && t.IsAutoSend && t.IsEnable).FirstOrDefault();
if (trialEmailConfig != null)
{
userinfoList = trialUser.Where(x => x.UserTypeEnum == UserTypeEnum.CPM || x.UserTypeEnum == UserTypeEnum.SPM).ToList();
}
var visitid = taskInfo.SourceSubjectVisit == null ? taskInfo.ReadModule.SubjectVisitId : taskInfo.SourceSubjectVisitId;
var criterion = await _readingQuestionCriterionTrialRepository.FirstOrDefaultAsync(x => x.Id == taskInfo.TrialReadingCriterionId);
var subjectVisit = await _subjectVisitRepository.Where(x => x.Id == visitid).FirstOrDefaultAsync();
var doctorInfo = await _userRoleRepository.Where(x => x.Id == taskInfo.DoctorUserId).FirstOrDefaultAsync();
var trialUserList = await _trialUseRoleRepository.Where(x => x.TrialId == taskInfo.TrialId && x.TrialUser.IsDeleted==false).Include(x => x.UserRole).Select(x => x.UserRole).ToListAsync();
foreach (var userinfo in userinfoList)
{
var messageToSend = new MimeMessage();
//发件地址
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == taskInfo.TrialId);
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
var dictionValue = await CommonEmailHelper.TranslationDictionary(new TranslationDictionaryDto()
var userinfoList = trialUserList.Where(x => x.UserTypeEnum == UserTypeEnum.ProjectManager || x.UserTypeEnum == UserTypeEnum.APM).ToList();
if (context.Message.ReReadingApplyState == ReReadingApplyState.TrialGroupHaveApplyed)
{
userinfoList = trialUserList.Where(x => x.UserTypeEnum == UserTypeEnum.CPM || x.UserTypeEnum == UserTypeEnum.SPM).ToList();
}
DictionaryRepository = _dictionaryRepository,
IsEn_US = isEn_US,
DictionaryList = new List<DictionaryDto>()
var visitid = taskInfo.SourceSubjectVisit == null ? taskInfo.ReadModule.SubjectVisitId : taskInfo.SourceSubjectVisitId;
var subjectVisit = await _subjectVisitRepository.Where(x => x.Id == visitid).FirstOrDefaultAsync();
foreach (var userinfo in userinfoList)
{
var messageToSend = new MimeMessage();
//发件地址
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == taskInfo.TrialId);
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
var dictionValue = await CommonEmailHelper.TranslationDictionary(new TranslationDictionaryDto()
{
DictionaryRepository = _dictionaryRepository,
IsEn_US = isEn_US,
DictionaryList = new List<DictionaryDto>()
{
new DictionaryDto (){DictionaryCode= "ReadingCategory",EnumValue=taskInfo.ReadingCategory.GetEnumInt(), }, //任务类型
new DictionaryDto (){DictionaryCode= "RequestReReadingResult",EnumValue="0", }, //审批结果 都是待审批
}
});
});
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
{
var subjectCode = taskInfo.BlindSubjectCode.IsNullOrEmpty() ? taskInfo.Subject.Code : taskInfo.BlindSubjectCode;
var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo, subjectCode, taskInfo.TaskBlindName);
var htmlBodyStr = string.Format(
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
userinfo.FullName, // 用户名 {0}
trialInfo.ExperimentName, // 项目 {1}
subjectCode, // 受试者 {2}
taskInfo.TaskBlindName, // 访视 {3}
dictionValue[0], // 任务类型 {4}
doctorInfo.FullName, // 阅片人 {5}
criterion.CriterionName, // 阅片标准 {6}
dictionValue[1], // 审批结果 {7}
_systemEmailConfig.SiteUrl // 链接 {8}
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
{
var subjectCode = taskInfo.BlindSubjectCode.IsNullOrEmpty() ? taskInfo.Subject.Code : taskInfo.BlindSubjectCode;
var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo, subjectCode, taskInfo.TaskBlindName);
var htmlBodyStr = string.Format(
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
userinfo.FullName, // 用户名 {0}
trialInfo.ExperimentName, // 项目 {1}
subjectCode, // 受试者 {2}
taskInfo.TaskBlindName, // 访视 {3}
dictionValue[0], // 任务类型 {4}
doctorInfo.FullName, // 阅片人 {5}
criterion.CriterionName, // 阅片标准 {6}
dictionValue[1], // 审批结果 {7}
_systemEmailConfig.SiteUrl // 链接 {8}
);
);
return (topicStr, htmlBodyStr);
};
return (topicStr, htmlBodyStr);
};
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(_emailNoticeConfigrepository,
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc);
context.Message.ReReadingApplyState == ReReadingApplyState.TrialGroupHaveApplyed? EmailBusinessScenario.ReReadFromPMApproval: EmailBusinessScenario.ReReadFromIRApproval,
messageToSend, emailConfigFunc);
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
}
}
}
}

View File

@ -25,7 +25,7 @@ using System.Threading.Tasks;
namespace IRaCIS.Core.Application.MassTransit.Consumer;
/// <summary>
/// 用户提交 发送邮件 通知SPM 或者PM
/// 用户提交 发送邮件 通知SPM 或者PM 项目默认邮件,不用添加到项目中,直接发送的
/// </summary>
public class UserSiteSurveySubmitedEventConsumer(
IRepository<Trial> _trialRepository,
@ -47,75 +47,83 @@ public class UserSiteSurveySubmitedEventConsumer(
var trialId = siteSurveyInfo.TrialId;
var trialUserList = await _trialUserRoleRepository.Where(t => t.TrialId == siteSurveyInfo.TrialId)
.Where(t => t.UserRole.UserTypeEnum == UserTypeEnum.SPM || t.UserRole.UserTypeEnum == UserTypeEnum.CPM || t.UserRole.UserTypeEnum == UserTypeEnum.ProjectManager || t.UserRole.UserTypeEnum == UserTypeEnum.APM)
.Select(t => new { t.UserRole.FullName, t.UserRole.IdentityUser.EMail, t.UserRole.UserTypeEnum }).ToListAsync();
var sPMOrCPMList = trialUserList.Where(t => t.UserTypeEnum == UserTypeEnum.SPM || t.UserTypeEnum == UserTypeEnum.CPM).ToList();
var pmAndAPMList = trialUserList.Where(t => t.UserTypeEnum == UserTypeEnum.ProjectManager || t.UserTypeEnum == UserTypeEnum.APM).ToList();
var messageToSend = new MimeMessage();
var toUserName = string.Empty;
//有SPM
if (sPMOrCPMList.Count > 0)
{
foreach (var user in sPMOrCPMList)
{
messageToSend.To.Add(new MailboxAddress(user.FullName, user.EMail));
}
toUserName = string.Join('、', sPMOrCPMList.Select(t => t.FullName));
foreach (var user in pmAndAPMList)
{
messageToSend.Cc.Add(new MailboxAddress(user.FullName, user.EMail));
}
}
else
{
foreach (var user in pmAndAPMList)
{
messageToSend.To.Add(new MailboxAddress(user.FullName, user.EMail));
}
toUserName = string.Join('、', pmAndAPMList.Select(t => t.FullName));
}
//发件地址
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == trialId);
var siteInfo = await _trialSiteRepository.FirstOrDefaultAsync(t => t.TrialId == trialId && t.Id == siteSurveyInfo.TrialSiteId, true);
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
{
var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo);
var htmlBodyStr = string.Format(CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
toUserName,
siteInfo.TrialSiteCode,
siteInfo.TrialSiteAliasName,
siteSurveyInfo.UserName,
siteSurveyInfo.Email,
siteSurveyInfo.Phone,
_systemEmailConfig.SiteUrl
);
return (topicStr, htmlBodyStr);
};
var scenario = _trialSiteSurveyRepository.Where(t => t.Id != trialSiteSurveyId && t.TrialId == siteSurveyInfo.TrialId && t.TrialSiteId == siteSurveyInfo.TrialSiteId).IgnoreQueryFilters().Any() ?
EmailBusinessScenario.Approval_UpdateSiteSurvey : EmailBusinessScenario.Approval_SubmitSiteSurvey;
EmailBusinessScenario.Approval_UpdateSiteSurvey : EmailBusinessScenario.Approval_SubmitSiteSurvey;
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(_emailNoticeConfigrepository, scenario, messageToSend, emailConfigFunc);
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
var emailConfig = _emailNoticeConfigrepository.Where(t => t.BusinessScenarioEnum == scenario && t.IsAutoSend && t.IsEnable).FirstOrDefault();
if (emailConfig != null)
{
var trialUserList = await _trialUserRoleRepository.Where(t => t.TrialId == siteSurveyInfo.TrialId && t.TrialUser.IsDeleted == false)
.Where(t => t.UserRole.UserTypeEnum == UserTypeEnum.SPM || t.UserRole.UserTypeEnum == UserTypeEnum.CPM || t.UserRole.UserTypeEnum == UserTypeEnum.ProjectManager || t.UserRole.UserTypeEnum == UserTypeEnum.APM)
.Select(t => new { t.UserRole.FullName, t.UserRole.IdentityUser.EMail, t.UserRole.UserTypeEnum }).ToListAsync();
var sPMOrCPMList = trialUserList.Where(t => t.UserTypeEnum == UserTypeEnum.SPM || t.UserTypeEnum == UserTypeEnum.CPM).ToList();
var pmAndAPMList = trialUserList.Where(t => t.UserTypeEnum == UserTypeEnum.ProjectManager || t.UserTypeEnum == UserTypeEnum.APM).ToList();
var messageToSend = new MimeMessage();
var toUserName = string.Empty;
//有SPM
if (sPMOrCPMList.Count > 0)
{
foreach (var user in sPMOrCPMList)
{
messageToSend.To.Add(new MailboxAddress(user.FullName, user.EMail));
}
toUserName = string.Join('、', sPMOrCPMList.Select(t => t.FullName));
foreach (var user in pmAndAPMList)
{
messageToSend.Cc.Add(new MailboxAddress(user.FullName, user.EMail));
}
}
else
{
foreach (var user in pmAndAPMList)
{
messageToSend.To.Add(new MailboxAddress(user.FullName, user.EMail));
}
toUserName = string.Join('、', pmAndAPMList.Select(t => t.FullName));
}
//发件地址
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == trialId);
var siteInfo = await _trialSiteRepository.FirstOrDefaultAsync(t => t.TrialId == trialId && t.Id == siteSurveyInfo.TrialSiteId, true);
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
{
var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo);
var htmlBodyStr = string.Format(CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
toUserName,
siteInfo.TrialSiteCode,
siteInfo.TrialSiteAliasName,
siteSurveyInfo.UserName,
siteSurveyInfo.Email,
siteSurveyInfo.Phone,
_systemEmailConfig.SiteUrl
);
return (topicStr, htmlBodyStr);
};
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(emailConfig, messageToSend, emailConfigFunc);
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
}
}
}
@ -143,55 +151,65 @@ public class SiteSurveySPMSubmitedEventConsumer(
var trialId = siteSurveyInfo.TrialId;
var messageToSend = new MimeMessage();
var scenario = EmailBusinessScenario.Approval_SubmitSiteSurvey;
var trialUserList = _trialUserRoleRepository.Where(t => t.TrialId == trialId)
.Where(t => t.UserRole.UserTypeEnum == UserTypeEnum.SPM || t.UserRole.UserTypeEnum == UserTypeEnum.CPM || t.UserRole.UserTypeEnum == UserTypeEnum.ProjectManager || t.UserRole.UserTypeEnum == UserTypeEnum.APM)
.Select(t => new { t.UserRole.IdentityUser.EMail, t.UserRole.FullName, t.UserRole.UserTypeEnum }).ToList();
var emailConfig = _emailNoticeConfigrepository.Where(t => t.BusinessScenarioEnum == scenario && t.IsAutoSend && t.IsEnable).FirstOrDefault();
var sPMOrCPMList = trialUserList.Where(t => t.UserTypeEnum == UserTypeEnum.SPM || t.UserTypeEnum == UserTypeEnum.CPM).ToList();
var pmAndAPMList = trialUserList.Where(t => t.UserTypeEnum == UserTypeEnum.ProjectManager || t.UserTypeEnum == UserTypeEnum.APM).ToList();
var toUserName = string.Empty;
foreach (var item in pmAndAPMList)
if (emailConfig != null)
{
messageToSend.To.Add(new MailboxAddress(item.FullName, item.EMail));
var messageToSend = new MimeMessage();
toUserName = string.Join('、', pmAndAPMList.Select(t => t.FullName));
var trialUserList = _trialUserRoleRepository.Where(t => t.TrialId == trialId && t.TrialUser.IsDeleted == false)
.Where(t => t.UserRole.UserTypeEnum == UserTypeEnum.SPM || t.UserRole.UserTypeEnum == UserTypeEnum.CPM || t.UserRole.UserTypeEnum == UserTypeEnum.ProjectManager || t.UserRole.UserTypeEnum == UserTypeEnum.APM)
.Select(t => new { t.UserRole.IdentityUser.EMail, t.UserRole.FullName, t.UserRole.UserTypeEnum }).ToList();
var sPMOrCPMList = trialUserList.Where(t => t.UserTypeEnum == UserTypeEnum.SPM || t.UserTypeEnum == UserTypeEnum.CPM).ToList();
var pmAndAPMList = trialUserList.Where(t => t.UserTypeEnum == UserTypeEnum.ProjectManager || t.UserTypeEnum == UserTypeEnum.APM).ToList();
var toUserName = string.Empty;
foreach (var item in pmAndAPMList)
{
messageToSend.To.Add(new MailboxAddress(item.FullName, item.EMail));
toUserName = string.Join('、', pmAndAPMList.Select(t => t.FullName));
}
//发件地址
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == trialId);
var siteInfo = await _trialSiteRepository.FirstOrDefaultAsync(t => t.TrialId == trialId && t.Id == siteSurveyInfo.TrialSiteId, true);
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
{
var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo);
var htmlBodyStr = string.Format(CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
toUserName,
siteInfo.TrialSiteCode, //中心编号
siteInfo.TrialSiteAliasName,//中心名称
siteSurveyInfo.UserName, //联系人
siteSurveyInfo.Email, //联系邮箱
siteSurveyInfo.Phone, //联系电话
_systemEmailConfig.SiteUrl
);
return (topicStr, htmlBodyStr);
};
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(emailConfig, messageToSend, emailConfigFunc);
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
}
//发件地址
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == trialId);
var siteInfo = await _trialSiteRepository.FirstOrDefaultAsync(t => t.TrialId == trialId && t.Id == siteSurveyInfo.TrialSiteId, true);
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
{
var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo);
var htmlBodyStr = string.Format(CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
toUserName,
siteInfo.TrialSiteCode,
siteInfo.TrialSiteAliasName,
siteSurveyInfo.UserName,
siteSurveyInfo.Email,
siteSurveyInfo.Phone,
_systemEmailConfig.SiteUrl
);
return (topicStr, htmlBodyStr);
};
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(_emailNoticeConfigrepository, EmailBusinessScenario.Approval_SubmitSiteSurvey, messageToSend, emailConfigFunc);
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
}
}
@ -204,7 +222,7 @@ public class SiteSurverRejectedEventConsumer(
IRepository<Trial> _trialRepository,
IRepository<TrialSite> _trialSiteRepository,
IRepository<TrialSiteSurvey> _trialSiteSurveyRepository,
IRepository<EmailNoticeConfig> _emailNoticeConfigrepository,
IRepository<EmailNoticeConfig> _emailNoticeConfigrepository,
IOptionsMonitor<SystemEmailSendConfig> systemEmailConfig
) : IConsumer<SiteSurverRejectedEvent>
{
@ -216,83 +234,95 @@ public class SiteSurverRejectedEventConsumer(
var trialSiteSurveyId = context.Message.TrialSiteSurveyId;
var siteSurveyInfo = _trialSiteSurveyRepository.Where(t => t.Id == trialSiteSurveyId).FirstOrDefault().IfNullThrowException();
var siteSurveyInfo = _trialSiteSurveyRepository.Where(t => t.Id == trialSiteSurveyId ,ignoreQueryFilters:true).FirstOrDefault().IfNullThrowException();
var trialId = siteSurveyInfo.TrialId;
var messageToSend = new MimeMessage();
var scenario = EmailBusinessScenario.SiteSurveyReject;
var toUserName = siteSurveyInfo.UserName;
var emailConfig = _emailNoticeConfigrepository.Where(t => t.BusinessScenarioEnum == scenario && t.IsAutoSend && t.IsEnable).FirstOrDefault();
if (context.Message.IsHaveSPMOrCPM)
if (emailConfig != null)
{
//PM 驳回到SPM
if (siteSurveyInfo.State == TrialSiteSurveyEnum.CRCSubmitted)
var messageToSend = new MimeMessage();
var toUserName = siteSurveyInfo.UserName;
if (context.Message.IsHaveSPMOrCPM)
{
//var user = await _userRoleRepository.FirstOrDefaultAsync(t => t.Id == siteSurveyInfo.PreliminaryUserId);
//name = user.FullName;
var sPMOrCPMList = _trialUserRoleRepository.Where(t => t.TrialId == trialId)
.Where(t => t.UserRole.UserTypeEnum == UserTypeEnum.SPM || t.UserRole.UserTypeEnum == UserTypeEnum.CPM)
.Select(t => new { t.UserRole.IdentityUser.EMail, t.UserRole.FullName, t.UserRole.UserTypeEnum }).ToList();
foreach (var user in sPMOrCPMList)
//PM 驳回到SPM
if (siteSurveyInfo.State == TrialSiteSurveyEnum.CRCSubmitted)
{
messageToSend.To.Add(new MailboxAddress(user.FullName, user.EMail));
//var user = await _userRoleRepository.FirstOrDefaultAsync(t => t.Id == siteSurveyInfo.PreliminaryUserId);
//name = user.FullName;
var sPMOrCPMList = _trialUserRoleRepository.Where(t => t.TrialId == trialId && t.TrialUser.IsDeleted == false)
.Where(t => t.UserRole.UserTypeEnum == UserTypeEnum.SPM || t.UserRole.UserTypeEnum == UserTypeEnum.CPM)
.Select(t => new { t.UserRole.IdentityUser.EMail, t.UserRole.FullName, t.UserRole.UserTypeEnum }).ToList();
foreach (var user in sPMOrCPMList)
{
messageToSend.To.Add(new MailboxAddress(user.FullName, user.EMail));
}
toUserName = string.Join('、', sPMOrCPMList.Select(t => t.FullName));
}
//SPM 驳回到CRC
else if (siteSurveyInfo.State == TrialSiteSurveyEnum.ToSubmit)
{
messageToSend.To.Add(new MailboxAddress(toUserName, siteSurveyInfo.Email));
}
toUserName = string.Join('、', sPMOrCPMList.Select(t => t.FullName));
}
//SPM 驳回到CRC
else if (siteSurveyInfo.State == TrialSiteSurveyEnum.ToSubmit)
else
{
messageToSend.To.Add(new MailboxAddress(toUserName, siteSurveyInfo.Email));
//没有SPM PM驳回到CRC
messageToSend.To.Add(new MailboxAddress(String.Empty, siteSurveyInfo.Email));
}
}
else
{
//没有SPM PM驳回到CRC
messageToSend.To.Add(new MailboxAddress(String.Empty, siteSurveyInfo.Email));
}
//发件地址
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
//发件地址
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == trialId);
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == trialId);
var siteInfo = await _trialSiteRepository.FirstOrDefaultAsync(t => t.TrialId == trialId && t.Id == siteSurveyInfo.TrialSiteId, true);
var siteInfo = await _trialSiteRepository.FirstOrDefaultAsync(t => t.TrialId == trialId && t.Id == siteSurveyInfo.TrialSiteId, true);
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
{
var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo);
var htmlBodyStr = string.Format(CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
toUserName,
trialInfo.TrialCode,
trialInfo.ResearchProgramNo,
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
{
var topicStr = string.Format(input.topicStr, companyName,trialInfo.ResearchProgramNo);
var htmlBodyStr = string.Format(CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
toUserName,
trialInfo.TrialCode,
trialInfo.ResearchProgramNo,
trialInfo.ExperimentName,
siteInfo.TrialSiteCode,
siteInfo.TrialSiteAliasName,
siteSurveyInfo.LatestBackReason,
_systemEmailConfig.SiteUrl,
(siteSurveyInfo.State == TrialSiteSurveyEnum.ToSubmit ? "inline - block" : "none")
);
return (topicStr, htmlBodyStr);
};
siteInfo.TrialSiteCode, //中心编号
siteInfo.TrialSiteAliasName,//中心名称
siteSurveyInfo.LatestBackReason, //驳回原因
_systemEmailConfig.SiteUrl, //链接
(siteSurveyInfo.State == TrialSiteSurveyEnum.ToSubmit ? "inline - block" : "none")
);
return (topicStr, htmlBodyStr);
};
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(emailConfig, messageToSend, emailConfigFunc);
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
}
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(_emailNoticeConfigrepository, EmailBusinessScenario.SiteSurveyReject, messageToSend, emailConfigFunc);
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
}

View File

@ -24,13 +24,13 @@ namespace IRaCIS.Core.Application.MassTransit.Consumer;
/// </summary>
public class CRCSubmitedAndQCToAuditEventConsumer(
IRepository<UserRole> _userRoleRepository,
IRepository<TrialUserRole> _trialUseRoleRepository,
IRepository<SubjectVisit> _subjectVisitRepository,
IRepository<TrialUserRole> _trialUseRoleRepository,
IRepository<SubjectVisit> _subjectVisitRepository,
IRepository<Trial> _trialRepository,
IRepository<ReadingQuestionCriterionTrial> _readingQuestionCriterionTrialRepository,
IRepository<VisitTask> _visitTaskRepository,
IRepository<Dictionary> _dictionaryRepository,
IRepository<EmailNoticeConfig> _emailNoticeConfigrepository,
IRepository<Dictionary> _dictionaryRepository,
IRepository<TrialEmailNoticeConfig> _trialEmailNoticeConfigrepository,
IOptionsMonitor<SystemEmailSendConfig> systemEmailConfig) : IConsumer<CRCSubmitedAndQCToAuditEvent>
{
private readonly SystemEmailSendConfig _systemEmailConfig = systemEmailConfig.CurrentValue;
@ -41,46 +41,55 @@ public class CRCSubmitedAndQCToAuditEventConsumer(
var subjectVisitId = context.Message.SubjectVisitId;
var subjectVisit = await _subjectVisitRepository.Where(x => x.Id == subjectVisitId).Include(x => x.Subject).FirstOrDefaultAsync();
var subjectVisit = await _subjectVisitRepository.Where(x => x.Id == subjectVisitId).Include(x => x.Subject).FirstNotNullAsync();
var trialUser = await _trialUseRoleRepository.Where(x => x.TrialId == subjectVisit.TrialId).Include(x => x.UserRole).Select(x => x.UserRole).ToListAsync();
var trialId = subjectVisit.TrialId;
var userinfoList = trialUser.Where(x => x.UserTypeEnum == UserTypeEnum.IQC).ToList();
var scenario = context.Message.IsPd ? EmailBusinessScenario.PDVerification_PendingImageQCClaim : EmailBusinessScenario.EligibilityVerification_PendingClaim;
var trialEmailConfig = _trialEmailNoticeConfigrepository.Where(t => t.TrialId == trialId && t.BusinessScenarioEnum == scenario && t.IsAutoSend && t.IsEnable).FirstOrDefault();
var pmandAPm = trialUser.Where(x => x.UserTypeEnum == UserTypeEnum.APM || x.UserTypeEnum == UserTypeEnum.ProjectManager).ToList();
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == subjectVisit.TrialId);
var auditStateCode = "AuditStatePE";
if (trialInfo.QCProcessEnum == TrialQCProcess.DoubleAudit)
{
auditStateCode = "AuditStateRC";
}
var dictionValue = await CommonEmailHelper.TranslationDictionary(new TranslationDictionaryDto()
if (trialEmailConfig != null)
{
DictionaryRepository = _dictionaryRepository,
IsEn_US = isEn_US,
DictionaryList = new List<DictionaryDto>()
var trialUserList = await _trialUseRoleRepository.Where(x => x.TrialId == subjectVisit.TrialId && x.TrialUser.IsDeleted == false).Include(x => x.UserRole).Select(x => x.UserRole).ToListAsync();
var userinfoList = trialUserList.Where(x => x.UserTypeEnum == UserTypeEnum.IQC).ToList();
var pmandAPm = trialUserList.Where(x => x.UserTypeEnum == UserTypeEnum.APM || x.UserTypeEnum == UserTypeEnum.ProjectManager).ToList();
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == subjectVisit.TrialId);
var auditStateCode = "AuditStatePE";
if (trialInfo.QCProcessEnum == TrialQCProcess.DoubleAudit)
{
auditStateCode = "AuditStateRC";
}
var dictionValue = await CommonEmailHelper.TranslationDictionary(new TranslationDictionaryDto()
{
DictionaryRepository = _dictionaryRepository,
IsEn_US = isEn_US,
DictionaryList = new List<DictionaryDto>()
{
new DictionaryDto (){DictionaryCode= auditStateCode,EnumValue=subjectVisit.AuditState.GetEnumInt(), }, //审核状态
}
});
});
var messageToSend = new MimeMessage();
//发件地址
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
foreach (var userinfo in userinfoList)
{
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
}
var userNames = userinfoList.Select(x => x.FullName).ToList();
@ -111,14 +120,11 @@ public class CRCSubmitedAndQCToAuditEventConsumer(
return (topicStr, htmlBodyStr);
};
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(_emailNoticeConfigrepository,
context.Message.IsPd ? EmailBusinessScenario.PDVerification_PendingImageQCClaim : EmailBusinessScenario.EligibilityVerification_PendingClaim,
messageToSend, emailConfigFunc);
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc);
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
}
}
}
@ -134,7 +140,7 @@ public class CRCRepliedQCChallengeEventConsumer(
IRepository<QCChallenge> _qCChallengeRepository,
IRepository<QCChallengeDialog> _qCChallengeDialogRepository,
IRepository<Dictionary> _dictionaryRepository,
IRepository<EmailNoticeConfig> _emailNoticeConfigrepository,
IRepository<TrialEmailNoticeConfig> _trialEmailNoticeConfigrepository,
IOptionsMonitor<SystemEmailSendConfig> systemEmailConfig) : IConsumer<CRCRepliedQCChallengeEvent>
{
@ -148,74 +154,75 @@ public class CRCRepliedQCChallengeEventConsumer(
var subjectVisit = await _subjectVisitRepository.Where(x => x.Id == subjectVisitId).Include(x => x.Subject).Include(x => x.CurrentActionUser).FirstNotNullAsync();
var trialId = subjectVisit.TrialId;
var qCChallengeDialog = await _qCChallengeDialogRepository.Where(x => x.Id == context.Message.QCChallengeDialogId).Include(x => x.QCChallenge).FirstNotNullAsync();
var scenario = context.Message.IsPd ? EmailBusinessScenario.PDVerification_UnderQCQuery : EmailBusinessScenario.EligibilityVerification_Pending;
var trialEmailConfig = _trialEmailNoticeConfigrepository.Where(t => t.TrialId == trialId && t.BusinessScenarioEnum == scenario && t.IsAutoSend && t.IsEnable).FirstOrDefault();
var trialUser = await _trialUseRoleRepository.Where(x => x.TrialId == subjectVisit.TrialId).Include(x => x.UserRole).Select(x => x.UserRole).ToListAsync();
var userinfoList = trialUser.Where(x => x.UserTypeEnum == UserTypeEnum.IQC).ToList();
var pmandAPm = trialUser.Where(x => x.UserTypeEnum == UserTypeEnum.APM || x.UserTypeEnum == UserTypeEnum.ProjectManager).ToList();
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == subjectVisit.TrialId);
var userinfo = subjectVisit.CurrentActionUser;
var messageToSend = new MimeMessage();
//发件地址
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
foreach (var pm in pmandAPm)
{
messageToSend.Cc.Add(new MailboxAddress(String.Empty, pm.EMail));
}
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
var dictionValue = await CommonEmailHelper.TranslationDictionary(new TranslationDictionaryDto()
if (trialEmailConfig != null)
{
DictionaryRepository = _dictionaryRepository,
IsEn_US = isEn_US,
DictionaryList = new List<DictionaryDto>()
var qCChallengeDialog = await _qCChallengeDialogRepository.Where(x => x.Id == context.Message.QCChallengeDialogId).Include(x => x.QCChallenge).FirstNotNullAsync();
var trialUserList = await _trialUseRoleRepository.Where(x => x.TrialId == subjectVisit.TrialId && x.TrialUser.IsDeleted == false).Include(x => x.UserRole).Select(x => x.UserRole).ToListAsync();
var userinfoList = trialUserList.Where(x => x.UserTypeEnum == UserTypeEnum.IQC).ToList();
var pmandAPm = trialUserList.Where(x => x.UserTypeEnum == UserTypeEnum.APM || x.UserTypeEnum == UserTypeEnum.ProjectManager).ToList();
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == subjectVisit.TrialId);
var userinfo = subjectVisit.CurrentActionUser;
var messageToSend = new MimeMessage();
//发件地址
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
foreach (var pm in pmandAPm)
{
messageToSend.Cc.Add(new MailboxAddress(String.Empty, pm.EMail));
}
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
var dictionValue = await CommonEmailHelper.TranslationDictionary(new TranslationDictionaryDto()
{
DictionaryRepository = _dictionaryRepository,
IsEn_US = isEn_US,
DictionaryList = new List<DictionaryDto>()
{
new DictionaryDto (){DictionaryCode= "YesOrNo",EnumValue=qCChallengeDialog.QCChallenge.IsClosed.ToString().ToLower(), }, //是否关闭
}
});
});
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
{
var subjectCode = subjectVisit.Subject.Code;
var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo, subjectCode, subjectVisit.VisitName);
var htmlBodyStr = string.Format(
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
userinfo.FullName, // 用户名 {0}
trialInfo.ExperimentName, // 项目 {1}
subjectCode, // 受试者 {2}
subjectVisit.VisitName, // 访视 {3}
dictionValue[0], // 是否关闭 {4}
qCChallengeDialog.TalkContent, // 质疑内容{5}
_systemEmailConfig.SiteUrl // 链接 {6}
);
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
{
var subjectCode = subjectVisit.Subject.Code;
var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo, subjectCode, subjectVisit.VisitName);
var htmlBodyStr = string.Format(
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
userinfo.FullName, // 用户名 {0}
trialInfo.ExperimentName, // 项目 {1}
subjectCode, // 受试者 {2}
subjectVisit.VisitName, // 访视 {3}
dictionValue[0], // 是否关闭 {4}
qCChallengeDialog.TalkContent, // 质疑内容{5}
_systemEmailConfig.SiteUrl // 链接 {6}
);
return (topicStr, htmlBodyStr);
};
return (topicStr, htmlBodyStr);
};
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc);
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(_emailNoticeConfigrepository,
context.Message.IsPd ? EmailBusinessScenario.PDVerification_UnderQCQuery : EmailBusinessScenario.EligibilityVerification_Pending,
messageToSend, emailConfigFunc);
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
}
}
}
@ -231,7 +238,7 @@ public class QCRepliedQCChallengeEventConsumer(
IRepository<QCChallenge> _qCChallengeRepository,
IRepository<QCChallengeDialog> _qCChallengeDialogRepository,
IRepository<Dictionary> _dictionaryRepository,
IRepository<EmailNoticeConfig> _emailNoticeConfigrepository,
IRepository<TrialEmailNoticeConfig> _trialEmailNoticeConfigrepository,
IOptionsMonitor<SystemEmailSendConfig> systemEmailConfig) : IConsumer<QCRepliedQCChallengeEvent>
{
private readonly SystemEmailSendConfig _systemEmailConfig = systemEmailConfig.CurrentValue;
@ -240,90 +247,100 @@ public class QCRepliedQCChallengeEventConsumer(
Console.WriteLine("发送(014015) 【 QC回复 质控质疑通知CRC】邮件");
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
string talkContent = string.Empty;
var isclose = false;
if (context.Message.QCChallengeDialogId != null)
{
var qCChallengeDialog = await _qCChallengeDialogRepository.Where(x => x.Id == context.Message.QCChallengeDialogId.Value).Include(x => x.QCChallenge).FirstNotNullAsync();
talkContent = qCChallengeDialog.TalkContent;
isclose = qCChallengeDialog.QCChallenge.IsClosed;
}
else if(context.Message.QCChallengeId != null)
{
var qCChallenge = await _qCChallengeRepository.Where(x => x.Id == context.Message.QCChallengeId.Value).FirstNotNullAsync();
talkContent = qCChallenge.Content;
isclose = qCChallenge.IsClosed;
}
var subjectVisitId = context.Message.SubjectVisitId;
var subjectVisit = await _subjectVisitRepository.Where(x => x.Id == subjectVisitId).Include(x => x.Subject).FirstNotNullAsync();
var trialUser = await _trialUseRoleRepository.Where(x => x.TrialId == subjectVisit.TrialId).Include(x => x.UserRole).Select(x => x.UserRole).ToListAsync();
var trialId = subjectVisit.TrialId;
var userinfoList = trialUser.Where(x => x.UserTypeEnum == UserTypeEnum.ClinicalResearchCoordinator).ToList();
var scenario = context.Message.IsPd ? EmailBusinessScenario.PDVerification_ImageQuery : EmailBusinessScenario.EligibilityVerification_ImageQuery;
var craInfo = trialUser.Where(x => x.UserTypeEnum == UserTypeEnum.CRA).FirstOrDefault();
var trialEmailConfig = _trialEmailNoticeConfigrepository.Where(t => t.TrialId == trialId && t.BusinessScenarioEnum == scenario && t.IsAutoSend && t.IsEnable).FirstOrDefault();
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == subjectVisit.TrialId);
var dictionValue = await CommonEmailHelper.TranslationDictionary(new TranslationDictionaryDto()
if (trialEmailConfig != null)
{
DictionaryRepository = _dictionaryRepository,
IsEn_US = isEn_US,
DictionaryList = new List<DictionaryDto>()
string talkContent = string.Empty;
var isclose = false;
if (context.Message.QCChallengeDialogId != null)
{
var qCChallengeDialog = await _qCChallengeDialogRepository.Where(x => x.Id == context.Message.QCChallengeDialogId.Value).Include(x => x.QCChallenge).FirstNotNullAsync();
talkContent = qCChallengeDialog.TalkContent;
isclose = qCChallengeDialog.QCChallenge.IsClosed;
}
else if (context.Message.QCChallengeId != null)
{
var qCChallenge = await _qCChallengeRepository.Where(x => x.Id == context.Message.QCChallengeId.Value).FirstNotNullAsync();
talkContent = qCChallenge.Content;
isclose = qCChallenge.IsClosed;
}
var trialUserList = await _trialUseRoleRepository.Where(x => x.TrialId == subjectVisit.TrialId && x.TrialUser.IsDeleted == false).Include(x => x.UserRole).Select(x => x.UserRole).ToListAsync();
var userinfoList = trialUserList.Where(x => x.UserTypeEnum == UserTypeEnum.ClinicalResearchCoordinator).ToList();
var craInfo = trialUserList.Where(x => x.UserTypeEnum == UserTypeEnum.CRA).FirstOrDefault();
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == subjectVisit.TrialId);
var dictionValue = await CommonEmailHelper.TranslationDictionary(new TranslationDictionaryDto()
{
DictionaryRepository = _dictionaryRepository,
IsEn_US = isEn_US,
DictionaryList = new List<DictionaryDto>()
{
new DictionaryDto (){DictionaryCode= "YesOrNo",EnumValue=isclose.ToString().ToLower(), }, //是否关闭
}
});
});
foreach (var userinfo in userinfoList)
{
var messageToSend = new MimeMessage();
//发件地址
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
if (craInfo != null)
foreach (var userinfo in userinfoList)
{
messageToSend.Cc.Add(new MailboxAddress(String.Empty, craInfo.EMail));
var messageToSend = new MimeMessage();
//发件地址
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
if (craInfo != null)
{
messageToSend.Cc.Add(new MailboxAddress(String.Empty, craInfo.EMail));
}
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
{
var subjectCode = subjectVisit.Subject.Code;
var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo, subjectCode, subjectVisit.VisitName);
var htmlBodyStr = string.Format(
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
userinfo.FullName, // 用户名 {0}
trialInfo.ExperimentName, // 项目 {1}
subjectCode, // 受试者 {2}
subjectVisit.VisitName, // 访视 {3}
dictionValue[0], // 质疑是否关闭 {4}
talkContent, // 质疑内容 {5}
_systemEmailConfig.SiteUrl // 链接 {65}
);
return (topicStr, htmlBodyStr);
};
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc);
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
}
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
{
var subjectCode = subjectVisit.Subject.Code;
var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo, subjectCode, subjectVisit.VisitName);
var htmlBodyStr = string.Format(
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
userinfo.FullName, // 用户名 {0}
trialInfo.ExperimentName, // 项目 {1}
subjectCode, // 受试者 {2}
subjectVisit.VisitName, // 访视 {3}
dictionValue[0], // 质疑是否关闭 {4}
talkContent, // 质疑内容 {5}
_systemEmailConfig.SiteUrl // 链接 {65}
);
return (topicStr, htmlBodyStr);
};
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(_emailNoticeConfigrepository,
context.Message.IsPd ? EmailBusinessScenario.PDVerification_ImageQuery : EmailBusinessScenario.EligibilityVerification_ImageQuery,
messageToSend, emailConfigFunc);
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
}
}
}
@ -340,7 +357,7 @@ public class CRCRepliedCheckChallengeEventConsumer(
IRepository<QCChallenge> _qCChallengeRepository,
IRepository<QCChallengeDialog> _qCChallengeDialogRepository,
IRepository<Dictionary> _dictionaryRepository,
IRepository<EmailNoticeConfig> _emailNoticeConfigrepository,
IRepository<TrialEmailNoticeConfig> _trialEmailNoticeConfigrepository,
IOptionsMonitor<SystemEmailSendConfig> systemEmailConfig) : IConsumer<CRCRepliedCheckChallengeEvent>
{
@ -350,70 +367,80 @@ public class CRCRepliedCheckChallengeEventConsumer(
Console.WriteLine("发送(019,020) 【 CRC 回复一致性核查质疑 通知PM】邮件");
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
var subjectVisitId = context.Message.SubjectVisitId;
var subjectVisit = await _subjectVisitRepository.Where(x => x.Id == subjectVisitId).Include(x => x.Subject).FirstNotNullAsync();
var trialUser = await _trialUseRoleRepository.Where(x => x.TrialId == subjectVisit.TrialId).Include(x => x.UserRole).Select(x => x.UserRole).ToListAsync();
var trialId = subjectVisit.TrialId;
var userinfoList = trialUser.Where(x => x.UserTypeEnum == UserTypeEnum.APM || x.UserTypeEnum == UserTypeEnum.ProjectManager).ToList();
var scenario = context.Message.IsPd ? EmailBusinessScenario.PDVerification_UnderDRForPMOrAPM : EmailBusinessScenario.EligibilityVerification_UnderDRForPMOrAPM;
var trialEmailConfig = _trialEmailNoticeConfigrepository.Where(t => t.TrialId == trialId && t.BusinessScenarioEnum == scenario && t.IsAutoSend && t.IsEnable).FirstOrDefault();
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == subjectVisit.TrialId);
var dictionValue = await CommonEmailHelper.TranslationDictionary(new TranslationDictionaryDto()
if (trialEmailConfig != null)
{
DictionaryRepository = _dictionaryRepository,
IsEn_US = isEn_US,
DictionaryList = new List<DictionaryDto>()
var trialUserList = await _trialUseRoleRepository.Where(x => x.TrialId == subjectVisit.TrialId && x.TrialUser.IsDeleted == false).Include(x => x.UserRole).Select(x => x.UserRole).ToListAsync();
var userinfoList = trialUserList.Where(x => x.UserTypeEnum == UserTypeEnum.APM || x.UserTypeEnum == UserTypeEnum.ProjectManager).ToList();
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == subjectVisit.TrialId);
var dictionValue = await CommonEmailHelper.TranslationDictionary(new TranslationDictionaryDto()
{
DictionaryRepository = _dictionaryRepository,
IsEn_US = isEn_US,
DictionaryList = new List<DictionaryDto>()
{
new DictionaryDto (){DictionaryCode= "CheckState",EnumValue=subjectVisit.CheckState.GetEnumInt(), }, //审核状态
}
});
});
foreach (var userinfo in userinfoList)
{
var messageToSend = new MimeMessage();
//发件地址
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
foreach (var userinfo in userinfoList)
{
var subjectCode = subjectVisit.Subject.Code;
var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo, subjectCode, subjectVisit.VisitName);
var htmlBodyStr = string.Format(
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
userinfo.FullName, // 用户名 {0}
trialInfo.ExperimentName, // 项目 {1}
subjectCode, // 受试者 {2}
subjectVisit.VisitName, // 访视 {3}
dictionValue[0], // 核查状态 {4}
_systemEmailConfig.SiteUrl // 链接 {5}
);
var messageToSend = new MimeMessage();
//发件地址
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
return (topicStr, htmlBodyStr);
};
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(_emailNoticeConfigrepository,
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
context.Message.IsPd ? EmailBusinessScenario.PDVerification_UnderDRForPMOrAPM : EmailBusinessScenario.EligibilityVerification_UnderDRForPMOrAPM,
messageToSend, emailConfigFunc);
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
{
var subjectCode = subjectVisit.Subject.Code;
var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo, subjectCode, subjectVisit.VisitName);
var htmlBodyStr = string.Format(
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
userinfo.FullName, // 用户名 {0}
trialInfo.ExperimentName, // 项目 {1}
subjectCode, // 受试者 {2}
subjectVisit.VisitName, // 访视 {3}
dictionValue[0], // 核查状态 {4}
_systemEmailConfig.SiteUrl // 链接 {5}
);
return (topicStr, htmlBodyStr);
};
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc);
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
}
}
}
}
@ -432,7 +459,7 @@ public class PMRepliedCheckChallengeEventConsumer(
IRepository<CheckChallengeDialog> _checkChallengeDialogRepository,
IRepository<QCChallengeDialog> _qCChallengeDialogRepository,
IRepository<Dictionary> _dictionaryRepository,
IRepository<EmailNoticeConfig> _emailNoticeConfigrepository,
IRepository<TrialEmailNoticeConfig> _trialEmailNoticeConfigrepository,
IOptionsMonitor<SystemEmailSendConfig> systemEmailConfig) : IConsumer<PMRepliedCheckChallengeEvent>
{
@ -441,80 +468,89 @@ public class PMRepliedCheckChallengeEventConsumer(
{
Console.WriteLine("发送(016017) 【 PM 一致性核查 通知CRC】邮件");
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
var checkChallengeDialog = await _checkChallengeDialogRepository.Where(x => x.Id == context.Message.CheckChallengeDialogId).FirstNotNullAsync();
var subjectVisitId = context.Message.SubjectVisitId;
var subjectVisit = await _subjectVisitRepository.Where(x => x.Id == subjectVisitId).Include(x => x.NoneDicomStudyList).Include(x => x.StudyList).Include(x => x.Subject).FirstNotNullAsync();
var trialUser = await _trialUseRoleRepository.Where(x => x.TrialId == subjectVisit.TrialId).Include(x => x.UserRole).Select(x => x.UserRole).ToListAsync();
var trialId = subjectVisit.TrialId;
var userinfoList = trialUser.Where(x => x.UserTypeEnum == UserTypeEnum.ClinicalResearchCoordinator).ToList();
var craInfo = trialUser.Where(x => x.UserTypeEnum == UserTypeEnum.CRA).FirstOrDefault();
var scenario = context.Message.IsPd ? EmailBusinessScenario.PDVerification_UnderDR : EmailBusinessScenario.EligibilityVerification_UnderDR;
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == subjectVisit.TrialId);
var trialEmailConfig = _trialEmailNoticeConfigrepository.Where(t => t.TrialId == trialId && t.BusinessScenarioEnum == scenario && t.IsAutoSend && t.IsEnable).FirstOrDefault();
List<string> modalities = new List<string>();
modalities =subjectVisit.NoneDicomStudyList.Select(t => t.Modality)
.Union(subjectVisit.StudyList.Select(k => k.ModalityForEdit)).ToList();
var dictionValue = await CommonEmailHelper.TranslationDictionary(new TranslationDictionaryDto()
if (trialEmailConfig != null)
{
DictionaryRepository = _dictionaryRepository,
IsEn_US = isEn_US,
DictionaryList = new List<DictionaryDto>()
var checkChallengeDialog = await _checkChallengeDialogRepository.Where(x => x.Id == context.Message.CheckChallengeDialogId).FirstNotNullAsync();
var trialUserList = await _trialUseRoleRepository.Where(x => x.TrialId == subjectVisit.TrialId && x.TrialUser.IsDeleted == false).Include(x => x.UserRole).Select(x => x.UserRole).ToListAsync();
var userinfoList = trialUserList.Where(x => x.UserTypeEnum == UserTypeEnum.ClinicalResearchCoordinator).ToList();
var craInfo = trialUserList.Where(x => x.UserTypeEnum == UserTypeEnum.CRA).FirstOrDefault();
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == subjectVisit.TrialId);
List<string> modalities = new List<string>();
modalities = subjectVisit.NoneDicomStudyList.Select(t => t.Modality)
.Union(subjectVisit.StudyList.Select(k => k.ModalityForEdit)).ToList();
var dictionValue = await CommonEmailHelper.TranslationDictionary(new TranslationDictionaryDto()
{
DictionaryRepository = _dictionaryRepository,
IsEn_US = isEn_US,
DictionaryList = new List<DictionaryDto>()
{
new DictionaryDto (){DictionaryCode= "CheckState",EnumValue=subjectVisit.CheckState.GetEnumInt(), }, //核查状态
}
});
});
foreach (var userinfo in userinfoList)
{
var messageToSend = new MimeMessage();
//发件地址
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
if (craInfo != null)
foreach (var userinfo in userinfoList)
{
messageToSend.Cc.Add(new MailboxAddress(String.Empty, craInfo.EMail));
var messageToSend = new MimeMessage();
//发件地址
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
if (craInfo != null)
{
messageToSend.Cc.Add(new MailboxAddress(String.Empty, craInfo.EMail));
}
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
{
var subjectCode = subjectVisit.Subject.Code;
var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo, subjectCode, subjectVisit.VisitName);
var htmlBodyStr = string.Format(
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
userinfo.FullName, // 用户名 {0}
trialInfo.ExperimentName, // 项目 {1}
subjectCode, // 受试者 {2}
subjectVisit.VisitName, // 访视 {3}
string.Join(',', modalities), // 检查类型 {4}
dictionValue[0], // 核查状态 {5}
checkChallengeDialog.TalkContent,// 质疑内容{6}
_systemEmailConfig.SiteUrl // 链接 {7}
);
return (topicStr, htmlBodyStr);
};
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc);
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
}
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
{
var subjectCode = subjectVisit.Subject.Code;
var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo, subjectCode, subjectVisit.VisitName);
var htmlBodyStr = string.Format(
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
userinfo.FullName, // 用户名 {0}
trialInfo.ExperimentName, // 项目 {1}
subjectCode, // 受试者 {2}
subjectVisit.VisitName, // 访视 {3}
string.Join(',', modalities), // 检查类型 {4}
dictionValue[0], // 核查状态 {5}
checkChallengeDialog.TalkContent,// 质疑内容{6}
_systemEmailConfig.SiteUrl // 链接 {7}
);
return (topicStr, htmlBodyStr);
};
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(_emailNoticeConfigrepository,
context.Message.IsPd ? EmailBusinessScenario.PDVerification_UnderDR : EmailBusinessScenario.EligibilityVerification_UnderDR,
messageToSend, emailConfigFunc);
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
}
}
}
@ -531,7 +567,7 @@ public class CheckStateChangedToAuditEventConsumer(
IRepository<QCChallenge> _qCChallengeRepository,
IRepository<QCChallengeDialog> _qCChallengeDialogRepository,
IRepository<Dictionary> _dictionaryRepository,
IRepository<EmailNoticeConfig> _emailNoticeConfigrepository,
IRepository<TrialEmailNoticeConfig> _trialEmailNoticeConfigrepository,
IOptionsMonitor<SystemEmailSendConfig> systemEmailConfig) : IConsumer<CheckStateChangedToAuditEvent>
{
private readonly SystemEmailSendConfig _systemEmailConfig = systemEmailConfig.CurrentValue;
@ -540,70 +576,80 @@ public class CheckStateChangedToAuditEventConsumer(
Console.WriteLine("发送(018,030) 【通知PM 进行一致性核查】邮件!!!");
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
var subjectVisitId = context.Message.SubjectVisitId;
var subjectVisit = await _subjectVisitRepository.Where(x => x.Id == subjectVisitId).Include(x => x.Subject).FirstNotNullAsync();
var trialUser = await _trialUseRoleRepository.Where(x => x.TrialId == subjectVisit.TrialId).Include(x => x.UserRole).Select(x => x.UserRole).ToListAsync();
var trialId = subjectVisit.TrialId;
var userinfoList = trialUser.Where(x => x.UserTypeEnum == UserTypeEnum.APM || x.UserTypeEnum == UserTypeEnum.ProjectManager).ToList();
var scenario = context.Message.IsPd ? EmailBusinessScenario.PDVerification_DR : EmailBusinessScenario.EligibilityVerification_DR;
var trialEmailConfig = _trialEmailNoticeConfigrepository.Where(t => t.TrialId == trialId && t.BusinessScenarioEnum == scenario && t.IsAutoSend && t.IsEnable).FirstOrDefault();
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == subjectVisit.TrialId);
var dictionValue = await CommonEmailHelper.TranslationDictionary(new TranslationDictionaryDto()
if (trialEmailConfig != null)
{
DictionaryRepository = _dictionaryRepository,
IsEn_US = isEn_US,
DictionaryList = new List<DictionaryDto>()
var trialUserList = await _trialUseRoleRepository.Where(x => x.TrialId == subjectVisit.TrialId && x.TrialUser.IsDeleted == false).Include(x => x.UserRole).Select(x => x.UserRole).ToListAsync();
var userinfoList = trialUserList.Where(x => x.UserTypeEnum == UserTypeEnum.APM || x.UserTypeEnum == UserTypeEnum.ProjectManager).ToList();
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == subjectVisit.TrialId);
var dictionValue = await CommonEmailHelper.TranslationDictionary(new TranslationDictionaryDto()
{
DictionaryRepository = _dictionaryRepository,
IsEn_US = isEn_US,
DictionaryList = new List<DictionaryDto>()
{
new DictionaryDto (){DictionaryCode= "CheckState",EnumValue=subjectVisit.CheckState.GetEnumInt(), }, //审核状态
}
});
});
foreach (var userinfo in userinfoList)
{
var messageToSend = new MimeMessage();
//发件地址
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
foreach (var userinfo in userinfoList)
{
var subjectCode = subjectVisit.Subject.Code;
var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo, subjectCode, subjectVisit.VisitName);
var htmlBodyStr = string.Format(
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
userinfo.FullName, // 姓名 {0}
trialInfo.ExperimentName, // 项目 {1}
subjectCode, // 受试者 {2}
subjectVisit.VisitName, // 访视 {3}
dictionValue[0], // 核查状态 {4}
_systemEmailConfig.SiteUrl // 链接 {5}
);
var messageToSend = new MimeMessage();
//发件地址
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
return (topicStr, htmlBodyStr);
};
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(_emailNoticeConfigrepository,
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
context.Message.IsPd ? EmailBusinessScenario.PDVerification_DR : EmailBusinessScenario.EligibilityVerification_DR,
messageToSend, emailConfigFunc);
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
{
var subjectCode = subjectVisit.Subject.Code;
var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo, subjectCode, subjectVisit.VisitName);
var htmlBodyStr = string.Format(
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
userinfo.FullName, // 姓名 {0}
trialInfo.ExperimentName, // 项目 {1}
subjectCode, // 受试者 {2}
subjectVisit.VisitName, // 访视 {3}
dictionValue[0], // 核查状态 {4}
_systemEmailConfig.SiteUrl // 链接 {5}
);
return (topicStr, htmlBodyStr);
};
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc);
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
}
}
}
}
@ -619,7 +665,7 @@ public class QCClaimTaskEventConsumer(
IRepository<QCChallenge> _qCChallengeRepository,
IRepository<QCChallengeDialog> _qCChallengeDialogRepository,
IRepository<Dictionary> _dictionaryRepository,
IRepository<EmailNoticeConfig> _emailNoticeConfigrepository,
IRepository<TrialEmailNoticeConfig> _trialEmailNoticeConfigrepository,
IOptionsMonitor<SystemEmailSendConfig> systemEmailConfig) : IConsumer<QCClaimTaskEvent>
{
private readonly SystemEmailSendConfig _systemEmailConfig = systemEmailConfig.CurrentValue;
@ -628,87 +674,90 @@ public class QCClaimTaskEventConsumer(
Console.WriteLine("发送(Code007008) 【QC 领取了质控任务】邮件!!!");
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
var subjectVisitId = context.Message.SubjectVisitId;
var subjectVisit = await _subjectVisitRepository.Where(x => x.Id == subjectVisitId).Include(x => x.Subject).Include(x => x.CurrentActionUser).FirstNotNullAsync();
var trialId = subjectVisit.TrialId;
var subjectVisitList = await _subjectVisitRepository.Where(x => x.CurrentActionUserId == context.Message.CurrentActionUserId && x.TrialId == subjectVisit.TrialId).Include(x => x.Subject).ToListAsync();
var scenario = context.Message.IsPd ? EmailBusinessScenario.PDVerification_PendingImageQC : EmailBusinessScenario.EligibilityVerification_PendingImageQC;
var trialEmailConfig = _trialEmailNoticeConfigrepository.Where(t => t.TrialId == trialId && t.BusinessScenarioEnum == scenario && t.IsAutoSend && t.IsEnable).FirstOrDefault();
var trialUser = await _trialUseRoleRepository.Where(x => x.TrialId == subjectVisit.TrialId).Include(x => x.UserRole).Select(x => x.UserRole).ToListAsync();
var pmandAPm = trialUser.Where(x => x.UserTypeEnum == UserTypeEnum.APM || x.UserTypeEnum == UserTypeEnum.ProjectManager).ToList();
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == subjectVisit.TrialId);
var userinfo = subjectVisit.CurrentActionUser;
var messageToSend = new MimeMessage();
//发件地址
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
foreach (var pm in pmandAPm)
if (trialEmailConfig != null)
{
messageToSend.Cc.Add(new MailboxAddress(String.Empty, pm.EMail));
}
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
var auditStateCode = "AuditStatePE";
if (trialInfo.QCProcessEnum == TrialQCProcess.DoubleAudit)
{
auditStateCode = "AuditStateRC";
}
var subjectVisitList = await _subjectVisitRepository.Where(x => x.CurrentActionUserId == context.Message.CurrentActionUserId && x.TrialId == subjectVisit.TrialId).Include(x => x.Subject).ToListAsync();
var dictionValue = await CommonEmailHelper.TranslationDictionary(new TranslationDictionaryDto()
{
DictionaryRepository = _dictionaryRepository,
IsEn_US = isEn_US,
DictionaryList = new List<DictionaryDto>()
var trialUserList = await _trialUseRoleRepository.Where(x => x.TrialId == subjectVisit.TrialId && x.TrialUser.IsDeleted == false).Include(x => x.UserRole).Select(x => x.UserRole).ToListAsync();
var pmandAPm = trialUserList.Where(x => x.UserTypeEnum == UserTypeEnum.APM || x.UserTypeEnum == UserTypeEnum.ProjectManager).ToList();
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == subjectVisit.TrialId);
var userinfo = subjectVisit.CurrentActionUser;
var messageToSend = new MimeMessage();
//发件地址
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
foreach (var pm in pmandAPm)
{
messageToSend.Cc.Add(new MailboxAddress(String.Empty, pm.EMail));
}
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
var auditStateCode = "AuditStatePE";
if (trialInfo.QCProcessEnum == TrialQCProcess.DoubleAudit)
{
auditStateCode = "AuditStateRC";
}
var dictionValue = await CommonEmailHelper.TranslationDictionary(new TranslationDictionaryDto()
{
DictionaryRepository = _dictionaryRepository,
IsEn_US = isEn_US,
DictionaryList = new List<DictionaryDto>()
{
new DictionaryDto (){DictionaryCode= auditStateCode,EnumValue=subjectVisit.AuditState.GetEnumInt(), }, //审核状态
}
});
});
var subjectcodes = subjectVisitList.Select(x => x.Subject.Code).Distinct().ToList();
var visitnames = subjectVisitList.Select(x => x.VisitName).Distinct().ToList();
var subjectcodes = subjectVisitList.Select(x => x.Subject.Code).Distinct().ToList();
var visitnames = subjectVisitList.Select(x => x.VisitName).Distinct().ToList();
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
{
var subjectCode = subjectVisit.Subject.Code;
var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo, subjectCode, subjectVisit.VisitName);
var htmlBodyStr = string.Format(
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
userinfo.FullName, // 用户名 {0}
trialInfo.ExperimentName, // 项目 {1}
string.Join(',', subjectcodes), // 受试者 {2}
string.Join(',', visitnames), // 访视 {3}
dictionValue[0], // 审核状态 {4}
_systemEmailConfig.SiteUrl // 链接 {5}
);
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
{
var subjectCode = subjectVisit.Subject.Code;
var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo, subjectCode, subjectVisit.VisitName);
var htmlBodyStr = string.Format(
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
userinfo.FullName, // 用户名 {0}
trialInfo.ExperimentName, // 项目 {1}
string.Join(',', subjectcodes), // 受试者 {2}
string.Join(',', visitnames), // 访视 {3}
dictionValue[0], // 审核状态 {4}
return (topicStr, htmlBodyStr);
};
_systemEmailConfig.SiteUrl // 链接 {5}
);
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(_emailNoticeConfigrepository,
return (topicStr, htmlBodyStr);
};
context.Message.IsPd ? EmailBusinessScenario.PDVerification_PendingImageQC : EmailBusinessScenario.EligibilityVerification_PendingImageQC,
messageToSend, emailConfigFunc);
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc);
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
}
}
}

View File

@ -1,4 +1,5 @@
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Spreadsheet;
using IRaCIS.Application.Contracts;
using IRaCIS.Core.Application.Contracts;
using IRaCIS.Core.Application.Helper;
@ -27,7 +28,7 @@ namespace IRaCIS.Core.Application.MassTransit.Recurring
IRepository<VisitTask> _visitTaskRepository,
IRepository<Dictionary> _dictionaryRepository,
IRepository<TrialUserRole> _trialUserRoleRepository,
IRepository<EmailNoticeConfig> _emailNoticeConfigrepository,
IRepository<TrialEmailNoticeConfig> _trialEmailNoticeConfigrepository,
IOptionsMonitor<SystemEmailSendConfig> systemEmailConfig) : IConsumer<UrgentIRUnReadTaskRecurringEvent>
{
private readonly SystemEmailSendConfig _systemEmailConfig = systemEmailConfig.CurrentValue;
@ -35,144 +36,151 @@ namespace IRaCIS.Core.Application.MassTransit.Recurring
public async Task Consume(ConsumeContext<UrgentIRUnReadTaskRecurringEvent> context)
{
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
var trialId = context.Message.TrialId;
var dictionValue = await CommonEmailHelper.TranslationDictionary(new TranslationDictionaryDto()
var scenario = EmailBusinessScenario.ExpeditedReading;
var trialEmailConfig = _trialEmailNoticeConfigrepository.Where(t => t.TrialId == trialId && t.BusinessScenarioEnum == scenario && t.IsAutoSend && t.IsEnable).FirstOrDefault();
if (trialEmailConfig != null)
{
DictionaryRepository = _dictionaryRepository,
IsEn_US = isEn_US,
DictionaryList = new List<DictionaryDto>()
var dictionValue = await CommonEmailHelper.TranslationDictionary(new TranslationDictionaryDto()
{
DictionaryRepository = _dictionaryRepository,
IsEn_US = isEn_US,
DictionaryList = new List<DictionaryDto>()
{
new DictionaryDto (){DictionaryCode= "YesOrNo",EnumValue="true" }
}
});
});
//找到该项目所有的IR 并且有加急 和Pd 未读的任务
//找到该项目所有的IR 并且有加急 和Pd 未读的任务
Expression<Func<VisitTask, bool>> comonTaskFilter = t => t.TrialId == trialId && t.TaskState == TaskState.Effect && t.ReadingTaskState != ReadingTaskState.HaveSigned && t.TaskAllocationState == TaskAllocationState.Allocated;
Expression<Func<VisitTask, bool>> comonTaskFilter = t => t.TrialId == trialId && t.TaskState == TaskState.Effect && t.ReadingTaskState != ReadingTaskState.HaveSigned && t.TaskAllocationState == TaskAllocationState.Allocated;
var trialUserList = _trialUserRoleRepository.Where(t => t.TrialId == trialId && t.TrialUser.JoinTime != null).Select(t => new
{
t.UserId,
t.UserRole.FullName,
t.UserRole.IdentityUser.EMail,
t.UserRole.IdentityUser.UserName,
t.Trial.TrialCode,
t.Trial.ResearchProgramNo
//TrialReadingCriterionList = t.Trial.TrialReadingCriterionList.Select(t => new { t.CriterionName, TrialReadingCriterionId = t.Id }).ToList()
});
foreach (var trialUser in trialUserList)
{
var userId = trialUser.UserId;
var doctorCriterionList = await _trialReadingCriterionRepository.Where(t => t.IsSigned && t.IsConfirm && t.TrialId == trialId && t.Trial.TrialUserRoleList.Any(t => t.UserId == userId))
.Select(c => new
{
TrialReadingCriterionId = c.Id,
CriterionName = c.CriterionName,
UnReadList = c.VisitTaskList.Where(t => t.ExpetidEmailNoticeTime == null) //没有被通知
.Where(t => t.DoctorUserId == userId && t.ReadingTaskState != ReadingTaskState.HaveSigned && t.TaskState == TaskState.Effect)
// 前序 不存在 未一致性核查未通过的
.Where(t => !t.Subject.SubjectVisitList.Any(sv => sv.CheckState != CheckStateEnum.CVPassed && t.VisitTaskNum > sv.VisitNum))
//前序 不存在 未生成任务的访视
.Where(t => c.IsAutoCreate == false ? !t.Subject.SubjectCriteriaEvaluationVisitFilterList.Where(d => d.TrialReadingCriterionId == t.TrialReadingCriterionId).Any(f => f.IsGeneratedTask == false && t.VisitTaskNum > f.SubjectVisit.VisitNum) : true)
.Where(y => y.IsFrontTaskNeedSignButNotSign == false && (y.IsNeedClinicalDataSign == false || y.IsClinicalDataSign == true))
.Where(t => t.SourceSubjectVisit.PDState == PDStateEnum.PDProgress || t.SourceSubjectVisit.IsEnrollmentConfirm ||
t.ReadModule.SubjectVisit.PDState == PDStateEnum.PDProgress || t.ReadModule.SubjectVisit.IsEnrollmentConfirm)
.Select(c => new
{
SubejctCode = c.IsAnalysisCreate ? c.BlindSubjectCode : c.Subject.Code,
c.TaskBlindName,
c.TaskName,
VisitTaskId = c.Id
}).ToList()
}).ToListAsync();
var toTalUnreadCount = doctorCriterionList.SelectMany(t => t.UnReadList).Count();
if (toTalUnreadCount > 0)
var trialUserList = _trialUserRoleRepository.Where(t => t.TrialId == trialId && t.TrialUser.JoinTime != null).Select(t => new
{
var messageToSend = new MimeMessage();
t.UserId,
t.UserRole.FullName,
t.UserRole.IdentityUser.EMail,
t.UserRole.IdentityUser.UserName,
t.Trial.TrialCode,
t.Trial.ResearchProgramNo
//TrialReadingCriterionList = t.Trial.TrialReadingCriterionList.Select(t => new { t.CriterionName, TrialReadingCriterionId = t.Id }).ToList()
});
//发件地址
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
messageToSend.To.Add(new MailboxAddress(trialUser.FullName, trialUser.EMail));
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
foreach (var trialUser in trialUserList)
{
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
var userId = trialUser.UserId;
var doctorCriterionList = await _trialReadingCriterionRepository.Where(t => t.IsSigned && t.IsConfirm && t.TrialId == trialId && t.Trial.TrialUserRoleList.Any(t => t.UserId == userId))
.Select(c => new
{
TrialReadingCriterionId = c.Id,
CriterionName = c.CriterionName,
UnReadList = c.VisitTaskList.Where(t => t.ExpetidEmailNoticeTime == null) //没有被通知
.Where(t => t.DoctorUserId == userId && t.ReadingTaskState != ReadingTaskState.HaveSigned && t.TaskState == TaskState.Effect)
// 前序 不存在 未一致性核查未通过的
.Where(t => !t.Subject.SubjectVisitList.Any(sv => sv.CheckState != CheckStateEnum.CVPassed && t.VisitTaskNum > sv.VisitNum))
//前序 不存在 未生成任务的访视
.Where(t => c.IsAutoCreate == false ? !t.Subject.SubjectCriteriaEvaluationVisitFilterList.Where(d => d.TrialReadingCriterionId == t.TrialReadingCriterionId).Any(f => f.IsGeneratedTask == false && t.VisitTaskNum > f.SubjectVisit.VisitNum) : true)
.Where(y => y.IsFrontTaskNeedSignButNotSign == false && (y.IsNeedClinicalDataSign == false || y.IsClinicalDataSign == true))
.Where(t => t.SourceSubjectVisit.PDState == PDStateEnum.PDProgress || t.SourceSubjectVisit.IsEnrollmentConfirm ||
t.ReadModule.SubjectVisit.PDState == PDStateEnum.PDProgress || t.ReadModule.SubjectVisit.IsEnrollmentConfirm)
.Select(c => new
{
SubejctCode = c.IsAnalysisCreate ? c.BlindSubjectCode : c.Subject.Code,
c.TaskBlindName,
c.TaskName,
VisitTaskId = c.Id
}).ToList()
}).ToListAsync();
var toTalUnreadCount = doctorCriterionList.SelectMany(t => t.UnReadList).Count();
if (toTalUnreadCount > 0)
{
var topicStr = string.Format(input.topicStr, trialUser.ResearchProgramNo);
var messageToSend = new MimeMessage();
var template = input.htmlBodyStr;
//发件地址
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
messageToSend.To.Add(new MailboxAddress(trialUser.FullName, trialUser.EMail));
//正则提取循环的部分 {%for%}(.*?){%end for%}
string pattern = @"{%for%}(.*?){%end for%}";
var match = Regex.Match(template, pattern, RegexOptions.Singleline);
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
string criteriaTemplate = match.Groups[1].Value; // 提取循环模板
// 构建循环内容
string criteriaDetails = "";
foreach (var criteria in doctorCriterionList)
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
{
if (criteria.UnReadList.Count() > 0)
var topicStr = string.Format(input.topicStr, trialUser.ResearchProgramNo);
var template = input.htmlBodyStr;
//正则提取循环的部分 {%for%}(.*?){%end for%}
string pattern = @"{%for%}(.*?){%end for%}";
var match = Regex.Match(template, pattern, RegexOptions.Singleline);
string criteriaTemplate = match.Groups[1].Value; // 提取循环模板
// 构建循环内容
string criteriaDetails = "";
foreach (var criteria in doctorCriterionList)
{
criteriaDetails += string.Format(criteriaTemplate, criteria.CriterionName, criteria.UnReadList.Select(t => t.SubejctCode).Distinct().Count(), criteria.UnReadList.Count());
if (criteria.UnReadList.Count() > 0)
{
criteriaDetails += string.Format(criteriaTemplate, criteria.CriterionName, criteria.UnReadList.Select(t => t.SubejctCode).Distinct().Count(), criteria.UnReadList.Count());
}
}
}
// 用循环内容替换原始模板中的循环部分
string emailContent = template.Replace(criteriaTemplate, criteriaDetails).Replace("{%for%}", "").Replace("{%end for%}", "");
// 用循环内容替换原始模板中的循环部分
string emailContent = template.Replace(criteriaTemplate, criteriaDetails).Replace("{%for%}", "").Replace("{%end for%}", "");
var htmlBodyStr = string.Format(CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, emailContent),
trialUser.FullName,
var htmlBodyStr = string.Format(CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, emailContent),
trialUser.FullName,
DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
toTalUnreadCount,
DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
toTalUnreadCount,
trialUser.ResearchProgramNo,
dictionValue[0],
_systemEmailConfig.SiteUrl
);
trialUser.ResearchProgramNo,
dictionValue[0],
_systemEmailConfig.SiteUrl
);
return (topicStr, htmlBodyStr);
};
return (topicStr, htmlBodyStr);
};
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(_emailNoticeConfigrepository, EmailBusinessScenario.ExpeditedReading, messageToSend, emailConfigFunc);
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc);
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
//处理标记已通知的任务
//处理标记已通知的任务
var visitTaskIdList = doctorCriterionList.Where(t => t.UnReadList.Count() > 0).SelectMany(t => t.UnReadList.Select(u => u.VisitTaskId)).ToList();
await _visitTaskRepository.BatchUpdateNoTrackingAsync(t => visitTaskIdList.Contains(t.Id), u => new VisitTask() { ExpetidEmailNoticeTime = DateTime.Now });
}
var visitTaskIdList = doctorCriterionList.Where(t => t.UnReadList.Count() > 0).SelectMany(t => t.UnReadList.Select(u => u.VisitTaskId)).ToList();
await _visitTaskRepository.BatchUpdateNoTrackingAsync(t => visitTaskIdList.Contains(t.Id), u => new VisitTask() { ExpetidEmailNoticeTime = DateTime.Now });
}
}
}
}
}

View File

@ -28,10 +28,10 @@ public static class OldRecurringEmailHelper
Guid trialId, EmailBusinessScenario businessScenario,
Func<TrialEmailNoticeConfig, (string topicStr, string htmlBodyStr, bool isEn_us, Guid? onlyToUserId)> topicAndHtmlFunc,
Guid? trialSiteId = null, Guid? trialReadingCriterionId = null)
Guid? trialSiteId = null)
{
//找到配置
var trialEmailConfig = await _trialEmailNoticeConfigRepository.Where(t => t.TrialId == trialId && t.TrialReadingCriterionId == trialReadingCriterionId && t.BusinessScenarioEnum == businessScenario, ignoreQueryFilters: true)
var trialEmailConfig = await _trialEmailNoticeConfigRepository.Where(t => t.TrialId == trialId && t.BusinessScenarioEnum == businessScenario, ignoreQueryFilters: true)
.Include(t => t.TrialEmailNoticeUserList).Include(t => t.TrialEmailBlackUserList).FirstOrDefaultAsync();

View File

@ -37,4 +37,45 @@ public class UrgentIRUnReadTaskRecurringEvent : DomainEvent
public Guid TrialId { get; set; }
}
/// <summary>
/// 定时提醒
/// </summary>
public class SystemDocumentErverDayEvent : DomainEvent
{
}
public class SystemDocumentPublishEvent : DomainEvent
{
public List<Guid> Ids { get; set; }
/// <summary>
/// 新增的需要发送邮件的用户角色ID列表
/// 如果为null或空则发送给所有相关角色
/// </summary>
public List<Guid> NewUserTypeIds { get; set; }
}
/// <summary>
/// 定时提醒
/// </summary>
public class TrialDocumentErverDayEvent : DomainEvent
{
}
public class TrialDocumentPublishEvent : DomainEvent
{
public List<Guid> Ids { get; set; }
/// <summary>
/// 新增的需要发送邮件的用户角色ID列表
/// 如果为null或空则发送给所有相关角色
/// </summary>
public List<Guid> NewUserTypeIds { get; set; }
}

View File

@ -0,0 +1,277 @@
using DocumentFormat.OpenXml;
using IRaCIS.Application.Contracts;
using IRaCIS.Core.Application.Contracts;
using IRaCIS.Core.Application.Helper;
using IRaCIS.Core.Application.MassTransit.Consumer;
using IRaCIS.Core.Application.Service.Reading.Dto;
using IRaCIS.Core.Domain.Models;
using MassTransit;
using Microsoft.Extensions.Options;
using MimeKit;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Reactive.Joins;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace IRaCIS.Core.Application.MassTransit.Recurring
{
/// <summary>
/// 定时过期提醒
/// </summary>
public class SystemDocumentErverDayEventConsumer(
IRepository<ReadingQuestionCriterionTrial> _trialReadingCriterionRepository,
IRepository<VisitTask> _visitTaskRepository,
IRepository<SystemDocument> _systemDocumentRepository,
IRepository<IdentityUser> _identityUserRepository,
IRepository<SystemDocConfirmedIdentityUser> _systemDocConfirmedUserRepository,
IRepository<Dictionary> _dictionaryRepository,
IRepository<TrialUserRole> _trialUserRoleRepository,
IRepository<EmailNoticeConfig> _emailNoticeConfigrepository,
IOptionsMonitor<SystemEmailSendConfig> systemEmailConfig) : IConsumer<SystemDocumentErverDayEvent>
{
private readonly SystemEmailSendConfig _systemEmailConfig = systemEmailConfig.CurrentValue;
public async Task Consume(ConsumeContext<SystemDocumentErverDayEvent> context)
{
DateTime now = DateTime.Now;
Console.WriteLine("发送定时过期提醒");
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
var systemDocQuery =
from sysDoc in _systemDocumentRepository.AsQueryable(false)
from identityUser in _identityUserRepository.AsQueryable(false).Where(t => t.UserRoleList.Where(t => t.IsUserRoleDisabled == false).Any(t => sysDoc.NeedConfirmedUserTypeList.AsQueryable().Any(c => c.NeedConfirmUserTypeId == t.UserTypeId)))
join confirmc in _systemDocConfirmedUserRepository.Where() on new { ConfirmUserId = identityUser.Id, SystemDocumentId = sysDoc.Id } equals new { confirmc.ConfirmUserId, confirmc.SystemDocumentId } into cc
from confirm in cc.DefaultIfEmpty()
select new UnionDocumentWithConfirmInfoView()
{
IsSystemDoc = true,
Id = sysDoc.Id,
CreateTime = sysDoc.CreateTime,
IsDeleted = sysDoc.IsDeleted,
SignViewMinimumMinutes = sysDoc.SignViewMinimumMinutes,
Name = sysDoc.Name,
Path = sysDoc.Path,
FileTypeId = sysDoc.FileTypeId,
UpdateTime = sysDoc.UpdateTime,
ConfirmUserId = identityUser.Id,
ConfirmTime = confirm.ConfirmTime,
UserCreateTime = identityUser.CreateTime,
CurrentStaffTrainDays = sysDoc.CurrentStaffTrainDays,
NewStaffTrainDays = sysDoc.NewStaffTrainDays,
RealName = identityUser.FullName,
UserName = identityUser.UserName,
IsNeedSendEmial = identityUser.IsZhiZhun || (!identityUser.IsZhiZhun && sysDoc.DocUserSignType == DocUserSignType.InnerAndOuter),
FullFilePath = sysDoc.Path
};
var datalist = await systemDocQuery.IgnoreQueryFilters().Where(t => t.IsDeleted == false && t.ConfirmTime == null)
.ToListAsync();
datalist = datalist.Where(x => x.SuggestFinishTime != null && x.SuggestFinishTime.Value.Date == DateTime.Now.Date)
.Where(x => x.IsNeedSendEmial).ToList();
var confirmUserIdList = datalist.Select(t => t.ConfirmUserId).Distinct().ToList();
var userinfoList = await _identityUserRepository.Where(x => confirmUserIdList.Contains(x.Id)).ToListAsync();
Console.WriteLine("发送定时过期提醒:人员数量" + userinfoList.Count);
int index = 1;
foreach (var userinfo in userinfoList)
{
try
{
Console.WriteLine($"{index}发送定时过期提醒,邮箱:{userinfo.EMail},姓名{userinfo.UserName}");
index++;
var messageToSend = new MimeMessage();
//发件地址
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
{
var topicStr = string.Format(input.topicStr, companyName);
var htmlBodyStr = string.Format(
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
userinfo.UserName, // 用户名 {0}
_systemEmailConfig.SiteUrl
);
return (topicStr, htmlBodyStr);
};
var scenario = EmailBusinessScenario.GeneralTraining_ExpirationNotification;
var emailConfig = _emailNoticeConfigrepository.Where(t => t.BusinessScenarioEnum == scenario && t.IsAutoSend && t.IsEnable).FirstOrDefault();
if (emailConfig != null)
{
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(emailConfig, messageToSend, emailConfigFunc);
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
}
}
catch (Exception)
{
}
}
}
}
/// <summary>
/// 生效通知
/// </summary>
public class SystemDocumentPublishEventConsumer(
IRepository<ReadingQuestionCriterionTrial> _trialReadingCriterionRepository,
IRepository<VisitTask> _visitTaskRepository,
IRepository<SystemDocument> _systemDocumentRepository,
IRepository<IdentityUser> _identityUserRepository,
IRepository<SystemDocConfirmedIdentityUser> _systemDocConfirmedUserRepository,
IRepository<Dictionary> _dictionaryRepository,
IRepository<TrialUserRole> _trialUserRoleRepository,
IRepository<EmailNoticeConfig> _emailNoticeConfigrepository,
IOptionsMonitor<SystemEmailSendConfig> systemEmailConfig) : IConsumer<SystemDocumentPublishEvent>
{
private readonly SystemEmailSendConfig _systemEmailConfig = systemEmailConfig.CurrentValue;
public async Task Consume(ConsumeContext<SystemDocumentPublishEvent> context)
{
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
// 记录是否只发送给新增角色的日志
if (context.Message.NewUserTypeIds != null && context.Message.NewUserTypeIds.Any())
{
Console.WriteLine($"只发送给新增的角色,角色数量: {context.Message.NewUserTypeIds.Count}");
}
// 构建查询
IQueryable<UnionDocumentWithConfirmInfoView> systemDocQuery;
if (context.Message.NewUserTypeIds != null && context.Message.NewUserTypeIds.Any())
{
// 只查询新增角色的用户
systemDocQuery =
from sysDoc in _systemDocumentRepository.AsQueryable(false).Where(x => context.Message.Ids.Contains(x.Id))
from identityUser in _identityUserRepository.AsQueryable(false)
.Where(t => t.Status == UserStateEnum.Enable &&
t.UserRoleList.Where(t => t.IsUserRoleDisabled == false)
.Any(t => context.Message.NewUserTypeIds.Contains(t.UserTypeId) &&
sysDoc.NeedConfirmedUserTypeList.AsQueryable().Any(c => c.NeedConfirmUserTypeId == t.UserTypeId)))
select new UnionDocumentWithConfirmInfoView()
{
IsSystemDoc = true,
Id = sysDoc.Id,
CreateTime = sysDoc.CreateTime,
IsDeleted = sysDoc.IsDeleted,
SignViewMinimumMinutes = sysDoc.SignViewMinimumMinutes,
Name = sysDoc.Name,
Path = sysDoc.Path,
FileTypeId = sysDoc.FileTypeId,
UpdateTime = sysDoc.UpdateTime,
ConfirmUserId = identityUser.Id,
RealName = identityUser.FullName,
UserName = identityUser.UserName,
IsNeedSendEmial = identityUser.IsZhiZhun || (!identityUser.IsZhiZhun && sysDoc.DocUserSignType == DocUserSignType.InnerAndOuter),
FullFilePath = sysDoc.Path
};
}
else
{
// 查询所有相关角色的用户
systemDocQuery =
from sysDoc in _systemDocumentRepository.AsQueryable(false).Where(x => context.Message.Ids.Contains(x.Id))
from identityUser in _identityUserRepository.AsQueryable(false)
.Where(t => t.Status == UserStateEnum.Enable &&
t.UserRoleList.Where(t => t.IsUserRoleDisabled == false)
.Any(t => sysDoc.NeedConfirmedUserTypeList.AsQueryable().Any(c => c.NeedConfirmUserTypeId == t.UserTypeId)))
select new UnionDocumentWithConfirmInfoView()
{
IsSystemDoc = true,
Id = sysDoc.Id,
CreateTime = sysDoc.CreateTime,
IsDeleted = sysDoc.IsDeleted,
SignViewMinimumMinutes = sysDoc.SignViewMinimumMinutes,
Name = sysDoc.Name,
Path = sysDoc.Path,
FileTypeId = sysDoc.FileTypeId,
UpdateTime = sysDoc.UpdateTime,
ConfirmUserId = identityUser.Id,
RealName = identityUser.FullName,
UserName = identityUser.UserName,
IsNeedSendEmial = identityUser.IsZhiZhun || (!identityUser.IsZhiZhun && sysDoc.DocUserSignType == DocUserSignType.InnerAndOuter),
FullFilePath = sysDoc.Path
};
}
var datalist = await systemDocQuery.IgnoreQueryFilters().Where(x => x.IsNeedSendEmial).ToListAsync();
var confirmUserIdList = datalist.Select(t => t.ConfirmUserId).Distinct().ToList();
var userinfoList = await _identityUserRepository.Where(x => confirmUserIdList.Contains(x.Id)).ToListAsync();
int index = 1;
foreach (var userinfo in userinfoList)
{
string msg = $"{index}生效通知,邮箱:{userinfo.EMail},姓名{userinfo.UserName},";
index++;
try
{
var messageToSend = new MimeMessage();
//发件地址
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
{
var topicStr = string.Format(input.topicStr, companyName);
var htmlBodyStr = string.Format(
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
userinfo.UserName, // 用户名 {0}
_systemEmailConfig.SiteUrl
);
return (topicStr, htmlBodyStr);
};
var scenario = EmailBusinessScenario.GeneralTraining_EffectiveNotification;
var emailConfig = _emailNoticeConfigrepository.Where(t => t.BusinessScenarioEnum == scenario && t.IsAutoSend && t.IsEnable).FirstOrDefault();
if (emailConfig != null)
{
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(emailConfig, messageToSend, emailConfigFunc);
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
msg += "发送成功";
}
}
catch (Exception)
{
msg += "发送失败";
}
Console.WriteLine(msg);
}
}
}
}

View File

@ -0,0 +1,307 @@
using DocumentFormat.OpenXml;
using IRaCIS.Application.Contracts;
using IRaCIS.Core.Application.Contracts;
using IRaCIS.Core.Application.Helper;
using IRaCIS.Core.Application.MassTransit.Consumer;
using IRaCIS.Core.Application.Service.Reading.Dto;
using IRaCIS.Core.Domain.Models;
using MassTransit;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using MimeKit;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Reactive.Joins;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace IRaCIS.Core.Application.MassTransit.Recurring
{
/// <summary>
/// 定时过期提醒
/// </summary>
public class TrialDocumentErverDayEventConsumer(
IRepository<ReadingQuestionCriterionTrial> _trialReadingCriterionRepository,
IRepository<VisitTask> _visitTaskRepository,
IRepository<SystemDocument> _systemDocumentRepository,
IRepository<IdentityUser> _identityUserRepository,
IRepository<SystemDocConfirmedIdentityUser> _systemDocConfirmedUserRepository,
IRepository<Dictionary> _dictionaryRepository,
IRepository<TrialUserRole> _trialUserRoleRepository, IRepository<TrialDocument> _trialDocumentRepository,
IRepository<Trial> _trialRepository,
ISystemDocumentService _systemDocumentService,
IRepository<SystemDocNeedConfirmedUserType> _systemDocNeedConfirmedUserTypeRepository,
IRepository<TrialDocNeedConfirmedUserType> _trialDocNeedConfirmedUserTypeRepository,
IServiceScopeFactory serviceScopeFactory,
IRepository<TrialIdentityUser> _trialIdentityUserRepository,
IRepository<TrialDocConfirmedIdentityUser> _trialDocConfirmedUserRepository,
IRepository<ReadingQuestionCriterionTrial> _readingQuestionCriterionTrialRepository,
IRepository<EmailNoticeConfig> _emailNoticeConfigrepository,
IOptionsMonitor<SystemEmailSendConfig> systemEmailConfig) : IConsumer<TrialDocumentErverDayEvent>
{
private readonly SystemEmailSendConfig _systemEmailConfig = systemEmailConfig.CurrentValue;
public async Task Consume(ConsumeContext<TrialDocumentErverDayEvent> context)
{
DateTime now = DateTime.Now;
Console.WriteLine("发送定时项目过期提醒");
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
var trialDocQuery =
from trialDoc in _trialDocumentRepository.AsQueryable(true)
join trialIdentityUser in _trialIdentityUserRepository.Where(x => x.IsDeleted == false) on trialDoc.TrialId equals trialIdentityUser.TrialId
join trialUserRole in _trialUserRoleRepository.Where(x => x.IsDeleted == false) on trialIdentityUser.Id equals trialUserRole.TrialUserId
from identityUser in _identityUserRepository.AsQueryable(false)
.Where(t => t.Status == UserStateEnum.Enable &&
t.Id == trialIdentityUser.IdentityUserId &&
t.UserRoleList.Where(t => t.IsUserRoleDisabled == false)
.Any(t => trialDoc.NeedConfirmedUserTypeList.AsQueryable().Any(c => c.NeedConfirmUserTypeId == t.UserTypeId)))
join confirm in _trialDocConfirmedUserRepository.Where() on
new { trialIdentityUser.IdentityUserId, TrialDocumentId = trialDoc.Id } equals new { IdentityUserId = confirm.ConfirmUserId, confirm.TrialDocumentId } into cc
from confirm in cc.DefaultIfEmpty()
select new TrialSignDocView()
{
TrialCode = trialDoc.Trial.TrialCode,
ResearchProgramNo = trialDoc.Trial.ResearchProgramNo,
ExperimentName = trialDoc.Trial.ExperimentName,
CurrentStaffTrainDays = trialDoc.CurrentStaffTrainDays,
NewStaffTrainDays = trialDoc.NewStaffTrainDays,
Id = trialDoc.Id,
IsSystemDoc = false,
CreateTime = trialDoc.CreateTime,
FullFilePath = trialDoc.Path,
IsDeleted = trialDoc.IsDeleted,
Name = trialDoc.Name,
Path = trialDoc.Path,
FileTypeId = trialDoc.FileTypeId,
UpdateTime = trialDoc.UpdateTime,
SignViewMinimumMinutes = trialDoc.SignViewMinimumMinutes,
//IsConfirmed = confirm.ConfirmTime != null,
ConfirmUserId = identityUser.Id,
ConfirmTime = confirm.ConfirmTime,
RealName = trialIdentityUser.IdentityUser.FullName,
UserName = trialIdentityUser.IdentityUser.UserName,
UserCreateTime= trialIdentityUser.CreateTime,
IdentityUserTypeList = trialIdentityUser.TrialUserRoleList.Select(t => t.UserRole.UserTypeRole.UserTypeShortName).ToList(),
DocNeedSignUserTypeList = trialDoc.NeedConfirmedUserTypeList.Select(t => t.UserTypeRole.UserTypeShortName).ToList(),
};
var datalist = await trialDocQuery.IgnoreQueryFilters().Where(t => t.IsDeleted == false && t.ConfirmTime == null&&t.ConfirmTime==null)
.ToListAsync();
datalist = datalist.Where(x => x.SuggestFinishTime != null && x.SuggestFinishTime.Value.Date == DateTime.Now.Date)
.ToList();
var confirmUserIdList = datalist.Select(t => t.ConfirmUserId).Distinct().ToList();
var userinfoList = await _identityUserRepository.Where(x => confirmUserIdList.Contains(x.Id)).ToListAsync();
Console.WriteLine("发送定时项目过期提醒:人员数量" + userinfoList.Count);
int index = 1;
foreach (var userinfo in userinfoList)
{
try
{
Console.WriteLine($"{index}发送定时过期提醒,邮箱:{userinfo.EMail},姓名{userinfo.UserName}");
index++;
var messageToSend = new MimeMessage();
//发件地址
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
{
var topicStr = string.Format(input.topicStr, companyName);
var htmlBodyStr = string.Format(
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
userinfo.UserName, // 用户名 {0}
_systemEmailConfig.SiteUrl
);
return (topicStr, htmlBodyStr);
};
var scenario = EmailBusinessScenario.TrialTraining_ExpirationNotification;
var emailConfig = _emailNoticeConfigrepository.Where(t => t.BusinessScenarioEnum == scenario && t.IsAutoSend && t.IsEnable).FirstOrDefault();
if (emailConfig != null)
{
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(emailConfig, messageToSend, emailConfigFunc);
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
}
}
catch (Exception)
{
}
}
}
}
/// <summary>
/// 生效通知
/// </summary>
public class TrialDocumentPublishEventConsumer(
IRepository<ReadingQuestionCriterionTrial> _trialReadingCriterionRepository,
IRepository<VisitTask> _visitTaskRepository,
IRepository<TrialDocument> _trialDocumentRepository,
IRepository<IdentityUser> _identityUserRepository,
IRepository<TrialIdentityUser> _trialIdentityUserRepository,
IRepository<Dictionary> _dictionaryRepository,
IRepository<TrialUserRole> _trialUserRoleRepository,
IRepository<EmailNoticeConfig> _emailNoticeConfigrepository,
IOptionsMonitor<SystemEmailSendConfig> systemEmailConfig) : IConsumer<TrialDocumentPublishEvent>
{
private readonly SystemEmailSendConfig _systemEmailConfig = systemEmailConfig.CurrentValue;
public async Task Consume(ConsumeContext<TrialDocumentPublishEvent> context)
{
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
// 记录是否只发送给新增角色的日志
if (context.Message.NewUserTypeIds != null && context.Message.NewUserTypeIds.Any())
{
Console.WriteLine($"只发送给新增项目的角色,角色数量: {context.Message.NewUserTypeIds.Count}");
}
// 构建查询
IQueryable<UnionDocumentWithConfirmInfoView> systemDocQuery;
if (context.Message.NewUserTypeIds != null && context.Message.NewUserTypeIds.Any())
{
// 只查询新增角色的用户
systemDocQuery =
from trialDoc in _trialDocumentRepository.AsQueryable(false).Where(x => context.Message.Ids.Contains(x.Id))
join trialIdentityUser in _trialIdentityUserRepository.Where(x=>x.IsDeleted==false) on trialDoc.TrialId equals trialIdentityUser.TrialId
join trialUserRole in _trialUserRoleRepository.Where(x => x.IsDeleted == false) on trialIdentityUser.Id equals trialUserRole.TrialUserId
from identityUser in _identityUserRepository.AsQueryable(false)
.Where(t => t.Status == UserStateEnum.Enable &&
t.Id == trialIdentityUser.IdentityUserId &&
t.UserRoleList.Where(t => t.IsUserRoleDisabled == false)
.Any(t => context.Message.NewUserTypeIds.Contains(t.UserTypeId) &&
trialDoc.NeedConfirmedUserTypeList.AsQueryable().Any(c => c.NeedConfirmUserTypeId == t.UserTypeId)))
select new UnionDocumentWithConfirmInfoView()
{
IsSystemDoc = true,
Id = trialDoc.Id,
CreateTime = trialDoc.CreateTime,
IsDeleted = trialDoc.IsDeleted,
SignViewMinimumMinutes = trialDoc.SignViewMinimumMinutes,
Name = trialDoc.Name,
Path = trialDoc.Path,
FileTypeId = trialDoc.FileTypeId,
UpdateTime = trialDoc.UpdateTime,
ConfirmUserId = identityUser.Id,
RealName = identityUser.FullName,
UserName = identityUser.UserName,
IsNeedSendEmial = identityUser.IsZhiZhun,
FullFilePath = trialDoc.Path
};
}
else
{
// 查询所有相关角色的用户
systemDocQuery =
from trialDoc in _trialDocumentRepository.AsQueryable(false).Where(x => context.Message.Ids.Contains(x.Id))
join trialIdentityUser in _trialIdentityUserRepository.Where(x => x.IsDeleted == false) on trialDoc.TrialId equals trialIdentityUser.TrialId
join trialUserRole in _trialUserRoleRepository.Where(x=>x.IsDeleted==false) on trialIdentityUser.Id equals trialUserRole.TrialUserId
from identityUser in _identityUserRepository.AsQueryable(false)
.Where(t => t.Status == UserStateEnum.Enable &&
t.Id== trialIdentityUser.IdentityUserId&&
t.UserRoleList.Where(t => t.IsUserRoleDisabled == false)
.Any(t => trialDoc.NeedConfirmedUserTypeList.AsQueryable().Any(c => c.NeedConfirmUserTypeId == t.UserTypeId)))
select new UnionDocumentWithConfirmInfoView()
{
IsSystemDoc = false,
Id = trialDoc.Id,
CreateTime = trialDoc.CreateTime,
IsDeleted = trialDoc.IsDeleted,
SignViewMinimumMinutes = trialDoc.SignViewMinimumMinutes,
Name = trialDoc.Name,
Path = trialDoc.Path,
FileTypeId = trialDoc.FileTypeId,
UpdateTime = trialDoc.UpdateTime,
ConfirmUserId = identityUser.Id,
RealName = identityUser.FullName,
UserName = identityUser.UserName,
IsNeedSendEmial = identityUser.IsZhiZhun ,
FullFilePath = trialDoc.Path
};
}
var datalist = await systemDocQuery.IgnoreQueryFilters().ToListAsync();
var confirmUserIdList = datalist.Select(t => t.ConfirmUserId).Distinct().ToList();
var userinfoList = await _identityUserRepository.Where(x => confirmUserIdList.Contains(x.Id)).ToListAsync();
int index = 1;
foreach (var userinfo in userinfoList)
{
string msg = $"{index}项目生效通知,邮箱:{userinfo.EMail},姓名{userinfo.UserName},";
index++;
try
{
var messageToSend = new MimeMessage();
//发件地址
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
{
var topicStr = string.Format(input.topicStr, companyName);
var htmlBodyStr = string.Format(
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
userinfo.UserName, // 用户名 {0}
_systemEmailConfig.SiteUrl
);
return (topicStr, htmlBodyStr);
};
var scenario = EmailBusinessScenario.TrialTraining_EffectiveNotification;
var emailConfig = _emailNoticeConfigrepository.Where(t => t.BusinessScenarioEnum == scenario && t.IsEnable).FirstOrDefault();
if (emailConfig != null)
{
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(emailConfig, messageToSend, emailConfigFunc);
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
msg += "发送成功";
}
}
catch (Exception)
{
msg += "发送失败";
}
Console.WriteLine(msg);
}
}
}
}

View File

@ -214,6 +214,10 @@ namespace IRaCIS.Core.Application.ViewModel
/// </summary>
public bool IsManualGeneration { get; set; }
//public bool IsAfterConvertedTask { get; set; }
public string PMBackReason { get; set; }
public int? RandomOrder { get; set; }
}
@ -233,7 +237,7 @@ namespace IRaCIS.Core.Application.ViewModel
//重阅原始编号
//public string ReReadingOriginalTaskCode { get; set; }
public string ApplicantName { get; set; }
public Guid Id { get; set; }
@ -339,6 +343,16 @@ namespace IRaCIS.Core.Application.ViewModel
public int UrgentCount { get; set; }
/// <summary>
/// 是否存在未处理的反馈
/// </summary>
public bool IsExistUnprocessedFeedback
{
get
{
return UnReadCanReadTaskList.Any(t => t.IsExistUnprocessedFeedback);
}
}
public List<IRUnreadTaskView> UnReadCanReadTaskList { get; set; } = new List<IRUnreadTaskView>();
@ -355,6 +369,11 @@ namespace IRaCIS.Core.Application.ViewModel
/// 是否是一致性分析产生
/// </summary>
public bool IsAnalysisCreate { get; set; }
/// <summary>
/// 是否存在未处理的反馈
/// </summary>
public bool IsExistUnprocessedFeedback { get; set; }
public bool IsUrgent { get; set; }
@ -465,6 +484,9 @@ namespace IRaCIS.Core.Application.ViewModel
public string? RequestReReadingReason { get; set; }
public ExportResult? ReadingExportType { get; set; }
public int? RandomOrder { get; set; }
public bool? IsRandomOrderList { get; set; }
}
@ -891,6 +913,30 @@ namespace IRaCIS.Core.Application.ViewModel
CancelAssign = 4,
}
public class SetRandomTaskOrderCommand
{
[NotDefault]
public Guid TrialId { get; set; }
[NotDefault]
public Guid TrialReadingCriterionId { get; set; }
[NotDefault]
public Guid DoctorUserId { get; set; }
public bool IsAutoSet { get; set; }
public List<VisitTaskOrderCommand> SetList { get; set; }
}
public class VisitTaskOrderCommand
{
public Guid Id { get; set; }
public int? RandomOrder { get; set; }
}
}

View File

@ -4,10 +4,12 @@
// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。
//--------------------------------------------------------------------
using DocumentFormat.OpenXml.Spreadsheet;
using IRaCIS.Core.Application.Contracts;
using IRaCIS.Core.Application.Filter;
using IRaCIS.Core.Application.Interfaces;
using IRaCIS.Core.Application.ViewModel;
using IRaCIS.Core.Domain.Models;
using IRaCIS.Core.Domain.Share;
using Microsoft.AspNetCore.Mvc;

View File

@ -35,7 +35,7 @@ namespace IRaCIS.Core.Application.Service
IRepository<ReadingQuestionCriterionTrial> _trialReadingCriterionRepository,
IRepository<ClinicalDataTrialSet> _trialClinicalDataSetRepository,
IRepository<ReadingClinicalData> _readingClinicalDataRepository,
IRepository<ReadingConsistentClinicalData> _readingConsistentClinicalDataRepository,
IRepository<ReadingConsistentClinicalData> _readingConsistentClinicalDataRepository,
IMapper _mapper, IUserInfo _userInfo, IStringLocalizer _localizer, IWebHostEnvironment _hostEnvironment, IFusionCache _fusionCache) : BaseService, IVisitTaskHelpeService
{
@ -47,16 +47,16 @@ namespace IRaCIS.Core.Application.Service
/// <param name="inDto"></param>
/// <returns></returns>
public async Task<FileResult> ExportTemplateAsync(ExportTemplateAsyncDto inDto)
public async Task<FileResult> ExportTemplateAsync(ExportTemplateAsyncDto inDto)
{
return await ExcelExportHelper.ExportTemplateAsync(new ExportTemplateServiceDto()
{
Data=inDto.Data,
commonDocumentRepository= _commonDocumentRepository,
TemplateCode=inDto.TemplateCode,
ExportFileName=inDto.ExportFileName,
hostEnvironment=_hostEnvironment,
IsEnglish=_userInfo.IsEn_Us,
Data = inDto.Data,
commonDocumentRepository = _commonDocumentRepository,
TemplateCode = inDto.TemplateCode,
ExportFileName = inDto.ExportFileName,
hostEnvironment = _hostEnvironment,
IsEnglish = _userInfo.IsEn_Us,
});
}
@ -757,248 +757,30 @@ namespace IRaCIS.Core.Application.Service
if (trialReadingCriterionConfig.TaskAllocateObjEnum == TaskAllocateObj.Subject)
{
var allocateSubjectArmList = _visitTaskRepository.Where(t => t.TrialReadingCriterionId == trialReadingCriterionConfig.TrialReadingCriterionId && t.SubjectId == subjectVisit.SubjectId && t.TrialId == trialId && t.DoctorUserId != null && t.ArmEnum != Arm.JudgeArm)
.Select(t => new { t.DoctorUserId, t.ArmEnum }).Distinct().ToList();
//当前任务没有分配医生,初次分配 不处理 只生成任务,后续根据生成的任务 再进行分配
if (allocateSubjectArmList.Count == 0)
{
}
else
if (trialReadingCriterionConfig.IsReadingTaskViewInOrder == ReadingOrder.InOrder)
{
//并且配置了医生
if (assignConfigList.Count > 0 && trialReadingCriterionConfig.IsFollowVisitAutoAssign)
#region 当前访视处理
//配置了医生
if (assignConfigList.Count > 0)
{
//之前有回退到影像上传的访视 那么当前访视一致性核查通过的时候,当前访视生成但是不分配出去(排除失访的)
#region 后续访视 未分配的进行再次分配,重置的或者失效的 需要重新生成新的任务 (PM 有序退回 或者PM 有序 申请重阅)
var beforeBackVisitTask = await _visitTaskRepository.Where(t => t.TrialId == trialId && t.TrialReadingCriterionId == trialReadingCriterionConfig.TrialReadingCriterionId && t.SubjectId == subjectVisit.SubjectId && t.VisitTaskNum < subjectVisit.VisitNum && t.ReadingCategory == ReadingCategory.Visit && t.SourceSubjectVisit.CheckState != CheckStateEnum.CVPassed && t.SourceSubjectVisit.IsLostVisit == false).OrderBy(t => t.VisitTaskNum).FirstOrDefaultAsync();
if (trialReadingCriterionConfig.IsReadingTaskViewInOrder == ReadingOrder.InOrder)
//之前有回退的,那么当前访视任务生成但是不分配
if (beforeBackVisitTask != null)
{
//之前有回退到影像上传的访视 那么当前访视一致性核查通过的时候,当前访视生成但是不分配出去(排除失访的)
var beforeBackVisitTask = await _visitTaskRepository.Where(t => t.TrialId == trialId && t.TrialReadingCriterionId == trialReadingCriterionConfig.TrialReadingCriterionId && t.SubjectId == subjectVisit.SubjectId && t.VisitTaskNum < subjectVisit.VisitNum && t.ReadingCategory == ReadingCategory.Visit && t.SourceSubjectVisit.CheckState != CheckStateEnum.CVPassed && t.SourceSubjectVisit.IsLostVisit == false).OrderBy(t => t.VisitTaskNum).FirstOrDefaultAsync();
//之前有回退的,那么当前访视任务生成但是不分配
if (beforeBackVisitTask != null)
{
//不用进行额外处理
//访视2 PM 回退 基线回退 访视2先一致性核查通过生成访视2任务但是不分配
}
else
{
#region 当前访视根据配置规则分配出去
var defaultState = trialReadingCriterionConfig.FollowVisitAutoAssignDefaultState == TaskAllocateDefaultState.InitAllocated ? TaskAllocationState.InitAllocated : TaskAllocationState.Allocated;
if (assignConfigList.Any(t => t.ArmEnum == Arm.DoubleReadingArm1) && task1 != null)
{
task1.TaskAllocationState = defaultState;
//分配给对应Arm的人
task1.DoctorUserId = assignConfigList.FirstOrDefault(t => t.ArmEnum == Arm.DoubleReadingArm1)!.DoctorUserId;
task1.AllocateTime = DateTime.Now;
task1.SuggesteFinishedTime = GetSuggessFinishTime(true, UrgentType.NotUrget);
}
if (assignConfigList.Any(t => t.ArmEnum == Arm.DoubleReadingArm2) && task2 != null)
{
task2.TaskAllocationState = defaultState;
task2.DoctorUserId = assignConfigList.FirstOrDefault(t => t.ArmEnum == Arm.DoubleReadingArm2)!.DoctorUserId;
task2.AllocateTime = DateTime.Now;
task2.SuggesteFinishedTime = GetSuggessFinishTime(true, UrgentType.NotUrget);
}
#endregion
}
//后续最近的未一致性核查通过的访视任务
var followBackVisitTask = await _visitTaskRepository.Where(t => t.TrialId == trialId && t.TrialReadingCriterionId == trialReadingCriterionConfig.TrialReadingCriterionId && t.SubjectId == subjectVisit.SubjectId && t.VisitTaskNum > subjectVisit.VisitNum && t.ReadingCategory == ReadingCategory.Visit && t.SourceSubjectVisit.CheckState != CheckStateEnum.CVPassed && t.SourceSubjectVisit.IsLostVisit == false).OrderBy(t => t.VisitTaskNum).FirstOrDefaultAsync();
//大于当前访视 同时小于最近的未一致性核查通过的访视任务分配 或者生成
//存在退回访视1 又退回基线 这种情况 生成任务 考虑基线先一致性核查通过但是访视1还未通过时 生成任务
var followVisitTaskList = await _visitTaskRepository
.Where(t => t.TrialId == trialId && t.TrialReadingCriterionId == trialReadingCriterionConfig.TrialReadingCriterionId && t.SubjectId == subjectVisit.SubjectId && t.VisitTaskNum > subjectVisit.VisitNum && t.SourceSubjectVisit.CheckState == CheckStateEnum.CVPassed && t.ReadingCategory == ReadingCategory.Visit && t.IsAnalysisCreate == false, true)
.WhereIf(followBackVisitTask != null, t => t.VisitTaskNum < followBackVisitTask!.VisitTaskNum)
.ToListAsync();
var followVisitGroup = followVisitTaskList.GroupBy(t => t.VisitTaskNum);
//每个访视去判断 是分配还是生成(因为影响哪里有些是取消分配,有些是重阅重置需要重新生成)
foreach (var visitGroup in followVisitGroup)
{
var visit = await _subjectVisitRepository.Where(x => x.Id == visitGroup.First().SourceSubjectVisitId).Select(x => new
{
x.PDState,
x.IsEnrollmentConfirm,
x.IsUrgent,
}).FirstNotNullAsync();
TaskUrgentType? urgentType = null;
if (subjectVisitInfo.PDState == PDStateEnum.PDProgress)
{
urgentType = TaskUrgentType.PDProgress;
}
else if (subjectVisitInfo.IsEnrollmentConfirm)
{
urgentType = TaskUrgentType.EnrollmentConfirm;
}
else if (subjectVisitInfo.IsUrgent)
{
urgentType = TaskUrgentType.VisitUrgent;
}
bool isCanEdit = urgentType == TaskUrgentType.EnrollmentConfirm || urgentType == TaskUrgentType.PDProgress ? false : true;
//如果后续访视已分配有效 就不用处理
if (visitGroup.Any(t => t.TaskState == TaskState.Effect && t.TaskAllocationState == TaskAllocationState.Allocated && t.ArmEnum == Arm.DoubleReadingArm1))
{
//不做处理
}
else
{
var arm1 = visitGroup.FirstOrDefault(t => t.TaskState == TaskState.Effect && t.TaskAllocationState == TaskAllocationState.NotAllocate && t.DoctorUserId == null && t.ArmEnum == Arm.DoubleReadingArm1);
if (arm1 != null)
{
//有可能仅仅只分配了一个Subject 未分配 那么
if (assignConfigList.Any(t => t.ArmEnum == Arm.DoubleReadingArm1) && task1 != null)
{
arm1.IsUrgent = visit.IsUrgent;
arm1.TaskUrgentType = urgentType;
arm1.IsCanEditUrgentState = isCanEdit;
arm1.TaskAllocationState = TaskAllocationState.Allocated;
arm1.AllocateTime = DateTime.Now;
arm1.DoctorUserId = task1.DoctorUserId;
arm1.SuggesteFinishedTime = GetSuggessFinishTime(true, UrgentType.NotUrget);
}
}
else
{
var latestTask = visitGroup.Where(t => t.ArmEnum == Arm.DoubleReadingArm1).OrderByDescending(t => t.CreateTime).First();
var taskOne = await _visitTaskRepository.AddAsync(new VisitTask()
{
TrialId = trialId,
SubjectId = subjectVisit.SubjectId,
IsUrgent = visit.IsUrgent,
TaskUrgentType = urgentType,
IsCanEditUrgentState = isCanEdit,
ArmEnum = Arm.DoubleReadingArm1,//特殊
Code = currentMaxCodeInt + 1,
TaskCode = AppSettings.GetCodeStr(currentMaxCodeInt + 1, nameof(VisitTask)),
ReadingCategory = ReadingCategory.Visit,
SourceSubjectVisitId = latestTask.SourceSubjectVisitId,
VisitTaskNum = latestTask.VisitTaskNum,
TaskBlindName = visitBlindConfig.BlindFollowUpPrefix + " " + visitNumList.IndexOf(latestTask.VisitTaskNum),
TaskName = latestTask.TaskName,
BlindSubjectCode = latestTask.BlindSubjectCode,
BlindTrialSiteCode = latestTask.BlindTrialSiteCode,
IsAnalysisCreate = latestTask.IsAnalysisCreate,
IsSelfAnalysis = latestTask.IsSelfAnalysis,
TaskAllocationState = TaskAllocationState.Allocated,
AllocateTime = DateTime.Now,
DoctorUserId = task1!.DoctorUserId,
SuggesteFinishedTime = GetSuggessFinishTime(true, UrgentType.NotUrget),
TrialReadingCriterionId = latestTask.TrialReadingCriterionId,
IsNeedClinicalDataSign = latestTask.IsNeedClinicalDataSign,
IsClinicalDataSign = latestTask.IsClinicalDataSign
});
currentMaxCodeInt = currentMaxCodeInt + 1;
_fusionCache.Set<int>(CacheKeys.TrialStudyMaxCode(trialId), currentMaxCodeInt, TimeSpan.FromMinutes(30));
}
}
//如果后续访视已分配有效 就不用处理
if (visitGroup.Any(t => t.TaskState == TaskState.Effect && t.TaskAllocationState == TaskAllocationState.Allocated && t.ArmEnum == Arm.DoubleReadingArm2))
{
//不做处理
}
else
{
var arm2 = visitGroup.FirstOrDefault(t => t.TaskState == TaskState.Effect && t.TaskAllocationState == TaskAllocationState.NotAllocate && t.DoctorUserId == null && t.ArmEnum == Arm.DoubleReadingArm2);
if (arm2 != null)
{
//有可能仅仅只分配了一个Subject
if (assignConfigList.Any(t => t.ArmEnum == Arm.DoubleReadingArm2) && task2 != null)
{
arm2.IsUrgent = visit.IsUrgent;
arm2.TaskUrgentType = urgentType;
arm2.IsCanEditUrgentState = isCanEdit;
arm2.TaskAllocationState = TaskAllocationState.Allocated;
arm2.AllocateTime = DateTime.Now;
arm2.DoctorUserId = task2.DoctorUserId;
arm2.SuggesteFinishedTime = GetSuggessFinishTime(true, UrgentType.NotUrget);
}
}
else
{
var latestTask = visitGroup.Where(t => t.ArmEnum == Arm.DoubleReadingArm2).OrderByDescending(t => t.CreateTime).First();
var taskTwo = await _visitTaskRepository.AddAsync(new VisitTask()
{
TrialId = trialId,
SubjectId = subjectVisit.SubjectId,
IsUrgent = visit.IsUrgent,
TaskUrgentType = urgentType,
IsCanEditUrgentState = isCanEdit,
//CheckPassedTime = subjectVisit.CheckPassedTime,
ArmEnum = Arm.DoubleReadingArm2,//特殊
Code = currentMaxCodeInt + 1,
TaskCode = AppSettings.GetCodeStr(currentMaxCodeInt + 1, nameof(VisitTask)),
ReadingCategory = ReadingCategory.Visit,
SourceSubjectVisitId = latestTask.SourceSubjectVisitId,
VisitTaskNum = latestTask.VisitTaskNum,
TaskBlindName = visitBlindConfig.BlindFollowUpPrefix + " " + visitNumList.IndexOf(latestTask.VisitTaskNum),
TaskName = latestTask.TaskName,
BlindSubjectCode = latestTask.BlindSubjectCode,
BlindTrialSiteCode = latestTask.BlindTrialSiteCode,
IsAnalysisCreate = latestTask.IsAnalysisCreate,
IsSelfAnalysis = latestTask.IsSelfAnalysis,
TaskAllocationState = TaskAllocationState.Allocated,
AllocateTime = DateTime.Now,
DoctorUserId = task2!.DoctorUserId,
SuggesteFinishedTime = GetSuggessFinishTime(true, UrgentType.NotUrget),
TrialReadingCriterionId = latestTask.TrialReadingCriterionId,
IsNeedClinicalDataSign = latestTask.IsNeedClinicalDataSign,
IsClinicalDataSign = latestTask.IsClinicalDataSign
});
currentMaxCodeInt = currentMaxCodeInt + 1;
_fusionCache.Set<int>(CacheKeys.TrialStudyMaxCode(trialId), currentMaxCodeInt, TimeSpan.FromMinutes(30));
}
}
}
//不用进行额外处理
//访视2 PM 回退 基线回退 访视2先一致性核查通过生成访视2任务但是不分配
}
//无序的时候 生成任务并分配出去
else
{
#region 当前访视根据配置规则分配出去
var defaultState = trialReadingCriterionConfig.FollowVisitAutoAssignDefaultState == TaskAllocateDefaultState.InitAllocated ? TaskAllocationState.InitAllocated : TaskAllocationState.Allocated;
if (assignConfigList.Any(t => t.ArmEnum == Arm.DoubleReadingArm1) && task1 != null)
@ -1019,21 +801,237 @@ namespace IRaCIS.Core.Application.Service
task2.SuggesteFinishedTime = GetSuggessFinishTime(true, UrgentType.NotUrget);
}
#endregion
}
#endregion
}
else
//后续访视不自动分配,或者配置的医生数量不足,就不进行分配
#endregion
#region 后续访视处理
//后续最近的未一致性核查通过的访视任务
var followBackVisitTask = await _visitTaskRepository.Where(t => t.TrialId == trialId && t.TrialReadingCriterionId == trialReadingCriterionConfig.TrialReadingCriterionId && t.SubjectId == subjectVisit.SubjectId && t.VisitTaskNum > subjectVisit.VisitNum && t.ReadingCategory == ReadingCategory.Visit && t.SourceSubjectVisit.CheckState != CheckStateEnum.CVPassed && t.SourceSubjectVisit.IsLostVisit == false).OrderBy(t => t.VisitTaskNum).FirstOrDefaultAsync();
//大于当前访视 同时小于最近的未一致性核查通过的访视任务分配 或者生成
//存在退回访视1 又退回基线 这种情况 生成任务 考虑基线先一致性核查通过但是访视1还未通过时 生成任务
var followVisitTaskList = await _visitTaskRepository
.Where(t => t.TrialId == trialId && t.TrialReadingCriterionId == trialReadingCriterionConfig.TrialReadingCriterionId && t.SubjectId == subjectVisit.SubjectId && t.VisitTaskNum > subjectVisit.VisitNum && t.SourceSubjectVisit.CheckState == CheckStateEnum.CVPassed && t.ReadingCategory == ReadingCategory.Visit && t.IsAnalysisCreate == false, true)
.WhereIf(followBackVisitTask != null, t => t.VisitTaskNum < followBackVisitTask!.VisitTaskNum)
.ToListAsync();
var followVisitGroup = followVisitTaskList.GroupBy(t => t.VisitTaskNum);
//每个访视去判断 是分配还是生成(因为影响哪里有些是取消分配,有些是重阅重置需要重新生成)
foreach (var visitGroup in followVisitGroup)
{
var visit = await _subjectVisitRepository.Where(x => x.Id == visitGroup.First().SourceSubjectVisitId).Select(x => new
{
x.PDState,
x.IsEnrollmentConfirm,
x.IsUrgent,
}).FirstNotNullAsync();
TaskUrgentType? urgentType = null;
if (subjectVisitInfo.PDState == PDStateEnum.PDProgress)
{
urgentType = TaskUrgentType.PDProgress;
}
else if (subjectVisitInfo.IsEnrollmentConfirm)
{
urgentType = TaskUrgentType.EnrollmentConfirm;
}
else if (subjectVisitInfo.IsUrgent)
{
urgentType = TaskUrgentType.VisitUrgent;
}
bool isCanEdit = urgentType == TaskUrgentType.EnrollmentConfirm || urgentType == TaskUrgentType.PDProgress ? false : true;
//如果后续访视已分配有效 就不用处理
if (visitGroup.Any(t => t.TaskState == TaskState.Effect && t.TaskAllocationState == TaskAllocationState.Allocated && t.ArmEnum == Arm.DoubleReadingArm1))
{
//不做处理
}
else
{
var arm1 = visitGroup.FirstOrDefault(t => t.TaskState == TaskState.Effect && t.TaskAllocationState == TaskAllocationState.NotAllocate && t.DoctorUserId == null && t.ArmEnum == Arm.DoubleReadingArm1);
if (arm1 != null)
{
arm1.IsUrgent = visit.IsUrgent;
arm1.TaskUrgentType = urgentType;
arm1.IsCanEditUrgentState = isCanEdit;
//有可能仅仅只分配了一个Subject 未分配 那么
if (assignConfigList.Any(t => t.ArmEnum == Arm.DoubleReadingArm1) && task1 != null)
{
arm1.TaskAllocationState = TaskAllocationState.Allocated;
arm1.AllocateTime = DateTime.Now;
arm1.DoctorUserId = task1.DoctorUserId;
arm1.SuggesteFinishedTime = GetSuggessFinishTime(true, UrgentType.NotUrget);
}
}
else
{
var latestTask = visitGroup.Where(t => t.ArmEnum == Arm.DoubleReadingArm1).OrderByDescending(t => t.CreateTime).First();
var taskOne = await _visitTaskRepository.AddAsync(new VisitTask()
{
TrialId = trialId,
SubjectId = subjectVisit.SubjectId,
IsUrgent = visit.IsUrgent,
TaskUrgentType = urgentType,
IsCanEditUrgentState = isCanEdit,
ArmEnum = Arm.DoubleReadingArm1,//特殊
Code = currentMaxCodeInt + 1,
TaskCode = AppSettings.GetCodeStr(currentMaxCodeInt + 1, nameof(VisitTask)),
ReadingCategory = ReadingCategory.Visit,
SourceSubjectVisitId = latestTask.SourceSubjectVisitId,
VisitTaskNum = latestTask.VisitTaskNum,
TaskBlindName = visitBlindConfig.BlindFollowUpPrefix + " " + visitNumList.IndexOf(latestTask.VisitTaskNum),
TaskName = latestTask.TaskName,
BlindSubjectCode = latestTask.BlindSubjectCode,
BlindTrialSiteCode = latestTask.BlindTrialSiteCode,
IsAnalysisCreate = latestTask.IsAnalysisCreate,
IsSelfAnalysis = latestTask.IsSelfAnalysis,
TrialReadingCriterionId = latestTask.TrialReadingCriterionId,
IsNeedClinicalDataSign = latestTask.IsNeedClinicalDataSign,
IsClinicalDataSign = latestTask.IsClinicalDataSign
});
if (assignConfigList.Any(t => t.ArmEnum == Arm.DoubleReadingArm1) && task1 != null)
{
taskOne.TaskAllocationState = TaskAllocationState.Allocated;
taskOne.AllocateTime = DateTime.Now;
taskOne. DoctorUserId = task1!.DoctorUserId;
taskOne. SuggesteFinishedTime = GetSuggessFinishTime(true, UrgentType.NotUrget);
}
currentMaxCodeInt = currentMaxCodeInt + 1;
_fusionCache.Set<int>(CacheKeys.TrialStudyMaxCode(trialId), currentMaxCodeInt, TimeSpan.FromMinutes(30));
}
}
//如果后续访视已分配有效 就不用处理
if (visitGroup.Any(t => t.TaskState == TaskState.Effect && t.TaskAllocationState == TaskAllocationState.Allocated && t.ArmEnum == Arm.DoubleReadingArm2))
{
//不做处理
}
else
{
var arm2 = visitGroup.FirstOrDefault(t => t.TaskState == TaskState.Effect && t.TaskAllocationState == TaskAllocationState.NotAllocate && t.DoctorUserId == null && t.ArmEnum == Arm.DoubleReadingArm2);
if (arm2 != null)
{
arm2.IsUrgent = visit.IsUrgent;
arm2.TaskUrgentType = urgentType;
arm2.IsCanEditUrgentState = isCanEdit;
//有可能仅仅只分配了一个Subject
if (assignConfigList.Any(t => t.ArmEnum == Arm.DoubleReadingArm2) && task2 != null)
{
arm2.TaskAllocationState = TaskAllocationState.Allocated;
arm2.AllocateTime = DateTime.Now;
arm2.DoctorUserId = task2.DoctorUserId;
arm2.SuggesteFinishedTime = GetSuggessFinishTime(true, UrgentType.NotUrget);
}
}
else
{
var latestTask = visitGroup.Where(t => t.ArmEnum == Arm.DoubleReadingArm2).OrderByDescending(t => t.CreateTime).First();
var taskTwo = await _visitTaskRepository.AddAsync(new VisitTask()
{
TrialId = trialId,
SubjectId = subjectVisit.SubjectId,
IsUrgent = visit.IsUrgent,
TaskUrgentType = urgentType,
IsCanEditUrgentState = isCanEdit,
//CheckPassedTime = subjectVisit.CheckPassedTime,
ArmEnum = Arm.DoubleReadingArm2,//特殊
Code = currentMaxCodeInt + 1,
TaskCode = AppSettings.GetCodeStr(currentMaxCodeInt + 1, nameof(VisitTask)),
ReadingCategory = ReadingCategory.Visit,
SourceSubjectVisitId = latestTask.SourceSubjectVisitId,
VisitTaskNum = latestTask.VisitTaskNum,
TaskBlindName = visitBlindConfig.BlindFollowUpPrefix + " " + visitNumList.IndexOf(latestTask.VisitTaskNum),
TaskName = latestTask.TaskName,
BlindSubjectCode = latestTask.BlindSubjectCode,
BlindTrialSiteCode = latestTask.BlindTrialSiteCode,
IsAnalysisCreate = latestTask.IsAnalysisCreate,
IsSelfAnalysis = latestTask.IsSelfAnalysis,
TrialReadingCriterionId = latestTask.TrialReadingCriterionId,
IsNeedClinicalDataSign = latestTask.IsNeedClinicalDataSign,
IsClinicalDataSign = latestTask.IsClinicalDataSign
});
if (assignConfigList.Any(t => t.ArmEnum == Arm.DoubleReadingArm2) && task2 != null)
{
taskTwo.TaskAllocationState = TaskAllocationState.Allocated;
taskTwo.AllocateTime = DateTime.Now;
taskTwo.DoctorUserId = task2!.DoctorUserId;
taskTwo.SuggesteFinishedTime = GetSuggessFinishTime(true, UrgentType.NotUrget);
}
currentMaxCodeInt = currentMaxCodeInt + 1;
_fusionCache.Set<int>(CacheKeys.TrialStudyMaxCode(trialId), currentMaxCodeInt, TimeSpan.FromMinutes(30));
}
}
}
}
#endregion
}
else
{
//无序的时候 生成任务并分配出去
if (assignConfigList.Count > 0 && trialReadingCriterionConfig.IsFollowVisitAutoAssign)
{
var defaultState = trialReadingCriterionConfig.FollowVisitAutoAssignDefaultState == TaskAllocateDefaultState.InitAllocated ? TaskAllocationState.InitAllocated : TaskAllocationState.Allocated;
if (assignConfigList.Any(t => t.ArmEnum == Arm.DoubleReadingArm1) && task1 != null)
{
task1.TaskAllocationState = defaultState;
//分配给对应Arm的人
task1.DoctorUserId = assignConfigList.FirstOrDefault(t => t.ArmEnum == Arm.DoubleReadingArm1)!.DoctorUserId;
task1.AllocateTime = DateTime.Now;
task1.SuggesteFinishedTime = GetSuggessFinishTime(true, UrgentType.NotUrget);
}
if (assignConfigList.Any(t => t.ArmEnum == Arm.DoubleReadingArm2) && task2 != null)
{
task2.TaskAllocationState = defaultState;
task2.DoctorUserId = assignConfigList.FirstOrDefault(t => t.ArmEnum == Arm.DoubleReadingArm2)!.DoctorUserId;
task2.AllocateTime = DateTime.Now;
task2.SuggesteFinishedTime = GetSuggessFinishTime(true, UrgentType.NotUrget);
}
}
}
}

View File

@ -74,7 +74,7 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
return await _visitTaskRepository.SaveChangesAsync();
}
public async Task<List<TrialReadingCriterionDto>> GetTrialCriterionList(Guid trialId, bool isHaveSigned = true, bool? isAutoCreate = null)
public async Task<List<TrialReadingCriterionDto>> GetTrialCriterionList(Guid trialId, bool isHaveSigned = true, bool? isAutoCreate = null, bool? isRandom = null)
{
var list = await _readingQuestionCriterionTrialRepository.Where(t => t.TrialId == trialId && t.IsConfirm)
@ -107,7 +107,8 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
return list.AsQueryable().WhereIf(isHaveSigned == true, t => t.ReadingInfoSignTime != null)
.WhereIf(isAutoCreate == false, t => t.IsAutoCreate == isAutoCreate).ToList();
.WhereIf(isAutoCreate == false, t => t.IsAutoCreate == isAutoCreate)
.WhereIf(isRandom == true, t => t.IsReadingTaskViewInOrder == ReadingOrder.Random).ToList();
}
@ -761,6 +762,8 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
.WhereIf(inQuery.EndAllocateDate != null, t => t.AllocateTime < inQuery.EndAllocateDate)
.WhereIf(inQuery.BeginSignTime != null, t => t.SignTime > inQuery.BeginSignTime)
.WhereIf(inQuery.EndSignTime != null, t => t.SignTime < inQuery.EndSignTime)
.WhereIf(inQuery.RandomOrder != null, t => t.RandomOrder == inQuery.RandomOrder)
.WhereIf(inQuery.IsRandomOrderList == true, t => (t.ReadingTaskState == ReadingTaskState.WaitReading || t.ReadingTaskState==ReadingTaskState.Reading) && t.TaskAllocationState==TaskAllocationState.Allocated)
.ProjectTo<ReadingTaskView>(_mapper.ConfigurationProvider);
var defalutSortArray = new string[] { nameof(ReadingTaskView.IsUrgent) + " desc", nameof(ReadingTaskView.SubjectCode), nameof(ReadingTaskView.VisitTaskNum) };
@ -1055,6 +1058,7 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
ReadingCategory = u.ReadingCategory,
IsAnalysisCreate = u.IsAnalysisCreate,
ArmEnum = u.ArmEnum,
IsExistUnprocessedFeedback = u.UserFeedBackList.Any(t => t.State == 0),
TrialReadingCriterionId = u.TrialReadingCriterionId,
IsNeedClinicalDataSign = u.IsNeedClinicalDataSign,
IsClinicalDataSign = u.IsClinicalDataSign,
@ -1085,6 +1089,7 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
ReadingCategory = u.ReadingCategory,
IsAnalysisCreate = u.IsAnalysisCreate,
ArmEnum = u.ArmEnum,
IsExistUnprocessedFeedback = u.UserFeedBackList.Any(t => t.State == 0),
TrialReadingCriterionId = u.TrialReadingCriterionId,
IsNeedClinicalDataSign = u.IsNeedClinicalDataSign,
IsClinicalDataSign = u.IsClinicalDataSign,
@ -1194,6 +1199,7 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
ReadingCategory = u.ReadingCategory,
IsAnalysisCreate = u.IsAnalysisCreate,
ArmEnum = u.ArmEnum,
IsExistUnprocessedFeedback = u.UserFeedBackList.Any(t => t.State == 0),
TrialReadingCriterionId = u.TrialReadingCriterionId,
IsNeedClinicalDataSign = u.IsNeedClinicalDataSign,
IsClinicalDataSign = u.IsClinicalDataSign,
@ -2200,12 +2206,13 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
{
item.Id = Guid.Empty;
item.VisitTaskId = newTask.Id;
item.MeasureData = item.MeasureData.Replace(origenalTask.Id.ToString(), newTask.Id.ToString());
}
_ = _readingCustomTagRepository.AddRangeAsync(readingCustomTagList).Result;
var readingTaskQuestionAnswerList = _readingTaskQuestionAnswerRepository.Where(t => t.VisitTaskId == origenalTask.Id).ToList();
@ -2213,6 +2220,7 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
{
item.Id = Guid.Empty;
item.VisitTaskId = newTask.Id;
}
_ = _readingTaskQuestionAnswerRepository.AddRangeAsync(readingTaskQuestionAnswerList).Result;
@ -2224,9 +2232,11 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
item.Id = Guid.Empty;
item.FristAddTaskId = item.VisitTaskId == item.FristAddTaskId ? newTask.Id : item.FristAddTaskId;
item.VisitTaskId = newTask.Id;
item.MeasureData = item.MeasureData.Replace(origenalTask.Id.ToString(), newTask.Id.ToString());
}
//ReadingTableAnswerRowInfo ReadingTableQuestionAnswer 一起加
var readingTableAnswerRowInfoList = _readingTableAnswerRowInfoRepository.Where(t => t.VisitTaskId == origenalTask.Id).Include(t => t.LesionAnswerList).ToList();
@ -2237,7 +2247,7 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
var originalVisitTaskId = item.VisitTaskId;
var originalFristAddTaskId = item.FristAddTaskId;
var newRowId= NewId.NextSequentialGuid();
var newRowId = NewId.NextSequentialGuid();
foreach (var mark in readingTaskQuestionMarkList)
{
@ -2250,6 +2260,7 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
//默认值是当前任务添加的
item.FristAddTaskId = originalVisitTaskId == originalFristAddTaskId ? newTask.Id : item.FristAddTaskId;
item.MeasureData = item.MeasureData.Replace(origenalTask.Id.ToString(), newTask.Id.ToString());
foreach (var item2 in item.LesionAnswerList)
{
@ -2273,11 +2284,11 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
/// PM 设置任务 退回
/// </summary>
/// <returns></returns>
[HttpPut("{trialId:guid}/{taskId:guid}")]
[HttpPut("{trialId:guid}/{taskId:guid}/{pmBackReason}")]
[UnitOfWork]
[TrialGlobalLimit("AfterStopCannNotOpt")]
public async Task<IResponseOutput> PMSetTaskBack(Guid trialId, Guid taskId)
public async Task<IResponseOutput> PMSetTaskBack(Guid trialId, Guid taskId, string pmBackReason)
{
//var trialConfig = (await _trialRepository.Where(t => t.Id == trialId).Select(t => new { TrialId = t.Id, t.IsReadingTaskViewInOrder, t.ReadingType }).FirstOrDefaultAsync()).IfNullThrowException();
@ -2338,7 +2349,6 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
#region 有序 无序公用流程
//执行类似一致性核查回退流程 回退访视到影像上传流程
await VisitBackAsync(task.SourceSubjectVisitId);
@ -2455,6 +2465,9 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
}
var trackTask = await _visitTaskRepository.FindAsync(task.Id);
trackTask.PMBackReason = pmBackReason;
}
else
{
@ -2606,7 +2619,7 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
private bool IsSpmOrCPM()
{
return _userInfo.UserTypeEnumInt == (int)UserTypeEnum.SPM || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.CPM ;
return _userInfo.UserTypeEnumInt == (int)UserTypeEnum.SPM || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.CPM;
}
/// <summary>
@ -2671,6 +2684,7 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
{
//默认影响的都是该标准的任务
filterExpression = filterExpression.And(t => t.TrialReadingCriterionId == filterObj.TrialReadingCriterionId);
}
@ -2931,6 +2945,88 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
}
#region 完全随机设置序号
[HttpPost]
[UnitOfWork]
[TrialGlobalLimit("AfterStopCannNotOpt")]
public async Task<IResponseOutput> SetRandomTaskOrder(SetRandomTaskOrderCommand inCommand)
{
if (inCommand.IsAutoSet)
{
//找到所有的已分配的,未阅片的,生效的 非一致性分析
var needRandomOrderList = _visitTaskRepository.Where(t => t.TrialId == inCommand.TrialId && t.TrialReadingCriterionId == inCommand.TrialReadingCriterionId && t.DoctorUserId == inCommand.DoctorUserId)
.Where(t => t.TaskAllocationState == TaskAllocationState.Allocated && t.ReadingTaskState == ReadingTaskState.WaitReading && (t.TaskState == TaskState.Effect || t.TaskState == TaskState.Freeze))
.Select(t => t.Id).ToList();
//var haveSignOrderList = _visitTaskRepository.Where(t => t.TrialId == inCommand.TrialId && t.TrialReadingCriterionId == inCommand.TrialReadingCriterionId && t.DoctorUserId == inCommand.DoctorUserId)
// .Where(t => t.ReadingTaskState == ReadingTaskState.HaveSigned && t.SignTime != null && (t.TaskState == TaskState.Effect || t.TaskState == TaskState.Freeze))
// .Select(t => t.RandomOrder).ToList();
//已阅,阅片中任务的序号
await _visitTaskRepository.BatchUpdateNoTrackingAsync(t => t.TrialId == inCommand.TrialId && t.TrialReadingCriterionId == inCommand.TrialReadingCriterionId
&& t.DoctorUserId == inCommand.DoctorUserId && t.ReadingTaskState != ReadingTaskState.WaitReading, u => new VisitTask() { RandomOrder = null });
//随机赋值编号 比如要处理5个任务实例化一个包含1-5的数组每次随机取出一个
List<int> availableNumbers = Enumerable.Range(1, needRandomOrderList.Count).ToList();
Random rng = new Random();
foreach (var id in needRandomOrderList)
{
int randomIndex = rng.Next(availableNumbers.Count);
var num = availableNumbers[randomIndex];
var order = 11 + 5 * (num - 1);
await _visitTaskRepository.BatchUpdateNoTrackingAsync(t => t.Id == id, t => new VisitTask() { RandomOrder = order });
availableNumbers.RemoveAt(randomIndex);
}
}
else
{
foreach (var item in inCommand.SetList)
{
var task = await _visitTaskRepository.Where(t => t.Id == item.Id).Select(t => new { t.RandomOrder, t.ReadingTaskState, t.TaskAllocationState, t.DoctorUserId }).FirstNotNullAsync();
if (task.ReadingTaskState != ReadingTaskState.WaitReading || task.DoctorUserId != inCommand.DoctorUserId
|| task.TaskAllocationState != TaskAllocationState.Allocated)
{
//"任务不符合设置阅片序号条件"
return ResponseOutput.NotOk(_localizer["VisitTask_NotRandomOrderTask"]);
}
//设置交换序号的任务,可能不符合条件
//没有序号,直接设置
if (task.RandomOrder == null)
{
await _visitTaskRepository.BatchUpdateNoTrackingAsync(t => t.Id == item.Id, t => new VisitTask() { RandomOrder = item.RandomOrder });
}
else
{
if(!_visitTaskRepository.Any(t => t.TrialId == inCommand.TrialId && t.TrialReadingCriterionId == inCommand.TrialReadingCriterionId && t.DoctorUserId == inCommand.DoctorUserId
&& t.TaskAllocationState == TaskAllocationState.Allocated && t.ReadingTaskState == ReadingTaskState.WaitReading && (t.TaskState == TaskState.Effect || t.TaskState == TaskState.Freeze)
&& t.RandomOrder == item.RandomOrder))
{
await _visitTaskRepository.BatchUpdateNoTrackingAsync(t => t.Id == item.Id, t => new VisitTask() { RandomOrder = item.RandomOrder });
}
else
{
//"序号已被占用!"
return ResponseOutput.NotOk(_localizer["VisitTask_RandomOrderUserd"]);
}
}
}
}
return ResponseOutput.Ok();
}
#endregion
#region 暂时废弃

View File

@ -160,6 +160,7 @@ namespace IRaCIS.Core.Application.Service
CreateMap<VisitTaskReReading, ReReadingTaskView>()
.ForMember(o => o.ApplicantName, t => t.MapFrom(u => u.CreateUserRole.IdentityUser.FullName))
.ForMember(o => o.ReReadingNewTaskCode, t => t.MapFrom(u => u.NewReReadingTask.TaskCode))
.ForMember(o => o.OriginalReReadingTask, t => t.MapFrom(u => u.OriginalReReadingTask))
;

View File

@ -39,6 +39,10 @@ namespace IRaCIS.Application.Contracts
public string ValueCN { get; set; } = string.Empty;
}
public class ChildInQuery:SortInput
{
public Guid ParentId { get; set; }
}
public class AddOrEditBasicDic
{

View File

@ -96,9 +96,6 @@ namespace IRaCIS.Core.Application.Contracts
public bool IsDeleted { get; set; }
public CriterionType? CriterionTypeEnum { get; set; }
/// <summary> 业务模块 /// </summary>
public int BusinessModuleEnum { get; set; }
@ -140,6 +137,24 @@ namespace IRaCIS.Core.Application.Contracts
public int? EmailDelaySeconds { get; set; }
[Comment("邮件配置的多个标准")]
public List<CriterionType>? CriterionTypeList { get; set; }
//public CriterionType? CriterionTypeEnum { get; set; }
}
public class BatchUpdateEmailTopicCommand
{
[NotDefault]
public Guid Id { get; set; }
public string EmailTopic { get; set; } = string.Empty;
public string EmailTopicCN { get; set; } = string.Empty;
}

View File

@ -3,6 +3,7 @@
// 生成时间 2022-03-28 16:43:52
// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。
//--------------------------------------------------------------------
using DocumentFormat.OpenXml.Wordprocessing;
using IRaCIS.Core.Infra.EFCore.Common;
using System.ComponentModel.DataAnnotations;
@ -63,6 +64,26 @@ namespace IRaCIS.Core.Application.ViewModel
}
public class ModuleTypeData
{
public bool IsShow { get; set; }
public Guid Id { get; set; }
public Guid? ParentId { get; set; }
public Guid DictionaryId { get; set; }
public int ShowOrder { get; set; }
public string DictionaryValue { get; set; }
}
public class GetModuleTypeListInDto
{
public Guid TrialId { get; set; }
}
/// <summary>
/// 复制其他对象到当前对象
/// </summary>
@ -148,6 +169,11 @@ namespace IRaCIS.Core.Application.ViewModel
public Guid UpdateUserId { get; set; }
public string Code { get; set; } = string.Empty;
/// <summary>
/// 是否默认选择
/// </summary>
public bool IsDefaultChoice { get; set; } = false;
/// <summary>
/// 字段的英文值
/// </summary>

View File

@ -96,6 +96,8 @@ namespace IRaCIS.Core.Application.ViewModel
public int State { get; set; }
public Guid? PublishLogId { get; set; }
public List<BatchAddInternationalizationDto> AddList { get; set; }
}
@ -112,6 +114,8 @@ namespace IRaCIS.Core.Application.ViewModel
public int? State { get; set; }
public int InternationalizationType { get; set; }
}
public class BatchAddInternationalizationDto : BatchInternationalizationDto
@ -121,6 +125,9 @@ namespace IRaCIS.Core.Application.ViewModel
public class InternationalizationSimpleDto : BatchInternationalizationDto
{
public string Version { get; set; }
public DateTime CreateTime { get; set; }
}

View File

@ -182,13 +182,12 @@ namespace IRaCIS.Core.Application.Service
/// <summary>
/// 获取子项数组
/// </summary>
/// <param name="parentId"></param>
/// <returns></returns>
[HttpGet("{parentId:guid}")]
public async Task<List<BasicDicView>> GetChildList(Guid parentId)
[HttpPost]
public async Task<List<BasicDicView>> GetChildList(ChildInQuery inQuery)
{
return await _dicRepository.Where(t => t.ParentId == parentId)
.OrderBy(t => t.ShowOrder).ProjectTo<BasicDicView>(_mapper.ConfigurationProvider).ToListAsync();
return await _dicRepository.Where(t => t.ParentId == inQuery.ParentId)
.ProjectTo<BasicDicView>(_mapper.ConfigurationProvider).SortToListAsync(inQuery);
}

View File

@ -4,8 +4,10 @@
// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。
//--------------------------------------------------------------------
using IRaCIS.Core.Application.Helper;
using IRaCIS.Core.Domain.Share;
using Microsoft.AspNetCore.Mvc;
using NPOI.SS.Formula.Functions;
using System.Text.RegularExpressions;
namespace IRaCIS.Core.Application.Contracts
@ -26,7 +28,7 @@ namespace IRaCIS.Core.Application.Contracts
.WhereIf(inQuery.SystemLevel != null, t => t.SystemLevel == inQuery.SystemLevel)
.WhereIf(inQuery.IsDistinguishCriteria != null, t => t.IsDistinguishCriteria == inQuery.IsDistinguishCriteria)
.WhereIf(inQuery.BusinessLevelEnum != null, t => t.BusinessLevelEnum == inQuery.BusinessLevelEnum)
.WhereIf(inQuery.CriterionTypeEnum != null, t => t.CriterionTypeEnum == inQuery.CriterionTypeEnum)
.WhereIf(inQuery.CriterionTypeEnum != null, t => t.CriterionTypeList.Any(t=>t==inQuery.CriterionTypeEnum))
.WhereIf(inQuery.BusinessModuleEnum != null, t => t.BusinessModuleEnum == inQuery.BusinessModuleEnum)
.WhereIf(inQuery.BusinessScenarioEnum != null, t => t.BusinessScenarioEnum == inQuery.BusinessScenarioEnum)
.WhereIf(inQuery.IsReturnRequired != null, t => t.IsReturnRequired == inQuery.IsReturnRequired)
@ -39,14 +41,83 @@ namespace IRaCIS.Core.Application.Contracts
return await emailNoticeConfigQueryable.ToPagedListAsync(inQuery);
}
/// <summary>
/// 批量更新邮件主题中英文
/// </summary>
/// <param name="inCommandList"></param>
/// <returns></returns>
public async Task<IResponseOutput> BatchUpdateEmail(List<BatchUpdateEmailTopicCommand> inCommandList)
{
var findIdList = inCommandList.Select(x => x.Id).ToList();
var regex = new Regex(@"\{\s*\d+\s*\}");
foreach (var inCommand in inCommandList)
{
if (regex.Matches($"{inCommand.EmailTopic}{inCommand.EmailTopicCN}")
.Any(t => t.Value.Contains(" ")))
{
//邮件模板占位符不允许有空格,请核查占位符的地方
return ResponseOutput.NotOk(I18n.T("EmailNoticeConfig_ContainEmpty"));
}
}
var list = _emailNoticeConfigrepository.Where(t => findIdList.Contains(t.Id), true).ToList();
foreach (var item in list)
{
var exist = inCommandList.FirstOrDefault(t => t.Id == item.Id);
if (exist != null)
{
item.EmailTopic = exist.EmailTopic;
item.EmailTopicCN = exist.EmailTopicCN;
}
}
await _emailNoticeConfigrepository.SaveChangesAsync();
return ResponseOutput.Ok();
}
public async Task<IResponseOutput> AddOrUpdateEmailNoticeConfig(EmailNoticeConfigAddOrEdit addOrEditEmailNoticeConfig)
{
var verifyExp1 = new EntityVerifyExp<EmailNoticeConfig>()
{
VerifyExp = t => t.BusinessScenarioEnum == addOrEditEmailNoticeConfig.BusinessScenarioEnum && t.CriterionTypeEnum== addOrEditEmailNoticeConfig.CriterionTypeEnum,
//var verifyExp1 = new EntityVerifyExp<EmailNoticeConfig>()
//{
// VerifyExp = t => t.BusinessScenarioEnum == addOrEditEmailNoticeConfig.BusinessScenarioEnum && t.CriterionTypeEnum == addOrEditEmailNoticeConfig.CriterionTypeEnum,
VerifyMsg = _localizer["EmailNoticeConfig_RepeatEmailScenario"]
// VerifyMsg = _localizer["EmailNoticeConfig_RepeatEmailScenario"]
//};
var criterionList = _emailNoticeConfigrepository.Where(t => t.BusinessScenarioEnum == addOrEditEmailNoticeConfig.BusinessScenarioEnum && t.IsEnable == true)
.Where(t => t.CriterionTypeList != null)
.WhereIf(addOrEditEmailNoticeConfig.Id != null, t => t.Id != addOrEditEmailNoticeConfig.Id)
.Select(t => t.CriterionTypeList).ToList();//不能使用selectMany 会当成关联对象,不能当成字符串
if (addOrEditEmailNoticeConfig.CriterionTypeList != null)
{
foreach (var item in addOrEditEmailNoticeConfig.CriterionTypeList)
{
foreach (var itemList in criterionList)
{
if (itemList.Any(t => t == item))
{
return ResponseOutput.NotOk(_localizer["EmailNoticeConfig_RepeatEmailScenario"]);
}
}
}
}
var verifyExp2 = new EntityVerifyExp<EmailNoticeConfig>()
{
VerifyExp = t => t.Code == addOrEditEmailNoticeConfig.Code && t.IsDistinguishCriteria == addOrEditEmailNoticeConfig.IsDistinguishCriteria && t.SystemLevel == addOrEditEmailNoticeConfig.SystemLevel,
VerifyMsg = _localizer["EmailNoticeConfig_RepeatCode"]
};
@ -61,10 +132,14 @@ namespace IRaCIS.Core.Application.Contracts
}
var entity = new EmailNoticeConfig() { };
if (addOrEditEmailNoticeConfig.Id == null)
{
var entity = _mapper.Map<EmailNoticeConfig>(addOrEditEmailNoticeConfig);
entity = _mapper.Map<EmailNoticeConfig>(addOrEditEmailNoticeConfig);
foreach (var item in addOrEditEmailNoticeConfig.ToUserTypeList)
@ -80,9 +155,9 @@ namespace IRaCIS.Core.Application.Contracts
}
await _emailNoticeConfigrepository.AddAsync(entity, true, verifyExp1);
await _emailNoticeConfigrepository.AddAsync(entity, true, verifyExp2);
return ResponseOutput.Ok(entity.Id.ToString());
}
else
{
@ -103,14 +178,32 @@ namespace IRaCIS.Core.Application.Contracts
}
var entity = await _emailNoticeConfigrepository.UpdateFromDTOAsync(addOrEditEmailNoticeConfig, true, false, verifyExp1);
entity = await _emailNoticeConfigrepository.UpdateFromDTOAsync(addOrEditEmailNoticeConfig, true, false, verifyExp2);
return ResponseOutput.Ok(entity.Id.ToString());
}
if (addOrEditEmailNoticeConfig.EmailCron != string.Empty)
{
var jobId = $"{entity.Id}_({addOrEditEmailNoticeConfig.BusinessScenarioEnum})";
HangfireJobHelper.RemoveCronJob(jobId);
//有的job 可能编辑控制直接不发,需要移除已存在的
if (entity.IsAutoSend && entity.IsEnable)
{
HangfireJobHelper.AddOrUpdateTimingCronJob(jobId, addOrEditEmailNoticeConfig.BusinessScenarioEnum, addOrEditEmailNoticeConfig.EmailCron);
}
}
return ResponseOutput.Ok(entity.Id.ToString());
}

View File

@ -4,6 +4,7 @@ using IRaCIS.Application.Interfaces;
using IRaCIS.Core.API._ServiceExtensions.NewtonsoftJson;
using IRaCIS.Core.Application.Contracts;
using IRaCIS.Core.Application.Contracts.DTO;
using IRaCIS.Core.Application.Service.Reading.Dto;
using IRaCIS.Core.Application.ViewModel;
using IRaCIS.Core.Domain.Models;
using IRaCIS.Core.Domain.Share;
@ -47,6 +48,54 @@ namespace IRaCIS.Core.Application.Service.Common
{
[HttpPost]
public async Task<IActionResult> GetTrialVisitImageStatList_Export(TrialVisitImageQuery inQuery,
[FromServices] IRepository<SubjectVisit> _subjectVisitRepository,
[FromServices] IRepository<Trial> _trialRepository,
[FromServices] IDictionaryService _dictionaryService)
{
var query = _subjectVisitRepository.Where(t => t.TrialId == inQuery.TrialId)
.WhereIf(inQuery.TrialSiteId != null, t => t.TrialSiteId == inQuery.TrialSiteId)
.WhereIf(inQuery.SubjectCode.IsNotNullOrEmpty(), t => t.Subject.Code.Contains(inQuery.SubjectCode))
.WhereIf(inQuery.BeginScanDate != null, t => t.LatestScanDate >= inQuery.BeginScanDate)
.WhereIf(inQuery.EndScanDate != null, t => t.LatestScanDate == inQuery.EndScanDate)
.Select(t => new TrialVisitImageStatView()
{
TrialId = t.TrialId,
SubjectVisitId = t.Id,
SubjectCode = t.Subject.Code,
TrialSiteCode = t.TrialSite.TrialSiteCode,
TrialSiteId = t.TrialSiteId,
VisitName = t.VisitName,
VisitNum = t.VisitNum,
EarliestScanDate = t.EarliestScanDate,
LatestScanDate = t.LatestScanDate,
IsHaveDicom = t.StudyList.Any(),
IsHaveNoneDicom = t.NoneDicomStudyList.Any(),
TotalStudyCount = t.StudyList.Count() + t.NoneDicomStudyList.Count(),
TotalImageCount = t.StudyList.Sum(t => t.InstanceCount) + t.NoneDicomStudyList.Sum(t => t.FileCount),
TotalImageSize = t.StudyList.SelectMany(t => t.InstanceList).Sum(t => t.FileSize) + t.NoneDicomStudyList.SelectMany(t => t.NoneDicomFileList).Sum(t => t.FileSize),
});
var list = await query.Where(t => t.TotalImageCount > 0).SortToListAsync(inQuery);
var exportInfo = (await _trialRepository.Where(t => t.Id == inQuery.TrialId).IgnoreQueryFilters().ProjectTo<ExcelExportInfo>(_mapper.ConfigurationProvider).FirstOrDefaultAsync()).IfNullThrowException();
exportInfo.List = ExportExcelConverterDate.ConvertToClientTimeInObject(list, _userInfo.TimeZoneId);
exportInfo.CurrentTime = ExportExcelConverterDate.DateTimeInternationalToString(DateTime.Now, _userInfo.TimeZoneId);
return await ExcelExportHelper.DataExportAsync(StaticData.Export.TriaVisitImageStat_Export, exportInfo, exportInfo.TrialCode, _commonDocumentRepository, _hostEnvironment, _dictionaryService, typeof(TrialMaintenanceDTO));
}
#region 后续需要移动过来
@ -81,7 +130,7 @@ namespace IRaCIS.Core.Application.Service.Common
.WhereIf(param.IsDeleted != null, t => t.IsDeleted == param.IsDeleted)
.WhereIf(!string.IsNullOrWhiteSpace(param.OrganizationName),
t => t.UserRole.OrganizationName.Contains(param.OrganizationName))
t => t.UserRole.IdentityUser.OrganizationName.Contains(param.OrganizationName))
.WhereIf(!string.IsNullOrWhiteSpace(param.UserRealName),
t => (t.UserRole.FullName).Contains(param.UserRealName))
.Select(t => new TrialMaintenanceDTO()
@ -349,6 +398,114 @@ namespace IRaCIS.Core.Application.Service.Common
#region 导表查询
///getSysDocumentConfirmList 系统文档培训查询
///
[HttpPost]
public async Task<IActionResult> GetSysDocumentConfirmList_Export(SystemDocQuery inQuery,
[FromServices] IRepository<SystemDocument> _systemDocumentRepository,
[FromServices] IRepository<IdentityUser> _identityUserRepository,
[FromServices] IDictionaryService _dictionaryService)
{
var systemDocQuery =
from sysDoc in _systemDocumentRepository.AsQueryable(false)
.Where(t => inQuery.UserTypeId != null ? t.NeedConfirmedUserTypeList.Any(t => t.NeedConfirmUserTypeId == inQuery.UserTypeId) : true)
from identityUser in _identityUserRepository.AsQueryable(false).Where(t => t.Status == UserStateEnum.Enable && t.UserRoleList.Where(t => t.IsUserRoleDisabled == false).Any(t => sysDoc.NeedConfirmedUserTypeList.AsQueryable().Any(c => c.NeedConfirmUserTypeId == t.UserTypeId)))
.Where(t => inQuery.UserId != null ? t.Id == inQuery.UserId : true)
.Where(t => inQuery.UserTypeId != null ? t.UserRoleList.Any(t => t.UserTypeId == inQuery.UserTypeId && t.IsUserRoleDisabled == false) : true)
join confirm in _systemDocConfirmedUserRepository.Where() on new { ConfirmUserId = identityUser.Id, SystemDocumentId = sysDoc.Id } equals new { confirm.ConfirmUserId, confirm.SystemDocumentId } into cc
from confirm in cc.DefaultIfEmpty()
select new TrainingRecordExportDTO()
{
IsSystemDoc = true,
Id = sysDoc.Id,
CreateTime = sysDoc.CreateTime,
IsDeleted = sysDoc.IsDeleted,
Name = sysDoc.Name,
FileType = _userInfo.IsEn_Us ? sysDoc.FileType.Value : sysDoc.FileType.ValueCN,
FileTypeId = sysDoc.FileTypeId,
//IsConfirmed = confirm.ConfirmTime != null,
ConfirmUserId = identityUser.Id,
ConfirmTime = confirm.ConfirmTime,
RealName = identityUser.FullName,
UserName = identityUser.UserName,
//UpdateTime = sysDoc.UpdateTime,
//SignViewMinimumMinutes = sysDoc.SignViewMinimumMinutes,
//UserTypeId = trialUser.UserRole.UserTypeId,
//UserTypeShortName = trialUser.UserRole.UserTypeRole.UserTypeShortName,
//Path = sysDoc.Path,
//FullFilePath = sysDoc.Path
};
var unionQuery = systemDocQuery.IgnoreQueryFilters().Where(t => !(t.IsDeleted == true && t.ConfirmTime == null))
.WhereIf(!string.IsNullOrEmpty(inQuery.Name), t => t.Name.Contains(inQuery.Name))
.WhereIf(inQuery.FileTypeId != null, t => t.FileTypeId == inQuery.FileTypeId)
.WhereIf(inQuery.IsConfirmed == true, t => t.ConfirmTime != null)
.WhereIf(inQuery.IsConfirmed == false, t => t.ConfirmTime == null)
.WhereIf(inQuery.StartConfirmTime != null, t => t.ConfirmTime >= inQuery.StartConfirmTime.Value)
.WhereIf(inQuery.EndConfirmTime != null, t => t.ConfirmTime <= inQuery.EndConfirmTime.Value)
.WhereIf(inQuery.BeginCreateTime != null, t => t.CreateTime >= inQuery.BeginCreateTime)
.WhereIf(inQuery.EndCreateTime != null, t => t.CreateTime <= inQuery.EndCreateTime)
.WhereIf(!string.IsNullOrEmpty(inQuery.UserName), t => t.UserName.Contains(inQuery.UserName))
.WhereIf(inQuery.IsDeleted != null, t => t.IsDeleted == inQuery.IsDeleted);
var list = await unionQuery.SortToListAsync(inQuery);
#region 处理文档 需要签署的角色类型 和每个人的角色信息
var trialDocIdList = list.Where(t => t.IsSystemDoc == false).Select(t => t.Id).ToList();
var sysDocIdList = list.Where(t => t.IsSystemDoc == true).Select(t => t.Id).ToList();
var identityUserIdList = list.Select(t => t.ConfirmUserId).Distinct().ToList();
var sysDocUserTypeList = _systemDocNeedConfirmedUserTypeRepository.Where(t => sysDocIdList.Contains(t.SystemDocumentId)).Select(t => new { t.SystemDocumentId, t.UserTypeRole.UserTypeShortName }).ToList();
var identityUserUserTypeList = _identityUserRepository.Where(t => identityUserIdList.Contains(t.Id)).IgnoreQueryFilters().Select(t => new { IdentityUserId = t.Id, UserTypeList = t.UserRoleList.Where(t => t.IsUserRoleDisabled == false).Select(c => c.UserTypeRole.UserTypeShortName).ToList() });
//Concat 不能用导航属性
var sysids = list.Where(t => t.IsSystemDoc == true).Select(t => t.Id).ToList();
//var sysDataList = await _systemDocumentRepository.Where(x => sysids.Contains(x.Id)).Include(x => x.SystemDocumentAttachmentList).ToListAsync();
foreach (var item in list)
{
//if (sysDataList.Any(y => y.Id == item.Id))
//{
// item.AttachmentCount = sysDataList.Where(y => y.Id == item.Id).Select(x => x.SystemDocumentAttachmentList.Where(z => !z.OffLine).Count()).FirstOrDefault();
//}
if (item.IsSystemDoc)
{
item.DocNeedSignUserTypeList = sysDocUserTypeList.Where(t => t.SystemDocumentId == item.Id).Select(t => t.UserTypeShortName).ToList();
}
item.IdentityUserTypeList = identityUserUserTypeList.Where(t => t.IdentityUserId == item.ConfirmUserId).SelectMany(c => c.UserTypeList).ToList();
}
#endregion
var exportInfo = new ExcelExportInfo();
exportInfo.List = list;
exportInfo.CurrentTime = ExportExcelConverterDate.DateTimeInternationalToString(DateTime.Now, _userInfo.TimeZoneId);
exportInfo.List = ExportExcelConverterDate.ConvertToClientTimeInObject(list, _userInfo.TimeZoneId);
return await ExcelExportHelper.DataExportAsync(StaticData.Export.CommonTrainingRecordList_Export, exportInfo, $"{exportInfo.ResearchProgramNo}", _commonDocumentRepository, _hostEnvironment, _dictionaryService, typeof(TrainingRecordExportDTO));
}
/// <summary>
/// getDocumentConfirmList 培训记录导出--new
/// </summary>
@ -1193,39 +1350,65 @@ namespace IRaCIS.Core.Application.Service.Common
[FromServices] IRepository<Trial> _trialRepository)
{
var list = await _visitTaskRepository.Where(t => t.TrialId == inQuery.TrialId && t.IsAnalysisCreate == false)
//.Where(t => t.IsAnalysisCreate == false && t.DoctorUserId != null)
//.Where(t => t.IsAnalysisCreate == false && t.DoctorUserId != null)
.WhereIf(inQuery.TaskState != null, t => t.TaskState == inQuery.TaskState)
.WhereIf(inQuery.ArmEnum != null, t => t.ArmEnum == inQuery.ArmEnum)
.WhereIf(inQuery.TrialSiteId != null, t => t.Subject.TrialSiteId == inQuery.TrialSiteId)
.WhereIf(inQuery.SubjectId != null, t => t.SubjectId == inQuery.SubjectId)
.WhereIf(inQuery.IsUrgent != null, t => t.IsUrgent == inQuery.IsUrgent)
.WhereIf(inQuery.DoctorUserId != null, t => t.DoctorUserId == inQuery.DoctorUserId)
.WhereIf(inQuery.ReadingCategory != null, t => t.ReadingCategory == inQuery.ReadingCategory)
.WhereIf(inQuery.ReadingTaskState != null, t => t.ReadingTaskState == inQuery.ReadingTaskState)
.WhereIf(inQuery.TaskAllocationState != null, t => t.TaskAllocationState == inQuery.TaskAllocationState)
.WhereIf(inQuery.TrialReadingCriterionId != null, t => t.TrialReadingCriterionId == inQuery.TrialReadingCriterionId)
.WhereIf(inQuery.IsEffect == true, t => t.TaskState == TaskState.Effect || t.TaskState == TaskState.Freeze)
.WhereIf(inQuery.IsEffect == false, t => t.TaskState == TaskState.Adbandon || t.TaskState == TaskState.HaveReturned)
.WhereIf(inQuery.TaskState != null, t => t.TaskState == inQuery.TaskState)
.WhereIf(inQuery.TrialSiteId != null, t => t.Subject.TrialSiteId == inQuery.TrialSiteId)
.WhereIf(inQuery.SubjectId != null, t => t.SubjectId == inQuery.SubjectId)
.WhereIf(inQuery.IsUrgent != null, t => t.IsUrgent == inQuery.IsUrgent)
.WhereIf(inQuery.DoctorUserId != null, t => t.DoctorUserId == inQuery.DoctorUserId)
.WhereIf(inQuery.ReadingCategory != null, t => t.ReadingCategory == inQuery.ReadingCategory)
.WhereIf(inQuery.ReadingTaskState != null, t => t.ReadingTaskState == inQuery.ReadingTaskState)
.WhereIf(inQuery.TaskAllocationState != null, t => t.TaskAllocationState == inQuery.TaskAllocationState)
.WhereIf(inQuery.TrialReadingCriterionId != null, t => t.TrialReadingCriterionId == inQuery.TrialReadingCriterionId)
.WhereIf(inQuery.ReReadingApplyState != null, t => t.ReReadingApplyState == inQuery.ReReadingApplyState)
.WhereIf(inQuery.CompleteClinicalDataEnum == CompleteClinicalDataEnum.Complete, t => t.IsClinicalDataSign && t.IsNeedClinicalDataSign == true)
.WhereIf(inQuery.CompleteClinicalDataEnum == CompleteClinicalDataEnum.NotComplete, t => t.IsClinicalDataSign == false && t.IsNeedClinicalDataSign == true)
.WhereIf(inQuery.CompleteClinicalDataEnum == CompleteClinicalDataEnum.NA, t => t.IsNeedClinicalDataSign == false)
.WhereIf(!string.IsNullOrEmpty(inQuery.TrialSiteCode), t => (t.BlindTrialSiteCode.Contains(inQuery.TrialSiteCode!) && t.IsAnalysisCreate) || (t.Subject.TrialSite.TrialSiteCode.Contains(inQuery.TrialSiteCode!) && t.IsAnalysisCreate == false))
.WhereIf(!string.IsNullOrEmpty(inQuery.TaskName), t => t.TaskName.Contains(inQuery.TaskName) || t.TaskBlindName.Contains(inQuery.TaskName))
.WhereIf(!string.IsNullOrEmpty(inQuery.SubjectCode), t => (t.Subject.Code.Contains(inQuery.SubjectCode) && t.IsAnalysisCreate == false) || (t.BlindSubjectCode.Contains(inQuery.SubjectCode) && t.IsAnalysisCreate))
.WhereIf(inQuery.BeginAllocateDate != null, t => t.AllocateTime > inQuery.BeginAllocateDate)
.WhereIf(inQuery.EndAllocateDate != null, t => t.AllocateTime < inQuery.EndAllocateDate)
.WhereIf(inQuery.BeginSignTime != null, t => t.SignTime > inQuery.BeginSignTime)
.WhereIf(inQuery.EndSignTime != null, t => t.SignTime < inQuery.EndSignTime)
.WhereIf(inQuery.RandomOrder != null, t => t.RandomOrder == inQuery.RandomOrder)
.WhereIf(inQuery.IsRandomOrderList == true, t => (t.ReadingTaskState == ReadingTaskState.WaitReading || t.ReadingTaskState == ReadingTaskState.Reading) && t.TaskAllocationState == TaskAllocationState.Allocated)
.WhereIf(!string.IsNullOrEmpty(inQuery.TrialSiteCode), t => (t.BlindTrialSiteCode.Contains(inQuery.TrialSiteCode!) && t.IsAnalysisCreate) || (t.Subject.TrialSite.TrialSiteCode.Contains(inQuery.TrialSiteCode!) && t.IsAnalysisCreate == false))
.WhereIf(!string.IsNullOrEmpty(inQuery.TaskName), t => t.TaskName.Contains(inQuery.TaskName) || t.TaskBlindName.Contains(inQuery.TaskName))
.WhereIf(!string.IsNullOrEmpty(inQuery.SubjectCode), t => t.Subject.Code.Contains(inQuery.SubjectCode) || t.BlindSubjectCode.Contains(inQuery.SubjectCode))
.WhereIf(inQuery.BeginAllocateDate != null, t => t.AllocateTime > inQuery.BeginAllocateDate)
.WhereIf(inQuery.EndAllocateDate != null, t => t.AllocateTime < inQuery.EndAllocateDate!.Value.AddDays(1))
.ProjectTo<ReadingTaskExportDto>(_mapper.ConfigurationProvider).ToListAsync();
//var defalutSortArray = new string[] { nameof(VisitTask.IsUrgent) + " desc", nameof(VisitTask.SubjectId), nameof(VisitTask.VisitTaskNum) };
list = list.OrderBy(t => t.TrialSiteCode).ThenBy(t => t.SubjectCode).ThenBy(t => t.VisitTaskNum).ToList();
var exportInfo = (await _trialRepository.Where(t => t.Id == inQuery.TrialId).IgnoreQueryFilters().ProjectTo<ExcelExportInfo>(_mapper.ConfigurationProvider).FirstOrDefaultAsync()).IfNullThrowException();
exportInfo.List = ExportExcelConverterDate.ConvertToClientTimeInObject(list, _userInfo.TimeZoneId);
exportInfo.CurrentTime = ExportExcelConverterDate.DateTimeInternationalToString(DateTime.Now, _userInfo.TimeZoneId);
return await ExcelExportHelper.DataExportAsync(StaticData.Export.TrialReadingTaskList_Export, exportInfo, $"{exportInfo.ResearchProgramNo}", _commonDocumentRepository, _hostEnvironment, _dictionaryService, typeof(ReadingTaskExportDto));
if (inQuery.IsRandomOrderList == true)
{
list = list.OrderBy(t => t.RandomOrder).ToList();
exportInfo.List = ExportExcelConverterDate.ConvertToClientTimeInObject(list, _userInfo.TimeZoneId);
return await ExcelExportHelper.DataExportAsync(StaticData.Export.TrialTaskRandomOrderList_Export, exportInfo, $"{exportInfo.ResearchProgramNo}", _commonDocumentRepository, _hostEnvironment, _dictionaryService, typeof(ReadingTaskExportDto));
}
else
{
list = list.OrderBy(t => t.TrialSiteCode).ThenBy(t => t.SubjectCode).ThenBy(t => t.VisitTaskNum).ToList();
exportInfo.List = ExportExcelConverterDate.ConvertToClientTimeInObject(list, _userInfo.TimeZoneId);
return await ExcelExportHelper.DataExportAsync(StaticData.Export.TrialReadingTaskList_Export, exportInfo, $"{exportInfo.ResearchProgramNo}", _commonDocumentRepository, _hostEnvironment, _dictionaryService, typeof(ReadingTaskExportDto));
}
}
@ -1549,9 +1732,9 @@ namespace IRaCIS.Core.Application.Service.Common
{
var emailNoticeConfigQueryable = _emailNoticeConfigrepository
//.WhereIf(inQuery.SystemLevel == null, t => t.SystemLevel == SysEmailLevel.not_sys)
//.WhereIf(inQuery.SystemLevel != null, t => t.SystemLevel == inQuery.SystemLevel)
//.WhereIf(inQuery.IsDistinguishCriteria != null, t => t.IsDistinguishCriteria == inQuery.IsDistinguishCriteria)
.WhereIf(inQuery.CriterionTypeEnum != null, t => t.CriterionTypeEnum == inQuery.CriterionTypeEnum)
.WhereIf(inQuery.SystemLevel != null, t => t.SystemLevel == inQuery.SystemLevel)
.WhereIf(inQuery.IsDistinguishCriteria != null, t => t.IsDistinguishCriteria == inQuery.IsDistinguishCriteria)
.WhereIf(inQuery.CriterionTypeEnum != null, t =>t.CriterionTypeList.Any(c=>c==inQuery.CriterionTypeEnum))
.WhereIf(inQuery.BusinessScenarioEnum != null, t => t.BusinessScenarioEnum == inQuery.BusinessScenarioEnum)
.WhereIf(inQuery.IsReturnRequired != null, t => t.IsReturnRequired == inQuery.IsReturnRequired)
.WhereIf(inQuery.IsEnable != null, t => t.IsEnable == inQuery.IsEnable)
@ -1613,8 +1796,52 @@ namespace IRaCIS.Core.Application.Service.Common
}
public List<T> DealOncologyResult<T>(IRepository<ReadingOncologyTaskInfo> _oncologyRepository, bool isOncology, List<T> list) where T : CommonEvaluationExport
{
if (isOncology)
{
var subjectIdList = list.Select(t => t.SubjectId).Distinct().ToList();
//已阅片完的肿瘤学结果
var oncologyResultList = _oncologyRepository.Where(t => subjectIdList.Contains(t.OncologyVisitTask.SubjectId)
&& t.OncologyVisitTask.IsAnalysisCreate == false && t.OncologyVisitTask.ReadingTaskState == ReadingTaskState.HaveSigned && t.OncologyVisitTask.TaskState == TaskState.Effect)
.Select(t => new { t.OncologyVisitTask.SubjectId, OncologyVisitTaskNum = t.OncologyVisitTask.VisitTaskNum, OncologyUserName = t.OncologyVisitTask.DoctorUser.UserName, t.VisitTask.VisitTaskNum, t.VisitTaskId, t.EvaluationReason, t.EvaluationResult })
.ToList();
foreach (var subjectOncologyResult in oncologyResultList.GroupBy(t => t.SubjectId))
{
//最后已完成的肿瘤学任务号
var lastOncologyVisitTaskNum = subjectOncologyResult.OrderByDescending(t => t.OncologyVisitTaskNum).FirstOrDefault().OncologyVisitTaskNum;
//需要填充的肿瘤学结果
var subjectOncologyList = subjectOncologyResult.Where(t => t.OncologyVisitTaskNum == lastOncologyVisitTaskNum).ToList();
foreach (var subjectOncology in subjectOncologyList)
{
foreach (var item in list)
{
if (item.SubjectId == subjectOncology.SubjectId && item.VisitTaskNum == subjectOncology.VisitTaskNum)
{
item.OncologyReason = subjectOncology.EvaluationReason;
item.OncologyResult = subjectOncology.EvaluationResult;
item.OncologyUserName = subjectOncology.OncologyUserName;
}
}
}
}
}
return list;
}
public List<T> DealJudgeMark<T>(ArbitrationRule arbitrationRule, bool isGlobalReading, IEnumerable<T> list) where T : CommonEvaluationExport
{
//处理访视任务的裁判标记
@ -1648,7 +1875,7 @@ namespace IRaCIS.Core.Application.Service.Common
{
//两个人都做了
if (resultExceptJudgeList.Count(t => t.VisitTaskNum == item.VisitTaskNum && t.SubjectCode == item.SubjectCode) == 2)
if (resultExceptJudgeList.Where(t => t.VisitTaskNum == item.VisitTaskNum && t.SubjectCode == item.SubjectCode).Select(t => t.ArmEnum).Distinct().Count() == 2)
{
//如果没有产生裁判默认选择R1
if (item.ArmEnum == Arm.DoubleReadingArm1)
@ -1703,6 +1930,7 @@ namespace IRaCIS.Core.Application.Service.Common
//已完成的全局数量
finishedGlobalCount = resultExceptJudgeList.Where(t => t.SubjectCode == visitItem.SubjectCode && subjectReadingPeriondVisitNumList.Any(c => (c + addReadingPeriodNum) == t.VisitTaskNum)
/*&& t.ReadingCategory == ReadingCategory.Global*/)
.Select(t => new { t.VisitTaskNum, t.ArmEnum }).Distinct()
.GroupBy(t => t.VisitTaskNum).Where(g => g.Count() == 2).Select(g => g.Key).Count();
}
@ -1716,7 +1944,8 @@ namespace IRaCIS.Core.Application.Service.Common
var subjectMaxFinishedGlobalTaskNum = resultExceptJudgeList.Where(t => t.SubjectCode == visitItem.SubjectCode && subjectReadingPeriondVisitNumList.Any(c => (c + addReadingPeriodNum) == t.VisitTaskNum)
/*&& t.ReadingCategory == ReadingCategory.Global*/)
.GroupBy(t => t.VisitTaskNum).Where(g => g.Count() == 2).Select(g => g.Key).DefaultIfEmpty().Max();
.Select(t => new { t.VisitTaskNum, t.ArmEnum }).Distinct()
.GroupBy(t => t.VisitTaskNum).Where(g => g.Count() == 2).Select(g => g.Key).DefaultIfEmpty().Max();
//最大的完成的全局是否产生裁判
if (subjectJudgeList.Any(t => t.VisitTaskNum == (subjectMaxFinishedGlobalTaskNum + ReadingCommon.TaskNumDic[ReadingCategory.Judge])))
@ -2172,12 +2401,13 @@ namespace IRaCIS.Core.Application.Service.Common
[FromServices] IDictionaryService _dictionaryService,
[FromServices] IRepository<ReadingQuestionTrial> _trialReadingQuestionRepository,
[FromServices] IRepository<ReadingTableQuestionTrial> _trialReadingTableQuestionRepository,
[FromServices] IRepository<ReadingOncologyTaskInfo> _oncologyRepository,
[FromServices] IRepository<Trial> _trialRepository)
{
var trialId = inQuery.TrialId;
var trialReadingCriterionId = inQuery.TrialReadingCriterionId;
//每次查询必须是单标准的
var criterion = await _readingQuestionCriterionTrialRepository.Where(t => t.Id == inQuery.TrialReadingCriterionId).Select(t => new { t.CriterionType, t.CriterionGroup, t.IsGlobalReading, t.IsArbitrationReading, t.CriterionName, t.ArbitrationRule }).FirstNotNullAsync();
var criterion = await _readingQuestionCriterionTrialRepository.Where(t => t.Id == inQuery.TrialReadingCriterionId).Select(t => new { t.CriterionType, t.CriterionGroup, t.IsGlobalReading, t.IsArbitrationReading, t.IsOncologyReading, t.CriterionName, t.ArbitrationRule }).FirstNotNullAsync();
var query = _visitTaskRepository.Where(t => t.TrialId == inQuery.TrialId && t.IsAnalysisCreate == false && (t.TaskState == TaskState.Effect || t.TaskState == TaskState.Freeze))
@ -2218,6 +2448,16 @@ namespace IRaCIS.Core.Application.Service.Common
//仲裁阅片 才有裁判阅片明细表 同时要把模板里面的三列给去掉
removeColumnIndexList = new List<int>() { 6, 7, 8 };
}
//阅片结果表 和阅片结果明细表 没有肿瘤学的时候需要移除肿瘤学三个字段
if (!criterion.IsOncologyReading)
{
if (inQuery.ReadingExportType == ExportResult.TableOfAssessmentResults || inQuery.ReadingExportType == ExportResult.DetailedTableOfAssessmentResults)
{
removeColumnIndexList = removeColumnIndexList.Union(new List<int>() { 9, 10, 11 }).ToList();
}
}
}
var export_Template = StaticData.Export.CommonReading_Export;
@ -2315,15 +2555,34 @@ namespace IRaCIS.Core.Application.Service.Common
// CDISC 导出 只管到 外层问题层级 和阅片结果表是保持一致
else if (inQuery.ReadingExportType == ExportResult.CDISC)
{
list = await query.ProjectTo<CommonEvaluationExport>(_mapper.ConfigurationProvider,
new
{
readingExportType = inQuery.ReadingExportType,
criterionType = criterion.CriterionType,
arbitrationRule = criterion.ArbitrationRule,
trialReadingCriterionId = inQuery.TrialReadingCriterionId,
isEn_Us = _userInfo.IsEn_Us
}).ToListAsync();
if (criterion.CriterionType == CriterionType.SelfDefine)
{
taskList = await query.ProjectTo<CommonLessionExport>(_mapper.ConfigurationProvider,
new
{
readingExportType = inQuery.ReadingExportType,
criterionType = criterion.CriterionType,
arbitrationRule = criterion.ArbitrationRule,
trialReadingCriterionId = inQuery.TrialReadingCriterionId,
isEn_Us = _userInfo.IsEn_Us
}).ToListAsync();
}
else
{
list = await query.ProjectTo<CommonEvaluationExport>(_mapper.ConfigurationProvider,
new
{
readingExportType = inQuery.ReadingExportType,
criterionType = criterion.CriterionType,
arbitrationRule = criterion.ArbitrationRule,
trialReadingCriterionId = inQuery.TrialReadingCriterionId,
isEn_Us = _userInfo.IsEn_Us
}).ToListAsync();
}
}
@ -2400,7 +2659,7 @@ namespace IRaCIS.Core.Application.Service.Common
{
Id = Guid.Empty,
Name = (t.QuestionType == QuestionType.ExistDisease || t.QuestionType == QuestionType.Tumor) ? (_userInfo.IsEn_Us ? "Overall Response" : "整体肿瘤评估") : t.QuestionName
}).ToList();
}).Distinct().ToList();
@ -2413,7 +2672,7 @@ namespace IRaCIS.Core.Application.Service.Common
{
Id = Guid.Empty,
Name = (t.QuestionType == QuestionType.ExistDisease || t.QuestionType == QuestionType.ImgOncology) ? (_userInfo.IsEn_Us ? "Overall Response" : "整体肿瘤评估") : t.QuestionName
}).ToList();
}).Distinct().ToList();
}
else if (criterion.CriterionType == CriterionType.PCWG3)
@ -2422,7 +2681,7 @@ namespace IRaCIS.Core.Application.Service.Common
{
Id = Guid.Empty,
Name = t.QuestionName
}).ToList();
}).Distinct().ToList();
}
@ -2596,7 +2855,7 @@ namespace IRaCIS.Core.Application.Service.Common
//多表格增加的一列就用Guid.Empty 标识
var extralNameList = new List<DynamicColumnConfig.ColumItem>() { new DynamicColumnConfig.ColumItem() { Id = Guid.Empty, Name = _userInfo.IsEn_Us ? "Table Name" : "表格名称" } };
var tableQuestionNameList = trialConfigTableQuestionList.Select(t => new DynamicColumnConfig.ColumItem() { Id = t.QuestionId, Name = t.TableName + "_" + t.QuestionName }).ToList();
var tableQuestionNameList = trialConfigTableQuestionList.OrderBy(t=>t.TableName).Select(t => new DynamicColumnConfig.ColumItem() { Id = t.QuestionId, Name = t.TableName + "_" + t.QuestionName }).ToList();
configCoumNameList = fistLeveLNameList.Union(extralNameList).Union(tableQuestionNameList).ToList();
}
@ -2636,7 +2895,7 @@ namespace IRaCIS.Core.Application.Service.Common
addLessionInfoList.Add(new CommonQuesionInfo() { QuestionId = Guid.Empty, QuestionName = _userInfo.IsEn_Us ? "Table Name" : "表格名称", QuestionValue = firstLessionAnser.TableName });
var dynamicPartialLessionInfoList = lession.LessionAnswerList.Select(t => new CommonQuesionInfo() { QuestionId=t.TableQuesionId, QuestionName = t.TableName + "_" + t.QuestionName, QuestionValue = t.QuestionValue, TranslateDicName = t.TranslateDicName, CDISCCode = t.CDISCCode });
var dynamicPartialLessionInfoList = lession.LessionAnswerList.Select(t => new CommonQuesionInfo() { QuestionId = t.TableQuesionId, QuestionName = t.TableName + "_" + t.QuestionName, QuestionValue = t.QuestionValue, TranslateDicName = t.TranslateDicName, CDISCCode = t.CDISCCode });
//有三部分组成 外层问题+ 固定列表格名称 + 动态的表格问题
dynamicLessionInfoList = item.QuestionAnswerList.Union(addLessionInfoList).Union(dynamicPartialLessionInfoList).ToList();
@ -2678,7 +2937,7 @@ namespace IRaCIS.Core.Application.Service.Common
{
AutoColumnTitleRowIndex = 2,
AutoColumnStartIndex = 6,
TempalteLastColumnIndex = 8,
TempalteLastColumnIndex = 11,
DynamicItemDicName = "TranslateDicName",
DynamicItemValueName = "QuestionValue",
DynamicItemTitleName = "QuestionName",
@ -2686,42 +2945,236 @@ namespace IRaCIS.Core.Application.Service.Common
DynamicListName = "QuestionAnswerList",
RemoveColunmIndexList = removeColumnIndexList,
ColumnIdNameList = configCoumNameList,
CDISCList = new List<string>(),
//CDISCList = new List<string>(),
TranslateDicNameList = translateDicNameList
};
if (export_Template == StaticData.Export.ReadingLession_Export || export_Template == StaticData.Export.CommonJudgeReadingDetail_Export)
{
dynamicColumnConfig.TempalteLastColumnIndex = 8;
}
}
else
{
//CDISC 导出 只到外层问题级别 使用Id 填充Excel
var totalConfigCoumNameList = trialConfigQuestionList.ToList();
var configCoumNameList = totalConfigCoumNameList.Select(t => new DynamicColumnConfig.ColumItem() { Id = t.QuestionId, Name = t.QuestionName }).ToList();
var translateDicList = totalConfigCoumNameList.Where(t => t.TranslateDicName.IsNotNullOrEmpty()).Select(t => t.TranslateDicName).Distinct().ToList();
var cdiscCodeList = totalConfigCoumNameList.Select(t => t.CDISCCode).ToList();
dynamicColumnConfig = new DynamicColumnConfig()
if (criterion.CriterionType == CriterionType.SelfDefine)
{
AutoColumnTitleRowIndex = 1,
AutoColumnStartIndex = 6,
TempalteLastColumnIndex = 10,
DynamicItemDicName = "TranslateDicName",
DynamicItemValueName = "QuestionValue",
DynamicItemTitleName = "QuestionName",
DynamicItemTitleId = "QuestionId",
DynamicListName = "QuestionAnswerList",
RemoveColunmIndexList = removeColumnIndexList,
ColumnIdNameList = configCoumNameList,
CDISCList = cdiscCodeList,
TranslateDicNameList = translateDicList
};
//最终EXCEL 列
var configCoumNameList = new List<DynamicColumnConfig.ColumItem>();
//表格全问题名
var trialConfigTableQuestionList = _trialReadingTableQuestionRepository.Where(t => t.TrialId == trialId && t.TrialCriterionId == trialReadingCriterionId).Where(t => t.ExportResultStr.Contains(((int)inQuery.ReadingExportType).ToString()))
.Select(t => new ExportQuestionBasicInfo()
{
QuestionId = t.Id,
TableName = _userInfo.IsEn_Us ? t.ReadingQuestionTrial.QuestionEnName : t.ReadingQuestionTrial.QuestionName,
QuestionName = _userInfo.IsEn_Us ? t.QuestionEnName : t.QuestionName,
CDISCCode = t.CDISCCode,
TranslateDicName = t.DictionaryCode,
ShowOrder = t.ShowOrder
}).OrderBy(t => t.TableName).ThenBy(t => t.ShowOrder).ToList();
var isMutiTable = trialConfigTableQuestionList.Select(t => t.TableName).Distinct().Count() > 1;
//外层问题处理
var fistLeveLNameList = trialConfigQuestionList.Select(t => new DynamicColumnConfig.ColumItem()
{
Id = t.QuestionId,
Name = t.QuestionName,
CDISCCode = t.CDISCCode
}).ToList();
var translateDicNameList = trialConfigQuestionList.Union(trialConfigTableQuestionList).Where(t => t.TranslateDicName.IsNotNullOrEmpty()).Select(t => t.TranslateDicName).Distinct().ToList();
//单表格问题(直接用表格问题名称) 或者没有表格问题
if (isMutiTable == false)
{
//一个表格,或者没有表格
var tableNameList = trialConfigTableQuestionList.Select(t => new DynamicColumnConfig.ColumItem() { Id = t.QuestionId, Name = t.QuestionName, CDISCCode = t.CDISCCode }).ToList();
configCoumNameList = fistLeveLNameList.Union(tableNameList).ToList();
//最终的病灶列表 要把裁判的也要加进去,需要处理裁判标记
foreach (var item in taskList.OrderBy(t => t.SubjectCode).ThenBy(t => t.ArmEnum).ThenBy(t => t.VisitTaskNum))
{
//有病灶,并且至少配置了一个病灶问题
if (item.LesionList.Count > 0 && item.LesionList.SelectMany(t => t.LessionAnswerList).Any())
{
foreach (var lession in item.LesionList.OrderBy(t => t.LessionType).ThenBy(t => t.LessionCode))
{
//病灶配置了问题 才进行导出
if (lession.LessionAnswerList.Count > 0)
{
var dynamicLessionInfoList = new List<CommonQuesionInfo>();
var dynamicPartialLessionInfoList = lession.LessionAnswerList.Select(t => new CommonQuesionInfo() { QuestionId = t.TableQuesionId, QuestionName = t.QuestionName, QuestionValue = t.QuestionValue, TranslateDicName = t.TranslateDicName, CDISCCode = t.CDISCCode });
//两部分组成 外层问题+ 动态的表格问题
dynamicLessionInfoList = item.QuestionAnswerList.Union(dynamicPartialLessionInfoList).ToList();
var cloneItem = item.Clone();
cloneItem.QuestionAnswerList = dynamicLessionInfoList;
list.Add(cloneItem);
}
}
}
else
{
//要把裁判任务加进去 裁判任务上没有病灶
list.Add(item);
}
}
dynamicColumnConfig = new DynamicColumnConfig()
{
AutoColumnTitleRowIndex = 1,
AutoColumnStartIndex = 6,
TempalteLastColumnIndex = 10,
DynamicItemDicName = "TranslateDicName",
DynamicItemValueName = "QuestionValue",
DynamicItemTitleName = "QuestionName",
DynamicItemTitleId = "QuestionId",
DynamicListName = "QuestionAnswerList",
RemoveColunmIndexList = removeColumnIndexList,
ColumnIdNameList = configCoumNameList,
IsCDISCExport = true,
//CDISCList = cdiscCodeList,
TranslateDicNameList = translateDicNameList
};
}
else
{
//多表格问题,需要用新的模板,并且用行的方式展示 不是之前表格名称_表格子问题名的方式
export_Template = StaticData.Export.CDISC_MutiTable_Reading_Export;
configCoumNameList = fistLeveLNameList.ToList();
var translateUnitList = await _dictionaryService.GetBasicDataSelect("ValueUnit");
foreach (var item in taskList.OrderBy(t => t.SubjectCode).ThenBy(t => t.ArmEnum).ThenBy(t => t.VisitTaskNum))
{
//有病灶,并且至少配置了一个病灶问题
if (item.LesionList.Count > 0 && item.LesionList.SelectMany(t => t.LessionAnswerList).Any())
{
foreach (var lession in item.LesionList.OrderBy(t => t.LessionType).ThenBy(t => t.LessionCode))
{
//病灶配置了问题 才进行导出
if (lession.LessionAnswerList.Count > 0)
{
//遍历病灶的每个问题
foreach (var lessionItem in lession.LessionAnswerList)
{
var cloneItem = item.Clone();
string unitText = lessionItem.Unit switch
{
ValueUnit.Custom => lessionItem.CustomUnit,
null => "",
_ => _userInfo.IsEn_Us ? translateUnitList.FirstOrDefault(t => t.Code == ((int)lessionItem.Unit).ToString())?.Value ?? ""
: translateUnitList.FirstOrDefault(t => t.Code == ((int)lessionItem.Unit).ToString())?.ValueCN ?? ""
};
cloneItem.Group = lessionItem.TableName;
cloneItem.TRLINKID = lessionItem.RowId.ToString();
cloneItem.TRORRES = lessionItem.QuestionValue;
cloneItem.TRTEST = lessionItem.CDISCCode;
cloneItem.TRORRESU = unitText;
list.Add(cloneItem);
}
//var dynamicLessionInfoList = new List<CommonQuesionInfo>();
//var dynamicPartialLessionInfoList = lession.LessionAnswerList.Select(t => new CommonQuesionInfo() { QuestionId = t.TableQuesionId, QuestionName = t.QuestionName, QuestionValue = t.QuestionValue, TranslateDicName = t.TranslateDicName, CDISCCode = t.CDISCCode });
////两部分组成 外层问题+ 动态的表格问题
//dynamicLessionInfoList = item.QuestionAnswerList.Union(dynamicPartialLessionInfoList).ToList();
}
}
}
else
{
//要把裁判任务加进去 裁判任务上没有病灶
list.Add(item);
}
}
dynamicColumnConfig = new DynamicColumnConfig()
{
AutoColumnTitleRowIndex = 1,
AutoColumnStartIndex = 6,
TempalteLastColumnIndex = 15,
DynamicItemDicName = "TranslateDicName",
DynamicItemValueName = "QuestionValue",
DynamicItemTitleName = "QuestionName",
DynamicItemTitleId = "QuestionId",
DynamicListName = "QuestionAnswerList",
RemoveColunmIndexList = removeColumnIndexList,
ColumnIdNameList = configCoumNameList,
IsCDISCExport = true,
//CDISCList = cdiscCodeList,
TranslateDicNameList = translateDicNameList
};
}
}
else
{
//CDISC 导出 只到外层问题级别 使用Id 填充Excel
var totalConfigCoumNameList = trialConfigQuestionList.ToList();
var configCoumNameList = totalConfigCoumNameList.Select(t => new DynamicColumnConfig.ColumItem() { Id = t.QuestionId, Name = t.QuestionName, CDISCCode = t.CDISCCode }).ToList();
var translateDicList = totalConfigCoumNameList.Where(t => t.TranslateDicName.IsNotNullOrEmpty()).Select(t => t.TranslateDicName).Distinct().ToList();
var cdiscCodeList = totalConfigCoumNameList.Select(t => t.CDISCCode).ToList();
dynamicColumnConfig = new DynamicColumnConfig()
{
AutoColumnTitleRowIndex = 1,
AutoColumnStartIndex = 6,
TempalteLastColumnIndex = 10,
DynamicItemDicName = "TranslateDicName",
DynamicItemValueName = "QuestionValue",
DynamicItemTitleName = "QuestionName",
DynamicItemTitleId = "QuestionId",
DynamicListName = "QuestionAnswerList",
RemoveColunmIndexList = removeColumnIndexList,
ColumnIdNameList = configCoumNameList,
IsCDISCExport = true,
//CDISCList = cdiscCodeList,
TranslateDicNameList = translateDicList
};
}
@ -2731,7 +3184,8 @@ namespace IRaCIS.Core.Application.Service.Common
#region 最终导出参数处理
if (inQuery.ReadingExportType == ExportResult.DetailedTableOfAdjudicationResults)
//裁判阅片明细表
if (export_Template == StaticData.Export.CommonJudgeReadingDetail_Export)
{
//R1 R2 两个人的访视都阅片完成了才可以,去除只有一个人阅片完成的访视
//找到只有一个人阅片的受试者 和访视
@ -2747,7 +3201,10 @@ namespace IRaCIS.Core.Application.Service.Common
//处理裁判标记
list = DealJudgeMark(criterion.ArbitrationRule, criterion.IsGlobalReading, list);
//裁判阅片明细表
//处理肿瘤学结果
list = DealOncologyResult(_oncologyRepository, criterion.IsOncologyReading, list);
//裁判阅片明细表 处理完标记后处理
if (export_Template == StaticData.Export.CommonJudgeReadingDetail_Export)
{
//裁判产生标记为空的数据过滤掉

View File

@ -94,7 +94,7 @@ namespace IRaCIS.Core.Application.Service
#region DTO
public class DoctorDownloadInfo
{
public Guid Id { get; set; }
public Guid? Id { get; set; }
public string Name { get; set; }
public string ReviewerCode { get; set; }
@ -111,7 +111,7 @@ namespace IRaCIS.Core.Application.Service
{
public int Language { get; set; }
public List<Guid> DoctorIdList { get; set; }
public List<Guid?> DoctorIdList { get; set; }
}
public class GetDoctoreAttachPathCommand

View File

@ -31,5 +31,7 @@ namespace IRaCIS.Application.Interfaces
Task<Dictionary<string, List<BasicDicSelectCopy>>> GetBasicDataSelect(string[] searchArray);
Task<List<BasicDicSelect>> GetBasicDataSelect(string searchKey);
}
}

View File

@ -45,6 +45,11 @@ namespace IRaCIS.Core.Application.Service
ValueCN = t.ValueCN,
FrontType = t.FrontType,
Description = t.Description,
Module = t.Module,
State = t.State,
Version = t.PublishLog.Version,
CreateTime = t.CreateTime,
InternationalizationType = t.InternationalizationType
}).ToListAsync();
await _fusionCache.SetAsync<List<InternationalizationSimpleDto>>(CacheKeys.FrontInternational, list, TimeSpan.FromDays(1));
@ -55,7 +60,7 @@ namespace IRaCIS.Core.Application.Service
}
/// <summary>
/// 前端批量提交,后端判断不存在就添加,存在就更新
/// 前端批量提交,后端查询判断不存在就添加,存在就更新 (这里提交接口也能提交后端的标识,对后端标识进行更新)
/// </summary>
/// <returns></returns>
[AllowAnonymous]
@ -65,7 +70,7 @@ namespace IRaCIS.Core.Application.Service
foreach (var item in batchList)
{
var find = await _internationalizationRepository.FirstOrDefaultAsync(t => t.Code == item.Code && t.Description == item.Description && t.InternationalizationType == 0);
var find = await _internationalizationRepository.FirstOrDefaultAsync(t => t.Code == item.Code && t.InternationalizationType == item.InternationalizationType);
if (find != null)
{
@ -77,7 +82,7 @@ namespace IRaCIS.Core.Application.Service
var frontState = item.State == null ? 0 : (int)item.State;
var mapItem = _mapper.Map<Internationalization>(item);
mapItem.InternationalizationType = 0;
//mapItem.InternationalizationType = 0;
// 0 是预翻译 1是已确认 2是后端废弃
mapItem.State = frontState;
@ -138,6 +143,7 @@ namespace IRaCIS.Core.Application.Service
mapItem.InternationalizationType = batchAdd.InternationalizationType;
mapItem.State = batchAdd.State;
mapItem.PublishLogId = batchAdd.PublishLogId;
var verifyExp1 = new EntityVerifyExp<Internationalization>()
{

View File

@ -7,6 +7,7 @@ using IRaCIS.Core.Infrastructure;
using MailKit;
using Medallion.Threading;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Options;
using MimeKit;
@ -485,8 +486,6 @@ namespace IRaCIS.Core.Application.Service
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig, null);
await _userLogRepository.AddAsync(new UserLog() { IP = _userInfo.IP, ActionIdentityUserId = _userInfo.IdentityUserId, ActionUserName = _userInfo.UserName, TargetIdentityUserId = userId, OptType = UserOptType.UpdateUserRole }, true);
}
//Site调研 用户加入项目
@ -547,8 +546,6 @@ namespace IRaCIS.Core.Application.Service
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
await _userLogRepository.AddAsync(new UserLog() { IP = _userInfo.IP, ActionIdentityUserId = _userInfo.IdentityUserId, ActionUserName = _userInfo.UserName, TargetIdentityUserId = userId, OptType = UserOptType.UpdateUserRole }, true);
}
@ -563,6 +560,9 @@ namespace IRaCIS.Core.Application.Service
var userType = await _userTypeRepository.FirstAsync(t => t.UserTypeEnum == UserTypeEnum.IndependentReviewer);
//需要创建新的账户
var isNeedCreateNewUser = false;
var @lock = _distributedLockProvider.CreateLock($"UserCode");
using (await @lock.AcquireAsync())
@ -572,6 +572,7 @@ namespace IRaCIS.Core.Application.Service
if (!isDoctorHaveAccount)
{
isNeedCreateNewUser = true;
var saveItem = new IdentityUser() { FirstName = doctor.FirstName, LastName = doctor.LastName, EMail = doctor.EMail };
@ -582,6 +583,8 @@ namespace IRaCIS.Core.Application.Service
saveItem.IsTestUser = true;
}
saveItem.UserCeateSource = UserCeateSource.ReviewerSelect;
saveItem.TrialId = trialId;
saveItem.Code = _identityUserRepository.Select(t => t.Code).DefaultIfEmpty().Max() + 1;
@ -673,7 +676,12 @@ namespace IRaCIS.Core.Application.Service
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig, null);
await _userLogRepository.AddAsync(new UserLog() { IP = _userInfo.IP, ActionIdentityUserId = _userInfo.IdentityUserId, ActionUserName = _userInfo.UserName, TargetIdentityUserId = sysUserInfo.Id, OptType = UserOptType.UpdateUserRole }, true);
//创建账号 和创建角色 一条,更新的时候才记录更新角色
if (isNeedCreateNewUser == false)
{
await _userLogRepository.AddAsync(new UserLog() { IP = _userInfo.IP, ActionIdentityUserId = _userInfo.IdentityUserId, ActionUserName = _userInfo.UserName, TargetIdentityUserId = sysUserInfo.Id, OptType = UserOptType.UpdateUserRole }, true);
}
return (sysUserInfo.Id, sysUserInfo.UserRoleList.First(t => t.UserTypeEnum == UserTypeEnum.IndependentReviewer).Id);

View File

@ -0,0 +1,181 @@
using DocumentFormat.OpenXml.EMMA;
using FellowOakDicom;
using IRaCIS.Core.Application.Helper;
using MassTransit;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Web;
using static IRaCIS.Core.Domain.Share.StaticData;
namespace IRaCIS.Core.Application.Service
{
/// <summary>
/// 项目影像后台下载,不打包
/// </summary>
/// <param name="_trialRepository"></param>
/// <param name="_oSSService"></param>
[ApiExplorerSettings(GroupName = "Common")]
public class TrialImageDownloadService(IRepository<Trial> _trialRepository, IOSSService _oSSService, IWebHostEnvironment _hostEnvironment) : BaseService
{
/// <summary>
/// 后端api swagger 下载项目影像
/// </summary>
/// <param name="trialId"></param>
/// <returns></returns>
[HttpPost]
[AllowAnonymous]
public async Task<IResponseOutput> DownloadTrialImage(Guid trialId)
{
var subjectCodeList = new List<string>() { "05002", "07006", "07026" };
var downloadInfo = _trialRepository.Where(t => t.Id == trialId).Select(t => new
{
t.ResearchProgramNo,
VisitList = t.SubjectVisitList.Where(t=>subjectCodeList.Contains(t.Subject.Code)).Select(sv => new
{
TrialSiteCode = sv.TrialSite.TrialSiteCode,
SubjectCode = sv.Subject.Code,
VisitName = sv.VisitName,
StudyList = sv.StudyList.Select(u => new
{
u.PatientId,
u.StudyTime,
u.StudyCode,
SeriesList = u.SeriesList.Select(z => new
{
z.Modality,
InstancePathList = z.DicomInstanceList.Select(k => new
{
k.Path
}).ToList()
})
}).ToList(),
NoneDicomStudyList = sv.NoneDicomStudyList.Select(nd => new
{
nd.Modality,
nd.StudyCode,
nd.ImageDate,
FileList = nd.NoneDicomFileList.Select(file => new
{
file.FileName,
file.Path,
file.FileType
}).ToList()
}).ToList()
}).ToList()
}).FirstOrDefault();
var count = downloadInfo.VisitList.SelectMany(t => t.NoneDicomStudyList).SelectMany(t => t.FileList).Count();
var count2 = downloadInfo.VisitList.SelectMany(t => t.StudyList).SelectMany(t => t.SeriesList).SelectMany(t=>t.InstancePathList).Count();
if (downloadInfo != null)
{
var rootFolder = FileStoreHelper.GetDonwnloadImageFolder(_hostEnvironment);
// 获取无效字符(系统定义的)
string invalidChars = new string(Path.GetInvalidFileNameChars()) + new string(Path.GetInvalidPathChars());
// 用正则表达式替换所有非法字符为下划线或空字符
string pattern = $"[{Regex.Escape(invalidChars)}]";
var regexNo = Regex.Replace(downloadInfo.ResearchProgramNo, pattern, "_");
// 创建一个临时文件夹来存放文件
string trialFolderPath = Path.Combine(rootFolder, $"{regexNo}_{NewId.NextGuid()}");
Directory.CreateDirectory(trialFolderPath);
foreach (var visitItem in downloadInfo.VisitList)
{
if (visitItem.StudyList.Count() == 0 && visitItem.NoneDicomStudyList.Count() == 0)
{
continue;
}
#region 处理 中心受试者dicom non-dicom 文件夹层级
var siteFolderPath = Path.Combine(trialFolderPath, visitItem.TrialSiteCode);
if (!Directory.Exists(siteFolderPath))
{
Directory.CreateDirectory(siteFolderPath);
}
#endregion
foreach (var studyInfo in visitItem.StudyList)
{
// 遍历 Series
foreach (var seriesInfo in studyInfo.SeriesList)
{
string studyDicomFolderPath = Path.Combine(siteFolderPath, $"{visitItem.SubjectCode}_{visitItem.VisitName}_DICOM", $"{studyInfo.StudyCode}_{studyInfo.StudyTime?.ToString("yyyy-MM-dd")}_{seriesInfo.Modality}");
// 创建 影像 文件夹
Directory.CreateDirectory(studyDicomFolderPath);
// 遍历 InstancePathList
foreach (var instanceInfo in seriesInfo.InstancePathList)
{
// 复制文件到相应的文件夹
string destinationPath = Path.Combine(studyDicomFolderPath, Path.GetFileName(instanceInfo.Path));
//下载到当前目录
await _oSSService.DownLoadFromOSSAsync(instanceInfo.Path, destinationPath);
}
}
}
foreach (var noneDicomStudy in visitItem.NoneDicomStudyList)
{
string studyNoneDicomFolderPath = Path.Combine(siteFolderPath, $"{visitItem.SubjectCode}_{visitItem.VisitName}_Non-DICOM", $"{noneDicomStudy.StudyCode}_{noneDicomStudy.ImageDate.ToString("yyyy-MM-dd")}_{noneDicomStudy.Modality}");
Directory.CreateDirectory(studyNoneDicomFolderPath);
foreach (var file in noneDicomStudy.FileList)
{
string destinationPath = Path.Combine(studyNoneDicomFolderPath, Path.GetFileName(file.FileName));
//下载到当前目录
await _oSSService.DownLoadFromOSSAsync(HttpUtility.UrlDecode(file.Path), destinationPath);
}
}
}
}
return ResponseOutput.Ok();
}
}
}

View File

@ -1,5 +1,8 @@
using IRaCIS.Application.Contracts;
using IRaCIS.Application.Interfaces;
using IRaCIS.Core.Application.Helper;
using IRaCIS.Core.Application.ViewModel;
using IRaCIS.Core.Infrastructure.Extention;
using Microsoft.AspNetCore.Mvc;
using Panda.DynamicWebApi.Attributes;
@ -9,7 +12,11 @@ namespace IRaCIS.Core.Application.Service
/// 医生文档关联关系维护
/// </summary>
[ApiExplorerSettings(GroupName = "Reviewer")]
public class AttachmentService(IRepository<Attachment> _attachmentrepository, IRepository<Doctor> _doctorrepository, IMapper _mapper, IUserInfo _userInfo, IStringLocalizer _localizer) : BaseService, IAttachmentService
public class AttachmentService(IRepository<Attachment> _attachmentrepository,
IRepository<Dictionary> _dictionaryRepository,
IRepository<Enroll> _enrollRepository,
IOSSService _oSSService,
IRepository<Doctor> _doctorrepository, IMapper _mapper, IUserInfo _userInfo, IStringLocalizer _localizer) : BaseService, IAttachmentService
{
@ -89,6 +96,150 @@ namespace IRaCIS.Core.Application.Service
return attachmentList;
}
/// <summary>
/// 获取项目医生附件
/// </summary>
/// <param name="inDto"></param>
/// <returns></returns>
[HttpPost]
public async Task<PageOutput<AttachmentDTO>> GetTrialAttachments(GetTrialAttachmentsInDto inDto)
{
var dicList=await _dictionaryRepository.Where(x => x.Parent.Code == "CertificateFileType").Select(x=>x.Code).ToListAsync();
var attachmentList = await _attachmentrepository
.Where(a =>a.Doctor.EnrollList.Any(x=>x.TrialId == inDto.TrialId && x.EnrollStatus >= EnrollStatus.InviteIntoGroup)|| a.TrialId == inDto.TrialId)
.WhereIf(inDto.DoctorId!=null,x=>x.DoctorId==inDto.DoctorId)
.WhereIf(inDto.IsAuthorizedView != null, x => x.IsAuthorizedView == inDto.IsAuthorizedView)
.WhereIf(inDto.Type.IsNotNullOrEmpty(), x => x.Type == inDto.Type)
.WhereIf(inDto.FileName.IsNotNullOrEmpty(), x => inDto.FileName.Contains(x.FileName))
.Where(a => dicList.Contains(a.Type)||a.Type==string.Empty)
.ProjectTo<AttachmentDTO>(_mapper.ConfigurationProvider, new { TrialId = inDto.TrialId }).ToPagedListAsync(inDto);
return attachmentList;
}
/// <summary>
/// 获取医生列表
/// </summary>
/// <param name="inDto"></param>
/// <returns></returns>
[HttpPost]
public async Task<List<GetTrialDoctorListOutDto>> GetTrialDoctorList(GetTrialDoctorSelectInDto inDto)
{
var result= await _enrollRepository.Where(x => x.TrialId == inDto.TrialId && x.EnrollStatus >= EnrollStatus.InviteIntoGroup).Select(x => x.Doctor)
.ProjectTo<GetTrialDoctorListOutDto>(_mapper.ConfigurationProvider).OrderBy(x=>x.BlindName).ToListAsync();
return result;
}
/// <summary>
/// 修改稽查状态
/// </summary>
/// <param name="inDto"></param>
/// <returns></returns>
[HttpPost]
public async Task<IResponseOutput> SetAuthorizedView(SetAttachmentAuthorizedView inDto)
{
await _attachmentrepository.UpdatePartialFromQueryAsync(x => inDto.Ids.Contains(x.Id), x => new Attachment() { IsAuthorizedView = inDto.IsAuthorizedView });
await _attachmentrepository.SaveChangesAsync();
return ResponseOutput.Ok();
}
/// <summary>
/// 删除
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[HttpPost]
public async Task<IResponseOutput> DeleteAttachment(DeleteAttachment inDto)
{
var success = await _attachmentrepository.DeleteFromQueryAsync(t => inDto.Ids.Contains(t.Id));
await _attachmentrepository.SaveChangesAsync();
return ResponseOutput.Ok();
}
/// <summary>
/// 上传项目医生
/// </summary>
/// <param name="attachmentList"></param>
/// <returns></returns>
[HttpPost]
public async Task<IEnumerable<AttachmentDTO>> SaveTrialAttachments(IEnumerable<AttachmentDTO> attachmentList)
{
foreach (var item in attachmentList)
{
if (item.DoctorId != null)
{
await _attachmentrepository.BatchDeleteNoTrackingAsync(a => a.DoctorId == item.DoctorId && a.Type == item.Type);
}
}
var attachments = _mapper.Map<IEnumerable<Attachment>>(attachmentList).ToList();
var newAttachment = attachments.Where(t => t.Id == Guid.Empty);
await _attachmentrepository.AddRangeAsync(newAttachment);
await _attachmentrepository.SaveChangesAsync();
var list = _mapper.Map<IEnumerable<AttachmentDTO>>(attachments).ToList();
list.ForEach(t => t.FullPath = t.Path + "?access_token=" + _userInfo.UserToken);
return list;
}
/// <summary>
/// 更新医生资质
/// </summary>
/// <param name="attachment"></param>
/// <returns></returns>
[HttpPost]
public async Task<IResponseOutput> UpdateTrialAttachments(AttachmentDTO attachment)
{
if (!attachment.Path.Contains(attachment.DoctorId.ToString()))
{
var attachmentData= await _attachmentrepository.Where(a => a.Id == attachment.Id).FirstNotNullAsync();
var fileName= attachmentData.Path.Split("/").Last();
attachment.Path = $"/systemData/reviewe/{attachment.Type}/{attachment.DoctorId}/{fileName}";
await _oSSService.MoveObject(attachmentData.Path, attachment.Path);
}
if (attachment.DoctorId != null)
{
await _attachmentrepository.DeleteFromQueryAsync(a =>a.Id!=attachment.Id&& a.DoctorId == attachment.DoctorId && a.Type == attachment.Type);
}
attachment.UpdateTime = DateTime.Now;
await _attachmentrepository.UpdateFromDTOAsync(attachment);
await _attachmentrepository.SaveChangesAsync();
return ResponseOutput.Ok();
}
[NonDynamicMethod]
public async Task<AttachmentDTO> GetDetailById(Guid attachmentId)
{

View File

@ -2,19 +2,80 @@
{
public class AttachmentDTO
{
public Guid Id { get; set; }
public Guid DoctorId { get; set; }
public Guid? Id { get; set; }
public Guid? DoctorId { get; set; }
public bool IsOfficial { get; set; }
public string Type { get; set; } = string.Empty;
public string Path { get; set; } = string.Empty;
public string FullPath { get; set; } = string.Empty;
public string FileName { get; set; } = string.Empty;
public DateTime? CreateTime { get; set; }
public DateTime? UpdateTime { get; set; }
public int Language { get; set; }
public bool ReUpload { get; set; } = false;
public Guid? TrialId { get; set; }
/// <summary>
/// 是否授权
/// </summary>
public bool IsAuthorizedView { get; set; } = false;
public string BlindName { get; set; } = string.Empty;
public string BlindNameCN { get; set; } = string.Empty;
public string ChineseName { get; set; } = string.Empty;
public string FirstName { get; set; } = string.Empty;
public string FullName => LastName + " / " + FirstName;
public string LastName { get; set; } = string.Empty;
public string UserName { get; set; } = string.Empty;
}
public class SetAttachmentAuthorizedView
{
public List<Guid> Ids { get; set; }
public bool IsAuthorizedView { get; set; }
}
public class DeleteAttachment
{
public List<Guid> Ids { get; set; }
}
public class GetTrialDoctorSelectInDto
{
public Guid TrialId { get; set; }
}
public class GetTrialDoctorListOutDto
{
public Guid Id { get; set; }
public string BlindName { get; set; } = string.Empty;
public string BlindNameCN { get; set; } = string.Empty;
public string ChineseName { get; set; } = string.Empty;
public string FirstName { get; set; } = string.Empty;
public string FullName => LastName + " / " + FirstName;
public string LastName { get; set; } = string.Empty;
}
public class GetTrialAttachmentsInDto:PageInput
{
public Guid TrialId { get; set; }
public string? Type { get; set; }
public Guid? DoctorId { get; set; }
public string? FileName { get; set; }
public bool? IsAuthorizedView { get; set; }
}
public class ReviewerAckDTO
{
public Guid Id { get; set; }

View File

@ -169,6 +169,16 @@ namespace IRaCIS.Application.Contracts
public class ReviewerConfirmationQueryDTO : PageInput
{
public Guid TrialId { get; set; } = Guid.Empty;
public DateTime? OptStartTime{ get; set; }
public DateTime? OptEndTime { get; set; }
public string? Code { get; set; }
public UserTypeEnum? UserTypeEnum { get; set; }
public string? OptUserName { get; set; }
}
public class SelectionReviewerDTO : DoctorDTO
@ -194,14 +204,48 @@ namespace IRaCIS.Application.Contracts
public string City { get; set; } = string.Empty;
public string HospitalName { get; set; } = string.Empty;
public string HospitalNameCN { get; set; } = string.Empty;
public string Country { get; set; } = string.Empty;
}
public class GetTrialDoctorListInDto : PageInput
{
public Guid TrialId { get; set; } = Guid.Empty;
/// <summary>
/// 亚专业
/// </summary>
public Guid? SubspecialityId { get; set; }
/// <summary>
/// 专业
/// </summary>
public Guid? SpecialityId { get; set; }
/// <summary>
/// 机构
/// </summary>
public Guid? HospitalId { get; set; }
}
public class TrialDoctorInfo : ConfirmationReviewerDTO
{
public List<string> SubspecialityList => DictionaryList.Where(t => t.ParentCode == StaticData.Subspeciality).OrderBy(t => t.ShowOrder).Select(t => t.Value).ToList();
public List<string> SubspecialityCNList => DictionaryList.Where(t => t.ParentCode == StaticData.Subspeciality).OrderBy(t => t.ShowOrder).Select(t => t.ValueCN).ToList();
}
public class ConfirmationReviewerDTO : DoctorOptDTO
{
public int DoctorTrialState { get; set; }
public string OptUserName { get; set; } = string.Empty;
public UserTypeEnum? UserTypeEnum { get; set; }
public DateTime? OptTime { get; set; }
public string? OptTimeStr => OptTime?.ToString("yyyy-MM-dd HH:mm:ss");
@ -223,6 +267,10 @@ namespace IRaCIS.Application.Contracts
public List<Guid> SubspecialityIds => DictionaryList.Where(t => t.ParentCode == StaticData.Subspeciality).OrderBy(t => t.ShowOrder).Select(t => t.Id).ToList();
public Guid? HospitalId { get; set; }
public Guid EnrollId { get; set; }
}
@ -235,6 +283,10 @@ namespace IRaCIS.Application.Contracts
public string OptUserName { get; set; } = String.Empty;
public DateTime? OptTime { get; set; }
public int OptUserType { get; set; }
public UserTypeEnum UserTypeEnum { get; set; }
}
#endregion
@ -523,6 +575,8 @@ namespace IRaCIS.Application.Contracts
{
public string BlindName { get; set; }
public bool IsHaveAccount { get; set; } = false;
public string BlindNameCN { get; set; } = string.Empty;
public List<DicView> DoctorDicViewDtos = new List<DicView>();

View File

@ -243,11 +243,137 @@ namespace IRaCIS.Core.Application.Service
{
#region 连表查询
//var query = from enroll in _enrollRepository.Where(x => x.TrialId == inQuery.TrialId && x.EnrollStatus >= EnrollStatus.InviteIntoGroup)
// join enrollDetail in _enrollDetailRepository.Where(x => x.TrialId == inQuery.TrialId && x.EnrollStatus >= EnrollStatus.InviteIntoGroup) on new { enroll.DoctorId, enroll.EnrollStatus } equals new { enrollDetail.DoctorId, enrollDetail.EnrollStatus } into enrollDetailtemp
// from leftenrollDetail in enrollDetailtemp.DefaultIfEmpty()
// select new ConfirmationReviewerDTO()
// {
// DoctorTrialState = (int)leftenrollDetail.EnrollStatus,
// OptTime = leftenrollDetail.CreateTime,
// UserTypeEnum = leftenrollDetail.CreateUserRole.UserTypeEnum,
// OptUserName = leftenrollDetail.CreateUserRole.UserName,
// SubmmitTime = leftenrollDetail.CreateTime,
// SubmmitUserName = leftenrollDetail.CreateUserRole.UserName,
// //BlindName= enroll.BlindName,
// //BlindNameCN= enroll.BlindNameCN,
// SpecialityId = enroll.Doctor.SpecialityId,
// SpecialityCN = enroll.Doctor.Speciality.ValueCN,
// Speciality = enroll.Doctor.Speciality.Value,
// //SpecialityOther=enroll.Doctor.Speciality.SpecialityOther,
// //SpecialityOtherCN= enroll.Doctor.Speciality.SpecialityOtherCN,
// DictionaryList = enroll.Doctor.DoctorDicRelationList.Where(t => t.KeyName == StaticData.ReadingType || t.KeyName == StaticData.Subspeciality).Select(t => t.Dictionary).OrderBy(t => t.ShowOrder).Select(x => new DicView()
// {
// Id = x.Id,
// ParentCode = x.Parent.Code,
// ShowOrder = x.ShowOrder,
// Value = x.Value,
// ValueCN = x.ValueCN
// }).ToList(),
// //HospitalId= enroll.BlindName
// Id = enroll.Doctor.Id,
// Code = enroll.DoctorUser.UserName,
// //FirstName=enroll.FirstName,
// //LastName = enroll.LastName,
// //ChineseName = enroll.ChineseName,
// //City = enroll.City,
// //HospitalName = enroll.HospitalName,
// //Country = enroll.Country,
// };
//var result = await query
// .WhereIf(inQuery.OptStartTime != null, x => x.OptTime >= inQuery.OptStartTime)
// .WhereIf(inQuery.OptEndTime != null, x => x.OptTime <= inQuery.OptEndTime)
// .WhereIf(inQuery.UserTypeEnum != null, x => x.UserTypeEnum == inQuery.UserTypeEnum)
// .WhereIf(inQuery.Code.IsNotNullOrEmpty(), x => x.Code.Contains(inQuery.Code))
// .WhereIf(inQuery.OptUserName.IsNotNullOrEmpty(), x => x.OptUserName.Contains(inQuery.OptUserName))
// .ToPagedListAsync(inQuery);
#endregion
#region 查询第一个
// 这里这样写的原因是因为 要取最新的入组详情 然后还要根据时间筛选 入组信息
var query = _enrollDetailRepository
.Where(x => x.TrialId == inQuery.TrialId && x.EnrollStatus >= EnrollStatus.InviteIntoGroup);
var enrollStateList = await query
.ProjectTo<DoctorStateModelDTO>(_mapper.ConfigurationProvider).ToListAsync();
enrollStateList = enrollStateList.GroupBy(e => e.DoctorId)
.Select(g => g.OrderByDescending(e => e.OptTime).FirstOrDefault()).ToList();
enrollStateList = enrollStateList
.WhereIf(inQuery.OptStartTime != null, x => x.OptTime >= inQuery.OptStartTime)
.WhereIf(inQuery.OptEndTime != null, x => x.OptTime <= inQuery.OptEndTime)
.WhereIf(inQuery.UserTypeEnum != null, x => x.UserTypeEnum == inQuery.UserTypeEnum)
.ToList();
#endregion
var doctorids = enrollStateList.Select(x => x.DoctorId).ToList();
var doctorQuery = _enrollRepository.Where(x => x.TrialId == inQuery.TrialId && x.EnrollStatus >= EnrollStatus.InviteIntoGroup)
.ProjectTo<ConfirmationReviewerDTO>(_mapper.ConfigurationProvider);
.WhereIf(inQuery.OptStartTime != null || inQuery.OptEndTime != null || inQuery.UserTypeEnum != null, x => doctorids.Contains(x.DoctorId))
.ProjectTo<ConfirmationReviewerDTO>(_mapper.ConfigurationProvider)
.WhereIf(inQuery.Code.IsNotNullOrEmpty(), x => x.Code.Contains(inQuery.Code))
.WhereIf(inQuery.OptUserName.IsNotNullOrEmpty(), x => x.OptUserName.Contains(inQuery.OptUserName));
var doctorPageList = await doctorQuery.ToPagedListAsync(inQuery);
doctorPageList.CurrentPageData.ToList().ForEach(u =>
{
u.DoctorTrialState = (int)EnrollStatus.InviteIntoGroup;
var opt = enrollStateList.OrderByDescending(x=>x.OptTime).FirstOrDefault(t => t.DoctorId == u.Id);
if (opt != null)
{
u.DoctorTrialState = opt.IntoGroupState;
u.OptTime = opt.OptTime;
u.UserTypeEnum = opt.UserTypeEnum;
u.OptUserName = opt.OptUserName;
}
});
return doctorPageList;
}
/// <summary>
/// 获取项目下医生列表
/// </summary>
/// <param name="inQuery"></param>
/// <returns></returns>
[HttpPost]
public async Task<PageOutput<TrialDoctorInfo>> GetTrialDoctorList(
GetTrialDoctorListInDto inQuery)
{
var doctorQuery = _enrollRepository.Where(x => x.TrialId == inQuery.TrialId && x.EnrollStatus == EnrollStatus.ConfirmIntoGroup)
.ProjectTo<TrialDoctorInfo>(_mapper.ConfigurationProvider);
var doctorPageList = await doctorQuery
.WhereIf(inQuery.HospitalId!=null,x=>x.HospitalId==inQuery.HospitalId.Value)
.WhereIf(inQuery.SpecialityId != null, x => x.SpecialityId == inQuery.SpecialityId.Value)
.WhereIf(inQuery.SubspecialityId != null, x => x.DictionaryList.Any(x=>x.Id==inQuery.SubspecialityId.Value))
.ToPagedListAsync(inQuery);
var enrollStateList = await _enrollDetailRepository.Where(x => x.TrialId == inQuery.TrialId && x.EnrollStatus > EnrollStatus.InviteIntoGroup)
.ProjectTo<DoctorStateModelDTO>(_mapper.ConfigurationProvider).ToListAsync();

View File

@ -1,4 +1,5 @@
using DocumentFormat.OpenXml.Office2019.Excel.ThreadedComments;
using DocumentFormat.OpenXml.Spreadsheet;
using IRaCIS.Application.Contracts;
using IRaCIS.Application.Interfaces;
using IRaCIS.Core.Application.Helper;
@ -17,6 +18,7 @@ namespace IRaCIS.Core.Application.Service
IRepository<DoctorDictionary> _doctorDictionaryRepository,
IRepository<DoctorSummarize> _doctorSummarizeRepository,
IRepository<Enroll> _enrollRepository,
IRepository<UserRole> _userRoleRepository,
IRepository<Hospital> _hospitalRepository,
IRepository<Dictionary> _dictionaryRepository,
IRepository<Attachment> _attachmentRepository,
@ -230,9 +232,9 @@ namespace IRaCIS.Core.Application.Service
/// <param name="inDto"></param>
/// <returns></returns>
[HttpPost]
public async Task<(DoctorSummarize,bool)> AddOrUpdateGneralSituation(SummarizeInfoDto inDto)
public async Task<(DoctorSummarize, bool)> AddOrUpdateGneralSituation(SummarizeInfoDto inDto)
{
if (inDto.TrialId != null&&inDto.Id==null)
if (inDto.TrialId != null && inDto.Id == null)
{
var systemCount = await _doctorSummarizeRepository.Where(x => x.DoctorId == inDto.DoctorId && x.TrialId == null).CountAsync();
if (systemCount == 0)
@ -241,7 +243,7 @@ namespace IRaCIS.Core.Application.Service
var dictionery = await _dictionaryRepository.Where(x => x.Parent.Code == "Indication" && x.Code == trialinfo.IndicationEnum.ToString()).FirstNotNullAsync();
inDto.IndicationEn = dictionery.Value;
inDto.Indication = dictionery.ValueCN;
var system= inDto.Clone();
var system = inDto.Clone();
system.TrialId = null;
system.IsMain = true;
await _doctorSummarizeRepository.InsertOrUpdateAsync(system, true);
@ -254,7 +256,7 @@ namespace IRaCIS.Core.Application.Service
if (systemCount == 0)
{
inDto.IsMain = true;
}
if (inDto.Indication.IsNullOrEmpty() && inDto.IndicationEn.IsNullOrEmpty())
{
@ -262,19 +264,19 @@ namespace IRaCIS.Core.Application.Service
inDto.IndicationEn = "Default";
}
var exists = await _doctorSummarizeRepository
.Where(x=> x.IndicationEn!=string.Empty && x.Indication!=string.Empty)
.Where( x => x.IndicationEn == inDto.IndicationEn|| x.Indication == inDto.Indication)
.AnyAsync(x => x.DoctorId == inDto.DoctorId && x.TrialId == null&&x.Id!=inDto.Id);
.Where(x => x.IndicationEn != string.Empty && x.Indication != string.Empty)
.Where(x => x.IndicationEn == inDto.IndicationEn || x.Indication == inDto.Indication)
.AnyAsync(x => x.DoctorId == inDto.DoctorId && x.TrialId == null && x.Id != inDto.Id);
if (exists)
{
throw new BusinessValidationFailedException(_localizer["DoctorSummarize_repeat"]);
}
}
if (inDto.TrialId != null && inDto.SaveSummarizeInfoType!= SaveSummarizeInfo.SaveSelf)
if (inDto.TrialId != null && inDto.SaveSummarizeInfoType != SaveSummarizeInfo.SaveSelf)
{
switch (inDto.SaveSummarizeInfoType)
{
@ -312,16 +314,16 @@ namespace IRaCIS.Core.Application.Service
if (inDto.TrialId == null && inDto.IsMain)
{
await _doctorSummarizeRepository.BatchUpdateNoTrackingAsync(x => x.DoctorId == inDto.DoctorId && x.TrialId == null,
await _doctorSummarizeRepository.BatchUpdateNoTrackingAsync(x => x.DoctorId == inDto.DoctorId && x.TrialId == null,
x => new DoctorSummarize()
{
IsMain = false
});
{
IsMain = false
});
}
var entity = await _doctorSummarizeRepository.InsertOrUpdateAsync(inDto, true);
return (entity,true);
return (entity, true);
}
/// <summary>
@ -332,7 +334,7 @@ namespace IRaCIS.Core.Application.Service
[HttpPost]
public async Task<bool> DeleteSummarizeInfo(DeleteSummarizeInfoInDto inDto)
{
var result = await _doctorSummarizeRepository.DeleteFromQueryAsync(x=>x.Id==inDto.Id);
var result = await _doctorSummarizeRepository.DeleteFromQueryAsync(x => x.Id == inDto.Id);
return await _doctorSummarizeRepository.SaveChangesAsync();
}
@ -344,7 +346,7 @@ namespace IRaCIS.Core.Application.Service
[HttpPost]
public async Task<UpdateGneralSituationDto> GetSummarizeInfo(GetSummarizeInfoInDto inDto)
{
if (inDto.TrialId != null)
{
var doctorInfo = await trialExperienceService.GetTrialDoctorInfo(new GetTrialDoctorInfoInDto()
@ -356,8 +358,9 @@ namespace IRaCIS.Core.Application.Service
}
var result = await _doctorSummarizeRepository.Where(t => t.DoctorId == inDto.DoctorId && t.TrialId == inDto.TrialId)
.ProjectTo<SummarizeInfoDto>(_mapper.ConfigurationProvider).ToListAsync();
return new UpdateGneralSituationDto() {
SummarizeList= result
return new UpdateGneralSituationDto()
{
SummarizeList = result
};
}
@ -377,30 +380,36 @@ namespace IRaCIS.Core.Application.Service
messageToSend.To.Add(new MailboxAddress(String.Empty, item));
}
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
{
;
var topicStr = string.Format(input.topicStr,string.Empty);
;
var topicStr = string.Format(input.topicStr, string.Empty);
var htmlBodyStr = string.Format(
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
_systemEmailConfig.SiteUrl.Replace("login",string.Empty)+inDto.Url
_systemEmailConfig.SiteUrl.Replace("login", string.Empty) + inDto.Url
);
return (topicStr, htmlBodyStr);
};
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(_emailNoticeConfigrepository,
EmailBusinessScenario.Reviewer_CV_Collection,
var scenario = EmailBusinessScenario.Reviewer_CV_Collection;
var emailConfig = _emailNoticeConfigrepository.Where(t => t.BusinessScenarioEnum == scenario && t.IsAutoSend && t.IsEnable).FirstOrDefault();
if (emailConfig != null)
{
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(emailConfig, messageToSend, emailConfigFunc);
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
}
messageToSend, emailConfigFunc);
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
return true;
}
@ -490,7 +499,7 @@ namespace IRaCIS.Core.Application.Service
var doctorBasicInfo = (await _doctorRepository.Where(t => t.Id == doctorId)
.ProjectTo<DoctorBasicInfoDTO>(_mapper.ConfigurationProvider).FirstOrDefaultAsync()).IfNullThrowException();
doctorBasicInfo.IsHaveAccount = await _userRoleRepository.AnyAsync(x => x.DoctorId == doctorId);
return doctorBasicInfo;

View File

@ -3,6 +3,7 @@ using AutoMapper.EquivalencyExpression;
using IRaCIS.Application.Contracts;
using IRaCIS.Application.Contracts.Pay;
using IRaCIS.Core.Application.Service.Reading.Dto;
using IRaCIS.Core.Domain.Models;
using IRaCIS.Core.Domain.Share;
namespace IRaCIS.Core.Application.Service
@ -13,6 +14,8 @@ namespace IRaCIS.Core.Application.Service
{
var isEn_Us = false;
Guid TrialId = Guid.Empty;
// 从 DateTime 映射到 DateOnly
CreateMap<DateTime, DateOnly>().ConvertUsing(dt => DateOnly.FromDateTime(dt));
@ -70,7 +73,15 @@ namespace IRaCIS.Core.Application.Service
CreateMap<Postgraduate, PostgraduateViewModel>();
// .ForMember(t => t.Hospital, c => c.MapFrom(d => d.HospitalEnt.HospitalName))
//.ForMember(t => t.HospitalCN, c => c.MapFrom(d => d.HospitalEnt.HospitalNameCN));
CreateMap<Attachment, AttachmentDTO>();
CreateMap<Attachment, AttachmentDTO>()
.ForMember(d => d.BlindName, u => u.MapFrom(t => t.Doctor.BlindName))
.ForMember(d => d.BlindNameCN, u => u.MapFrom(t => t.Doctor.BlindNameCN))
.ForMember(d => d.ChineseName, u => u.MapFrom(t => t.Doctor.ChineseName))
.ForMember(d => d.FirstName, u => u.MapFrom(t => t.Doctor.FirstName))
.ForMember(d => d.LastName, u => u.MapFrom(t => t.Doctor.LastName))
.ForMember(d => d.UserName, u => u.MapFrom(t => t.Doctor.EnrollList.Where(y=>y.TrialId== TrialId).Select(x=>x.DoctorUser.UserName).First()))
;
CreateMap<Doctor, GetTrialDoctorListOutDto>();
CreateMap<Doctor, ResumeConfirmDTO>();
CreateMap<Doctor, DoctorSelectDTO>();
@ -178,16 +189,32 @@ namespace IRaCIS.Core.Application.Service
CreateMap<Hospital, EmploymentDTO>();
CreateMap<EnrollDetail, DoctorStateModelDTO>()
.ForMember(d => d.UserTypeEnum, u => u.MapFrom(s => s.CreateUserRole.UserTypeEnum))
.ForMember(d => d.IntoGroupState, u => u.MapFrom(s => s.EnrollStatus))
.ForMember(d => d.OptTime, u => u.MapFrom(s => s.CreateTime))
.ForMember(d => d.OptUserName, u => u.MapFrom(s => s.CreateUserRole.IdentityUser.UserName));
CreateMap<Enroll, TrialDoctorInfo>().IncludeMembers(t => t.Doctor, t => t.Doctor.Hospital)
.ForMember(o => o.DictionaryList, t => t.MapFrom(u => u.Doctor.DoctorDicRelationList.Where(t => t.KeyName == StaticData.ReadingType || t.KeyName == StaticData.Subspeciality).Select(t => t.Dictionary).OrderBy(t => t.ShowOrder)))
.ForMember(d => d.Speciality, u => u.MapFrom(s => s.Doctor.Speciality.Value))
.ForMember(d => d.SpecialityCN, u => u.MapFrom(s => s.Doctor.Speciality.ValueCN))
.ForMember(d => d.Id, u => u.MapFrom(s => s.Doctor.Id))
.ForMember(d => d.Code, u => u.MapFrom(s => s.DoctorUser.UserName));
CreateMap<Doctor, TrialDoctorInfo>();
CreateMap<Hospital, TrialDoctorInfo>();
CreateMap<Enroll, ConfirmationReviewerDTO>().IncludeMembers(t => t.Doctor, t => t.Doctor.Hospital)
.ForMember(o => o.DictionaryList, t => t.MapFrom(u => u.Doctor.DoctorDicRelationList.Where(t => t.KeyName == StaticData.ReadingType || t.KeyName == StaticData.Subspeciality).Select(t => t.Dictionary).OrderBy(t => t.ShowOrder)))
.ForMember(d => d.Speciality, u => u.MapFrom(s => s.Doctor.Speciality.Value))
.ForMember(d => d.SpecialityCN, u => u.MapFrom(s => s.Doctor.Speciality.ValueCN))
.ForMember(d => d.Id, u => u.MapFrom(s => s.Doctor.Id))
.ForMember(d => d.Code, u => u.MapFrom(s => s.DoctorUser.UserName))
.ForMember(d => d.HospitalName, u => u.MapFrom(s => s.Doctor.HospitalName))
.ForMember(d => d.HospitalNameCN, u => u.MapFrom(s => s.Doctor.HospitalNameCN))
.ForMember(d => d.EnrollId, u => u.MapFrom(s => s.Id))
;
CreateMap<Doctor, ConfirmationReviewerDTO>();
CreateMap<Hospital, ConfirmationReviewerDTO>();

View File

@ -0,0 +1,585 @@
//--------------------------------------------------------------------
// 此代码由liquid模板自动生成 byzhouhang 20240909
// 生成时间 2025-03-27 06:13:33Z
// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。
//--------------------------------------------------------------------
using IRaCIS.Core.Domain.Models;
using Microsoft.AspNetCore.Mvc;
using IRaCIS.Core.Application.Interfaces;
using IRaCIS.Core.Application.ViewModel;
using IRaCIS.Core.Infrastructure.Extention;
using System.Threading.Tasks;
using IRaCIS.Core.Infra.EFCore;
using AutoMapper.Execution;
using System.Linq;
using IRaCIS.Core.Infrastructure;
using DocumentFormat.OpenXml.Office2010.Excel;
using MassTransit;
using NPOI.POIFS.Properties;
using Org.BouncyCastle.Crypto;
using Microsoft.AspNetCore.Http;
namespace IRaCIS.Core.Application.Service;
/// <summary>
/// 稽查文档
/// </summary>
/// <returns></returns>
[ApiExplorerSettings(GroupName = "FileRecord")]
public class AuditDocumentService(IRepository<AuditDocument> _auditDocumentRepository,
IMapper _mapper, IUserInfo _userInfo, IStringLocalizer _localizer): BaseService
{
/// <summary>
/// 获取稽查文档
/// </summary>
/// <param name="inQuery"></param>
/// <returns></returns>
[HttpPost]
public async Task<PageOutput<AuditDocumentView>> GetAuditDocumentList(AuditDocumentQuery inQuery)
{
var auditDocumentQueryable = _auditDocumentRepository
.ProjectTo<AuditDocumentView>(_mapper.ConfigurationProvider);
var pageList = await auditDocumentQueryable.ToPagedListAsync(inQuery);
return pageList;
}
/// <summary>
/// 修改稽查文档
/// </summary>
/// <param name="inDto"></param>
/// <returns></returns>
[HttpPost]
public async Task<IResponseOutput> UpdateAuditDocument(AuditDocumentUpdateDto inDto)
{
AuditDocumentAddOrEdit addOrEdit = _mapper.Map<AuditDocumentAddOrEdit>(inDto);
addOrEdit.IsUpdate = true;
var result= await AddOrUpdateAuditDocument(addOrEdit);
return ResponseOutput.Ok(result.Id);
}
/// <summary>
/// 新增文件夹
/// </summary>
/// <param name="inDto"></param>
/// <returns></returns>
/// <exception cref="BusinessValidationFailedException"></exception>
[HttpPost]
public async Task<IResponseOutput> AddFolder(AuditDocumentAddOrEdit inDto)
{
if (await _auditDocumentRepository.AnyAsync(x => x.Name == inDto.Name && x.AuditDocumentTypeEnum == inDto.AuditDocumentTypeEnum && x.ParentId == inDto.ParentId))
{
throw new BusinessValidationFailedException(_localizer["AuditDocument_CanNotAddFolder"]);
}
List<AuditDocumentAddOrEdit> auditDocumentAddOrEdits = new List<AuditDocumentAddOrEdit>();
auditDocumentAddOrEdits.Add(inDto);
var result= await AddAuditDocument(auditDocumentAddOrEdits);
return ResponseOutput.Ok(result);
}
/// <summary>
/// 新增稽查文档
/// </summary>
/// <param name="inDto"></param>
/// <returns></returns>
[HttpPost]
public async Task<IResponseOutput> AddAuditDocument(List<AuditDocumentAddOrEdit> inDto)
{
List<Guid> resultData = new List<Guid>();
inDto.ForEach(x => x.IsUpdate = false);
await addData(inDto);
async Task addData(List<AuditDocumentAddOrEdit> data)
{
foreach(var item in data)
{
var result= await AddOrUpdateAuditDocument(item);
resultData.Add(result.Id);
item.Children.ForEach(x => {
x.ParentId = result.Id;
x.IsUpdate = false;
});
if (item.Children.Count() > 0)
{
await addData(item.Children);
}
}
}
return ResponseOutput.Ok(resultData);
}
/// <summary>
/// 通用方法
/// </summary>
/// <param name="inDto"></param>
private async Task<AuditDocument> AddOrUpdateAuditDocument(AuditDocumentAddOrEdit inDto)
{
var alikeData = await _auditDocumentRepository.Where(x =>x.Id!=inDto.Id&& x.ParentId == inDto.ParentId&&x.Name==inDto.Name&&x.AuditDocumentTypeEnum==inDto.AuditDocumentTypeEnum).FirstOrDefaultAsync();
if (alikeData != null)
{
if (inDto.AuditDocumentTypeEnum == AuditDocumentType.Folder)
{
if (inDto.IsUpdate)
{
throw new BusinessValidationFailedException(_localizer["AuditDocument_CanNotAddFolder"]);
}
else
{
return alikeData;
}
}
else
{
var entityData = await _auditDocumentRepository.InsertOrUpdateAsync(inDto, true);
var historicalVersionIds = await _auditDocumentRepository.Where(x => x.MainFileId == alikeData.Id).OrderBy(x => x.Version).Select(x => x.Id).ToListAsync();
historicalVersionIds.Add(alikeData.Id);
int num = 1;
foreach (var item in historicalVersionIds)
{
await _auditDocumentRepository.UpdatePartialFromQueryAsync(item, x => new AuditDocument()
{
MainFileId = entityData.Id,
ParentId = null,
Version = num,
AuditDocumentTypeEnum = AuditDocumentType.HistoricalVersion
},true);
num++;
}
return entityData;
}
}
var entity = await _auditDocumentRepository.InsertOrUpdateAsync(inDto, true);
return entity;
}
/// <summary>
/// 获取面包屑导航
/// </summary>
/// <param name="inDto"></param>
/// <returns></returns>
[HttpPost]
public async Task<List<AuditDocumentUpdateDto>> GetBreadcrumbData(GetBreadcrumbDataInDto inDto)
{
List<AuditDocumentUpdateDto> result=new List<AuditDocumentUpdateDto>();
await findParent(result, inDto.Id);
async Task findParent(List<AuditDocumentUpdateDto> datas, Guid id)
{
var data= await _auditDocumentRepository.Where(x => x.Id == id).ProjectTo<AuditDocumentUpdateDto>(_mapper.ConfigurationProvider).FirstNotNullAsync();
datas.Add(data);
if (data.ParentId != null)
{
await findParent(datas, data.ParentId.Value);
}
}
result.Reverse();
return result;
}
/// <summary>
/// 获取文件树形结构 (传Id 根节点就是自己)
/// </summary>
/// <param name="inDto"></param>
/// <returns></returns>
[HttpPost]
public async Task<PageOutput<AuditDocumentData>> GetAuditDocumentData(GetAuditDocumentDataInDto inDto)
{
var defalutSortArray = new string[] { nameof(AuditDocumentData.AuditDocumentTypeEnum), nameof(AuditDocumentData.Name) };
if (inDto.SortField.IsNotNullOrEmpty())
{
defalutSortArray = new string[] { nameof(AuditDocumentData.AuditDocumentTypeEnum), inDto.SortField + (inDto.Asc ? " asc" : " desc") };
inDto.SortField = string.Empty;
}
// 新取出来排序 然后再找子项
var data= (await _auditDocumentRepository
.Where(x=>x.AuditDocumentTypeEnum!=AuditDocumentType.HistoricalVersion)
.WhereIf(inDto.IsAuthorization!=null,x=>x.IsAuthorization==inDto.IsAuthorization)
.ProjectTo<AuditDocumentData>(_mapper.ConfigurationProvider).ToPagedListAsync(new PageInput() {
PageIndex=1,
PageSize=999999,
}, defalutSortArray) ).CurrentPageData.ToList();
if (inDto.Name.IsNotNullOrEmpty())
{
List<Guid> findIds = new List<Guid>();
var findData = data.Where(x => x.Name.Contains(inDto.Name)).Select(x => x.Id.Value).ToList();
GetParentId(findIds, findData, data);
findIds.AddRange(findData);
data = data.Where(x => findIds.Distinct().Contains(x.Id.Value)).ToList();
}
var query = data
.WhereIf(inDto.SelfId != null, x => inDto.SelfId == x.Id)
.WhereIf(inDto.Id != null, x => inDto.Id == x.ParentId)
.WhereIf(inDto.Id == null&& inDto.SelfId == null, x => x.ParentId == null);
PageOutput<AuditDocumentData> result = new PageOutput<AuditDocumentData>()
{
PageIndex = inDto.PageIndex,
PageSize = inDto.PageSize,
TotalCount = query.Count(),
};
var root = query
.Skip(inDto.PageSize * (inDto.PageIndex - 1)).Take(inDto.PageSize).ToList();
var historicalVersionList = await _auditDocumentRepository
.Where(x => x.AuditDocumentTypeEnum == AuditDocumentType.HistoricalVersion).ProjectTo<AuditDocumentData>(_mapper.ConfigurationProvider).ToListAsync();
foreach (var item in root)
{
GetChildren(item, data, historicalVersionList);
}
result.CurrentPageData = root;
return result;
}
private void GetParentId(List<Guid> parentIds, List<Guid> ids, List<AuditDocumentData> dataList)
{
var parentid = dataList.Where(x => ids.Contains(x.Id.Value) && x.ParentId != null).Select(x => x.ParentId.Value).ToList();
if (parentid.Count() > 0)
{
parentIds.AddRange(parentid);
GetParentId(parentIds, parentid, dataList);
}
}
private void GetChildren(AuditDocumentData item, List<AuditDocumentData> dataList, List<AuditDocumentData> historyList)
{
item.Children = dataList.Where(x => x.ParentId == item.Id).ToList();
item.HistoricalVersionsCount= historyList.Where(x => x.MainFileId == item.Id).Count();
foreach (var x in item.Children)
{
GetChildren(x, dataList, historyList);
}
}
/// <summary>
/// 删除稽查文档
/// </summary>
/// <param name="inDto"></param>
/// <returns></returns>
[HttpPost]
public async Task<IResponseOutput> DeleteAuditDocument(DeleteAuditDocumentInDto inDto)
{
var data = await _auditDocumentRepository.Select(x => new DeleteAudit (){
Id=x.Id,
ParentId= x.ParentId,
MainFileId= x.MainFileId
}).ToListAsync();
List<Guid> DeleteId= inDto.Ids;
finId(inDto.Ids, data);
void finId(List<Guid> deletids, List<DeleteAudit> deletes)
{
DeleteId.AddRange(deletids);
var temp = deletes.Where(x =>(x.ParentId!=null&& deletids.Contains(x.ParentId.Value))||(x.MainFileId!=null&& deletids.Contains(x.MainFileId.Value))).Select(x => x.Id).ToList();
if (temp.Count() > 0)
{
finId(temp, deletes);
}
}
DeleteId = DeleteId.Distinct().ToList();
var mainFileId=await _auditDocumentRepository.Where(x => DeleteId.Contains(x.Id)&&x.MainFileId!=null).Select(x => x.MainFileId).Distinct().ToListAsync();
var success = await _auditDocumentRepository.DeleteFromQueryAsync(t => DeleteId.Distinct().Contains(t.Id), true);
foreach (var item in mainFileId)
{
var historicalVersionList = await _auditDocumentRepository.Where(x => x.MainFileId == item).OrderBy(x=>x.Version).ToListAsync();
var num = 1;
foreach (var historical in historicalVersionList)
{
await _auditDocumentRepository.UpdatePartialFromQueryAsync(historical.Id, x => new AuditDocument()
{
Version = num,
}, true);
num++;
}
}
return ResponseOutput.Ok();
}
/// <summary>
/// 移动文件或者文件夹 到其他文件夹
/// </summary>
/// <returns></returns>
[HttpPost]
public async Task<IResponseOutput> MovieFileOrFolder(MovieFileOrFolderInDto inDto)
{
var data = await _auditDocumentRepository.Select(x => new DeleteAudit()
{
Id = x.Id,
ParentId = x.ParentId,
AuditDocumentTypeEnum = x.AuditDocumentTypeEnum,
MainFileId = x.MainFileId
}).ToListAsync();
foreach (var id in inDto.Ids)
{
var file = data.Where(x => x.Id == id).FirstOrDefault();
if (file.AuditDocumentTypeEnum == AuditDocumentType.Folder)
{
if (finChild(new List<Guid> { id }, inDto.ParentId, data))
{
throw new BusinessValidationFailedException(_localizer["AuditDocument_CanNotMove"]);
}
}
}
bool finChild(List<Guid> ids, Guid ChildId, List<DeleteAudit> data)
{
var child = data.Where(x => x.ParentId != null && ids.Contains(x.ParentId.Value)).ToList();
if (child.Count() == 0)
{
return false;
}
else if (child.Any(x => x.Id == ChildId))
{
return true;
}
else
{
var newids = child.Select(x => x.Id).ToList();
return finChild(newids, ChildId, data);
}
}
foreach (var id in inDto.Ids)
{
await _auditDocumentRepository.UpdatePartialFromQueryAsync(id, x => new AuditDocument()
{
ParentId = inDto.ParentId
});
await _auditDocumentRepository.SaveChangesAsync();
}
return ResponseOutput.Ok();
}
/// <summary>
/// 复制文件或者文件夹
/// </summary>
/// <param name="inDto"></param>
/// <returns></returns>
[HttpPost]
public async Task<IResponseOutput> CopyFileOrFolder(MovieFileOrFolderInDto inDto)
{
foreach (var item in inDto.Ids)
{
var data = (await GetAuditDocumentData(new GetAuditDocumentDataInDto()
{
SelfId =item,
PageIndex=1,
PageSize= 1000
})).CurrentPageData;
List<AuditDocumentAddOrEdit> auditDocumentAddOrEdits = _mapper.Map<List<AuditDocumentAddOrEdit>>(data);
auditDocumentAddOrEdits.ForEach(x => {
x.IsUpdate = false;
x.Id = null;
x.ParentId = inDto.ParentId;
});
await addData(auditDocumentAddOrEdits);
}
async Task addData(List<AuditDocumentAddOrEdit> data)
{
foreach (var item in data)
{
item.Id = null;
var result = await AddOrUpdateAuditDocument(item);
item.Children.ForEach(x => {
x.ParentId = result.Id;
x.IsUpdate = false;
x.Id = null;
});
if (item.Children.Count() > 0)
{
await addData(item.Children);
}
}
}
return ResponseOutput.Ok();
}
/// <summary>
/// 获取历史版本
/// </summary>
/// <param name="inDto"></param>
/// <returns></returns>
[HttpPost]
public async Task<List<HistoricalVersionDto>> GetHistoricalVersion(GetHistoricalVersionInDto inDto)
{
List< HistoricalVersionDto > result=new List<HistoricalVersionDto>();
result = await _auditDocumentRepository.Where(x => x.MainFileId == inDto.Id).ProjectTo<HistoricalVersionDto>(_mapper.ConfigurationProvider).OrderByDescending(x => x.Version).ToListAsync();
var currentData = await _auditDocumentRepository.Where(x => x.Id == inDto.Id).ProjectTo<HistoricalVersionDto>(_mapper.ConfigurationProvider).FirstNotNullAsync();
currentData.IsCurrentVersion = true;
result.Insert(0, currentData);
return result;
}
/// <summary>
/// 把历史版本设置为当前版本
/// </summary>
/// <returns></returns>
[HttpPost]
public async Task<IResponseOutput> SetCurrentVersion(SetCurrentVersionInDto inDto)
{
var file = await _auditDocumentRepository.Where(x => x.Id == inDto.Id).FirstNotNullAsync();
if (file.AuditDocumentTypeEnum != AuditDocumentType.HistoricalVersion)
{
throw new BusinessValidationFailedException(_localizer["AuditDocument_CanNotSetCurrentVersion"]);
}
var mainFile = await _auditDocumentRepository.Where(x => x.Id == file.MainFileId).FirstNotNullAsync();
var historicalVersionIds= await _auditDocumentRepository.Where(x => x.MainFileId == mainFile.Id&&x.Id!=inDto.Id).OrderBy(x=>x.Version).Select(x=>x.Id).ToListAsync();
historicalVersionIds.Add(mainFile.Id);
await _auditDocumentRepository.UpdatePartialFromQueryAsync(inDto.Id, x => new AuditDocument() {
MainFileId=null,
ParentId= mainFile.ParentId,
AuditDocumentTypeEnum=AuditDocumentType.File,
});
int num = 1;
foreach (var item in historicalVersionIds)
{
await _auditDocumentRepository.UpdatePartialFromQueryAsync(item, x => new AuditDocument()
{
MainFileId = inDto.Id,
ParentId = null,
Version=num,
AuditDocumentTypeEnum= AuditDocumentType.HistoricalVersion
});
num++;
}
await _auditDocumentRepository.SaveChangesAsync();
return ResponseOutput.Ok();
}
/// <summary>
/// 设置是否授权
/// </summary>
/// <param name="inDto"></param>
/// <returns></returns>
[HttpPost]
public async Task<IResponseOutput> SetIsAuthorization(SetIsAuthorizationInDto inDto)
{
var data = await _auditDocumentRepository.Select(x => new DeleteAudit()
{
Id = x.Id,
ParentId = x.ParentId,
MainFileId = x.MainFileId
}).ToListAsync();
List<Guid> allid = new List<Guid>();
findChild(allid, inDto.Ids, data);
if (inDto.IsAuthorization)
{
findParent(allid, inDto.Ids, data);
}
allid= allid.Distinct().ToList();
await _auditDocumentRepository.UpdatePartialFromQueryAsync(t => allid.Contains(t.Id), x => new AuditDocument() {
IsAuthorization = inDto.IsAuthorization
});
await _auditDocumentRepository.SaveChangesAsync();
return ResponseOutput.Ok();
void findParent(List<Guid> allId, List<Guid> current, List<DeleteAudit> data)
{
allId.AddRange(current);
var parent = data.Where(x => current.Contains(x.Id)).Select(x => x.ParentId).Where(x=>x!=null).Select(x=>(Guid)x).ToList();
if (parent.Count() > 0)
{
findParent(allId, parent, data);
}
}
void findChild(List<Guid> allId,List<Guid> current, List<DeleteAudit> data)
{
allId.AddRange(current);
var child = data.Where(x =>(x.ParentId!=null&& current.Contains(x.ParentId.Value))||(x.MainFileId!=null&&current.Contains(x.MainFileId.Value))).Select(x => x.Id).ToList();
if (child.Count() > 0)
{
findChild(allId, child, data);
}
}
}
}

View File

@ -0,0 +1,167 @@
//--------------------------------------------------------------------
// 此代码由liquid模板自动生成 byzhouhang 20240909
// 生成时间 2025-03-27 06:13:37Z
// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。
//--------------------------------------------------------------------
using System;
using IRaCIS.Core.Domain.Share;
using System.Collections.Generic;
namespace IRaCIS.Core.Application.ViewModel;
public class DeleteAudit
{
public Guid Id { get; set; }
public AuditDocumentType AuditDocumentTypeEnum { get; set; }
public Guid? ParentId { get; set; }
public Guid? MainFileId { get; set; }
}
public class DeleteAuditDocumentInDto
{
public List<Guid> Ids { get; set; }
}
public class GetAuditDocumentDataInDto:PageInput
{
public Guid? Id { get; set; }
public Guid? SelfId { get; set; }
public bool? IsAuthorization { get; set; }
public string Name { get; set; } = string.Empty;
}
public class GetAuditDocumentDataOutDto
{
public List<AuditDocumentData> Data { get; set; } = new List<AuditDocumentData> { };
}
public class AuditDocumentData : AuditDocumentUpdateDto
{
public DateTime CreateTime { get; set; }
public DateTime UpdateTime { get; set; }
public Guid? MainFileId { get; set; }
public int? Version { get; set; }
public int HistoricalVersionsCount { get; set; }
public List<AuditDocumentData> Children { get; set; }=new List<AuditDocumentData> (){ };
}
public class AuditDocumentView : AuditDocumentAddOrEdit
{
public DateTime CreateTime { get; set; }
public DateTime UpdateTime { get; set; }
}
public class SetIsAuthorizationInDto
{
public List<Guid> Ids { get; set; }
public bool IsAuthorization { get; set; }
}
public class SetCurrentVersionInDto
{
public Guid Id { get; set; }
}
public class GetHistoricalVersionInDto
{
public Guid Id { get; set; }
}
public class GetHistoricalVersionOutDto
{
public AuditDocumentData CurrentData { get; set; }
public List<AuditDocumentData> HistoricalVersionList { get; set; } = new List<AuditDocumentData> { };
}
public class MovieFileOrFolderInDto
{
public List<Guid> Ids { get; set; }
public Guid ParentId { get; set; }
}
public class HistoricalVersionDto: AuditDocumentUpdateDto
{
public bool IsCurrentVersion { get; set; } = false;
public int? Version { get; set; }
public DateTime CreateTime { get; set; }
public DateTime UpdateTime { get; set; }
}
public class AuditDocumentUpdateDto
{
public Guid? Id { get; set; }
public AuditDocumentType AuditDocumentTypeEnum { get; set; }
public string? FileFormat { get; set; }
public string? FilePath { get; set; }
public decimal? FileSize { get; set; }
public bool IsAuthorization { get; set; }
// public Guid? MainFileId { get; set; }
public string Name { get; set; }
public Guid? ParentId { get; set; }
}
public class GetBreadcrumbDataInDto
{
public Guid Id { get; set; }
}
public class AuditDocumentAddOrEdit: AuditDocumentUpdateDto
{
public List<AuditDocumentAddOrEdit> Children { get; set; }=new List<AuditDocumentAddOrEdit>() { };
public bool IsUpdate { get; set; } = true;
}
public class AuditDocumentQuery:PageInput
{
public AuditDocumentType? AuditDocumentTypeEnum { get; set; }
public string? FileFormat { get; set; }
public string? FilePath { get; set; }
public decimal? FileSize { get; set; }
public bool? IsAuthorization { get; set; }
public Guid? MainFileId { get; set; }
public string? Name { get; set; }
public Guid? ParentId { get; set; }
}

View File

@ -0,0 +1,68 @@
//--------------------------------------------------------------------
// 此代码由liquid模板自动生成 byzhouhang 20240909
// 生成时间 2025-02-21 07:47:30Z
// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。
//--------------------------------------------------------------------
using System;
using IRaCIS.Core.Domain.Share;
using System.Collections.Generic;
namespace IRaCIS.Core.Application.ViewModel;
public class SysFileTypeView : SysFileTypeAddOrEdit
{
public DateTime CreateTime { get; set; }
public DateTime UpdateTime { get; set; }
}
public class SysFileTypeAddOrEdit
{
public Guid? Id { get; set; }
public ArchiveType ArchiveTypeEnum { get; set; }
/// <summary>
/// 文档类型
/// </summary>
public SysTemplateType SysTemplateTypeEnum { get; set; }
public bool IsConfirmRecord { get; set; }
public bool IsEnable { get; set; }
public string Name { get; set; }
public string NameCN { get; set; }
/// <summary>
/// 显示顺序
/// </summary>
public int ShowOrder { get; set; }
public SubIdentification SubIdentificationEnum { get; set; }
}
public class SysFileTypeQuery : PageInput
{
public ArchiveType? ArchiveTypeEnum { get; set; }
public SysTemplateType? SysTemplateTypeEnum { get; set; }
public bool? IsConfirmRecord { get; set; }
public bool? IsEnable { get; set; }
public string? Name { get; set; }
public string? NameCN { get; set; }
public SubIdentification? SubIdentificationEnum { get; set; }
}

View File

@ -8,6 +8,51 @@ using System.ComponentModel.DataAnnotations;
namespace IRaCIS.Core.Application.Contracts
{
public class SystemDocumentAttachmentView : SystemDocumentAttachmentAddOrEdit
{
public DateTime CreateTime { get; set; }
public DateTime UpdateTime { get; set; }
}
public class SystemDocumentAttachmentAddOrEdit
{
public Guid? Id { get; set; }
public string FileFormat { get; set; }
public string Name { get; set; }
public string FileName { get; set; }
public string FilePath { get; set; }
public decimal? FileSize { get; set; }
public bool OffLine { get; set; }
public Guid SystemDocumentId { get; set; }
}
public class SystemDocumentAttachmentQuery : PageInput
{
public string? FileFormat { get; set; }
public string? FileName { get; set; }
public string? FilePath { get; set; }
public decimal? FileSize { get; set; }
public bool? OffLine { get; set; }
public Guid? SystemDocumentId { get; set; }
}
/// <summary> SystemDocumentView 列表视图模型 </summary>
public class SystemDocumentView : SystemDocumentAddOrEdit
{
@ -17,6 +62,8 @@ namespace IRaCIS.Core.Application.Contracts
public string FileType { get; set; } = string.Empty;
public int AttachmentCount { get; set; }
public List<NeedConfirmedUserTypeView> NeedConfirmedUserTypeList { get; set; } = new List<NeedConfirmedUserTypeView>();
}
@ -46,6 +93,10 @@ namespace IRaCIS.Core.Application.Contracts
public class UnionDocumentWithConfirmInfoView : UnionDocumentView
{
public bool IsNeedSendEmial { get; set; }
public DateTime UserCreateTime { get; set; }
public DateTime? ConfirmTime { get; set; }
@ -58,6 +109,28 @@ namespace IRaCIS.Core.Application.Contracts
public string RealName { get; set; } = string.Empty;
public int AttachmentCount { get; set; }
public DateTime? SuggestFinishTime
{
get
{
if (this.CreateTime > this.UserCreateTime && this.CurrentStaffTrainDays != null)
{
return DateTime.Parse(this.CreateTime.AddDays(this.CurrentStaffTrainDays.Value).ToString("yyyy-MM-dd")+" 22:00:00");
}
if (this.CreateTime < this.UserCreateTime && this.NewStaffTrainDays != null)
{
return DateTime.Parse(this.UserCreateTime.AddDays(this.NewStaffTrainDays.Value).ToString("yyyy-MM-dd") + " 22:00:00");
}
return null;
}
}
public string DocTypeCode { get; set; }
public List<string> UserConfirmedUserTypeList => (DocNeedSignUserTypeList != null && IdentityUserTypeList != null) ? IdentityUserTypeList.Intersect(DocNeedSignUserTypeList).ToList() : new List<string>();
@ -119,6 +192,15 @@ namespace IRaCIS.Core.Application.Contracts
public bool IsSigned { get; set; }
public Guid? UserTypeId { get; set; }
public DocUserSignType? DocUserSignType { get; set; }
public DateTime? BeginCreateTime { get; set; }
public DateTime? EndCreateTime { get; set; }
}
public class GetNextUnSignDocumentInDto
@ -170,6 +252,47 @@ namespace IRaCIS.Core.Application.Contracts
public string SignText { get; set; } = string.Empty;
}
public class SystemDocQuery : PageInput
{
public Guid? FileTypeId { get; set; }
public string Name { get; set; } = string.Empty;
public bool? IsSign { get; set; }
public Guid? UserTypeId { get; set; }
public Guid? UserId { get; set; }
public bool? IsConfirmed { get; set; }
/// <summary>
/// 课时状态
/// </summary>
public bool? IsDeleted { get; set; }
/// <summary>
/// 用户名
/// </summary>
public string? UserName { get; set; }
/// <summary>
/// 开始时间
/// </summary>
public DateTime? StartConfirmTime { get; set; }
/// <summary>
/// 结束时间
/// </summary>
public DateTime? EndConfirmTime { get; set; }
public DateTime? BeginCreateTime { get; set; }
public DateTime? EndCreateTime { get; set; }
}
public class DocumentTrialUnionQuery : TrialUserDocUnionQuery
{
@ -178,7 +301,29 @@ namespace IRaCIS.Core.Application.Contracts
public bool? IsConfirmed { get; set; }
/// <summary>
/// 课时状态
/// </summary>
public bool? IsDeleted { get; set; }
/// <summary>
/// 用户名
/// </summary>
public string? UserName { get; set; }
/// <summary>
/// 开始时间
/// </summary>
public DateTime? StartConfirmTime { get; set; }
/// <summary>
/// 结束时间
/// </summary>
public DateTime? EndConfirmTime { get; set; }
public DateTime? BeginCreateTime { get; set; }
public DateTime? EndCreateTime { get; set; }
}
///<summary> SystemDocumentAddOrEdit 列表查询参数模型</summary>
@ -195,6 +340,80 @@ namespace IRaCIS.Core.Application.Contracts
public DocUserSignType DocUserSignType { get; set; }
/// <summary>
/// 现有员工培训天数
/// </summary>
public int? CurrentStaffTrainDays { get; set; }
/// <summary>
/// 新员工培训天数
/// </summary>
public int? NewStaffTrainDays { get; set; }
/// <summary>
/// 是否发布
/// </summary>
public bool IsPublish { get; set; } = false;
}
public class PublishSystemDocumentInDto
{
public List<Guid> Ids { get; set; }
}
public class PublishTrialDocumentInDto
{
public List<Guid> Ids { get; set; }
}
public class TrialDocumentAttachmentView : TrialDocumentAttachmentAddOrEdit
{
public DateTime CreateTime { get; set; }
public DateTime UpdateTime { get; set; }
}
public class TrialDocumentAttachmentAddOrEdit
{
public Guid? Id { get; set; }
public string FileFormat { get; set; }
public string FileName { get; set; }
public string FilePath { get; set; }
public decimal? FileSize { get; set; }
public string Name { get; set; }
public bool OffLine { get; set; }
public Guid TrialDocumentId { get; set; }
}
public class TrialDocumentAttachmentQuery : PageInput
{
public string? FileFormat { get; set; }
public string? FileName { get; set; }
public string? FilePath { get; set; }
public decimal? FileSize { get; set; }
public string? Name { get; set; }
public bool? OffLine { get; set; }
public Guid? TrialDocumentId { get; set; }
}
public class AddOrEditSystemDocument : SystemDocumentAddOrEdit

View File

@ -13,6 +13,7 @@ namespace IRaCIS.Core.Application.Contracts
{
public string FullFilePath { get; set; } = String.Empty;
public int AttachmentCount { get; set; }
public bool IsSomeUserSigned { get; set; }
public DateTime CreateTime { get; set; }
@ -60,6 +61,24 @@ namespace IRaCIS.Core.Application.Contracts
public int SignViewMinimumMinutes { get; set; }
/// <summary>
/// 现有员工培训天数
/// </summary>
public int? CurrentStaffTrainDays { get; set; }
/// <summary>
/// 新员工培训天数
/// </summary>
public int? NewStaffTrainDays { get; set; }
/// <summary>
/// 是否发布
/// </summary>
public bool IsPublish { get; set; } = true;
}
public class AddOrEditTrialDocument : TrialDocumentAddOrEdit

View File

@ -99,7 +99,7 @@ namespace IRaCIS.Core.Application.ViewModel
public EmailBusinessScenario? BusinessScenarioEnum { get; set; }
//public CriterionType? CriterionTypeEnum { get; set; }
public CriterionType? CriterionTypeEnum { get; set; }
public Guid? TrialReadingCriterionId { get; set; }
@ -146,9 +146,6 @@ namespace IRaCIS.Core.Application.ViewModel
public Guid TrialId { get; set; }
public CriterionType? CriterionTypeEnum { get; set; }
public List<UserTypeEnum> ToUserTypeList { get; set; }
public List<UserTypeEnum> CopyUserTypeList { get; set; }
@ -197,6 +194,11 @@ namespace IRaCIS.Core.Application.ViewModel
public int? EmailDelaySeconds { get; set; }
[Comment("邮件配置的多个标准")]
public List<CriterionType>? CriterionTypeList { get; set; }
public bool IsDistinguishCriteria { get; set; }
}

View File

@ -0,0 +1,153 @@
//--------------------------------------------------------------------
// 此代码由liquid模板自动生成 byzhouhang 20240909
// 生成时间 2025-02-21 07:47:30Z
// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。
//--------------------------------------------------------------------
using System;
using IRaCIS.Core.Domain.Share;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Reflection.Metadata;
namespace IRaCIS.Core.Application.ViewModel;
public class TrialFileTypeView : TrialFileTypeAddOrEdit
{
public DateTime CreateTime { get; set; }
public DateTime UpdateTime { get; set; }
}
public class GetTrialFileTypeDataOutDto
{
public List<TrialFileTypeData> TrialFileTypeDataList { get; set; }
}
public class TrialFileTypeData
{
public ArchiveType ArchiveTypeEnum { get; set; }
public List<TrialFileTypeView> TrialFileTypeList { get; set; }
}
public class GetTrialFileTypeFileInDto
{
public Guid TrialFileTypeId { get; set; }
}
public class UploadTrialFileTypeFileInDto
{
public Guid TrialFileTypeId { get; set; }
/// <summary>
/// 文件
/// </summary>
public TrialFileDto? FileRecord { get; set; }
}
public class SetAuthorizedViewInDto
{
public Guid Id { get; set; }
public bool IsEnable { get; set; }
}
public class GetTrialFileTypeDataInDto
{
public Guid TrialId { get; set; }
/// <summary>
/// 是否启用
/// </summary>
public bool IsEnable { get; set; } = false;
}
public class CopySystemFileTypeToTrialInDto
{
public Guid TrialId { get; set; }
}
public class TrialFileTypeSelectView
{
public Guid Id { get; set; }
public string Name { get; set; }
public bool IsEnable { get; set; }
public bool IsSelfDefine { get; set; }
public ArchiveType ArchiveTypeEnum { get; set; }
}
public class TrialFileTypeAddOrEdit
{
public Guid? Id { get; set; }
public ArchiveType ArchiveTypeEnum { get; set; }
public DateOnly? FirstFinalDate { get; set; }
public bool IsConfirmRecord { get; set; }
public bool IsEnable { get; set; }
public bool IsSelfDefine { get; set; }
public string Name { get; set; }
public string NameCN { get; set; }
public SubIdentification SubIdentificationEnum { get; set; }
public Guid? SysFileTypeId { get; set; }
public Guid TrialId { get; set; }
/// <summary>
/// 显示顺序
/// </summary>
public int ShowOrder { get; set; }
}
public class TrialFileTypeQuery : PageInput
{
public ArchiveType? ArchiveTypeEnum { get; set; }
public DateOnly? FirstFinalDate { get; set; }
public bool? IsConfirmRecord { get; set; }
public bool? IsEnable { get; set; }
public bool? IsSelfDefine { get; set; }
public string? Name { get; set; }
public string? NameCN { get; set; }
public SubIdentification? SubIdentificationEnum { get; set; }
[NotDefault]
public Guid TrialId { get; set; }
}
public class TrialFileTypeSelectQuery
{
[NotDefault]
public Guid TrialId { get; set; }
public bool? IsEnable { get; set; }
public bool? IsSelfDefine { get; set; }
public ArchiveType? ArchiveTypeEnum { get; set; }
}

View File

@ -0,0 +1,117 @@
//--------------------------------------------------------------------
// 此代码由liquid模板自动生成 byzhouhang 20240909
// 生成时间 2025-02-21 07:47:30Z
// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。
//--------------------------------------------------------------------
using System;
using IRaCIS.Core.Domain.Share;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using Newtonsoft.Json;
using System.ComponentModel.DataAnnotations;
namespace IRaCIS.Core.Application.ViewModel;
public class TrialFinalRecordView : TrialFinalRecordAddOrEdit
{
public int HistoryCount { get; set; }
public DateTime CreateTime { get; set; }
public DateTime UpdateTime { get; set; }
}
public class DeleteTrialNormalRecordListInDto
{
public List<Guid> Ids { get; set; }
}
public class AuthorizedTrialFinalRecordInDto
{
public List<Guid> Ids { get; set; }
public bool IsAuthorizedView { get; set; }
}
public class TrialFinalRecordAddOrEdit
{
public Guid? Id { get; set; }
public bool IsAuthorizedView { get; set; }
public string Name { get; set; }
public int State { get; set; }
public Guid TrialFileTypeId { get; set; }
public Guid TrialId { get; set; }
public string Version { get; set; }
/// <summary>
/// 定稿PDF
/// </summary>
public TrialFileDto? PDFFileRecord { get; set; }
/// <summary>
/// 定稿Word
/// </summary>
public TrialFileDto? WordFileRecord { get; set; }
/// <summary>
/// 签名页
/// </summary>
public TrialFileDto? SignFileRecord { get; set; }
/// <summary>
/// 历史记录
/// </summary>
public TrialFileDto? HistoryFileRecord { get; set; }
}
public class TrialFileDto
{
public Guid TrialFileTypeId { get; set; }
public string FileName { get; set; }
public string FilePath { get; set; }
public string FileSize { get; set; }
public string FileFormat { get; set; }
}
public class TrialFinalRecordQuery : PageInput
{
public Guid? HistoryFileRecordId { get; set; }
public bool? IsAuthorizedView { get; set; }
public string? Name { get; set; }
[NotDefault]
public Guid TrialFileTypeId { get; set; }
public Guid? TrialId { get; set; }
public string? Version { get; set; }
public Guid? WordFileRecordId { get; set; }
}

View File

@ -0,0 +1,56 @@
//--------------------------------------------------------------------
// 此代码由liquid模板自动生成 byzhouhang 20240909
// 生成时间 2025-06-09 02:09:12Z
// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。
//--------------------------------------------------------------------
using System;
using IRaCIS.Core.Domain.Share;
using System.Collections.Generic;
namespace IRaCIS.Core.Application.Service.DTO;
public class TrialHistoryRecordFileView : TrialHistoryRecordFileAddOrEdit
{
public DateTime CreateTime { get; set; }
public DateTime UpdateTime { get; set; }
}
public class TrialHistoryRecordFileAddOrEdit
{
public Guid? Id { get; set; }
public string FileFormat { get; set; }
public string FileName { get; set; }
public string FilePath { get; set; }
public string FileSize { get; set; }
public Guid TrialFileTypeId { get; set; }
public Guid TrialRecordId { get; set; }
}
public class TrialHistoryRecordFileQuery:PageInput
{
public string? FileFormat { get; set; }
public string? FileName { get; set; }
public string? FilePath { get; set; }
public string? FileSize { get; set; }
public Guid? TrialFileTypeId { get; set; }
public Guid? TrialRecordId { get; set; }
}

View File

@ -0,0 +1,60 @@
//--------------------------------------------------------------------
// 此代码由liquid模板自动生成 byzhouhang 20240909
// 生成时间 2025-02-21 07:47:30Z
// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。
//--------------------------------------------------------------------
using System;
using IRaCIS.Core.Domain.Share;
using System.Collections.Generic;
namespace IRaCIS.Core.Application.ViewModel;
public class TrialNormalRecordView : TrialNormalRecordAddOrEdit
{
public int HistoryCount { get; set; }
public DateTime CreateTime { get; set; }
public DateTime UpdateTime { get; set; }
public string FileName { get; set; }
}
public class BatchAddTrialNormalRecordInDto
{
public List<TrialNormalRecordAddOrEdit> TrialNormalRecordList { get; set; }
}
public class TrialNormalRecordAddOrEdit
{
public Guid? Id { get; set; }
public bool IsAuthorizedView { get; set; }
public int State { get; set; }
public TrialFileDto? TrialFileRecord { get; set; }
public TrialFileDto? HistoryFileRecord { get; set; }
public Guid TrialFileTypeId { get; set; }
public Guid TrialId { get; set; }
}
public class TrialNormalRecordQuery : PageInput
{
public bool? IsAuthorizedView { get; set; }
public int? State { get; set; }
public string? FileName { get; set; }
public Guid? TrialFileTypeId { get; set; }
public Guid? TrialId { get; set; }
}

View File

@ -0,0 +1,77 @@
//--------------------------------------------------------------------
// 此代码由liquid模板自动生成 byzhouhang 20240909
// 生成时间 2025-02-21 07:47:30Z
// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。
//--------------------------------------------------------------------
using System;
using IRaCIS.Core.Domain.Share;
using System.Collections.Generic;
namespace IRaCIS.Core.Application.ViewModel;
public class TrialTrianingRecordView : TrialTrianingRecordAddOrEdit
{
public int HistoryCount { get; set; }
public DateTime CreateTime { get; set; }
public DateTime UpdateTime { get; set; }
public string FileName { get; set; }
}
public class BatchAddTTrianingRecordInDto
{
public List<TrialTrianingRecordAddOrEdit> TrianingRecordList { get; set; }
}
public class TrialTrianingRecordAddOrEdit
{
public Guid? Id { get; set; }
public bool IsAuthorizedView { get; set; }
public string Note { get; set; }=string.Empty;
public int? State { get; set; }
public TrialFileDto? TrialFileRecord { get; set; }
public TrialFileDto? HistoryFileRecord { get; set; }
public Guid TrialFileTypeId { get; set; }
public Guid TrialId { get; set; }
public int? TrianingCount { get; set; }
public DateOnly? TrianingDate { get; set; }
public string? TrianingState { get; set; }
}
public class TrialTrianingRecordQuery : PageInput
{
public bool? IsAuthorizedView { get; set; }
public string? FileName { get; set; }
public Guid? TrialFileTypeId { get; set; }
public Guid? TrialId { get; set; }
public int? TrianingCount { get; set; }
public DateOnly? TrianingDateStartTime { get; set; }
public DateOnly? TrianingDateEndTime { get; set; }
public DateTime? UpdateStartTime { get; set; }
public DateTime? UpdateEndTime { get; set; }
public string? TrianingState { get; set; }
}

View File

@ -11,7 +11,7 @@ namespace IRaCIS.Core.Application.Service
{
Task SendEnrollOrPdEmail(Guid visitTaskId, bool? isEnrollment, bool? isPDConfirm);
Task<(TrialEmailNoticeConfig?, SMTPEmailConfig?)> BuildEmailConfig(Guid trialId, EmailBusinessScenario businessScenario, Func<TrialEmailNoticeConfig, (string topicStr, string htmlBodyStr, bool isEn_us, Guid? onlyToUserId)> topicAndHtmlFunc, Guid? siteId = null, Guid? trialReadingCriterionId = null);
Task<(TrialEmailNoticeConfig?, SMTPEmailConfig?)> BuildEmailConfig(Guid trialId, EmailBusinessScenario businessScenario, Func<TrialEmailNoticeConfig, (string topicStr, string htmlBodyStr, bool isEn_us, Guid? onlyToUserId)> topicAndHtmlFunc, Guid? siteId = null);
}
public class EmailSendService(IRepository<TrialEmailNoticeConfig> _trialEmailNoticeConfigRepository,
@ -102,9 +102,9 @@ namespace IRaCIS.Core.Application.Service
public async Task SendTrialEmailAsync(Guid trialId, EmailBusinessScenario businessScenario, Func<TrialEmailNoticeConfig, (string topicStr, string htmlBodyStr, bool isEn_us, Guid? onlyToUserId)> topicAndHtmlFunc, Guid? trialSiteId = null, Guid? trialReadingCriterionId = null, Func<TrialEmailNoticeConfig, SMTPEmailConfig, SMTPEmailConfig> emailAttachFunc = null)
public async Task SendTrialEmailAsync(Guid trialId, EmailBusinessScenario businessScenario, Func<TrialEmailNoticeConfig, (string topicStr, string htmlBodyStr, bool isEn_us, Guid? onlyToUserId)> topicAndHtmlFunc, Guid? trialSiteId = null, Func<TrialEmailNoticeConfig, SMTPEmailConfig, SMTPEmailConfig> emailAttachFunc = null)
{
var (trialEmailConfig, sendEmailConfig) = await BuildEmailConfig(trialId, businessScenario, topicAndHtmlFunc, trialSiteId, trialReadingCriterionId);
var (trialEmailConfig, sendEmailConfig) = await BuildEmailConfig(trialId, businessScenario, topicAndHtmlFunc, trialSiteId);
if (sendEmailConfig != null)
{
@ -114,10 +114,10 @@ namespace IRaCIS.Core.Application.Service
}
public async Task<(TrialEmailNoticeConfig?, SMTPEmailConfig?)> BuildEmailConfig(Guid trialId, EmailBusinessScenario businessScenario, Func<TrialEmailNoticeConfig, (string topicStr, string htmlBodyStr, bool isEn_us, Guid? onlyToUserId)> topicAndHtmlFunc, Guid? trialSiteId = null, Guid? trialReadingCriterionId = null)
public async Task<(TrialEmailNoticeConfig?, SMTPEmailConfig?)> BuildEmailConfig(Guid trialId, EmailBusinessScenario businessScenario, Func<TrialEmailNoticeConfig, (string topicStr, string htmlBodyStr, bool isEn_us, Guid? onlyToUserId)> topicAndHtmlFunc, Guid? trialSiteId = null)
{
//找到配置
var trialEmailConfig = await _trialEmailNoticeConfigRepository.Where(t => t.TrialId == trialId && t.TrialReadingCriterionId == trialReadingCriterionId && t.BusinessScenarioEnum == businessScenario, ignoreQueryFilters: true)
var trialEmailConfig = await _trialEmailNoticeConfigRepository.Where(t => t.TrialId == trialId && t.BusinessScenarioEnum == businessScenario, ignoreQueryFilters: true)
.Include(t => t.TrialEmailNoticeUserList).Include(t => t.TrialEmailBlackUserList).FirstOrDefaultAsync();

Some files were not shown because too many files have changed in this diff Show More