Compare commits

..

484 Commits

Author SHA1 Message Date
hang 71e28f1a37 x 2023-11-03 15:44:39 +08:00
he 375e50bab7 Merge branch 'Test.Study' of http://192.168.3.69:2000/XCKJ/irc-netcore-api into Test.Study 2023-11-03 12:27:57 +08:00
he 39e2651edf 修改 2023-11-03 12:27:54 +08:00
hang b461216d87 修改配置文件 2023-11-03 12:15:13 +08:00
he dc6ae39130 修改 2023-11-03 09:55:05 +08:00
he 4b65f1e922 Merge branch 'Test.Study' of http://192.168.3.69:2000/XCKJ/irc-netcore-api into Test.Study 2023-11-03 09:51:44 +08:00
hang 99439a4809 容器真实ip 2023-11-02 15:53:09 +08:00
hang 419d5de7f1 admin 项目列表查看 2023-11-02 10:06:53 +08:00
hang a56ffaee58 admin 可以看到项目列表 2023-11-01 13:21:17 +08:00
hang e925d5bdb2 x 2023-11-01 11:18:53 +08:00
hang 9ec97f7d37 拷贝病灶修改 2023-10-31 15:21:35 +08:00
hang 3c532389d9 项目默认邮件配置 2023-10-30 16:20:33 +08:00
hang 1094f318f9 预留项目配置系统邮件配置 2023-10-30 16:13:26 +08:00
hang 56f261b8ce 拷贝表单屏蔽 2023-10-30 15:26:59 +08:00
hang 34b12b89e4 访视拷贝表单测试 2023-10-30 14:43:36 +08:00
hang bca8f2ddb9 数据库保存异常提示 2023-10-18 10:41:20 +08:00
hang 4023a59097 Merge branch 'Test.Study' of http://192.168.3.69:2000/XCKJ/irc-netcore-api into Test.Study 2023-10-16 09:21:59 +08:00
hang 8afcbf3d5e 打开分布式锁 2023-10-16 09:21:58 +08:00
hang 69c2cd339c 打开分布式锁 2023-10-16 09:20:44 +08:00
hang 4c7ce217da nuget 包确认 2023-10-13 23:38:04 +08:00
hang d66209452c 升级EF core 7 2023-10-13 22:48:00 +08:00
hang 77352245fc 测试 升级nuget包ok 2023-10-13 22:38:28 +08:00
hang 17ebdce96e 移除废弃的包 2023-10-13 22:17:11 +08:00
hang 3962178c68 efcore 6 升级到6版本最新,同时EFCore.BulkExtensions 也是6版本最新,但是projectable 不能升级 2023-10-13 21:28:10 +08:00
hang 7c6dcbbe38 回退包,查询临时方案 2023-10-13 18:02:33 +08:00
hang 75ed6375c2 修改nuget包位置 2023-10-13 11:53:52 +08:00
hang 290578d824 删除没用的文件 2023-10-13 11:34:55 +08:00
hang cd2f5aa1e1 Merge branch 'Test.Study' of http://192.168.3.69:2000/XCKJ/irc-netcore-api into Test.Study 2023-10-13 11:27:21 +08:00
hang 79d150829a EntityFrameworkCore.Projectables 包问题 导致更新出错 2023-10-13 11:21:21 +08:00
hang 270de86658 批量删除修改 2023-10-12 17:28:34 +08:00
hang edefc8f3e7 修改nuget 2023-10-12 15:16:52 +08:00
hang f1b3ec7d7c 更新nuget 2023-10-12 14:20:03 +08:00
hang 5171af975d nuget 包升级,清理没用的包,预备net8 2023-10-12 14:07:33 +08:00
hang 33b9113993 nuget 包升级,清理没用的包,预备net8 2023-10-12 14:03:30 +08:00
he a53626b66c 修改缓存 2023-10-12 13:56:39 +08:00
hang a27dcd6399 升级efcore 清理之前的写法 2023-10-12 13:31:52 +08:00
hang 14908c4eed 分布式锁 sqlserver 05 项目迁移修改 2023-10-12 09:34:09 +08:00
hang dfef88d663 分布式锁 sqlserver 测试 04 增加日志 2023-10-11 17:44:13 +08:00
hang 1afe020e83 分布式锁 sqlserver 测试 03 2023-10-11 17:38:34 +08:00
hang 7544e0c2e9 引入分布式组件 redis 2023-10-11 16:29:57 +08:00
hang 68d72fce53 引入 EasyCaching redis 准备工作完毕 2023-10-11 13:16:57 +08:00
hang 423f2fcb7c 修改PI 审核 2023-10-07 10:55:04 +08:00
hang 92b715de3a Merge branch 'Test.Study' of http://192.168.3.69:3000/XCKJ/irc-netcore-api into Test.Study 2023-10-07 10:53:28 +08:00
hang 912bc2edb8 修改pi 审核 2023-10-07 10:53:26 +08:00
hang a5f6c24aec 修改pi 审核 2023-10-07 10:49:20 +08:00
hang 8fc886a1db 修改异常提示 2023-09-28 11:37:49 +08:00
hang 5832610d3d x 2023-09-27 13:19:52 +08:00
hang e675542136 x 2023-09-27 13:19:30 +08:00
hang 8f7a3ab713 删除表 2023-09-27 11:42:10 +08:00
hang 02d98cfa75 x 2023-09-26 15:31:38 +08:00
hang 8bb1e8cf43 x 2023-09-26 14:20:35 +08:00
hang b3e37c7ea4 测试oss 改为1分钟 2023-09-26 14:13:07 +08:00
he 5bdb89df7d 维护临床数据 2023-09-26 11:25:21 +08:00
hang 10d50f3b5a 修改 2023-09-26 11:23:45 +08:00
hang bb6d36c8be 修改site 2023-09-25 17:25:59 +08:00
hang d91f4dc728 CRA 签名权限 以及Site 列表 2023-09-25 16:58:33 +08:00
hang f01e5335d6 增加地址 2023-09-22 15:32:26 +08:00
hang 3e8794b335 邮件发送 2023-09-22 15:23:42 +08:00
hang 79375f7106 Merge branch 'Test.Study' of http://192.168.3.69:3000/XCKJ/irc-netcore-api into Test.Study 2023-09-22 15:22:35 +08:00
hang 72b2047862 修改site 查询 2023-09-22 15:22:29 +08:00
he 8b124e1c60 关键序列修改 2023-09-21 13:14:02 +08:00
he 5b2449b607 预览新增ParentTriggerValueList
RelevanceValueList 字段
2023-09-21 11:43:22 +08:00
he 3f4a5e3040 Merge branch 'Test.Study' of http://192.168.3.69:3000/XCKJ/irc-netcore-api into Test.Study 2023-09-21 11:41:57 +08:00
hang 75a24b8aef Merge branch 'Test.Study' of http://192.168.3.69:3000/XCKJ/irc-netcore-api into Test.Study 2023-09-19 13:38:22 +08:00
hang 493c46ed15 修改数据库配置 2023-09-19 13:38:20 +08:00
he 5c2467bdbb Merge branch 'Test.Study' of http://192.168.3.69:3000/XCKJ/irc-netcore-api into Test.Study 2023-09-19 11:19:53 +08:00
he 0777ea830c 修改端口 2023-09-19 11:19:46 +08:00
hang b75223f8a0 Merge branch 'Test.Study' of http://192.168.3.69:3000/XCKJ/irc-netcore-api into Test.Study 2023-09-18 11:42:15 +08:00
hang 6a4e42f9dc 修改cookie 测试 2023-09-18 11:42:14 +08:00
he f7eec6327a 两个问题bug修改 2023-09-18 10:24:42 +08:00
he 1a4301233c 临床数据排序 自定义标准关键序列 2023-09-18 10:16:26 +08:00
he b6b5a5d0de Revert "添加问题分类字段"
This reverts commit da9a7affa9.
2023-09-14 15:45:32 +08:00
he 92c435cf23 Merge branch 'Test.Study' of http://192.168.3.69:3000/XCKJ/irc-netcore-api into Test.Study 2023-09-14 15:45:13 +08:00
hang 8697ec952c Merge branch 'Test.Study' of http://192.168.3.69:3000/XCKJ/irc-netcore-api into Test.Study 2023-09-14 14:17:16 +08:00
hang c6d3ae7c8b VisitBaseDataDes 遗漏 2023-09-14 14:17:13 +08:00
he 376c02f44c Merge branch 'Test.Study' of http://192.168.3.69:3000/XCKJ/irc-netcore-api into Test.Study 2023-09-14 14:02:34 +08:00
he da9a7affa9 添加问题分类字段 2023-09-14 14:02:23 +08:00
hang ac62f48a71 给默认值 2023-09-14 11:44:15 +08:00
hang 3d8cdd6ac6 Merge branch 'Test.Study' of http://192.168.3.69:3000/XCKJ/irc-netcore-api into Test.Study 2023-09-14 10:37:14 +08:00
hang 9380b40c28 增加访视基准日期说明 2023-09-14 10:37:11 +08:00
he cc823daee5 Merge branch 'Test.Study' of http://192.168.3.69:3000/XCKJ/irc-netcore-api into Test.Study 2023-09-13 14:08:24 +08:00
he b4594a4c47 获取下一个质控任务 2023-09-13 14:08:22 +08:00
hang c8660c0426 Merge branch 'Test.Study' of http://192.168.3.69:3000/XCKJ/irc-netcore-api into Test.Study 2023-09-12 10:36:19 +08:00
hang 375a724169 修改邮件 2023-09-12 10:36:06 +08:00
he 67375c3cbd 去掉doctor筛选 2023-09-07 17:59:52 +08:00
he 1645aaf69a Merge branch 'Test.Study' of http://192.168.3.69:3000/XCKJ/irc-netcore-api into Test.Study 2023-09-07 17:39:05 +08:00
hang 99b7d3a4f8 x 2023-09-07 14:32:21 +08:00
hang eac77e45d2 x 2023-09-07 14:22:09 +08:00
hang ebf05df568 修改下拉框 2023-09-07 14:21:38 +08:00
hang f464b25c2b 修改下拉框 2023-09-07 14:20:50 +08:00
hang b0dbe1442c 修改json 2023-09-07 10:27:51 +08:00
hang 5ae3220e67 修改稽查查询 2023-09-06 14:39:31 +08:00
hang f31a6408ac 修改已阅片任务 2023-09-06 11:06:49 +08:00
hang 9234e7ebe9 x 2023-09-05 15:52:49 +08:00
hang 609a7fb427 修改配置 2023-09-05 13:38:34 +08:00
hang a92bbbb275 x 2023-09-05 13:17:32 +08:00
hang c2bf16e1ff 稽查查询 2023-09-05 11:49:35 +08:00
hang dce2509613 oss 2023-09-05 11:11:57 +08:00
hang 1ac336220e oss 权限修改 2023-09-05 10:45:01 +08:00
hang a47f2fd496 x 2023-09-04 17:25:42 +08:00
hang 45c0704b40 修改邮件发送逻辑 2023-09-04 17:23:03 +08:00
hang 97cf5ce93c 邮件用户名修改 2023-09-04 16:35:26 +08:00
hang 786367f9fe x 2023-09-04 16:31:17 +08:00
hang f8c9cb1564 x 2023-09-04 16:20:22 +08:00
hang ee11290037 Merge branch 'Test.Study' of http://192.168.3.69:3000/XCKJ/irc-netcore-api into Test.Study 2023-09-04 16:05:38 +08:00
hang 42f3f6fb67 修改PI 审核发送邮件 2023-09-04 16:05:35 +08:00
he 2a844b11bd Merge branch 'Test.Study' of http://192.168.3.69:3000/XCKJ/irc-netcore-api into Test.Study 2023-09-04 15:47:28 +08:00
hang 5579fe4e95 Merge branch 'Test.Study' of http://192.168.3.69:3000/XCKJ/irc-netcore-api into Test.Study 2023-09-04 09:21:36 +08:00
hang ee96435379 object 参数会必传 2023-09-04 09:21:34 +08:00
he 4fce96db0e 代码修改 2023-09-04 09:21:01 +08:00
he 14e7ecd171 Merge branch 'Test.Study' of http://192.168.3.69:3000/XCKJ/irc-netcore-api into Test.Study 2023-09-04 09:13:26 +08:00
hang ffeece79fe 修改中心查询 2023-09-01 17:36:04 +08:00
hang 5c86989dfa x 2023-09-01 17:18:28 +08:00
hang c6a8c4d24f Merge branch 'Test.Study' of http://192.168.3.69:3000/XCKJ/irc-netcore-api into Test.Study 2023-09-01 15:53:18 +08:00
hang 477691ec70 修改中心 2023-09-01 15:53:17 +08:00
he 5518609d54 Merge branch 'Test.Study' of http://192.168.3.69:3000/XCKJ/irc-netcore-api into Test.Study 2023-09-01 15:47:02 +08:00
he 9f706d3123 临床数据表格问题添加依赖父问题 2023-09-01 15:46:56 +08:00
hang 5956d32c36 修改中心 2023-09-01 15:45:20 +08:00
hang a60e00159d 邮件发送翻译 2023-09-01 15:16:25 +08:00
hang 47277086ac xx 2023-09-01 14:29:54 +08:00
hang c7571570e9 x 2023-09-01 14:23:16 +08:00
hang 83d6859347 修改问题 2023-09-01 14:19:39 +08:00
hang a42986dcab 修改 2023-09-01 14:10:45 +08:00
hang 13fbf36de0 修改查询 2023-09-01 14:00:54 +08:00
hang 4ba2aa4769 修改PI 审核 2023-09-01 11:54:52 +08:00
hang 6b1a6a81eb 修改邮件--034 2023-09-01 10:35:13 +08:00
hang 62f8d5823f 调研表 2023-08-31 17:00:01 +08:00
hang 5cbefec0b0 修改提示 2023-08-31 16:33:34 +08:00
hang b154540885 Merge branch 'Test.Study' of http://192.168.3.69:3000/XCKJ/irc-netcore-api into Test.Study 2023-08-31 16:20:50 +08:00
hang 9eda07b253 修改启动 2023-08-31 16:20:47 +08:00
he 2995d2273a Merge branch 'Test.Study' of http://192.168.3.69:3000/XCKJ/irc-netcore-api into Test.Study 2023-08-31 16:14:06 +08:00
he 8fffdb244d oos 上传 2023-08-31 16:14:00 +08:00
hang 657da0c554 Merge branch 'Test.Study' of http://192.168.3.69:3000/XCKJ/irc-netcore-api into Test.Study 2023-08-31 16:04:21 +08:00
hang 1912e1cfcd 一致性核查文件名修改 2023-08-31 16:04:19 +08:00
he 3313c0223d Merge branch 'Test.Study' of http://192.168.3.69:3000/XCKJ/irc-netcore-api into Test.Study 2023-08-31 15:46:31 +08:00
hang 3c9b87ba01 修改上传bug 2023-08-31 13:30:39 +08:00
hang 65b52a1836 修改稽查 2023-08-31 10:10:29 +08:00
hang 684dbbd8e1 修改日志查询 2023-08-31 10:10:22 +08:00
hang 5a3127bb8e x 2023-08-30 16:42:45 +08:00
hang 0b9cf600ba 加 看内存缓存数据接口 2023-08-30 16:34:24 +08:00
hang b2f4fa3fb9 修改邮件--033 2023-08-30 15:14:08 +08:00
hang ecae4f663a 修改邮件--032 2023-08-30 15:08:38 +08:00
he 200da3375b Merge branch 'Test.Study' of http://192.168.3.69:3000/XCKJ/irc-netcore-api into Test.Study 2023-08-30 14:45:30 +08:00
he a3cedaa7b8 OSS上传 2023-08-30 14:45:25 +08:00
hang 37fc48c64d Merge branch 'Test.Study' of http://192.168.3.69:3000/XCKJ/irc-netcore-api into Test.Study 2023-08-30 14:40:17 +08:00
hang 1e4ee0a5e9 修改邮件--031 2023-08-30 14:40:13 +08:00
he bc383d9b23 OOS上传 2023-08-30 11:11:22 +08:00
he 27634b5aac Merge branch 'Test.Study' of http://192.168.3.69:3000/XCKJ/irc-netcore-api into Test.Study 2023-08-30 11:10:47 +08:00
he 2d479a15ec OOS 上传 2023-08-30 11:10:37 +08:00
hang a719ca752f 调研表--30 2023-08-29 17:47:08 +08:00
hang 089e329e4c Merge branch 'Test.Study' of http://192.168.3.69:3000/XCKJ/irc-netcore-api into Test.Study 2023-08-29 17:33:49 +08:00
hang b84abaa0a9 修改医学审核 2023-08-29 17:33:46 +08:00
he 6f4d85ea2b Merge branch 'Test.Study' of http://192.168.3.69:3000/XCKJ/irc-netcore-api into Test.Study 2023-08-29 16:54:48 +08:00
he 0f47a14e69 国际化修改 2023-08-29 16:54:38 +08:00
hang 788c24a6f3 修改邮件发送和稽查 2023-08-29 14:31:11 +08:00
hang a6c0bc7a21 Merge branch 'Test.Study' of http://192.168.3.69:3000/XCKJ/irc-netcore-api into Test.Study 2023-08-28 18:05:43 +08:00
hang 661140ebf8 邮件发送逻辑修改--029 项目邮件配置增加是否启用 2023-08-28 18:05:40 +08:00
he 9692ae569e Merge branch 'Test.Study' of http://192.168.3.69:3000/XCKJ/irc-netcore-api into Test.Study 2023-08-28 14:09:10 +08:00
he f6d46bcf12 国际化修改 2023-08-28 14:09:08 +08:00
he 595635d93d 国际化修改 2023-08-28 14:08:23 +08:00
hang 89d92baf56 邮件发送逻辑修改--028 2023-08-25 19:21:43 +08:00
hang 995605a2b6 邮件修改 2023-08-25 18:59:11 +08:00
hang 749c123229 x 2023-08-25 18:50:05 +08:00
hang f24f60cdef 修改映射 2023-08-25 18:46:58 +08:00
hang 451ffec65b 邮件发送逻辑修改--028 2023-08-25 18:36:27 +08:00
hang 033ac555f2 邮件发送逻辑修改--027 2023-08-25 18:05:01 +08:00
hang 14b57769d3 中心调研--026 2023-08-25 17:27:20 +08:00
hang 7c5b1b6fd1 x 2023-08-25 17:26:58 +08:00
hang 14bc9f4429 修改邮件发送枚举--026 2023-08-25 17:16:09 +08:00
hang b72b13509b xx 2023-08-25 11:25:26 +08:00
hang daaf4540a4 修改邮件发送枚举--025 2023-08-25 10:28:43 +08:00
hang eee828ca46 hangfire授权--025 2023-08-25 09:56:23 +08:00
hang a084a6b49b 测试路径 2023-08-24 16:36:47 +08:00
hang 1676056e43 测试路径 2023-08-24 16:04:58 +08:00
hang a601cf3ef8 虚拟路径测试 2023-08-24 15:34:38 +08:00
hang 3a7ddab1f3 修改访问路径 2023-08-24 15:23:42 +08:00
hang d1634d0813 继续测试 2023-08-24 14:55:49 +08:00
hang 31ccf4cefa x 2023-08-24 14:44:26 +08:00
hang f528b7ff5e 访问路径测试 2023-08-24 14:38:31 +08:00
hang d48333e71e 项目邮件配置修改--024 2023-08-24 13:09:28 +08:00
hang 7c0c096be4 项目邮件配置修改--023 2023-08-24 11:38:51 +08:00
hang b23befe18c x 2023-08-24 11:38:37 +08:00
hang ea48a57f95 x 2023-08-23 18:20:26 +08:00
hang c7faf920db 项目邮件配置修改--022 2023-08-23 18:12:58 +08:00
hang 6f722a43f5 修改定时任务 2023-08-23 18:06:07 +08:00
hang cbe66ac397 项目邮件配置修改--021 2023-08-23 17:54:48 +08:00
hang 0006f12151 修改定时任务 2023-08-23 17:49:06 +08:00
hang 8a3db78cd0 项目邮件配置修改--020 2023-08-23 17:48:56 +08:00
hang 3dfe229024 项目邮件配置修改--019 2023-08-23 17:46:31 +08:00
hang a8e4d6285d 项目邮件配置修改--018 修改邮件查询条件 2023-08-23 10:48:41 +08:00
hang bfe82bd322 修改发送邮件的方式test 2023-08-23 10:46:02 +08:00
hang e0ebd083b9 临床数据质询 增加新的表和回复接口 2023-08-23 09:46:59 +08:00
hang 6c6b18818a 项目邮件配置修改--017 2023-08-23 09:46:29 +08:00
hang 4338481f9f 项目邮件配置修改--016 2023-08-22 17:12:41 +08:00
hang e05f43b0b8 项目邮件配置修改--015 2023-08-22 14:54:30 +08:00
hang a4e26f9895 项目邮件配置修改--014 2023-08-22 14:14:31 +08:00
hang bc1e39450d 项目邮件配置修改--013 2023-08-22 13:31:40 +08:00
hang 87aa2cd8da 项目邮件配置修改--012 2023-08-22 11:07:01 +08:00
hang 9fa9896f0a 项目邮件配置修改--011 2023-08-22 11:06:16 +08:00
hang c4c034f2bb 修改字典表bug 2023-08-22 10:38:23 +08:00
hang aaed933eb9 项目邮件配置修改--010 2023-08-22 10:36:08 +08:00
hang c7b541dec6 项目邮件配置修改--009 2023-08-21 17:30:08 +08:00
hang e24933f0dd 项目邮件配置修改--008 2023-08-21 17:18:17 +08:00
hang e46b51d6a1 项目邮件配置修改--007 2023-08-21 16:15:44 +08:00
hang f3f00e3d0f Merge branch 'Test.Study' of http://192.168.3.69:3000/XCKJ/irc-netcore-api into Test.Study 2023-08-21 15:47:04 +08:00
hang 5a71e3eb25 系统邮件配置预先修改-005 2023-08-21 15:47:02 +08:00
hang d9b108553a 系统邮件配置预先修改-004 2023-08-21 15:43:07 +08:00
hang b9a99cef90 修改名称 2023-08-21 15:41:42 +08:00
hang 2f366b55d9 系统邮件配置预先修改-003 2023-08-21 15:34:18 +08:00
hang 13263cf39c 系统邮件配置预先修改-002 2023-08-21 15:28:26 +08:00
hang e03e93b0f5 修改上传路径 2023-08-21 15:13:56 +08:00
hang d62d1f2047 修改上传 2023-08-21 15:04:42 +08:00
hang ca3f3b6f9d 系统邮件配置预先修改-001 2023-08-21 15:04:30 +08:00
hang 96e1a2c393 修改中心调研,引入hangfire 2023-08-18 17:55:43 +08:00
hang b6403580de 修改退出改为加入 2023-08-18 14:44:36 +08:00
hang 8c5cca7ba5 修改中心调研限制条件 2023-08-18 13:19:51 +08:00
hang 4200daa1b9 修改调研表 2023-08-18 10:27:49 +08:00
hang 1a5cfa8bbd 修改废除状态 2023-08-18 09:23:35 +08:00
hang c371c4b127 OK 2023-08-18 08:57:39 +08:00
hang bd8123af59 修改调研表限制 2023-08-18 08:56:57 +08:00
hang 00cb2fc67c 修改调研表限制 2023-08-18 08:56:44 +08:00
hang ea157cdb71 修改调研表查询和提交 2023-08-17 18:07:34 +08:00
hang aa343ade2e xx 2023-08-17 17:11:02 +08:00
hang 536e9ef262 修改更新后的状态 2023-08-17 16:44:55 +08:00
hang d200f12fa8 修改人员状态变更 2023-08-17 16:42:05 +08:00
hang c85bb7f22b 修改调研验证提示 2023-08-17 16:21:34 +08:00
hang d575d8ac73 修改中心调研拷贝数据逻辑错误 2023-08-17 15:59:11 +08:00
hang 585b665b53 继续test 2023-08-17 15:17:06 +08:00
hang 8ffe69d38e 修改服务器时间,自动发布测试 2023-08-17 15:03:40 +08:00
hang 5e743b162d 提交中心调研 2023-08-17 14:54:44 +08:00
hang b9ccf4bb12 中心调研临时提交 2023-08-17 11:18:01 +08:00
hang 4bc2c3107a 前端限制查询配置增加 2023-08-16 13:10:51 +08:00
hang 008f5203c0 外部用户修改 2023-08-15 16:40:36 +08:00
hang 5d2acf44c1 修改中心调研 isJoin 2023-08-15 15:25:18 +08:00
hang c04d3a3fe4 修改PI 审核 2023-08-14 18:24:45 +08:00
hang 33ef76ad98 返回字段控制界面tab显示与否 2023-08-14 17:48:44 +08:00
hang c77cf1bf1e 废除的也可以看到调研信息 2023-08-10 17:44:10 +08:00
hang f9274c8fda 修改锁定废除 2023-08-10 17:21:32 +08:00
hang 5ae02e0d1f 修改调研查询trialId 2023-08-10 16:56:22 +08:00
hang dcdf03ca8e x 2023-08-10 16:40:01 +08:00
hang 775927c484 修改 Test.Study 2023-08-10 13:54:36 +08:00
hang bf230924d7 显示指定缓存发布尝试 2023-08-10 11:52:33 +08:00
hang d81fee17ba bat 执行一部分 测试时间 2023-08-10 11:36:28 +08:00
hang 0a398bd712 增加Test.IRC发布配置 2023-08-10 10:56:39 +08:00
hang a7d41772f7 ps 等待执行结束测试 2023-08-09 18:18:40 +08:00
hang 08754b9a24 尝试call 2023-08-09 18:15:25 +08:00
hang 133120e8c0 修改自动发布配置文件 2023-08-09 18:05:48 +08:00
hang ce3bfaeb5c 修改中心调研 2023-08-09 17:18:21 +08:00
hang 7e518bf3ab 修改中心调研 2023-08-08 14:53:07 +08:00
hang 106cbfacce 修改 参数可传与否 2023-08-08 13:14:38 +08:00
hang eb58745f19 增加Uat环境 2023-08-08 09:27:35 +08:00
hang cf0fa32101 Merge branch '中心影像_Test环境' of http://192.168.1.2:8033/IRaCIS_Core_Api into 中心影像_Test环境 2023-08-07 17:28:57 +08:00
hang d8894e92c0 修改邮件模板 2023-08-07 17:28:21 +08:00
he fc800672e1 代码修改 2023-08-07 16:44:32 +08:00
he 462ef4671e Merge branch '中心影像_Test环境' of http://192.168.1.2:8033/IRaCIS_Core_Api into 中心影像_Test环境 2023-08-07 16:31:46 +08:00
hang 212701fa8c 修改阅片任务 2023-08-07 14:57:27 +08:00
hang 75ccbe9bfb 删除多余的配置文件 2023-08-07 14:06:49 +08:00
hang 56e3bf64f2 任务阅片修改 2023-08-07 12:22:17 +08:00
hang 228254db09 调研表修改 2023-08-07 12:22:03 +08:00
hang 711c3f3639 修改退回 2023-08-03 13:53:42 +08:00
hang 5aa4c81c0d Merge branch '中心影像_Test环境' of http://192.168.1.2:8033/IRaCIS_Core_Api into 中心影像_Test环境 2023-08-03 11:49:08 +08:00
hang db9ce69594 修改引用包 2023-08-03 11:48:59 +08:00
he 891044eaf7 S-102 自定义标记 2023-08-03 10:53:13 +08:00
he 2e84fb8063 S-101 自定义标记 2023-08-03 10:52:40 +08:00
he d0e6f06f38 S-100 国际化修改 2023-08-03 10:52:14 +08:00
he 32a7fc3239 Merge branch '中心影像_Test环境' of http://192.168.1.2:8033/IRaCIS_Core_Api into 中心影像_Test环境 2023-08-03 10:51:11 +08:00
hang 29e4df9792 修改任务生成 2023-08-03 10:19:33 +08:00
hang 447bc6cb13 修改查询 2023-08-03 09:38:21 +08:00
hang dbb23132b3 修改instanceId 2023-08-01 13:02:41 +08:00
hang 179c283d75 修改DTO 2023-07-31 17:04:01 +08:00
hang 427cb38f39 修改非dicom 上传 2023-07-31 13:20:04 +08:00
hang ca9193438b Merge branch '中心影像_Test环境' of http://192.168.1.2:8033/IRaCIS_Core_Api into 中心影像_Test环境 2023-07-31 09:25:32 +08:00
hang d3d991cb3a 修改领取逻辑 2023-07-31 09:24:42 +08:00
he d8ea93dcd6 提示语言修改 2023-07-28 13:32:43 +08:00
he 3a649cfcfc S-99 临床数据 2023-07-27 16:04:58 +08:00
he 446d47167b Merge branch '中心影像_Test环境' of http://192.168.1.2:8033/IRaCIS_Core_Api into 中心影像_Test环境 2023-07-27 15:41:10 +08:00
he 7b10424238 修改 2023-07-27 15:40:46 +08:00
hang 9bfca25044 修改退回逻辑 2023-07-27 09:50:44 +08:00
hang 2bde5d1309 暂存修改 2023-07-27 09:38:41 +08:00
hang 93646d8f58 Merge branch '中心影像_Test环境' of http://192.168.1.2:8033/IRaCIS_Core_Api into 中心影像_Test环境 2023-07-27 09:23:23 +08:00
hang 9162f6ec99 删除没用的数据 2023-07-27 09:23:13 +08:00
he e413191bf4 S-98 时间修改 2023-07-26 11:14:42 +08:00
he 36cb770a86 S-97 添加分组 2023-07-26 11:01:25 +08:00
he 7a383a3fa5 Merge branch '中心影像_Test环境' of http://192.168.1.2:8033/IRaCIS_Core_Api into 中心影像_Test环境 2023-07-26 10:50:35 +08:00
he c9342b6ad1 S-96 添加时间 2023-07-26 10:50:14 +08:00
hang aadbc191a8 Merge branch '中心影像_Test环境' of http://192.168.1.2:8033/IRaCIS_Core_Api into 中心影像_Test环境 2023-07-25 18:06:24 +08:00
hang 4c9b46a20c 序列bug 2023-07-25 18:06:16 +08:00
he 0bde983ae9 Merge branch '中心影像_Test环境' of http://192.168.1.2:8033/IRaCIS_Core_Api into 中心影像_Test环境 2023-07-25 10:38:59 +08:00
he f55e295c01 添加映射 2023-07-25 10:38:37 +08:00
hang e06f564905 修改退回重阅片 2023-07-24 17:20:31 +08:00
he 1a64de066e 修改映射关系 2023-07-24 17:19:42 +08:00
he 53251e53a0 删除不要的功能 2023-07-24 14:40:31 +08:00
he f0cde942fb Merge branch '中心影像_Test环境' of http://192.168.1.2:8033/IRaCIS_Core_Api into 中心影像_Test环境 2023-07-24 13:30:44 +08:00
hang 7c9a07f384 Merge branch '中心影像_Test环境' of http://192.168.1.2:8033/IRaCIS_Core_Api into 中心影像_Test环境 2023-07-24 10:52:07 +08:00
hang 96b9d264a9 hang-清除工作量脏数据 2023-07-24 10:51:55 +08:00
he 0f3802474d 阅片关联去掉DoctorId 2023-07-24 10:12:24 +08:00
he f4c8d6c999 S-95 2023-07-21 15:56:27 +08:00
he fd25c99609 S-94 2023-07-21 15:56:22 +08:00
he 79da2c0adc S-93 2023-07-21 15:56:16 +08:00
he 7ec682668c S-92 2023-07-21 15:56:10 +08:00
he 93df8b81b7 S-91 2023-07-21 15:56:03 +08:00
he a089807bf0 S-90 2023-07-21 15:55:58 +08:00
he f97956c181 S-90 2023-07-21 15:55:53 +08:00
he 8bc59c53b6 S-89 2023-07-21 15:55:48 +08:00
he a136ed3f87 S-88 2023-07-21 15:55:42 +08:00
he d903e76d7a S-87 2023-07-21 15:55:37 +08:00
he ac1c0e6baa S-86 2023-07-21 15:55:32 +08:00
he e57617c305 S-85 2023-07-21 15:55:27 +08:00
he 4d307bc6fa S-84 2023-07-21 15:55:20 +08:00
he f3f3eb463b S-83 2023-07-21 15:55:12 +08:00
he a8232341dd S-82 2023-07-21 15:55:04 +08:00
he 4e2b03000d S-81 2023-07-21 15:54:56 +08:00
he 4e777e05db S-80 2023-07-21 15:54:49 +08:00
he 1f7fe0d854 S-79 2023-07-21 15:54:43 +08:00
he a58d7c9314 S-78 2023-07-21 15:54:37 +08:00
he 22d66ce856 S-77 2023-07-21 15:54:31 +08:00
he 546d7e7280 S-76 2023-07-21 15:54:25 +08:00
he 9f2d241d87 S-75 2023-07-21 15:54:19 +08:00
he bcdd8b7e2b S-74 2023-07-21 15:54:12 +08:00
he 11ad8f8d5f S-73 2023-07-21 15:54:04 +08:00
he 24b2067f50 S-72 2023-07-21 15:53:58 +08:00
he f85c51f7de S-71 2023-07-21 15:53:51 +08:00
he 449098ad72 S-70 2023-07-21 15:53:46 +08:00
he d8c1f9ee6f S-69 2023-07-21 15:53:42 +08:00
he 05bc298cfc S-68 2023-07-21 15:53:36 +08:00
he d03c405d54 S-67 2023-07-21 15:53:29 +08:00
he be786c2cbe S-66 同步 2023-07-21 15:53:23 +08:00
he 3a32825642 S-65 确认就同步标准 2023-07-21 15:53:17 +08:00
he 72e6bcc297 S-64 2023-07-21 15:53:12 +08:00
he 586000a98d S-63 2023-07-21 15:53:06 +08:00
he eae14095c0 S-62 2023-07-21 15:53:01 +08:00
he f7027d3fe6 S-61 2023-07-21 15:52:55 +08:00
he ca0a29b033 S-60 VisitHelper 任务临床状态维护 2023-07-21 15:52:50 +08:00
he 9e695dcfe8 S-59 2023-07-21 15:52:44 +08:00
he 4a8fd956ff S-58 2023-07-21 15:52:39 +08:00
he 284495700a S-57 2023-07-21 15:52:31 +08:00
he 1ba147009b S-56 2023-07-21 15:52:24 +08:00
he 2c541e68e1 S-55 2023-07-21 15:52:19 +08:00
he 95b39ea5e7 S-54 2023-07-21 15:52:12 +08:00
he 44e109cefa S-53 2023-07-21 15:52:05 +08:00
he 48763989d9 S-52 2023-07-21 15:52:00 +08:00
he f88bbee37b S-51 2023-07-21 15:51:55 +08:00
he fa3e1a6c35 S-50 2023-07-21 15:51:49 +08:00
he 6a5ce9aaa6 S-50 2023-07-21 15:51:43 +08:00
he bd7f840536 S-49 任务的临床状态维护 2023-07-21 15:51:35 +08:00
he 53fab12535 S-48 2023-07-21 15:51:30 +08:00
he 77745cfae6 S-47 2023-07-21 15:51:25 +08:00
he 73cb05956f S-46 2023-07-21 15:51:17 +08:00
he b3386c20ab S-45 2023-07-21 15:51:10 +08:00
he 83f419bff1 S-44 2023-07-21 15:50:55 +08:00
he c6a4a2dd0b S-43 2023-07-21 15:50:48 +08:00
he de3c59b7f5 S-42 2023-07-21 15:50:42 +08:00
he 28c89ce51d S-41 2023-07-21 15:50:36 +08:00
he a6fcf3421c S-40 2023-07-21 15:50:31 +08:00
he 2fcb2493d1 S-39 2023-07-21 15:50:25 +08:00
he 64b2b7850f S-38 2023-07-21 15:50:18 +08:00
he 429b53feba S-37 2023-07-21 15:50:11 +08:00
he a7042ba8a6 S-36 2023-07-21 15:50:05 +08:00
he f4c0413360 S-35 2023-07-21 15:49:57 +08:00
he 216b14663c S-34 2023-07-21 15:49:51 +08:00
he 895bda2e3f S-33 2023-07-21 15:49:45 +08:00
he 459866e924 S-32 2023-07-21 15:49:38 +08:00
he bdada76518 S-31 2023-07-21 15:49:33 +08:00
he 53129d2fcb S-30 2023-07-21 15:49:28 +08:00
he f174a555dd S-29 2023-07-21 15:49:23 +08:00
he 21da76f493 S-28 2023-07-21 15:49:17 +08:00
he 4d74de93ba S-27 2023-07-21 15:49:12 +08:00
he 92780b7152 S-26 2023-07-21 15:49:07 +08:00
he 0d73d19e44 S-25 2023-07-21 15:49:01 +08:00
he 689511a130 S-24 2023-07-21 15:48:55 +08:00
he d22d4d01c5 S-23 2023-07-21 15:48:49 +08:00
he fe691c2460 S-22 2023-07-21 15:48:44 +08:00
he f0d3c787d9 S-21 2023-07-21 15:48:37 +08:00
he 6b0945dc6f S-20 2023-07-21 15:48:31 +08:00
he d84358db3a S-19 2023-07-21 15:48:25 +08:00
he 5bb90e6f15 S-18 2023-07-21 15:48:18 +08:00
he 0840ac2c8b S-17 2023-07-21 15:48:13 +08:00
he 44a153ebb0 S-16 2023-07-21 15:48:07 +08:00
he 2214bdc1ad S-15 2023-07-21 15:45:38 +08:00
he c958e49118 S_014 2023-07-21 15:45:08 +08:00
he f7ae01ddc1 S-13 2023-07-21 15:44:56 +08:00
he 71b68f3354 修改 2023-07-21 15:44:49 +08:00
he a7e2e56175 S-011 2023-07-21 15:44:42 +08:00
he 7f4e82c27c S-010 2023-07-21 15:44:32 +08:00
he 6e2013f0c7 S-009 2023-07-21 15:44:26 +08:00
he 438e0947ac S-008 2023-07-21 15:44:19 +08:00
he 2e2d912aeb S-007 2023-07-21 15:44:13 +08:00
he 44d0f67e18 S-006 临床数据 2023-07-21 15:44:06 +08:00
he 22225dd841 修改 2023-07-21 15:43:53 +08:00
he 650d1e9647 S-004 临床数据 2023-07-21 15:43:42 +08:00
he 9265147e12 S-003 临床数据 2023-07-21 15:43:34 +08:00
he 8358d67f3e S-002 临床数据修改 2023-07-21 15:43:16 +08:00
he 2930c2b640 S-01临床数据 2023-07-21 15:32:08 +08:00
he 42e64d77cb Merge branch '中心影像_Test环境' of http://192.168.1.2:8033/IRaCIS_Core_Api into 中心影像_Test环境 2023-07-21 15:30:36 +08:00
hang 440fc103b1 修改医学审核返回字段 2023-07-21 08:48:59 +08:00
hang 128e3c747e 修改角色 2023-07-20 14:30:18 +08:00
hang a9439758c9 修改查询 2023-07-20 14:01:19 +08:00
hang 1247baaa36 修改未读任务查询 2023-07-20 13:43:25 +08:00
hang 59239060a6 修改中心调研 2023-07-20 11:41:56 +08:00
hang 42eae556e8 修改中心调研 2023-07-20 09:25:09 +08:00
hang a61b20ace6 修改PI 审核空数组 2023-07-19 16:38:13 +08:00
hang 9425fc521e 自动发送邮件代码屏蔽 2023-07-18 14:23:55 +08:00
hang 47c629f9a1 hang- 修改CRC 看到的检查列表 2023-07-18 11:14:20 +08:00
hang 11a669bf99 国际化修改 2023-07-17 14:26:02 +08:00
hang 366aba68ce 前端限制 界面显示,加字段 2023-07-17 10:41:46 +08:00
hang 2f5a4d5515 修改查询 2023-07-17 10:17:47 +08:00
hang f7c41d1b9a 修改中心调研 2023-07-14 13:39:15 +08:00
hang fbd24fd059 修改中心调研 2023-07-14 12:43:43 +08:00
hang cc6994e238 修改中心调研 2023-07-14 11:29:30 +08:00
hang 96c07f86b5 发送邮件修改 2023-07-14 09:30:16 +08:00
hang e502721d13 发布修改 2023-07-13 16:06:31 +08:00
hang a3b52dc9ff hang-用户登录日志 -04 2023-07-13 14:07:13 +08:00
hang e07fe76e39 修改任务 2023-07-13 14:06:56 +08:00
hang a4ee90ca5c hang-用户登录日志 -03 2023-07-13 11:29:16 +08:00
hang c016187498 hang-用户登录日志 -02 2023-07-13 11:12:05 +08:00
hang b0d74d31de hang - 用户登录日志修改-01 2023-07-13 10:49:09 +08:00
hang a85255b5fd PI 审核 2023-07-13 09:34:23 +08:00
hang 888dfbcafb 修改解决方案依赖 2023-07-12 15:07:04 +08:00
hang 5de4745303 发送邮件 2023-07-12 14:03:57 +08:00
hang 6883e3b5fa 修改查询 2023-07-12 13:18:24 +08:00
hang ef15b96f15 修改查询列表 2023-07-12 11:41:58 +08:00
hang 03810e0093 国际化 2023-07-12 09:03:50 +08:00
hang bbbc9da76e 修改审核 2023-07-11 16:23:08 +08:00
hang 11b4cf0351 PI 审核接口提交 2023-07-11 11:06:16 +08:00
hang 6df64d68af 修改中心调研 2023-07-11 09:58:09 +08:00
hang 3925fe74af 修改中心调研 2023-07-07 17:59:22 +08:00
hang edb48d8364 修改查询 2023-07-07 17:16:56 +08:00
hang 1d6b67658e 国际化修改 2023-07-07 16:24:37 +08:00
hang bcd1476d2d 修改阅片 2023-07-07 16:18:11 +08:00
hang d2251e4cb2 修改重阅列表 2023-07-07 11:21:38 +08:00
hang bd275539d1 修改定时任务 2023-07-06 16:39:39 +08:00
hang 6042909915 修改默认值 2023-07-06 16:03:24 +08:00
hang 38fc05e0d9 定时任务 2023-07-06 15:10:50 +08:00
hang 3ddbd59995 修改中心调研 2023-07-06 10:23:19 +08:00
hang 0a2a5bdd7a 修改查询条件 2023-07-05 14:39:50 +08:00
hang 088aa83fea 重阅跟踪修改 2023-07-05 14:36:43 +08:00
hang 0797b8b262 中心调研两个版本修改 2023-07-05 11:43:32 +08:00
hang 54a9db2acb 修改调研下拉框 2023-07-05 11:40:04 +08:00
hang e9f88a9c92 修改中心调研 2023-07-05 11:35:31 +08:00
hang 311d651ba7 修改用户类型筛选 2023-07-05 11:24:49 +08:00
hang 7789252f76 修改用户过滤 2023-07-05 10:47:17 +08:00
hang c58e96bce7 修改用户登录日志 2023-07-05 10:35:08 +08:00
hang 2a0ee12780 修改过滤查询 2023-07-05 10:27:26 +08:00
hang e7bc9168ae TA筛选人员,PM 根据标准筛选人员 2023-07-05 10:00:09 +08:00
he 98e5cb4979 修改 2023-07-05 09:37:50 +08:00
hang 6fc6829bc8 hang-S-002增加枚举,修改密码,重置密码,记录日志 2023-07-05 09:37:42 +08:00
hang 516f0fa1b4 hang_S_1_可逆加密算法增加 2023-07-05 09:37:37 +08:00
hang 6f556c45df 用户日志修改 2023-07-04 17:02:51 +08:00
hang e237d58906 修改用户类型 2023-07-04 15:53:38 +08:00
hang cc9807846e Merge branch '中心影像_Test环境' of http://192.168.1.2:8033/IRaCIS_Core_Api into 中心影像_Test环境 2023-07-04 15:04:54 +08:00
hang df7f9dbaeb 编译错误 2023-07-04 15:04:50 +08:00
he dee350742f 修改 2023-07-04 14:35:12 +08:00
he 735f9a9534 修改 2023-07-04 14:35:08 +08:00
he 859f10ee1f 修改 2023-07-04 14:35:06 +08:00
he a002eeed25 修改 2023-07-04 14:35:04 +08:00
he c9c5e5658f 修改 2023-07-04 14:35:01 +08:00
he edfcb53491 修改 2023-07-04 14:34:58 +08:00
he d158c13c7c 修改 2023-07-04 14:34:52 +08:00
he f2dc43b47f 修改 2023-07-04 14:34:47 +08:00
he e7cb246639 修改 2023-07-04 14:34:32 +08:00
he 19d109b3bf 修改 2023-07-04 14:33:46 +08:00
he b2fc4b9886 修改 2023-07-04 14:33:31 +08:00
he e6dd0f0abe 修改 2023-07-04 14:33:27 +08:00
he 6f4186ae07 修改 2023-07-04 14:33:26 +08:00
hang 44a3d03974 修改接口名字 2023-07-04 14:11:50 +08:00
hang 649f6897a4 Merge branch '中心影像_Test环境' of http://192.168.1.2:8033/IRaCIS_Core_Api into 中心影像_Test环境 2023-07-04 14:04:56 +08:00
hang 9edc30cef0 修改限制配置时间 2023-07-04 14:03:06 +08:00
he b0aaa2b791 修改 2023-07-04 13:58:24 +08:00
hang 108a4ee2bf 修改用户类型角色 2023-07-04 13:57:05 +08:00
hang 1083b80849 测试环境修改配置环境,同步中心影像测试 2023-07-04 13:48:05 +08:00
hang a2ff420f89 医学审核修改,暂时不让报错,需要修改之前的接口,待定 2023-07-04 13:34:16 +08:00
hang 5d718f7621 医学审核修改,暂时不让报错,需要修改之前的接口,待定 2023-07-04 13:32:17 +08:00
hang 63096d5919 测试挑拣 2023-07-03 17:13:07 +08:00
hang b80f1794e2 修改配置文件 2023-07-03 17:05:40 +08:00
hang 166c64798e 暂时不让统计报错 2023-07-03 16:58:37 +08:00
hang 150b51b327 修改阅片单元配置 2023-07-03 15:16:52 +08:00
hang 878e9541da 角色列表查询增加展示具体的枚举值 2023-07-03 14:01:18 +08:00
hang 8b416b113c 修改冲突 2023-07-03 13:58:09 +08:00
hang b8dd85c88f 新增配置,加字段,修改查询、编辑接口 2023-07-03 13:57:30 +08:00
hang 7098b15d0d 新增配置,加字段,修改查询、编辑接口 2023-07-03 13:56:51 +08:00
hang 1bd796cd94 项目表增加字段 2023-07-03 13:32:32 +08:00
hang cd41f8f060 模拟提交2 2023-07-03 11:34:43 +08:00
hang 2e117d6641 去掉添加修改项目里面写死的权限控制 2023-07-03 11:21:59 +08:00
hang 70513ff8b8 中心影像首次提交 2023-07-03 11:03:09 +08:00
877 changed files with 44339 additions and 143618 deletions
+1 -6
View File
@@ -22,9 +22,4 @@
**/secrets.dev.yaml
**/values.dev.yaml
LICENSE
README.md
!**/.gitignore
!.git/HEAD
!.git/config
!.git/packed-refs
!.git/refs/heads/**
README.md
-9
View File
@@ -126,15 +126,6 @@ csharp_style_deconstructed_variable_declaration = true:suggestion
csharp_style_unused_value_assignment_preference = discard_variable:suggestion
csharp_style_unused_value_expression_statement_preference = discard_variable:silent
dotnet_diagnostic.CS1570.severity = none
dotnet_diagnostic.CS1587.severity = none
dotnet_diagnostic.CS8619.severity = none
dotnet_diagnostic.CS8620.severity = none
dotnet_diagnostic.CS8632.severity = none
dotnet_diagnostic.CS8604.severity = none
dotnet_diagnostic.CS1998.severity = none
[*.vb]
#### 命名样式 ####
+26 -4
View File
@@ -1,4 +1,26 @@
FROM registry.cn-shanghai.aliyuncs.com/extimaging/aspnetcore:v8.2
WORKDIR /app
COPY publish .
ENTRYPOINT ["dotnet", "IRaCIS.Core.API.dll"]
#See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging.
FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
WORKDIR /app
EXPOSE 80
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
WORKDIR /src
COPY ["IRaCIS.Core.API/IRaCIS.Core.API.csproj", "IRaCIS.Core.API/"]
COPY ["IRaCIS.Core.Application/IRaCIS.Core.Application.csproj", "IRaCIS.Core.Application/"]
COPY ["IRaCIS.Core.Infra.EFCore/IRaCIS.Core.Infra.EFCore.csproj", "IRaCIS.Core.Infra.EFCore/"]
COPY ["IRaCIS.Core.Domain/IRaCIS.Core.Domain.csproj", "IRaCIS.Core.Domain/"]
COPY ["IRaCIS.Core.Domain.Share/IRaCIS.Core.Domain.Share.csproj", "IRaCIS.Core.Domain.Share/"]
COPY ["IRaCIS.Core.Infrastructure/IRaCIS.Core.Infrastructure.csproj", "IRaCIS.Core.Infrastructure/"]
RUN dotnet restore "IRaCIS.Core.API/IRaCIS.Core.API.csproj"
COPY . .
WORKDIR "/src/IRaCIS.Core.API"
RUN dotnet build "IRaCIS.Core.API.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "IRaCIS.Core.API.csproj" -c Release -o /app/publish /p:UseAppHost=false
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "IRaCIS.Core.API.dll"]
@@ -1,37 +0,0 @@
using IRaCIS.Core.Infrastructure.Extention;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Localization;
using Newtonsoft.Json;
namespace IRaCIS.Core.SCP.Filter
{
public class ModelActionFilter : ActionFilterAttribute, IActionFilter
{
public IStringLocalizer _localizer;
public ModelActionFilter(IStringLocalizer localizer)
{
_localizer = localizer;
}
public override void OnActionExecuting(ActionExecutingContext context)
{
if (!context.ModelState.IsValid)
{
var validationErrors = context.ModelState
.Keys
.SelectMany(k => context.ModelState[k]!.Errors)
.Select(e => e.ErrorMessage)
.ToArray();
//---提供给接口的参数无效。
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["ModelAction_InvalidAPIParameter"] + JsonConvert.SerializeObject( validationErrors)));
}
}
}
}
@@ -1,28 +0,0 @@
using System;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.ModelBinding;
namespace IRaCIS.Core.SCP.Filter
{
#region snippet_DisableFormValueModelBindingAttribute
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class DisableFormValueModelBindingAttribute : Attribute, IResourceFilter
{
public void OnResourceExecuting(ResourceExecutingContext context)
{
var factories = context.ValueProviderFactories;
//factories.RemoveType<FormValueProviderFactory>();
factories.RemoveType<FormFileValueProviderFactory>();
factories.RemoveType<JQueryFormValueProviderFactory>();
context.HttpContext.Request.EnableBuffering();
}
public void OnResourceExecuted(ResourceExecutedContext context)
{
}
}
#endregion
}
@@ -1,61 +0,0 @@
using IRaCIS.Core.Infrastructure;
using IRaCIS.Core.Infrastructure.Extention;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
namespace IRaCIS.Core.SCP.Filter
{
public class ProjectExceptionFilter : Attribute, IExceptionFilter
{
private readonly ILogger<ProjectExceptionFilter> _logger;
public IStringLocalizer _localizer;
public ProjectExceptionFilter(IStringLocalizer localizer, ILogger<ProjectExceptionFilter> logger)
{
_logger = logger;
_localizer = localizer;
}
public void OnException(ExceptionContext context)
{
//context.ExceptionHandled;//记录当前这个异常是否已经被处理过了
if (!context.ExceptionHandled)
{
if (context.Exception.GetType().Name == "DbUpdateConcurrencyException")
{
//---并发更新,当前不允许该操作
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["ProjectException_ConcurrentUpdateNotAllowed"] + context.Exception.Message));
}
if (context.Exception.GetType() == typeof(BusinessValidationFailedException))
{
var error = context.Exception as BusinessValidationFailedException;
context.Result = new JsonResult(ResponseOutput.NotOk(context.Exception.Message, error!.Code));
}
else if(context.Exception.GetType() == typeof(QueryBusinessObjectNotExistException))
{
context.Result = new JsonResult(ResponseOutput.NotOk( context.Exception.Message, ApiResponseCodeEnum.DataNotExist));
}
else
{
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["Project_ExceptionContactDeveloper"] + (context.Exception.InnerException is null ? (context.Exception.Message /*+ context.Exception.StackTrace*/)
: (context.Exception.InnerException?.Message /*+ context.Exception.InnerException?.StackTrace*/)), ApiResponseCodeEnum.ProgramException));
}
_logger.LogError(context.Exception.InnerException is null ? (context.Exception.Message + context.Exception.StackTrace) : (context.Exception.InnerException?.Message + context.Exception.InnerException?.StackTrace));
}
else
{
//继续
}
context.ExceptionHandled = true;//标记当前异常已经被处理过了
}
}
}
@@ -1,120 +0,0 @@
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc;
using IRaCIS.Core.Domain.Share;
using IRaCIS.Core.Infrastructure.Extention;
namespace IRaCIS.Core.Application.Service.BusinessFilter
{
/// <summary>
/// 统一返回前端数据包装,之前在控制器包装,现在修改为动态Api 在ResultFilter这里包装,减少重复冗余代码
/// by zhouhang 2021.09.12 周末
/// </summary>
public class UnifiedApiResultFilter : Attribute, IAsyncResultFilter
{
/// <summary>
/// 异步版本
/// </summary>
/// <param name="context"></param>
/// <param name="next"></param>
/// <returns></returns>
public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next)
{
if (context.Result is ObjectResult objectResult)
{
var statusCode = objectResult.StatusCode ?? context.HttpContext.Response.StatusCode;
//是200 并且没有包装 那么包装结果
if (statusCode == 200 && !(objectResult.Value is IResponseOutput))
{
//if (objectResult.Value == null)
//{
// var apiResponse = ResponseOutput.DBNotExist();
// objectResult.Value = apiResponse;
// objectResult.DeclaredType = apiResponse.GetType();
//}
//else
//{
var type = objectResult.Value?.GetType();
if ( type!=null&& type.IsGenericType&&(type.GetGenericTypeDefinition()==typeof(ValueTuple<,>)|| type.GetGenericTypeDefinition()==typeof(Tuple<,>)))
{
//报错
//var tuple = (object, object))objectResult.Value;
//var (val1, val2) = ((dynamic, dynamic))objectResult.Value;
//var apiResponse = ResponseOutput.Ok(val1, val2);
//OK
var tuple = (dynamic)objectResult.Value;
var apiResponse = ResponseOutput.Ok(tuple.Item1, tuple.Item2);
objectResult.Value = apiResponse;
objectResult.DeclaredType = apiResponse.GetType();
}
else
{
var apiResponse = ResponseOutput.Ok(objectResult.Value);
objectResult.Value = apiResponse;
objectResult.DeclaredType = apiResponse.GetType();
}
//}
}
//如果不是200 是IResponseOutput 不处理
else if (statusCode != 200 && (objectResult.Value is IResponseOutput))
{
}
else if(statusCode != 200&&!(objectResult.Value is IResponseOutput))
{
//---程序错误,请联系开发人员。
var apiResponse = ResponseOutput.NotOk(StaticData.International("UnifiedAPI_ProgramError"));
objectResult.Value = apiResponse;
objectResult.DeclaredType = apiResponse.GetType();
}
}
await next.Invoke();
}
public static bool IsTupleType(Type type, bool checkBaseTypes = false)
{
if (type == null)
throw new ArgumentNullException(nameof(type));
if (type == typeof(Tuple))
return true;
while (type != null)
{
if (type.IsGenericType)
{
var genType = type.GetGenericTypeDefinition();
if (genType == typeof(Tuple<>)
|| genType == typeof(Tuple<,>)
|| genType == typeof(Tuple<,>))
return true;
}
if (!checkBaseTypes)
break;
type = type.BaseType;
}
return false;
}
}
}
-62
View File
@@ -1,62 +0,0 @@
using FellowOakDicom.Imaging;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Processing;
using System.Security.Cryptography;
using System.Text;
namespace IRaCIS.Core.Application.Helper;
public static class ImageHelper
{
// 采用ImageSharp组件 跨平台,不依赖windows 平台图像处理组件,这里大坑(net 6 下,之前由于Dicom处理最新组件 依赖了这个库的一个旧版本,导致缩略图bug,单独升级依赖组件才解决)
public static void ResizeSave(string filePath, string? fileStorePath)
{
fileStorePath = fileStorePath ?? filePath + ".preview.jpeg";
using (var image = SixLabors.ImageSharp.Image.Load(filePath))
{
image.Mutate(x => x.Resize(500, 500));
image.Save(fileStorePath);
}
}
public static Stream RenderPreviewJpeg(string filePath)
{
string jpegPath = filePath + ".preview.jpg";
if (!File.Exists(jpegPath))
{
using (Stream stream = new FileStream(jpegPath, FileMode.Create))
{
DicomImage image = new DicomImage(filePath);
var sharpimage = image.RenderImage().AsSharpImage();
sharpimage.Save(stream, new JpegEncoder());
}
}
return new FileStream(jpegPath, FileMode.Open);
}
public static void RemovePreviewJpeg(string filePath)
{
string jpegPath = filePath + ".preview.jpg";
if (File.Exists(jpegPath)) File.Delete(jpegPath);
}
}
@@ -1,57 +0,0 @@
using Autofac;
using IRaCIS.Core.Infra.EFCore;
using Microsoft.AspNetCore.Http;
using Panda.DynamicWebApi;
using System;
using System.Linq;
using System.Reflection;
using IRaCIS.Core.Domain.Models;
using IRaCIS.Core.Domain.Share;
using IRaCIS.Core.Application.Service;
using AutoMapper;
using IRaCIS.Core.SCP.Service;
namespace IRaCIS.Core.SCP
{
// ReSharper disable once IdentifierTypo
public class AutofacModuleSetup : Autofac.Module
{
protected override void Load(ContainerBuilder containerBuilder)
{
#region byzhouhang 20210917 Domain层 Infra.EFcore
containerBuilder.RegisterGeneric(typeof(Repository<>))
.As(typeof(IRepository<>)).InstancePerLifetimeScope();//注册泛型仓储
containerBuilder.RegisterType<Repository>().As<IRepository>().InstancePerLifetimeScope();
#endregion
#region autofac https://www.cnblogs.com/xwhqwer/p/15320838.html
//获取所有控制器类型并使用属性注入
containerBuilder.RegisterAssemblyTypes(typeof(BaseService).Assembly)
.Where(type => typeof(IDynamicWebApi).IsAssignableFrom(type))
.PropertiesAutowired();
#endregion
Assembly application = Assembly.LoadFrom(AppDomain.CurrentDomain.BaseDirectory + typeof(BaseService).Assembly.GetName().Name+".dll");
containerBuilder.RegisterAssemblyTypes(application).Where(t => t.FullName.Contains("Service"))
.PropertiesAutowired().AsImplementedInterfaces();
//containerBuilder.RegisterType<HttpContextAccessor>().As<IHttpContextAccessor>().SingleInstance();
//containerBuilder.RegisterType<UserInfo>().As<IUserInfo>().InstancePerLifetimeScope();
}
}
}
-64
View File
@@ -1,64 +0,0 @@
using EntityFramework.Exceptions.SqlServer;
using IRaCIS.Core.Domain.Share;
using IRaCIS.Core.Infra.EFCore;
using Medallion.Threading;
using Medallion.Threading.SqlServer;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace IRaCIS.Core.SCP
{
public static class EFSetup
{
public static void AddEFSetup( this IServiceCollection services, IConfiguration configuration)
{
services.AddHttpContextAccessor();
services.AddScoped<IUserInfo, UserInfo>();
services.AddScoped<ISaveChangesInterceptor, AuditEntityInterceptor>();
//这个注入没有成功--注入是没问题的,构造函数也只是支持参数就好,错在注入的地方不能写DbContext
//Web程序中通过重用池中DbContext实例可提高高并发场景下的吞吐量, 这在概念上类似于ADO.NET Provider原生的连接池操作方式,具有节省DbContext实例化成本的优点
services.AddDbContext<IRaCISDBContext>((sp, options) =>
{
// 在控制台
//public static readonly ILoggerFactory MyLoggerFactory = LoggerFactory.Create(builder => { builder.AddConsole(); });
var logFactory = LoggerFactory.Create(builder => { builder.AddDebug(); });
options.UseSqlServer(configuration.GetSection("ConnectionStrings:RemoteNew").Value,
contextOptionsBuilder => contextOptionsBuilder.EnableRetryOnFailure());
options.UseLoggerFactory(logFactory);
options.UseExceptionProcessor();
options.EnableSensitiveDataLogging();
options.AddInterceptors(new QueryWithNoLockDbCommandInterceptor());
options.AddInterceptors(sp.GetServices<ISaveChangesInterceptor>());
options.UseProjectables();
});
//// Register an additional context factory as a Scoped service, which gets a pooled context from the Singleton factory we registered above,
//services.AddScoped<IRaCISDBScopedFactory>();
//// Finally, arrange for a context to get injected from our Scoped factory:
//services.AddScoped(sp => sp.GetRequiredService<IRaCISDBScopedFactory>().CreateDbContext());
//注意区分 easy caching 也有 IDistributedLockProvider
services.AddSingleton<IDistributedLockProvider>(sp =>
{
//var connection = ConnectionMultiplexer.Connect(configuration["Redis:Configuration"]!);
return new SqlDistributedSynchronizationProvider(configuration.GetSection("ConnectionStrings:RemoteNew").Value);
});
}
}
}
@@ -1,59 +0,0 @@
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using System;
namespace IRaCIS.Core.SCP
{
public static class NewtonsoftJsonSetup
{
public static void AddNewtonsoftJsonSetup(this IMvcBuilder builder, IServiceCollection services)
{
services.AddHttpContextAccessor();
services.AddScoped<IOSSService,OSSService>();
builder.AddNewtonsoftJson(options =>
{
//options.SerializerSettings.PreserveReferencesHandling = PreserveReferencesHandling.Objects;
// 忽略循环引用
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
//options.SerializerSettings.TypeNameHandling = TypeNameHandling.All;
//处理返回给前端 可空类型 给出默认值 比如in? 为null 设置 默认值0
options.SerializerSettings.ContractResolver = new NullToEmptyStringResolver(); //new DefaultContractResolver();// new NullToEmptyStringResolver();
// 设置时间格式
options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss";
options.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.RoundtripKind;
//options.SerializerSettings.Converters.Add(new JSONCustomDateConverter()) ;
//options.SerializerSettings.Converters.Add(services.BuildServiceProvider().GetService<JSONTimeZoneConverter>());
})
.AddControllersAsServices()//动态webApi属性注入需要
.ConfigureApiBehaviorOptions(o =>
{
o.SuppressModelStateInvalidFilter = true; //自己写验证
});
Newtonsoft.Json.JsonSerializerSettings setting = new Newtonsoft.Json.JsonSerializerSettings();
JsonConvert.DefaultSettings = new Func<JsonSerializerSettings>(() =>
{
//日期类型默认格式化处理
setting.DateFormatString = "yyyy-MM-dd HH:mm:ss";
setting.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
return setting;
});
}
}
}
@@ -1,36 +0,0 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using System;
using System.Collections.Generic;
using System.Linq;
namespace IRaCIS.Core.SCP
{
public class NullToEmptyStringResolver : DefaultContractResolver
{
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
IList<JsonProperty> properties = base.CreateProperties(type, memberSerialization);
var list= type.GetProperties()
.Select(p =>
{
var jp = base.CreateProperty(p, memberSerialization);
jp.ValueProvider = new NullToEmptyStringValueProvider(p);
return jp;
}).ToList();
var uu = list.Select(t => t.PropertyName).ToList();
//获取复杂对象属性
properties = properties.TakeWhile(t => !uu.Contains(t.PropertyName)).ToList();
list.AddRange(properties);
return list;
}
}
}
@@ -1,42 +0,0 @@
using System;
using System.Reflection;
using Newtonsoft.Json.Serialization;
namespace IRaCIS.Core.SCP
{
public class NullToEmptyStringValueProvider : IValueProvider
{
PropertyInfo _MemberInfo;
public NullToEmptyStringValueProvider(PropertyInfo memberInfo)
{
_MemberInfo = memberInfo;
}
public object GetValue(object target)
{
object result = _MemberInfo.GetValue(target);
if (_MemberInfo.PropertyType == typeof(string) && result == null) result = "";
else if (_MemberInfo.PropertyType == typeof(String[]) && result == null) result = new string[] { };
//else if (_MemberInfo.PropertyType == typeof(Nullable<Int32>) && result == null) result = 0;
else if (_MemberInfo.PropertyType == typeof(Nullable<Decimal>) && result == null) result = 0.00M;
return result;
}
public void SetValue(object target, object value)
{
if(_MemberInfo.PropertyType == typeof(string))
{
//去掉前后空格
_MemberInfo.SetValue(target, value==null?string.Empty: value.ToString()==string.Empty? value:value.ToString().Trim());
}
else
{
_MemberInfo.SetValue(target, value);
}
}
}
}
-39
View File
@@ -1,39 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AlibabaCloud.SDK.Sts20150401" Version="1.1.4" />
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="10.0.0" />
<PackageReference Include="Aliyun.OSS.SDK.NetCore" Version="2.14.1" />
<PackageReference Include="AWSSDK.S3" Version="3.7.402.7" />
<PackageReference Include="AWSSDK.SecurityToken" Version="3.7.400.16" />
<PackageReference Include="DistributedLock.Core" Version="1.0.7" />
<PackageReference Include="DistributedLock.SqlServer" Version="1.0.5" />
<PackageReference Include="fo-dicom" Version="5.1.3" />
<PackageReference Include="fo-dicom.Codecs" Version="5.14.5" />
<PackageReference Include="fo-dicom.Imaging.ImageSharp" Version="5.1.3" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.8" />
<PackageReference Include="AutoMapper" Version="13.0.1" />
<PackageReference Include="Minio" Version="6.0.3" />
<PackageReference Include="My.Extensions.Localization.Json" Version="3.3.0">
<TreatAsUsed>true</TreatAsUsed>
</PackageReference>
<PackageReference Include="Panda.DynamicWebApi" Version="1.2.2" />
<PackageReference Include="Serilog.Enrichers.ClientInfo" Version="2.1.1" />
<PackageReference Include="Serilog.Extensions.Hosting" Version="8.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.7.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\IRaCIS.Core.Domain\IRaCIS.Core.Domain.csproj" />
<ProjectReference Include="..\IRaCIS.Core.Infra.EFCore\IRaCIS.Core.Infra.EFCore.csproj" />
</ItemGroup>
</Project>
-219
View File
@@ -1,219 +0,0 @@
using Autofac;
using Autofac.Extensions.DependencyInjection;
using AutoMapper.EquivalencyExpression;
using FellowOakDicom;
using FellowOakDicom.Imaging;
using FellowOakDicom.Imaging.NativeCodec;
using FellowOakDicom.Network;
using IRaCIS.Core.Infra.EFCore;
using IRaCIS.Core.SCP;
using IRaCIS.Core.SCP.Filter;
using IRaCIS.Core.SCP.Service;
using MassTransit;
using MassTransit.NewIdProviders;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.Extensions.DependencyInjection;
using Panda.DynamicWebApi;
using Serilog;
using Serilog.Events;
using System.Runtime.InteropServices;
//以配置文件为准,否则 从url中取环境值(服务以命令行传递参数启动,配置文件配置了就不需要传递环境参数)
var config = new ConfigurationBuilder()
.AddEnvironmentVariables()
.Build();
var enviromentName = config["ASPNETCORE_ENVIRONMENT"];
var builder = WebApplication.CreateBuilder(new WebApplicationOptions
{
EnvironmentName = enviromentName
});
#region windows
int urlsIndex = Array.FindIndex(args, arg => arg != null && arg.StartsWith("--port"));
if (urlsIndex > -1)
{
var port = args[urlsIndex].Substring("--port=".Length);
Console.WriteLine(port);
builder.WebHost.UseUrls($"http://0.0.0.0:{port}");
}
#endregion
#region
NewId.SetProcessIdProvider(new CurrentProcessIdProvider());
builder.Configuration.AddJsonFile("appsettings.json", false, true)
.AddJsonFile($"appsettings.{enviromentName}.json", false, true);
builder.Host
.UseServiceProviderFactory(new AutofacServiceProviderFactory())
.ConfigureContainer<ContainerBuilder>(containerBuilder =>
{
containerBuilder.RegisterModule<AutofacModuleSetup>();
})
.UseSerilog();
#endregion
#region
var _configuration = builder.Configuration;
//健康检查
builder.Services.AddHealthChecks();
//本地化
builder.Services.AddJsonLocalization(options => options.ResourcesPath = "Resources");
// 异常、参数统一验证过滤器、Json序列化配置、字符串参数绑型统一Trim()
builder.Services.AddControllers(options =>
{
options.Filters.Add<ModelActionFilter>();
options.Filters.Add<ProjectExceptionFilter>();
options.Filters.Add<UnitOfWorkFilter>();
})
.AddNewtonsoftJsonSetup(builder.Services); // NewtonsoftJson 序列化 处理
builder.Services.AddOptions().Configure<AliyunOSSOptions>(_configuration.GetSection("AliyunOSS"));
builder.Services.AddOptions().Configure<ObjectStoreServiceOptions>(_configuration.GetSection("ObjectStoreService"));
builder.Services.AddOptions().Configure<DicomSCPServiceOption>(_configuration.GetSection("DicomSCPServiceConfig"));
//动态WebApi + UnifiedApiResultFilter 省掉控制器代码
//动态webApi 目前存在的唯一小坑是生成api上服务上的动态代理AOP失效 间接掉用不影响
builder.Services
.AddDynamicWebApi(dynamicWebApiOption =>
{
//默认是 api
dynamicWebApiOption.DefaultApiPrefix = "";
//首字母小写
dynamicWebApiOption.GetRestFulActionName = (actionName) => char.ToLower(actionName[0]) + actionName.Substring(1);
//删除 Service后缀
dynamicWebApiOption.RemoveControllerPostfixes.Add("Service");
});
//AutoMapper
builder.Services.AddAutoMapper(automapper =>
{
automapper.AddCollectionMappers();
}, typeof(BaseService).Assembly);
//EF ORM QueryWithNoLock
builder.Services.AddEFSetup(_configuration);
builder.Services.AddMediator(cfg =>
{
});
//转发头设置 获取真实IP
builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders =
ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
});
//Dicom影像渲染图片 跨平台
//builder.Services.AddDicomSetup();
new DicomSetupBuilder()
.RegisterServices(s =>
s.AddFellowOakDicom()
.AddTranscoderManager<NativeTranscoderManager>()
//.AddTranscoderManager<FellowOakDicom.Imaging.NativeCodec.NativeTranscoderManager>()
.AddImageManager<ImageSharpImageManager>())
.SkipValidation()
.Build();
#endregion
// Add services to the container.
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// Configure the HTTP request pipeline.
//if (app.Environment.IsDevelopment())
//{
app.UseSwagger();
app.UseSwaggerUI();
//}
app.UseAuthorization();
app.MapControllers();
#region
//Log.Logger = new LoggerConfiguration()
// .MinimumLevel.Information()
// .MinimumLevel.Override("Microsoft", LogEventLevel.Information)
// // Filter out ASP.NET Core infrastructre logs that are Information and below 日志太多了 一个请求 记录好几条
// .MinimumLevel.Override("Microsoft.AspNetCore", LogEventLevel.Warning)
// .MinimumLevel.Override("Hangfire", LogEventLevel.Warning)
// .MinimumLevel.Override("System.Net.Http.HttpClient.HttpReports", LogEventLevel.Warning)
// .Enrich.WithClientIp()
// .Enrich.FromLogContext()
// //控制台 方便调试 问题 我们显示记录日志 时 获取上下文的ip 和用户名 用户类型
// .WriteTo.Console(restrictedToMinimumLevel: LogEventLevel.Warning,
// outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {SourceContext:l} || {Message} || {Exception} ||end {NewLine}")
// .WriteTo.File($"{AppContext.BaseDirectory}Serilogs/.log", rollingInterval: RollingInterval.Day,
// outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {SourceContext:l} || {Message} || {Exception} ||end {NewLine}")
// .CreateLogger();
Log.Logger = new LoggerConfiguration()
//.MinimumLevel.Information()
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
.WriteTo.Console()
.WriteTo.File($"{AppContext.BaseDirectory}Serilogs/.log", rollingInterval: RollingInterval.Day)
.CreateLogger();
#endregion
#region
Log.Logger.Warning($"当前环境:{enviromentName}");
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
Log.Logger.Warning($"当前部署平台环境:windows");
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
Log.Logger.Warning($"当前部署平台环境:linux");
}
else
{
Log.Logger.Warning($"当前部署平台环境:OSX or FreeBSD");
}
#endregion
var logger = app.Services.GetService<Microsoft.Extensions.Logging.ILogger<Program>>();
var server = DicomServerFactory.Create<CStoreSCPService>(_configuration.GetSection("DicomSCPServiceConfig").GetValue<int>("ServerPort"), userState: app.Services,logger: logger);
app.Run();
@@ -1,25 +0,0 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"profiles": {
"Test_IRC_SCP": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "swagger",
"applicationUrl": "http://localhost:6200",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Test_IRC_SCP"
}
},
"Uat_IRC_SCP": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "swagger",
"applicationUrl": "http://localhost:6200",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Uat_IRC_SCP"
}
}
}
}
-111
View File
@@ -1,111 +0,0 @@
using AutoMapper;
using IRaCIS.Core.Application.Service.BusinessFilter;
using IRaCIS.Core.Infra.EFCore;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Localization;
using Panda.DynamicWebApi;
using Panda.DynamicWebApi.Attributes;
using System.Diagnostics.CodeAnalysis;
using IRaCIS.Core.Domain.Share;
using IRaCIS.Core.Infrastructure.Extention;
using IRaCIS.Core.Domain.Models;
namespace IRaCIS.Core.SCP.Service
{
#pragma warning disable CS8618
#region
[Authorize, DynamicWebApi, UnifiedApiResultFilter]
public class BaseService : IBaseService, IDynamicWebApi
{
public IMapper _mapper { get; set; }
public IUserInfo _userInfo { get; set; }
public IStringLocalizer _localizer { get; set; }
public IWebHostEnvironment _hostEnvironment { get; set; }
public static IResponseOutput Null404NotFound<TEntity>(TEntity? businessObject) where TEntity : class
{
return new ResponseOutput<string>()
.NotOk($"The query object {typeof(TEntity).Name} does not exist , or was deleted by someone else, or an incorrect parameter query caused", code: ApiResponseCodeEnum.DataNotExist);
}
}
public interface IBaseService
{
[MemberNotNull(nameof(_mapper))]
public IMapper _mapper { get; set; }
[MemberNotNull(nameof(_userInfo))]
public IUserInfo _userInfo { get; set; }
[MemberNotNull(nameof(_localizer))]
public IStringLocalizer _localizer { get; set; }
[MemberNotNull(nameof(_hostEnvironment))]
public IWebHostEnvironment _hostEnvironment { get; set; }
}
#endregion
#region
public interface IBaseServiceTest<T> where T : Entity
{
[MemberNotNull(nameof(_mapper))]
public IMapper _mapper { get; set; }
[MemberNotNull(nameof(_userInfo))]
public IUserInfo _userInfo { get; set; }
[MemberNotNull(nameof(_localizer))]
public IStringLocalizer _localizer { get; set; }
}
[Authorize, DynamicWebApi, UnifiedApiResultFilter]
public class BaseServiceTest<T> : IBaseServiceTest<T>, IDynamicWebApi where T : Entity
{
public IMapper _mapper { get; set; }
public IUserInfo _userInfo { get; set; }
public IStringLocalizer _localizer { get; set; }
public static IResponseOutput Null404NotFound<TEntity>(TEntity? businessObject) where TEntity : class
{
return new ResponseOutput<string>()
.NotOk($"The query object {typeof(TEntity).Name} does not exist , or was deleted by someone else, or an incorrect parameter query caused", code: ApiResponseCodeEnum.DataNotExist);
}
}
#endregion
}
-377
View File
@@ -1,377 +0,0 @@
using FellowOakDicom.Network;
using FellowOakDicom;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using IRaCIS.Core.SCP.Service;
using IRaCIS.Core.Domain.Models;
using IRaCIS.Core.Infra.EFCore;
using Medallion.Threading;
using IRaCIS.Core.Domain.Share;
using Serilog;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion.Internal;
using Microsoft.Extensions.Options;
using System.Data;
using FellowOakDicom.Imaging;
using SharpCompress.Common;
using SixLabors.ImageSharp.Formats.Jpeg;
using IRaCIS.Core.Infrastructure;
namespace IRaCIS.Core.SCP.Service
{
public class DicomSCPServiceOption
{
public List<string> CalledAEList { get; set; }
public string ServerPort { get; set; }
}
public class CStoreSCPService : DicomService, IDicomServiceProvider, IDicomCStoreProvider, IDicomCEchoProvider
{
private IServiceProvider _serviceProvider { get; set; }
private List<Guid> _SCPStudyIdList { get; set; } = new List<Guid>();
private SCPImageUpload _upload { get; set; }
private Guid _trialId { get; set; }
private Guid _trialSiteId { get; set; }
private static readonly DicomTransferSyntax[] _acceptedTransferSyntaxes = new DicomTransferSyntax[]
{
DicomTransferSyntax.ExplicitVRLittleEndian,
DicomTransferSyntax.ExplicitVRBigEndian,
DicomTransferSyntax.ImplicitVRLittleEndian
};
private static readonly DicomTransferSyntax[] _acceptedImageTransferSyntaxes = new DicomTransferSyntax[]
{
// Lossless
DicomTransferSyntax.JPEGLSLossless, //1.2.840.10008.1.2.4.80
DicomTransferSyntax.JPEG2000Lossless, //1.2.840.10008.1.2.4.90
DicomTransferSyntax.JPEGProcess14SV1, //1.2.840.10008.1.2.4.70
DicomTransferSyntax.JPEGProcess14, //1.2.840.10008.1.2.4.57 JPEG Lossless, Non-Hierarchical (Process 14)
DicomTransferSyntax.RLELossless, //1.2.840.10008.1.2.5
// Lossy
DicomTransferSyntax.JPEGLSNearLossless,//1.2.840.10008.1.2.4.81"
DicomTransferSyntax.JPEG2000Lossy, //1.2.840.10008.1.2.4.91
DicomTransferSyntax.JPEGProcess1, //1.2.840.10008.1.2.4.50
DicomTransferSyntax.JPEGProcess2_4, //1.2.840.10008.1.2.4.51
// Uncompressed
DicomTransferSyntax.ExplicitVRLittleEndian, //1.2.840.10008.1.2.1
DicomTransferSyntax.ExplicitVRBigEndian, //1.2.840.10008.1.2.2
DicomTransferSyntax.ImplicitVRLittleEndian //1.2.840.10008.1.2
};
public CStoreSCPService(INetworkStream stream, Encoding fallbackEncoding, Microsoft.Extensions.Logging.ILogger log, DicomServiceDependencies dependencies)
: base(stream, fallbackEncoding, log, dependencies)
{
}
public Task OnReceiveAssociationRequestAsync(DicomAssociation association)
{
_upload = new SCPImageUpload() { StartTime = DateTime.Now, CallingAE = association.CallingAE, CalledAE = association.CalledAE, CallingAEIP = association.RemoteHost };
Log.Logger.Warning($"接收到来自{association.CallingAE}的连接");
_serviceProvider = (IServiceProvider)this.UserState;
var _trialDicomAERepository = _serviceProvider.GetService<IRepository<TrialDicomAE>>();
var trialDicomAEList = _trialDicomAERepository.Select(t => new { t.CalledAE, t.TrialId }).ToList();
var trialCalledAEList = trialDicomAEList.Select(t => t.CalledAE).ToList();
Log.Logger.Information("当前系统配置:", string.Join('|', trialDicomAEList));
var findCalledAE = trialDicomAEList.Where(t => t.CalledAE == association.CalledAE).FirstOrDefault();
var isCanReceiveIamge = false;
if (findCalledAE != null)
{
_trialId = findCalledAE.TrialId;
var _trialSiteDicomAERepository = _serviceProvider.GetService<IRepository<TrialSiteDicomAE>>();
var findTrialSiteAE = _trialSiteDicomAERepository.Where(t => t.CallingAE == association.CallingAE && t.TrialId==_trialId).FirstOrDefault();
if (findTrialSiteAE != null)
{
_trialSiteId = findTrialSiteAE.TrialSiteId;
isCanReceiveIamge = true;
}
}
if (association.CallingAE == "test-callingAE")
{
isCanReceiveIamge = true;
}
if (!trialCalledAEList.Contains(association.CalledAE) || isCanReceiveIamge == false)
{
Log.Logger.Warning($"拒绝CallingAE:{association.CallingAE} CalledAE:{association.CalledAE}的连接");
return SendAssociationRejectAsync(
DicomRejectResult.Permanent,
DicomRejectSource.ServiceUser,
DicomRejectReason.CalledAENotRecognized);
}
foreach (var pc in association.PresentationContexts)
{
if (pc.AbstractSyntax == DicomUID.Verification)
{
pc.AcceptTransferSyntaxes(_acceptedTransferSyntaxes);
}
else if (pc.AbstractSyntax.StorageCategory != DicomStorageCategory.None)
{
pc.AcceptTransferSyntaxes(_acceptedImageTransferSyntaxes);
}
}
return SendAssociationAcceptAsync(association);
}
public async Task OnReceiveAssociationReleaseRequestAsync()
{
await DataMaintenanceAsaync();
//记录监控
var _SCPImageUploadRepository = _serviceProvider.GetService<IRepository<SCPImageUpload>>();
_upload.EndTime = DateTime.Now;
_upload.StudyCount = _SCPStudyIdList.Count;
_upload.TrialId = _trialId;
_upload.TrialSiteId = _trialSiteId;
await _SCPImageUploadRepository.AddAsync(_upload, true);
var _studyRepository = _serviceProvider.GetService<IRepository<SCPStudy>>();
//将检查设置为传输结束
await _studyRepository.BatchUpdateNoTrackingAsync(t => _SCPStudyIdList.Contains(t.Id), u => new SCPStudy() { IsUploadFinished = true });
await _studyRepository.SaveChangesAndClearAllTrackingAsync();
await SendAssociationReleaseResponseAsync();
}
private async Task DataMaintenanceAsaync()
{
Log.Logger.Warning($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE}传输结束:开始维护数据,处理检查Modality");
//处理检查Modality
var _dictionaryRepository = _serviceProvider.GetService<IRepository<Dictionary>>();
var _seriesRepository = _serviceProvider.GetService<IRepository<SCPSeries>>();
var _studyRepository = _serviceProvider.GetService<IRepository<SCPStudy>>();
var dicModalityList = _dictionaryRepository.Where(t => t.Code == "Modality").SelectMany(t => t.ChildList.Select(c => c.Value)).ToList();
var seriesModalityList = _seriesRepository.Where(t => _SCPStudyIdList.Contains(t.StudyId)).Select(t => new { SCPStudyId = t.StudyId, t.Modality }).ToList();
foreach (var g in seriesModalityList.GroupBy(t => t.SCPStudyId))
{
var modality = string.Join('、', g.Select(t => t.Modality).Distinct().ToList());
//特殊逻辑
var modalityForEdit = dicModalityList.Contains(modality) ? modality : String.Empty;
if (modality == "MR")
{
modalityForEdit = "MRI";
}
if (modality == "PT")
{
modalityForEdit = "PET";
}
if (modality == "PT、CT" || modality == "CT、PT")
{
modalityForEdit = "PET-CT";
}
await _studyRepository.BatchUpdateNoTrackingAsync(t => t.Id == g.Key, u => new SCPStudy() { Modalities = modality, ModalityForEdit = modalityForEdit });
}
Log.Logger.Warning($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE}维护数据结束");
}
public void OnReceiveAbort(DicomAbortSource source, DicomAbortReason reason)
{
Log.Logger.Warning($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE}接收中断,中断原因:{source.ToString() + reason.ToString()}");
/* nothing to do here */
}
public async void OnConnectionClosed(Exception exception)
{
/* nothing to do here */
//奇怪的bug 上传的时候,用王捷修改的影像,会关闭,重新连接,导致检查id 丢失,然后状态不一致
if (exception == null)
{
//var _studyRepository = _serviceProvider.GetService<IRepository<SCPStudy>>();
////将检查设置为传输结束
//await _studyRepository.BatchUpdateNoTrackingAsync(t => _SCPStudyIdList.Contains(t.Id), u => new SCPStudy() { IsUploadFinished = true });
//await _studyRepository.SaveChangesAndClearAllTrackingAsync();
}
Log.Logger.Warning($"连接关闭 {exception?.Message} {exception?.InnerException?.Message}");
}
public async Task<DicomCStoreResponse> OnCStoreRequestAsync(DicomCStoreRequest request)
{
string studyInstanceUid = request.Dataset.GetString(DicomTag.StudyInstanceUID);
string seriesInstanceUid = request.Dataset.GetString(DicomTag.SeriesInstanceUID);
string sopInstanceUid = request.Dataset.GetString(DicomTag.SOPInstanceUID);
//Guid studyId = IdentifierHelper.CreateGuid(studyInstanceUid, trialId.ToString());
Guid seriesId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid, _trialId.ToString());
Guid instanceId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid, sopInstanceUid, _trialId.ToString());
var ossService = _serviceProvider.GetService<IOSSService>();
var dicomArchiveService = _serviceProvider.GetService<IDicomArchiveService>();
var _seriesRepository = _serviceProvider.GetService<IRepository<SCPSeries>>();
var _distributedLockProvider = _serviceProvider.GetService<IDistributedLockProvider>();
var storeRelativePath = string.Empty;
var ossFolderPath = $"{_trialId}/Image/PACS/{_trialSiteId}{studyInstanceUid}";
long fileSize = 0;
try
{
using (MemoryStream ms = new MemoryStream())
{
await request.File.SaveAsync(ms);
//irc 从路径最后一截取Guid
storeRelativePath = await ossService.UploadToOSSAsync(ms, ossFolderPath, instanceId.ToString(), false);
fileSize = ms.Length;
}
Log.Logger.Information($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE} {request.SOPInstanceUID} 上传完成 ");
}
catch (Exception ec)
{
Log.Logger.Warning($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE} 上传异常 {ec.Message}");
}
var @lock = _distributedLockProvider.CreateLock($"{studyInstanceUid}");
using (await @lock.AcquireAsync())
{
try
{
var scpStudyId = await dicomArchiveService.ArchiveDicomFileAsync(request.Dataset, _trialId, _trialSiteId, storeRelativePath, Association.CallingAE, Association.CalledAE,fileSize);
if (!_SCPStudyIdList.Contains(scpStudyId))
{
_SCPStudyIdList.Add(scpStudyId);
}
var series = await _seriesRepository.FindAsync(seriesId);
//没有缩略图
if (series != null && string.IsNullOrEmpty(series.ImageResizePath))
{
// 生成缩略图
using (var memoryStream = new MemoryStream())
{
DicomImage image = new DicomImage(request.Dataset);
var sharpimage = image.RenderImage().AsSharpImage();
sharpimage.Save(memoryStream, new JpegEncoder());
// 上传缩略图到 OSS
var seriesPath = await ossService.UploadToOSSAsync(memoryStream, ossFolderPath, seriesId.ToString() + ".preview.jpg", false);
Console.WriteLine(seriesPath + " Id: " + seriesId);
series.ImageResizePath = seriesPath;
}
}
await _seriesRepository.SaveChangesAsync();
}
catch (Exception ex)
{
Log.Logger.Warning($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE} 传输处理异常:{ex.ToString()}");
}
}
//监控信息设置
_upload.FileCount++;
_upload.FileSize = _upload.FileSize + fileSize;
return new DicomCStoreResponse(request, DicomStatus.Success);
}
public Task OnCStoreRequestExceptionAsync(string tempFileName, Exception e)
{
// let library handle logging and error response
return Task.CompletedTask;
}
public Task<DicomCEchoResponse> OnCEchoRequestAsync(DicomCEchoRequest request)
{
return Task.FromResult(new DicomCEchoResponse(request, DicomStatus.Success));
}
}
}
-356
View File
@@ -1,356 +0,0 @@
using IRaCIS.Core.Domain.Share;
using System.Text;
using Microsoft.AspNetCore.Hosting;
using IRaCIS.Core.Infrastructure;
using Medallion.Threading;
using FellowOakDicom;
using FellowOakDicom.Imaging.Codec;
using System.Data;
using IRaCIS.Core.Domain.Models;
using FellowOakDicom.Network;
using IRaCIS.Core.SCP.Service;
using IRaCIS.Core.Infra.EFCore;
using MassTransit;
using System.Runtime.Intrinsics.X86;
using Serilog.Sinks.File;
namespace IRaCIS.Core.SCP.Service
{
public class DicomArchiveService : BaseService, IDicomArchiveService
{
private readonly IRepository<SCPPatient> _patientRepository;
private readonly IRepository<SCPStudy> _studyRepository;
private readonly IRepository<SCPSeries> _seriesRepository;
private readonly IRepository<SCPInstance> _instanceRepository;
private readonly IRepository<Dictionary> _dictionaryRepository;
private readonly IDistributedLockProvider _distributedLockProvider;
private List<Guid> _instanceIdList = new List<Guid>();
public DicomArchiveService(IRepository<SCPPatient> patientRepository, IRepository<SCPStudy> studyRepository,
IRepository<SCPSeries> seriesRepository,
IRepository<SCPInstance> instanceRepository,
IRepository<Dictionary> dictionaryRepository,
IDistributedLockProvider distributedLockProvider)
{
_distributedLockProvider = distributedLockProvider;
_studyRepository = studyRepository;
_patientRepository = patientRepository;
_seriesRepository = seriesRepository;
_instanceRepository = instanceRepository;
_dictionaryRepository = dictionaryRepository;
}
/// <summary>
/// 单个文件接收 归档
/// </summary>
/// <param name="dataset"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public async Task<Guid> ArchiveDicomFileAsync(DicomDataset dataset, Guid trialId, Guid trialSiteId, string fileRelativePath, string callingAE, string calledAE,long fileSize)
{
string studyInstanceUid = dataset.GetString(DicomTag.StudyInstanceUID);
string seriesInstanceUid = dataset.GetString(DicomTag.SeriesInstanceUID);
string sopInstanceUid = dataset.GetString(DicomTag.SOPInstanceUID);
string patientIdStr = dataset.GetSingleValueOrDefault(DicomTag.PatientID,string.Empty);
//Guid patientId= IdentifierHelper.CreateGuid(patientIdStr);
Guid studyId = IdentifierHelper.CreateGuid(studyInstanceUid,trialId.ToString());
Guid seriesId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid, trialId.ToString());
Guid instanceId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid, sopInstanceUid, trialId.ToString());
var isStudyNeedAdd = false;
var isSeriesNeedAdd = false;
var isInstanceNeedAdd = false;
var isPatientNeedAdd = false;
//var @lock = _distributedLockProvider.CreateLock($"{studyInstanceUid}");
//using (@lock.Acquire())
{
var findPatient = await _patientRepository.FirstOrDefaultAsync(t => t.PatientIdStr == patientIdStr && t.TrialSiteId==trialSiteId );
var findStudy = await _studyRepository.FindAsync(studyId);
var findSerice = await _seriesRepository.FindAsync(seriesId);
var findInstance = await _instanceRepository.FindAsync(instanceId);
DateTime? studyTime = dataset.GetSingleValueOrDefault(DicomTag.StudyDate, string.Empty) == string.Empty ? null : dataset.GetSingleValue<DateTime>(DicomTag.StudyDate).Add(dataset.GetSingleValueOrDefault(DicomTag.StudyTime, string.Empty) == string.Empty ? TimeSpan.Zero : dataset.GetSingleValue<DateTime>(DicomTag.StudyTime).TimeOfDay);
//先传输了修改了患者编号的,又传输了没有修改患者编号的,导致后传输的没有修改患者编号的下面的检查为0
if (findPatient == null && findStudy==null)
{
isPatientNeedAdd = true;
findPatient = new SCPPatient()
{
Id = NewId.NextSequentialGuid(),
TrialId=trialId,
TrialSiteId=trialSiteId,
PatientIdStr = dataset.GetSingleValueOrDefault(DicomTag.PatientID, string.Empty),
PatientName = dataset.GetSingleValueOrDefault(DicomTag.PatientName, string.Empty),
PatientAge = dataset.GetSingleValueOrDefault(DicomTag.PatientAge, string.Empty),
PatientSex = dataset.GetSingleValueOrDefault(DicomTag.PatientSex, string.Empty),
PatientBirthDate = dataset.GetSingleValueOrDefault(DicomTag.PatientBirthDate, string.Empty),
EarliestStudyTime = studyTime,
LatestStudyTime = studyTime,
LatestPushTime = DateTime.Now,
};
if (findPatient.PatientBirthDate.Length == 8)
{
var birthDateStr = $"{findPatient.PatientBirthDate[0]}{findPatient.PatientBirthDate[1]}{findPatient.PatientBirthDate[2]}{findPatient.PatientBirthDate[3]}-{findPatient.PatientBirthDate[4]}{findPatient.PatientBirthDate[5]}-{findPatient.PatientBirthDate[6]}{findPatient.PatientBirthDate[7]}";
var yearStr = $"{findPatient.PatientBirthDate[0]}{findPatient.PatientBirthDate[1]}{findPatient.PatientBirthDate[2]}{findPatient.PatientBirthDate[3]}";
int year = 0;
var canParse = int.TryParse(yearStr, out year);
if (canParse && year > 1900)
{
findPatient.PatientBirthDate = birthDateStr;
DateTime birthDate;
if (findPatient.PatientAge == string.Empty && studyTime.HasValue && DateTime.TryParse(findPatient.PatientBirthDate,out birthDate))
{
var patientAge = studyTime.Value.Year - birthDate.Year;
// 如果生日还未到,年龄减去一岁
if (studyTime.Value < birthDate.AddYears(patientAge))
{
patientAge--;
}
findPatient.PatientAge = patientAge.ToString();
}
}
else
{
findPatient.PatientBirthDate = string.Empty;
}
}
}
else
{
if (studyTime < findPatient.EarliestStudyTime)
{
findPatient.EarliestStudyTime = studyTime;
}
if (studyTime > findPatient.LatestStudyTime)
{
findPatient.LatestStudyTime = studyTime;
}
findPatient.LatestPushTime = DateTime.Now;
}
if (findStudy == null)
{
isStudyNeedAdd = true;
findStudy = new SCPStudy
{
CalledAE = calledAE,
CallingAE = callingAE,
PatientId = findPatient.Id,
Id = studyId,
TrialId = trialId,
TrialSiteId = trialSiteId,
StudyInstanceUid = studyInstanceUid,
StudyTime = studyTime,
Modalities = dataset.GetSingleValueOrDefault(DicomTag.Modality, string.Empty),
//ModalityForEdit = modalityForEdit,
Description = dataset.GetSingleValueOrDefault(DicomTag.StudyDescription, string.Empty),
InstitutionName = dataset.GetSingleValueOrDefault(DicomTag.InstitutionName, string.Empty),
PatientIdStr = dataset.GetSingleValueOrDefault(DicomTag.PatientID, string.Empty),
PatientName = dataset.GetSingleValueOrDefault(DicomTag.PatientName, string.Empty),
PatientAge = dataset.GetSingleValueOrDefault(DicomTag.PatientAge, string.Empty),
PatientSex = dataset.GetSingleValueOrDefault(DicomTag.PatientSex, string.Empty),
BodyPartExamined = dataset.GetSingleValueOrDefault(DicomTag.BodyPartExamined, string.Empty),
StudyId = dataset.GetSingleValueOrDefault(DicomTag.StudyID, string.Empty),
AccessionNumber = dataset.GetSingleValueOrDefault(DicomTag.AccessionNumber, string.Empty),
//需要特殊处理
PatientBirthDate = dataset.GetSingleValueOrDefault(DicomTag.PatientBirthDate, string.Empty),
AcquisitionTime = dataset.GetSingleValueOrDefault(DicomTag.AcquisitionTime, string.Empty),
AcquisitionNumber = dataset.GetSingleValueOrDefault(DicomTag.AcquisitionNumber, string.Empty),
TriggerTime = dataset.GetSingleValueOrDefault(DicomTag.TriggerTime, string.Empty),
//IsDoubleReview = addtionalInfo.IsDoubleReview,
SeriesCount = 0,
InstanceCount = 0
};
if (findStudy.PatientBirthDate.Length == 8)
{
findStudy.PatientBirthDate = $"{findStudy.PatientBirthDate[0]}{findStudy.PatientBirthDate[1]}{findStudy.PatientBirthDate[2]}{findStudy.PatientBirthDate[3]}-{findStudy.PatientBirthDate[4]}{findStudy.PatientBirthDate[5]}-{findStudy.PatientBirthDate[6]}{findStudy.PatientBirthDate[7]}";
}
}
if (findSerice == null)
{
isSeriesNeedAdd = true;
findSerice = new SCPSeries
{
Id = seriesId,
StudyId = findStudy.Id,
StudyInstanceUid = findStudy.StudyInstanceUid,
SeriesInstanceUid = seriesInstanceUid,
SeriesNumber = dataset.GetSingleValueOrDefault(DicomTag.SeriesNumber, 1),
//SeriesTime = dataset.GetSingleValueOrDefault(DicomTag.SeriesDate, DateTime.Now).Add(dataset.GetSingleValueOrDefault(DicomTag.SeriesTime, DateTime.Now).TimeOfDay),
//SeriesTime = DateTime.TryParse(dataset.GetSingleValue<string>(DicomTag.SeriesDate) + dataset.GetSingleValue<string>(DicomTag.SeriesTime), out DateTime dt) ? dt : null,
SeriesTime = dataset.GetSingleValueOrDefault(DicomTag.SeriesDate, string.Empty) == string.Empty ? null : dataset.GetSingleValue<DateTime>(DicomTag.SeriesDate).Add(dataset.GetSingleValueOrDefault(DicomTag.SeriesTime, string.Empty) == string.Empty ? TimeSpan.Zero : dataset.GetSingleValue<DateTime>(DicomTag.SeriesTime).TimeOfDay),
Modality = dataset.GetSingleValueOrDefault(DicomTag.Modality, string.Empty),
Description = dataset.GetSingleValueOrDefault(DicomTag.SeriesDescription, string.Empty),
SliceThickness = dataset.GetSingleValueOrDefault(DicomTag.SliceThickness, string.Empty),
ImagePositionPatient = dataset.GetSingleValueOrDefault(DicomTag.ImagePositionPatient, string.Empty),
ImageOrientationPatient = dataset.GetSingleValueOrDefault(DicomTag.ImageOrientationPatient, string.Empty),
BodyPartExamined = dataset.GetSingleValueOrDefault(DicomTag.BodyPartExamined, string.Empty),
SequenceName = dataset.GetSingleValueOrDefault(DicomTag.SequenceName, string.Empty),
ProtocolName = dataset.GetSingleValueOrDefault(DicomTag.ProtocolName, string.Empty),
ImagerPixelSpacing = dataset.GetSingleValueOrDefault(DicomTag.ImagerPixelSpacing, string.Empty),
AcquisitionTime = dataset.GetSingleValueOrDefault(DicomTag.AcquisitionTime, string.Empty),
AcquisitionNumber = dataset.GetSingleValueOrDefault(DicomTag.AcquisitionNumber, string.Empty),
TriggerTime = dataset.GetSingleValueOrDefault(DicomTag.TriggerTime, string.Empty),
InstanceCount = 0
};
++findStudy.SeriesCount;
}
if (findInstance == null)
{
isInstanceNeedAdd = true;
findInstance = new SCPInstance
{
Id = instanceId,
StudyId = findStudy.Id,
SeriesId = findSerice.Id,
StudyInstanceUid = findStudy.StudyInstanceUid,
SeriesInstanceUid = findSerice.SeriesInstanceUid,
SopInstanceUid = sopInstanceUid,
InstanceNumber = dataset.GetSingleValueOrDefault(DicomTag.InstanceNumber, 1),
InstanceTime = dataset.GetSingleValueOrDefault(DicomTag.ContentDate, string.Empty) == string.Empty ? null : dataset.GetSingleValue<DateTime>(DicomTag.ContentDate).Add(dataset.GetSingleValueOrDefault(DicomTag.ContentTime, string.Empty) == string.Empty ? TimeSpan.Zero : dataset.GetSingleValue<DateTime>(DicomTag.ContentTime).TimeOfDay),
//InstanceTime = DateTime.TryParse(dataset.GetSingleValue<string>(DicomTag.ContentDate) + dataset.GetSingleValue<string>(DicomTag.ContentTime), out DateTime dt) ? dt : null,
//InstanceTime = dataset.GetSingleValueOrDefault(DicomTag.ContentDate,(DateTime?)null)?.Add(dataset.GetSingleValueOrDefault(DicomTag.ContentTime, TimeSpan.Zero)),
//dataset.GetSingleValueOrDefault(DicomTag.ContentDate,DateTime.Now);//, DicomTag.ContentTime)
CPIStatus = false,
ImageRows = dataset.GetSingleValueOrDefault(DicomTag.Rows, 0),
ImageColumns = dataset.GetSingleValueOrDefault(DicomTag.Columns, 0),
SliceLocation = dataset.GetSingleValueOrDefault(DicomTag.SliceLocation, 0),
SliceThickness = dataset.GetSingleValueOrDefault(DicomTag.SliceThickness, string.Empty),
NumberOfFrames = dataset.GetSingleValueOrDefault(DicomTag.NumberOfFrames, 0),
PixelSpacing = dataset.GetSingleValueOrDefault(DicomTag.PixelSpacing, string.Empty),
ImagerPixelSpacing = dataset.GetSingleValueOrDefault(DicomTag.ImagerPixelSpacing, string.Empty),
FrameOfReferenceUID = dataset.GetSingleValueOrDefault(DicomTag.FrameOfReferenceUID, string.Empty),
WindowCenter = dataset.GetSingleValueOrDefault(DicomTag.WindowCenter, string.Empty),
WindowWidth = dataset.GetSingleValueOrDefault(DicomTag.WindowWidth, string.Empty),
Path = fileRelativePath,
FileSize= fileSize,
};
++findStudy.InstanceCount;
++findSerice.InstanceCount;
}
if (isPatientNeedAdd)
{
var ss = await _patientRepository.AddAsync(findPatient);
}
if (isStudyNeedAdd)
{
var dd = await _studyRepository.AddAsync(findStudy);
}
else
{
await _studyRepository.BatchUpdateNoTrackingAsync(t => t.Id == findStudy.Id, t => new SCPStudy() { IsUploadFinished = false });
}
if (isSeriesNeedAdd)
{
await _seriesRepository.AddAsync(findSerice);
}
if (isInstanceNeedAdd)
{
await _instanceRepository.AddAsync(findInstance);
}
else
{
await _instanceRepository.BatchUpdateNoTrackingAsync(t => t.Id == instanceId, u => new SCPInstance() { Path = fileRelativePath,FileSize=fileSize });
}
//await _studyRepository.SaveChangesAsync();
return findStudy.Id;
}
}
// 从DICOM文件中获取使用的字符集
private string GetEncodingVaulueFromDicomFile(DicomDataset dataset, DicomTag dicomTag)
{
// 获取DICOM文件的特定元素,通常用于指示使用的字符集
var charset = dataset.GetSingleValueOrDefault(DicomTag.SpecificCharacterSet, string.Empty);
var dicomEncoding = DicomEncoding.GetEncoding(charset);
var dicomStringElement = dataset.GetDicomItem<DicomStringElement>(dicomTag);
var bytes = dicomStringElement.Buffer.Data;
return dicomEncoding.GetString(bytes);
//// 从DICOM文件中获取使用的字符集
//string filePath = "C:\\Users\\hang\\Documents\\WeChat Files\\wxid_r2imdzb7j3q922\\FileStorage\\File\\2024-05\\1.2.840.113619.2.80.169103990.5390.1271401378.4.dcm";
//DicomFile dicomFile = DicomFile.Open(filePath);
//// 获取DICOM文件的特定元素,通常用于指示使用的字符集
//var charset = dicomFile.Dataset.GetSingleValueOrDefault(DicomTag.SpecificCharacterSet, string.Empty);
//var dicomEncoding = DicomEncoding.GetEncoding(charset);
//var value = dicomFile.Dataset.GetSingleValueOrDefault(DicomTag.PatientName, string.Empty);
//var dicomStringElement = dicomFile.Dataset.GetDicomItem<DicomStringElement>(DicomTag.PatientName);
//var bytes = dicomStringElement.Buffer.Data;
//var aa= dicomEncoding.GetString(bytes);
}
}
}
@@ -1,11 +0,0 @@
using FellowOakDicom;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion.Internal;
namespace IRaCIS.Core.SCP.Service
{
public interface IDicomArchiveService
{
Task<Guid> ArchiveDicomFileAsync(DicomDataset dicomDataset,Guid trialId,Guid trialSiteId, string fileRelativePath,string callingAE,string calledAE,long fileSize);
}
}
-770
View File
@@ -1,770 +0,0 @@
using AlibabaCloud.SDK.Sts20150401;
using Aliyun.OSS;
using Amazon;
using Amazon.Runtime;
using Amazon.S3;
using Amazon.S3.Model;
using Amazon.SecurityToken;
using Amazon.SecurityToken.Model;
using IRaCIS.Core.Infrastructure;
using IRaCIS.Core.Infrastructure.NewtonsoftJson;
using MassTransit;
using Microsoft.Extensions.Options;
using Minio;
using Minio.DataModel.Args;
using System.Reactive.Linq;
using System.Runtime.InteropServices;
namespace IRaCIS.Core.SCP;
#region
[LowerCamelCaseJson]
public class MinIOOptions : AWSOptions
{
public int Port { get; set; }
}
public class AWSOptions
{
public string EndPoint { get; set; }
public bool UseSSL { get; set; }
public string AccessKeyId { get; set; }
public string RoleArn { get; set; }
public string SecretAccessKey { get; set; }
public string BucketName { get; set; }
public string ViewEndpoint { get; set; }
public int DurationSeconds { get; set; }
public string Region { get; set; }
}
public class AliyunOSSOptions
{
public string RegionId { get; set; }
public string AccessKeyId { get; set; }
public string AccessKeySecret { get; set; }
public string InternalEndpoint { get; set; }
public string EndPoint { get; set; }
public string BucketName { get; set; }
public string RoleArn { get; set; }
public string Region { get; set; }
public string ViewEndpoint { get; set; }
public int DurationSeconds { get; set; }
}
public class ObjectStoreServiceOptions
{
public string ObjectStoreUse { get; set; }
public AliyunOSSOptions AliyunOSS { get; set; }
public MinIOOptions MinIO { get; set; }
public AWSOptions AWS { get; set; }
}
public class ObjectStoreDTO
{
public string ObjectStoreUse { get; set; }
public AliyunOSSTempToken AliyunOSS { get; set; }
public MinIOOptions MinIO { get; set; }
public AWSTempToken AWS { get; set; }
}
[LowerCamelCaseJson]
public class AliyunOSSTempToken
{
public string AccessKeyId { get; set; }
public string AccessKeySecret { get; set; }
public string EndPoint { get; set; }
public string BucketName { get; set; }
public string Region { get; set; }
public string ViewEndpoint { get; set; }
public string SecurityToken { get; set; }
public DateTime Expiration { get; set; }
}
[LowerCamelCaseJson]
public class AWSTempToken
{
public string Region { get; set; }
public string SessionToken { get; set; }
public string EndPoint { get; set; }
public string AccessKeyId { get; set; }
public string SecretAccessKey { get; set; }
public string BucketName { get; set; }
public string ViewEndpoint { get; set; }
public DateTime Expiration { get; set; }
}
public enum ObjectStoreUse
{
AliyunOSS = 0,
MinIO = 1,
AWS = 2,
}
#endregion
// aws 参考链接 https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/dotnetv3/S3/S3_Basics
public interface IOSSService
{
public Task<string> UploadToOSSAsync(Stream fileStream, string oosFolderPath, string fileRealName, bool isFileNameAddGuid = true);
public Task<string> UploadToOSSAsync(string localFilePath, string oosFolderPath, bool isFileNameAddGuid = true);
public Task DownLoadFromOSSAsync(string ossRelativePath, string localFilePath);
public ObjectStoreServiceOptions ObjectStoreServiceOptions { get; set; }
public Task<string> GetSignedUrl(string ossRelativePath);
public Task DeleteFromPrefix(string prefix);
public ObjectStoreDTO GetObjectStoreTempToken();
}
public class OSSService : IOSSService
{
public ObjectStoreServiceOptions ObjectStoreServiceOptions { get; set; }
private AliyunOSSTempToken AliyunOSSTempToken { get; set; }
private AWSTempToken AWSTempToken { get; set; }
public OSSService(IOptionsMonitor<ObjectStoreServiceOptions> options)
{
ObjectStoreServiceOptions = options.CurrentValue;
}
/// <summary>
/// oosFolderPath 不要 "/ "开头 应该: TempFolder/ChildFolder
/// </summary>
/// <param name="fileStream"></param>
/// <param name="oosFolderPath"></param>
/// <param name="fileRealName"></param>
/// <param name="isFileNameAddGuid"></param>
/// <returns></returns>
public async Task<string> UploadToOSSAsync(Stream fileStream, string oosFolderPath, string fileRealName, bool isFileNameAddGuid = true)
{
GetObjectStoreTempToken();
var ossRelativePath = isFileNameAddGuid ? $"{oosFolderPath}/{Guid.NewGuid()}_{fileRealName}" : $"{oosFolderPath}/{fileRealName}";
try
{
using (var memoryStream = new MemoryStream())
{
fileStream.Seek(0, SeekOrigin.Begin);
fileStream.CopyTo(memoryStream);
memoryStream.Seek(0, SeekOrigin.Begin);
if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS")
{
var aliConfig = ObjectStoreServiceOptions.AliyunOSS;
var _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.EndPoint : aliConfig.InternalEndpoint, AliyunOSSTempToken.AccessKeyId, AliyunOSSTempToken.AccessKeySecret, AliyunOSSTempToken.SecurityToken);
// 上传文件
var result = _ossClient.PutObject(aliConfig.BucketName, ossRelativePath, memoryStream);
}
else if (ObjectStoreServiceOptions.ObjectStoreUse == "MinIO")
{
var minIOConfig = ObjectStoreServiceOptions.MinIO;
var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.EndPoint}:{minIOConfig.Port}")
.WithCredentials(minIOConfig.AccessKeyId, minIOConfig.SecretAccessKey).WithSSL(minIOConfig.UseSSL)
.Build();
var putObjectArgs = new PutObjectArgs()
.WithBucket(minIOConfig.BucketName)
.WithObject(ossRelativePath)
.WithStreamData(memoryStream)
.WithObjectSize(memoryStream.Length);
await minioClient.PutObjectAsync(putObjectArgs);
}
else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS")
{
var awsConfig = ObjectStoreServiceOptions.AWS;
var credentials = new SessionAWSCredentials(AWSTempToken.AccessKeyId, AWSTempToken.SecretAccessKey, AWSTempToken.SessionToken);
//提供awsEndPoint(域名)进行访问配置
var clientConfig = new AmazonS3Config
{
RegionEndpoint = RegionEndpoint.USEast1,
UseHttp = true,
};
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
var putObjectRequest = new Amazon.S3.Model.PutObjectRequest()
{
BucketName = awsConfig.BucketName,
InputStream = memoryStream,
Key = ossRelativePath,
};
await amazonS3Client.PutObjectAsync(putObjectRequest);
}
else
{
throw new BusinessValidationFailedException("未定义的存储介质类型");
}
}
}
catch (Exception ex)
{
throw new BusinessValidationFailedException($"上传发生异常:{ex.Message}");
}
return "/" + ossRelativePath;
}
/// <summary>
/// oosFolderPath 不要 "/ "开头 应该: TempFolder/ChildFolder
/// </summary>
/// <param name="localFilePath"></param>
/// <param name="oosFolderPath"></param>
/// <param name="isFileNameAddGuid"></param>
/// <returns></returns>
/// <exception cref="BusinessValidationFailedException"></exception>
public async Task<string> UploadToOSSAsync(string localFilePath, string oosFolderPath, bool isFileNameAddGuid = true)
{
GetObjectStoreTempToken();
var localFileName = Path.GetFileName(localFilePath);
var ossRelativePath = isFileNameAddGuid ? $"{oosFolderPath}/{Guid.NewGuid()}_{localFileName}" : $"{oosFolderPath}/{localFileName}";
if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS")
{
var aliConfig = ObjectStoreServiceOptions.AliyunOSS;
var _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.EndPoint : aliConfig.InternalEndpoint, AliyunOSSTempToken.AccessKeyId, AliyunOSSTempToken.AccessKeySecret, AliyunOSSTempToken.SecurityToken);
// 上传文件
var result = _ossClient.PutObject(aliConfig.BucketName, ossRelativePath, localFilePath);
}
else if (ObjectStoreServiceOptions.ObjectStoreUse == "MinIO")
{
var minIOConfig = ObjectStoreServiceOptions.MinIO;
var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.EndPoint}:{minIOConfig.Port}")
.WithCredentials(minIOConfig.AccessKeyId, minIOConfig.SecretAccessKey).WithSSL(minIOConfig.UseSSL)
.Build();
var putObjectArgs = new PutObjectArgs()
.WithBucket(minIOConfig.BucketName)
.WithObject(ossRelativePath)
.WithFileName(localFilePath);
await minioClient.PutObjectAsync(putObjectArgs);
}
else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS")
{
var awsConfig = ObjectStoreServiceOptions.AWS;
// 提供awsAccessKeyId和awsSecretAccessKey构造凭证
var credentials = new SessionAWSCredentials(AWSTempToken.AccessKeyId, AWSTempToken.SecretAccessKey, AWSTempToken.SessionToken);
//提供awsEndPoint(域名)进行访问配置
var clientConfig = new AmazonS3Config
{
RegionEndpoint = RegionEndpoint.USEast1,
UseHttp = true,
};
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
var putObjectRequest = new Amazon.S3.Model.PutObjectRequest()
{
BucketName = awsConfig.BucketName,
FilePath = localFilePath,
Key = ossRelativePath,
};
await amazonS3Client.PutObjectAsync(putObjectRequest);
}
else
{
throw new BusinessValidationFailedException("未定义的存储介质类型");
}
return "/" + ossRelativePath;
}
public async Task DownLoadFromOSSAsync(string ossRelativePath, string localFilePath)
{
GetObjectStoreTempToken();
ossRelativePath = ossRelativePath.TrimStart('/');
try
{
if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS")
{
var aliConfig = ObjectStoreServiceOptions.AliyunOSS;
var _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.EndPoint : aliConfig.InternalEndpoint, AliyunOSSTempToken.AccessKeyId, AliyunOSSTempToken.AccessKeySecret, AliyunOSSTempToken.SecurityToken);
// 上传文件
var result = _ossClient.GetObject(aliConfig.BucketName, ossRelativePath);
// 将下载的文件流保存到本地文件
using (var fs = File.OpenWrite(localFilePath))
{
result.Content.CopyTo(fs);
fs.Close();
}
}
else if (ObjectStoreServiceOptions.ObjectStoreUse == "MinIO")
{
var minIOConfig = ObjectStoreServiceOptions.MinIO;
var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.EndPoint}:{minIOConfig.Port}")
.WithCredentials(minIOConfig.AccessKeyId, minIOConfig.SecretAccessKey).WithSSL(minIOConfig.UseSSL)
.Build();
var getObjectArgs = new GetObjectArgs()
.WithBucket(minIOConfig.BucketName)
.WithObject(ossRelativePath)
.WithFile(localFilePath);
await minioClient.GetObjectAsync(getObjectArgs);
}
else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS")
{
var awsConfig = ObjectStoreServiceOptions.AWS;
// 提供awsAccessKeyId和awsSecretAccessKey构造凭证
var credentials = new SessionAWSCredentials(AWSTempToken.AccessKeyId, AWSTempToken.SecretAccessKey, AWSTempToken.SessionToken);
//提供awsEndPoint(域名)进行访问配置
var clientConfig = new AmazonS3Config
{
RegionEndpoint = RegionEndpoint.USEast1,
UseHttp = true,
};
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
var getObjectArgs = new Amazon.S3.Model.GetObjectRequest()
{
BucketName = awsConfig.BucketName,
Key = ossRelativePath,
};
await (await amazonS3Client.GetObjectAsync(getObjectArgs)).WriteResponseStreamToFileAsync(localFilePath, true, CancellationToken.None);
}
else
{
throw new BusinessValidationFailedException("未定义的存储介质类型");
}
}
catch (Exception ex)
{
throw new BusinessValidationFailedException("oss下载失败!" + ex.Message);
}
}
public async Task<string> GetSignedUrl(string ossRelativePath)
{
GetObjectStoreTempToken();
ossRelativePath = ossRelativePath.TrimStart('/');
try
{
if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS")
{
var aliConfig = ObjectStoreServiceOptions.AliyunOSS;
var _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.EndPoint : aliConfig.InternalEndpoint, AliyunOSSTempToken.AccessKeyId, AliyunOSSTempToken.AccessKeySecret, AliyunOSSTempToken.SecurityToken);
// 生成签名URL。
var req = new GeneratePresignedUriRequest(aliConfig.BucketName, ossRelativePath, SignHttpMethod.Get)
{
// 设置签名URL过期时间,默认值为3600秒。
Expiration = DateTime.Now.AddHours(1),
};
var uri = _ossClient.GeneratePresignedUri(req);
return uri.PathAndQuery;
}
else if (ObjectStoreServiceOptions.ObjectStoreUse == "MinIO")
{
var minIOConfig = ObjectStoreServiceOptions.MinIO;
var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.EndPoint}:{minIOConfig.Port}")
.WithCredentials(minIOConfig.AccessKeyId, minIOConfig.SecretAccessKey).WithSSL(minIOConfig.UseSSL)
.Build();
var args = new PresignedGetObjectArgs()
.WithBucket(minIOConfig.BucketName)
.WithObject(ossRelativePath)
.WithExpiry(3600)
/*.WithHeaders(reqParams)*/;
var presignedUrl = await minioClient.PresignedGetObjectAsync(args);
Uri uri = new Uri(presignedUrl);
string relativePath = uri.PathAndQuery;
return relativePath;
}
else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS")
{
var awsConfig = ObjectStoreServiceOptions.AWS;
// 提供awsAccessKeyId和awsSecretAccessKey构造凭证
var credentials = new SessionAWSCredentials(AWSTempToken.AccessKeyId, AWSTempToken.SecretAccessKey, AWSTempToken.SessionToken);
//提供awsEndPoint(域名)进行访问配置
var clientConfig = new AmazonS3Config
{
RegionEndpoint = RegionEndpoint.USEast1,
UseHttp = true,
};
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
var presignedUrl = await amazonS3Client.GetPreSignedURLAsync(new GetPreSignedUrlRequest()
{
BucketName = awsConfig.BucketName,
Key = ossRelativePath,
Expires = DateTime.UtcNow.AddMinutes(120)
});
Uri uri = new Uri(presignedUrl);
string relativePath = uri.PathAndQuery;
return relativePath;
}
else
{
throw new BusinessValidationFailedException("未定义的存储介质类型");
}
}
catch (Exception ex)
{
throw new BusinessValidationFailedException("oss授权url失败!" + ex.Message);
}
}
/// <summary>
/// 删除某个目录的文件
/// </summary>
/// <param name="prefix"></param>
/// <returns></returns>
public async Task DeleteFromPrefix(string prefix)
{
GetObjectStoreTempToken();
if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS")
{
var aliConfig = ObjectStoreServiceOptions.AliyunOSS;
var _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.EndPoint : aliConfig.InternalEndpoint, AliyunOSSTempToken.AccessKeyId, AliyunOSSTempToken.AccessKeySecret, AliyunOSSTempToken.SecurityToken);
try
{
ObjectListing objectListing = null;
string nextMarker = null;
do
{
// 使用 prefix 模拟目录结构,设置 MaxKeys 和 NextMarker
objectListing = _ossClient.ListObjects(new Aliyun.OSS.ListObjectsRequest(aliConfig.BucketName)
{
Prefix = prefix,
MaxKeys = 1000,
Marker = nextMarker
});
List<string> keys = objectListing.ObjectSummaries.Select(t => t.Key).ToList();
// 删除获取到的文件
if (keys.Count > 0)
{
_ossClient.DeleteObjects(new Aliyun.OSS.DeleteObjectsRequest(aliConfig.BucketName, keys, false));
}
// 设置 NextMarker 以获取下一页的数据
nextMarker = objectListing.NextMarker;
} while (objectListing.IsTruncated);
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
}
else if (ObjectStoreServiceOptions.ObjectStoreUse == "MinIO")
{
var minIOConfig = ObjectStoreServiceOptions.MinIO;
var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.EndPoint}:{minIOConfig.Port}")
.WithCredentials(minIOConfig.AccessKeyId, minIOConfig.SecretAccessKey).WithSSL(minIOConfig.UseSSL)
.Build();
var listArgs = new ListObjectsArgs().WithBucket(minIOConfig.BucketName).WithPrefix(prefix).WithRecursive(true);
// 创建一个空列表用于存储对象键
var objects = new List<string>();
// 使用 await foreach 来异步迭代对象列表
await foreach (var item in minioClient.ListObjectsEnumAsync(listArgs))
{
objects.Add(item.Key);
}
if (objects.Count > 0)
{
var objArgs = new RemoveObjectsArgs()
.WithBucket(minIOConfig.BucketName)
.WithObjects(objects);
// 删除对象
await minioClient.RemoveObjectsAsync(objArgs);
}
}
else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS")
{
var awsConfig = ObjectStoreServiceOptions.AWS;
// 提供awsAccessKeyId和awsSecretAccessKey构造凭证
var credentials = new SessionAWSCredentials(AWSTempToken.AccessKeyId, AWSTempToken.SecretAccessKey, AWSTempToken.SessionToken);
//提供awsEndPoint(域名)进行访问配置
var clientConfig = new AmazonS3Config
{
RegionEndpoint = RegionEndpoint.USEast1,
UseHttp = true,
};
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
// 列出指定前缀下的所有对象
var listObjectsRequest = new ListObjectsV2Request
{
BucketName = awsConfig.BucketName,
Prefix = prefix
};
var listObjectsResponse = await amazonS3Client.ListObjectsV2Async(listObjectsRequest);
if (listObjectsResponse.S3Objects.Count > 0)
{
// 准备删除请求
var deleteObjectsRequest = new Amazon.S3.Model.DeleteObjectsRequest
{
BucketName = awsConfig.BucketName,
Objects = new List<KeyVersion>()
};
foreach (var s3Object in listObjectsResponse.S3Objects)
{
deleteObjectsRequest.Objects.Add(new KeyVersion
{
Key = s3Object.Key
});
}
// 批量删除对象
var deleteObjectsResponse = await amazonS3Client.DeleteObjectsAsync(deleteObjectsRequest);
}
}
else
{
throw new BusinessValidationFailedException("未定义的存储介质类型");
}
}
public ObjectStoreDTO GetObjectStoreTempToken()
{
var ossOptions = ObjectStoreServiceOptions.AliyunOSS;
if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS")
{
var client = new Client(new AlibabaCloud.OpenApiClient.Models.Config()
{
AccessKeyId = ossOptions.AccessKeyId,
AccessKeySecret = ossOptions.AccessKeySecret,
//AccessKeyId = "LTAI5tJV76pYX5yPg1N9QVE8",
//AccessKeySecret = "roRNLa9YG1of4pYruJGCNKBXEWTAWa",
Endpoint = "sts.cn-hangzhou.aliyuncs.com"
});
var assumeRoleRequest = new AlibabaCloud.SDK.Sts20150401.Models.AssumeRoleRequest();
// 将<YOUR_ROLE_SESSION_NAME>设置为自定义的会话名称,例如oss-role-session。
assumeRoleRequest.RoleSessionName = $"session-name-{NewId.NextGuid()}";
// 将<YOUR_ROLE_ARN>替换为拥有上传文件到指定OSS Bucket权限的RAM角色的ARN。
assumeRoleRequest.RoleArn = ossOptions.RoleArn;
//assumeRoleRequest.RoleArn = "acs:ram::1899121822495495:role/webdirect";
assumeRoleRequest.DurationSeconds = ossOptions.DurationSeconds;
var runtime = new AlibabaCloud.TeaUtil.Models.RuntimeOptions();
var response = client.AssumeRoleWithOptions(assumeRoleRequest, runtime);
var credentials = response.Body.Credentials;
var tempToken = new AliyunOSSTempToken()
{
AccessKeyId = credentials.AccessKeyId,
AccessKeySecret = credentials.AccessKeySecret,
//转为服务器时区,最后统一转为客户端时区
Expiration = TimeZoneInfo.ConvertTimeFromUtc(DateTime.Parse(credentials.Expiration), TimeZoneInfo.Local),
SecurityToken = credentials.SecurityToken,
Region = ossOptions.Region,
BucketName = ossOptions.BucketName,
EndPoint = ossOptions.EndPoint,
ViewEndpoint = ossOptions.ViewEndpoint,
};
AliyunOSSTempToken = tempToken;
return new ObjectStoreDTO() { ObjectStoreUse = ObjectStoreServiceOptions.ObjectStoreUse, AliyunOSS = tempToken };
}
else if (ObjectStoreServiceOptions.ObjectStoreUse == "MinIO")
{
return new ObjectStoreDTO() { ObjectStoreUse = ObjectStoreServiceOptions.ObjectStoreUse, MinIO = ObjectStoreServiceOptions.MinIO };
}
else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS")
{
var awsOptions = ObjectStoreServiceOptions.AWS;
//aws 临时凭证
// 创建 STS 客户端
var stsClient = new AmazonSecurityTokenServiceClient(awsOptions.AccessKeyId, awsOptions.SecretAccessKey);
// 使用 AssumeRole 请求临时凭证
var assumeRoleRequest = new AssumeRoleRequest
{
RoleArn = awsOptions.RoleArn, // 角色 ARN
RoleSessionName = $"session-name-{NewId.NextGuid()}",
DurationSeconds = awsOptions.DurationSeconds // 临时凭证有效期
};
var assumeRoleResponse = stsClient.AssumeRoleAsync(assumeRoleRequest).Result;
var credentials = assumeRoleResponse.Credentials;
var tempToken = new AWSTempToken()
{
AccessKeyId = credentials.AccessKeyId,
SecretAccessKey = credentials.SecretAccessKey,
SessionToken = credentials.SessionToken,
Expiration = credentials.Expiration,
Region = awsOptions.Region,
BucketName = awsOptions.BucketName,
EndPoint = awsOptions.EndPoint,
ViewEndpoint = awsOptions.ViewEndpoint,
};
AWSTempToken = tempToken;
return new ObjectStoreDTO() { ObjectStoreUse = ObjectStoreServiceOptions.ObjectStoreUse, AWS = tempToken };
}
else
{
throw new BusinessValidationFailedException("未定义的存储介质类型");
}
}
}
@@ -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": "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
}
},
"ConnectionStrings": {
"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"
},
"DicomSCPServiceConfig": {
"CalledAEList": [
"STORESCP"
],
"ServerPort": 11112
}
}
@@ -1,48 +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": "LTAI5tRRZehUp2V9pyTPtAJm",
"AccessKeySecret": "FLizxkHsMm4CGYHtkV8E3PNJJZU7oV",
"RoleArn": "acs:ram::1899121822495495:role/dev-oss-access",
"BucketName": "zy-irc-test-store",
"ViewEndpoint": "https://zy-irc-test-store.oss-cn-shanghai.aliyuncs.com",
"Region": "oss-cn-shanghai",
"DurationSeconds": 7200
},
"MinIO": {
"endPoint": "106.14.89.110",
"port": "9001",
"useSSL": false,
"accessKey": "fbStsVYCIPKHQneeqMwD",
"secretKey": "TzgvyA3zGXMUnpilJNUlyMYHfosl1hBMl6lxPmjy",
"bucketName": "hir-test",
"viewEndpoint": "http://106.14.89.110:9001/hir-test/"
}
},
"ConnectionStrings": {
"RemoteNew": "Server=106.14.89.110,1435;Database=Test_IRC;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"
},
"DicomSCPServiceConfig": {
"CalledAEList": [
"STORESCP"
],
"ServerPort": 11112
}
}
-34
View File
@@ -1,34 +0,0 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"ObjectStoreService": {
"ObjectStoreUse": "AWS",
"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-lili-store",
"ViewEndpoint": "https://ei-med-s3-lili-store.s3.amazonaws.com",
"DurationSeconds": 7200
}
},
"ConnectionStrings": {
"RemoteNew": "Server=us-prod-mssql-service,1433;Database=US_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
"Hangfire": "Server=us-prod-mssql-service,1433;Database=US_IRC_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
},
"DicomSCPServiceConfig": {
"CalledAEList": [
"STORESCP"
],
"ServerPort": 11112
}
}
-34
View File
@@ -1,34 +0,0 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"ObjectStoreService": {
"ObjectStoreUse": "AWS",
"AWS": {
"Region": "us-east-1",
"EndPoint": "s3.us-east-1.amazonaws.com",
"UseSSL": true,
"RoleArn": "arn:aws:iam::471112624751:role/uat_s3_access",
"AccessKeyId": "AKIAW3MEAFJX7IPXISP4",
"SecretAccessKey": "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",
"BucketName": "ei-med-s3-lili-uat-store",
"ViewEndpoint": "https://ei-med-s3-lili-uat-store.s3.amazonaws.com/",
"DurationSeconds": 7200
}
},
"ConnectionStrings": {
"RemoteNew": "Server=us-mssql-service,1433;Database=US_Uat_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
"Hangfire": "Server=us-mssql-service,1433;Database=US_Uat_IRC_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
},
"DicomSCPServiceConfig": {
"CalledAEList": [
"STORESCP"
],
"ServerPort": 11112
}
}
-35
View File
@@ -1,35 +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": "LTAI5tRRZehUp2V9pyTPtAJm",
"AccessKeySecret": "FLizxkHsMm4CGYHtkV8E3PNJJZU7oV",
"RoleArn": "acs:ram::1899121822495495:role/dev-oss-access",
"BucketName": "zy-irc-uat-store",
"ViewEndpoint": "https://zy-irc-uat-store.oss-cn-shanghai.aliyuncs.com",
"Region": "oss-cn-shanghai",
"DurationSeconds": 7200
}
},
"ConnectionStrings": {
"RemoteNew": "Server=47.117.164.182,1434;Database=Uat_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
"Hangfire": "Server=47.117.164.182,1434;Database=Uat_IRC.Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
},
"DicomSCPServiceConfig": {
"CalledAEList": [
"STORESCP"
],
"ServerPort": 11112
}
}
-9
View File
@@ -1,9 +0,0 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}
-6
View File
@@ -17,8 +17,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IRaCIS.Core.Infra.EFCore",
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IRaCIS.Core.Infrastructure", "IRaCIS.Core.Infrastructure\IRaCIS.Core.Infrastructure.csproj", "{07EED0F8-08E6-46F3-ACBE-17BC1391BD4C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IRC.Core.SCP", "IRC.Core.SCP\IRC.Core.SCP.csproj", "{ECD08F47-DC1A-484E-BB91-6CDDC8823CC5}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -53,10 +51,6 @@ Global
{07EED0F8-08E6-46F3-ACBE-17BC1391BD4C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{07EED0F8-08E6-46F3-ACBE-17BC1391BD4C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{07EED0F8-08E6-46F3-ACBE-17BC1391BD4C}.Release|Any CPU.Build.0 = Release|Any CPU
{ECD08F47-DC1A-484E-BB91-6CDDC8823CC5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{ECD08F47-DC1A-484E-BB91-6CDDC8823CC5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{ECD08F47-DC1A-484E-BB91-6CDDC8823CC5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{ECD08F47-DC1A-484E-BB91-6CDDC8823CC5}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
+231
View File
@@ -0,0 +1,231 @@
//using Autofac;
//using Autofac.Extensions.DependencyInjection;
//using IRaCIS.Core.API;
//using IRaCIS.Core.Application.Filter;
//using IRaCIS.Core.Application.MediatR.Handlers;
//using LogDashboard;
//using MassTransit;
//using MassTransit.NewIdProviders;
//using MediatR;
//using Microsoft.AspNetCore.Builder;
//using Microsoft.AspNetCore.Http.Features;
//using Microsoft.AspNetCore.HttpOverrides;
//using Microsoft.AspNetCore.SignalR;
//using Microsoft.Extensions.Configuration;
//using Microsoft.Extensions.DependencyInjection;
//using Microsoft.Extensions.Hosting;
//using Serilog;
//using System;
//var builder = WebApplication.CreateBuilder(args);
// //以配置文件为准,否则 从url中取环境值(服务以命令行传递参数启动,配置文件配置了就不需要传递环境参数)
// var config = new ConfigurationBuilder()
// .AddEnvironmentVariables()
// .Build();
// var enviromentName = config["ASPNETCORE_ENVIRONMENT"];
// if (string.IsNullOrWhiteSpace(enviromentName))
// {
// var index = Array.IndexOf(args, "--env");
// enviromentName = index > -1
// ? args[index + 1]
// : "Development";
// }
// NewId.SetProcessIdProvider(new CurrentProcessIdProvider());
//builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory())
// .ConfigureContainer<ContainerBuilder>(containerBuilder =>
// {
// containerBuilder.RegisterModule<AutofacModuleSetup>();
// })
// .UseWindowsService().UseSerilog();
//// Add services to the container.
////本地化
//builder.Services.AddJsonLocalization(options => options.ResourcesPath = "Resources");
//// 异常、参数统一验证过滤器、Json序列化配置、字符串参数绑型统一Trim()
//builder.Services.AddControllers(options =>
//{
// //options.Filters.Add<LogActionFilter>();
// options.Filters.Add<ModelActionFilter>();
// options.Filters.Add<ProjectExceptionFilter>();
// //options.Filters.Add<UnitOfWorkFilter>();
// //if (_configuration.GetSection("BasicSystemConfig").GetValue<bool>("OpenLoginLimit"))
// //{
// // options.Filters.Add<LimitUserRequestAuthorization>();
// //}
//}).AddNewtonsoftJsonSetup(); // NewtonsoftJson 序列化 处理
////动态WebApi + UnifiedApiResultFilter 省掉控制器代码
//builder.Services.AddDynamicWebApiSetup();
////AutoMapper
//builder.Services.AddAutoMapperSetup();
////EF ORM QueryWithNoLock
//builder.Services.AddEFSetup(builder.Configuration);
////Http 响应压缩
//builder.Services.AddResponseCompressionSetup();
////Swagger Api 文档
//builder.Services.AddSwaggerSetup();
////JWT Token 验证
//builder.Services.AddJWTAuthSetup(builder.Configuration);
//// MediatR 进程内消息 事件解耦 从程序集中 注册命令和handler对应关系
//builder.Services.AddMediatR(typeof(ConsistencyVerificationHandler).Assembly);
//// EasyCaching 缓存
//builder.Services.AddEasyCachingSetup();
////services.AddDistributedMemoryCache();
////// hangfire 定时任务框架 有界面,更友好~
//builder.Services.AddhangfireSetup(builder.Configuration);
////// QuartZ 定时任务框架 使用了hangfire 暂时不用,后续需要可以打开,已经配好
////builder.Services.AddQuartZSetup(_configuration);
//// 保护上传文件
////services.AddStaticFileAuthorizationSetup();
//////HttpReports 暂时废弃
////services.AddHttpReports().AddHttpTransport();
////Serilog 日志可视化 LogDashboard日志
//builder.Services.AddLogDashboardSetup();
////上传限制 配置
//builder.Services.Configure<FormOptions>(options =>
//{
// options.MultipartBodyLengthLimit = int.MaxValue;
// options.ValueCountLimit = int.MaxValue;
// options.ValueLengthLimit = int.MaxValue;
//});
////IP 限流 可设置白名单 或者黑名单
////services.AddIpPolicyRateLimitSetup(_configuration);
////用户类型 策略授权
//builder.Services.AddAuthorizationPolicySetup(builder.Configuration);
//builder.Services.AddJsonConfigSetup(builder.Configuration);
////转发头设置 获取真实IP
//builder.Services.Configure<ForwardedHeadersOptions>(options =>
//{
// options.ForwardedHeaders =
// ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
//});
////Dicom影像渲染图片 跨平台
//builder.Services.AddDicomSetup();
//// 实时应用
//builder.Services.AddSignalR();
//builder.Services.AddSingleton<IUserIdProvider, IRaCISUserIdProvider>();
//builder.Services.AddControllers();
//// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
////builder.Services.AddEndpointsApiExplorer();
////builder.Services.AddSwaggerGen();
////SerilogExtension.AddSerilogSetup(enviromentName, builder.Host.confi);
//var app = builder.Build();
//// Configure the HTTP request pipeline.
////本地化
//app.UseLocalization();
//app.UseForwardedHeaders();
////不需要 token 访问的静态文件 wwwroot css, JavaScript, and images don't require authentication.
//app.UseStaticFiles();
//app.UseIRacisHostStaticFileStore(app.Environment);
////LogDashboard
//app.UseLogDashboard("/LogDashboard");
////hangfire
////app.UseHangfireConfig(app.Environment);
//////暂时废弃
////app.UseHttpReports();
//////限流 中间件
////app.UseIpRateLimiting();
////响应压缩
//app.UseResponseCompression();
//if (app.Environment.IsDevelopment())
//{
// app.UseDeveloperExceptionPage();
//}
//else
//{
// //app.UseHsts();
//}
//SwaggerSetup.Configure(app, app.Environment);
//Console.WriteLine("当前环境: " + builder.Environment.EnvironmentName);
////app.UseMiddleware<AuthMiddleware>();
//// 特殊异常处理 比如 404
//app.UseStatusCodePagesWithReExecute("/Error/{0}");
//////serilog 记录请求的用户信息
//app.UseSerilogConfig(app.Environment);
//app.UseRouting();
//app.UseCors(t => t.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader());
//app.UseAuthentication();
////app.UseJwtBearerQueryString();
//app.UseAuthorization();
//////文件伺服 必须带Token 访问
//////app.UseIRacisHostStaticFileStore(env);
////app.UseEndpoints(endpoints =>
////{
//// endpoints.MapControllers();
//// endpoints.MapHub<UploadHub>("/UploadHub")/*.RequireCors(t=>t.WithOrigins(new string[] {"null"}).AllowAnyMethod().AllowAnyHeader().AllowCredentials())*/;
////});
//app.MapControllers();
//app.MapHub<UploadHub>("/UploadHub")/*.RequireCors(t=>t.WithOrigins(new string[] {"null"}).AllowAnyMethod().AllowAnyHeader().AllowCredentials())*/;
//app.Run();
//测试同步
+106
View File
@@ -0,0 +1,106 @@
{
"needAnonymizeTag": [
{ //PatientsName
"Group": "0010",
"Element": "0010",
"ReplaceValue": "",
"Enable": true
},
{ // PatientID
"Group": "0010",
"Element": "0020",
"ReplaceValue": "",
"Enable": true
},
{ // IssuerOfPatientID
"Group": "0010",
"Element": "0021",
"ReplaceValue": "",
"Enable": true
},
{ // PatientsBirthDate
"Group": "0010",
"Element": "0030",
"ReplaceValue": "",
"Enable": true
},
{ // PatientsBirthTime
"Group": "0010",
"Element": "0032",
"ReplaceValue": "",
"Enable": false
},
{ // PatientsSex
"Group": "0010",
"Element": "0040",
"ReplaceValue": "",
"Enable": false
},
{ // OtherPatientIDs
"Group": "0010",
"Element": "1000",
"ReplaceValue": "",
"Enable": false
},
{ // OtherPatientNames
"Group": "0010",
"Element": "1001",
"ReplaceValue": "",
"Enable": false
},
{ // OtherPatientNames
"Group": "0010",
"Element": "1005",
"ReplaceValue": "",
"Enable": true
},
{ // PatientBirthName
"Group": "0010",
"Element": "1005",
"ReplaceValue": "",
"Enable": true
},
{ // PatientsAge
"Group": "0010",
"Element": "1010",
"ReplaceValue": "",
"Enable": true
},
{ // PatientsAddress
"Group": "0010",
"Element": "1040",
"ReplaceValue": "",
"Enable": true
},
{ // PatientsMothersBirthName
"Group": "0010",
"Element": "1060",
"ReplaceValue": "",
"Enable": true
},
{
"Group": "0010",
"Element": "2150",
"ReplaceValue": "",
"Enable": true
},
{
"Group": "0010",
"Element": "2152",
"ReplaceValue": "",
"Enable": true
},
{
"Group": "0010",
"Element": "2154",
"ReplaceValue": "",
"Enable": true
},
{
"Group": "0012",
"Element": "0040",
"ReplaceValue": "XXX",
"Enable": true
}
]
}
+119 -331
View File
@@ -1,5 +1,6 @@
using System;
using System.Net.Http;
using EasyCaching.Core;
using IRaCIS.Application.Interfaces;
using IRaCIS.Application.Contracts;
using IRaCIS.Core.Application.Auth;
@@ -14,30 +15,17 @@ using System.Text;
using Microsoft.AspNetCore.Http;
using IRaCIS.Core.Application.Interfaces;
using System.Threading.Tasks;
using IRaCIS.Core.Application.Service;
using IRaCIS.Application.Services;
using IRaCIS.Core.Infra.EFCore;
using IRaCIS.Core.Domain.Models;
using IRaCIS.Core.Infrastructure;
using System.Linq;
using Microsoft.Extensions.Logging;
using MassTransit;
using Aliyun.Acs.Core;
using Aliyun.Acs.Core.Profile;
using Aliyun.Acs.Sts.Model.V20150401;
using Microsoft.AspNetCore.Hosting;
using IRaCIS.Core.Application.Helper;
using Microsoft.Extensions.Options;
using IRaCIS.Core.Application.Contracts;
using LoginReturnDTO = IRaCIS.Application.Contracts.LoginReturnDTO;
using DocumentFormat.OpenXml.Spreadsheet;
using AutoMapper.QueryableExtensions;
using NetTopologySuite.Algorithm;
using ZiggyCreatures.Caching.Fusion;
using AlibabaCloud.SDK.Sts20150401;
using AlibabaCloud.SDK.Sts20150401.Models;
using Org.BouncyCastle.Tls;
using Amazon.SecurityToken.Model;
using Amazon.SecurityToken;
using Amazon;
using AssumeRoleRequest = Amazon.SecurityToken.Model.AssumeRoleRequest;
using AutoMapper;
using MassTransit;
namespace IRaCIS.Api.Controllers
{
@@ -49,258 +37,150 @@ namespace IRaCIS.Api.Controllers
{
/// <summary>
/// 获取医生详情
/// </summary>
/// <param name="attachmentService"></param>
/// <param name="_doctorService"></param>
/// <param name="_educationService"></param>
/// <param name="_trialExperienceService"></param>
/// <param name="_researchPublicationService"></param>
/// <param name="_vacationService"></param>
/// <param name="doctorId"></param>
/// <returns></returns>
[HttpGet, Route("doctor/getDetail/{doctorId:guid}")]
public async Task<IResponseOutput<DoctorDetailDTO>> GetDoctorDetail([FromServices] IAttachmentService attachmentService, [FromServices] IDoctorService _doctorService,
[FromServices] IEducationService _educationService, [FromServices] ITrialExperienceService _trialExperienceService,
[FromServices] IResearchPublicationService _researchPublicationService, [FromServices] IVacationService _vacationService, Guid doctorId)
{
var education = await _educationService.GetEducation(doctorId);
var sowList = _doctorService.GetDoctorSowList(doctorId);
var ackSowList = _doctorService.GetDoctorAckSowList(doctorId);
var doctorDetail = new DoctorDetailDTO
{
AuditView = await _doctorService.GetAuditState(doctorId),
BasicInfoView = await _doctorService.GetBasicInfo(doctorId),
EmploymentView = await _doctorService.GetEmploymentInfo(doctorId),
AttachmentList = await attachmentService.GetAttachments(doctorId),
EducationList = education.EducationList,
PostgraduateList = education.PostgraduateList,
TrialExperienceView = await _trialExperienceService.GetTrialExperience(doctorId),
ResearchPublicationView = await _researchPublicationService.GetResearchPublication(doctorId),
SpecialtyView = await _doctorService.GetSpecialtyInfo(doctorId),
InHoliday = (await _vacationService.OnVacation(doctorId)).IsSuccess,
IntoGroupInfo = _doctorService.GetDoctorIntoGroupInfo(doctorId),
SowList = sowList,
AckSowList = ackSowList
};
return ResponseOutput.Ok(doctorDetail);
}
/// <summary> 系统用户登录接口[New] </summary>
[HttpPost, Route("user/login")]
[AllowAnonymous]
public async Task<IResponseOutput> Login(UserLoginDTO loginUser,
[FromServices] IFusionCache _fusionCache,
[FromServices] IUserService _userService,
[FromServices] ITokenService _tokenService,
[FromServices] IReadingImageTaskService readingImageTaskService,
[FromServices] IOptionsMonitor<ServiceVerifyConfigOption> _verifyConfig,
[FromServices] IOptionsMonitor<SystemEmailSendConfig> _emailConfig,
[FromServices] IMailVerificationService _mailVerificationService)
public async Task<IResponseOutput<LoginReturnDTO>> Login(UserLoginDTO loginUser, [FromServices] IEasyCachingProvider provider, [FromServices] IUserService _userService,
[FromServices] ITokenService _tokenService, [FromServices] IConfiguration configuration)
{
var emailConfig = _emailConfig.CurrentValue;
var companyInfo = new SystemEmailSendConfigView() { CompanyName = emailConfig.CompanyName, CompanyNameCN = emailConfig.CompanyNameCN, CompanyShortName = emailConfig.CompanyShortName, CompanyShortNameCN = emailConfig.CompanyShortNameCN };
//MFA 邮箱验证 前端传递用户Id 和MFACode
if (loginUser.UserId != null && _verifyConfig.CurrentValue.OpenLoginMFA)
var returnModel = await _userService.Login(loginUser.UserName, loginUser.Password);
if (returnModel.IsSuccess)
{
Guid userId = (Guid)loginUser.UserId;
#region GRPC IIS问题 http/2 使
//验证MFA 编码是否有问题 ,前端要拆开,自己调用验证的逻辑
//await _userService.VerifyMFACodeAsync(userId, loginUser.MFACode);
////重试策略
//var defaultMethodConfig = new MethodConfig
//{
// Names = { MethodName.Default },
// RetryPolicy = new RetryPolicy
// {
// MaxAttempts = 3,
// InitialBackoff = TimeSpan.FromSeconds(1),
// MaxBackoff = TimeSpan.FromSeconds(5),
// BackoffMultiplier = 1.5,
// RetryableStatusCodes = { Grpc.Core.StatusCode.Unavailable }
// }
//};
//var loginUser = await _userRepository.Where(u => u.UserName.Equals(userName) && u.Password == password).ProjectTo<UserBasicInfo>(_mapper.ConfigurationProvider).FirstOrDefaultAsync();
//#region unable to trust the certificate then the gRPC client can be configured to ignore the invalid certificate
var basicInfo = await _userService.GetUserBasicInfo(userId, loginUser.Password);
//var httpHandler = new HttpClientHandler();
//// Return `true` to allow certificates that are untrusted/invalid
//httpHandler.ServerCertificateCustomValidationCallback =
// HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
var loginReturn = new LoginReturnDTO() { BasicInfo = basicInfo };
loginReturn.JWTStr = _tokenService.GetToken(IRaCISClaims.Create(loginReturn.BasicInfo));
//////这一句是让grpc支持本地 http 如果本地访问部署在服务器上,那么是访问不成功的
//AppContext.SetSwitch(
// "System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
//#endregion
//var grpcAdress = configuration.GetValue<string>("GrpcAddress");
////var grpcAdress = "http://localhost:7200";
//var channel = GrpcChannel.ForAddress(grpcAdress, new GrpcChannelOptions
//{
// HttpHandler = httpHandler,
// ServiceConfig = new ServiceConfig { MethodConfigs = { defaultMethodConfig } }
//});
////var channel = GrpcChannel.ForAddress(grpcAdress);
//var grpcClient = new TokenGrpcService.TokenGrpcServiceClient(channel);
//var userInfo = returnModel.Data.BasicInfo;
//var tokenResponse = grpcClient.GetUserToken(new GetTokenReuqest()
//{
// Id = userInfo.Id.ToString(),
// ReviewerCode = userInfo.ReviewerCode,
// IsAdmin = userInfo.IsAdmin,
// RealName = userInfo.RealName,
// UserTypeEnumInt = (int)userInfo.UserTypeEnum,
// UserTypeShortName = userInfo.UserTypeShortName,
// UserName = userInfo.UserName
//});
//returnModel.Data.JWTStr = tokenResponse.Token;
#endregion
returnModel.Data.JWTStr = _tokenService.GetToken(IRaCISClaims.Create(returnModel.Data.BasicInfo));
// 创建一个 CookieOptions 对象,用于设置 Cookie 的属性
var option = new CookieOptions
{
Expires = DateTime.Now.AddMonths(1), // 设置过期时间为 30 分钟之后
HttpOnly = false, // 确保 cookie 只能通过 HTTP 访问
SameSite = Microsoft.AspNetCore.Http.SameSiteMode.None, // 设置 SameSite 属性
Expires = DateTime.Now.AddMonths(1),
HttpOnly = true, // 确保 cookie 只能通过 HTTP 访问
SameSite = Microsoft.AspNetCore.Http.SameSiteMode.Unspecified, // 设置 SameSite 属性
Secure = false // 确保 cookie 只能通过 HTTPS 访问
};
HttpContext.Response.Cookies.Append("access_token", loginReturn.JWTStr, option);
// 验证阅片休息时间
await readingImageTaskService.ResetReadingRestTime(userId);
await _fusionCache.SetAsync(CacheKeys.UserToken(userId), loginReturn.JWTStr, TimeSpan.FromDays(7));
await _fusionCache.SetAsync(CacheKeys.UserAutoLoginOut(userId), DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), TimeSpan.FromMinutes(_verifyConfig.CurrentValue.AutoLoginOutMinutes));
loginReturn.CompanyInfo = companyInfo;
return ResponseOutput.Ok(loginReturn);
}
else
{
var returnModel = await _userService.Login(loginUser.UserName, loginUser.Password);
if (returnModel.IsSuccess)
{
#region GRPC IIS问题 http/2 使
////重试策略
//var defaultMethodConfig = new MethodConfig
//{
// Names = { MethodName.Default },
// RetryPolicy = new RetryPolicy
// {
// MaxAttempts = 3,
// InitialBackoff = TimeSpan.FromSeconds(1),
// MaxBackoff = TimeSpan.FromSeconds(5),
// BackoffMultiplier = 1.5,
// RetryableStatusCodes = { Grpc.Core.StatusCode.Unavailable }
// }
//};
//#region unable to trust the certificate then the gRPC client can be configured to ignore the invalid certificate
//var httpHandler = new HttpClientHandler();
//// Return `true` to allow certificates that are untrusted/invalid
//httpHandler.ServerCertificateCustomValidationCallback =
// HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
//////这一句是让grpc支持本地 http 如果本地访问部署在服务器上,那么是访问不成功的
//AppContext.SetSwitch(
// "System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
//#endregion
//var grpcAdress = configuration.GetValue<string>("GrpcAddress");
////var grpcAdress = "http://localhost:7200";
//var channel = GrpcChannel.ForAddress(grpcAdress, new GrpcChannelOptions
//{
// HttpHandler = httpHandler,
// ServiceConfig = new ServiceConfig { MethodConfigs = { defaultMethodConfig } }
//});
////var channel = GrpcChannel.ForAddress(grpcAdress);
//var grpcClient = new TokenGrpcService.TokenGrpcServiceClient(channel);
//var userInfo = returnModel.Data.BasicInfo;
//var tokenResponse = grpcClient.GetUserToken(new GetTokenReuqest()
//{
// Id = userInfo.Id.ToString(),
// ReviewerCode = userInfo.ReviewerCode,
// IsAdmin = userInfo.IsAdmin,
// RealName = userInfo.RealName,
// UserTypeEnumInt = (int)userInfo.UserTypeEnum,
// UserTypeShortName = userInfo.UserTypeShortName,
// UserName = userInfo.UserName
//});
//returnModel.Data.JWTStr = tokenResponse.Token;
#endregion
var userId = returnModel.Data.BasicInfo.Id;
if (_verifyConfig.CurrentValue.OpenLoginMFA)
{
//MFA 发送邮件
returnModel.Data.IsMFA = true;
var email = returnModel.Data.BasicInfo.EMail;
var hiddenEmail = IRCEmailPasswordHelper.MaskEmail(email);
returnModel.Data.BasicInfo.EMail = hiddenEmail;
//修改密码
if (returnModel.Data.BasicInfo.IsFirstAdd || returnModel.Data.BasicInfo.LoginState == 1)
{
returnModel.Data.JWTStr = _tokenService.GetToken(IRaCISClaims.Create(returnModel.Data.BasicInfo));
}
else
{
//正常登录才发送邮件
await _userService.SendMFAEmail(userId);
}
}
else
{
returnModel.Data.JWTStr = _tokenService.GetToken(IRaCISClaims.Create(returnModel.Data.BasicInfo));
// 创建一个 CookieOptions 对象,用于设置 Cookie 的属性
var option = new CookieOptions
{
Expires = DateTime.Now.AddMonths(1), // 设置过期时间为 30 分钟之后
HttpOnly = false, // 确保 cookie 只能通过 HTTP 访问
SameSite = Microsoft.AspNetCore.Http.SameSiteMode.None, // 设置 SameSite 属性
Secure = false // 确保 cookie 只能通过 HTTPS 访问
};
HttpContext.Response.Cookies.Append("access_token", returnModel.Data.JWTStr, option);
// 验证阅片休息时间
await readingImageTaskService.ResetReadingRestTime(returnModel.Data.BasicInfo.Id);
await _fusionCache.SetAsync(CacheKeys.UserToken(userId), returnModel.Data.JWTStr, TimeSpan.FromDays(7));
await _fusionCache.SetAsync(CacheKeys.UserAutoLoginOut(userId), DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), TimeSpan.FromMinutes(_verifyConfig.CurrentValue.AutoLoginOutMinutes));
}
}
returnModel.Data.CompanyInfo = companyInfo;
return returnModel;
HttpContext.Response.Cookies.Append("access_token", returnModel.Data.JWTStr, option);
}
var userId = returnModel.Data.BasicInfo.Id.ToString();
//provider.Set(userId, userId, TimeSpan.FromMinutes(AppSettings.LoginExpiredTimeSpan));
await provider.SetAsync(userId.ToString(), returnModel.Data.JWTStr, TimeSpan.FromDays(7));
return returnModel;
}
[AllowAnonymous]
[HttpGet, Route("user/getPublicKey")]
public IResponseOutput GetPublicKey([FromServices] IOptionsMonitor<IRCEncreptOption> _IRCEncreptOption)
[HttpGet("user/GenerateSTS")]
public IResponseOutput GenerateSTS( [FromServices] IWebHostEnvironment webHostEnvironment)
{
//var pemPublicKey = Encoding.UTF8.GetString(Convert.FromBase64String(_IRCEncreptOption.CurrentValue.Base64RSAPublicKey));
var configuration = new ConfigurationBuilder()
.AddJsonFile($"appsettings.{webHostEnvironment.EnvironmentName}.json")
.Build();
IClientProfile profile = DefaultProfile.GetProfile("cn-shanghai", configuration["AliyunOSS:accessKeyId"], configuration["AliyunOSS:accessKeySecret"]);
DefaultAcsClient client = new DefaultAcsClient(profile);
// 创建一个STS请求
AssumeRoleRequest request = new AssumeRoleRequest
{
RoleArn = "acs:ram::1899121822495495:role/oss-upload", // 角色ARN,需要替换为你的角色ARN
RoleSessionName = $"session-name-{NewId.NextGuid()}", // 角色会话名称,可自定义
DurationSeconds = 900, // 令牌有效期(单位:秒),这里设置为1小时
};
AssumeRoleResponse response = client.GetAcsResponse(request);
// 返回STS令牌信息给前端
var stsToken = new
{
AccessKeyId = response.Credentials.AccessKeyId,
AccessKeySecret = response.Credentials.AccessKeySecret,
SecurityToken = response.Credentials.SecurityToken,
Expiration = response.Credentials.Expiration,
Region= configuration["AliyunOSS:region"],
BucketName = configuration["AliyunOSS:bucketName"],
ViewEndpoint = configuration["AliyunOSS:viewEndpoint"],
};
return ResponseOutput.Ok(stsToken);
return ResponseOutput.Ok(_IRCEncreptOption.CurrentValue.Base64RSAPublicKey);
}
[HttpGet, Route("imageShare/ShareImage")]
[AllowAnonymous]
public IResponseOutput ShareImage([FromServices] ITokenService _tokenService)
@@ -320,98 +200,6 @@ namespace IRaCIS.Api.Controllers
return ResponseOutput.Ok("/showdicom?studyId=f7b67793-8155-0223-2f15-118f2642efb8&type=Share&token=" + token);
}
[HttpGet("user/GetObjectStoreToken")]
public async Task<IResponseOutput> GetObjectStoreTokenAsync([FromServices] IOptionsMonitor<ObjectStoreServiceOptions> options, [FromServices] IOSSService _oSSService)
{
var result = _oSSService.GetObjectStoreTempToken();
//result.AWS = await GetAWSTemToken(options.CurrentValue);
return ResponseOutput.Ok(result);
}
private async Task<AWSTempToken> GetAWSTemToken(ObjectStoreServiceOptions serviceOption)
{
var awsOptions = serviceOption.AWS;
//aws 临时凭证
// 创建 STS 客户端
var stsClient = new AmazonSecurityTokenServiceClient(awsOptions.AccessKeyId, awsOptions.SecretAccessKey);
// 使用 AssumeRole 请求临时凭证
var assumeRoleRequest = new AssumeRoleRequest
{
RoleArn = awsOptions.RoleArn, // 角色 ARN
RoleSessionName = $"session-name-{NewId.NextGuid()}",
DurationSeconds = awsOptions.DurationSeconds // 临时凭证有效期
};
var assumeRoleResponse = await stsClient.AssumeRoleAsync(assumeRoleRequest);
var credentials = assumeRoleResponse.Credentials;
var tempToken = new AWSTempToken()
{
AccessKeyId = credentials.AccessKeyId,
SecretAccessKey = credentials.SecretAccessKey,
SessionToken = credentials.SessionToken,
Expiration = credentials.Expiration,
Region = awsOptions.Region,
BucketName = awsOptions.BucketName,
EndPoint = awsOptions.EndPoint,
ViewEndpoint = awsOptions.ViewEndpoint,
};
return tempToken;
}
#region aliyun-net-sdk-sts
//[HttpGet("user/GenerateSTS")]
//public IResponseOutput GenerateSTS([FromServices] IOptionsMonitor<AliyunOSSOptions> options)
//{
// var ossOptions = options.CurrentValue;
// IClientProfile profile = DefaultProfile.GetProfile(ossOptions.regionId, ossOptions.accessKeyId, ossOptions.accessKeySecret);
// DefaultAcsClient client = new DefaultAcsClient(profile);
// // 创建一个STS请求
// AssumeRoleRequest request = new AssumeRoleRequest
// {
// RoleArn = ossOptions.roleArn, // 角色ARN,需要替换为你的角色ARN
// RoleSessionName = $"session-name-{NewId.NextGuid()}", // 角色会话名称,可自定义
// DurationSeconds = 900, // 令牌有效期(单位:秒),这里设置为1小时
// };
// AssumeRoleResponse response = client.GetAcsResponse(request);
// // 返回STS令牌信息给前端
// var stsToken = new
// {
// AccessKeyId = response.Credentials.AccessKeyId,
// AccessKeySecret = response.Credentials.AccessKeySecret,
// SecurityToken = response.Credentials.SecurityToken,
// Expiration = response.Credentials.Expiration,
// Region = ossOptions.region,
// BucketName = ossOptions.bucketName,
// ViewEndpoint = ossOptions.viewEndpoint,
// };
// return ResponseOutput.Ok(stsToken);
//}
#endregion
@@ -448,7 +236,7 @@ namespace IRaCIS.Api.Controllers
[HttpGet, Route("ip")]
[AllowAnonymous]
public IResponseOutput Get([FromServices] IHttpContextAccessor _context)
public IResponseOutput Get([FromServices] IHttpContextAccessor _context/*, [FromServices] IUserService _userService*/)
{
StringBuilder sb = new StringBuilder();
@@ -469,7 +257,7 @@ namespace IRaCIS.Api.Controllers
[HttpGet, Route("ip2")]
[AllowAnonymous]
public IResponseOutput Get2([FromServices] IHttpContextAccessor _context)
public IResponseOutput Get2([FromServices] IHttpContextAccessor _context, [FromServices] IRepository _userService)
{
StringBuilder sb = new StringBuilder();
@@ -7,7 +7,7 @@ using IRaCIS.Core.Application.Filter;
using IRaCIS.Core.Infrastructure.Extention;
using Microsoft.AspNetCore.Authorization;
using System.Threading.Tasks;
using IRaCIS.Core.Application.Service;
using IRaCIS.Application.Services;
using IRaCIS.Core.Application.Service.Inspection.DTO;
using IRaCIS.Core.Infra.EFCore;
using IRaCIS.Core.Domain.Models;
@@ -19,12 +19,18 @@ namespace IRaCIS.Core.API.Controllers.Special
//谨慎修改 涉及到财务模块
[ApiController, Authorize, ApiExplorerSettings(GroupName = "Financial")]
public class FinancialChangeController(
ITrialService _trialService,
ICalculateService _calculateService,
IStringLocalizer _localizer) : ControllerBase
public class FinancialChangeController : ControllerBase
{
private readonly ITrialService _trialService;
private IStringLocalizer _localizer { get; set; }
public FinancialChangeController(ITrialService trialService, IStringLocalizer localizer
)
{
_localizer = localizer;
_trialService = trialService;
}
//[TrialAudit(AuditType.TrialAudit, AuditOptType.AddOrUpdateTrial)]
@@ -54,268 +60,28 @@ namespace IRaCIS.Core.API.Controllers.Special
var userId = Guid.Parse(User.FindFirst("id").Value);
var result = await _trialService.AddOrUpdateTrial(param);
if (_trialService.TrialExpeditedChange)
{
var needCalReviewerIds = await _trialService.GetTrialEnrollmentReviewerIds(param.Id.Value);
var calcList = await _calculateService.GetNeedCalculateReviewerList(Guid.Empty, string.Empty);
calcList.ForEach(t =>
{
if (needCalReviewerIds.Contains(t.DoctorId))
{
_calculateService.CalculateMonthlyPayment(new CalculateDoctorAndMonthDTO()
{
NeedCalculateReviewers = new List<Guid>()
{
t.DoctorId
},
CalculateMonth = DateTime.Parse(t.YearMonth)
}, User.FindFirst("id").Value);
}
});
}
return result;
}
/// <summary>
/// 添加或更新工作量[AUTH]
/// </summary>
/// <param name="_trialWorkloadService"></param>
/// <param name="workLoadAddOrUpdateModel"></param>
/// <returns></returns>
[HttpPost, Route("doctorWorkload/workLoadAddOrUpdate")]
[TypeFilter(typeof(TrialResourceFilter),Arguments = new object[] { "AfterStopCannNotOpt" })]
public async Task<IResponseOutput> WorkLoadAddOrUpdate([FromServices] IDoctorWorkloadService _trialWorkloadService, WorkloadCommand workLoadAddOrUpdateModel)
{
var userId = Guid.Parse(User.FindFirst("id").Value);
var result = await _trialWorkloadService.AddOrUpdateWorkload(workLoadAddOrUpdateModel, userId);
if (result.IsSuccess && workLoadAddOrUpdateModel.DataFrom == 2)
{
await _calculateService.CalculateMonthlyPayment(new CalculateDoctorAndMonthDTO()
{
NeedCalculateReviewers = new List<Guid>()
{
workLoadAddOrUpdateModel.DoctorId
},
CalculateMonth = workLoadAddOrUpdateModel.WorkTime
}, User.FindFirst("id").Value);
}
return result;
}
[HttpDelete, Route("doctorWorkload/deleteWorkLoad/{id:guid}/{trialId:guid}")]
[TypeFilter(typeof(TrialResourceFilter),Arguments = new object[] { "AfterStopCannNotOpt" })]
public async Task<IResponseOutput> DeleteWorkLoad([FromServices] IDoctorWorkloadService _trialWorkloadService, Guid id)
{
//先判断该工作量的费用是否被锁定,如果被锁定,则不能删除
var workload = await _trialWorkloadService.GetWorkloadDetailById(id);
var yearMonth = workload.WorkTime.ToString("yyyy-MM");
var isLock = await _calculateService.IsLock(workload.DoctorId, yearMonth);
if (isLock)
{
//---Expenses have been settled and workload can not be reset.
return ResponseOutput.NotOk(_localizer["Financial_ChargeSettled"]);
}
var deleteResult = await _trialWorkloadService.DeleteWorkload(id);
if (workload.DataFrom == (int)Domain.Share.WorkLoadFromStatus.FinalConfirm)
{
await _calculateService.CalculateMonthlyPayment(new CalculateDoctorAndMonthDTO()
{
NeedCalculateReviewers = new List<Guid>()
{
workload.DoctorId
},
CalculateMonth = workload.WorkTime
}, User.FindFirst("id").Value);
}
return deleteResult;
}
/// <summary>
/// 添加或更新汇率(会触发没有对锁定的费用计算)
/// </summary>
[HttpPost, Route("exchangeRate/addOrUpdateExchangeRate")]
public async Task<IResponseOutput> AddOrUpdateExchangeRate([FromServices] IExchangeRateService _exchangeRateService, [FromServices] IPaymentAdjustmentService _costAdjustmentService, ExchangeRateCommand addOrUpdateModel)
{
var result = await _exchangeRateService.AddOrUpdateExchangeRate(addOrUpdateModel);
var calcList = await _calculateService.GetNeedCalculateReviewerList(Guid.Empty, addOrUpdateModel.YearMonth);
foreach (var item in calcList)
{
if (item != null)
{
await _calculateService.CalculateMonthlyPayment(new CalculateDoctorAndMonthDTO()
{
NeedCalculateReviewers = new List<Guid>()
{
item.DoctorId
},
CalculateMonth = DateTime.Parse(item.YearMonth)
}, User.FindFirst("id").Value);
}
}
await _costAdjustmentService.CalculateCNY(addOrUpdateModel.YearMonth, addOrUpdateModel.Rate);
return result;
}
/// <summary>
/// 添加或更新 职称单价[AUTH]
/// </summary>
[HttpPost, Route("rankPrice/addOrUpdateRankPrice")]
public async Task<IResponseOutput> AddOrUpdateRankPrice([FromServices] IReviewerPayInfoService _reviewerPayInfoService, [FromServices] IRankPriceService _rankPriceService, RankPriceCommand addOrUpdateModel)
{
if (addOrUpdateModel.Id != Guid.Empty && addOrUpdateModel.Id != null)
{
var needCalReviewerIds =await _reviewerPayInfoService.GetReviewerIdByRankId(Guid.Parse(addOrUpdateModel.Id.ToString()));
var calcList = await _calculateService.GetNeedCalculateReviewerList(Guid.Empty, string.Empty);
foreach (var item in calcList)
{
if (item != null && needCalReviewerIds.Contains(item.DoctorId))
{
await _calculateService.CalculateMonthlyPayment(new CalculateDoctorAndMonthDTO()
{
NeedCalculateReviewers = new List<Guid>()
{
item.DoctorId
},
CalculateMonth = DateTime.Parse(item.YearMonth)
}, User.FindFirst("id").Value);
}
}
}
var userId = Guid.Parse(User.FindFirst("id").Value);
return await _rankPriceService.AddOrUpdateRankPrice(addOrUpdateModel, userId);
}
/// <summary>
/// 添加或更新(替换)医生支付展信息[AUTH]
/// </summary>
[HttpPost, Route("reviewerPayInfo/addOrUpdateReviewerPayInfo")]
public async Task<IResponseOutput> AddOrUpdateReviewerPayInfo([FromServices] IReviewerPayInfoService _doctorPayInfoService, ReviewerPayInfoCommand addOrUpdateModel)
{
var userId = Guid.Parse(User.FindFirst("id").Value);
var result =await _doctorPayInfoService.AddOrUpdateReviewerPayInfo(addOrUpdateModel, userId);
var calcList = await _calculateService.GetNeedCalculateReviewerList(addOrUpdateModel.DoctorId, string.Empty);
foreach (var item in calcList)
{
if (item != null)
{
await _calculateService.CalculateMonthlyPayment(new CalculateDoctorAndMonthDTO()
{
NeedCalculateReviewers = new List<Guid>()
{
item.DoctorId
},
CalculateMonth = DateTime.Parse(item.YearMonth)
}, User.FindFirst("id").Value);
}
}
return result;
}
/// <summary>
/// 保存(替换)项目支付价格信息(会触发没有被锁定的费用计算)[AUTH]
/// </summary>
[HttpPost, Route("trialPaymentPrice/addOrUpdateTrialPaymentPrice")]
public async Task<IResponseOutput> AddOrUpdateTrialPaymentPrice([FromServices] ITrialPaymentPriceService _trialPaymentPriceService, TrialPaymentPriceCommand addOrUpdateModel)
{
var userId = Guid.Parse(User.FindFirst("id").Value);
var result =await _trialPaymentPriceService.AddOrUpdateTrialPaymentPrice(addOrUpdateModel);
var needCalReviewerIds = await _trialService.GetTrialEnrollmentReviewerIds(addOrUpdateModel.TrialId);
var calcList = await _calculateService.GetNeedCalculateReviewerList(Guid.Empty, string.Empty);
foreach (var item in calcList)
{
if (item != null && needCalReviewerIds.Contains(item.DoctorId))
{
await _calculateService.CalculateMonthlyPayment(new CalculateDoctorAndMonthDTO()
{
NeedCalculateReviewers = new List<Guid>()
{
item.DoctorId
},
CalculateMonth = DateTime.Parse(item.YearMonth)
}, User.FindFirst("id").Value);
}
}
return result;
}
/// <summary>
/// 批量更新奖励费用[AUTH]
/// </summary>
[HttpPost, Route("volumeReward/addOrUpdatevolumeRewardPriceList")]
public async Task<IResponseOutput> AddOrUpdateAwardPriceList([FromServices] IVolumeRewardService _volumeRewardService, IEnumerable<AwardPriceCommand> addOrUpdateModel)
{
var result =await _volumeRewardService.AddOrUpdateVolumeRewardPriceList(addOrUpdateModel);
var calcList = await _calculateService.GetNeedCalculateReviewerList(Guid.Empty, string.Empty);
foreach (var item in calcList)
{
if (item != null)
{
await _calculateService.CalculateMonthlyPayment(new CalculateDoctorAndMonthDTO()
{
NeedCalculateReviewers = new List<Guid>()
{
item.DoctorId
},
CalculateMonth = DateTime.Parse(item.YearMonth)
}, User.FindFirst("id").Value);
}
}
return result;
}
/// <summary>
/// 计算医生月度费用,并将计算的结果存入费用表
/// </summary>
[HttpPost, Route("financial/calculateMonthlyPayment")]
public async Task<IResponseOutput> CalculateMonthlyPayment(CalculateDoctorAndMonthDTO param)
{
if (!ModelState.IsValid)
{
//---Invalid parameter.
return ResponseOutput.NotOk(_localizer["Financial_InvalidParameter"]);
}
return await _calculateService.CalculateMonthlyPayment(param, User.FindFirst("id").Value);
}
/// <summary>
/// Financials /Monthly Payment 列表查询接口
/// </summary>
[HttpPost, Route("financial/getMonthlyPaymentList")]
public async Task<IResponseOutput<PaymentDTO>> GetMonthlyPaymentList([FromServices] IPaymentService _paymentService, [FromServices] IExchangeRateService _exchangeRateService, MonthlyPaymentQueryDTO queryParam)
{
return ResponseOutput.Ok(new PaymentDTO
{
CostList = await _paymentService.GetMonthlyPaymentList(queryParam),
ExchangeRate = await _exchangeRateService.GetExchangeRateByMonth(queryParam.StatisticsDate.ToString("yyyy-MM"))
});
}
}
}
@@ -26,19 +26,78 @@ namespace IRaCIS.Core.API.Controllers
[ApiController, ApiExplorerSettings(GroupName = "Reviewer")]
[UnitOfWork]
public class InspectionController(
ITrialDocumentService _trialDocumentService,
IReadingImageTaskService _iReadingImageTaskService,
ITrialConfigService _trialConfigService,
IClinicalAnswerService _clinicalAnswerService,
IReadingClinicalDataService _readingClinicalDataService,
IQCOperationService _qCOperationService,
IInspectionService _inspectionService,
IReadingMedicalReviewService _readingMedicalReviewService,
IReadingMedicineQuestionService _readingMedicineQuestionService
) : ControllerBase
public class InspectionController : ControllerBase
{
private readonly IRepository _repository;
private readonly IMapper _mapper;
private readonly IUserInfo _userInfo;
private readonly ITrialDocumentService _trialDocumentService;
private readonly IQCListService _qCListService;
private readonly IReadingImageTaskService _iReadingImageTaskService;
private readonly IHttpContextAccessor _httpContext;
private readonly ITrialConfigService _trialConfigService;
private readonly INoneDicomStudyService _noneDicomStudyService;
private readonly IClinicalAnswerService _clinicalAnswerService;
private readonly ISubjectService _subjectService;
private readonly IReadingClinicalDataService _readingClinicalDataService;
private readonly ISubjectVisitService _subjectVisitService;
private readonly IQCOperationService _qCOperationService;
private readonly IClinicalDataService _clinicalDataService;
private readonly IVisitPlanService _visitPlanService;
private readonly IInspectionService _inspectionService;
private readonly IReadingMedicalReviewService _readingMedicalReviewService;
private readonly IReadingMedicineQuestionService _readingMedicineQuestionService;
private readonly IRepository<DataInspection> _dataInspectionRepository;
private delegate Task<IResponseOutput> executionFun(dynamic data);
public InspectionController(IRepository repository,
IRepository<DataInspection> _repositoryDataInspection,
IMapper mapper, IUserInfo userInfo,
ITrialDocumentService trialDocumentService,
IRepository<DataInspection> dataInspectionRepository,
IQCListService _qCListService,
IReadingImageTaskService _iReadingImageTaskService,
IHttpContextAccessor httpContext,
IInspectionService sinspectionService,
IReadingMedicalReviewService readingMedicalReviewService,
IReadingMedicineQuestionService readingMedicineQuestionService,
ITrialConfigService _trialConfigService,
INoneDicomStudyService noneDicomStudyService,
IClinicalAnswerService clinicalAnswerService,
ISubjectService _subjectService,
IReadingClinicalDataService _readingClinicalDataService,
ISubjectVisitService subjectVisitService,
IQCOperationService qCOperationService,
IClinicalDataService clinicalDataService,
IVisitPlanService visitPlanService
)
{
this._repository = repository;
this._mapper = mapper;
this._userInfo = userInfo;
this._inspectionService = sinspectionService;
this._readingMedicalReviewService = readingMedicalReviewService;
this._readingMedicineQuestionService = readingMedicineQuestionService;
this._trialDocumentService = trialDocumentService;
this._qCListService = _qCListService;
this._iReadingImageTaskService = _iReadingImageTaskService;
this._httpContext = httpContext;
this._trialConfigService = _trialConfigService;
this._noneDicomStudyService = noneDicomStudyService;
this._clinicalAnswerService = clinicalAnswerService;
this._subjectService = _subjectService;
this._readingClinicalDataService = _readingClinicalDataService;
this._subjectVisitService = subjectVisitService;
this._qCOperationService = qCOperationService;
this._clinicalDataService = clinicalDataService;
this._visitPlanService = visitPlanService;
this._dataInspectionRepository = dataInspectionRepository;
}
#region
/// <summary>
/// 获取稽查数据
@@ -249,28 +308,11 @@ namespace IRaCIS.Core.API.Controllers
}
/// <summary>
/// PM签名一致性分析临床数据
/// </summary>
/// <param name="opt"></param>
/// <returns></returns>
[HttpPost, Route("Inspection/ReadingClinicalData/SignConsistencyAnalysisReadingClinicalData")]
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
[UnitOfWork]
public async Task<IResponseOutput> SignConsistencyAnalysisReadingClinicalData(DataInspectionDto<SignConsistencyAnalysisReadingClinicalDataInDto> opt)
{
var singid = await _inspectionService.RecordSing(opt.SignInfo);
var result = await _readingClinicalDataService.SignConsistencyAnalysisReadingClinicalData(opt.Data);
await _inspectionService.CompletedSign(singid, result);
return result;
}
/// <summary>
/// 提交结构化录入并签名
/// </summary>
/// <param name="opt"></param>
/// <returns></returns>
/// <summary>
/// 提交结构化录入并签名
/// </summary>
/// <param name="opt"></param>
/// <returns></returns>
[HttpPost, Route("Inspection/ClinicalAnswer/SubmitClinicalFormAndSign")]
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
@@ -360,19 +402,6 @@ namespace IRaCIS.Core.API.Controllers
return result;
}
[HttpPost, Route("Inspection/configTrialBasicInfo/ConfigTrialPACSInfoConfirm")]
[UnitOfWork]
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "BeforeOngoingCantOpt" })]
public async Task<IResponseOutput> ConfigTrialPACSInfoConfirm(DataInspectionDto<TrialPACSConfig> opt)
{
opt.Data.IsTrialPACSConfirmed = true;
var singid = await _inspectionService.RecordSing(opt.SignInfo);
var result = await _trialConfigService.ConfigTrialPACSInfo(opt.Data);
await _inspectionService.CompletedSign(singid, result);
return result;
}
/// <summary>
/// 签名确认
/// </summary>
@@ -390,30 +419,13 @@ namespace IRaCIS.Core.API.Controllers
}
/// <summary>
/// 重置并同步项目阅片标准
/// </summary>
/// <returns></returns>
[HttpPost, Route("Inspection/ReadingCriterion/ResetAndAsyncCriterion")]
[UnitOfWork]
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
public async Task<IResponseOutput> ResetAndAsyncCriterion(DataInspectionDto<ResetAndAsyncCriterionInDto> opt)
{
var singid = await _inspectionService.RecordSing(opt.SignInfo);
var result = await _trialConfigService.ResetAndAsyncCriterion(opt.Data);
await _inspectionService.CompletedSign(singid, result);
return result;
}
/// <summary>
/// CRC RequestToQC 批量提交
/// </summary>
/// <param name="opt"></param>
/// <returns></returns>
[HttpPost, Route("Inspection/QCOperation/CRCRequestToQC")]
/// <summary>
/// CRC RequestToQC 批量提交
/// </summary>
/// <param name="opt"></param>
/// <returns></returns>
[HttpPost, Route("Inspection/QCOperation/CRCRequestToQC")]
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
[UnitOfWork]
public async Task<IResponseOutput> CRCRequestToQC(DataInspectionDto<CRCRequestToQCCommand> opt)
File diff suppressed because it is too large Load Diff
+26
View File
@@ -0,0 +1,26 @@
#See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging.
FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
WORKDIR /app
EXPOSE 80
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
WORKDIR /src
COPY ["IRaCIS.Core.API/IRaCIS.Core.API.csproj", "IRaCIS.Core.API/"]
COPY ["IRaCIS.Core.Application/IRaCIS.Core.Application.csproj", "IRaCIS.Core.Application/"]
COPY ["IRaCIS.Core.Infra.EFCore/IRaCIS.Core.Infra.EFCore.csproj", "IRaCIS.Core.Infra.EFCore/"]
COPY ["IRaCIS.Core.Domain/IRaCIS.Core.Domain.csproj", "IRaCIS.Core.Domain/"]
COPY ["IRaCIS.Core.Domain.Share/IRaCIS.Core.Domain.Share.csproj", "IRaCIS.Core.Domain.Share/"]
COPY ["IRaCIS.Core.Infrastructure/IRaCIS.Core.Infrastructure.csproj", "IRaCIS.Core.Infrastructure/"]
RUN dotnet restore "IRaCIS.Core.API/IRaCIS.Core.API.csproj"
COPY . .
WORKDIR "/src/IRaCIS.Core.API"
RUN dotnet build "IRaCIS.Core.API.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "IRaCIS.Core.API.csproj" -c Release -o /app/publish /p:UseAppHost=false
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "IRaCIS.Core.API.dll"]
+16 -16
View File
@@ -1,16 +1,16 @@
// 使用的是proto3版本
// 使用的是proto3版本
syntax = "proto3";
// 定义命名空间,后续生成代码时就会生成对应的命名空间
// 定义命名空间,后续生成代码时就会生成对应的命名空间
option csharp_namespace = "gRPC.ZHiZHUN.AuthServer.protos";
/*
每一句需要用分号结尾
message 用来定义请求和返回数据格式
tag message后面的值数字代表是字段的标识(tag),不是赋值,
每一句需要用分号结尾
message 用来定义请求和返回数据格式
tag message后面的值数字代表是字段的标识(tag),不是赋值,
*/
// 新增用户时需要传递数据消息, 可理解为一个类
// 新增用户时需要传递数据消息, 可理解为一个类
message GetTokenReuqest{
string id=1;
string userName=2;
@@ -22,48 +22,48 @@ message GetTokenReuqest{
}
// 新增时返回的消息格式
// 新增时返回的消息格式
message GetTokenResponse {
int32 code=1;
string token =2;
}
// service 用标识定义服务的,里面写对应的方法
// service 用标识定义服务的,里面写对应的方法
service TokenGrpcService{
// 获取token
// 获取token
rpc GetUserToken(GetTokenReuqest) returns (GetTokenResponse);
}
/*
// 新增用户时需要传递数据消息, 可理解为一个类
// 新增用户时需要传递数据消息, 可理解为一个类
message AddUserReuqest{
string name=1;
int32 age=2;
bool isBoy=3;
}
// 新增时返回的消息格式
// 新增时返回的消息格式
message ResultResponse {
int32 code=1;
string msg =2;
}
//传递的查询条件信息格式,可理解为平时传入的查询条件对象
//传递的查询条件信息格式,可理解为平时传入的查询条件对象
message QueryUserReuqest{
string name=1;
}
//查询返回的用户信息格式,可理解为返回的类
//查询返回的用户信息格式,可理解为返回的类
message UserInfoResponse {
string name=1;
int32 age=2;
string gender=3;
}
// service 用标识定义服务的,里面写对应的方法
// service 用标识定义服务的,里面写对应的方法
service UserService{
// 新增用户
// 新增用户
rpc AddUser(AddUserReuqest) returns (ResultResponse);
// 查询用户
// 查询用户
rpc GetAllUser(QueryUserReuqest) returns (UserInfoResponse);
}
*/
@@ -0,0 +1,107 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<SignAssembly>false</SignAssembly>
<UserSecretsId>354572d4-9e15-4099-807c-63a2d29ff9f2</UserSecretsId>
<LangVersion>default</LangVersion>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DocumentationFile>.\IRaCIS.Core.API.xml</DocumentationFile>
<NoWarn>1701;1702;1591;</NoWarn>
<OutputPath>..\bin\</OutputPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<DocumentationFile>bin\Release\IRaCIS.Core.API.xml</DocumentationFile>
<OutputPath>bin\Release\</OutputPath>
<NoWarn>1701;1702;1591</NoWarn>
</PropertyGroup>
<ItemGroup>
<Compile Remove="Controllers\ReviewerApi\**" />
<Compile Remove="UploadFile\**" />
<Content Remove="Controllers\ReviewerApi\**" />
<Content Remove="UploadFile\**" />
<EmbeddedResource Remove="Controllers\ReviewerApi\**" />
<EmbeddedResource Remove="UploadFile\**" />
<None Remove="Controllers\ReviewerApi\**" />
<None Remove="UploadFile\**" />
</ItemGroup>
<ItemGroup>
<Content Remove="web.config" />
<Content Remove="wwwroot\swagger\ui\abp.js" />
<Content Remove="wwwroot\swagger\ui\abp.swagger.js" />
<Content Remove="wwwroot\swagger\ui\Index.html" />
</ItemGroup>
<ItemGroup>
<None Remove=".preview.jpg" />
<None Remove="GrpcToken.proto" />
<None Remove="IRaCIS.Core.API.xml" />
<None Remove="Protos\GrpcToken.proto" />
</ItemGroup>
<ItemGroup>
<ApplicationDefinition Include="GrpcToken.proto" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="wwwroot\swagger\ui\abp.js" />
<EmbeddedResource Include="wwwroot\swagger\ui\abp.swagger.js" />
<EmbeddedResource Include="wwwroot\swagger\ui\Index.html" />
</ItemGroup>
<ItemGroup>
<Protobuf Include="Protos\GrpcToken.proto">
<GrpcServices>Client</GrpcServices>
</Protobuf>
</ItemGroup>
<ItemGroup>
<PackageReference Include="AspNetCoreRateLimit" Version="4.0.1" />
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="7.2.0" />
<PackageReference Include="EasyCaching.InMemory" Version="1.4.1" />
<PackageReference Include="EasyCaching.Interceptor.Castle" Version="1.4.1" />
<PackageReference Include="Google.Protobuf" Version="3.19.1" />
<PackageReference Include="Grpc.Net.Client" Version="2.41.0" />
<PackageReference Include="Grpc.Tools" Version="2.42.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Hangfire.Tags.SqlServer" Version="1.8.0" />
<PackageReference Include="Invio.Extensions.Authentication.JwtBearer" Version="2.0.1" />
<PackageReference Include="LogDashboard" Version="1.4.8" />
<PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="9.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="6.0.1" />
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="6.0.0" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.14.0" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="6.0.1" />
<PackageReference Include="Serilog.AspNetCore" Version="4.1.0" />
<PackageReference Include="Serilog.Enrichers.ClientInfo" Version="1.1.4" />
<PackageReference Include="Serilog.Sinks.Email" Version="2.4.0" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\IRaCIS.Core.Application\IRaCIS.Core.Application.csproj" />
<ProjectReference Include="..\IRaCIS.Core.Infra.EFCore\IRaCIS.Core.Infra.EFCore.csproj" />
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\PublishProfiles\" />
</ItemGroup>
<ItemGroup>
<Content Update="NLog.config">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ProjectExtensions><VisualStudio><UserProperties anonymizetagsetting_1json__JsonSchema="http://json.schemastore.org/jovo-language-model" /></VisualStudio></ProjectExtensions>
</Project>
+160 -93
View File
@@ -1,112 +1,179 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<SignAssembly>false</SignAssembly>
<UserSecretsId>354572d4-9e15-4099-807c-63a2d29ff9f2</UserSecretsId>
<LangVersion>default</LangVersion>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
<Version>1.0.1.001</Version>
<Company>上海展影医疗科技有限公司</Company>
<Product>IRC影像系统 (EICS)</Product>
<Copyright>上海展影医疗科技有限公司版权所有</Copyright>
</PropertyGroup>
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<SignAssembly>false</SignAssembly>
<UserSecretsId>354572d4-9e15-4099-807c-63a2d29ff9f2</UserSecretsId>
<LangVersion>default</LangVersion>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
<Version>1.0.1.001</Version>
<Company>上海展影医疗科技有限公司</Company>
<Product>IRC影像系统 (EICS)</Product>
<Copyright>上海展影医疗科技有限公司版权所有</Copyright>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DocumentationFile>.\IRaCIS.Core.API.xml</DocumentationFile>
<NoWarn>1701;1702;1591;1570;</NoWarn>
<OutputPath>..\bin\</OutputPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DocumentationFile>.\IRaCIS.Core.API.xml</DocumentationFile>
<NoWarn>1701;1702;1591;</NoWarn>
<OutputPath>..\bin\</OutputPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<DocumentationFile>bin\Release\IRaCIS.Core.API.xml</DocumentationFile>
<OutputPath>bin\Release\</OutputPath>
<NoWarn>1701;1702;1591</NoWarn>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<DocumentationFile>bin\Release\IRaCIS.Core.API.xml</DocumentationFile>
<OutputPath>bin\Release\</OutputPath>
<NoWarn>1701;1702;1591</NoWarn>
</PropertyGroup>
<ItemGroup>
<Compile Remove="wwwroot\**" />
<EmbeddedResource Remove="UploadFile\**" />
<EmbeddedResource Remove="wwwroot\**" />
</ItemGroup>
<ItemGroup>
<Content Remove="web.config" />
<Content Remove="wwwroot\swagger\ui\abp.js" />
<Content Remove="wwwroot\swagger\ui\abp.swagger.js" />
<Content Remove="wwwroot\swagger\ui\Index.html" />
</ItemGroup>
<ItemGroup>
<Content Remove="web.config" />
<Content Remove="wwwroot\swagger\ui\abp.js" />
<Content Remove="wwwroot\swagger\ui\abp.swagger.js" />
<Content Remove="wwwroot\swagger\ui\Index.html" />
</ItemGroup>
<ItemGroup>
<None Remove=".preview.jpg" />
<None Remove="GrpcToken.proto" />
<None Remove="IRaCIS.Core.API.xml" />
<None Remove="Protos\GrpcToken.proto" />
</ItemGroup>
<ItemGroup>
<None Remove=".preview.jpg" />
<None Remove="GrpcToken.proto" />
<None Remove="IRaCIS.Core.API.xml" />
<None Remove="Protos\GrpcToken.proto" />
</ItemGroup>
<ItemGroup>
<ApplicationDefinition Include="GrpcToken.proto" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="wwwroot\swagger\ui\abp.js">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</EmbeddedResource>
<EmbeddedResource Include="wwwroot\swagger\ui\abp.swagger.js">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</EmbeddedResource>
<EmbeddedResource Include="wwwroot\swagger\ui\Index.html">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<Protobuf Include="Protos\GrpcToken.proto">
<GrpcServices>Client</GrpcServices>
</Protobuf>
</ItemGroup>
<ItemGroup>
<ApplicationDefinition Include="GrpcToken.proto" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="wwwroot\swagger\ui\abp.js">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</EmbeddedResource>
<EmbeddedResource Include="wwwroot\swagger\ui\abp.swagger.js">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</EmbeddedResource>
<EmbeddedResource Include="wwwroot\swagger\ui\Index.html">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<Protobuf Include="Protos\GrpcToken.proto">
<GrpcServices>Client</GrpcServices>
</Protobuf>
</ItemGroup>
<ItemGroup>
<PackageReference Include="aliyun-net-sdk-sts" Version="3.1.2" />
<PackageReference Include="AspNetCoreRateLimit" Version="5.0.0" />
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="8.0.0" />
<PackageReference Include="EasyCaching.Interceptor.Castle" Version="1.9.1" />
<PackageReference Include="EasyCaching.Serialization.MessagePack" Version="1.9.1" />
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.1" />
<PackageReference Include="EasyCaching.InMemory" Version="1.9.1" />
<PackageReference Include="Hangfire.AspNetCore" Version="1.8.5" />
<PackageReference Include="Hangfire.Dashboard.BasicAuthorization" Version="1.0.2" />
<PackageReference Include="Hangfire.SqlServer" Version="1.8.5" />
<PackageReference Include="Invio.Extensions.Authentication.JwtBearer" Version="2.0.1" />
<PackageReference Include="LogDashboard" Version="1.4.8" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="6.0.23" />
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="7.0.1" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.17.2" />
<PackageReference Include="Quartz.Extensions.DependencyInjection" Version="3.7.0" />
<PackageReference Include="Quartz.Extensions.Hosting" Version="3.7.0" />
<PackageReference Include="Serilog.AspNetCore" Version="7.0.0" />
<PackageReference Include="Serilog.Enrichers.ClientInfo" Version="2.0.1" />
<PackageReference Include="Serilog.Sinks.Email" Version="2.4.0" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="6.5.0" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="AspNetCoreRateLimit" Version="5.0.0" />
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="10.0.0" />
<ItemGroup>
<ProjectReference Include="..\IRaCIS.Core.Application\IRaCIS.Core.Application.csproj" />
<ProjectReference Include="..\IRaCIS.Core.Infra.EFCore\IRaCIS.Core.Infra.EFCore.csproj" />
</ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.8">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="ConfigMapFileProvider" Version="2.0.1" />
<PackageReference Include="Hangfire.AspNetCore" Version="1.8.14" />
<PackageReference Include="Hangfire.Dashboard.BasicAuthorization" Version="1.0.2" />
<PackageReference Include="Hangfire.InMemory" Version="0.10.3" />
<PackageReference Include="Hangfire.SqlServer" Version="1.8.14" />
<PackageReference Include="Invio.Extensions.Authentication.JwtBearer" Version="2.0.1" />
<PackageReference Include="LogDashboard" Version="1.4.8" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.8" />
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="8.0.0" />
<PackageReference Include="Serilog.AspNetCore" Version="8.0.2" />
<PackageReference Include="Serilog.Enrichers.ClientInfo" Version="2.1.1" />
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="6.7.3" />
</ItemGroup>
<ItemGroup>
<None Include="..\.editorconfig" Link=".editorconfig" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\IRaCIS.Core.Application\IRaCIS.Core.Application.csproj" />
<ProjectReference Include="..\IRaCIS.Core.Infra.EFCore\IRaCIS.Core.Infra.EFCore.csproj" />
</ItemGroup>
<ItemGroup>
<Content Update="appsettings.Uat_Study.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Update="wwwroot\EmailTemplate\AdminAddUser_US.html">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Update="wwwroot\EmailTemplate\AdminResetUser_US.html">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Update="wwwroot\EmailTemplate\AdminResetUser.html">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Update="wwwroot\EmailTemplate\SubjectEnrollConfirmOrPDProgress_US.html">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Update="wwwroot\EmailTemplate\TrialDoctorExistJoin_US.html">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Update="wwwroot\EmailTemplate\TrialDoctorFirstJoin.html">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Update="wwwroot\EmailTemplate\TrialSiteSurveyReject.html">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Update="wwwroot\EmailTemplate\TrialSiteSurveyReject_US.html">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Update="wwwroot\EmailTemplate\TrialDoctorExistJoin.html">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Update="wwwroot\EmailTemplate\TrialUserExistJoin_US.html">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Update="wwwroot\EmailTemplate\TrialUserExistJoin.html">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Update="wwwroot\EmailTemplate\AdminAddUser.html">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Update="wwwroot\EmailTemplate\TrialDoctorFirstJoin_US.html">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Update="wwwroot\EmailTemplate\SubjectEnrollConfirmOrPDProgress.html">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Update="wwwroot\EmailTemplate\TrialUserFirstJoin_US.html">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Update="wwwroot\EmailTemplate\UserOptCommon_US.html">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Update="wwwroot\EmailTemplate\UserOptCommon.html">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Update="wwwroot\EmailTemplate\TrialUserFirstJoin.html">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<None Include="..\.editorconfig" Link=".editorconfig" />
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\PublishProfiles\" />
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\PublishProfiles\" />
</ItemGroup>
<ProjectExtensions><VisualStudio><UserProperties anonymizetagsetting_1json__JsonSchema="http://json.schemastore.org/jovo-language-model" properties_4launchsettings_1json__JsonSchema="" /></VisualStudio></ProjectExtensions>
<ItemGroup>
<Content Update="appsettings.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<None Remove="..\.dockerignore" />
</ItemGroup>
<ProjectExtensions>
<VisualStudio>
<UserProperties properties_4launchsettings_1json__JsonSchema="" />
</VisualStudio>
</ProjectExtensions>
<ItemGroup>
<None Remove="..\.dockerignore" />
</ItemGroup>
</Project>
+11 -128
View File
@@ -16,20 +16,7 @@
医生基本信息 、工作信息 专业信息、审核状态
</summary>
</member>
<member name="M:IRaCIS.Api.Controllers.ExtraController.GetDoctorDetail(IRaCIS.Application.Interfaces.IAttachmentService,IRaCIS.Application.Interfaces.IDoctorService,IRaCIS.Application.Interfaces.IEducationService,IRaCIS.Application.Interfaces.ITrialExperienceService,IRaCIS.Application.Interfaces.IResearchPublicationService,IRaCIS.Application.Interfaces.IVacationService,System.Guid)">
<summary>
获取医生详情
</summary>
<param name="attachmentService"></param>
<param name="_doctorService"></param>
<param name="_educationService"></param>
<param name="_trialExperienceService"></param>
<param name="_researchPublicationService"></param>
<param name="_vacationService"></param>
<param name="doctorId"></param>
<returns></returns>
</member>
<member name="M:IRaCIS.Api.Controllers.ExtraController.Login(IRaCIS.Application.Contracts.UserLoginDTO,ZiggyCreatures.Caching.Fusion.IFusionCache,IRaCIS.Core.Application.Service.IUserService,IRaCIS.Core.Application.Auth.ITokenService,IRaCIS.Core.Application.Contracts.IReadingImageTaskService,Microsoft.Extensions.Options.IOptionsMonitor{IRaCIS.Core.Domain.Share.ServiceVerifyConfigOption},Microsoft.Extensions.Options.IOptionsMonitor{IRaCIS.Core.Domain.Share.SystemEmailSendConfig},IRaCIS.Core.Application.Service.IMailVerificationService)">
<member name="M:IRaCIS.Api.Controllers.ExtraController.Login(IRaCIS.Application.Contracts.UserLoginDTO,EasyCaching.Core.IEasyCachingProvider,IRaCIS.Application.Services.IUserService,IRaCIS.Core.Application.Auth.ITokenService,Microsoft.Extensions.Configuration.IConfiguration)">
<summary> 系统用户登录接口[New] </summary>
</member>
<member name="M:IRaCIS.Core.API.Controllers.Special.FinancialChangeController.AddOrUpdateTrialInspection(IRaCIS.Core.Application.Service.Inspection.DTO.DataInspectionDto{IRaCIS.Application.Contracts.TrialCommand})">
@@ -41,49 +28,6 @@
<param name="param"></param>
<returns>新记录Id</returns>
</member>
<member name="M:IRaCIS.Core.API.Controllers.Special.FinancialChangeController.WorkLoadAddOrUpdate(IRaCIS.Core.Application.Service.IDoctorWorkloadService,IRaCIS.Application.Contracts.WorkloadCommand)">
<summary>
添加或更新工作量[AUTH]
</summary>
<param name="_trialWorkloadService"></param>
<param name="workLoadAddOrUpdateModel"></param>
<returns></returns>
</member>
<member name="M:IRaCIS.Core.API.Controllers.Special.FinancialChangeController.AddOrUpdateExchangeRate(IRaCIS.Application.Interfaces.IExchangeRateService,IRaCIS.Application.Interfaces.IPaymentAdjustmentService,IRaCIS.Application.Contracts.ExchangeRateCommand)">
<summary>
添加或更新汇率(会触发没有对锁定的费用计算)
</summary>
</member>
<member name="M:IRaCIS.Core.API.Controllers.Special.FinancialChangeController.AddOrUpdateRankPrice(IRaCIS.Application.Interfaces.IReviewerPayInfoService,IRaCIS.Application.Interfaces.IRankPriceService,IRaCIS.Application.Contracts.RankPriceCommand)">
<summary>
添加或更新 职称单价[AUTH]
</summary>
</member>
<member name="M:IRaCIS.Core.API.Controllers.Special.FinancialChangeController.AddOrUpdateReviewerPayInfo(IRaCIS.Application.Interfaces.IReviewerPayInfoService,IRaCIS.Application.Contracts.ReviewerPayInfoCommand)">
<summary>
添加或更新(替换)医生支付展信息[AUTH]
</summary>
</member>
<member name="M:IRaCIS.Core.API.Controllers.Special.FinancialChangeController.AddOrUpdateTrialPaymentPrice(IRaCIS.Application.Interfaces.ITrialPaymentPriceService,IRaCIS.Application.Contracts.TrialPaymentPriceCommand)">
<summary>
保存(替换)项目支付价格信息(会触发没有被锁定的费用计算)[AUTH]
</summary>
</member>
<member name="M:IRaCIS.Core.API.Controllers.Special.FinancialChangeController.AddOrUpdateAwardPriceList(IRaCIS.Application.Interfaces.IVolumeRewardService,System.Collections.Generic.IEnumerable{IRaCIS.Application.Interfaces.AwardPriceCommand})">
<summary>
批量更新奖励费用[AUTH]
</summary>
</member>
<member name="M:IRaCIS.Core.API.Controllers.Special.FinancialChangeController.CalculateMonthlyPayment(IRaCIS.Application.Contracts.CalculateDoctorAndMonthDTO)">
<summary>
计算医生月度费用,并将计算的结果存入费用表
</summary>
</member>
<member name="M:IRaCIS.Core.API.Controllers.Special.FinancialChangeController.GetMonthlyPaymentList(IRaCIS.Application.Interfaces.IPaymentService,IRaCIS.Application.Interfaces.IExchangeRateService,IRaCIS.Application.Contracts.MonthlyPaymentQueryDTO)">
<summary>
Financials /Monthly Payment 列表查询接口
</summary>
</member>
<member name="M:IRaCIS.Core.API.Controllers.InspectionController.GetInspectionList(IRaCIS.Core.Application.Service.Inspection.DTO.GetDataInspectionDto)">
<summary>
获取稽查数据
@@ -167,13 +111,6 @@
<param name="opt"></param>
<returns></returns>
</member>
<member name="M:IRaCIS.Core.API.Controllers.InspectionController.SignConsistencyAnalysisReadingClinicalData(IRaCIS.Core.Application.Service.Inspection.DTO.DataInspectionDto{IRaCIS.Core.Application.Service.Reading.Dto.SignConsistencyAnalysisReadingClinicalDataInDto})">
<summary>
PM签名一致性分析临床数据
</summary>
<param name="opt"></param>
<returns></returns>
</member>
<member name="M:IRaCIS.Core.API.Controllers.InspectionController.SubmitClinicalFormAndSign(IRaCIS.Core.Application.Service.Inspection.DTO.DataInspectionDto{IRaCIS.Core.Application.Service.Reading.Dto.SubmitClinicalFormInDto})">
<summary>
提交结构化录入并签名
@@ -215,12 +152,6 @@
</summary>
<returns></returns>
</member>
<member name="M:IRaCIS.Core.API.Controllers.InspectionController.ResetAndAsyncCriterion(IRaCIS.Core.Application.Service.Inspection.DTO.DataInspectionDto{IRaCIS.Core.Application.Service.Reading.Dto.ResetAndAsyncCriterionInDto})">
<summary>
重置并同步项目阅片标准
</summary>
<returns></returns>
</member>
<member name="M:IRaCIS.Core.API.Controllers.InspectionController.CRCRequestToQC(IRaCIS.Core.Application.Service.Inspection.DTO.DataInspectionDto{IRaCIS.Core.Application.Contracts.CRCRequestToQCCommand})">
<summary>
CRC RequestToQC 批量提交
@@ -278,67 +209,22 @@
<member name="M:IRaCIS.Core.API.Controllers.UploadBaseController.DicomFileUploadAsync(System.Func{System.String,System.IO.Stream,System.Int32,System.Threading.Tasks.Task},System.String)">
<summary> 流式上传 Dicom上传 </summary>
</member>
<member name="M:IRaCIS.Core.API.Controllers.StudyController.ArchiveStudyNew(System.Guid,System.Guid,System.String,System.Nullable{System.Guid},System.Guid,Microsoft.Extensions.Logging.ILogger{IRaCIS.Core.API.Controllers.UploadDownLoadController},IRaCIS.Core.Application.Contracts.IStudyService,Microsoft.AspNetCore.SignalR.IHubContext{IRaCIS.Core.API.UploadHub,IRaCIS.Core.API.IUploadClient},IRaCIS.Core.Application.Contracts.Dicom.IDicomArchiveService,IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.StudyMonitor})">
<member name="M:IRaCIS.Core.API.Controllers.StudyController.ArchiveStudyNew(System.Guid,System.Guid,System.String,System.Nullable{System.Guid},System.Guid,Microsoft.Extensions.Logging.ILogger{IRaCIS.Core.API.Controllers.UploadDownLoadController},EasyCaching.Core.IEasyCachingProvider,IRaCIS.Core.Application.Contracts.IStudyService,Microsoft.AspNetCore.SignalR.IHubContext{IRaCIS.Core.API.UploadHub,IRaCIS.Core.API.IUploadClient},IRaCIS.Core.Application.Contracts.Dicom.IDicomArchiveService,IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.StudyMonitor})">
<summary>Dicom 归档</summary>
</member>
<member name="M:IRaCIS.Core.API.Controllers.StudyController.PreArchiveStudy(IRaCIS.Core.Application.Contracts.PreArchiveStudyCommand,IRaCIS.Core.Application.Contracts.IStudyService,IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.StudyMonitor})">
<summary>
非dicom 上传预上传接口
</summary>
<param name="preArchiveStudyCommand"></param>
<param name="_studyService"></param>
<param name="_studyMonitorRepository"></param>
<returns></returns>
</member>
<member name="M:IRaCIS.Core.API.Controllers.StudyController.UploadNoneDicomFile(IRaCIS.Core.API.Controllers.StudyController.UploadNoneDicomFileCommand,IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.NoneDicomStudy},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.StudyMonitor},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.NoneDicomStudyFile})">
<member name="M:IRaCIS.Core.API.Controllers.StudyController.UploadNoneDicomFile(IRaCIS.Core.API.Controllers.StudyController.UploadNoneDicomFileCommand,IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.NoneDicomStudy},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.StudyMonitor})">
<summary>
上传非Dicom 文件 支持压缩包 多文件上传
</summary>
<param name="incommand"></param>
<param name="_noneDicomStudyRepository"></param>
<param name="_studyMonitorRepository"></param>
<param name="_noneDicomStudyFileRepository"></param>
<returns></returns>
</member>
<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})">
<member name="M:IRaCIS.Core.API.Controllers.StudyController.UploadVisitCheckExcel(System.Guid)">
<summary>
一致性核查 excel上传 支持三种格式
</summary>
<param name="trialId"></param>
<param name="oSSService"></param>
<param name="_inspectionFileRepository"></param>
<returns></returns>
<exception cref="T:IRaCIS.Core.Infrastructure.BusinessValidationFailedException"></exception>
</member>
<member name="T:IRaCIS.Core.API.Controllers.FileController">
<summary>医生文件上传下载</summary>
</member>
<member name="M:IRaCIS.Core.API.Controllers.FileController.GetOfficialResume(IRaCIS.Core.API.Controllers.GetDoctorPathCommand,IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.Attachment})">
<summary>
New 医生首页 多选 获取多个医生信息+文件路径列表 医生压缩包名称 doctorCode + "_" + doctorName _(时间戳或者随机的Guid)
</summary>
<returns></returns>
</member>
<member name="M:IRaCIS.Core.API.Controllers.FileController.GetTrialDoctorOfficialResume(IRaCIS.Core.API.Controllers.GetDoctorPathCommand,IRaCIS.Application.Interfaces.IDoctorService,IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.Attachment})">
<summary>
New 项目入组 勾选获取简历路径
</summary>
<param name="command"></param>
<param name="_doctorService"></param>
<param name="_attachmentrepository"></param>
<returns></returns>
</member>
<member name="M:IRaCIS.Core.API.Controllers.FileController.GetDoctorAttachment(IRaCIS.Core.API.Controllers.GetDoctoreAttachPathCommand,IRaCIS.Application.Interfaces.IDoctorService,IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.Attachment})">
<summary>
new 医生详情 勾选或者下载文件路径
</summary>
<param name="command"></param>
<param name="_doctorService"></param>
<param name="_attachmentrepository"></param>
<returns></returns>
</member>
<member name="M:IRaCIS.Core.API.Controllers.UploadDownLoadController.DownloadCommonFile(System.String,IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.CommonDocument})">
<summary> 通用文件下载 </summary>
</member>
<member name="M:IRaCIS.Core.API.Controllers.UploadDownLoadController.UploadCommonDoc">
<summary>
@@ -357,16 +243,6 @@
IPLimit限流 启动服务
</summary>
</member>
<member name="T:IRaCIS.Core.API.JSONTimeZoneConverter">
<summary>
序列化,反序列化的时候,处理时间 时区转换
</summary>
</member>
<member name="M:IRaCIS.Core.API.JSONTimeZoneConverter.#ctor(Microsoft.AspNetCore.Http.IHttpContextAccessor)">
<summary>
序列化,反序列化的时候,处理时间 时区转换
</summary>
</member>
<member name="M:IRaCIS.Core.API.NullToEmptyStringResolver.CreateProperties(System.Type,Newtonsoft.Json.MemberSerialization)">
<summary>
创建属性
@@ -375,6 +251,13 @@
<param name="memberSerialization">序列化成员</param>
<returns></returns>
</member>
<member name="M:IRaCIS.WX.CoreApi.Auth.AuthMiddleware.Invoke(Microsoft.AspNetCore.Http.HttpContext)">
<summary>
为了前端 一段时间无操作,需要重新登陆
</summary>
<param name="httpContext"></param>
<returns></returns>
</member>
<member name="T:ZhaoXi._001.NET5Demo.Practice.WebApi.Utility.Jwt.CustomHSJWTService">
<summary>
对称可逆加密
+114
View File
@@ -0,0 +1,114 @@
using System;
using Autofac.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Configuration;
using Serilog;
using MediatR;
using IRaCIS.Core.Application.MediatR.Handlers;
using System.Threading.Tasks;
using MassTransit;
using MassTransit.NewIdProviders;
using System.IO;
using IRaCIS.Core.Domain.Share;
using IRaCIS.Core.Infra.EFCore;
using IRaCIS.Core.Application.Helper;
using IRaCIS.Application.Services.BackGroundJob;
using Hangfire;
namespace IRaCIS.Core.API
{
public class Program
{
public readonly string environment;
public static async Task Main(string[] args)
{
try
{
//以配置文件为准,否则 从url中取环境值(服务以命令行传递参数启动,配置文件配置了就不需要传递环境参数)
var config = new ConfigurationBuilder()
.AddEnvironmentVariables()
.Build();
var enviromentName = config["ASPNETCORE_ENVIRONMENT"];
if (string.IsNullOrWhiteSpace(enviromentName))
{
var index = Array.IndexOf(args, "--env");
enviromentName = index > -1
? args[index + 1]
: "Development";
}
//Dicom 浏览
//ImageManager.SetImplementation(WinFormsImageManager.Instance);
var host = CreateHostBuilder(args)
.UseEnvironment(enviromentName) //命令行传入环境
.ConfigureAppConfiguration((hostContext, config) =>
{
//Console.WriteLine(hostContext.HostingEnvironment.EnvironmentName);
config.AddJsonFile("appsettings.json", false, true)
.AddJsonFile($"appsettings.{enviromentName}.json", false, true);
})
.Build();
//// Serilog
SerilogExtension.AddSerilogSetup(enviromentName, host.Services);
Log.Logger.Warning($"当前环境:{enviromentName}");
NewId.SetProcessIdProvider(new CurrentProcessIdProvider());
////缓存项目的状态 匿名化数据
//await InitCache(host);
host.Run();
}
catch (Exception e)
{
Log.Logger.Error(e.InnerException is null ? e.Message + e.StackTrace : e.InnerException?.Message + e.InnerException?.StackTrace);
}
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseWindowsService()
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.ConfigureKestrel((context, options) =>
{
//设置应用服务器Kestrel请求体最大为1GB // if don't set default value is: 30 MB
options.Limits.MaxRequestBodySize = long.MaxValue;
options.Limits.KeepAliveTimeout = TimeSpan.FromMinutes(30);
options.Limits.RequestHeadersTimeout = TimeSpan.FromMinutes(20);
});
//webBuilder.UseSerilog();//在宿主机启动的时候配置serilog,与微软ILogger进行整合
webBuilder.UseStartup<Startup>();
}).UseSerilog()
.UseServiceProviderFactory(new AutofacServiceProviderFactory());
private static async Task InitCache(IHost host)
{
var _repository = host.Services.GetService(typeof(IRepository)) as IRepository;
//初始化 国际化数据,并且监测国际化文件变更
//await InternationalizationHelper.InitInternationlizationDataAndWatchJsonFileAsync(_repository);
}
}
}
-409
View File
@@ -1,409 +0,0 @@
using System;
using Autofac.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Configuration;
using Serilog;
using System.Threading.Tasks;
using MassTransit;
using MassTransit.NewIdProviders;
using System.IO;
using IRaCIS.Core.Domain.Share;
using IRaCIS.Core.Infra.EFCore;
using IRaCIS.Core.Application.Helper;
using System.Runtime.InteropServices;
using Microsoft.AspNetCore.Builder;
using IRaCIS.Core.API;
using Autofac;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.DependencyInjection;
using IRaCIS.Core.Application.Filter;
using Microsoft.AspNetCore.HttpOverrides;
using IRaCIS.Core.Application.Service.BackGroundJob;
using LogDashboard;
using FellowOakDicom.Network;
using IRaCIS.Core.Application.Service.ImageAndDoc;
using IP2Region.Net.Abstractions;
using IP2Region.Net.XDB;
using IRaCIS.Core.Application.BusinessFilter;
using Microsoft.AspNetCore.Http;
using IRaCIS.Core.Infrastructure.Extention;
using Newtonsoft.Json;
using Microsoft.AspNetCore.Diagnostics;
using IRaCIS.Core.Application.MassTransit.Command;
using IRaCIS.Core.Application.MassTransit.Consumer;
using DocumentFormat.OpenXml.InkML;
using IRaCIS.Core.Domain;
AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);
AppContext.SetSwitch("Npgsql.DisableDateTimeInfinityConversions", true);
#region
//以配置文件为准,否则 从url中取环境值(服务以命令行传递参数启动,配置文件配置了就不需要传递环境参数)
var config = new ConfigurationBuilder()
.AddEnvironmentVariables()
.Build();
var enviromentName = config["ASPNETCORE_ENVIRONMENT"];
if (string.IsNullOrWhiteSpace(enviromentName))
{
var index = Array.IndexOf(args, "--env");
enviromentName = index > -1
? args[index + 1]
: "Development";
}
#endregion
var builder = WebApplication.CreateBuilder(new WebApplicationOptions
{
EnvironmentName = enviromentName
});
#region windows
//foreach (var arg in args)
//{
// Console.WriteLine(arg);
//}
int urlsIndex = Array.FindIndex(args, arg => arg != null && arg.StartsWith("--urls"));
if (urlsIndex > -1)
{
var url = args[urlsIndex].Substring("--urls=".Length);
Console.WriteLine(url);
builder.WebHost.UseUrls(url);
}
#endregion
#region
NewId.SetProcessIdProvider(new CurrentProcessIdProvider());
builder.Configuration.AddJsonFile(ConfigMapFileProvider.FromRelativePath(""), "appsettings.json", false, true)
.AddJsonFile(ConfigMapFileProvider.FromRelativePath(""), $"appsettings.{enviromentName}.json", false, true);
builder.Host
.UseServiceProviderFactory(new AutofacServiceProviderFactory())
.ConfigureContainer<ContainerBuilder>(containerBuilder =>
{
containerBuilder.RegisterModule<AutofacModuleSetup>();
})
.UseWindowsService().UseSerilog();
#endregion
#region
var _configuration = builder.Configuration;
//健康检查
builder.Services.AddHealthChecks();
//本地化
builder.Services.AddJsonLocalization(options => options.ResourcesPath = "Resources");
// 异常、参数统一验证过滤器、Json序列化配置、字符串参数绑型统一Trim()
builder.Services.AddControllers(options =>
{
//options.Filters.Add<LogActionFilter>();
options.Filters.Add<ModelActionFilter>();
options.Filters.Add<ProjectExceptionFilter>();
options.Filters.Add<UnitOfWorkFilter>();
//options.Filters.Add<EncreptApiResultFilter>(10);
options.Filters.Add<LimitUserRequestAuthorization>();
})
.AddNewtonsoftJsonSetup(builder.Services); // NewtonsoftJson 序列化 处理
builder.Services.AddOptions().Configure<SystemEmailSendConfig>(_configuration.GetSection("SystemEmailSendConfig"));
builder.Services.AddOptions().Configure<ServiceVerifyConfigOption>(_configuration.GetSection("BasicSystemConfig"));
builder.Services.AddOptions().Configure<AliyunOSSOptions>(_configuration.GetSection("AliyunOSS"));
builder.Services.AddOptions().Configure<ObjectStoreServiceOptions>(_configuration.GetSection("ObjectStoreService"));
builder.Services.AddOptions().Configure<IRCEncreptOption>(_configuration.GetSection("EncrypteResponseConfig"));
builder.Services.AddOptions().Configure<SystemPacsConfig>(_configuration.GetSection("SystemPacsConfig"));
builder.Services.Configure<IRaCISBasicConfigOption>(_configuration.GetSection("IRaCISBasicConfig"));
//动态WebApi + UnifiedApiResultFilter 省掉控制器代码
builder.Services.AddDynamicWebApiSetup();
//AutoMapper
builder.Services.AddAutoMapperSetup();
//EF ORM QueryWithNoLock
builder.Services.AddEFSetup(_configuration, enviromentName);
//Http 响应压缩
builder.Services.AddResponseCompressionSetup();
//Swagger Api 文档
builder.Services.AddSwaggerSetup();
//JWT Token 验证
builder.Services.AddJWTAuthSetup(_configuration);
// MediatR 进程内消息 事件解耦 从程序集中 注册命令和handler对应关系
//builder.Services.AddMediatR(cfg => cfg.RegisterServicesFromAssemblyContaining<ConsistencyVerificationHandler>());
#region MassTransit
//masstransit组件 也支持MediatR 中介者模式,但是支持分布式,考虑后续,所以在次替代MediatR
//参考链接:https://masstransit.io/documentation/concepts/mediator#scoped-mediator
builder.Services.AddMediator(cfg =>
{
cfg.AddConsumer<ConsistencyCheckConsumer>();
cfg.AddConsumer<AddSubjectTriggerConsumer>();
cfg.AddConsumer<AddSubjectTriggerConsumer2>();
//cfg.ConfigureMediator((context, cfg) => cfg.UseHttpContextScopeFilter(context));
});
//添加 MassTransit 和 InMemory 传输
builder.Services.AddMassTransit(cfg =>
{
// 注册消费者
cfg.AddConsumer<AddSubjectTriggerConsumer>(); // 替换为你的消费者类
cfg.AddConsumer<AddSubjectTriggerConsumer2>();
// 使用 InMemory 作为消息传递机制
cfg.UsingInMemory((context, cfg) =>
{
// 这里可以进行额外的配置
cfg.ConfigureEndpoints(context); // 自动配置所有消费者的端点
});
});
#endregion
#region FusionCache
builder.Services.AddFusionCache();
#endregion
// EasyCaching 缓存
//builder.Services.AddEasyCachingSetup(_configuration);
// hangfire 定时任务框架 有界面,更友好~
builder.Services.AddhangfireSetup(_configuration);
//Serilog 日志可视化 LogDashboard日志
builder.Services.AddLogDashboardSetup();
builder.Services.AddJsonConfigSetup(_configuration);
//转发头设置 获取真实IP
builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders =
ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
});
//Dicom影像渲染图片 跨平台
builder.Services.AddDicomSetup();
// 实时应用
builder.Services.AddSignalR();
builder.Services.AddSingleton<IUserIdProvider, IRaCISUserIdProvider>();
builder.Services.AddSingleton<ISearcher>(new Searcher(CachePolicy.Content, Path.Combine(AppContext.BaseDirectory, StaticData.Folder.Resources, "ip2region.xdb")));
//builder.Services.AddExceptionHandler<GlobalExceptionHandler>();
//builder.Services.AddProblemDetails();
#region
//builder.Services.AddMemoryCache();
////上传限制 配置
//builder.Services.Configure<FormOptions>(options =>
//{
// options.MultipartBodyLengthLimit = int.MaxValue;
// options.ValueCountLimit = int.MaxValue;
// options.ValueLengthLimit = int.MaxValue;
//});
//IP 限流 可设置白名单 或者黑名单
//services.AddIpPolicyRateLimitSetup(_configuration);
// 用户类型 策略授权
//services.AddAuthorizationPolicySetup(_configuration);
#endregion
#endregion
var app = builder.Build();
var env = app.Environment;
#region
app.UseMiddleware<EncryptionRequestMiddleware>();
// Configure the HTTP request pipeline.
#region 400 -500
//app.UseStatusCodePagesWithReExecute("/Error/{0}");
app.UseStatusCodePages(async context =>
{
var code = context.HttpContext.Response.StatusCode;
context.HttpContext.Response.ContentType = "application/json";
if (code < 500)
{
await context.HttpContext.Response.WriteAsync(JsonConvert.SerializeObject(ResponseOutput.NotOk($"Client error, actual request error status code({code})")));
}
else
{
//ResultFilter 里面的异常并不会到这里
await context.HttpContext.Response.WriteAsync(JsonConvert.SerializeObject((ResponseOutput.NotOk($"Server error , actual request error status code({code})"))));
}
});
//app.UseExceptionHandler(o => { });
//这里没生效,原因未知,官方文档也是这种写法,也用了GlobalExceptionHandler 尝试,还是不行,怀疑框架bug
//app.UseExceptionHandler(configure =>
//{
// configure.Run(async context =>
// {
// var exceptionHandlerPathFeature = context.Features.Get<IExceptionHandlerPathFeature>();
//var ex = exceptionHandlerPathFeature?.Error;
//context.Response.ContentType = "application/json";
//if (ex != null)
//{
// var errorInfo = $"Exception: {ex.Message}[{ex.StackTrace}]" + (ex.InnerException != null ? $" InnerException: {ex.InnerException.Message}[{ex.InnerException.StackTrace}]" : "");
// await context.Response.WriteAsync(JsonConvert.SerializeObject(ResponseOutput.NotOk($"{ex?.Message}")));
// Log.Logger.Error(errorInfo);
//}
// });
//});
#endregion
//本地化
app.UseLocalization();
app.UseForwardedHeaders();
//响应压缩
app.UseResponseCompression();
//不需要 token 访问的静态文件 wwwroot css, JavaScript, and images don't require authentication.
app.UseStaticFiles();
//LogDashboard
app.UseLogDashboard("/LogDashboard");
//hangfire
app.UseHangfireConfig(env);
#region
//app.UseMiddleware<MultiDiskStaticFilesMiddleware>();
////限流 中间件
//app.UseIpRateLimiting();
//if (env.IsDevelopment())
//{
// app.UseDeveloperExceptionPage();
//}
//else
//{
// //app.UseHsts();
//}
#endregion
SwaggerSetup.Configure(app, env);
////serilog 记录请求的用户信息
app.UseSerilogConfig(env);
app.UseRouting();
app.UseCors(t => t.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader());
//app.UseIRacisHostStaticFileStore(env);
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.MapHub<UploadHub>("/UploadHub");
app.MapHealthChecks("/health");
// Serilog
SerilogExtension.AddSerilogSetup(enviromentName, app.Services);
var hangfireJobService = app.Services.GetRequiredService<IIRaCISHangfireJob>();
await hangfireJobService.InitHangfireJobTaskAsync();
#endregion
try
{
#region
Log.Logger.Warning($"当前环境:{enviromentName}");
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
Log.Logger.Warning($"当前部署平台环境:windows");
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
Log.Logger.Warning($"当前部署平台环境:linux");
}
else
{
Log.Logger.Warning($"当前部署平台环境:OSX or FreeBSD");
}
#endregion
Log.Logger.Warning($"ContentRootPath{env.ContentRootPath}");
string parentDirectory = Path.GetFullPath(Path.Combine(env.ContentRootPath, ".."));
Log.Logger.Warning($"ContentRootPath——parentDirectory{parentDirectory}");
//Log.Logger.Warning($"ContentRootPath——GetParent{Directory.GetParent(env.ContentRootPath).Parent.FullName}");
//Log.Logger.Warning($"ContentRootPath——xx{Path.GetDirectoryName(Path.GetDirectoryName(env.ContentRootPath))}");
app.Run();
}
catch (Exception e)
{
Log.Logger.Error(e.InnerException is null ? e.Message + e.StackTrace : e.InnerException?.Message + e.InnerException?.StackTrace);
}
+5 -53
View File
@@ -1,75 +1,27 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:3305",
"sslPort": 0
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Test_IRC"
}
},
"IRaCIS.Test_IRC": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Test_IRC"
},
"applicationUrl": "http://localhost:6100"
},
"IRaCIS.Test_IRC_PGSQL": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Test_IRC_PGSQL"
},
"applicationUrl": "http://localhost:6100"
},
"IRaCIS.Event_IRC": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Event_IRC"
},
"applicationUrl": "http://localhost:6100"
},
"profiles": {
"Docker": {
"commandName": "Docker",
"launchBrowser": true,
"launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}",
"publishAllPorts": true
},
"IRaCIS.Uat_IRC": {
"Uat_Study": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Uat_IRC"
"ASPNETCORE_ENVIRONMENT": "Uat_Study"
},
"applicationUrl": "http://localhost:6100"
},
"IRaCIS.Prod_IRC": {
"Test_Study": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Prod_IRC"
},
"applicationUrl": "http://localhost:6100"
},
"IRaCIS.US_Uat_IRC": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "US_Uat_IRC"
"ASPNETCORE_ENVIRONMENT": "Test_Study"
},
"applicationUrl": "http://localhost:6100"
}
}
}
+16 -16
View File
@@ -1,16 +1,16 @@
// 使用的是proto3版本
// 使用的是proto3版本
syntax = "proto3";
// 定义命名空间,后续生成代码时就会生成对应的命名空间
// 定义命名空间,后续生成代码时就会生成对应的命名空间
option csharp_namespace = "gRPC.ZHiZHUN.AuthServer.protos";
/*
每一句需要用分号结尾
message 用来定义请求和返回数据格式
tag message后面的值数字代表是字段的标识(tag),不是赋值,
每一句需要用分号结尾
message 用来定义请求和返回数据格式
tag message后面的值数字代表是字段的标识(tag),不是赋值,
*/
// 新增用户时需要传递数据消息, 可理解为一个类
// 新增用户时需要传递数据消息, 可理解为一个类
message GetTokenReuqest{
string id=1;
string userName=2;
@@ -22,48 +22,48 @@ message GetTokenReuqest{
}
// 新增时返回的消息格式
// 新增时返回的消息格式
message GetTokenResponse {
int32 code=1;
string token =2;
}
// service 用标识定义服务的,里面写对应的方法
// service 用标识定义服务的,里面写对应的方法
service TokenGrpcService{
// 获取token
// 获取token
rpc GetUserToken(GetTokenReuqest) returns (GetTokenResponse);
}
/*
// 新增用户时需要传递数据消息, 可理解为一个类
// 新增用户时需要传递数据消息, 可理解为一个类
message AddUserReuqest{
string name=1;
int32 age=2;
bool isBoy=3;
}
// 新增时返回的消息格式
// 新增时返回的消息格式
message ResultResponse {
int32 code=1;
string msg =2;
}
//传递的查询条件信息格式,可理解为平时传入的查询条件对象
//传递的查询条件信息格式,可理解为平时传入的查询条件对象
message QueryUserReuqest{
string name=1;
}
//查询返回的用户信息格式,可理解为返回的类
//查询返回的用户信息格式,可理解为返回的类
message UserInfoResponse {
string name=1;
int32 age=2;
string gender=3;
}
// service 用标识定义服务的,里面写对应的方法
// service 用标识定义服务的,里面写对应的方法
service UserService{
// 新增用户
// 新增用户
rpc AddUser(AddUserReuqest) returns (ResultResponse);
// 查询用户
// 查询用户
rpc GetAllUser(QueryUserReuqest) returns (UserInfoResponse);
}
*/
+2 -1
View File
@@ -1,4 +1,5 @@
using IRaCIS.Core.Domain.Share;
using EasyCaching.Core;
using IRaCIS.Core.Domain.Share;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.SignalR;
+257
View File
@@ -0,0 +1,257 @@
using Autofac;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;
using IRaCIS.Core.Application.Filter;
using LogDashboard;
using MediatR;
using IRaCIS.Core.Application.MediatR.Handlers;
using Microsoft.Extensions.Logging;
using AspNetCoreRateLimit;
using Microsoft.AspNetCore.HttpOverrides;
using IRaCIS.Core.Infra.EFCore;
using System.Globalization;
using Microsoft.AspNetCore.Localization;
using Localization;
using Magicodes.ExporterAndImporter.Core.Filters;
using IRaCIS.Core.Application.MediatR.CommandAndQueries;
using IRaCIS.Core.Infra.EFCore.Common;
using Invio.Extensions.Authentication.JwtBearer;
using Microsoft.AspNetCore.SignalR;
using IRaCIS.Core.Domain.Share;
using Microsoft.AspNetCore.StaticFiles;
using IRaCIS.Application.Services.BackGroundJob;
using IRaCIS.Core.Application.Helper;
using Microsoft.AspNetCore.Http;
using Autofac.Core;
using DocumentFormat.OpenXml.InkML;
using EasyCaching.Core;
namespace IRaCIS.Core.API
{
public class Startup
{
public Startup(IConfiguration configuration)
{
_configuration = configuration;
}
public ILogger<Startup> _logger { get; }
public IConfiguration _configuration { get; }
//// ConfigureContainer is where you can register things directly
//// with Autofac. This runs after ConfigureServices so the things
//// here will override registrations made in ConfigureServices.
//// Don't build the container; that gets done for you by the factory.
// for castle
public void ConfigureContainer(ContainerBuilder containerBuilder)
{
containerBuilder.RegisterModule<AutofacModuleSetup>();
#region Test
//containerBuilder.RegisterType<ClinicalDataService>().PropertiesAutowired().InstancePerLifetimeScope();//注册仓储
//var container = containerBuilder.Build();
//// Now you can resolve services using Autofac. For example,
//// this line will execute the lambda expression registered
//// to the IConfigReader service.
//using (var scope = container.BeginLifetimeScope())
//{
// var reader = scope.Resolve<BaseService>();
// var test = scope.Resolve<ClinicalDataService>();
// var test2 = scope.Resolve<IClinicalDataService>();
// var test3 = scope.Resolve<IEFUnitOfWork<IRaCISDBContext>>();
//}
#endregion
}
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
//本地化
services.AddJsonLocalization(options => options.ResourcesPath = "Resources");
// 异常、参数统一验证过滤器、Json序列化配置、字符串参数绑型统一Trim()
services.AddControllers(options =>
{
//options.Filters.Add<LogActionFilter>();
options.Filters.Add<ModelActionFilter>();
options.Filters.Add<ProjectExceptionFilter>();
options.Filters.Add<UnitOfWorkFilter>();
if (_configuration.GetSection("BasicSystemConfig").GetValue<bool>("OpenLoginLimit"))
{
options.Filters.Add<LimitUserRequestAuthorization>();
}
})
.AddNewtonsoftJsonSetup(); // NewtonsoftJson 序列化 处理
services.AddOptions().Configure<SystemEmailSendConfig>(_configuration.GetSection("SystemEmailSendConfig"));
services.AddOptions().Configure<ServiceVerifyConfigOption>(_configuration.GetSection("BasicSystemConfig"));
//动态WebApi + UnifiedApiResultFilter 省掉控制器代码
services.AddDynamicWebApiSetup();
//AutoMapper
services.AddAutoMapperSetup();
//EF ORM QueryWithNoLock
services.AddEFSetup(_configuration);
//Http 响应压缩
services.AddResponseCompressionSetup();
//Swagger Api 文档
services.AddSwaggerSetup();
//JWT Token 验证
services.AddJWTAuthSetup(_configuration);
// MediatR 进程内消息 事件解耦 从程序集中 注册命令和handler对应关系
services.AddMediatR(cfg => cfg.RegisterServicesFromAssemblyContaining<ConsistencyVerificationHandler>());
// EasyCaching 缓存
services.AddEasyCachingSetup(_configuration);
//services.AddDistributedMemoryCache();
// hangfire 定时任务框架 有界面,更友好~
services.AddhangfireSetup(_configuration);
// QuartZ 定时任务框架 使用了hangfire 暂时不用,后续需要可以打开,已经配好
services.AddQuartZSetup(_configuration);
// 保护上传文件
//services.AddStaticFileAuthorizationSetup();
////HttpReports 暂时废弃
//services.AddHttpReports().AddHttpTransport();
//Serilog 日志可视化 LogDashboard日志
services.AddLogDashboardSetup();
//上传限制 配置
services.Configure<FormOptions>(options =>
{
options.MultipartBodyLengthLimit = int.MaxValue;
options.ValueCountLimit = int.MaxValue;
options.ValueLengthLimit = int.MaxValue;
});
//IP 限流 可设置白名单 或者黑名单
//services.AddIpPolicyRateLimitSetup(_configuration);
// 用户类型 策略授权
//services.AddAuthorizationPolicySetup(_configuration);
services.AddJsonConfigSetup(_configuration);
//转发头设置 获取真实IP
services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders =
ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
});
//Dicom影像渲染图片 跨平台
services.AddDicomSetup();
// 实时应用
services.AddSignalR();
services.AddSingleton<IUserIdProvider, IRaCISUserIdProvider>();
//services.AddSingleton<IImportResultFilter, ImportResultFilteTest>();
services.AddMemoryCache();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public async void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
//app.UsePathBase(PathString.FromUriComponent("/api"));
//本地化
app.UseLocalization();
app.UseForwardedHeaders();
//响应压缩
app.UseResponseCompression();
//app.UseCors(t => t.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader());
//不需要 token 访问的静态文件 wwwroot css, JavaScript, and images don't require authentication.
app.UseStaticFiles();
//LogDashboard
app.UseLogDashboard("/back/logs");
//hangfire
app.UseHangfireConfig(env);
////暂时废弃
//app.UseHttpReports();
////限流 中间件
//app.UseIpRateLimiting();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
//app.UseHsts();
}
Console.WriteLine("当前环境: " + env.EnvironmentName);
//app.UseMiddleware<AuthMiddleware>();
// 特殊异常处理 比如 404
app.UseStatusCodePagesWithReExecute("/Error/{0}");
SwaggerSetup.Configure(app, env);
////serilog 记录请求的用户信息
app.UseSerilogConfig(env);
app.UseRouting();
app.UseCors(t => t.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader());
//app.UseIRacisHostStaticFileStore(env);
app.UseMiddleware<MultiDiskStaticFilesMiddleware>();
app.UseAuthentication();
//app.UseJwtBearerQueryString();
app.UseAuthorization();
////文件伺服 必须带Token 访问
////app.UseIRacisHostStaticFileStore(env);
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapHub<UploadHub>("/UploadHub");
});
var hangfireJobService = app.ApplicationServices.GetRequiredService<IIRaCISHangfireJob>();
await hangfireJobService.InitHangfireJobTaskAsync();
//有的时候每调用
//HangfireJobHelper.NotImmediatelyOnceOnlyJob<IIRaCISHangfireJob>(t => t.InitHangfireJobTaskAsync(),TimeSpan.FromSeconds(1));
}
}
}
@@ -0,0 +1,66 @@
using System;
using System.IdentityModel.Tokens.Jwt;
using System.Net;
using System.Threading.Tasks;
using EasyCaching.Core;
using IRaCIS.Core.Domain.Share;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Http;
namespace IRaCIS.WX.CoreApi.Auth
{
public class AuthMiddleware
{
private readonly RequestDelegate _next;
private readonly IEasyCachingProvider _provider;
public AuthMiddleware(RequestDelegate next, IEasyCachingProvider provider)
{
_next = next;
_provider = provider;
}
/// <summary>
///为了前端 一段时间无操作,需要重新登陆
/// </summary>
/// <param name="httpContext"></param>
/// <returns></returns>
public async Task Invoke(HttpContext httpContext)
{
var isLogin = httpContext.Request.Path.ToString().ToLower().Contains("login");
var result = await httpContext.AuthenticateAsync(JwtBearerDefaults.AuthenticationScheme);
if (!isLogin)
{
if (!result.Succeeded)
{
httpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
await httpContext.Response.WriteAsync("Unauthorized");
}
else
{
var toekn = result.Properties.Items[".Token.access_token"];
var jwtHandler = new JwtSecurityTokenHandler();
JwtSecurityToken jwtToken = jwtHandler.ReadJwtToken(toekn);
object userId;
jwtToken.Payload.TryGetValue("id", out userId);
var cacheValueExist = await _provider.ExistsAsync(userId.ToString()); //Get<string>(userId.ToString()).ToString();
if (!cacheValueExist)
{
httpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
await httpContext.Response.WriteAsync("Unauthorized");
}
else
{
await _provider.SetAsync(userId.ToString(), userId.ToString(), TimeSpan.FromMinutes(15));
httpContext.User = result.Principal;
await _next.Invoke(httpContext);
}
}
}
else await _next.Invoke(httpContext);
}
}
}
@@ -0,0 +1,39 @@
using Hangfire.Dashboard;
using System.IdentityModel.Tokens.Jwt;
using System;
using System.Linq;
using IRaCIS.Core.Domain.Share;
namespace IRaCIS.Core.API.Filter
{
//从cookie 中取值
public class hangfireAuthorizationFilter : IDashboardAuthorizationFilter
{
public bool Authorize(DashboardContext context)
{
var httpContext = context.GetHttpContext();
// Allow all authenticated users to see the Dashboard (potentially dangerous).
//return httpContext.User.Identity.IsAuthenticated;
var jwtToken = httpContext.Request.Cookies["access_token"]?.ToString();
var handler = new JwtSecurityTokenHandler();
if (handler.CanReadToken(jwtToken))
{
var jwtSecurityToken = handler.ReadJwtToken(jwtToken);
return jwtSecurityToken.Claims.Any(t => t.Type == JwtIRaCISClaimType.UserTypeEnum && (t.Value == UserTypeEnum.Admin.ToString()|| t.Value== UserTypeEnum.SuperAdmin.ToString()));
}
else
{
return false;
}
}
}
}
@@ -1,17 +0,0 @@
using Hangfire.Dashboard;
namespace IRaCIS.Core.API.Filter
{
public class hangfireAuthorizationFilter : IDashboardAuthorizationFilter
{
public bool Authorize(DashboardContext context)
{
var httpContext = context.GetHttpContext();
// Allow all authenticated users to see the Dashboard (potentially dangerous).
return httpContext.User.Identity.IsAuthenticated;
//return true;
}
}
}
@@ -1,7 +1,7 @@
using Hangfire;
using Hangfire.Dashboard;
using Hangfire.Dashboard.BasicAuthorization;
using IRaCIS.Core.Application.Service.BackGroundJob;
using IRaCIS.Application.Services.BackGroundJob;
using IRaCIS.Core.API.Filter;
using IRaCIS.Core.Application.Helper;
using Microsoft.AspNetCore.Builder;
@@ -1,11 +1,8 @@
using Azure;
using IRaCIS.Core.Application.Helper;
using IRaCIS.Core.Application.Helper;
using IRaCIS.Core.Domain.Share;
using IRaCIS.Core.Infrastructure.Extention;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.StaticFiles;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.FileProviders.Physical;
@@ -14,7 +11,6 @@ using Microsoft.Extensions.Hosting.Internal;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.VisualBasic;
using Newtonsoft.Json;
using SharpCompress.Common;
using System.IO;
using System.Linq;
@@ -51,7 +47,7 @@ namespace IRaCIS.Core.API
var path = context.Request.Path.Value;
var isIRacisFile = path.StartsWith($"/{StaticData.Folder.IRaCISDataFolder}");
var isDicomFile = isIRacisFile && path.Contains($"{StaticData.Folder.DicomFolder}");
var isDicomFile = path.Contains($"{StaticData.Folder.DicomFolder}");
@@ -151,12 +147,6 @@ namespace IRaCIS.Core.API
return;
}
}
// 如果没有找到文件,返回404
context.Response.StatusCode = 404;
context.Response.ContentType = "application/json";
await context.Response.WriteAsync(JsonConvert.SerializeObject(ResponseOutput.NotOk("File not found")));
}
@@ -1,5 +1,4 @@
using IRaCIS.Core.Domain.Share;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Localization;
using System.Collections.Generic;
using System.Globalization;
@@ -13,9 +12,8 @@ namespace IRaCIS.Core.API
{
var supportedCultures = new List<CultureInfo>
{
new CultureInfo(StaticData.CultureInfo.en_US),
new CultureInfo(StaticData.CultureInfo.zh_CN)
new CultureInfo("en-US"),
new CultureInfo("zh-CN")
};
var options = new RequestLocalizationOptions
@@ -1,6 +1,4 @@
using Hangfire;
using Hangfire.Dashboard;
using IRaCIS.Core.API._PipelineExtensions.Serilog;
using IRaCIS.Core.API._PipelineExtensions.Serilog;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Serilog;
@@ -19,7 +17,7 @@ namespace IRaCIS.Core.API
app.UseSerilogRequestLogging(opts
=>
{
opts.MessageTemplate = "{TokenUserRealName} {TokenUserTypeShortName} {ClientIp} {LocalIP} {Host} {Protocol} {RequestMethod} {RequestPath} {RequestBody} responded {StatusCode} in {Elapsed:0.0000} ms";
opts.MessageTemplate = "{TokenUserRealName} {TokenUserType} {ClientIp} {RequestIP} {Host} {Protocol} {RequestMethod} {RequestPath} {RequestBody} responded {StatusCode} in {Elapsed:0.0000} ms";
opts.EnrichDiagnosticContext = SerilogHelper.EnrichFromRequest;
});
@@ -1,5 +1,4 @@
using IRaCIS.Core.Domain.Share;
using IRaCIS.Core.Infrastructure.Extention;
using IRaCIS.Core.Infrastructure.Extention;
using Microsoft.AspNetCore.Http;
using Serilog;
using System;
@@ -20,20 +19,18 @@ namespace IRaCIS.Core.API
// Set all the common properties available for every request
diagnosticContext.Set("Host", request.Host);
diagnosticContext.Set("Protocol", request.Protocol);
diagnosticContext.Set("Scheme", request.Scheme);
#region old
//这种获取的Ip不准 配置服务才行
//diagnosticContext.Set("RequestIP", httpContext.Connection.RemoteIpAddress.ToString());
diagnosticContext.Set("RequestIP", httpContext.Connection.RemoteIpAddress.ToString());
//这种方式可以,但是serilog提供了 就不用了
//diagnosticContext.Set("TestIP", httpContext.GetUserIp());
diagnosticContext.Set("Protocol", request.Protocol);
diagnosticContext.Set("Scheme", request.Scheme);
//这种方式不行 读取的body为空字符串 必须在中间件中读取
//diagnosticContext.Set("RequestBody", await ReadRequestBody(httpContext.Request));
//diagnosticContext.Set("RequestBody", RequestPayload);
#endregion
// Only set it if available. You're not sending sensitive data in a querystring right?!
if (request.QueryString.HasValue)
@@ -44,9 +41,9 @@ namespace IRaCIS.Core.API
// Set the content-type of the Response at this point
diagnosticContext.Set("ContentType", httpContext.Response.ContentType);
diagnosticContext.Set("TokenUserRealName", httpContext?.User?.FindFirst(JwtIRaCISClaimType.RealName)?.Value);
diagnosticContext.Set("TokenUserRealName", httpContext?.User?.FindFirst("realName")?.Value);
diagnosticContext.Set("TokenUserTypeShortName", httpContext?.User?.FindFirst(JwtIRaCISClaimType.UserTypeShortName)?.Value);
diagnosticContext.Set("TokenUserType", httpContext?.User?.FindFirst("userTypeEnumName")?.Value);
// Retrieve the IEndpointFeature selected for the request
var endpoint = httpContext.GetEndpoint();
@@ -16,7 +16,7 @@ namespace IRaCIS.Core.API
public IStringLocalizer _localizer;
public ApiResponseHandler(IOptionsMonitor<AuthenticationSchemeOptions> options,
IStringLocalizer localizer,
ILoggerFactory logger, UrlEncoder encoder) : base(options, logger, encoder)
ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock)
{
_localizer = localizer;
@@ -1,86 +0,0 @@
using IRaCIS.Core.Application.Auth;
using IRaCIS.Core.Domain.Share;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace IRaCIS.Core.API
{
public static class AuthorizationPolicySetup
{
public static void AddAuthorizationPolicySetup(this IServiceCollection services, IConfiguration configuration)
{
services.AddAuthorization(options =>
{
//影像质控策略 只允许 CRC IQC进行操作
options.AddPolicy(IRaCISPolicy.CRC_IQC, policyBuilder =>
{
policyBuilder.RequireClaim("userTypeEnumInt", ((int)UserTypeEnum.ClinicalResearchCoordinator).ToString(), ((int)UserTypeEnum.IQC).ToString());
});
//一致性核查策略 只允许 CRC PM APM 进行操作
options.AddPolicy(IRaCISPolicy.PM_APM_CRC, policyBuilder =>
{
policyBuilder.RequireClaim("userTypeEnumInt", ((int)UserTypeEnum.ProjectManager).ToString(), ((int)UserTypeEnum.ClinicalResearchCoordinator).ToString(), ((int)UserTypeEnum.APM).ToString());
});
options.AddPolicy(IRaCISPolicy.PM_APM, policyBuilder =>
{
policyBuilder.RequireClaim("userTypeEnumInt", ((int)UserTypeEnum.ProjectManager).ToString(), ((int)UserTypeEnum.APM).ToString());
});
options.AddPolicy(IRaCISPolicy.PM_IQC, policyBuilder =>
{
policyBuilder.RequireClaim("userTypeEnumInt", ((int)UserTypeEnum.ProjectManager).ToString(), ((int)UserTypeEnum.IQC).ToString());
});
options.AddPolicy(IRaCISPolicy.PM, policyBuilder =>
{
policyBuilder.RequireClaim("userTypeEnumInt", ((int)UserTypeEnum.ProjectManager).ToString());
});
options.AddPolicy(IRaCISPolicy.IQC, policyBuilder =>
{
policyBuilder.RequireClaim("userTypeEnumInt", ((int)UserTypeEnum.IQC).ToString());
});
options.AddPolicy(IRaCISPolicy.CRC, policyBuilder =>
{
policyBuilder.RequireClaim("userTypeEnumInt", ((int)UserTypeEnum.ClinicalResearchCoordinator).ToString());
});
options.AddPolicy(IRaCISPolicy.PM_APM_CRC_QC, policyBuilder =>
{
policyBuilder.RequireClaim("userTypeEnumInt", ((int)UserTypeEnum.ProjectManager).ToString(),((int)UserTypeEnum.ClinicalResearchCoordinator).ToString(), ((int)UserTypeEnum.APM).ToString(), ((int)UserTypeEnum.IQC).ToString());
});
options.AddPolicy(IRaCISPolicy.SPM_CPM, policyBuilder =>
{
policyBuilder.RequireClaim("userTypeEnumInt", ((int)UserTypeEnum.SPM).ToString(), ((int)UserTypeEnum.CPM).ToString());
});
options.AddPolicy(IRaCISPolicy.PM_APM_SPM_CPM, policyBuilder =>
{
policyBuilder.RequireClaim("userTypeEnumInt", ((int)UserTypeEnum.ProjectManager).ToString(), ((int)UserTypeEnum.APM).ToString(),((int)UserTypeEnum.SPM).ToString(), ((int)UserTypeEnum.CPM).ToString());
});
options.AddPolicy(IRaCISPolicy.PM_APM_SPM_CPM_SMM_CMM, policyBuilder =>
{
policyBuilder.RequireClaim("userTypeEnumInt", ((int)UserTypeEnum.ProjectManager).ToString(), ((int)UserTypeEnum.APM).ToString(), ((int)UserTypeEnum.SPM).ToString(),
((int)UserTypeEnum.CPM).ToString(), ((int)UserTypeEnum.SMM).ToString(), ((int)UserTypeEnum.CMM).ToString());
});
});
}
}
}
@@ -1,6 +1,5 @@
using AutoMapper.EquivalencyExpression;
using IRaCIS.Core.Application.Service;
using IRaCIS.Core.Domain.Models;
using Microsoft.Extensions.DependencyInjection;
namespace IRaCIS.Core.API
@@ -15,16 +14,11 @@ namespace IRaCIS.Core.API
//AutoMapper.Collection.EntityFrameworkCore
automapper.AddCollectionMappers();
// 全局忽略 DomainEvents 属性
automapper.AddGlobalIgnore(nameof(Entity.DomainEvents));
automapper.AddGlobalIgnore(nameof(Entity.DomainCommands));
#region 使 IncludeMembers 使
//mapping an EntityFramework Core DbContext-object.
//automapper.UseEntityFrameworkCoreModel<IRaCISDBContext>(services);
//automapper.ForAllMaps((a, b) => b.ForAllMembers(opt => opt.Condition((src, dest, srcMember, desMenber) =>
//{
// //// Can test When Guid? -> Guid if source is null will change to Guid.Empty
@@ -33,7 +27,7 @@ namespace IRaCIS.Core.API
// // not want to map a null Guid? value to db Guid value
//})));
#endregion
}, typeof(QCConfig).Assembly);
@@ -1,5 +1,7 @@
using Autofac;
using Autofac.Extras.DynamicProxy;
using IRaCIS.Core.Application;
using IRaCIS.Core.Application.AOP;
using IRaCIS.Core.Application.BackGroundJob;
using IRaCIS.Core.Infra.EFCore;
using Microsoft.AspNetCore.Http;
@@ -9,9 +11,11 @@ using System.Linq;
using System.Reflection;
using IRaCIS.Core.Domain.Models;
using IRaCIS.Core.Domain.Share;
using IRaCIS.Core.Application.Service;
using MediatR;
using IRaCIS.Application.Services;
using IRaCIS.Application.Interfaces;
using AutoMapper;
using Quartz;
namespace IRaCIS.Core.API
{
@@ -28,6 +32,10 @@ namespace IRaCIS.Core.API
containerBuilder.RegisterType<Repository>().As<IRepository>().InstancePerLifetimeScope();
//containerBuilder.RegisterType<Mapper>().As<IMapper>().InstancePerLifetimeScope();
//containerBuilder.RegisterGeneric(typeof(EFUnitOfWork<>))
// .As(typeof(IEFUnitOfWork<>)).InstancePerLifetimeScope();//注册仓储
#endregion
@@ -43,21 +51,30 @@ namespace IRaCIS.Core.API
#endregion
//containerBuilder.RegisterType<BaseService>().As<IBaseService>().PropertiesAutowired().InstancePerLifetimeScope();
//containerBuilder.RegisterType<DictionaryService>().As<IDictionaryService>().InstancePerLifetimeScope();
Assembly application = Assembly.LoadFrom(AppDomain.CurrentDomain.BaseDirectory + "IRaCIS.Core.Application.dll");
containerBuilder.RegisterAssemblyTypes(application).Where(t => t.FullName.Contains("Service"))
.PropertiesAutowired().AsImplementedInterfaces();
.PropertiesAutowired().AsImplementedInterfaces().EnableClassInterceptors();
//Assembly infrastructure = Assembly.Load("IRaCIS.Core.Infra.EFCore");
//containerBuilder.RegisterAssemblyTypes(infrastructure).AsImplementedInterfaces();
containerBuilder.RegisterType<HttpContextAccessor>().As<IHttpContextAccessor>().SingleInstance();
containerBuilder.RegisterType<UserInfo>().As<IUserInfo>().InstancePerLifetimeScope();
//containerBuilder.RegisterType<Dictionary>().InstancePerLifetimeScope();
//Autofac 注册拦截器 需要注意的是生成api上服务上的动态代理AOP失效 间接掉用不影响
//containerBuilder.RegisterType<TrialStatusAutofacAOP>();
//containerBuilder.RegisterType<UserAddAOP>();
//containerBuilder.RegisterType<QANoticeAOP>();
//containerBuilder.RegisterType<LogService>().As<ILogService>().SingleInstance();
//containerBuilder.RegisterType<HttpContextAccessor>().As<IHttpContextAccessor>().SingleInstance();
//containerBuilder.RegisterType<UserInfo>().As<IUserInfo>().InstancePerLifetimeScope();
//注册hangfire任务 依赖注入
//注册hangfire任务 依赖注入
containerBuilder.RegisterType<ObtainTaskAutoCancelJob>().As<IObtainTaskAutoCancelJob>().InstancePerDependency();
+14 -60
View File
@@ -1,68 +1,31 @@
using EntityFramework.Exceptions.SqlServer;
using Hangfire.SqlServer;
using Hangfire.SqlServer;
using IRaCIS.Core.Application.Triggers;
using IRaCIS.Core.Domain.Share;
using IRaCIS.Core.Infra.EFCore;
using IRaCIS.Core.Infra.EFCore.Interceptor;
using Medallion.Threading;
using Medallion.Threading.SqlServer;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using StackExchange.Redis;
namespace IRaCIS.Core.API
{
public static class EFSetup
{
public static void AddEFSetup( this IServiceCollection services, IConfiguration configuration,string envName)
public static void AddEFSetup( this IServiceCollection services, IConfiguration configuration)
{
services.AddHttpContextAccessor();
services.AddScoped<IUserInfo, UserInfo>();
services.AddScoped<ISaveChangesInterceptor, AuditEntityInterceptor>();
services.AddScoped<ISaveChangesInterceptor, DispatchDomainEventsInterceptor>();
// First, register a pooling context factory as a Singleton service, as usual:
//services.AddScoped<DbContext, IRaCISDBContext>();
//这个注入没有成功--注入是没问题的,构造函数也只是支持参数就好,错在注入的地方不能写DbContext
//Web程序中通过重用池中DbContext实例可提高高并发场景下的吞吐量, 这在概念上类似于ADO.NET Provider原生的连接池操作方式,具有节省DbContext实例化成本的优点
services.AddDbContext<IRaCISDBContext>((sp, options) =>
services.AddDbContext<IRaCISDBContext>(options =>
{
// 在控制台
//public static readonly ILoggerFactory MyLoggerFactory = LoggerFactory.Create(builder => { builder.AddConsole(); });
var logFactory = LoggerFactory.Create(builder => { builder.AddDebug(); });
var dbType = configuration.GetSection("ConnectionStrings:Db_Type").Value ;
if (!string.IsNullOrWhiteSpace(dbType) && dbType == "pgsql")
{
options.UseNpgsql(configuration.GetSection("ConnectionStrings:RemoteNew").Value, contextOptionsBuilder => contextOptionsBuilder.EnableRetryOnFailure());
}
else
{
options.UseSqlServer(configuration.GetSection("ConnectionStrings:RemoteNew").Value, contextOptionsBuilder => contextOptionsBuilder.EnableRetryOnFailure());
}
//迁移的时候,不生成外键
options.ReplaceService<IMigrationsSqlGenerator, NoForeignKeyMigrationsSqlGenerator>();
options.UseLoggerFactory(logFactory);
options.UseExceptionProcessor();
options.UseSqlServer(configuration.GetSection("ConnectionStrings:RemoteNew").Value,
contextOptionsBuilder => contextOptionsBuilder.EnableRetryOnFailure());
options.EnableSensitiveDataLogging();
options.AddInterceptors(new QueryWithNoLockDbCommandInterceptor());
options.AddInterceptors(sp.GetServices<ISaveChangesInterceptor>());
options.UseProjectables();
@@ -75,33 +38,24 @@ namespace IRaCIS.Core.API
options.UseTriggers(triggerOptions =>
{
triggerOptions.AddTrigger<SubjectTrigger>();
triggerOptions.AddTrigger<ChallengeStateTrigger>();
triggerOptions.AddTrigger<AddSubjectTrigger>();
triggerOptions.AddTrigger<ChallengeStateTrigger>();
triggerOptions.AddTrigger<SubjectStateTrigger>();
triggerOptions.AddTrigger<AddCRCCliniaclDataTrigger>();
triggerOptions.AddTrigger<SubjectVisitCheckPassedTrigger>();
triggerOptions.AddTrigger<SubjectVisitFinalVisitTrigger>();
triggerOptions.AddTrigger<SubjectVisitTrigger>();
triggerOptions.AddTrigger<SubjectVisitScanDateTrigger>();
triggerOptions.AddTrigger<TrialCriterionSignTrigger>();
triggerOptions.AddTrigger<TableQuestionRowTrigger>();
//triggerOptions.AddTrigger<AddlTrialUserTrigger>();
triggerOptions.AddTrigger<VisitTaskIsFrontTaskNeedSignButNotSignTrigger>();
triggerOptions.AddTrigger<JudgeVisitTaskTrigger>();
triggerOptions.AddTrigger<UserLogTrigger>();
triggerOptions.AddTrigger<VisitTaskIAfterSignTrigger>();
});
});
// Register an additional context factory as a Scoped service, which gets a pooled context from the Singleton factory we registered above,
//services.AddScoped<IRaCISDBScopedFactory>();
//// Finally, arrange for a context to get injected from our Scoped factory:
//services.AddScoped(sp => sp.GetRequiredService<IRaCISDBScopedFactory>().CreateDbContext());
//注意区分 easy caching 也有 IDistributedLockProvider
services.AddSingleton<IDistributedLockProvider>(sp =>
{
@@ -1,24 +1,27 @@
using Microsoft.Extensions.Configuration;
using EasyCaching.Core;
using EasyCaching.Core.Configurations;
using EasyCaching.Interceptor.Castle;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace IRaCIS.Core.API
{
public static class EasyCachingSetup
public static class EasyCachingSetup
{
public static void AddEasyCachingSetup(this IServiceCollection services, IConfiguration configuration)
{
//services.AddEasyCaching(options =>
//{
// options.UseInMemory();
services.AddEasyCaching(options =>
{
options.UseInMemory();
// //options.UseRedis(configuration, EasyCachingConstValue.DefaultRedisName).WithMessagePack(EasyCachingConstValue.DefaultRedisName);
//options.UseRedis(configuration, EasyCachingConstValue.DefaultRedisName).WithMessagePack(EasyCachingConstValue.DefaultRedisName);
});
//});
//services.ConfigureCastleInterceptor(options => options.CacheProviderName = EasyCachingConstValue.DefaultRedisName);
////services.ConfigureCastleInterceptor(options => options.CacheProviderName = EasyCachingConstValue.DefaultRedisName);
//services.ConfigureCastleInterceptor(options => options.CacheProviderName = EasyCachingConstValue.DefaultInMemoryName);
services.ConfigureCastleInterceptor(options => options.CacheProviderName = EasyCachingConstValue.DefaultInMemoryName);
}
}
}
@@ -13,12 +13,14 @@ namespace IRaCIS.Core.API
services.AddLogDashboard(opt =>
{
//opt.PathMatch = "/api/LogDashboard";
opt.PathMatch = "/LogDashboard";
opt.PathMatch = "/back/logs";
//opt.AddAuthorizationFilter(new LogDashboardBasicAuthFilter("admin", "zhizhun2018"));
//opt.AddAuthorizationFilter(new LogDashBoardAuthFilter());
});
}
@@ -0,0 +1,81 @@
//using System;
//using Microsoft.Extensions.DependencyInjection;
//using StackExchange.Profiling.Storage;
//namespace IRaCIS.Core.API
//{
// public class MiniProfilerConfigure
// {
// public static void ConfigureMiniProfiler(IServiceCollection services)
// {
// services.AddMiniProfiler(options =>
// {
// // All of this is optional. You can simply call .AddMiniProfiler() for all defaults
// // (Optional) Path to use for profiler URLs, default is /mini-profiler-resources
// options.RouteBasePath = "/profiler";
// //// (Optional) Control storage
// //// (default is 30 minutes in MemoryCacheStorage)
// (options.Storage as MemoryCacheStorage).CacheDuration = TimeSpan.FromMinutes(10);
// //// (Optional) Control which SQL formatter to use, InlineFormatter is the default
// //options.SqlFormatter = new StackExchange.Profiling.SqlFormatters.InlineFormatter();
// //// (Optional) To control authorization, you can use the Func<HttpRequest, bool> options:
// //// (default is everyone can access profilers)
// //options.ResultsAuthorize = request => MyGetUserFunction(request).CanSeeMiniProfiler;
// //options.ResultsListAuthorize = request => MyGetUserFunction(request).CanSeeMiniProfiler;
// //// Or, there are async versions available:
// //options.ResultsAuthorizeAsync = async request => (await MyGetUserFunctionAsync(request)).CanSeeMiniProfiler;
// //options.ResultsAuthorizeListAsync = async request => (await MyGetUserFunctionAsync(request)).CanSeeMiniProfilerLists;
// //// (Optional) To control which requests are profiled, use the Func<HttpRequest, bool> option:
// //// (default is everything should be profiled)
// //options.ShouldProfile = request => MyShouldThisBeProfiledFunction(request);
// //// (Optional) Profiles are stored under a user ID, function to get it:
// //// (default is null, since above methods don't use it by default)
// //options.UserIdProvider = request => MyGetUserIdFunction(request);
// //// (Optional) Swap out the entire profiler provider, if you want
// //// (default handles async and works fine for almost all applications)
// //options.ProfilerProvider = new MyProfilerProvider();
// //// (Optional) You can disable "Connection Open()", "Connection Close()" (and async variant) tracking.
// //// (defaults to true, and connection opening/closing is tracked)
// //options.TrackConnectionOpenClose = true;
// //// (Optional) Use something other than the "light" color scheme.
// //// (defaults to "light")
// //options.ColorScheme = StackExchange.Profiling.ColorScheme.Auto;
// //// The below are newer options, available in .NET Core 3.0 and above:
// //// (Optional) You can disable MVC filter profiling
// //// (defaults to true, and filters are profiled)
// //options.EnableMvcFilterProfiling = true;
// //// ...or only save filters that take over a certain millisecond duration (including their children)
// //// (defaults to null, and all filters are profiled)
// //// options.MvcFilterMinimumSaveMs = 1.0m;
// //// (Optional) You can disable MVC view profiling
// //// (defaults to true, and views are profiled)
// //options.EnableMvcViewProfiling = true;
// //// ...or only save views that take over a certain millisecond duration (including their children)
// //// (defaults to null, and all views are profiled)
// //// options.MvcViewMinimumSaveMs = 1.0m;
// //// (Optional) listen to any errors that occur within MiniProfiler itself
// //// options.OnInternalError = e => MyExceptionLogger(e);
// //// (Optional - not recommended) You can enable a heavy debug mode with stacks and tooltips when using memory storage
// //// It has a lot of overhead vs. normal profiling and should only be used with that in mind
// //// (defaults to false, debug/heavy mode is off)
// ////options.EnableDebugMode = true;
// });
// //.AddEntityFramework();
// }
// }
//}
@@ -0,0 +1,46 @@
using IRaCIS.Core.Domain.Share;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using System;
namespace IRaCIS.Core.API
{
public class JSONCustomDateConverter : DateTimeConverterBase
{
private TimeZoneInfo _timeZoneInfo;
private string _dateFormat;
private IUserInfo _userInfo;
public JSONCustomDateConverter(string dateFormat, TimeZoneInfo timeZoneInfo, IUserInfo userInfo)
{
_dateFormat = dateFormat;
_timeZoneInfo = timeZoneInfo;
_userInfo = userInfo;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var timeZoneId = _userInfo.TimeZoneId;
var needConvertUtcDateTime = Convert.ToDateTime(value);
var tz = TimeZoneInfo.FindSystemTimeZoneById(timeZoneId);
var dateTimeOffset = new DateTimeOffset(needConvertUtcDateTime);
var time = TimeZoneInfo.ConvertTimeFromUtc(needConvertUtcDateTime, tz).ToString(_dateFormat);
writer.WriteValue(time);
writer.Flush();
}
}
}
@@ -1,128 +0,0 @@
using IRaCIS.Core.Domain.Share;
using Microsoft.AspNetCore.Http;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using StackExchange.Redis;
using System;
using System.Globalization;
namespace IRaCIS.Core.API
{
/// <summary>
/// 序列化,反序列化的时候,处理时间 时区转换
/// </summary>
public class JSONTimeZoneConverter(IHttpContextAccessor _httpContextAccessor) : DateTimeConverterBase
{
private TimeZoneInfo _clientTimeZone;
private string _dateFormat;
public override bool CanConvert(Type objectType)
{
#region
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
if (!isEn_US)
{
// Chinese date format
_dateFormat = "yyyy-MM-dd HH:mm:ss";
}
else
{
// Default or English date format
//_dateFormat = "MM/dd/yyyy HH:mm:ss";
_dateFormat = "yyyy-MM-dd HH:mm:ss";
}
#endregion
#region
//var timeZoneId = "Etc/UTC";
var timeZoneId = "Asia/Shanghai";
var timeZoneIdHeader = _httpContextAccessor?.HttpContext?.Request?.Headers["TimeZoneId"];
if (timeZoneIdHeader is not null && !string.IsNullOrEmpty(timeZoneIdHeader.Value))
{
timeZoneId = timeZoneIdHeader.Value;
}
_clientTimeZone = TimeZoneInfo.FindSystemTimeZoneById(timeZoneId);
#endregion
// 仅支持 DateTime 类型的转换
return objectType == typeof(DateTime)|| objectType == typeof(DateTime?);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.Value == null)
{
return null;
}
DateTime dateTime;
if (reader.ValueType == typeof(DateTime) || reader.ValueType == typeof(DateTime?))
{
DateTime? nullableDateTime = reader.Value as DateTime?;
if (nullableDateTime != null && nullableDateTime.HasValue)
{
dateTime = nullableDateTime.Value;
}
else
{
return null;
}
}
else
{
if (DateTime.TryParse((string)reader.Value, out dateTime) == false)
{
return null;
}
}
// 将客户端时间转换为服务器时区的时间
var serverZoneTime = TimeZoneInfo.ConvertTime(dateTime, _clientTimeZone, TimeZoneInfo.Local);
return serverZoneTime;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
DateTime? nullableDateTime = value as DateTime?;
if (nullableDateTime != null && nullableDateTime.HasValue)
{
//第一个参数默认使用系统本地时区 也就是应用服务器的时区
DateTime clientZoneTime = TimeZoneInfo.ConvertTime(nullableDateTime.Value, _clientTimeZone);
//writer.WriteValue(clientZoneTime);
writer.WriteValue(clientZoneTime.ToString(_dateFormat));
}
else
{
writer.WriteNull();
}
}
}
}
@@ -1,99 +0,0 @@
using IRaCIS.Core.Domain.Share;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using System;
using System.Globalization;
using System.Reflection;
namespace IRaCIS.Core.API
{
//public class CustomContractResolver : DefaultContractResolver
//{
// protected override JsonContract CreateContract(Type objectType)
// {
// var contract = base.CreateContract(objectType);
// // 检查类是否有 LowercaseJsonAttribute 标记
// if (objectType.GetCustomAttribute<LowerCamelCaseJsonAttribute>() != null)
// {
// contract.NamingStrategy = new IRCCamelCaseNamingStrategy();
// }
// return contract;
// }
//}
#region
public class MyDateTimeConverter : JsonConverter<DateTime>
{
public override DateTime ReadJson(JsonReader reader, Type objectType, DateTime existingValue, bool hasExistingValue, JsonSerializer serializer)
{
return reader.ReadAsDateTime().Value;
}
public override void WriteJson(JsonWriter writer, DateTime value, JsonSerializer serializer)
{
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
string dateFormat;
if (!isEn_US)
{
// Chinese date format
dateFormat = "yyyy-MM-dd HH:mm:ss";
}
else
{
// Default or English date format
dateFormat = "MM/dd/yyyy HH:mm:ss";
}
writer.WriteValue(value.ToString(dateFormat));
}
}
public class MyNullableDateTimeConverter : JsonConverter<DateTime?>
{
public override DateTime? ReadJson(JsonReader reader, Type objectType, DateTime? existingValue, bool hasExistingValue, JsonSerializer serializer)
{
var val = reader.ReadAsDateTime();
return val;
}
public override void WriteJson(JsonWriter writer, DateTime? value, JsonSerializer serializer)
{
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
string dateFormat;
if (!isEn_US)
{
// Chinese date format
dateFormat = "yyyy-MM-dd HH:mm:ss";
}
else
{
// Default or English date format
dateFormat = "MM/dd/yyyy HH:mm:ss";
}
if (value.HasValue)
{
writer.WriteValue(value.Value.ToString(dateFormat));
}
else
{
writer.WriteValue(default(DateTime?));
}
}
}
#endregion
}
@@ -1,60 +1,32 @@
using IRaCIS.Core.API._ServiceExtensions.NewtonsoftJson;
using IRaCIS.Core.Application.Helper;
using IRaCIS.Core.Domain.Share;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using System;
using System.Globalization;
using System.Text.Json;
using Newtonsoft.Json.Serialization;
namespace IRaCIS.Core.API
{
public static class NewtonsoftJsonSetup
{
public static void AddNewtonsoftJsonSetup(this IMvcBuilder builder, IServiceCollection services)
public static void AddNewtonsoftJsonSetup(this IMvcBuilder builder)
{
services.AddHttpContextAccessor();
services.AddScoped<JSONTimeZoneConverter>();
services.AddScoped<ObjectStorePathConvert>();
services.AddScoped<IOSSService, OSSService>();
builder.AddNewtonsoftJson(options =>
{
//大驼峰
//options.SerializerSettings.ContractResolver = new DefaultContractResolver();
//小驼峰
//options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
//options.SerializerSettings.PreserveReferencesHandling = PreserveReferencesHandling.Objects;
// 忽略循环引用
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
//options.SerializerSettings.TypeNameHandling = TypeNameHandling.All;
//处理返回给前端 可空类型 给出默认值 比如in? 为null 设置 默认值0
options.SerializerSettings.ContractResolver = new NullToEmptyStringResolver();
options.SerializerSettings.ContractResolver = new NullToEmptyStringResolver(); //new DefaultContractResolver();// new NullToEmptyStringResolver();
// 设置时间格式
options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss";
// 设置时间格式 isEn_US? "MM/dd/yyyy HH:mm:ss" :
//options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss";
options.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.RoundtripKind;
//二者只能取其一
//options.SerializerSettings.Converters.Add(new MyDateTimeConverter());
//options.SerializerSettings.Converters.Add(new MyNullableDateTimeConverter());
//必须放在后面
options.SerializerSettings.Converters.Add(services.BuildServiceProvider().GetService<JSONTimeZoneConverter>());
//options.SerializerSettings.Converters.Add(new JSONCustomDateConverter()) ;
//IsoDateTimeConverter
//options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore;
})
.AddControllersAsServices()//动态webApi属性注入需要
@@ -1,10 +1,8 @@
using IRaCIS.Core.Infrastructure.NewtonsoftJson;
using Newtonsoft.Json;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace IRaCIS.Core.API
{
@@ -32,20 +30,8 @@ namespace IRaCIS.Core.API
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
// 检查类是否有 LowerCamelCaseJsonAttribute 标记 有的话,属性名小写
if (type.GetCustomAttribute<LowerCamelCaseJsonAttribute>() != null)
{
base.NamingStrategy= new LowerCamelCaseNamingStrategy();
}
else
{
base.NamingStrategy = null;
}
IList<JsonProperty> properties = base.CreateProperties(type, memberSerialization);
var list= type.GetProperties()
.Select(p =>
{
@@ -28,7 +28,7 @@ namespace IRaCIS.Core.API
if(_MemberInfo.PropertyType == typeof(string))
{
//去掉前后空格
_MemberInfo.SetValue(target, value==null?string.Empty: value.ToString()==string.Empty? value:value/*.ToString().Trim()*/);
_MemberInfo.SetValue(target, value==null?string.Empty: value.ToString()==string.Empty? value:value.ToString().Trim());
}
else
@@ -1,63 +0,0 @@
using IRaCIS.Core.Application.Helper;
using Microsoft.AspNetCore.Http;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
using System;
using System.Linq;
using System.Text.RegularExpressions;
namespace IRaCIS.Core.API._ServiceExtensions.NewtonsoftJson
{
public class ObjectStorePathConvert : JsonConverter<string>
{
private readonly IOSSService _oSSService;
// 构造函数
public ObjectStorePathConvert( IOSSService oSSService)
{
_oSSService = oSSService;
}
public override string ReadJson(JsonReader reader, Type objectType, string existingValue, bool hasExistingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.String)
{
return (string)reader.Value;
}
return null;
}
public override void WriteJson(JsonWriter writer, string value, JsonSerializer serializer)
{
if (value != null)
{
// 在这里对字符串进行处理,例如转换大小写、去除空格等
// 这里只是一个示例,您可以根据实际需求进行更改
// 获取当前正在序列化的属性名
string propertyName = writer.Path.Split('.').Last();
Regex guidRegex = new Regex(@"[0-9a-fA-F]{8}(-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}");
if (propertyName.IndexOf("Path") > -1 && value.IndexOf('.')>-1 && guidRegex.IsMatch(value))
{
var tt= _oSSService.GetSignedUrl(value);
writer.WriteValue(tt);
}
else
{
// 将处理后的字符串写入到 JsonWriter 中
writer.WriteValue(value);
}
}
else
{
writer.WriteNull();
}
}
}
}
@@ -0,0 +1,49 @@
using IRaCIS.Application.Services.BackGroundJob;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Quartz;
namespace IRaCIS.Core.API
{
public static class QuartZSetup
{
public static void AddQuartZSetup(this IServiceCollection services, IConfiguration configuration)
{
services.AddTransient<CancelTaskQuartZJob>();
services.AddQuartz(q =>
{
// base quartz scheduler, job and trigger configuration
// as of 3.3.2 this also injects scoped services (like EF DbContext) without problems
q.UseMicrosoftDependencyInjectionJobFactory();
// 基本Quartz调度器、作业和触发器配置
//var jobKey = new JobKey("RegularTrialWork", "regularWorkGroup");
//q.AddJob<CacheTrialStatusQuartZJob>(jobKey, j => j
// .WithDescription("Trial regular work")
//);
//q.AddTrigger(t => t
// .WithIdentity("TrialStatusTrigger")
// .ForJob(jobKey)
// .WithCronSchedule("0 0 * * * ?")
// .WithDescription("My regular trial work trigger")
//);
});
// ASP.NET Core hosting
services.AddQuartzHostedService(options =>
{
// when shutting down we want jobs to complete gracefully
options.WaitForJobsToComplete = true;
});
}
}
}
@@ -1,30 +1,18 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.ResponseCompression;
using Microsoft.Extensions.DependencyInjection;
using System.IO.Compression;
namespace IRaCIS.Core.API
{
public static class ResponseCompressionSetup
public static class ResponseCompressionSetup
{
public static void AddResponseCompressionSetup(this IServiceCollection services)
{
services.AddResponseCompression(options =>
{
options.EnableForHttps = true;
options.Providers.Add<BrotliCompressionProvider>();
options.Providers.Add<GzipCompressionProvider>();
});
services.Configure<BrotliCompressionProviderOptions>(options =>
{
options.Level = CompressionLevel.Optimal;
});
services.Configure<GzipCompressionProviderOptions>(options =>
{
options.Level = CompressionLevel.Optimal;
});
}
}
}
@@ -31,13 +31,13 @@ namespace IRaCIS.Core.API
logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("LocalIP", httpContext.Connection.LocalIpAddress.MapToIPv4().ToString()));
logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("TokenUserRealName", httpContext?.User?.FindFirst(ClaimAttributes.RealName)?.Value));
logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("TokenUserType", httpContext?.User?.FindFirst(JwtIRaCISClaimType.UserTypeShortName)?.Value));
//这样读取没用
//logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("RequestBody", await ReadRequestBody(httpContext.Request)));
//logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("RequestIP", IPHelper.GetIP(httpContext.Request) ));
logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("TokenUserRealName", httpContext?.User?.FindFirst(ClaimAttributes.RealName)?.Value));
logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("TokenUserType", httpContext?.User?.FindFirst(JwtIRaCISClaimType.UserTypeShortName)?.Value));
//logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("Referer", httpContext.Request.Headers["Referer"].ToString()));
//logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("request_path", httpContext.Request.Path));
//logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("request_method", httpContext.Request.Method));
@@ -1,7 +1,7 @@
using Microsoft.AspNetCore.Builder;
using Serilog;
using Serilog.Events;
//using Serilog.Sinks.Email;
using Serilog.Sinks.Email;
using System;
using System.Net;
@@ -19,33 +19,31 @@ namespace IRaCIS.Core.API
// Filter out ASP.NET Core infrastructre logs that are Information and below 日志太多了 一个请求 记录好几条
.MinimumLevel.Override("Microsoft.AspNetCore", LogEventLevel.Warning)
.MinimumLevel.Override("Hangfire", LogEventLevel.Warning)
.MinimumLevel.Override("System.Net.Http.HttpClient.HttpReports", LogEventLevel.Warning)
.MinimumLevel.Override("System.Net.Http.HttpClient.HttpReports", LogEventLevel.Warning)
.Enrich.WithClientIp()
.Enrich.FromLogContext()
.Filter.ByExcluding(logEvent =>logEvent.Properties.ContainsKey("RequestPath") && logEvent.Properties["RequestPath"].ToString().Contains("/health"))
.Enrich.FromLogContext()
//控制台 方便调试 问题 我们显示记录日志 时 获取上下文的ip 和用户名 用户类型
.WriteTo.Console(restrictedToMinimumLevel: LogEventLevel.Warning,
outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {SourceContext:l} || {Message} || {Exception} ||end {NewLine}")
outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3} ] {LocalIP} {ClientIp} {TokenUserRealName} {TokenUserType} {Message:lj} {Properties:j}{NewLine} {Exception}")
.WriteTo.File($"{AppContext.BaseDirectory}Serilogs/.log", rollingInterval: RollingInterval.Day,
outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {SourceContext:l} || {Message} || {Exception} ||end {NewLine}");
outputTemplate: "{Timestamp:HH:mm:ss} || {Level} || {SourceContext:l} || {Message} ||{Exception} ||end {NewLine}");
//.WriteTo.MSSqlServer("Data Source=DESKTOP-4TU9A6M;Initial Catalog=CoreFrame;User ID=sa;Password=123456", "logs", autoCreateSqlTable: true, restrictedToMinimumLevel: LogEventLevel.Information)//从左至右四个参数分别是数据库连接字符串、表名、如果表不存在是否创建、最低等级。Serilog会默认创建一些列。
//if (environment == "Production")
//{
// config.WriteTo.Email(new EmailConnectionInfo()
// {
// EmailSubject = "系统警告,请速速查看!",//邮件标题
// FromEmail = "test@extimaging.com",//发件人邮箱
// MailServer = "smtp.qiye.aliyun.com",//smtp服务器地址
// NetworkCredentials = new NetworkCredential("test@extimaging.com", "SHzyyl2021"),//两个参数分别是发件人邮箱与客户端授权码
// Port = 465,//端口号
// ToEmail = "872297557@qq.com"//收件人
// }, restrictedToMinimumLevel: LogEventLevel.Error,
// outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff} [ {Level} {ClientIp} {ClientAgent} {TokenUserRealName} {TokenUserType} ] || [path: {RequestPath} arguments: {RequestBody}] {SourceContext:l} || {Message} || {Exception} ||end {NewLine})");
//}
if (environment == "Production")
{
config.WriteTo.Email(new EmailConnectionInfo()
{
EmailSubject = "系统警告,请速速查看!",//邮件标题
FromEmail = "test@extimaging.com",//发件人邮箱
MailServer = "smtp.qiye.aliyun.com",//smtp服务器地址
NetworkCredentials = new NetworkCredential("test@extimaging.com", "SHzyyl2021"),//两个参数分别是发件人邮箱与客户端授权码
Port = 465,//端口号
ToEmail = "872297557@qq.com"//收件人
}, restrictedToMinimumLevel: LogEventLevel.Error,
outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff} [ {Level} {ClientIp} {ClientAgent} {TokenUserRealName} {TokenUserType} ] || [path: {RequestPath} arguments: {RequestBody}] {SourceContext:l} || {Message} || {Exception} ||end {NewLine})");
}
//扩展方法 获取上下文的ip 用户名 用户类型
Log.Logger = config.Enrich.WithHttpContextInfo(serviceProvider).CreateLogger();
@@ -3,7 +3,6 @@ using Hangfire.SqlServer;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Runtime.InteropServices;
namespace IRaCIS.Core.API
{
@@ -15,29 +14,19 @@ namespace IRaCIS.Core.API
services.AddHangfire(hangFireConfig =>
{
//本地window 调试 使用内存,服务器部署使用数据库,防止服务器任务调度到本地
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
//hangFireConfig.UseInMemoryStorage();
//指定存储介质
hangFireConfig.UseSqlServerStorage(hangFireConnStr, new SqlServerStorageOptions()
{
hangFireConfig.UseInMemoryStorage();
}
else
{
//指定存储介质
hangFireConfig.UseSqlServerStorage(hangFireConnStr, new SqlServerStorageOptions()
{
SchemaName = "dbo",
CommandBatchMaxTimeout = TimeSpan.FromMinutes(5),
SlidingInvisibilityTimeout = TimeSpan.FromMinutes(5),
QueuePollInterval = TimeSpan.Zero,
UseRecommendedIsolationLevel = true,
DisableGlobalLocks = true
});
}
SchemaName = "dbo",
CommandBatchMaxTimeout = TimeSpan.FromMinutes(5),
SlidingInvisibilityTimeout = TimeSpan.FromMinutes(5),
QueuePollInterval = TimeSpan.Zero,
UseRecommendedIsolationLevel = true,
DisableGlobalLocks = true
});
//hangFireConfig.UseTagsWithSql(); //nuget引入Hangfire.Tags.SqlServer
//.UseHangfireHttpJob();
@@ -46,7 +35,7 @@ namespace IRaCIS.Core.API
services.AddHangfireServer(option =>
{
option.Queues = new[] { "immediately_once", "default", "sys_init", "not_immediately_once" };
option.Queues = new[] { "immediately_once","default","sys_init" , "not_immediately_once" };
});
}
@@ -1,72 +0,0 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"ConnectionStrings": {
"RemoteNew": "Server=47.117.164.182,1434;Database=Event_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
"Hangfire": "Server=47.117.164.182,1434;Database=Event_IRC_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
},
"ObjectStoreService": {
"ObjectStoreUse": "AliyunOSS",
"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
},
"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
},
"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
}
}
-79
View File
@@ -1,79 +0,0 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"ConnectionStrings": {
//"RemoteNew": "Server=101.132.193.237,1434;Database=Prod_IRC;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true",
//"Hangfire": "Server=101.132.193.237,1434;Database=Prod_IRC_Hangfire;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true"
"RemoteNew": "Server=prod_mssql_standard,1433;Database=Prod_IRC;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true",
"Hangfire": "Server=prod_mssql_standard,1433;Database=Prod_IRC_Hangfire;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true"
},
"ObjectStoreService": {
"ObjectStoreUse": "AliyunOSS",
"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
},
"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": 360,
"OpenLoginMFA": false,
"ContinuousReadingTimeMin": 120,
"ReadingRestTimeMin": 10,
"IsNeedChangePassWord": true,
"ChangePassWordDays": 90
},
"SystemEmailSendConfig": {
"Port": 465,
"Host": "smtp.qiye.aliyun.com",
"FromEmail": "IRC@extimaging.com",
"FromName": "IRC",
"AuthorizationCode": "ExtImg@2022",
"SiteUrl": "http://irc.extimaging.com/login",
"OrganizationName": "Extlmaging",
"OrganizationNameCN": "Extlmaging",
"CompanyName": "Extensive Imaging",
"CompanyNameCN": "上海展影医疗科技有限公司",
"CompanyShortName": "Extensive Imaging",
"CompanyShortNameCN": "展影医疗",
"IsEnv_US": false
},
"SystemPacsConfig": {
"Port": "11113",
"IP": "101.132.193.237"
}
}
@@ -0,0 +1,48 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AliyunOSS": {
"regionId": "cn-shanghai",
"region": "oss-cn-shanghai",
"endpoint": "https://oss-cn-shanghai.aliyuncs.com",
"accessKeyId": "LTAI5tKvzs7ed3UfSpNk3xwQ",
"accessKeySecret": "zTIceGEShlZDGnLrCFfIGFE7TXVRio",
"bucketName": "zy-sir-store",
"viewEndpoint": "https://zy-sir-cache.oss-cn-shanghai.aliyuncs.com"
},
"ConnectionStrings": {
"RemoteNew": "Server=47.117.165.18,1434;Database=Prod_Study;User ID=sa;Password=WHxckj@2019;TrustServerCertificate=true",
"Hangfire": "Server=47.117.165.18,1434;Database=Prod_Study.hangfire;User ID=sa;Password=WHxckj@2019;TrustServerCertificate=true"
},
"BasicSystemConfig": {
"OpenUserComplexPassword": true,
"OpenSignDocumentBeforeWork": true,
"OpenTrialRelationDelete": true,
"OpenLoginLimit": true,
"LoginMaxFailCount": 5,
"LoginFailLockMinutes": 30
},
"SystemEmailSendConfig": {
"Port": 465,
"Host": "smtp.qiye.aliyun.com",
"FromEmail": "study@extimaging.com",
"FromName": "研究单位阅片系统",
"AuthorizationCode": "zhanying123",
"SiteUrl": "https://study.extimaging.com/login"
}
}
-99
View File
@@ -1,99 +0,0 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"ConnectionStrings": {
"RemoteNew": "Server=106.14.89.110,1435;Database=Test_IRC;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"
},
"ObjectStoreService": {
"ObjectStoreUse": "AliyunOSS",
"AliyunOSS": {
"RegionId": "cn-shanghai",
"InternalEndpoint": "https://oss-cn-shanghai-internal.aliyuncs.com",
"EndPoint": "https://oss-cn-shanghai.aliyuncs.com",
"AccessKeyId": "LTAI5tRRZehUp2V9pyTPtAJm",
"AccessKeySecret": "FLizxkHsMm4CGYHtkV8E3PNJJZU7oV",
"RoleArn": "acs:ram::1899121822495495:role/dev-oss-access",
"BucketName": "zy-irc-test-store",
"ViewEndpoint": "https://zy-irc-test-store.oss-cn-shanghai.aliyuncs.com",
"Region": "oss-cn-shanghai",
"DurationSeconds": 7200
},
"MinIO": {
"EndPoint": "hir-oss.test.extimaging.com",
"Port": "443",
"UseSSL": true,
"AccessKey": "fbStsVYCIPKHQneeqMwD",
"SecretKey": "TzgvyA3zGXMUnpilJNUlyMYHfosl1hBMl6lxPmjy",
"BucketName": "irc-test",
"ViewEndpoint": "https://hir-oss.test.extimaging.com/irc-test"
},
"AWS": {
"Region": "us-east-1",
"EndPoint": "s3.us-east-1.amazonaws.com",
"UseSSL": true,
"RoleArn": "arn:aws:iam::471112624751:role/uat_s3_access",
"AccessKeyId": "AKIAW3MEAFJX7IPXISP4",
"SecretAccessKey": "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",
"BucketName": "ei-med-s3-lili-uat-store",
"ViewEndpoint": "https://ei-med-s3-lili-uat-store.s3.amazonaws.com",
"DurationSeconds": 7200
}
},
"BasicSystemConfig": {
"OpenUserComplexPassword": false,
"OpenSignDocumentBeforeWork": false,
"OpenTrialRelationDelete": true,
"OpenLoginLimit": false,
"LoginMaxFailCount": 5,
"LoginFailLockMinutes": 1,
"AutoLoginOutMinutes": 1,
"OpenLoginMFA": false,
"ContinuousReadingTimeMin": 120,
"ReadingRestTimeMin": 10,
"IsNeedChangePassWord": true,
"ChangePassWordDays": 90
},
"SystemEmailSendConfig": {
"Port": 465,
"Host": "smtp.qiye.aliyun.com",
"FromEmail": "test@extimaging.com",
"FromName": "Test_IRC",
"AuthorizationCode": "SHzyyl2021",
"SiteUrl": "http://irc.test.extimaging.com/login",
"OrganizationName": "Extlmaging",
"OrganizationNameCN": "Extlmaging",
"CompanyName": "Extensive Imaging",
"CompanyNameCN": "上海展影医疗科技有限公司",
"CompanyShortName": "Extensive Imaging",
"CompanyShortNameCN": "展影医疗",
"IsEnv_US": false
},
"SystemPacsConfig": {
"Port": "11113",
"IP": "106.14.89.110"
}
}
@@ -1,95 +0,0 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"ConnectionStrings": {
"Db_Type": "pgsql",
"RemoteNew": "Host=106.14.89.110;Port=5432;Username=sa;Password=pgsql_pwd;Database=Test2_PG",
"Hangfire": "Server=106.14.89.110,1435;Database=Test_IRC_Hangfire;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": "LTAI5tKvzs7ed3UfSpNk3xwQ",
"accessKeySecret": "zTIceGEShlZDGnLrCFfIGFE7TXVRio",
"bucketName": "zy-irc-test-store",
"roleArn": "acs:ram::1899121822495495:role/oss-upload",
"viewEndpoint": "https://zy-irc-test-store.oss-cn-shanghai.aliyuncs.com",
"region": "oss-cn-shanghai"
},
"MinIO": {
"endPoint": "hir-oss.test.extimaging.com",
"port": "443",
"useSSL": true,
"accessKey": "fbStsVYCIPKHQneeqMwD",
"secretKey": "TzgvyA3zGXMUnpilJNUlyMYHfosl1hBMl6lxPmjy",
"bucketName": "irc-test",
"viewEndpoint": "https://hir-oss.test.extimaging.com/irc-test"
},
"AWS": {
"endPoint": "s3.us-east-1.amazonaws.com",
"useSSL": true,
"accessKey": "AKIAZQ3DRSOHFPJJ6FEU",
"secretKey": "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",
"bucketName": "ei-irc-test-store",
"viewEndpoint": "https://ei-irc-test-store.s3.amazonaws.com/"
}
},
"BasicSystemConfig": {
"OpenUserComplexPassword": false,
"OpenSignDocumentBeforeWork": false,
"OpenTrialRelationDelete": true,
"OpenLoginLimit": false,
"LoginMaxFailCount": 5,
"LoginFailLockMinutes": 1,
"AutoLoginOutMinutes": 1,
"OpenLoginMFA": false,
"ContinuousReadingTimeMin": 120,
"ReadingRestTimeMin": 10,
"IsNeedChangePassWord": true,
"ChangePassWordDays": 90
},
"SystemEmailSendConfig": {
"Port": 465,
"Host": "smtp.qiye.aliyun.com",
"FromEmail": "test@extimaging.com",
"FromName": "Test_IRC",
"AuthorizationCode": "SHzyyl2021",
"SiteUrl": "http://irc.test.extimaging.com/login",
"OrganizationName": "Extlmaging",
"OrganizationNameCN": "Extlmaging",
"CompanyName": "Extensive Imaging",
"CompanyNameCN": "上海展影医疗科技有限公司",
"CompanyShortName": "Extensive Imaging",
"CompanyShortNameCN": "展影医疗"
},
"SystemPacsConfig": {
"Port": "11113",
"IP": "106.14.89.110"
}
}
@@ -0,0 +1,47 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AliyunOSS": {
"regionId": "cn-shanghai",
"region": "oss-cn-shanghai",
"endpoint": "https://oss-cn-shanghai.aliyuncs.com",
"accessKeyId": "LTAI5tKvzs7ed3UfSpNk3xwQ",
"accessKeySecret": "zTIceGEShlZDGnLrCFfIGFE7TXVRio",
"bucketName": "zy-sir-test-store",
"viewEndpoint": "https://zy-sir-test-store.oss-cn-shanghai.aliyuncs.com"
},
"ConnectionStrings": {
"RemoteNew": "Server=123.56.94.154,1433\\MSSQLSERVER;Database=Test.Study;User ID=sa;Password=dev123456DEV;TrustServerCertificate=true",
"Hangfire": "Server=123.56.94.154,1433\\MSSQLSERVER;Database=Test.Study.hangfire;User ID=sa;Password=dev123456DEV;TrustServerCertificate=true"
},
"BasicSystemConfig": {
"OpenUserComplexPassword": false,
"OpenSignDocumentBeforeWork": false,
"OpenTrialRelationDelete": true,
"OpenLoginLimit": false,
"LoginMaxFailCount": 5,
"LoginFailLockMinutes": 30
},
"SystemEmailSendConfig": {
"Port": 465,
"Host": "smtp.qiye.aliyun.com",
"FromEmail": "test-study@extimaging.com",
"FromName": "Test_Study",
"AuthorizationCode": "zhanying123",
"SiteUrl": "http://test.study.extimaging.com/login"
}
}
@@ -1,85 +0,0 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"ConnectionStrings": {
"RemoteNew": "Server=us-prod-mssql-service,1433;Database=US_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
"Hangfire": "Server=us-prod-mssql-service,1433;Database=US_IRC_Hangfire;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",
},
"ObjectStoreService": {
"ObjectStoreUse": "AWS",
"MinIO": {
"endPoint": "44.210.231.169",
"port": "9001",
"useSSL": false,
"accessKey": "e9bT1isTOqSAUxb6wd4n",
"secretKey": "b5TaDzNdQCBtCvfm8eZ3dR6yY7tfZu2JYze2Po1i",
"bucketName": "prod-irc-us",
"viewEndpoint": "http://44.210.231.169:9001/prod-irc-us/"
},
"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-lili-store",
"ViewEndpoint": "https://ei-med-s3-lili-store.s3.amazonaws.com",
"DurationSeconds": 7200
}
},
"BasicSystemConfig": {
"OpenUserComplexPassword": false,
"OpenSignDocumentBeforeWork": false,
"OpenTrialRelationDelete": true,
"OpenLoginLimit": false,
"LoginMaxFailCount": 5,
"LoginFailLockMinutes": 30,
"AutoLoginOutMinutes": 60,
"ContinuousReadingTimeMin": 120,
"ReadingRestTimeMin": 10,
"IsNeedChangePassWord": true,
"ChangePassWordDays": 90
},
"SystemEmailSendConfig": {
"Port": 587,
"Host": "smtp-mail.outlook.com",
"FromEmail": "donotreply@elevateimaging.ai",
"FromName": "LiLi",
"AuthorizationCode": "Q#669869497420ul",
"OrganizationName": "Elevate Imaging",
"OrganizationNameCN": "Elevate Imaging",
"CompanyName": "Elevate Imaging Inc.",
"CompanyNameCN": "上海展影医疗科技有限公司",
"CompanyShortName": "Elevate Imaging",
"CompanyShortNameCN": "展影医疗",
"SiteUrl": "https://lili.elevateimaging.ai/login",
"IsEnv_US": true
},
"SystemPacsConfig": {
"Port": "104",
"IP": "44.210.231.169"
}
}
@@ -1,93 +0,0 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"ConnectionStrings": {
"RemoteNew": "Server=106.14.89.110,1435;Database=Test_IRC;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"
},
"ObjectStoreService": {
"ObjectStoreUse": "AWS",
"MinIO": {
//"endPoint": "hir-oss.uat.extimaging.com",
//"port": "443",
//"useSSL": true,
//"viewEndpoint": "https://hir-oss.uat.extimaging.com/hir-uat",
"endPoint": "47.117.164.182",
"port": "9001",
"useSSL": false,
"viewEndpoint": "http://47.117.164.182:9001/test-irc-us",
"accessKey": "b9Ul0e98xPzt6PwRXA1Q",
"secretKey": "DzMaU2L4OXl90uytwOmDXF2encN0Jf4Nxu2XkYqQ",
"bucketName": "test-irc-us"
},
"AWS": {
"Region": "us-east-1",
"EndPoint": "s3.us-east-1.amazonaws.com",
"UseSSL": true,
"RoleArn": "arn:aws:iam::471112624751:role/uat_s3_access",
"AccessKeyId": "AKIAW3MEAFJX7IPXISP4",
"SecretAccessKey": "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",
"BucketName": "ei-med-s3-lili-uat-store",
"ViewEndpoint": "https://ei-med-s3-lili-uat-store.s3.amazonaws.com",
"DurationSeconds": 7200
}
},
"BasicSystemConfig": {
"OpenUserComplexPassword": true,
"OpenSignDocumentBeforeWork": true,
"OpenTrialRelationDelete": true,
"OpenLoginLimit": true,
"LoginMaxFailCount": 5,
"LoginFailLockMinutes": 30,
"AutoLoginOutMinutes": 60,
"ContinuousReadingTimeMin": 120,
"ReadingRestTimeMin": 10,
"IsNeedChangePassWord": true,
"ChangePassWordDays": 90,
"OpenLoginMFA": true
},
"SystemEmailSendConfig": {
"Port": 587,
"Host": "smtp-mail.outlook.com",
"FromEmail": "donotreply@elevateimaging.ai",
"FromName": "LiLi",
"AuthorizationCode": "Q#669869497420ul",
"OrganizationName": "Elevate Imaging",
"OrganizationNameCN": "Elevate Imaging",
"CompanyName": "Elevate Imaging Inc.",
"CompanyNameCN": "上海展影医疗科技有限公司",
"CompanyShortName": "Elevate Imaging",
"CompanyShortNameCN": "展影医疗",
"SiteUrl": "https://lili.test.elevateimaging.ai/login",
"IsEnv_US": true
},
"SystemPacsConfig": {
"Port": "104",
"IP": "3.226.182.187"
}
}
@@ -1,97 +0,0 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"ConnectionStrings": {
//"RemoteNew": "Server=us-mssql-service,1433;Database=US_Uat_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
//"Hangfire": "Server=us-mssql-service,1433;Database=US_Uat_IRC_Hangfire;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"
},
"ObjectStoreService": {
"ObjectStoreUse": "AWS",
"MinIO": {
//"endPoint": "hir-minio.uat.elevateimaging.ai",
//"port": "443",
//"useSSL": true,
//"viewEndpoint": "https://hir-minio.uat.elevateimaging.ai/uat-irc-us",
"endPoint": "3.226.182.187",
"port": "9001",
"useSSL": false,
"viewEndpoint": "http://44.218.11.19:9001/uat-irc-us",
"accessKey": "lH8DkKskLuDqPaiubuSQ",
"secretKey": "pdPdicvvLeH7xAC5yFUrI7odMyBfOXxvVWMvKYV4",
"bucketName": "uat-irc-us"
},
"AWS": {
"Region": "us-east-1",
"EndPoint": "s3.us-east-1.amazonaws.com",
"UseSSL": true,
"RoleArn": "arn:aws:iam::471112624751:role/uat_s3_access",
"AccessKeyId": "AKIAW3MEAFJX7IPXISP4",
"SecretAccessKey": "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",
"BucketName": "ei-med-s3-lili-uat-store",
"ViewEndpoint": "https://ei-med-s3-lili-uat-store.s3.amazonaws.com/",
"DurationSeconds": 7200
}
},
"BasicSystemConfig": {
"OpenUserComplexPassword": true,
"OpenSignDocumentBeforeWork": true,
"OpenTrialRelationDelete": true,
"OpenLoginLimit": false,
"LoginMaxFailCount": 5,
"LoginFailLockMinutes": 30,
"AutoLoginOutMinutes": 60,
"ContinuousReadingTimeMin": 120,
"ReadingRestTimeMin": 10,
"IsNeedChangePassWord": true,
"ChangePassWordDays": 90,
"OpenLoginMFA": false
},
"SystemEmailSendConfig": {
"Port": 587,
"Host": "smtp-mail.outlook.com",
"FromEmail": "donotreply@elevateimaging.ai",
"FromName": "LiLi",
"AuthorizationCode": "Q#669869497420ul",
"OrganizationName": "Elevate Imaging",
"OrganizationNameCN": "Elevate Imaging",
"CompanyName": "Elevate Imaging Inc.",
"CompanyNameCN": "上海展影医疗科技有限公司",
"CompanyShortName": "Elevate Imaging",
"CompanyShortNameCN": "展影医疗",
"SiteUrl": "https://lili.test.elevateimaging.ai/login",
"IsEnv_US": true
},
"SystemPacsConfig": {
"Port": "104",
"IP": "3.226.182.187"
}
}
-98
View File
@@ -1,98 +0,0 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"ConnectionStrings": {
"RemoteNew": "Server=47.117.164.182,1434;Database=Uat_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
"Hangfire": "Server=47.117.164.182,1434;Database=Uat_IRC.Hangfire;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": "LTAI5tRRZehUp2V9pyTPtAJm",
"AccessKeySecret": "FLizxkHsMm4CGYHtkV8E3PNJJZU7oV",
"RoleArn": "acs:ram::1899121822495495:role/dev-oss-access",
"BucketName": "zy-irc-uat-store",
"ViewEndpoint": "https://zy-irc-uat-store.oss-cn-shanghai.aliyuncs.com",
"Region": "oss-cn-shanghai",
"DurationSeconds": 7200
},
"MinIO": {
"endPoint": "hir-oss.uat.extimaging.com",
"port": "80",
"useSSL": false,
"viewEndpoint": "http://hir-oss.uat.extimaging.com/irc-uat",
//"port": "443",
//"useSSL": true,
//"viewEndpoint": "https://hir-oss.uat.extimaging.com/irc-uat",
"accessKey": "b9Ul0e98xPzt6PwRXA1Q",
"secretKey": "DzMaU2L4OXl90uytwOmDXF2encN0Jf4Nxu2XkYqQ",
"bucketName": "irc-uat"
},
"AWS": {
"Region": "us-east-1",
"EndPoint": "s3.us-east-1.amazonaws.com",
"UseSSL": true,
"RoleArn": "arn:aws:iam::471112624751:role/sts_s3_upload",
"AccessKeyId": "AKIAW3MEAFJXWRCGSX5Z",
"SecretAccessKey": "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",
"BucketName": "ei-med-s3-lili-uat-store",
"ViewEndpoint": "https://ei-med-s3-lili-uat-store.s3.amazonaws.com/",
"DurationSeconds": 7200
}
},
"BasicSystemConfig": {
"OpenUserComplexPassword": true,
"OpenSignDocumentBeforeWork": true,
"OpenLoginLimit": true,
"LoginMaxFailCount": 5,
"LoginFailLockMinutes": 30,
"AutoLoginOutMinutes": 60,
"OpenLoginMFA": false,
"ContinuousReadingTimeMin": 120,
"ReadingRestTimeMin": 10,
"IsNeedChangePassWord": true,
"ChangePassWordDays": 90
},
"SystemEmailSendConfig": {
"Port": 465,
"Host": "smtp.qiye.aliyun.com",
"FromEmail": "uat@extimaging.com",
"FromName": "UAT_IRC",
"AuthorizationCode": "SHzyyl2021",
"SiteUrl": "http://irc.uat.extimaging.com/login",
"OrganizationName": "Extlmaging",
"OrganizationNameCN": "Extlmaging",
"CompanyName": "Extensive Imaging",
"CompanyNameCN": "上海展影医疗科技有限公司",
"CompanyShortName": "Extensive Imaging",
"CompanyShortNameCN": "展影医疗",
"IsEnv_US": false
},
"SystemPacsConfig": {
"Port": "11113",
"IP": "47.117.164.182"
}
}
@@ -0,0 +1,46 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AliyunOSS": {
"regionId": "cn-shanghai",
"region": "oss-cn-shanghai",
"endpoint": "https://oss-cn-shanghai.aliyuncs.com",
"accessKeyId": "LTAI5tKvzs7ed3UfSpNk3xwQ",
"accessKeySecret": "zTIceGEShlZDGnLrCFfIGFE7TXVRio",
"bucketName": "zy-sir-uat-store",
"viewEndpoint": "https://zy-sir-uat-store.oss-cn-shanghai.aliyuncs.com"
},
"ConnectionStrings": {
"RemoteNew": "Server=47.117.164.182,1434;Database=Uat.Study;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
"Hangfire": "Server=47.117.164.182,1434;Database=Uat.Study.hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
},
"BasicSystemConfig": {
"OpenUserComplexPassword": false,
"OpenSignDocumentBeforeWork": false,
"OpenTrialRelationDelete": true,
"OpenLoginLimit": false,
"LoginMaxFailCount": 5,
"LoginFailLockMinutes": 30
},
"SystemEmailSendConfig": {
"Port": 465,
"Host": "smtp.qiye.aliyun.com",
"FromEmail": "uat-study@extimaging.com",
"FromName": "Uat_Study",
"AuthorizationCode": "zhanying123",
"SiteUrl": "http://uat.study.extimaging.com/login"
}
}
+40 -12
View File
@@ -1,10 +1,11 @@
{
"JwtSetting": {
"SecurityKey": "ShangHaiZhanYing_SecurityKey_SHzyyl@2021",
"Issuer": "Extimaging",
"Audience": "EICS",
"SecurityKey": "3e6dbfa227234a03977a2f421bdb7f4f",
"Issuer": "IRaCIS",
"Audience": "ZhiZhun",
"TokenExpireDays": "7"
},
//ip
"IpRateLimiting": {
"EnableEndpointRateLimiting": true,
"StackBlockedRequests": false,
@@ -19,6 +20,7 @@
"EndpointWhitelist": [
"post:/study/archivestudy/*"
],
"IpWhitelist": [],
"GeneralRules": [
{
@@ -43,6 +45,40 @@
}
]
},
"easycaching": {
"inmemory": {
"MaxRdSecond": 120,
"EnableLogging": false,
"LockMs": 5000,
"SleepMs": 300,
"DBConfig": {
"SizeLimit": 10000,
"ExpirationScanFrequency": 60,
"EnableReadDeepClone": true,
"EnableWriteDeepClone": false
}
},
"redis": {
"MaxRdSecond": 120,
"EnableLogging": false,
"LockMs": 5000,
"SleepMs": 300,
"dbconfig": {
"Password": "xc@123456",
"IsSsl": false,
"SslHost": null,
"ConnectionTimeout": 5000,
"AllowAdmin": true,
"Endpoints": [
{
"Host": "47.117.164.182",
"Port": 6379
}
],
"Database": 0
}
}
},
"IRaCISImageStore": {
"SwitchingMode": "RemainingDiskCapacity",
"SwitchingRatio": 80,
@@ -54,17 +90,9 @@
"QCChallengeCodePrefix": "Q",
"NoneDicomStudyCodePrefix": "NST",
"DicomStudyCodePrefix": "ST",
"TaskStudyCodePrefix": "P-ST",
"SystemSiteCodePrefix": "S",
"DefaultPassword": "123456",
"DefaultInternalOrganizationName": "ExtImaging",
"ImageShareExpireDays": 10
},
"EncrypteResponseConfig": {
"Base64RSAPublicKey": "LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0NCk1JSUJJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBUThBTUlJQkNnS0NBUUVBaHp3T1hYTWYyaEFkS1ZoWHczYUYNCmNaT3QycE1lcmdEaFVrOUdQK2s4VDBrUjFTRVlGVGtzNlkzaEVvL0dRTExqMHZFYVV3bTNhSFNuTTl5NmdLRWoNCmY5cTN6dkoyZzRSQjE4Z0UrTnNWWi9DMkVRZ3k5OWFiWGc5TitGREVlT0NmSjlSRTJPV3JBQ2s0V0RPbFFUdXYNCnhvR2JmcnkwVElSaFBrOGtuYkFmVkZ1and1VXJGblpJZ0ExYXhKZVZ6aDhwcmV1SEgreW1jdHp6NVo4V1pSV3kNCi9ISURHUy90dkg2NUMra2l6cUxRYUpKNHYwODMrRGpaVTBmNzNCdkk5eWt1dW1saXFzY1pvU2preDFOUFJwSkUNCkFtMVFNQ0hMRCtFdzlHT2Vsc2Mwa1ZxdjdaeEF1TkFrMkZuUURNRk1BUmZuUFd0aGVhOGZYVTJsMW9ROWs3WDcNCmN3SURBUUFCDQotLS0tLUVORCBQVUJMSUMgS0VZLS0tLS0NCg==",
"Base64RSAPrivateKey": "LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQ0KTUlJRW9nSUJBQUtDQVFFQWh6d09YWE1mMmhBZEtWaFh3M2FGY1pPdDJwTWVyZ0RoVWs5R1ArazhUMGtSMVNFWQ0KRlRrczZZM2hFby9HUUxMajB2RWFVd20zYUhTbk05eTZnS0VqZjlxM3p2SjJnNFJCMThnRStOc1ZaL0MyRVFneQ0KOTlhYlhnOU4rRkRFZU9DZko5UkUyT1dyQUNrNFdET2xRVHV2eG9HYmZyeTBUSVJoUGs4a25iQWZWRnVqd3VVcg0KRm5aSWdBMWF4SmVWemg4cHJldUhIK3ltY3R6ejVaOFdaUld5L0hJREdTL3R2SDY1QytraXpxTFFhSko0djA4Mw0KK0RqWlUwZjczQnZJOXlrdXVtbGlxc2Nab1Nqa3gxTlBScEpFQW0xUU1DSExEK0V3OUdPZWxzYzBrVnF2N1p4QQ0KdU5BazJGblFETUZNQVJmblBXdGhlYThmWFUybDFvUTlrN1g3Y3dJREFRQUJBb0lCQUNDRFoxNi9XWWFYZmpOZA0KQ29pemU2VFJZU2llVzI5eFJic0ExVDV6YUcwVmY4U1NsdFF2Y1dWYmw2UGJUa3BxMkF4MHJDUVB2M2xOSm8vNA0KL3h3QzRlS1E1c1ZLRlFWTXJIbmhISlRxTTJ6UWVpMkJINlBuaEdZcVh0QVhOdzFxejhrSEoyQlFZM3IvN2d5Qw0KcWpZVFVCRDFRem5HeThCanlXOXVIcnNNeDVPRHRRZWxBM3B1TFd1bXZNb3Z1L2JhaDZvTGtOSHY4b0VTdzhGSQ0KTllyTUtscHhFTjZaWUdwSTl2VTZnYUhuTmhEa2ExTHlvUnZ5NnA2dTRLR0FsRTc1VXk4T0dsdncydU5uay9sdg0KSEMyYnY5TnlCRGJpSFNDY0MyK1JXUXMrN0tNWFlwYnBvTVFCR0hqV01GRHVBODFaUCs1TWYxUm9yQUpRNGxrRw0KQnRDQThva0NnWUVBeWY4alBjcFIvQ3dJNU5MYnlwb3ZUWEFHVkFKMmtWRlJVUC9HT2ZOMkFyYWMvL20wdDJ5NA0KemNYVkJZc0pJeHkvVWYxRTFJNFg3VFg3V3NBNGxVNGlPTkwzTnN4dDBEbk1PV2tKUlBPZlF1bW1JaWw5QVRiYQ0KTnRVWFNlTmRoUFFGMGlCb21acFFJYWpSN1RmUnVBbzR6dmpTLzkzeXZZY0lIWU1zM0tjR00ya0NnWUVBcTJPbg0KZlp0RmhLTElGanVlRzRrNklGTWdjbytEUUh5TTFWSUp5Tk05K1o2TEgybzI5eDJCaSs2Qm82M2NKUjQ2STZncQ0KNWUrSTBvdzZRYmNQeTZUNHFSQ0o3Ujd6MllkdEw4eXdJTkNYS3U0cC9qaFNqNWJ1TzFJWlI3ZStSV29CakNtUQ0KWFd0ZVBCbldqWVlLdVRCazROc2FXV09GTXg1QndKdUp2MjBnQ0hzQ2dZQlV4QnFYM1lWV0cyeUlDZXh1TXhIUw0KbjBZb2p2Z090MTgyYkg5VTVsUUpnM1NTL3NqVmlHeHMvYTROSzNGa0tMWW93KzNVZk9TUmlPdTRBNTQ3R1pURw0KMzlFYVQrTnRWRFBkaTdSMkdQNG1hRUp0WjVlcm9NY2w1M3BrYVdOZlhiL3JrK29STzI2UkVYVTI1UXUrL1pzbA0KVDhuTDBlb0JtdDdPODdNcHpYV09zUUtCZ0ZxVGFQSGx2RUNUY3FEbFV2S0VmRmFXOTkvelhrOFhRNnA5RjdTdA0KaHVSRDJJeDZxcC9BVlRWcGo5Tzd6MHRDaFVGUTM1THpHMkVDUU10My9uNEdLbS9XMEwyakRRWWFIeWNTeXNZYw0KMXJjV2ROVG9XU0dQaDBtTVl0WFhFbFJHNkpoMVl0a3NJL29wUVkwN21MRTBGU3dNUHdtY29jbFpKVEN3UW9VTA0KRzlHL0FvR0FWM25kcWcydnUyR0x4TlRUbm1UTWRJNFB3UzBmN0V4VnVNUnRicGZyOWhKNzlDakVNTGFXQ1FsNg0KeE43TElMTnArNVQwQW1DZVBqcitkbnJkRUNidFZPTDFybDc3Z0krRkwzaVVqYmZmMVZqa0N3M0x6K3cyb1FFdA0KbGE4aTZrL1NRK01iYkRPaWRJOVczdlN6MmlJRlZobWJiK1Q2SlZwakxqNjlkblM3eUxZPQ0KLS0tLS1FTkQgUlNBIFBSSVZBVEUgS0VZLS0tLS0NCg==",
"IsResponseEncreptEnable": true,
"ApiPathList": [
"/test/get"
]
}
}
@@ -0,0 +1,40 @@
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<title>Title</title>
</head>
<body style='font-family: 微软雅黑;padding: 0;margin: 0;'>
<div style='padding-left: 40px;background: #f6f6f6'>
<div style='padding-top: 20px;'>
<div style='line-height: 40px;font-size: 18px'>
尊敬的 {0} ,您好:
</div>
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
展影医疗为您添加了账户,账户信息如下:
</div>
<span style="color: #00D1B2"></span>
<div style='border: 1px solid #eee;box-sizing:border-box;width: 50%;background: #fff;padding: 20px;line-height: 40px;font-size: 14px;border-radius: 5px;margin-left: 60px;margin-bottom: 30px;'>
<div>
用户名: {1}
</div>
<div>
角色: {2}
</div>
<div>
首次登陆前,请通过该链接修改您的账户信息:
<a href='{3}' style='margin-left:30px;font-size:14px;text-decoration: none;display: inline-block;color:#00D1B2;border-radius: 5px;line-height: 40px;text-align: center;'>
初始化账号信息
</a>
</div>
</div>
<div style='line-height: 24px;font-size: 14px;color:#333;margin-top: 20px;padding-bottom: 40px;'>
<div>祝您顺利!/Best Regards</div>
<div style="font-size: 14px;">展影医疗</div>
</div>
</div>
</div>
</body>
</html>
@@ -0,0 +1,40 @@
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<title>Title</title>
</head>
<body style='font-family: Times New Roman;padding: 0;margin: 0;'>
<div style='padding-left: 40px;background: #f6f6f6;font-size: 16px'>
<div style='padding-top: 20px;'>
<div style='line-height: 40px;font-size: 16px'>
Dear {0} ,
</div>
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;font-size: 16px'>
Extensive Imaging has generated a user account for you. <br>The account information is as follows:
</div>
<span style="color: #00D1B2"></span>
<div style='border: 1px solid #eee;box-sizing:border-box;width: 50%;background: #fff;padding: 20px;line-height: 40px;font-size: 14px;border-radius: 5px;margin-left: 60px;margin-bottom: 30px;font-size: 16px'>
<div>
User ID: {1}
</div>
<div>
Role: {2}
</div>
<div>
Before logging in for the first time, please initialize your account information through this link:
<a href='{3}' style='margin-left:30px;font-size:12px;text-decoration: none;display: inline-block;color:#00D1B2;border-radius: 5px;line-height: 40px;text-align: center;font-size: 16px'>
Initialize Account Information
</a>
</div>
</div>
<div style='line-height: 24px;font-size: 16px;color:#333;margin-top: 20px;padding-bottom: 40px;'>
<div>Best regards,</div>
<div style="font-size: 16px;">Extensive Imaging</div>
</div>
</div>
</div>
</body>
</html>
@@ -0,0 +1,37 @@
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<title>Title</title>
</head>
<body style='font-family: 微软雅黑;padding: 0;margin: 0;'>
<div style='padding-left: 40px;background: #f6f6f6'>
<div style='padding-top: 20px;'>
<div style='line-height: 40px;font-size: 18px'>
尊敬的 {0} ,您好:
</div>
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
展影医疗将您的账户密码已重置,账户信息如下:
</div>
<span style="color: #00D1B2"></span>
<div style='border: 1px solid #eee;box-sizing:border-box;width: 50%;background: #fff;padding: 20px;line-height: 40px;font-size: 14px;border-radius: 5px;margin-left: 60px;margin-bottom: 30px;'>
<div>
用户名: {1}
</div>
<div>
角色: {2}
</div>
<div>
密码: {3}
</div>
</div>
<div style='line-height: 24px;font-size: 14px;color:#333;margin-top: 20px;padding-bottom: 40px;'>
<div>祝您顺利!/Best Regards</div>
<div style="font-size: 14px;">展影医疗</div>
</div>
</div>
</div>
</body>
</html>
@@ -0,0 +1,37 @@
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<title>Title</title>
</head>
<body style='font-family: Times New Roman;padding: 0;margin: 0;'>
<div style='padding-left: 40px;background: #f6f6f6;font-size: 16px'>
<div style='padding-top: 20px;'>
<div style='line-height: 40px;font-size: 16px'>
Dear {0} ,
</div>
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;font-size: 16px'>
Extensive Imaging has reset your password.<br>The account information is as follows:
</div>
<span style="color: #00D1B2"></span>
<div style='border: 1px solid #eee;box-sizing:border-box;width: 50%;background: #fff;padding: 20px;line-height: 40px;font-size: 12px;border-radius: 5px;margin-left: 60px;margin-bottom: 30px;font-size: 16px'>
<div>
User ID: {1}
</div>
<div>
Role: {2}
</div>
<div>
Password: {3}
</div>
</div>
<div style='line-height: 24px;font-size: 16px;color:#333;margin-top: 20px;padding-bottom: 40px;'>
<div>Best regards,</div>
<div style="font-size: 16px;">Extensive Imaging</div>
</div>
</div>
</div>
</body>
</html>
@@ -0,0 +1,27 @@
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<title>Title</title>
</head>
<body style='font-family: 微软雅黑;padding: 0;margin: 0;'>
<div style='padding-left: 40px;background: #f6f6f6'>
<div style='padding-top: 20px;'>
<div style='line-height: 40px;font-size: 18px'>
您好:
</div>
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
感谢您使用展影云平台。
</div>
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
{0}。
</div>
<div style='line-height: 24px;font-size: 14px;color:#333;margin-top: 20px;padding-bottom: 40px;'>
<div>祝您顺利!/Best Regards</div>
<div style="font-size: 14px;">展影医疗</div>
</div>
</div>
</div>
</body>
</html>
@@ -0,0 +1,27 @@
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<title>Title</title>
</head>
<body style='font-family: Times New Roman;padding: 0;margin: 0;font-size: 16px'>
<div style='padding-left: 40px;background: #f6f6f6;font-size: 16px'>
<div style='padding-top: 20px;'>
<div style='line-height: 40px;font-size: 16px'>
Dear Sir or Madam:
</div>
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;font-size: 16px'>
Thank you for using our IRC imaging system.
</div>
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;font-size: 16px'>
{0}。
</div>
<div style='line-height: 24px;font-size: 16px;color:#333;margin-top: 20px;padding-bottom: 40px;'>
<div>Best regards,</div>
<div style="font-size: 16px;">Extensive Imaging</div>
</div>
</div>
</div>
</body>
</html>
@@ -0,0 +1,48 @@
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<title>Title</title>
</head>
<body style='font-family: 微软雅黑;padding: 0;margin: 0;'>
<div style='padding-left: 40px;background: #f6f6f6'>
<div style='padding-top: 20px;'>
<div style='line-height: 40px;font-size: 18px'>
尊敬的 {0} ,您好:
</div>
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
展影医疗作为 [{1} (试验方案号:{2 })] 项目的供应商,诚邀您参加该项目阅片相关工作。
</div>
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
该项目采用电子化工作流,系统及您的账号信息如下:
</div>
<span style="color: #00D1B2"></span>
<div style='border: 1px solid #eee;box-sizing:border-box;width: 50%;background: #fff;padding: 20px;line-height: 40px;font-size: 14px;border-radius: 5px;margin-left: 60px;margin-bottom: 30px;'>
<div>
项目编号: {3}
</div>
<div>
试验方案号: {2}
</div>
<div>
试验名称: {1}
</div>
<div>
用户名: {4}
</div>
<div>
角色: {5}
</div>
<div>
系统登录地址:{6}
</div>
</div>
<div style='line-height: 24px;font-size: 14px;color:#333;margin-top: 20px;padding-bottom: 40px;'>
<div>祝您顺利!/Best Regards</div>
<div style="font-size: 14px;">展影医疗</div>
</div>
</div>
</div>
</body>
</html>
@@ -0,0 +1,48 @@
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<title>Title</title>
</head>
<body style='font-family: Times New Roman;padding: 0;margin: 0;'>
<div style='padding-left: 40px;background: #f6f6f6'>
<div style='padding-top: 20px;'>
<div style='line-height: 40px;font-size: 16px'>
Dear {0},
</div>
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;font-size: 16px'>
As the service provider of {1} trialProtocol ID is {2}, Extensive Imaging invites you to participate in the independent assessment work of this trial.
</div>
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;font-size: 16px'>
Digital workflow is implemented for this trial, and your account information is as follows:
</div>
<span style="color: #00D1B2"></span>
<div style='border: 1px solid #eee;box-sizing:border-box;width: 50%;background: #fff;padding: 20px;line-height: 40px;font-size: 14px;border-radius: 5px;margin-left: 60px;margin-bottom: 30px;font-size: 16px'>
<div>
Trial ID: {3}
</div>
<div>
Protocol ID: {2}
</div>
<div>
Study Name: {1}
</div>
<div>
User Name: {4}
</div>
<div>
Role: {5}
</div>
<div>
Login URL: {6}
</div>
</div>
<div style='line-height: 24px;font-size: 16px;color:#333;margin-top: 20px;padding-bottom: 40px;'>
<div>Best regards,</div>
<div style="font-size: 16px;">Extensive Imaging</div>
</div>
</div>
</div>
</body>
</html>
@@ -0,0 +1,52 @@
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<title>Title</title>
</head>
<body style='font-family: 微软雅黑;padding: 0;margin: 0;'>
<div style='padding-left: 40px;background: #f6f6f6'>
<div style='padding-top: 20px;'>
<div style='line-height: 40px;font-size: 18px'>
尊敬的 {0} ,您好:
</div>
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
展影医疗作为 [{1} (试验方案号:{2 })] 项目的供应商,诚邀您参加该项目阅片相关工作,欢迎您提供指导和建议,非常感谢!
</div>
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
该项目采用电子化工作流,系统及您的账号信息如下:
</div>
<span style="color: #00D1B2"></span>
<div style='border: 1px solid #eee;box-sizing:border-box;width: 50%;background: #fff;padding: 20px;line-height: 40px;font-size: 14px;border-radius: 5px;margin-left: 60px;margin-bottom: 30px;'>
<div>
项目编号: {3}
</div>
<div>
试验方案号: {2}
</div>
<div>
试验名称: {1}
</div>
<div>
用户名: {4}
</div>
<div>
角色: {5}
</div>
<div>
首次登陆前,请通过该链接修改您的账户信息:
<a href='{6}' style='margin-left:30px;font-size:14px;text-decoration: none;display: inline-block;color:#00D1B2;border-radius: 5px;line-height: 40px;text-align: center;'>
初始化账号信息
</a>
</div>
</div>
<div style='line-height: 24px;font-size: 14px;color:#333;margin-top: 20px;padding-bottom: 40px;'>
<div>祝您顺利!/Best Regards</div>
<div style="font-size: 14px;">展影医疗</div>
</div>
</div>
</div>
</body>
</html>
@@ -0,0 +1,53 @@
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<title>Title</title>
</head>
<body style='font-family: Times New Roman;padding: 0;margin: 0;'>
<div style='padding-left: 40px;background: #f6f6f6'>
<div style='padding-top: 20px;'>
<div style='line-height: 40px;font-size: 16px'>
Dear {0},
</div>
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;font-size: 16px'>
As the service provider of {1}trial (Protocol ID is {2}), Extensive Imaging invites you to participate in the independent assessment work of this trial.
</div>
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;font-size: 16px'>
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;font-size: 16px'>
Digital workflow is implemented for this trial, and your account information is as follows:
</div>
<span style="color: #00D1B2"></span>
<div style='border: 1px solid #eee;box-sizing:border-box;width: 50%;background: #fff;padding: 20px;line-height: 40px;font-size: 14px;border-radius: 5px;margin-left: 60px;margin-bottom: 30px;font-size: 16px'>
<div>
Trial ID: {3}
</div>
<div>
Protocol ID: {2}
</div>
<div>
Study Name: {1}
</div>
<div>
User ID: {4}
</div>
<div>
Role: {5}
</div>
<div>
Before logging in for the first time, please modify your account information through this link:
<a href='{6}' style='margin-left:30px;font-size:16px;text-decoration: none;display: inline-block;color:#00D1B2;border-radius: 5px;line-height: 40px;text-align: center;'>
Initialize Account Information
</a>
</div>
</div>
<div style='line-height: 24px;font-size: 14px;color:#333;margin-top: 20px;padding-bottom: 40px;font-size: 16px'>
<div>Best regards,</div>
<div style="font-size: 16px;">Extensive Imaging</div>
</div>
</div>
</div>
</body>
</html>
@@ -0,0 +1,49 @@
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<title>Title</title>
</head>
<body style='font-family: 微软雅黑;padding: 0;margin: 0;'>
<div style='padding-left: 40px;background: #f6f6f6'>
<div style='padding-top: 20px;'>
<div style='line-height: 40px;font-size: 18px'>
尊敬的 {0} ,您好:
</div>
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
您好, 您填写的中心调研表被驳回,详细信息如下:
</div>
<span style="color: #00D1B2"></span>
<div style='border: 1px solid #eee;box-sizing:border-box;width: 50%;background: #fff;padding: 20px;line-height: 40px;font-size: 14px;border-radius: 5px;margin-left: 60px;margin-bottom: 30px;'>
<div>
项目编号: {1}
</div>
<div>
试验方案号: {2}
</div>
<div>
试验名称: {3}
</div>
<div>
中心编号: {4}
</div>
<div>
中心名称: {5}
</div>
<div>
驳回原因: {6}
</div>
</div>
<a href='{7}' style='margin-left:60px;font-size:14px;padding: 10px;text-decoration: none;display:{8} ;height: 40px;width: 140px;background: #00D1B2;color:#fff;border-radius: 5px;line-height: 40px;text-align: center;margin-bottom: 100px;'>
登陆并查看
</a>
<div style='line-height: 24px;font-size: 14px;color:#333;margin-top: 20px;padding-bottom: 40px;'>
<div>祝您顺利!/Best Regards</div>
<div style="font-size: 14px;">展影医疗</div>
</div>
</div>
</div>
</body>
</html>
@@ -0,0 +1,49 @@
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<title>Title</title>
</head>
<body style='font-family: Times New Roman;padding: 0;margin: 0;font-size: 16px'>
<div style='padding-left: 40px;background: #f6f6f6;font-size: 16px'>
<div style='padding-top: 20px;font-size: 16px'>
<div style='line-height: 40px;font-size: 16px'>
Dear {0} ,
</div>
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;font-size: 16px'>
The site survey questionnaire you filled in has been rejected. Details are as follows:
</div>
<span style="color: #00D1B2"></span>
<div style='border: 1px solid #eee;box-sizing:border-box;width: 50%;background: #fff;padding: 20px;line-height: 40px;font-size: 14px;border-radius: 5px;margin-left: 60px;margin-bottom: 30px;font-size: 16px'>
<div>
Trial ID: {1}
</div>
<div>
Protocol ID: {2}
</div>
<div>
Study Name: {3}
</div>
<div>
Site ID: {4}
</div>
<div>
Site Name: {5}
</div>
<div>
Reason for rejection: {6}
</div>
</div>
<a href='{7}' style='margin-left:60px;font-size:16px;padding: 10px;text-decoration: none;display:{8} ;height: 40px;width: 140px;background: #00D1B2;color:#fff;border-radius: 5px;line-height: 40px;text-align: center;margin-bottom: 100px;font-size: 16px'>
Login & View
</a>
<div style='line-height: 24px;font-size: 16px;color:#333;margin-top: 20px;padding-bottom: 40px;'>
<div>Best regards,</div>
<div style="font-size: 16px;">Extensive Imaging</div>
</div>
</div>
</div>
</body>
</html>
@@ -0,0 +1,49 @@
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<title>Title</title>
</head>
<body style='font-family: 微软雅黑;padding: 0;margin: 0;'>
<div style='padding-left: 40px;background: #f6f6f6'>
<div style='padding-top: 20px;'>
<div style='line-height: 40px;font-size: 18px'>
尊敬的 {0} ,您好:
</div>
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
展影医疗作为 [{1} (试验方案号:{2 })] 项目的供应商,诚邀您参加该项目相关工作,欢迎您提供指导和建议,非常感谢!
</div>
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
该项目采用电子化工作流,系统及您的账号信息如下:
</div>
<span style="color: #00D1B2"></span>
<div style='border: 1px solid #eee;box-sizing:border-box;width: 50%;background: #fff;padding: 20px;line-height: 40px;font-size: 14px;border-radius: 5px;margin-left: 60px;margin-bottom: 30px;'>
<div>
项目编号: {3}
</div>
<div>
试验方案号: {2}
</div>
<div>
试验名称: {1}
</div>
<div>
用户名: {4}
</div>
<div>
角色: {5}
</div>
<div>
系统登录地址:{6}
</div>
</div>
<div style='line-height: 24px;font-size: 14px;color:#333;margin-top: 20px;padding-bottom: 40px;'>
<div>祝您顺利!/Best Regards</div>
<div style="font-size: 14px;">展影医疗</div>
</div>
</div>
</div>
</body>
</html>
@@ -0,0 +1,49 @@
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<title>Title</title>
</head>
<body style='font-family: Times New Roman;padding: 0;margin: 0;font-size: 16px'>
<div style='padding-left: 40px;background: #f6f6f6;font-size: 16px'>
<div style='padding-top: 20px;font-size: 16px'>
<div style='line-height: 40px;font-size: 16px'>
Dear {0},
</div>
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;font-size: 16px'>
As the service provider of {1} trial (Protocol ID is {2}), Extensive Imaging invites you to participate in related work of this trial.
</div>
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;font-size: 16px'>
Digital workflow is implemented for this trial, and your account information is as follows:
</div>
<span style="color: #00D1B2"></span>
<div style='border: 1px solid #eee;box-sizing:border-box;width: 50%;background: #fff;padding: 20px;line-height: 40px;font-size: 14px;border-radius: 5px;margin-left: 60px;margin-bottom: 30px;font-size: 16px'>
<div>
Trial ID: {3}
</div>
<div>
Protocol ID: {2}
</div>
<div>
Study Name: {1}
</div>
<div>
User Name: {4}
</div>
<div>
Role: {5}
</div>
<div>
Login URL: {6}
</div>
</div>
<div style='line-height: 24px;font-size: 16px;color:#333;margin-top: 20px;padding-bottom: 40px;'>
<div>Best regards,</div>
<div style="font-size: 16px;">Extensive Imaging</div>
</div>
</div>
</div>
</body>
</html>

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