Compare commits

..

219 Commits

Author SHA1 Message Date
hang 8fe84cd2ad uat-ivus-oct-再次修改8
continuous-integration/drone/push Build is passing
2025-12-16 11:20:30 +08:00
hang dd1da21738 uat-ivus-oct-再次修改7
continuous-integration/drone/push Build is passing
2025-12-16 10:16:33 +08:00
hang 7185be2ff2 合并冲突
continuous-integration/drone/push Build is passing
2025-12-16 09:54:11 +08:00
hang c1d0bf8914 uat-ivus-oct-再次修改6 2025-12-16 09:52:43 +08:00
hang 64fec97864 uat-ivus-oct-再次修改5
continuous-integration/drone/push Build is passing
2025-12-16 09:30:41 +08:00
hang edd562183d uat-ivus-oct-再次修改4
continuous-integration/drone/push Build is passing
2025-12-15 16:32:16 +08:00
hang 84bf6d9145 uat-ivus-oct-再次修改3
continuous-integration/drone/push Build is passing
2025-12-15 16:22:51 +08:00
hang a065f4533c uat-ivus-oct-再次修改2
continuous-integration/drone/push Build is passing
2025-12-15 16:00:46 +08:00
hang 52d64682cb 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
2025-12-15 15:35:26 +08:00
hang 5e0513ebcb uat-ivus-oct-再次修改1 2025-12-15 15:35:24 +08:00
he f7525942e7 添加报表图表
continuous-integration/drone/push Build is passing
2025-12-15 14:06:24 +08:00
he 3971e65482 修改邮件
continuous-integration/drone/push Build is passing
2025-12-15 13:38:28 +08:00
he 4fde957519 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
2025-12-10 13:58:56 +08:00
he 8faa078afc 稽查翻译修改 OCT06 2025-12-10 13:58:54 +08:00
hang 7f55877688 ivus-oct-导表修改-uat-4
continuous-integration/drone/push Build is passing
2025-12-09 17:48:14 +08:00
hang 84922152bd ivus-oct-导表修改-uat-3
continuous-integration/drone/push Build is passing
2025-12-09 17:41:46 +08:00
hang a267c52109 ivus-oct-导表修改-uat-2
continuous-integration/drone/push Build is passing
2025-12-09 14:37:42 +08:00
hang 1cad62fe1b ivus-oct-导表修改-uat-1
continuous-integration/drone/push Build is passing
2025-12-09 13:28:18 +08:00
hang ac284241b7 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
2025-12-09 11:39:51 +08:00
hang efbcdce768 修改多选导表 2025-12-09 11:39:49 +08:00
he 00d8be6ff5 OCT05
continuous-integration/drone/push Build is passing
2025-12-09 10:05:48 +08:00
he 152ee46fd6 稽查修改 OCT4
continuous-integration/drone/push Build is passing
2025-12-08 17:48:54 +08:00
he 0208ffa8f4 稽查修改 OCT4
continuous-integration/drone/push Build is passing
2025-12-08 17:18:16 +08:00
he 71b765a28b OCT修改3
continuous-integration/drone/push Build is passing
2025-12-08 16:56:02 +08:00
he e72791b0f8 修改
continuous-integration/drone/push Build is passing
2025-12-08 16:34:17 +08:00
he 77f9275d93 OCT 修改2
continuous-integration/drone/push Build is failing
2025-12-08 16:15:44 +08:00
he 3ba953ac7e OCT 添加1
continuous-integration/drone/push Build is failing
2025-12-08 15:46:21 +08:00
he a627be0234 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 running
2025-12-08 15:46:14 +08:00
he 5eb49ca378 添加字段 2025-12-08 15:46:06 +08:00
hang 04954a4b25 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
2025-12-05 09:45:03 +08:00
hang 7d08a3d920 uat-项目影像下载-dir 2025-12-05 09:45:00 +08:00
hang 4328862c92 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
2025-12-04 15:21:32 +08:00
hang 58e705716e uat-发布导表-1 2025-12-04 15:21:31 +08:00
he d786b3bb61 接受合并
continuous-integration/drone/push Build is passing
2025-12-04 15:07:16 +08:00
he 905f11625a OCT导入修改1 2025-12-04 15:06:40 +08:00
hang 24c5b3c91f uat-ivus-增加标识-31
continuous-integration/drone/push Build is passing
2025-12-01 17:56:24 +08:00
hang ad6f593743 修改导表错误-uat
continuous-integration/drone/push Build is passing
2025-12-01 13:23:19 +08:00
hang f0369e15c7 uat-标注-30
continuous-integration/drone/push Build is passing
2025-11-28 16:45:47 +08:00
hang 4b7778ee7f uat-标注-29
continuous-integration/drone/push Build is passing
2025-11-28 15:50:35 +08:00
hang 7f970edd52 uat-标注-28
continuous-integration/drone/push Build is passing
2025-11-28 14:09:56 +08:00
hang d37ab8ee65 uat-标注-27
continuous-integration/drone/push Build is passing
2025-11-28 13:43:19 +08:00
hang 1570731516 uat-标注-26
continuous-integration/drone/push Build is passing
2025-11-28 11:34:07 +08:00
hang ed004fe54e uat-标注-25
continuous-integration/drone/push Build is passing
2025-11-28 11:18:58 +08:00
hang cbca0ac08f uat-标注-24
continuous-integration/drone/push Build is passing
2025-11-28 11:14:03 +08:00
hang a5b5dfb214 uat-标注-23
continuous-integration/drone/push Build is passing
2025-11-28 10:53:14 +08:00
hang 19cd09439d uat-标注-22
continuous-integration/drone/push Build is passing
2025-11-28 10:08:26 +08:00
hang 18f844601f uat-标注-21
continuous-integration/drone/push Build is passing
2025-11-28 10:03:14 +08:00
hang ea79bb71fc ivus-导表-uat-手动修改
continuous-integration/drone/push Build is passing
2025-11-27 17:48:32 +08:00
hang 01783f9a7f 调整ivus导表位置 2025-11-27 17:48:01 +08:00
hang c9573fc610 ivus-导表修改 2025-11-27 17:33:20 +08:00
hang 270c4abde4 uat-标注-20
continuous-integration/drone/push Build is passing
2025-11-27 17:07:27 +08:00
hang 07270ee83d uat-标注-19
continuous-integration/drone/push Build is passing
2025-11-27 16:52:00 +08:00
hang 64060c125d Revert "ivus-导表修改"
This reverts commit 9bf639dac0.
2025-11-27 16:38:47 +08:00
hang c9b998213b 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 failing
2025-11-27 16:28:34 +08:00
hang fe79c77285 uat-标注-18
continuous-integration/drone/push Build is passing
2025-11-27 14:23:19 +08:00
hang 72591ee203 uat-标注-17
continuous-integration/drone/push Build is passing
2025-11-27 14:04:23 +08:00
hang 1d180fa83f uat-标注-16
continuous-integration/drone/push Build is passing
2025-11-27 13:39:33 +08:00
hang 9bf639dac0 ivus-导表修改 2025-11-27 11:22:23 +08:00
hang 11450a973a 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
2025-11-27 10:56:35 +08:00
hang bbb361d0cd uat-标注-15
continuous-integration/drone/push Build is passing
2025-11-27 10:38:48 +08:00
hang 7c9959b9ee uat-标注-14
continuous-integration/drone/push Build is passing
2025-11-26 17:44:52 +08:00
hang d1fb6314db uat-标注-13
continuous-integration/drone/push Build is passing
2025-11-26 16:45:08 +08:00
hang 4047220d53 uat-标注-12
continuous-integration/drone/push Build is passing
2025-11-26 15:49:09 +08:00
hang d1b45fa56d uat-标注-11
continuous-integration/drone/push Build is passing
2025-11-26 13:45:47 +08:00
hang 4c2c72fe12 uat-标注-10
continuous-integration/drone/push Build is passing
2025-11-26 10:47:44 +08:00
hang a0d1a39125 非dicom检查预览
continuous-integration/drone/push Build is failing
2025-11-26 10:39:25 +08:00
hang 33618e8dfb uat-标注-8
continuous-integration/drone/push Build is failing
2025-11-26 10:10:33 +08:00
hang 1edc52bd76 uat-标注-7
continuous-integration/drone/push Build is passing
2025-11-26 09:05:34 +08:00
hang 2fa0e9e88e 非dicom bodyPart 修改
continuous-integration/drone/push Build is passing
2025-11-25 18:00:39 +08:00
hang 7f5e9f1bfc uat-标注增加访视Id-6
continuous-integration/drone/push Build is passing
2025-11-25 17:56:17 +08:00
hang 9910fb3c8e uat-标注增加访视Id-5
continuous-integration/drone/push Build is passing
2025-11-25 16:19:45 +08:00
hang aaf8fd62e0 uat-标注增加访视Id-4
continuous-integration/drone/push Build is passing
2025-11-25 15:59:20 +08:00
hang 5df54fd75d uat-标注增加访视Id-3
continuous-integration/drone/push Build is passing
2025-11-25 13:38:38 +08:00
hang ad00478511 uat-标注增加访视Id-2
continuous-integration/drone/push Build is passing
2025-11-25 11:02:24 +08:00
hang cd3508da21 uat-标注增加访视Id-1
continuous-integration/drone/push Build is passing
2025-11-25 10:47:14 +08:00
hang e7a116f228 非dicom bodyPart 修改 2025-11-24 15:39:42 +08:00
hang 82c0d2bcb4 修改检查部位bug
continuous-integration/drone/push Build is passing
2025-11-24 14:28:55 +08:00
hang afba3b073c ivus-oct阅片列表过滤
continuous-integration/drone/push Build is passing
2025-11-19 15:13:56 +08:00
hang 4e37c2d53e subjectcode 查询掉了
continuous-integration/drone/push Build is passing
2025-11-19 14:23:57 +08:00
hang c03c739ba0 分页处理
continuous-integration/drone/push Build is passing
2025-11-19 14:04:47 +08:00
hang 65137e585c 返回subject 标注的非dicom检查数量
continuous-integration/drone/push Build is passing
2025-11-19 13:53:10 +08:00
hang e0111a2419 oct-ivus-修改1
continuous-integration/drone/push Build is passing
2025-11-19 10:36:31 +08:00
hang 73b7aae6c0 提交后允许删除检查特殊逻辑修改
continuous-integration/drone/push Build is passing
2025-11-13 16:25:54 +08:00
he 14e212c4a8 覆盖TrialImageDownloadService
continuous-integration/drone/push Build is passing
2025-10-16 13:33:42 +08:00
he 344146a46e 验证修改
continuous-integration/drone/push Build is failing
2025-10-16 13:24:25 +08:00
he cd5a1fcbb7 修改 2025-10-16 13:24:03 +08:00
he 424448db2f 添加表格预设 2025-10-16 13:23:46 +08:00
he 629116cb6a 合并
continuous-integration/drone/push Build is failing
2025-10-16 13:20:01 +08:00
hang 64c3d96807 修改维护统计数字问题
continuous-integration/drone/push Build is passing
2025-09-23 09:37:26 +08:00
hang d607018832 数据库存在脏数据,导致归档失败
continuous-integration/drone/push Build is passing
2025-09-22 16:04:07 +08:00
hang 0d65625caa 修改维护数据代码-3测试
continuous-integration/drone/push Build is passing
2025-09-22 15:41:34 +08:00
hang 2b82044213 修改维护数据代码-2
continuous-integration/drone/push Build is passing
2025-09-22 14:49:51 +08:00
hang 2769cf7efd 修改请求方式
continuous-integration/drone/push Build is passing
2025-09-18 17:24:25 +08:00
hang 1c84a374a1 参数必填限制
continuous-integration/drone/push Build is passing
2025-09-18 17:22:09 +08:00
hang 2a59cfe3b5 修改实际恢复情况
continuous-integration/drone/push Build is passing
2025-09-18 13:44:44 +08:00
hang 0dff3d6e4b 记录重复修改
continuous-integration/drone/push Build is passing
2025-09-18 13:17:18 +08:00
hang 1d6731e3a4 设置文件大小
continuous-integration/drone/push Build is passing
2025-09-18 08:58:14 +08:00
hang 224318e0c4 配置文件修改
continuous-integration/drone/push Build is passing
2025-09-17 17:08:30 +08:00
hang 7096062d76 增加日志测试
continuous-integration/drone/push Build is passing
2025-09-17 17:05:40 +08:00
hang a0b06fbc28 修改维护数据
continuous-integration/drone/push Build is passing
2025-09-17 16:44:00 +08:00
hang 4a76705ec7 Uat 分支维护数据提交
continuous-integration/drone/push Build is passing
2025-09-17 16:02:29 +08:00
hang 7676e3cc4d 维护数据第二次提交
continuous-integration/drone/push Build is passing
2025-09-16 10:18:16 +08:00
hang 234a5706a8 下载删除标记方法提交
continuous-integration/drone/push Build is passing
2025-09-15 17:49:12 +08:00
he 956e214632 Merge branch 'Test_IRC_Net8' into Uat_IRC_Net8
continuous-integration/drone/push Build is passing
2025-08-18 11:44:27 +08:00
he 974adb4367 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
2025-08-15 13:18:39 +08:00
he 00189b992f 修改稽查 2025-08-15 13:18:37 +08:00
hang f192219621 合并问题
continuous-integration/drone/push Build is passing
2025-08-07 11:37:24 +08:00
hang 4b0d6fe8b9 Test 合并到Uat 冲突解决
continuous-integration/drone/push Build is failing
2025-08-07 11:30:37 +08:00
hang bb6dc19924 更新里面有添加bug
continuous-integration/drone/push Build is passing
2025-07-29 15:21:18 +08:00
hang ec9af97742 初审通过bug 修改
continuous-integration/drone/push Build is passing
2025-07-29 09:07:55 +08:00
hang a5472e1509 取tag 做判断
continuous-integration/drone/push Build is passing
2025-07-25 10:30:31 +08:00
hang a1bb730cfe 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
2025-07-23 15:00:27 +08:00
hang 1aa411c0f8 下一个任务不会跳复核任务 2025-07-23 15:00:25 +08:00
he 10de82132d 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
2025-07-23 13:34:56 +08:00
he db11656862 邮件修改 区分CRC与CRA 2025-07-23 13:34:54 +08:00
hang a47ac76729 复核指派bug修改
continuous-integration/drone/push Build is passing
2025-07-22 15:29:09 +08:00
hang f86a88332c 复核稽查修改016
continuous-integration/drone/push Build is passing
2025-07-18 13:15:23 +08:00
hang f6be5e4355 复核稽查修改015
continuous-integration/drone/push Build is failing
2025-07-18 13:04:58 +08:00
hang 8deeb0290a 复核稽查修改014
continuous-integration/drone/push Build is failing
2025-07-18 12:17:55 +08:00
hang fb6231373b 复核稽查修改013
continuous-integration/drone/push Build is passing
2025-07-18 11:34:13 +08:00
hang a407313a95 复核稽查修改012
continuous-integration/drone/push Build is passing
2025-07-18 09:25:56 +08:00
hang b1806d2eff 复核稽查修改011
continuous-integration/drone/push Build is passing
2025-07-17 17:25:45 +08:00
hang 19351f34e1 复核稽查修改010
continuous-integration/drone/push Build is passing
2025-07-17 17:10:06 +08:00
hang e0f708082d 复核稽查修改009
continuous-integration/drone/push Build is passing
2025-07-17 16:49:20 +08:00
hang 44464a809d 复核稽查修改008
continuous-integration/drone/push Build is passing
2025-07-17 15:26:28 +08:00
hang c7c01b7ba3 复核稽查修改007
continuous-integration/drone/push Build is passing
2025-07-17 15:08:11 +08:00
hang 38b71d5267 复核稽查修改006
continuous-integration/drone/push Build is passing
2025-07-17 14:00:03 +08:00
hang 3950a3453a 复核稽查修改005
continuous-integration/drone/push Build is passing
2025-07-17 13:26:44 +08:00
hang 7aa59da586 复核稽查修改004
continuous-integration/drone/push Build is passing
2025-07-17 11:51:41 +08:00
hang 9b2fbdb13f 复核稽查修改003
continuous-integration/drone/push Build is passing
2025-07-17 11:28:47 +08:00
hang c98b9e1bd4 复核稽查修改002
continuous-integration/drone/push Build is passing
2025-07-17 11:08:02 +08:00
hang 9522461508 复核稽查修改
continuous-integration/drone/push Build is passing
2025-07-17 10:34:15 +08:00
hang 5bb99180b6 修改复核-退回-003
continuous-integration/drone/push Build is passing
2025-07-16 17:59:42 +08:00
hang 62ec1e4955 修改复核查询接口-002
continuous-integration/drone/push Build is passing
2025-07-16 17:34:09 +08:00
hang 58e1af24c9 修改复核查询接口-001
continuous-integration/drone/push Build is passing
2025-07-16 16:30:03 +08:00
hang a7394e7119 指派其他质控-uat-13
continuous-integration/drone/push Build is passing
2025-07-16 10:30:55 +08:00
hang a93d81de0d 指派其他质控-uat-12 2025-07-16 10:30:51 +08:00
hang 6aafe02c6c 指派其他质控-uat-11 2025-07-16 10:30:48 +08:00
hang 1511ef1608 修改QC复核 -二次提交-uat-10 2025-07-16 10:30:43 +08:00
hang 1141439c8e 修改QC复核 -二次提交-uat-9 2025-07-16 10:30:32 +08:00
hang 559663082b 修改QC复核 -二次提交-uat-8 2025-07-16 10:26:30 +08:00
hang 3a298518f9 修改QC复核 -二次提交-uat-7 2025-07-16 10:26:25 +08:00
hang 7f20232401 修改QC复核 -二次提交-uat-6 2025-07-16 10:26:21 +08:00
hang 20b0849b40 修改QC复核 -二次提交-uat-5 2025-07-16 10:26:16 +08:00
hang db951e9195 修改QC复核 -二次提交-uat-4 2025-07-16 10:26:13 +08:00
hang d5d78a99d2 QC复核 -二次提交-uat-3 2025-07-16 10:26:08 +08:00
hang 1609a7a88a QC复核 -二次提交-uat-3 2025-07-16 10:26:03 +08:00
hang 289831fce8 QC复核 -二次提交-uat-2 2025-07-16 10:25:59 +08:00
hang dde058be18 QC 审核提交--迁移uat 2025-07-16 10:25:26 +08:00
hang be8b730ee4 返回文件大小
continuous-integration/drone/push Build is passing
2025-07-14 15:00:36 +08:00
hang 28360861aa 修改subject 随机阅片下载影像重阅bug
continuous-integration/drone/push Build is passing
2025-07-14 13:36:27 +08:00
hang 3e097fa42b uat 序列获取Instance 增加文件大小 2025-07-14 13:32:21 +08:00
hang b8a7cd6ebc 修改非dicom 设置删除,检查上的数量不变更 2025-07-14 13:32:16 +08:00
hang a9aefaf240 修改预览看到阅片影像 修改后受试者随机bug修改 4 2025-07-14 13:32:10 +08:00
hang 266a0c5dd5 修改后受试者随机bug修改 3 2025-07-14 13:32:05 +08:00
hang 54822ae038 修改后受试者随机bug修改 2 2025-07-14 13:31:07 +08:00
hang 05203d6716 受试者随机bug修改 2025-07-14 13:31:00 +08:00
hang 2ce8edde80 uat升级excel 组件测试
continuous-integration/drone/push Build is passing
2025-07-05 13:34:09 +08:00
hang 3c30f5dc14 完全随机跟下载关联
continuous-integration/drone/push Build is passing
2025-07-03 14:54:23 +08:00
hang e4ad1c0653 增加失访可读配置-uat
continuous-integration/drone/push Build is passing
2025-06-25 15:31:59 +08:00
he 84df49ca07 单位修改2
continuous-integration/drone/push Build is passing
2025-06-24 14:58:39 +08:00
he 400edd0b70 修改单位1 2025-06-24 14:58:37 +08:00
he c4add1ecc8 修改单位 2025-06-24 14:58:35 +08:00
he d5879e28d7 修改OCT计算触发逻辑
continuous-integration/drone/push Build is passing
2025-06-24 14:07:31 +08:00
he 37e083face 阅片标准默认值修改 2025-06-24 14:07:28 +08:00
he 290506a060 保存测量值
continuous-integration/drone/push Build is passing
2025-06-24 09:28:27 +08:00
he 1b4bc3690a 稽查保存Ivus和Oct 2025-06-24 09:28:25 +08:00
he 6374781d26 修改阅片期计划的排序
continuous-integration/drone/push Build is failing
2025-06-20 17:33:23 +08:00
he b28013a02f 阅片期计划可以选择基线 2025-06-20 17:32:07 +08:00
he fe429d2bdc 修改受试者内随机
continuous-integration/drone/push Build is passing
2025-06-20 09:37:48 +08:00
he d63af2ed20 修改稽查的单位翻译
continuous-integration/drone/push Build is passing
2025-06-19 17:16:42 +08:00
hang cebf9875b1 禁用人员系统查看文档
continuous-integration/drone/push Build is passing
2025-06-19 10:31:24 +08:00
hang 131681a0b3 完全随机触发系统盲态名称 2025-06-19 10:22:06 +08:00
hang f833adc710 修改有序阅片生成任务bug 2025-06-19 10:21:59 +08:00
hang df80d1e551 修改影像下载bug
continuous-integration/drone/push Build is passing
2025-06-18 10:36:26 +08:00
he 82dc092247 修改阅片期计划生成裁判的逻辑
continuous-integration/drone/push Build is passing
2025-06-17 16:42:47 +08:00
he 44894fcaa9 生成阅片期计划的时候 产生裁判
continuous-integration/drone/push Build is passing
2025-06-17 16:24:00 +08:00
hang 083bebdc03 真实随机阅片导表-1
continuous-integration/drone/push Build is passing
2025-06-17 10:06:41 +08:00
hang e808764dd6 随机阅片排序-5 2025-06-17 10:06:33 +08:00
hang 77d198cb09 随机阅片-NextTask-5 2025-06-17 10:05:55 +08:00
hang 180b02b28c 随机阅片NextTask-4 2025-06-17 10:04:26 +08:00
hang c49a74d742 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
2025-06-17 10:02:34 +08:00
he c76fc8f052 修改阅片期选择访视下拉框的数据
continuous-integration/drone/push Build is running
2025-06-17 10:02:04 +08:00
he 14875dfacc 生成了裁判任务 阅片期不让删除和修改 2025-06-17 10:02:02 +08:00
hang 0a68788c0f 随机阅片序号3 2025-06-17 10:00:39 +08:00
hang b17e5d9ff3 随机阅片序号2 2025-06-17 10:00:34 +08:00
hang 05ce4b7155 随机阅片随机序号 2025-06-17 10:00:28 +08:00
he c8bbb3da48 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
2025-06-16 15:16:20 +08:00
he 65de108ecd 添加阅片期 产生裁判 2025-06-16 15:16:19 +08:00
hang d20c306219 末次访视的展示+ 展示基线选择阅片期
continuous-integration/drone/push Build is passing
2025-06-16 14:50:23 +08:00
hang e69991ae37 修改重置阅片稽查3
continuous-integration/drone/push Build is passing
2025-06-13 09:57:33 +08:00
hang e1e2544ee3 修改阅片重置稽查 2025-06-13 09:57:30 +08:00
hang 318333bae4 修改重置阅片稽查 2025-06-13 09:57:26 +08:00
hang d569de4dfe 确认浏览临床数据
continuous-integration/drone/push Build is passing
2025-06-12 10:52:46 +08:00
hang 05627ff126 肿瘤学稽查修改
continuous-integration/drone/push Build is passing
2025-06-11 10:03:01 +08:00
hang 627618c854 增加进入阅片中稽查 2025-06-11 10:02:57 +08:00
hang 2711c5eb04 稽查 申请PM 重阅,有SPM 判断 2025-06-11 10:00:57 +08:00
hang a6982f79c3 稽查修改,区分标识 2025-06-11 10:00:53 +08:00
hang 2a542949cd 增加稽查标识 2025-06-11 10:00:49 +08:00
he a8ccbce76f 稽查添加
continuous-integration/drone/push Build is passing
2025-06-05 16:46:06 +08:00
he df7cf30aed 测量值可以为空
continuous-integration/drone/push Build is passing
2025-06-04 14:25:57 +08:00
he 0664fbdfb7 验证修改
continuous-integration/drone/push Build is passing
2025-06-04 09:57:08 +08:00
he 0a6ee05417 Merge branch 'Test_IRC_Net8' into Uat_IRC_Net8
continuous-integration/drone/push Build is passing
2025-05-22 18:01:11 +08:00
hang f9bd8c3f5c Merge branch 'Test_IRC_Net8' into Uat_IRC_Net8
continuous-integration/drone/push Build is passing
2025-05-21 15:16:26 +08:00
he cb83c2a737 Merge branch 'Test_IRC_Net8' into Uat_IRC_Net8
continuous-integration/drone/push Build is passing
2025-05-21 14:05:30 +08:00
he fdd9afd4f0 Merge branch 'Test_IRC_Net8' into Uat_IRC_Net8
continuous-integration/drone/push Build is passing
2025-05-20 15:46:24 +08:00
he 53d26c0445 Merge branch 'Test_IRC_Net8' into Uat_IRC_Net8
continuous-integration/drone/push Build is passing
2025-05-20 10:55:36 +08:00
he 40d035e7a7 解决合并的冲突
continuous-integration/drone/push Build is passing
2025-05-15 09:33:17 +08:00
he a13807ae5f 合并冲突修改
continuous-integration/drone/push Build is failing
2025-05-15 09:29:41 +08:00
he 9d5aaf1e26 Merge branch 'Test_IRC_Net8' into Uat_IRC_Net8
continuous-integration/drone/push Build is failing
2025-05-15 09:20:22 +08:00
he c347d08e2b 合并
continuous-integration/drone/push Build is failing
2025-05-15 09:14:10 +08:00
hang 87f2d0e429 非dicom 删除上传
continuous-integration/drone/push Build is passing
2025-04-15 13:27:33 +08:00
he 89d009fcbf Revert "修改"
continuous-integration/drone/push Build is passing
This reverts commit 7d65cf5051.
2025-04-09 11:00:45 +08:00
he 7d65cf5051 修改
continuous-integration/drone/push Build is passing
2025-04-09 09:45:49 +08:00
he e74427c45c 解决冲突
continuous-integration/drone/push Build is passing
2025-04-08 11:05:31 +08:00
he e8825c7efa 修改查询 2025-04-08 11:04:52 +08:00
hang 628e0ad034 修改异地登录bug 设置最后一次登录的iP
continuous-integration/drone/push Build is passing
2025-04-03 13:55:04 +08:00
hang 2f3f639918 修改异地登录ip逻辑 2025-04-03 13:55:00 +08:00
hang 87caf24dd1 验证环境生成缩略图测试ok提交
continuous-integration/drone/push Build is passing
2025-04-03 13:49:34 +08:00
454 changed files with 7341 additions and 1507362 deletions
+2 -3
View File
@@ -12,9 +12,8 @@
<PackageReference Include="AlibabaCloud.SDK.Sts20150401" Version="1.1.5" /> <PackageReference Include="AlibabaCloud.SDK.Sts20150401" Version="1.1.5" />
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="10.0.0" /> <PackageReference Include="Autofac.Extensions.DependencyInjection" Version="10.0.0" />
<PackageReference Include="Aliyun.OSS.SDK.NetCore" Version="2.14.1" /> <PackageReference Include="Aliyun.OSS.SDK.NetCore" Version="2.14.1" />
<PackageReference Include="AWSSDK.S3" Version="4.0.21" /> <PackageReference Include="AWSSDK.S3" Version="3.7.416.8" />
<PackageReference Include="AWSSDK.SecurityToken" Version="4.0.5.19" /> <PackageReference Include="AWSSDK.SecurityToken" Version="3.7.401.81" />
<PackageReference Include="DistributedLock.Core" Version="1.0.8" /> <PackageReference Include="DistributedLock.Core" Version="1.0.8" />
<PackageReference Include="DistributedLock.SqlServer" Version="1.0.6" /> <PackageReference Include="DistributedLock.SqlServer" Version="1.0.6" />
<PackageReference Include="fo-dicom" Version="5.2.1" /> <PackageReference Include="fo-dicom" Version="5.2.1" />
+11 -11
View File
@@ -118,7 +118,7 @@ public class AWSTempToken
public string SecretAccessKey { get; set; } public string SecretAccessKey { get; set; }
public string BucketName { get; set; } public string BucketName { get; set; }
public string ViewEndpoint { get; set; } public string ViewEndpoint { get; set; }
public DateTime? Expiration { get; set; } public DateTime Expiration { get; set; }
} }
public enum ObjectStoreUse public enum ObjectStoreUse
@@ -231,8 +231,8 @@ public class OSSService : IOSSService
//提供awsEndPoint(域名)进行访问配置 //提供awsEndPoint(域名)进行访问配置
var clientConfig = new AmazonS3Config var clientConfig = new AmazonS3Config
{ {
RegionEndpoint = RegionEndpoint.GetBySystemName(awsConfig.Region) RegionEndpoint = RegionEndpoint.USEast1,
//,UseHttp = true, UseHttp = true,
}; };
var amazonS3Client = new AmazonS3Client(credentials, clientConfig); var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
@@ -320,8 +320,8 @@ public class OSSService : IOSSService
//提供awsEndPoint(域名)进行访问配置 //提供awsEndPoint(域名)进行访问配置
var clientConfig = new AmazonS3Config var clientConfig = new AmazonS3Config
{ {
RegionEndpoint = RegionEndpoint.GetBySystemName(awsConfig.Region) RegionEndpoint = RegionEndpoint.USEast1,
//,UseHttp = true, UseHttp = true,
}; };
var amazonS3Client = new AmazonS3Client(credentials, clientConfig); var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
@@ -397,8 +397,8 @@ public class OSSService : IOSSService
//提供awsEndPoint(域名)进行访问配置 //提供awsEndPoint(域名)进行访问配置
var clientConfig = new AmazonS3Config var clientConfig = new AmazonS3Config
{ {
RegionEndpoint = RegionEndpoint.GetBySystemName(awsConfig.Region) RegionEndpoint = RegionEndpoint.USEast1,
//,UseHttp = true, UseHttp = true,
}; };
var amazonS3Client = new AmazonS3Client(credentials, clientConfig); var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
@@ -493,8 +493,8 @@ public class OSSService : IOSSService
//提供awsEndPoint(域名)进行访问配置 //提供awsEndPoint(域名)进行访问配置
var clientConfig = new AmazonS3Config var clientConfig = new AmazonS3Config
{ {
RegionEndpoint = RegionEndpoint.GetBySystemName(awsConfig.Region) RegionEndpoint = RegionEndpoint.USEast1,
//,UseHttp = true, UseHttp = true,
}; };
var amazonS3Client = new AmazonS3Client(credentials, clientConfig); var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
@@ -625,8 +625,8 @@ public class OSSService : IOSSService
//提供awsEndPoint(域名)进行访问配置 //提供awsEndPoint(域名)进行访问配置
var clientConfig = new AmazonS3Config var clientConfig = new AmazonS3Config
{ {
RegionEndpoint = RegionEndpoint.GetBySystemName(awsConfig.Region) RegionEndpoint = RegionEndpoint.USEast1,
//,UseHttp = true, UseHttp = true,
}; };
var amazonS3Client = new AmazonS3Client(credentials, clientConfig); var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
@@ -1,175 +0,0 @@
using IRaCIS.Core.Application.Helper;
using IRaCIS.Core.Application.Service;
using IRaCIS.Core.Domain.Models;
using IRaCIS.Core.Infra.EFCore;
using IRaCIS.Core.SCP.Service;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
namespace IRaCIS.Core.SCP;
public class SyncFileRecoveryService(IServiceScopeFactory _scopeFactory, FileSyncQueue _fileSyncQueue) : BackgroundService
{
private readonly int _pageSize = 500;
/// <summary>
/// 多个程序,如果恢复同一份数据,造成重复同步,SCP服务不恢复任务
/// </summary>
/// <param name="stoppingToken"></param>
/// <returns></returns>
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
//在本地调试的时候,不干涉部署同步任务
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
return;
}
await Task.CompletedTask;
//using var scope = _scopeFactory.CreateScope();
//var fileUploadRecordRepository = scope.ServiceProvider.GetRequiredService<IRepository<FileUploadRecord>>();
//// 延迟启动,保证主机快速启动
//await Task.Delay(5000, stoppingToken);
//int page = 0;
//while (!stoppingToken.IsCancellationRequested)
//{
// // 分页获取未入队任务
// var pending = await fileUploadRecordRepository
// .Where(x => x.IsNeedSync == true && (x.IsSync == false || x.IsSync == null))
// .OrderByDescending(x => x.Priority)
// .Select(t => new { t.Id, t.Priority })
// .Skip(page * _pageSize)
// .Take(_pageSize)
// .ToListAsync(stoppingToken);
// if (!pending.Any())
// break; // 扫描完毕,退出循环
// foreach (var file in pending)
// {
// //file.IsQueued = true; // 避免重复入队
// _fileSyncQueue.Enqueue(file.Id, file.Priority ?? 0); // 放入队列
// }
// page++; // 下一页
// await Task.Delay(200, stoppingToken); // 缓解数据库压力
//}
}
}
public class FileSyncWorker(IServiceScopeFactory _scopeFactory, ILogger<FileSyncWorker> _logger, FileSyncQueue _fileSyncQueue) : BackgroundService
{
// ⭐ 自动根据服务器CPU
private readonly int _workerCount = Math.Max(1, Environment.ProcessorCount - 1);
protected override Task ExecuteAsync(CancellationToken stoppingToken)
{
for (int i = 0; i < _workerCount; i++)
Task.Run(() => WorkerLoop(stoppingToken));
return Task.CompletedTask;
}
private async Task WorkerLoop(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
var id = await _fileSyncQueue.DequeueAsync(stoppingToken);
try
{
using var scope = _scopeFactory.CreateScope();
var _fileUploadRecordRepository = scope.ServiceProvider.GetRequiredService<IRepository<FileUploadRecord>>();
var _uploadFileSyncRecordRepository = scope.ServiceProvider.GetRequiredService<IRepository<UploadFileSyncRecord>>();
var syncConfig = (scope.ServiceProvider.GetRequiredService<IOptionsMonitor<ObjectStoreServiceOptions>>()).CurrentValue;
var oss = scope.ServiceProvider.GetRequiredService<IOSSService>();
var file = await _fileUploadRecordRepository.FirstOrDefaultAsync(t => t.Id == id);
// ✅ 不要 return
if (file == null || file.IsNeedSync != true || syncConfig.IsOpenStoreSync == false)
{
continue;
}
if (syncConfig.SyncConfigList.Any(t => t.UploadRegion == file.UploadRegion && t.IsOpenSync == false))
{
continue;
}
//如果发现系统配置某一边同步进行了关闭,那么就直接返回,不执行任务
if (syncConfig.SyncConfigList.Any(t => t.UploadRegion == file.UploadRegion && t.IsOpenSync == false))
{
return;
}
var log = new UploadFileSyncRecord
{
FileUploadRecordId = id,
StartTime = DateTime.Now,
JobState = jobState.RUNNING
};
await _uploadFileSyncRecordRepository.AddAsync(log);
await _uploadFileSyncRecordRepository.SaveChangesAsync(stoppingToken);
try
{
await oss.SyncFileAsync(file.Path.TrimStart('/'), file.UploadRegion == "CN" ? ObjectStoreUse.AliyunOSS : ObjectStoreUse.AWS, file.UploadRegion == "CN" ? ObjectStoreUse.AWS : ObjectStoreUse.AliyunOSS);
file.IsSync = true;
file.SyncFinishedTime = DateTime.Now;
log.JobState = jobState.SUCCESS;
}
catch (Exception ex)
{
log.JobState = jobState.FAILED;
log.Msg = ex.Message.Length > 300 ? ex.Message.Substring(0, 300) : ex.Message;
}
log.EndTime = DateTime.Now;
await _uploadFileSyncRecordRepository.SaveChangesAsync(stoppingToken);
}
catch (Exception ex)
{
_logger.LogError(ex, "Sync failed {Id}", id);
}
finally
{
// ⭐⭐⭐ 永远执行
_fileSyncQueue.Complete(id);
}
}
}
}
+16 -16
View File
@@ -7,28 +7,28 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="AlibabaCloud.SDK.Sts20150401" Version="1.2.0" /> <PackageReference Include="AlibabaCloud.SDK.Sts20150401" Version="1.1.5" />
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="11.0.0" /> <PackageReference Include="Autofac.Extensions.DependencyInjection" Version="10.0.0" />
<PackageReference Include="Aliyun.OSS.SDK.NetCore" Version="2.14.1" /> <PackageReference Include="Aliyun.OSS.SDK.NetCore" Version="2.14.1" />
<PackageReference Include="AWSSDK.S3" Version="4.0.21" /> <PackageReference Include="AWSSDK.S3" Version="3.7.416.8" />
<PackageReference Include="AWSSDK.SecurityToken" Version="4.0.5.19" /> <PackageReference Include="AWSSDK.SecurityToken" Version="3.7.401.81" />
<PackageReference Include="DistributedLock.Core" Version="1.0.9" /> <PackageReference Include="DistributedLock.Core" Version="1.0.8" />
<PackageReference Include="DistributedLock.SqlServer" Version="1.0.7" /> <PackageReference Include="DistributedLock.SqlServer" Version="1.0.6" />
<PackageReference Include="fo-dicom" Version="5.2.6" /> <PackageReference Include="fo-dicom" Version="5.2.1" />
<PackageReference Include="fo-dicom.Codecs" Version="5.16.7" /> <PackageReference Include="fo-dicom.Codecs" Version="5.16.1" />
<PackageReference Include="fo-dicom.Imaging.ImageSharp" Version="5.2.6" /> <PackageReference Include="fo-dicom.Imaging.ImageSharp" Version="5.2.1" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.10" /> <PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.10" />
<PackageReference Include="AutoMapper" Version="16.1.1" /> <PackageReference Include="AutoMapper" Version="13.0.1" />
<PackageReference Include="Minio" Version="7.0.0" /> <PackageReference Include="Minio" Version="6.0.3" />
<PackageReference Include="My.Extensions.Localization.Json" Version="3.3.0"> <PackageReference Include="My.Extensions.Localization.Json" Version="3.3.0">
<TreatAsUsed>true</TreatAsUsed> <TreatAsUsed>true</TreatAsUsed>
</PackageReference> </PackageReference>
<PackageReference Include="Panda.DynamicWebApi" Version="1.2.2" /> <PackageReference Include="Panda.DynamicWebApi" Version="1.2.2" />
<PackageReference Include="Serilog.Enrichers.ClientInfo" Version="2.9.0" /> <PackageReference Include="Serilog.Enrichers.ClientInfo" Version="2.1.2" />
<PackageReference Include="Serilog.Extensions.Hosting" Version="10.0.0" /> <PackageReference Include="Serilog.Extensions.Hosting" Version="9.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="6.1.1" /> <PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
<PackageReference Include="Serilog.Sinks.File" Version="7.0.0" /> <PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="10.1.7" /> <PackageReference Include="Swashbuckle.AspNetCore" Version="8.1.1" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
-8
View File
@@ -63,10 +63,6 @@ builder.Host
#region #region
var _configuration = builder.Configuration; var _configuration = builder.Configuration;
builder.Services.AddSingleton<FileSyncQueue>();
builder.Services.AddHostedService<SyncFileRecoveryService>();
builder.Services.AddHostedService<FileSyncWorker>();
//健康检查 //健康检查
builder.Services.AddHealthChecks(); builder.Services.AddHealthChecks();
@@ -117,9 +113,6 @@ builder.Services.AddAutoMapper(automapper =>
//EF ORM QueryWithNoLock //EF ORM QueryWithNoLock
builder.Services.AddEFSetup(_configuration); builder.Services.AddEFSetup(_configuration);
// FusionCache
builder.Services.AddFusionCache();
builder.Services.AddMediator(cfg => builder.Services.AddMediator(cfg =>
{ {
@@ -198,7 +191,6 @@ app.MapControllers();
Log.Logger = new LoggerConfiguration() Log.Logger = new LoggerConfiguration()
//.MinimumLevel.Information() //.MinimumLevel.Information()
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning) .MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
.MinimumLevel.Override("ZiggyCreatures.Caching.Fusion", LogEventLevel.Warning)
.WriteTo.Console() .WriteTo.Console()
.WriteTo.File($"{AppContext.BaseDirectory}Serilogs/.log", rollingInterval: RollingInterval.Day) .WriteTo.File($"{AppContext.BaseDirectory}Serilogs/.log", rollingInterval: RollingInterval.Day)
.CreateLogger(); .CreateLogger();
+65 -318
View File
@@ -1,30 +1,28 @@
using FellowOakDicom; using FellowOakDicom.Network;
using FellowOakDicom.Imaging; using FellowOakDicom;
using FellowOakDicom.IO.Buffer;
using FellowOakDicom.Network;
using IRaCIS.Core.Domain.Models;
using IRaCIS.Core.Domain.Share;
using IRaCIS.Core.Infra.EFCore;
using IRaCIS.Core.Infrastructure;
using IRaCIS.Core.Infrastructure.Extention;
using IRaCIS.Core.SCP.Service;
using MassTransit;
using Medallion.Threading;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion.Internal;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Serilog;
using SharpCompress.Common;
using SixLabors.ImageSharp.Formats.Jpeg;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Data;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using ZiggyCreatures.Caching.Fusion; 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;
using IRaCIS.Core.Infrastructure.Extention;
using FellowOakDicom.IO.Buffer;
namespace IRaCIS.Core.SCP.Service namespace IRaCIS.Core.SCP.Service
{ {
@@ -53,8 +51,6 @@ namespace IRaCIS.Core.SCP.Service
private Guid _trialSiteId { get; set; } private Guid _trialSiteId { get; set; }
private bool _releasedNormally = false;
private static readonly DicomTransferSyntax[] _acceptedTransferSyntaxes = new DicomTransferSyntax[] private static readonly DicomTransferSyntax[] _acceptedTransferSyntaxes = new DicomTransferSyntax[]
@@ -96,7 +92,7 @@ namespace IRaCIS.Core.SCP.Service
public Task OnReceiveAssociationRequestAsync(DicomAssociation association) public Task OnReceiveAssociationRequestAsync(DicomAssociation association)
{ {
_upload = new SCPImageUpload() { Id = NewId.NextSequentialGuid(), StartTime = DateTime.Now, CallingAE = association.CallingAE, CalledAE = association.CalledAE, CallingAEIP = association.RemoteHost }; _upload = new SCPImageUpload() { StartTime = DateTime.Now, CallingAE = association.CallingAE, CalledAE = association.CalledAE, CallingAEIP = association.RemoteHost };
Log.Logger.Warning($"接收到来自{association.CallingAE}的连接"); Log.Logger.Warning($"接收到来自{association.CallingAE}的连接");
@@ -184,15 +180,12 @@ namespace IRaCIS.Core.SCP.Service
await AddUploadLogAsync(); await AddUploadLogAsync();
_releasedNormally = true;
Log.Logger.Information($"进入释放连接请求 {_releasedNormally}"); var _studyRepository = _serviceProvider.GetService<IRepository<SCPStudy>>();
//将检查设置为传输结束
await _studyRepository.BatchUpdateNoTrackingAsync(t => _SCPStudyIdList.Contains(t.Id), u => new SCPStudy() { IsUploadFinished = true });
//var _studyRepository = _serviceProvider.GetService<IRepository<SCPStudy>>(); await _studyRepository.SaveChangesAndClearAllTrackingAsync();
////将检查设置为传输结束
//await _studyRepository.BatchUpdateNoTrackingAsync(t => _SCPStudyIdList.Contains(t.Id), u => new SCPStudy() { IsUploadFinished = true });
//await _studyRepository.SaveChangesAndClearAllTrackingAsync();
} }
@@ -206,9 +199,6 @@ namespace IRaCIS.Core.SCP.Service
var _SCPImageUploadRepository = _serviceProvider.GetService<IRepository<SCPImageUpload>>(); var _SCPImageUploadRepository = _serviceProvider.GetService<IRepository<SCPImageUpload>>();
//有时候没有建立连接会走关闭通过日志发现
if (_upload != null)
{
_upload.EndTime = DateTime.Now; _upload.EndTime = DateTime.Now;
_upload.StudyCount = _ImageUploadList.Count; _upload.StudyCount = _ImageUploadList.Count;
_upload.TrialId = _trialId; _upload.TrialId = _trialId;
@@ -217,14 +207,7 @@ namespace IRaCIS.Core.SCP.Service
_upload.UploadJsonStr = (new SCPImageLog() { UploadList = _ImageUploadList }).ToJsonStr(); _upload.UploadJsonStr = (new SCPImageLog() { UploadList = _ImageUploadList }).ToJsonStr();
//可能是测试echo 导致记录了 //可能是测试echo 导致记录了
if (_upload.FileCount > 0) await _SCPImageUploadRepository.AddAsync(_upload, _upload.FileCount > 0 ? true : false);
{
//可能是测试echo 导致记录了
await _SCPImageUploadRepository.AddAsync(_upload, true);
}
}
} }
@@ -279,26 +262,23 @@ namespace IRaCIS.Core.SCP.Service
public async void OnConnectionClosed(Exception exception) public async void OnConnectionClosed(Exception exception)
{ {
var _studyRepository = _serviceProvider.GetService<IRepository<SCPStudy>>(); /* nothing to do here */
//奇怪的bug 上传的时候,用王捷修改的影像,会关闭,重新连接,导致检查id 丢失,然后状态不一致
if (exception != null || _releasedNormally == false) if (exception == null)
{ {
//客户端断网,恢复后,也是没有异常的,估计是超时走了关闭 //var _studyRepository = _serviceProvider.GetService<IRepository<SCPStudy>>();
////将检查设置为传输结束
//await _studyRepository.BatchUpdateNoTrackingAsync(t => _SCPStudyIdList.Contains(t.Id), u => new SCPStudy() { IsUploadFinished = true });
await _studyRepository.BatchUpdateNoTrackingAsync(t => _SCPStudyIdList.Contains(t.Id), u => new SCPStudy() { IsUploadFinished = true, IsUploadFaild = true }); //await _studyRepository.SaveChangesAndClearAllTrackingAsync();
//记录日志
await AddUploadLogAsync();
} }
else else
{ {
//将检查设置为传输结束 //记录日志
await _studyRepository.BatchUpdateNoTrackingAsync(t => _SCPStudyIdList.Contains(t.Id), u => new SCPStudy() { IsUploadFinished = true, IsUploadFaild = false }); await AddUploadLogAsync();
} }
await _studyRepository.SaveChangesAndClearAllTrackingAsync();
Log.Logger.Warning($"连接关闭 {exception?.Message} {exception?.InnerException?.Message}"); Log.Logger.Warning($"连接关闭 {exception?.Message} {exception?.InnerException?.Message}");
} }
@@ -336,11 +316,6 @@ namespace IRaCIS.Core.SCP.Service
var _seriesRepository = _serviceProvider.GetService<IRepository<SCPSeries>>(); var _seriesRepository = _serviceProvider.GetService<IRepository<SCPSeries>>();
var _distributedLockProvider = _serviceProvider.GetService<IDistributedLockProvider>(); var _distributedLockProvider = _serviceProvider.GetService<IDistributedLockProvider>();
var _fusionCache = _serviceProvider.GetService<IFusionCache>();
var _trialSiteRepository = _serviceProvider.GetService<IRepository<TrialSite>>();
var _systemAnonymizationRepository = _serviceProvider.GetService<IRepository<SystemAnonymization>>();
var storeRelativePath = string.Empty; var storeRelativePath = string.Empty;
var ossFolderPath = $"{_trialId}/Image/PACS/{_trialSiteId}/{studyInstanceUid}"; var ossFolderPath = $"{_trialId}/Image/PACS/{_trialSiteId}/{studyInstanceUid}";
@@ -349,285 +324,58 @@ namespace IRaCIS.Core.SCP.Service
long fileSize = 0; long fileSize = 0;
try try
{ {
// 直接拿 Dataset(已经完整) using (MemoryStream ms = new MemoryStream())
var dataset = request.Dataset;
#region
var anonymizeList = await _fusionCache.GetOrSetAsync(CacheKeys.SystemAnonymization, _ => CacheHelper.GetSystemAnonymizationListAsync(_systemAnonymizationRepository), TimeSpan.FromDays(7));
var trialSiteInfo = await _fusionCache.GetOrSetAsync(CacheKeys.TrialSiteInfo(_trialSiteId), _ => CacheHelper.GetTrialSiteInfo(_trialSiteId, _trialSiteRepository), TimeSpan.FromMinutes(2));
var fixedFiledList = anonymizeList.Where(t => t.IsFixed).ToList();
var ircFiledList = anonymizeList.Where(t => t.IsFixed == false).ToList();
foreach (var item in fixedFiledList)
{ {
await request.File.SaveAsync(ms);
var dicomTag = new DicomTag(Convert.ToUInt16(item.Group, 16), Convert.ToUInt16(item.Element, 16));
dataset.AddOrUpdate(dicomTag, item.ReplaceValue);
}
foreach (var item in ircFiledList)
{
var dicomTag = new DicomTag(Convert.ToUInt16(item.Group, 16), Convert.ToUInt16(item.Element, 16));
if (dicomTag == DicomTag.ClinicalTrialProtocolID)
{
dataset.AddOrUpdate(DicomTag.ClinicalTrialProtocolID, trialSiteInfo.TrialCode);
}
if (dicomTag == DicomTag.ClinicalTrialSiteID)
{
dataset.AddOrUpdate(DicomTag.ClinicalTrialSiteID, trialSiteInfo.TrialSiteCode);
}
if (dicomTag == DicomTag.ClinicalTrialSubjectID)
{
dataset.AddOrUpdate(DicomTag.ClinicalTrialSubjectID, "");
}
if (dicomTag == DicomTag.ClinicalTrialTimePointID)
{
dataset.AddOrUpdate(DicomTag.ClinicalTrialTimePointID, "");
}
if (dicomTag == DicomTag.PatientID)
{
var pid = dataset.GetSingleValueOrDefault(DicomTag.PatientID, string.Empty);
dataset.AddOrUpdate(DicomTag.PatientID, trialSiteInfo.TrialCode + "-" + pid);
}
}
#endregion
// 构造 DicomFile(不用 Open
var dicomFile = new DicomFile(dataset);
#region 1便 #region 1便
// 回到开头,读取 dicom
ms.Position = 0;
var dicomFile = DicomFile.Open(ms);
var numberOfFrames = dicomFile.Dataset.GetSingleValueOrDefault(DicomTag.NumberOfFrames, 1);
//多帧处理逻辑
if (numberOfFrames > 1)
{
//一定要有像素数据才处理
var pixelData = DicomPixelData.Create(dicomFile.Dataset); var pixelData = DicomPixelData.Create(dicomFile.Dataset);
if (pixelData != null)
{
try
{
Log.Logger.Warning($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE} 开始处理多帧instanceId:{instanceId}");
var syntax = pixelData.Syntax; var syntax = pixelData.Syntax;
// 每个 fragment 固定大小 (64KB 示例,可以自己调整) // 每个 fragment 固定大小 (64KB 示例,可以自己调整)
int fragmentSize = 20 * 1024; int fragmentSize = 20 * 1024;
if (syntax.IsEncapsulated)
var frag = dicomFile.Dataset.GetDicomItem<DicomOtherByteFragment>(DicomTag.PixelData);
int fragmentCount = frag?.Fragments?.Count() ?? 0;
var originOffsetTable = frag?.OffsetTable; //有可能没有表,需要自己重建
var bot = new List<uint>();
uint botOffset = 0;
//需要拆成固定片段的
if (syntax.IsEncapsulated && fragmentCount == pixelData.NumberOfFrames && numberOfFrames > 1)
{ {
var newFragments = new DicomOtherByteFragment(DicomTag.PixelData); var newFragments = new DicomOtherByteFragment(DicomTag.PixelData);
#region test
//var newDicomFile = dicomFile.Clone();
//var newDataset = newDicomFile.Dataset;
//var dstPd = DicomPixelData.Create(newDataset, true);
//for (int i = 0; i < pixelData.NumberOfFrames; i++)
//{
// var frame = pixelData.GetFrame(i);
// dstPd.AddFrame(frame);
// var data = frame.Data;
// int offset = 0;
// while (offset < data.Length)
// {
// int size = Math.Min(fragmentSize, data.Length - offset);
// var buffer = new byte[size];
// Buffer.BlockCopy(data, offset, buffer, 0, size);
// newFragments.Fragments.Add(new MemoryByteBuffer(buffer));
// offset += size;
// }
//}
//var newOffsetTable = newDataset.GetDicomItem<DicomOtherByteFragment>(DicomTag.PixelData).OffsetTable;
//newFragments.OffsetTable.AddRange(newOffsetTable.ToArray());
#endregion
#region test fo-dicom auto bot
//var newDicomFile = dicomFile.Clone();
//var newDataset = newDicomFile.Dataset;
//var dstPd = DicomPixelData.Create(newDataset, true);
//for (int i = 0; i < pixelData.NumberOfFrames; i++)
//{
// var frame = pixelData.GetFrame(i);
// dstPd.AddFrame(frame);
//}
//var newOffsetTable = newDataset.GetDicomItem<DicomOtherByteFragment>(DicomTag.PixelData).OffsetTable;
//Console.WriteLine(newOffsetTable.ToJsonStr());
#endregion
#region 使
for (int n = 0; n < pixelData.NumberOfFrames; n++) for (int n = 0; n < pixelData.NumberOfFrames; n++)
{ {
var frameData = pixelData.GetFrame(n); // 获取完整一帧 var frameData = pixelData.GetFrame(n); // 获取完整一帧
var data = frameData.Data; var data = frameData.Data;
int offset = 0; int offset = 0;
bot.Add(botOffset);
botOffset += (uint)data.Length;
while (offset < data.Length) while (offset < data.Length)
{ {
botOffset += 8;
int size = Math.Min(fragmentSize, data.Length - offset); int size = Math.Min(fragmentSize, data.Length - offset);
var buffer = new byte[size]; var buffer = new byte[size];
Buffer.BlockCopy(data, offset, buffer, 0, size); Buffer.BlockCopy(data, offset, buffer, 0, size);
newFragments.Fragments.Add(new MemoryByteBuffer(buffer)); newFragments.Fragments.Add(new MemoryByteBuffer(buffer));
offset += size; offset += size;
} }
} }
//保留原始偏移表 // 替换原 PixelData
//if (originOffsetTable.Count == pixelData.NumberOfFrames)
//{
// newFragments.OffsetTable.AddRange(originOffsetTable.ToArray());
//}
//else
{
newFragments.OffsetTable.AddRange(bot.ToArray());
//Console.WriteLine(bot.ToJsonStr());
}
#endregion
dicomFile.Dataset.AddOrUpdate(newFragments); dicomFile.Dataset.AddOrUpdate(newFragments);
} // 重新保存 dicom 到流
//传递过来的就是拆分的,但是是没有偏移表的,我需要自己创建偏移表,不然生成缩略图失败 ms.SetLength(0);
else if (syntax.IsEncapsulated && fragmentCount > pixelData.NumberOfFrames && originOffsetTable.Count == 0) dicomFile.Save(ms);
{
uint offset = 0;
bot.Add(offset);
var fistSize = frag.Fragments.FirstOrDefault()?.Size ?? 0;
//和上一个大小不一样
var isDiffrentBefore = false;
uint count = 0;
// 假设你知道每帧对应的 fragment 数量
foreach (var frameFragments in frag.Fragments)
{
count++;
if (frameFragments.Size == fistSize)
{
isDiffrentBefore = false;
// 累加这一帧所有 fragment 的大小
offset += (uint)frameFragments.Size;
continue;
}
else
{
offset += (uint)frameFragments.Size;
isDiffrentBefore = true;
}
if (isDiffrentBefore)
{
//每个Fragment 也占用字节
offset += 8 * count;
bot.Add(offset);
count = 0;
}
}
bot.RemoveAt(bot.Count - 1);
// 设置到新的 PixelData
frag.OffsetTable.AddRange(bot.ToArray());
}
}
catch (Exception mutiEx)
{
Log.Logger.Warning($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE} 处理多帧失败,上传原始文件:{mutiEx.ToString()}");
}
} }
} ms.Position = 0;
#endregion #endregion
#region #region
//// --- 保存到本地文件测试 --- //// --- 保存到本地文件测试 ---
//var localPath = @"D:\TestDicom.dcm"; //var localPath = @"D:\TestDicom.dcm";
@@ -639,15 +387,21 @@ namespace IRaCIS.Core.SCP.Service
#endregion #endregion
// 直接写入内存
await using var ms = new MemoryStream();
await dicomFile.SaveAsync(ms);
ms.Position = 0;
//irc 从路径最后一截取Guid //irc 从路径最后一截取Guid
storeRelativePath = await ossService.UploadToOSSAsync(ms, ossFolderPath, instanceId.ToString(), false, uploadInfo: new FileUploadRecordAddOrEdit() { TrialId = _trialId, BatchDataType = BatchDataType.PACSReceive, UploadBatchId = _upload.Id.ToString() }); storeRelativePath = await ossService.UploadToOSSAsync(ms, ossFolderPath, instanceId.ToString(), false);
fileSize = ms.Length; 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}"); var @lock = _distributedLockProvider.CreateLock($"{studyInstanceUid}");
@@ -656,7 +410,7 @@ namespace IRaCIS.Core.SCP.Service
{ {
try try
{ {
var scpStudyId = await dicomArchiveService.ArchiveDicomFileAsync(dicomFile, _trialId, _trialSiteId, storeRelativePath, Association.CallingAE, Association.CalledAE, fileSize); var scpStudyId = await dicomArchiveService.ArchiveDicomFileAsync(request.File, _trialId, _trialSiteId, storeRelativePath, Association.CallingAE, Association.CalledAE,fileSize);
var series = await _seriesRepository.FirstOrDefaultAsync(t => t.Id == seriesId); var series = await _seriesRepository.FirstOrDefaultAsync(t => t.Id == seriesId);
@@ -668,15 +422,16 @@ namespace IRaCIS.Core.SCP.Service
// 生成缩略图 // 生成缩略图
using (var memoryStream = new MemoryStream()) using (var memoryStream = new MemoryStream())
{ {
DicomImage image = new DicomImage(dicomFile.Dataset); DicomImage image = new DicomImage(request.Dataset);
var sharpimage = image.RenderImage().AsSharpImage(); var sharpimage = image.RenderImage().AsSharpImage();
sharpimage.Save(memoryStream, new JpegEncoder()); sharpimage.Save(memoryStream, new JpegEncoder());
// 上传缩略图到 OSS // 上传缩略图到 OSS
var seriesPath = await ossService.UploadToOSSAsync(memoryStream, ossFolderPath, $"{seriesId.ToString()}_{instanceId.ToString()}.preview.jpg", false, uploadInfo: new FileUploadRecordAddOrEdit() { TrialId = _trialId, BatchDataType = BatchDataType.PACSReceive }); var seriesPath = await ossService.UploadToOSSAsync(memoryStream, ossFolderPath, seriesId.ToString() + ".preview.jpg", false);
Console.WriteLine(seriesPath + " Id: " + seriesId);
series.ImageResizePath = seriesPath; series.ImageResizePath = seriesPath;
@@ -705,11 +460,6 @@ namespace IRaCIS.Core.SCP.Service
} }
} }
//监控信息设置
_upload.FileCount++;
_upload.FileSize = _upload.FileSize + fileSize;
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -728,14 +478,11 @@ namespace IRaCIS.Core.SCP.Service
} }
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}");
}
//监控信息设置
_upload.FileCount++;
_upload.FileSize = _upload.FileSize + fileSize;
return new DicomCStoreResponse(request, DicomStatus.Success); return new DicomCStoreResponse(request, DicomStatus.Success);
} }
-103
View File
@@ -1,103 +0,0 @@
using IRaCIS.Core.Domain.Models;
using IRaCIS.Core.Infra.EFCore;
using Microsoft.EntityFrameworkCore;
namespace IRaCIS.Core.SCP.Service;
public static class CacheKeys
{
//项目缓存
public static string Trial(string trialIdStr) => $"TrialId:{trialIdStr}";
//检查编号递增锁
public static string TrialStudyMaxCode(Guid trialId) => $"TrialStudyMaxCode:{trialId}";
public static string TrialStudyUidUploading(Guid trialId, string studyUid) => $"TrialStudyUid:{trialId}_{studyUid}";
//CRC上传影像提交锁key
public static string TrialStudyUidDBLock(Guid trialId, string studyUid) => $"TrialStudyUidDBLock:{trialId}_{studyUid}";
public static string TrialTaskStudyUidUploading(Guid trialId, Guid visiTaskId, string studyUid) => $"TrialStudyUid:{trialId}_{visiTaskId}_{studyUid}";
//影像后处理上传提交锁key
public static string TrialTaskStudyUidDBLock(Guid trialId, Guid visiTaskId, string studyUid) => $"TrialTaskStudyUidDBLock:{trialId}_{visiTaskId}_{studyUid}";
//系统匿名化
public static string SystemAnonymization => $"SystemAnonymization";
//前端国际化
public static string FrontInternational => $"FrontInternationalList";
//登录挤账号
public static string UserToken(Guid userId) => $"UserToken:{userId}";
//超时没请求接口自动退出
public static string UserAutoLoginOut(Guid userId) => $"UserAutoLoginOut:{userId}";
public static string UserDisable(Guid userId) => $"UserDisable:{userId}";
public static string UserRoleDisable(Guid userRoleId) => $"UserRoleDisable:{userRoleId}";
/// <summary>
/// 用户登录错误 限制登录
/// </summary>
/// <param name="userName"></param>
/// <returns></returns>
public static string UserLoginError(string userName) => $"login-failures:{userName}";
/// <summary>
/// 跳过阅片
/// </summary>
/// <param name="userId"></param>
/// <returns></returns>
public static string SkipReadingCacheKey(Guid userId) => $"{userId}SkipReadingCache";
/// <summary>
/// 开始阅片时间
/// </summary>
/// <param name="userId"></param>
/// <returns></returns>
public static string StartReadingTimeKey(Guid userId) => $"{userId}StartReadingTime";
/// <summary>
/// 开始休息时间
/// </summary>
/// <param name="userId"></param>
/// <returns></returns>
public static string StartRestTime(Guid userId) => $"{userId}StartRestTime";
//每个用户 每个浏览器独立时间
public static string UserMFAVerifyPass(Guid userId, string browserFingerprint) => $"UserMFAVerifyPass:{userId}:{browserFingerprint}";
public static string TrialSiteInfo(Guid trialSiteId) => $"{trialSiteId}TrialSiteInfo";
public static string TrialDataStoreType(Guid trialId) => $"TrialDataStoreType:{trialId}";
}
public static class CacheHelper
{
public static async Task<string?> GetTrialStatusAsync(Guid trialId, IRepository<Trial> _trialRepository)
{
var statusStr = await _trialRepository.Where(t => t.Id == trialId, ignoreQueryFilters: true).Select(t => t.TrialStatusStr).FirstOrDefaultAsync();
return statusStr;
}
public class TrialSiteInfoDTO
{
public string TrialSiteCode { get; set; }
public string TrialCode { get; set; }
}
public static async Task<TrialSiteInfoDTO> GetTrialSiteInfo(Guid trialSiteId, IRepository<TrialSite> _trialSiteRepository)
{
var obj = await _trialSiteRepository.Where(t => t.Id == trialSiteId, ignoreQueryFilters: true).Select(t => new TrialSiteInfoDTO { TrialCode= t.Trial.TrialCode, TrialSiteCode= t.TrialSiteCode }).FirstOrDefaultAsync();
return obj??new TrialSiteInfoDTO();
}
public static async Task<List<SystemAnonymization>> GetSystemAnonymizationListAsync(IRepository<SystemAnonymization> _systemAnonymizationRepository)
{
var list = await _systemAnonymizationRepository.Where(t => t.IsEnable).ToListAsync();
return list;
}
}
+40 -55
View File
@@ -1,32 +1,49 @@
using FellowOakDicom; using IRaCIS.Core.Domain.Share;
using FellowOakDicom.Imaging.Codec;
using FellowOakDicom.Network;
using IRaCIS.Core.Domain.Models;
using IRaCIS.Core.Domain.Share;
using IRaCIS.Core.Infra.EFCore;
using IRaCIS.Core.Infrastructure;
using IRaCIS.Core.Infrastructure.Extention;
using IRaCIS.Core.SCP.Service;
using MassTransit;
using Medallion.Threading;
using Microsoft.AspNetCore.Hosting;
using Serilog.Sinks.File;
using System.Data;
using System.Runtime.Intrinsics.X86;
using System.Text; using System.Text;
using ZiggyCreatures.Caching.Fusion; using Microsoft.AspNetCore.Hosting;
using static IRaCIS.Core.SCP.Service.CacheHelper; 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;
using IRaCIS.Core.Infrastructure.Extention;
namespace IRaCIS.Core.SCP.Service namespace IRaCIS.Core.SCP.Service
{ {
public class DicomArchiveService(IRepository<SCPPatient> _patientRepository, public class DicomArchiveService : BaseService, IDicomArchiveService
IRepository<SCPStudy> _studyRepository,
IRepository<SCPSeries> _seriesRepository,
IRepository<SCPInstance> _instanceRepository
) : 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;
}
@@ -38,7 +55,6 @@ namespace IRaCIS.Core.SCP.Service
/// <exception cref="NotImplementedException"></exception> /// <exception cref="NotImplementedException"></exception>
public async Task<Guid> ArchiveDicomFileAsync(DicomFile dicomFile, Guid trialId, Guid trialSiteId, string fileRelativePath, string callingAE, string calledAE,long fileSize) public async Task<Guid> ArchiveDicomFileAsync(DicomFile dicomFile, Guid trialId, Guid trialSiteId, string fileRelativePath, string callingAE, string calledAE,long fileSize)
{ {
var dataset = dicomFile.Dataset; var dataset = dicomFile.Dataset;
string studyInstanceUid = dataset.GetString(DicomTag.StudyInstanceUID); string studyInstanceUid = dataset.GetString(DicomTag.StudyInstanceUID);
@@ -204,18 +220,6 @@ namespace IRaCIS.Core.SCP.Service
AcquisitionNumber = dataset.GetSingleValueOrDefault(DicomTag.AcquisitionNumber, string.Empty), AcquisitionNumber = dataset.GetSingleValueOrDefault(DicomTag.AcquisitionNumber, string.Empty),
TriggerTime = dataset.GetSingleValueOrDefault(DicomTag.TriggerTime, string.Empty), TriggerTime = dataset.GetSingleValueOrDefault(DicomTag.TriggerTime, string.Empty),
RadionuclideTotalDose= dataset.GetSingleValueOrDefault(DicomTag.RadionuclideTotalDose, string.Empty),
RadionuclideHalfLife= dataset.GetSingleValueOrDefault(DicomTag.RadionuclideHalfLife, string.Empty),
RadiopharmaceuticalStartTime = dataset.GetSingleValueOrDefault(DicomTag.RadiopharmaceuticalStartTime, string.Empty),
Manufacturer = dataset.GetSingleValueOrDefault(DicomTag.Manufacturer, string.Empty),
ManufacturerModelName = dataset.GetSingleValueOrDefault(DicomTag.ManufacturerModelName, string.Empty),
DeviceSerialNumber = dataset.GetSingleValueOrDefault(DicomTag.DeviceSerialNumber, string.Empty),
DeviceUID = dataset.GetSingleValueOrDefault(DicomTag.DeviceUID, string.Empty),
SoftwareVersions = dataset.GetSingleValueOrDefault(DicomTag.SoftwareVersions, string.Empty),
PatientWeight = dataset.GetSingleValueOrDefault(DicomTag.PatientWeight, string.Empty),
//IsDoubleReview = addtionalInfo.IsDoubleReview, //IsDoubleReview = addtionalInfo.IsDoubleReview,
@@ -235,7 +239,6 @@ namespace IRaCIS.Core.SCP.Service
findStudy.DicomStudyTime = dataset.GetSingleValueOrDefault(DicomTag.StudyTime, string.Empty); findStudy.DicomStudyTime = dataset.GetSingleValueOrDefault(DicomTag.StudyTime, string.Empty);
findStudy.CalledAE = calledAE; findStudy.CalledAE = calledAE;
findStudy.CallingAE = callingAE; findStudy.CallingAE = callingAE;
findStudy.PatientIdStr = patientIdStr;
findStudy.PatientName = dataset.GetSingleValueOrDefault(DicomTag.PatientName, string.Empty); findStudy.PatientName = dataset.GetSingleValueOrDefault(DicomTag.PatientName, string.Empty);
findStudy.PatientSex = dataset.GetSingleValueOrDefault(DicomTag.PatientSex, string.Empty); findStudy.PatientSex = dataset.GetSingleValueOrDefault(DicomTag.PatientSex, string.Empty);
findStudy.PatientAge = dataset.GetSingleValueOrDefault(DicomTag.PatientAge, string.Empty); findStudy.PatientAge = dataset.GetSingleValueOrDefault(DicomTag.PatientAge, string.Empty);
@@ -278,9 +281,6 @@ namespace IRaCIS.Core.SCP.Service
AcquisitionNumber = dataset.GetSingleValueOrDefault(DicomTag.AcquisitionNumber, string.Empty), AcquisitionNumber = dataset.GetSingleValueOrDefault(DicomTag.AcquisitionNumber, string.Empty),
TriggerTime = dataset.GetSingleValueOrDefault(DicomTag.TriggerTime, string.Empty), TriggerTime = dataset.GetSingleValueOrDefault(DicomTag.TriggerTime, string.Empty),
RadiopharmaceuticalInformationSequence = dataset.GetSingleValueOrDefault(DicomTag.RadiopharmaceuticalInformationSequence, string.Empty),
AcquisitionDate = dataset.GetSingleValueOrDefault(DicomTag.AcquisitionDate, string.Empty),
InstanceCount = 0 InstanceCount = 0
}; };
@@ -325,28 +325,13 @@ namespace IRaCIS.Core.SCP.Service
SliceLocation = dataset.GetSingleValueOrDefault(DicomTag.SliceLocation, 0), SliceLocation = dataset.GetSingleValueOrDefault(DicomTag.SliceLocation, 0),
SliceThickness = dataset.GetSingleValueOrDefault(DicomTag.SliceThickness, string.Empty), SliceThickness = dataset.GetSingleValueOrDefault(DicomTag.SliceThickness, string.Empty),
NumberOfFrames = dataset.GetSingleValueOrDefault(DicomTag.NumberOfFrames, 1), NumberOfFrames = dataset.GetSingleValueOrDefault(DicomTag.NumberOfFrames, 0),
PixelSpacing = dataset.GetSingleValueOrDefault(DicomTag.PixelSpacing, string.Empty), PixelSpacing = dataset.GetSingleValueOrDefault(DicomTag.PixelSpacing, string.Empty),
ImagerPixelSpacing = dataset.GetSingleValueOrDefault(DicomTag.ImagerPixelSpacing, string.Empty), ImagerPixelSpacing = dataset.GetSingleValueOrDefault(DicomTag.ImagerPixelSpacing, string.Empty),
FrameOfReferenceUID = dataset.GetSingleValueOrDefault(DicomTag.FrameOfReferenceUID, string.Empty), FrameOfReferenceUID = dataset.GetSingleValueOrDefault(DicomTag.FrameOfReferenceUID, string.Empty),
WindowCenter = dataset.GetSingleValueOrDefault(DicomTag.WindowCenter, string.Empty), WindowCenter = dataset.GetSingleValueOrDefault(DicomTag.WindowCenter, string.Empty),
WindowWidth = dataset.GetSingleValueOrDefault(DicomTag.WindowWidth, string.Empty), WindowWidth = dataset.GetSingleValueOrDefault(DicomTag.WindowWidth, string.Empty),
PhotometricInterpretation = dataset.GetSingleValueOrDefault(DicomTag.PhotometricInterpretation, string.Empty),
BitsAllocated = dataset.GetSingleValueOrDefault(DicomTag.BitsAllocated, 0),
PixelRepresentation = dataset.GetSingleValueOrDefault(DicomTag.PixelRepresentation, string.Empty),
RescaleIntercept = dataset.GetSingleValueOrDefault(DicomTag.RescaleIntercept, string.Empty),
RescaleSlope = dataset.GetSingleValueOrDefault(DicomTag.RescaleSlope, string.Empty),
ImagePositionPatient = dataset.GetSingleValueOrDefault(DicomTag.ImagePositionPatient, string.Empty),
ImageOrientationPatient = dataset.GetSingleValueOrDefault(DicomTag.ImageOrientationPatient, string.Empty),
SequenceOfUltrasoundRegions = dataset.GetSingleValueOrDefault(DicomTag.SequenceOfUltrasoundRegions, string.Empty),
FrameTime = dataset.GetSingleValueOrDefault(DicomTag.FrameTime, string.Empty),
CorrectedImage = dataset.GetSingleValueOrDefault(DicomTag.CorrectedImage, string.Empty),
Units = dataset.GetSingleValueOrDefault(DicomTag.Units, string.Empty),
DecayCorrection = dataset.GetSingleValueOrDefault(DicomTag.DecayCorrection, string.Empty),
EncapsulatedDocument = dataset.GetSingleValueOrDefault(DicomTag.EncapsulatedDocument, string.Empty),
Path = fileRelativePath, Path = fileRelativePath,
FileSize= fileSize, FileSize= fileSize,
@@ -1,334 +0,0 @@
//--------------------------------------------------------------------
// 此代码由liquid模板自动生成 byzhouhang 20240909
// 生成时间 2026-03-10 06:15:17Z
// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。
//--------------------------------------------------------------------
using AutoMapper;
using IRaCIS.Core.Application.Helper;
using IRaCIS.Core.Domain.Models;
using IRaCIS.Core.Domain.Share;
using IRaCIS.Core.Infra.EFCore;
using IRaCIS.Core.Infra.EFCore.Common;
using IRaCIS.Core.Infrastructure.Extention;
using IRaCIS.Core.SCP;
using IRaCIS.Core.SCP.Service;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Options;
using System.Drawing;
using System.Threading.Channels;
using System.Threading.Tasks;
using ZiggyCreatures.Caching.Fusion;
namespace IRaCIS.Core.SCP.Service;
public class FileUploadRecordAddOrEdit
{
public Guid? Id { get; set; }
public string FileName { get; set; }
public long FileSize { get; set; }
public string FileType { get; set; }
public string Path { get; set; }
public string UploadBatchId { get; set; }
public BatchDataType BatchDataType { get; set; }
public string StudyCode { get; set; }
public Guid? TrialId { get; set; }
public Guid? SubjectId { get; set; }
public Guid? SubjectVisitId { get; set; }
public Guid? DicomStudyId { get; set; }
public Guid? NoneDicomStudyId { get; set; }
public string FileMarkId { get; set; }
public int? Priority { get; set; }
public string IP { get; set; }
public bool? IsNeedSync { get; set; }
public string UploadRegion { get; set; }
public string TargetRegion { get; set; }
public bool? IsSync { get; set; }
}
public interface IFileUploadRecordService
{
Task<IResponseOutput> AddOrUpdateFileUploadRecord(FileUploadRecordAddOrEdit addOrEditFileUploadRecord);
}
[ApiExplorerSettings(GroupName = "Common")]
public class FileUploadRecordService(IRepository<FileUploadRecord> _fileUploadRecordRepository,
IMapper _mapper, IUserInfo _userInfo, IStringLocalizer _localizer, IOptionsMonitor<ObjectStoreServiceOptions> options,
IFusionCache _fusionCache, IRepository<Trial> _trialRepository, FileSyncQueue _fileSyncQueue) : BaseService, IFileUploadRecordService
{
ObjectStoreServiceOptions ObjectStoreServiceConfig = options.CurrentValue;
public async Task<IResponseOutput> AddOrUpdateFileUploadRecord(FileUploadRecordAddOrEdit addOrEditFileUploadRecord)
{
addOrEditFileUploadRecord.IP = _userInfo.IP;
if (ObjectStoreServiceConfig.IsOpenStoreSync && _userInfo.Domain.IsNotNullOrEmpty())
{
var find = ObjectStoreServiceConfig.SyncConfigList.FirstOrDefault(t => t.Domain == _userInfo.Domain);
if (find != null)
{
addOrEditFileUploadRecord.UploadRegion = find.UploadRegion;
addOrEditFileUploadRecord.TargetRegion = find.TargetRegion;
}
else
{
//前后端调试的时候,上传的时候域名不对应,自动按照后端配置设置上传区域和同步区域
var apiDefalut = ObjectStoreServiceConfig.SyncConfigList.FirstOrDefault(t => t.UploadRegion == ObjectStoreServiceConfig.ApiDeployRegion);
if (apiDefalut != null)
{
addOrEditFileUploadRecord.UploadRegion = apiDefalut.UploadRegion;
addOrEditFileUploadRecord.TargetRegion = apiDefalut.TargetRegion;
}
}
}
//SCP推送过来IP为空,后端归档的,设置区域
if (_userInfo.IP.IsNullOrEmpty() && _userInfo.Domain.IsNullOrEmpty())
{
var apiDefalut = ObjectStoreServiceConfig.SyncConfigList.FirstOrDefault(t => t.UploadRegion == ObjectStoreServiceConfig.ApiDeployRegion);
if (apiDefalut != null)
{
addOrEditFileUploadRecord.UploadRegion = apiDefalut.UploadRegion;
addOrEditFileUploadRecord.TargetRegion = apiDefalut.TargetRegion;
}
}
if (addOrEditFileUploadRecord.TrialId != null)
{
var trialDataStore = await _fusionCache.GetOrSetAsync(CacheKeys.TrialDataStoreType(addOrEditFileUploadRecord.TrialId.Value), async _ =>
{
return await _trialRepository.Where(t => t.Id == addOrEditFileUploadRecord.TrialId).Select(t => t.TrialDataStoreType)
.FirstOrDefaultAsync();
},
TimeSpan.FromDays(7)
);
//项目配置了,那么就设置需要同步
if (trialDataStore == TrialDataStore.MUtiCenter)
{
addOrEditFileUploadRecord.IsNeedSync = true;
addOrEditFileUploadRecord.Priority = addOrEditFileUploadRecord.Priority ?? 0;
addOrEditFileUploadRecord.IsSync = false;
}
else
{
addOrEditFileUploadRecord.IsNeedSync = false;
//addOrEditFileUploadRecord.TargetRegion = "";
}
}
else
{
//系统文件,默认同步
addOrEditFileUploadRecord.IsNeedSync = true;
addOrEditFileUploadRecord.IsSync = false;
addOrEditFileUploadRecord.Priority = addOrEditFileUploadRecord.Priority ?? 0;
}
var entity = await _fileUploadRecordRepository.InsertOrUpdateAsync(addOrEditFileUploadRecord, true);
if (addOrEditFileUploadRecord.IsNeedSync == true)
{
_fileSyncQueue.Enqueue(entity.Id, addOrEditFileUploadRecord.Priority ?? 0);
}
return ResponseOutput.Ok(entity.Id.ToString());
}
}
public sealed class FileSyncQueue
{
/// <summary>
/// 优先级队列(仅负责排序)
/// </summary>
private readonly PriorityQueue<Guid, int> _queue = new();
/// <summary>
/// 当前等待中的任务(唯一真实数据)
/// key = Guid
/// value = 最新 priority
/// </summary>
private readonly Dictionary<Guid, int> _waiting = new();
/// <summary>
/// 正在执行的任务(防止重复执行)
/// </summary>
private readonly HashSet<Guid> _running = new();
/// <summary>
/// worker 等待信号
/// </summary>
private readonly SemaphoreSlim _signal = new(0);
private readonly object _lock = new();
// ============================================================
// Enqueue
// ============================================================
/// <summary>
/// 入队(同 Guid 会覆盖优先级)
/// </summary>
public void Enqueue(Guid id, int priority)
{
bool needSignal = false;
lock (_lock)
{
// 如果正在执行,忽略(防止重复)
if (_running.Contains(id))
return;
// 是否新任务(用于减少 signal 风暴)
if (!_waiting.ContainsKey(id))
needSignal = true;
// 更新为最新优先级(最后一次为准)
_waiting[id] = priority; //等价于添加或者更新
// PriorityQueue 无法更新节点
// 允许旧节点存在,Dequeue 时过滤
_queue.Enqueue(id, -priority);
}
// 只有新增任务才唤醒 worker
if (needSignal)
_signal.Release();
}
// ============================================================
// Dequeue
// ============================================================
/// <summary>
/// 获取一个待执行任务(无任务时自动等待)
/// </summary>
public async Task<Guid> DequeueAsync(CancellationToken ct)
{
while (true)
{
await _signal.WaitAsync(ct);
lock (_lock)
{
while (_queue.Count > 0)
{
var id = _queue.Dequeue();
// 已被覆盖或取消
if (!_waiting.TryGetValue(id, out _))
continue;
// 标记为运行中
_waiting.Remove(id);
_running.Add(id);
return id;
}
}
}
}
// ============================================================
// Complete
// ============================================================
/// <summary>
/// 任务执行完成(必须调用)
/// </summary>
public void Complete(Guid id)
{
lock (_lock)
{
_running.Remove(id);
}
}
// ============================================================
// Snapshot
// ============================================================
/// <summary>
/// 当前等待中的任务快照
/// </summary>
public Guid[] Snapshot()
{
lock (_lock)
{
return _waiting.Keys.ToArray();
}
}
// ============================================================
// 状态信息(调试用)
// ============================================================
public int WaitingCount
{
get
{
lock (_lock)
return _waiting.Count;
}
}
public int RunningCount
{
get
{
lock (_lock)
return _running.Count;
}
}
}
File diff suppressed because it is too large Load Diff
-16
View File
@@ -1,16 +0,0 @@
using AutoMapper;
using IRaCIS.Core.Domain.Models;
using IRaCIS.Core.SCP.Service;
namespace IRaCIS.Core.Application.Service
{
public class CommonConfig : Profile
{
public CommonConfig()
{
CreateMap<FileUploadRecordAddOrEdit, FileUploadRecord>().ReverseMap();
}
}
}
@@ -1,37 +0,0 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"ObjectStoreService": {
"ObjectStoreUse": "AliyunOSS",
"AliyunOSS": {
"RegionId": "cn-shanghai",
"InternalEndpoint": "https://oss-cn-shanghai-internal.aliyuncs.com",
"EndPoint": "https://oss-cn-shanghai.aliyuncs.com",
"AccessKeyId": "LTAI5tFUCCmz5TwghZHsj45Y",
"AccessKeySecret": "8evrBy1fVfzJG25i67Jm0xqn9Xcw2T",
"RoleArn": "acs:ram::1078130221702011:role/uat-oss-access",
"BucketName": "tl-med-irc-event-store",
"ViewEndpoint": "https://tl-med-irc-event-store.oss-cn-shanghai.aliyuncs.com",
"Region": "oss-cn-shanghai",
"DurationSeconds": 7200
}
},
"ConnectionStrings": {
"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"
},
"DicomSCPServiceConfig": {
"CalledAEList": [
"STORESCP"
],
"ServerPort": 11112
}
}
@@ -8,26 +8,6 @@
}, },
"ObjectStoreService": { "ObjectStoreService": {
"ObjectStoreUse": "AliyunOSS", "ObjectStoreUse": "AliyunOSS",
"IsOpenStoreSync": true,
"ApiDeployRegion": "CN",
"SyncConfigList": [
{
"Domain": "irc.extimaging.com",
"Primary": "AliyunOSS",
"Target": "AWS",
"UploadRegion": "CN",
"TargetRegion": "US",
"IsOpenSync": true
},
{
"Domain": "eiirc.extimaging.com",
"Primary": "AWS",
"Target": "AliyunOSS",
"UploadRegion": "US",
"TargetRegion": "CN",
"IsOpenSync": true
}
],
"AliyunOSS": { "AliyunOSS": {
"RegionId": "cn-shanghai", "RegionId": "cn-shanghai",
"InternalEndpoint": "https://oss-cn-shanghai-internal.aliyuncs.com", "InternalEndpoint": "https://oss-cn-shanghai-internal.aliyuncs.com",
@@ -39,18 +19,6 @@
"ViewEndpoint": "https://zy-irc-cache.oss-cn-shanghai.aliyuncs.com", "ViewEndpoint": "https://zy-irc-cache.oss-cn-shanghai.aliyuncs.com",
"Region": "oss-cn-shanghai", "Region": "oss-cn-shanghai",
"DurationSeconds": 7200 "DurationSeconds": 7200
},
"AWS": {
"Region": "us-east-1",
"EndPoint": "s3.us-east-1.amazonaws.com",
"UseSSL": true,
"RoleArn": "arn:aws:iam::471112624751:role/lili_s3_access",
"AccessKeyId": "AKIAW3MEAFJXZ2TZK7GM",
"SecretAccessKey": "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",
"BucketName": "ei-med-s3-irc-store",
"ViewEndpoint": "https://ei-med-s3-irc-store.s3.amazonaws.com",
"DurationSeconds": 7200
} }
}, },
@@ -7,28 +7,7 @@
} }
}, },
"ObjectStoreService": { "ObjectStoreService": {
// 使用的对象存储服务类型
"ObjectStoreUse": "AliyunOSS", "ObjectStoreUse": "AliyunOSS",
"IsOpenStoreSync": true,
"ApiDeployRegion": "CN",
"SyncConfigList": [
{
"Domain": "irc.test.extimaging.com",
"Primary": "AliyunOSS",
"Target": "AWS",
"UploadRegion": "CN",
"TargetRegion": "US",
"IsOpenSync": true
},
{
"Domain": "lili.test.extimaging.com",
"Primary": "AWS",
"Target": "AliyunOSS",
"UploadRegion": "US",
"TargetRegion": "CN",
"IsOpenSync": true
}
],
"AliyunOSS": { "AliyunOSS": {
"RegionId": "cn-shanghai", "RegionId": "cn-shanghai",
"InternalEndpoint": "https://oss-cn-shanghai-internal.aliyuncs.com", "InternalEndpoint": "https://oss-cn-shanghai-internal.aliyuncs.com",
@@ -50,27 +29,6 @@
"secretKey": "TzgvyA3zGXMUnpilJNUlyMYHfosl1hBMl6lxPmjy", "secretKey": "TzgvyA3zGXMUnpilJNUlyMYHfosl1hBMl6lxPmjy",
"bucketName": "hir-test", "bucketName": "hir-test",
"viewEndpoint": "http://106.14.89.110:9001/hir-test/" "viewEndpoint": "http://106.14.89.110:9001/hir-test/"
},
// AWS S3 对象存储服务的配置
"AWS": {
// AWS S3 的Region
"Region": "us-east-1",
// AWS S3 的内部访问端点
"EndPoint": "s3.us-east-1.amazonaws.com",
// 是否使用 SSL
"UseSSL": true,
// AWS S3 的角色 ARN
"RoleArn": "arn:aws:iam::471112624751:role/uat_s3_access",
// AWS S3 的访问密钥 ID
"AccessKeyId": "AKIAW3MEAFJX7IPXISP4",
// AWS S3 的访问密钥 Secret
"SecretAccessKey": "Pgrg3le5jPxZQ7MR1yYNS30J0XRyJeKVyIIjElXc",
// AWS S3 的Bucket名称
"BucketName": "ei-med-s3-lili-uat-store",
// AWS S3 的访问端点
"ViewEndpoint": "https://ei-med-s3-lili-uat-store.s3.amazonaws.com",
// AWS S3 的持续数秒
"DurationSeconds": 7200
} }
}, },
-32
View File
@@ -8,38 +8,6 @@
}, },
"ObjectStoreService": { "ObjectStoreService": {
"ObjectStoreUse": "AWS", "ObjectStoreUse": "AWS",
"IsOpenStoreSync": true,
"ApiDeployRegion": "US",
"SyncConfigList": [
{
"Domain": "eilili.elevateimaging.ai",
"Primary": "AliyunOSS",
"Target": "AWS",
"UploadRegion": "CN",
"TargetRegion": "US",
"IsOpenSync": true
},
{
"Domain": "lili.elevateimaging.ai",
"Primary": "AWS",
"Target": "AliyunOSS",
"UploadRegion": "US",
"TargetRegion": "CN",
"IsOpenSync": true
}
],
"AliyunOSS": {
"RegionId": "cn-shanghai",
"InternalEndpoint": "https://oss-cn-shanghai-internal.aliyuncs.com",
"EndPoint": "https://oss-cn-shanghai.aliyuncs.com",
"AccessKeyId": "LTAI5tNRTsqL6aWmHkDmTwoH",
"AccessKeySecret": "7mtGz3qrYWI6JMMBZiLeC119VWicZH",
"RoleArn": "acs:ram::1899121822495495:role/irc-oss-access",
"BucketName": "zy-lili-store",
"ViewEndpoint": "https://zy-lili-cache.oss-cn-shanghai.aliyuncs.com",
"Region": "oss-cn-shanghai",
"DurationSeconds": 7200
},
"AWS": { "AWS": {
"Region": "us-east-1", "Region": "us-east-1",
"EndPoint": "s3.us-east-1.amazonaws.com", "EndPoint": "s3.us-east-1.amazonaws.com",
+5 -46
View File
@@ -8,58 +8,17 @@
}, },
"ObjectStoreService": { "ObjectStoreService": {
"ObjectStoreUse": "AliyunOSS", "ObjectStoreUse": "AliyunOSS",
"IsOpenStoreSync": true,
"ApiDeployRegion": "CN",
"SyncConfigList": [
{
"Domain": "irc.uat.extimaging.com",
"Primary": "AliyunOSS",
"Target": "AWS",
"UploadRegion": "CN",
"TargetRegion": "US",
"IsOpenSync": true
},
{
"Domain": "lili.uat.extimaging.com",
"Primary": "AWS",
"Target": "AliyunOSS",
"UploadRegion": "US",
"TargetRegion": "CN",
"IsOpenSync": true
}
],
"AliyunOSS": { "AliyunOSS": {
"RegionId": "cn-shanghai", "RegionId": "cn-shanghai",
"InternalEndpoint": "https://oss-cn-shanghai-internal.aliyuncs.com", "InternalEndpoint": "https://oss-cn-shanghai-internal.aliyuncs.com",
"EndPoint": "https://oss-cn-shanghai.aliyuncs.com", "EndPoint": "https://oss-cn-shanghai.aliyuncs.com",
"AccessKeyId": "LTAI5tRRZehUp2V9pyTPtAJm", "AccessKeyId": "LTAI5tFUCCmz5TwghZHsj45Y",
"AccessKeySecret": "FLizxkHsMm4CGYHtkV8E3PNJJZU7oV", "AccessKeySecret": "8evrBy1fVfzJG25i67Jm0xqn9Xcw2T",
"RoleArn": "acs:ram::1899121822495495:role/dev-oss-access", "RoleArn": "acs:ram::1078130221702011:role/uat-oss-access",
"BucketName": "zy-irc-test-store", "BucketName": "tl-med-irc-uat-store",
"ViewEndpoint": "https://zy-irc-test-store.oss-cn-shanghai.aliyuncs.com", "ViewEndpoint": "https://tl-med-irc-uat-store.oss-cn-shanghai.aliyuncs.com",
"Region": "oss-cn-shanghai", "Region": "oss-cn-shanghai",
"DurationSeconds": 7200 "DurationSeconds": 7200
},
// AWS S3 对象存储服务的配置
"AWS": {
// AWS S3 的Region
"Region": "us-east-1",
// AWS S3 的内部访问端点
"EndPoint": "s3.us-east-1.amazonaws.com",
// 是否使用 SSL
"UseSSL": true,
// AWS S3 的角色 ARN
"RoleArn": "arn:aws:iam::471112624751:role/uat_s3_access",
// AWS S3 的访问密钥 ID
"AccessKeyId": "AKIAW3MEAFJX7IPXISP4",
// AWS S3 的访问密钥 Secret
"SecretAccessKey": "Pgrg3le5jPxZQ7MR1yYNS30J0XRyJeKVyIIjElXc",
// AWS S3 的Bucket名称
"BucketName": "ei-med-s3-lili-uat-store",
// AWS S3 的访问端点
"ViewEndpoint": "https://ei-med-s3-lili-uat-store.s3.amazonaws.com",
// AWS S3 的持续数秒
"DurationSeconds": 7200
} }
}, },
"ConnectionStrings": { "ConnectionStrings": {
+4 -10
View File
@@ -25,7 +25,6 @@ using Microsoft.Extensions.Options;
using Org.BouncyCastle.Tls; using Org.BouncyCastle.Tls;
using RestSharp; using RestSharp;
using RestSharp.Authenticators; using RestSharp.Authenticators;
using Serilog;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
@@ -127,7 +126,7 @@ namespace IRaCIS.Api.Controllers
var token = _tokenService.GetToken(new UserTokenInfo() var token = _tokenService.GetToken(new UserTokenInfo()
{ {
IdentityUserId = Guid.NewGuid(), IdentityUserId = Guid.NewGuid(),
UserName = "ImageShare", UserName = "Share001",
UserTypeEnum = UserTypeEnum.ShareImage, UserTypeEnum = UserTypeEnum.ShareImage,
}); });
@@ -138,14 +137,9 @@ namespace IRaCIS.Api.Controllers
public async Task<IResponseOutput> GetObjectStoreTokenAsync([FromServices] IOptionsMonitor<ObjectStoreServiceOptions> options, [FromServices] IOSSService _oSSService) public async Task<IResponseOutput> GetObjectStoreTokenAsync([FromServices] IOptionsMonitor<ObjectStoreServiceOptions> options, [FromServices] IOSSService _oSSService)
{ {
var domain = HttpContext.Request.Host.Host; var result = _oSSService.GetObjectStoreTempToken();
//result.AWS = await GetAWSTemToken(options.CurrentValue);
var result = _oSSService.GetObjectStoreTempToken(domain);
Log.Logger.Information($"使用域名:{domain}请求token.返回{result.ToJsonStr()}");
return ResponseOutput.Ok(result); return ResponseOutput.Ok(result);
@@ -285,7 +279,7 @@ namespace IRaCIS.Api.Controllers
var isExpire = _tokenService.IsTokenExpired(token); var isExpire = _tokenService.IsTokenExpired(token);
if ( Guid.TryParse(userId,out _) == false || isExpire || !await _useRepository.AnyAsync(t => t.Id == Guid.Parse(userId) && t.EmailToken == token && t.IsFirstAdd) ) 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"))} "; decodeUrl = errorUrl + $"?lang={lang}&ErrorMessage={System.Web.HttpUtility.UrlEncode(I18n.T("UserRedirect_InitializationLinkExpire"))} ";
} }
@@ -26,7 +26,6 @@ namespace IRaCIS.Core.API.Controllers
ITrialDocumentService _trialDocumentService, ITrialDocumentService _trialDocumentService,
IReadingImageTaskService _iReadingImageTaskService, IReadingImageTaskService _iReadingImageTaskService,
ITrialConfigService _trialConfigService, ITrialConfigService _trialConfigService,
IStudyService _studyService,
IClinicalAnswerService _clinicalAnswerService, IClinicalAnswerService _clinicalAnswerService,
IReadingClinicalDataService _readingClinicalDataService, IReadingClinicalDataService _readingClinicalDataService,
IQCOperationService _qCOperationService, IQCOperationService _qCOperationService,
@@ -147,26 +146,6 @@ namespace IRaCIS.Core.API.Controllers
} }
/// <summary>
/// 修正患者基本信息
/// </summary>
/// <param name="opt"></param>
/// <returns></returns>
[HttpPost, Route("Inspection/Study/AmendmentPatientInfo")]
[TrialGlobalLimit("AfterStopCannNotOpt")]
[UnitOfWork]
public async Task<IResponseOutput> AmendmentPatientInfo(DataInspectionDto<EditPatientInfoCommand> opt)
{
var singid = await _inspectionService.RecordSing(opt.SignInfo);
var result = await _studyService.AmendmentPatientInfo(opt.Data);
await _inspectionService.CompletedSign(singid, result);
return result;
}
/// <summary> /// <summary>
/// 医学审核完成 /// 医学审核完成
/// </summary> /// </summary>
@@ -176,19 +155,10 @@ namespace IRaCIS.Core.API.Controllers
[TrialGlobalLimit("AfterStopCannNotOpt")] [TrialGlobalLimit("AfterStopCannNotOpt")]
[UnitOfWork] [UnitOfWork]
public async Task<IResponseOutput> FinishMedicalReview(DataInspectionDto<CloseAndFinishMedicalReview> opt) public async Task<IResponseOutput> FinishMedicalReview(DataInspectionDto<FinishMedicalReviewInDto> opt)
{ {
var singid = await _inspectionService.RecordSing(opt.SignInfo); var singid = await _inspectionService.RecordSing(opt.SignInfo);
var result = await _readingMedicalReviewService.FinishMedicalReview(opt.Data);
await _readingMedicalReviewService.ClosedMedicalReviewDialog(new ClosedMedicalReviewDialogInDto() {
DialogCloseReason=opt.Data.DialogCloseReason,
IsClosedDialog=opt.Data.IsClosedDialog,
MedicalDialogCloseEnum=opt.Data.MedicalDialogCloseEnum,
TaskMedicalReviewId=opt.Data.TaskMedicalReviewId
});
var result = await _readingMedicalReviewService.FinishMedicalReview(new FinishMedicalReviewInDto() {
TaskMedicalReviewId=opt.Data.TaskMedicalReviewId
});
await _inspectionService.CompletedSign(singid, result); await _inspectionService.CompletedSign(singid, result);
return result; return result;
} }
@@ -9,7 +9,6 @@ using IRaCIS.Core.Application.Filter;
using IRaCIS.Core.Application.Helper; using IRaCIS.Core.Application.Helper;
using IRaCIS.Core.Application.MassTransit.Command; using IRaCIS.Core.Application.MassTransit.Command;
using IRaCIS.Core.Application.Service; using IRaCIS.Core.Application.Service;
using IRaCIS.Core.Application.ViewModel;
using IRaCIS.Core.Domain.Models; using IRaCIS.Core.Domain.Models;
using IRaCIS.Core.Domain.Share; using IRaCIS.Core.Domain.Share;
using IRaCIS.Core.Infra.EFCore; using IRaCIS.Core.Infra.EFCore;
@@ -569,7 +568,6 @@ namespace IRaCIS.Core.API.Controllers
/// 一致性核查 excel上传 支持三种格式 /// 一致性核查 excel上传 支持三种格式
/// </summary> /// </summary>
/// <param name="trialId"></param> /// <param name="trialId"></param>
/// <param name="isFullCheck"></param>
/// <param name="oSSService"></param> /// <param name="oSSService"></param>
/// <param name="_inspectionFileRepository"></param> /// <param name="_inspectionFileRepository"></param>
/// <returns></returns> /// <returns></returns>
@@ -577,14 +575,12 @@ namespace IRaCIS.Core.API.Controllers
[HttpPost("QCOperation/UploadVisitCheckExcel/{trialId:guid}")] [HttpPost("QCOperation/UploadVisitCheckExcel/{trialId:guid}")]
[TrialGlobalLimit("AfterStopCannNotOpt")] [TrialGlobalLimit("AfterStopCannNotOpt")]
public async Task<IResponseOutput> UploadVisitCheckExcel(Guid trialId, bool isFullCheck, [FromServices] IOSSService oSSService, [FromServices] IRepository<InspectionFile> _inspectionFileRepository) public async Task<IResponseOutput> UploadVisitCheckExcel(Guid trialId, [FromServices] IOSSService oSSService, [FromServices] IRepository<InspectionFile> _inspectionFileRepository)
{ {
var fileName = string.Empty; var fileName = string.Empty;
var templateFileStream = new MemoryStream(); var templateFileStream = new MemoryStream();
var inspectionFileId = Guid.Empty;
await FileUploadToOSSAsync(async (realFileName, fileStream) => await FileUploadToOSSAsync(async (realFileName, fileStream) =>
{ {
fileName = realFileName; fileName = realFileName;
@@ -599,11 +595,9 @@ namespace IRaCIS.Core.API.Controllers
templateFileStream.Seek(0, SeekOrigin.Begin); templateFileStream.Seek(0, SeekOrigin.Begin);
var ossRelativePath = await oSSService.UploadToOSSAsync(fileStream, $"{trialId.ToString()}/InspectionUpload/DataReconciliation", realFileName, uploadInfo: new FileUploadRecordAddOrEdit() { TrialId = trialId, BatchDataType = BatchDataType.DataReconciliation }); var ossRelativePath = await oSSService.UploadToOSSAsync(fileStream, $"{trialId.ToString()}/InspectionUpload/DataReconciliation", realFileName);
var addEntity = await _inspectionFileRepository.AddAsync(new InspectionFile() { FileName = realFileName, RelativePath = ossRelativePath, TrialId = trialId }, true); await _inspectionFileRepository.AddAsync(new InspectionFile() { FileName = realFileName, RelativePath = ossRelativePath, TrialId = trialId }, true);
inspectionFileId = addEntity.Id;
return ossRelativePath; return ossRelativePath;
@@ -749,11 +743,8 @@ namespace IRaCIS.Core.API.Controllers
if (etcCheckList == null || etcCheckList.Count == 0) if (etcCheckList == null || etcCheckList.Count == 0)
{ {
await _inspectionFileRepository.BatchUpdateNoTrackingAsync(t => t.Id == inspectionFileId, u => new InspectionFile() { CheckState = EDCCheckState.Failed });
//---请保证上传数据符合模板文件中的样式,且存在有效数据。 //---请保证上传数据符合模板文件中的样式,且存在有效数据。
return ResponseOutput.NotOk(_localizer["UploadDownLoad_InvalidData"]); return ResponseOutput.NotOk(_localizer["UploadDownLoad_InvalidData"]);
} }
else else
{ {
@@ -773,8 +764,6 @@ namespace IRaCIS.Core.API.Controllers
if (etcCheckList.Count == 0) if (etcCheckList.Count == 0)
{ {
await _inspectionFileRepository.BatchUpdateNoTrackingAsync(t => t.Id == inspectionFileId, u => new InspectionFile() { CheckState = EDCCheckState.Failed });
//---请保证上传数据符合模板文件中的样式,且存在有效数据。 //---请保证上传数据符合模板文件中的样式,且存在有效数据。
return ResponseOutput.NotOk(_localizer["UploadDownLoad_InvalidData"]); return ResponseOutput.NotOk(_localizer["UploadDownLoad_InvalidData"]);
} }
@@ -786,19 +775,8 @@ namespace IRaCIS.Core.API.Controllers
//var client = _mediator.CreateRequestClient<ConsistenCheckCommand>(); //var client = _mediator.CreateRequestClient<ConsistenCheckCommand>();
//await client.GetResponse<ConsistenCheckResult>(new ConsistenCheckCommand() { ETCList = etcCheckList, TrialId = trialId }); //await client.GetResponse<ConsistenCheckResult>(new ConsistenCheckCommand() { ETCList = etcCheckList, TrialId = trialId });
if (isFullCheck)
{
await _mediator.Send(new ConsistenFullCheckCommand() { ETCList = etcCheckList, TrialId = trialId, InspectionFileId = inspectionFileId });
}
else
{
//不获取结果,不用定义返回类型 //不获取结果,不用定义返回类型
await _mediator.Send(new ConsistenCheckCommand() { ETCList = etcCheckList, TrialId = trialId }); await _mediator.Send(new ConsistenCheckCommand() { ETCList = etcCheckList, TrialId = trialId });
}
return ResponseOutput.Ok(); return ResponseOutput.Ok();
@@ -857,7 +835,7 @@ namespace IRaCIS.Core.API.Controllers
throw new BusinessValidationFailedException(_localizer["UploadDownLoad_TemplateUploadData"]); throw new BusinessValidationFailedException(_localizer["UploadDownLoad_TemplateUploadData"]);
} }
var ossRelativePath = await oSSService.UploadToOSSAsync(fileStream, $"{trialId.ToString()}/InspectionUpload/SiteSurvey", realFileName, uploadInfo: new FileUploadRecordAddOrEdit() { TrialId = trialId, BatchDataType = BatchDataType.SiteUserSurvey }); var ossRelativePath = await oSSService.UploadToOSSAsync(fileStream, $"{trialId.ToString()}/InspectionUpload/SiteSurvey", realFileName);
await _inspectionFileRepository.AddAsync(new InspectionFile() { FileName = realFileName, RelativePath = ossRelativePath, TrialId = trialId }, true); await _inspectionFileRepository.AddAsync(new InspectionFile() { FileName = realFileName, RelativePath = ossRelativePath, TrialId = trialId }, true);
@@ -933,7 +911,7 @@ namespace IRaCIS.Core.API.Controllers
} }
//处理好 用户类型 和用户类型枚举 //处理好 用户类型 和用户类型枚举
var sysUserTypeList = _usertypeRepository.Where(t => t.UserTypeEnum == UserTypeEnum.CRA || t.UserTypeEnum == UserTypeEnum.ClinicalResearchCoordinator).Select(t => new { UserTypeId = t.Id, t.UserTypeEnum }).ToList(); var sysUserTypeList = _usertypeRepository.Where(t => t.UserTypeEnum == UserTypeEnum.CRA || t.UserTypeEnum == UserTypeEnum.ClinicalResearchCoordinator).Select(t => new { UserTypeId = t.Id, t.UserTypeEnum }).ToList();
var siteList = _trialSiteRepository.Where(t => t.TrialId == trialId && siteCodeList.Contains(t.TrialSiteCode)).Select(t => new { t.TrialSiteCode, TrialSiteId = t.Id, t.Country }).ToList(); var siteList = _trialSiteRepository.Where(t => t.TrialId == trialId && siteCodeList.Contains(t.TrialSiteCode)).Select(t => new { t.TrialSiteCode, TrialSiteId = t.Id }).ToList();
foreach (var item in excelList) foreach (var item in excelList)
{ {
@@ -955,8 +933,6 @@ namespace IRaCIS.Core.API.Controllers
} }
item.TrialSiteId = siteList.FirstOrDefault(t => t.TrialSiteCode.Trim().ToUpper() == item.TrialSiteCode.Trim().ToUpper()).TrialSiteId; item.TrialSiteId = siteList.FirstOrDefault(t => t.TrialSiteCode.Trim().ToUpper() == item.TrialSiteCode.Trim().ToUpper()).TrialSiteId;
item.Country = siteList.FirstOrDefault(t => t.TrialSiteCode.Trim().ToUpper() == item.TrialSiteCode.Trim().ToUpper()).Country;
} }
var list = excelList.Where(t => t.UserTypeEnum == UserTypeEnum.ClinicalResearchCoordinator || t.UserTypeEnum == UserTypeEnum.CRA).ToList(); var list = excelList.Where(t => t.UserTypeEnum == UserTypeEnum.ClinicalResearchCoordinator || t.UserTypeEnum == UserTypeEnum.CRA).ToList();
@@ -1007,11 +983,7 @@ namespace IRaCIS.Core.API.Controllers
EmailBodyHtml = 4, EmailBodyHtml = 4,
ReadKeyFile = 5, Other = 5
Other = 6,
} }
/// <summary> /// <summary>
@@ -1043,9 +1015,6 @@ namespace IRaCIS.Core.API.Controllers
case UploadFileType.EmailBodyHtml: case UploadFileType.EmailBodyHtml:
result = await SingleFileUploadAsync((fileName) => FileStoreHelper.GetSystemFileUploadPath(_hostEnvironment, StaticData.Folder.EmailTemplate, fileName)); result = await SingleFileUploadAsync((fileName) => FileStoreHelper.GetSystemFileUploadPath(_hostEnvironment, StaticData.Folder.EmailTemplate, fileName));
break; break;
case UploadFileType.ReadKeyFile:
result = await SingleFileUploadAsync((fileName) => FileStoreHelper.GetSystemFileUploadPath(_hostEnvironment, StaticData.Folder.ReadKetFile, fileName));
break;
default: default:
result = await SingleFileUploadAsync((fileName) => FileStoreHelper.GetOtherFileUploadPath(_hostEnvironment, StaticData.Folder.TempFile, fileName)); result = await SingleFileUploadAsync((fileName) => FileStoreHelper.GetOtherFileUploadPath(_hostEnvironment, StaticData.Folder.TempFile, fileName));
@@ -15,13 +15,12 @@ using Hangfire.Storage;
using System.Linq; using System.Linq;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using MassTransit.Mediator; using MassTransit.Mediator;
using Microsoft.Extensions.Configuration;
namespace IRaCIS.Core.API.HostService; namespace IRaCIS.Core.API.HostService;
public class HangfireHostService(IRecurringMessageScheduler _recurringMessageScheduler, public class HangfireHostService(IRecurringMessageScheduler _recurringMessageScheduler,
IRepository<TrialEmailNoticeConfig> _trialEmailNoticeConfigRepository, IRepository<TrialEmailNoticeConfig> _trialEmailNoticeConfigRepository,
IConfiguration _config,
IRepository<EmailNoticeConfig> _emailNoticeConfigrepository, IRepository<EmailNoticeConfig> _emailNoticeConfigrepository,
IMediator _mediator, IMediator _mediator,
ILogger<HangfireHostService> _logger) : IHostedService ILogger<HangfireHostService> _logger) : IHostedService
@@ -57,9 +56,7 @@ public class HangfireHostService(IRecurringMessageScheduler _recurringMessageSch
} }
} }
string defaultCulture = _config.GetValue<string>("SystemEmailSendConfig:CronEmailDefaultCulture");
// 项目手动选择,周期性邮件
var taskInfoList = await _trialEmailNoticeConfigRepository.Where(t => t.Trial.TrialStatusStr == StaticData.TrialState.TrialOngoing && t.EmailCron != string.Empty && t.IsAutoSend) var taskInfoList = await _trialEmailNoticeConfigRepository.Where(t => t.Trial.TrialStatusStr == StaticData.TrialState.TrialOngoing && t.EmailCron != string.Empty && t.IsAutoSend)
.Select(t => new { t.Id, t.Code, TrialCode = t.Trial.TrialCode, t.EmailCron, t.BusinessScenarioEnum, t.TrialId }) .Select(t => new { t.Id, t.Code, TrialCode = t.Trial.TrialCode, t.EmailCron, t.BusinessScenarioEnum, t.TrialId })
.ToListAsync(); .ToListAsync();
@@ -71,11 +68,11 @@ public class HangfireHostService(IRecurringMessageScheduler _recurringMessageSch
var trialId = task.TrialId; var trialId = task.TrialId;
HangfireJobHelper.AddOrUpdateTrialCronJob(jobId, trialId, task.BusinessScenarioEnum, task.EmailCron, defaultCulture); HangfireJobHelper.AddOrUpdateTrialCronJob(jobId, trialId, task.BusinessScenarioEnum, task.EmailCron);
} }
// 系统邮件定时任务 要设置默认语言 // 系统邮件定时任务
var systemTaskInfoList = await _emailNoticeConfigrepository.Where(t => t.EmailCron != string.Empty && t.IsAutoSend) var systemTaskInfoList = await _emailNoticeConfigrepository.Where(t => t.EmailCron != string.Empty && t.IsAutoSend)
.Select(t => new { t.Id, t.Code, t.EmailCron, t.BusinessScenarioEnum, }) .Select(t => new { t.Id, t.Code, t.EmailCron, t.BusinessScenarioEnum, })
.ToListAsync(); .ToListAsync();
@@ -85,7 +82,7 @@ public class HangfireHostService(IRecurringMessageScheduler _recurringMessageSch
//利用主键作为任务Id //利用主键作为任务Id
var jobId = $"{task.Id}_({task.BusinessScenarioEnum})"; var jobId = $"{task.Id}_({task.BusinessScenarioEnum})";
HangfireJobHelper.AddOrUpdateTimingCronJob(jobId, task.BusinessScenarioEnum, task.EmailCron, defaultCulture); HangfireJobHelper.AddOrUpdateTimingCronJob(jobId, task.BusinessScenarioEnum, task.EmailCron);
} }
@@ -1,168 +0,0 @@
using IRaCIS.Core.Application.Helper;
using IRaCIS.Core.Application.Service;
using IRaCIS.Core.Domain.Models;
using IRaCIS.Core.Infra.EFCore;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
namespace IRaCIS.Core.API.HostService;
public class SyncFileRecoveryService(IServiceScopeFactory _scopeFactory, FileSyncQueue _fileSyncQueue) : BackgroundService
{
private readonly int _pageSize = 500;
/// <summary>
/// 多个程序,如果恢复同一份数据,造成重复同步,SCP服务不恢复任务
/// </summary>
/// <param name="stoppingToken"></param>
/// <returns></returns>
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
//在本地调试的时候,不干涉部署同步任务
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
return;
}
using var scope = _scopeFactory.CreateScope();
var fileUploadRecordRepository = scope.ServiceProvider.GetRequiredService<IRepository<FileUploadRecord>>();
// 延迟启动,保证主机快速启动
await Task.Delay(5000, stoppingToken);
int page = 0;
while (!stoppingToken.IsCancellationRequested)
{
// 分页获取未入队任务
var pending = await fileUploadRecordRepository
.Where(x => x.IsNeedSync == true && (x.IsSync == false || x.IsSync == null))
.OrderByDescending(x => x.Priority)
.Select(t => new { t.Id, t.Priority })
.Skip(page * _pageSize)
.Take(_pageSize)
.ToListAsync(stoppingToken);
if (!pending.Any())
break; // 扫描完毕,退出循环
foreach (var file in pending)
{
//file.IsQueued = true; // 避免重复入队
_fileSyncQueue.Enqueue(file.Id, file.Priority ?? 0); // 放入队列
}
page++; // 下一页
await Task.Delay(200, stoppingToken); // 缓解数据库压力
}
}
}
public class FileSyncWorker(IServiceScopeFactory _scopeFactory, ILogger<FileSyncWorker> _logger, FileSyncQueue _fileSyncQueue) : BackgroundService
{
// ⭐ 自动根据服务器CPU
private readonly int _workerCount = Math.Max(1, Environment.ProcessorCount - 1);
protected override Task ExecuteAsync(CancellationToken stoppingToken)
{
for (int i = 0; i < _workerCount; i++)
Task.Run(() => WorkerLoop(stoppingToken));
return Task.CompletedTask;
}
private async Task WorkerLoop(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
var id = await _fileSyncQueue.DequeueAsync(stoppingToken);
try
{
using var scope = _scopeFactory.CreateScope();
var _fileUploadRecordRepository = scope.ServiceProvider.GetRequiredService<IRepository<FileUploadRecord>>();
var _uploadFileSyncRecordRepository = scope.ServiceProvider.GetRequiredService<IRepository<UploadFileSyncRecord>>();
var syncConfig = (scope.ServiceProvider.GetRequiredService<IOptionsMonitor<ObjectStoreServiceOptions>>()).CurrentValue;
var oss = scope.ServiceProvider.GetRequiredService<IOSSService>();
var file = await _fileUploadRecordRepository.FirstOrDefaultAsync(t => t.Id == id);
// ✅ 不要 return
if (file == null || file.IsNeedSync != true || syncConfig.IsOpenStoreSync == false)
{
continue;
}
if (syncConfig.SyncConfigList.Any(t => t.UploadRegion == file.UploadRegion && t.IsOpenSync == false))
{
continue;
}
//如果发现系统配置某一边同步进行了关闭,那么就直接返回,不执行任务
if (syncConfig.SyncConfigList.Any(t => t.UploadRegion == file.UploadRegion && t.IsOpenSync == false))
{
return;
}
var log = new UploadFileSyncRecord
{
FileUploadRecordId = id,
StartTime = DateTime.Now,
JobState = jobState.RUNNING
};
await _uploadFileSyncRecordRepository.AddAsync(log);
await _uploadFileSyncRecordRepository.SaveChangesAsync(stoppingToken);
try
{
await oss.SyncFileAsync(file.Path.TrimStart('/'), file.UploadRegion == "CN" ? ObjectStoreUse.AliyunOSS : ObjectStoreUse.AWS, file.UploadRegion == "CN" ? ObjectStoreUse.AWS : ObjectStoreUse.AliyunOSS);
file.IsSync = true;
file.SyncFinishedTime = DateTime.Now;
log.JobState = jobState.SUCCESS;
}
catch (Exception ex)
{
log.JobState = jobState.FAILED;
log.Msg = ex.Message.Length > 300 ? ex.Message.Substring(0, 300) : ex.Message;
}
log.EndTime = DateTime.Now;
await _uploadFileSyncRecordRepository.SaveChangesAsync(stoppingToken);
}
catch (Exception ex)
{
_logger.LogError(ex, "Sync failed {Id}", id);
}
finally
{
// ⭐⭐⭐ 永远执行
_fileSyncQueue.Complete(id);
}
}
}
}
+2 -17
View File
@@ -136,14 +136,7 @@
<param name="opt"></param> <param name="opt"></param>
<returns></returns> <returns></returns>
</member> </member>
<member name="M:IRaCIS.Core.API.Controllers.InspectionController.AmendmentPatientInfo(IRaCIS.Core.Application.Service.Inspection.DTO.DataInspectionDto{IRaCIS.Core.Application.Contracts.EditPatientInfoCommand})"> <member name="M:IRaCIS.Core.API.Controllers.InspectionController.FinishMedicalReview(IRaCIS.Core.Application.Service.Inspection.DTO.DataInspectionDto{IRaCIS.Core.Application.Service.Reading.Dto.FinishMedicalReviewInDto})">
<summary>
修正患者基本信息
</summary>
<param name="opt"></param>
<returns></returns>
</member>
<member name="M:IRaCIS.Core.API.Controllers.InspectionController.FinishMedicalReview(IRaCIS.Core.Application.Service.Inspection.DTO.DataInspectionDto{IRaCIS.Core.Application.Service.Reading.Dto.CloseAndFinishMedicalReview})">
<summary> <summary>
医学审核完成 医学审核完成
</summary> </summary>
@@ -325,12 +318,11 @@
<param name="_noneDicomStudyFileRepository"></param> <param name="_noneDicomStudyFileRepository"></param>
<returns></returns> <returns></returns>
</member> </member>
<member name="M:IRaCIS.Core.API.Controllers.StudyController.UploadVisitCheckExcel(System.Guid,System.Boolean,IRaCIS.Core.Application.Helper.IOSSService,IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.InspectionFile})"> <member name="M:IRaCIS.Core.API.Controllers.StudyController.UploadVisitCheckExcel(System.Guid,IRaCIS.Core.Application.Helper.IOSSService,IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.InspectionFile})">
<summary> <summary>
一致性核查 excel上传 支持三种格式 一致性核查 excel上传 支持三种格式
</summary> </summary>
<param name="trialId"></param> <param name="trialId"></param>
<param name="isFullCheck"></param>
<param name="oSSService"></param> <param name="oSSService"></param>
<param name="_inspectionFileRepository"></param> <param name="_inspectionFileRepository"></param>
<returns></returns> <returns></returns>
@@ -351,13 +343,6 @@
</summary> </summary>
<returns></returns> <returns></returns>
</member> </member>
<member name="M:IRaCIS.Core.API.HostService.SyncFileRecoveryService.ExecuteAsync(System.Threading.CancellationToken)">
<summary>
多个程序,如果恢复同一份数据,造成重复同步,SCP服务不恢复任务
</summary>
<param name="stoppingToken"></param>
<returns></returns>
</member>
<member name="T:IRaCIS.Core.API.IpPolicyRateLimitSetup"> <member name="T:IRaCIS.Core.API.IpPolicyRateLimitSetup">
<summary> <summary>
IPLimit限流 启动服务 IPLimit限流 启动服务
+2 -23
View File
@@ -1,8 +1,6 @@
using FellowOakDicom; using IRaCIS.Core.API;
using IRaCIS.Core.API;
using IRaCIS.Core.API.HostService; using IRaCIS.Core.API.HostService;
using IRaCIS.Core.Application.BusinessFilter; using IRaCIS.Core.Application.BusinessFilter;
using IRaCIS.Core.Application.BusinessFilter.LegacyController.Database.Api;
using IRaCIS.Core.Application.Filter; using IRaCIS.Core.Application.Filter;
using IRaCIS.Core.Application.MassTransit.Consumer; using IRaCIS.Core.Application.MassTransit.Consumer;
using IRaCIS.Core.Application.Service; using IRaCIS.Core.Application.Service;
@@ -15,7 +13,6 @@ using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
@@ -24,7 +21,6 @@ using Newtonsoft.Json;
using Serilog; using Serilog;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.IO; using System.IO;
using System.Reflection; using System.Reflection;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
@@ -32,10 +28,6 @@ using System.Runtime.InteropServices;
AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true); AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);
AppContext.SetSwitch("Npgsql.DisableDateTimeInfinityConversions", true); AppContext.SetSwitch("Npgsql.DisableDateTimeInfinityConversions", true);
Console.WriteLine("Startup Culture: " + CultureInfo.CurrentCulture.Name);
Console.WriteLine("Startup UI Culture: " + CultureInfo.CurrentUICulture.Name);
#region #region
//以配置文件为准,否则 从url中取环境值(服务以命令行传递参数启动,配置文件配置了就不需要传递环境参数) //以配置文件为准,否则 从url中取环境值(服务以命令行传递参数启动,配置文件配置了就不需要传递环境参数)
var config = new ConfigurationBuilder() var config = new ConfigurationBuilder()
@@ -65,8 +57,7 @@ SerilogExtension.AddSerilogSetup(enviromentName);
var builder = WebApplication.CreateBuilder(new WebApplicationOptions var builder = WebApplication.CreateBuilder(new WebApplicationOptions
{ {
EnvironmentName = enviromentName, EnvironmentName = enviromentName
Args = args
}); });
#region #region
@@ -91,10 +82,6 @@ builder.Services.ConfigureServices(_configuration);
builder.Services.AddHostedService<HangfireHostService>(); builder.Services.AddHostedService<HangfireHostService>();
builder.Services.AddSingleton<FileSyncQueue>();
builder.Services.AddHostedService<SyncFileRecoveryService>();
builder.Services.AddHostedService<FileSyncWorker>();
//minimal api 异常处理 //minimal api 异常处理
builder.Services.AddExceptionHandler<GlobalExceptionHandler>(); builder.Services.AddExceptionHandler<GlobalExceptionHandler>();
//builder.Services.AddProblemDetails(); //builder.Services.AddProblemDetails();
@@ -108,18 +95,11 @@ builder.Services.AddJsonLocalization(options => options.ResourcesPath = "Resourc
// 异常、参数统一验证过滤器、Json序列化配置、字符串参数绑型统一Trim() // 异常、参数统一验证过滤器、Json序列化配置、字符串参数绑型统一Trim()
builder.Services.AddControllers(options => builder.Services.AddControllers(options =>
{ {
// 关键配置:禁用不可空引用类型的自动 Required 验证
options.SuppressImplicitRequiredAttributeForNonNullableReferenceTypes = true;
// 插到最前,抢在默认绑定器之前
//options.ModelBinderProviders.Insert(0, new SpecificNullableBinderProvider());
options.Filters.Add<ModelActionFilter>(); options.Filters.Add<ModelActionFilter>();
options.Filters.Add<ProjectExceptionFilter>(); options.Filters.Add<ProjectExceptionFilter>();
options.Filters.Add<UnitOfWorkFilter>(); options.Filters.Add<UnitOfWorkFilter>();
options.Filters.Add<LimitUserRequestAuthorization>(); options.Filters.Add<LimitUserRequestAuthorization>();
options.Filters.Add<TrialGlobalLimitActionFilter>(); options.Filters.Add<TrialGlobalLimitActionFilter>();
options.Filters.Add<RequestDuplicationFilter>();
}) })
.AddNewtonsoftJsonSetup(builder.Services); // NewtonsoftJson 序列化 处理 .AddNewtonsoftJsonSetup(builder.Services); // NewtonsoftJson 序列化 处理
@@ -180,7 +160,6 @@ var env = app.Environment;
#region #region
DicomSetupBuilder.UseServiceProvider(app.Services);
app.UseMiddleware<EncryptionRequestMiddleware>(); app.UseMiddleware<EncryptionRequestMiddleware>();
+10 -10
View File
@@ -4,7 +4,7 @@
"windowsAuthentication": false, "windowsAuthentication": false,
"anonymousAuthentication": true, "anonymousAuthentication": true,
"iisExpress": { "iisExpress": {
"applicationUrl": "http://0.0.0.0:3305", "applicationUrl": "http://localhost:3305",
"sslPort": 0 "sslPort": 0
} }
}, },
@@ -23,7 +23,7 @@
"ASPNETCORE_ENVIRONMENT": "Test_IRC", "ASPNETCORE_ENVIRONMENT": "Test_IRC",
"ASPNETCORE_OpenSwagger": "true" "ASPNETCORE_OpenSwagger": "true"
}, },
"applicationUrl": "http://0.0.0.0:6100" "applicationUrl": "http://localhost:6100"
}, },
"IRaCIS.Test_IRC_PGSQL": { "IRaCIS.Test_IRC_PGSQL": {
"commandName": "Project", "commandName": "Project",
@@ -31,15 +31,15 @@
"environmentVariables": { "environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Test_IRC_PGSQL" "ASPNETCORE_ENVIRONMENT": "Test_IRC_PGSQL"
}, },
"applicationUrl": "http://0.0.0.0:6100" "applicationUrl": "http://localhost:6100"
}, },
"IRaCIS.Prod_Event_IRC": { "IRaCIS.Event_IRC": {
"commandName": "Project", "commandName": "Project",
"launchBrowser": true, "launchBrowser": true,
"environmentVariables": { "environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Prod_Event_IRC" "ASPNETCORE_ENVIRONMENT": "Event_IRC"
}, },
"applicationUrl": "http://0.0.0.0:6100" "applicationUrl": "http://localhost:6100"
}, },
"Docker": { "Docker": {
"commandName": "Docker", "commandName": "Docker",
@@ -53,7 +53,7 @@
"environmentVariables": { "environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Uat_IRC" "ASPNETCORE_ENVIRONMENT": "Uat_IRC"
}, },
"applicationUrl": "http://0.0.0.0:6100" "applicationUrl": "http://localhost:6100"
}, },
"IRaCIS.Prod_IRC": { "IRaCIS.Prod_IRC": {
"commandName": "Project", "commandName": "Project",
@@ -61,7 +61,7 @@
"environmentVariables": { "environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Prod_IRC" "ASPNETCORE_ENVIRONMENT": "Prod_IRC"
}, },
"applicationUrl": "http://0.0.0.0:6100" "applicationUrl": "http://localhost:6100"
}, },
"IRaCIS.US_Uat_IRC": { "IRaCIS.US_Uat_IRC": {
"commandName": "Project", "commandName": "Project",
@@ -69,7 +69,7 @@
"environmentVariables": { "environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "US_Uat_IRC" "ASPNETCORE_ENVIRONMENT": "US_Uat_IRC"
}, },
"applicationUrl": "http://0.0.0.0:6100" "applicationUrl": "http://localhost:6100"
}, },
"IRaCIS.US_Prod_IRC": { "IRaCIS.US_Prod_IRC": {
"commandName": "Project", "commandName": "Project",
@@ -77,7 +77,7 @@
"environmentVariables": { "environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "US_Prod_IRC" "ASPNETCORE_ENVIRONMENT": "US_Prod_IRC"
}, },
"applicationUrl": "http://0.0.0.0:6100" "applicationUrl": "http://localhost:6100"
} }
} }
-1
View File
@@ -1,3 +1,2 @@
{ {
} }
@@ -39,8 +39,6 @@ namespace IRaCIS.Core.API._PipelineExtensions.Serilog
var requestBodyPayload = await ReadRequestBody(context.Request); var requestBodyPayload = await ReadRequestBody(context.Request);
context.Items["RequestBody"] = requestBodyPayload;
using (LogContext.PushProperty("RequestBody", requestBodyPayload)) using (LogContext.PushProperty("RequestBody", requestBodyPayload))
{ {
//await _next.Invoke() //await _next.Invoke()
@@ -8,21 +8,13 @@ namespace IRaCIS.Core.API
{ {
public static void AddDicomSetup(this IServiceCollection services) public static void AddDicomSetup(this IServiceCollection services)
{ {
// ⭐ 先做全局 DICOM 配置
new DicomSetupBuilder() new DicomSetupBuilder()
.SkipValidation() // 👈 在这里设置 .RegisterServices(s => s.AddFellowOakDicom()
.AddTranscoderManager<FellowOakDicom.Imaging.NativeCodec.NativeTranscoderManager>()
.AddImageManager<ImageSharpImageManager>()
)
.SkipValidation()
.Build(); .Build();
services.AddFellowOakDicom().AddTranscoderManager<FellowOakDicom.Imaging.NativeCodec.NativeTranscoderManager>().AddImageManager<ImageSharpImageManager>();
// new DicomSetupBuilder()
// .RegisterServices(s => s.AddFellowOakDicom()
//.AddTranscoderManager<FellowOakDicom.Imaging.NativeCodec.NativeTranscoderManager>()
// .AddImageManager<ImageSharpImageManager>()
// )
// .SkipValidation()
// .Build();
} }
} }
} }
@@ -40,12 +40,12 @@ namespace IRaCIS.Core.API
var dbType = configuration.GetSection("ConnectionStrings:Db_Type").Value; var dbType = configuration.GetSection("ConnectionStrings:Db_Type").Value;
if (!string.IsNullOrWhiteSpace(dbType) && dbType == "pgsql") if (!string.IsNullOrWhiteSpace(dbType) && dbType == "pgsql")
{ {
options.UseNpgsql(configuration.GetSection("ConnectionStrings:RemoteNew").Value, contextOptionsBuilder => contextOptionsBuilder.EnableRetryOnFailure().CommandTimeout(90)); options.UseNpgsql(configuration.GetSection("ConnectionStrings:RemoteNew").Value, contextOptionsBuilder => contextOptionsBuilder.EnableRetryOnFailure());
} }
else else
{ {
options.UseSqlServer(configuration.GetSection("ConnectionStrings:RemoteNew").Value, contextOptionsBuilder => contextOptionsBuilder.EnableRetryOnFailure().CommandTimeout(90)); options.UseSqlServer(configuration.GetSection("ConnectionStrings:RemoteNew").Value, contextOptionsBuilder => contextOptionsBuilder.EnableRetryOnFailure());
} }
@@ -71,27 +71,29 @@ namespace IRaCIS.Core.API
DateTime dateTime; DateTime dateTime;
// 2. 检查目标类型是否可空 if (reader.ValueType == typeof(DateTime) || reader.ValueType == typeof(DateTime?))
bool isNullable = objectType == typeof(DateTime?);
var canConvert = DateTime.TryParse(reader.Value?.ToString(), out dateTime);
if (isNullable == false && canConvert == false)
{ {
DateTime? nullableDateTime = reader.Value as DateTime?;
throw new JsonSerializationException($"Could not convert string to DateTime: {reader.Value} Path {reader.Path}");
if (nullableDateTime != null && nullableDateTime.HasValue)
{
dateTime = nullableDateTime.Value;
} }
else else
{ {
if (canConvert == false) return null;
}
}
else
{
if (DateTime.TryParse((string)reader.Value, out dateTime) == false)
{ {
return null; return null;
} }
} }
////如果前端传递带时区的,那么转换会报错,需要DateTimeKind.Unspecified
//dateTime = DateTime.SpecifyKind(dateTime, DateTimeKind.Unspecified);
// 将客户端时间转换为服务器时区的时间 // 将客户端时间转换为服务器时区的时间
var serverZoneTime = TimeZoneInfo.ConvertTime(dateTime, _clientTimeZone, TimeZoneInfo.Local); var serverZoneTime = TimeZoneInfo.ConvertTime(dateTime, _clientTimeZone, TimeZoneInfo.Local);
@@ -111,10 +113,7 @@ namespace IRaCIS.Core.API
//第一个参数默认使用系统本地时区 也就是应用服务器的时区 //第一个参数默认使用系统本地时区 也就是应用服务器的时区
DateTime clientZoneTime = TimeZoneInfo.ConvertTime(nullableDateTime.Value, _clientTimeZone); DateTime clientZoneTime = TimeZoneInfo.ConvertTime(nullableDateTime.Value, _clientTimeZone);
//writer.WriteValue(clientZoneTime);
//// 最简单的方式:创建 DateTimeOffset
//DateTimeOffset dateTimeOffset = new DateTimeOffset(clientZoneTime, _clientTimeZone.GetUtcOffset(clientZoneTime));
//writer.WriteValue(dateTimeOffset.ToString("yyyy-MM-dd HH:mm:sszzz"));
writer.WriteValue(clientZoneTime.ToString(_dateFormat)); writer.WriteValue(clientZoneTime.ToString(_dateFormat));
} }
@@ -3,10 +3,8 @@ using Newtonsoft.Json;
using Newtonsoft.Json.Serialization; using Newtonsoft.Json.Serialization;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Reflection.Metadata;
namespace IRaCIS.Core.API namespace IRaCIS.Core.API
{ {
@@ -53,90 +51,34 @@ namespace IRaCIS.Core.API
} }
public class NullToEmptyStringValueProvider : IValueProvider public class NullToEmptyStringValueProvider : IValueProvider
{ {
PropertyInfo _memberInfo; PropertyInfo _MemberInfo;
private readonly bool _isNullable;
public NullToEmptyStringValueProvider(PropertyInfo memberInfo) public NullToEmptyStringValueProvider(PropertyInfo memberInfo)
{ {
_memberInfo = memberInfo; _MemberInfo = memberInfo;
} }
// DTO → JSON 返回前端的时候 处理null 变为"" 方便前端判断
public object GetValue(object target) public object GetValue(object target)
{ {
var result = _memberInfo.GetValue(target); object result = _MemberInfo.GetValue(target);
// 检查类型是否为string或string? if (_MemberInfo.PropertyType == typeof(string) && result == null) result = "";
if (_memberInfo.PropertyType == typeof(string) && result == null) 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;
#region string string? null ""
//// 如果是string?类型,返回null 可以做到,但是得反射,因为 string string? 是编译层区分的
//var isNullable1 = _memberInfo.CustomAttributes
// .Any(a => a.AttributeType.Name == "NullableAttribute");
////var isNullable2 = _memberInfo.CustomAttributes
//// .Any(a => a.AttributeType.FullName == "System.Runtime.CompilerServices.NullableAttribute");
//if (isNullable1)
//{
// return result;
//}
#endregion
// 如果是string类型,返回空字符串
return string.Empty;
}
return result; return result;
} }
//影响 模型绑定时接收前端的值,同时影响 正常 JSON 序列化
public void SetValue(object target, object value) public void SetValue(object target, object value)
{ {
_memberInfo.SetValue(target, value); if (_MemberInfo.PropertyType == typeof(string))
{
#region string null //去掉前后空格
_MemberInfo.SetValue(target, value == null ? string.Empty : value.ToString() == string.Empty ? value : value/*.ToString().Trim()*/);
//if (_memberInfo.PropertyType == typeof(string))
//{
// _memberInfo.SetValue(target, value == null ? string.Empty : value);
//}
//else
//{
// _memberInfo.SetValue(target, value);
//}
#endregion
#region string string?
//////接收模型的时候 定义的明明是string 但是上面也有该属性,判断不准的 比如阅片跟踪列表查询
//var isNullable1 = _memberInfo.CustomAttributes.Any(a => a.AttributeType.Name == "NullableAttribute");
////不影响 string? 传递null 变为""
//if (_memberInfo.PropertyType == typeof(string) && isNullable1 == false)
//{
// //如果不处理 前段传递null string不会接收,说前段没传递会验证报错
// _memberInfo.SetValue(target, value == null ? string.Empty : value);
//}
//else
//{
// _memberInfo.SetValue(target, value);
//}
#endregion
}
else
{
_MemberInfo.SetValue(target, value);
}
} }
} }
@@ -35,7 +35,7 @@ namespace IRaCIS.Core.API
.Enrich.FromLogContext() .Enrich.FromLogContext()
.Filter.ByExcluding(logEvent => logEvent.Properties.ContainsKey("RequestPath") && logEvent.Properties["RequestPath"].ToString().Contains("/health")) .Filter.ByExcluding(logEvent => logEvent.Properties.ContainsKey("RequestPath") && logEvent.Properties["RequestPath"].ToString().Contains("/health"))
.WriteTo.Console() .WriteTo.Console()
.WriteTo.File($"{AppContext.BaseDirectory}Serilogs/.log", rollingInterval: RollingInterval.Day,retainedFileCountLimit:365); .WriteTo.File($"{AppContext.BaseDirectory}Serilogs/.log", rollingInterval: RollingInterval.Day,retainedFileCountLimit:60);
#region #region
@@ -46,19 +46,19 @@ namespace IRaCIS.Core.API
var emailConfig = new SystemEmailSendConfig(); var emailConfig = new SystemEmailSendConfig();
configuration.GetSection("SystemEmailSendConfig").Bind(emailConfig); configuration.GetSection("SystemEmailSendConfig").Bind(emailConfig);
//if (emailConfig.IsOpenErrorNoticeEmail) if (emailConfig.IsOpenErrorNoticeEmail)
//{ {
// config.WriteTo.Email(options: new Serilog.Sinks.Email.EmailSinkOptions() config.WriteTo.Email(options: new Serilog.Sinks.Email.EmailSinkOptions()
// { {
// From = emailConfig.FromEmail, From = emailConfig.FromEmail,
// To = emailConfig.ErrorNoticeEmailList, To = emailConfig.ErrorNoticeEmailList,
// Host = emailConfig.Host, Host = emailConfig.Host,
// Port = emailConfig.Port, Port = emailConfig.Port,
// Subject = new MessageTemplateTextFormatter("Log Alert - 系统发生了异常,请核查"), Subject = new MessageTemplateTextFormatter("Log Alert - 系统发生了异常,请核查"),
// Credentials = new NetworkCredential(emailConfig.FromEmail, emailConfig.AuthorizationCode) Credentials = new NetworkCredential(emailConfig.FromEmail, emailConfig.AuthorizationCode)
// }, restrictedToMinimumLevel: Serilog.Events.LogEventLevel.Error); }, restrictedToMinimumLevel: Serilog.Events.LogEventLevel.Error);
//} }
#endregion #endregion
Log.Logger = config.CreateLogger(); Log.Logger = config.CreateLogger();
@@ -28,13 +28,12 @@ public static class ServiceCollectionSetup
services.AddOptions().Configure<AliyunOSSOptions>(_configuration.GetSection("AliyunOSS")); services.AddOptions().Configure<AliyunOSSOptions>(_configuration.GetSection("AliyunOSS"));
services.AddOptions().Configure<ObjectStoreServiceOptions>(_configuration.GetSection("ObjectStoreService")); services.AddOptions().Configure<ObjectStoreServiceOptions>(_configuration.GetSection("ObjectStoreService"));
services.AddOptions().Configure<IRCEncreptOption>(_configuration.GetSection("EncrypteResponseConfig")); services.AddOptions().Configure<IRCEncreptOption>(_configuration.GetSection("EncrypteResponseConfig"));
services.AddOptions().Configure<RequestDuplicationOptions>(_configuration.GetSection("RequestDuplicationOptions"));
services.AddOptions().Configure<SystemPacsConfig>(_configuration.GetSection("SystemPacsConfig")); services.AddOptions().Configure<SystemPacsConfig>(_configuration.GetSection("SystemPacsConfig"));
services.Configure<IRaCISBasicConfigOption>(_configuration.GetSection("IRaCISBasicConfig")); services.Configure<IRaCISBasicConfigOption>(_configuration.GetSection("IRaCISBasicConfig"));
services.Configure<ServiceVerifyConfigOption>(_configuration.GetSection("BasicSystemConfig")); services.Configure<ServiceVerifyConfigOption>(_configuration.GetSection("BasicSystemConfig"));
//תͷ ȡʵIP //转发头设置 获取真实IP
services.Configure<ForwardedHeadersOptions>(options => services.Configure<ForwardedHeadersOptions>(options =>
{ {
options.ForwardedHeaders = options.ForwardedHeaders =
@@ -52,7 +51,7 @@ public static class ServiceCollectionSetup
services.AddScoped<IObtainTaskAutoCancelJob, ObtainTaskAutoCancelJob>(); services.AddScoped<IObtainTaskAutoCancelJob, ObtainTaskAutoCancelJob>();
// עService βķ // 注册以Service 结尾的服务
services.Scan(scan => scan services.Scan(scan => scan
.FromAssemblies(typeof(BaseService).Assembly) .FromAssemblies(typeof(BaseService).Assembly)
.AddClasses(classes => classes.Where(t => t.Name.Contains("Service"))) .AddClasses(classes => classes.Where(t => t.Name.Contains("Service")))
@@ -71,23 +70,23 @@ public static class ServiceCollectionSetup
#endregion #endregion
// MediatR Ϣ ¼ ӳ עhandlerӦϵ // MediatR 进程内消息 事件解耦 从程序集中 注册命令和handler对应关系
//builder.Services.AddMediatR(cfg => cfg.RegisterServicesFromAssemblyContaining<ConsistencyVerificationHandler>()); //builder.Services.AddMediatR(cfg => cfg.RegisterServicesFromAssemblyContaining<ConsistencyVerificationHandler>());
#region ʷ #region
//builder.Services.AddMemoryCache(); //builder.Services.AddMemoryCache();
////ϴ ////上传限制 配置
//builder.Services.Configure<FormOptions>(options => //builder.Services.Configure<FormOptions>(options =>
//{ //{
// options.MultipartBodyLengthLimit = int.MaxValue; // options.MultipartBodyLengthLimit = int.MaxValue;
// options.ValueCountLimit = int.MaxValue; // options.ValueCountLimit = int.MaxValue;
// options.ValueLengthLimit = int.MaxValue; // options.ValueLengthLimit = int.MaxValue;
//}); //});
//IP ð ߺ //IP 限流 可设置白名单 或者黑名单
//services.AddIpPolicyRateLimitSetup(_configuration); //services.AddIpPolicyRateLimitSetup(_configuration);
// û Ȩ // 用户类型 策略授权
//services.AddAuthorizationPolicySetup(_configuration); //services.AddAuthorizationPolicySetup(_configuration);
#endregion #endregion
} }
@@ -95,25 +94,25 @@ public static class ServiceCollectionSetup
} }
#region Autofac #region Autofac
//public class AutofacModuleSetup : Autofac.Module //public class AutofacModuleSetup : Autofac.Module
//{ //{
// protected override void Load(ContainerBuilder containerBuilder) // protected override void Load(ContainerBuilder containerBuilder)
// { // {
// #region byzhouhang 20210917 ˴עͲִ ԼDomain Infra.EFcore յIJִӿڶ ִļ // #region byzhouhang 20210917 此处注册泛型仓储 可以减少Domain层 和Infra.EFcore 两层 空的仓储接口定义和 仓储文件定义
// containerBuilder.RegisterGeneric(typeof(Repository<>)) // containerBuilder.RegisterGeneric(typeof(Repository<>))
// .As(typeof(IRepository<>)).InstancePerLifetimeScope();//עͲִ // .As(typeof(IRepository<>)).InstancePerLifetimeScope();//注册泛型仓储
// containerBuilder.RegisterType<Repository>().As<IRepository>().InstancePerLifetimeScope(); // containerBuilder.RegisterType<Repository>().As<IRepository>().InstancePerLifetimeScope();
// #endregion // #endregion
// #region ָҲautofac ʵȡ https://www.cnblogs.com/xwhqwer/p/15320838.html // #region 指定控制器也由autofac 来进行实例获取 https://www.cnblogs.com/xwhqwer/p/15320838.html
// //ȡпͲʹע // //获取所有控制器类型并使用属性注入
// containerBuilder.RegisterAssemblyTypes(typeof(BaseService).Assembly) // containerBuilder.RegisterAssemblyTypes(typeof(BaseService).Assembly)
// .Where(type => typeof(IDynamicWebApi).IsAssignableFrom(type)) // .Where(type => typeof(IDynamicWebApi).IsAssignableFrom(type))
// .PropertiesAutowired(); // .PropertiesAutowired();
@@ -129,7 +128,7 @@ public static class ServiceCollectionSetup
// //containerBuilder.RegisterType<UserInfo>().As<IUserInfo>().InstancePerLifetimeScope(); // //containerBuilder.RegisterType<UserInfo>().As<IUserInfo>().InstancePerLifetimeScope();
// //עhangfire ע // //注册hangfire任务 依赖注入
// containerBuilder.RegisterType<ObtainTaskAutoCancelJob>().As<IObtainTaskAutoCancelJob>().InstancePerDependency(); // containerBuilder.RegisterType<ObtainTaskAutoCancelJob>().As<IObtainTaskAutoCancelJob>().InstancePerDependency();
// } // }
@@ -1,4 +1,4 @@
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
@@ -116,9 +116,6 @@ public static class SwaggerSetup
//DocExpansion设置为none可折叠所有方法 //DocExpansion设置为none可折叠所有方法
options.DocExpansion(DocExpansion.None); options.DocExpansion(DocExpansion.None);
// 开启Swagger UI的搜索/过滤功能
options.EnableFilter();
//DefaultModelsExpandDepth设置为 - 1 可不显示models //DefaultModelsExpandDepth设置为 - 1 可不显示models
options.DefaultModelsExpandDepth(-1); options.DefaultModelsExpandDepth(-1);
}); });
@@ -0,0 +1,75 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"ConnectionStrings": {
"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",
"AliyunOSS": {
"RegionId": "cn-shanghai",
"InternalEndpoint": "https://oss-cn-shanghai-internal.aliyuncs.com",
"EndPoint": "https://oss-cn-shanghai.aliyuncs.com",
"AccessKeyId": "LTAI5tFUCCmz5TwghZHsj45Y",
"AccessKeySecret": "8evrBy1fVfzJG25i67Jm0xqn9Xcw2T",
"RoleArn": "acs:ram::1078130221702011:role/uat-oss-access",
"BucketName": "tl-med-irc-event-store",
"ViewEndpoint": "https://tl-med-irc-event-store.oss-cn-shanghai.aliyuncs.com",
"Region": "oss-cn-shanghai",
"DurationSeconds": 7200
},
"MinIO": {
"endpoint": "http://192.168.3.68",
"port": "8001",
"useSSL": false,
"accessKey": "IDFkwEpWej0b4DtiuThL",
"secretKey": "Lhuu83yMhVwu7c1SnjvGY6lq74jzpYqifK6Qtj4h",
"bucketName": "test"
}
},
"BasicSystemConfig": {
"OpenUserComplexPassword": true,
"OpenSignDocumentBeforeWork": true,
"OpenLoginLimit": true,
"LoginMaxFailCount": 5,
"LoginFailLockMinutes": 30,
"AutoLoginOutMinutes": 60,
"ContinuousReadingTimeMin": 120,
"ReadingRestTimeMin": 10,
"IsNeedChangePassWord": true,
"ChangePassWordDays": 90,
// 模板类型 1 Elevate 2 Extensive
"TemplateType": 2
},
"SystemEmailSendConfig": {
"Port": 465,
"Host": "smtp.qiye.aliyun.com",
"FromEmail": "uat@extimaging.com",
"FromName": "UAT_IRC",
"AuthorizationCode": "SHzyyl2021",
"SiteUrl": "http://irc.event.extimaging.com/login",
"CompanyName": "Extensive Imaging",
"CompanyNameCN": "上海展影医疗科技有限公司",
"CompanyShortName": "Extensive Imaging",
"CompanyShortNameCN": "展影医疗",
"IsEnv_US": false
}
}
@@ -1,125 +0,0 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"ConnectionStrings": {
"RemoteNew": "Server=101.132.193.237,1433;Database=Prod_IRC_pidr_restore;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
"Hangfire": "Server=101.132.193.237,1433;Database=irc_Hangfire_bak;User ID=sa;Password=xc@123456;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"
},
"WeComNoticeConfig": {
"IsOpenWeComNotice": true,
"WebhookUrl": "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=4355b98e-1e72-4678-8dfb-2fc6ad0bf449", //4355b98e-1e72-4678-8dfb-2fc6ad0bf449 //cdd97aab-d256-4f07-9145-a0a2b1555322
"APINoticeUserList": [ "u", "wait..." ],
"VueNoticeUserList": [ "wangxiaoshuang", "6b7717a31647293621b97b96f74e6f3d" ]
},
"ObjectStoreService": {
"ObjectStoreUse": "AliyunOSS",
"IsOpenStoreSync": true,
"ApiDeployRegion": "CN",
"SyncConfigList": [
{
"Domain": "irc.extimaging.com",
"Primary": "AliyunOSS",
"Target": "AWS",
"UploadRegion": "CN",
"TargetRegion": "US",
"IsOpenSync": true
},
{
"Domain": "eiirc.extimaging.com",
"Primary": "AWS",
"Target": "AliyunOSS",
"UploadRegion": "US",
"TargetRegion": "CN",
"IsOpenSync": true
}
],
"AliyunOSS": {
"RegionId": "cn-shanghai",
"InternalEndpoint": "https://oss-cn-shanghai-internal.aliyuncs.com",
"EndPoint": "https://oss-cn-shanghai.aliyuncs.com",
"AccessKeyId": "LTAI5tNRTsqL6aWmHkDmTwoH",
"AccessKeySecret": "7mtGz3qrYWI6JMMBZiLeC119VWicZH",
"RoleArn": "acs:ram::1899121822495495:role/irc-oss-access",
"BucketName": "zy-irc-store",
"ViewEndpoint": "https://zy-irc-cache.oss-cn-shanghai.aliyuncs.com",
"Region": "oss-cn-shanghai",
"DurationSeconds": 7200
},
"AWS": {
"Region": "us-east-1",
"EndPoint": "s3.us-east-1.amazonaws.com",
"UseSSL": true,
"RoleArn": "arn:aws:iam::471112624751:role/lili_s3_access",
"AccessKeyId": "AKIAW3MEAFJXZ2TZK7GM",
"SecretAccessKey": "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",
"BucketName": "ei-med-s3-irc-store",
"ViewEndpoint": "https://ei-med-s3-irc-store.s3.amazonaws.com",
"DurationSeconds": 7200
}
},
"BasicSystemConfig": {
// 启用质控风险控制功能
"QCRiskControl": true,
"OpenUserComplexPassword": true,
"OpenSignDocumentBeforeWork": true,
"OpenLoginLimit": true,
"LoginMaxFailCount": 5,
"LoginFailLockMinutes": 30,
"AutoLoginOutMinutes": 120,
"OpenLoginMFA": false,
"ContinuousReadingTimeMin": 120,
"ReadingRestTimeMin": 10,
"IsNeedChangePassWord": true,
"ChangePassWordDays": 90,
// 模板类型 1 Elevate 2 Extensive
"TemplateType": 2,
//MFA免验证发送天数
"UserMFAVerifyMinutes": 1440
},
"SystemEmailSendConfig": {
"Port": 465,
"Host": "smtp.qiye.aliyun.com",
"Imap": "imap.qiye.aliyun.com",
"ImapPort": 993,
"FromEmail": "irc@extimaging.com",
"FromName": "IRC Imaging System",
"AuthorizationCode": "ExtImg@2022",
"SiteUrl": "http://irc.extimaging.com/login",
"PlatformName": "EICS",
"PlatformNameCN": "展影云平台",
"SystemShortName": "IRC",
"OrganizationName": "ExtImaging",
"OrganizationNameCN": "ExtImaging",
"CompanyName": "Extensive Imaging",
"CompanyNameCN": "上海展影医疗科技有限公司",
"CompanyShortName": "Extensive Imaging",
"CompanyShortNameCN": "展影医疗",
"IsEnv_US": false,
"EmailRegexStr": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$",
"CronEmailDefaultCulture": "zh-CN"
},
"SystemPacsConfig": {
"Port": "11113",
"IP": "101.132.193.237"
},
"RequestDuplicationOptions": {
"IsEnabled": true,
"DuplicationWindowMs": 200,
"CacheTimeSeconds": 5,
"ExcludedPaths": [
]
}
}
+16 -58
View File
@@ -7,39 +7,13 @@
} }
}, },
"ConnectionStrings": { "ConnectionStrings": {
"RemoteNew": "Server=10.10.10.49,1434;Database=Prod_IRC;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=10.10.10.49,1434;Database=Prod_IRC_Hangfire;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", //"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" //"Hangfire": "Server=prod_mssql_standard,1433;Database=Prod_IRC_Hangfire;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true"
}, },
"WeComNoticeConfig": {
"IsOpenWeComNotice": true,
"WebhookUrl": "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=4355b98e-1e72-4678-8dfb-2fc6ad0bf449", //4355b98e-1e72-4678-8dfb-2fc6ad0bf449 //cdd97aab-d256-4f07-9145-a0a2b1555322
"APINoticeUserList": [ "u", "wait..." ],
"VueNoticeUserList": [ "wangxiaoshuang", "6b7717a31647293621b97b96f74e6f3d" ]
},
"ObjectStoreService": { "ObjectStoreService": {
"ObjectStoreUse": "AliyunOSS", "ObjectStoreUse": "AliyunOSS",
"IsOpenStoreSync": true,
"ApiDeployRegion": "CN",
"SyncConfigList": [
{
"Domain": "irc.extimaging.com",
"Primary": "AliyunOSS",
"Target": "AWS",
"UploadRegion": "CN",
"TargetRegion": "US",
"IsOpenSync": true
},
{
"Domain": "eiirc.extimaging.com",
"Primary": "AWS",
"Target": "AliyunOSS",
"UploadRegion": "US",
"TargetRegion": "CN",
"IsOpenSync": true
}
],
"AliyunOSS": { "AliyunOSS": {
"RegionId": "cn-shanghai", "RegionId": "cn-shanghai",
"InternalEndpoint": "https://oss-cn-shanghai-internal.aliyuncs.com", "InternalEndpoint": "https://oss-cn-shanghai-internal.aliyuncs.com",
@@ -52,21 +26,17 @@
"Region": "oss-cn-shanghai", "Region": "oss-cn-shanghai",
"DurationSeconds": 7200 "DurationSeconds": 7200
}, },
"AWS": { "MinIO": {
"Region": "us-east-1", "endpoint": "http://192.168.3.68",
"EndPoint": "s3.us-east-1.amazonaws.com", "port": "8001",
"UseSSL": true, "useSSL": false,
"RoleArn": "arn:aws:iam::471112624751:role/lili_s3_access", "accessKey": "IDFkwEpWej0b4DtiuThL",
"AccessKeyId": "AKIAW3MEAFJXZ2TZK7GM", "secretKey": "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",
"SecretAccessKey": "9MLQCQ1HifEVW1gf068zBRAOb4wNnfrOkvBVByth", "bucketName": "test"
"BucketName": "ei-med-s3-irc-store",
"ViewEndpoint": "https://ei-med-s3-irc-store.s3.amazonaws.com",
"DurationSeconds": 7200
} }
}, },
"BasicSystemConfig": { "BasicSystemConfig": {
// 启用质控风险控制功能
"QCRiskControl": true,
"OpenUserComplexPassword": true, "OpenUserComplexPassword": true,
"OpenSignDocumentBeforeWork": true, "OpenSignDocumentBeforeWork": true,
@@ -83,43 +53,31 @@
"IsNeedChangePassWord": true, "IsNeedChangePassWord": true,
"ChangePassWordDays": 90, "ChangePassWordDays": 90,
// 模板类型 1 Elevate 2 Extensive // 模板类型 1 Elevate 2 Extensive
"TemplateType": 2, "TemplateType": 2
//MFA免验证发送天数
"UserMFAVerifyMinutes": 1440
}, },
"SystemEmailSendConfig": { "SystemEmailSendConfig": {
"Port": 465, "Port": 465,
"Host": "smtp.qiye.aliyun.com", "Host": "smtp.qiye.aliyun.com",
"Imap": "imap.qiye.aliyun.com",
"ImapPort": 993,
"FromEmail": "irc@extimaging.com", "FromEmail": "irc@extimaging.com",
"FromName": "IRC Imaging System", "FromName": "IRC Imaging System",
"AuthorizationCode": "ExtImg@2022", "AuthorizationCode": "ExtImg@2022",
"SiteUrl": "http://irc.extimaging.com/login", "SiteUrl": "http://irc.extimaging.com/login",
"PlatformName": "EICS",
"PlatformNameCN": "展影云平台",
"SystemShortName": "IRC", "SystemShortName": "IRC",
"OrganizationName": "ExtImaging", "OrganizationName": "Extlmaging",
"OrganizationNameCN": "ExtImaging", "OrganizationNameCN": "Extlmaging",
"CompanyName": "Extensive Imaging", "CompanyName": "Extensive Imaging",
"CompanyNameCN": "上海展影医疗科技有限公司", "CompanyNameCN": "上海展影医疗科技有限公司",
"CompanyShortName": "Extensive Imaging", "CompanyShortName": "Extensive Imaging",
"CompanyShortNameCN": "展影医疗", "CompanyShortNameCN": "展影医疗",
"IsEnv_US": false, "IsEnv_US": false,
"IsOpenErrorNoticeEmail": false,
"EmailRegexStr": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$", "EmailRegexStr": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$",
"CronEmailDefaultCulture": "zh-CN" "ErrorNoticeEmailList": [ "872297557@qq.com" ]
}, },
"SystemPacsConfig": { "SystemPacsConfig": {
"Port": "11113", "Port": "11113",
"IP": "101.132.193.237" "IP": "101.132.193.237"
},
"RequestDuplicationOptions": {
"IsEnabled": true,
"DuplicationWindowMs": 200,
"CacheTimeSeconds": 5,
"ExcludedPaths": [
]
} }
} }
+13 -64
View File
@@ -18,36 +18,10 @@
// Hangfire 定时任务数据库链接字符串 // Hangfire 定时任务数据库链接字符串
"Hangfire": "Server=106.14.89.110,1435;Database=Test_IRC_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true" "Hangfire": "Server=106.14.89.110,1435;Database=Test_IRC_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
}, },
"WeComNoticeConfig": {
"IsOpenWeComNotice": true,
"WebhookUrl": "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=4355b98e-1e72-4678-8dfb-2fc6ad0bf449", //4355b98e-1e72-4678-8dfb-2fc6ad0bf449 //cdd97aab-d256-4f07-9145-a0a2b1555322
"APINoticeUserList": [ "u", "wait..." ],
"VueNoticeUserList": [ "wangxiaoshuang", "6b7717a31647293621b97b96f74e6f3d" ]
},
// 对象存储服务配置 // 对象存储服务配置
"ObjectStoreService": { "ObjectStoreService": {
// 使用的对象存储服务类型 // 使用的对象存储服务类型
"ObjectStoreUse": "AliyunOSS", "ObjectStoreUse": "AliyunOSS",
"IsOpenStoreSync": true,
"ApiDeployRegion": "CN",
"SyncConfigList": [
{
"Domain": "irc.test.extimaging.com",
"Primary": "AliyunOSS",
"Target": "AWS",
"UploadRegion": "CN",
"TargetRegion": "US",
"IsOpenSync": true
},
{
"Domain": "lili.test.extimaging.com",
"Primary": "AWS",
"Target": "AliyunOSS",
"UploadRegion": "US",
"TargetRegion": "CN",
"IsOpenSync": true
}
],
// 阿里云对象存储服务的配置 // 阿里云对象存储服务的配置
"AliyunOSS": { "AliyunOSS": {
// 阿里云 OSS 的 Region ID // 阿里云 OSS 的 Region ID
@@ -114,8 +88,6 @@
}, },
// 系统配置 // 系统配置
"BasicSystemConfig": { "BasicSystemConfig": {
// 启用质控风险控制功能
"QCRiskControl": true,
// 打开用户复杂密码 // 打开用户复杂密码
"OpenUserComplexPassword": false, "OpenUserComplexPassword": false,
// 是否在开始工作前强制签署电子知情同意书 // 是否在开始工作前强制签署电子知情同意书
@@ -138,26 +110,19 @@
"IsNeedChangePassWord": true, "IsNeedChangePassWord": true,
// 密码有效期(天),到期后必须修改 // 密码有效期(天),到期后必须修改
"ChangePassWordDays": 90, "ChangePassWordDays": 90,
// 模板类型 1 ElevateLiLi 2 Extensive(展影) // 模板类型 1 Elevate 2 Extensive
"TemplateType": 2, "TemplateType": 2,
// 是否打开项目关联删除 // 是否打开项目关联删除
"OpenTrialRelationDelete": true, "OpenTrialRelationDelete": true,
// 转换PDF服务配置 // 转换PDF服务配置
"ThirdPdfUrl": "http://106.14.89.110:30088/api/v1/convert/file/pdf", "ThirdPdfUrl": "http://106.14.89.110:30088/api/v1/convert/file/pdf"
//MFA免验证发送天数
"UserMFAVerifyMinutes": 1440
}, },
// 邮件服务配置(用于系统通知、找回密码、错误报警等) // 邮件服务配置(用于系统通知、找回密码、错误报警等)
"SystemEmailSendConfig": { "SystemEmailSendConfig": {
// 企业邮箱SMTP服务器地址
"Host": "smtp.qiye.aliyun.com",
// SMTP端口 // SMTP端口
"Port": 465, "Port": 465,
// 企业邮箱SMTP服务器地址
"Imap": "imap.qiye.aliyun.com", "Host": "smtp.qiye.aliyun.com",
"ImapPort": 993,
// 发件人邮箱地址 // 发件人邮箱地址
"FromEmail": "test@extimaging.com", "FromEmail": "test@extimaging.com",
// 发件人显示名称 // 发件人显示名称
@@ -166,17 +131,12 @@
"AuthorizationCode": "SHzyyl2021", "AuthorizationCode": "SHzyyl2021",
// 系统对外访问地址 // 系统对外访问地址
"SiteUrl": "http://irc.test.extimaging.com/login", "SiteUrl": "http://irc.test.extimaging.com/login",
// 系统简称
// 系统简称 - 一致性核查使用
"SystemShortName": "IRC", "SystemShortName": "IRC",
// 组织英文名称-添加用户默认值 // 组织英文名称
"OrganizationName": "ExtImaging", "OrganizationName": "Extlmaging",
// 组织中文名称 // 组织中文名称
"OrganizationNameCN": "ExtImaging", "OrganizationNameCN": "Extlmaging",
"PlatformName": "EICS",
"PlatformNameCN": "展影云平台",
// 公司英文全称 // 公司英文全称
"CompanyName": "Extensive Imaging", "CompanyName": "Extensive Imaging",
// 公司中文全称 // 公司中文全称
@@ -185,14 +145,14 @@
"CompanyShortName": "Extensive Imaging", "CompanyShortName": "Extensive Imaging",
// 公司中文简称 // 公司中文简称
"CompanyShortNameCN": "展影医疗", "CompanyShortNameCN": "展影医疗",
// 是否为国际版环境
// 是否为国际版环境 方便前端区分,用于区分lili 还是irc 做一些事情
"IsEnv_US": false, "IsEnv_US": false,
// 是否开启系统异常邮件报警
"IsOpenErrorNoticeEmail": false,
// 邮箱格式校验正则表达式 // 邮箱格式校验正则表达式
"EmailRegexStr": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$", "EmailRegexStr": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$",
// 接收系统异常报警的邮箱列表
//后端周期性邮件默认语言 "ErrorNoticeEmailList": [ "872297557@qq.com" ]
"CronEmailDefaultCulture": "zh-CN"
}, },
// PACS 连接配置 // PACS 连接配置
"SystemPacsConfig": { "SystemPacsConfig": {
@@ -200,16 +160,5 @@
"Port": "11113", "Port": "11113",
// PACS服务器IP地址 // PACS服务器IP地址
"IP": "106.14.89.110" "IP": "106.14.89.110"
},
// 重复请求配置
"RequestDuplicationOptions": {
// 是否启用重复请求检测
"IsEnabled": true,
// 重复请求时间窗口(毫秒)
"DuplicationWindowMs": 200,
// 缓存请求时间(秒)
"CacheTimeSeconds": 5,
"ExcludedPaths": [
]
} }
} }
@@ -47,8 +47,7 @@
}, },
"BasicSystemConfig": { "BasicSystemConfig": {
// 启用质控风险控制功能
"QCRiskControl": true,
"OpenUserComplexPassword": false, "OpenUserComplexPassword": false,
"OpenSignDocumentBeforeWork": false, "OpenSignDocumentBeforeWork": false,
@@ -76,15 +75,13 @@
"SystemEmailSendConfig": { "SystemEmailSendConfig": {
"Port": 465, "Port": 465,
"Host": "smtp.qiye.aliyun.com", "Host": "smtp.qiye.aliyun.com",
"Imap": "imap.qiye.aliyun.com",
"ImapPort": 993,
"FromEmail": "test@extimaging.com", "FromEmail": "test@extimaging.com",
"FromName": "Test_IRC", "FromName": "Test_IRC",
"AuthorizationCode": "SHzyyl2021", "AuthorizationCode": "SHzyyl2021",
"SiteUrl": "http://irc.test.extimaging.com/login", "SiteUrl": "http://irc.test.extimaging.com/login",
"OrganizationName": "ExtImaging", "OrganizationName": "Extlmaging",
"OrganizationNameCN": "ExtImaging", "OrganizationNameCN": "Extlmaging",
"CompanyName": "Extensive Imaging", "CompanyName": "Extensive Imaging",
"CompanyNameCN": "上海展影医疗科技有限公司", "CompanyNameCN": "上海展影医疗科技有限公司",
"CompanyShortName": "Extensive Imaging", "CompanyShortName": "Extensive Imaging",
@@ -94,12 +91,5 @@
"SystemPacsConfig": { "SystemPacsConfig": {
"Port": "11113", "Port": "11113",
"IP": "106.14.89.110" "IP": "106.14.89.110"
},
"RequestDuplicationOptions": {
"IsEnabled": true,
"DuplicationWindowMs": 200,
"CacheTimeSeconds": 5,
"ExcludedPaths": [
]
} }
} }
+14 -55
View File
@@ -12,46 +12,19 @@
//"RemoteNew": "Server=44.210.231.169,1435;Database=US_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true", //"RemoteNew": "Server=44.210.231.169,1435;Database=US_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
//"Hangfire": "Server=44.210.231.169,1435;Database=US_IRC_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true", //"Hangfire": "Server=44.210.231.169,1435;Database=US_IRC_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
}, },
"WeComNoticeConfig": {
"IsOpenWeComNotice": true,
"WebhookUrl": "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=4355b98e-1e72-4678-8dfb-2fc6ad0bf449", //4355b98e-1e72-4678-8dfb-2fc6ad0bf449 //cdd97aab-d256-4f07-9145-a0a2b1555322
"APINoticeUserList": [ "u", "wait..." ],
"VueNoticeUserList": [ "wangxiaoshuang", "6b7717a31647293621b97b96f74e6f3d" ]
},
"ObjectStoreService": { "ObjectStoreService": {
"ObjectStoreUse": "AWS", "ObjectStoreUse": "AWS",
"IsOpenStoreSync": true, "MinIO": {
"ApiDeployRegion": "US", "endPoint": "44.210.231.169",
"SyncConfigList": [ "port": "9001",
{ "useSSL": false,
"Domain": "eilili.elevateimaging.ai", "accessKey": "e9bT1isTOqSAUxb6wd4n",
"Primary": "AliyunOSS", "secretKey": "b5TaDzNdQCBtCvfm8eZ3dR6yY7tfZu2JYze2Po1i",
"Target": "AWS", "bucketName": "prod-irc-us",
"UploadRegion": "CN", "viewEndpoint": "http://44.210.231.169:9001/prod-irc-us/"
"TargetRegion": "US",
"IsOpenSync": true
},
{
"Domain": "lili.elevateimaging.ai",
"Primary": "AWS",
"Target": "AliyunOSS",
"UploadRegion": "US",
"TargetRegion": "CN",
"IsOpenSync": true
}
],
"AliyunOSS": {
"RegionId": "cn-shanghai",
"InternalEndpoint": "https://oss-cn-shanghai-internal.aliyuncs.com",
"EndPoint": "https://oss-cn-shanghai.aliyuncs.com",
"AccessKeyId": "LTAI5tNRTsqL6aWmHkDmTwoH",
"AccessKeySecret": "7mtGz3qrYWI6JMMBZiLeC119VWicZH",
"RoleArn": "acs:ram::1899121822495495:role/irc-oss-access",
"BucketName": "zy-lili-store",
"ViewEndpoint": "https://zy-lili-cache.oss-cn-shanghai.aliyuncs.com",
"Region": "oss-cn-shanghai",
"DurationSeconds": 7200
}, },
"AWS": { "AWS": {
"Region": "us-east-1", "Region": "us-east-1",
"EndPoint": "s3.us-east-1.amazonaws.com", "EndPoint": "s3.us-east-1.amazonaws.com",
@@ -65,8 +38,7 @@
} }
}, },
"BasicSystemConfig": { "BasicSystemConfig": {
// 启用质控风险控制功能
"QCRiskControl": true,
"OpenUserComplexPassword": true, "OpenUserComplexPassword": true,
"OpenSignDocumentBeforeWork": true, "OpenSignDocumentBeforeWork": true,
@@ -85,24 +57,17 @@
// 模板类型 1 Elevate 2 Extensive // 模板类型 1 Elevate 2 Extensive
"TemplateType": 1, "TemplateType": 1,
"OpenTrialRelationDelete": false, "OpenTrialRelationDelete": false
//MFA免验证
"UserMFAVerifyMinutes": 1440
}, },
"SystemEmailSendConfig": { "SystemEmailSendConfig": {
"Port": 587, "Port": 587,
"Host": "smtp-mail.outlook.com", "Host": "smtp-mail.outlook.com",
"Imap": "imap-mail.outlook.com",
"ImapPort": 993,
"FromEmail": "donotreply@elevateimaging.ai", "FromEmail": "donotreply@elevateimaging.ai",
"FromName": "LiLi System", "FromName": "LiLi System",
"AuthorizationCode": "Q#669869497420ul", "AuthorizationCode": "Q#669869497420ul",
"PlatformName": "LiLi",
"PlatformNameCN": "LiLi",
"SystemShortName": "LiLi", "SystemShortName": "LiLi",
"OrganizationName": "Elevate Imaging", "OrganizationName": "Elevate Imaging",
"OrganizationNameCN": "Elevate Imaging", "OrganizationNameCN": "Elevate Imaging",
@@ -112,20 +77,14 @@
"CompanyShortNameCN": "展影医疗", "CompanyShortNameCN": "展影医疗",
"SiteUrl": "https://lili.elevateimaging.ai/login", "SiteUrl": "https://lili.elevateimaging.ai/login",
"IsEnv_US": true, "IsEnv_US": true,
"IsOpenErrorNoticeEmail": false,
"EmailRegexStr": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$", "EmailRegexStr": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$",
"CronEmailDefaultCulture": "en-US" "ErrorNoticeEmailList": [ "872297557@qq.com" ]
}, },
"SystemPacsConfig": { "SystemPacsConfig": {
"Port": "104", "Port": "104",
"IP": "44.210.231.169" "IP": "44.210.231.169"
},
"RequestDuplicationOptions": {
"IsEnabled": true,
"DuplicationWindowMs": 200,
"CacheTimeSeconds": 5,
"ExcludedPaths": [
]
} }
} }
+5 -19
View File
@@ -44,8 +44,7 @@
} }
}, },
"BasicSystemConfig": { "BasicSystemConfig": {
// 启用质控风险控制功能
"QCRiskControl": true,
"OpenUserComplexPassword": true, "OpenUserComplexPassword": true,
"OpenSignDocumentBeforeWork": true, "OpenSignDocumentBeforeWork": true,
@@ -67,24 +66,16 @@
"ChangePassWordDays": 90, "ChangePassWordDays": 90,
// 模板类型 1 Elevate 2 Extensive // 模板类型 1 Elevate 2 Extensive
"TemplateType": 1, "TemplateType": 1,
"OpenLoginMFA": true, "OpenLoginMFA": true
//MFA免验证发送天数
"UserMFAVerifyMinutes": 1440
}, },
"SystemEmailSendConfig": { "SystemEmailSendConfig": {
"Port": 587, "Port": 587,
"Host": "smtp-mail.outlook.com", "Host": "smtp-mail.outlook.com",
"Imap": "imap-mail.outlook.com",
"ImapPort": 993,
"FromEmail": "donotreply@elevateimaging.ai", "FromEmail": "donotreply@elevateimaging.ai",
"FromName": "LiLi System", "FromName": "LiLi System",
"AuthorizationCode": "Q#669869497420ul", "AuthorizationCode": "Q#669869497420ul",
"PlatformName": "LiLi",
"PlatformNameCN": "LiLi",
"SystemShortName": "LiLi", "SystemShortName": "LiLi",
"OrganizationName": "Elevate Imaging", "OrganizationName": "Elevate Imaging",
"OrganizationNameCN": "Elevate Imaging", "OrganizationNameCN": "Elevate Imaging",
@@ -93,19 +84,14 @@
"CompanyShortName": "Elevate Imaging", "CompanyShortName": "Elevate Imaging",
"CompanyShortNameCN": "展影医疗", "CompanyShortNameCN": "展影医疗",
"SiteUrl": "https://lili.test.elevateimaging.ai/login", "SiteUrl": "https://lili.test.elevateimaging.ai/login",
"IsEnv_US": true "IsEnv_US": true,
"IsOpenErrorNoticeEmail": false,
"ErrorNoticeEmailList": [ "872297557@qq.com" ]
}, },
"SystemPacsConfig": { "SystemPacsConfig": {
"Port": "104", "Port": "104",
"IP": "3.226.182.187" "IP": "3.226.182.187"
},
"RequestDuplicationOptions": {
"IsEnabled": true,
"DuplicationWindowMs": 200,
"CacheTimeSeconds": 5,
"ExcludedPaths": [
]
} }
} }
+5 -23
View File
@@ -13,12 +13,6 @@
"RemoteNew": "Server=3.226.182.187,1435;Database=US_Uat_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true", "RemoteNew": "Server=3.226.182.187,1435;Database=US_Uat_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
"Hangfire": "Server=3.226.182.187,1435;Database=US_Uat_IRC_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true" "Hangfire": "Server=3.226.182.187,1435;Database=US_Uat_IRC_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
}, },
"WeComNoticeConfig": {
"IsOpenWeComNotice": true,
"WebhookUrl": "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=4355b98e-1e72-4678-8dfb-2fc6ad0bf449", //4355b98e-1e72-4678-8dfb-2fc6ad0bf449 //cdd97aab-d256-4f07-9145-a0a2b1555322
"APINoticeUserList": [ "u", "wait..." ],
"VueNoticeUserList": [ "wangxiaoshuang", "6b7717a31647293621b97b96f74e6f3d" ]
},
"ObjectStoreService": { "ObjectStoreService": {
@@ -54,8 +48,7 @@
} }
}, },
"BasicSystemConfig": { "BasicSystemConfig": {
// 启用质控风险控制功能
"QCRiskControl": true,
"OpenUserComplexPassword": true, "OpenUserComplexPassword": true,
"OpenSignDocumentBeforeWork": true, "OpenSignDocumentBeforeWork": true,
@@ -72,22 +65,16 @@
"IsNeedChangePassWord": true, "IsNeedChangePassWord": true,
"ChangePassWordDays": 90, "ChangePassWordDays": 90,
// 模板类型 1 Elevate 2 Extensive // 模板类型 1 Elevate 2 Extensive
"TemplateType": 1, "TemplateType": 1
//MFA免验证发送天数
"UserMFAVerifyMinutes": 1440
}, },
"SystemEmailSendConfig": { "SystemEmailSendConfig": {
"Port": 587, "Port": 587,
"Host": "smtp-mail.outlook.com", "Host": "smtp-mail.outlook.com",
"Imap": "imap-mail.outlook.com",
"ImapPort": 993,
"FromEmail": "donotreply@elevateimaging.ai", "FromEmail": "donotreply@elevateimaging.ai",
"FromName": "LiLi System", "FromName": "LiLi System",
"AuthorizationCode": "Q#669869497420ul", "AuthorizationCode": "Q#669869497420ul",
"PlatformName": "LiLi",
"PlatformNameCN": "LiLi",
"SystemShortName": "LiLi", "SystemShortName": "LiLi",
"OrganizationName": "Elevate Imaging", "OrganizationName": "Elevate Imaging",
"OrganizationNameCN": "Elevate Imaging", "OrganizationNameCN": "Elevate Imaging",
@@ -97,19 +84,14 @@
"CompanyShortNameCN": "展影医疗", "CompanyShortNameCN": "展影医疗",
"SiteUrl": "https://lili.uat.elevateimaging.ai/login", "SiteUrl": "https://lili.uat.elevateimaging.ai/login",
"IsEnv_US": true, "IsEnv_US": true,
"IsOpenErrorNoticeEmail": false,
"EmailRegexStr": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$", "EmailRegexStr": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$",
"CronEmailDefaultCulture": "en-US" "ErrorNoticeEmailList": [ "872297557@qq.com" ]
}, },
"SystemPacsConfig": { "SystemPacsConfig": {
"Port": "104", "Port": "104",
"IP": "3.226.182.187" "IP": "3.226.182.187"
},
"RequestDuplicationOptions": {
"IsEnabled": true,
"DuplicationWindowMs": 200,
"CacheTimeSeconds": 5,
"ExcludedPaths": [
]
} }
} }
+9 -48
View File
@@ -10,35 +10,9 @@
"RemoteNew": "Server=101.132.253.119,1435;Database=Uat_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true", "RemoteNew": "Server=101.132.253.119,1435;Database=Uat_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
"Hangfire": "Server=101.132.253.119,1435;Database=Uat_IRC_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true" "Hangfire": "Server=101.132.253.119,1435;Database=Uat_IRC_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
}, },
"WeComNoticeConfig": {
"IsOpenWeComNotice": true,
"WebhookUrl": "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=4355b98e-1e72-4678-8dfb-2fc6ad0bf449", //4355b98e-1e72-4678-8dfb-2fc6ad0bf449 //cdd97aab-d256-4f07-9145-a0a2b1555322
"APINoticeUserList": [ "u", "wait..." ],
"VueNoticeUserList": [ "wangxiaoshuang", "6b7717a31647293621b97b96f74e6f3d" ]
},
"ObjectStoreService": { "ObjectStoreService": {
"ObjectStoreUse": "AliyunOSS", "ObjectStoreUse": "AliyunOSS",
"IsOpenStoreSync": true,
"ApiDeployRegion": "CN",
"SyncConfigList": [
{
"Domain": "irc.uat.extimaging.com",
"Primary": "AliyunOSS",
"Target": "AWS",
"UploadRegion": "CN",
"TargetRegion": "US",
"IsOpenSync": true
},
{
"Domain": "lili.uat.extimaging.com",
"Primary": "AWS",
"Target": "AliyunOSS",
"UploadRegion": "US",
"TargetRegion": "CN",
"IsOpenSync": true
}
],
"AliyunOSS": { "AliyunOSS": {
"RegionId": "cn-shanghai", "RegionId": "cn-shanghai",
@@ -70,9 +44,9 @@
"Region": "us-east-1", "Region": "us-east-1",
"EndPoint": "s3.us-east-1.amazonaws.com", "EndPoint": "s3.us-east-1.amazonaws.com",
"UseSSL": true, "UseSSL": true,
"RoleArn": "arn:aws:iam::471112624751:role/uat_s3_access", "RoleArn": "arn:aws:iam::471112624751:role/sts_s3_upload",
"AccessKeyId": "AKIAW3MEAFJX7IPXISP4", "AccessKeyId": "AKIAW3MEAFJXWRCGSX5Z",
"SecretAccessKey": "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", "SecretAccessKey": "miais4jQGSd37A+TfBEP11AQM5u/CvotSmznJd8k",
"BucketName": "ei-med-s3-lili-uat-store", "BucketName": "ei-med-s3-lili-uat-store",
"ViewEndpoint": "https://ei-med-s3-lili-uat-store.s3.amazonaws.com/", "ViewEndpoint": "https://ei-med-s3-lili-uat-store.s3.amazonaws.com/",
"DurationSeconds": 7200 "DurationSeconds": 7200
@@ -80,8 +54,7 @@
}, },
"BasicSystemConfig": { "BasicSystemConfig": {
// 启用质控风险控制功能
"QCRiskControl": true,
"OpenUserComplexPassword": true, "OpenUserComplexPassword": true,
"OpenSignDocumentBeforeWork": true, "OpenSignDocumentBeforeWork": true,
@@ -99,45 +72,33 @@
"IsNeedChangePassWord": true, "IsNeedChangePassWord": true,
"ChangePassWordDays": 90, "ChangePassWordDays": 90,
// 模板类型 1 Elevate 2 Extensive // 模板类型 1 Elevate 2 Extensive
"TemplateType": 2, "TemplateType": 2
//MFA免验证发送天数
"UserMFAVerifyMinutes": 1440
}, },
"SystemEmailSendConfig": { "SystemEmailSendConfig": {
"Port": 465, "Port": 465,
"Host": "smtp.qiye.aliyun.com", "Host": "smtp.qiye.aliyun.com",
"Imap": "imap.qiye.aliyun.com",
"ImapPort": 993,
"FromEmail": "uat@extimaging.com", "FromEmail": "uat@extimaging.com",
"FromName": "Uat IRC Imaging System", "FromName": "Uat IRC Imaging System",
"AuthorizationCode": "SHzyyl2021", "AuthorizationCode": "SHzyyl2021",
"SiteUrl": "http://irc.uat.extimaging.com/login", "SiteUrl": "http://irc.uat.extimaging.com/login",
"PlatformName": "EICS",
"PlatformNameCN": "展影云平台",
"SystemShortName": "IRC", "SystemShortName": "IRC",
"OrganizationName": "ExtImaging", "OrganizationName": "Extlmaging",
"OrganizationNameCN": "ExtImaging", "OrganizationNameCN": "Extlmaging",
"CompanyName": "Extensive Imaging", "CompanyName": "Extensive Imaging",
"CompanyNameCN": "上海展影医疗科技有限公司", "CompanyNameCN": "上海展影医疗科技有限公司",
"CompanyShortName": "Extensive Imaging", "CompanyShortName": "Extensive Imaging",
"CompanyShortNameCN": "展影医疗", "CompanyShortNameCN": "展影医疗",
"IsEnv_US": false, "IsEnv_US": false,
"IsOpenErrorNoticeEmail": false,
"EmailRegexStr": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$", "EmailRegexStr": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$",
"CronEmailDefaultCulture": "zh-CN" "ErrorNoticeEmailList": [ "872297557@qq.com" ]
}, },
"SystemPacsConfig": { "SystemPacsConfig": {
"Port": "11113", "Port": "11113",
"IP": "101.132.253.119" "IP": "101.132.253.119"
},
"RequestDuplicationOptions": {
"IsEnabled": true,
"DuplicationWindowMs": 200,
"CacheTimeSeconds": 5,
"ExcludedPaths": [
]
} }
} }
-7
View File
@@ -74,12 +74,5 @@
"redirect_uri": "https://oauthlogin.net/oauth/githubcallback", "redirect_uri": "https://oauthlogin.net/oauth/githubcallback",
"scope": "repo" "scope": "repo"
} }
},
"RequestDuplicationOptions": {
"IsEnabled": true,
"DuplicationWindowMs": 200,
"CacheTimeSeconds": 5,
"ExcludedPaths": [
]
} }
} }
+3 -17
View File
@@ -1,9 +1,10 @@
<!-- HTML for static distribution bundle build --> <!-- HTML for static distribution bundle build -->
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>%(DocumentTitle)</title> <title>%(DocumentTitle)</title>
<link href="https://fonts.googleapis.com/css?family=Open+Sans:400,700|Source+Code+Pro:300,600|Titillium+Web:400,600,700" rel="stylesheet">
<link rel="stylesheet" type="text/css" href="./swagger-ui.css"> <link rel="stylesheet" type="text/css" href="./swagger-ui.css">
<link rel="icon" type="image/png" href="./favicon-32x32.png" sizes="32x32" /> <link rel="icon" type="image/png" href="./favicon-32x32.png" sizes="32x32" />
<link rel="icon" type="image/png" href="./favicon-16x16.png" sizes="16x16" /> <link rel="icon" type="image/png" href="./favicon-16x16.png" sizes="16x16" />
@@ -112,23 +113,8 @@
}, info: function () { }, info: function () {
return null; return null;
} }
},
fn: {
opsFilter: function (taggedOps, phrase) {
var normalPhrase = phrase.toLowerCase();
return taggedOps.map(function (tagObj, tag) {
var operations = tagObj.get("operations").filter(function (op) {
var summary = op.getIn(["operation", "summary"]) || "";
var path = op.get("path") || "";
var tagMatch = tag.toLowerCase().indexOf(normalPhrase) !== -1;
return tagMatch || summary.toLowerCase().indexOf(normalPhrase) !== -1 || path.toLowerCase().indexOf(normalPhrase) !== -1;
});
return tagObj.set("operations", operations);
}).filter(function (tagObj) {
return tagObj.get("operations").size > 0;
});
}
} }
} }
} }
]; ];
@@ -505,8 +505,8 @@ var abp = abp || {};
//Inputs //Inputs
createInput(modalUxContent, 'tenancyName', 'Tenancy Name (Leave empty for Host)'); createInput(modalUxContent, 'tenancyName', 'Tenancy Name (Leave empty for Host)');
createInput(modalUxContent, 'userName', 'Username or email address', 'text', 'user1wj'); createInput(modalUxContent, 'userName', 'Username or email address', 'text', 'cyldev');
createInput(modalUxContent, 'password', 'Password', 'password', '1'); createInput(modalUxContent, 'password', 'Password', 'password', '123456');
createInput(modalUxContent, 'pwdMd5', 'PwdMd5', 'text', ''); createInput(modalUxContent, 'pwdMd5', 'PwdMd5', 'text', '');
createSelect(modalUxContent, 'roleSelect', 'role', []) createSelect(modalUxContent, 'roleSelect', 'role', [])
+1 -2
View File
@@ -39,8 +39,7 @@ namespace IRaCIS.Core.Application.Auth
new Claim(JwtIRaCISClaimType.UserTypeShortName,user.UserTypeShortName), new Claim(JwtIRaCISClaimType.UserTypeShortName,user.UserTypeShortName),
new Claim(JwtIRaCISClaimType.PermissionStr,user.PermissionStr), new Claim(JwtIRaCISClaimType.PermissionStr,user.PermissionStr),
new Claim(JwtIRaCISClaimType.IsZhiZhun,user.IsZhiZhun.ToString()), new Claim(JwtIRaCISClaimType.IsZhiZhun,user.IsZhiZhun.ToString()),
new Claim(JwtIRaCISClaimType.IsTestUser,user.IsTestUser.ToString()), new Claim(JwtIRaCISClaimType.IsTestUser,user.IsTestUser.ToString())
new Claim(JwtIRaCISClaimType.UserWorkLanguage,user.UserWorkLanguage.ToString())
}; };
////创建令牌 ////创建令牌
@@ -23,7 +23,5 @@ namespace IRaCIS.Core.Application.Auth
public bool IsZhiZhun { get; set; } public bool IsZhiZhun { get; set; }
public string UserTypeShortName { get; set; } = string.Empty; public string UserTypeShortName { get; set; } = string.Empty;
public UserWorkLanguage UserWorkLanguage { get; set; }
} }
} }
@@ -1,22 +1,14 @@
using IRaCIS.Core.Application.Helper.OtherTool; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.ModelBinding.Binders;
using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Localization; using Microsoft.Extensions.Localization;
using Newtonsoft.Json; using Newtonsoft.Json;
using System;
using System.Reflection;
using static System.Runtime.InteropServices.JavaScript.JSType;
namespace IRaCIS.Core.Application.Filter; namespace IRaCIS.Core.Application.Filter;
public class ModelActionFilter(IStringLocalizer _localizer, IConfiguration _config, IUserInfo _userInfo) : ActionFilterAttribute, IActionFilter public class ModelActionFilter(IStringLocalizer _localizer) : ActionFilterAttribute, IActionFilter
{ {
@@ -31,55 +23,8 @@ public class ModelActionFilter(IStringLocalizer _localizer, IConfiguration _conf
.Select(e => e.ErrorMessage) .Select(e => e.ErrorMessage)
.ToArray(); .ToArray();
var request = context.HttpContext.Request;
try
{
bool isOpenWeComNotice = _config.GetValue<bool>("WeComNoticeConfig:IsOpenWeComNotice");
if (isOpenWeComNotice)
{
string webhook = _config["WeComNoticeConfig:WebhookUrl"] ?? string.Empty;
var uri = new Uri(_config["SystemEmailSendConfig:SiteUrl"]);
var baseUrl = uri.GetLeftPart(UriPartial.Authority);
var userList = _config.GetSection("WeComNoticeConfig:VueNoticeUserList").Get<string[]>();
var requestBody = context.HttpContext.Items["RequestBody"] as string;
// 🔔 异步告警(不要阻塞请求)
_ = WeComNotifier.SendAlertAsync(
webhook: webhook,
alert: new WeComAlert
{
Env = baseUrl,
UserName = _userInfo.UserName.IsNotNullOrEmpty()? $"{_userInfo.UserName}({_userInfo.UserTypeShortName})": "匿名",
Api = $"{request.Method} {request.Path}",
Message = $"前端传递参数,后端模型验证失败\n 具体信息:{JsonConvert.SerializeObject(validationErrors)}",
JsonData = requestBody,
AtUsers = userList ?? []
}
);
}
}
catch (Exception ex)
{
Log.Logger.Error($"模型验证过滤器里发送企业微信出现错误:{ex.Message}");
}
//---提供给接口的参数无效。 //---提供给接口的参数无效。
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["ModelAction_InvalidAPIParameter"] + JsonConvert.SerializeObject(validationErrors))); context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["ModelAction_InvalidAPIParameter"] + JsonConvert.SerializeObject(validationErrors)));
} }
} }
} }
@@ -1,16 +1,13 @@
using IRaCIS.Core.Application.Helper.OtherTool; using IRaCIS.Core.Domain.Share;
using IRaCIS.Core.Domain.Share;
using IRaCIS.Core.Infrastructure; using IRaCIS.Core.Infrastructure;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Localization; using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace IRaCIS.Core.Application.Filter; namespace IRaCIS.Core.Application.Filter;
public class ProjectExceptionFilter(ILogger<ProjectExceptionFilter> _logger, IStringLocalizer _localizer, IConfiguration _config, IUserInfo _userInfo) : Attribute, IExceptionFilter public class ProjectExceptionFilter(ILogger<ProjectExceptionFilter> _logger, IStringLocalizer _localizer) : Attribute, IExceptionFilter
{ {
public void OnException(ExceptionContext context) public void OnException(ExceptionContext context)
@@ -49,44 +46,7 @@ public class ProjectExceptionFilter(ILogger<ProjectExceptionFilter> _logger, ISt
else else
{ {
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["Project_ExceptionContactDeveloper"] + (exception.InnerException is null ? (exception.Message) context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["Project_ExceptionContactDeveloper"] + (exception.InnerException is null ? (exception.Message)
: (exception.Message + "Inner ExceptionMsg:" + exception.InnerException?.Message)), ApiResponseCodeEnum.ProgramException)); : (exception.InnerException?.Message )), ApiResponseCodeEnum.ProgramException));
try
{
bool isOpenWeComNotice = _config.GetValue<bool>("WeComNoticeConfig:IsOpenWeComNotice");
if (isOpenWeComNotice)
{
string webhook = _config["WeComNoticeConfig:WebhookUrl"] ?? string.Empty;
var uri = new Uri(_config["SystemEmailSendConfig:SiteUrl"]);
var baseUrl = uri.GetLeftPart(UriPartial.Authority);
var userList = _config.GetSection("WeComNoticeConfig:APINoticeUserList").Get<string[]>();
var requestBody = context.HttpContext.Items["RequestBody"] as string;
var alert = new WeComAlert
{
Env = baseUrl,
UserName = $"{_userInfo.UserName}({_userInfo.UserTypeShortName})",
Api = context.HttpContext.Request.Path,
Message = exception.Message,
JsonData = requestBody,
Stack = exception.StackTrace,
AtUsers = userList ?? []
};
_ = WeComNotifier.SendAlertAsync(webhook, alert);
}
}
catch (Exception ex)
{
_logger.LogError($"异常过滤器里发送企业微信出现错误:{ex.Message}");
}
} }
@@ -98,7 +58,6 @@ public class ProjectExceptionFilter(ILogger<ProjectExceptionFilter> _logger, ISt
_logger.LogError(errorInfo); _logger.LogError(errorInfo);
//_logger.LogError(exception, exception.Message); //_logger.LogError(exception, exception.Message);
} }
else else
@@ -1,137 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace IRaCIS.Core.Application.BusinessFilter.LegacyController
{
using Database.Api;
using DocumentFormat.OpenXml.InkML;
using IRaCIS.Core.Application.Service.Common;
using IRaCIS.Core.Infrastructure;
using IRaCIS.Core.Infrastructure.Extention;
using Microsoft.AspNetCore.Components.Endpoints;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Minio.Helper;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Threading.Tasks;
namespace Database.Api
{
/// <summary>
/// 请求拦截 请求前后的操作
/// </summary>
/// <param name="logger">logger</param>
/// <param name="accessor">loggerHelper</param>
public class RequestDuplicationFilter(ILogger<RequestDuplicationFilter> logger, IHttpContextAccessor accessor, IUserInfo _userInfo, IStringLocalizer _localizer, IOptionsMonitor<RequestDuplicationOptions> RequestDuplicationOptionsMonitor) : ExceptionFilterAttribute, IAsyncActionFilter
{
/// <summary>
/// 传入的参数
/// </summary>
public string Intoparam { get; set; }
/// <summary>
/// 这个是正常记录(请求刚进入的时候)
/// </summary>
/// <param name="context">context</param>
/// <param name="next">next</param>
/// <returns>返回的对象</returns>
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
if (RequestDuplicationOptionsMonitor.CurrentValue.IsEnabled)
{
Dictionary<string, object?> dic = new Dictionary<string, object?>();
var desc = context.ActionDescriptor as ControllerActionDescriptor;
foreach (var p in desc.Parameters)
{
// 关键判断:绑定源是否是 Services
if (p.BindingInfo?.BindingSource == BindingSource.Services) continue;
// 普通参数,取值
if (context.ActionArguments.TryGetValue(p.Name, out var value) && value != null)
{
dic.Add(p.Name, value);
}
}
this.Intoparam = JsonConvert.SerializeObject(dic);
try
{
this.RequestDuplication();
}
catch (Exception)
{
}
}
var resultContext = await next();
}
/// <summary>
///
/// </summary>
private void RequestDuplication()
{
var requestPath = accessor?.HttpContext?.Request?.Path.ToString() ?? string.Empty;
// 验证请求频繁情况
if (
!requestPath
.Split("/", StringSplitOptions.RemoveEmptyEntries)
.Any(segment => segment.StartsWith("get", StringComparison.OrdinalIgnoreCase)) &&
_userInfo.UserRoleId != default(Guid)&&
RequestDuplicationOptionsMonitor.CurrentValue.IsEnabled &&
!RequestDuplicationOptionsMonitor.CurrentValue.ExcludePaths.Contains(requestPath))
{
RequestInfo requestInfo = new RequestInfo
{
UserRoleId = _userInfo.UserRoleId,
RequestPath = requestPath,
ParameterHash = GenerateParameterHash(this.Intoparam),
RequestTime = DateTime.Now
};
IRCSystemInfo.RequestRecordList= IRCSystemInfo.RequestRecordList.Where(x => x.RequestTime >= DateTime.Now.AddSeconds(-RequestDuplicationOptionsMonitor.CurrentValue.CacheTimeSeconds)).ToList();
var requestsTimes = IRCSystemInfo.RequestRecordList.Any(x=>
x.RequestTime>= requestInfo.RequestTime.AddMilliseconds(-RequestDuplicationOptionsMonitor.CurrentValue.DuplicationWindowMs)&&
x.RequestKey== requestInfo.RequestKey);
if (requestsTimes)
{
throw new BusinessValidationFailedException(_localizer["RequestDuplicationFilter_RequestDuplication"], ApiResponseCodeEnum.BusinessValidationFailed);
}
IRCSystemInfo.RequestRecordList.Add(requestInfo);
}
}
public string GenerateParameterHash(string parameters)
{
if (string.IsNullOrEmpty(parameters))
return string.Empty;
using var sha256 = SHA256.Create();
var hashBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(parameters));
return Convert.ToBase64String(hashBytes);
}
}
}
}
@@ -29,11 +29,11 @@ public class TrialGlobalLimitActionFilter(IFusionCache _fusionCache, IUserInfo _
var requestHost = context.HttpContext.Request.Host; var requestHost = context.HttpContext.Request.Host;
// 检查请求是否来自 localhost:6100 // 检查请求是否来自 localhost:6100
if (requestHost.Host == "localhost" && (requestHost.Port == 6100 || requestHost.Port == 3305)) //if (requestHost.Host == "localhost" && (requestHost.Port == 6100 || requestHost.Port == 3305))
{ //{
await next(); // await next();
return; // return;
} //}
#region #region
// 用户类型检查 // 用户类型检查
@@ -7,7 +7,6 @@ namespace IRaCIS.Core.Domain.Share;
[Description("多环境 配置环境实体")] [Description("多环境 配置环境实体")]
public class ServiceVerifyConfigOption public class ServiceVerifyConfigOption
{ {
public bool QCRiskControl { get; set; }
public bool OpenUserComplexPassword { get; set; } public bool OpenUserComplexPassword { get; set; }
public bool OpenSignDocumentBeforeWork { get; set; } public bool OpenSignDocumentBeforeWork { get; set; }
@@ -39,8 +38,6 @@ public class ServiceVerifyConfigOption
public string ThirdPdfUrl { get; set; } public string ThirdPdfUrl { get; set; }
public int UserMFAVerifyMinutes { get; set; } = 1440;
} }
public class SystemEmailSendConfig public class SystemEmailSendConfig
@@ -48,10 +45,6 @@ public class SystemEmailSendConfig
public int Port { get; set; } public int Port { get; set; }
public string Host { get; set; } = string.Empty; public string Host { get; set; } = string.Empty;
public string Imap { get; set; } = string.Empty;
public int ImapPort { get; set; }
public string FromEmail { get; set; } = string.Empty; public string FromEmail { get; set; } = string.Empty;
public string FromName { get; set; } = string.Empty; public string FromName { get; set; } = string.Empty;
@@ -60,10 +53,6 @@ public class SystemEmailSendConfig
public string SiteUrl { get; set; } = string.Empty; public string SiteUrl { get; set; } = string.Empty;
public string PlatformName { get; set; } = string.Empty;
public string PlatformNameCN { get; set; } = string.Empty;
public string SystemShortName { get; set; } = string.Empty; public string SystemShortName { get; set; } = string.Empty;
public string OrganizationName { get; set; } = string.Empty; public string OrganizationName { get; set; } = string.Empty;
@@ -79,12 +68,11 @@ public class SystemEmailSendConfig
public bool IsEnv_US { get; set; } public bool IsEnv_US { get; set; }
public bool IsOpenErrorNoticeEmail { get; set; }
public string EmailRegexStr { get; set; } public string EmailRegexStr { get; set; }
//public bool IsOpenErrorNoticeEmail { get; set; } public List<string> ErrorNoticeEmailList { get; set; } =new List<string>();
//public List<string> ErrorNoticeEmailList { get; set; } = new List<string>();
} }
public class SystemEmailSendConfigView public class SystemEmailSendConfigView
@@ -117,54 +105,6 @@ public class IRCEncreptOption
public List<string> ApiPathList { get; set; } public List<string> ApiPathList { get; set; }
} }
/// <summary>
/// 请求缓存配置
/// </summary>
public class RequestDuplicationOptions
{
/// <summary>
/// 缓存时间(秒),默认5秒
/// </summary>
public int CacheTimeSeconds { get; set; } = 5;
/// <summary>
/// 重复请求检测时间窗口(毫秒),默认500毫秒
/// </summary>
public int DuplicationWindowMs { get; set; } = 500;
/// <summary>
/// 是否启用防重复请求
/// </summary>
public bool IsEnabled { get; set; } = true;
/// <summary>
/// 需要排除的路径(不进行重复检测)
/// </summary>
public List<string> ExcludePaths { get; set; } = new List<string>();
}
public static class IRCSystemInfo
{
public static List<RequestInfo> RequestRecordList { get; set; } = new List<RequestInfo>();
}
public class RequestInfo
{
public Guid UserRoleId { get; set; }
public string RequestPath { get; set; } = string.Empty;
public string ParameterHash { get; set; } = string.Empty;
public DateTime RequestTime { get; set; }
/// <summary>
/// 请求的唯一标识
/// </summary>
public string RequestKey => $"{UserRoleId}_{RequestPath}_{ParameterHash}";
}
public class IRaCISBasicConfigOption public class IRaCISBasicConfigOption
{ {
public string DoctorCodePrefix { get; set; } public string DoctorCodePrefix { get; set; }
+1 -15
View File
@@ -1,6 +1,4 @@
using DocumentFormat.OpenXml.Spreadsheet; namespace IRaCIS.Core.Application.Helper;
namespace IRaCIS.Core.Application.Helper;
public static class CacheKeys public static class CacheKeys
@@ -63,15 +61,6 @@ public static class CacheKeys
/// <returns></returns> /// <returns></returns>
public static string StartRestTime(Guid userId) => $"{userId}StartRestTime"; public static string StartRestTime(Guid userId) => $"{userId}StartRestTime";
//每个用户 每个浏览器独立时间
public static string UserMFAVerifyPass(Guid userId,string browserFingerprint) => $"UserMFAVerifyPass:{userId}:{browserFingerprint}";
public static string UserMFATag(Guid userId) => $"UserMFAVerifyPass:{userId}";
public static string TrialDataStoreType(Guid trialId) => $"TrialDataStoreType:{trialId}";
public static string UserQCSkipTask(Guid userId) => $"UserSKipQCTask:{userId}";
} }
public static class CacheHelper public static class CacheHelper
@@ -83,9 +72,6 @@ public static class CacheHelper
return statusStr; return statusStr;
} }
public static async Task<List<SystemAnonymization>> GetSystemAnonymizationListAsync(IRepository<SystemAnonymization> _systemAnonymizationRepository) public static async Task<List<SystemAnonymization>> GetSystemAnonymizationListAsync(IRepository<SystemAnonymization> _systemAnonymizationRepository)
{ {
var list = await _systemAnonymizationRepository.Where(t => t.IsEnable).ToListAsync(); var list = await _systemAnonymizationRepository.Where(t => t.IsEnable).ToListAsync();
@@ -1,9 +1,5 @@
using DocumentFormat.OpenXml.Office.CustomUI; using FellowOakDicom;
using FellowOakDicom;
using FellowOakDicom.Media; using FellowOakDicom.Media;
using IRaCIS.Core.Application.ViewModel;
using IRaCIS.Core.Domain.Models;
using NPOI.Util;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Data; using System.Data;
@@ -61,11 +57,6 @@ namespace IRaCIS.Core.Application.Helper
var mappings = new List<string>(); var mappings = new List<string>();
int index = 1; int index = 1;
var trialId = Guid.Empty;
Guid.TryParse(ossFolder.Split('/', StringSplitOptions.RemoveEmptyEntries)[0], out trialId);
var studyUid = list.FirstOrDefault()?.StudyInstanceUid;
var dicomDir = new DicomDirectory(); var dicomDir = new DicomDirectory();
@@ -109,11 +100,11 @@ namespace IRaCIS.Core.Application.Helper
var dicomFile = new DicomFile(dataset); var dicomFile = new DicomFile(dataset);
// 文件名递增格式:IM_00001, IM_00002, ... // 文件名递增格式:IM_00001, IM_00002, ...
string filename = $@"IMAGE\IM_{index:D5}"; // :D5 表示补足5位 string filename = $@"IMAGE/IM_{index:D5}"; // :D5 表示补足5位
mappings.Add($"{filename} => {item.InstanceId}"); mappings.Add($"{filename} => {item.InstanceId}");
dic.Add(item.InstanceId.ToString(), filename.TrimEnd('/', '\\').Split('/', '\\').Last()); dic.Add(item.InstanceId.ToString(), Path.GetFileName(filename));
dicomDir.AddFile(dicomFile, filename); dicomDir.AddFile(dicomFile, filename);
@@ -137,9 +128,7 @@ namespace IRaCIS.Core.Application.Helper
// 重置流位置 // 重置流位置
memoryStream.Position = 0; memoryStream.Position = 0;
var relativePath = await _oSSService.UploadToOSSAsync(memoryStream, ossFolder, "DICOMDIR", true, uploadInfo: new FileUploadRecordAddOrEdit() { TrialId = trialId, BatchDataType = BatchDataType.DICOMDIR }); await _oSSService.UploadToOSSAsync(memoryStream, ossFolder, "DICOMDIR", false);
dic.Add($"{studyUid}_DICOMDIR", relativePath.Split('/').Last());
} }
//清理临时文件 //清理临时文件
@@ -153,7 +142,7 @@ namespace IRaCIS.Core.Application.Helper
var mappingText = string.Join(Environment.NewLine, mappings); var mappingText = string.Join(Environment.NewLine, mappings);
await using var mappingStream = new MemoryStream(Encoding.UTF8.GetBytes(mappingText)); await using var mappingStream = new MemoryStream(Encoding.UTF8.GetBytes(mappingText));
await _oSSService.UploadToOSSAsync(mappingStream, ossFolder, $"Download_{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}", false, uploadInfo: new FileUploadRecordAddOrEdit() { TrialId = trialId, BatchDataType = BatchDataType.DICOMDIR }); await _oSSService.UploadToOSSAsync(mappingStream, ossFolder, $"Download_{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}", false);
#endregion #endregion
} }
@@ -161,91 +150,6 @@ namespace IRaCIS.Core.Application.Helper
} }
public static async Task GenerateStudyDIR(List<StudyDIRInfo> list, Dictionary<string, string> dic, string? dirSavePath = null, Stream? outputStream = null)
{
var mappings = new List<string>();
int index = 1;
var trialId = Guid.Empty;
var studyUid = list.FirstOrDefault()?.StudyInstanceUid;
var dicomDir = new DicomDirectory();
foreach (var item in list.OrderBy(t => t.SeriesNumber).ThenBy(t => t.InstanceNumber))
{
var dicomUid = DicomUID.Enumerate().FirstOrDefault(uid => uid.UID == item.TransferSytaxUID);
if (dicomUid != null)
{
var ts = DicomTransferSyntax.Query(dicomUid);
var dataset = new DicomDataset(ts)
{
{ DicomTag.PatientID, item.PatientId ?? string.Empty },
{ DicomTag.PatientName, item.PatientName ?? string.Empty },
{ DicomTag.PatientBirthDate, item.PatientBirthDate ?? string.Empty },
{ DicomTag.PatientSex, item.PatientSex ?? string.Empty },
{ DicomTag.StudyInstanceUID, item.StudyInstanceUid ?? string.Empty },
{ DicomTag.StudyID, item.StudyId ?? string.Empty },
{ DicomTag.StudyDate, item.DicomStudyDate ?? string.Empty },
{ DicomTag.StudyTime, item.DicomStudyTime ?? string.Empty },
{ DicomTag.AccessionNumber, item.AccessionNumber ?? string.Empty },
{ DicomTag.StudyDescription, item.StudyDescription ?? string.Empty },
{ DicomTag.SeriesInstanceUID, item.SeriesInstanceUid ?? string.Empty },
{ DicomTag.Modality, item.Modality ?? string.Empty },
{ DicomTag.SeriesDate, item.DicomSeriesDate ?? string.Empty },
{ DicomTag.SeriesTime, item.DicomSeriesTime ?? string.Empty },
{ DicomTag.SeriesNumber, item.SeriesNumber.ToString() ?? string.Empty },
{ DicomTag.SeriesDescription, item.SeriesDescription ?? string.Empty },
{ DicomTag.SOPInstanceUID, item.SopInstanceUid ?? string.Empty },
{ DicomTag.SOPClassUID, item.SOPClassUID ?? string.Empty },
{ DicomTag.InstanceNumber, item.InstanceNumber.ToString() ?? string.Empty },
{ DicomTag.MediaStorageSOPClassUID, item.MediaStorageSOPClassUID ?? string.Empty },
{ DicomTag.MediaStorageSOPInstanceUID, item.MediaStorageSOPInstanceUID ?? string.Empty },
{ DicomTag.TransferSyntaxUID, item.TransferSytaxUID ?? string.Empty },
};
var dicomFile = new DicomFile(dataset);
// 文件名递增格式:IM_00001, IM_00002, ...
string filename = $@"IMAGE\IM_{index:D5}"; // :D5 表示补足5位
mappings.Add($"{filename} => {item.InstanceId}");
dic.Add(item.InstanceId.ToString(), filename.TrimEnd('/', '\\').Split('/', '\\').Last());
dicomDir.AddFile(dicomFile, filename);
index++;
}
}
//有实际的文件
if (mappings.Count > 0)
{
// 保存 DICOMDIR 到临时文件 不能直接写入到流种
if (dirSavePath.IsNotNullOrEmpty())
{
await dicomDir.SaveAsync(dirSavePath);
}
else
{
await dicomDir.SaveAsync(outputStream);
}
}
}
public static StudyDIRInfo ReadDicomDIRInfo(DicomFile dicomFile) public static StudyDIRInfo ReadDicomDIRInfo(DicomFile dicomFile)
{ {
@@ -2,7 +2,6 @@
using MailKit; using MailKit;
using MailKit.Security; using MailKit.Security;
using MimeKit; using MimeKit;
using Org.BouncyCastle.Tls;
namespace IRaCIS.Core.Application.Helper; namespace IRaCIS.Core.Application.Helper;
@@ -10,15 +9,12 @@ namespace IRaCIS.Core.Application.Helper;
public static class SendEmailHelper public static class SendEmailHelper
{ {
public static async Task<string> SendEmailAsync(MimeMessage messageToSend, SystemEmailSendConfig _systemEmailConfig, EventHandler<MessageSentEventArgs>? messageSentSuccess = null) public static async Task SendEmailAsync(MimeMessage messageToSend, SystemEmailSendConfig _systemEmailConfig, EventHandler<MessageSentEventArgs>? messageSentSuccess = null)
{ {
string result = string.Empty;
result = messageToSend.MessageId;
//没有收件人 那么不发送 //没有收件人 那么不发送
if (messageToSend.To.Count == 0) if (messageToSend.To.Count == 0)
{ {
return string.Empty; return;
} }
try try
@@ -46,7 +42,6 @@ public static class SendEmailHelper
await smtp.DisconnectAsync(true); await smtp.DisconnectAsync(true);
} }
} }
catch (Exception ex) catch (Exception ex)
@@ -56,133 +51,9 @@ public static class SendEmailHelper
throw new Exception(I18n.T("SendEmail_SendFail"), new Exception(ex.Message)); throw new Exception(I18n.T("SendEmail_SendFail"), new Exception(ex.Message));
} }
return result;
} }
/// <summary>
/// 发送项目邮件
/// </summary>
/// <param name="messageToSend"></param>
/// <param name="trial"></param>
/// <param name="messageSentSuccess"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public static async Task<string> SendTrialEmailAsync(MimeMessage messageToSend, Trial trial, EventHandler<MessageSentEventArgs>? messageSentSuccess = null)
{
// 项目的需要重设 发件地址与邮件地址
var fromAddress = messageToSend.From.Mailboxes.FirstOrDefault();
if (fromAddress != null)
{
messageToSend.From.Clear();
messageToSend.From.Add(new MailboxAddress(trial.EmailFromName, trial.EmailFromEmail));
}
string result = string.Empty;
result = messageToSend.MessageId;
//没有收件人 那么不发送
if (messageToSend.To.Count == 0)
{
return string.Empty;
}
// 替换邮件标题
if (!string.IsNullOrEmpty(messageToSend.Subject))
{
foreach (var item in trial.TrialObjectNameList)
{
// 把标题里的占位符替换成真实名称
messageToSend.Subject = messageToSend.Subject.Replace(item.Name, item.TrialName);
}
}
// 构建替换字典
var replacements = new Dictionary<string, string>();
foreach (var item in trial.TrialObjectNameList)
{
replacements[item.Name] = item.TrialName;
}
// 安全替换 HTML
ReplaceHtmlContent(messageToSend.Body, replacements);
try
{
using (var smtp = new MailKit.Net.Smtp.SmtpClient())
{
if (messageSentSuccess != null)
{
smtp.MessageSent += messageSentSuccess;
}
smtp.ServerCertificateValidationCallback = (s, c, h, e) => true;
//await smtp.ConnectAsync("smtp.qq.com", 465, SecureSocketOptions.SslOnConnect);
//await smtp.AuthenticateAsync("zhou941003@qq.com", "sqfhlpfdvnexbcab");
//await smtp.ConnectAsync(_systemEmailConfig.Host, _systemEmailConfig.Port, SecureSocketOptions.Auto);
//await smtp.AuthenticateAsync(_systemEmailConfig.FromEmail, _systemEmailConfig.AuthorizationCode);
await smtp.ConnectAsync(trial.EmailSMTPServerAddress, trial.EmailSMTPServerPort, SecureSocketOptions.Auto);
await smtp.AuthenticateAsync(trial.EmailFromEmail, trial.EmailAuthorizationCode);
await smtp.SendAsync(messageToSend);
await smtp.DisconnectAsync(true);
}
}
catch (Exception ex)
{
//---邮件发送失败,您进行的操作未能成功,请检查邮箱或联系维护人员
throw new Exception(I18n.T("SendEmail_SendFail"), new Exception(ex.Message));
}
return result;
}
// 遍历邮件体,找到 HTML 部分并替换
public static void ReplaceHtmlContent(MimeEntity entity, Dictionary<string, string> replacements)
{
if (entity is Multipart multipart)
{
foreach (var part in multipart)
{
ReplaceHtmlContent(part, replacements);
}
}
else if (entity is TextPart textPart && textPart.IsHtml)
{
// 只有这里才是真正的 HTML
foreach (var kv in replacements)
{
textPart.Text = textPart.Text.Replace(kv.Key, kv.Value);
}
}
}
public static async Task<bool> TestEmailConfigAsync(SystemEmailSendConfig _systemEmailConfig) public static async Task<bool> TestEmailConfigAsync(SystemEmailSendConfig _systemEmailConfig)
{ {
using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5))) using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5)))
@@ -202,7 +73,7 @@ public static class SendEmailHelper
return true; return true;
} }
public static async Task SendEmailAsync(SMTPEmailConfig sMTPEmailConfig, Trial? trial, EventHandler<MessageSentEventArgs>? messageSentSuccess = null) public static async Task SendEmailAsync(SMTPEmailConfig sMTPEmailConfig, EventHandler<MessageSentEventArgs>? messageSentSuccess = null)
{ {
var messageToSend = new MimeMessage(); var messageToSend = new MimeMessage();
@@ -216,9 +87,8 @@ public static class SendEmailHelper
if (sMTPEmailConfig.ToMailAddressList.Count == 0) if (sMTPEmailConfig.ToMailAddressList.Count == 0)
{ {
return;
//---没有收件人 //---没有收件人
//throw new ArgumentException(I18n.T("SendEmail_NoRecipient")); throw new ArgumentException(I18n.T("SendEmail_NoRecipient"));
} }
else else
{ {
@@ -273,7 +143,7 @@ public static class SendEmailHelper
await smtp.ConnectAsync(sMTPEmailConfig.Host, sMTPEmailConfig.Port, SecureSocketOptions.Auto); await smtp.ConnectAsync(sMTPEmailConfig.Host, sMTPEmailConfig.Port, SecureSocketOptions.Auto);
await smtp.AuthenticateAsync(trial.EmailFromEmail, trial.EmailAuthorizationCode); await smtp.AuthenticateAsync(sMTPEmailConfig.UserName, sMTPEmailConfig.AuthorizationCode);
await smtp.SendAsync(messageToSend); await smtp.SendAsync(messageToSend);
@@ -18,7 +18,6 @@ using NPOI.HSSF.UserModel;
using NPOI.SS.Formula.Functions; using NPOI.SS.Formula.Functions;
using NPOI.SS.UserModel; using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel; using NPOI.XSSF.UserModel;
using SharpCompress.Common;
using System.Collections; using System.Collections;
using System.Globalization; using System.Globalization;
using Xceed.Document.NET; using Xceed.Document.NET;
@@ -725,15 +724,6 @@ public static class ExcelExportHelper
row.GetCell(writeIndex).SetCellValue(translatedItemData); row.GetCell(writeIndex).SetCellValue(translatedItemData);
}
else
{
var unit = iteObjDic.ContainsKey("Unit") ? iteObjDic["Unit"]?.ToString() : null;
if (unit.IsNotNullOrEmpty() && unit == "9")
{
row.GetCell(writeIndex).SetCellValue(itemValue + "%");
} }
else else
{ {
@@ -741,9 +731,6 @@ public static class ExcelExportHelper
} }
}
} }
} }
@@ -871,16 +858,17 @@ public static class ExcelExportHelper
//模板路径 //模板路径
var tplPath = physicalPath; var tplPath = physicalPath;
// 获取文件流
var templateStream = new MemoryStream();
#region sheet #region sheet
// 打开模板文件 // 打开模板文件
var templateFileStream = new FileStream(tplPath, FileMode.Open, FileAccess.Read); var templateFile = new FileStream(tplPath, FileMode.Open, FileAccess.Read);
var workbook = new XSSFWorkbook(templateFileStream); // 获取文件流
var templateStream = new MemoryStream();
templateFile.CopyTo(templateStream);
templateStream.Seek(0, SeekOrigin.Begin);
var workbook = new XSSFWorkbook(templateStream);
int sheetCount = workbook.NumberOfSheets; int sheetCount = workbook.NumberOfSheets;
@@ -985,8 +973,14 @@ public static class ExcelExportHelper
} }
} }
workbook.Write(templateStream, leaveOpen: true); using (var memoryStream2 = new MemoryStream())
templateStream.Position = 0; {
workbook.Write(memoryStream2, true);
memoryStream2.Seek(0, SeekOrigin.Begin);
templateStream = memoryStream2;
}
} }
@@ -1084,91 +1078,6 @@ public static class ExcelExportHelper
public static async Task<IActionResult> MutiSheetDataExportAsync(string code, object data, string exportFileNamePrefix, IRepository<CommonDocument> _commonDocumentRepository, IWebHostEnvironment _hostEnvironment)
{
var (physicalPath, fileName) = await FileStoreHelper.GetCommonDocPhysicalFilePathAsync(_hostEnvironment, _commonDocumentRepository, code);
//模板路径
var tplPath = physicalPath;
var templateStream = new MemoryStream();
#region npoi
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
if (isEn_US)
{
// 打开模板文件
using var templateFileStream = new FileStream(tplPath, FileMode.Open, FileAccess.Read);
using var workbook = new XSSFWorkbook(templateFileStream);
int sheetCount = workbook.NumberOfSheets;
int removeRowIndex = 1; // 要删除的行(0-based
for (int i = 0; i < sheetCount; i++)
{
var sheet = workbook.GetSheetAt(i);
// 2 删除行
var row = sheet.GetRow(removeRowIndex);
if (row != null)
{
sheet.RemoveRow(row);
}
// 3 上移后续行
if (removeRowIndex < sheet.LastRowNum)
{
sheet.ShiftRows(
removeRowIndex + 1,
sheet.LastRowNum,
-1,
true, // copyRowHeight
false // resetOriginalRowHeight
);
}
}
workbook.Write(templateStream, leaveOpen: true);
templateStream.Position = 0;
}
else
{
using (var fs = new FileStream(tplPath, FileMode.Open, FileAccess.Read))
{
fs.CopyTo(templateStream);
}
templateStream.Position = 0;
}
#endregion
var memoryStream = new MemoryStream();
var config = new OpenXmlConfiguration()
{
IgnoreTemplateParameterMissing = true,
};
await MiniExcel.SaveAsByTemplateAsync(memoryStream, templateStream.ToArray(), data, config);
memoryStream.Seek(0, SeekOrigin.Begin);
return new FileStreamResult(memoryStream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
{
FileDownloadName = $"{(string.IsNullOrEmpty(exportFileNamePrefix) ? "" : exportFileNamePrefix + "_")}{Path.GetFileNameWithoutExtension(fileName)}_{DateTime.Now.ToString("yyyyMMddHHmmss")}.xlsx"
};
}
/// <summary> /// <summary>
/// 导出文件模板 /// 导出文件模板
/// </summary> /// </summary>
@@ -1250,7 +1159,4 @@ public static class ExcelExportHelper
}; };
} }
} }
@@ -2,7 +2,6 @@
using IRaCIS.Core.Application.MassTransit.Consumer; using IRaCIS.Core.Application.MassTransit.Consumer;
using IRaCIS.Core.Domain.Share; using IRaCIS.Core.Domain.Share;
using MassTransit.Mediator; using MassTransit.Mediator;
using System.Globalization;
namespace IRaCIS.Core.Application.Helper namespace IRaCIS.Core.Application.Helper
{ {
@@ -58,29 +57,25 @@ namespace IRaCIS.Core.Application.Helper
} }
public static void AddOrUpdateTrialCronJob(string jobId, Guid trialId, EmailBusinessScenario businessScenario, string emailCron,string defaultCulture="") public static void AddOrUpdateTrialCronJob(string jobId, Guid trialId, EmailBusinessScenario businessScenario, string emailCron)
{ {
if (defaultCulture == "")
{
defaultCulture = CultureInfo.CurrentCulture.Name;
}
switch (businessScenario) switch (businessScenario)
{ {
case EmailBusinessScenario.QCTask: case EmailBusinessScenario.QCTask:
HangfireJobHelper.AddOrUpdateCronJob<IMediator>(jobId, t => t.Send(new ImageQCRecurringEvent() { TrialId = trialId,CultureInfoName= defaultCulture }, default), emailCron); HangfireJobHelper.AddOrUpdateCronJob<IMediator>(jobId, t => t.Send(new ImageQCRecurringEvent() { TrialId = trialId }, default), emailCron);
break; break;
case EmailBusinessScenario.CRCToQCQuestion: case EmailBusinessScenario.CRCToQCQuestion:
HangfireJobHelper.AddOrUpdateCronJob<IMediator>(jobId, t => t.Send(new CRCImageQuestionRecurringEvent() { TrialId = trialId, CultureInfoName = defaultCulture }, default), emailCron); HangfireJobHelper.AddOrUpdateCronJob<IMediator>(jobId, t => t.Send(new CRCImageQuestionRecurringEvent() { TrialId = trialId }, default), emailCron);
break; break;
case EmailBusinessScenario.QCToCRCImageQuestion: case EmailBusinessScenario.QCToCRCImageQuestion:
HangfireJobHelper.AddOrUpdateCronJob<IMediator>(jobId, t => t.Send(new QCImageQuestionRecurringEvent() { TrialId = trialId, CultureInfoName = defaultCulture }, default), emailCron); HangfireJobHelper.AddOrUpdateCronJob<IMediator>(jobId, t => t.Send(new QCImageQuestionRecurringEvent() { TrialId = trialId }, default), emailCron);
break; break;
//加急阅片 10分钟 //加急阅片 10分钟
case EmailBusinessScenario.ExpeditedReading: case EmailBusinessScenario.ExpeditedReading:
HangfireJobHelper.AddOrUpdateCronJob<IMediator>(jobId, t => t.Send(new UrgentIRUnReadTaskRecurringEvent() { TrialId = trialId, CultureInfoName = defaultCulture }, default), emailCron); HangfireJobHelper.AddOrUpdateCronJob<IMediator>(jobId, t => t.Send(new UrgentIRUnReadTaskRecurringEvent() { TrialId = trialId }, default), emailCron);
break; break;
default: default:
break; break;
@@ -88,25 +83,20 @@ namespace IRaCIS.Core.Application.Helper
} }
public static void AddOrUpdateTimingCronJob(string jobId, EmailBusinessScenario businessScenario, string emailCron, string defaultCulture="") public static void AddOrUpdateTimingCronJob(string jobId, EmailBusinessScenario businessScenario, string emailCron)
{ {
if (defaultCulture == "")
{
defaultCulture = CultureInfo.CurrentCulture.Name;
}
switch (businessScenario) switch (businessScenario)
{ {
case EmailBusinessScenario.GeneralTraining_ExpirationNotification: case EmailBusinessScenario.GeneralTraining_ExpirationNotification:
HangfireJobHelper.AddOrUpdateCronJob<IMediator>(jobId, t => t.Send(new SystemDocumentErverDayEvent() { CultureInfoName = defaultCulture }, default), emailCron); HangfireJobHelper.AddOrUpdateCronJob<IMediator>(jobId, t => t.Send(new SystemDocumentErverDayEvent() { }, default), emailCron);
break; break;
case EmailBusinessScenario.TrialTraining_ExpirationNotification: case EmailBusinessScenario.TrialTraining_ExpirationNotification:
Console.WriteLine("更新项目到期job"); Console.WriteLine("更新项目到期job");
HangfireJobHelper.AddOrUpdateCronJob<IMediator>(jobId, t => t.Send(new TrialDocumentErverDayEvent() { CultureInfoName = defaultCulture }, default), emailCron); HangfireJobHelper.AddOrUpdateCronJob<IMediator>(jobId, t => t.Send(new TrialDocumentErverDayEvent() { }, default), emailCron);
break; break;
default: default:
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -1,106 +0,0 @@
using IdentityModel;
using Newtonsoft.Json;
using RestSharp;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Threading.Tasks;
namespace IRaCIS.Core.Application.Helper.OtherTool;
public class WeComAlert
{
public string Env { get; set; } = "";
public string UserName { get; set; } = "";
public string Api { get; set; } = "";
public string Message { get; set; } = "";
public string? JsonData { get; set; }
public string? Stack { get; set; }
public bool HasStack => !string.IsNullOrWhiteSpace(Stack);
public string[] AtUsers { get; set; } = [];
}
public static class WeComNotifier
{
public static async Task SendAlertAsync(string webhook, WeComAlert alert)
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
await Task.CompletedTask;
return;
}
try
{
var client = new RestClient();
var request = new RestRequest(webhook, Method.Post);
var time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
// 处理 @
var atText = alert.AtUsers != null && alert.AtUsers.Any()
? string.Join("", alert.AtUsers.Select(u => $" <@{u}>"))
: "";
// 处理堆栈
var stack = alert.Stack;
if (!string.IsNullOrWhiteSpace(stack))
{
stack = stack.Replace("\n", "\n> ");
if (stack.Length > 600)
stack = stack[..600] + "...(已截断)";
}
var markdown = $@"## 🚨 系统告警
> {atText}
> **部署环境:** [{alert.Env}]({alert.Env})
> **发生时间:** {time}
> **操作人:** {alert.UserName}
> **接口地址:** {alert.Api}
### ❗ 告警信息
<font color=""warning"">{alert.Message}</font>";
if (!string.IsNullOrWhiteSpace(alert.JsonData))
{
markdown += $@"
> **📦 请求数据(JSON 格式):**
```json
{alert.JsonData}
```";
}
if (!string.IsNullOrWhiteSpace(stack))
{
markdown += $@"
### 堆栈信息(部分)
<font color=""comment"">{stack}</font>";
}
var payload = new
{
msgtype = "markdown",
markdown = new { content = markdown }
};
request.AddHeader("Content-Type", "application/json");
request.AddStringBody(JsonConvert.SerializeObject(payload), DataFormat.Json);
await client.ExecuteAsync(request);
}
catch (Exception ex)
{
Log.Logger.Error("企业微信告警发送失败: " + ex.Message);
}
}
}
@@ -33,37 +33,38 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="IdentityModel.OidcClient" Version="6.0.0" /> <PackageReference Include="IdentityModel.OidcClient" Version="6.0.0" />
<PackageReference Include="AlibabaCloud.SDK.Sts20150401" Version="1.2.0" /> <PackageReference Include="AlibabaCloud.SDK.Sts20150401" Version="1.1.6" />
<PackageReference Include="AWSSDK.SecurityToken" Version="4.0.1.3" />
<PackageReference Include="Aliyun.OSS.SDK.NetCore" Version="2.14.1" /> <PackageReference Include="Aliyun.OSS.SDK.NetCore" Version="2.14.1" />
<PackageReference Include="AWSSDK.S3" Version="4.0.21" /> <PackageReference Include="AWSSDK.S3" Version="4.0.4.1" />
<PackageReference Include="AWSSDK.SecurityToken" Version="4.0.5.19" /> <PackageReference Include="DocX" Version="4.0.25105.5786" />
<PackageReference Include="DocX" Version="5.0.0" />
<PackageReference Include="FreeSpire.Doc" Version="12.2.0" /> <PackageReference Include="FreeSpire.Doc" Version="12.2.0" />
<PackageReference Include="ExcelDataReader" Version="3.8.0" /> <PackageReference Include="ExcelDataReader" Version="3.7.0" />
<PackageReference Include="ExcelDataReader.DataSet" Version="3.8.0" /> <PackageReference Include="ExcelDataReader.DataSet" Version="3.7.0" />
<PackageReference Include="DistributedLock.Redis" Version="1.1.1" /> <PackageReference Include="DistributedLock.Redis" Version="1.1.0" />
<PackageReference Include="DistributedLock.SqlServer" Version="1.0.7" /> <PackageReference Include="DistributedLock.SqlServer" Version="1.0.6" />
<PackageReference Include="fo-dicom" Version="5.2.6" /> <PackageReference Include="fo-dicom" Version="5.2.2" />
<PackageReference Include="fo-dicom.Imaging.ImageSharp" Version="5.2.6" /> <PackageReference Include="fo-dicom.Imaging.ImageSharp" Version="5.2.2" />
<PackageReference Include="fo-dicom.Codecs" Version="5.16.7" /> <PackageReference Include="fo-dicom.Codecs" Version="5.16.4" />
<PackageReference Include="IP2Region.Net" Version="3.0.2" /> <PackageReference Include="IP2Region.Net" Version="2.0.2" />
<PackageReference Include="MailKit" Version="4.15.1" /> <PackageReference Include="MailKit" Version="4.11.0" />
<PackageReference Include="Masa.Contrib.Service.MinimalAPIs" Version="1.1.0" /> <PackageReference Include="Masa.Contrib.Service.MinimalAPIs" Version="1.0.0" />
<PackageReference Include="MaxMind.GeoIP2" Version="5.4.1" /> <PackageReference Include="MaxMind.GeoIP2" Version="5.3.0" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.10" /> <PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.10" />
<PackageReference Include="MimeKit" Version="4.15.1" /> <PackageReference Include="MimeKit" Version="4.11.0" />
<PackageReference Include="MiniExcel" Version="1.41.2" /> <PackageReference Include="MiniExcel" Version="1.41.2" />
<PackageReference Include="Minio" Version="6.0.3" /> <PackageReference Include="Minio" Version="6.0.3" />
<PackageReference Include="MiniWord" Version="0.9.2" /> <PackageReference Include="MiniWord" Version="0.9.2" />
<PackageReference Include="Serilog.AspNetCore" Version="10.0.0" /> <PackageReference Include="Serilog.AspNetCore" Version="9.0.0" />
<PackageReference Include="My.Extensions.Localization.Json" Version="3.3.0"> <PackageReference Include="My.Extensions.Localization.Json" Version="3.3.0">
<TreatAsUsed>true</TreatAsUsed> <TreatAsUsed>true</TreatAsUsed>
</PackageReference> </PackageReference>
<PackageReference Include="NPOI" Version="2.7.4" /> <PackageReference Include="NPOI" Version="2.7.4" />
<PackageReference Include="Panda.DynamicWebApi" Version="1.2.2" /> <PackageReference Include="Panda.DynamicWebApi" Version="1.2.2" />
<PackageReference Include="RestSharp" Version="114.0.0" /> <PackageReference Include="RestSharp" Version="112.1.0" />
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.12" /> <PackageReference Include="SixLabors.ImageSharp" Version="3.1.11" />
<PackageReference Include="Swashbuckle.AspNetCore.Filters" Version="8.0.2" /> <PackageReference Include="Swashbuckle.AspNetCore.Filters" Version="8.0.2" />
<PackageReference Include="ZiggyCreatures.FusionCache" Version="2.4.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@@ -74,8 +75,4 @@
<Folder Include="Service\MinimalApiService\CodeTemplate\FrontTemplate\" /> <Folder Include="Service\MinimalApiService\CodeTemplate\FrontTemplate\" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<None Include="Service\TrialSiteUser\TrialStatService.cs" />
</ItemGroup>
</Project> </Project>
File diff suppressed because it is too large Load Diff
@@ -1,21 +1,8 @@
using IRaCIS.Core.Application.Helper; using MiniExcelLibs.Attributes;
using MiniExcelLibs.Attributes;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
namespace IRaCIS.Core.Application.MassTransit.Command namespace IRaCIS.Core.Application.MassTransit.Command
{ {
/// <summary>
/// 全量一致性核查
/// </summary>
public record ConsistenFullCheckCommand
{
public List<CheckViewModel> ETCList { get; set; } = new List<CheckViewModel>();
public Guid TrialId { get; set; }
public Guid InspectionFileId { get; set; }
}
public record ConsistenCheckCommand public record ConsistenCheckCommand
{ {
public List<CheckViewModel> ETCList { get; set; } = new List<CheckViewModel>(); public List<CheckViewModel> ETCList { get; set; } = new List<CheckViewModel>();
@@ -34,22 +21,9 @@ namespace IRaCIS.Core.Application.MassTransit.Command
public Guid SubjectVisitId { get; set; } public Guid SubjectVisitId { get; set; }
public Guid StudyId { get; set; } public Guid StudyId { get; set; }
public string SiteCounty { get; set; }
} }
public class FullCheckResult: CheckViewModel
{
public string LatestScanDateStr { get; set; } = string.Empty;
public string Modalitys { get; set; } = string.Empty;
public DateTime CheckTime { get; set; }
public string CheckResult { get; set; }
[DictionaryTranslateAttribute("CheckState")]
public CheckStateEnum CheckState { get; set; }
}
//[ExcelImporter(/*ImportResultFilter = typeof(ImportResultFilteTest),*/ IsLabelingError = true)] //[ExcelImporter(/*ImportResultFilter = typeof(ImportResultFilteTest),*/ IsLabelingError = true)]
@@ -88,13 +62,8 @@ namespace IRaCIS.Core.Application.MassTransit.Command
[ExcelColumnName("Modality")] [ExcelColumnName("Modality")]
public string Modality { get; set; } = string.Empty; public string Modality { get; set; } = string.Empty;
#region
[DictionaryTranslateAttribute("Subject_Visit_Status")]
public SubjectStatus SubjectStatus { get; set; }
#endregion
public override bool Equals(object? obj) public override bool Equals(object? obj)
{ {
@@ -21,14 +21,12 @@ public static class CommonEmailHelper
/// <param name="configInfo"></param> /// <param name="configInfo"></param>
/// <param name="messageToSend"></param> /// <param name="messageToSend"></param>
/// <param name="emailFunc"></param> /// <param name="emailFunc"></param>
/// <param name="userWorkLanguage"></param>
/// <returns></returns> /// <returns></returns>
/// <exception cref="BusinessValidationFailedException"></exception> /// <exception cref="BusinessValidationFailedException"></exception>
public static async Task<EmailNoticeConfig> GetEmailSubejctAndHtmlInfoAndBuildAsync(EmailNoticeConfig configInfo, MimeMessage messageToSend, public static async Task<EmailNoticeConfig> GetEmailSubejctAndHtmlInfoAndBuildAsync(EmailNoticeConfig configInfo, MimeMessage messageToSend,
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailFunc, UserWorkLanguage? userWorkLanguage = null) Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailFunc)
{ {
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
bool isEn_US = userWorkLanguage.HasValue ? userWorkLanguage.Value == UserWorkLanguage.US : CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
var (topicStr, htmlBodyStr) = isEn_US ? (configInfo.EmailTopic, configInfo.EmailHtmlContent) : (configInfo.EmailTopicCN, configInfo.EmailHtmlContentCN); var (topicStr, htmlBodyStr) = isEn_US ? (configInfo.EmailTopic, configInfo.EmailHtmlContent) : (configInfo.EmailTopicCN, configInfo.EmailHtmlContentCN);
@@ -62,14 +60,13 @@ Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr
/// <param name="configInfo"></param> /// <param name="configInfo"></param>
/// <param name="messageToSend"></param> /// <param name="messageToSend"></param>
/// <param name="emailFunc"></param> /// <param name="emailFunc"></param>
/// <param name="userWorkLanguage"></param>
/// <returns></returns> /// <returns></returns>
/// <exception cref="BusinessValidationFailedException"></exception> /// <exception cref="BusinessValidationFailedException"></exception>
public static async Task<TrialEmailNoticeConfig> GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(TrialEmailNoticeConfig configInfo, MimeMessage messageToSend, public static async Task<TrialEmailNoticeConfig> GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(TrialEmailNoticeConfig configInfo, MimeMessage messageToSend,
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailFunc, UserWorkLanguage? userWorkLanguage = null) Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailFunc)
{ {
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
bool isEn_US = userWorkLanguage.HasValue ? userWorkLanguage.Value == UserWorkLanguage.US : CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
var (topicStr, htmlBodyStr) = isEn_US ? (configInfo.EmailTopic, configInfo.EmailHtmlContent) : (configInfo.EmailTopicCN, configInfo.EmailHtmlContentCN); var (topicStr, htmlBodyStr) = isEn_US ? (configInfo.EmailTopic, configInfo.EmailHtmlContent) : (configInfo.EmailTopicCN, configInfo.EmailHtmlContentCN);
@@ -97,20 +94,12 @@ Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr
return configInfo; return configInfo;
} }
public static string ReplacePlatformName(SystemEmailSendConfig _systemEmailConfig, string needDealtxt, UserWorkLanguage? userWorkLanguage = null)
public static string ReplaceCompanyName(SystemEmailSendConfig _systemEmailConfig, string needDealtxt)
{ {
var isEn_US = userWorkLanguage.HasValue ? userWorkLanguage.Value == UserWorkLanguage.US : CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US; var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
var platformName = isEn_US ? _systemEmailConfig.PlatformName : _systemEmailConfig.PlatformNameCN;
var str = needDealtxt.Replace("{platformName}", platformName);
return str;
}
public static string ReplaceCompanyName(SystemEmailSendConfig _systemEmailConfig, string needDealtxt, UserWorkLanguage? userWorkLanguage = null)
{
var isEn_US = userWorkLanguage.HasValue ? userWorkLanguage.Value == UserWorkLanguage.US : CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
var str = needDealtxt.Replace("{company}", isEn_US ? _systemEmailConfig.CompanyName : _systemEmailConfig.CompanyNameCN) var str = needDealtxt.Replace("{company}", isEn_US ? _systemEmailConfig.CompanyName : _systemEmailConfig.CompanyNameCN)
.Replace("{company abbreviation}", isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN); .Replace("{company abbreviation}", isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN);
@@ -1,52 +1,53 @@
using AutoMapper; using AutoMapper;
using IRaCIS.Application.Contracts;
using IRaCIS.Application.Interfaces; 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.Contracts.DTO;
using IRaCIS.Core.Application.Helper;
using IRaCIS.Core.Application.MassTransit.Command; using IRaCIS.Core.Application.MassTransit.Command;
using IRaCIS.Core.Application.ViewModel;
using IRaCIS.Core.Domain.Models;
using IRaCIS.Core.Domain.Share; using IRaCIS.Core.Domain.Share;
using MassTransit; using MassTransit;
using MaxMind.GeoIP2.Model;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Localization; using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Newtonsoft.Json; using Newtonsoft.Json;
using System.Globalization;
using System.IO;
using System.Text; using System.Text;
namespace IRaCIS.Core.Application.MassTransit.Consumer namespace IRaCIS.Core.Application.MassTransit.Consumer
{ {
public class ConsistencyCheckConsumer( public class ConsistencyCheckConsumer : IConsumer<ConsistenCheckCommand>
IRepository<DicomStudy> _studyRepository,
IUserInfo _userInfo,
IRepository<Subject> _subjectRepository,
IRepository<NoneDicomStudy> _noneDicomStudyRepository,
IRepository<SubjectVisit> _subjectVisitRepository,
IRepository<Dictionary> _dictionaryRepository,
IOSSService _oSSService,
IMapper _mapper,
IDictionaryService _dictionaryService,
IRepository<CommonDocument> _commonDocumentRepository,
IStringLocalizer _localizer,
IWebHostEnvironment _hostEnvironment,
IRepository<InspectionFile> _inspectionFileRepository,
IOptionsMonitor<SystemEmailSendConfig> _systemEmailSendConfig
) : IConsumer<ConsistenCheckCommand>, IConsumer<ConsistenFullCheckCommand>
{ {
private readonly IRepository<DicomStudy> _studyRepository;
private readonly IUserInfo _userInfo;
private readonly IRepository<Subject> _subjectRepository;
private readonly IRepository<SubjectVisit> _subjectVisitRepository;
private readonly IRepository<TrialSite> _trialSiteRepository;
private readonly IMapper _mapper;
private readonly IRepository<NoneDicomStudy> _noneDicomStudyRepository;
public IStringLocalizer _localizer { get; set; }
private readonly SystemEmailSendConfig _systemEmailConfig;
private readonly SystemEmailSendConfig _systemEmailConfig = _systemEmailSendConfig.CurrentValue; private readonly IRepository<Dictionary> _dictionaryRepository;
/// <summary>
/// 构造函数注入
/// </summary>
public ConsistencyCheckConsumer(IRepository<DicomStudy> studyRepository, IUserInfo userInfo,
IRepository<Subject> subjectRepository, IRepository<SubjectVisit> subjectVisitRepository,
IRepository<TrialSite> trialSiteRepository, IRepository<NoneDicomStudy> noneDicomStudyRepository,
IMapper mapper, IStringLocalizer localizer, IOptionsMonitor<SystemEmailSendConfig> systemEmailConfig, IRepository<Dictionary> dictionaryRepository)
{
_noneDicomStudyRepository = noneDicomStudyRepository;
_studyRepository = studyRepository;
_userInfo = userInfo;
_subjectRepository = subjectRepository;
_subjectVisitRepository = subjectVisitRepository;
_trialSiteRepository = trialSiteRepository;
_mapper = mapper;
_localizer = localizer;
_systemEmailConfig = systemEmailConfig.CurrentValue;
_dictionaryRepository = dictionaryRepository;
}
@@ -66,8 +67,8 @@ namespace IRaCIS.Core.Application.MassTransit.Consumer
//subjectVisitLambda2= subjectVisitLambda2.And(x => x.CheckState == CheckStateEnum.ToCheck && x.AuditState == AuditStateEnum.QCPassed || (x.CheckState == CheckStateEnum.CVIng && x.AuditState == AuditStateEnum.QCPassed)); //subjectVisitLambda2= subjectVisitLambda2.And(x => x.CheckState == CheckStateEnum.ToCheck && x.AuditState == AuditStateEnum.QCPassed || (x.CheckState == CheckStateEnum.CVIng && x.AuditState == AuditStateEnum.QCPassed));
Expression<Func<SubjectVisit, bool>> subjectVisitLambda = x => x.TrialId == trialId && x.Subject.IsSubjectQuit == false && x.AuditState == AuditStateEnum.QCPassed && Expression<Func<SubjectVisit, bool>> subjectVisitLambda = x => x.TrialId == trialId && x.Subject.IsSubjectQuit == false &&
(x.CheckState == CheckStateEnum.ToCheck || x.CheckState == CheckStateEnum.CVIng); (x.CheckState == CheckStateEnum.ToCheck && x.AuditState == AuditStateEnum.QCPassed || (x.CheckState == CheckStateEnum.CVIng && x.AuditState == AuditStateEnum.QCPassed));
var dicomQuery = from sv in _subjectVisitRepository.Where(subjectVisitLambda) var dicomQuery = from sv in _subjectVisitRepository.Where(subjectVisitLambda)
join subject in _subjectRepository.AsQueryable() on sv.SubjectId equals subject.Id join subject in _subjectRepository.AsQueryable() on sv.SubjectId equals subject.Id
@@ -81,8 +82,6 @@ namespace IRaCIS.Core.Application.MassTransit.Consumer
Modality = study.ModalityForEdit, Modality = study.ModalityForEdit,
SubjectCode = subject.Code, SubjectCode = subject.Code,
VisitName = sv.VisitName, VisitName = sv.VisitName,
SiteCounty = sv.TrialSite.Country,
}; };
var noneDicomQuey = from sv in _subjectVisitRepository.Where(subjectVisitLambda) var noneDicomQuey = from sv in _subjectVisitRepository.Where(subjectVisitLambda)
@@ -97,8 +96,6 @@ namespace IRaCIS.Core.Application.MassTransit.Consumer
Modality = noneDicomStudy.Modality, Modality = noneDicomStudy.Modality,
SubjectCode = subject.Code, SubjectCode = subject.Code,
VisitName = sv.VisitName, VisitName = sv.VisitName,
SiteCounty = sv.TrialSite.Country,
}; };
var dbList = (await dicomQuery.ToListAsync()).Union(await noneDicomQuey.ToListAsync()).ToList(); var dbList = (await dicomQuery.ToListAsync()).Union(await noneDicomQuey.ToListAsync()).ToList();
@@ -120,27 +117,12 @@ namespace IRaCIS.Core.Application.MassTransit.Consumer
foreach (var sv in svExcelGroup) foreach (var sv in svExcelGroup)
{ {
//Excel 的数据 在IRC 中可以找到该访视(一致性核查中,或者待核查) //Excel 的数据 在IRC 中可以找到该访视
if (dbCheckList.Any(t => t.SubjectCode == sv.SubjectCode && t.SiteCode == sv.SiteCode && t.VisitName == sv.VisitName)) if (dbCheckList.Any(t => t.SubjectCode == sv.SubjectCode && t.SiteCode == sv.SiteCode && t.VisitName == sv.VisitName))
{ {
var dbVisitStudyList = dbList.Where(t => t.SubjectCode == sv.SubjectCode && t.SiteCode == sv.SiteCode && t.VisitName == sv.VisitName).ToList(); var dbVisitStudyList = dbList.Where(t => t.SubjectCode == sv.SubjectCode && t.SiteCode == sv.SiteCode && t.VisitName == sv.VisitName).ToList();
//设置当前中心语言
var isEn_us = dbVisitStudyList.First().SiteCounty == StaticData.SiteCountry.US;
var cultureInfoName = StaticData.CultureInfo.en_US;
if (isEn_us == false)
{
cultureInfoName = StaticData.CultureInfo.zh_CN;
}
CultureInfo.CurrentCulture = new CultureInfo(cultureInfoName);
CultureInfo.CurrentUICulture = new CultureInfo(cultureInfoName);
//找到etc 当前visit site 和subject 一致的检查列表 //找到etc 当前visit site 和subject 一致的检查列表
var etcVisitStudyList = etcList.Where(t => t.SubjectCode == sv.SubjectCode && t.SiteCode == sv.SiteCode && t.VisitName == sv.VisitName).ToList(); var etcVisitStudyList = etcList.Where(t => t.SubjectCode == sv.SubjectCode && t.SiteCode == sv.SiteCode && t.VisitName == sv.VisitName).ToList();
@@ -219,7 +201,7 @@ namespace IRaCIS.Core.Application.MassTransit.Consumer
var dbExceptExcel = dbVisitStudyList.Except(etcVisitStudyList); var dbExceptExcel = dbVisitStudyList.Except(etcVisitStudyList);
// excel 存在 // excel 存在
var excelExceptDB = etcVisitStudyList.Except(dbVisitStudyList); var excelExceptDB = etcVisitStudyList.Except(dbCheckList);
//ETC 和系统的完全一致 两者没有差别 //ETC 和系统的完全一致 两者没有差别
if (dbExceptExcel.Count() == 0 && excelExceptDB.Count() == 0) if (dbExceptExcel.Count() == 0 && excelExceptDB.Count() == 0)
@@ -319,169 +301,12 @@ namespace IRaCIS.Core.Application.MassTransit.Consumer
} }
await _subjectVisitRepository.SaveChangesAsync(); await _subjectVisitRepository.SaveChangesAsync();
//await context.RespondAsync<ConsistenCheckResult>(new
//{
} //});
public async Task Consume(ConsumeContext<ConsistenFullCheckCommand> context)
{
var inspectionFileId = context.Message.InspectionFileId;
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;
Expression<Func<SubjectVisit, bool>> subjectVisitLambda = x => x.TrialId == trialId /*&& x.Subject.IsSubjectQuit == false*/ && x.AuditState == AuditStateEnum.QCPassed;
var dicomQuery = from sv in _subjectVisitRepository.Where(subjectVisitLambda)
join subject in _subjectRepository.AsQueryable() on sv.SubjectId equals subject.Id
join study in _studyRepository.AsQueryable() on sv.Id equals study.SubjectVisitId
select new CheckDBModel()
{
SubjectStatus = sv.Subject.Status,
SubjectVisitId = sv.Id,
SiteCode = sv.TrialSite.TrialSiteCode,
StudyDate = study.StudyTime == null ? string.Empty : ((DateTime)study.StudyTime).ToString("yyyy-MM-dd"),
StudyId = study.Id,
Modality = study.ModalityForEdit,
SubjectCode = subject.Code,
VisitName = sv.VisitName,
};
var noneDicomQuey = from sv in _subjectVisitRepository.Where(subjectVisitLambda)
join subject in _subjectRepository.AsQueryable() on sv.SubjectId equals subject.Id
join noneDicomStudy in _noneDicomStudyRepository.Where(t => t.FileCount > 0) on sv.Id equals noneDicomStudy.SubjectVisitId
select new CheckDBModel()
{
SubjectStatus = sv.Subject.Status,
SubjectVisitId = sv.Id,
SiteCode = sv.TrialSite.TrialSiteCode,
StudyDate = noneDicomStudy.ImageDate.ToString("yyyy-MM-dd"),
StudyId = noneDicomStudy.Id,
Modality = noneDicomStudy.Modality,
SubjectCode = subject.Code,
VisitName = sv.VisitName,
};
var dbList = (await dicomQuery.ToListAsync()).Union(await noneDicomQuey.ToListAsync()).ToList();
//处理数据库 大小写
dbList.ForEach(t => { t.Modality = t.Modality.ToUpper().Trim(); t.SiteCode = t.SiteCode.ToUpper().Trim(); t.VisitName = t.VisitName.ToUpper().Trim(); t.SubjectCode = t.SubjectCode.ToUpper().Trim(); });
var dbCheckList = _mapper.Map<List<CheckViewModel>>(dbList);
//按照Excel数据访视分组 按照数据库的数据 一个个的访视对比
var svExcelGroup = etcList.GroupBy(t => new { t.SiteCode, t.SubjectCode, t.VisitName })
.Select(g => new { g.Key.SubjectCode, g.Key.VisitName, g.Key.SiteCode, ExcelStudyList = g.ToList() }).ToList();
var fullCheckResultList = new List<FullCheckResult>();
foreach (var sv in svExcelGroup)
{
//Excel 的数据 在IRC 中可以找到该访视(一致性核查中,或者待核查)
if (dbCheckList.Any(t => t.SubjectCode == sv.SubjectCode && t.SiteCode == sv.SiteCode && t.VisitName == sv.VisitName))
{
var dbVisitStudyList = dbList.Where(t => t.SubjectCode == sv.SubjectCode && t.SiteCode == sv.SiteCode && t.VisitName == sv.VisitName).ToList();
//找到etc 当前visit site 和subject 一致的检查列表
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;
}
}
//etc 和数据库 并集
var unionList = dbVisitStudyList.Union(etcVisitStudyList);
// 数据库存在
var dbExceptExcel = dbVisitStudyList.Except(etcVisitStudyList);
// excel 存在
var excelExceptDB = etcVisitStudyList.Except(dbVisitStudyList);
var dbCurrentVisitFirst = dbVisitStudyList.First();
//ETC 和系统的完全一致 两者没有差别
if (dbExceptExcel.Count() == 0 && excelExceptDB.Count() == 0)
{
//每个受试者每个访视加一条记录
fullCheckResultList.Add(new FullCheckResult()
{
SubjectStatus = dbCurrentVisitFirst.SubjectStatus,
CheckTime = DateTime.Now,
CheckState = CheckStateEnum.CVPassed,
SiteCode = dbCurrentVisitFirst.SiteCode,
SubjectCode = dbCurrentVisitFirst.SubjectCode,
VisitName = dbCurrentVisitFirst.VisitName,
Modalitys = string.Join('、', dbVisitStudyList.Select(t => t.Modality)),
LatestScanDateStr = dbVisitStudyList.Select(t => t.StudyDate).MaxBy(d => DateTime.Parse(d)) ?? ""
});
}
else
{
var 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]}"));
//每个受试者每个访视加一条记录
fullCheckResultList.Add(new FullCheckResult()
{
SubjectStatus = dbCurrentVisitFirst.SubjectStatus,
CheckState = CheckStateEnum.None,
CheckTime = DateTime.Now,
SiteCode = dbCurrentVisitFirst.SiteCode,
SubjectCode = dbCurrentVisitFirst.SubjectCode,
VisitName = dbCurrentVisitFirst.VisitName,
Modalitys = string.Join('、', dbVisitStudyList.Select(t => t.Modality)),
LatestScanDateStr = dbVisitStudyList.Select(t => t.StudyDate).MaxBy(d => DateTime.Parse(d)) ?? "",
CheckResult = checkResult
});
} }
} }
}
//导到Excel 上传oss 回更记录状态
var list = fullCheckResultList;
var exportInfo = new ExcelExportInfo();
exportInfo.List = ExportExcelConverterDate.ConvertToClientTimeInObject(list, _userInfo.TimeZoneId);
exportInfo.CurrentTime = ExportExcelConverterDate.DateTimeInternationalToString(DateTime.Now, _userInfo.TimeZoneId);
var fileStreamResult = (FileStreamResult)await ExcelExportHelper.DataExportAsync(StaticData.Export.TrialConsistentFUllCheckList_Export, exportInfo, exportInfo.TrialCode, _commonDocumentRepository, _hostEnvironment, _dictionaryService, typeof(FullCheckResult));
var ossRelativePath = await _oSSService.UploadToOSSAsync(fileStreamResult.FileStream, $"{trialId.ToString()}/InspectionUpload/DataReconciliation", "DataReconciliation", uploadInfo: new FileUploadRecordAddOrEdit() { TrialId = trialId, BatchDataType = BatchDataType.DataReconciliation });
//var add = await _inspectionFileRepository.FindAsync(inspectionFileId);
//add.CheckState = EDCCheckState.Success;
//add.ResultPath = ossRelativePath;
//更新
await _inspectionFileRepository.BatchUpdateNoTrackingAsync(t => t.Id == inspectionFileId, u => new InspectionFile() { CheckState = EDCCheckState.Success, ResultPath = ossRelativePath });
await _subjectVisitRepository.SaveChangesAsync();
}
}
} }
@@ -27,16 +27,5 @@ namespace IRaCIS.Core.Application.MassTransit.Consumer.Dto
}
public class SendEmailUserDto
{
public UserTypeEnum UserTypeEnum { get; set; }
public string FullName { get; set; }
public UserWorkLanguage UserWorkLanguage { get; set; }
public string EMail { get; set; }
} }
} }
@@ -256,7 +256,7 @@ public class ImageConsumer(
} }
/// <summary> /// <summary>
/// 发送影像重传相关邮件的通用方法 已经处理项目 /// 发送影像重传相关邮件的通用方法
/// </summary> /// </summary>
private async Task SendImageReuploadEmail(SendImageReuploadEmailInDto inDto) private async Task SendImageReuploadEmail(SendImageReuploadEmailInDto inDto)
{ {
@@ -273,30 +273,15 @@ public class ImageConsumer(
}; };
// 根据不同场景获取不同角色的用户 先排除CRC和CRA // 根据不同场景获取不同角色的用户 先排除CRC和CRA
var trialUserList = await _trialUseRoleRepository.Where(x => x.TrialId == inDto.TrialId && !x.TrialUser.IsDeleted && !filterUserTypeList.Contains(x.UserRole.UserTypeEnum) ).Include(x => x.UserRole).Select(x => x.UserRole) var trialUserList = await _trialUseRoleRepository.Where(x => x.TrialId == inDto.TrialId && !x.TrialUser.IsDeleted && !filterUserTypeList.Contains(x.UserRole.UserTypeEnum) ).Include(x => x.UserRole).Select(x => x.UserRole).ToListAsync();
.Select(x => new SendEmailUserDto()
{
UserTypeEnum = x.UserTypeEnum,
FullName = x.FullName,
UserWorkLanguage = x.IdentityUser.UserWorkLanguage,
EMail=x.EMail,
})
.ToListAsync();
// CRC和CRA单独取 // CRC和CRA单独取
var crcAndcraUserList = await _trialSiteRepository.Where(x => x.Id == inDto.SubjectVisit.TrialSiteId).SelectMany(x => x.CRCUserList.Select(y => y.UserRole)) var crcAndcraUserList = await _trialSiteRepository.Where(x => x.Id == inDto.SubjectVisit.TrialSiteId).SelectMany(x => x.CRCUserList.Select(y => y.UserRole)).ToListAsync();
.Select(x => new SendEmailUserDto()
{
UserTypeEnum = x.UserTypeEnum,
FullName = x.FullName,
UserWorkLanguage = x.IdentityUser.UserWorkLanguage,
EMail = x.EMail,
}).ToListAsync();
trialUserList.AddRange(crcAndcraUserList); trialUserList.AddRange(crcAndcraUserList);
// 根据场景确定收件人 // 根据场景确定收件人
List<SendEmailUserDto> toUserList = new List<SendEmailUserDto>(); List<UserRole> toUserList = new List<UserRole>();
List<SendEmailUserDto> ccUserList = new List<SendEmailUserDto>(); List<UserRole> ccUserList = new List<UserRole>();
var emailNoticeUserList = await _emailNoticeUserTypeRepository.Where(x => x.EmailNoticeConfigId == inDto.EmailNoticeConfig.Id).ToListAsync(); var emailNoticeUserList = await _emailNoticeUserTypeRepository.Where(x => x.EmailNoticeConfigId == inDto.EmailNoticeConfig.Id).ToListAsync();
var userTypeEnumList = emailNoticeUserList.Select(x => x.UserType).ToList(); var userTypeEnumList = emailNoticeUserList.Select(x => x.UserType).ToList();
@@ -349,39 +334,26 @@ public class ImageConsumer(
DictionaryList = dictionaryDtos DictionaryList = dictionaryDtos
}); });
foreach (var userinfo in toUserList)
var userWorkLanguageList = toUserList.Select(x => x.UserWorkLanguage).Distinct().ToList();
foreach (var workLanguage in userWorkLanguageList)
{ {
var userinfoList = toUserList.Where(x => x.UserWorkLanguage == workLanguage).ToList();
var messageToSend = new MimeMessage(); var messageToSend = new MimeMessage();
// 发件地址 // 发件地址
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail)); messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
foreach (var userinfo in userinfoList)
{
messageToSend.To.Add(new MailboxAddress(userinfo.FullName, userinfo.EMail));
}
// 添加抄送 // 添加抄送
foreach (var ccUser in ccUserList) foreach (var ccUser in ccUserList)
{ {
messageToSend.Cc.Add(new MailboxAddress(ccUser.FullName, ccUser.EMail)); messageToSend.Cc.Add(new MailboxAddress(String.Empty, ccUser.EMail));
} }
var userNames = userinfoList.Select(x => x.FullName).ToList();
// 格式化邮件内容 // 格式化邮件内容
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input => Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
{ {
var subjectCode = inDto.SubjectVisit.Subject.Code; var subjectCode = inDto.SubjectVisit.Subject.Code;
var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo, subjectCode, inDto.SubjectVisit.VisitName); var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo, subjectCode, inDto.SubjectVisit.VisitName);
var htmlBodyStr = string.Format( var htmlBodyStr = string.Format(
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr, workLanguage), CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
string.Join(',', userNames), // 用户名 {0} userinfo.FullName, // 用户名 {0}
trialInfo.ExperimentName, // 项目 {1} trialInfo.ExperimentName, // 项目 {1}
subjectCode, // 受试者 {2} subjectCode, // 受试者 {2}
inDto.SubjectVisit.VisitName, // 访视 {3} inDto.SubjectVisit.VisitName, // 访视 {3}
@@ -393,11 +365,9 @@ public class ImageConsumer(
return (topicStr, htmlBodyStr); return (topicStr, htmlBodyStr);
}; };
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(inDto.EmailNoticeConfig, messageToSend, emailConfigFunc);
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(inDto.EmailNoticeConfig, messageToSend, emailConfigFunc, workLanguage);
await SendEmailHelper.SendTrialEmailAsync(messageToSend, trialInfo);
} }
} }
@@ -464,7 +434,7 @@ public class ImageConsumer(
} }
/// <summary> /// <summary>
/// 发送阅片人筛选相关邮件的通用方法 已经处理项目 /// 发送阅片人筛选相关邮件的通用方法
/// </summary> /// </summary>
private async Task SendReviewerSelectionEmail(EmailNoticeConfig emailNoticeConfig, Guid trialId, List<Guid> enrollIdList) private async Task SendReviewerSelectionEmail(EmailNoticeConfig emailNoticeConfig, Guid trialId, List<Guid> enrollIdList)
{ {
@@ -474,19 +444,11 @@ public class ImageConsumer(
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == trialId); 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) var trialUser = await _trialUseRoleRepository.Where(x => x.TrialId == trialId && !x.TrialUser.IsDeleted).Include(x => x.UserRole).Select(x => x.UserRole).ToListAsync();
.Select(x => new SendEmailUserDto()
{
UserTypeEnum = x.UserTypeEnum,
FullName = x.FullName,
UserWorkLanguage = x.IdentityUser.UserWorkLanguage,
EMail = x.EMail,
})
.ToListAsync();
// 根据场景确定收件人 // 根据场景确定收件人
List<SendEmailUserDto> toUserList = new List<SendEmailUserDto>(); List<UserRole> toUserList = new List<UserRole>();
List<SendEmailUserDto> ccUserList = new List<SendEmailUserDto>(); List<UserRole> ccUserList = new List<UserRole>();
var emailNoticeUserList = await _emailNoticeUserTypeRepository.Where(x => x.EmailNoticeConfigId == emailNoticeConfig.Id).ToListAsync(); var emailNoticeUserList = await _emailNoticeUserTypeRepository.Where(x => x.EmailNoticeConfigId == emailNoticeConfig.Id).ToListAsync();
@@ -505,35 +467,26 @@ public class ImageConsumer(
return; return;
} }
var userWorkLanguageList = toUserList.Select(x => x.UserWorkLanguage).Distinct().ToList(); foreach (var userinfo in toUserList)
foreach (var workLanguage in userWorkLanguageList)
{ {
var userinfoList = toUserList.Where(x => x.UserWorkLanguage == workLanguage).ToList();
var messageToSend = new MimeMessage(); var messageToSend = new MimeMessage();
// 发件地址 // 发件地址
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail)); messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
foreach (var userinfo in userinfoList)
{
messageToSend.To.Add(new MailboxAddress(userinfo.FullName, userinfo.EMail));
}
// 添加抄送 // 添加抄送
foreach (var ccUser in ccUserList) foreach (var ccUser in ccUserList)
{ {
messageToSend.Cc.Add(new MailboxAddress(ccUser.FullName, ccUser.EMail)); messageToSend.Cc.Add(new MailboxAddress(String.Empty, ccUser.EMail));
} }
var userNames = userinfoList.Select(x => x.FullName).ToList();
// 格式化邮件内容 // 格式化邮件内容
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input => Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
{ {
var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo); var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo);
var htmlBodyStr = string.Format( var htmlBodyStr = string.Format(
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr, workLanguage), CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
string.Join(',', userNames), // 用户名 {0} userinfo.FullName, // 用户名 {0}
trialInfo.ExperimentName, // 项目 {1} trialInfo.ExperimentName, // 项目 {1}
"", // 受试者 {2} - 阅片人筛选不涉及受试者 "", // 受试者 {2} - 阅片人筛选不涉及受试者
"", // 访视 {3} - 阅片人筛选不涉及访视 "", // 访视 {3} - 阅片人筛选不涉及访视
@@ -545,9 +498,9 @@ public class ImageConsumer(
return (topicStr, htmlBodyStr); return (topicStr, htmlBodyStr);
}; };
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(emailNoticeConfig, messageToSend, emailConfigFunc, workLanguage); await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(emailNoticeConfig, messageToSend, emailConfigFunc);
await SendEmailHelper.SendTrialEmailAsync(messageToSend, trialInfo); await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
} }
} }
} }
@@ -1,6 +1,5 @@
using IRaCIS.Core.Application.Helper; using IRaCIS.Core.Application.Helper;
using IRaCIS.Core.Application.MassTransit.Command; using IRaCIS.Core.Application.MassTransit.Command;
using IRaCIS.Core.Application.MassTransit.Consumer.Dto;
using IRaCIS.Core.Domain; using IRaCIS.Core.Domain;
using IRaCIS.Core.Domain.BaseModel; using IRaCIS.Core.Domain.BaseModel;
using IRaCIS.Core.Domain.Models; using IRaCIS.Core.Domain.Models;
@@ -69,7 +68,7 @@ public class UrgentMedicalReviewAddedEventConsumer(
var criterion = await _readingQuestionCriterionTrialRepository.FirstOrDefaultAsync(x => x.Id == medicalReview.VisitTask.TrialReadingCriterionId); var criterion = await _readingQuestionCriterionTrialRepository.FirstOrDefaultAsync(x => x.Id == medicalReview.VisitTask.TrialReadingCriterionId);
var userinfo = await _userRoleRepository.Where(x => x.Id == medicalReview.MedicalManagerUserId).Include(x => x.IdentityUser).FirstOrDefaultAsync(); 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 taskInfo = await _visitTaskRepository.Where(x => x.Id == medicalReview.VisitTaskId).Include(x => x.SourceSubjectVisit).Include(x => x.ReadModule).Include(x => x.Subject).FirstNotNullAsync();
@@ -79,23 +78,22 @@ public class UrgentMedicalReviewAddedEventConsumer(
var subjectVisit = await _subjectVisitRepository.Where(x => x.Id == visitid).FirstOrDefaultAsync(); var subjectVisit = await _subjectVisitRepository.Where(x => x.Id == visitid).FirstOrDefaultAsync();
var workLanguage = userinfo.IdentityUser?.UserWorkLanguage;
var userIsEn_US = workLanguage.HasValue ? workLanguage.Value == UserWorkLanguage.US : isEn_US;
var messageToSend = new MimeMessage(); var messageToSend = new MimeMessage();
//发件地址 //发件地址
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail)); messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
messageToSend.To.Add(new MailboxAddress(userinfo.FullName, userinfo.EMail)); messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == medicalReview.TrialId); var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == medicalReview.TrialId);
var companyName = userIsEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN; var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
var dictionValue = await CommonEmailHelper.TranslationDictionary(new TranslationDictionaryDto() var dictionValue = await CommonEmailHelper.TranslationDictionary(new TranslationDictionaryDto()
{ {
DictionaryRepository = _dictionaryRepository, DictionaryRepository = _dictionaryRepository,
IsEn_US = userIsEn_US, IsEn_US = isEn_US,
DictionaryList = new List<DictionaryDto>() DictionaryList = new List<DictionaryDto>()
@@ -112,7 +110,7 @@ public class UrgentMedicalReviewAddedEventConsumer(
var subjectName = taskInfo.BlindSubjectCode.IsNullOrEmpty() ? taskInfo.Subject.Code : taskInfo.BlindSubjectCode; var subjectName = taskInfo.BlindSubjectCode.IsNullOrEmpty() ? taskInfo.Subject.Code : taskInfo.BlindSubjectCode;
var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo, subjectName, taskInfo.TaskBlindName); var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo, subjectName, taskInfo.TaskBlindName);
var htmlBodyStr = string.Format( var htmlBodyStr = string.Format(
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr, workLanguage), CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
userinfo.FullName, // 用户名 {0} userinfo.FullName, // 用户名 {0}
trialInfo.ExperimentName, // 项目 {1} trialInfo.ExperimentName, // 项目 {1}
criterion.CriterionName, // 阅片标准 {2} criterion.CriterionName, // 阅片标准 {2}
@@ -128,9 +126,9 @@ public class UrgentMedicalReviewAddedEventConsumer(
return (topicStr, htmlBodyStr); return (topicStr, htmlBodyStr);
}; };
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc, workLanguage); await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc);
await SendEmailHelper.SendTrialEmailAsync(messageToSend, trialInfo); await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
} }
@@ -179,7 +177,7 @@ public class UrgentIRRepliedMedicalReviewConsumer(
var criterion = await _readingQuestionCriterionTrialRepository.FirstOrDefaultAsync(x => x.Id == medicalReview.VisitTask.TrialReadingCriterionId); var criterion = await _readingQuestionCriterionTrialRepository.FirstOrDefaultAsync(x => x.Id == medicalReview.VisitTask.TrialReadingCriterionId);
var userinfo = await _userRoleRepository.Where(x => x.Id == medicalReview.MedicalManagerUserId).Include(x => x.IdentityUser).FirstOrDefaultAsync(); 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 taskInfo = await _visitTaskRepository.Where(x => x.Id == medicalReview.VisitTaskId).Include(x => x.SourceSubjectVisit).Include(x => x.ReadModule).Include(x => x.Subject).FirstNotNullAsync();
@@ -189,24 +187,22 @@ public class UrgentIRRepliedMedicalReviewConsumer(
var subjectVisit = await _subjectVisitRepository.Where(x => x.Id == visitid).FirstOrDefaultAsync(); var subjectVisit = await _subjectVisitRepository.Where(x => x.Id == visitid).FirstOrDefaultAsync();
var workLanguage = userinfo.IdentityUser?.UserWorkLanguage;
var userIsEn_US = workLanguage.HasValue ? workLanguage.Value == UserWorkLanguage.US : isEn_US;
var messageToSend = new MimeMessage(); var messageToSend = new MimeMessage();
//发件地址 //发件地址
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail)); messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
messageToSend.To.Add(new MailboxAddress(userinfo.FullName, userinfo.EMail)); messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == medicalReview.TrialId); var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == medicalReview.TrialId);
var companyName = userIsEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN; var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
var dictionValue = await CommonEmailHelper.TranslationDictionary(new TranslationDictionaryDto() var dictionValue = await CommonEmailHelper.TranslationDictionary(new TranslationDictionaryDto()
{ {
DictionaryRepository = _dictionaryRepository, DictionaryRepository = _dictionaryRepository,
IsEn_US = userIsEn_US, IsEn_US = isEn_US,
DictionaryList = new List<DictionaryDto>() DictionaryList = new List<DictionaryDto>()
@@ -223,7 +219,7 @@ public class UrgentIRRepliedMedicalReviewConsumer(
var subjectCode = taskInfo.BlindSubjectCode.IsNullOrEmpty() ? taskInfo.Subject.Code : taskInfo.BlindSubjectCode; var subjectCode = taskInfo.BlindSubjectCode.IsNullOrEmpty() ? taskInfo.Subject.Code : taskInfo.BlindSubjectCode;
var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo, subjectCode, taskInfo.TaskBlindName); var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo, subjectCode, taskInfo.TaskBlindName);
var htmlBodyStr = string.Format( var htmlBodyStr = string.Format(
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr, workLanguage), CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
userinfo.FullName, // 用户名 {0} userinfo.FullName, // 用户名 {0}
trialInfo.ExperimentName, // 项目 {1} trialInfo.ExperimentName, // 项目 {1}
criterion.CriterionName, // 阅片标准 {2} criterion.CriterionName, // 阅片标准 {2}
@@ -238,9 +234,9 @@ public class UrgentIRRepliedMedicalReviewConsumer(
return (topicStr, htmlBodyStr); return (topicStr, htmlBodyStr);
}; };
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc, workLanguage); await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc);
await SendEmailHelper.SendTrialEmailAsync(messageToSend, trialInfo); await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
} }
} }
@@ -297,7 +293,7 @@ public class UrgentMIMRepliedMedicalReviewConsumer(
var criterion = await _readingQuestionCriterionTrialRepository.FirstOrDefaultAsync(x => x.Id == medicalReview.VisitTask.TrialReadingCriterionId); 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 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).Include(x => x.IdentityUser).FirstOrDefaultAsync(); var userinfo = await _userRoleRepository.Where(x => x.Id == taskInfo.DoctorUserId).FirstOrDefaultAsync();
@@ -307,25 +303,22 @@ public class UrgentMIMRepliedMedicalReviewConsumer(
var subjectVisit = await _subjectVisitRepository.Where(x => x.Id == visitid).FirstOrDefaultAsync(); var subjectVisit = await _subjectVisitRepository.Where(x => x.Id == visitid).FirstOrDefaultAsync();
var workLanguage = userinfo.IdentityUser?.UserWorkLanguage;
var userIsEn_US = workLanguage.HasValue ? workLanguage.Value == UserWorkLanguage.US : isEn_US;
var messageToSend = new MimeMessage(); var messageToSend = new MimeMessage();
//发件地址 //发件地址
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail)); messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
messageToSend.To.Add(new MailboxAddress(userinfo.FullName, userinfo.EMail)); messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == medicalReview.TrialId); var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == medicalReview.TrialId);
var companyName = userIsEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN; var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
var dictionValue = await CommonEmailHelper.TranslationDictionary(new TranslationDictionaryDto() var dictionValue = await CommonEmailHelper.TranslationDictionary(new TranslationDictionaryDto()
{ {
DictionaryRepository = _dictionaryRepository, DictionaryRepository = _dictionaryRepository,
IsEn_US = userIsEn_US, IsEn_US = isEn_US,
DictionaryList = new List<DictionaryDto>() DictionaryList = new List<DictionaryDto>()
@@ -342,7 +335,7 @@ public class UrgentMIMRepliedMedicalReviewConsumer(
var subjectCode = taskInfo.BlindSubjectCode.IsNullOrEmpty() ? taskInfo.Subject.Code : taskInfo.BlindSubjectCode; var subjectCode = taskInfo.BlindSubjectCode.IsNullOrEmpty() ? taskInfo.Subject.Code : taskInfo.BlindSubjectCode;
var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo, subjectCode, taskInfo.TaskBlindName); var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo, subjectCode, taskInfo.TaskBlindName);
var htmlBodyStr = string.Format( var htmlBodyStr = string.Format(
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr, workLanguage), CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
userinfo.FullName, // 用户名 {0} userinfo.FullName, // 用户名 {0}
trialInfo.ExperimentName, // 项目 {1} trialInfo.ExperimentName, // 项目 {1}
subjectCode, // 受试者 {2} subjectCode, // 受试者 {2}
@@ -359,8 +352,9 @@ public class UrgentMIMRepliedMedicalReviewConsumer(
return (topicStr, htmlBodyStr); return (topicStr, htmlBodyStr);
}; };
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc, workLanguage); await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc);
await SendEmailHelper.SendTrialEmailAsync(messageToSend, trialInfo);
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
} }
} }
} }
@@ -406,15 +400,7 @@ public class UrgentIRApplyedReReadingConsumer(
var doctorInfo = await _userRoleRepository.Where(x => x.Id == taskInfo.DoctorUserId).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) var trialUserList = await _trialUseRoleRepository.Where(x => x.TrialId == taskInfo.TrialId && x.TrialUser.IsDeleted==false).Include(x => x.UserRole).Select(x => x.UserRole).ToListAsync();
.Select(x => new SendEmailUserDto()
{
UserTypeEnum = x.UserTypeEnum,
FullName = x.FullName,
UserWorkLanguage = x.IdentityUser.UserWorkLanguage,
EMail = x.EMail,
})
.ToListAsync();
var userinfoList = trialUserList.Where(x => x.UserTypeEnum == UserTypeEnum.ProjectManager || x.UserTypeEnum == UserTypeEnum.APM).ToList(); var userinfoList = trialUserList.Where(x => x.UserTypeEnum == UserTypeEnum.ProjectManager || x.UserTypeEnum == UserTypeEnum.APM).ToList();
if (context.Message.ReReadingApplyState == ReReadingApplyState.TrialGroupHaveApplyed) if (context.Message.ReReadingApplyState == ReReadingApplyState.TrialGroupHaveApplyed)
@@ -427,21 +413,23 @@ public class UrgentIRApplyedReReadingConsumer(
var subjectVisit = await _subjectVisitRepository.Where(x => x.Id == visitid).FirstOrDefaultAsync(); var subjectVisit = await _subjectVisitRepository.Where(x => x.Id == visitid).FirstOrDefaultAsync();
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == taskInfo.TrialId);
var userWorkLanguageList = userinfoList.Select(x => x.UserWorkLanguage).Distinct().ToList(); foreach (var userinfo in userinfoList)
foreach (var workLanguage in userWorkLanguageList)
{ {
var langUserinfoList = userinfoList.Where(x => x.UserWorkLanguage == workLanguage).ToList(); var messageToSend = new MimeMessage();
if (!langUserinfoList.Any()) continue; //发件地址
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 userIsEn_US = workLanguage == UserWorkLanguage.US;
var dictionValue = await CommonEmailHelper.TranslationDictionary(new TranslationDictionaryDto() var dictionValue = await CommonEmailHelper.TranslationDictionary(new TranslationDictionaryDto()
{ {
DictionaryRepository = _dictionaryRepository, DictionaryRepository = _dictionaryRepository,
IsEn_US = userIsEn_US, IsEn_US = isEn_US,
DictionaryList = new List<DictionaryDto>() DictionaryList = new List<DictionaryDto>()
{ {
new DictionaryDto (){DictionaryCode= "ReadingCategory",EnumValue=taskInfo.ReadingCategory.GetEnumInt(), }, //任务类型 new DictionaryDto (){DictionaryCode= "ReadingCategory",EnumValue=taskInfo.ReadingCategory.GetEnumInt(), }, //任务类型
@@ -449,26 +437,13 @@ public class UrgentIRApplyedReReadingConsumer(
} }
}); });
var messageToSend = new MimeMessage();
//发件地址
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
foreach (var userinfo in langUserinfoList)
{
messageToSend.To.Add(new MailboxAddress(userinfo.FullName, userinfo.EMail));
}
var companyName = userIsEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
var userNames = langUserinfoList.Select(x => x.FullName).ToList();
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input => Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
{ {
var subjectCode = taskInfo.BlindSubjectCode.IsNullOrEmpty() ? taskInfo.Subject.Code : taskInfo.BlindSubjectCode; var subjectCode = taskInfo.BlindSubjectCode.IsNullOrEmpty() ? taskInfo.Subject.Code : taskInfo.BlindSubjectCode;
var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo, subjectCode, taskInfo.TaskBlindName); var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo, subjectCode, taskInfo.TaskBlindName);
var htmlBodyStr = string.Format( var htmlBodyStr = string.Format(
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr, workLanguage), CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
string.Join(',', userNames), // 用户名 {0} userinfo.FullName, // 用户名 {0}
trialInfo.ExperimentName, // 项目 {1} trialInfo.ExperimentName, // 项目 {1}
subjectCode, // 受试者 {2} subjectCode, // 受试者 {2}
taskInfo.TaskBlindName, // 访视 {3} taskInfo.TaskBlindName, // 访视 {3}
@@ -477,14 +452,18 @@ public class UrgentIRApplyedReReadingConsumer(
criterion.CriterionName, // 阅片标准 {6} criterion.CriterionName, // 阅片标准 {6}
dictionValue[1], // 审批结果 {7} dictionValue[1], // 审批结果 {7}
_systemEmailConfig.SiteUrl // 链接 {8} _systemEmailConfig.SiteUrl // 链接 {8}
); );
return (topicStr, htmlBodyStr); return (topicStr, htmlBodyStr);
}; };
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc, workLanguage); await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc);
await SendEmailHelper.SendTrialEmailAsync(messageToSend, trialInfo); await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
} }
} }
@@ -2,7 +2,6 @@
using DocumentFormat.OpenXml.Spreadsheet; using DocumentFormat.OpenXml.Spreadsheet;
using DocumentFormat.OpenXml.Vml; using DocumentFormat.OpenXml.Vml;
using DocumentFormat.OpenXml.Wordprocessing; using DocumentFormat.OpenXml.Wordprocessing;
using IRaCIS.Application.Contracts;
using IRaCIS.Core.Application.Contracts; using IRaCIS.Core.Application.Contracts;
using IRaCIS.Core.Application.Helper; using IRaCIS.Core.Application.Helper;
using IRaCIS.Core.Application.MassTransit.Command; using IRaCIS.Core.Application.MassTransit.Command;
@@ -56,39 +55,27 @@ public class UserSiteSurveySubmitedEventConsumer(
if (emailConfig != null) if (emailConfig != null)
{ {
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == trialId);
var siteInfo = await _trialSiteRepository.FirstOrDefaultAsync(t => t.TrialId == trialId && t.Id == siteSurveyInfo.TrialSiteId, true);
var trialUserList = await _trialUserRoleRepository.Where(t => t.TrialId == siteSurveyInfo.TrialId && t.TrialUser.IsDeleted == false) 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) .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, t.UserRole.IdentityUser.UserWorkLanguage }).ToListAsync(); .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 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 pmAndAPMList = trialUserList.Where(t => t.UserTypeEnum == UserTypeEnum.ProjectManager || t.UserTypeEnum == UserTypeEnum.APM).ToList();
// 遍历语言枚举 在底层判断收件人是否为空,决定是否跳过邮件发送,避免每个邮件场景的地方都重复判断
foreach (UserWorkLanguage workLanguage in Enum.GetValues<UserWorkLanguage>())
{
//重新设置当前发送邮件的语言
isEn_US = workLanguage == UserWorkLanguage.US;
var messageToSend = new MimeMessage(); var messageToSend = new MimeMessage();
var toUserName = string.Empty; var toUserName = string.Empty;
//有SPM 并且参与 //有SPM
if (trialInfo.IsSPMJoinSiteSurvey && sPMOrCPMList.Count > 0) if (sPMOrCPMList.Count > 0)
{ {
var lan_SPMOrCPMList = sPMOrCPMList.Where(t => t.UserWorkLanguage == workLanguage); foreach (var user in sPMOrCPMList)
foreach (var user in lan_SPMOrCPMList)
{ {
messageToSend.To.Add(new MailboxAddress(user.FullName, user.EMail)); messageToSend.To.Add(new MailboxAddress(user.FullName, user.EMail));
} }
toUserName = string.Join('、', lan_SPMOrCPMList.Select(t => t.FullName)); toUserName = string.Join('、', sPMOrCPMList.Select(t => t.FullName));
foreach (var user in pmAndAPMList) foreach (var user in pmAndAPMList)
{ {
@@ -98,26 +85,27 @@ public class UserSiteSurveySubmitedEventConsumer(
} }
else else
{ {
var lan_pmAndAPMList = pmAndAPMList.Where(t => t.UserWorkLanguage == workLanguage); foreach (var user in pmAndAPMList)
foreach (var user in lan_pmAndAPMList)
{ {
messageToSend.To.Add(new MailboxAddress(user.FullName, user.EMail)); messageToSend.To.Add(new MailboxAddress(user.FullName, user.EMail));
} }
toUserName = string.Join('、', lan_pmAndAPMList.Select(t => t.FullName)); toUserName = string.Join('、', pmAndAPMList.Select(t => t.FullName));
} }
//发件地址 //发件地址
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 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 => Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
{ {
var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo); var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo);
var htmlBodyStr = string.Format(CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr, workLanguage), var htmlBodyStr = string.Format(CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
toUserName, toUserName,
siteInfo.TrialSiteCode, siteInfo.TrialSiteCode,
siteInfo.TrialSiteAliasName, siteInfo.TrialSiteAliasName,
@@ -132,10 +120,9 @@ public class UserSiteSurveySubmitedEventConsumer(
}; };
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(emailConfig, messageToSend, emailConfigFunc, workLanguage); await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(emailConfig, messageToSend, emailConfigFunc);
await SendEmailHelper.SendTrialEmailAsync(messageToSend, trialInfo); await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
}
} }
} }
@@ -164,54 +151,45 @@ public class SiteSurveySPMSubmitedEventConsumer(
var trialId = siteSurveyInfo.TrialId; var trialId = siteSurveyInfo.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 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, t.UserRole.IdentityUser.UserWorkLanguage }).ToList();
var scenario = EmailBusinessScenario.Approval_SubmitSiteSurvey; var scenario = EmailBusinessScenario.Approval_SubmitSiteSurvey;
var emailConfig = _emailNoticeConfigrepository.Where(t => t.BusinessScenarioEnum == scenario && t.IsAutoSend && t.IsEnable).FirstOrDefault(); var emailConfig = _emailNoticeConfigrepository.Where(t => t.BusinessScenarioEnum == scenario && t.IsAutoSend && t.IsEnable).FirstOrDefault();
if (emailConfig != null) if (emailConfig != null)
{ {
var messageToSend = new MimeMessage();
// 遍历语言枚举 在底层判断收件人是否为空,决定是否跳过邮件发送,避免每个邮件场景的地方都重复判断 var trialUserList = _trialUserRoleRepository.Where(t => t.TrialId == trialId && t.TrialUser.IsDeleted == false)
foreach (UserWorkLanguage workLanguage in Enum.GetValues<UserWorkLanguage>()) .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();
//重新设置当前发送邮件的语言
isEn_US = workLanguage == UserWorkLanguage.US;
var sPMOrCPMList = trialUserList.Where(t => t.UserTypeEnum == UserTypeEnum.SPM || t.UserTypeEnum == UserTypeEnum.CPM).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 pmAndAPMList = trialUserList.Where(t => t.UserTypeEnum == UserTypeEnum.ProjectManager || t.UserTypeEnum == UserTypeEnum.APM).ToList();
var messageToSend = new MimeMessage();
var toUserName = string.Empty; var toUserName = string.Empty;
var lan_pmAndAPMList = pmAndAPMList.Where(t => t.UserWorkLanguage == workLanguage); foreach (var item in pmAndAPMList)
foreach (var item in lan_pmAndAPMList)
{ {
messageToSend.To.Add(new MailboxAddress(item.FullName, item.EMail)); messageToSend.To.Add(new MailboxAddress(item.FullName, item.EMail));
toUserName = string.Join('、', lan_pmAndAPMList.Select(t => t.FullName)); toUserName = string.Join('、', pmAndAPMList.Select(t => t.FullName));
} }
//发件地址 //发件地址
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 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 => Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
{ {
var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo); var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo);
var htmlBodyStr = string.Format(CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr, workLanguage), var htmlBodyStr = string.Format(CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
toUserName, toUserName,
siteInfo.TrialSiteCode, //中心编号 siteInfo.TrialSiteCode, //中心编号
siteInfo.TrialSiteAliasName,//中心名称 siteInfo.TrialSiteAliasName,//中心名称
@@ -225,13 +203,9 @@ public class SiteSurveySPMSubmitedEventConsumer(
return (topicStr, htmlBodyStr); return (topicStr, htmlBodyStr);
}; };
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(emailConfig, messageToSend, emailConfigFunc, workLanguage); await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(emailConfig, messageToSend, emailConfigFunc);
await SendEmailHelper.SendTrialEmailAsync(messageToSend, trialInfo);
}
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
} }
@@ -264,21 +238,12 @@ public class SiteSurverRejectedEventConsumer(
var trialId = siteSurveyInfo.TrialId; var trialId = siteSurveyInfo.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 scenario = EmailBusinessScenario.SiteSurveyReject; var scenario = EmailBusinessScenario.SiteSurveyReject;
var emailConfig = _emailNoticeConfigrepository.Where(t => t.BusinessScenarioEnum == scenario && t.IsAutoSend && t.IsEnable).FirstOrDefault(); var emailConfig = _emailNoticeConfigrepository.Where(t => t.BusinessScenarioEnum == scenario && t.IsAutoSend && t.IsEnable).FirstOrDefault();
if (emailConfig != null) if (emailConfig != null)
{ {
// 遍历语言枚举 在底层判断收件人是否为空,决定是否跳过邮件发送,避免每个邮件场景的地方都重复判断
foreach (UserWorkLanguage workLanguage in Enum.GetValues<UserWorkLanguage>())
{
//重新设置当前发送邮件的语言
isEn_US = workLanguage == UserWorkLanguage.US;
var messageToSend = new MimeMessage(); var messageToSend = new MimeMessage();
@@ -290,31 +255,27 @@ public class SiteSurverRejectedEventConsumer(
//PM 驳回到SPM //PM 驳回到SPM
if (siteSurveyInfo.State == TrialSiteSurveyEnum.CRCSubmitted) if (siteSurveyInfo.State == TrialSiteSurveyEnum.CRCSubmitted)
{ {
//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) var sPMOrCPMList = _trialUserRoleRepository.Where(t => t.TrialId == trialId && t.TrialUser.IsDeleted == false)
.Where(t => t.UserRole.UserTypeEnum == UserTypeEnum.SPM || t.UserRole.UserTypeEnum == UserTypeEnum.CPM) .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, t.UserRole.IdentityUser.UserWorkLanguage }).ToList(); .Select(t => new { t.UserRole.IdentityUser.EMail, t.UserRole.FullName, t.UserRole.UserTypeEnum }).ToList();
var lan_SPMOrCPMList = sPMOrCPMList.Where(t => t.UserWorkLanguage == workLanguage);
foreach (var user in lan_SPMOrCPMList) foreach (var user in sPMOrCPMList)
{ {
messageToSend.To.Add(new MailboxAddress(user.FullName, user.EMail)); messageToSend.To.Add(new MailboxAddress(user.FullName, user.EMail));
} }
toUserName = string.Join('、', lan_SPMOrCPMList.Select(t => t.FullName)); toUserName = string.Join('、', sPMOrCPMList.Select(t => t.FullName));
} }
//SPM 驳回到CRC //SPM 驳回到CRC
else if (siteSurveyInfo.State == TrialSiteSurveyEnum.ToSubmit) else if (siteSurveyInfo.State == TrialSiteSurveyEnum.ToSubmit)
{
//使用中心国家语言 一致才发送
if ((siteInfo.Country == StaticData.SiteCountry.US && isEn_US) || (siteInfo.Country == StaticData.SiteCountry.CN && isEn_US == false))
{ {
messageToSend.To.Add(new MailboxAddress(toUserName, siteSurveyInfo.Email)); messageToSend.To.Add(new MailboxAddress(toUserName, siteSurveyInfo.Email));
}
} }
} }
@@ -322,24 +283,25 @@ public class SiteSurverRejectedEventConsumer(
{ {
//没有SPM PM驳回到CRC //没有SPM PM驳回到CRC
//使用中心国家语言 一致才发送 messageToSend.To.Add(new MailboxAddress(String.Empty, siteSurveyInfo.Email));
if ((siteInfo.Country == StaticData.SiteCountry.US && isEn_US) || (siteInfo.Country == StaticData.SiteCountry.CN && isEn_US == false))
{
messageToSend.To.Add(new MailboxAddress(toUserName, 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 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 => Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
{ {
var topicStr = string.Format(input.topicStr, companyName,trialInfo.ResearchProgramNo); var topicStr = string.Format(input.topicStr, companyName,trialInfo.ResearchProgramNo);
var htmlBodyStr = string.Format(CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr, workLanguage), var htmlBodyStr = string.Format(CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
toUserName, toUserName,
trialInfo.TrialCode, trialInfo.TrialCode,
trialInfo.ResearchProgramNo, trialInfo.ResearchProgramNo,
@@ -357,11 +319,7 @@ public class SiteSurverRejectedEventConsumer(
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(emailConfig, messageToSend, emailConfigFunc); await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(emailConfig, messageToSend, emailConfigFunc);
await SendEmailHelper.SendTrialEmailAsync(messageToSend, trialInfo); await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
}
} }
@@ -36,7 +36,7 @@ public class CRCSubmitedAndQCToAuditEventConsumer(
private readonly SystemEmailSendConfig _systemEmailConfig = systemEmailConfig.CurrentValue; private readonly SystemEmailSendConfig _systemEmailConfig = systemEmailConfig.CurrentValue;
public async Task Consume(ConsumeContext<CRCSubmitedAndQCToAuditEvent> context) public async Task Consume(ConsumeContext<CRCSubmitedAndQCToAuditEvent> context)
{ {
Log.Logger.Warning("发送(005,006) 【加急项目所有IQC待领取质控任务】邮件!!!"); Console.WriteLine("发送(005,006) 【加急项目所有IQC待领取质控任务】邮件!!!");
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US; var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
var subjectVisitId = context.Message.SubjectVisitId; var subjectVisitId = context.Message.SubjectVisitId;
@@ -49,25 +49,17 @@ public class CRCSubmitedAndQCToAuditEventConsumer(
var trialEmailConfig = _trialEmailNoticeConfigrepository.Where(t => t.TrialId == trialId && t.BusinessScenarioEnum == scenario && t.IsAutoSend && t.IsEnable).FirstOrDefault(); var trialEmailConfig = _trialEmailNoticeConfigrepository.Where(t => t.TrialId == trialId && t.BusinessScenarioEnum == scenario && t.IsAutoSend && t.IsEnable).FirstOrDefault();
var trialUserList = await _trialUseRoleRepository.Where(x => x.TrialId == subjectVisit.TrialId && x.TrialUser.IsDeleted == false)
.Select(t => new { t.UserRole.IdentityUser.EMail, t.UserRole.FullName, t.UserRole.UserTypeEnum, t.UserRole.IdentityUser.UserWorkLanguage }).ToListAsync();
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == subjectVisit.TrialId);
if (trialEmailConfig != null) if (trialEmailConfig != null)
{ {
// 遍历语言枚举 在底层判断收件人是否为空,决定是否跳过邮件发送,避免每个邮件场景的地方都重复判断
foreach (UserWorkLanguage workLanguage in Enum.GetValues<UserWorkLanguage>())
{
//重新设置当前发送邮件的语言
isEn_US = workLanguage == UserWorkLanguage.US;
var trialUserList = await _trialUseRoleRepository.Where(x => x.TrialId == subjectVisit.TrialId && x.TrialUser.IsDeleted == false).Include(x => x.UserRole).Select(x => x.UserRole).ToListAsync();
var iqcList = trialUserList.Where(x => x.UserTypeEnum == UserTypeEnum.IQC).Where(t => t.UserWorkLanguage == workLanguage).ToList(); 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 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"; var auditStateCode = "AuditStatePE";
@@ -76,6 +68,8 @@ public class CRCSubmitedAndQCToAuditEventConsumer(
auditStateCode = "AuditStateRC"; auditStateCode = "AuditStateRC";
} }
var dictionValue = await CommonEmailHelper.TranslationDictionary(new TranslationDictionaryDto() var dictionValue = await CommonEmailHelper.TranslationDictionary(new TranslationDictionaryDto()
{ {
@@ -93,16 +87,16 @@ public class CRCSubmitedAndQCToAuditEventConsumer(
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail)); messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
foreach (var userinfo in iqcList) foreach (var userinfo in userinfoList)
{ {
messageToSend.To.Add(new MailboxAddress(userinfo.FullName, userinfo.EMail)); messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
} }
var userNames = iqcList.Select(x => x.FullName).ToList(); var userNames = userinfoList.Select(x => x.FullName).ToList();
foreach (var pm in pmandAPm) foreach (var pm in pmandAPm)
{ {
messageToSend.Cc.Add(new MailboxAddress(pm.FullName, pm.EMail)); messageToSend.Cc.Add(new MailboxAddress(String.Empty, pm.EMail));
} }
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN; var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
@@ -114,7 +108,7 @@ public class CRCSubmitedAndQCToAuditEventConsumer(
var subjectCode = subjectVisit.Subject.Code; var subjectCode = subjectVisit.Subject.Code;
var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo, subjectCode, subjectVisit.VisitName); var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo, subjectCode, subjectVisit.VisitName);
var htmlBodyStr = string.Format( var htmlBodyStr = string.Format(
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr, workLanguage), CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
string.Join(',', userNames), // 用户名 {0} string.Join(',', userNames), // 用户名 {0}
trialInfo.ExperimentName, // 项目 {1} trialInfo.ExperimentName, // 项目 {1}
subjectCode, // 受试者 {2} subjectCode, // 受试者 {2}
@@ -126,14 +120,9 @@ public class CRCSubmitedAndQCToAuditEventConsumer(
return (topicStr, htmlBodyStr); return (topicStr, htmlBodyStr);
}; };
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc, workLanguage); await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc);
await SendEmailHelper.SendTrialEmailAsync(messageToSend, trialInfo);
}
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
} }
} }
@@ -158,7 +147,7 @@ public class CRCRepliedQCChallengeEventConsumer(
private readonly SystemEmailSendConfig _systemEmailConfig = systemEmailConfig.CurrentValue; private readonly SystemEmailSendConfig _systemEmailConfig = systemEmailConfig.CurrentValue;
public async Task Consume(ConsumeContext<CRCRepliedQCChallengeEvent> context) public async Task Consume(ConsumeContext<CRCRepliedQCChallengeEvent> context)
{ {
Log.Logger.Warning("发送(Code012013) 【CRC 回复质控质疑 通知QC】邮件!!!"); Console.WriteLine("发送(Code012013) 【CRC 回复质控质疑 通知QC】邮件!!!");
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US; var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
var subjectVisitId = context.Message.SubjectVisitId; var subjectVisitId = context.Message.SubjectVisitId;
@@ -169,37 +158,34 @@ public class CRCRepliedQCChallengeEventConsumer(
var scenario = context.Message.IsPd ? EmailBusinessScenario.PDVerification_UnderQCQuery : EmailBusinessScenario.EligibilityVerification_Pending; 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 trialEmailConfig = _trialEmailNoticeConfigrepository.Where(t => t.TrialId == trialId && t.BusinessScenarioEnum == scenario && t.IsAutoSend && t.IsEnable).FirstOrDefault();
if (trialEmailConfig != null) if (trialEmailConfig != null)
{ {
var qCChallengeDialog = await _qCChallengeDialogRepository.Where(x => x.Id == context.Message.QCChallengeDialogId).Include(x => x.QCChallenge).FirstNotNullAsync(); 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)
.Select(t => new { t.UserRole.IdentityUser.EMail, t.UserRole.FullName, t.UserRole.UserTypeEnum, t.UserRole.IdentityUser.UserWorkLanguage }).ToListAsync();
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 userinfoList = trialUserList.Where(x => x.UserTypeEnum == UserTypeEnum.IQC).ToList();
var pmandAPm = trialUserList.Where(x => x.UserTypeEnum == UserTypeEnum.APM || x.UserTypeEnum == UserTypeEnum.ProjectManager).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 trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == subjectVisit.TrialId);
var userinfo = subjectVisit.CurrentActionUser; var userinfo = subjectVisit.CurrentActionUser;
//重新设置当前发送邮件的语言
var workLanguage = _userRoleRepository.Where(t => t.Id == userinfo.Id).Select(t => t.IdentityUser.UserWorkLanguage).First();
isEn_US = workLanguage == UserWorkLanguage.US;
var messageToSend = new MimeMessage(); var messageToSend = new MimeMessage();
//发件地址 //发件地址
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail)); messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
messageToSend.To.Add(new MailboxAddress(userinfo.FullName, userinfo.EMail)); messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
foreach (var pm in pmandAPm) foreach (var pm in pmandAPm)
{ {
messageToSend.Cc.Add(new MailboxAddress(pm.FullName, pm.EMail)); messageToSend.Cc.Add(new MailboxAddress(String.Empty, pm.EMail));
} }
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN; var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
@@ -221,7 +207,7 @@ public class CRCRepliedQCChallengeEventConsumer(
var subjectCode = subjectVisit.Subject.Code; var subjectCode = subjectVisit.Subject.Code;
var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo, subjectCode, subjectVisit.VisitName); var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo, subjectCode, subjectVisit.VisitName);
var htmlBodyStr = string.Format( var htmlBodyStr = string.Format(
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr, workLanguage), CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
userinfo.FullName, // 用户名 {0} userinfo.FullName, // 用户名 {0}
trialInfo.ExperimentName, // 项目 {1} trialInfo.ExperimentName, // 项目 {1}
subjectCode, // 受试者 {2} subjectCode, // 受试者 {2}
@@ -233,9 +219,9 @@ public class CRCRepliedQCChallengeEventConsumer(
return (topicStr, htmlBodyStr); return (topicStr, htmlBodyStr);
}; };
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc, workLanguage); await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc);
await SendEmailHelper.SendTrialEmailAsync(messageToSend, trialInfo); await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
} }
} }
} }
@@ -258,7 +244,7 @@ public class QCRepliedQCChallengeEventConsumer(
private readonly SystemEmailSendConfig _systemEmailConfig = systemEmailConfig.CurrentValue; private readonly SystemEmailSendConfig _systemEmailConfig = systemEmailConfig.CurrentValue;
public async Task Consume(ConsumeContext<QCRepliedQCChallengeEvent> context) public async Task Consume(ConsumeContext<QCRepliedQCChallengeEvent> context)
{ {
Log.Logger.Warning("发送(014015) 【 QC回复 质控质疑,通知CRC】邮件!!!"); Console.WriteLine("发送(014015) 【 QC回复 质控质疑,通知CRC】邮件!!!");
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US; var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
var subjectVisitId = context.Message.SubjectVisitId; var subjectVisitId = context.Message.SubjectVisitId;
@@ -291,23 +277,18 @@ public class QCRepliedQCChallengeEventConsumer(
} }
var trialUserList = await _trialUseRoleRepository.Where(x => x.TrialId == subjectVisit.TrialId && x.TrialUser.IsDeleted == false)
.Select(t => new { t.UserRole.IdentityUser.EMail, t.UserRole.FullName, t.UserRole.UserTypeEnum, t.UserRole.IdentityUser.UserWorkLanguage }).ToListAsync(); 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 trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == subjectVisit.TrialId);
// 遍历语言枚举 在底层判断收件人是否为空,决定是否跳过邮件发送,避免每个邮件场景的地方都重复判断
foreach (UserWorkLanguage workLanguage in Enum.GetValues<UserWorkLanguage>())
{
//重新设置当前发送邮件的语言
isEn_US = workLanguage == UserWorkLanguage.US;
var userinfoList = trialUserList.Where(x => x.UserTypeEnum == UserTypeEnum.ClinicalResearchCoordinator).Where(t => t.UserWorkLanguage == workLanguage).ToList();
var craInfoList = trialUserList.Where(x => x.UserTypeEnum == UserTypeEnum.CRA).ToList();
var dictionValue = await CommonEmailHelper.TranslationDictionary(new TranslationDictionaryDto() var dictionValue = await CommonEmailHelper.TranslationDictionary(new TranslationDictionaryDto()
{ {
@@ -321,31 +302,29 @@ public class QCRepliedQCChallengeEventConsumer(
}); });
foreach (var userinfo in userinfoList)
{
var messageToSend = new MimeMessage(); var messageToSend = new MimeMessage();
//发件地址 //发件地址
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail)); messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
var toUserName = string.Join('、', userinfoList.Select(t => t.FullName)); if (craInfo != null)
foreach (var userinfo in userinfoList)
{ {
messageToSend.To.Add(new MailboxAddress(userinfo.FullName, userinfo.EMail)); messageToSend.Cc.Add(new MailboxAddress(String.Empty, craInfo.EMail));
}
foreach (var user in craInfoList)
{
messageToSend.Cc.Add(new MailboxAddress(user.FullName, user.EMail));
} }
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 => Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
{ {
var subjectCode = subjectVisit.Subject.Code; var subjectCode = subjectVisit.Subject.Code;
var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo, subjectCode, subjectVisit.VisitName); var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo, subjectCode, subjectVisit.VisitName);
var htmlBodyStr = string.Format( var htmlBodyStr = string.Format(
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr, workLanguage), CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
toUserName, // 用户名 {0} userinfo.FullName, // 用户名 {0}
trialInfo.ExperimentName, // 项目 {1} trialInfo.ExperimentName, // 项目 {1}
subjectCode, // 受试者 {2} subjectCode, // 受试者 {2}
subjectVisit.VisitName, // 访视 {3} subjectVisit.VisitName, // 访视 {3}
@@ -358,13 +337,10 @@ public class QCRepliedQCChallengeEventConsumer(
}; };
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc, workLanguage); await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc);
await SendEmailHelper.SendTrialEmailAsync(messageToSend, trialInfo);
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
} }
} }
} }
} }
@@ -388,7 +364,7 @@ public class CRCRepliedCheckChallengeEventConsumer(
private readonly SystemEmailSendConfig _systemEmailConfig = systemEmailConfig.CurrentValue; private readonly SystemEmailSendConfig _systemEmailConfig = systemEmailConfig.CurrentValue;
public async Task Consume(ConsumeContext<CRCRepliedCheckChallengeEvent> context) public async Task Consume(ConsumeContext<CRCRepliedCheckChallengeEvent> context)
{ {
Log.Logger.Warning("发送(019,020) 【 CRC 回复一致性核查质疑 通知PM】邮件!!!"); Console.WriteLine("发送(019,020) 【 CRC 回复一致性核查质疑 通知PM】邮件!!!");
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US; var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
@@ -407,18 +383,13 @@ public class CRCRepliedCheckChallengeEventConsumer(
{ {
var trialUserList = await _trialUseRoleRepository.Where(x => x.TrialId == subjectVisit.TrialId && x.TrialUser.IsDeleted == false) var trialUserList = await _trialUseRoleRepository.Where(x => x.TrialId == subjectVisit.TrialId && x.TrialUser.IsDeleted == false).Include(x => x.UserRole).Select(x => x.UserRole).ToListAsync();
.Select(t => new { t.UserRole.IdentityUser.EMail, t.UserRole.FullName, t.UserRole.UserTypeEnum, t.UserRole.IdentityUser.UserWorkLanguage }).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 trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == subjectVisit.TrialId);
// 遍历语言枚举 在底层判断收件人是否为空,决定是否跳过邮件发送,避免每个邮件场景的地方都重复判断
foreach (UserWorkLanguage workLanguage in Enum.GetValues<UserWorkLanguage>())
{
//重新设置当前发送邮件的语言
isEn_US = workLanguage == UserWorkLanguage.US;
@@ -434,31 +405,27 @@ public class CRCRepliedCheckChallengeEventConsumer(
} }
}); });
var userinfoList = trialUserList.Where(x => x.UserTypeEnum == UserTypeEnum.APM || x.UserTypeEnum == UserTypeEnum.ProjectManager)
.Where(t => t.UserWorkLanguage == workLanguage).ToList();
var messageToSend = new MimeMessage();
//发件地址
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
var toUserName = string.Join('、', userinfoList.Select(t => t.FullName));
foreach (var userinfo in userinfoList) foreach (var userinfo in userinfoList)
{ {
messageToSend.To.Add(new MailboxAddress(userinfo.FullName, userinfo.EMail)); 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; var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input => Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
{ {
var subjectCode = subjectVisit.Subject.Code; var subjectCode = subjectVisit.Subject.Code;
var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo, subjectCode, subjectVisit.VisitName); var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo, subjectCode, subjectVisit.VisitName);
var htmlBodyStr = string.Format( var htmlBodyStr = string.Format(
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr, workLanguage), CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
toUserName, // 用户名 {0} userinfo.FullName, // 用户名 {0}
trialInfo.ExperimentName, // 项目 {1} trialInfo.ExperimentName, // 项目 {1}
subjectCode, // 受试者 {2} subjectCode, // 受试者 {2}
subjectVisit.VisitName, // 访视 {3} subjectVisit.VisitName, // 访视 {3}
@@ -470,17 +437,10 @@ public class CRCRepliedCheckChallengeEventConsumer(
}; };
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc, workLanguage); await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc);
await SendEmailHelper.SendTrialEmailAsync(messageToSend, trialInfo);
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
} }
} }
} }
} }
@@ -506,12 +466,12 @@ public class PMRepliedCheckChallengeEventConsumer(
private readonly SystemEmailSendConfig _systemEmailConfig = systemEmailConfig.CurrentValue; private readonly SystemEmailSendConfig _systemEmailConfig = systemEmailConfig.CurrentValue;
public async Task Consume(ConsumeContext<PMRepliedCheckChallengeEvent> context) public async Task Consume(ConsumeContext<PMRepliedCheckChallengeEvent> context)
{ {
Log.Logger.Warning("发送(016017) 【 PM 一致性核查 通知CRC】邮件!!!"); Console.WriteLine("发送(016017) 【 PM 一致性核查 通知CRC】邮件!!!");
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US; var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
var subjectVisitId = context.Message.SubjectVisitId; 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).Include(t => t.TrialSite).FirstNotNullAsync(); var subjectVisit = await _subjectVisitRepository.Where(x => x.Id == subjectVisitId).Include(x => x.NoneDicomStudyList).Include(x => x.StudyList).Include(x => x.Subject).FirstNotNullAsync();
var trialId = subjectVisit.TrialId; var trialId = subjectVisit.TrialId;
@@ -526,11 +486,11 @@ public class PMRepliedCheckChallengeEventConsumer(
var checkChallengeDialog = await _checkChallengeDialogRepository.Where(x => x.Id == context.Message.CheckChallengeDialogId).FirstNotNullAsync(); 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) var trialUserList = await _trialUseRoleRepository.Where(x => x.TrialId == subjectVisit.TrialId && x.TrialUser.IsDeleted == false).Include(x => x.UserRole).Select(x => x.UserRole).ToListAsync();
.Select(t => new { t.UserRole.IdentityUser.EMail, t.UserRole.FullName, t.UserRole.UserTypeEnum, t.UserRole.IdentityUser.UserWorkLanguage }).ToListAsync();
var userinfoList = trialUserList.Where(x => x.UserTypeEnum == UserTypeEnum.ClinicalResearchCoordinator).ToList();
var craInfoList = trialUserList.Where(x => x.UserTypeEnum == UserTypeEnum.CRA).ToList(); var craInfo = trialUserList.Where(x => x.UserTypeEnum == UserTypeEnum.CRA).FirstOrDefault();
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == subjectVisit.TrialId); var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == subjectVisit.TrialId);
@@ -539,20 +499,6 @@ public class PMRepliedCheckChallengeEventConsumer(
modalities = subjectVisit.NoneDicomStudyList.Select(t => t.Modality) modalities = subjectVisit.NoneDicomStudyList.Select(t => t.Modality)
.Union(subjectVisit.StudyList.Select(k => k.ModalityForEdit)).ToList(); .Union(subjectVisit.StudyList.Select(k => k.ModalityForEdit)).ToList();
// 遍历语言枚举 在底层判断收件人是否为空,决定是否跳过邮件发送,避免每个邮件场景的地方都重复判断
//foreach (UserWorkLanguage workLanguage in Enum.GetValues<UserWorkLanguage>())
{
//重新设置当前发送邮件的语言
isEn_US = subjectVisit.TrialSite.Country == StaticData.SiteCountry.US;
var workLanguage = isEn_US ? UserWorkLanguage.US : UserWorkLanguage.CN;
var userinfoList = trialUserList.Where(x => x.UserTypeEnum == UserTypeEnum.ClinicalResearchCoordinator).Where(t => t.UserWorkLanguage == workLanguage).ToList();
var dictionValue = await CommonEmailHelper.TranslationDictionary(new TranslationDictionaryDto() var dictionValue = await CommonEmailHelper.TranslationDictionary(new TranslationDictionaryDto()
{ {
@@ -565,20 +511,16 @@ public class PMRepliedCheckChallengeEventConsumer(
} }
}); });
var messageToSend = new MimeMessage();
//发件地址
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
var toUserName = string.Join('、', userinfoList.Select(t => t.FullName));
foreach (var userinfo in userinfoList) foreach (var userinfo in userinfoList)
{ {
messageToSend.To.Add(new MailboxAddress(userinfo.FullName, userinfo.EMail)); var messageToSend = new MimeMessage();
} //发件地址
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
foreach (var user in craInfoList) messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
if (craInfo != null)
{ {
messageToSend.Cc.Add(new MailboxAddress(user.FullName, user.EMail)); messageToSend.Cc.Add(new MailboxAddress(String.Empty, craInfo.EMail));
} }
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN; var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
@@ -589,8 +531,8 @@ public class PMRepliedCheckChallengeEventConsumer(
var subjectCode = subjectVisit.Subject.Code; var subjectCode = subjectVisit.Subject.Code;
var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo, subjectCode, subjectVisit.VisitName); var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo, subjectCode, subjectVisit.VisitName);
var htmlBodyStr = string.Format( var htmlBodyStr = string.Format(
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr, workLanguage), CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
toUserName, // 用户名 {0} userinfo.FullName, // 用户名 {0}
trialInfo.ExperimentName, // 项目 {1} trialInfo.ExperimentName, // 项目 {1}
subjectCode, // 受试者 {2} subjectCode, // 受试者 {2}
@@ -605,11 +547,10 @@ public class PMRepliedCheckChallengeEventConsumer(
}; };
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc, workLanguage); await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc);
await SendEmailHelper.SendTrialEmailAsync(messageToSend, trialInfo); await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
} }
} }
} }
} }
@@ -651,22 +592,15 @@ public class CheckStateChangedToAuditEventConsumer(
var trialUserList = await _trialUseRoleRepository.Where(x => x.TrialId == subjectVisit.TrialId && x.TrialUser.IsDeleted == false) var trialUserList = await _trialUseRoleRepository.Where(x => x.TrialId == subjectVisit.TrialId && x.TrialUser.IsDeleted == false).Include(x => x.UserRole).Select(x => x.UserRole).ToListAsync();
.Select(t => new { t.UserRole.IdentityUser.EMail, t.UserRole.FullName, t.UserRole.UserTypeEnum, t.UserRole.IdentityUser.UserWorkLanguage }).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 trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == subjectVisit.TrialId);
// 遍历语言枚举 在底层判断收件人是否为空,决定是否跳过邮件发送,避免每个邮件场景的地方都重复判断
foreach (UserWorkLanguage workLanguage in Enum.GetValues<UserWorkLanguage>())
{
//重新设置当前发送邮件的语言
isEn_US = workLanguage == UserWorkLanguage.US;
var userinfoList = trialUserList.Where(x => x.UserTypeEnum == UserTypeEnum.APM || x.UserTypeEnum == UserTypeEnum.ProjectManager)
.Where(t => t.UserWorkLanguage == workLanguage).ToList();
var dictionValue = await CommonEmailHelper.TranslationDictionary(new TranslationDictionaryDto() var dictionValue = await CommonEmailHelper.TranslationDictionary(new TranslationDictionaryDto()
{ {
@@ -681,27 +615,26 @@ public class CheckStateChangedToAuditEventConsumer(
}); });
foreach (var userinfo in userinfoList)
{
var messageToSend = new MimeMessage(); var messageToSend = new MimeMessage();
//发件地址 //发件地址
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail)); messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
var toUserName = string.Join('、', userinfoList.Select(t => t.FullName));
foreach (var userinfo in userinfoList)
{
messageToSend.To.Add(new MailboxAddress(userinfo.FullName, userinfo.EMail));
}
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 => Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
{ {
var subjectCode = subjectVisit.Subject.Code; var subjectCode = subjectVisit.Subject.Code;
var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo, subjectCode, subjectVisit.VisitName); var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo, subjectCode, subjectVisit.VisitName);
var htmlBodyStr = string.Format( var htmlBodyStr = string.Format(
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr, workLanguage), CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
toUserName, // 姓名 {0} userinfo.FullName, // 姓名 {0}
trialInfo.ExperimentName, // 项目 {1} trialInfo.ExperimentName, // 项目 {1}
subjectCode, // 受试者 {2} subjectCode, // 受试者 {2}
subjectVisit.VisitName, // 访视 {3} subjectVisit.VisitName, // 访视 {3}
@@ -713,11 +646,10 @@ public class CheckStateChangedToAuditEventConsumer(
}; };
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc, workLanguage); await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc);
await SendEmailHelper.SendTrialEmailAsync(messageToSend, trialInfo); await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
} }
} }
} }
} }
@@ -739,7 +671,7 @@ public class QCClaimTaskEventConsumer(
private readonly SystemEmailSendConfig _systemEmailConfig = systemEmailConfig.CurrentValue; private readonly SystemEmailSendConfig _systemEmailConfig = systemEmailConfig.CurrentValue;
public async Task Consume(ConsumeContext<QCClaimTaskEvent> context) public async Task Consume(ConsumeContext<QCClaimTaskEvent> context)
{ {
Log.Logger.Warning("发送(Code007008) 【QC 领取了质控任务】邮件!!!"); Console.WriteLine("发送(Code007008) 【QC 领取了质控任务】邮件!!!");
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US; var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
@@ -771,23 +703,15 @@ public class QCClaimTaskEventConsumer(
var userinfo = subjectVisit.CurrentActionUser; var userinfo = subjectVisit.CurrentActionUser;
if (userinfo == null)
{
return;
}
//重新设置当前发送邮件的语言
var workLanguage = _userRoleRepository.Where(t => t.Id == userinfo.Id).Select(t => t.IdentityUser.UserWorkLanguage).First();
isEn_US = workLanguage == UserWorkLanguage.US;
var messageToSend = new MimeMessage(); var messageToSend = new MimeMessage();
//发件地址 //发件地址
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail)); messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
messageToSend.To.Add(new MailboxAddress(userinfo.FullName, userinfo.EMail)); messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
foreach (var pm in pmandAPm) foreach (var pm in pmandAPm)
{ {
messageToSend.Cc.Add(new MailboxAddress(pm.FullName, pm.EMail)); messageToSend.Cc.Add(new MailboxAddress(String.Empty, pm.EMail));
} }
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN; var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
@@ -817,7 +741,7 @@ public class QCClaimTaskEventConsumer(
var subjectCode = subjectVisit.Subject.Code; var subjectCode = subjectVisit.Subject.Code;
var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo, subjectCode, subjectVisit.VisitName); var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo, subjectCode, subjectVisit.VisitName);
var htmlBodyStr = string.Format( var htmlBodyStr = string.Format(
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr, workLanguage), CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
userinfo.FullName, // 用户名 {0} userinfo.FullName, // 用户名 {0}
trialInfo.ExperimentName, // 项目 {1} trialInfo.ExperimentName, // 项目 {1}
string.Join(',', subjectcodes), // 受试者 {2} string.Join(',', subjectcodes), // 受试者 {2}
@@ -831,9 +755,9 @@ public class QCClaimTaskEventConsumer(
}; };
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc, workLanguage); await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc);
await SendEmailHelper.SendTrialEmailAsync(messageToSend, trialInfo); await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
} }
} }
} }
@@ -1,16 +1,14 @@
using IRaCIS.Core.Application.Helper.OtherTool; using System;
using IRaCIS.Core.Domain.BaseModel;
using IRaCIS.Core.Domain.Share;
using MassTransit;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System;
using System.Globalization; using System.Globalization;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using IRaCIS.Core.Domain.BaseModel;
using IRaCIS.Core.Domain.Share;
using MassTransit;
using Microsoft.Extensions.Logging;
using NPOI.SS.Formula.Functions;
public class ConsumeExceptionFilter<T>(ILogger<ConsumeExceptionFilter<T>> _logger, IConfiguration _config) : IFilter<ConsumeContext<T>> where T : DomainEvent public class ConsumeExceptionFilter<T>(ILogger<ConsumeExceptionFilter<T>> _logger) : IFilter<ConsumeContext<T>> where T : DomainEvent
{ {
public async Task Send(ConsumeContext<T> context, IPipe<ConsumeContext<T>> next) public async Task Send(ConsumeContext<T> context, IPipe<ConsumeContext<T>> next)
@@ -25,44 +23,6 @@ public class ConsumeExceptionFilter<T>(ILogger<ConsumeExceptionFilter<T>> _logge
{ {
var errorInfo = $"Exception: {exception.Message}[{exception.StackTrace}]" + (exception.InnerException != null ? $" InnerException: {exception.InnerException.Message}[{exception.InnerException.StackTrace}]" : ""); var errorInfo = $"Exception: {exception.Message}[{exception.StackTrace}]" + (exception.InnerException != null ? $" InnerException: {exception.InnerException.Message}[{exception.InnerException.StackTrace}]" : "");
_logger.LogError(errorInfo); _logger.LogError(errorInfo);
try
{
bool isOpenWeComNotice = _config.GetValue<bool>("WeComNoticeConfig:IsOpenWeComNotice");
if (isOpenWeComNotice)
{
string webhook = _config["WeComNoticeConfig:WebhookUrl"] ?? string.Empty;
var uri = new Uri(_config["SystemEmailSendConfig:SiteUrl"]);
var baseUrl = uri.GetLeftPart(UriPartial.Authority);
var userList = _config.GetSection("WeComNoticeConfig:APINoticeUserList").Get<string[]>();
// 🔔 异步告警(不要阻塞请求)
_ = WeComNotifier.SendAlertAsync(
webhook: webhook,
alert: new WeComAlert
{
Env = baseUrl,
UserName = "MassTransit 自动触发邮件",
Api = "",
Message = $"异常信息:{exception.Message} ",
Stack = exception.StackTrace,
AtUsers = userList ?? []
}
);
}
}
catch (Exception ex)
{
Log.Logger.Error($"MassTransit里发送企业微信出现错误:{ex.Message}");
}
} }
} }
@@ -5,7 +5,6 @@ using IRaCIS.Core.Application.Contracts;
using IRaCIS.Core.Application.Helper; using IRaCIS.Core.Application.Helper;
using IRaCIS.Core.Application.MassTransit.Consumer; using IRaCIS.Core.Application.MassTransit.Consumer;
using IRaCIS.Core.Application.Service.Reading.Dto; using IRaCIS.Core.Application.Service.Reading.Dto;
using IRaCIS.Core.Domain.Models;
using MassTransit; using MassTransit;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using MimeKit; using MimeKit;
@@ -27,7 +26,6 @@ namespace IRaCIS.Core.Application.MassTransit.Recurring
public class UrgentIRUnReadTaskRecurringEventConsumer( public class UrgentIRUnReadTaskRecurringEventConsumer(
IRepository<ReadingQuestionCriterionTrial> _trialReadingCriterionRepository, IRepository<ReadingQuestionCriterionTrial> _trialReadingCriterionRepository,
IRepository<VisitTask> _visitTaskRepository, IRepository<VisitTask> _visitTaskRepository,
IRepository<Trial> _trialRepository,
IRepository<Dictionary> _dictionaryRepository, IRepository<Dictionary> _dictionaryRepository,
IRepository<TrialUserRole> _trialUserRoleRepository, IRepository<TrialUserRole> _trialUserRoleRepository,
IRepository<TrialEmailNoticeConfig> _trialEmailNoticeConfigrepository, IRepository<TrialEmailNoticeConfig> _trialEmailNoticeConfigrepository,
@@ -37,15 +35,9 @@ namespace IRaCIS.Core.Application.MassTransit.Recurring
public async Task Consume(ConsumeContext<UrgentIRUnReadTaskRecurringEvent> context) public async Task Consume(ConsumeContext<UrgentIRUnReadTaskRecurringEvent> context)
{ {
var isEn_US = context.Message.CultureInfoName == StaticData.CultureInfo.en_US; var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
//设置当前事件传递过来的语言
var culture = context.Message.CultureInfoName;
CultureInfo.CurrentCulture = new CultureInfo(culture);
var trialId = context.Message.TrialId; var trialId = context.Message.TrialId;
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == trialId);
var scenario = EmailBusinessScenario.ExpeditedReading; var scenario = EmailBusinessScenario.ExpeditedReading;
var trialEmailConfig = _trialEmailNoticeConfigrepository.Where(t => t.TrialId == trialId && t.BusinessScenarioEnum == scenario && t.IsAutoSend && t.IsEnable).FirstOrDefault(); var trialEmailConfig = _trialEmailNoticeConfigrepository.Where(t => t.TrialId == trialId && t.BusinessScenarioEnum == scenario && t.IsAutoSend && t.IsEnable).FirstOrDefault();
@@ -74,17 +66,13 @@ namespace IRaCIS.Core.Application.MassTransit.Recurring
t.UserRole.IdentityUser.EMail, t.UserRole.IdentityUser.EMail,
t.UserRole.IdentityUser.UserName, t.UserRole.IdentityUser.UserName,
t.Trial.TrialCode, t.Trial.TrialCode,
t.Trial.ResearchProgramNo, t.Trial.ResearchProgramNo
t.UserRole.IdentityUser.UserWorkLanguage
//TrialReadingCriterionList = t.Trial.TrialReadingCriterionList.Select(t => new { t.CriterionName, TrialReadingCriterionId = t.Id }).ToList() //TrialReadingCriterionList = t.Trial.TrialReadingCriterionList.Select(t => new { t.CriterionName, TrialReadingCriterionId = t.Id }).ToList()
}); });
foreach (var trialUser in trialUserList) foreach (var trialUser in trialUserList)
{ {
//重新设置当前发送邮件的语言
var workLanguage = trialUser.UserWorkLanguage;
isEn_US = workLanguage == UserWorkLanguage.US;
var userId = trialUser.UserId; var userId = trialUser.UserId;
@@ -157,7 +145,7 @@ namespace IRaCIS.Core.Application.MassTransit.Recurring
var htmlBodyStr = string.Format(CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, emailContent, workLanguage), var htmlBodyStr = string.Format(CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, emailContent),
trialUser.FullName, trialUser.FullName,
DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
@@ -171,9 +159,9 @@ namespace IRaCIS.Core.Application.MassTransit.Recurring
return (topicStr, htmlBodyStr); return (topicStr, htmlBodyStr);
}; };
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc, workLanguage); await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc);
await SendEmailHelper.SendTrialEmailAsync(messageToSend, trialInfo); await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
//处理标记已通知的任务 //处理标记已通知的任务
@@ -18,7 +18,7 @@ public static class OldRecurringEmailHelper
public static string EmailNamePlaceholder => StaticData.EmailSend.EmailNamePlaceholder; public static string EmailNamePlaceholder => StaticData.EmailSend.EmailNamePlaceholder;
//发送项目邮件的通用方法 已经处理
public static async Task SendTrialEmailAsync( public static async Task SendTrialEmailAsync(
IRepository<TrialEmailNoticeConfig> _trialEmailNoticeConfigRepository, IRepository<TrialEmailNoticeConfig> _trialEmailNoticeConfigRepository,
IRepository<Trial> _trialRepository, IRepository<Trial> _trialRepository,
@@ -34,7 +34,6 @@ public static class OldRecurringEmailHelper
var trialEmailConfig = await _trialEmailNoticeConfigRepository.Where(t => t.TrialId == trialId && 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(); .Include(t => t.TrialEmailNoticeUserList).Include(t => t.TrialEmailBlackUserList).FirstOrDefaultAsync();
var trialInfo=await _trialRepository.Where(t=>t.Id== trialId).FirstOrDefaultAsync();
if (trialEmailConfig == null || trialEmailConfig.IsAutoSend == false || trialEmailConfig.IsEnable == false) if (trialEmailConfig == null || trialEmailConfig.IsAutoSend == false || trialEmailConfig.IsEnable == false)
{ {
@@ -163,7 +162,7 @@ public static class OldRecurringEmailHelper
if (sendEmailConfig != null) if (sendEmailConfig != null)
{ {
await SendEmailHelper.SendEmailAsync(sendEmailConfig, trialInfo); await SendEmailHelper.SendEmailAsync(sendEmailConfig);
} }
@@ -7,7 +7,6 @@ using MassTransit.Scheduling;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -15,7 +14,7 @@ using System.Threading.Tasks;
namespace IRaCIS.Core.Application.MassTransit.Consumer; namespace IRaCIS.Core.Application.MassTransit.Consumer;
//项目手动选择 周期性邮件
/// <summary> /// <summary>
@@ -35,26 +34,17 @@ public class QCImageQuestionRecurringEventConsumer(IRepository<Trial> _trialRepo
{ {
var trialId = context.Message.TrialId; var trialId = context.Message.TrialId;
var isEn_us = context.Message.CultureInfoName == StaticData.CultureInfo.en_US; var isEn_us = false;
//设置当前事件传递过来的语言
var culture = context.Message.CultureInfoName;
CultureInfo.CurrentCulture = new CultureInfo(culture);
var trialInfo = await _trialRepository.Where(t => t.Id == trialId).Select(t => new { t.ResearchProgramNo, t.ExperimentName, t.TrialCode, t.TrialStatusStr }).FirstNotNullAsync(); var trialInfo = await _trialRepository.Where(t => t.Id == trialId).Select(t => new { t.ResearchProgramNo, t.ExperimentName, t.TrialCode, t.TrialStatusStr }).FirstNotNullAsync();
//找到 该项目的CRC 用户Id //找到 该项目的CRC 用户Id
var userList = await _trialUserRoleRepository.Where(t => t.TrialId == trialId).Where(t => t.UserRole.UserTypeEnum == UserTypeEnum.ClinicalResearchCoordinator).Select(t => new { t.UserId, t.UserRole.FullName, t.UserRole.IdentityUser.UserWorkLanguage }).ToListAsync(); var userList = await _trialUserRoleRepository.Where(t => t.TrialId == trialId).Where(t => t.UserRole.UserTypeEnum == UserTypeEnum.ClinicalResearchCoordinator).Select(t => new { t.UserId, t.UserRole.FullName }).ToListAsync();
//判断是否任务可以领取 ,可以的话 发送邮件 //判断是否任务可以领取 ,可以的话 发送邮件
foreach (var user in userList) foreach (var user in userList)
{ {
//重新设置当前发送邮件的语言
var workLanguage = user.UserWorkLanguage;
isEn_us = workLanguage == UserWorkLanguage.US;
var userId = user.UserId; var userId = user.UserId;
//过滤项目 并且 将 _userInfo.Id 换位 当前发送邮件的Id //过滤项目 并且 将 _userInfo.Id 换位 当前发送邮件的Id
var query = _trialRepository.Where(t => t.Id == trialId) var query = _trialRepository.Where(t => t.Id == trialId)
@@ -77,7 +67,7 @@ public class QCImageQuestionRecurringEventConsumer(IRepository<Trial> _trialRepo
{ {
var topicStr = string.Format(isEn_us ? trialEmailConfig.EmailTopic : trialEmailConfig.EmailTopicCN, trialInfo.ResearchProgramNo); var topicStr = string.Format(isEn_us ? trialEmailConfig.EmailTopic : trialEmailConfig.EmailTopicCN, trialInfo.ResearchProgramNo);
var htmlContent = isEn_us ? trialEmailConfig.EmailHtmlContent : trialEmailConfig.EmailHtmlContentCN; var htmlContent = isEn_us ? trialEmailConfig.EmailHtmlContent : trialEmailConfig.EmailHtmlContentCN;
var htmlBodyStr = string.Format(CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, htmlContent, workLanguage), var htmlBodyStr = string.Format(CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, htmlContent),
user.FullName, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), sendStat.ToBeDealedCount, _SystemEmailSendConfig.CurrentValue.SiteUrl); user.FullName, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), sendStat.ToBeDealedCount, _SystemEmailSendConfig.CurrentValue.SiteUrl);
return (topicStr, htmlBodyStr, isEn_us, userId); return (topicStr, htmlBodyStr, isEn_us, userId);
@@ -112,26 +102,16 @@ public class CRCImageQuestionRecurringEventConsumer(IRepository<Trial> _trialRep
{ {
var trialId = context.Message.TrialId; var trialId = context.Message.TrialId;
var isEn_us = context.Message.CultureInfoName == StaticData.CultureInfo.en_US; var isEn_us = false;
//设置当前事件传递过来的语言
var culture = context.Message.CultureInfoName;
CultureInfo.CurrentCulture = new CultureInfo(culture);
var trialInfo = await _trialRepository.Where(t => t.Id == trialId).Select(t => new { t.ResearchProgramNo, t.ExperimentName, t.TrialCode, t.TrialStatusStr, t.DeclarationTypeEnumList }).FirstNotNullAsync(); var trialInfo = await _trialRepository.Where(t => t.Id == trialId).Select(t => new { t.ResearchProgramNo, t.ExperimentName, t.TrialCode, t.TrialStatusStr, t.DeclarationTypeEnumList }).FirstNotNullAsync();
//找到 该项目的IQC 用户Id //找到 该项目的IQC 用户Id
var userList = await _trialUserRoleRepository.Where(t => t.TrialId == trialId).Where(t => t.UserRole.UserTypeEnum == UserTypeEnum.IQC).Select(t => new { t.UserId, t.UserRole.FullName, t.UserRole.IdentityUser.UserWorkLanguage }).ToListAsync(); var userList = await _trialUserRoleRepository.Where(t => t.TrialId == trialId).Where(t => t.UserRole.UserTypeEnum == UserTypeEnum.IQC).Select(t => new { t.UserId, t.UserRole.FullName }).ToListAsync();
//判断是否任务可以领取 ,可以的话 发送邮件 //判断是否任务可以领取 ,可以的话 发送邮件
foreach (var user in userList) foreach (var user in userList)
{ {
//重新设置当前发送邮件的语言
var workLanguage = user.UserWorkLanguage;
isEn_us = workLanguage == UserWorkLanguage.US;
var userId = user.UserId; var userId = user.UserId;
//过滤项目 并且 将 _userInfo.Id 换位 当前发送邮件的Id //过滤项目 并且 将 _userInfo.Id 换位 当前发送邮件的Id
@@ -157,7 +137,7 @@ public class CRCImageQuestionRecurringEventConsumer(IRepository<Trial> _trialRep
{ {
var topicStr = string.Format(isEn_us ? trialEmailConfig.EmailTopic : trialEmailConfig.EmailTopicCN, trialInfo.ResearchProgramNo); var topicStr = string.Format(isEn_us ? trialEmailConfig.EmailTopic : trialEmailConfig.EmailTopicCN, trialInfo.ResearchProgramNo);
var htmlContent = isEn_us ? trialEmailConfig.EmailHtmlContent : trialEmailConfig.EmailHtmlContentCN; var htmlContent = isEn_us ? trialEmailConfig.EmailHtmlContent : trialEmailConfig.EmailHtmlContentCN;
var htmlBodyStr = string.Format(CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, htmlContent, workLanguage), var htmlBodyStr = string.Format(CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, htmlContent),
user.FullName, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), sendStat.ToBeDealedCount - sendStat.ReUploadTobeDealedCount, sendStat.ReUploadTobeDealedCount, _SystemEmailSendConfig.CurrentValue.SiteUrl); user.FullName, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), sendStat.ToBeDealedCount - sendStat.ReUploadTobeDealedCount, sendStat.ReUploadTobeDealedCount, _SystemEmailSendConfig.CurrentValue.SiteUrl);
@@ -193,26 +173,18 @@ public class ImageQCRecurringEventConsumer(IRepository<Trial> _trialRepository,
{ {
var trialId=context.Message.TrialId; var trialId=context.Message.TrialId;
var isEn_us = context.Message.CultureInfoName == StaticData.CultureInfo.en_US; var isEn_us = false;
//设置当前事件传递过来的语言
var culture = context.Message.CultureInfoName;
CultureInfo.CurrentCulture = new CultureInfo(culture);
var trialInfo = await _trialRepository.Where(t => t.Id == trialId).Select(t => new { t.ResearchProgramNo, t.ExperimentName, t.TrialCode, t.TrialStatusStr }).FirstNotNullAsync(); var trialInfo = await _trialRepository.Where(t => t.Id == trialId).Select(t => new { t.ResearchProgramNo, t.ExperimentName, t.TrialCode, t.TrialStatusStr }).FirstNotNullAsync();
//找到 该项目的IQC 用户Id //找到 该项目的IQC 用户Id
var userList = await _trialUserRoleRepository.Where(t => t.TrialId == trialId).Where(t => t.UserRole.UserTypeEnum == UserTypeEnum.IQC).Select(t => new { t.UserId, t.UserRole.FullName, t.UserRole.IdentityUser.UserWorkLanguage }).ToListAsync(); var userList = await _trialUserRoleRepository.Where(t => t.TrialId == trialId).Where(t => t.UserRole.UserTypeEnum == UserTypeEnum.IQC).Select(t => new { t.UserId, t.UserRole.FullName }).ToListAsync();
//判断是否任务可以领取 ,可以的话 发送邮件 //判断是否任务可以领取 ,可以的话 发送邮件
var userIdList = userList.Select(t => t.UserId).ToList(); var userIdList = userList.Select(t => t.UserId).ToList();
foreach (var user in userList) foreach (var user in userList)
{ {
//重新设置当前发送邮件的语言
var workLanguage = user.UserWorkLanguage;
isEn_us = workLanguage == UserWorkLanguage.US;
var userId = user.UserId; var userId = user.UserId;
//过滤项目 并且 将 _userInfo.Id 换位 当前发送邮件的Id //过滤项目 并且 将 _userInfo.Id 换位 当前发送邮件的Id
@@ -240,7 +212,7 @@ public class ImageQCRecurringEventConsumer(IRepository<Trial> _trialRepository,
var topicStr = string.Format(isEn_us ? trialEmailConfig.EmailTopic : trialEmailConfig.EmailTopicCN, trialInfo.ResearchProgramNo); var topicStr = string.Format(isEn_us ? trialEmailConfig.EmailTopic : trialEmailConfig.EmailTopicCN, trialInfo.ResearchProgramNo);
var htmlContent = isEn_us ? trialEmailConfig.EmailHtmlContent : trialEmailConfig.EmailHtmlContentCN; var htmlContent = isEn_us ? trialEmailConfig.EmailHtmlContent : trialEmailConfig.EmailHtmlContentCN;
var htmlBodyStr = string.Format(CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, htmlContent, workLanguage), var htmlBodyStr = string.Format(CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, htmlContent),
user.FullName, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), sendStat.ToBeClaimedCount, sendStat.ToBeReviewedCount, _SystemEmailSendConfig.CurrentValue.SiteUrl); user.FullName, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), sendStat.ToBeClaimedCount, sendStat.ToBeReviewedCount, _SystemEmailSendConfig.CurrentValue.SiteUrl);
return (topicStr, htmlBodyStr, false, userId); return (topicStr, htmlBodyStr, false, userId);
@@ -5,7 +5,6 @@ using IRaCIS.Core.Application.Helper;
using IRaCIS.Core.Application.MassTransit.Consumer; using IRaCIS.Core.Application.MassTransit.Consumer;
using IRaCIS.Core.Application.Service.Reading.Dto; using IRaCIS.Core.Application.Service.Reading.Dto;
using IRaCIS.Core.Domain.Models; using IRaCIS.Core.Domain.Models;
using IRaCIS.Core.Domain.Share;
using MassTransit; using MassTransit;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using MimeKit; using MimeKit;
@@ -43,12 +42,7 @@ namespace IRaCIS.Core.Application.MassTransit.Recurring
DateTime now = DateTime.Now; DateTime now = DateTime.Now;
Console.WriteLine("发送定时过期提醒"); Console.WriteLine("发送定时过期提醒");
var isEn_US = context.Message.CultureInfoName == StaticData.CultureInfo.en_US; var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
//设置当前事件传递过来的语言
var culture = context.Message.CultureInfoName;
CultureInfo.CurrentCulture = new CultureInfo(culture);
var systemDocQuery = var systemDocQuery =
from sysDoc in _systemDocumentRepository.AsQueryable(false) 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))) 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)))
@@ -85,61 +79,58 @@ namespace IRaCIS.Core.Application.MassTransit.Recurring
var userinfoList = await _identityUserRepository.Where(x => confirmUserIdList.Contains(x.Id)).ToListAsync(); var userinfoList = await _identityUserRepository.Where(x => confirmUserIdList.Contains(x.Id)).ToListAsync();
Console.WriteLine("发送定时过期提醒:人员数量" + userinfoList.Count); Console.WriteLine("发送定时过期提醒:人员数量" + userinfoList.Count);
int index = 1;
var scenario = EmailBusinessScenario.GeneralTraining_ExpirationNotification; foreach (var userinfo in userinfoList)
var emailConfig = _emailNoticeConfigrepository.Where(t => t.BusinessScenarioEnum == scenario && t.IsAutoSend && t.IsEnable).FirstOrDefault();
if (emailConfig == null)
{
return;
}
var userWorkLanguageList = userinfoList.Select(x => x.UserWorkLanguage).Distinct().ToList();
foreach (var workLanguage in userWorkLanguageList)
{ {
try try
{ {
var langUserInfoList = userinfoList.Where(x => x.UserWorkLanguage == workLanguage).ToList(); Console.WriteLine($"{index}发送定时过期提醒,邮箱:{userinfo.EMail},姓名{userinfo.UserName}");
if (!langUserInfoList.Any()) index++;
{
continue;
}
var userIsEn_US = workLanguage == UserWorkLanguage.US;
var messageToSend = new MimeMessage(); var messageToSend = new MimeMessage();
//发件地址
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail)); messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
foreach (var userinfo in langUserInfoList)
{
messageToSend.To.Add(new MailboxAddress(userinfo.FullName, userinfo.EMail));
}
var companyName = userIsEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
var userNames = langUserInfoList.Select(x => x.UserName).ToList();
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input => Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
{ {
var topicStr = string.Format(input.topicStr, companyName); var topicStr = string.Format(input.topicStr, companyName);
var htmlBodyStr = string.Format( var htmlBodyStr = string.Format(
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr, workLanguage), CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
string.Join(',', userNames), userinfo.UserName, // 用户名 {0}
_systemEmailConfig.SiteUrl _systemEmailConfig.SiteUrl
); );
return (topicStr, htmlBodyStr); return (topicStr, htmlBodyStr);
}; };
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(emailConfig, messageToSend, emailConfigFunc, workLanguage); 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); await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
} }
}
catch (Exception) catch (Exception)
{ {
}
} }
} }
}
}
/// <summary> /// <summary>
/// 生效通知 /// 生效通知
/// </summary> /// </summary>
@@ -159,11 +150,7 @@ namespace IRaCIS.Core.Application.MassTransit.Recurring
public async Task Consume(ConsumeContext<SystemDocumentPublishEvent> context) public async Task Consume(ConsumeContext<SystemDocumentPublishEvent> context)
{ {
var isEn_US = context.Message.CultureInfoName == StaticData.CultureInfo.en_US; var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
//设置当前事件传递过来的语言
var culture = context.Message.CultureInfoName;
CultureInfo.CurrentCulture = new CultureInfo(culture);
// 记录是否只发送给新增角色的日志 // 记录是否只发送给新增角色的日志
if (context.Message.NewUserTypeIds != null && context.Message.NewUserTypeIds.Any()) if (context.Message.NewUserTypeIds != null && context.Message.NewUserTypeIds.Any())
@@ -232,53 +219,47 @@ namespace IRaCIS.Core.Application.MassTransit.Recurring
var confirmUserIdList = datalist.Select(t => t.ConfirmUserId).Distinct().ToList(); var confirmUserIdList = datalist.Select(t => t.ConfirmUserId).Distinct().ToList();
var userinfoList = await _identityUserRepository.Where(x => confirmUserIdList.Contains(x.Id)).ToListAsync(); var userinfoList = await _identityUserRepository.Where(x => confirmUserIdList.Contains(x.Id)).ToListAsync();
int index = 1;
var scenario = EmailBusinessScenario.GeneralTraining_EffectiveNotification; foreach (var userinfo in userinfoList)
var emailConfig = _emailNoticeConfigrepository.Where(t => t.BusinessScenarioEnum == scenario && t.IsAutoSend && t.IsEnable).FirstOrDefault();
if (emailConfig == null)
{ {
return; string msg = $"{index}生效通知,邮箱:{userinfo.EMail},姓名{userinfo.UserName},";
} index++;
var userWorkLanguageList = userinfoList.Select(x => x.UserWorkLanguage).Distinct().ToList();
foreach (var workLanguage in userWorkLanguageList)
{
var langUserInfoList = userinfoList.Where(x => x.UserWorkLanguage == workLanguage).ToList();
if (!langUserInfoList.Any())
{
continue;
}
string msg = $"{langUserInfoList.Count}生效通知,";
try try
{ {
var messageToSend = new MimeMessage(); var messageToSend = new MimeMessage();
//发件地址
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail)); messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
foreach (var userinfo in langUserInfoList)
{
messageToSend.To.Add(new MailboxAddress(userinfo.FullName, userinfo.EMail));
}
var userIsEn_US = workLanguage == UserWorkLanguage.US;
var companyName = userIsEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
var userNames = langUserInfoList.Select(x => x.UserName).ToList();
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input => Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
{ {
var topicStr = string.Format(input.topicStr, companyName); var topicStr = string.Format(input.topicStr, companyName);
var htmlBodyStr = string.Format( var htmlBodyStr = string.Format(
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr, workLanguage), CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
string.Join(',', userNames), userinfo.UserName, // 用户名 {0}
_systemEmailConfig.SiteUrl _systemEmailConfig.SiteUrl
); );
return (topicStr, htmlBodyStr); return (topicStr, htmlBodyStr);
}; };
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(emailConfig, messageToSend, emailConfigFunc, workLanguage);
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); await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
msg += "发送成功"; msg += "发送成功";
}
} }
catch (Exception) catch (Exception)
@@ -294,4 +275,3 @@ namespace IRaCIS.Core.Application.MassTransit.Recurring
} }
} }
} }
}
@@ -5,7 +5,6 @@ using IRaCIS.Core.Application.Helper;
using IRaCIS.Core.Application.MassTransit.Consumer; using IRaCIS.Core.Application.MassTransit.Consumer;
using IRaCIS.Core.Application.Service.Reading.Dto; using IRaCIS.Core.Application.Service.Reading.Dto;
using IRaCIS.Core.Domain.Models; using IRaCIS.Core.Domain.Models;
using IRaCIS.Core.Domain.Share;
using MassTransit; using MassTransit;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
@@ -15,7 +14,6 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Linq; using System.Linq;
using System.Linq.Dynamic.Core;
using System.Reactive.Joins; using System.Reactive.Joins;
using System.Text; using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
@@ -54,11 +52,7 @@ namespace IRaCIS.Core.Application.MassTransit.Recurring
DateTime now = DateTime.Now; DateTime now = DateTime.Now;
Console.WriteLine("发送定时项目过期提醒"); Console.WriteLine("发送定时项目过期提醒");
var isEn_US = context.Message.CultureInfoName == StaticData.CultureInfo.en_US; var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
//设置当前事件传递过来的语言
var culture = context.Message.CultureInfoName;
CultureInfo.CurrentCulture = new CultureInfo(culture);
var trialDocQuery = var trialDocQuery =
from trialDoc in _trialDocumentRepository.AsQueryable(true) from trialDoc in _trialDocumentRepository.AsQueryable(true)
@@ -81,7 +75,6 @@ namespace IRaCIS.Core.Application.MassTransit.Recurring
CurrentStaffTrainDays = trialDoc.CurrentStaffTrainDays, CurrentStaffTrainDays = trialDoc.CurrentStaffTrainDays,
NewStaffTrainDays = trialDoc.NewStaffTrainDays, NewStaffTrainDays = trialDoc.NewStaffTrainDays,
Id = trialDoc.Id, Id = trialDoc.Id,
TrialId= trialDoc.TrialId,
IsSystemDoc = false, IsSystemDoc = false,
CreateTime = trialDoc.CreateTime, CreateTime = trialDoc.CreateTime,
FullFilePath = trialDoc.Path, FullFilePath = trialDoc.Path,
@@ -109,65 +102,45 @@ namespace IRaCIS.Core.Application.MassTransit.Recurring
var userinfoList = await _identityUserRepository.Where(x => confirmUserIdList.Contains(x.Id)).ToListAsync(); var userinfoList = await _identityUserRepository.Where(x => confirmUserIdList.Contains(x.Id)).ToListAsync();
Console.WriteLine("发送定时项目过期提醒:人员数量" + userinfoList.Count); Console.WriteLine("发送定时项目过期提醒:人员数量" + userinfoList.Count);
int index = 1;
var scenario = EmailBusinessScenario.TrialTraining_ExpirationNotification; foreach (var userinfo in userinfoList)
var emailConfig = _emailNoticeConfigrepository.Where(t => t.BusinessScenarioEnum == scenario && t.IsAutoSend && t.IsEnable).FirstOrDefault();
if (emailConfig == null)
{
return;
}
var userTrialIdList = datalist.Select(t => new { t.ConfirmUserId, t.TrialId }).Distinct().ToList();
var userWithTrialList = (from ut in userTrialIdList
join u in userinfoList on ut.ConfirmUserId equals u.Id
select new { ut.TrialId, User = u }).ToList();
var groupedList = userWithTrialList.GroupBy(x => new { x.TrialId, x.User.UserWorkLanguage }).ToList();
foreach (var groupItem in groupedList)
{ {
try try
{ {
var langUserInfoList = groupItem.Select(x => x.User).Distinct().ToList(); Console.WriteLine($"{index}发送定时过期提醒,邮箱:{userinfo.EMail},姓名{userinfo.UserName}");
if (!langUserInfoList.Any()) index++;
{
continue;
}
Console.WriteLine($"发送定时过期提醒,数量:{langUserInfoList.Count}");
var trialInfo = await _trialRepository.Where(x => x.Id == groupItem.Key.TrialId).FirstOrDefaultAsync();
if (trialInfo == null)
{
continue;
}
var workLanguage = groupItem.Key.UserWorkLanguage;
var userIsEn_US = workLanguage == UserWorkLanguage.US;
var messageToSend = new MimeMessage(); var messageToSend = new MimeMessage();
//发件地址
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail)); messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
foreach (var userinfo in langUserInfoList)
{
messageToSend.To.Add(new MailboxAddress(userinfo.FullName, userinfo.EMail));
}
var companyName = userIsEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
var userNames = langUserInfoList.Select(x => x.UserName).ToList(); var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input => Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
{ {
var topicStr = string.Format(input.topicStr, companyName); var topicStr = string.Format(input.topicStr, companyName);
var htmlBodyStr = string.Format( var htmlBodyStr = string.Format(
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr, workLanguage), CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
string.Join(',', userNames), userinfo.UserName, // 用户名 {0}
_systemEmailConfig.SiteUrl _systemEmailConfig.SiteUrl
); );
return (topicStr, htmlBodyStr); return (topicStr, htmlBodyStr);
}; };
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(emailConfig, messageToSend, emailConfigFunc, workLanguage); var scenario = EmailBusinessScenario.TrialTraining_ExpirationNotification;
await SendEmailHelper.SendTrialEmailAsync(messageToSend, trialInfo);
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) catch (Exception)
@@ -189,7 +162,6 @@ namespace IRaCIS.Core.Application.MassTransit.Recurring
IRepository<VisitTask> _visitTaskRepository, IRepository<VisitTask> _visitTaskRepository,
IRepository<TrialDocument> _trialDocumentRepository, IRepository<TrialDocument> _trialDocumentRepository,
IRepository<IdentityUser> _identityUserRepository, IRepository<IdentityUser> _identityUserRepository,
IRepository<Trial> _trialRepository,
IRepository<TrialIdentityUser> _trialIdentityUserRepository, IRepository<TrialIdentityUser> _trialIdentityUserRepository,
IRepository<Dictionary> _dictionaryRepository, IRepository<Dictionary> _dictionaryRepository,
IRepository<TrialUserRole> _trialUserRoleRepository, IRepository<TrialUserRole> _trialUserRoleRepository,
@@ -201,11 +173,7 @@ namespace IRaCIS.Core.Application.MassTransit.Recurring
public async Task Consume(ConsumeContext<TrialDocumentPublishEvent> context) public async Task Consume(ConsumeContext<TrialDocumentPublishEvent> context)
{ {
var isEn_US = context.Message.CultureInfoName == StaticData.CultureInfo.en_US; var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
//设置当前事件传递过来的语言
var culture = context.Message.CultureInfoName;
CultureInfo.CurrentCulture = new CultureInfo(culture);
// 记录是否只发送给新增角色的日志 // 记录是否只发送给新增角色的日志
if (context.Message.NewUserTypeIds != null && context.Message.NewUserTypeIds.Any()) if (context.Message.NewUserTypeIds != null && context.Message.NewUserTypeIds.Any())
@@ -230,7 +198,6 @@ namespace IRaCIS.Core.Application.MassTransit.Recurring
{ {
IsSystemDoc = true, IsSystemDoc = true,
Id = trialDoc.Id, Id = trialDoc.Id,
TrialId= trialDoc.TrialId,
CreateTime = trialDoc.CreateTime, CreateTime = trialDoc.CreateTime,
IsDeleted = trialDoc.IsDeleted, IsDeleted = trialDoc.IsDeleted,
SignViewMinimumMinutes = trialDoc.SignViewMinimumMinutes, SignViewMinimumMinutes = trialDoc.SignViewMinimumMinutes,
@@ -252,8 +219,6 @@ namespace IRaCIS.Core.Application.MassTransit.Recurring
from trialDoc in _trialDocumentRepository.AsQueryable(false).Where(x => context.Message.Ids.Contains(x.Id)) 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 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 join trialUserRole in _trialUserRoleRepository.Where(x=>x.IsDeleted==false) on trialIdentityUser.Id equals trialUserRole.TrialUserId
join trial in _trialRepository.AsQueryable(false) on trialDoc.TrialId equals trial.Id
join identityUser in _identityUserRepository.AsQueryable(false).Where(u => u.Status == UserStateEnum.Enable) join identityUser in _identityUserRepository.AsQueryable(false).Where(u => u.Status == UserStateEnum.Enable)
on trialIdentityUser.IdentityUserId equals identityUser.Id on trialIdentityUser.IdentityUserId equals identityUser.Id
where trialIdentityUser.TrialUserRoleList.Any(ur => !ur.IsDeleted &&trialDoc.NeedConfirmedUserTypeList.Any(c => c.NeedConfirmUserTypeId == ur.UserRole.UserTypeId)) where trialIdentityUser.TrialUserRoleList.Any(ur => !ur.IsDeleted &&trialDoc.NeedConfirmedUserTypeList.Any(c => c.NeedConfirmUserTypeId == ur.UserRole.UserTypeId))
@@ -263,8 +228,6 @@ namespace IRaCIS.Core.Application.MassTransit.Recurring
{ {
IsSystemDoc = false, IsSystemDoc = false,
Id = trialDoc.Id, Id = trialDoc.Id,
TrialId = trialDoc.TrialId,
EmailFromName =trial.EmailFromName,
CreateTime = trialDoc.CreateTime, CreateTime = trialDoc.CreateTime,
IsDeleted = trialDoc.IsDeleted, IsDeleted = trialDoc.IsDeleted,
SignViewMinimumMinutes = trialDoc.SignViewMinimumMinutes, SignViewMinimumMinutes = trialDoc.SignViewMinimumMinutes,
@@ -279,70 +242,51 @@ namespace IRaCIS.Core.Application.MassTransit.Recurring
FullFilePath = trialDoc.Path FullFilePath = trialDoc.Path
}; };
} }
var datalist = await systemDocQuery.IgnoreQueryFilters().ToListAsync(); var datalist = await systemDocQuery.IgnoreQueryFilters().ToListAsync();
var confirmUserIdList = datalist.Select(t => t.ConfirmUserId).Distinct().ToList(); var confirmUserIdList = datalist.Select(t => t.ConfirmUserId).Distinct().ToList();
var userinfoList = await _identityUserRepository.Where(x => confirmUserIdList.Contains(x.Id)).ToListAsync(); var userinfoList = await _identityUserRepository.Where(x => confirmUserIdList.Contains(x.Id)).ToListAsync();
int index = 1;
var scenario = EmailBusinessScenario.TrialTraining_EffectiveNotification; foreach (var userinfo in userinfoList)
var emailConfig = _emailNoticeConfigrepository.Where(t => t.BusinessScenarioEnum == scenario && t.IsEnable).FirstOrDefault();
if (emailConfig == null)
{ {
return; string msg = $"{index}项目生效通知,邮箱:{userinfo.EMail},姓名{userinfo.UserName},";
} index++;
var userTrialIdList = datalist.Select(t => new { t.ConfirmUserId, t.TrialId }).Distinct().ToList();
var userWithTrialList = (from ut in userTrialIdList
join u in userinfoList on ut.ConfirmUserId equals u.Id
select new { ut.TrialId, User = u }).ToList();
var groupedList = userWithTrialList.GroupBy(x => new { x.TrialId, x.User.UserWorkLanguage }).ToList();
foreach (var groupItem in groupedList)
{
var langUserInfoList = groupItem.Select(x => x.User).Distinct().ToList();
if (!langUserInfoList.Any())
{
continue;
}
string msg = $"{langUserInfoList.Count}项目生效通知,";
try try
{ {
var messageToSend = new MimeMessage(); var messageToSend = new MimeMessage();
//发件地址
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail)); messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
foreach (var userinfo in langUserInfoList)
{
messageToSend.To.Add(new MailboxAddress(userinfo.FullName, userinfo.EMail));
}
var workLanguage = groupItem.Key.UserWorkLanguage;
var userIsEn_US = workLanguage == UserWorkLanguage.US; var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
var companyName = userIsEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
var userNames = langUserInfoList.Select(x => x.UserName).ToList();
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input => Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
{ {
var topicStr = string.Format(input.topicStr, companyName); var topicStr = string.Format(input.topicStr, companyName);
var htmlBodyStr = string.Format( var htmlBodyStr = string.Format(
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr, workLanguage), CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
string.Join(',', userNames), userinfo.UserName, // 用户名 {0}
_systemEmailConfig.SiteUrl _systemEmailConfig.SiteUrl
); );
return (topicStr, htmlBodyStr); return (topicStr, htmlBodyStr);
}; };
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(emailConfig, messageToSend, emailConfigFunc, workLanguage);
var trialInfo = await _trialRepository.Where(x => x.Id == groupItem.Key.TrialId).FirstOrDefaultAsync(); var scenario = EmailBusinessScenario.TrialTraining_EffectiveNotification;
if (trialInfo == null)
var emailConfig = _emailNoticeConfigrepository.Where(t => t.BusinessScenarioEnum == scenario && t.IsEnable).FirstOrDefault();
if (emailConfig != null)
{ {
continue; await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(emailConfig, messageToSend, emailConfigFunc);
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
msg += "发送成功";
} }
await SendEmailHelper.SendTrialEmailAsync(messageToSend, trialInfo);
msg += "发送成功";
} }
catch (Exception) catch (Exception)
@@ -873,7 +873,7 @@ namespace IRaCIS.Core.Application.Service
{ {
arm1.TaskAllocationState = TaskAllocationState.Allocated; arm1.TaskAllocationState = TaskAllocationState.Allocated;
arm1.AllocateTime = DateTime.Now; arm1.AllocateTime = DateTime.Now;
arm1.DoctorUserId = assignConfigList.FirstOrDefault(t => t.ArmEnum == Arm.DoubleReadingArm1)!.DoctorUserId; arm1.DoctorUserId = task1.DoctorUserId;
arm1.SuggesteFinishedTime = GetSuggessFinishTime(true, UrgentType.NotUrget); arm1.SuggesteFinishedTime = GetSuggessFinishTime(true, UrgentType.NotUrget);
} }
@@ -913,7 +913,7 @@ namespace IRaCIS.Core.Application.Service
{ {
taskOne.TaskAllocationState = TaskAllocationState.Allocated; taskOne.TaskAllocationState = TaskAllocationState.Allocated;
taskOne.AllocateTime = DateTime.Now; taskOne.AllocateTime = DateTime.Now;
taskOne.DoctorUserId = assignConfigList.FirstOrDefault(t => t.ArmEnum == Arm.DoubleReadingArm1)!.DoctorUserId; taskOne.DoctorUserId = task1!.DoctorUserId;
taskOne.SuggesteFinishedTime = GetSuggessFinishTime(true, UrgentType.NotUrget); taskOne.SuggesteFinishedTime = GetSuggessFinishTime(true, UrgentType.NotUrget);
} }
@@ -944,7 +944,7 @@ namespace IRaCIS.Core.Application.Service
{ {
arm2.TaskAllocationState = TaskAllocationState.Allocated; arm2.TaskAllocationState = TaskAllocationState.Allocated;
arm2.AllocateTime = DateTime.Now; arm2.AllocateTime = DateTime.Now;
arm2.DoctorUserId = assignConfigList.FirstOrDefault(t => t.ArmEnum == Arm.DoubleReadingArm2)!.DoctorUserId; arm2.DoctorUserId = task2.DoctorUserId;
arm2.SuggesteFinishedTime = GetSuggessFinishTime(true, UrgentType.NotUrget); arm2.SuggesteFinishedTime = GetSuggessFinishTime(true, UrgentType.NotUrget);
} }
@@ -986,7 +986,7 @@ namespace IRaCIS.Core.Application.Service
{ {
taskTwo.TaskAllocationState = TaskAllocationState.Allocated; taskTwo.TaskAllocationState = TaskAllocationState.Allocated;
taskTwo.AllocateTime = DateTime.Now; taskTwo.AllocateTime = DateTime.Now;
taskTwo.DoctorUserId = assignConfigList.FirstOrDefault(t => t.ArmEnum == Arm.DoubleReadingArm2)!.DoctorUserId; taskTwo.DoctorUserId = task2!.DoctorUserId;
taskTwo.SuggesteFinishedTime = GetSuggessFinishTime(true, UrgentType.NotUrget); taskTwo.SuggesteFinishedTime = GetSuggessFinishTime(true, UrgentType.NotUrget);
} }
@@ -17,9 +17,7 @@ using MassTransit;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Newtonsoft.Json; using Newtonsoft.Json;
using System.Linq;
using System.Linq.Dynamic.Core; using System.Linq.Dynamic.Core;
using System.Threading.Tasks;
using Subject = IRaCIS.Core.Domain.Models.Subject; using Subject = IRaCIS.Core.Domain.Models.Subject;
namespace IRaCIS.Core.Application.Service.Allocation; namespace IRaCIS.Core.Application.Service.Allocation;
@@ -43,15 +41,10 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
IRepository<ReadingQuestionCriterionTrial> _trialReadingCriterionRepository, IRepository<ReadingQuestionCriterionTrial> _trialReadingCriterionRepository,
IRepository<ReadingQuestionCriterionTrial> _readingQuestionCriterionTrialRepository, IRepository<ReadingQuestionCriterionTrial> _readingQuestionCriterionTrialRepository,
IRepository<ReadingTaskQuestionAnswer> _readingTaskQuestionAnswerRepository, IRepository<ReadingTaskQuestionAnswer> _readingTaskQuestionAnswerRepository,
IRepository<Segment> _segmentRepository,
IRepository<Segmentation> _segmentationRepository,
IRepository<SegmentBinding> _segmentBindingRepository,
IRepository<DicomInstance> _dicomInstanceRepository, IRepository<DicomInstance> _dicomInstanceRepository,
IRepository<DicomSeries> _dicomSeriesRepository, IRepository<DicomSeries> _dicomSeriesRepository,
IRepository<SubjectCanceDoctor> _subjectCanceDoctorRepository, IRepository<SubjectCanceDoctor> _subjectCanceDoctorRepository,
IRepository<ReadingTaskQuestionMark> _readingTaskQuestionMarkRepository, IRepository<ReadingTaskQuestionMark> _readingTaskQuestionMarkRepository,
IRepository<ReadingNoneDicomMark> _readingNoneDicomMarkRepository,
IRepository<ReadingNoneDicomMarkBinding> _readingNoneDicomMarkBindingRepository,
IRepository<ReadingTableAnswerRowInfo> _readingTableAnswerRowInfoRepository, IRepository<ReadingTableAnswerRowInfo> _readingTableAnswerRowInfoRepository,
//IRepository<ReadingCustomTag> _readingCustomTagRepository, //IRepository<ReadingCustomTag> _readingCustomTagRepository,
IRepository<TaskInfluence> _taskInfluenceRepository, IRepository<TaskInfluence> _taskInfluenceRepository,
@@ -1016,9 +1009,9 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
if (critrion.IsReadingTaskViewInOrder == ReadingOrder.InOrder) if (critrion.IsReadingTaskViewInOrder == ReadingOrder.InOrder)
{ {
var extralObj = _trialRepository.Where(t => t.Id == trialId).Select(t => new { t.IsOpenLostVistRead }).FirstOrDefault(); var extralObj = _trialRepository.Where(t => t.Id == trialId).Select(t => new { t.TrialExtraConfigJsonStr }).FirstOrDefault();
//var extralConfig = JsonConvert.DeserializeObject<TrialExtraConfig>(extralObj?.TrialExtraConfigJsonStr) ?? new TrialExtraConfig(); var extralConfig = JsonConvert.DeserializeObject<TrialExtraConfig>(extralObj?.TrialExtraConfigJsonStr) ?? new TrialExtraConfig();
var visitQuery = _visitTaskRepository.Where(x => x.TrialId == inQuery.TrialId && x.DoctorUserId == _userInfo.UserRoleId && x.TaskState == TaskState.Effect && x.Subject.IsSubjectQuit == false) var visitQuery = _visitTaskRepository.Where(x => x.TrialId == inQuery.TrialId && x.DoctorUserId == _userInfo.UserRoleId && x.TaskState == TaskState.Effect && x.Subject.IsSubjectQuit == false)
@@ -1027,7 +1020,7 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
.WhereIf(critrion.IsAutoCreate == false, t => !t.Subject.SubjectCriteriaEvaluationVisitFilterList.Where(t => t.TrialReadingCriterionId == trialReadingCriterionId).Any(f => f.IsGeneratedTask == false && t.VisitTaskNum > f.SubjectVisit.VisitNum)) .WhereIf(critrion.IsAutoCreate == false, t => !t.Subject.SubjectCriteriaEvaluationVisitFilterList.Where(t => t.TrialReadingCriterionId == trialReadingCriterionId).Any(f => f.IsGeneratedTask == false && t.VisitTaskNum > f.SubjectVisit.VisitNum))
// 前序 不存在 未一致性核查未通过的 // 前序 不存在 未一致性核查未通过的
.Where(t => !t.Subject.SubjectVisitList.Where(t => extralObj.IsOpenLostVistRead ? t.IsLostVisit == false : true).Any(sv => sv.CheckState != CheckStateEnum.CVPassed && t.VisitTaskNum >= sv.VisitNum)) .Where(t => !t.Subject.SubjectVisitList.Where(t => extralConfig.IsOpenLostVistRead ? t.IsLostVisit == false : true).Any(sv => sv.CheckState != CheckStateEnum.CVPassed && t.VisitTaskNum >= sv.VisitNum))
//.WhereIf(critrion.IsAutoCreate == false, t => t.Subject.SubjectCriteriaEvaluationVisitFilterList.Where(t => t.TrialReadingCriterionId == trialReadingCriterionId).Any(t => t.IsGeneratedTask == false) ? //.WhereIf(critrion.IsAutoCreate == false, t => t.Subject.SubjectCriteriaEvaluationVisitFilterList.Where(t => t.TrialReadingCriterionId == trialReadingCriterionId).Any(t => t.IsGeneratedTask == false) ?
//t.VisitTaskNum <= t.Subject.SubjectCriteriaEvaluationVisitFilterList.Where(t => t.TrialReadingCriterionId == trialReadingCriterionId && t.IsGeneratedTask == false).Min(t => t.SubjectVisit.VisitNum) : true) //t.VisitTaskNum <= t.Subject.SubjectCriteriaEvaluationVisitFilterList.Where(t => t.TrialReadingCriterionId == trialReadingCriterionId && t.IsGeneratedTask == false).Min(t => t.SubjectVisit.VisitNum) : true)
//.Where(t => t.Subject.SubjectVisitList.Any(t => t.CheckState != CheckStateEnum.CVPassed) ? t.VisitTaskNum <= t.Subject.SubjectVisitList.Where(t => t.CheckState != CheckStateEnum.CVPassed).Min(t => t.VisitNum) : true) //.Where(t => t.Subject.SubjectVisitList.Any(t => t.CheckState != CheckStateEnum.CVPassed) ? t.VisitTaskNum <= t.Subject.SubjectVisitList.Where(t => t.CheckState != CheckStateEnum.CVPassed).Min(t => t.VisitNum) : true)
@@ -1050,14 +1043,14 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
UnReadTaskCount = x.Where(y => y.TrialReadingCriterionId == trialReadingCriterionId && y.ReadingTaskState != ReadingTaskState.HaveSigned).Count(), UnReadTaskCount = x.Where(y => y.TrialReadingCriterionId == trialReadingCriterionId && y.ReadingTaskState != ReadingTaskState.HaveSigned).Count(),
//未读 里可读任务量 //未读 里可读任务量
UnReadCanReadTaskCount = x.Where(t => extralObj.IsOpenLostVistRead ? true : t.IsFrontTaskNeedSignButNotSign == false).Where(y => y.TrialReadingCriterionId == trialReadingCriterionId && y.ReadingTaskState != ReadingTaskState.HaveSigned && (y.IsNeedClinicalDataSign == false || y.IsClinicalDataSign == true) UnReadCanReadTaskCount = x.Where(t => extralConfig.IsOpenLostVistRead ? true : t.IsFrontTaskNeedSignButNotSign == false).Where(y => y.TrialReadingCriterionId == trialReadingCriterionId && y.ReadingTaskState != ReadingTaskState.HaveSigned && (y.IsNeedClinicalDataSign == false || y.IsClinicalDataSign == true)
//不能对包含聚合或子查询的表达式执行聚合函数 //不能对包含聚合或子查询的表达式执行聚合函数
//&& !x.Any(t => t.ReadingTaskState != ReadingTaskState.HaveSigned && t.IsNeedClinicalDataSign == true && t.IsClinicalDataSign == false && t.VisitTaskNum<y.VisitTaskNum ) //&& !x.Any(t => t.ReadingTaskState != ReadingTaskState.HaveSigned && t.IsNeedClinicalDataSign == true && t.IsClinicalDataSign == false && t.VisitTaskNum<y.VisitTaskNum )
).Count(), ).Count(),
UnReadCanReadTaskList = x.Where(y => y.TrialReadingCriterionId == trialReadingCriterionId && y.ReadingTaskState != ReadingTaskState.HaveSigned) UnReadCanReadTaskList = x.Where(y => y.TrialReadingCriterionId == trialReadingCriterionId && y.ReadingTaskState != ReadingTaskState.HaveSigned)
.Where(t => extralObj.IsOpenLostVistRead ? true : t.IsFrontTaskNeedSignButNotSign == false) .Where(t => extralConfig.IsOpenLostVistRead ? true : t.IsFrontTaskNeedSignButNotSign == false)
.Where(y => /*y.IsFrontTaskNeedSignButNotSign == false &&*/ (y.IsNeedClinicalDataSign == false || y.IsClinicalDataSign == true)) .Where(y => /*y.IsFrontTaskNeedSignButNotSign == false &&*/ (y.IsNeedClinicalDataSign == false || y.IsClinicalDataSign == true))
.OrderBy(x => x.VisitTaskNum) .OrderBy(x => x.VisitTaskNum)
.Select(u => new IRUnreadTaskView() .Select(u => new IRUnreadTaskView()
@@ -1179,8 +1172,8 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
var visitQuery = _visitTaskRepository.Where(x => x.TrialId == trialId && x.DoctorUserId == _userInfo.UserRoleId && x.TaskState == TaskState.Effect) var visitQuery = _visitTaskRepository.Where(x => x.TrialId == trialId && x.DoctorUserId == _userInfo.UserRoleId && x.TaskState == TaskState.Effect)
.WhereIf(inQuery.SubjectId != null, x => x.SubjectId == inQuery.SubjectId) .WhereIf(inQuery.SubjectId != null, x => x.SubjectId == inQuery.SubjectId)
.WhereIf(!string.IsNullOrEmpty(subjectCode), t => (t.Subject.Code.Contains(subjectCode!) && t.IsAnalysisCreate == false) || (t.BlindSubjectCode.Contains(subjectCode!) && t.IsAnalysisCreate)) .WhereIf(!string.IsNullOrEmpty(subjectCode), t => (t.Subject.Code.Contains(subjectCode!) && t.IsAnalysisCreate == false) || (t.BlindSubjectCode.Contains(subjectCode!) && t.IsAnalysisCreate))
.WhereIf(critrion.CriterionType == CriterionType.OCT, t => t.ReadingCategory == ReadingCategory.Visit ? t.SourceSubjectVisit.NoneDicomStudyList.Where(t => t.Modality == "OCT").SelectMany(t => t.ImageLabelNoneDicomFileList).Any() : true) .WhereIf(critrion.CriterionType == CriterionType.OCT, t => t.ReadingCategory == ReadingCategory.Visit ? t.SourceSubjectVisit.NoneDicomStudyList.SelectMany(t => t.ImageLabelNoneDicomFileList).Any() : true)
.WhereIf(critrion.CriterionType == CriterionType.IVUS, t => t.ReadingCategory == ReadingCategory.Visit ? t.SourceSubjectVisit.NoneDicomStudyList.Where(t => t.Modality == "IVUS").SelectMany(t => t.ImageLabelNoneDicomFileList).Any() : true); .WhereIf(critrion.CriterionType == CriterionType.IVUS, t => t.ReadingCategory == ReadingCategory.Visit ? t.SourceSubjectVisit.TaskStudyList.Any() : true);
var visitGroupQuery = visitQuery.GroupBy(x => new { x.SubjectId, x.Subject.Code, x.BlindSubjectCode }); var visitGroupQuery = visitQuery.GroupBy(x => new { x.SubjectId, x.Subject.Code, x.BlindSubjectCode });
@@ -2090,9 +2083,7 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
{ {
if (origenalTask.ReadingCategory == ReadingCategory.Visit) if (origenalTask.ReadingCategory == ReadingCategory.Visit)
{ {
newTask.IsCopyLesionAnswer = true; CopyForms(newTask, origenalTask);
//CopyForms(newTask, origenalTask);
} }
@@ -2190,6 +2181,7 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
if (origenalTask.ReadingCategory == ReadingCategory.Visit) if (origenalTask.ReadingCategory == ReadingCategory.Visit)
{ {
CopyForms(newTask, origenalTask); CopyForms(newTask, origenalTask);
} }
} }
@@ -2199,8 +2191,7 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
{ {
if (origenalTask.ReadingCategory == ReadingCategory.Visit) if (origenalTask.ReadingCategory == ReadingCategory.Visit)
{ {
newTask.IsCopyLesionAnswer = true; CopyForms(newTask, origenalTask);
//CopyForms(newTask, origenalTask);
} }
} }
@@ -2223,9 +2214,9 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
} }
} }
private async Task CopyForms(VisitTask newTask, VisitTask origenalTask) private void CopyForms(VisitTask newTask, VisitTask origenalTask)
{ {
newTask.IsCopyLesionAnswer = true;
//自定义 //自定义
//var readingCustomTagList = _readingCustomTagRepository.Where(t => t.VisitTaskId == origenalTask.Id).ToList(); //var readingCustomTagList = _readingCustomTagRepository.Where(t => t.VisitTaskId == origenalTask.Id).ToList();
@@ -2241,7 +2232,6 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
var readingTaskQuestionAnswerList = _readingTaskQuestionAnswerRepository.Where(t => t.VisitTaskId == origenalTask.Id).ToList(); var readingTaskQuestionAnswerList = _readingTaskQuestionAnswerRepository.Where(t => t.VisitTaskId == origenalTask.Id).ToList();
foreach (var item in readingTaskQuestionAnswerList) foreach (var item in readingTaskQuestionAnswerList)
@@ -2268,7 +2258,7 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
//ReadingTableAnswerRowInfo ReadingTableQuestionAnswer 一起加 //ReadingTableAnswerRowInfo ReadingTableQuestionAnswer 一起加
var readingTableAnswerRowInfoList = _readingTableAnswerRowInfoRepository.Where(t => t.VisitTaskId == origenalTask.Id).Include(t => t.LesionAnswerList).ToList(); var readingTableAnswerRowInfoList = _readingTableAnswerRowInfoRepository.Where(t => t.VisitTaskId == origenalTask.Id).Include(t => t.LesionAnswerList).ToList();
Dictionary<Guid, Guid> lesionRelationship = new Dictionary<Guid, Guid>() { };
foreach (var item in readingTableAnswerRowInfoList) foreach (var item in readingTableAnswerRowInfoList)
{ {
@@ -2276,7 +2266,7 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
var originalFristAddTaskId = item.FristAddTaskId; var originalFristAddTaskId = item.FristAddTaskId;
var newRowId = NewId.NextSequentialGuid(); var newRowId = NewId.NextSequentialGuid();
lesionRelationship.Add(item.Id, newRowId);
foreach (var mark in readingTaskQuestionMarkList) foreach (var mark in readingTaskQuestionMarkList)
{ {
mark.RowId = mark.RowId == item.Id ? newRowId : mark.RowId; mark.RowId = mark.RowId == item.Id ? newRowId : mark.RowId;
@@ -2298,112 +2288,13 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
} }
} }
#region
Dictionary<Guid, Guid> segmentationRelationship = new Dictionary<Guid, Guid>() { };
var segmentationList = _segmentationRepository.Where(t => t.VisitTaskId == origenalTask.Id).ToList();
foreach (var item in segmentationList)
{
var newSegmentationId = NewId.NextSequentialGuid();
segmentationRelationship.Add(item.Id, newSegmentationId);
item.Id = newSegmentationId;
item.VisitTaskId = newTask.Id;
}
Dictionary<Guid, Guid> segmentRelationship = new Dictionary<Guid, Guid>() { };
var segmentList = _segmentRepository.Where(t => t.VisitTaskId == origenalTask.Id).ToList();
foreach (var item in segmentList)
{
var newSegmentationId = NewId.NextSequentialGuid();
segmentRelationship.Add(item.Id, newSegmentationId);
if (segmentationRelationship.ContainsKey(item.SegmentationId))
{
item.SegmentationId = segmentationRelationship[item.SegmentationId];
}
item.Id = newSegmentationId;
item.VisitTaskId = newTask.Id;
}
var segmentBindingList = _segmentBindingRepository.Where(x => x.VisitTaskId == origenalTask.Id).ToList();
foreach (var item in segmentBindingList)
{
if (segmentationRelationship.ContainsKey(item.SegmentationId))
{
item.SegmentationId = segmentationRelationship[item.SegmentationId];
}
if (segmentRelationship.ContainsKey(item.SegmentId))
{
item.SegmentId = segmentRelationship[item.SegmentId];
}
if (item.RowId != null && lesionRelationship.ContainsKey(item.RowId.Value))
{
item.RowId = lesionRelationship[item.RowId.Value];
}
item.Id = NewId.NextSequentialGuid();
item.VisitTaskId = newTask.Id;
}
_ = _segmentationRepository.AddRangeAsync(segmentationList).Result;
_ = _segmentRepository.AddRangeAsync(segmentList).Result;
_ = _segmentBindingRepository.AddRangeAsync(segmentBindingList).Result;
#endregion
foreach (var item in readingTableAnswerRowInfoList)
{
if (item.SplitRowId != null && lesionRelationship.ContainsKey(item.SplitRowId.Value))
{
item.SplitRowId = lesionRelationship[item.SplitRowId.Value];
}
if (item.MergeRowId != null && lesionRelationship.ContainsKey(item.MergeRowId.Value))
{
item.MergeRowId = lesionRelationship[item.MergeRowId.Value];
}
}
_ = _readingTaskQuestionMarkRepository.AddRangeAsync(readingTaskQuestionMarkList).Result; _ = _readingTaskQuestionMarkRepository.AddRangeAsync(readingTaskQuestionMarkList).Result;
_ = _readingTableAnswerRowInfoRepository.AddRangeAsync(readingTableAnswerRowInfoList).Result; _ = _readingTableAnswerRowInfoRepository.AddRangeAsync(readingTableAnswerRowInfoList).Result;
var noneMarkList = _readingNoneDicomMarkRepository.Where(x => x.VisitTaskId == origenalTask.Id).ToList();
var noneMarkBindingList = _readingNoneDicomMarkBindingRepository.Where(x => x.VisitTaskId == origenalTask.Id).ToList();
foreach (var item in noneMarkList)
{
var newid = NewId.NextSequentialGuid();
item.MeasureData = item.MeasureData.Replace(origenalTask.Id.ToString(), newTask.Id.ToString());
item.VisitTaskId = newTask.Id;
foreach (var item1 in noneMarkBindingList)
{
if (item1.NoneDicomMarkId == item.Id)
{
item1.NoneDicomMarkId = newid;
}
}
item.Id = newid;
}
foreach (var item in noneMarkBindingList)
{
item.Id = NewId.NextSequentialGuid();
item.VisitTaskId = newTask.Id;
}
_ = _readingNoneDicomMarkRepository.AddRangeAsync(noneMarkList).Result;
_ = _readingNoneDicomMarkBindingRepository.AddRangeAsync(noneMarkBindingList).Result;
} }
@@ -2496,10 +2387,6 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
//另一个阅片人的任务根据任务进度自动进入PM退回或PM申请重阅 //另一个阅片人的任务根据任务进度自动进入PM退回或PM申请重阅
filterExpression = filterExpression.And(t => t.VisitTaskNum >= task.VisitTaskNum); filterExpression = filterExpression.And(t => t.VisitTaskNum >= task.VisitTaskNum);
//退回只影响有序的后续所有的,无序的当前访视
filterExpression = filterExpression.And(t => (t.TrialReadingCriterion.IsReadingTaskViewInOrder == ReadingOrder.InOrder) ||
(t.TrialReadingCriterion.IsReadingTaskViewInOrder != ReadingOrder.InOrder && t.SourceSubjectVisitId == task.SourceSubjectVisitId));
var influenceTaskList = await _visitTaskRepository.Where(filterExpression, true).ToListAsync(); var influenceTaskList = await _visitTaskRepository.Where(filterExpression, true).ToListAsync();
@@ -2657,12 +2544,9 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
//删除序列数据 //删除序列数据
await _subjectCriteriaEvaluationVisitStudyFilterRepository.BatchDeleteNoTrackingAsync(t => t.TrialReadingCriterion.CriterionType == CriterionType.RECIST1Pointt1_MB && t.SubjectVisit.SubjectId == task.SubjectId && t.SubjectVisitId == task.SourceSubjectVisitId); await _subjectCriteriaEvaluationVisitStudyFilterRepository.BatchDeleteNoTrackingAsync(t => t.TrialReadingCriterion.CriterionType == CriterionType.RECIST1Pointt1_MB && t.SubjectVisit.SubjectId == task.SubjectId && t.SubjectVisitId == task.SourceSubjectVisitId);
otherVisitIdList = otherVisitIdList.Where(t => t != task.SourceSubjectVisitId.Value).ToList();
} }
//BM后续访视 ,筛选状态不变,任务生成状态重置(实际该访视任务状态 可能是重阅重置了或者失效了,需要后续生成,或者取消分配了,需要后续重新分配) //BM后续访视 ,筛选状态不变,任务生成状态重置(实际该访视任务状态 可能是重阅重置了或者失效了,需要后续生成,或者取消分配了,需要后续重新分配)
await _subjectCriteriaEvaluationVisitFilterRepository.UpdatePartialFromQueryAsync(t => t.TrialReadingCriterion.CriterionType == CriterionType.RECIST1Pointt1_MB && t.SubjectVisit.SubjectId == task.SubjectId && otherVisitIdList.Contains(t.SubjectVisitId), await _subjectCriteriaEvaluationVisitFilterRepository.UpdatePartialFromQueryAsync(t => t.TrialReadingCriterion.CriterionType == CriterionType.RECIST1Pointt1_MB && t.SubjectVisit.SubjectId == task.SubjectId && otherVisitIdList.Contains(t.SubjectVisitId),
t => new SubjectCriteriaEvaluationVisitFilter() t => new SubjectCriteriaEvaluationVisitFilter()
@@ -2716,19 +2600,6 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
sv.ReviewAuditUserId = null; sv.ReviewAuditUserId = null;
sv.SecondReviewState = SecondReviewState.None; sv.SecondReviewState = SecondReviewState.None;
// 处理阅片期的临床数据
var readModuleIdList =await _readModuleRepository.Where(x => x.SubjectVisitId == sv.Id).Select(x => x.Id).ToListAsync();
await _readingClinicalDataReposiotry.UpdatePartialFromQueryAsync(t => readModuleIdList.Contains(t.ReadingId), c => new ReadingClinicalData()
{
IsSign = false,
ReadingClinicalDataState = ReadingClinicalDataStatus.HaveUploaded,
});
if (sv.IsBaseLine) if (sv.IsBaseLine)
{ {
await _readingClinicalDataReposiotry.UpdatePartialFromQueryAsync(t => t.ReadingId == sv.Id && (t.ClinicalDataTrialSet.ClinicalDataLevel == ClinicalLevel.Subject || t.ClinicalDataTrialSet.ClinicalDataLevel == ClinicalLevel.SubjectVisit), c => new ReadingClinicalData() { IsSign = false, ReadingClinicalDataState = ReadingClinicalDataStatus.HaveUploaded }); await _readingClinicalDataReposiotry.UpdatePartialFromQueryAsync(t => t.ReadingId == sv.Id && (t.ClinicalDataTrialSet.ClinicalDataLevel == ClinicalLevel.Subject || t.ClinicalDataTrialSet.ClinicalDataLevel == ClinicalLevel.SubjectVisit), c => new ReadingClinicalData() { IsSign = false, ReadingClinicalDataState = ReadingClinicalDataStatus.HaveUploaded });
@@ -2837,13 +2708,6 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
//默认影响的都是该标准的任务 //默认影响的都是该标准的任务
filterExpression = filterExpression.And(t => t.TrialReadingCriterionId == filterObj.TrialReadingCriterionId); filterExpression = filterExpression.And(t => t.TrialReadingCriterionId == filterObj.TrialReadingCriterionId);
} }
//退回只影响有序的后续所有的,无序的当前访视
if (isReReading == false)
{
filterExpression = filterExpression.And(t => (t.TrialReadingCriterion.IsReadingTaskViewInOrder == ReadingOrder.InOrder) ||
(t.TrialReadingCriterion.IsReadingTaskViewInOrder != ReadingOrder.InOrder && t.SourceSubjectVisitId == filterObj.SourceSubjectVisitId));
}
} }
@@ -1,187 +0,0 @@
//--------------------------------------------------------------------
// 此代码由liquid模板自动生成 byzhouhang 20240909
// 生成时间 2025-10-28 06:22:47Z
// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。
//--------------------------------------------------------------------
using IRaCIS.Core.Domain.Share;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace IRaCIS.Core.Application.ViewModel;
public class GetReSendEmailInDto : PageInput
{
public Guid Id { get; set; }
public DateTime? EmailStartDate { get; set; }
public DateTime? EmailEndDate { get; set; }
public EmailState? EmailStateEnum { get; set; }
public string ToRecipientName { get; set; } = string.Empty;
public string CcRecipientName { get; set; } = string.Empty;
}
public class EmailLogView : EmailLogAddOrEdit
{
public DateTime CreateTime { get; set; }
public DateTime UpdateTime { get; set; }
public List<EmailRecipientLogView> RecipientList { get; set; }
public List<EmaliAttachmentInfo> AttachmentList { get; set; }
}
public class EmaliAttachmentInfo
{
/// <summary>
/// 附件名称
/// </summary>
public string AttachmentName { get; set; }
/// <summary>
/// 附件路径
/// </summary>
public string AttachmentPath { get; set; }
}
public class EmailRecipientLogView
{
public Guid Id { get; set; }
/// <summary>
/// 邮件Id
/// </summary>
public Guid EmailLogId { get; set; }
/// <summary>
/// 收件人姓名
/// </summary>
public string RecipientName { get; set; }
/// <summary>
/// 收件人地址
/// </summary>
public string RecipientAddress { get; set; }
/// <summary>
/// 收件人类型
/// </summary>
public RecipientType RecipientTypeEnum { get; set; }
/// <summary>
/// 排序
/// </summary>
public int Sort { get; set; }
}
public class EmailLogAddOrEdit
{
public Guid? Id { get; set; }
/// <summary>
/// 邮件Id
/// </summary>
public string MessageId { get; set; }
/// <summary>
/// 唯一Id
/// </summary>
public string UniqueId { get; set; }
/// <summary>
/// 邮件主题
/// </summary>
public string EmailSubject { get; set; } = string.Empty;
/// <summary>
/// 日期
/// </summary>
public DateTime? EmailDate { get; set; }
/// <summary>
/// 错误信息
/// </summary>
public string ErrorInfo { get; set; }
/// <summary>
/// 发件人地址
/// </summary>
public string SenderAddress { get; set; }
/// <summary>
/// 发件人姓名
/// </summary>
public string SenderName { get; set; }
/// <summary>
/// 邮件内容
/// </summary>
public string Content { get; set; }
/// <summary>
/// 邮件状态
/// </summary>
public EmailState EmailStateEnum { get; set; }
}
public class GetEmailInfoOutDto:EmailLogView
{
}
public class GetEmailInfoInDto
{
public Guid Id { get; set; }
public Guid? TrialId { get; set; }
}
public class SynchronizationEmailInDto
{
public Guid? TrialId { get; set; }
}
public class ResendEmailInDto
{
public Guid Id { get; set; }
public Guid? TrialId { get; set; }
}
public class EmailAuthorization
{
public string FromEmail { get; set; } = string.Empty;
public string AuthorizationCode { get; set; } = string.Empty;
}
public class EmailLogQuery:PageInput
{
public Guid? TrialId { get; set; }
public DateTime? EmailStartDate { get; set; }
public DateTime? EmailEndDate { get; set; }
public EmailState? EmailStateEnum { get; set; }
public string ToRecipientName { get; set; } = string.Empty;
public string CcRecipientName { get; set; } = string.Empty;
}
@@ -1,234 +0,0 @@
//--------------------------------------------------------------------
// 此代码由liquid模板自动生成 byzhouhang 20240909
// 生成时间 2026-03-10 06:15:31Z
// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。
//--------------------------------------------------------------------
using System;
using IRaCIS.Core.Domain.Share;
using System.Collections.Generic;
namespace IRaCIS.Core.Application.ViewModel;
public class SubjectFileUploadRecordView
{
public string? SubjectCode { get; set; }
public string? VisitName { get; set; }
public string StudyCode { get; set; }
public Guid? SubjectId { get; set; }
public Guid? SubjectVisitId { get; set; }
public int FileCount { get; set; }
public string? UploadRegion { get; set; }
public string? TargetRegion { get; set; }
public DateTime CreateTime { get; set; }
public DateTime? SyncFinishedTime { get; set; }
public bool IsSync { get; set; }
}
public class FileUploadRecordView : FileUploadRecordAddOrEdit
{
public DateTime CreateTime { get; set; }
public DateTime UpdateTime { get; set; }
[Comment("同步结束时间-最后一个任务的时间")]
public DateTime? SyncFinishedTime { get; set; }
public string? SubjectCode { get; set; }
public string? VisitName { get; set; }
}
public class FileUploadRecordAddOrEdit
{
public Guid? Id { get; set; }
public string FileName { get; set; }
public long FileSize { get; set; }
public string FileType { get; set; }
public string Path { get; set; }
public string UploadBatchId { get; set; }
public BatchDataType BatchDataType { get; set; }
public string StudyCode { get; set; }
public Guid? TrialId { get; set; }
public Guid? SubjectId { get; set; }
public Guid? SubjectVisitId { get; set; }
public Guid? DicomStudyId { get; set; }
public Guid? NoneDicomStudyId { get; set; }
public string FileMarkId { get; set; }
public int? Priority { get; set; }
public string IP { get; set; }
public bool? IsNeedSync { get; set; }
public string UploadRegion { get; set; }
public string TargetRegion { get; set; }
public bool? IsSync { get; set; }
}
public class SubjectFileUploadRecordQuery : PageInput
{
public Guid TrialId { get; set; }
public string? SubjectCode { get; set; }
public string? VisitName { get; set; }
public string? StudyCode { get; set; }
public string? UploadRegion { get; set; }
public string? TargetRegion { get; set; }
public bool? IsSync { get; set; }
}
public class FileUploadRecordQuery : PageInput
{
public BatchDataType? BatchDataType { get; set; }
public Guid? TrialId { get; set; }
public int? DataFileType { get; set; }
public string StudyCode { get; set; }
public string? SubjectCode { get; set; }
public string? VisitName { get; set; }
public Guid? SubjectId { get; set; }
public Guid? SubjectVisitId { get; set; }
public string? FileMarkId { get; set; }
public string? FileName { get; set; }
public string? FileType { get; set; }
public string? IP { get; set; }
public bool? IsNeedSync { get; set; }
public bool? IsSync { get; set; }
public string? Path { get; set; }
public int? Priority { get; set; }
public string? TargetRegion { get; set; }
public string? UploadBatchId { get; set; }
public string? UploadRegion { get; set; }
public DateTime? SyncFinishedStartTime { get; set; }
public DateTime? SyncFinishedEndTime { get; set; }
public DateTime? UploadStartTime { get; set; }
public DateTime? UploadEndTime { get; set; }
}
public class UploadFileSyncRecordView
{
public Guid Id { get; set; }
public DateTime? StartTime { get; set; }
public DateTime? EndTime { get; set; }
public jobState JobState { get; set; }
public string Msg { get; set; }
public DateTime CreateTime { get; set; }
public DateTime UpdateTime { get; set; }
public Guid? FileUploadRecordId { get; set; }
public string? FileName { get; set; }
public string? FileType { get; set; }
public string? Path { get; set; }
public string StudyCode { get; set; }
public string? SubjectCode { get; set; }
public string? VisitName { get; set; }
}
public class UploadFileSyncRecordQuery : PageInput
{
public Guid? FileUploadRecordId { get; set; }
public jobState? JobState { get; set; }
public string? SubjectCode { get; set; }
public string? VisitName { get; set; }
public string? StudyCode { get; set; }
}
public class BatchAddSyncFileCommand
{
public List<Guid> FileUploadRecordIdList { get; set; }
public int? Priority { get; set; }
}
@@ -159,12 +159,6 @@ namespace IRaCIS.Core.Application.ViewModel
public class FrontAuditConfigAddOrEdit public class FrontAuditConfigAddOrEdit
{ {
public Guid? Id { get; set; } public Guid? Id { get; set; }
/// <summary>
/// 适用的标准
/// </summary>
public List<CriterionType> ApplyCriterionList { get; set; } = new List<CriterionType>() { };
public string Value { get; set; } = string.Empty; public string Value { get; set; } = string.Empty;
public string ValueCN { get; set; } = string.Empty; public string ValueCN { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty; public string Description { get; set; } = string.Empty;
@@ -1,12 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace IRaCIS.Core.Application.Service.Common.DTO
{
//public class MailModel
//{
//}
}
@@ -1,130 +0,0 @@
using DocumentFormat.OpenXml.Spreadsheet;
using IRaCIS.Application.Contracts;
using Medallion.Threading;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace IRaCIS.Core.Application.Service.Common;
[ApiExplorerSettings(GroupName = "Common")]
public class DeployConfigService(IMapper _mapper, IUserInfo _userInfo, IWebHostEnvironment _hostEnvironment, IStringLocalizer _localizer, IFusionCache _fusionCache) : BaseService
{
/// <summary>
/// 获取系统基础配置信息
/// </summary>
/// <param name="options"></param>
/// <returns></returns>
public async Task<ServiceVerifyConfigOption> GetSystemBasicConfigInfo([FromServices] IOptionsMonitor<ServiceVerifyConfigOption> options)
{
return options.CurrentValue;
}
/// <summary>
/// 更新系统基础配置
/// </summary>
/// <param name="basicSystemConfigOption"></param>
/// <returns></returns>
[HttpPost]
public async Task<IResponseOutput> UpdateSystemBasicConfig(ServiceVerifyConfigOption basicSystemConfigOption)
{
var path = $"appsettings.{_hostEnvironment.EnvironmentName}.json";
string text = System.IO.File.ReadAllText(path);
// 修改
JObject obj = JObject.Parse(text);
// QCRiskControl 相关配置
obj["BasicSystemConfig"][nameof(ServiceVerifyConfigOption.QCRiskControl)] = basicSystemConfigOption.QCRiskControl;
obj["BasicSystemConfig"][nameof(ServiceVerifyConfigOption.OpenUserComplexPassword)] = basicSystemConfigOption.OpenUserComplexPassword;
obj["BasicSystemConfig"][nameof(ServiceVerifyConfigOption.OpenSignDocumentBeforeWork)] = basicSystemConfigOption.OpenSignDocumentBeforeWork;
obj["BasicSystemConfig"][nameof(ServiceVerifyConfigOption.OpenTrialRelationDelete)] = basicSystemConfigOption.OpenTrialRelationDelete;
obj["BasicSystemConfig"][nameof(ServiceVerifyConfigOption.OpenLoginLimit)] = basicSystemConfigOption.OpenLoginLimit;
obj["BasicSystemConfig"][nameof(ServiceVerifyConfigOption.LoginMaxFailCount)] = basicSystemConfigOption.LoginMaxFailCount;
obj["BasicSystemConfig"][nameof(ServiceVerifyConfigOption.LoginFailLockMinutes)] = basicSystemConfigOption.LoginFailLockMinutes;
obj["BasicSystemConfig"][nameof(ServiceVerifyConfigOption.AutoLoginOutMinutes)] = basicSystemConfigOption.AutoLoginOutMinutes;
obj["BasicSystemConfig"][nameof(ServiceVerifyConfigOption.OpenLoginMFA)] = basicSystemConfigOption.OpenLoginMFA;
obj["BasicSystemConfig"][nameof(ServiceVerifyConfigOption.ContinuousReadingTimeMin)] = basicSystemConfigOption.ContinuousReadingTimeMin;
obj["BasicSystemConfig"][nameof(ServiceVerifyConfigOption.ReadingRestTimeMin)] = basicSystemConfigOption.ReadingRestTimeMin;
obj["BasicSystemConfig"][nameof(ServiceVerifyConfigOption.IsNeedChangePassWord)] = basicSystemConfigOption.IsNeedChangePassWord;
obj["BasicSystemConfig"][nameof(ServiceVerifyConfigOption.ChangePassWordDays)] = basicSystemConfigOption.ChangePassWordDays;
obj["BasicSystemConfig"][nameof(ServiceVerifyConfigOption.TemplateType)] = ((int)basicSystemConfigOption.TemplateType); // 枚举需要转换为字符串
obj["BasicSystemConfig"][nameof(ServiceVerifyConfigOption.ThirdPdfUrl)] = basicSystemConfigOption.ThirdPdfUrl;
obj["BasicSystemConfig"][nameof(ServiceVerifyConfigOption.UserMFAVerifyMinutes)] = basicSystemConfigOption.UserMFAVerifyMinutes;
// 重新写入appsettings.json
string result = obj.ToString();
System.IO.File.WriteAllText(path, result);
return ResponseOutput.Ok();
}
/// <summary>
/// 获取系统邮件配置信息
/// </summary>
/// <param name="options"></param>
/// <returns></returns>
public async Task<SystemEmailSendConfig> GetEmailConfigInfo([FromServices] IOptionsMonitor<SystemEmailSendConfig> options)
{
return options.CurrentValue;
}
/// <summary>
/// 更新系统邮件配置
/// </summary>
/// <param name="emailConfig"></param>
/// <returns></returns>
[HttpPost]
public async Task<IResponseOutput> UpdateSystemEmailConfig(SystemEmailSendConfig emailConfig)
{
var path = $"appsettings.{_hostEnvironment.EnvironmentName}.json";
string text = System.IO.File.ReadAllText(path);
// 修改
JObject obj = JObject.Parse(text);
// SystemEmailSendConfig 相关配置
obj["SystemEmailSendConfig"][nameof(SystemEmailSendConfig.Port)] = emailConfig.Port;
obj["SystemEmailSendConfig"][nameof(SystemEmailSendConfig.Host)] = emailConfig.Host;
obj["SystemEmailSendConfig"][nameof(SystemEmailSendConfig.Imap)] = emailConfig.Imap;
obj["SystemEmailSendConfig"][nameof(SystemEmailSendConfig.ImapPort)] = emailConfig.ImapPort;
obj["SystemEmailSendConfig"][nameof(SystemEmailSendConfig.FromEmail)] = emailConfig.FromEmail;
obj["SystemEmailSendConfig"][nameof(SystemEmailSendConfig.FromName)] = emailConfig.FromName;
obj["SystemEmailSendConfig"][nameof(SystemEmailSendConfig.AuthorizationCode)] = emailConfig.AuthorizationCode;
obj["SystemEmailSendConfig"][nameof(SystemEmailSendConfig.SiteUrl)] = emailConfig.SiteUrl;
obj["SystemEmailSendConfig"][nameof(SystemEmailSendConfig.PlatformName)] = emailConfig.PlatformName;
obj["SystemEmailSendConfig"][nameof(SystemEmailSendConfig.PlatformNameCN)] = emailConfig.PlatformNameCN;
obj["SystemEmailSendConfig"][nameof(SystemEmailSendConfig.SystemShortName)] = emailConfig.SystemShortName;
obj["SystemEmailSendConfig"][nameof(SystemEmailSendConfig.OrganizationName)] = emailConfig.OrganizationName;
obj["SystemEmailSendConfig"][nameof(SystemEmailSendConfig.OrganizationNameCN)] = emailConfig.OrganizationNameCN;
obj["SystemEmailSendConfig"][nameof(SystemEmailSendConfig.CompanyName)] = emailConfig.CompanyName;
obj["SystemEmailSendConfig"][nameof(SystemEmailSendConfig.CompanyNameCN)] = emailConfig.CompanyNameCN;
obj["SystemEmailSendConfig"][nameof(SystemEmailSendConfig.CompanyShortName)] = emailConfig.CompanyShortName;
obj["SystemEmailSendConfig"][nameof(SystemEmailSendConfig.CompanyShortNameCN)] = emailConfig.CompanyShortNameCN;
obj["SystemEmailSendConfig"][nameof(SystemEmailSendConfig.IsEnv_US)] = emailConfig.IsEnv_US;
obj["SystemEmailSendConfig"][nameof(SystemEmailSendConfig.EmailRegexStr)] = emailConfig.EmailRegexStr;
// 重新写入appsettings.json
string result = obj.ToString();
System.IO.File.WriteAllText(path, result);
return ResponseOutput.Ok();
}
}
@@ -1,769 +0,0 @@
//--------------------------------------------------------------------
// 此代码由liquid模板自动生成 byzhouhang 20240909
// 生成时间 2025-10-28 06:22:42Z
// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。
//--------------------------------------------------------------------
using DocumentFormat.OpenXml.Wordprocessing;
using IdentityModel.Client;
using IRaCIS.Core.Application.Helper;
using IRaCIS.Core.Application.Interfaces;
using IRaCIS.Core.Application.ViewModel;
using IRaCIS.Core.Domain.Models;
using IRaCIS.Core.Infra.EFCore;
using IRaCIS.Core.Infrastructure.Extention;
using MailKit;
using MailKit.Net.Imap;
using MailKit.Search;
using MailKit.Security;
using MassTransit;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using MimeKit;
using Panda.DynamicWebApi.Attributes;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Mail;
using System.Threading.Tasks;
namespace IRaCIS.Core.Application.Service;
/// <summary>
/// 邮件日志
/// </summary>
/// <param name="_emailLogRepository"></param>
/// <param name="systemEmailConfig"></param>
/// <param name="_mapper"></param>
/// <param name="_userInfo"></param>
/// <param name="_localizer"></param>
[ApiExplorerSettings(GroupName = "Common")]
public class EmailLogService(IRepository<EmailLog> _emailLogRepository,
IRepository<Trial> _trialRepository,
IRepository<EmailAttachmentLog> _emailAttachmentLogRepository,
IOSSService oSSService,
IRepository<EmailReSendLog> _emailReSendLog,
IRepository<EmailRecipientLog> _emailRecipientLogRepository,
IOptionsMonitor<SystemEmailSendConfig> systemEmailConfig,
IMapper _mapper, IUserInfo _userInfo, IStringLocalizer _localizer): BaseService, IEmailLogService
{
private readonly SystemEmailSendConfig _systemEmailConfig = systemEmailConfig.CurrentValue;
/// <summary>
/// 获取邮件日志列表
/// </summary>
/// <param name="inQuery"></param>
/// <returns></returns>
[HttpPost]
public async Task<PageOutput<EmailLogView>> GetEmailLogList(EmailLogQuery inDto)
{
var emailFromName = await _trialRepository.Where(x=>x.Id==inDto.TrialId).Select(x=>x.EmailFromName).FirstOrDefaultAsync();
if (emailFromName.IsNullOrEmpty())
{
emailFromName = _systemEmailConfig.FromName;
}
var emailLogQueryable = _emailLogRepository
.Where(x=>x.SenderName== emailFromName)
.WhereIf(inDto.EmailStartDate.HasValue, x => x.EmailDate >= inDto.EmailStartDate.Value)
.WhereIf(inDto.EmailEndDate.HasValue, x => x.EmailDate <= inDto.EmailEndDate.Value)
.WhereIf(inDto.EmailStateEnum.HasValue, x => x.EmailStateEnum == inDto.EmailStateEnum.Value)
.WhereIf(inDto.CcRecipientName.IsNotNullOrEmpty(),x=>x.EmailRecipientLogList.Any(x=>x.RecipientTypeEnum==RecipientType.Cc&&x.RecipientName.Contains(inDto.CcRecipientName)))
.WhereIf(inDto.ToRecipientName.IsNotNullOrEmpty(), x => x.EmailRecipientLogList.Any(x => x.RecipientTypeEnum == RecipientType.To && x.RecipientName.Contains(inDto.ToRecipientName)))
.ProjectTo<EmailLogView>(_mapper.ConfigurationProvider);
var defalutSortArray = new string[] { nameof(EmailLogView.EmailDate) + " desc" };
var pageList = await emailLogQueryable.ToPagedListAsync(inDto, defalutSortArray);
return pageList;
}
/// <summary>
/// 获取重发信息
/// </summary>
/// <param name="inDto"></param>
/// <returns></returns>
[HttpPost]
public async Task<PageOutput<EmailLogView>> GetReSendEmail(GetReSendEmailInDto inDto)
{
var messageId = await _emailLogRepository.Where(x => x.Id == inDto.Id).Select(x => x.MessageId).FirstOrDefaultAsync();
var reSendMessagelist = await _emailReSendLog.Where(x => x.MainMailMessageId == messageId).Select(x => x.ReMailMessageId).ToListAsync();
var emailLogQueryable = _emailLogRepository
.Where(x => reSendMessagelist.Contains(x.MessageId))
.WhereIf(inDto.EmailStartDate.HasValue, x => x.EmailDate >= inDto.EmailStartDate.Value)
.WhereIf(inDto.EmailEndDate.HasValue, x => x.EmailDate <= inDto.EmailEndDate.Value)
.WhereIf(inDto.EmailStateEnum.HasValue, x => x.EmailStateEnum == inDto.EmailStateEnum.Value)
.WhereIf(inDto.CcRecipientName.IsNotNullOrEmpty(), x => x.EmailRecipientLogList.Any(x => x.RecipientTypeEnum == RecipientType.Cc && x.RecipientName.Contains(inDto.CcRecipientName)))
.WhereIf(inDto.ToRecipientName.IsNotNullOrEmpty(), x => x.EmailRecipientLogList.Any(x => x.RecipientTypeEnum == RecipientType.To && x.RecipientName.Contains(inDto.ToRecipientName)))
.ProjectTo<EmailLogView>(_mapper.ConfigurationProvider);
var defalutSortArray = new string[] { nameof(EmailLogView.EmailDate) + " desc" };
var pageList = await emailLogQueryable.ToPagedListAsync(inDto, defalutSortArray);
return pageList;
}
/// <summary>
/// 获取单条邮件日志详情
/// </summary>
/// <param name="inDto"></param>
/// <returns></returns>
[HttpPost]
public async Task<GetEmailInfoOutDto> GetEmailInfo(GetEmailInfoInDto inDto)
{
var emailInfo=await _emailLogRepository
.Where(x => x.Id == inDto.Id).Include(x=>x.EmailRecipientLogList)
.ProjectTo<GetEmailInfoOutDto>(_mapper.ConfigurationProvider).AsNoTracking()
.FirstNotNullAsync();
if (emailInfo.UniqueId.IsNotNullOrEmpty())
{
using (var client = new ImapClient())
{
try
{
await AuthenticateImap(client,inDto.TrialId);
var sentFolder = OpenSentFolder(client);
var uid = new UniqueId(uint.Parse(emailInfo.UniqueId));
var message = sentFolder.GetMessage(uid);
emailInfo.Content = message.HtmlBody ?? message.TextBody ?? string.Empty;
if (emailInfo.AttachmentList.Count == 0)
{
List< EmaliAttachmentInfo > attachmentInfos = new List<EmaliAttachmentInfo>();
foreach (var att in message.Attachments)
{
EmaliAttachmentInfo emaliAttachmentInfo = new EmaliAttachmentInfo();
emaliAttachmentInfo.AttachmentName = att.ContentDisposition?.FileName ?? att.ContentType.Name ?? "unknown";
// 2. 解码后的流直接上传,不落盘
if (att is MimePart part)
{
// 重要:每次上传新建一个独立流,否则迭代过程中流位置会乱
await using var decodeStream = new MemoryStream();
await part.Content.DecodeToAsync(decodeStream);
decodeStream.Position = 0;
emaliAttachmentInfo.AttachmentPath = await oSSService.UploadToOSSAsync(
fileStream: decodeStream,
oosFolderPath: $"EmailAttachment/{emailInfo.Id}", // OSS 虚拟目录
fileRealName: emaliAttachmentInfo.AttachmentName,
isFileNameAddGuid: true,
uploadInfo: new FileUploadRecordAddOrEdit() {TrialId= inDto.TrialId, BatchDataType = BatchDataType.EmailAttach }); // 让方法自己在文件名前加 Guid
attachmentInfos.Add(emaliAttachmentInfo);
}
}
List<EmailAttachmentLog> emailAttachmentLog = attachmentInfos.Select(x => new EmailAttachmentLog()
{
EmailLogId = emailInfo.Id.Value,
AttachmentName = x.AttachmentName,
AttachmentPath = x.AttachmentPath,
}).ToList();
await _emailAttachmentLogRepository.AddRangeAsync(emailAttachmentLog);
await _emailAttachmentLogRepository.SaveChangesAsync();
emailInfo.AttachmentList = attachmentInfos;
}
sentFolder.Close();
}
catch (Exception ex)
{
}
finally
{
client.Disconnect(true);
}
}
}
//var ossRelativePath = await oSSService.UploadToOSSAsync(fileStream, $"{trialId.ToString()}/InspectionUpload/DataReconciliation", realFileName);
return emailInfo;
}
/// <summary>
/// 重发邮件
/// </summary>
/// <param name="inDto"></param>
/// <returns></returns>
[HttpPost]
public async Task<IResponseOutput> ResendEmail(ResendEmailInDto inDto)
{
var emailInfo = await _emailLogRepository
.Where(x => x.Id == inDto.Id)
.ProjectTo<GetEmailInfoOutDto>(_mapper.ConfigurationProvider)
.FirstNotNullAsync();
if (emailInfo.UniqueId.IsNotNullOrEmpty())
{
using (var client = new ImapClient())
{
try
{
await AuthenticateImap(client,inDto.TrialId);
var sentFolder = OpenSentFolder(client);
var uid = new UniqueId(uint.Parse(emailInfo.UniqueId));
var message = sentFolder.GetMessage(uid);
emailInfo.Content = message.HtmlBody ?? message.TextBody ?? string.Empty;
sentFolder.Close();
}
catch (Exception ex)
{
}
finally
{
client.Disconnect(true);
}
}
}
var messageToSend = new MimeMessage();
// 发件地址
messageToSend.From.Add(new MailboxAddress(emailInfo.SenderName, emailInfo.SenderAddress));
foreach (var item in emailInfo.RecipientList.Where(x => x.RecipientTypeEnum == RecipientType.To))
{
messageToSend.To.Add(new MailboxAddress(item.RecipientName, item.RecipientAddress));
}
foreach (var item in emailInfo.RecipientList.Where(x => x.RecipientTypeEnum == RecipientType.Cc))
{
messageToSend.Cc.Add(new MailboxAddress(item.RecipientName, item.RecipientAddress));
}
messageToSend.Subject = emailInfo.EmailSubject;
var builder = new BodyBuilder();
builder.HtmlBody = emailInfo.Content;
messageToSend.Body = builder.ToMessageBody();
SystemEmailSendConfig sendConfig = new SystemEmailSendConfig()
{
};
if (inDto.TrialId != null)
{
sendConfig = await _trialRepository.Where(x => x.Id == inDto.TrialId.Value).Select(x => new SystemEmailSendConfig()
{
Host = _systemEmailConfig.Host,
Port = _systemEmailConfig.Port,
FromEmail = x.EmailFromEmail,
AuthorizationCode = x.EmailAuthorizationCode
}).FirstNotNullAsync();
}
else
{
sendConfig = _systemEmailConfig;
}
var msgid= await SendEmailHelper.SendEmailAsync(messageToSend, sendConfig);
await _emailReSendLog.AddAsync(new EmailReSendLog()
{
MainMailMessageId= emailInfo.MessageId,
ReMailMessageId= msgid
});
await _emailReSendLog.SaveChangesAsync();
await SynchronizationEmail(new SynchronizationEmailInDto() {
TrialId= inDto.TrialId
});
return ResponseOutput.Ok();
}
/// <summary>
/// 同步邮件
/// </summary>
/// <returns></returns>
[HttpPost]
public async Task<IResponseOutput> SynchronizationEmail(SynchronizationEmailInDto inDto)
{
var fromEmail = _systemEmailConfig.FromEmail;
if (inDto.TrialId != null)
{
fromEmail= await _trialRepository.Where(x => x.Id == inDto.TrialId.Value).Select(x => x.EmailFromEmail).FirstNotNullAsync();
}
var maxTime = await _emailLogRepository.Where(x=>x.SenderAddress== fromEmail).MaxAsync(t => t.EmailDate);
var startDate = maxTime ?? DateTime.MinValue;
List<EmailLog> emailList = new List<EmailLog>();
List<EmailRecipientLog> EmailRecipientLogList = new List<EmailRecipientLog>();
// 第一步:同步发件箱邮件
using (var client = new ImapClient())
{
try
{
await AuthenticateImap(client, inDto.TrialId);
var sentFolder = OpenSentFolder(client);
var searchQuery = SearchQuery.All.And(SearchQuery.DeliveredAfter(startDate));
var uids = sentFolder.Search(searchQuery).ToList();
foreach (var uid in uids)
{
try
{
var message = sentFolder.GetMessage(uid);
if (message.Date.DateTime <= startDate)
{
continue;
}
var emaillog = new EmailLog
{
Id = NewId.NextGuid(),
UniqueId = uid.ToString(),
MessageId = message.MessageId ?? string.Empty,
EmailSubject = message.Subject ?? string.Empty,
EmailDate = message.Date.DateTime,
EmailStateEnum = EmailState.Success,
};
var fromMailbox = message.From.Mailboxes.FirstOrDefault();
if (fromMailbox != null)
{
emaillog.SenderAddress = fromMailbox.Address;
emaillog.SenderName = fromMailbox.Name ?? string.Empty;
}
List<EmailRecipientLog> recipientLogs = new List<EmailRecipientLog>();
int sort = 0;
message.To.Mailboxes.ForEach(x =>
{
recipientLogs.Add(new EmailRecipientLog()
{
RecipientName = x.Name ?? string.Empty,
RecipientAddress = x.Address,
EmailLogId = emaillog.Id,
RecipientTypeEnum = RecipientType.To,
Sort = sort++,
});
});
sort = 0;
message.Cc.Mailboxes.ForEach(x =>
{
recipientLogs.Add(new EmailRecipientLog()
{
RecipientName = x.Name ?? string.Empty,
RecipientAddress = x.Address,
EmailLogId = emaillog.Id,
RecipientTypeEnum = RecipientType.Cc,
Sort = sort++,
});
});
emailList.Add(emaillog);
EmailRecipientLogList.AddRange(recipientLogs);
}
catch (Exception ex)
{
Console.WriteLine($"处理邮件 {uid} 失败: {ex.Message}");
}
}
sentFolder.Close();
}
catch (Exception ex)
{
Console.WriteLine($"同步发件箱邮件时出错: {ex.Message}");
}
finally
{
client.Disconnect(true);
}
}
// 保存同步的发件箱邮件
await _emailLogRepository.AddRangeAsync(emailList);
await _emailRecipientLogRepository.AddRangeAsync(EmailRecipientLogList);
await _emailLogRepository.SaveChangesAsync();
// 第二步:处理收件箱中的邮件发送失败通知
try
{
await ProcessInboxFailureNotifications(startDate,inDto.TrialId);
}
catch (Exception ex)
{
Console.WriteLine($"处理收件箱失败通知邮件时出错: {ex.Message}");
Console.WriteLine($"异常堆栈: {ex.StackTrace}");
// 即使处理失败通知邮件出错,也不影响整体同步操作的成功
// 记录警告但不中断主流程
}
return ResponseOutput.Ok();
}
private IMailFolder OpenSentFolder(ImapClient client)
{
IMailFolder folder = null;
try
{
folder = client.GetFolder(SpecialFolder.Sent);
}
catch { }
if (folder == null)
{
var candidates = new[] { "已发送", "已发送邮件", "Sent", "Sent Items", "[Gmail]/Sent Mail" };
foreach (var name in candidates)
{
try
{
var f = client.GetFolder(name);
if (f != null)
{
folder = f;
break;
}
}
catch { }
}
}
if (folder == null)
{
try
{
var personal = client.GetFolder(client.PersonalNamespaces.FirstOrDefault());
if (personal != null)
{
foreach (var sub in personal.GetSubfolders(false))
{
if (string.Equals(sub.FullName, "Sent", StringComparison.OrdinalIgnoreCase) ||
string.Equals(sub.FullName, "Sent Items", StringComparison.OrdinalIgnoreCase))
{
folder = sub;
break;
}
}
}
}
catch { }
}
if (folder == null)
throw new InvalidOperationException("未找到已发送文件夹");
folder.Open(FolderAccess.ReadOnly);
return folder;
}
private IMailFolder OpenInboxFolder(ImapClient client)
{
IMailFolder folder = null;
try
{
folder = client.GetFolder(SpecialFolder.All);
}
catch { }
if (folder == null)
{
var candidates = new[] { "收件箱", "Inbox" };
foreach (var name in candidates)
{
try
{
var f = client.GetFolder(name);
if (f != null)
{
folder = f;
break;
}
}
catch { }
}
}
if (folder == null)
{
try
{
var personal = client.GetFolder(client.PersonalNamespaces.FirstOrDefault());
if (personal != null)
{
foreach (var sub in personal.GetSubfolders(false))
{
if (string.Equals(sub.FullName, "Inbox", StringComparison.OrdinalIgnoreCase))
{
folder = sub;
break;
}
}
}
}
catch { }
}
if (folder == null)
throw new InvalidOperationException("未找到收件箱文件夹");
folder.Open(FolderAccess.ReadOnly);
return folder;
}
private async Task AuthenticateImap(ImapClient client, Guid? trialId)
{
try
{
EmailAuthorization authorization = new EmailAuthorization()
{
FromEmail = _systemEmailConfig.FromEmail,
AuthorizationCode = _systemEmailConfig.AuthorizationCode,
};
if (trialId != null)
{
authorization = await _trialRepository.Where(x => x.Id == trialId.Value).Select(x => new EmailAuthorization()
{
AuthorizationCode = x.EmailAuthorizationCode,
FromEmail = x.EmailFromEmail
}).FirstNotNullAsync();
}
client.Connect(_systemEmailConfig.Imap, _systemEmailConfig.ImapPort, SecureSocketOptions.SslOnConnect);
client.Authenticate(authorization.FromEmail, authorization.AuthorizationCode);
}
catch (AuthenticationException)
{
//if (_systemEmailConfig.UseOAuth2 && _systemEmailConfig.OAuth2AccessToken.IsNotNullOrEmpty())
//{
// var sasl = new SaslMechanismOAuth2(_systemEmailConfig.FromEmail, _systemEmailConfig.OAuth2AccessToken);
// client.Authenticate(sasl);
// return;
//}
//if (_systemEmailConfig.OAuth2AccessToken.IsNullOrEmpty())
//{
// var token = AcquireOAuth2TokenByPassword();
// if (token.IsNotNullOrEmpty())
// {
// _systemEmailConfig.OAuth2AccessToken = token;
// var sasl = new SaslMechanismOAuth2(_systemEmailConfig.FromEmail, token);
// client.Authenticate(sasl);
// return;
// }
//}
throw;
}
}
/// <summary>
/// 处理收件箱中的邮件发送失败通知
/// </summary>
/// <returns></returns>
public async Task ProcessInboxFailureNotifications(DateTime startTime, Guid? trialId)
{
using (var client = new ImapClient())
{
try
{
await AuthenticateImap(client, trialId);
var inboxFolder = OpenInboxFolder(client);
// 搜索可能包含失败通知的邮件
// 通常失败通知邮件会包含"失败"、"错误"、"undeliverable"、"delivery failed"等关键词
var searchQuery = SearchQuery.All.Or(SearchQuery.SubjectContains("失败"))
.Or(SearchQuery.SubjectContains("错误"))
.Or(SearchQuery.SubjectContains("undeliverable"))
.Or(SearchQuery.SubjectContains("delivery failed"))
.Or(SearchQuery.SubjectContains("Delivery Status Notification"))
.Or(SearchQuery.BodyContains("失败"))
.Or(SearchQuery.BodyContains("错误"))
.Or(SearchQuery.BodyContains("undeliverable"))
.Or(SearchQuery.BodyContains("delivery failed")).And(SearchQuery.DeliveredAfter(startTime));
var uids = inboxFolder.Search(searchQuery).ToList();
var processedCount = 0;
var updatedCount = 0;
foreach (var uid in uids)
{
try
{
var message = inboxFolder.GetMessage(uid);
// 尝试从邮件内容中提取原始邮件的Message-Id或相关信息
var originalMessageId = ExtractOriginalMessageId(message);
if (!string.IsNullOrEmpty(originalMessageId))
{
// 查找对应的EmailLog记录
var emailLog = await _emailLogRepository
.Where(x => x.MessageId == originalMessageId)
.FirstOrDefaultAsync();
await _emailLogRepository.BatchUpdateNoTrackingAsync(x => x.MessageId == originalMessageId, x => new EmailLog()
{
EmailStateEnum = EmailState.Error,
ErrorInfo = message.TextBody
});
updatedCount++;
}
processedCount++;
}
catch (Exception ex)
{
}
}
inboxFolder.Close();
}
catch (Exception ex)
{
Console.WriteLine($"连接收件箱处理失败通知邮件时出错: {ex.Message}");
Console.WriteLine($"异常堆栈: {ex.StackTrace}");
}
finally
{
try
{
client.Disconnect(true);
}
catch (Exception ex)
{
Console.WriteLine($"断开IMAP客户端连接时出错: {ex.Message}");
}
}
}
}
/// <summary>
/// 从失败通知邮件中提取原始邮件的Message-Id
/// </summary>
/// <param name="message"></param>
/// <returns></returns>
private string ExtractOriginalMessageId(MimeMessage message)
{
// 首先检查邮件头中是否有原始Message-Id
var originalMessageId = message.Headers["Original-Message-ID"] ??
message.Headers["X-Original-Message-ID"] ??
message.Headers["In-Reply-To"];
if (!string.IsNullOrEmpty(originalMessageId))
{
// 清理Message-Id格式(移除尖括号)
originalMessageId = originalMessageId.Trim('<', '>');
return originalMessageId;
}
// 检查邮件附件中是否包含.eml文件(这是最常见的失败通知格式)
foreach (var attachment in message.Attachments)
{
if (attachment is MessagePart messagePart)
{
// 直接获取嵌入的邮件消息
var originalMessageIdFromAttachment = messagePart.Message?.MessageId;
if (!string.IsNullOrEmpty(originalMessageIdFromAttachment))
{
return originalMessageIdFromAttachment.Trim('<', '>');
}
}
else if (attachment is MimePart mimePart)
{
// 检查文件扩展名是否为.eml
var fileName = mimePart.FileName ?? mimePart.ContentType.Name;
if (!string.IsNullOrEmpty(fileName) &&
(fileName.EndsWith(".eml", StringComparison.OrdinalIgnoreCase) ||
mimePart.ContentType.MimeType.Equals("message/rfc822", StringComparison.OrdinalIgnoreCase)))
{
try
{
// 解析.eml附件内容
using var memoryStream = new MemoryStream();
mimePart.Content.DecodeTo(memoryStream);
memoryStream.Position = 0;
var originalMessage = MimeMessage.Load(memoryStream);
if (!string.IsNullOrEmpty(originalMessage?.MessageId))
{
return originalMessage.MessageId.Trim('<', '>');
}
}
catch (Exception ex)
{
Console.WriteLine($"解析.eml附件时出错: {ex.Message}");
}
}
}
}
// 如果邮件头和附件中都没有,尝试从邮件正文中提取
var content = message.HtmlBody ?? message.TextBody ?? string.Empty;
// 尝试匹配常见的Message-Id格式
var messageIdPattern = @"(?:Message-ID|Message-Id|message-id):\s*<?([^>\s]+)>?";
var match = System.Text.RegularExpressions.Regex.Match(content, messageIdPattern, System.Text.RegularExpressions.RegexOptions.IgnoreCase);
if (match.Success)
{
return match.Groups[1].Value;
}
// 尝试从邮件引用部分提取
var referencePattern = @"^>.*Message-ID:\s*<?([^>\s]+)>?";
var matches = System.Text.RegularExpressions.Regex.Matches(content, referencePattern, System.Text.RegularExpressions.RegexOptions.Multiline | System.Text.RegularExpressions.RegexOptions.IgnoreCase);
if (matches.Count > 0)
{
return matches[0].Groups[1].Value;
}
return null;
}
public async Task<IResponseOutput> AddOrUpdateEmailLog(EmailLogAddOrEdit addOrEditEmailLog)
{
var entity = await _emailLogRepository.InsertOrUpdateAsync(addOrEditEmailLog, true);
return ResponseOutput.Ok(entity.Id.ToString());
}
[HttpDelete("{emailLogId:guid}")]
public async Task<IResponseOutput> DeleteEmailLog(Guid emailLogId)
{
var success = await _emailLogRepository.DeleteFromQueryAsync(t => t.Id == emailLogId,true);
return ResponseOutput.Ok();
}
}
@@ -4,18 +4,9 @@
// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 // 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。
//-------------------------------------------------------------------- //--------------------------------------------------------------------
using DocumentFormat.OpenXml.Spreadsheet;
using IRaCIS.Core.Application.Helper; using IRaCIS.Core.Application.Helper;
using IRaCIS.Core.Domain.Share; using IRaCIS.Core.Domain.Share;
using MailKit;
using MailKit.Net.Imap;
using MailKit.Search;
using MailKit.Security;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using NPOI.SS.Formula.Functions; using NPOI.SS.Formula.Functions;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
@@ -26,75 +17,8 @@ namespace IRaCIS.Core.Application.Contracts
/// </summary> /// </summary>
[ApiExplorerSettings(GroupName = "Common")] [ApiExplorerSettings(GroupName = "Common")]
public class EmailNoticeConfigService(IRepository<EmailNoticeConfig> _emailNoticeConfigrepository, public class EmailNoticeConfigService(IRepository<EmailNoticeConfig> _emailNoticeConfigrepository,
IRepository<EmailNoticeUserType> _emailNoticeUserTypeRepository, IRepository<EmailNoticeUserType> _emailNoticeUserTypeRepository, IMapper _mapper, IUserInfo _userInfo, IStringLocalizer _localizer) : BaseService, IEmailNoticeConfigService
IOptionsMonitor<SystemEmailSendConfig> systemEmailConfig,
IMapper _mapper, IUserInfo _userInfo, IStringLocalizer _localizer) : BaseService, IEmailNoticeConfigService
{ {
private readonly SystemEmailSendConfig _systemEmailConfig = systemEmailConfig.CurrentValue;
/// <summary>
/// 获取邮件列表
/// </summary>
/// <returns></returns>
[HttpPost]
public async Task<object> GetEmailList()
{
List<IMessageSummary> emailList = new List<IMessageSummary>();
using (var client = new ImapClient())
{
try
{
// 连接阿里邮箱 IMAP 服务器(使用 SSL)
client.Connect(_systemEmailConfig.Imap, 993, SecureSocketOptions.SslOnConnect);
// 登录
client.Authenticate(_systemEmailConfig.FromEmail, _systemEmailConfig.AuthorizationCode);
// 3. 获取发件箱文件夹 - 使用你找到的“已发送”
var sentFolder = client.GetFolder("已发送");
sentFolder.Open(FolderAccess.ReadOnly);
// 4. 搜索所有邮件(你可以在这里添加更精确的搜索条件)
var uids = sentFolder.Search(SearchQuery.All);
Console.WriteLine($"找到 {uids.Count} 封已发送邮件");
// 5. 遍历并处理邮件(示例中处理前10封)
foreach (var uid in uids.Take(10))
{
var message = sentFolder.GetMessage(uid);
// 输出邮件基本信息
Console.WriteLine($"主题: {message.Subject}");
Console.WriteLine($"收件人: {string.Join(", ", message.To.Mailboxes.Select(m => m.Address))}");
Console.WriteLine($"日期: {message.Date.LocalDateTime:yyyy-MM-dd HH:mm:ss}");
Console.WriteLine($"发件人: {message.From}");
Console.WriteLine("----------------------------------");
}
sentFolder.Close();
}
catch (Exception ex)
{
Console.WriteLine($"操作失败: {ex.Message}");
}
finally
{
client.Disconnect(true);
}
}
return true;
}
[HttpPost] [HttpPost]
public async Task<PageOutput<EmailNoticeConfigView>> GetEmailNoticeConfigList(EmailNoticeConfigQuery inQuery) public async Task<PageOutput<EmailNoticeConfigView>> GetEmailNoticeConfigList(EmailNoticeConfigQuery inQuery)
@@ -22,8 +22,6 @@ using System.ComponentModel.Design;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Reflection;
using System.Text;
using static IRaCIS.Core.Application.Service.ExcelExportHelper; using static IRaCIS.Core.Application.Service.ExcelExportHelper;
using IDictionaryService = IRaCIS.Application.Interfaces.IDictionaryService; using IDictionaryService = IRaCIS.Application.Interfaces.IDictionaryService;
using TrialIdentityUser = IRaCIS.Core.Domain.Models.TrialIdentityUser; using TrialIdentityUser = IRaCIS.Core.Domain.Models.TrialIdentityUser;
@@ -34,8 +32,6 @@ namespace IRaCIS.Core.Application.Service.Common
[ApiExplorerSettings(GroupName = "Common")] [ApiExplorerSettings(GroupName = "Common")]
public class ExcelExportService(IRepository<TrialUserRole> _trialUserRoleRepository, public class ExcelExportService(IRepository<TrialUserRole> _trialUserRoleRepository,
IRepository<VisitTask> _visitTaskRepository, IRepository<VisitTask> _visitTaskRepository,
IRepository<Dictionary> _dictionaryRepository,
IRepository<Internationalization> _internationalizationRepository,
IRepository<ReadingQuestionCriterionTrial> _readingQuestionCriterionTrialRepository, IRepository<ReadingQuestionCriterionTrial> _readingQuestionCriterionTrialRepository,
IRepository<SystemDocNeedConfirmedUserType> _systemDocNeedConfirmedUserTypeRepository, IRepository<SystemDocNeedConfirmedUserType> _systemDocNeedConfirmedUserTypeRepository,
IRepository<DicomStudy> _dicomStudyRepository, IRepository<DicomStudy> _dicomStudyRepository,
@@ -471,10 +467,7 @@ namespace IRaCIS.Core.Application.Service.Common
.WhereIf(!string.IsNullOrEmpty(inQuery.Code), o => o.TrialCode.Contains(inQuery.Code)) .WhereIf(!string.IsNullOrEmpty(inQuery.Code), o => o.TrialCode.Contains(inQuery.Code))
.WhereIf(!string.IsNullOrEmpty(inQuery.ResearchProgramNo), o => o.ResearchProgramNo.Contains(inQuery.ResearchProgramNo)) .WhereIf(!string.IsNullOrEmpty(inQuery.ResearchProgramNo), o => o.ResearchProgramNo.Contains(inQuery.ResearchProgramNo))
.WhereIf(!string.IsNullOrWhiteSpace(inQuery.ExperimentName), o => o.ExperimentName.Contains(inQuery.ExperimentName)) .WhereIf(!string.IsNullOrWhiteSpace(inQuery.ExperimentName), o => o.ExperimentName.Contains(inQuery.ExperimentName))
.WhereIf(_userInfo.UserTypeEnumInt != (int)UserTypeEnum.SuperAdmin && _userInfo.UserTypeEnumInt != (int)UserTypeEnum.Admin && _userInfo.UserTypeEnumInt != (int)UserTypeEnum.OP, .WhereIf(_userInfo.UserTypeEnumInt != (int)UserTypeEnum.SuperAdmin && _userInfo.UserTypeEnumInt != (int)UserTypeEnum.Admin && _userInfo.UserTypeEnumInt != (int)UserTypeEnum.OP, t => t.TrialIdentityUserList.Any(t => t.IdentityUserId == _userInfo.IdentityUserId && t.IsDeleted == false) && t.IsDeleted == false)
t => t.TrialIdentityUserList.Any(t => t.IdentityUserId == _userInfo.IdentityUserId && t.IsDeleted == false
&& t.TrialUserRoleList.Any(t => t.UserId == _userInfo.UserRoleId && t.IsDeleted == false))
&& t.IsDeleted == false)
.WhereIf(inQuery.CriterionType != null, o => o.TrialReadingCriterionList.Any(t => t.CriterionType == inQuery.CriterionType && t.IsSigned && t.IsConfirm)) .WhereIf(inQuery.CriterionType != null, o => o.TrialReadingCriterionList.Any(t => t.CriterionType == inQuery.CriterionType && t.IsSigned && t.IsConfirm))
.WhereIf(!string.IsNullOrEmpty(inQuery.PM_EMail), o => o.TrialUserRoleList.Any(t => t.UserRole.IdentityUser.EMail.Contains(inQuery.PM_EMail) && (t.UserRole.UserTypeEnum == UserTypeEnum.ProjectManager || t.UserRole.UserTypeEnum == UserTypeEnum.APM))) .WhereIf(!string.IsNullOrEmpty(inQuery.PM_EMail), o => o.TrialUserRoleList.Any(t => t.UserRole.IdentityUser.EMail.Contains(inQuery.PM_EMail) && (t.UserRole.UserTypeEnum == UserTypeEnum.ProjectManager || t.UserRole.UserTypeEnum == UserTypeEnum.APM)))
.Select(t => new TrialToBeDoneDto() .Select(t => new TrialToBeDoneDto()
@@ -984,7 +977,7 @@ namespace IRaCIS.Core.Application.Service.Common
var query = _subjectVisitRepository.Where(x => x.TrialId == inQuery.TrialId) var query = _subjectVisitRepository.Where(x => x.TrialId == inQuery.TrialId)
.Where(t => t.Subject.IsSubjectQuit == false || t.SubmitState >= SubmitStateEnum.ToSubmit) .Where(t => t.Subject.IsSubjectQuit == false || t.SubmitState >= SubmitStateEnum.ToSubmit)
.WhereIf(inQuery.SubjectStatus != null, t => t.Subject.Status == inQuery.SubjectStatus)
.Where(t => t.Subject.FinalSubjectVisitId != null ? t.VisitNum <= t.Subject.FinalSubjectVisit.VisitNum : true) .Where(t => t.Subject.FinalSubjectVisitId != null ? t.VisitNum <= t.Subject.FinalSubjectVisit.VisitNum : true)
.WhereIf(inQuery.TrialSiteId != null, t => t.TrialSiteId == inQuery.TrialSiteId) .WhereIf(inQuery.TrialSiteId != null, t => t.TrialSiteId == inQuery.TrialSiteId)
.WhereIf(inQuery.SubjectId != null, t => t.Subject.Id == inQuery.SubjectId) .WhereIf(inQuery.SubjectId != null, t => t.Subject.Id == inQuery.SubjectId)
@@ -1034,7 +1027,7 @@ namespace IRaCIS.Core.Application.Service.Common
var query = _subjectVisitRepository.Where(x => x.TrialId == inQuery.TrialId) var query = _subjectVisitRepository.Where(x => x.TrialId == inQuery.TrialId)
.Where(t => t.Subject.IsSubjectQuit == false || t.AuditState > AuditStateEnum.ToAudit) .Where(t => t.Subject.IsSubjectQuit == false || t.AuditState > AuditStateEnum.ToAudit)
.WhereIf(inQuery.SubjectStatus != null, t => t.Subject.Status == inQuery.SubjectStatus)
.WhereIf(inQuery.VisitId != null, t => t.Id == inQuery.VisitId) .WhereIf(inQuery.VisitId != null, t => t.Id == inQuery.VisitId)
.WhereIf(inQuery.CurrentActionUserId != null, t => t.CurrentActionUserId == inQuery.CurrentActionUserId) .WhereIf(inQuery.CurrentActionUserId != null, t => t.CurrentActionUserId == inQuery.CurrentActionUserId)
.WhereIf(inQuery.ChallengeState != null, t => t.ChallengeState == inQuery.ChallengeState) .WhereIf(inQuery.ChallengeState != null, t => t.ChallengeState == inQuery.ChallengeState)
@@ -1626,7 +1619,6 @@ namespace IRaCIS.Core.Application.Service.Common
exportInfo.CurrentTime = ExportExcelConverterDate.DateTimeInternationalToString(DateTime.Now, _userInfo.TimeZoneId); exportInfo.CurrentTime = ExportExcelConverterDate.DateTimeInternationalToString(DateTime.Now, _userInfo.TimeZoneId);
if (inQuery.IsRandomOrderList == true) if (inQuery.IsRandomOrderList == true)
{ {
list = list.OrderBy(t => t.RandomOrder).ToList(); list = list.OrderBy(t => t.RandomOrder).ToList();
@@ -1765,80 +1757,6 @@ namespace IRaCIS.Core.Application.Service.Common
var list = taskMedicalReviewQueryable.OrderBy(t => t.TrialSiteCode).ThenBy(t => t.SubjectCode).ThenBy(t => t.VisitTaskNum).ToList(); var list = taskMedicalReviewQueryable.OrderBy(t => t.TrialSiteCode).ThenBy(t => t.SubjectCode).ThenBy(t => t.VisitTaskNum).ToList();
#region by he wen tao
Dictionary<string, string> i18NKeys = new Dictionary<string, string>()
{
{ "Msg1","trials:medicalFeedback:message:msg1"},
{ "Msg2","trials:medicalFeedback:message:msg2"},
{ "Msg3","trials:medicalFeedback:message:msg3"},
{ "Msg4", "trials:medicalFeedback:message:msg4"},
{ "Msg5", "trials:medicalFeedback:message:msg5"},
{ "CloseReasonEnum", "trials:medicalFeedback:title:closeReasonEnum"},
{ "IsEndorse", "trials:medicalFeedback:title:isEndorse"},
{ "Reason", "trials:medicalFeedback:title:reason"},
{ "IsRequestReread", "trials:medicalFeedback:title:isRequestReread"},
};
var i18Values = i18NKeys.Select(x => x.Value).ToList();
var i18nList = await _internationalizationRepository.Where(x => i18Values.Contains(x.Code))
.Select(x => new
{
Code = x.Code,
Value = _userInfo.IsEn_Us ? x.Value : x.ValueCN
}).ToListAsync();
List<string> dictionaryCodeList = new List<string>()
{
"AuditAdvice",
"MedicalDialogCloseEnum",
"MedicalReviewDoctorUserIdea",
"YesOrNo",
};
var dictionadParents = await _dictionaryRepository.Where(x => dictionaryCodeList.Contains(x.Parent.Code))
.Select(x => new
{
ParentCode = x.Parent.Code,
Code = x.Code,
Value = _userInfo.IsEn_Us ? x.Value : x.ValueCN
}).ToListAsync();
JointMedicalReviewI18n i18N = new JointMedicalReviewI18n() { };
foreach (var kv in i18NKeys)
{
PropertyInfo? pi = i18N.GetType().GetProperty(kv.Key);
if (pi != null && pi.CanWrite)
{
var value = i18nList.Where(x => x.Code == kv.Value).Select(x => x.Value).FirstOrDefault() ?? string.Empty;
object safeValue = Convert.ChangeType(value, pi.PropertyType);
pi.SetValue(i18N, safeValue);
}
}
foreach (var item in dictionaryCodeList)
{
PropertyInfo? pi = i18N.GetType().GetProperty(item);
if (pi != null && pi.CanWrite)
{
var value = dictionadParents.Where(x => x.ParentCode == item).ToDictionary(x => x.Code, x => x.Value);
object safeValue = Convert.ChangeType(value, pi.PropertyType);
pi.SetValue(i18N, safeValue);
}
}
foreach (var item in list)
{
foreach (var dialog in item.DialogList)
{
dialog.ResultContent = JointMedicalReviewDialog(dialog, i18N);
}
}
#endregion
var exportInfo = (await _trialRepository.Where(t => t.Id == inQuery.TrialId).IgnoreQueryFilters().ProjectTo<ExcelExportInfo>(_mapper.ConfigurationProvider).FirstOrDefaultAsync()).IfNullThrowException(); 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.List = ExportExcelConverterDate.ConvertToClientTimeInObject(list, _userInfo.TimeZoneId);
@@ -1848,88 +1766,6 @@ namespace IRaCIS.Core.Application.Service.Common
return await ExcelExportHelper.DataExportAsync(StaticData.Export.TrialMedicalReviewList_Export, exportInfo, $"{exportInfo.ResearchProgramNo}", _commonDocumentRepository, _hostEnvironment, _dictionaryService, typeof(TaskMedicalReviewExportDto)); return await ExcelExportHelper.DataExportAsync(StaticData.Export.TrialMedicalReviewList_Export, exportInfo, $"{exportInfo.ResearchProgramNo}", _commonDocumentRepository, _hostEnvironment, _dictionaryService, typeof(TaskMedicalReviewExportDto));
} }
/// <summary>
/// 获取医学审核对话拼接内容
/// </summary>
/// <param name="record"></param>
/// <param name="i18NAndDic"></param>
/// <returns></returns>
private string JointMedicalReviewDialog(GetMedicalReviewDialogOutDto record, JointMedicalReviewI18n i18NAndDic)
{
StringBuilder str = new StringBuilder() { };
var userTypes = new List<int>() { 14, 30 };
// 这里userTypes.Contains(record.UserTypeEnumInt) 可以提出来 但是还是按照前端写吧 免得不好对照
// v-if="[14, 30].includes(record.UserTypeEnumInt) && record.Questioning
if (userTypes.Contains(record.UserTypeEnumInt) && record.Questioning.IsNotNullOrEmpty())
{
// <!-- 您好,根据医学审核反馈,该阅片任务的评估有如下问题需要您确认或澄清: -->
str.AppendLine(i18NAndDic.Msg1);
str.AppendLine(record.Questioning);
if (record.FileList.Count() > 0)
{
str.AppendLine(i18NAndDic.Msg2);
record.FileList.ForEach(x =>
{
str.AppendLine(x.FileName);
});
}
str.AppendLine(i18NAndDic.Msg3);
str.AppendLine(i18NAndDic.AuditAdvice[record.AuditAdviceEnum.GetEnumInt()]);
str.AppendLine(i18NAndDic.Msg4);
}
//v-if="[14, 30].includes(record.UserTypeEnumInt) && record.MedicalDialogCloseEnum!== null"
if (userTypes.Contains(record.UserTypeEnumInt) && record.MedicalDialogCloseEnum != null)
{
str.AppendLine(i18NAndDic.CloseReasonEnum + i18NAndDic.MedicalDialogCloseEnum[record.MedicalDialogCloseEnum.Value.GetEnumInt()]);
}
// v-if="[14, 30].includes(record.UserTypeEnumInt) && record.Content
if (userTypes.Contains(record.UserTypeEnumInt) && record.Content.IsNotNullOrEmpty())
{
str.AppendLine(record.Content);
}
// v-if="[13].includes(record.UserTypeEnumInt)"
if (13 == record.UserTypeEnumInt)
{
str.AppendLine(i18NAndDic.IsEndorse + i18NAndDic.MedicalReviewDoctorUserIdea[record.DoctorUserIdeaEnum.GetEnumInt()]);
// v-if="record.DoctorUserIdeaEnum===2"
if ((int)record.DoctorUserIdeaEnum == 2)
{
str.AppendLine(i18NAndDic.Reason + record.DisagreeReason);
}
// record.MedicalDialogCloseEnum!== null
if (record.MedicalDialogCloseEnum != null)
{
str.AppendLine(i18NAndDic.IsRequestReread + i18NAndDic.YesOrNo[record.IsApplyHeavyReading.ToString()]);
}
// v-if="record.FileList && record.FileList.length > 0"
if (record.FileList.Count() > 0)
{
record.FileList.ForEach(x =>
{
str.AppendLine(x.FileName);
});
}
}
return str.ToString();
}
/// <summary> /// <summary>
/// 影像下载记录表 /// 影像下载记录表
@@ -2474,263 +2310,8 @@ namespace IRaCIS.Core.Application.Service.Common
#region #region
/// <summary>
/// 一致性分析结果导出 7 8 分别是自身 和组件一致性
/// </summary>
/// <param name="inQuery"></param>
/// <param name="_commonDocumentRepository"></param>
/// <param name="_dictionaryService"></param>
/// <param name="_trialRepository"></param>
/// <returns></returns>
[HttpPost]
public async Task<IActionResult> GetAnalysisTaskList_Export(VisitTaskQuery inQuery,
[FromServices] IRepository<CommonDocument> _commonDocumentRepository,
[FromServices] IDictionaryService _dictionaryService,
[FromServices] IRepository<Trial> _trialRepository)
{
//每次查询必须是单标准的
var criterion = await _readingQuestionCriterionTrialRepository.Where(t => t.Id == inQuery.TrialReadingCriterionId).Select(t => new { t.CriterionType, t.CriterionName, t.ArbitrationRule }).FirstNotNullAsync();
var query = _visitTaskRepository.Where(t => t.TrialId == inQuery.TrialId && t.TaskAllocationState == TaskAllocationState.Allocated && (t.TaskState == TaskState.Effect || t.TaskState == TaskState.Freeze))
//一致性分析
.WhereIf(inQuery.ReadingExportType == ExportResult.DetailedTableOfIntraReaderAnalysisResults, t => t.IsSelfAnalysis == true || t.IsSelfAnalysis == null)
.WhereIf(inQuery.ReadingExportType == ExportResult.DetailedTableOfIntraReaderAnalysisResults, t => t.IsSelfAnalysis == null ? t.Subject.SubjectVisitTaskList.Any(u => u.IsSelfAnalysis == true && u.VisitTaskNum == t.VisitTaskNum && u.DoctorUserId == t.DoctorUserId && u.TrialReadingCriterionId == t.TrialReadingCriterionId) : true)
.WhereIf(inQuery.ReadingExportType == ExportResult.DetailedTableOfInterReaderAnalysisResults, t => t.IsSelfAnalysis == false || t.IsSelfAnalysis == null)
//访视和全局查询已签名完成的,裁判可以是未签名,未完成的
.Where(t => (t.ReadingTaskState == ReadingTaskState.HaveSigned && (t.ReadingCategory == ReadingCategory.Visit || t.ReadingCategory == ReadingCategory.Global)) || t.ReadingCategory == ReadingCategory.Judge)
.WhereIf(inQuery.TrialReadingCriterionId != null, t => t.TrialReadingCriterionId == inQuery.TrialReadingCriterionId)
.WhereIf(inQuery.TrialSiteId != null, t => t.Subject.TrialSiteId == inQuery.TrialSiteId)
.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.ArmEnum != null, t => t.ArmEnum == inQuery.ArmEnum)
.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))
.WhereIf(inQuery.BeginAllocateDate != null, t => t.AllocateTime > inQuery.BeginAllocateDate)
.WhereIf(inQuery.EndAllocateDate != null, t => t.AllocateTime < inQuery.EndAllocateDate);
var list = await query.ProjectTo<AnalysisDynamicCommonExport>(_mapper.ConfigurationProvider,
new
{
readingExportType = inQuery.ReadingExportType,
criterionType = criterion.CriterionType,
trialReadingCriterionId = inQuery.TrialReadingCriterionId,
isEn_Us = _userInfo.IsEn_Us
}).ToListAsync();
list = list.OrderBy(t => t.SubjectCode).ThenBy(t => t.ArmEnum).ThenBy(t => t.VisitTaskNum).ToList();
var exportInfo = (await _trialRepository.Where(t => t.Id == inQuery.TrialId).IgnoreQueryFilters().ProjectTo<ExcelExportInfo>(_mapper.ConfigurationProvider).FirstOrDefaultAsync()).IfNullThrowException();
exportInfo.CriterionName = criterion.CriterionName;
#region
//如果是以合并后的找翻译字典,会少,所以必须放在前面
var translateDicNameList = list.SelectMany(t => t.QuestionAnswerList).Where(t => t.TranslateDicName.IsNotNullOrEmpty()).Select(t => t.TranslateDicName).Distinct().ToList();
//针对1.1 整体肿瘤评估 有的两列要合并一列
if (criterion.CriterionType == CriterionType.RECIST1Point1 || criterion.CriterionType == CriterionType.RECIST1Pointt1_MB || criterion.CriterionType == CriterionType.IRECIST1Point1)
{
foreach (var item in list)
{
//处理合并表头
var questionType = item.IsBaseline == true ? QuestionType.ExistDisease : QuestionType.Tumor;
var findItem = item.QuestionAnswerList.Where(t => t.QuestionType == questionType).FirstOrDefault();
if (findItem != null)
{
findItem.QuestionName = _userInfo.IsEn_Us ? "Overall Response" : "整体肿瘤评估";
}
if (item.IsBaseline == true)
{
item.QuestionAnswerList = item.QuestionAnswerList.Where(t => t.QuestionType != QuestionType.Tumor).ToList();
}
else
{
item.QuestionAnswerList = item.QuestionAnswerList.Where(t => t.QuestionType != QuestionType.ExistDisease).ToList();
}
}
}
else if (criterion.CriterionType == CriterionType.Lugano2014 || criterion.CriterionType == CriterionType.Lugano2014WithoutPET)
{
foreach (var item in list)
{
//处理合并表头
var questionType = item.IsBaseline == true ? QuestionType.ExistDisease : QuestionType.ImgOncology;
var findItem = item.QuestionAnswerList.Where(t => t.QuestionType == questionType).FirstOrDefault();
if (findItem != null)
{
findItem.QuestionName = _userInfo.IsEn_Us ? "Overall Response" : "整体肿瘤评估";
}
if (item.IsBaseline == true)
{
item.QuestionAnswerList = item.QuestionAnswerList.Where(t => t.QuestionType != QuestionType.ImgOncology).ToList();
}
else
{
item.QuestionAnswerList = item.QuestionAnswerList.Where(t => t.QuestionType != QuestionType.ExistDisease).ToList();
}
}
}
else if (criterion.CriterionType == CriterionType.PCWG3)
{
}
else if (criterion.CriterionType == CriterionType.SelfDefine)
{
//自定义的又问题名称重复 所以统一加上组名
//有重复的就加,没有重复的就不加
if (list.Any(t => t.QuestionAnswerList.Select(t => t.QuestionName).Count() != t.QuestionAnswerList.Select(t => t.QuestionName).Distinct().Count()))
{
foreach (var item in list)
{
foreach (var qs in item.QuestionAnswerList)
{
qs.QuestionName = qs.Group + "_" + qs.QuestionName;
}
}
}
}
#endregion
var export_Template = StaticData.Export.TrialSelfAnalysisList_Export;
#region
if (inQuery.ReadingExportType == ExportResult.DetailedTableOfIntraReaderAnalysisResults)
{
//找到非一致性分析的任务
var selfExportList = list.Where(t => t.IsSelfAnalysis == null).ToList();
//处理一致性分析结果是否和原始阅片是否一致
foreach (var item in selfExportList)
{
//找到一致性分析的结果
var selfAnalysisTask = list.Where(t => t.IsSelfAnalysis == true && t.SubjectCode == item.SubjectCode && t.VisitTaskNum == item.VisitTaskNum && t.TaskName == t.TaskName && t.UserName == item.UserName).FirstOrDefault();
//将自身一致性分析的字段 赋值到访视任务这个字段
item.IsAnalysisDiffToOriginalData = selfAnalysisTask?.IsAnalysisDiffToOriginalData;
//处理再次阅片人的结果
if (selfAnalysisTask != null)
{
var cloneQuestionAnswerList = selfAnalysisTask.QuestionAnswerList.Clone();
foreach (var qItem in cloneQuestionAnswerList)
{
qItem.QuestionName = qItem.QuestionName + $"{(_userInfo.IsEn_Us ? "(Again)" : "()")}";
}
item.QuestionAnswerList = item.QuestionAnswerList.Union(cloneQuestionAnswerList).ToList();
}
}
list = selfExportList;
}
else
{
export_Template = StaticData.Export.TrialGroupAnalysisList_Export;
var newList = new List<AnalysisDynamicCommonExport>();
foreach (var group in list.GroupBy(t => new { t.SubjectCode, t.VisitTaskNum, t.TaskName }).OrderBy(g => g.Key.SubjectCode).ThenBy(g => g.Key.VisitTaskNum))
{
var subjectVisitGroupList = group.ToList();
//找到当前访视组间一致性分析的任务结果
var groupOtherTaskList = subjectVisitGroupList.Where(t => t.IsSelfAnalysis == false).ToList();
foreach (var subjectVisitTaskArm in subjectVisitGroupList.Where(t => t.IsSelfAnalysis == null).OrderBy(t => t.ArmEnum))
{
foreach (var otherTask in groupOtherTaskList)
{
//非一致性分析任务
var cloneObj = subjectVisitTaskArm.Clone();
var otherTaskQuestionAnserList = otherTask.QuestionAnswerList.Clone();
foreach (var qItem in otherTaskQuestionAnserList)
{
qItem.QuestionName = qItem.QuestionName + $"{(_userInfo.IsEn_Us ? "(Again)" : "()")}";
}
//处理 再次阅片人,再次阅片人角色 两列
var addQuestionList = new List<CommonQuesionInfo>();
addQuestionList.Add(new CommonQuesionInfo() { QuestionName = _userInfo.IsEn_Us ? "Reviwer(Again)" : "阅片人(再次)", QuestionValue = otherTask.UserName });
addQuestionList.Add(new CommonQuesionInfo() { QuestionName = _userInfo.IsEn_Us ? "Reviwer Role(Again)" : "阅片人角色(再次)", QuestionValue = ((int)otherTask.ArmEnum).ToString(), TranslateDicName = "ArmEnum" });
cloneObj.QuestionAnswerList = cloneObj.QuestionAnswerList.Union(addQuestionList).Union(otherTaskQuestionAnserList).ToList();
cloneObj.IsGroupAnalysisDiffToOriginalData = cloneObj.ArmEnum == Arm.DoubleReadingArm1 ? otherTask.IsGroupDiffArm1 : otherTask.IsGroupDiffArm2;
newList.Add(cloneObj);
}
}
}
translateDicNameList.Add("ArmEnum");
list = newList;
}
#endregion
var columNameList = list.SelectMany(t => t.QuestionAnswerList).Where(t => t.QuestionName.IsNotNullOrEmpty()).Select(t => t.QuestionName).Distinct().ToList();
exportInfo.List = ExportExcelConverterDate.ConvertToClientTimeInObject(list.Where(t => t.ReadingCategory != ReadingCategory.Global).ToList(), _userInfo.TimeZoneId);
exportInfo.CurrentTime = ExportExcelConverterDate.DateTimeInternationalToString(DateTime.Now, _userInfo.TimeZoneId);
var dynamicColumnConfig = new DynamicColumnConfig()
{
//可读的列表名行索引,不是{{}} 模板行索引
AutoColumnTitleRowIndex = 2,
AutoColumnStartIndex = 5,
TempalteLastColumnIndex = 4,
DynamicItemDicName = "TranslateDicName",
DynamicItemValueName = "QuestionValue",
DynamicItemTitleName = "QuestionName",
DynamicListName = "QuestionAnswerList",
RemoveColunmIndexList = new List<int>() { },
ColumnIdNameList = columNameList.Select(t => new DynamicColumnConfig.ColumItem() { Id = Guid.Empty, Name = t }).ToList(),
TranslateDicNameList = translateDicNameList ?? new List<string>()
};
var (memoryStream, fileName) = await ExcelExportHelper.DataExport_NpoiTestAsync(export_Template, exportInfo, _commonDocumentRepository, _hostEnvironment, _dictionaryService, typeof(AnalysisDynamicCommonExport), criterion.CriterionType, dynamicColumnConfig);
return new FileStreamResult(memoryStream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
{
FileDownloadName = $"{exportInfo.ResearchProgramNo}_{exportInfo.CriterionName}_{fileName}_{DateTime.Now.ToString("yyyyMMddHHmmss")}.xlsx"
};
}
/// <summary> /// <summary>
/// 获取阅片标准可以导出的列表 /// 获取阅片标准可以导出的列表
@@ -2760,11 +2341,6 @@ namespace IRaCIS.Core.Application.Service.Common
list.Add(new ExportDocumentDes() { Code = StaticData.Export.ReadingLession_Export, ExportCatogory = ExportResult.DetailedTableOfLesions }); list.Add(new ExportDocumentDes() { Code = StaticData.Export.ReadingLession_Export, ExportCatogory = ExportResult.DetailedTableOfLesions });
} }
if (criterion.CriterionType == CriterionType.RECIST1Point1 || criterion.CriterionType == CriterionType.Lugano2014)
{
list.Add(new ExportDocumentDes() { Code = StaticData.Export.TumorCDISC_Export, ExportCatogory = ExportResult.TumorCDISC_Export });
}
if (criterion.CriterionType == CriterionType.OCT) if (criterion.CriterionType == CriterionType.OCT)
{ {
list.Add(new ExportDocumentDes() { Code = StaticData.Export.OCT_ReadingLession_Export, ExportCatogory = ExportResult.OCT_ReadingLession_Export }); list.Add(new ExportDocumentDes() { Code = StaticData.Export.OCT_ReadingLession_Export, ExportCatogory = ExportResult.OCT_ReadingLession_Export });
@@ -2774,9 +2350,9 @@ namespace IRaCIS.Core.Application.Service.Common
{ {
list.Add(new ExportDocumentDes() { Code = StaticData.Export.IVUS_CDISC_Export, ExportCatogory = ExportResult.IVUS_CDISC_Export }); list.Add(new ExportDocumentDes() { Code = StaticData.Export.IVUS_CDISC_Export, ExportCatogory = ExportResult.IVUS_CDISC_Export });
} }
if (criterion.CriterionGroup != CriterionGroup.Tumor /*&& criterion.CriterionType != CriterionType.OCT && criterion.CriterionType != CriterionType.IVUS*/) //else if (criterion.CriterionType == CriterionType.SelfDefine)
{ {
list.Add(new ExportDocumentDes() { Code = StaticData.Export.CDISC_Reading_Export, ExportCatogory = ExportResult.NoneTumorCDISC }); list.Add(new ExportDocumentDes() { Code = StaticData.Export.CDISC_Reading_Export, ExportCatogory = ExportResult.CDISC });
} }
switch (criterion.ArbitrationRule) switch (criterion.ArbitrationRule)
@@ -2903,7 +2479,7 @@ namespace IRaCIS.Core.Application.Service.Common
//阅片结果表 //阅片结果表
export_Template = StaticData.Export.CommonReading_Export; export_Template = StaticData.Export.CommonReading_Export;
} }
//斑块表
else if (inQuery.ReadingExportType == ExportResult.OCT_ReadingLession_Export) else if (inQuery.ReadingExportType == ExportResult.OCT_ReadingLession_Export)
{ {
//OCT //OCT
@@ -2957,7 +2533,7 @@ namespace IRaCIS.Core.Application.Service.Common
} }
// 非肿瘤标准 包括自定义,以及系统非肿瘤 会配置到表格层级 问题名称是 表格名称_表格子问题名 // 非肿瘤标准 包括自定义,以及系统非肿瘤 会配置到表格层级 问题名称是 表格名称_表格子问题名
else if ( else if (
criterion.CriterionGroup == CriterionGroup.Nontumorous && inQuery.ReadingExportType != ExportResult.NoneTumorCDISC criterion.CriterionGroup == CriterionGroup.Nontumorous && inQuery.ReadingExportType != ExportResult.CDISC
) )
{ {
@@ -2974,7 +2550,7 @@ namespace IRaCIS.Core.Application.Service.Common
} }
// CDISC 导出 只管到 外层问题层级 和阅片结果表是保持一致 // CDISC 导出 只管到 外层问题层级 和阅片结果表是保持一致
else if (inQuery.ReadingExportType == ExportResult.NoneTumorCDISC) else if (inQuery.ReadingExportType == ExportResult.CDISC)
{ {
if (criterion.CriterionGroup == CriterionGroup.Tumor) if (criterion.CriterionGroup == CriterionGroup.Tumor)
@@ -3042,7 +2618,7 @@ namespace IRaCIS.Core.Application.Service.Common
#endregion #endregion
if (inQuery.ReadingExportType != ExportResult.NoneTumorCDISC) if (inQuery.ReadingExportType != ExportResult.CDISC)
{ {
//最终EXCEL 列 //最终EXCEL 列
var configCoumNameList = new List<DynamicColumnConfig.ColumItem>(); var configCoumNameList = new List<DynamicColumnConfig.ColumItem>();
@@ -3170,7 +2746,7 @@ namespace IRaCIS.Core.Application.Service.Common
addLessionInfoList.Add(new CommonQuesionInfo() { QuestionName = _userInfo.IsEn_Us ? "Lesion Type" : "病灶类型", QuestionValue = lession.LessionType, TranslateDicName = "LesionType" }); addLessionInfoList.Add(new CommonQuesionInfo() { QuestionName = _userInfo.IsEn_Us ? "Lesion Type" : "病灶类型", QuestionValue = lession.LessionType, TranslateDicName = "LesionType" });
} }
var dynamicPartialLessionInfoList = lession.LessionAnswerList.Select(t => new CommonQuesionInfo() { QuestionName = t.QuestionName, OptionTypeEnum = t.OptionTypeEnum, QuestionValue = t.QuestionValue, TranslateDicName = t.TranslateDicName ,Unit=t.Unit}); var dynamicPartialLessionInfoList = lession.LessionAnswerList.Select(t => new CommonQuesionInfo() { QuestionName = t.QuestionName, OptionTypeEnum = t.OptionTypeEnum, QuestionValue = t.QuestionValue, TranslateDicName = t.TranslateDicName });
//有三部分组成 外层问题+ 没有配置病灶编号和类型+ 动态的表格问题 //有三部分组成 外层问题+ 没有配置病灶编号和类型+ 动态的表格问题
var dynamicLessionInfoList = item.QuestionAnswerList.Union(addLessionInfoList).Union(dynamicPartialLessionInfoList).ToList(); var dynamicLessionInfoList = item.QuestionAnswerList.Union(addLessionInfoList).Union(dynamicPartialLessionInfoList).ToList();
@@ -3318,14 +2894,14 @@ namespace IRaCIS.Core.Application.Service.Common
addLessionInfoList.Add(new CommonQuesionInfo() { QuestionId = Guid.Empty, QuestionName = _userInfo.IsEn_Us ? "Table Name" : "表格名称", QuestionValue = firstLessionAnser.TableName }); 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, OptionTypeEnum = t.OptionTypeEnum, QuestionName = t.TableName + "_" + t.QuestionName, QuestionValue = t.QuestionValue, TranslateDicName = t.TranslateDicName, CDISCCode = t.CDISCCode, Unit = t.Unit }); var dynamicPartialLessionInfoList = lession.LessionAnswerList.Select(t => new CommonQuesionInfo() { QuestionId = t.TableQuesionId, OptionTypeEnum = t.OptionTypeEnum, QuestionName = t.TableName + "_" + t.QuestionName, QuestionValue = t.QuestionValue, TranslateDicName = t.TranslateDicName, CDISCCode = t.CDISCCode });
//有三部分组成 外层问题+ 固定列表格名称 + 动态的表格问题 //有三部分组成 外层问题+ 固定列表格名称 + 动态的表格问题
dynamicLessionInfoList = item.QuestionAnswerList.Union(addLessionInfoList).Union(dynamicPartialLessionInfoList).ToList(); dynamicLessionInfoList = item.QuestionAnswerList.Union(addLessionInfoList).Union(dynamicPartialLessionInfoList).ToList();
} }
else else
{ {
var dynamicPartialLessionInfoList = lession.LessionAnswerList.Select(t => new CommonQuesionInfo() { QuestionId = t.TableQuesionId, OptionTypeEnum = t.OptionTypeEnum, QuestionName = t.QuestionName, QuestionValue = t.QuestionValue, TranslateDicName = t.TranslateDicName, CDISCCode = t.CDISCCode, Unit = t.Unit }); var dynamicPartialLessionInfoList = lession.LessionAnswerList.Select(t => new CommonQuesionInfo() { QuestionId = t.TableQuesionId, OptionTypeEnum = t.OptionTypeEnum, QuestionName = t.QuestionName, QuestionValue = t.QuestionValue, TranslateDicName = t.TranslateDicName, CDISCCode = t.CDISCCode });
//两部分组成 外层问题+ 动态的表格问题 //两部分组成 外层问题+ 动态的表格问题
dynamicLessionInfoList = item.QuestionAnswerList.Union(dynamicPartialLessionInfoList).ToList(); dynamicLessionInfoList = item.QuestionAnswerList.Union(dynamicPartialLessionInfoList).ToList();
@@ -3372,7 +2948,7 @@ namespace IRaCIS.Core.Application.Service.Common
TranslateDicNameList = translateDicNameList TranslateDicNameList = translateDicNameList
}; };
if (export_Template == StaticData.Export.ReadingLession_Export || export_Template == StaticData.Export.CommonJudgeReadingDetail_Export || export_Template== StaticData.Export.OCT_ReadingLession_Export) if (export_Template == StaticData.Export.ReadingLession_Export || export_Template == StaticData.Export.CommonJudgeReadingDetail_Export)
{ {
dynamicColumnConfig.TempalteLastColumnIndex = 8; dynamicColumnConfig.TempalteLastColumnIndex = 8;
} }
@@ -3435,7 +3011,7 @@ namespace IRaCIS.Core.Application.Service.Common
var dynamicLessionInfoList = new List<CommonQuesionInfo>(); var dynamicLessionInfoList = new List<CommonQuesionInfo>();
var dynamicPartialLessionInfoList = lession.LessionAnswerList.Select(t => new CommonQuesionInfo() { QuestionId = t.TableQuesionId,OptionTypeEnum=t.OptionTypeEnum, QuestionName = t.QuestionName, QuestionValue = t.QuestionValue, TranslateDicName = t.TranslateDicName, CDISCCode = t.CDISCCode, Unit = t.Unit }); var dynamicPartialLessionInfoList = lession.LessionAnswerList.Select(t => new CommonQuesionInfo() { QuestionId = t.TableQuesionId,OptionTypeEnum=t.OptionTypeEnum, QuestionName = t.QuestionName, QuestionValue = t.QuestionValue, TranslateDicName = t.TranslateDicName, CDISCCode = t.CDISCCode });
//两部分组成 外层问题+ 动态的表格问题 //两部分组成 外层问题+ 动态的表格问题
dynamicLessionInfoList = item.QuestionAnswerList.Union(dynamicPartialLessionInfoList).ToList(); dynamicLessionInfoList = item.QuestionAnswerList.Union(dynamicPartialLessionInfoList).ToList();
@@ -3648,7 +3224,7 @@ namespace IRaCIS.Core.Application.Service.Common
string fileName = ""; string fileName = "";
if (inQuery.ReadingExportType == ExportResult.NoneTumorCDISC) if (inQuery.ReadingExportType == ExportResult.CDISC)
{ {
(memoryStream, fileName) = await ExcelExportHelper.DataExport_NpoiTestAsync(export_Template, exportInfo, _commonDocumentRepository, _hostEnvironment, _dictionaryService, typeof(CommonEvaluationExport), criterion.CriterionType, dynamicColumnConfig); (memoryStream, fileName) = await ExcelExportHelper.DataExport_NpoiTestAsync(export_Template, exportInfo, _commonDocumentRepository, _hostEnvironment, _dictionaryService, typeof(CommonEvaluationExport), criterion.CriterionType, dynamicColumnConfig);
@@ -125,23 +125,7 @@ public class TumorExportBaseModel : TU_TR_RSBaseModel
public string VisitNote { get; set; } public string VisitNote { get; set; }
#endregion #endregion
#region
public List<OncologyExportInfo> OncologyResultList { get; set; }
//public string OncologyUserName { get; set; }
#endregion
}
public class OncologyExportInfo
{
public decimal? VisitTaskNum { get; set; }
public string VisitName { get; set; }
public string OncologyResult { get; set; } = string.Empty;
public string OncologyReason { get; set; } = string.Empty;
} }
@@ -410,33 +410,7 @@ public class IVUS_OCTExportService(IRepository<ReadingQuestionCriterionTrial> _r
//靶段 //靶段
var target = task.QuestionAnswerList.Where(t => t.QuestionType == QuestionType.TargetSegment).FirstOrDefault(); var target = task.QuestionAnswerList.Where(t => t.QuestionType == QuestionType.TargetSegment).FirstOrDefault();
var itemValue = target?.QuestionValue??string.Empty; ivus.TARGETV = translateDataList[target?.TranslateDicName].Where(t => t.Code.ToLower() == target?.QuestionValue?.ToLower()).Select(t => isEn_Us ? t.Value : t.ValueCN).FirstOrDefault() ?? String.Empty;
//多选
int[] enumValues = new int[0];
// 1. 反序列化 JSON 数组 (字符串枚举)
if (!itemValue.StartsWith("[") || !itemValue.EndsWith("]"))
{
enumValues = new int[1] { int.Parse(itemValue) };
}
else
{
enumValues = JsonConvert.DeserializeObject<int[]>(itemValue) ?? new int[0];
}
// 2. 翻译每一项并输出逗号拼接字符串
var translatedItemData = string.Join(",",
enumValues.OrderBy(t=>t).Select(code =>
translateDataList[target?.TranslateDicName]
.FirstOrDefault(t =>
string.Equals(code.ToString(), t.Code, StringComparison.OrdinalIgnoreCase)
) is var r && r != null
? (isEn_Us ? r.Value : r.ValueCN)
: string.Empty
));
ivus.TARGETV = translatedItemData;
var marks = task.QuestionAnswerList.Where(t => t.QuestionType == QuestionType.TargetSegmentRemarks).FirstOrDefault()?.QuestionValue ?? string.Empty; var marks = task.QuestionAnswerList.Where(t => t.QuestionType == QuestionType.TargetSegmentRemarks).FirstOrDefault()?.QuestionValue ?? string.Empty;
@@ -477,11 +451,6 @@ public class IVUS_OCTExportService(IRepository<ReadingQuestionCriterionTrial> _r
ivus.PAV = findPAVLession?.LessionAnswerList.Where(t => t.QuestionMark == QuestionMark.PAV).FirstOrDefault()?.QuestionValue ?? string.Empty; ivus.PAV = findPAVLession?.LessionAnswerList.Where(t => t.QuestionMark == QuestionMark.PAV).FirstOrDefault()?.QuestionValue ?? string.Empty;
if (ivus.PAV.IsNotNullOrEmpty())
{
ivus.PAV = ivus.PAV + "%";
}
ivus.DOST = findPAVLession?.LessionAnswerList.Where(t => t.QuestionMark == QuestionMark.PlaqueToOstiumDistance).FirstOrDefault()?.QuestionValue ?? string.Empty; ivus.DOST = findPAVLession?.LessionAnswerList.Where(t => t.QuestionMark == QuestionMark.PlaqueToOstiumDistance).FirstOrDefault()?.QuestionValue ?? string.Empty;
@@ -774,33 +743,8 @@ public class IVUS_OCTExportService(IRepository<ReadingQuestionCriterionTrial> _r
var target = task.QuestionAnswerList.Where(t => t.QuestionType == QuestionType.TargetSegment).FirstOrDefault(); var target = task.QuestionAnswerList.Where(t => t.QuestionType == QuestionType.TargetSegment).FirstOrDefault();
var itemValue = target?.QuestionValue ?? string.Empty; oct.TARGETV = translateDataList[target?.TranslateDicName].Where(t => t.Code.ToLower() == target?.QuestionValue?.ToLower()).Select(t => isEn_US ? t.Value : t.ValueCN).FirstOrDefault() ?? String.Empty;
//多选
int[] enumValueArray = new int[0];
// 1. 反序列化 JSON 数组 (字符串枚举)
if (!itemValue.StartsWith("[") || !itemValue.EndsWith("]"))
{
enumValueArray = new int[1] { int.Parse(itemValue) };
}
else
{
enumValueArray = JsonConvert.DeserializeObject<int[]>(itemValue) ?? new int[0];
}
// 2. 翻译每一项并输出逗号拼接字符串
var translatedItemData = string.Join(",",
enumValueArray.OrderBy(t => t).Select(code =>
translateDataList[target?.TranslateDicName]
.FirstOrDefault(t =>
string.Equals(code.ToString(), t.Code, StringComparison.OrdinalIgnoreCase)
) is var r && r != null
? (isEn_US ? r.Value : r.ValueCN)
: string.Empty
));
oct.TARGETV = translatedItemData;
var marks = task.QuestionAnswerList.Where(t => t.QuestionType == QuestionType.TargetSegmentRemarks).FirstOrDefault()?.QuestionValue ?? string.Empty; var marks = task.QuestionAnswerList.Where(t => t.QuestionType == QuestionType.TargetSegmentRemarks).FirstOrDefault()?.QuestionValue ?? string.Empty;
File diff suppressed because it is too large Load Diff
@@ -1,589 +0,0 @@
//--------------------------------------------------------------------
// 此代码由liquid模板自动生成 byzhouhang 20240909
// 生成时间 2026-03-10 06:15:17Z
// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。
//--------------------------------------------------------------------
using DocumentFormat.OpenXml.Office2010.ExcelAc;
using IRaCIS.Core.Application.Helper;
using IRaCIS.Core.Application.Interfaces;
using IRaCIS.Core.Application.ViewModel;
using IRaCIS.Core.Domain.Models;
using IRaCIS.Core.Infra.EFCore;
using IRaCIS.Core.Infra.EFCore.Common;
using IRaCIS.Core.Infrastructure.Extention;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using Spire.Doc.Interface;
using System.Drawing;
using System.Threading.Channels;
using System.Threading.Tasks;
namespace IRaCIS.Core.Application.Service;
[ApiExplorerSettings(GroupName = "Common")]
public class FileUploadRecordService(IRepository<FileUploadRecord> _fileUploadRecordRepository, IRepository<UploadFileSyncRecord> _uploadFileSyncRecordRepository,
IMapper _mapper, IUserInfo _userInfo, IStringLocalizer _localizer, IOptionsMonitor<ObjectStoreServiceOptions> options,
IFusionCache _fusionCache, IRepository<Trial> _trialRepository, FileSyncQueue _fileSyncQueue) : BaseService, IFileUploadRecordService
{
ObjectStoreServiceOptions ObjectStoreServiceConfig = options.CurrentValue;
/// <summary>
/// 按照 subject visit studyCode 三个维度进行分组的查询列表 (subject相关)
/// </summary>
/// <param name="inQuery"></param>
/// <returns></returns>
[HttpPost]
public async Task<PageOutput<SubjectFileUploadRecordView>> GetSubjectUploadRecordList(SubjectFileUploadRecordQuery inQuery)
{
var query = _fileUploadRecordRepository.Where(t => t.TrialId == inQuery.TrialId && t.SubjectId != null)
.WhereIf(!string.IsNullOrEmpty(inQuery.VisitName), t => t.SubjectVisit.VisitName.Contains(inQuery.VisitName))
.WhereIf(!string.IsNullOrEmpty(inQuery.SubjectCode), t => t.Subject.Code.Contains(inQuery.SubjectCode))
.WhereIf(!string.IsNullOrEmpty(inQuery.StudyCode), t => t.StudyCode.Contains(inQuery.StudyCode))
.WhereIf(!string.IsNullOrEmpty(inQuery.UploadRegion), t => t.UploadRegion == inQuery.UploadRegion)
.WhereIf(!string.IsNullOrEmpty(inQuery.TargetRegion), t => t.TargetRegion == inQuery.TargetRegion)
.WhereIf(inQuery.IsSync != null, t => t.IsSync == inQuery.IsSync)
.GroupBy(t => new { t.StudyCode, SubjectCode = t.Subject.Code, t.SubjectVisit.VisitName, t.SubjectId, t.SubjectVisitId })
.Select(g => new SubjectFileUploadRecordView()
{
SubjectCode = g.Key.SubjectCode,
VisitName = g.Key.VisitName,
StudyCode = g.Key.StudyCode,
SubjectId = g.Key.SubjectId,
SubjectVisitId = g.Key.SubjectVisitId,
FileCount = g.Count(),
CreateTime = g.Max(t => t.CreateTime),
SyncFinishedTime = g.Max(t => t.SyncFinishedTime),
UploadRegion = g.First().UploadRegion,
TargetRegion = g.First().TargetRegion,
IsSync = !g.Any(t => t.IsSync == false || t.IsSync == null)
});
var pageList = await query.ToPagedListAsync(inQuery);
return pageList;
}
/// <summary>
/// 上传记录表--里面包含待同步任务 DataFileType= 0 :代表系统文件 1Subject相关 2:项目相关,但是和subject 没关系
/// </summary>
/// <param name="inQuery"></param>
/// <returns></returns>
[HttpPost]
public async Task<PageOutput<FileUploadRecordView>> GetFileUploadRecordList(FileUploadRecordQuery inQuery)
{
var fileUploadRecordQueryable = _fileUploadRecordRepository
.WhereIf(!string.IsNullOrEmpty(inQuery.FileName), t => t.FileName.Contains(inQuery.FileName))
.WhereIf(!string.IsNullOrEmpty(inQuery.FileType), t => t.FileType.Contains(inQuery.FileType))
.WhereIf(inQuery.TrialId != null, t => t.TrialId == inQuery.TrialId)
.WhereIf(inQuery.SubjectId != null, t => t.SubjectId == inQuery.SubjectId)
.WhereIf(inQuery.SubjectVisitId != null, t => t.SubjectVisitId == inQuery.SubjectVisitId)
.WhereIf(!string.IsNullOrEmpty(inQuery.StudyCode), t => t.StudyCode.Contains(inQuery.StudyCode))
.WhereIf(!string.IsNullOrEmpty(inQuery.VisitName), t => t.SubjectVisit.VisitName.Contains(inQuery.VisitName))
.WhereIf(!string.IsNullOrEmpty(inQuery.SubjectCode), t => t.Subject.Code.Contains(inQuery.SubjectCode))
.WhereIf(inQuery.DataFileType == 1 && inQuery.SubjectId != null && inQuery.SubjectVisitId == null, t => t.SubjectVisitId == null)
.WhereIf(inQuery.DataFileType == 1 && inQuery.SubjectVisitId != null && inQuery.StudyCode == "", t => t.StudyCode == "")
.WhereIf(inQuery.DataFileType == 1 && inQuery.SubjectCode.IsNotNullOrEmpty() && inQuery.VisitName.IsNullOrEmpty(), t => t.SubjectVisitId == null)
.WhereIf(inQuery.DataFileType == 1 && inQuery.VisitName.IsNotNullOrEmpty() && inQuery.StudyCode == "", t => t.StudyCode == "")
.WhereIf(inQuery.DataFileType == 0, t => t.TrialId == null)
.WhereIf(inQuery.DataFileType == 1, t => t.SubjectId != null)
.WhereIf(inQuery.DataFileType == 2, t => t.SubjectId == null)
.WhereIf(inQuery.IsNeedSync != null, t => t.IsNeedSync == inQuery.IsNeedSync)
.WhereIf(inQuery.IsSync != null, t => t.IsSync == inQuery.IsSync)
.WhereIf(inQuery.Priority != null, t => t.Priority == inQuery.Priority)
.WhereIf(inQuery.BatchDataType != null, t => t.BatchDataType == inQuery.BatchDataType)
.WhereIf(!string.IsNullOrEmpty(inQuery.UploadRegion), t => t.UploadRegion == inQuery.UploadRegion)
.WhereIf(!string.IsNullOrEmpty(inQuery.TargetRegion), t => t.TargetRegion == inQuery.TargetRegion)
.WhereIf(!string.IsNullOrEmpty(inQuery.UploadBatchId), t => t.UploadBatchId.Contains(inQuery.UploadBatchId))
.WhereIf(!string.IsNullOrEmpty(inQuery.Path), t => t.Path.Contains(inQuery.Path))
.WhereIf(inQuery.UploadStartTime != null, t => t.CreateTime >= inQuery.UploadStartTime)
.WhereIf(inQuery.UploadEndTime != null, t => t.CreateTime <= inQuery.UploadEndTime)
.WhereIf(inQuery.SyncFinishedStartTime != null, t => t.SyncFinishedTime >= inQuery.SyncFinishedStartTime)
.WhereIf(inQuery.SyncFinishedEndTime != null, t => t.SyncFinishedTime <= inQuery.SyncFinishedEndTime)
.ProjectTo<FileUploadRecordView>(_mapper.ConfigurationProvider);
var pageList = await fileUploadRecordQueryable.ToPagedListAsync(inQuery);
return pageList;
}
/// <summary>
/// 任务具体执行记录表
/// </summary>
/// <param name="inQuery"></param>
/// <returns></returns>
[HttpPost]
public async Task<PageOutput<UploadFileSyncRecordView>> GetUploadFileSyncRecordList(UploadFileSyncRecordQuery inQuery)
{
var fileUploadRecordQueryable = _uploadFileSyncRecordRepository
.WhereIf(inQuery.JobState != null, t => t.JobState == inQuery.JobState)
.WhereIf(inQuery.FileUploadRecordId != null, t => t.FileUploadRecordId == inQuery.FileUploadRecordId)
.WhereIf(!string.IsNullOrEmpty(inQuery.StudyCode), t => t.FileUploadRecord.StudyCode.Contains(inQuery.StudyCode))
.WhereIf(!string.IsNullOrEmpty(inQuery.VisitName), t => t.FileUploadRecord.SubjectVisit.VisitName.Contains(inQuery.VisitName))
.WhereIf(!string.IsNullOrEmpty(inQuery.SubjectCode), t => t.FileUploadRecord.Subject.Code.Contains(inQuery.SubjectCode))
.ProjectTo<UploadFileSyncRecordView>(_mapper.ConfigurationProvider);
var pageList = await fileUploadRecordQueryable.ToPagedListAsync(inQuery);
return pageList;
}
/// <summary>
/// 批量设置为需要同步,并且设置优先级
/// </summary>
/// <param name="inComand"></param>
/// <returns></returns>
public async Task<IResponseOutput> BatchAddSyncFileTask(BatchAddSyncFileCommand inComand)
{
await _fileUploadRecordRepository.BatchUpdateNoTrackingAsync(t => inComand.FileUploadRecordIdList.Contains(t.Id), u => new FileUploadRecord() { IsNeedSync = true, Priority = inComand.Priority ?? 0 });
foreach (var item in inComand.FileUploadRecordIdList)
{
_fileSyncQueue.Enqueue(item, inComand.Priority ?? 0);
}
await _fileUploadRecordRepository.SaveChangesAsync();
return ResponseOutput.Ok();
}
public async Task<IResponseOutput> AddOrUpdateFileUploadRecord(FileUploadRecordAddOrEdit addOrEditFileUploadRecord)
{
addOrEditFileUploadRecord.IP = _userInfo.IP;
if (ObjectStoreServiceConfig.IsOpenStoreSync && _userInfo.Domain.IsNotNullOrEmpty())
{
var find = ObjectStoreServiceConfig.SyncConfigList.FirstOrDefault(t => t.Domain == _userInfo.Domain);
if (find != null)
{
addOrEditFileUploadRecord.UploadRegion = find.UploadRegion;
addOrEditFileUploadRecord.TargetRegion = find.TargetRegion;
}
else
{
//前后端调试的时候,上传的时候域名不对应,自动按照后端配置设置上传区域和同步区域
var apiDefalut = ObjectStoreServiceConfig.SyncConfigList.FirstOrDefault(t => t.UploadRegion == ObjectStoreServiceConfig.ApiDeployRegion);
if (apiDefalut != null)
{
addOrEditFileUploadRecord.UploadRegion = apiDefalut.UploadRegion;
addOrEditFileUploadRecord.TargetRegion = apiDefalut.TargetRegion;
}
}
}
if (addOrEditFileUploadRecord.TrialId != null)
{
var trialDataStore = await _fusionCache.GetOrSetAsync(CacheKeys.TrialDataStoreType(addOrEditFileUploadRecord.TrialId.Value), async _ =>
{
return await _trialRepository.Where(t => t.Id == addOrEditFileUploadRecord.TrialId).Select(t => t.TrialDataStoreType)
.FirstOrDefaultAsync();
},
TimeSpan.FromDays(7)
);
//项目配置了,那么就设置需要同步
if (trialDataStore == TrialDataStore.MUtiCenter)
{
addOrEditFileUploadRecord.IsNeedSync = true;
addOrEditFileUploadRecord.Priority = addOrEditFileUploadRecord.Priority?? 0;
addOrEditFileUploadRecord.IsSync = false;
}
else
{
addOrEditFileUploadRecord.IsNeedSync = false;
//addOrEditFileUploadRecord.TargetRegion = "";
}
}
else
{
//系统文件,默认同步
addOrEditFileUploadRecord.IsNeedSync = true;
addOrEditFileUploadRecord.IsSync = false;
addOrEditFileUploadRecord.Priority = addOrEditFileUploadRecord.Priority ?? 0;
}
var entity = await _fileUploadRecordRepository.InsertOrUpdateAsync(addOrEditFileUploadRecord, true);
if (addOrEditFileUploadRecord.IsNeedSync == true)
{
_fileSyncQueue.Enqueue(entity.Id, addOrEditFileUploadRecord.Priority ?? 0);
}
return ResponseOutput.Ok(entity.Id.ToString());
}
[HttpDelete("{fileUploadRecordId:guid}")]
public async Task<IResponseOutput> DeleteFileUploadRecord(Guid fileUploadRecordId)
{
var success = await _fileUploadRecordRepository.BatchDeleteNoTrackingAsync(t => t.Id == fileUploadRecordId);
return ResponseOutput.Ok();
}
}
#region
public sealed class FileSyncQueue
{
/// <summary>
/// 优先级队列(仅负责排序)
/// </summary>
private readonly PriorityQueue<Guid, int> _queue = new();
/// <summary>
/// 当前等待中的任务(唯一真实数据)
/// key = Guid
/// value = 最新 priority
/// </summary>
private readonly Dictionary<Guid, int> _waiting = new();
/// <summary>
/// 正在执行的任务(防止重复执行)
/// </summary>
private readonly HashSet<Guid> _running = new();
/// <summary>
/// worker 等待信号
/// </summary>
private readonly SemaphoreSlim _signal = new(0);
private readonly object _lock = new();
// ============================================================
// Enqueue
// ============================================================
/// <summary>
/// 入队(同 Guid 会覆盖优先级)
/// </summary>
public void Enqueue(Guid id, int priority)
{
bool needSignal = false;
lock (_lock)
{
// 如果正在执行,忽略(防止重复)
if (_running.Contains(id))
return;
// 是否新任务(用于减少 signal 风暴)
if (!_waiting.ContainsKey(id))
needSignal = true;
// 更新为最新优先级(最后一次为准)
_waiting[id] = priority; //等价于添加或者更新
// PriorityQueue 无法更新节点
// 允许旧节点存在,Dequeue 时过滤
_queue.Enqueue(id, -priority);
}
// 只有新增任务才唤醒 worker
if (needSignal)
_signal.Release();
}
// ============================================================
// Dequeue
// ============================================================
/// <summary>
/// 获取一个待执行任务(无任务时自动等待)
/// </summary>
public async Task<Guid> DequeueAsync(CancellationToken ct)
{
while (true)
{
await _signal.WaitAsync(ct);
lock (_lock)
{
while (_queue.Count > 0)
{
var id = _queue.Dequeue();
// 已被覆盖或取消 如果这个任务已经不是“当前最新版任务”,那它只是 PriorityQueue 里的垃圾数据,直接跳过。
if (!_waiting.TryGetValue(id, out _)) //能从等待任务中取到,那么就是有效的,不能取到那么就是覆盖的
continue;
// 标记为运行中
_waiting.Remove(id);
_running.Add(id);
return id;
}
}
}
}
// ============================================================
// Complete
// ============================================================
/// <summary>
/// 任务执行完成(必须调用)
/// </summary>
public void Complete(Guid id)
{
lock (_lock)
{
_running.Remove(id);
}
}
// ============================================================
// Snapshot
// ============================================================
/// <summary>
/// 当前等待中的任务快照
/// </summary>
public Guid[] Snapshot()
{
lock (_lock)
{
return _waiting.Keys.ToArray();
}
}
// ============================================================
// 状态信息(调试用)
// ============================================================
public int WaitingCount
{
get
{
lock (_lock)
return _waiting.Count;
}
}
public int RunningCount
{
get
{
lock (_lock)
return _running.Count;
}
}
}
///// <summary>
///// 同步队列 信号量
///// </summary>
//public class FileSyncQueue
//{
// private readonly PriorityQueue<Guid, int> _queue = new();
// private readonly SemaphoreSlim _signal = new(0);
// private readonly object _lock = new();
// public void Enqueue(Guid id, int priority)
// {
// lock (_lock)
// {
// // priority 越大越优先
// _queue.Enqueue(id, -priority);
// }
// //类似于计数器,不会产生通知风暴,可消费资源 +1
// //if (有等待线程) 唤醒一个 else 仅增加计数
// _signal.Release(); // 唤醒一个 worker
// }
// /// <summary>
// /// 如果没有任务 → 挂起等待 有任务 → 被唤醒并返回
// /// </summary>
// /// <param name="ct"></param>
// /// <returns></returns>
// public async Task<Guid> DequeueAsync(CancellationToken ct)
// {
// await _signal.WaitAsync(ct);
// lock (_lock)
// {
// return _queue.Dequeue();
// }
// }
// /// <summary>
// /// 获取队列任务Id
// /// </summary>
// /// <returns></returns>
// public Guid[] Snapshot()
// {
// lock (_lock)
// {
// return _queue.UnorderedItems
// .Select(x => x.Element)
// .ToArray();
// }
// }
//}
#region SyncQueueUseChannel SyncScheduler
public class SyncQueueUseChannel
{
// 优先级队列(priority 越大越先执行)
private readonly PriorityQueue<Guid, int> _queue = new();
// Worker 唤醒信号
private readonly Channel<bool> _signal =
Channel.CreateUnbounded<bool>(new UnboundedChannelOptions
{
SingleReader = false,
SingleWriter = false
});
// 队列任务数量(不是CPU数量!)
private int _count = 0;
private readonly object _lock = new();
/// <summary>
/// 入队任务
/// </summary>
public void Enqueue(Guid id, int priority)
{
bool needSignal = false;
lock (_lock)
{
// priority 越大越优先 → 转负数
_queue.Enqueue(id, -priority);
// 只有从 0 → 1 才需要唤醒 worker
if (_count == 0)
needSignal = true;
_count++;
}
// 避免 signal 风暴
if (needSignal)
_signal.Writer.TryWrite(true);
}
/// <summary>
/// Worker 等待并获取任务
/// </summary>
public async Task<Guid> DequeueAsync(CancellationToken ct)
{
// 没任务时挂起(不会占CPU
await _signal.Reader.ReadAsync(ct);
lock (_lock)
{
var id = _queue.Dequeue();
_count--;
// 如果还有任务,继续唤醒下一个 worker
if (_count > 0)
_signal.Writer.TryWrite(true);
return id;
}
}
/// <summary>
/// 当前排队数量(调试用)
/// </summary>
public int Count
{
get
{
lock (_lock)
return _count;
}
}
}
/// <summary>
/// 同步调度器
/// </summary>
public class FileSyncScheduler
{
private readonly FileSyncQueue _queue;
public FileSyncScheduler(FileSyncQueue queue)
{
_queue = queue;
}
public void Enqueue(FileUploadRecord file)
{
if (file.IsNeedSync != true)
return;
_queue.Enqueue(file.Id, file.Priority ?? 0);
}
/// <summary>
/// 如果没有任务 → 挂起等待 有任务 → 被唤醒并返回
/// </summary>
/// <param name="ct"></param>
/// <returns></returns>
public Task<Guid> WaitAsync(CancellationToken ct)
=> _queue.DequeueAsync(ct);
}
#endregion
#endregion
@@ -1,23 +0,0 @@
//--------------------------------------------------------------------
// 此代码由liquid模板自动生成 byzhouhang 20240909
// 生成时间 2025-10-28 06:22:47Z
// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。
//--------------------------------------------------------------------
using System;
using IRaCIS.Core.Infrastructure.Extention;
using System.Threading.Tasks;
using IRaCIS.Core.Application.ViewModel;
namespace IRaCIS.Core.Application.Interfaces;
public interface IEmailLogService
{
Task<PageOutput<EmailLogView>> GetEmailLogList(EmailLogQuery inQuery);
Task<IResponseOutput> AddOrUpdateEmailLog(EmailLogAddOrEdit addOrEditEmailLog);
Task<IResponseOutput> DeleteEmailLog(Guid emailLogId);
}
@@ -1,23 +0,0 @@
//--------------------------------------------------------------------
// 此代码由liquid模板自动生成 byzhouhang 20240909
// 生成时间 2026-03-10 06:15:31Z
// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。
//--------------------------------------------------------------------
using System;
using IRaCIS.Core.Infrastructure.Extention;
using System.Threading.Tasks;
using IRaCIS.Core.Application.ViewModel;
namespace IRaCIS.Core.Application.Interfaces;
public interface IFileUploadRecordService
{
Task<PageOutput<FileUploadRecordView>> GetFileUploadRecordList(FileUploadRecordQuery inQuery);
Task<IResponseOutput> AddOrUpdateFileUploadRecord(FileUploadRecordAddOrEdit addOrEditFileUploadRecord);
Task<IResponseOutput> DeleteFileUploadRecord(Guid fileUploadRecordId);
}
@@ -1,22 +1,15 @@
using DocumentFormat.OpenXml.Spreadsheet; using IRaCIS.Application.Contracts;
using IRaCIS.Application.Contracts;
using IRaCIS.Core.Application.Auth; using IRaCIS.Core.Application.Auth;
using IRaCIS.Core.Application.Helper; using IRaCIS.Core.Application.Helper;
using IRaCIS.Core.Application.MassTransit.Consumer;
using IRaCIS.Core.Domain.Models; using IRaCIS.Core.Domain.Models;
using IRaCIS.Core.Domain.Share; using IRaCIS.Core.Domain.Share;
using IRaCIS.Core.Infrastructure; using IRaCIS.Core.Infrastructure;
using MailKit; using MailKit;
using MailKit.Net.Imap;
using MailKit.Search;
using MailKit.Security;
using Medallion.Threading; using Medallion.Threading;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using MimeKit; using MimeKit;
using System.Net.Mail;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace IRaCIS.Core.Application.Service namespace IRaCIS.Core.Application.Service
@@ -55,7 +48,7 @@ namespace IRaCIS.Core.Application.Service
public interface IMailVerificationService public interface IMailVerificationService
{ {
Task AnolymousSendEmail(Guid trialId, string emailAddress, int verificationCode); Task AnolymousSendEmail(string researchProgramNo, string emailAddress, int verificationCode);
Task SendEmailVerification(string emailAddress, int verificationCode); Task SendEmailVerification(string emailAddress, int verificationCode);
@@ -68,7 +61,7 @@ namespace IRaCIS.Core.Application.Service
Task AddUserSendEmailAsync(Guid userId, string baseUrl, string routeUrl); Task AddUserSendEmailAsync(Guid userId, string baseUrl, string routeUrl);
Task AdminResetPwdSendEmailAsync(Guid userId, string pwdNotMd5 = "123456", string baseUrl = ""); Task AdminResetPwdSendEmailAsync(Guid userId, string pwdNotMd5 = "123456");
Task SiteSurveyUserJoinEmail(Guid trialId, Guid userId, string userTypes, string baseUrl, string rootUrl); Task SiteSurveyUserJoinEmail(Guid trialId, Guid userId, string userTypes, string baseUrl, string rootUrl);
@@ -77,14 +70,8 @@ namespace IRaCIS.Core.Application.Service
Task<(Guid identityUserId, Guid userRoleId)> DoctorJoinTrialEmail(Guid trialId, Guid doctorId, string baseUrl, string rootUrl); Task<(Guid identityUserId, Guid userRoleId)> DoctorJoinTrialEmail(Guid trialId, Guid doctorId, string baseUrl, string rootUrl);
Task UserFeedBackMail(Guid feedBackId); Task UserFeedBackMail(Guid feedBackId);
Task AfterUserModifyPasswordSendEmailAsync(Guid userId);
Task SiteSuervyCheckUser(Guid trialId, string email, string name);
Task SiteSuervyUpdateUser(Guid trialSiteId, string email, string name, string url);
} }
[ApiExplorerSettings(GroupName = "Common")]
public class MailVerificationService(IRepository<VerificationCode> _verificationCodeRepository, public class MailVerificationService(IRepository<VerificationCode> _verificationCodeRepository,
IRepository<SystemBasicData> _systemBasicDatarepository, IRepository<SystemBasicData> _systemBasicDatarepository,
IRepository<VisitTask> _visitTaskRepository, IRepository<VisitTask> _visitTaskRepository,
@@ -99,7 +86,6 @@ namespace IRaCIS.Core.Application.Service
IRepository<UserType> _userTypeRepository, IRepository<UserType> _userTypeRepository,
IRepository<Doctor> _doctorTypeRepository, IRepository<Doctor> _doctorTypeRepository,
IRepository<Dictionary> _dictionaryRepository, IRepository<Dictionary> _dictionaryRepository,
IRepository<EmailNoticeConfig> _emailNoticeConfigrepository, IRepository<EmailNoticeConfig> _emailNoticeConfigrepository,
IOptionsMonitor<SystemEmailSendConfig> systemEmailConfig, IOptionsMonitor<SystemEmailSendConfig> systemEmailConfig,
IDistributedLockProvider _distributedLockProvider, IMapper _mapper, IUserInfo _userInfo, IStringLocalizer _localizer) : BaseService, IMailVerificationService IDistributedLockProvider _distributedLockProvider, IMapper _mapper, IUserInfo _userInfo, IStringLocalizer _localizer) : BaseService, IMailVerificationService
@@ -107,17 +93,8 @@ namespace IRaCIS.Core.Application.Service
private readonly SystemEmailSendConfig _systemEmailConfig = systemEmailConfig.CurrentValue; private readonly SystemEmailSendConfig _systemEmailConfig = systemEmailConfig.CurrentValue;
/// <summary>
/// 获取邮件主题和html 通用封装 以用户语言为主,没有的就用当前的请求语言_userInfo.IsEn_Us
/// </summary>
/// <param name="scenario"></param>
/// <param name="messageToSend"></param>
/// <param name="emailFunc"></param>
/// <param name="userWorkLanguage"></param>
/// <returns></returns>
/// <exception cref="BusinessValidationFailedException"></exception>
private async Task<EmailNoticeConfig> GetEmailSubejctAndHtmlInfoAndBuildAsync(EmailBusinessScenario scenario, MimeMessage messageToSend, private async Task<EmailNoticeConfig> GetEmailSubejctAndHtmlInfoAndBuildAsync(EmailBusinessScenario scenario, MimeMessage messageToSend,
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailFunc, UserWorkLanguage? userWorkLanguage = null) Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailFunc)
{ {
var configInfo = await _emailNoticeConfigrepository.Where(t => t.BusinessScenarioEnum == scenario).FirstOrDefaultAsync(); var configInfo = await _emailNoticeConfigrepository.Where(t => t.BusinessScenarioEnum == scenario).FirstOrDefaultAsync();
@@ -129,9 +106,7 @@ namespace IRaCIS.Core.Application.Service
} }
bool isEn_Us = userWorkLanguage.HasValue ? userWorkLanguage.Value == UserWorkLanguage.US : _userInfo.IsEn_Us; var (topicStr, htmlBodyStr) = _userInfo.IsEn_Us ? (configInfo.EmailTopic, configInfo.EmailHtmlContent) : (configInfo.EmailTopicCN, configInfo.EmailHtmlContentCN);
var (topicStr, htmlBodyStr) = isEn_Us ? (configInfo.EmailTopic, configInfo.EmailHtmlContent) : (configInfo.EmailTopicCN, configInfo.EmailHtmlContentCN);
try try
{ {
@@ -194,16 +169,16 @@ namespace IRaCIS.Core.Application.Service
return sucessHandle; return sucessHandle;
} }
private string ReplaceCompanyName(string needDealtxt)
{
var str = needDealtxt.Replace("{company}", _userInfo.IsEn_Us ? _systemEmailConfig.CompanyName : _systemEmailConfig.CompanyNameCN)
.Replace("{company abbreviation}", _userInfo.IsEn_Us ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN);
return str;
}
//MFA //MFA
public async Task SenMFAVerifyEmail(Guid userId, string userName, string emailAddress, int verificationCode, UserMFAType mfaType = UserMFAType.Login) public async Task SenMFAVerifyEmail(Guid userId, string userName, string emailAddress, int verificationCode, UserMFAType mfaType = UserMFAType.Login)
{ {
//设置工作语言
var workLanguage = _identityUserRepository.Where(t => t.Id == userId).Select(t => t.UserWorkLanguage).First();
var isEn_US = workLanguage == UserWorkLanguage.US;
var messageToSend = new MimeMessage(); var messageToSend = new MimeMessage();
//发件地址 //发件地址
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail)); messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
@@ -211,13 +186,13 @@ namespace IRaCIS.Core.Application.Service
messageToSend.To.Add(new MailboxAddress(userName, emailAddress)); messageToSend.To.Add(new MailboxAddress(userName, emailAddress));
//主题---[来自{0}] 关于MFA邮箱验证的提醒 //主题---[来自{0}] 关于MFA邮箱验证的提醒
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN; var companyName = _userInfo.IsEn_Us ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input => Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
{ {
var topicStr = string.Format(input.topicStr, companyName); var topicStr = string.Format(input.topicStr, companyName);
var htmlBodyStr = string.Format(CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr, workLanguage), var htmlBodyStr = string.Format(ReplaceCompanyName(input.htmlBodyStr),
userName, userName,
verificationCode verificationCode
); );
@@ -226,7 +201,7 @@ namespace IRaCIS.Core.Application.Service
}; };
await GetEmailSubejctAndHtmlInfoAndBuildAsync(mfaType == UserMFAType.Login ? EmailBusinessScenario.MFALogin : EmailBusinessScenario.MFAUnlock, messageToSend, emailConfigFunc, workLanguage); await GetEmailSubejctAndHtmlInfoAndBuildAsync(mfaType == UserMFAType.Login ? EmailBusinessScenario.MFALogin : EmailBusinessScenario.MFAUnlock, messageToSend, emailConfigFunc);
var sucessHandle = GetEmailSuccessHandle(userId, verificationCode, emailAddress); var sucessHandle = GetEmailSuccessHandle(userId, verificationCode, emailAddress);
@@ -249,11 +224,6 @@ namespace IRaCIS.Core.Application.Service
public async Task SendEmailVerification(string emailAddress, int verificationCode) public async Task SendEmailVerification(string emailAddress, int verificationCode)
{ {
//设置工作语言
var isEn_US = _userInfo.IsEn_Us;
var workLanguage = isEn_US ? UserWorkLanguage.US : UserWorkLanguage.CN;
var messageToSend = new MimeMessage(); var messageToSend = new MimeMessage();
//发件地址 //发件地址
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail)); messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
@@ -262,13 +232,13 @@ namespace IRaCIS.Core.Application.Service
//主题 //主题
//---[来自展影IRC]的提醒 //---[来自展影IRC]的提醒
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN; var companyName = _userInfo.IsEn_Us ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input => Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
{ {
var topicStr = string.Format(input.topicStr, companyName); var topicStr = string.Format(input.topicStr, companyName);
var htmlBodyStr = string.Format(CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr, workLanguage), var htmlBodyStr = string.Format(ReplaceCompanyName(input.htmlBodyStr),
"Sir/Madam", "Sir/Madam",
//---您正在参与展影医疗IRC项目 //---您正在参与展影医疗IRC项目
@@ -280,7 +250,7 @@ namespace IRaCIS.Core.Application.Service
}; };
await GetEmailSubejctAndHtmlInfoAndBuildAsync(EmailBusinessScenario.ReviewerLogin, messageToSend, emailConfigFunc, workLanguage); await GetEmailSubejctAndHtmlInfoAndBuildAsync(EmailBusinessScenario.ReviewerLogin, messageToSend, emailConfigFunc);
//此时不知道用户 //此时不知道用户
var sucessHandle = GetEmailSuccessHandle(Guid.Empty, verificationCode, emailAddress); var sucessHandle = GetEmailSuccessHandle(Guid.Empty, verificationCode, emailAddress);
@@ -293,14 +263,9 @@ namespace IRaCIS.Core.Application.Service
//中心调研 登陆 发送验证码 //中心调研 登陆 发送验证码
public async Task AnolymousSendEmail(Guid trialId, string emailAddress, int verificationCode) public async Task AnolymousSendEmail(string researchProgramNo, string emailAddress, int verificationCode)
{ {
//throw new BusinessValidationFailedException("模拟邮件取数据或者发送异常!!!");
//设置工作语言
var isEn_US = _userInfo.IsEn_Us;
var workLanguage = isEn_US ? UserWorkLanguage.US : UserWorkLanguage.CN;
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == trialId);
var messageToSend = new MimeMessage(); var messageToSend = new MimeMessage();
//发件地址 //发件地址
@@ -310,13 +275,13 @@ namespace IRaCIS.Core.Application.Service
//主题 //主题
//$"[来自展影IRC] [{researchProgramNo}] 关于中心调研的提醒"; //$"[来自展影IRC] [{researchProgramNo}] 关于中心调研的提醒";
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN; var companyName = _userInfo.IsEn_Us ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input => Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
{ {
var topicStr = string.Format(input.topicStr, companyName, trialInfo.ResearchProgramNo); var topicStr = string.Format(input.topicStr, companyName, researchProgramNo);
var htmlBodyStr = string.Format(CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr, workLanguage), var htmlBodyStr = string.Format(ReplaceCompanyName(input.htmlBodyStr),
"Sir/Madam", "Sir/Madam",
//---您正在参与展影医疗IRC项目中心调研工作 //---您正在参与展影医疗IRC项目中心调研工作
@@ -328,13 +293,13 @@ namespace IRaCIS.Core.Application.Service
}; };
await GetEmailSubejctAndHtmlInfoAndBuildAsync(EmailBusinessScenario.SiteSurveyLogin, messageToSend, emailConfigFunc, workLanguage); await GetEmailSubejctAndHtmlInfoAndBuildAsync(EmailBusinessScenario.SiteSurveyLogin, messageToSend, emailConfigFunc);
//此时不知道用户 //此时不知道用户
var sucessHandle = GetEmailSuccessHandle(Guid.Empty, verificationCode, emailAddress); var sucessHandle = GetEmailSuccessHandle(Guid.Empty, verificationCode, emailAddress);
await SendEmailHelper.SendTrialEmailAsync(messageToSend, trialInfo, sucessHandle); await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig, sucessHandle);
} }
@@ -346,9 +311,6 @@ namespace IRaCIS.Core.Application.Service
var sysUserInfo = (await _identityUserRepository.Where(t => t.Id == userId).Include(t => t.UserRoleList).ThenInclude(c => c.UserTypeRole).FirstOrDefaultAsync()).IfNullThrowException(); var sysUserInfo = (await _identityUserRepository.Where(t => t.Id == userId).Include(t => t.UserRoleList).ThenInclude(c => c.UserTypeRole).FirstOrDefaultAsync()).IfNullThrowException();
//设置工作语言
var workLanguage = sysUserInfo.UserWorkLanguage;
var isEn_US = workLanguage == UserWorkLanguage.US;
var messageToSend = new MimeMessage(); var messageToSend = new MimeMessage();
//发件地址 //发件地址
@@ -363,19 +325,20 @@ namespace IRaCIS.Core.Application.Service
await _identityUserRepository.BatchUpdateNoTrackingAsync(t => t.Id == sysUserInfo.Id, u => new IdentityUser() { EmailToken = token }); await _identityUserRepository.BatchUpdateNoTrackingAsync(t => t.Id == sysUserInfo.Id, u => new IdentityUser() { EmailToken = token });
routeUrl = routeUrl + "?UserId=" + sysUserInfo.Id + "&Email=" + sysUserInfo.EMail + "&UserName=" + sysUserInfo.UserName + "&lang=" + (isEn_US ? "en" : "zh") + "&access_token=" + token; routeUrl = routeUrl + "?UserId=" + sysUserInfo.Id + "&Email=" + sysUserInfo.EMail + "&UserName=" + sysUserInfo.UserName + "&lang=" + (_userInfo.IsEn_Us ? "en" : "zh") + "&access_token=" + token;
var domain = baseUrl.Substring(0, baseUrl.IndexOf("/login")); var domain = baseUrl.Substring(0, baseUrl.IndexOf("/login"));
var redirectUrl = $"{domain}/api/User/UserRedirect?url={System.Web.HttpUtility.UrlEncode(routeUrl)}"; var redirectUrl = $"{domain}/api/User/UserRedirect?url={System.Web.HttpUtility.UrlEncode(routeUrl)}";
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
var companyName = _userInfo.IsEn_Us ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input => Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
{ {
var topicStr = string.Format(input.topicStr, companyName); var topicStr = string.Format(input.topicStr, companyName);
var htmlBodyStr = string.Format(CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr, workLanguage), var htmlBodyStr = string.Format(ReplaceCompanyName(input.htmlBodyStr),
sysUserInfo.FullName, sysUserInfo.FullName,
sysUserInfo.EMail, sysUserInfo.EMail,
@@ -387,22 +350,17 @@ namespace IRaCIS.Core.Application.Service
}; };
await GetEmailSubejctAndHtmlInfoAndBuildAsync(EmailBusinessScenario.SysCreateUser, messageToSend, emailConfigFunc, sysUserInfo.UserWorkLanguage); await GetEmailSubejctAndHtmlInfoAndBuildAsync(EmailBusinessScenario.SysCreateUser, messageToSend, emailConfigFunc);
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig); await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
} }
//管理员重置密码发送邮件 //管理员重置密码发送邮件
public async Task AdminResetPwdSendEmailAsync(Guid userId, string pwdNotMd5 = "123456", string baseUrl = "") public async Task AdminResetPwdSendEmailAsync(Guid userId, string pwdNotMd5 = "123456")
{ {
var sysUserInfo = (await _identityUserRepository.Where(t => t.Id == userId).Include(t => t.UserRoleList).ThenInclude(c => c.UserTypeRole).FirstOrDefaultAsync()).IfNullThrowException(); var sysUserInfo = (await _identityUserRepository.Where(t => t.Id == userId).Include(t => t.UserRoleList).ThenInclude(c => c.UserTypeRole).FirstOrDefaultAsync()).IfNullThrowException();
//设置工作语言
var workLanguage = sysUserInfo.UserWorkLanguage;
var isEn_US = workLanguage == UserWorkLanguage.US;
var messageToSend = new MimeMessage(); var messageToSend = new MimeMessage();
//发件地址 //发件地址
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail)); messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
@@ -411,27 +369,25 @@ namespace IRaCIS.Core.Application.Service
//主题 //主题
//---[来自展影IRC] 关于重置账户密码的提醒 //---[来自展影IRC] 关于重置账户密码的提醒
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN; var companyName = _userInfo.IsEn_Us ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input => Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
{ {
var topicStr = string.Format(input.topicStr, companyName); var topicStr = string.Format(input.topicStr, companyName);
var htmlBodyStr = string.Format(CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr, workLanguage), var htmlBodyStr = string.Format(ReplaceCompanyName(input.htmlBodyStr),
sysUserInfo.FullName, sysUserInfo.FullName,
sysUserInfo.UserName, sysUserInfo.UserName,
//string.Join(',', sysUserInfo.UserRoleList.Select(t => t.UserTypeRole.UserTypeShortName)), //string.Join(',', sysUserInfo.UserRoleList.Select(t => t.UserTypeRole.UserTypeShortName)),
pwdNotMd5, pwdNotMd5
baseUrl
); );
return (topicStr, htmlBodyStr); return (topicStr, htmlBodyStr);
}; };
await GetEmailSubejctAndHtmlInfoAndBuildAsync(EmailBusinessScenario.SysResetPassword, messageToSend, emailConfigFunc, sysUserInfo.UserWorkLanguage); await GetEmailSubejctAndHtmlInfoAndBuildAsync(EmailBusinessScenario.SysResetPassword, messageToSend, emailConfigFunc);
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig); await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
} }
@@ -439,10 +395,6 @@ namespace IRaCIS.Core.Application.Service
//用户重置邮箱 //用户重置邮箱
public async Task SendMailEditEmail(Guid userId, string userName, string emailAddress, int verificationCode) public async Task SendMailEditEmail(Guid userId, string userName, string emailAddress, int verificationCode)
{ {
//设置工作语言
var workLanguage = _identityUserRepository.Where(t => t.Id == userId).Select(t => t.UserWorkLanguage).First();
var isEn_US = workLanguage == UserWorkLanguage.US;
var messageToSend = new MimeMessage(); var messageToSend = new MimeMessage();
//发件地址 //发件地址
@@ -452,13 +404,13 @@ namespace IRaCIS.Core.Application.Service
//主题 //主题
//---[来自展影IRC] 关于重置邮箱的提醒 //---[来自展影IRC] 关于重置邮箱的提醒
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN; var companyName = _userInfo.IsEn_Us ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input => Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
{ {
var topicStr = string.Format(input.topicStr, companyName); var topicStr = string.Format(input.topicStr, companyName);
var htmlBodyStr = string.Format(CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr, workLanguage), var htmlBodyStr = string.Format(ReplaceCompanyName(input.htmlBodyStr),
userName, userName,
//---您正在进行邮箱重置操作 //---您正在进行邮箱重置操作
@@ -469,7 +421,7 @@ namespace IRaCIS.Core.Application.Service
}; };
await GetEmailSubejctAndHtmlInfoAndBuildAsync(EmailBusinessScenario.UserResetEmail, messageToSend, emailConfigFunc, workLanguage); await GetEmailSubejctAndHtmlInfoAndBuildAsync(EmailBusinessScenario.UserResetEmail, messageToSend, emailConfigFunc);
var sucessHandle = GetEmailSuccessHandle(userId, verificationCode, emailAddress); var sucessHandle = GetEmailSuccessHandle(userId, verificationCode, emailAddress);
@@ -482,11 +434,6 @@ namespace IRaCIS.Core.Application.Service
//不登录 通过邮箱重置密码 //不登录 通过邮箱重置密码
public async Task AnolymousSendEmailForResetAccount(string emailAddress, int verificationCode) public async Task AnolymousSendEmailForResetAccount(string emailAddress, int verificationCode)
{ {
//设置工作语言
var isEn_US = _userInfo.IsEn_Us;
var workLanguage = isEn_US ? UserWorkLanguage.US : UserWorkLanguage.CN;
var messageToSend = new MimeMessage(); var messageToSend = new MimeMessage();
//发件地址 //发件地址
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail)); messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
@@ -494,13 +441,13 @@ namespace IRaCIS.Core.Application.Service
messageToSend.To.Add(new MailboxAddress(String.Empty, emailAddress)); messageToSend.To.Add(new MailboxAddress(String.Empty, emailAddress));
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN; var companyName = _userInfo.IsEn_Us ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input => Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
{ {
var topicStr = string.Format(input.topicStr, companyName); var topicStr = string.Format(input.topicStr, companyName);
var htmlBodyStr = string.Format(CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr, workLanguage), var htmlBodyStr = string.Format(ReplaceCompanyName(input.htmlBodyStr),
"Sir/Madam", "Sir/Madam",
//---您正在进行邮箱重置密码操作 //---您正在进行邮箱重置密码操作
@@ -511,7 +458,7 @@ namespace IRaCIS.Core.Application.Service
}; };
await GetEmailSubejctAndHtmlInfoAndBuildAsync(EmailBusinessScenario.UnloginUseEmailResetPassword, messageToSend, emailConfigFunc, workLanguage); await GetEmailSubejctAndHtmlInfoAndBuildAsync(EmailBusinessScenario.UnloginUseEmailResetPassword, messageToSend, emailConfigFunc);
////此时不知道用户 ////此时不知道用户
var sucessHandle = GetEmailSuccessHandle(Guid.Empty, verificationCode, emailAddress); var sucessHandle = GetEmailSuccessHandle(Guid.Empty, verificationCode, emailAddress);
@@ -527,11 +474,6 @@ namespace IRaCIS.Core.Application.Service
var sysUserInfo = (await _identityUserRepository.Where(t => t.Id == userId).FirstOrDefaultAsync()).IfNullThrowException(); var sysUserInfo = (await _identityUserRepository.Where(t => t.Id == userId).FirstOrDefaultAsync()).IfNullThrowException();
//设置工作语言
var workLanguage = sysUserInfo.UserWorkLanguage;
var isEn_US = workLanguage == UserWorkLanguage.US;
var messageToSend = new MimeMessage(); var messageToSend = new MimeMessage();
//发件地址 //发件地址
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail)); messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
@@ -550,18 +492,18 @@ namespace IRaCIS.Core.Application.Service
var domain = baseUrl.Substring(0, baseUrl.IndexOf("/login")); var domain = baseUrl.Substring(0, baseUrl.IndexOf("/login"));
var routeUrl = rootUrl + "?UserId=" + sysUserInfo.Id + "&Email=" + sysUserInfo.EMail + "&lang=" + (isEn_US ? "en" : "zh") + "&access_token=" + token; var routeUrl = rootUrl + "?UserId=" + sysUserInfo.Id + "&Email=" + sysUserInfo.EMail + "&lang=" + (_userInfo.IsEn_Us ? "en" : "zh") + "&access_token=" + token;
var redirectUrl = $"{domain}/api/User/UserRedirect?url={System.Web.HttpUtility.UrlEncode(routeUrl)}"; var redirectUrl = $"{domain}/api/User/UserRedirect?url={System.Web.HttpUtility.UrlEncode(routeUrl)}";
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN; var companyName = _userInfo.IsEn_Us ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input => Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
{ {
var topicStr = string.Format(input.topicStr, companyName, trialInfo.ResearchProgramNo); var topicStr = string.Format(input.topicStr, companyName, trialInfo.ResearchProgramNo);
var htmlBodyStr = string.Format(CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr, workLanguage), var htmlBodyStr = string.Format(ReplaceCompanyName(input.htmlBodyStr),
sysUserInfo.FullName, sysUserInfo.FullName,
trialInfo.ExperimentName, trialInfo.ExperimentName,
@@ -576,9 +518,10 @@ namespace IRaCIS.Core.Application.Service
}; };
await GetEmailSubejctAndHtmlInfoAndBuildAsync(sysUserInfo.IsFirstAdd ? EmailBusinessScenario.SiteUseOrExternalUserFirstrJoinTrial : EmailBusinessScenario.SiteUserOrExternalUserExistJoinTrial, messageToSend, emailConfigFunc, workLanguage); await GetEmailSubejctAndHtmlInfoAndBuildAsync(sysUserInfo.IsFirstAdd ? EmailBusinessScenario.SiteUseOrExternalUserFirstrJoinTrial : EmailBusinessScenario.SiteUserOrExternalUserExistJoinTrial, messageToSend, emailConfigFunc);
await SendEmailHelper.SendTrialEmailAsync(messageToSend, trialInfo);
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig, null);
} }
@@ -587,11 +530,9 @@ namespace IRaCIS.Core.Application.Service
{ {
var sysUserInfo = (await _identityUserRepository.Where(t => t.Id == userId).FirstOrDefaultAsync()).IfNullThrowException(); var sysUserInfo = (await _identityUserRepository.Where(t => t.Id == userId).FirstOrDefaultAsync()).IfNullThrowException();
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == trialId);
//设置工作语言
var workLanguage = sysUserInfo.UserWorkLanguage; var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == trialId);
var isEn_US = workLanguage == UserWorkLanguage.US;
var messageToSend = new MimeMessage(); var messageToSend = new MimeMessage();
//发件地址 //发件地址
@@ -609,20 +550,20 @@ namespace IRaCIS.Core.Application.Service
await _identityUserRepository.BatchUpdateNoTrackingAsync(t => t.Id == sysUserInfo.Id, u => new Domain.Models.IdentityUser() { EmailToken = token }); await _identityUserRepository.BatchUpdateNoTrackingAsync(t => t.Id == sysUserInfo.Id, u => new Domain.Models.IdentityUser() { EmailToken = token });
} }
var routeUrl = rootUrl + "?UserId=" + sysUserInfo.Id + "&Email=" + sysUserInfo.EMail + "&lang=" + (isEn_US ? "en" : "zh") + "&access_token=" + token; var routeUrl = rootUrl + "?UserId=" + sysUserInfo.Id + "&Email=" + sysUserInfo.EMail + "&lang=" + (_userInfo.IsEn_Us ? "en" : "zh") + "&access_token=" + token;
var domain = baseUrl.Substring(0, baseUrl.IndexOf("/login")); var domain = baseUrl.Substring(0, baseUrl.IndexOf("/login"));
var redirectUrl = $"{domain}/api/User/UserRedirect?url={System.Web.HttpUtility.UrlEncode(routeUrl)}"; var redirectUrl = $"{domain}/api/User/UserRedirect?url={System.Web.HttpUtility.UrlEncode(routeUrl)}";
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN; var companyName = _userInfo.IsEn_Us ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input => Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
{ {
var topicStr = string.Format(input.topicStr, companyName, trialInfo.ResearchProgramNo); var topicStr = string.Format(input.topicStr, companyName, trialInfo.ResearchProgramNo);
var htmlBodyStr = string.Format(CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr, workLanguage), var htmlBodyStr = string.Format(ReplaceCompanyName(input.htmlBodyStr),
sysUserInfo.FullName, sysUserInfo.FullName,
trialInfo.ExperimentName, trialInfo.ExperimentName,
@@ -637,9 +578,10 @@ namespace IRaCIS.Core.Application.Service
}; };
await GetEmailSubejctAndHtmlInfoAndBuildAsync(sysUserInfo.IsFirstAdd ? EmailBusinessScenario.SiteUseOrExternalUserFirstrJoinTrial : EmailBusinessScenario.SiteUserOrExternalUserExistJoinTrial, messageToSend, emailConfigFunc, workLanguage); await GetEmailSubejctAndHtmlInfoAndBuildAsync(sysUserInfo.IsFirstAdd ? EmailBusinessScenario.SiteUseOrExternalUserFirstrJoinTrial : EmailBusinessScenario.SiteUserOrExternalUserExistJoinTrial, messageToSend, emailConfigFunc);
await SendEmailHelper.SendTrialEmailAsync(messageToSend, trialInfo);
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
} }
@@ -678,12 +620,6 @@ namespace IRaCIS.Core.Application.Service
saveItem.IsTestUser = true; saveItem.IsTestUser = true;
} }
//通过医生信息,设置工作语言
var nation = _doctorTypeRepository.Where(t => t.Id == doctorId).Select(t => t.Nation).FirstOrDefault();
saveItem.UserWorkLanguage = nation == DoctorNation.CN ? UserWorkLanguage.CN : UserWorkLanguage.US;
saveItem.UserCeateSource = UserCeateSource.ReviewerSelect; saveItem.UserCeateSource = UserCeateSource.ReviewerSelect;
saveItem.TrialId = trialId; saveItem.TrialId = trialId;
@@ -726,11 +662,6 @@ namespace IRaCIS.Core.Application.Service
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == trialId); var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == trialId);
//设置工作语言
var workLanguage = sysUserInfo.UserWorkLanguage;
var isEn_US = workLanguage == UserWorkLanguage.US;
var messageToSend = new MimeMessage(); var messageToSend = new MimeMessage();
//发件地址 //发件地址
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail)); messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
@@ -746,7 +677,7 @@ namespace IRaCIS.Core.Application.Service
await _identityUserRepository.BatchUpdateNoTrackingAsync(t => t.Id == sysUserInfo.Id, u => new Domain.Models.IdentityUser() { EmailToken = token }); await _identityUserRepository.BatchUpdateNoTrackingAsync(t => t.Id == sysUserInfo.Id, u => new Domain.Models.IdentityUser() { EmailToken = token });
} }
var routeUrl = rootUrl + "?UserId=" + sysUserInfo.Id + "&Email=" + sysUserInfo.EMail + "&lang=" + (isEn_US ? "en" : "zh") + "&access_token=" + token; var routeUrl = rootUrl + "?UserId=" + sysUserInfo.Id + "&Email=" + sysUserInfo.EMail + "&lang=" + (_userInfo.IsEn_Us ? "en" : "zh") + "&access_token=" + token;
var domain = baseUrl.Substring(0, baseUrl.IndexOf("/login")); var domain = baseUrl.Substring(0, baseUrl.IndexOf("/login"));
@@ -756,13 +687,13 @@ namespace IRaCIS.Core.Application.Service
// $"[来自展影IRC] [{trialInfo.ResearchProgramNo}]邀请信"; // $"[来自展影IRC] [{trialInfo.ResearchProgramNo}]邀请信";
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN; var companyName = _userInfo.IsEn_Us ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input => Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
{ {
var topicStr = string.Format(input.topicStr, companyName, trialInfo.ResearchProgramNo); var topicStr = string.Format(input.topicStr, companyName, trialInfo.ResearchProgramNo);
var htmlBodyStr = string.Format(CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr, workLanguage), var htmlBodyStr = string.Format(ReplaceCompanyName(input.htmlBodyStr),
sysUserInfo.FullName, sysUserInfo.FullName,
trialInfo.ExperimentName, trialInfo.ExperimentName,
@@ -777,9 +708,10 @@ namespace IRaCIS.Core.Application.Service
}; };
await GetEmailSubejctAndHtmlInfoAndBuildAsync(sysUserInfo.IsFirstAdd ? EmailBusinessScenario.DoctorUserFirstJoinTrial : EmailBusinessScenario.DoctorUserExistJoinTrial, messageToSend, emailConfigFunc,workLanguage); await GetEmailSubejctAndHtmlInfoAndBuildAsync(sysUserInfo.IsFirstAdd ? EmailBusinessScenario.DoctorUserFirstJoinTrial : EmailBusinessScenario.DoctorUserExistJoinTrial, messageToSend, emailConfigFunc);
await SendEmailHelper.SendTrialEmailAsync(messageToSend, trialInfo, null);
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig, null);
//创建账号 和创建角色 一条,更新的时候才记录更新角色 //创建账号 和创建角色 一条,更新的时候才记录更新角色
if (isNeedCreateNewUser == false) if (isNeedCreateNewUser == false)
@@ -799,19 +731,13 @@ namespace IRaCIS.Core.Application.Service
{ {
var feedBack = await _userFeedBackRepository.Where(t => t.Id == feedBackId).Include(t => t.CreateUserRole.UserTypeRole).Include(t => t.CreateUserRole.IdentityUser).FirstNotNullAsync(); var feedBack = await _userFeedBackRepository.Where(t => t.Id == feedBackId).Include(t => t.CreateUserRole.UserTypeRole).Include(t => t.CreateUserRole.IdentityUser).FirstNotNullAsync();
//设置工作语言
var workLanguage = _identityUserRepository.Where(t => t.Id == _userInfo.IdentityUserId).Select(t => t.UserWorkLanguage).First();
var isEn_US = workLanguage == UserWorkLanguage.US;
var messageToSend = new MimeMessage(); var messageToSend = new MimeMessage();
//发件地址 //发件地址
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail)); messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
var isHaveTrialId = feedBack.TrialId != null; var isHaveTrialId = feedBack.TrialId != null;
var trialinfo = await _trialRepository.Where(x => x.Id == feedBack.TrialId).FirstOrDefaultAsync(); var companyName = _userInfo.IsEn_Us ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
var emialScenario = feedBack.VisitTaskId != null ? EmailBusinessScenario.IRImageError : (feedBack.SubjectVisitId != null ? EmailBusinessScenario.TrialSubjectVisitFeedBack : (feedBack.TrialId != null ? EmailBusinessScenario.TrialFeedBack : EmailBusinessScenario.SysFeedBack)); var emialScenario = feedBack.VisitTaskId != null ? EmailBusinessScenario.IRImageError : (feedBack.SubjectVisitId != null ? EmailBusinessScenario.TrialSubjectVisitFeedBack : (feedBack.TrialId != null ? EmailBusinessScenario.TrialFeedBack : EmailBusinessScenario.SysFeedBack));
@@ -834,7 +760,7 @@ namespace IRaCIS.Core.Application.Service
if (feedBack.VisitTaskId != null) if (feedBack.VisitTaskId != null)
{ {
var emailType = await _dictionaryRepository.Where(t => t.Parent.Code == "FeedBackTypeToIR" && t.ParentId != null && t.Code == ((int)feedBack.QuestionType).ToString()).Select(t => isEn_US ? t.Value : t.ValueCN).FirstOrDefaultAsync(); var emailType = await _dictionaryRepository.Where(t => t.Parent.Code == "FeedBackTypeToIR" && t.ParentId != null && t.Code == ((int)feedBack.QuestionType).ToString()).Select(t => _userInfo.IsEn_Us ? t.Value : t.ValueCN).FirstOrDefaultAsync();
var info = await _visitTaskRepository.Where(t => t.Id == feedBack.VisitTaskId).Select(t => new { t.Trial.ResearchProgramNo, t.Trial.TrialCode, SubejctCode = t.Subject.Code, t.SourceSubjectVisit.VisitName }).FirstNotNullAsync(); var info = await _visitTaskRepository.Where(t => t.Id == feedBack.VisitTaskId).Select(t => new { t.Trial.ResearchProgramNo, t.Trial.TrialCode, SubejctCode = t.Subject.Code, t.SourceSubjectVisit.VisitName }).FirstNotNullAsync();
@@ -843,7 +769,7 @@ namespace IRaCIS.Core.Application.Service
{ {
var topicStr = string.Format(input.topicStr, info.ResearchProgramNo, info.SubejctCode, info.VisitName); var topicStr = string.Format(input.topicStr, info.ResearchProgramNo, info.SubejctCode, info.VisitName);
var htmlBodyStr = string.Format(CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr, workLanguage), var htmlBodyStr = string.Format(ReplaceCompanyName(input.htmlBodyStr),
userNames, userNames,
info.TrialCode, info.TrialCode,
info.SubejctCode, info.SubejctCode,
@@ -859,12 +785,12 @@ namespace IRaCIS.Core.Application.Service
}; };
await GetEmailSubejctAndHtmlInfoAndBuildAsync(EmailBusinessScenario.IRImageError, messageToSend, emailConfigFunc, workLanguage); await GetEmailSubejctAndHtmlInfoAndBuildAsync(EmailBusinessScenario.IRImageError, messageToSend, emailConfigFunc);
} }
else if (feedBack.SubjectVisitId != null) else if (feedBack.SubjectVisitId != null)
{ {
var emailType = await _dictionaryRepository.Where(t => t.Parent.Code == "Email_BusinessScenario" && t.ParentId != null && t.Code == ((int)EmailBusinessScenario.TrialSubjectVisitFeedBack).ToString()).Select(t => isEn_US ? t.Value : t.ValueCN).FirstOrDefaultAsync(); var emailType = await _dictionaryRepository.Where(t => t.Parent.Code == "Email_BusinessScenario" && t.ParentId != null && t.Code == ((int)EmailBusinessScenario.TrialSubjectVisitFeedBack).ToString()).Select(t => _userInfo.IsEn_Us ? t.Value : t.ValueCN).FirstOrDefaultAsync();
var info = await _subjectVisitRepository.Where(t => t.Id == feedBack.SubjectVisitId).Select(t => new { t.Trial.ResearchProgramNo, t.Trial.TrialCode, SubejctCode = t.Subject.Code, t.VisitName }).FirstNotNullAsync(); var info = await _subjectVisitRepository.Where(t => t.Id == feedBack.SubjectVisitId).Select(t => new { t.Trial.ResearchProgramNo, t.Trial.TrialCode, SubejctCode = t.Subject.Code, t.VisitName }).FirstNotNullAsync();
@@ -873,7 +799,7 @@ namespace IRaCIS.Core.Application.Service
{ {
var topicStr = string.Format(input.topicStr, info.ResearchProgramNo, info.SubejctCode, info.VisitName); var topicStr = string.Format(input.topicStr, info.ResearchProgramNo, info.SubejctCode, info.VisitName);
var htmlBodyStr = string.Format(CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr, workLanguage), var htmlBodyStr = string.Format(ReplaceCompanyName(input.htmlBodyStr),
userNames, userNames,
info.TrialCode, info.TrialCode,
info.SubejctCode, info.SubejctCode,
@@ -889,13 +815,13 @@ namespace IRaCIS.Core.Application.Service
}; };
await GetEmailSubejctAndHtmlInfoAndBuildAsync(EmailBusinessScenario.TrialSubjectVisitFeedBack, messageToSend, emailConfigFunc, workLanguage); await GetEmailSubejctAndHtmlInfoAndBuildAsync(EmailBusinessScenario.TrialSubjectVisitFeedBack, messageToSend, emailConfigFunc);
} }
//项目相关的反馈 pm admin //项目相关的反馈 pm admin
else if (feedBack.TrialId != null) else if (feedBack.TrialId != null)
{ {
var emailType = await _dictionaryRepository.Where(t => t.Parent.Code == "Email_BusinessScenario" && t.ParentId != null && t.Code == ((int)EmailBusinessScenario.TrialFeedBack).ToString()).Select(t => isEn_US ? t.Value : t.ValueCN).FirstOrDefaultAsync(); var emailType = await _dictionaryRepository.Where(t => t.Parent.Code == "Email_BusinessScenario" && t.ParentId != null && t.Code == ((int)EmailBusinessScenario.TrialFeedBack).ToString()).Select(t => _userInfo.IsEn_Us ? t.Value : t.ValueCN).FirstOrDefaultAsync();
var info = await _trialRepository.Where(t => t.Id == feedBack.TrialId).Select(t => new { t.ResearchProgramNo, t.TrialCode }).FirstNotNullAsync(); var info = await _trialRepository.Where(t => t.Id == feedBack.TrialId).Select(t => new { t.ResearchProgramNo, t.TrialCode }).FirstNotNullAsync();
@@ -904,7 +830,7 @@ namespace IRaCIS.Core.Application.Service
{ {
var topicStr = string.Format(input.topicStr, info.ResearchProgramNo); var topicStr = string.Format(input.topicStr, info.ResearchProgramNo);
var htmlBodyStr = string.Format(CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr, workLanguage), var htmlBodyStr = string.Format(ReplaceCompanyName(input.htmlBodyStr),
userNames, userNames,
info.TrialCode, info.TrialCode,
feedBack.CreateUserRole.UserTypeRole.UserTypeShortName, feedBack.CreateUserRole.UserTypeRole.UserTypeShortName,
@@ -918,7 +844,7 @@ namespace IRaCIS.Core.Application.Service
}; };
await GetEmailSubejctAndHtmlInfoAndBuildAsync(EmailBusinessScenario.TrialFeedBack, messageToSend, emailConfigFunc, workLanguage); await GetEmailSubejctAndHtmlInfoAndBuildAsync(EmailBusinessScenario.TrialFeedBack, messageToSend, emailConfigFunc);
} }
//项目无关的反馈 admin //项目无关的反馈 admin
@@ -930,7 +856,7 @@ namespace IRaCIS.Core.Application.Service
{ {
var topicStr = string.Format(input.topicStr); var topicStr = string.Format(input.topicStr);
var htmlBodyStr = string.Format(CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr, workLanguage), var htmlBodyStr = string.Format(ReplaceCompanyName(input.htmlBodyStr),
userNames, userNames,
feedBack.CreateUserRole.UserTypeRole.UserTypeShortName, feedBack.CreateUserRole.UserTypeRole.UserTypeShortName,
feedBack.CreateUserRole.IdentityUser.FullName, feedBack.CreateUserRole.IdentityUser.FullName,
@@ -942,140 +868,11 @@ namespace IRaCIS.Core.Application.Service
}; };
await GetEmailSubejctAndHtmlInfoAndBuildAsync(EmailBusinessScenario.SysFeedBack, messageToSend, emailConfigFunc, workLanguage); await GetEmailSubejctAndHtmlInfoAndBuildAsync(EmailBusinessScenario.SysFeedBack, messageToSend, emailConfigFunc);
} }
if (trialinfo == null)
{
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
}
else
{
await SendEmailHelper.SendTrialEmailAsync(messageToSend, trialinfo);
}
}
//用户修改密码发送邮件
public async Task AfterUserModifyPasswordSendEmailAsync(Guid userId)
{
var sysUserInfo = (await _identityUserRepository.Where(t => t.Id == userId).FirstOrDefaultAsync()).IfNullThrowException();
//设置工作语言
var workLanguage = sysUserInfo.UserWorkLanguage;
var isEn_US = workLanguage == UserWorkLanguage.US;
var messageToSend = new MimeMessage();
//发件地址
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
//收件地址
messageToSend.To.Add(new MailboxAddress(sysUserInfo.FullName, sysUserInfo.EMail));
//主题
//---[来自展影IRC] 关于重置邮箱的提醒
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, workLanguage),
sysUserInfo.FullName,
_systemEmailConfig.PlatformName
);
return (topicStr, htmlBodyStr);
};
await GetEmailSubejctAndHtmlInfoAndBuildAsync(EmailBusinessScenario.IdentityUser_ModifyPassword, messageToSend, emailConfigFunc, workLanguage);
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig); await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
}
//中心调研核对人员提醒
public async Task SiteSuervyCheckUser(Guid trialId, string email, string name)
{
var trialInfo = await _trialRepository.Where(t => t.Id == trialId).FirstOrDefaultAsync();
//设置工作语言
var isEn_US = _userInfo.IsEn_Us;
var workLanguage = isEn_US ? UserWorkLanguage.US : UserWorkLanguage.CN;
var messageToSend = new MimeMessage();
//发件地址
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
//收件地址
messageToSend.To.Add(new MailboxAddress(name, email));
//主题
//---[来自展影IRC] 关于重置邮箱的提醒
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, trialInfo.ResearchProgramNo);
var htmlBodyStr = string.Format(CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr, workLanguage),
name,
trialInfo.TrialCode,
trialInfo.ResearchProgramNo
);
return (topicStr, htmlBodyStr);
};
await GetEmailSubejctAndHtmlInfoAndBuildAsync(EmailBusinessScenario.SiteSurvey_CheckUser, messageToSend, emailConfigFunc);
await SendEmailHelper.SendTrialEmailAsync(messageToSend, trialInfo);
}
public async Task SiteSuervyUpdateUser(Guid trialSiteId, string email, string name, string url)
{
var siteInfo = await _trialSiteRepository.Where(t => t.Id == trialSiteId).Include(x => x.Trial).FirstOrDefaultAsync();
var trialInfo = siteInfo.Trial;
//设置工作语言
var isEn_US = _userInfo.IsEn_Us;
var workLanguage = isEn_US ? UserWorkLanguage.US : UserWorkLanguage.CN;
var messageToSend = new MimeMessage();
//发件地址
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
//收件地址
messageToSend.To.Add(new MailboxAddress(name, email));
//主题
//---[来自展影IRC] 关于重置邮箱的提醒
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, trialInfo.ResearchProgramNo);
var htmlBodyStr = string.Format(CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr, workLanguage),
name,
trialInfo.TrialCode,
siteInfo.TrialSiteCode,
siteInfo.TrialSiteName,
url
);
return (topicStr, htmlBodyStr);
};
await GetEmailSubejctAndHtmlInfoAndBuildAsync(EmailBusinessScenario.SiteSurvey_UpdateUser, messageToSend, emailConfigFunc);
await SendEmailHelper.SendTrialEmailAsync(messageToSend, trialInfo);
}
} }
} }
}
File diff suppressed because it is too large Load Diff
@@ -11,18 +11,7 @@ namespace IRaCIS.Core.Application.Service
{ {
public CommonConfig() public CommonConfig()
{ {
// 在此处拷贝automapper 映射
CreateMap<EmailAttachmentLog, EmaliAttachmentInfo>();
CreateMap<EmailRecipientLog, EmailRecipientLogView>();
CreateMap<EmailLog, EmailLogView>()
.ForMember(t => t.RecipientList, u => u.MapFrom(c => c.EmailRecipientLogList))
.ForMember(t => t.AttachmentList, u => u.MapFrom(c => c.AttachmentList));
CreateMap<EmailLog, GetEmailInfoOutDto>()
.ForMember(t => t.RecipientList, u => u.MapFrom(c => c.EmailRecipientLogList))
.ForMember(t => t.AttachmentList, u => u.MapFrom(c => c.AttachmentList));
CreateMap<EmailLog, EmailLogAddOrEdit>().ReverseMap();
CreateMap<FrontAuditConfig, FrontAuditConfigAddOrEdit>().ReverseMap(); CreateMap<FrontAuditConfig, FrontAuditConfigAddOrEdit>().ReverseMap();
@@ -124,21 +113,6 @@ namespace IRaCIS.Core.Application.Service
CreateMap<IVUS_OCTBaseDto, IvusExportDto>(); CreateMap<IVUS_OCTBaseDto, IvusExportDto>();
CreateMap<IVUS_OCTBaseDto, OctExportDto>(); CreateMap<IVUS_OCTBaseDto, OctExportDto>();
CreateMap<FileUploadRecord, FileUploadRecordView>()
.ForMember(d => d.SubjectCode, u => u.MapFrom(s => s.Subject.Code))
.ForMember(d => d.VisitName, u => u.MapFrom(s => s.SubjectVisit.VisitName));
CreateMap<FileUploadRecord, FileUploadRecordAddOrEdit>().ReverseMap();
CreateMap<UploadFileSyncRecord, UploadFileSyncRecordView>()
.ForMember(d => d.FileName, u => u.MapFrom(s => s.FileUploadRecord.FileName))
.ForMember(d => d.FileType, u => u.MapFrom(s => s.FileUploadRecord.FileType))
.ForMember(d => d.Path, u => u.MapFrom(s => s.FileUploadRecord.Path))
.ForMember(d => d.StudyCode, u => u.MapFrom(s => s.FileUploadRecord.StudyCode))
.ForMember(d => d.SubjectCode, u => u.MapFrom(s => s.FileUploadRecord.Subject.Code))
.ForMember(d => d.VisitName, u => u.MapFrom(s => s.FileUploadRecord.SubjectVisit.VisitName));
} }
} }
@@ -149,7 +149,7 @@ namespace IRaCIS.Application.Contracts
public bool? AcceptingNewTrial { get; set; }//是否接受新的项目 public bool? AcceptingNewTrial { get; set; }//是否接受新的项目
public bool? ActivelyReading { get; set; }// 是否接受新的读片任务 public bool? ActivelyReading { get; set; }// 是否接受新的读片任务
public DoctorNation? Nation { get; set; }// 0-中国医生,2-美国医生,3-全部 public int? Nation { get; set; }// 0-中国医生,2-美国医生,3-全部
} }
/// <summary> /// <summary>
@@ -183,7 +183,6 @@ namespace IRaCIS.Application.Contracts
public class SelectionReviewerDTO : DoctorDTO public class SelectionReviewerDTO : DoctorDTO
{ {
public bool IsVacation { get; set; }
public int DoctorTrialState { get; set; } public int DoctorTrialState { get; set; }
public bool IsEnroll { get; set; } = false; public bool IsEnroll { get; set; } = false;
@@ -133,29 +133,6 @@ namespace IRaCIS.Core.Application.Service
} }
/// <summary>
/// 维护 医生基本信息 Code
/// </summary>
/// <returns></returns>
public async Task ServiceDoctorBasicInfo()
{
var doctorList = await _doctorRepository.Where(t => t.Code == 0 && t.TrialId == null).ToListAsync();
var code = await _doctorRepository.Select(t => t.Code).DefaultIfEmpty().MaxAsync();
foreach (var doctorItem in doctorList)
{
code++;
doctorItem.Code = code;
doctorItem.ReviewerCode = AppSettings.GetCodeStr(doctorItem.Code, nameof(Doctor));
await _doctorRepository.BatchUpdateNoTrackingAsync(x => x.Id == doctorItem.Id, x => new Doctor()
{
Code = doctorItem.Code,
ReviewerCode = doctorItem.ReviewerCode
});
}
}
/// <summary> /// <summary>
/// 新增修改 医生基本信息和工作 /// 新增修改 医生基本信息和工作
/// </summary> /// </summary>
@@ -234,7 +211,6 @@ namespace IRaCIS.Core.Application.Service
//重新插入新的 Title记录 //重新插入新的 Title记录
updateModel.TitleIds.ForEach(titleId => adddata.Add(new DoctorDictionary() { DoctorId = updateModel.Id.Value, KeyName = StaticData.Title, DictionaryId = titleId })); updateModel.TitleIds.ForEach(titleId => adddata.Add(new DoctorDictionary() { DoctorId = updateModel.Id.Value, KeyName = StaticData.Title, DictionaryId = titleId }));
indto.ReviewerCode = doctor.ReviewerCode;
await _doctorDictionaryRepository.AddRangeAsync(adddata); await _doctorDictionaryRepository.AddRangeAsync(adddata);
_mapper.Map(indto, doctor); _mapper.Map(indto, doctor);
@@ -131,7 +131,6 @@ namespace IRaCIS.Core.Application.Service
#region #region
CreateMap<Doctor, SelectionReviewerDTO>() CreateMap<Doctor, SelectionReviewerDTO>()
.ForMember(d => d.IsVacation, u => u.MapFrom(t => t.VacationList.Any(x=>x.StartDate<DateTime.Now&&x.EndDate>DateTime.Now)))
.ForMember(d => d.DoctorUserName, u => u.MapFrom(t => t.UserRole.IdentityUser.UserName)); .ForMember(d => d.DoctorUserName, u => u.MapFrom(t => t.UserRole.IdentityUser.UserName));
@@ -97,7 +97,6 @@ public class AuditDocumentService(IRepository<AuditDocument> _auditDocumentRepos
.WhereIf(inQuery.IdentityUserName.IsNotNullOrEmpty(), t => t.AuditRecordIdentityUserList.Any(c => c.IdentityUser.UserName.Contains(inQuery.IdentityUserName) || c.IdentityUser.FullName.Contains(inQuery.IdentityUserName))) .WhereIf(inQuery.IdentityUserName.IsNotNullOrEmpty(), t => t.AuditRecordIdentityUserList.Any(c => c.IdentityUser.UserName.Contains(inQuery.IdentityUserName) || c.IdentityUser.FullName.Contains(inQuery.IdentityUserName)))
.WhereIf(inQuery.CompanyName.IsNotNullOrEmpty(), t => t.CompanyName.Contains(inQuery.CompanyName)) .WhereIf(inQuery.CompanyName.IsNotNullOrEmpty(), t => t.CompanyName.Contains(inQuery.CompanyName))
.WhereIf(inQuery.AuditContent.IsNotNullOrEmpty(), t => t.AuditContent.Contains(inQuery.AuditContent)) .WhereIf(inQuery.AuditContent.IsNotNullOrEmpty(), t => t.AuditContent.Contains(inQuery.AuditContent))
.WhereIf(inQuery.IsViewTrainingRecord != null, t => t.IsViewTrainingRecord == inQuery.IsViewTrainingRecord)
.ProjectTo<AuditRecordView>(_mapper.ConfigurationProvider); .ProjectTo<AuditRecordView>(_mapper.ConfigurationProvider);
var pageList = await auditRecordQueryable.ToPagedListAsync(inQuery); var pageList = await auditRecordQueryable.ToPagedListAsync(inQuery);
@@ -668,7 +667,7 @@ public class AuditDocumentService(IRepository<AuditDocument> _auditDocumentRepos
#endregion #endregion
// 不能动到自己父类这个文件夹 // 不能动到自己父类这个文件夹
if (await _auditDocumentRepository.AnyAsync(x => x.ParentId == inDto.ParentId && inDto.Ids.Contains(x.Id))) if (await _auditDocumentRepository.AnyAsync(x => x.ParentId == inDto.ParentId && inDto.Ids.Contains(x.Id)))
{ {
throw new BusinessValidationFailedException(_localizer["AuditDocument_CanNotMoveToParent"]); throw new BusinessValidationFailedException(_localizer["AuditDocument_CanNotMoveToParent"]);
@@ -49,8 +49,6 @@ public class AuditRecordAddOrEdit
public AuditType AuditType { get; set; } public AuditType AuditType { get; set; }
public bool IsViewTrainingRecord { get; set; }
//public List<Guid> IdnetityUserIdList { get; set; } //public List<Guid> IdnetityUserIdList { get; set; }
} }
@@ -87,8 +85,6 @@ public class AuditRecordQuery : PageInput
public DateTime? EndCreateTime { get; set; } public DateTime? EndCreateTime { get; set; }
public string? IdentityUserName { get; set; } public string? IdentityUserName { get; set; }
public bool? IsViewTrainingRecord { get; set; }
} }
public class SetAuditRecordPermissionCommand public class SetAuditRecordPermissionCommand

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