Compare commits
1141 Commits
EICS-V1.8.
...
Test_IRC_N
Author | SHA1 | Date |
---|---|---|
|
b143ffbcfc | |
|
1d8be8c04b | |
|
139e1f8186 | |
|
81cb43d3a3 | |
|
c2db7a2c61 | |
|
8cd19b929c | |
|
c62e7b13a9 | |
|
fc81738176 | |
|
b1e1840c87 | |
|
b31d7ee762 | |
|
fb0c3879cf | |
|
5ffe788a3b | |
|
056573874a | |
|
c959a47fa6 | |
|
41fe678708 | |
|
e7bbf09a50 | |
|
3bd16d7fce | |
|
db1d1b0f14 | |
|
ef980fdc68 | |
|
4de186a36b | |
|
0dd73991f0 | |
|
27e8e0e79c | |
|
fa53d6e88e | |
|
f3fc3fa4bf | |
|
476f692355 | |
|
41b2a638fe | |
|
c3631eee51 | |
|
3ed74a578e | |
|
918728a42e | |
|
a8c0bef99e | |
|
b3468a6d1b | |
|
dba8d3c964 | |
|
8f5b024d3b | |
|
ea836d9349 | |
|
c32878911d | |
|
0c4bd680dd | |
|
90cd6ad0eb | |
|
013561541c | |
|
f1f584c337 | |
|
c443969f18 | |
|
01621511f1 | |
|
0740b38b10 | |
|
ebd70e2202 | |
|
80c026d5b2 | |
|
8e2fbf06c5 | |
|
daee20e309 | |
|
3ba7481df8 | |
|
1e299e3f9e | |
|
05cc46a6d6 | |
|
f1b943b6b0 | |
|
9f00c1c49b | |
|
b1fd623065 | |
|
7683866718 | |
|
d58395c25e | |
|
15dd21451e | |
|
483c7c68e8 | |
|
738ad29c0b | |
|
f87b57ecc5 | |
|
fccd704659 | |
|
e5b41735ec | |
|
07b496eb67 | |
|
762e46480c | |
|
a7ad22ed28 | |
|
8610a627ea | |
|
302658d467 | |
|
97c882b0b2 | |
|
d711bb4b7f | |
|
1a4ca6e36b | |
|
aaac85b7b2 | |
|
cb3e4bc671 | |
|
9f187e4b36 | |
|
663ec11c8d | |
|
122447a621 | |
|
31bd3c646b | |
|
f53fc409cb | |
|
7f4357de68 | |
|
23fa689498 | |
|
e564debbab | |
|
04aa2c79f6 | |
|
68d66523e8 | |
|
549f394d43 | |
|
5a83bf9221 | |
|
65bffa26b7 | |
|
39a6f6a9dc | |
|
fc02428435 | |
|
4262189508 | |
|
6dd6a5f696 | |
|
e03f2e35b5 | |
|
ea4a3db145 | |
|
f4ac12d4f8 | |
|
a2afe4f6e4 | |
|
ef31afa1d8 | |
|
d9fda60b99 | |
|
6508698bda | |
|
734675a1a5 | |
|
d2697d5133 | |
|
1c7f4eac90 | |
|
6dcaa953d4 | |
|
1f67d24abc | |
|
c7a310c1ad | |
|
578aaa467c | |
|
82a7c4de29 | |
|
fe4df21a68 | |
|
9cb6fb2a92 | |
|
37cfe07ea6 | |
|
52ffdac948 | |
|
83a15a0b47 | |
|
1952a108a9 | |
|
ccff36507f | |
|
285fd0e1bb | |
|
918aafa7e5 | |
|
60accb2f87 | |
|
018c9afee0 | |
|
1b9351b23f | |
|
86ea3373fc | |
|
dd3b43da7b | |
|
3151e42e78 | |
|
5d8f94ef65 | |
|
26acc94172 | |
|
5c663ec3e9 | |
|
5c0917f14e | |
|
74230da400 | |
|
df104ec527 | |
|
5cdef2051a | |
|
470d05131e | |
|
5e046b847d | |
|
8b6fcf9ae1 | |
|
4dfa1e9812 | |
|
a05cb71e0d | |
|
137b18446c | |
|
e44478dafe | |
|
0a5a36adb3 | |
|
91e4553795 | |
|
f504cfee9e | |
|
2e7e3acf68 | |
|
c00b98daf8 | |
|
235f420e97 | |
|
da15b352a1 | |
|
9857b1ae92 | |
|
9e83f8df36 | |
|
ae431df9b5 | |
|
e5592c5bfa | |
|
1b2bca9f1e | |
|
4235e6025a | |
|
209ce439c2 | |
|
eabf8f4532 | |
|
07ddb7b234 | |
|
1f157bd1a2 | |
|
0c28f85f49 | |
|
44536b803f | |
|
750c31a159 | |
|
8325ccad72 | |
|
c27c4ce617 | |
|
b9ecabf915 | |
|
4f13a98eff | |
|
2a4e4e66fa | |
|
b72cc5cbc0 | |
|
04f91560b8 | |
|
87af2edf17 | |
|
856977612d | |
|
d1ac17cee1 | |
|
9748e70cdf | |
|
5ba0a9bbd0 | |
|
d8ea1e8f94 | |
|
806d663cfb | |
|
7ab1728bc7 | |
|
32e64eaeff | |
|
7b6efd52bb | |
|
7467f2ae11 | |
|
c498ffb21b | |
|
1dadf7b8b6 | |
|
793cf1b570 | |
|
5a8f564b74 | |
|
e24c9e6ac2 | |
|
814f6fd83c | |
|
d34be9b07d | |
|
e205ea0018 | |
|
ba9d3a30b9 | |
|
10cf908775 | |
|
ee9ad472f0 | |
|
1cdfc5f2b5 | |
|
9e9f07e4fd | |
|
a0294e6bd6 | |
|
55fdf431e0 | |
|
9f1110f91a | |
|
d76b24bc25 | |
|
0787875b3b | |
|
b33aca3230 | |
|
0b291e8b21 | |
|
0cf7e5fddd | |
|
95f708da58 | |
|
ce47776f19 | |
|
7fff48b07a | |
|
4688cb388e | |
|
21e1a7ab01 | |
|
5952af85f1 | |
|
5e193db33b | |
|
30bcfcc675 | |
|
514764ee6e | |
|
685269eebf | |
|
276ad83080 | |
|
7a318eae45 | |
|
0769f8fe05 | |
|
03ac25835e | |
|
29c3888108 | |
|
91951be107 | |
|
935e46a8f1 | |
|
1345808019 | |
|
400d0d3336 | |
|
ca0fd77176 | |
|
f7cf6d9669 | |
|
7f19944005 | |
|
e8aec5f04c | |
|
0afbd24923 | |
|
932772f8c8 | |
|
b7a803678c | |
|
e99360bf5b | |
|
5064630d1f | |
|
4034da47f2 | |
|
381b1965cf | |
|
d5e747becd | |
|
758273d362 | |
|
1560a0278e | |
|
64c275a91c | |
|
05189b4e5a | |
|
80aaac997d | |
|
c560921092 | |
|
81cd9aa6b7 | |
|
4bfc6dd295 | |
|
a8464364c1 | |
|
e333368528 | |
|
58c1c25fc2 | |
|
3045b0ec48 | |
|
2d50037c03 | |
|
6a1cd07951 | |
|
c7103654e3 | |
|
4e03071c4c | |
|
bf9e8a6758 | |
|
00c6705161 | |
|
209d932757 | |
|
e954ea8538 | |
|
f79a8b4cfb | |
|
8fc01e2509 | |
|
232e607b8c | |
|
bdeb7c464e | |
|
3668079479 | |
|
c115e9453b | |
|
f157a0f473 | |
|
c2252b4e9f | |
|
b0554119f1 | |
|
37387692dd | |
|
970c41a064 | |
|
67a3fe0f1b | |
|
15108156c2 | |
|
1a2c51da12 | |
|
3d10482da8 | |
|
3abc8787c6 | |
|
df346d31e6 | |
|
a8babb53f2 | |
|
3c1cc92e1b | |
|
99ee8c4757 | |
|
fc81d6da22 | |
|
47edcbd761 | |
|
a499971e91 | |
|
afd34c7c9b | |
|
529c646277 | |
|
b8f03153ca | |
|
a14df73879 | |
|
cb9141c1ce | |
|
ff2461dafc | |
|
779af013e6 | |
|
75fb357cc5 | |
|
89eb1032cf | |
|
eca9c36110 | |
|
2430651ed4 | |
|
87d658031b | |
|
34b90538ab | |
|
d2571d5f79 | |
|
1688d7c934 | |
|
9ab5dc7d02 | |
|
97cdec61be | |
|
c0d6eb3203 | |
|
d125ff6324 | |
|
1b830b386e | |
|
ee83909123 | |
|
844e27e94e | |
|
6fded9f028 | |
|
b5103f1940 | |
|
8bf54dfc1b | |
|
1069292c54 | |
|
88c6f574ec | |
|
f5f6d3b6ac | |
|
38f937ea9e | |
|
2913eb67f4 | |
|
a0ceec28e7 | |
|
b77a50caf6 | |
|
e17fc6bce9 | |
|
0970e2807b | |
|
a64d26513a | |
|
73e80f2571 | |
|
770f361d04 | |
|
7476472071 | |
|
1fff2f7256 | |
|
1e4044488d | |
|
1e1822151b | |
|
c37b6c4c84 | |
|
8f65b9e60e | |
|
a8a9a09f88 | |
|
7526ca2905 | |
|
fd83c3e7a7 | |
|
a3c575857a | |
|
c292f6ce44 | |
|
84a7d75174 | |
|
048afdcc7b | |
|
06e319cf09 | |
|
cee0730e03 | |
|
adaa672185 | |
|
b6b5d3e4c9 | |
|
6ac057a0c7 | |
|
6607a0ff8f | |
|
0374551655 | |
|
be9f0075e3 | |
|
2cc004746e | |
|
b1a7ac1d28 | |
|
5a31e16c65 | |
|
0b56836097 | |
|
a4a11aaaec | |
|
ae1b2523b1 | |
|
87f9d08c74 | |
|
81abd9622f | |
|
faf87f5b86 | |
|
66dc231850 | |
|
34ff47bc1c | |
|
842b7117a2 | |
|
6543aba53f | |
|
06cee8926e | |
|
a35fe11f5b | |
|
cc13544968 | |
|
19ee511d8d | |
|
0b47d6ca09 | |
|
00193bb4bc | |
|
4379fcbdb3 | |
|
8464625bbf | |
|
e88f949cb7 | |
|
4fdb0737fc | |
|
04be8cf642 | |
|
089ecbf144 | |
|
b95e97a5df | |
|
e84710c4aa | |
|
f549d1fca9 | |
|
2c72dc000f | |
|
2ceddc1340 | |
|
f118c48d0e | |
|
a3a1236d5b | |
|
a7848727ee | |
|
5787c36687 | |
|
4e66ea43d7 | |
|
68d69181a9 | |
|
b879b3f2c7 | |
|
4fd77ce70d | |
|
8213c6d10f | |
|
b79b4567b4 | |
|
8c5b759a92 | |
|
ab1e33a799 | |
|
824b65d5ee | |
|
c68004b804 | |
|
d5cd8dc338 | |
|
1fef5509d1 | |
|
06d12ce709 | |
|
ad7ac80444 | |
|
29d0ebdb7c | |
|
9bcaa07c1a | |
|
bf5f56f5f2 | |
|
c1c2b59641 | |
|
33820c2acb | |
|
9814f63246 | |
|
5e6c018199 | |
|
b24751287c | |
|
759cc6c1be | |
|
1a929ce2ca | |
|
1e4571a814 | |
|
848670d128 | |
|
6d9b5fdb2d | |
|
ab00d64725 | |
|
5e36054ee9 | |
|
2b11042f35 | |
|
e9e5075c1c | |
|
9149dd6cf6 | |
|
24bfb4920c | |
|
553ad4760c | |
|
5acd56689b | |
|
5f0643d4d4 | |
|
17e4ed38be | |
|
c49f16b055 | |
|
aa42d64cac | |
|
8f0ef86ab9 | |
|
c17b2f6bac | |
|
be498f2329 | |
|
fea3d8d415 | |
|
bffc8626c7 | |
|
1efe1228ac | |
|
f900b811c7 | |
|
a5f7e03f6c | |
|
7aba4adc14 | |
|
12c8c641ea | |
|
977adf3153 | |
|
409b76df26 | |
|
2d9ebe7094 | |
|
71e797cf7d | |
|
f781027388 | |
|
56e316e6a1 | |
|
edef3e1990 | |
|
d6d0965467 | |
|
62f888710c | |
|
58e8c2392f | |
|
0e1310324d | |
|
efcda61104 | |
|
97133a5554 | |
|
577ed42f97 | |
|
8f704c4617 | |
|
a03d7f55c7 | |
|
2bbc101f95 | |
|
2b11e9165f | |
|
b25dec68cb | |
|
2f57238f66 | |
|
8239061a84 | |
|
a43ebc016a | |
|
c036aab9c4 | |
|
dd6419dc44 | |
|
54a9cfbb8e | |
|
2eb85b6b71 | |
|
712f1ceb61 | |
|
adf873639c | |
|
4e9460e7da | |
|
5481eac988 | |
|
b7c9abca02 | |
|
447d3fc36f | |
|
71702b2abb | |
|
2ffb24d630 | |
|
81ecd9ff7f | |
|
ebe012ca67 | |
|
805176c3ea | |
|
f99cab6983 | |
|
c94d800646 | |
|
0cbc345f49 | |
|
d34f3f1576 | |
|
73f5e67e3c | |
|
5cf8c7a898 | |
|
11c9cebf47 | |
|
bd9362fb25 | |
|
3aa588f125 | |
|
6d33a2c1b0 | |
|
9e4cf1d1db | |
|
ae38c84ef7 | |
|
6306c6752a | |
|
5490630922 | |
|
d1ea886e76 | |
|
7dc1205bb6 | |
|
15b094a422 | |
|
78c46572a2 | |
|
abeda7820d | |
|
2dac67618c | |
|
bb9b1283bf | |
|
b9458ecb38 | |
|
b205757f8f | |
|
0b937663ff | |
|
7a75d107dc | |
|
0f682dd184 | |
|
c2360dd519 | |
|
f9c2ac030a | |
|
7ce2386aea | |
|
67b226ee44 | |
|
70c7f8a740 | |
|
e409aa3c05 | |
|
a3b5129ee2 | |
|
57cd0281fa | |
|
ba9d3abc52 | |
|
b9bc1f6da0 | |
|
08f1998e61 | |
|
bc19ead2f5 | |
|
f9327c56db | |
|
17f3d821ea | |
|
02f3e97654 | |
|
94bba7837c | |
|
626fcb9cf3 | |
|
bf92165432 | |
|
92e99edbbd | |
|
94507c7887 | |
|
0cce75befa | |
|
96469c28d3 | |
|
a12db2637e | |
|
0a406da3a4 | |
|
6271dd4144 | |
|
4182809657 | |
|
ab6ec064fa | |
|
d921a48a09 | |
|
0f03dbc75b | |
|
e5fa3a09b3 | |
|
4cdfb60fdf | |
|
200bdaccc4 | |
|
17a35e0fdb | |
|
28f2bf1629 | |
|
bec04ab795 | |
|
54cefab633 | |
|
2918c0e93f | |
|
92c495ed07 | |
|
d9d807e3d1 | |
|
959b49a2cc | |
|
17f131cf63 | |
|
4ef67beafc | |
|
826a04ef31 | |
|
104b1208ed | |
|
8b3040c24d | |
|
153dbd2016 | |
|
91d5cbce16 | |
|
7218427ec2 | |
|
5d91ae8157 | |
|
0f61d746e5 | |
|
e931e740e6 | |
|
38917e05ab | |
|
dd33f7e086 | |
|
43d4d679b5 | |
|
e02fa28cd0 | |
|
055e085d2a | |
|
caf1fb6b32 | |
|
6b049e68cd | |
|
2a4360cc64 | |
|
8efc254f3c | |
|
f89b3daf50 | |
|
29f6196698 | |
|
09ff2468e9 | |
|
1867277e1b | |
|
053a09a5b2 | |
|
fae86f0836 | |
|
2e539dfa71 | |
|
a9efd85aec | |
|
09e70b9f21 | |
|
2d184e0f08 | |
|
43d6c7d8db | |
|
19487acc64 | |
|
336e7768b8 | |
|
9b49dc41c8 | |
|
74f896cf00 | |
|
10b0bde1d0 | |
|
411b5d5f8f | |
|
b6da27ac07 | |
|
a6b81b038f | |
|
e2c80babfa | |
|
01b9f28e7b | |
|
2c9fad60fb | |
|
398baea876 | |
|
aec94d0a46 | |
|
4b69e45d44 | |
|
12f2c2becb | |
|
699f79278b | |
|
88e07d28fd | |
|
226ba06bf3 | |
|
c1667fc48b | |
|
f900e675da | |
|
ee51b9eade | |
|
bf62ee92dd | |
|
78532803bc | |
|
86ea3ebc32 | |
|
01aba3f3e0 | |
|
6dc1ef5728 | |
|
02e5d770db | |
|
f8c3704c87 | |
|
4c21459e00 | |
|
cdbbe460ab | |
|
45da1645e0 | |
|
9a1f4697c2 | |
|
217b8713ff | |
|
0998f047e4 | |
|
47755bc2aa | |
|
72efd36649 | |
|
553a8d392f | |
|
f050416265 | |
|
a7d1a92a01 | |
|
bf9ce9b213 | |
|
a9c7c79fb5 | |
|
dc3a830909 | |
|
171bfde031 | |
|
f471ce1535 | |
|
56170b73ed | |
|
2a9c1ec45c | |
|
3ccb3ff4de | |
|
6c078806cd | |
|
c2f768bef8 | |
|
1b7bb5a4fe | |
|
dfe6d13182 | |
|
4c04718db4 | |
|
64d803cbc0 | |
|
54ccae5762 | |
|
8d6bf0c7b1 | |
|
8496e6bcd7 | |
|
7770fc264b | |
|
51f21805be | |
|
434e49ea85 | |
|
468f7c7070 | |
|
4577db7582 | |
|
cc17816dae | |
|
172cecebe4 | |
|
7913acdf70 | |
|
e9f57b5001 | |
|
b8c0bd7082 | |
|
6200ec1ba7 | |
|
73912fd875 | |
|
812035686e | |
|
1f8caa4616 | |
|
4a1e0a87ba | |
|
337bda45fd | |
|
c2412bfb4b | |
|
cff6b87daa | |
|
2f93a490f0 | |
|
1856ef9483 | |
|
b45602556c | |
|
b5326d4928 | |
|
8bbc8c449f | |
|
dcd6375d7d | |
|
8b53517f2c | |
|
1c3ddcf1cd | |
|
fc4ef6b939 | |
|
6d009c4f86 | |
|
f995187925 | |
|
67a6a5f861 | |
|
6bfa4da72d | |
|
9797c94aa8 | |
|
5dd2dfd17b | |
|
5791aca9fd | |
|
f61ce70b82 | |
|
c8091d3215 | |
|
7153d810aa | |
|
68053d24b6 | |
|
a49b4c8ef7 | |
|
c6c036c152 | |
|
db0d087396 | |
|
263f43b1a9 | |
|
21626cda9d | |
|
77d920a2ef | |
|
bbf7af8405 | |
|
d0bbc58b20 | |
|
fed2cbe043 | |
|
4d4bb08423 | |
|
dd09c78cd2 | |
|
771091a557 | |
|
8ce1bdf21c | |
|
679cc26225 | |
|
ff5be827d0 | |
|
4555997957 | |
|
3b43bee0f1 | |
|
3677a30bab | |
|
2e8b2a24ae | |
|
26500aa1cf | |
|
adfd6b50c1 | |
|
b58bdc184e | |
|
580eea7a61 | |
|
da27b65e56 | |
|
bb493fe827 | |
|
655fe18138 | |
|
9d70d61013 | |
|
68452beba0 | |
|
409e9e9bc0 | |
|
f241f2cd2b | |
|
2ea90a66ae | |
|
c2c8f65aaa | |
|
a578be8336 | |
|
45b469682a | |
|
1e9f4d890f | |
|
44f74fd9e6 | |
|
f1c8415899 | |
|
823ada5673 | |
|
55f146a44a | |
|
5fe972bd99 | |
|
0578263e10 | |
|
9316c0814f | |
|
8bdd9e29ab | |
|
ae14a8a530 | |
|
2a170cc97e | |
|
c109e14a93 | |
|
240d406a1a | |
|
b6cf988058 | |
|
c16153fa19 | |
|
a0797a99ff | |
|
376cfa3167 | |
|
14690dc3cc | |
|
6dd0831a66 | |
|
a150156810 | |
|
d61fba9d95 | |
|
7cb8d04ca9 | |
|
a2fc2c5075 | |
|
1ed8788c83 | |
|
14e95fd303 | |
|
3dca5aa06f | |
|
faea3a4f86 | |
|
a5dcad1767 | |
|
6754121b17 | |
|
131425774a | |
|
585e4cdc1b | |
|
12ddeba2a3 | |
|
ab0c256e0b | |
|
8a24b33757 | |
|
8f6e2e342a | |
|
4dfeb653b1 | |
|
670a754eb2 | |
|
3f46103dc3 | |
|
6c2901daca | |
|
a73e5d4133 | |
|
b067cb66f3 | |
|
f1185758d6 | |
|
0d4a0adf00 | |
|
20f6f95b89 | |
|
7e7c186784 | |
|
70e0a1325a | |
|
91471dee13 | |
|
a80dc14dcc | |
|
e1cd187f4b | |
|
35fb7533db | |
|
bdd9b69dfd | |
|
82a043a233 | |
|
66819a1e89 | |
|
9089376e5e | |
|
8d3f654fe5 | |
|
c607af9736 | |
|
0b45c30d0b | |
|
1b7de79209 | |
|
4ffedb4052 | |
|
d8eb7acf2e | |
|
6f3a4d723c | |
|
0b2305ae65 | |
|
e86c255186 | |
|
d8658dca2a | |
|
4372202a5b | |
|
affdc3e229 | |
|
cfa384d02a | |
|
a33b7d606b | |
|
da9ea70e5e | |
|
6f254ca958 | |
|
da3bd07607 | |
|
1569010d69 | |
|
ea7b3f4f7a | |
|
fc041e810c | |
|
ebc00e3e6d | |
|
612c0b127c | |
|
a67df04da6 | |
|
43ae270eb7 | |
|
d41c88beeb | |
|
92486d8320 | |
|
0d2d3408b5 | |
|
f76b66c35d | |
|
48a98de7cc | |
|
23ba634a42 | |
|
23d7598722 | |
|
7885cd151a | |
|
fb478aa5f2 | |
|
31f56a39a5 | |
|
6860f9437b | |
|
a06e7a9a2c | |
|
ac8875acdf | |
|
f79c12165e | |
|
91b358e674 | |
|
7ec274fb72 | |
|
7d419595ca | |
|
a6e260dce1 | |
|
2e9a75279d | |
|
68a57594ff | |
|
bd4d6c8c5f | |
|
a36a0513fe | |
|
02e418f660 | |
|
199f7060fa | |
|
9bdb782b8d | |
|
9b9bbfbd5d | |
|
a9a034923f | |
|
07b63077d1 | |
|
013b9720b3 | |
|
f924b4985a | |
|
40b986e310 | |
|
146b0635c8 | |
|
e76a5d189e | |
|
6f81c39c62 | |
|
036565ec81 | |
|
a2225d7cba | |
|
3463b69b78 | |
|
bf61a05e45 | |
|
d930d51d0d | |
|
629fbfb037 | |
|
2255ee1b03 | |
|
5d269b699c | |
|
7b7723426b | |
|
07f1511bb9 | |
|
36f0e20340 | |
|
63919c8f6b | |
|
77798e929e | |
|
f407c5f514 | |
|
e66df9bbc9 | |
|
38fe5aea25 | |
|
2d792b4557 | |
|
5222cdb8f1 | |
|
453d3f7653 | |
|
dca0dfb003 | |
|
2d1e2f53ee | |
|
82167463e4 | |
|
e46f204204 | |
|
9d6cdd5711 | |
|
fc70ec7ad0 | |
|
68845ae44b | |
|
5570c56b1e | |
|
2fdd7bf464 | |
|
24c259a38b | |
|
f4a97b940a | |
|
0f74444b22 | |
|
cf9d42dc49 | |
|
f518b44007 | |
|
95a203881e | |
|
b86361bf0e | |
|
5c97f37a75 | |
|
307d982fcb | |
|
3d791d2e41 | |
|
538cfa279a | |
|
3f2315c96d | |
|
293933ec7d | |
|
97d3b986de | |
|
f8fea6561d | |
|
dfa8474a6e | |
|
e022eb058b | |
|
d04b425d62 | |
|
c1fdf67d39 | |
|
dd902c1355 | |
|
b21b122d24 | |
|
7544bc81a3 | |
|
aab998d5c6 | |
|
3dc5507b6b | |
|
12fbc2643c | |
|
b14e712126 | |
|
c303f6677b | |
|
1976cd8cdd | |
|
cc6d5d2b4f | |
|
b72edbf326 | |
|
b0b23593f6 | |
|
941eee9857 | |
|
8f8751103e | |
|
c575e15c55 | |
|
56afa2bf8d | |
|
2e993149a9 | |
|
89421ee731 | |
|
2509e26aae | |
|
db7e9b90ec | |
|
9827b22b6f | |
|
9636efcb56 | |
|
06d8e4bea8 | |
|
5111372972 | |
|
4cdf94d29c | |
|
5bacf2667f | |
|
81cb833d57 | |
|
53e96811ce | |
|
7b52c0b785 | |
|
9362d29fae | |
|
212268cfa5 | |
|
862da87a0c | |
|
0dae3c000c | |
|
55ea791387 | |
|
3a62189503 | |
|
d3396d9683 | |
|
7672c9ab85 | |
|
afbc43b83f | |
|
4613f98154 | |
|
52f34d70b7 | |
|
02d378e0e6 | |
|
c29bb8e1a3 | |
|
1eaa56bb2a | |
|
54cdd14889 | |
|
01c253952b | |
|
060be72143 | |
|
f5032b95ce | |
|
8fce9f4bf2 | |
|
857e7c5523 | |
|
67dbf7c701 | |
|
17fd46c2dc | |
|
75b3a60ca6 | |
|
f499a9b803 | |
|
c87c724685 | |
|
36ea9f526e | |
|
e5bf8c4c8c | |
|
60a5cc4602 | |
|
c376fe1d22 | |
|
8111b60d4f | |
|
86c871bc9f | |
|
a21cca53f8 | |
|
4a03467968 | |
|
f6151b7ce1 | |
|
cbfc2f9538 | |
|
35ca08ee04 | |
|
0781496c69 | |
|
0cca369eaa | |
|
62fafeb7d2 | |
|
f513829c94 | |
|
bbb3a41293 | |
|
e966779cc8 | |
|
bcc576dfd1 | |
|
dac9453048 | |
|
faf5430248 | |
|
561e1748d6 | |
|
83749e15cb | |
|
1a64c07c8e | |
|
6ead8ffd53 | |
|
1676516441 | |
|
c97186a557 | |
|
59f88175b4 | |
|
77b45e80fe | |
|
2b9272c495 | |
|
5dae08d9c0 | |
|
653533020f | |
|
5a12879182 | |
|
8132f9a2ea | |
|
293865fc3f | |
|
cfbe3d2589 | |
|
69151bbddb | |
|
5faf1a5442 | |
|
25648a8a69 | |
|
522d619ee8 | |
|
4a901085d8 | |
|
49d60f6f68 | |
|
73b8cfe723 | |
|
05a4de1464 | |
|
9002d850a5 | |
|
23cfe72d40 | |
|
8303f9faae | |
|
517c799e00 | |
|
69f5b54919 | |
|
5c0d30d2bc | |
|
c62086f8e8 | |
|
2dfcedfe3c | |
|
77180ea7ba | |
|
8656ed09bb | |
|
7cdfce354c | |
|
2ac7af44b9 | |
|
a0b18bbf72 | |
|
6713a9c8e2 | |
|
ecfc603dda | |
|
55db1a2eab | |
|
f117fbb571 | |
|
5a1687b45f | |
|
d31e560a6b | |
|
de4bf6951f | |
|
f034d9ca87 | |
|
f62e849f80 | |
|
055537ddbb | |
|
13efa0f1d3 | |
|
901438a4a7 | |
|
7b26af01b1 | |
|
de0534c3a1 | |
|
1f490138a0 | |
|
ed3d0421db | |
|
b83e42d788 | |
|
141fbc2e75 | |
|
2e7cbbb8b0 | |
|
461a59b678 | |
|
774680ced0 | |
|
8a26c42857 | |
|
25a91904b8 | |
|
1fa94b95f0 | |
|
52efdc28e4 | |
|
678599f218 | |
|
ba49164db6 | |
|
6089fef0a8 | |
|
8b3dd419b7 | |
|
86236e865e | |
|
e696873025 | |
|
e4c602095f | |
|
be1f8dfad9 | |
|
9571775343 | |
|
518ed694fb | |
|
bf2b439ce1 | |
|
bfed24028a | |
|
da570f8c5a | |
|
99fa476b80 | |
|
e0ee9abd61 | |
|
4696951923 | |
|
ea0209f20d | |
|
28a56ee298 | |
|
2691da82ae | |
|
c5519976ee | |
|
bc079d3586 | |
|
a8ec762a7d | |
|
2b6ae95160 | |
|
c9c38b5053 | |
|
f97df34b65 | |
|
aec6757748 | |
|
33c27cccc2 | |
|
a449013c5d | |
|
b0b3744cb1 | |
|
b2ba48ea90 | |
|
8a20051436 | |
|
eedfa4b84d | |
|
4403a09e79 | |
|
12fb19c47c | |
|
14c62a8852 | |
|
2c818b9caa | |
|
db8f587e93 | |
|
260be98cef | |
|
b42bf4e201 | |
|
2a42a921c4 | |
|
e84b8c25f1 | |
|
8b44900931 | |
|
918362e854 | |
|
ba2965e295 | |
|
ecdd0fda85 | |
|
dba356446e | |
|
4e1ea21ba8 | |
|
7853614d2d | |
|
8cabb7440a | |
|
4fc68e1f82 | |
|
c06681f379 | |
|
57539b0471 | |
|
8b7c429e9f | |
|
9b94ad70ee | |
|
9aa8bb99dc | |
|
b203c891a4 | |
|
3833a1e696 | |
|
6e83891139 | |
|
39616448b8 | |
|
6b9feca865 | |
|
e0779ab295 | |
|
32e3153434 | |
|
b40bfe26c5 | |
|
b8d5ed2bb7 | |
|
96e1f84c8c | |
|
353e03c4fe | |
|
aa60cb4822 | |
|
7eaa61678b | |
|
d74cf15138 | |
|
6238361bd2 | |
|
cf9248b0cc | |
|
8c884b3269 | |
|
8710edf1bd | |
|
5f9572a0ab | |
|
21c10307b9 | |
|
6e0dc64981 | |
|
66922f8e7b | |
|
2aa0382376 | |
|
5cdc5400dc | |
|
53bcdaa6ce | |
|
473e218b2b | |
|
99ae6c2a14 | |
|
bf3d3be6c9 | |
|
6f775a3303 | |
|
2eaf2d5c69 | |
|
6de66c9a5e | |
|
3ae311bd02 | |
|
25116a9f0e | |
|
2e9afc2ce8 | |
|
7535d49844 | |
|
340f5c580e | |
|
1d4eedf5bb | |
|
3ff0230fc1 | |
|
c2166556ea | |
|
4b938e6643 | |
|
4740e04ff1 | |
|
ccdad2f4fd | |
|
54d17f2e24 | |
|
0b3fe111b7 | |
|
a6080b76b1 | |
|
7d00b51db2 | |
|
be0d1c5c65 | |
|
e1f19e9eae | |
|
26cba15742 | |
|
32c6d745b6 | |
|
76649f264b | |
|
db360938fd | |
|
bd82470995 | |
|
87560e0602 | |
|
89cd31cd03 | |
|
1badf2610e | |
|
1129a2210a | |
|
a1199b44a3 | |
|
dc32df27ec | |
|
7f874eaff0 | |
|
21fdd4b0e4 | |
|
f7519b2095 | |
|
686c7bac9b | |
|
3cde829319 | |
|
230852b2a9 | |
|
613957d0ac | |
|
4fe61ac51a | |
|
e9bafe9e38 | |
|
d1bf517aa7 | |
|
a2809dc15a | |
|
c1a58401eb | |
|
e22a015fcb | |
|
f45b0afc49 | |
|
f652c95d8c | |
|
bcfb91bc05 | |
|
d1f04d5c0c | |
|
e0cf5f804e | |
|
a0725e1318 | |
|
70b7fe73ad | |
|
5b616026ab | |
|
a001db2da5 | |
|
2e7a0fe68d | |
|
519eeb6df1 | |
|
3e9221b39c | |
|
d165632cc8 | |
|
65e50fec54 | |
|
460575d959 | |
|
6e34bc3c17 | |
|
781a4a7b0b | |
|
89d1f2f800 | |
|
3cd3315964 | |
|
e73c775296 | |
|
496396fa6b | |
|
3b28e13fd8 | |
|
eedc9c5ab8 | |
|
0578168db0 | |
|
b12877e39b | |
|
5694addef6 | |
|
f037f0fac2 | |
|
55132ff36f | |
|
33e0470f5d | |
|
90b8bd8812 | |
|
9424de56c2 | |
|
789e7ea2b0 | |
|
3fecac40df | |
|
80426f5ccd | |
|
12a4b9151c | |
|
e3a781b950 | |
|
e16f32b5b9 | |
|
bba3a038af | |
|
b385dcfdc0 | |
|
11dd4fa789 | |
|
5576b54518 | |
|
483c622fce | |
|
b13976d620 | |
|
23cb3e1278 | |
|
67c01da64b | |
|
e5592338d0 | |
|
07df412118 | |
|
4c672cd91c | |
|
a74b264f39 | |
|
2a217c1a20 | |
|
1f4aff2796 | |
|
d10c936003 | |
|
4330afd683 |
|
@ -0,0 +1,37 @@
|
||||||
|
using IRaCIS.Core.Infrastructure.Extention;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
|
using Microsoft.Extensions.Localization;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.SCP.Filter
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
public class ModelActionFilter : ActionFilterAttribute, IActionFilter
|
||||||
|
{
|
||||||
|
public IStringLocalizer _localizer;
|
||||||
|
public ModelActionFilter(IStringLocalizer localizer)
|
||||||
|
{
|
||||||
|
_localizer = localizer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnActionExecuting(ActionExecutingContext context)
|
||||||
|
{
|
||||||
|
if (!context.ModelState.IsValid)
|
||||||
|
{
|
||||||
|
|
||||||
|
var validationErrors = context.ModelState
|
||||||
|
.Keys
|
||||||
|
.SelectMany(k => context.ModelState[k]!.Errors)
|
||||||
|
.Select(e => e.ErrorMessage)
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
//---提供给接口的参数无效。
|
||||||
|
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["ModelAction_InvalidAPIParameter"] + JsonConvert.SerializeObject( validationErrors)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
using IRaCIS.Core.Infrastructure;
|
||||||
|
using IRaCIS.Core.Infrastructure.Extention;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
|
using Microsoft.Extensions.Localization;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.SCP.Filter
|
||||||
|
{
|
||||||
|
public class ProjectExceptionFilter : Attribute, IExceptionFilter
|
||||||
|
{
|
||||||
|
private readonly ILogger<ProjectExceptionFilter> _logger;
|
||||||
|
|
||||||
|
public IStringLocalizer _localizer;
|
||||||
|
|
||||||
|
public ProjectExceptionFilter(IStringLocalizer localizer, ILogger<ProjectExceptionFilter> logger)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
_localizer = localizer;
|
||||||
|
}
|
||||||
|
public void OnException(ExceptionContext context)
|
||||||
|
{
|
||||||
|
//context.ExceptionHandled;//记录当前这个异常是否已经被处理过了
|
||||||
|
|
||||||
|
if (!context.ExceptionHandled)
|
||||||
|
{
|
||||||
|
if (context.Exception.GetType().Name == "DbUpdateConcurrencyException")
|
||||||
|
{
|
||||||
|
//---并发更新,当前不允许该操作
|
||||||
|
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["ProjectException_ConcurrentUpdateNotAllowed"] + context.Exception.Message));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (context.Exception.GetType() == typeof(BusinessValidationFailedException))
|
||||||
|
{
|
||||||
|
var error = context.Exception as BusinessValidationFailedException;
|
||||||
|
|
||||||
|
context.Result = new JsonResult(ResponseOutput.NotOk(context.Exception.Message, error!.Code));
|
||||||
|
}
|
||||||
|
else if(context.Exception.GetType() == typeof(QueryBusinessObjectNotExistException))
|
||||||
|
{
|
||||||
|
context.Result = new JsonResult(ResponseOutput.NotOk( context.Exception.Message, ApiResponseCodeEnum.DataNotExist));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["Project_ExceptionContactDeveloper"] + (context.Exception.InnerException is null ? (context.Exception.Message /*+ context.Exception.StackTrace*/)
|
||||||
|
: (context.Exception.InnerException?.Message /*+ context.Exception.InnerException?.StackTrace*/)), ApiResponseCodeEnum.ProgramException));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_logger.LogError(context.Exception.InnerException is null ? (context.Exception.Message + context.Exception.StackTrace) : (context.Exception.InnerException?.Message + context.Exception.InnerException?.StackTrace));
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//继续
|
||||||
|
}
|
||||||
|
context.ExceptionHandled = true;//标记当前异常已经被处理过了
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,120 @@
|
||||||
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using IRaCIS.Core.Domain.Share;
|
||||||
|
using IRaCIS.Core.Infrastructure.Extention;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.Application.Service.BusinessFilter
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 统一返回前端数据包装,之前在控制器包装,现在修改为动态Api 在ResultFilter这里包装,减少重复冗余代码
|
||||||
|
/// by zhouhang 2021.09.12 周末
|
||||||
|
/// </summary>
|
||||||
|
public class UnifiedApiResultFilter : Attribute, IAsyncResultFilter
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 异步版本
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context"></param>
|
||||||
|
/// <param name="next"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (context.Result is ObjectResult objectResult)
|
||||||
|
{
|
||||||
|
var statusCode = objectResult.StatusCode ?? context.HttpContext.Response.StatusCode;
|
||||||
|
|
||||||
|
//是200 并且没有包装 那么包装结果
|
||||||
|
if (statusCode == 200 && !(objectResult.Value is IResponseOutput))
|
||||||
|
{
|
||||||
|
//if (objectResult.Value == null)
|
||||||
|
//{
|
||||||
|
// var apiResponse = ResponseOutput.DBNotExist();
|
||||||
|
|
||||||
|
// objectResult.Value = apiResponse;
|
||||||
|
// objectResult.DeclaredType = apiResponse.GetType();
|
||||||
|
//}
|
||||||
|
//else
|
||||||
|
//{
|
||||||
|
|
||||||
|
var type = objectResult.Value?.GetType();
|
||||||
|
|
||||||
|
|
||||||
|
if ( type!=null&& type.IsGenericType&&(type.GetGenericTypeDefinition()==typeof(ValueTuple<,>)|| type.GetGenericTypeDefinition()==typeof(Tuple<,>)))
|
||||||
|
{
|
||||||
|
|
||||||
|
//报错
|
||||||
|
//var tuple = (object, object))objectResult.Value;
|
||||||
|
|
||||||
|
//var (val1, val2) = ((dynamic, dynamic))objectResult.Value;
|
||||||
|
//var apiResponse = ResponseOutput.Ok(val1, val2);
|
||||||
|
|
||||||
|
//OK
|
||||||
|
var tuple = (dynamic)objectResult.Value;
|
||||||
|
var apiResponse = ResponseOutput.Ok(tuple.Item1, tuple.Item2);
|
||||||
|
|
||||||
|
|
||||||
|
objectResult.Value = apiResponse;
|
||||||
|
objectResult.DeclaredType = apiResponse.GetType();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var apiResponse = ResponseOutput.Ok(objectResult.Value);
|
||||||
|
|
||||||
|
objectResult.Value = apiResponse;
|
||||||
|
objectResult.DeclaredType = apiResponse.GetType();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//}
|
||||||
|
|
||||||
|
}
|
||||||
|
//如果不是200 是IResponseOutput 不处理
|
||||||
|
else if (statusCode != 200 && (objectResult.Value is IResponseOutput))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
else if(statusCode != 200&&!(objectResult.Value is IResponseOutput))
|
||||||
|
{
|
||||||
|
//---程序错误,请联系开发人员。
|
||||||
|
var apiResponse = ResponseOutput.NotOk(I18n.T("UnifiedAPI_ProgramError"));
|
||||||
|
|
||||||
|
objectResult.Value = apiResponse;
|
||||||
|
objectResult.DeclaredType = apiResponse.GetType();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
await next.Invoke();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsTupleType(Type type, bool checkBaseTypes = false)
|
||||||
|
{
|
||||||
|
if (type == null)
|
||||||
|
throw new ArgumentNullException(nameof(type));
|
||||||
|
|
||||||
|
if (type == typeof(Tuple))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
while (type != null)
|
||||||
|
{
|
||||||
|
if (type.IsGenericType)
|
||||||
|
{
|
||||||
|
var genType = type.GetGenericTypeDefinition();
|
||||||
|
if (genType == typeof(Tuple<>)
|
||||||
|
|| genType == typeof(Tuple<,>)
|
||||||
|
|| genType == typeof(Tuple<,>))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!checkBaseTypes)
|
||||||
|
break;
|
||||||
|
|
||||||
|
type = type.BaseType;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
using Autofac;
|
||||||
|
using IRaCIS.Core.Infra.EFCore;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Panda.DynamicWebApi;
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using IRaCIS.Core.Domain.Models;
|
||||||
|
using IRaCIS.Core.Domain.Share;
|
||||||
|
using IRaCIS.Core.Application.Service;
|
||||||
|
using AutoMapper;
|
||||||
|
using IRaCIS.Core.SCP.Service;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.SCP
|
||||||
|
{
|
||||||
|
// ReSharper disable once IdentifierTypo
|
||||||
|
public class AutofacModuleSetup : Autofac.Module
|
||||||
|
{
|
||||||
|
protected override void Load(ContainerBuilder containerBuilder)
|
||||||
|
{
|
||||||
|
|
||||||
|
#region byzhouhang 20210917 此处注册泛型仓储 可以减少Domain层 和Infra.EFcore 两层 空的仓储接口定义和 仓储文件定义
|
||||||
|
|
||||||
|
containerBuilder.RegisterGeneric(typeof(Repository<>))
|
||||||
|
.As(typeof(IRepository<>)).InstancePerLifetimeScope();//注册泛型仓储
|
||||||
|
|
||||||
|
containerBuilder.RegisterType<Repository>().As<IRepository>().InstancePerLifetimeScope();
|
||||||
|
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 指定控制器也由autofac 来进行实例获取 https://www.cnblogs.com/xwhqwer/p/15320838.html
|
||||||
|
|
||||||
|
//获取所有控制器类型并使用属性注入
|
||||||
|
containerBuilder.RegisterAssemblyTypes(typeof(BaseService).Assembly)
|
||||||
|
.Where(type => typeof(IDynamicWebApi).IsAssignableFrom(type))
|
||||||
|
.PropertiesAutowired();
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Assembly application = Assembly.LoadFrom(AppDomain.CurrentDomain.BaseDirectory + typeof(BaseService).Assembly.GetName().Name+".dll");
|
||||||
|
containerBuilder.RegisterAssemblyTypes(application).Where(t => t.FullName.Contains("Service"))
|
||||||
|
.PropertiesAutowired().AsImplementedInterfaces();
|
||||||
|
|
||||||
|
|
||||||
|
//containerBuilder.RegisterType<HttpContextAccessor>().As<IHttpContextAccessor>().SingleInstance();
|
||||||
|
//containerBuilder.RegisterType<UserInfo>().As<IUserInfo>().InstancePerLifetimeScope();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
using EntityFramework.Exceptions.SqlServer;
|
||||||
|
using IRaCIS.Core.Domain.Share;
|
||||||
|
using IRaCIS.Core.Infra.EFCore;
|
||||||
|
using Medallion.Threading;
|
||||||
|
using Medallion.Threading.SqlServer;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Diagnostics;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.SCP
|
||||||
|
{
|
||||||
|
public static class EFSetup
|
||||||
|
{
|
||||||
|
public static void AddEFSetup( this IServiceCollection services, IConfiguration configuration)
|
||||||
|
{
|
||||||
|
services.AddHttpContextAccessor();
|
||||||
|
services.AddScoped<IUserInfo, UserInfo>();
|
||||||
|
services.AddScoped<ISaveChangesInterceptor, AuditEntityInterceptor>();
|
||||||
|
|
||||||
|
|
||||||
|
//这个注入没有成功--注入是没问题的,构造函数也只是支持参数就好,错在注入的地方不能写DbContext
|
||||||
|
//Web程序中通过重用池中DbContext实例可提高高并发场景下的吞吐量, 这在概念上类似于ADO.NET Provider原生的连接池操作方式,具有节省DbContext实例化成本的优点
|
||||||
|
services.AddDbContext<IRaCISDBContext>((sp, options) =>
|
||||||
|
{
|
||||||
|
// 在控制台
|
||||||
|
//public static readonly ILoggerFactory MyLoggerFactory = LoggerFactory.Create(builder => { builder.AddConsole(); });
|
||||||
|
var logFactory = LoggerFactory.Create(builder => { builder.AddDebug(); });
|
||||||
|
|
||||||
|
options.UseSqlServer(configuration.GetSection("ConnectionStrings:RemoteNew").Value,
|
||||||
|
contextOptionsBuilder => contextOptionsBuilder.EnableRetryOnFailure());
|
||||||
|
|
||||||
|
options.UseLoggerFactory(logFactory);
|
||||||
|
|
||||||
|
options.UseExceptionProcessor();
|
||||||
|
|
||||||
|
options.EnableSensitiveDataLogging();
|
||||||
|
|
||||||
|
options.AddInterceptors(new QueryWithNoLockDbCommandInterceptor());
|
||||||
|
options.AddInterceptors(sp.GetServices<ISaveChangesInterceptor>());
|
||||||
|
|
||||||
|
options.UseProjectables();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
//// Register an additional context factory as a Scoped service, which gets a pooled context from the Singleton factory we registered above,
|
||||||
|
//services.AddScoped<IRaCISDBScopedFactory>();
|
||||||
|
|
||||||
|
//// Finally, arrange for a context to get injected from our Scoped factory:
|
||||||
|
//services.AddScoped(sp => sp.GetRequiredService<IRaCISDBScopedFactory>().CreateDbContext());
|
||||||
|
|
||||||
|
//注意区分 easy caching 也有 IDistributedLockProvider
|
||||||
|
services.AddSingleton<IDistributedLockProvider>(sp =>
|
||||||
|
{
|
||||||
|
//var connection = ConnectionMultiplexer.Connect(configuration["Redis:Configuration"]!);
|
||||||
|
|
||||||
|
return new SqlDistributedSynchronizationProvider(configuration.GetSection("ConnectionStrings:RemoteNew").Value);
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
|
||||||
|
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Converters;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.SCP
|
||||||
|
{
|
||||||
|
public static class NewtonsoftJsonSetup
|
||||||
|
{
|
||||||
|
public static void AddNewtonsoftJsonSetup(this IMvcBuilder builder, IServiceCollection services)
|
||||||
|
{
|
||||||
|
services.AddHttpContextAccessor();
|
||||||
|
services.AddScoped<IOSSService,OSSService>();
|
||||||
|
|
||||||
|
builder.AddNewtonsoftJson(options =>
|
||||||
|
{
|
||||||
|
//options.SerializerSettings.PreserveReferencesHandling = PreserveReferencesHandling.Objects;
|
||||||
|
// 忽略循环引用
|
||||||
|
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
|
||||||
|
//options.SerializerSettings.TypeNameHandling = TypeNameHandling.All;
|
||||||
|
|
||||||
|
//处理返回给前端 可空类型 给出默认值 比如in? 为null 设置 默认值0
|
||||||
|
options.SerializerSettings.ContractResolver = new NullToEmptyStringResolver(); //new DefaultContractResolver();// new NullToEmptyStringResolver();
|
||||||
|
// 设置时间格式
|
||||||
|
options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss";
|
||||||
|
|
||||||
|
options.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.RoundtripKind;
|
||||||
|
|
||||||
|
//options.SerializerSettings.Converters.Add(new JSONCustomDateConverter()) ;
|
||||||
|
|
||||||
|
//options.SerializerSettings.Converters.Add(services.BuildServiceProvider().GetService<JSONTimeZoneConverter>());
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
})
|
||||||
|
.AddControllersAsServices()//动态webApi属性注入需要
|
||||||
|
.ConfigureApiBehaviorOptions(o =>
|
||||||
|
{
|
||||||
|
o.SuppressModelStateInvalidFilter = true; //自己写验证
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
Newtonsoft.Json.JsonSerializerSettings setting = new Newtonsoft.Json.JsonSerializerSettings();
|
||||||
|
JsonConvert.DefaultSettings = new Func<JsonSerializerSettings>(() =>
|
||||||
|
{
|
||||||
|
//日期类型默认格式化处理
|
||||||
|
setting.DateFormatString = "yyyy-MM-dd HH:mm:ss";
|
||||||
|
setting.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
|
||||||
|
|
||||||
|
return setting;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Serialization;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.SCP
|
||||||
|
{
|
||||||
|
public class NullToEmptyStringResolver : DefaultContractResolver
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
|
||||||
|
{
|
||||||
|
IList<JsonProperty> properties = base.CreateProperties(type, memberSerialization);
|
||||||
|
|
||||||
|
var list= type.GetProperties()
|
||||||
|
.Select(p =>
|
||||||
|
{
|
||||||
|
var jp = base.CreateProperty(p, memberSerialization);
|
||||||
|
jp.ValueProvider = new NullToEmptyStringValueProvider(p);
|
||||||
|
return jp;
|
||||||
|
}).ToList();
|
||||||
|
|
||||||
|
var uu = list.Select(t => t.PropertyName).ToList();
|
||||||
|
|
||||||
|
//获取复杂对象属性
|
||||||
|
properties = properties.TakeWhile(t => !uu.Contains(t.PropertyName)).ToList();
|
||||||
|
|
||||||
|
list.AddRange(properties);
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
using Newtonsoft.Json.Serialization;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.SCP
|
||||||
|
{
|
||||||
|
|
||||||
|
public class NullToEmptyStringValueProvider : IValueProvider
|
||||||
|
{
|
||||||
|
PropertyInfo _MemberInfo;
|
||||||
|
public NullToEmptyStringValueProvider(PropertyInfo memberInfo)
|
||||||
|
{
|
||||||
|
_MemberInfo = memberInfo;
|
||||||
|
}
|
||||||
|
public object GetValue(object target)
|
||||||
|
{
|
||||||
|
object result = _MemberInfo.GetValue(target);
|
||||||
|
if (_MemberInfo.PropertyType == typeof(string) && result == null) result = "";
|
||||||
|
else if (_MemberInfo.PropertyType == typeof(String[]) && result == null) result = new string[] { };
|
||||||
|
//else if (_MemberInfo.PropertyType == typeof(Nullable<Int32>) && result == null) result = 0;
|
||||||
|
else if (_MemberInfo.PropertyType == typeof(Nullable<Decimal>) && result == null) result = 0.00M;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
public void SetValue(object target, object value)
|
||||||
|
{
|
||||||
|
|
||||||
|
if(_MemberInfo.PropertyType == typeof(string))
|
||||||
|
{
|
||||||
|
//去掉前后空格
|
||||||
|
_MemberInfo.SetValue(target, value==null?string.Empty: value.ToString()==string.Empty? value:value.ToString().Trim());
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_MemberInfo.SetValue(target, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.11" />
|
||||||
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2" />
|
||||||
|
<PackageReference Include="AlibabaCloud.SDK.Sts20150401" Version="1.1.5" />
|
||||||
|
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="10.0.0" />
|
||||||
|
<PackageReference Include="Aliyun.OSS.SDK.NetCore" Version="2.14.1" />
|
||||||
|
<PackageReference Include="AWSSDK.S3" Version="3.7.416.8" />
|
||||||
|
<PackageReference Include="AWSSDK.SecurityToken" Version="3.7.401.81" />
|
||||||
|
<PackageReference Include="DistributedLock.Core" Version="1.0.8" />
|
||||||
|
<PackageReference Include="DistributedLock.SqlServer" Version="1.0.6" />
|
||||||
|
<PackageReference Include="fo-dicom" Version="5.2.1" />
|
||||||
|
<PackageReference Include="fo-dicom.Codecs" Version="5.16.1" />
|
||||||
|
<PackageReference Include="fo-dicom.Imaging.ImageSharp" Version="5.2.1" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.10" />
|
||||||
|
<PackageReference Include="AutoMapper" Version="13.0.1" />
|
||||||
|
<PackageReference Include="Minio" Version="6.0.4" />
|
||||||
|
<PackageReference Include="My.Extensions.Localization.Json" Version="3.3.0">
|
||||||
|
<TreatAsUsed>true</TreatAsUsed>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="Panda.DynamicWebApi" Version="1.2.2" />
|
||||||
|
<PackageReference Include="Serilog.Enrichers.ClientInfo" Version="2.1.2" />
|
||||||
|
<PackageReference Include="Serilog.Extensions.Hosting" Version="9.0.0" />
|
||||||
|
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
|
||||||
|
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
|
||||||
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="8.1.1" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\IRaCIS.Core.Infra.EFCore\IRaCIS.Core.Infra.EFCore.csproj" />
|
||||||
|
<ProjectReference Include="..\IRaCIS.Core.Infrastructure\IRaCIS.Core.Infrastructure.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="Helper\" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
|
@ -0,0 +1,186 @@
|
||||||
|
|
||||||
|
using Autofac;
|
||||||
|
using Autofac.Extensions.DependencyInjection;
|
||||||
|
using AutoMapper.EquivalencyExpression;
|
||||||
|
using FellowOakDicom;
|
||||||
|
using FellowOakDicom.Imaging;
|
||||||
|
using FellowOakDicom.Imaging.NativeCodec;
|
||||||
|
using FellowOakDicom.Network;
|
||||||
|
using IRaCIS.Core.Infra.EFCore;
|
||||||
|
using IRaCIS.Core.SCP;
|
||||||
|
using IRaCIS.Core.SCP.Filter;
|
||||||
|
using IRaCIS.Core.SCP.Service;
|
||||||
|
using MassTransit;
|
||||||
|
using MassTransit.NewIdProviders;
|
||||||
|
using Microsoft.AspNetCore.HttpOverrides;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Panda.DynamicWebApi;
|
||||||
|
using Serilog;
|
||||||
|
using Serilog.Events;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
|
||||||
|
//以配置文件为准,否则 从url中取环境值(服务以命令行传递参数启动,配置文件配置了就不需要传递环境参数)
|
||||||
|
var config = new ConfigurationBuilder()
|
||||||
|
.AddEnvironmentVariables()
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
var enviromentName = config["ASPNETCORE_ENVIRONMENT"];
|
||||||
|
|
||||||
|
var builder = WebApplication.CreateBuilder(new WebApplicationOptions
|
||||||
|
{
|
||||||
|
EnvironmentName = enviromentName
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#region 主机配置
|
||||||
|
|
||||||
|
NewId.SetProcessIdProvider(new CurrentProcessIdProvider());
|
||||||
|
|
||||||
|
builder.Configuration.AddJsonFile("appsettings.json", false, true)
|
||||||
|
.AddJsonFile($"appsettings.{enviromentName}.json", false, true);
|
||||||
|
builder.Host
|
||||||
|
.UseServiceProviderFactory(new AutofacServiceProviderFactory())
|
||||||
|
.ConfigureContainer<ContainerBuilder>(containerBuilder =>
|
||||||
|
{
|
||||||
|
containerBuilder.RegisterModule<AutofacModuleSetup>();
|
||||||
|
})
|
||||||
|
.UseSerilog();
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 配置服务
|
||||||
|
var _configuration = builder.Configuration;
|
||||||
|
|
||||||
|
//健康检查
|
||||||
|
builder.Services.AddHealthChecks();
|
||||||
|
|
||||||
|
//本地化
|
||||||
|
builder.Services.AddJsonLocalization(options => options.ResourcesPath = "Resources");
|
||||||
|
|
||||||
|
|
||||||
|
// 异常、参数统一验证过滤器、Json序列化配置、字符串参数绑型统一Trim()
|
||||||
|
builder.Services.AddControllers(options =>
|
||||||
|
{
|
||||||
|
options.Filters.Add<ModelActionFilter>();
|
||||||
|
options.Filters.Add<ProjectExceptionFilter>();
|
||||||
|
options.Filters.Add<UnitOfWorkFilter>();
|
||||||
|
|
||||||
|
|
||||||
|
})
|
||||||
|
.AddNewtonsoftJsonSetup(builder.Services); // NewtonsoftJson 序列化 处理
|
||||||
|
|
||||||
|
|
||||||
|
builder.Services.AddOptions().Configure<AliyunOSSOptions>(_configuration.GetSection("AliyunOSS"));
|
||||||
|
builder.Services.AddOptions().Configure<ObjectStoreServiceOptions>(_configuration.GetSection("ObjectStoreService"));
|
||||||
|
builder.Services.AddOptions().Configure<DicomSCPServiceOption>(_configuration.GetSection("DicomSCPServiceConfig"));
|
||||||
|
|
||||||
|
|
||||||
|
//动态WebApi + UnifiedApiResultFilter 省掉控制器代码
|
||||||
|
//动态webApi 目前存在的唯一小坑是生成api上服务上的动态代理AOP失效 间接掉用不影响
|
||||||
|
builder.Services
|
||||||
|
.AddDynamicWebApi(dynamicWebApiOption =>
|
||||||
|
{
|
||||||
|
//默认是 api
|
||||||
|
dynamicWebApiOption.DefaultApiPrefix = "";
|
||||||
|
//首字母小写
|
||||||
|
dynamicWebApiOption.GetRestFulActionName = (actionName) => char.ToLower(actionName[0]) + actionName.Substring(1);
|
||||||
|
//删除 Service后缀
|
||||||
|
dynamicWebApiOption.RemoveControllerPostfixes.Add("Service");
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
//AutoMapper
|
||||||
|
builder.Services.AddAutoMapper(automapper =>
|
||||||
|
{
|
||||||
|
|
||||||
|
automapper.AddCollectionMappers();
|
||||||
|
|
||||||
|
|
||||||
|
}, typeof(BaseService).Assembly);
|
||||||
|
|
||||||
|
//EF ORM QueryWithNoLock
|
||||||
|
builder.Services.AddEFSetup(_configuration);
|
||||||
|
|
||||||
|
builder.Services.AddMediator(cfg =>
|
||||||
|
{
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
//转发头设置 获取真实IP
|
||||||
|
builder.Services.Configure<ForwardedHeadersOptions>(options =>
|
||||||
|
{
|
||||||
|
options.ForwardedHeaders =
|
||||||
|
ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
builder.Services.AddFellowOakDicom().AddTranscoderManager<NativeTranscoderManager>()
|
||||||
|
//.AddTranscoderManager<FellowOakDicom.Imaging.NativeCodec.NativeTranscoderManager>()
|
||||||
|
.AddImageManager<ImageSharpImageManager>();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
// Add services to the container.
|
||||||
|
|
||||||
|
builder.Services.AddControllers();
|
||||||
|
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
|
||||||
|
builder.Services.AddEndpointsApiExplorer();
|
||||||
|
builder.Services.AddSwaggerGen();
|
||||||
|
|
||||||
|
var app = builder.Build();
|
||||||
|
|
||||||
|
// Configure the HTTP request pipeline.
|
||||||
|
//if (app.Environment.IsDevelopment())
|
||||||
|
//{
|
||||||
|
app.UseSwagger();
|
||||||
|
app.UseSwaggerUI();
|
||||||
|
//}
|
||||||
|
|
||||||
|
app.UseAuthorization();
|
||||||
|
|
||||||
|
app.MapControllers();
|
||||||
|
|
||||||
|
#region 日志
|
||||||
|
|
||||||
|
|
||||||
|
Log.Logger = new LoggerConfiguration()
|
||||||
|
//.MinimumLevel.Information()
|
||||||
|
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
|
||||||
|
.WriteTo.Console()
|
||||||
|
.WriteTo.File($"{AppContext.BaseDirectory}Serilogs/.log", rollingInterval: RollingInterval.Day)
|
||||||
|
.CreateLogger();
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
#region 运行环境 部署平台
|
||||||
|
|
||||||
|
Log.Logger.Warning($"当前环境:{enviromentName}");
|
||||||
|
|
||||||
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||||
|
{
|
||||||
|
Log.Logger.Warning($"当前部署平台环境:windows");
|
||||||
|
}
|
||||||
|
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
||||||
|
{
|
||||||
|
Log.Logger.Warning($"当前部署平台环境:linux");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log.Logger.Warning($"当前部署平台环境:OSX or FreeBSD");
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
DicomSetupBuilder.UseServiceProvider(app.Services);
|
||||||
|
|
||||||
|
var logger = app.Services.GetService<Microsoft.Extensions.Logging.ILogger<Program>>();
|
||||||
|
|
||||||
|
var server = DicomServerFactory.Create<CStoreSCPService>(_configuration.GetSection("DicomSCPServiceConfig").GetValue<int>("ServerPort"), userState: app.Services, logger: logger);
|
||||||
|
|
||||||
|
|
||||||
|
app.Run();
|
|
@ -0,0 +1,31 @@
|
||||||
|
{
|
||||||
|
"$schema": "http://json.schemastore.org/launchsettings.json",
|
||||||
|
"iisSettings": {
|
||||||
|
"windowsAuthentication": false,
|
||||||
|
"anonymousAuthentication": true,
|
||||||
|
"iisExpress": {
|
||||||
|
"applicationUrl": "http://localhost:11224",
|
||||||
|
"sslPort": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"profiles": {
|
||||||
|
"http": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"dotnetRunMessages": true,
|
||||||
|
"launchBrowser": true,
|
||||||
|
"launchUrl": "swagger",
|
||||||
|
"applicationUrl": "http://localhost:5127",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"IIS Express": {
|
||||||
|
"commandName": "IISExpress",
|
||||||
|
"launchBrowser": true,
|
||||||
|
"launchUrl": "swagger",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,111 @@
|
||||||
|
using AutoMapper;
|
||||||
|
using IRaCIS.Core.Application.Service.BusinessFilter;
|
||||||
|
using IRaCIS.Core.Infra.EFCore;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.Extensions.Localization;
|
||||||
|
using Panda.DynamicWebApi;
|
||||||
|
using Panda.DynamicWebApi.Attributes;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using IRaCIS.Core.Domain.Share;
|
||||||
|
using IRaCIS.Core.Infrastructure.Extention;
|
||||||
|
using IRaCIS.Core.Domain.Models;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.SCP.Service
|
||||||
|
{
|
||||||
|
|
||||||
|
#pragma warning disable CS8618
|
||||||
|
|
||||||
|
|
||||||
|
#region 非泛型版本
|
||||||
|
|
||||||
|
[Authorize, DynamicWebApi, UnifiedApiResultFilter]
|
||||||
|
public class BaseService : IBaseService, IDynamicWebApi
|
||||||
|
{
|
||||||
|
public IMapper _mapper { get; set; }
|
||||||
|
|
||||||
|
public IUserInfo _userInfo { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
public IStringLocalizer _localizer { get; set; }
|
||||||
|
|
||||||
|
public IWebHostEnvironment _hostEnvironment { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static IResponseOutput Null404NotFound<TEntity>(TEntity? businessObject) where TEntity : class
|
||||||
|
{
|
||||||
|
return new ResponseOutput<string>()
|
||||||
|
.NotOk($"The query object {typeof(TEntity).Name} does not exist , or was deleted by someone else, or an incorrect parameter query caused", code: ApiResponseCodeEnum.DataNotExist);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public interface IBaseService
|
||||||
|
{
|
||||||
|
[MemberNotNull(nameof(_mapper))]
|
||||||
|
public IMapper _mapper { get; set; }
|
||||||
|
|
||||||
|
[MemberNotNull(nameof(_userInfo))]
|
||||||
|
public IUserInfo _userInfo { get; set; }
|
||||||
|
|
||||||
|
[MemberNotNull(nameof(_localizer))]
|
||||||
|
public IStringLocalizer _localizer { get; set; }
|
||||||
|
|
||||||
|
[MemberNotNull(nameof(_hostEnvironment))]
|
||||||
|
public IWebHostEnvironment _hostEnvironment { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
#region 泛型版本测试
|
||||||
|
|
||||||
|
|
||||||
|
public interface IBaseServiceTest<T> where T : Entity
|
||||||
|
{
|
||||||
|
[MemberNotNull(nameof(_mapper))]
|
||||||
|
public IMapper _mapper { get; set; }
|
||||||
|
|
||||||
|
[MemberNotNull(nameof(_userInfo))]
|
||||||
|
public IUserInfo _userInfo { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[MemberNotNull(nameof(_localizer))]
|
||||||
|
public IStringLocalizer _localizer { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Authorize, DynamicWebApi, UnifiedApiResultFilter]
|
||||||
|
public class BaseServiceTest<T> : IBaseServiceTest<T>, IDynamicWebApi where T : Entity
|
||||||
|
{
|
||||||
|
public IMapper _mapper { get; set; }
|
||||||
|
|
||||||
|
public IUserInfo _userInfo { get; set; }
|
||||||
|
|
||||||
|
public IStringLocalizer _localizer { get; set; }
|
||||||
|
|
||||||
|
public static IResponseOutput Null404NotFound<TEntity>(TEntity? businessObject) where TEntity : class
|
||||||
|
{
|
||||||
|
return new ResponseOutput<string>()
|
||||||
|
.NotOk($"The query object {typeof(TEntity).Name} does not exist , or was deleted by someone else, or an incorrect parameter query caused", code: ApiResponseCodeEnum.DataNotExist);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,376 @@
|
||||||
|
using FellowOakDicom.Network;
|
||||||
|
using FellowOakDicom;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using IRaCIS.Core.SCP.Service;
|
||||||
|
using IRaCIS.Core.Domain.Models;
|
||||||
|
using IRaCIS.Core.Infra.EFCore;
|
||||||
|
using Medallion.Threading;
|
||||||
|
using IRaCIS.Core.Domain.Share;
|
||||||
|
using Serilog;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion.Internal;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using System.Data;
|
||||||
|
using FellowOakDicom.Imaging;
|
||||||
|
using SharpCompress.Common;
|
||||||
|
using SixLabors.ImageSharp.Formats.Jpeg;
|
||||||
|
using IRaCIS.Core.Infrastructure;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.SCP.Service
|
||||||
|
{
|
||||||
|
|
||||||
|
public class DicomSCPServiceOption
|
||||||
|
{
|
||||||
|
public List<string> CalledAEList { get; set; }
|
||||||
|
|
||||||
|
public string ServerPort { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public class CStoreSCPService : DicomService, IDicomServiceProvider, IDicomCStoreProvider, IDicomCEchoProvider
|
||||||
|
{
|
||||||
|
private IServiceProvider _serviceProvider { get; set; }
|
||||||
|
|
||||||
|
private List<Guid> _SCPStudyIdList { get; set; } = new List<Guid>();
|
||||||
|
|
||||||
|
private SCPImageUpload _upload { get; set; }
|
||||||
|
|
||||||
|
private Guid _trialId { get; set; }
|
||||||
|
|
||||||
|
private Guid _trialSiteId { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private static readonly DicomTransferSyntax[] _acceptedTransferSyntaxes = new DicomTransferSyntax[]
|
||||||
|
{
|
||||||
|
DicomTransferSyntax.ExplicitVRLittleEndian,
|
||||||
|
DicomTransferSyntax.ExplicitVRBigEndian,
|
||||||
|
DicomTransferSyntax.ImplicitVRLittleEndian
|
||||||
|
};
|
||||||
|
|
||||||
|
private static readonly DicomTransferSyntax[] _acceptedImageTransferSyntaxes = new DicomTransferSyntax[]
|
||||||
|
{
|
||||||
|
// Lossless
|
||||||
|
DicomTransferSyntax.JPEGLSLossless, //1.2.840.10008.1.2.4.80
|
||||||
|
DicomTransferSyntax.JPEG2000Lossless, //1.2.840.10008.1.2.4.90
|
||||||
|
DicomTransferSyntax.JPEGProcess14SV1, //1.2.840.10008.1.2.4.70
|
||||||
|
DicomTransferSyntax.JPEGProcess14, //1.2.840.10008.1.2.4.57 JPEG Lossless, Non-Hierarchical (Process 14)
|
||||||
|
DicomTransferSyntax.RLELossless, //1.2.840.10008.1.2.5
|
||||||
|
// Lossy
|
||||||
|
DicomTransferSyntax.JPEGLSNearLossless,//1.2.840.10008.1.2.4.81"
|
||||||
|
DicomTransferSyntax.JPEG2000Lossy, //1.2.840.10008.1.2.4.91
|
||||||
|
DicomTransferSyntax.JPEGProcess1, //1.2.840.10008.1.2.4.50
|
||||||
|
DicomTransferSyntax.JPEGProcess2_4, //1.2.840.10008.1.2.4.51
|
||||||
|
// Uncompressed
|
||||||
|
DicomTransferSyntax.ExplicitVRLittleEndian, //1.2.840.10008.1.2.1
|
||||||
|
DicomTransferSyntax.ExplicitVRBigEndian, //1.2.840.10008.1.2.2
|
||||||
|
DicomTransferSyntax.ImplicitVRLittleEndian //1.2.840.10008.1.2
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
public CStoreSCPService(INetworkStream stream, Encoding fallbackEncoding, Microsoft.Extensions.Logging.ILogger log, DicomServiceDependencies dependencies, IServiceProvider injectServiceProvider)
|
||||||
|
: base(stream, fallbackEncoding, log, dependencies)
|
||||||
|
{
|
||||||
|
_serviceProvider = injectServiceProvider.CreateScope().ServiceProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public Task OnReceiveAssociationRequestAsync(DicomAssociation association)
|
||||||
|
{
|
||||||
|
|
||||||
|
_upload = new SCPImageUpload() { StartTime = DateTime.Now, CallingAE = association.CallingAE, CalledAE = association.CalledAE, CallingAEIP = association.RemoteHost };
|
||||||
|
|
||||||
|
|
||||||
|
Log.Logger.Warning($"接收到来自{association.CallingAE}的连接");
|
||||||
|
|
||||||
|
//_serviceProvider = (IServiceProvider)this.UserState;
|
||||||
|
|
||||||
|
var _trialDicomAERepository = _serviceProvider.GetService<IRepository<TrialDicomAE>>();
|
||||||
|
|
||||||
|
|
||||||
|
var trialDicomAEList = _trialDicomAERepository.Select(t => new { t.CalledAE, t.TrialId }).ToList();
|
||||||
|
var trialCalledAEList = trialDicomAEList.Select(t => t.CalledAE).ToList();
|
||||||
|
|
||||||
|
Log.Logger.Information("当前系统配置:", string.Join('|', trialDicomAEList));
|
||||||
|
|
||||||
|
var findCalledAE = trialDicomAEList.Where(t => t.CalledAE == association.CalledAE).FirstOrDefault();
|
||||||
|
|
||||||
|
var isCanReceiveIamge = false;
|
||||||
|
|
||||||
|
if (findCalledAE != null)
|
||||||
|
{
|
||||||
|
_trialId = findCalledAE.TrialId;
|
||||||
|
|
||||||
|
var _trialSiteDicomAERepository = _serviceProvider.GetService<IRepository<TrialSiteDicomAE>>();
|
||||||
|
|
||||||
|
|
||||||
|
var findTrialSiteAE = _trialSiteDicomAERepository.Where(t => t.CallingAE == association.CallingAE && t.TrialId==_trialId).FirstOrDefault();
|
||||||
|
|
||||||
|
if (findTrialSiteAE != null)
|
||||||
|
{
|
||||||
|
_trialSiteId = findTrialSiteAE.TrialSiteId;
|
||||||
|
|
||||||
|
isCanReceiveIamge = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (association.CallingAE == "test-callingAE")
|
||||||
|
{
|
||||||
|
isCanReceiveIamge = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!trialCalledAEList.Contains(association.CalledAE) || isCanReceiveIamge == false)
|
||||||
|
{
|
||||||
|
|
||||||
|
Log.Logger.Warning($"拒绝CallingAE:{association.CallingAE} CalledAE:{association.CalledAE}的连接");
|
||||||
|
|
||||||
|
return SendAssociationRejectAsync(
|
||||||
|
DicomRejectResult.Permanent,
|
||||||
|
DicomRejectSource.ServiceUser,
|
||||||
|
DicomRejectReason.CalledAENotRecognized);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var pc in association.PresentationContexts)
|
||||||
|
{
|
||||||
|
if (pc.AbstractSyntax == DicomUID.Verification)
|
||||||
|
{
|
||||||
|
pc.AcceptTransferSyntaxes(_acceptedTransferSyntaxes);
|
||||||
|
}
|
||||||
|
else if (pc.AbstractSyntax.StorageCategory != DicomStorageCategory.None)
|
||||||
|
{
|
||||||
|
pc.AcceptTransferSyntaxes(_acceptedImageTransferSyntaxes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return SendAssociationAcceptAsync(association);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public async Task OnReceiveAssociationReleaseRequestAsync()
|
||||||
|
{
|
||||||
|
await DataMaintenanceAsaync();
|
||||||
|
|
||||||
|
//记录监控
|
||||||
|
|
||||||
|
var _SCPImageUploadRepository = _serviceProvider.GetService<IRepository<SCPImageUpload>>();
|
||||||
|
|
||||||
|
_upload.EndTime = DateTime.Now;
|
||||||
|
_upload.StudyCount = _SCPStudyIdList.Count;
|
||||||
|
_upload.TrialId = _trialId;
|
||||||
|
_upload.TrialSiteId = _trialSiteId;
|
||||||
|
|
||||||
|
await _SCPImageUploadRepository.AddAsync(_upload, true);
|
||||||
|
|
||||||
|
|
||||||
|
var _studyRepository = _serviceProvider.GetService<IRepository<SCPStudy>>();
|
||||||
|
//将检查设置为传输结束
|
||||||
|
await _studyRepository.BatchUpdateNoTrackingAsync(t => _SCPStudyIdList.Contains(t.Id), u => new SCPStudy() { IsUploadFinished = true });
|
||||||
|
|
||||||
|
await _studyRepository.SaveChangesAndClearAllTrackingAsync();
|
||||||
|
|
||||||
|
await SendAssociationReleaseResponseAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private async Task DataMaintenanceAsaync()
|
||||||
|
{
|
||||||
|
Log.Logger.Warning($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE}传输结束:开始维护数据,处理检查Modality");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//处理检查Modality
|
||||||
|
var _dictionaryRepository = _serviceProvider.GetService<IRepository<Dictionary>>();
|
||||||
|
var _seriesRepository = _serviceProvider.GetService<IRepository<SCPSeries>>();
|
||||||
|
var _studyRepository = _serviceProvider.GetService<IRepository<SCPStudy>>();
|
||||||
|
|
||||||
|
var dicModalityList = _dictionaryRepository.Where(t => t.Code == "Modality").SelectMany(t => t.ChildList.Select(c => c.Value)).ToList();
|
||||||
|
var seriesModalityList = _seriesRepository.Where(t => _SCPStudyIdList.Contains(t.StudyId)).Select(t => new { SCPStudyId = t.StudyId, t.Modality }).ToList();
|
||||||
|
|
||||||
|
foreach (var g in seriesModalityList.GroupBy(t => t.SCPStudyId))
|
||||||
|
{
|
||||||
|
var modality = string.Join('、', g.Select(t => t.Modality).Distinct().ToList());
|
||||||
|
|
||||||
|
//特殊逻辑
|
||||||
|
var modalityForEdit = dicModalityList.Contains(modality) ? modality : String.Empty;
|
||||||
|
|
||||||
|
if (modality == "MR")
|
||||||
|
{
|
||||||
|
modalityForEdit = "MRI";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (modality == "PT")
|
||||||
|
{
|
||||||
|
modalityForEdit = "PET";
|
||||||
|
}
|
||||||
|
if (modality == "PT、CT" || modality == "CT、PT")
|
||||||
|
{
|
||||||
|
modalityForEdit = "PET-CT";
|
||||||
|
}
|
||||||
|
|
||||||
|
await _studyRepository.BatchUpdateNoTrackingAsync(t => t.Id == g.Key, u => new SCPStudy() { Modalities = modality, ModalityForEdit = modalityForEdit });
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.Logger.Warning($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE}维护数据结束");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnReceiveAbort(DicomAbortSource source, DicomAbortReason reason)
|
||||||
|
{
|
||||||
|
Log.Logger.Warning($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE}接收中断,中断原因:{source.ToString() + reason.ToString()}");
|
||||||
|
/* nothing to do here */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public async void OnConnectionClosed(Exception exception)
|
||||||
|
{
|
||||||
|
/* nothing to do here */
|
||||||
|
|
||||||
|
//奇怪的bug 上传的时候,用王捷修改的影像,会关闭,重新连接,导致检查id 丢失,然后状态不一致
|
||||||
|
if (exception == null)
|
||||||
|
{
|
||||||
|
//var _studyRepository = _serviceProvider.GetService<IRepository<SCPStudy>>();
|
||||||
|
////将检查设置为传输结束
|
||||||
|
//await _studyRepository.BatchUpdateNoTrackingAsync(t => _SCPStudyIdList.Contains(t.Id), u => new SCPStudy() { IsUploadFinished = true });
|
||||||
|
|
||||||
|
//await _studyRepository.SaveChangesAndClearAllTrackingAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.Logger.Warning($"连接关闭 {exception?.Message} {exception?.InnerException?.Message}");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public async Task<DicomCStoreResponse> OnCStoreRequestAsync(DicomCStoreRequest request)
|
||||||
|
{
|
||||||
|
|
||||||
|
string studyInstanceUid = request.Dataset.GetString(DicomTag.StudyInstanceUID);
|
||||||
|
string seriesInstanceUid = request.Dataset.GetString(DicomTag.SeriesInstanceUID);
|
||||||
|
string sopInstanceUid = request.Dataset.GetString(DicomTag.SOPInstanceUID);
|
||||||
|
|
||||||
|
//Guid studyId = IdentifierHelper.CreateGuid(studyInstanceUid, trialId.ToString());
|
||||||
|
Guid seriesId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid, _trialId.ToString());
|
||||||
|
Guid instanceId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid, sopInstanceUid, _trialId.ToString());
|
||||||
|
|
||||||
|
|
||||||
|
var ossService = _serviceProvider.GetService<IOSSService>();
|
||||||
|
var dicomArchiveService = _serviceProvider.GetService<IDicomArchiveService>();
|
||||||
|
var _seriesRepository = _serviceProvider.GetService<IRepository<SCPSeries>>();
|
||||||
|
|
||||||
|
var _distributedLockProvider = _serviceProvider.GetService<IDistributedLockProvider>();
|
||||||
|
|
||||||
|
var storeRelativePath = string.Empty;
|
||||||
|
var ossFolderPath = $"{_trialId}/Image/PACS/{_trialSiteId}/{studyInstanceUid}";
|
||||||
|
|
||||||
|
|
||||||
|
long fileSize = 0;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
|
||||||
|
using (MemoryStream ms = new MemoryStream())
|
||||||
|
{
|
||||||
|
await request.File.SaveAsync(ms);
|
||||||
|
|
||||||
|
//irc 从路径最后一截取Guid
|
||||||
|
storeRelativePath = await ossService.UploadToOSSAsync(ms, ossFolderPath, instanceId.ToString(), false);
|
||||||
|
|
||||||
|
fileSize = ms.Length;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.Logger.Information($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE} {request.SOPInstanceUID} 上传完成 ");
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (Exception ec)
|
||||||
|
{
|
||||||
|
Log.Logger.Warning($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE} 上传异常 {ec.Message}");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var @lock = _distributedLockProvider.CreateLock($"{studyInstanceUid}");
|
||||||
|
|
||||||
|
using (await @lock.AcquireAsync())
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var scpStudyId = await dicomArchiveService.ArchiveDicomFileAsync(request.Dataset, _trialId, _trialSiteId, storeRelativePath, Association.CallingAE, Association.CalledAE,fileSize);
|
||||||
|
|
||||||
|
if (!_SCPStudyIdList.Contains(scpStudyId))
|
||||||
|
{
|
||||||
|
_SCPStudyIdList.Add(scpStudyId);
|
||||||
|
}
|
||||||
|
|
||||||
|
var series = await _seriesRepository.FirstOrDefaultAsync(t => t.Id == seriesId);
|
||||||
|
|
||||||
|
//没有缩略图
|
||||||
|
if (series != null && string.IsNullOrEmpty(series.ImageResizePath))
|
||||||
|
{
|
||||||
|
|
||||||
|
// 生成缩略图
|
||||||
|
using (var memoryStream = new MemoryStream())
|
||||||
|
{
|
||||||
|
DicomImage image = new DicomImage(request.Dataset);
|
||||||
|
|
||||||
|
var sharpimage = image.RenderImage().AsSharpImage();
|
||||||
|
sharpimage.Save(memoryStream, new JpegEncoder());
|
||||||
|
|
||||||
|
// 上传缩略图到 OSS
|
||||||
|
|
||||||
|
var seriesPath = await ossService.UploadToOSSAsync(memoryStream, ossFolderPath, seriesId.ToString() + ".preview.jpg", false);
|
||||||
|
|
||||||
|
Console.WriteLine(seriesPath + " Id: " + seriesId);
|
||||||
|
|
||||||
|
series.ImageResizePath = seriesPath;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
await _seriesRepository.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
|
||||||
|
Log.Logger.Warning($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE} 传输处理异常:{ex.ToString()}");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//监控信息设置
|
||||||
|
_upload.FileCount++;
|
||||||
|
_upload.FileSize = _upload.FileSize + fileSize;
|
||||||
|
return new DicomCStoreResponse(request, DicomStatus.Success);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Task OnCStoreRequestExceptionAsync(string tempFileName, Exception e)
|
||||||
|
{
|
||||||
|
// let library handle logging and error response
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Task<DicomCEchoResponse> OnCEchoRequestAsync(DicomCEchoRequest request)
|
||||||
|
{
|
||||||
|
return Task.FromResult(new DicomCEchoResponse(request, DicomStatus.Success));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,356 @@
|
||||||
|
using IRaCIS.Core.Domain.Share;
|
||||||
|
using System.Text;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using IRaCIS.Core.Infrastructure;
|
||||||
|
using Medallion.Threading;
|
||||||
|
using FellowOakDicom;
|
||||||
|
using FellowOakDicom.Imaging.Codec;
|
||||||
|
using System.Data;
|
||||||
|
using IRaCIS.Core.Domain.Models;
|
||||||
|
using FellowOakDicom.Network;
|
||||||
|
using IRaCIS.Core.SCP.Service;
|
||||||
|
using IRaCIS.Core.Infra.EFCore;
|
||||||
|
using MassTransit;
|
||||||
|
using System.Runtime.Intrinsics.X86;
|
||||||
|
using Serilog.Sinks.File;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.SCP.Service
|
||||||
|
{
|
||||||
|
public class DicomArchiveService : BaseService, IDicomArchiveService
|
||||||
|
{
|
||||||
|
private readonly IRepository<SCPPatient> _patientRepository;
|
||||||
|
private readonly IRepository<SCPStudy> _studyRepository;
|
||||||
|
private readonly IRepository<SCPSeries> _seriesRepository;
|
||||||
|
private readonly IRepository<SCPInstance> _instanceRepository;
|
||||||
|
private readonly IRepository<Dictionary> _dictionaryRepository;
|
||||||
|
private readonly IDistributedLockProvider _distributedLockProvider;
|
||||||
|
|
||||||
|
|
||||||
|
private List<Guid> _instanceIdList = new List<Guid>();
|
||||||
|
|
||||||
|
public DicomArchiveService(IRepository<SCPPatient> patientRepository, IRepository<SCPStudy> studyRepository,
|
||||||
|
IRepository<SCPSeries> seriesRepository,
|
||||||
|
IRepository<SCPInstance> instanceRepository,
|
||||||
|
IRepository<Dictionary> dictionaryRepository,
|
||||||
|
IDistributedLockProvider distributedLockProvider)
|
||||||
|
{
|
||||||
|
_distributedLockProvider = distributedLockProvider;
|
||||||
|
_studyRepository = studyRepository;
|
||||||
|
_patientRepository = patientRepository;
|
||||||
|
_seriesRepository = seriesRepository;
|
||||||
|
_instanceRepository = instanceRepository;
|
||||||
|
_dictionaryRepository = dictionaryRepository;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 单个文件接收 归档
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dataset"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <exception cref="NotImplementedException"></exception>
|
||||||
|
public async Task<Guid> ArchiveDicomFileAsync(DicomDataset dataset, Guid trialId, Guid trialSiteId, string fileRelativePath, string callingAE, string calledAE,long fileSize)
|
||||||
|
{
|
||||||
|
string studyInstanceUid = dataset.GetString(DicomTag.StudyInstanceUID);
|
||||||
|
string seriesInstanceUid = dataset.GetString(DicomTag.SeriesInstanceUID);
|
||||||
|
string sopInstanceUid = dataset.GetString(DicomTag.SOPInstanceUID);
|
||||||
|
|
||||||
|
string patientIdStr = dataset.GetSingleValueOrDefault(DicomTag.PatientID,string.Empty);
|
||||||
|
|
||||||
|
//Guid patientId= IdentifierHelper.CreateGuid(patientIdStr);
|
||||||
|
Guid studyId = IdentifierHelper.CreateGuid(studyInstanceUid,trialId.ToString());
|
||||||
|
Guid seriesId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid, trialId.ToString());
|
||||||
|
Guid instanceId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid, sopInstanceUid, trialId.ToString());
|
||||||
|
|
||||||
|
var isStudyNeedAdd = false;
|
||||||
|
var isSeriesNeedAdd = false;
|
||||||
|
var isInstanceNeedAdd = false;
|
||||||
|
var isPatientNeedAdd = false;
|
||||||
|
|
||||||
|
//var @lock = _distributedLockProvider.CreateLock($"{studyInstanceUid}");
|
||||||
|
|
||||||
|
//using (@lock.Acquire())
|
||||||
|
{
|
||||||
|
var findPatient = await _patientRepository.FirstOrDefaultAsync(t => t.PatientIdStr == patientIdStr && t.TrialSiteId==trialSiteId );
|
||||||
|
var findStudy = await _studyRepository.FirstOrDefaultAsync(t=>t.Id== studyId);
|
||||||
|
var findSerice = await _seriesRepository.FirstOrDefaultAsync(t => t.Id == seriesId);
|
||||||
|
var findInstance = await _instanceRepository.FirstOrDefaultAsync(t => t.Id == instanceId);
|
||||||
|
|
||||||
|
DateTime? studyTime = dataset.GetSingleValueOrDefault(DicomTag.StudyDate, string.Empty) == string.Empty ? null : dataset.GetSingleValue<DateTime>(DicomTag.StudyDate).Add(dataset.GetSingleValueOrDefault(DicomTag.StudyTime, string.Empty) == string.Empty ? TimeSpan.Zero : dataset.GetSingleValue<DateTime>(DicomTag.StudyTime).TimeOfDay);
|
||||||
|
|
||||||
|
//先传输了修改了患者编号的,又传输了没有修改患者编号的,导致后传输的没有修改患者编号的下面的检查为0
|
||||||
|
if (findPatient == null && findStudy==null)
|
||||||
|
{
|
||||||
|
isPatientNeedAdd = true;
|
||||||
|
|
||||||
|
|
||||||
|
findPatient = new SCPPatient()
|
||||||
|
{
|
||||||
|
Id = NewId.NextSequentialGuid(),
|
||||||
|
TrialId=trialId,
|
||||||
|
TrialSiteId=trialSiteId,
|
||||||
|
PatientIdStr = dataset.GetSingleValueOrDefault(DicomTag.PatientID, string.Empty),
|
||||||
|
PatientName = dataset.GetSingleValueOrDefault(DicomTag.PatientName, string.Empty),
|
||||||
|
PatientAge = dataset.GetSingleValueOrDefault(DicomTag.PatientAge, string.Empty),
|
||||||
|
PatientSex = dataset.GetSingleValueOrDefault(DicomTag.PatientSex, string.Empty),
|
||||||
|
PatientBirthDate = dataset.GetSingleValueOrDefault(DicomTag.PatientBirthDate, string.Empty),
|
||||||
|
|
||||||
|
EarliestStudyTime = studyTime,
|
||||||
|
LatestStudyTime = studyTime,
|
||||||
|
LatestPushTime = DateTime.Now,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (findPatient.PatientBirthDate.Length == 8)
|
||||||
|
{
|
||||||
|
var birthDateStr = $"{findPatient.PatientBirthDate[0]}{findPatient.PatientBirthDate[1]}{findPatient.PatientBirthDate[2]}{findPatient.PatientBirthDate[3]}-{findPatient.PatientBirthDate[4]}{findPatient.PatientBirthDate[5]}-{findPatient.PatientBirthDate[6]}{findPatient.PatientBirthDate[7]}";
|
||||||
|
|
||||||
|
var yearStr = $"{findPatient.PatientBirthDate[0]}{findPatient.PatientBirthDate[1]}{findPatient.PatientBirthDate[2]}{findPatient.PatientBirthDate[3]}";
|
||||||
|
|
||||||
|
int year = 0;
|
||||||
|
|
||||||
|
var canParse = int.TryParse(yearStr, out year);
|
||||||
|
|
||||||
|
if (canParse && year > 1900)
|
||||||
|
{
|
||||||
|
findPatient.PatientBirthDate = birthDateStr;
|
||||||
|
|
||||||
|
DateTime birthDate;
|
||||||
|
|
||||||
|
if (findPatient.PatientAge == string.Empty && studyTime.HasValue && DateTime.TryParse(findPatient.PatientBirthDate,out birthDate))
|
||||||
|
{
|
||||||
|
var patientAge = studyTime.Value.Year - birthDate.Year;
|
||||||
|
// 如果生日还未到,年龄减去一岁
|
||||||
|
if (studyTime.Value < birthDate.AddYears(patientAge))
|
||||||
|
{
|
||||||
|
patientAge--;
|
||||||
|
}
|
||||||
|
|
||||||
|
findPatient.PatientAge = patientAge.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
findPatient.PatientBirthDate = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (studyTime < findPatient.EarliestStudyTime)
|
||||||
|
{
|
||||||
|
findPatient.EarliestStudyTime = studyTime;
|
||||||
|
}
|
||||||
|
if (studyTime > findPatient.LatestStudyTime)
|
||||||
|
{
|
||||||
|
findPatient.LatestStudyTime = studyTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
findPatient.LatestPushTime = DateTime.Now;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (findStudy == null)
|
||||||
|
{
|
||||||
|
isStudyNeedAdd = true;
|
||||||
|
findStudy = new SCPStudy
|
||||||
|
{
|
||||||
|
CalledAE = calledAE,
|
||||||
|
CallingAE = callingAE,
|
||||||
|
|
||||||
|
PatientId = findPatient.Id,
|
||||||
|
Id = studyId,
|
||||||
|
TrialId = trialId,
|
||||||
|
TrialSiteId = trialSiteId,
|
||||||
|
StudyInstanceUid = studyInstanceUid,
|
||||||
|
StudyTime = studyTime,
|
||||||
|
Modalities = dataset.GetSingleValueOrDefault(DicomTag.Modality, string.Empty),
|
||||||
|
//ModalityForEdit = modalityForEdit,
|
||||||
|
Description = dataset.GetSingleValueOrDefault(DicomTag.StudyDescription, string.Empty),
|
||||||
|
InstitutionName = dataset.GetSingleValueOrDefault(DicomTag.InstitutionName, string.Empty),
|
||||||
|
PatientIdStr = dataset.GetSingleValueOrDefault(DicomTag.PatientID, string.Empty),
|
||||||
|
PatientName = dataset.GetSingleValueOrDefault(DicomTag.PatientName, string.Empty),
|
||||||
|
PatientAge = dataset.GetSingleValueOrDefault(DicomTag.PatientAge, string.Empty),
|
||||||
|
PatientSex = dataset.GetSingleValueOrDefault(DicomTag.PatientSex, string.Empty),
|
||||||
|
BodyPartExamined = dataset.GetSingleValueOrDefault(DicomTag.BodyPartExamined, string.Empty),
|
||||||
|
|
||||||
|
StudyId = dataset.GetSingleValueOrDefault(DicomTag.StudyID, string.Empty),
|
||||||
|
AccessionNumber = dataset.GetSingleValueOrDefault(DicomTag.AccessionNumber, string.Empty),
|
||||||
|
|
||||||
|
//需要特殊处理
|
||||||
|
PatientBirthDate = dataset.GetSingleValueOrDefault(DicomTag.PatientBirthDate, string.Empty),
|
||||||
|
|
||||||
|
|
||||||
|
AcquisitionTime = dataset.GetSingleValueOrDefault(DicomTag.AcquisitionTime, string.Empty),
|
||||||
|
AcquisitionNumber = dataset.GetSingleValueOrDefault(DicomTag.AcquisitionNumber, string.Empty),
|
||||||
|
TriggerTime = dataset.GetSingleValueOrDefault(DicomTag.TriggerTime, string.Empty),
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//IsDoubleReview = addtionalInfo.IsDoubleReview,
|
||||||
|
SeriesCount = 0,
|
||||||
|
InstanceCount = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
if (findStudy.PatientBirthDate.Length == 8)
|
||||||
|
{
|
||||||
|
findStudy.PatientBirthDate = $"{findStudy.PatientBirthDate[0]}{findStudy.PatientBirthDate[1]}{findStudy.PatientBirthDate[2]}{findStudy.PatientBirthDate[3]}-{findStudy.PatientBirthDate[4]}{findStudy.PatientBirthDate[5]}-{findStudy.PatientBirthDate[6]}{findStudy.PatientBirthDate[7]}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (findSerice == null)
|
||||||
|
{
|
||||||
|
isSeriesNeedAdd = true;
|
||||||
|
|
||||||
|
findSerice = new SCPSeries
|
||||||
|
{
|
||||||
|
Id = seriesId,
|
||||||
|
StudyId = findStudy.Id,
|
||||||
|
|
||||||
|
StudyInstanceUid = findStudy.StudyInstanceUid,
|
||||||
|
SeriesInstanceUid = seriesInstanceUid,
|
||||||
|
SeriesNumber = dataset.GetSingleValueOrDefault(DicomTag.SeriesNumber, 1),
|
||||||
|
//SeriesTime = dataset.GetSingleValueOrDefault(DicomTag.SeriesDate, DateTime.Now).Add(dataset.GetSingleValueOrDefault(DicomTag.SeriesTime, DateTime.Now).TimeOfDay),
|
||||||
|
//SeriesTime = DateTime.TryParse(dataset.GetSingleValue<string>(DicomTag.SeriesDate) + dataset.GetSingleValue<string>(DicomTag.SeriesTime), out DateTime dt) ? dt : null,
|
||||||
|
SeriesTime = dataset.GetSingleValueOrDefault(DicomTag.SeriesDate, string.Empty) == string.Empty ? null : dataset.GetSingleValue<DateTime>(DicomTag.SeriesDate).Add(dataset.GetSingleValueOrDefault(DicomTag.SeriesTime, string.Empty) == string.Empty ? TimeSpan.Zero : dataset.GetSingleValue<DateTime>(DicomTag.SeriesTime).TimeOfDay),
|
||||||
|
Modality = dataset.GetSingleValueOrDefault(DicomTag.Modality, string.Empty),
|
||||||
|
Description = dataset.GetSingleValueOrDefault(DicomTag.SeriesDescription, string.Empty),
|
||||||
|
SliceThickness = dataset.GetSingleValueOrDefault(DicomTag.SliceThickness, string.Empty),
|
||||||
|
|
||||||
|
ImagePositionPatient = dataset.GetSingleValueOrDefault(DicomTag.ImagePositionPatient, string.Empty),
|
||||||
|
ImageOrientationPatient = dataset.GetSingleValueOrDefault(DicomTag.ImageOrientationPatient, string.Empty),
|
||||||
|
BodyPartExamined = dataset.GetSingleValueOrDefault(DicomTag.BodyPartExamined, string.Empty),
|
||||||
|
SequenceName = dataset.GetSingleValueOrDefault(DicomTag.SequenceName, string.Empty),
|
||||||
|
ProtocolName = dataset.GetSingleValueOrDefault(DicomTag.ProtocolName, string.Empty),
|
||||||
|
ImagerPixelSpacing = dataset.GetSingleValueOrDefault(DicomTag.ImagerPixelSpacing, string.Empty),
|
||||||
|
|
||||||
|
AcquisitionTime = dataset.GetSingleValueOrDefault(DicomTag.AcquisitionTime, string.Empty),
|
||||||
|
AcquisitionNumber = dataset.GetSingleValueOrDefault(DicomTag.AcquisitionNumber, string.Empty),
|
||||||
|
TriggerTime = dataset.GetSingleValueOrDefault(DicomTag.TriggerTime, string.Empty),
|
||||||
|
|
||||||
|
|
||||||
|
InstanceCount = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
++findStudy.SeriesCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (findInstance == null)
|
||||||
|
{
|
||||||
|
isInstanceNeedAdd = true;
|
||||||
|
findInstance = new SCPInstance
|
||||||
|
{
|
||||||
|
Id = instanceId,
|
||||||
|
StudyId = findStudy.Id,
|
||||||
|
SeriesId = findSerice.Id,
|
||||||
|
StudyInstanceUid = findStudy.StudyInstanceUid,
|
||||||
|
SeriesInstanceUid = findSerice.SeriesInstanceUid,
|
||||||
|
|
||||||
|
SopInstanceUid = sopInstanceUid,
|
||||||
|
InstanceNumber = dataset.GetSingleValueOrDefault(DicomTag.InstanceNumber, 1),
|
||||||
|
InstanceTime = dataset.GetSingleValueOrDefault(DicomTag.ContentDate, string.Empty) == string.Empty ? null : dataset.GetSingleValue<DateTime>(DicomTag.ContentDate).Add(dataset.GetSingleValueOrDefault(DicomTag.ContentTime, string.Empty) == string.Empty ? TimeSpan.Zero : dataset.GetSingleValue<DateTime>(DicomTag.ContentTime).TimeOfDay),
|
||||||
|
//InstanceTime = DateTime.TryParse(dataset.GetSingleValue<string>(DicomTag.ContentDate) + dataset.GetSingleValue<string>(DicomTag.ContentTime), out DateTime dt) ? dt : null,
|
||||||
|
//InstanceTime = dataset.GetSingleValueOrDefault(DicomTag.ContentDate,(DateTime?)null)?.Add(dataset.GetSingleValueOrDefault(DicomTag.ContentTime, TimeSpan.Zero)),
|
||||||
|
//dataset.GetSingleValueOrDefault(DicomTag.ContentDate,DateTime.Now);//, DicomTag.ContentTime)
|
||||||
|
CPIStatus = false,
|
||||||
|
ImageRows = dataset.GetSingleValueOrDefault(DicomTag.Rows, 0),
|
||||||
|
ImageColumns = dataset.GetSingleValueOrDefault(DicomTag.Columns, 0),
|
||||||
|
SliceLocation = dataset.GetSingleValueOrDefault(DicomTag.SliceLocation, 0),
|
||||||
|
|
||||||
|
SliceThickness = dataset.GetSingleValueOrDefault(DicomTag.SliceThickness, string.Empty),
|
||||||
|
NumberOfFrames = dataset.GetSingleValueOrDefault(DicomTag.NumberOfFrames, 0),
|
||||||
|
PixelSpacing = dataset.GetSingleValueOrDefault(DicomTag.PixelSpacing, string.Empty),
|
||||||
|
ImagerPixelSpacing = dataset.GetSingleValueOrDefault(DicomTag.ImagerPixelSpacing, string.Empty),
|
||||||
|
FrameOfReferenceUID = dataset.GetSingleValueOrDefault(DicomTag.FrameOfReferenceUID, string.Empty),
|
||||||
|
WindowCenter = dataset.GetSingleValueOrDefault(DicomTag.WindowCenter, string.Empty),
|
||||||
|
WindowWidth = dataset.GetSingleValueOrDefault(DicomTag.WindowWidth, string.Empty),
|
||||||
|
|
||||||
|
Path = fileRelativePath,
|
||||||
|
|
||||||
|
FileSize= fileSize,
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
++findStudy.InstanceCount;
|
||||||
|
++findSerice.InstanceCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isPatientNeedAdd)
|
||||||
|
{
|
||||||
|
var ss = await _patientRepository.AddAsync(findPatient);
|
||||||
|
}
|
||||||
|
if (isStudyNeedAdd)
|
||||||
|
{
|
||||||
|
var dd = await _studyRepository.AddAsync(findStudy);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await _studyRepository.BatchUpdateNoTrackingAsync(t => t.Id == findStudy.Id, t => new SCPStudy() { IsUploadFinished = false });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isSeriesNeedAdd)
|
||||||
|
{
|
||||||
|
await _seriesRepository.AddAsync(findSerice);
|
||||||
|
}
|
||||||
|
if (isInstanceNeedAdd)
|
||||||
|
{
|
||||||
|
await _instanceRepository.AddAsync(findInstance);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await _instanceRepository.BatchUpdateNoTrackingAsync(t => t.Id == instanceId, u => new SCPInstance() { Path = fileRelativePath,FileSize=fileSize });
|
||||||
|
}
|
||||||
|
|
||||||
|
await _studyRepository.SaveChangesAsync();
|
||||||
|
|
||||||
|
return findStudy.Id;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 从DICOM文件中获取使用的字符集
|
||||||
|
private string GetEncodingVaulueFromDicomFile(DicomDataset dataset, DicomTag dicomTag)
|
||||||
|
{
|
||||||
|
|
||||||
|
// 获取DICOM文件的特定元素,通常用于指示使用的字符集
|
||||||
|
var charset = dataset.GetSingleValueOrDefault(DicomTag.SpecificCharacterSet, string.Empty);
|
||||||
|
|
||||||
|
var dicomEncoding = DicomEncoding.GetEncoding(charset);
|
||||||
|
|
||||||
|
|
||||||
|
var dicomStringElement = dataset.GetDicomItem<DicomStringElement>(dicomTag);
|
||||||
|
|
||||||
|
var bytes = dicomStringElement.Buffer.Data;
|
||||||
|
|
||||||
|
|
||||||
|
return dicomEncoding.GetString(bytes);
|
||||||
|
|
||||||
|
|
||||||
|
//// 从DICOM文件中获取使用的字符集
|
||||||
|
//string filePath = "C:\\Users\\hang\\Documents\\WeChat Files\\wxid_r2imdzb7j3q922\\FileStorage\\File\\2024-05\\1.2.840.113619.2.80.169103990.5390.1271401378.4.dcm";
|
||||||
|
//DicomFile dicomFile = DicomFile.Open(filePath);
|
||||||
|
|
||||||
|
//// 获取DICOM文件的特定元素,通常用于指示使用的字符集
|
||||||
|
//var charset = dicomFile.Dataset.GetSingleValueOrDefault(DicomTag.SpecificCharacterSet, string.Empty);
|
||||||
|
|
||||||
|
//var dicomEncoding = DicomEncoding.GetEncoding(charset);
|
||||||
|
|
||||||
|
//var value = dicomFile.Dataset.GetSingleValueOrDefault(DicomTag.PatientName, string.Empty);
|
||||||
|
|
||||||
|
//var dicomStringElement = dicomFile.Dataset.GetDicomItem<DicomStringElement>(DicomTag.PatientName);
|
||||||
|
|
||||||
|
//var bytes = dicomStringElement.Buffer.Data;
|
||||||
|
|
||||||
|
//var aa= dicomEncoding.GetString(bytes);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
using FellowOakDicom;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion.Internal;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.SCP.Service
|
||||||
|
{
|
||||||
|
public interface IDicomArchiveService
|
||||||
|
{
|
||||||
|
Task<Guid> ArchiveDicomFileAsync(DicomDataset dicomDataset,Guid trialId,Guid trialSiteId, string fileRelativePath,string callingAE,string calledAE,long fileSize);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,770 @@
|
||||||
|
using AlibabaCloud.SDK.Sts20150401;
|
||||||
|
using Aliyun.OSS;
|
||||||
|
using Amazon;
|
||||||
|
using Amazon.Runtime;
|
||||||
|
using Amazon.S3;
|
||||||
|
using Amazon.S3.Model;
|
||||||
|
using Amazon.SecurityToken;
|
||||||
|
using Amazon.SecurityToken.Model;
|
||||||
|
using IRaCIS.Core.Infrastructure;
|
||||||
|
using IRaCIS.Core.Infrastructure.NewtonsoftJson;
|
||||||
|
using MassTransit;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using Minio;
|
||||||
|
using Minio.DataModel.Args;
|
||||||
|
using System.Reactive.Linq;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.SCP;
|
||||||
|
|
||||||
|
#region 绑定和返回模型
|
||||||
|
|
||||||
|
[LowerCamelCaseJson]
|
||||||
|
public class MinIOOptions : AWSOptions
|
||||||
|
{
|
||||||
|
public int Port { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public class AWSOptions
|
||||||
|
{
|
||||||
|
public string EndPoint { get; set; }
|
||||||
|
public bool UseSSL { get; set; }
|
||||||
|
public string AccessKeyId { get; set; }
|
||||||
|
public string RoleArn { get; set; }
|
||||||
|
public string SecretAccessKey { get; set; }
|
||||||
|
public string BucketName { get; set; }
|
||||||
|
public string ViewEndpoint { get; set; }
|
||||||
|
public int DurationSeconds { get; set; }
|
||||||
|
public string Region { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AliyunOSSOptions
|
||||||
|
{
|
||||||
|
public string RegionId { get; set; }
|
||||||
|
public string AccessKeyId { get; set; }
|
||||||
|
public string AccessKeySecret { get; set; }
|
||||||
|
|
||||||
|
public string InternalEndpoint { get; set; }
|
||||||
|
|
||||||
|
public string EndPoint { get; set; }
|
||||||
|
public string BucketName { get; set; }
|
||||||
|
|
||||||
|
public string RoleArn { get; set; }
|
||||||
|
|
||||||
|
public string Region { get; set; }
|
||||||
|
|
||||||
|
public string ViewEndpoint { get; set; }
|
||||||
|
|
||||||
|
public int DurationSeconds { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ObjectStoreServiceOptions
|
||||||
|
{
|
||||||
|
public string ObjectStoreUse { get; set; }
|
||||||
|
|
||||||
|
public AliyunOSSOptions AliyunOSS { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
public MinIOOptions MinIO { get; set; }
|
||||||
|
|
||||||
|
public AWSOptions AWS { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ObjectStoreDTO
|
||||||
|
{
|
||||||
|
public string ObjectStoreUse { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
public AliyunOSSTempToken AliyunOSS { get; set; }
|
||||||
|
|
||||||
|
public MinIOOptions MinIO { get; set; }
|
||||||
|
|
||||||
|
public AWSTempToken AWS { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
[LowerCamelCaseJson]
|
||||||
|
public class AliyunOSSTempToken
|
||||||
|
{
|
||||||
|
public string AccessKeyId { get; set; }
|
||||||
|
public string AccessKeySecret { get; set; }
|
||||||
|
|
||||||
|
public string EndPoint { get; set; }
|
||||||
|
public string BucketName { get; set; }
|
||||||
|
|
||||||
|
public string Region { get; set; }
|
||||||
|
|
||||||
|
public string ViewEndpoint { get; set; }
|
||||||
|
|
||||||
|
public string SecurityToken { get; set; }
|
||||||
|
public DateTime Expiration { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
[LowerCamelCaseJson]
|
||||||
|
public class AWSTempToken
|
||||||
|
{
|
||||||
|
public string Region { get; set; }
|
||||||
|
public string SessionToken { get; set; }
|
||||||
|
public string EndPoint { get; set; }
|
||||||
|
public string AccessKeyId { get; set; }
|
||||||
|
public string SecretAccessKey { get; set; }
|
||||||
|
public string BucketName { get; set; }
|
||||||
|
public string ViewEndpoint { get; set; }
|
||||||
|
public DateTime Expiration { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum ObjectStoreUse
|
||||||
|
{
|
||||||
|
AliyunOSS = 0,
|
||||||
|
MinIO = 1,
|
||||||
|
AWS = 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
// aws 参考链接 https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/dotnetv3/S3/S3_Basics
|
||||||
|
|
||||||
|
public interface IOSSService
|
||||||
|
{
|
||||||
|
public Task<string> UploadToOSSAsync(Stream fileStream, string oosFolderPath, string fileRealName, bool isFileNameAddGuid = true);
|
||||||
|
public Task<string> UploadToOSSAsync(string localFilePath, string oosFolderPath, bool isFileNameAddGuid = true);
|
||||||
|
|
||||||
|
public Task DownLoadFromOSSAsync(string ossRelativePath, string localFilePath);
|
||||||
|
|
||||||
|
public ObjectStoreServiceOptions ObjectStoreServiceOptions { get; set; }
|
||||||
|
|
||||||
|
public Task<string> GetSignedUrl(string ossRelativePath);
|
||||||
|
|
||||||
|
public Task DeleteFromPrefix(string prefix);
|
||||||
|
|
||||||
|
public ObjectStoreDTO GetObjectStoreTempToken();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public class OSSService : IOSSService
|
||||||
|
{
|
||||||
|
public ObjectStoreServiceOptions ObjectStoreServiceOptions { get; set; }
|
||||||
|
|
||||||
|
private AliyunOSSTempToken AliyunOSSTempToken { get; set; }
|
||||||
|
|
||||||
|
private AWSTempToken AWSTempToken { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
public OSSService(IOptionsMonitor<ObjectStoreServiceOptions> options)
|
||||||
|
{
|
||||||
|
ObjectStoreServiceOptions = options.CurrentValue;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// oosFolderPath 不要 "/ "开头 应该: TempFolder/ChildFolder
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="fileStream"></param>
|
||||||
|
/// <param name="oosFolderPath"></param>
|
||||||
|
/// <param name="fileRealName"></param>
|
||||||
|
/// <param name="isFileNameAddGuid"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<string> UploadToOSSAsync(Stream fileStream, string oosFolderPath, string fileRealName, bool isFileNameAddGuid = true)
|
||||||
|
{
|
||||||
|
GetObjectStoreTempToken();
|
||||||
|
|
||||||
|
var ossRelativePath = isFileNameAddGuid ? $"{oosFolderPath}/{Guid.NewGuid()}_{fileRealName}" : $"{oosFolderPath}/{fileRealName}";
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var memoryStream = new MemoryStream())
|
||||||
|
{
|
||||||
|
fileStream.Seek(0, SeekOrigin.Begin);
|
||||||
|
|
||||||
|
fileStream.CopyTo(memoryStream);
|
||||||
|
|
||||||
|
memoryStream.Seek(0, SeekOrigin.Begin);
|
||||||
|
|
||||||
|
|
||||||
|
if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS")
|
||||||
|
{
|
||||||
|
var aliConfig = ObjectStoreServiceOptions.AliyunOSS;
|
||||||
|
|
||||||
|
var _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.EndPoint : aliConfig.InternalEndpoint, AliyunOSSTempToken.AccessKeyId, AliyunOSSTempToken.AccessKeySecret, AliyunOSSTempToken.SecurityToken);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 上传文件
|
||||||
|
var result = _ossClient.PutObject(aliConfig.BucketName, ossRelativePath, memoryStream);
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (ObjectStoreServiceOptions.ObjectStoreUse == "MinIO")
|
||||||
|
{
|
||||||
|
var minIOConfig = ObjectStoreServiceOptions.MinIO;
|
||||||
|
|
||||||
|
|
||||||
|
var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.EndPoint}:{minIOConfig.Port}")
|
||||||
|
.WithCredentials(minIOConfig.AccessKeyId, minIOConfig.SecretAccessKey).WithSSL(minIOConfig.UseSSL)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
var putObjectArgs = new PutObjectArgs()
|
||||||
|
.WithBucket(minIOConfig.BucketName)
|
||||||
|
.WithObject(ossRelativePath)
|
||||||
|
.WithStreamData(memoryStream)
|
||||||
|
.WithObjectSize(memoryStream.Length);
|
||||||
|
|
||||||
|
await minioClient.PutObjectAsync(putObjectArgs);
|
||||||
|
}
|
||||||
|
else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS")
|
||||||
|
{
|
||||||
|
var awsConfig = ObjectStoreServiceOptions.AWS;
|
||||||
|
|
||||||
|
var credentials = new SessionAWSCredentials(AWSTempToken.AccessKeyId, AWSTempToken.SecretAccessKey, AWSTempToken.SessionToken);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//提供awsEndPoint(域名)进行访问配置
|
||||||
|
var clientConfig = new AmazonS3Config
|
||||||
|
{
|
||||||
|
RegionEndpoint = RegionEndpoint.USEast1,
|
||||||
|
UseHttp = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
|
||||||
|
|
||||||
|
var putObjectRequest = new Amazon.S3.Model.PutObjectRequest()
|
||||||
|
{
|
||||||
|
BucketName = awsConfig.BucketName,
|
||||||
|
InputStream = memoryStream,
|
||||||
|
Key = ossRelativePath,
|
||||||
|
};
|
||||||
|
|
||||||
|
await amazonS3Client.PutObjectAsync(putObjectRequest);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new BusinessValidationFailedException("未定义的存储介质类型");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
|
||||||
|
throw new BusinessValidationFailedException($"上传发生异常:{ex.Message}");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return "/" + ossRelativePath;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// oosFolderPath 不要 "/ "开头 应该: TempFolder/ChildFolder
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="localFilePath"></param>
|
||||||
|
/// <param name="oosFolderPath"></param>
|
||||||
|
/// <param name="isFileNameAddGuid"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <exception cref="BusinessValidationFailedException"></exception>
|
||||||
|
public async Task<string> UploadToOSSAsync(string localFilePath, string oosFolderPath, bool isFileNameAddGuid = true)
|
||||||
|
{
|
||||||
|
GetObjectStoreTempToken();
|
||||||
|
|
||||||
|
var localFileName = Path.GetFileName(localFilePath);
|
||||||
|
|
||||||
|
var ossRelativePath = isFileNameAddGuid ? $"{oosFolderPath}/{Guid.NewGuid()}_{localFileName}" : $"{oosFolderPath}/{localFileName}";
|
||||||
|
|
||||||
|
|
||||||
|
if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS")
|
||||||
|
{
|
||||||
|
var aliConfig = ObjectStoreServiceOptions.AliyunOSS;
|
||||||
|
|
||||||
|
var _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.EndPoint : aliConfig.InternalEndpoint, AliyunOSSTempToken.AccessKeyId, AliyunOSSTempToken.AccessKeySecret, AliyunOSSTempToken.SecurityToken);
|
||||||
|
|
||||||
|
// 上传文件
|
||||||
|
var result = _ossClient.PutObject(aliConfig.BucketName, ossRelativePath, localFilePath);
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (ObjectStoreServiceOptions.ObjectStoreUse == "MinIO")
|
||||||
|
{
|
||||||
|
var minIOConfig = ObjectStoreServiceOptions.MinIO;
|
||||||
|
|
||||||
|
|
||||||
|
var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.EndPoint}:{minIOConfig.Port}")
|
||||||
|
.WithCredentials(minIOConfig.AccessKeyId, minIOConfig.SecretAccessKey).WithSSL(minIOConfig.UseSSL)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
var putObjectArgs = new PutObjectArgs()
|
||||||
|
.WithBucket(minIOConfig.BucketName)
|
||||||
|
.WithObject(ossRelativePath)
|
||||||
|
.WithFileName(localFilePath);
|
||||||
|
|
||||||
|
await minioClient.PutObjectAsync(putObjectArgs);
|
||||||
|
}
|
||||||
|
else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS")
|
||||||
|
{
|
||||||
|
var awsConfig = ObjectStoreServiceOptions.AWS;
|
||||||
|
|
||||||
|
// 提供awsAccessKeyId和awsSecretAccessKey构造凭证
|
||||||
|
var credentials = new SessionAWSCredentials(AWSTempToken.AccessKeyId, AWSTempToken.SecretAccessKey, AWSTempToken.SessionToken);
|
||||||
|
|
||||||
|
//提供awsEndPoint(域名)进行访问配置
|
||||||
|
var clientConfig = new AmazonS3Config
|
||||||
|
{
|
||||||
|
RegionEndpoint = RegionEndpoint.USEast1,
|
||||||
|
UseHttp = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
|
||||||
|
|
||||||
|
var putObjectRequest = new Amazon.S3.Model.PutObjectRequest()
|
||||||
|
{
|
||||||
|
BucketName = awsConfig.BucketName,
|
||||||
|
FilePath = localFilePath,
|
||||||
|
Key = ossRelativePath,
|
||||||
|
};
|
||||||
|
|
||||||
|
await amazonS3Client.PutObjectAsync(putObjectRequest);
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new BusinessValidationFailedException("未定义的存储介质类型");
|
||||||
|
}
|
||||||
|
return "/" + ossRelativePath;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public async Task DownLoadFromOSSAsync(string ossRelativePath, string localFilePath)
|
||||||
|
{
|
||||||
|
GetObjectStoreTempToken();
|
||||||
|
|
||||||
|
ossRelativePath = ossRelativePath.TrimStart('/');
|
||||||
|
try
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS")
|
||||||
|
{
|
||||||
|
var aliConfig = ObjectStoreServiceOptions.AliyunOSS;
|
||||||
|
|
||||||
|
var _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.EndPoint : aliConfig.InternalEndpoint, AliyunOSSTempToken.AccessKeyId, AliyunOSSTempToken.AccessKeySecret, AliyunOSSTempToken.SecurityToken);
|
||||||
|
|
||||||
|
// 上传文件
|
||||||
|
var result = _ossClient.GetObject(aliConfig.BucketName, ossRelativePath);
|
||||||
|
|
||||||
|
// 将下载的文件流保存到本地文件
|
||||||
|
using (var fs = File.OpenWrite(localFilePath))
|
||||||
|
{
|
||||||
|
result.Content.CopyTo(fs);
|
||||||
|
fs.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (ObjectStoreServiceOptions.ObjectStoreUse == "MinIO")
|
||||||
|
{
|
||||||
|
var minIOConfig = ObjectStoreServiceOptions.MinIO;
|
||||||
|
|
||||||
|
var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.EndPoint}:{minIOConfig.Port}")
|
||||||
|
.WithCredentials(minIOConfig.AccessKeyId, minIOConfig.SecretAccessKey).WithSSL(minIOConfig.UseSSL)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
var getObjectArgs = new GetObjectArgs()
|
||||||
|
.WithBucket(minIOConfig.BucketName)
|
||||||
|
.WithObject(ossRelativePath)
|
||||||
|
.WithFile(localFilePath);
|
||||||
|
|
||||||
|
await minioClient.GetObjectAsync(getObjectArgs);
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS")
|
||||||
|
{
|
||||||
|
var awsConfig = ObjectStoreServiceOptions.AWS;
|
||||||
|
|
||||||
|
// 提供awsAccessKeyId和awsSecretAccessKey构造凭证
|
||||||
|
var credentials = new SessionAWSCredentials(AWSTempToken.AccessKeyId, AWSTempToken.SecretAccessKey, AWSTempToken.SessionToken);
|
||||||
|
|
||||||
|
//提供awsEndPoint(域名)进行访问配置
|
||||||
|
var clientConfig = new AmazonS3Config
|
||||||
|
{
|
||||||
|
RegionEndpoint = RegionEndpoint.USEast1,
|
||||||
|
UseHttp = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
|
||||||
|
|
||||||
|
var getObjectArgs = new Amazon.S3.Model.GetObjectRequest()
|
||||||
|
{
|
||||||
|
BucketName = awsConfig.BucketName,
|
||||||
|
Key = ossRelativePath,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
await (await amazonS3Client.GetObjectAsync(getObjectArgs)).WriteResponseStreamToFileAsync(localFilePath, true, CancellationToken.None);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new BusinessValidationFailedException("未定义的存储介质类型");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
|
||||||
|
throw new BusinessValidationFailedException("oss下载失败!" + ex.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<string> GetSignedUrl(string ossRelativePath)
|
||||||
|
{
|
||||||
|
GetObjectStoreTempToken();
|
||||||
|
|
||||||
|
ossRelativePath = ossRelativePath.TrimStart('/');
|
||||||
|
try
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS")
|
||||||
|
{
|
||||||
|
var aliConfig = ObjectStoreServiceOptions.AliyunOSS;
|
||||||
|
|
||||||
|
var _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.EndPoint : aliConfig.InternalEndpoint, AliyunOSSTempToken.AccessKeyId, AliyunOSSTempToken.AccessKeySecret, AliyunOSSTempToken.SecurityToken);
|
||||||
|
|
||||||
|
// 生成签名URL。
|
||||||
|
var req = new GeneratePresignedUriRequest(aliConfig.BucketName, ossRelativePath, SignHttpMethod.Get)
|
||||||
|
{
|
||||||
|
// 设置签名URL过期时间,默认值为3600秒。
|
||||||
|
Expiration = DateTime.Now.AddHours(1),
|
||||||
|
};
|
||||||
|
var uri = _ossClient.GeneratePresignedUri(req);
|
||||||
|
|
||||||
|
return uri.PathAndQuery;
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (ObjectStoreServiceOptions.ObjectStoreUse == "MinIO")
|
||||||
|
{
|
||||||
|
var minIOConfig = ObjectStoreServiceOptions.MinIO;
|
||||||
|
|
||||||
|
var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.EndPoint}:{minIOConfig.Port}")
|
||||||
|
.WithCredentials(minIOConfig.AccessKeyId, minIOConfig.SecretAccessKey).WithSSL(minIOConfig.UseSSL)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
|
||||||
|
var args = new PresignedGetObjectArgs()
|
||||||
|
.WithBucket(minIOConfig.BucketName)
|
||||||
|
.WithObject(ossRelativePath)
|
||||||
|
.WithExpiry(3600)
|
||||||
|
/*.WithHeaders(reqParams)*/;
|
||||||
|
|
||||||
|
var presignedUrl = await minioClient.PresignedGetObjectAsync(args);
|
||||||
|
|
||||||
|
Uri uri = new Uri(presignedUrl);
|
||||||
|
|
||||||
|
string relativePath = uri.PathAndQuery;
|
||||||
|
|
||||||
|
|
||||||
|
return relativePath;
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS")
|
||||||
|
{
|
||||||
|
var awsConfig = ObjectStoreServiceOptions.AWS;
|
||||||
|
|
||||||
|
|
||||||
|
// 提供awsAccessKeyId和awsSecretAccessKey构造凭证
|
||||||
|
var credentials = new SessionAWSCredentials(AWSTempToken.AccessKeyId, AWSTempToken.SecretAccessKey, AWSTempToken.SessionToken);
|
||||||
|
|
||||||
|
//提供awsEndPoint(域名)进行访问配置
|
||||||
|
var clientConfig = new AmazonS3Config
|
||||||
|
{
|
||||||
|
RegionEndpoint = RegionEndpoint.USEast1,
|
||||||
|
UseHttp = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
|
||||||
|
|
||||||
|
var presignedUrl = await amazonS3Client.GetPreSignedURLAsync(new GetPreSignedUrlRequest()
|
||||||
|
{
|
||||||
|
BucketName = awsConfig.BucketName,
|
||||||
|
Key = ossRelativePath,
|
||||||
|
Expires = DateTime.UtcNow.AddMinutes(120)
|
||||||
|
});
|
||||||
|
|
||||||
|
Uri uri = new Uri(presignedUrl);
|
||||||
|
|
||||||
|
string relativePath = uri.PathAndQuery;
|
||||||
|
|
||||||
|
|
||||||
|
return relativePath;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new BusinessValidationFailedException("未定义的存储介质类型");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
|
||||||
|
throw new BusinessValidationFailedException("oss授权url失败!" + ex.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 删除某个目录的文件
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="prefix"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task DeleteFromPrefix(string prefix)
|
||||||
|
{
|
||||||
|
GetObjectStoreTempToken();
|
||||||
|
|
||||||
|
if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS")
|
||||||
|
{
|
||||||
|
var aliConfig = ObjectStoreServiceOptions.AliyunOSS;
|
||||||
|
|
||||||
|
var _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.EndPoint : aliConfig.InternalEndpoint, AliyunOSSTempToken.AccessKeyId, AliyunOSSTempToken.AccessKeySecret, AliyunOSSTempToken.SecurityToken);
|
||||||
|
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ObjectListing objectListing = null;
|
||||||
|
string nextMarker = null;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
// 使用 prefix 模拟目录结构,设置 MaxKeys 和 NextMarker
|
||||||
|
objectListing = _ossClient.ListObjects(new Aliyun.OSS.ListObjectsRequest(aliConfig.BucketName)
|
||||||
|
{
|
||||||
|
Prefix = prefix,
|
||||||
|
MaxKeys = 1000,
|
||||||
|
Marker = nextMarker
|
||||||
|
});
|
||||||
|
|
||||||
|
List<string> keys = objectListing.ObjectSummaries.Select(t => t.Key).ToList();
|
||||||
|
|
||||||
|
// 删除获取到的文件
|
||||||
|
if (keys.Count > 0)
|
||||||
|
{
|
||||||
|
_ossClient.DeleteObjects(new Aliyun.OSS.DeleteObjectsRequest(aliConfig.BucketName, keys, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置 NextMarker 以获取下一页的数据
|
||||||
|
nextMarker = objectListing.NextMarker;
|
||||||
|
|
||||||
|
} while (objectListing.IsTruncated);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Error: {ex.Message}");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (ObjectStoreServiceOptions.ObjectStoreUse == "MinIO")
|
||||||
|
{
|
||||||
|
var minIOConfig = ObjectStoreServiceOptions.MinIO;
|
||||||
|
|
||||||
|
|
||||||
|
var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.EndPoint}:{minIOConfig.Port}")
|
||||||
|
.WithCredentials(minIOConfig.AccessKeyId, minIOConfig.SecretAccessKey).WithSSL(minIOConfig.UseSSL)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
|
||||||
|
var listArgs = new ListObjectsArgs().WithBucket(minIOConfig.BucketName).WithPrefix(prefix).WithRecursive(true);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 创建一个空列表用于存储对象键
|
||||||
|
var objects = new List<string>();
|
||||||
|
|
||||||
|
// 使用 await foreach 来异步迭代对象列表
|
||||||
|
await foreach (var item in minioClient.ListObjectsEnumAsync(listArgs))
|
||||||
|
{
|
||||||
|
objects.Add(item.Key);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (objects.Count > 0)
|
||||||
|
{
|
||||||
|
var objArgs = new RemoveObjectsArgs()
|
||||||
|
.WithBucket(minIOConfig.BucketName)
|
||||||
|
.WithObjects(objects);
|
||||||
|
|
||||||
|
// 删除对象
|
||||||
|
await minioClient.RemoveObjectsAsync(objArgs);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS")
|
||||||
|
{
|
||||||
|
|
||||||
|
var awsConfig = ObjectStoreServiceOptions.AWS;
|
||||||
|
|
||||||
|
|
||||||
|
// 提供awsAccessKeyId和awsSecretAccessKey构造凭证
|
||||||
|
var credentials = new SessionAWSCredentials(AWSTempToken.AccessKeyId, AWSTempToken.SecretAccessKey, AWSTempToken.SessionToken);
|
||||||
|
|
||||||
|
//提供awsEndPoint(域名)进行访问配置
|
||||||
|
var clientConfig = new AmazonS3Config
|
||||||
|
{
|
||||||
|
RegionEndpoint = RegionEndpoint.USEast1,
|
||||||
|
UseHttp = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
|
||||||
|
|
||||||
|
// 列出指定前缀下的所有对象
|
||||||
|
var listObjectsRequest = new ListObjectsV2Request
|
||||||
|
{
|
||||||
|
BucketName = awsConfig.BucketName,
|
||||||
|
Prefix = prefix
|
||||||
|
};
|
||||||
|
|
||||||
|
var listObjectsResponse = await amazonS3Client.ListObjectsV2Async(listObjectsRequest);
|
||||||
|
|
||||||
|
if (listObjectsResponse.S3Objects.Count > 0)
|
||||||
|
{
|
||||||
|
// 准备删除请求
|
||||||
|
var deleteObjectsRequest = new Amazon.S3.Model.DeleteObjectsRequest
|
||||||
|
{
|
||||||
|
BucketName = awsConfig.BucketName,
|
||||||
|
Objects = new List<KeyVersion>()
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (var s3Object in listObjectsResponse.S3Objects)
|
||||||
|
{
|
||||||
|
deleteObjectsRequest.Objects.Add(new KeyVersion
|
||||||
|
{
|
||||||
|
Key = s3Object.Key
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量删除对象
|
||||||
|
var deleteObjectsResponse = await amazonS3Client.DeleteObjectsAsync(deleteObjectsRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new BusinessValidationFailedException("未定义的存储介质类型");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public ObjectStoreDTO GetObjectStoreTempToken()
|
||||||
|
{
|
||||||
|
|
||||||
|
var ossOptions = ObjectStoreServiceOptions.AliyunOSS;
|
||||||
|
|
||||||
|
if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS")
|
||||||
|
{
|
||||||
|
var client = new Client(new AlibabaCloud.OpenApiClient.Models.Config()
|
||||||
|
{
|
||||||
|
AccessKeyId = ossOptions.AccessKeyId,
|
||||||
|
AccessKeySecret = ossOptions.AccessKeySecret,
|
||||||
|
//AccessKeyId = "LTAI5tJV76pYX5yPg1N9QVE8",
|
||||||
|
//AccessKeySecret = "roRNLa9YG1of4pYruJGCNKBXEWTAWa",
|
||||||
|
|
||||||
|
Endpoint = "sts.cn-hangzhou.aliyuncs.com"
|
||||||
|
});
|
||||||
|
|
||||||
|
var assumeRoleRequest = new AlibabaCloud.SDK.Sts20150401.Models.AssumeRoleRequest();
|
||||||
|
// 将<YOUR_ROLE_SESSION_NAME>设置为自定义的会话名称,例如oss-role-session。
|
||||||
|
assumeRoleRequest.RoleSessionName = $"session-name-{NewId.NextGuid()}";
|
||||||
|
// 将<YOUR_ROLE_ARN>替换为拥有上传文件到指定OSS Bucket权限的RAM角色的ARN。
|
||||||
|
assumeRoleRequest.RoleArn = ossOptions.RoleArn;
|
||||||
|
//assumeRoleRequest.RoleArn = "acs:ram::1899121822495495:role/webdirect";
|
||||||
|
assumeRoleRequest.DurationSeconds = ossOptions.DurationSeconds;
|
||||||
|
var runtime = new AlibabaCloud.TeaUtil.Models.RuntimeOptions();
|
||||||
|
var response = client.AssumeRoleWithOptions(assumeRoleRequest, runtime);
|
||||||
|
var credentials = response.Body.Credentials;
|
||||||
|
|
||||||
|
var tempToken = new AliyunOSSTempToken()
|
||||||
|
{
|
||||||
|
AccessKeyId = credentials.AccessKeyId,
|
||||||
|
AccessKeySecret = credentials.AccessKeySecret,
|
||||||
|
|
||||||
|
//转为服务器时区,最后统一转为客户端时区
|
||||||
|
Expiration = TimeZoneInfo.ConvertTimeFromUtc(DateTime.Parse(credentials.Expiration), TimeZoneInfo.Local),
|
||||||
|
SecurityToken = credentials.SecurityToken,
|
||||||
|
|
||||||
|
|
||||||
|
Region = ossOptions.Region,
|
||||||
|
BucketName = ossOptions.BucketName,
|
||||||
|
EndPoint = ossOptions.EndPoint,
|
||||||
|
ViewEndpoint = ossOptions.ViewEndpoint,
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
AliyunOSSTempToken = tempToken;
|
||||||
|
|
||||||
|
return new ObjectStoreDTO() { ObjectStoreUse = ObjectStoreServiceOptions.ObjectStoreUse, AliyunOSS = tempToken };
|
||||||
|
}
|
||||||
|
else if (ObjectStoreServiceOptions.ObjectStoreUse == "MinIO")
|
||||||
|
{
|
||||||
|
return new ObjectStoreDTO() { ObjectStoreUse = ObjectStoreServiceOptions.ObjectStoreUse, MinIO = ObjectStoreServiceOptions.MinIO };
|
||||||
|
}
|
||||||
|
else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS")
|
||||||
|
{
|
||||||
|
var awsOptions = ObjectStoreServiceOptions.AWS;
|
||||||
|
|
||||||
|
//aws 临时凭证
|
||||||
|
// 创建 STS 客户端
|
||||||
|
var stsClient = new AmazonSecurityTokenServiceClient(awsOptions.AccessKeyId, awsOptions.SecretAccessKey);
|
||||||
|
|
||||||
|
// 使用 AssumeRole 请求临时凭证
|
||||||
|
var assumeRoleRequest = new AssumeRoleRequest
|
||||||
|
{
|
||||||
|
|
||||||
|
RoleArn = awsOptions.RoleArn, // 角色 ARN
|
||||||
|
RoleSessionName = $"session-name-{NewId.NextGuid()}",
|
||||||
|
DurationSeconds = awsOptions.DurationSeconds // 临时凭证有效期
|
||||||
|
};
|
||||||
|
|
||||||
|
var assumeRoleResponse = stsClient.AssumeRoleAsync(assumeRoleRequest).Result;
|
||||||
|
|
||||||
|
var credentials = assumeRoleResponse.Credentials;
|
||||||
|
|
||||||
|
var tempToken = new AWSTempToken()
|
||||||
|
{
|
||||||
|
AccessKeyId = credentials.AccessKeyId,
|
||||||
|
SecretAccessKey = credentials.SecretAccessKey,
|
||||||
|
SessionToken = credentials.SessionToken,
|
||||||
|
Expiration = credentials.Expiration,
|
||||||
|
Region = awsOptions.Region,
|
||||||
|
BucketName = awsOptions.BucketName,
|
||||||
|
EndPoint = awsOptions.EndPoint,
|
||||||
|
ViewEndpoint = awsOptions.ViewEndpoint,
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
AWSTempToken = tempToken;
|
||||||
|
return new ObjectStoreDTO() { ObjectStoreUse = ObjectStoreServiceOptions.ObjectStoreUse, AWS = tempToken };
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new BusinessValidationFailedException("未定义的存储介质类型");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft.AspNetCore": "Warning"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft.AspNetCore": "Warning"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"AllowedHosts": "*"
|
||||||
|
}
|
|
@ -7,28 +7,28 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="AlibabaCloud.SDK.Sts20150401" Version="1.1.4" />
|
<PackageReference Include="AlibabaCloud.SDK.Sts20150401" Version="1.1.5" />
|
||||||
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="10.0.0" />
|
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="10.0.0" />
|
||||||
<PackageReference Include="Aliyun.OSS.SDK.NetCore" Version="2.14.1" />
|
<PackageReference Include="Aliyun.OSS.SDK.NetCore" Version="2.14.1" />
|
||||||
<PackageReference Include="AWSSDK.S3" Version="3.7.405" />
|
<PackageReference Include="AWSSDK.S3" Version="3.7.416.8" />
|
||||||
<PackageReference Include="AWSSDK.SecurityToken" Version="3.7.400.36" />
|
<PackageReference Include="AWSSDK.SecurityToken" Version="3.7.401.81" />
|
||||||
<PackageReference Include="DistributedLock.Core" Version="1.0.7" />
|
<PackageReference Include="DistributedLock.Core" Version="1.0.8" />
|
||||||
<PackageReference Include="DistributedLock.SqlServer" Version="1.0.5" />
|
<PackageReference Include="DistributedLock.SqlServer" Version="1.0.6" />
|
||||||
<PackageReference Include="fo-dicom" Version="5.1.3" />
|
<PackageReference Include="fo-dicom" Version="5.2.1" />
|
||||||
<PackageReference Include="fo-dicom.Codecs" Version="5.15.1" />
|
<PackageReference Include="fo-dicom.Codecs" Version="5.16.1" />
|
||||||
<PackageReference Include="fo-dicom.Imaging.ImageSharp" Version="5.1.3" />
|
<PackageReference Include="fo-dicom.Imaging.ImageSharp" Version="5.2.1" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.10" />
|
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.10" />
|
||||||
<PackageReference Include="AutoMapper" Version="13.0.1" />
|
<PackageReference Include="AutoMapper" Version="13.0.1" />
|
||||||
<PackageReference Include="Minio" Version="6.0.3" />
|
<PackageReference Include="Minio" Version="6.0.4" />
|
||||||
<PackageReference Include="My.Extensions.Localization.Json" Version="3.3.0">
|
<PackageReference Include="My.Extensions.Localization.Json" Version="3.3.0">
|
||||||
<TreatAsUsed>true</TreatAsUsed>
|
<TreatAsUsed>true</TreatAsUsed>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Panda.DynamicWebApi" Version="1.2.2" />
|
<PackageReference Include="Panda.DynamicWebApi" Version="1.2.2" />
|
||||||
<PackageReference Include="Serilog.Enrichers.ClientInfo" Version="2.1.2" />
|
<PackageReference Include="Serilog.Enrichers.ClientInfo" Version="2.1.2" />
|
||||||
<PackageReference Include="Serilog.Extensions.Hosting" Version="8.0.0" />
|
<PackageReference Include="Serilog.Extensions.Hosting" Version="9.0.0" />
|
||||||
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
|
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
|
||||||
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
|
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.9.0" />
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="8.1.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
@ -126,16 +126,22 @@ builder.Services.Configure<ForwardedHeadersOptions>(options =>
|
||||||
ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
|
ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
builder.Services.AddFellowOakDicom().AddTranscoderManager<NativeTranscoderManager>()
|
||||||
|
//.AddTranscoderManager<FellowOakDicom.Imaging.NativeCodec.NativeTranscoderManager>()
|
||||||
|
.AddImageManager<ImageSharpImageManager>();
|
||||||
|
|
||||||
|
|
||||||
//Dicom影像渲染图片 跨平台
|
//Dicom影像渲染图片 跨平台
|
||||||
//builder.Services.AddDicomSetup();
|
//builder.Services.AddDicomSetup();
|
||||||
new DicomSetupBuilder()
|
//new DicomSetupBuilder()
|
||||||
.RegisterServices(s =>
|
// .RegisterServices(s =>
|
||||||
s.AddFellowOakDicom()
|
// s.AddFellowOakDicom()
|
||||||
.AddTranscoderManager<NativeTranscoderManager>()
|
// .AddTranscoderManager<NativeTranscoderManager>()
|
||||||
//.AddTranscoderManager<FellowOakDicom.Imaging.NativeCodec.NativeTranscoderManager>()
|
// //.AddTranscoderManager<FellowOakDicom.Imaging.NativeCodec.NativeTranscoderManager>()
|
||||||
.AddImageManager<ImageSharpImageManager>())
|
// .AddImageManager<ImageSharpImageManager>())
|
||||||
.SkipValidation()
|
// .SkipValidation()
|
||||||
.Build();
|
// .Build();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -211,6 +217,8 @@ else
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
DicomSetupBuilder.UseServiceProvider(app.Services);
|
||||||
|
|
||||||
var logger = app.Services.GetService<Microsoft.Extensions.Logging.ILogger<Program>>();
|
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);
|
var server = DicomServerFactory.Create<CStoreSCPService>(_configuration.GetSection("DicomSCPServiceConfig").GetValue<int>("ServerPort"), userState: app.Services,logger: logger);
|
||||||
|
|
|
@ -20,6 +20,16 @@
|
||||||
"environmentVariables": {
|
"environmentVariables": {
|
||||||
"ASPNETCORE_ENVIRONMENT": "Uat_IRC_SCP"
|
"ASPNETCORE_ENVIRONMENT": "Uat_IRC_SCP"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"US_Prod_IRC_SCP": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"dotnetRunMessages": true,
|
||||||
|
"launchBrowser": true,
|
||||||
|
"launchUrl": "swagger",
|
||||||
|
"applicationUrl": "http://localhost:6200",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "US_Prod_SCP"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,9 +76,10 @@ namespace IRaCIS.Core.SCP.Service
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
public CStoreSCPService(INetworkStream stream, Encoding fallbackEncoding, Microsoft.Extensions.Logging.ILogger log, DicomServiceDependencies dependencies)
|
public CStoreSCPService(INetworkStream stream, Encoding fallbackEncoding, Microsoft.Extensions.Logging.ILogger log, DicomServiceDependencies dependencies, IServiceProvider injectServiceProvider)
|
||||||
: base(stream, fallbackEncoding, log, dependencies)
|
: base(stream, fallbackEncoding, log, dependencies)
|
||||||
{
|
{
|
||||||
|
_serviceProvider = injectServiceProvider.CreateScope().ServiceProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -92,9 +93,7 @@ namespace IRaCIS.Core.SCP.Service
|
||||||
|
|
||||||
Log.Logger.Warning($"接收到来自{association.CallingAE}的连接");
|
Log.Logger.Warning($"接收到来自{association.CallingAE}的连接");
|
||||||
|
|
||||||
_serviceProvider = (IServiceProvider)this.UserState;
|
//_serviceProvider = (IServiceProvider)this.UserState;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var _trialDicomAERepository = _serviceProvider.GetService<IRepository<TrialDicomAE>>();
|
var _trialDicomAERepository = _serviceProvider.GetService<IRepository<TrialDicomAE>>();
|
||||||
|
|
||||||
|
@ -275,7 +274,7 @@ namespace IRaCIS.Core.SCP.Service
|
||||||
var _distributedLockProvider = _serviceProvider.GetService<IDistributedLockProvider>();
|
var _distributedLockProvider = _serviceProvider.GetService<IDistributedLockProvider>();
|
||||||
|
|
||||||
var storeRelativePath = string.Empty;
|
var storeRelativePath = string.Empty;
|
||||||
var ossFolderPath = $"{_trialId}/Image/PACS/{_trialSiteId}{studyInstanceUid}";
|
var ossFolderPath = $"{_trialId}/Image/PACS/{_trialSiteId}/{studyInstanceUid}";
|
||||||
|
|
||||||
|
|
||||||
long fileSize = 0;
|
long fileSize = 0;
|
||||||
|
|
|
@ -21,8 +21,8 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ConnectionStrings": {
|
"ConnectionStrings": {
|
||||||
"RemoteNew": "Server=us-prod-mssql-service,1433;Database=US_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
"RemoteNew": "Server=us-mssql-prod,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"
|
"Hangfire": "Server=us-mssql-prod,1433;Database=US_IRC_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
|
||||||
},
|
},
|
||||||
"DicomSCPServiceConfig": {
|
"DicomSCPServiceConfig": {
|
||||||
"CalledAEList": [
|
"CalledAEList": [
|
||||||
|
|
|
@ -19,6 +19,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IRaCIS.Core.Infrastructure"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IRC.Core.SCP", "IRC.Core.SCP\IRC.Core.SCP.csproj", "{ECD08F47-DC1A-484E-BB91-6CDDC8823CC5}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IRC.Core.SCP", "IRC.Core.SCP\IRC.Core.SCP.csproj", "{ECD08F47-DC1A-484E-BB91-6CDDC8823CC5}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IRC.Core.Dicom", "IRC.Core.Dicom\IRC.Core.Dicom.csproj", "{0545F0A5-D97B-4A47-92A6-A8A02A181322}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
@ -57,6 +59,10 @@ Global
|
||||||
{ECD08F47-DC1A-484E-BB91-6CDDC8823CC5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{ECD08F47-DC1A-484E-BB91-6CDDC8823CC5}.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.ActiveCfg = Release|Any CPU
|
||||||
{ECD08F47-DC1A-484E-BB91-6CDDC8823CC5}.Release|Any CPU.Build.0 = Release|Any CPU
|
{ECD08F47-DC1A-484E-BB91-6CDDC8823CC5}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{0545F0A5-D97B-4A47-92A6-A8A02A181322}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{0545F0A5-D97B-4A47-92A6-A8A02A181322}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{0545F0A5-D97B-4A47-92A6-A8A02A181322}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{0545F0A5-D97B-4A47-92A6-A8A02A181322}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
using Amazon.Auth.AccessControlPolicy;
|
using AlibabaCloud.SDK.Sts20150401;
|
||||||
|
using Amazon.Auth.AccessControlPolicy;
|
||||||
using Amazon.SecurityToken;
|
using Amazon.SecurityToken;
|
||||||
|
using AutoMapper;
|
||||||
using Azure.Core;
|
using Azure.Core;
|
||||||
using IdentityModel.Client;
|
using IdentityModel.Client;
|
||||||
using IdentityModel.OidcClient;
|
using IdentityModel.OidcClient;
|
||||||
|
@ -20,10 +22,12 @@ using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
using Org.BouncyCastle.Tls;
|
||||||
using RestSharp;
|
using RestSharp;
|
||||||
using RestSharp.Authenticators;
|
using RestSharp.Authenticators;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
|
@ -105,196 +109,6 @@ namespace IRaCIS.Api.Controllers
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// <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)
|
|
||||||
{
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
Guid userId = (Guid)loginUser.UserId;
|
|
||||||
|
|
||||||
//验证MFA 编码是否有问题 ,前端要拆开,自己调用验证的逻辑
|
|
||||||
//await _userService.VerifyMFACodeAsync(userId, loginUser.MFACode);
|
|
||||||
|
|
||||||
//var loginUser = await _userRepository.Where(u => u.UserName.Equals(userName) && u.Password == password).ProjectTo<UserBasicInfo>(_mapper.ConfigurationProvider).FirstOrDefaultAsync();
|
|
||||||
|
|
||||||
var basicInfo = await _userService.GetUserBasicInfo(userId, loginUser.Password);
|
|
||||||
|
|
||||||
var loginReturn = new LoginReturnDTO() { BasicInfo = basicInfo };
|
|
||||||
|
|
||||||
loginReturn.JWTStr = _tokenService.GetToken(IRaCISClaims.Create(loginReturn.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", 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;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
[AllowAnonymous]
|
[AllowAnonymous]
|
||||||
[HttpGet, Route("user/getPublicKey")]
|
[HttpGet, Route("user/getPublicKey")]
|
||||||
public IResponseOutput GetPublicKey([FromServices] IOptionsMonitor<IRCEncreptOption> _IRCEncreptOption)
|
public IResponseOutput GetPublicKey([FromServices] IOptionsMonitor<IRCEncreptOption> _IRCEncreptOption)
|
||||||
|
@ -309,18 +123,13 @@ namespace IRaCIS.Api.Controllers
|
||||||
[AllowAnonymous]
|
[AllowAnonymous]
|
||||||
public IResponseOutput ShareImage([FromServices] ITokenService _tokenService)
|
public IResponseOutput ShareImage([FromServices] ITokenService _tokenService)
|
||||||
{
|
{
|
||||||
var token = _tokenService.GetToken(IRaCISClaims.Create(new UserBasicInfo()
|
var token = _tokenService.GetToken(new UserTokenInfo()
|
||||||
{
|
{
|
||||||
Id = Guid.NewGuid(),
|
IdentityUserId = Guid.NewGuid(),
|
||||||
IsReviewer = false,
|
|
||||||
IsAdmin = false,
|
|
||||||
RealName = "Share001",
|
|
||||||
UserName = "Share001",
|
UserName = "Share001",
|
||||||
Sex = 0,
|
|
||||||
//UserType = "ShareType",
|
|
||||||
UserTypeEnum = UserTypeEnum.ShareImage,
|
UserTypeEnum = UserTypeEnum.ShareImage,
|
||||||
Code = "ShareCode001",
|
|
||||||
}));
|
});
|
||||||
return ResponseOutput.Ok("/showdicom?studyId=f7b67793-8155-0223-2f15-118f2642efb8&type=Share&token=" + token);
|
return ResponseOutput.Ok("/showdicom?studyId=f7b67793-8155-0223-2f15-118f2642efb8&type=Share&token=" + token);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -375,11 +184,74 @@ namespace IRaCIS.Api.Controllers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#region 老项目依赖
|
||||||
|
|
||||||
|
[HttpGet("user/GenerateSTS")]
|
||||||
|
public IResponseOutput GenerateSTS([FromServices] IOptionsMonitor<ObjectStoreServiceOptions> options)
|
||||||
|
{
|
||||||
|
|
||||||
|
var ossOptions = options.CurrentValue.AliyunOSS;
|
||||||
|
|
||||||
|
var client = new Client(new AlibabaCloud.OpenApiClient.Models.Config()
|
||||||
|
{
|
||||||
|
AccessKeyId = ossOptions.AccessKeyId,
|
||||||
|
AccessKeySecret = ossOptions.AccessKeySecret,
|
||||||
|
Endpoint = "sts.cn-hangzhou.aliyuncs.com"
|
||||||
|
});
|
||||||
|
|
||||||
|
var assumeRoleRequest = new AlibabaCloud.SDK.Sts20150401.Models.AssumeRoleRequest();
|
||||||
|
// 将<YOUR_ROLE_SESSION_NAME>设置为自定义的会话名称,例如oss-role-session。
|
||||||
|
assumeRoleRequest.RoleSessionName = $"session-name-{NewId.NextGuid()}";
|
||||||
|
// 将<YOUR_ROLE_ARN>替换为拥有上传文件到指定OSS Bucket权限的RAM角色的ARN。
|
||||||
|
assumeRoleRequest.RoleArn = ossOptions.RoleArn;
|
||||||
|
//assumeRoleRequest.RoleArn = "acs:ram::1899121822495495:role/webdirect";
|
||||||
|
assumeRoleRequest.DurationSeconds = ossOptions.DurationSeconds;
|
||||||
|
var runtime = new AlibabaCloud.TeaUtil.Models.RuntimeOptions();
|
||||||
|
var response = client.AssumeRoleWithOptions(assumeRoleRequest, runtime);
|
||||||
|
var credentials = response.Body.Credentials;
|
||||||
|
|
||||||
|
var tempToken = new AliyunOSSTempToken()
|
||||||
|
{
|
||||||
|
AccessKeyId = credentials.AccessKeyId,
|
||||||
|
AccessKeySecret = credentials.AccessKeySecret,
|
||||||
|
|
||||||
|
//转为服务器时区,最后统一转为客户端时区
|
||||||
|
Expiration = TimeZoneInfo.ConvertTimeFromUtc(DateTime.Parse(credentials.Expiration), TimeZoneInfo.Local),
|
||||||
|
SecurityToken = credentials.SecurityToken,
|
||||||
|
|
||||||
|
|
||||||
|
Region = ossOptions.Region,
|
||||||
|
BucketName = ossOptions.BucketName,
|
||||||
|
EndPoint = ossOptions.EndPoint,
|
||||||
|
ViewEndpoint = ossOptions.ViewEndpoint,
|
||||||
|
PreviewEndpoint = ossOptions.PreviewEndpoint
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
// 返回STS令牌信息给前端
|
||||||
|
var stsToken = new
|
||||||
|
{
|
||||||
|
AccessKeyId = credentials.AccessKeyId,
|
||||||
|
AccessKeySecret = credentials.AccessKeySecret,
|
||||||
|
SecurityToken = credentials.SecurityToken,
|
||||||
|
Expiration = credentials.Expiration,
|
||||||
|
|
||||||
|
Region = ossOptions.Region,
|
||||||
|
BucketName = ossOptions.BucketName,
|
||||||
|
ViewEndpoint = ossOptions.ViewEndpoint,
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
return ResponseOutput.Ok(stsToken);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
[HttpGet("User/UserRedirect")]
|
[HttpGet("User/UserRedirect")]
|
||||||
[AllowAnonymous]
|
[AllowAnonymous]
|
||||||
public async Task<IActionResult> UserRedirect([FromServices] IRepository<User> _userRepository, string url, [FromServices] ILogger<ExtraController> _logger)
|
public async Task<IActionResult> UserRedirect([FromServices] IRepository<IdentityUser> _useRepository, string url, [FromServices] ILogger<ExtraController> _logger, [FromServices] ITokenService _tokenService)
|
||||||
{
|
{
|
||||||
|
|
||||||
var decodeUrl = System.Web.HttpUtility.UrlDecode(url);
|
var decodeUrl = System.Web.HttpUtility.UrlDecode(url);
|
||||||
|
@ -394,10 +266,22 @@ namespace IRaCIS.Api.Controllers
|
||||||
|
|
||||||
var errorUrl = domainStrList[0] + "//" + domainStrList[2] + "/error";
|
var errorUrl = domainStrList[0] + "//" + domainStrList[2] + "/error";
|
||||||
|
|
||||||
|
if (lang == "zh")
|
||||||
if (!await _userRepository.AnyAsync(t => t.Id == Guid.Parse(userId) && t.EmailToken == token && t.IsFirstAdd))
|
|
||||||
{
|
{
|
||||||
decodeUrl = errorUrl + $"?lang={lang}&ErrorMessage={System.Web.HttpUtility.UrlEncode(lang == "zh" ? "您的初始化链接已过期" : "Error!The initialization link has expired. Return")} ";
|
CultureInfo.CurrentCulture = new CultureInfo(StaticData.CultureInfo.zh_CN);
|
||||||
|
CultureInfo.CurrentUICulture = new CultureInfo(StaticData.CultureInfo.zh_CN);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CultureInfo.CurrentCulture = new CultureInfo(StaticData.CultureInfo.en_US);
|
||||||
|
CultureInfo.CurrentUICulture = new CultureInfo(StaticData.CultureInfo.en_US);
|
||||||
|
}
|
||||||
|
|
||||||
|
var isExpire = _tokenService.IsTokenExpired(token);
|
||||||
|
|
||||||
|
if (!await _useRepository.AnyAsync(t => t.Id == Guid.Parse(userId) && t.EmailToken == token && t.IsFirstAdd) || isExpire)
|
||||||
|
{
|
||||||
|
decodeUrl = errorUrl + $"?lang={lang}&ErrorMessage={System.Web.HttpUtility.UrlEncode(I18n.T("UserRedirect_InitializationLinkExpire"))} ";
|
||||||
}
|
}
|
||||||
|
|
||||||
return Redirect(decodeUrl);
|
return Redirect(decodeUrl);
|
||||||
|
|
|
@ -51,30 +51,30 @@ namespace IRaCIS.Core.API.Controllers.Special
|
||||||
[TrialGlobalLimit( "AddOrUpdateTrial", "BeforeOngoingCantOpt", "AfterStopCannNotOpt" )]
|
[TrialGlobalLimit( "AddOrUpdateTrial", "BeforeOngoingCantOpt", "AfterStopCannNotOpt" )]
|
||||||
public async Task<IResponseOutput<Trial>> AddOrUpdateTrial(TrialCommand param)
|
public async Task<IResponseOutput<Trial>> AddOrUpdateTrial(TrialCommand param)
|
||||||
{
|
{
|
||||||
var userId = Guid.Parse(User.FindFirst("id").Value);
|
//var userId = Guid.Parse(User.FindFirst("id").Value);
|
||||||
var result = await _trialService.AddOrUpdateTrial(param);
|
var result = await _trialService.AddOrUpdateTrial(param);
|
||||||
|
|
||||||
if (_trialService.TrialExpeditedChange)
|
//if (_trialService.TrialExpeditedChange)
|
||||||
{
|
//{
|
||||||
var needCalReviewerIds = await _trialService.GetTrialEnrollmentReviewerIds(param.Id.Value);
|
// var needCalReviewerIds = await _trialService.GetTrialEnrollmentReviewerIds(param.Id.Value);
|
||||||
var calcList = await _calculateService.GetNeedCalculateReviewerList(Guid.Empty, string.Empty);
|
// var calcList = await _calculateService.GetNeedCalculateReviewerList(Guid.Empty, string.Empty);
|
||||||
|
|
||||||
calcList.ForEach(t =>
|
// calcList.ForEach(t =>
|
||||||
{
|
// {
|
||||||
if (needCalReviewerIds.Contains(t.DoctorId))
|
// if (needCalReviewerIds.Contains(t.DoctorId))
|
||||||
{
|
// {
|
||||||
_calculateService.CalculateMonthlyPayment(new CalculateDoctorAndMonthDTO()
|
// _calculateService.CalculateMonthlyPayment(new CalculateDoctorAndMonthDTO()
|
||||||
{
|
// {
|
||||||
NeedCalculateReviewers = new List<Guid>()
|
// NeedCalculateReviewers = new List<Guid>()
|
||||||
{
|
// {
|
||||||
t.DoctorId
|
// t.DoctorId
|
||||||
},
|
// },
|
||||||
CalculateMonth = DateTime.Parse(t.YearMonth)
|
// CalculateMonth = DateTime.Parse(t.YearMonth)
|
||||||
}, User.FindFirst("id").Value);
|
// }, User.FindFirst("id").Value);
|
||||||
|
|
||||||
}
|
// }
|
||||||
});
|
// });
|
||||||
}
|
//}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -522,10 +522,10 @@ namespace IRaCIS.Core.API.Controllers
|
||||||
[TrialGlobalLimit( "AfterStopCannNotOpt" )]
|
[TrialGlobalLimit( "AfterStopCannNotOpt" )]
|
||||||
[UnitOfWork]
|
[UnitOfWork]
|
||||||
|
|
||||||
public async Task<IResponseOutput> ConfirmReReading(DataInspectionDto<ConfirmReReadingCommand> opt, [FromServices] IVisitTaskHelpeService _visitTaskCommonService, [FromServices] IVisitTaskService _visitTaskService)
|
public async Task<IResponseOutput> ConfirmReReading(DataInspectionDto<ConfirmReReadingCommand> opt, [FromServices] IVisitTaskService _visitTaskService)
|
||||||
{
|
{
|
||||||
var singId = await _inspectionService.RecordSing(opt.SignInfo);
|
var singId = await _inspectionService.RecordSing(opt.SignInfo);
|
||||||
var result = await _visitTaskService.ConfirmReReading(opt.Data, _visitTaskCommonService);
|
var result = await _visitTaskService.ConfirmReReading(opt.Data);
|
||||||
await _inspectionService.CompletedSign(singId, result);
|
await _inspectionService.CompletedSign(singId, result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -297,7 +297,7 @@ namespace IRaCIS.Core.API.Controllers
|
||||||
[HttpPost, Route("Study/ArchiveStudy")]
|
[HttpPost, Route("Study/ArchiveStudy")]
|
||||||
[DisableFormValueModelBinding]
|
[DisableFormValueModelBinding]
|
||||||
[DisableRequestSizeLimit]
|
[DisableRequestSizeLimit]
|
||||||
[TrialGlobalLimit( "AfterStopCannNotOpt" )]
|
[TrialGlobalLimit("AfterStopCannNotOpt")]
|
||||||
public async Task<IResponseOutput> ArchiveStudyNew(Guid trialId, Guid subjectVisitId, string studyInstanceUid, Guid? abandonStudyId, Guid studyMonitorId,
|
public async Task<IResponseOutput> ArchiveStudyNew(Guid trialId, Guid subjectVisitId, string studyInstanceUid, Guid? abandonStudyId, Guid studyMonitorId,
|
||||||
[FromServices] ILogger<UploadDownLoadController> _logger,
|
[FromServices] ILogger<UploadDownLoadController> _logger,
|
||||||
[FromServices] IStudyService _studyService,
|
[FromServices] IStudyService _studyService,
|
||||||
|
@ -370,7 +370,7 @@ namespace IRaCIS.Core.API.Controllers
|
||||||
//await _uploadHub.Clients.All.ReceivProgressAsync(archiveStudyCommand.StudyInstanceUid, receivedCount);
|
//await _uploadHub.Clients.All.ReceivProgressAsync(archiveStudyCommand.StudyInstanceUid, receivedCount);
|
||||||
|
|
||||||
|
|
||||||
await _uploadHub.Clients.User(_userInfo.Id.ToString()).ReceivProgressAsync(archiveStudyCommand.StudyInstanceUid, receivedCount);
|
await _uploadHub.Clients.User(_userInfo.UserRoleId.ToString()).ReceivProgressAsync(archiveStudyCommand.StudyInstanceUid, receivedCount);
|
||||||
|
|
||||||
archiveResult.ReceivedFileCount = receivedCount;
|
archiveResult.ReceivedFileCount = receivedCount;
|
||||||
|
|
||||||
|
@ -452,7 +452,7 @@ namespace IRaCIS.Core.API.Controllers
|
||||||
/// <param name="_studyMonitorRepository"></param>
|
/// <param name="_studyMonitorRepository"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost, Route("Study/PreArchiveStudy")]
|
[HttpPost, Route("Study/PreArchiveStudy")]
|
||||||
[TrialGlobalLimit( "AfterStopCannNotOpt" )]
|
[TrialGlobalLimit("AfterStopCannNotOpt")]
|
||||||
public async Task<IResponseOutput> PreArchiveStudy(PreArchiveStudyCommand preArchiveStudyCommand,
|
public async Task<IResponseOutput> PreArchiveStudy(PreArchiveStudyCommand preArchiveStudyCommand,
|
||||||
[FromServices] IStudyService _studyService,
|
[FromServices] IStudyService _studyService,
|
||||||
[FromServices] IRepository<StudyMonitor> _studyMonitorRepository)
|
[FromServices] IRepository<StudyMonitor> _studyMonitorRepository)
|
||||||
|
@ -488,7 +488,7 @@ namespace IRaCIS.Core.API.Controllers
|
||||||
/// <param name="_noneDicomStudyFileRepository"></param>
|
/// <param name="_noneDicomStudyFileRepository"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost("NoneDicomStudy/UploadNoneDicomFile")]
|
[HttpPost("NoneDicomStudy/UploadNoneDicomFile")]
|
||||||
[TrialGlobalLimit( "AfterStopCannNotOpt" )]
|
[TrialGlobalLimit("AfterStopCannNotOpt")]
|
||||||
public async Task<IResponseOutput> UploadNoneDicomFile(UploadNoneDicomFileCommand incommand,
|
public async Task<IResponseOutput> UploadNoneDicomFile(UploadNoneDicomFileCommand incommand,
|
||||||
[FromServices] IRepository<NoneDicomStudy> _noneDicomStudyRepository,
|
[FromServices] IRepository<NoneDicomStudy> _noneDicomStudyRepository,
|
||||||
[FromServices] IRepository<StudyMonitor> _studyMonitorRepository,
|
[FromServices] IRepository<StudyMonitor> _studyMonitorRepository,
|
||||||
|
@ -527,7 +527,7 @@ namespace IRaCIS.Core.API.Controllers
|
||||||
}
|
}
|
||||||
var uploadFinishedTime = DateTime.Now;
|
var uploadFinishedTime = DateTime.Now;
|
||||||
|
|
||||||
var noneDicomStudy = await _noneDicomStudyRepository.FirstOrDefaultAsync((t => t.Id == noneDicomStudyId));
|
var noneDicomStudy = await _noneDicomStudyRepository.FirstOrDefaultAsync(t => t.Id == noneDicomStudyId,true);
|
||||||
|
|
||||||
noneDicomStudy.FileCount = noneDicomStudy.FileCount + (incommand.VisitTaskId != null ? 0 : incommand.UploadedFileList.Count);
|
noneDicomStudy.FileCount = noneDicomStudy.FileCount + (incommand.VisitTaskId != null ? 0 : incommand.UploadedFileList.Count);
|
||||||
|
|
||||||
|
@ -560,7 +560,7 @@ namespace IRaCIS.Core.API.Controllers
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
/// <exception cref="BusinessValidationFailedException"></exception>
|
/// <exception cref="BusinessValidationFailedException"></exception>
|
||||||
[HttpPost("QCOperation/UploadVisitCheckExcel/{trialId:guid}")]
|
[HttpPost("QCOperation/UploadVisitCheckExcel/{trialId:guid}")]
|
||||||
[TrialGlobalLimit( "AfterStopCannNotOpt" )]
|
[TrialGlobalLimit("AfterStopCannNotOpt")]
|
||||||
|
|
||||||
public async Task<IResponseOutput> UploadVisitCheckExcel(Guid trialId, [FromServices] IOSSService oSSService, [FromServices] IRepository<InspectionFile> _inspectionFileRepository)
|
public async Task<IResponseOutput> UploadVisitCheckExcel(Guid trialId, [FromServices] IOSSService oSSService, [FromServices] IRepository<InspectionFile> _inspectionFileRepository)
|
||||||
{
|
{
|
||||||
|
@ -850,11 +850,18 @@ namespace IRaCIS.Core.API.Controllers
|
||||||
throw new BusinessValidationFailedException(_localizer["UploadDownLoad_InvalidCenters"]);
|
throw new BusinessValidationFailedException(_localizer["UploadDownLoad_InvalidCenters"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (excelList.GroupBy(t => new { t.TrialSiteCode, t.UserTypeStr, t.Email }).Any(g => g.Count() > 1))
|
foreach (var item in excelList.GroupBy(t => t.Email.Trim()))
|
||||||
{
|
{
|
||||||
// 同一邮箱,同一用户类型,只能生成一个账户,请核查Excel数据
|
var itemList = item.ToList();
|
||||||
|
|
||||||
throw new BusinessValidationFailedException(_localizer["UploadDownLoad_CheckDuplicateAccounts"]);
|
var first = item.First();
|
||||||
|
|
||||||
|
if (itemList.Any(t => t.Email.Trim() != first.Email.Trim() || t.Phone.Trim() != first.Phone.Trim() || t.OrganizationName.Trim() != first.OrganizationName.Trim() || t.FirstName.Trim() != first.FirstName.Trim() || t.LastName.Trim() != first.LastName.Trim()))
|
||||||
|
{
|
||||||
|
//同一邮箱,用户信息应该保持一致!
|
||||||
|
|
||||||
|
throw new BusinessValidationFailedException(_localizer["UploadDownLoad_CheckDuplicateAccounts"]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (excelList.Any(t => !t.Email.Contains("@")))
|
if (excelList.Any(t => !t.Email.Contains("@")))
|
||||||
|
@ -879,7 +886,7 @@ namespace IRaCIS.Core.API.Controllers
|
||||||
|
|
||||||
foreach (var item in excelList)
|
foreach (var item in excelList)
|
||||||
{
|
{
|
||||||
switch (item.UserTypeStr.ToUpper())
|
switch (item.UserTypeStr.Trim().ToUpper())
|
||||||
{
|
{
|
||||||
case "CRC":
|
case "CRC":
|
||||||
|
|
||||||
|
@ -896,7 +903,7 @@ namespace IRaCIS.Core.API.Controllers
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
item.TrialSiteId = siteList.FirstOrDefault(t => t.TrialSiteCode.ToUpper() == item.TrialSiteCode.ToUpper()).TrialSiteId;
|
item.TrialSiteId = siteList.FirstOrDefault(t => t.TrialSiteCode.Trim().ToUpper() == item.TrialSiteCode.Trim().ToUpper()).TrialSiteId;
|
||||||
}
|
}
|
||||||
|
|
||||||
var list = excelList.Where(t => t.UserTypeEnum == UserTypeEnum.ClinicalResearchCoordinator || t.UserTypeEnum == UserTypeEnum.CRA).ToList();
|
var list = excelList.Where(t => t.UserTypeEnum == UserTypeEnum.ClinicalResearchCoordinator || t.UserTypeEnum == UserTypeEnum.CRA).ToList();
|
||||||
|
|
|
@ -20,6 +20,8 @@ namespace IRaCIS.Core.API.HostService;
|
||||||
|
|
||||||
public class HangfireHostService(IRecurringMessageScheduler _recurringMessageScheduler,
|
public class HangfireHostService(IRecurringMessageScheduler _recurringMessageScheduler,
|
||||||
IRepository<TrialEmailNoticeConfig> _trialEmailNoticeConfigRepository,
|
IRepository<TrialEmailNoticeConfig> _trialEmailNoticeConfigRepository,
|
||||||
|
|
||||||
|
IRepository<EmailNoticeConfig> _emailNoticeConfigrepository,
|
||||||
IMediator _mediator,
|
IMediator _mediator,
|
||||||
ILogger<HangfireHostService> _logger) : IHostedService
|
ILogger<HangfireHostService> _logger) : IHostedService
|
||||||
{
|
{
|
||||||
|
@ -55,6 +57,20 @@ public class HangfireHostService(IRecurringMessageScheduler _recurringMessageSch
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 系统邮件定时任务
|
||||||
|
var systemTaskInfoList = await _emailNoticeConfigrepository.Where(t => t.EmailCron != string.Empty && t.IsAutoSend)
|
||||||
|
.Select(t => new { t.Id, t.Code, t.EmailCron, t.BusinessScenarioEnum, })
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
foreach (var task in systemTaskInfoList)
|
||||||
|
{
|
||||||
|
//利用主键作为任务Id
|
||||||
|
var jobId = $"{task.Id}_({task.BusinessScenarioEnum})";
|
||||||
|
|
||||||
|
HangfireJobHelper.AddOrUpdateTimingCronJob(jobId, task.BusinessScenarioEnum, task.EmailCron);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//await _recurringMessageScheduler.ScheduleRecurringPublish(new QCImageQuestionSchedule() { CronExpression = "0/3 * * * * ? " }, new MasstransiTestCommand { value = "message at " + DateTime.Now.ToString() });
|
//await _recurringMessageScheduler.ScheduleRecurringPublish(new QCImageQuestionSchedule() { CronExpression = "0/3 * * * * ? " }, new MasstransiTestCommand { value = "message at " + DateTime.Now.ToString() });
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -69,24 +69,23 @@
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="AspNetCoreRateLimit" Version="5.0.0" />
|
<PackageReference Include="AspNetCoreRateLimit" Version="5.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.14" />
|
||||||
|
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.10">
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.15">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="ConfigMapFileProvider" Version="2.0.1" />
|
<PackageReference Include="ConfigMapFileProvider" Version="2.0.1" />
|
||||||
<PackageReference Include="Hangfire.AspNetCore" Version="1.8.14" />
|
<PackageReference Include="Hangfire.AspNetCore" Version="1.8.18" />
|
||||||
<PackageReference Include="Hangfire.Dashboard.BasicAuthorization" Version="1.0.2" />
|
<PackageReference Include="Hangfire.Dashboard.BasicAuthorization" Version="1.0.2" />
|
||||||
<PackageReference Include="Hangfire.InMemory" Version="1.0.0" />
|
<PackageReference Include="Hangfire.InMemory" Version="1.0.0" />
|
||||||
<PackageReference Include="Hangfire.SqlServer" Version="1.8.14" />
|
<PackageReference Include="Hangfire.SqlServer" Version="1.8.18" />
|
||||||
<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.10" />
|
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.10" />
|
||||||
<PackageReference Include="Serilog.AspNetCore" Version="8.0.3" />
|
<PackageReference Include="Serilog.AspNetCore" Version="9.0.0" />
|
||||||
<PackageReference Include="Serilog.Formatting.Compact" Version="3.0.0" />
|
<PackageReference Include="Serilog.Formatting.Compact" Version="3.0.0" />
|
||||||
<PackageReference Include="Serilog.Sinks.Email" Version="4.0.0" />
|
<PackageReference Include="Serilog.Sinks.Email" Version="4.0.0" />
|
||||||
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
|
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="6.9.0" />
|
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="8.1.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -114,6 +113,12 @@
|
||||||
</Content>
|
</Content>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Update="Resources\GeoLite2-City.mmdb">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ProjectExtensions>
|
<ProjectExtensions>
|
||||||
<VisualStudio>
|
<VisualStudio>
|
||||||
<UserProperties properties_4launchsettings_1json__JsonSchema="" />
|
<UserProperties properties_4launchsettings_1json__JsonSchema="" />
|
||||||
|
|
|
@ -34,9 +34,6 @@
|
||||||
<param name="doctorId"></param>
|
<param name="doctorId"></param>
|
||||||
<returns></returns>
|
<returns></returns>
|
||||||
</member>
|
</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)">
|
|
||||||
<summary> 系统用户登录接口[New] </summary>
|
|
||||||
</member>
|
|
||||||
<member name="M:IRaCIS.Api.Controllers.ExtraController.OAuthCallBack(System.String,System.String)">
|
<member name="M:IRaCIS.Api.Controllers.ExtraController.OAuthCallBack(System.String,System.String)">
|
||||||
<summary>
|
<summary>
|
||||||
回调到前端,前端调用后端的接口
|
回调到前端,前端调用后端的接口
|
||||||
|
@ -284,7 +281,7 @@
|
||||||
</summary>
|
</summary>
|
||||||
<returns></returns>
|
<returns></returns>
|
||||||
</member>
|
</member>
|
||||||
<member name="M:IRaCIS.Core.API.Controllers.InspectionController.ConfirmReReading(IRaCIS.Core.Application.Service.Inspection.DTO.DataInspectionDto{IRaCIS.Core.Application.ViewModel.ConfirmReReadingCommand},IRaCIS.Core.Application.Service.IVisitTaskHelpeService,IRaCIS.Core.Application.Service.IVisitTaskService)">
|
<member name="M:IRaCIS.Core.API.Controllers.InspectionController.ConfirmReReading(IRaCIS.Core.Application.Service.Inspection.DTO.DataInspectionDto{IRaCIS.Core.Application.ViewModel.ConfirmReReadingCommand},IRaCIS.Core.Application.Service.IVisitTaskService)">
|
||||||
<summary>
|
<summary>
|
||||||
重阅同意
|
重阅同意
|
||||||
</summary>
|
</summary>
|
||||||
|
|
|
@ -7,7 +7,6 @@ using IRaCIS.Core.Application.Service;
|
||||||
using IRaCIS.Core.Application.Service.BusinessFilter;
|
using IRaCIS.Core.Application.Service.BusinessFilter;
|
||||||
using IRaCIS.Core.Infra.EFCore;
|
using IRaCIS.Core.Infra.EFCore;
|
||||||
using IRaCIS.Core.Infrastructure.Extention;
|
using IRaCIS.Core.Infrastructure.Extention;
|
||||||
using LogDashboard;
|
|
||||||
using MassTransit;
|
using MassTransit;
|
||||||
using MassTransit.NewIdProviders;
|
using MassTransit.NewIdProviders;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
@ -37,6 +36,11 @@ var config = new ConfigurationBuilder()
|
||||||
|
|
||||||
var enviromentName = config["ASPNETCORE_ENVIRONMENT"];
|
var enviromentName = config["ASPNETCORE_ENVIRONMENT"];
|
||||||
|
|
||||||
|
var openSwaggerStr = config["ASPNETCORE_OpenSwagger"];
|
||||||
|
|
||||||
|
var isOpenSwagger= openSwaggerStr == null|| openSwaggerStr?.ToLower()=="true";
|
||||||
|
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(enviromentName))
|
if (string.IsNullOrWhiteSpace(enviromentName))
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -111,8 +115,13 @@ builder.Services.AddAutoMapperSetup();
|
||||||
builder.Services.AddEFSetup(_configuration, enviromentName);
|
builder.Services.AddEFSetup(_configuration, enviromentName);
|
||||||
//Http 响应压缩
|
//Http 响应压缩
|
||||||
builder.Services.AddResponseCompressionSetup();
|
builder.Services.AddResponseCompressionSetup();
|
||||||
//Swagger Api 文档
|
|
||||||
builder.Services.AddSwaggerSetup();
|
if (isOpenSwagger)
|
||||||
|
{
|
||||||
|
//Swagger Api 文档
|
||||||
|
builder.Services.AddSwaggerSetup();
|
||||||
|
}
|
||||||
|
|
||||||
//JWT Token 验证
|
//JWT Token 验证
|
||||||
builder.Services.AddJWTAuthSetup(_configuration);
|
builder.Services.AddJWTAuthSetup(_configuration);
|
||||||
|
|
||||||
|
@ -126,7 +135,7 @@ builder.Services.AddFusionCache();
|
||||||
builder.Services.AddhangfireSetup(_configuration);
|
builder.Services.AddhangfireSetup(_configuration);
|
||||||
|
|
||||||
//Serilog 日志可视化 LogDashboard日志
|
//Serilog 日志可视化 LogDashboard日志
|
||||||
builder.Services.AddLogDashboardSetup();
|
//builder.Services.AddLogDashboardSetup();
|
||||||
|
|
||||||
//Dicom影像渲染图片 跨平台
|
//Dicom影像渲染图片 跨平台
|
||||||
builder.Services.AddDicomSetup();
|
builder.Services.AddDicomSetup();
|
||||||
|
@ -211,13 +220,18 @@ app.UseResponseCompression();
|
||||||
app.UseStaticFiles();
|
app.UseStaticFiles();
|
||||||
|
|
||||||
//LogDashboard
|
//LogDashboard
|
||||||
app.UseLogDashboard("/LogDashboard");
|
//app.UseLogDashboard("/LogDashboard");
|
||||||
|
|
||||||
//hangfire
|
//hangfire
|
||||||
app.UseHangfireConfig(env);
|
app.UseHangfireConfig(env);
|
||||||
|
|
||||||
// Swagger
|
// Swagger
|
||||||
SwaggerSetup.Configure(app, env);
|
|
||||||
|
if (isOpenSwagger)
|
||||||
|
{
|
||||||
|
SwaggerSetup.Configure(app, env);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//serilog 记录请求的用户信息
|
//serilog 记录请求的用户信息
|
||||||
app.UseSerilogConfig(env);
|
app.UseSerilogConfig(env);
|
||||||
|
|
|
@ -20,7 +20,8 @@
|
||||||
"commandName": "Project",
|
"commandName": "Project",
|
||||||
"launchBrowser": true,
|
"launchBrowser": true,
|
||||||
"environmentVariables": {
|
"environmentVariables": {
|
||||||
"ASPNETCORE_ENVIRONMENT": "Test_IRC"
|
"ASPNETCORE_ENVIRONMENT": "Test_IRC",
|
||||||
|
"ASPNETCORE_OpenSwagger": "true"
|
||||||
},
|
},
|
||||||
"applicationUrl": "http://localhost:6100"
|
"applicationUrl": "http://localhost:6100"
|
||||||
},
|
},
|
||||||
|
@ -69,6 +70,14 @@
|
||||||
"ASPNETCORE_ENVIRONMENT": "US_Uat_IRC"
|
"ASPNETCORE_ENVIRONMENT": "US_Uat_IRC"
|
||||||
},
|
},
|
||||||
"applicationUrl": "http://localhost:6100"
|
"applicationUrl": "http://localhost:6100"
|
||||||
|
},
|
||||||
|
"IRaCIS.US_Prod_IRC": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"launchBrowser": true,
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "US_Prod_IRC"
|
||||||
|
},
|
||||||
|
"applicationUrl": "http://localhost:6100"
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 73 MiB |
|
@ -17,7 +17,7 @@ namespace IRaCIS.Core.API
|
||||||
{
|
{
|
||||||
public virtual string GetUserId(HubConnectionContext connection)
|
public virtual string GetUserId(HubConnectionContext connection)
|
||||||
{
|
{
|
||||||
return connection.User?.FindFirst(JwtIRaCISClaimType.Id)?.Value!;
|
return connection.User?.FindFirst(JwtIRaCISClaimType.IdentityUserId)?.Value!;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
using LogDashboard;
|
//using LogDashboard;
|
||||||
using LogDashboard.Authorization;
|
//using LogDashboard.Authorization;
|
||||||
|
|
||||||
namespace IRaCIS.Core.API.Filter
|
//namespace IRaCIS.Core.API.Filter
|
||||||
{
|
//{
|
||||||
|
|
||||||
public class LogDashBoardAuthFilter : ILogDashboardAuthorizationFilter
|
// public class LogDashBoardAuthFilter : ILogDashboardAuthorizationFilter
|
||||||
{
|
// {
|
||||||
//在此可以利用 本系统的UerTypeEnum 判断
|
// //在此可以利用 本系统的UerTypeEnum 判断
|
||||||
public bool Authorization(LogDashboardContext context)
|
// public bool Authorization(LogDashboardContext context)
|
||||||
{
|
// {
|
||||||
return context.HttpContext.User.Identity.IsAuthenticated;
|
// return context.HttpContext.User.Identity.IsAuthenticated;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
//}
|
|
@ -38,7 +38,7 @@ namespace IRaCIS.Core.API
|
||||||
diagnosticContext.Set("QueryString", request.QueryString.Value);
|
diagnosticContext.Set("QueryString", request.QueryString.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
diagnosticContext.Set("FullName", httpContext?.User?.FindFirst(JwtIRaCISClaimType.RealName)?.Value);
|
diagnosticContext.Set("FullName", httpContext?.User?.FindFirst(JwtIRaCISClaimType.FullName)?.Value);
|
||||||
|
|
||||||
diagnosticContext.Set("UserType", httpContext?.User?.FindFirst(JwtIRaCISClaimType.UserTypeShortName)?.Value);
|
diagnosticContext.Set("UserType", httpContext?.User?.FindFirst(JwtIRaCISClaimType.UserTypeShortName)?.Value);
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using EntityFramework.Exceptions.SqlServer;
|
using EntityFramework.Exceptions.SqlServer;
|
||||||
using IRaCIS.Core.Application.Triggers;
|
using IRaCIS.Core.Application.Triggers;
|
||||||
|
using IRaCIS.Core.Application.Triggers.AfterSaveTrigger;
|
||||||
using IRaCIS.Core.Domain.Share;
|
using IRaCIS.Core.Domain.Share;
|
||||||
using IRaCIS.Core.Infra.EFCore;
|
using IRaCIS.Core.Infra.EFCore;
|
||||||
using IRaCIS.Core.Infra.EFCore.Interceptor;
|
using IRaCIS.Core.Infra.EFCore.Interceptor;
|
||||||
|
@ -80,6 +81,11 @@ namespace IRaCIS.Core.API
|
||||||
triggerOptions.AddTrigger<UserLogTrigger>();
|
triggerOptions.AddTrigger<UserLogTrigger>();
|
||||||
|
|
||||||
triggerOptions.AddTrigger<UserAddTrigger>();
|
triggerOptions.AddTrigger<UserAddTrigger>();
|
||||||
|
|
||||||
|
triggerOptions.AddTrigger<UserLogAfterTrigger>();
|
||||||
|
|
||||||
|
triggerOptions.AddTrigger<IdenttiyUserRoleInfoTrigger>();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,25 +1,25 @@
|
||||||
|
|
||||||
using LogDashboard;
|
//using LogDashboard;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
//using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
namespace IRaCIS.Core.API
|
//namespace IRaCIS.Core.API
|
||||||
{
|
//{
|
||||||
public static class LogDashboardSetup
|
// public static class LogDashboardSetup
|
||||||
{
|
// {
|
||||||
public static void AddLogDashboardSetup(this IServiceCollection services)
|
// public static void AddLogDashboardSetup(this IServiceCollection services)
|
||||||
{
|
// {
|
||||||
//IIS 配置虚拟路径部署,会出现IIS静态文件404
|
// //IIS 配置虚拟路径部署,会出现IIS静态文件404
|
||||||
services.AddLogDashboard(opt =>
|
// services.AddLogDashboard(opt =>
|
||||||
{
|
// {
|
||||||
//opt.PathMatch = "/api/LogDashboard";
|
// //opt.PathMatch = "/api/LogDashboard";
|
||||||
opt.PathMatch = "/LogDashboard";
|
// opt.PathMatch = "/LogDashboard";
|
||||||
|
|
||||||
//opt.AddAuthorizationFilter(new LogDashboardBasicAuthFilter("admin", "zhizhun2018"));
|
// //opt.AddAuthorizationFilter(new LogDashboardBasicAuthFilter("admin", "zhizhun2018"));
|
||||||
|
|
||||||
//opt.AddAuthorizationFilter(new LogDashBoardAuthFilter());
|
// //opt.AddAuthorizationFilter(new LogDashBoardAuthFilter());
|
||||||
|
|
||||||
});
|
// });
|
||||||
|
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
|
|
|
@ -31,7 +31,7 @@ namespace IRaCIS.Core.API
|
||||||
.Enrich.FromLogContext()
|
.Enrich.FromLogContext()
|
||||||
.Filter.ByExcluding(logEvent => logEvent.Properties.ContainsKey("RequestPath") && logEvent.Properties["RequestPath"].ToString().Contains("/health"))
|
.Filter.ByExcluding(logEvent => logEvent.Properties.ContainsKey("RequestPath") && logEvent.Properties["RequestPath"].ToString().Contains("/health"))
|
||||||
.WriteTo.Console()
|
.WriteTo.Console()
|
||||||
.WriteTo.File($"{AppContext.BaseDirectory}Serilogs/.log", rollingInterval: RollingInterval.Day);
|
.WriteTo.File($"{AppContext.BaseDirectory}Serilogs/.log", rollingInterval: RollingInterval.Day,retainedFileCountLimit:60);
|
||||||
|
|
||||||
#region 根据环境配置是否打开错误发送邮件通知
|
#region 根据环境配置是否打开错误发送邮件通知
|
||||||
|
|
||||||
|
|
|
@ -16,27 +16,30 @@ namespace IRaCIS.Core.API;
|
||||||
|
|
||||||
public enum SwaggerVersion
|
public enum SwaggerVersion
|
||||||
{
|
{
|
||||||
[Description("医生模块")]
|
[Description("文件记录(FileRecord)")]
|
||||||
|
FileRecord = -1,
|
||||||
|
|
||||||
|
[Description("医生模块(Reviewer)")]
|
||||||
Reviewer = 1,
|
Reviewer = 1,
|
||||||
[Description("项目模块")]
|
[Description("项目模块(Trial)")]
|
||||||
Trial = 2,
|
Trial = 2,
|
||||||
[Description("入组模块")]
|
[Description("入组模块(Enroll)")]
|
||||||
Enroll = 3,
|
Enroll = 3,
|
||||||
[Description("工作量模块")]
|
[Description("工作量模块(Workload)")]
|
||||||
Workload = 4,
|
Workload = 4,
|
||||||
[Description("通用信息获取")]
|
[Description("通用信息获取(Common)")]
|
||||||
Common = 5,
|
Common = 5,
|
||||||
[Description("机构信息模块")]
|
[Description("机构信息模块(Institution)")]
|
||||||
Institution = 6,
|
Institution = 6,
|
||||||
[Description("统计模块")]
|
[Description("统计模块(DashboardStatistics)")]
|
||||||
DashboardStatistics = 7,
|
DashboardStatistics = 7,
|
||||||
[Description("财务模块")]
|
[Description("财务模块(Financial)")]
|
||||||
Financial = 8,
|
Financial = 8,
|
||||||
[Description("管理模块")]
|
[Description("管理模块(Management)")]
|
||||||
Management =9,
|
Management =9,
|
||||||
[Description("影像模块")]
|
[Description("影像模块(Image)")]
|
||||||
Image =10,
|
Image =10,
|
||||||
[Description("读片模块")]
|
[Description("读片模块(Reading)")]
|
||||||
Reading =11
|
Reading =11
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -7,8 +7,8 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ConnectionStrings": {
|
"ConnectionStrings": {
|
||||||
"RemoteNew": "Server=47.117.164.182,1434;Database=Event_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
"RemoteNew": "Server=101.132.253.119,1435;Database=irc_Prpd_bak;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
||||||
"Hangfire": "Server=47.117.164.182,1434;Database=Event_IRC_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
|
"Hangfire": "Server=101.132.253.119,1435;Database=irc_Hangfire_bak;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
|
||||||
},
|
},
|
||||||
"ObjectStoreService": {
|
"ObjectStoreService": {
|
||||||
"ObjectStoreUse": "AliyunOSS",
|
"ObjectStoreUse": "AliyunOSS",
|
||||||
|
@ -52,7 +52,10 @@
|
||||||
|
|
||||||
"IsNeedChangePassWord": true,
|
"IsNeedChangePassWord": true,
|
||||||
|
|
||||||
"ChangePassWordDays": 90
|
"ChangePassWordDays": 90,
|
||||||
|
|
||||||
|
// 模板类型 1 Elevate 2 Extensive
|
||||||
|
"TemplateType": 2
|
||||||
},
|
},
|
||||||
"SystemEmailSendConfig": {
|
"SystemEmailSendConfig": {
|
||||||
"Port": 465,
|
"Port": 465,
|
||||||
|
|
|
@ -7,10 +7,10 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ConnectionStrings": {
|
"ConnectionStrings": {
|
||||||
//"RemoteNew": "Server=101.132.193.237,1434;Database=Prod_IRC;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true",
|
"RemoteNew": "Server=101.132.193.237,1434;Database=Prod_IRC;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true",
|
||||||
//"Hangfire": "Server=101.132.193.237,1434;Database=Prod_IRC_Hangfire;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true"
|
"Hangfire": "Server=101.132.193.237,1434;Database=Prod_IRC_Hangfire;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true"
|
||||||
"RemoteNew": "Server=prod_mssql_standard,1433;Database=Prod_IRC;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true",
|
//"RemoteNew": "Server=prod_mssql_standard,1433;Database=Prod_IRC;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true",
|
||||||
"Hangfire": "Server=prod_mssql_standard,1433;Database=Prod_IRC_Hangfire;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true"
|
//"Hangfire": "Server=prod_mssql_standard,1433;Database=Prod_IRC_Hangfire;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true"
|
||||||
},
|
},
|
||||||
"ObjectStoreService": {
|
"ObjectStoreService": {
|
||||||
"ObjectStoreUse": "AliyunOSS",
|
"ObjectStoreUse": "AliyunOSS",
|
||||||
|
@ -43,27 +43,27 @@
|
||||||
"OpenLoginLimit": true,
|
"OpenLoginLimit": true,
|
||||||
"LoginMaxFailCount": 5,
|
"LoginMaxFailCount": 5,
|
||||||
"LoginFailLockMinutes": 30,
|
"LoginFailLockMinutes": 30,
|
||||||
|
"AutoLoginOutMinutes": 120,
|
||||||
"AutoLoginOutMinutes": 360,
|
|
||||||
|
|
||||||
"OpenLoginMFA": false,
|
"OpenLoginMFA": false,
|
||||||
|
|
||||||
"ContinuousReadingTimeMin": 120,
|
"ContinuousReadingTimeMin": 120,
|
||||||
|
|
||||||
"ReadingRestTimeMin": 10,
|
"ReadingRestTimeMin": 10,
|
||||||
|
|
||||||
"IsNeedChangePassWord": true,
|
"IsNeedChangePassWord": true,
|
||||||
|
"ChangePassWordDays": 90,
|
||||||
"ChangePassWordDays": 90
|
// 模板类型 1 Elevate 2 Extensive
|
||||||
|
"TemplateType": 2
|
||||||
},
|
},
|
||||||
|
|
||||||
"SystemEmailSendConfig": {
|
"SystemEmailSendConfig": {
|
||||||
"Port": 465,
|
"Port": 465,
|
||||||
"Host": "smtp.qiye.aliyun.com",
|
"Host": "smtp.qiye.aliyun.com",
|
||||||
"FromEmail": "IRC@extimaging.com",
|
"FromEmail": "irc@extimaging.com",
|
||||||
"FromName": "IRC",
|
"FromName": "irc",
|
||||||
"AuthorizationCode": "ExtImg@2022",
|
"AuthorizationCode": "ExtImg@2022",
|
||||||
"SiteUrl": "http://irc.extimaging.com/login",
|
"SiteUrl": "http://irc.extimaging.com/login",
|
||||||
|
"SystemShortName": "IRC",
|
||||||
"OrganizationName": "Extlmaging",
|
"OrganizationName": "Extlmaging",
|
||||||
"OrganizationNameCN": "Extlmaging",
|
"OrganizationNameCN": "Extlmaging",
|
||||||
"CompanyName": "Extensive Imaging",
|
"CompanyName": "Extensive Imaging",
|
||||||
|
|
|
@ -23,9 +23,11 @@
|
||||||
"AccessKeySecret": "FLizxkHsMm4CGYHtkV8E3PNJJZU7oV",
|
"AccessKeySecret": "FLizxkHsMm4CGYHtkV8E3PNJJZU7oV",
|
||||||
"RoleArn": "acs:ram::1899121822495495:role/dev-oss-access",
|
"RoleArn": "acs:ram::1899121822495495:role/dev-oss-access",
|
||||||
"BucketName": "zy-irc-test-store",
|
"BucketName": "zy-irc-test-store",
|
||||||
"ViewEndpoint": "https://zy-irc-test-store.oss-cn-shanghai.aliyuncs.com",
|
//"ViewEndpoint": "https://zy-irc-test-store.oss-cn-shanghai.aliyuncs.com",
|
||||||
|
"ViewEndpoint": "https://zy-irc-test-dev-cache.oss-cn-shanghai.aliyuncs.com",
|
||||||
"Region": "oss-cn-shanghai",
|
"Region": "oss-cn-shanghai",
|
||||||
"DurationSeconds": 7200
|
"DurationSeconds": 7200,
|
||||||
|
"PreviewEndpoint": "https://test-oss.test.extimaging.com"
|
||||||
},
|
},
|
||||||
"MinIO": {
|
"MinIO": {
|
||||||
"EndPoint": "hir-oss.test.extimaging.com",
|
"EndPoint": "hir-oss.test.extimaging.com",
|
||||||
|
@ -52,28 +54,26 @@
|
||||||
"BasicSystemConfig": {
|
"BasicSystemConfig": {
|
||||||
|
|
||||||
"OpenUserComplexPassword": false,
|
"OpenUserComplexPassword": false,
|
||||||
|
|
||||||
"OpenSignDocumentBeforeWork": false,
|
"OpenSignDocumentBeforeWork": false,
|
||||||
|
|
||||||
"OpenTrialRelationDelete": true,
|
|
||||||
|
|
||||||
"OpenLoginLimit": false,
|
"OpenLoginLimit": false,
|
||||||
|
|
||||||
"LoginMaxFailCount": 5,
|
"LoginMaxFailCount": 5,
|
||||||
|
|
||||||
"LoginFailLockMinutes": 1,
|
"LoginFailLockMinutes": 1,
|
||||||
|
"AutoLoginOutMinutes": 10,
|
||||||
"AutoLoginOutMinutes": 1,
|
|
||||||
|
|
||||||
"OpenLoginMFA": false,
|
"OpenLoginMFA": false,
|
||||||
|
|
||||||
"ContinuousReadingTimeMin": 120,
|
"ContinuousReadingTimeMin": 120,
|
||||||
|
|
||||||
"ReadingRestTimeMin": 10,
|
"ReadingRestTimeMin": 10,
|
||||||
"IsNeedChangePassWord": true,
|
|
||||||
|
|
||||||
|
"IsNeedChangePassWord": true,
|
||||||
"ChangePassWordDays": 90,
|
"ChangePassWordDays": 90,
|
||||||
|
|
||||||
|
// 模板类型 1 Elevate 2 Extensive
|
||||||
|
"TemplateType": 2,
|
||||||
|
|
||||||
|
"OpenTrialRelationDelete": true,
|
||||||
|
|
||||||
"ThirdPdfUrl": "http://106.14.89.110:30088/api/v1/convert/file/pdf"
|
"ThirdPdfUrl": "http://106.14.89.110:30088/api/v1/convert/file/pdf"
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -85,6 +85,7 @@
|
||||||
"AuthorizationCode": "SHzyyl2021",
|
"AuthorizationCode": "SHzyyl2021",
|
||||||
"SiteUrl": "http://irc.test.extimaging.com/login",
|
"SiteUrl": "http://irc.test.extimaging.com/login",
|
||||||
|
|
||||||
|
"SystemShortName": "IRC",
|
||||||
"OrganizationName": "Extlmaging",
|
"OrganizationName": "Extlmaging",
|
||||||
"OrganizationNameCN": "Extlmaging",
|
"OrganizationNameCN": "Extlmaging",
|
||||||
"CompanyName": "Extensive Imaging",
|
"CompanyName": "Extensive Imaging",
|
||||||
|
|
|
@ -7,8 +7,8 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ConnectionStrings": {
|
"ConnectionStrings": {
|
||||||
"RemoteNew": "Server=us-prod-mssql-service,1433;Database=US_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
"RemoteNew": "Server=us-mssql-prod,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"
|
"Hangfire": "Server=us-mssql-prod,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",
|
//"RemoteNew": "Server=44.210.231.169,1435;Database=US_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
||||||
//"Hangfire": "Server=44.210.231.169,1435;Database=US_IRC_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
//"Hangfire": "Server=44.210.231.169,1435;Database=US_IRC_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
||||||
},
|
},
|
||||||
|
@ -39,25 +39,26 @@
|
||||||
},
|
},
|
||||||
"BasicSystemConfig": {
|
"BasicSystemConfig": {
|
||||||
|
|
||||||
"OpenUserComplexPassword": false,
|
"OpenUserComplexPassword": true,
|
||||||
|
"OpenSignDocumentBeforeWork": true,
|
||||||
"OpenSignDocumentBeforeWork": false,
|
|
||||||
|
|
||||||
"OpenTrialRelationDelete": true,
|
|
||||||
|
|
||||||
"OpenLoginLimit": false,
|
|
||||||
|
|
||||||
|
"OpenLoginLimit": true,
|
||||||
"LoginMaxFailCount": 5,
|
"LoginMaxFailCount": 5,
|
||||||
|
|
||||||
"LoginFailLockMinutes": 30,
|
"LoginFailLockMinutes": 30,
|
||||||
"AutoLoginOutMinutes": 60,
|
"AutoLoginOutMinutes": 120,
|
||||||
|
|
||||||
|
"OpenLoginMFA": true,
|
||||||
|
|
||||||
"ContinuousReadingTimeMin": 120,
|
"ContinuousReadingTimeMin": 120,
|
||||||
|
|
||||||
"ReadingRestTimeMin": 10,
|
"ReadingRestTimeMin": 10,
|
||||||
"IsNeedChangePassWord": true,
|
|
||||||
|
|
||||||
"ChangePassWordDays": 90
|
"IsNeedChangePassWord": true,
|
||||||
|
"ChangePassWordDays": 90,
|
||||||
|
// 模板类型 1 Elevate 2 Extensive
|
||||||
|
"TemplateType": 1,
|
||||||
|
|
||||||
|
"OpenTrialRelationDelete": false
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
"SystemEmailSendConfig": {
|
"SystemEmailSendConfig": {
|
||||||
|
@ -67,6 +68,7 @@
|
||||||
"FromName": "LiLi",
|
"FromName": "LiLi",
|
||||||
"AuthorizationCode": "Q#669869497420ul",
|
"AuthorizationCode": "Q#669869497420ul",
|
||||||
|
|
||||||
|
"SystemShortName": "LiLi",
|
||||||
"OrganizationName": "Elevate Imaging",
|
"OrganizationName": "Elevate Imaging",
|
||||||
"OrganizationNameCN": "Elevate Imaging",
|
"OrganizationNameCN": "Elevate Imaging",
|
||||||
"CompanyName": "Elevate Imaging Inc.",
|
"CompanyName": "Elevate Imaging Inc.",
|
||||||
|
@ -75,7 +77,7 @@
|
||||||
"CompanyShortNameCN": "展影医疗",
|
"CompanyShortNameCN": "展影医疗",
|
||||||
"SiteUrl": "https://lili.elevateimaging.ai/login",
|
"SiteUrl": "https://lili.elevateimaging.ai/login",
|
||||||
"IsEnv_US": true,
|
"IsEnv_US": true,
|
||||||
"IsOpenErrorNoticeEmail": true,
|
"IsOpenErrorNoticeEmail": false,
|
||||||
"ErrorNoticeEmailList": [ "872297557@qq.com" ]
|
"ErrorNoticeEmailList": [ "872297557@qq.com" ]
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -64,7 +64,8 @@
|
||||||
"IsNeedChangePassWord": true,
|
"IsNeedChangePassWord": true,
|
||||||
|
|
||||||
"ChangePassWordDays": 90,
|
"ChangePassWordDays": 90,
|
||||||
|
// 模板类型 1 Elevate 2 Extensive
|
||||||
|
"TemplateType": 1,
|
||||||
"OpenLoginMFA": true
|
"OpenLoginMFA": true
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -75,6 +76,7 @@
|
||||||
"FromName": "LiLi",
|
"FromName": "LiLi",
|
||||||
"AuthorizationCode": "Q#669869497420ul",
|
"AuthorizationCode": "Q#669869497420ul",
|
||||||
|
|
||||||
|
"SystemShortName": "LiLi",
|
||||||
"OrganizationName": "Elevate Imaging",
|
"OrganizationName": "Elevate Imaging",
|
||||||
"OrganizationNameCN": "Elevate Imaging",
|
"OrganizationNameCN": "Elevate Imaging",
|
||||||
"CompanyName": "Elevate Imaging Inc.",
|
"CompanyName": "Elevate Imaging Inc.",
|
||||||
|
|
|
@ -50,26 +50,22 @@
|
||||||
"BasicSystemConfig": {
|
"BasicSystemConfig": {
|
||||||
|
|
||||||
"OpenUserComplexPassword": true,
|
"OpenUserComplexPassword": true,
|
||||||
|
|
||||||
"OpenSignDocumentBeforeWork": true,
|
"OpenSignDocumentBeforeWork": true,
|
||||||
|
|
||||||
"OpenTrialRelationDelete": true,
|
"OpenLoginLimit": true,
|
||||||
|
|
||||||
"OpenLoginLimit": false,
|
|
||||||
|
|
||||||
"LoginMaxFailCount": 5,
|
"LoginMaxFailCount": 5,
|
||||||
|
|
||||||
"LoginFailLockMinutes": 30,
|
"LoginFailLockMinutes": 30,
|
||||||
"AutoLoginOutMinutes": 60,
|
"AutoLoginOutMinutes": 120,
|
||||||
|
|
||||||
|
"OpenLoginMFA": true,
|
||||||
|
|
||||||
"ContinuousReadingTimeMin": 120,
|
"ContinuousReadingTimeMin": 120,
|
||||||
|
|
||||||
"ReadingRestTimeMin": 10,
|
"ReadingRestTimeMin": 10,
|
||||||
|
|
||||||
"IsNeedChangePassWord": true,
|
"IsNeedChangePassWord": true,
|
||||||
|
|
||||||
"ChangePassWordDays": 90,
|
"ChangePassWordDays": 90,
|
||||||
|
// 模板类型 1 Elevate 2 Extensive
|
||||||
"OpenLoginMFA": false
|
"TemplateType": 1
|
||||||
},
|
},
|
||||||
|
|
||||||
"SystemEmailSendConfig": {
|
"SystemEmailSendConfig": {
|
||||||
|
@ -79,13 +75,14 @@
|
||||||
"FromName": "LiLi",
|
"FromName": "LiLi",
|
||||||
"AuthorizationCode": "Q#669869497420ul",
|
"AuthorizationCode": "Q#669869497420ul",
|
||||||
|
|
||||||
|
"SystemShortName": "LiLi",
|
||||||
"OrganizationName": "Elevate Imaging",
|
"OrganizationName": "Elevate Imaging",
|
||||||
"OrganizationNameCN": "Elevate Imaging",
|
"OrganizationNameCN": "Elevate Imaging",
|
||||||
"CompanyName": "Elevate Imaging Inc.",
|
"CompanyName": "Elevate Imaging Inc.",
|
||||||
"CompanyNameCN": "上海展影医疗科技有限公司",
|
"CompanyNameCN": "上海展影医疗科技有限公司",
|
||||||
"CompanyShortName": "Elevate Imaging",
|
"CompanyShortName": "Elevate Imaging",
|
||||||
"CompanyShortNameCN": "展影医疗",
|
"CompanyShortNameCN": "展影医疗",
|
||||||
"SiteUrl": "https://lili.test.elevateimaging.ai/login",
|
"SiteUrl": "https://lili.uat.elevateimaging.ai/login",
|
||||||
"IsEnv_US": true,
|
"IsEnv_US": true,
|
||||||
"IsOpenErrorNoticeEmail": false,
|
"IsOpenErrorNoticeEmail": false,
|
||||||
"ErrorNoticeEmailList": [ "872297557@qq.com" ]
|
"ErrorNoticeEmailList": [ "872297557@qq.com" ]
|
||||||
|
|
|
@ -56,22 +56,23 @@
|
||||||
"BasicSystemConfig": {
|
"BasicSystemConfig": {
|
||||||
|
|
||||||
"OpenUserComplexPassword": true,
|
"OpenUserComplexPassword": true,
|
||||||
|
|
||||||
"OpenSignDocumentBeforeWork": true,
|
"OpenSignDocumentBeforeWork": true,
|
||||||
|
|
||||||
"OpenLoginLimit": true,
|
"OpenLoginLimit": true,
|
||||||
"LoginMaxFailCount": 5,
|
"LoginMaxFailCount": 5,
|
||||||
|
|
||||||
"LoginFailLockMinutes": 30,
|
"LoginFailLockMinutes": 30,
|
||||||
"AutoLoginOutMinutes": 60,
|
"AutoLoginOutMinutes": 120,
|
||||||
|
|
||||||
"OpenLoginMFA": false,
|
"OpenLoginMFA": false,
|
||||||
|
|
||||||
"ContinuousReadingTimeMin": 120,
|
"ContinuousReadingTimeMin": 120,
|
||||||
|
|
||||||
"ReadingRestTimeMin": 10,
|
"ReadingRestTimeMin": 10,
|
||||||
"IsNeedChangePassWord": true,
|
|
||||||
|
|
||||||
"ChangePassWordDays": 90
|
"IsNeedChangePassWord": true,
|
||||||
|
"ChangePassWordDays": 90,
|
||||||
|
// 模板类型 1 Elevate 2 Extensive
|
||||||
|
"TemplateType": 2
|
||||||
|
|
||||||
},
|
},
|
||||||
"SystemEmailSendConfig": {
|
"SystemEmailSendConfig": {
|
||||||
|
@ -81,6 +82,8 @@
|
||||||
"FromName": "UAT_IRC",
|
"FromName": "UAT_IRC",
|
||||||
"AuthorizationCode": "SHzyyl2021",
|
"AuthorizationCode": "SHzyyl2021",
|
||||||
"SiteUrl": "http://irc.uat.extimaging.com/login",
|
"SiteUrl": "http://irc.uat.extimaging.com/login",
|
||||||
|
|
||||||
|
"SystemShortName": "IRC",
|
||||||
"OrganizationName": "Extlmaging",
|
"OrganizationName": "Extlmaging",
|
||||||
"OrganizationNameCN": "Extlmaging",
|
"OrganizationNameCN": "Extlmaging",
|
||||||
"CompanyName": "Extensive Imaging",
|
"CompanyName": "Extensive Imaging",
|
||||||
|
@ -94,7 +97,7 @@
|
||||||
|
|
||||||
"SystemPacsConfig": {
|
"SystemPacsConfig": {
|
||||||
"Port": "11113",
|
"Port": "11113",
|
||||||
"IP": "47.117.164.182"
|
"IP": "101.132.253.119"
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
"SecurityKey": "ShangHaiZhanYing_SecurityKey_SHzyyl@2021",
|
"SecurityKey": "ShangHaiZhanYing_SecurityKey_SHzyyl@2021",
|
||||||
"Issuer": "Extimaging",
|
"Issuer": "Extimaging",
|
||||||
"Audience": "EICS",
|
"Audience": "EICS",
|
||||||
"TokenExpireDays": "7"
|
"TokenExpireMinute": "10080"//7天
|
||||||
},
|
},
|
||||||
"IpRateLimiting": {
|
"IpRateLimiting": {
|
||||||
"EnableEndpointRateLimiting": true,
|
"EnableEndpointRateLimiting": true,
|
||||||
|
|
|
@ -268,12 +268,12 @@ var abp = abp || {};
|
||||||
}
|
}
|
||||||
|
|
||||||
function loginUserInternal(tenantId, callback) {
|
function loginUserInternal(tenantId, callback) {
|
||||||
|
|
||||||
var usernameOrEmailAddress = document.getElementById('userName').value;
|
var usernameOrEmailAddress = document.getElementById('userName').value;
|
||||||
if (!usernameOrEmailAddress) {
|
// if (!usernameOrEmailAddress) {
|
||||||
alert('UserName Can Not Be Null');
|
// alert('UserName Can Not Be Null');
|
||||||
return false;
|
// return false;
|
||||||
}
|
// }
|
||||||
|
|
||||||
var password = document.getElementById('password').value;
|
var password = document.getElementById('password').value;
|
||||||
var pwdMd5 = document.getElementById('pwdMd5').value;
|
var pwdMd5 = document.getElementById('pwdMd5').value;
|
||||||
|
@ -286,12 +286,12 @@ var abp = abp || {};
|
||||||
var xhr = new XMLHttpRequest();
|
var xhr = new XMLHttpRequest();
|
||||||
|
|
||||||
xhr.onreadystatechange = function () {
|
xhr.onreadystatechange = function () {
|
||||||
|
|
||||||
if (xhr.readyState === XMLHttpRequest.DONE) {
|
if (xhr.readyState === XMLHttpRequest.DONE) {
|
||||||
debugger;
|
// debugger;
|
||||||
if (xhr.status === 200) {
|
if (xhr.status === 200) {
|
||||||
debugger;
|
// debugger;
|
||||||
|
|
||||||
var resultdata = JSON.parse(xhr.responseText);
|
var resultdata = JSON.parse(xhr.responseText);
|
||||||
if (resultdata.ErrorMessage != '') {
|
if (resultdata.ErrorMessage != '') {
|
||||||
alert(resultdata.ErrorMessage);
|
alert(resultdata.ErrorMessage);
|
||||||
|
@ -303,18 +303,23 @@ var abp = abp || {};
|
||||||
else {
|
else {
|
||||||
var responseJSON = JSON.parse(xhr.responseText);
|
var responseJSON = JSON.parse(xhr.responseText);
|
||||||
var result = responseJSON;
|
var result = responseJSON;
|
||||||
var expireDate = new Date(Date.now() + (60*60*24 * 1000));
|
var expireDate = new Date(Date.now() + (60 * 60 * 24 * 1000));
|
||||||
abp.auth.setToken(result.Result.JWTStr, expireDate);
|
abp.auth.setToken(result.Result.JWTStr, expireDate);
|
||||||
callback();
|
let selectDom = document.getElementById("roleSelect")
|
||||||
|
selectDom.options.length = 0;
|
||||||
|
result.Result.BasicInfo.AccountList.forEach(item => {
|
||||||
|
selectDom.options.add(new Option(item.UserTypeShortName, item.Id));
|
||||||
|
})
|
||||||
|
// callback();
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
alert('Login failed !');
|
alert('Login failed !');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
xhr.open('POST', '/user/login', true);
|
xhr.open('POST', '/User/getUserLoginRoleList', true);
|
||||||
xhr.setRequestHeader('Abp.TenantId', tenantId);
|
xhr.setRequestHeader('Abp.TenantId', tenantId);
|
||||||
xhr.setRequestHeader('Content-type', 'application/json');
|
xhr.setRequestHeader('Content-type', 'application/json');
|
||||||
var parm = {
|
var parm = {
|
||||||
|
@ -330,7 +335,63 @@ var abp = abp || {};
|
||||||
//xhr.send("{" + "userName:'" + usernameOrEmailAddress + "'," + "passWord:'" + password + "'}");
|
//xhr.send("{" + "userName:'" + usernameOrEmailAddress + "'," + "passWord:'" + password + "'}");
|
||||||
|
|
||||||
};
|
};
|
||||||
|
function loginUserInternalRole(tenantId, callback) {
|
||||||
|
|
||||||
|
var usernameOrEmailAddress = document.getElementById('roleSelect').value;
|
||||||
|
//if (!usernameOrEmailAddress) {
|
||||||
|
// alert('UserName Can Not Be Null');
|
||||||
|
// return false;
|
||||||
|
//}
|
||||||
|
|
||||||
|
var password = document.getElementById('password').value;
|
||||||
|
var pwdMd5 = document.getElementById('pwdMd5').value;
|
||||||
|
console.log(pwdMd5);
|
||||||
|
if (!password && !pwdMd5) {
|
||||||
|
alert('PassWord And Md5 Can Not Be Null');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var xhr = new XMLHttpRequest();
|
||||||
|
|
||||||
|
xhr.onreadystatechange = function () {
|
||||||
|
|
||||||
|
if (xhr.readyState === XMLHttpRequest.DONE) {
|
||||||
|
// debugger;
|
||||||
|
if (xhr.status === 200) {
|
||||||
|
// debugger;
|
||||||
|
|
||||||
|
var resultdata = JSON.parse(xhr.responseText);
|
||||||
|
if (resultdata.ErrorMessage != '') {
|
||||||
|
alert(resultdata.ErrorMessage);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (resultdata.code == 300) {
|
||||||
|
alert(resultdata.message);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var responseJSON = JSON.parse(xhr.responseText);
|
||||||
|
var result = responseJSON;
|
||||||
|
var expireDate = new Date(Date.now() + (60 * 60 * 24 * 1000));
|
||||||
|
abp.auth.setToken(result.Result, expireDate);
|
||||||
|
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
alert('Login failed !');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
xhr.open('get', `/User/loginSelectUserRole?userRoleId=${usernameOrEmailAddress}`, true);
|
||||||
|
xhr.setRequestHeader('Abp.TenantId', tenantId);
|
||||||
|
xhr.setRequestHeader('Content-type', 'application/json');
|
||||||
|
var authToken = abp.auth.getToken();
|
||||||
|
xhr.setRequestHeader('Authorization', `Bearer ${authToken}`);
|
||||||
|
xhr.send();
|
||||||
|
//xhr.send("{" + "userName:'" + usernameOrEmailAddress + "'," + "passWord:'" + password + "'}");
|
||||||
|
|
||||||
|
};
|
||||||
abp.swagger.login = function (callback) {
|
abp.swagger.login = function (callback) {
|
||||||
//Get TenantId first
|
//Get TenantId first
|
||||||
var tenancyName = document.getElementById('tenancyName').value;
|
var tenancyName = document.getElementById('tenancyName').value;
|
||||||
|
@ -356,6 +417,31 @@ var abp = abp || {};
|
||||||
loginUserInternal(null, callback); // Login for host
|
loginUserInternal(null, callback); // Login for host
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
abp.swagger.loginRole = function (callback) {
|
||||||
|
//Get TenantId first
|
||||||
|
var tenancyName = document.getElementById('tenancyName').value;
|
||||||
|
|
||||||
|
if (tenancyName) {
|
||||||
|
var xhrTenancyName = new XMLHttpRequest();
|
||||||
|
xhrTenancyName.onreadystatechange = function () {
|
||||||
|
if (xhrTenancyName.readyState === XMLHttpRequest.DONE && xhrTenancyName.status === 200) {
|
||||||
|
var responseJSON = JSON.parse(xhrTenancyName.responseText);
|
||||||
|
var result = responseJSON.result;
|
||||||
|
if (result.state === 1) { // Tenant exists and active.
|
||||||
|
loginUserInternalRole(result.tenantId, callback); // Login for tenant
|
||||||
|
} else {
|
||||||
|
alert('There is no such tenant or tenant is not active !');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
xhrTenancyName.open('POST', '/api/services/app/Account/IsTenantAvailable', true);
|
||||||
|
xhrTenancyName.setRequestHeader('Content-type', 'application/json');
|
||||||
|
xhrTenancyName.send("{" + "tenancyName:'" + tenancyName + "'}");
|
||||||
|
} else {
|
||||||
|
loginUserInternalRole(null, callback); // Login for host
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
abp.swagger.logout = function () {
|
abp.swagger.logout = function () {
|
||||||
abp.auth.clearToken();
|
abp.auth.clearToken();
|
||||||
|
@ -419,9 +505,10 @@ var abp = abp || {};
|
||||||
|
|
||||||
//Inputs
|
//Inputs
|
||||||
createInput(modalUxContent, 'tenancyName', 'Tenancy Name (Leave empty for Host)');
|
createInput(modalUxContent, 'tenancyName', 'Tenancy Name (Leave empty for Host)');
|
||||||
createInput(modalUxContent, 'userName', 'Username or email address','text','cyldev');
|
createInput(modalUxContent, 'userName', 'Username or email address', 'text', 'cyldev');
|
||||||
createInput(modalUxContent, 'password', 'Password', 'password', '123456');
|
createInput(modalUxContent, 'password', 'Password', 'password', '123456');
|
||||||
createInput(modalUxContent, 'pwdMd5', 'PwdMd5', 'text', '');
|
createInput(modalUxContent, 'pwdMd5', 'PwdMd5', 'text', '');
|
||||||
|
createSelect(modalUxContent, 'roleSelect', 'role', [])
|
||||||
|
|
||||||
//Buttons
|
//Buttons
|
||||||
var authBtnWrapper = document.createElement('div');
|
var authBtnWrapper = document.createElement('div');
|
||||||
|
@ -444,9 +531,18 @@ var abp = abp || {};
|
||||||
abp.swagger.login(loginCallback);
|
abp.swagger.login(loginCallback);
|
||||||
};
|
};
|
||||||
authBtnWrapper.appendChild(authorizeButton);
|
authBtnWrapper.appendChild(authorizeButton);
|
||||||
|
|
||||||
|
// login role
|
||||||
|
var authorizeButton = document.createElement('button');
|
||||||
|
authorizeButton.className = 'btn modal-btn auth authorize button';
|
||||||
|
authorizeButton.innerText = 'LoginRole';
|
||||||
|
authorizeButton.onclick = function () {
|
||||||
|
abp.swagger.loginRole(loginCallback);
|
||||||
|
};
|
||||||
|
authBtnWrapper.appendChild(authorizeButton);
|
||||||
}
|
}
|
||||||
|
|
||||||
function createInput(container, id, title, type, value="") {
|
function createInput(container, id, title, type, value = "") {
|
||||||
var wrapper = document.createElement('div');
|
var wrapper = document.createElement('div');
|
||||||
wrapper.className = 'wrapper';
|
wrapper.className = 'wrapper';
|
||||||
if (id == "tenancyName") {
|
if (id == "tenancyName") {
|
||||||
|
@ -470,6 +566,31 @@ var abp = abp || {};
|
||||||
input.autocomplete = "off";
|
input.autocomplete = "off";
|
||||||
|
|
||||||
|
|
||||||
|
section.appendChild(input);
|
||||||
|
}
|
||||||
|
function createSelect(container, id, title, option = []) {
|
||||||
|
var wrapper = document.createElement('div');
|
||||||
|
wrapper.className = 'wrapper';
|
||||||
|
if (id == "tenancyName") {
|
||||||
|
wrapper.style.display = 'none';
|
||||||
|
}
|
||||||
|
container.appendChild(wrapper);
|
||||||
|
|
||||||
|
var label = document.createElement('label');
|
||||||
|
label.innerText = title;
|
||||||
|
wrapper.appendChild(label);
|
||||||
|
|
||||||
|
var section = document.createElement('section');
|
||||||
|
section.className = 'block-tablet col-10-tablet block-desktop col-10-desktop';
|
||||||
|
wrapper.appendChild(section);
|
||||||
|
|
||||||
|
var input = document.createElement('select');
|
||||||
|
input.id = id;
|
||||||
|
input.style.width = '100%';
|
||||||
|
option.forEach(item => {
|
||||||
|
input.options.add(new Option(item.UserTypeShortName, item.Id));
|
||||||
|
})
|
||||||
|
|
||||||
section.appendChild(input);
|
section.appendChild(input);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,46 +0,0 @@
|
||||||
using IRaCIS.Application.Contracts;
|
|
||||||
using IRaCIS.Core.Domain.Share;
|
|
||||||
|
|
||||||
namespace IRaCIS.Core.Application.Auth
|
|
||||||
{
|
|
||||||
public class IRaCISClaims
|
|
||||||
{
|
|
||||||
public Guid Id { get; set; }
|
|
||||||
public string FullName { get; set; } = String.Empty;
|
|
||||||
public string Code { get; set; } = String.Empty;
|
|
||||||
public string RealName { get; set; } = String.Empty;
|
|
||||||
|
|
||||||
public string UserTypeShortName { get; set; } = String.Empty;
|
|
||||||
|
|
||||||
public UserTypeEnum UserTypeEnum { get; set; }
|
|
||||||
|
|
||||||
public string PermissionStr { get; set; } = String.Empty;
|
|
||||||
|
|
||||||
public Guid UserTypeId { get; set; }
|
|
||||||
|
|
||||||
public int IsAdmin { get; }
|
|
||||||
|
|
||||||
public bool IsTestUser { get; set; }
|
|
||||||
|
|
||||||
public bool IsZhiZhun { get; set; }
|
|
||||||
|
|
||||||
public string Phone { get; set; } = String.Empty;
|
|
||||||
|
|
||||||
public static IRaCISClaims Create(UserBasicInfo user)
|
|
||||||
{
|
|
||||||
return new IRaCISClaims
|
|
||||||
{
|
|
||||||
Id = user.Id,
|
|
||||||
FullName = user.UserName,
|
|
||||||
RealName = user.RealName,
|
|
||||||
UserTypeEnum = user.UserTypeEnum,
|
|
||||||
UserTypeId = user.UserTypeId,
|
|
||||||
IsTestUser = user.IsTestUser,
|
|
||||||
Code = user.Code,
|
|
||||||
PermissionStr = user.PermissionStr,
|
|
||||||
IsZhiZhun = user.IsZhiZhun,
|
|
||||||
UserTypeShortName = user.UserTypeShortName
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -23,7 +23,7 @@ namespace IRaCIS.Core.Application.Auth
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 过期时间
|
/// 过期时间
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int TokenExpireDays { get; set; }
|
public int TokenExpireMinute { get; set; }
|
||||||
|
|
||||||
//public Dictionary<string, object> Claims { get; set; }
|
//public Dictionary<string, object> Claims { get; set; }
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,9 @@ namespace IRaCIS.Core.Application.Auth
|
||||||
|
|
||||||
public interface ITokenService
|
public interface ITokenService
|
||||||
{
|
{
|
||||||
string GetToken(IRaCISClaims user);
|
string GetToken(UserTokenInfo user);
|
||||||
|
|
||||||
|
bool IsTokenExpired(string token);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -21,16 +23,16 @@ namespace IRaCIS.Core.Application.Auth
|
||||||
_jwtSetting = option.Value;
|
_jwtSetting = option.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetToken(IRaCISClaims user)
|
public string GetToken(UserTokenInfo user)
|
||||||
{
|
{
|
||||||
//创建用户身份标识,可按需要添加更多信息
|
//创建用户身份标识,可按需要添加更多信息
|
||||||
var claims = new Claim[]
|
var claims = new Claim[]
|
||||||
{
|
{
|
||||||
new Claim(Microsoft.IdentityModel.JsonWebTokens.JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
|
new Claim(Microsoft.IdentityModel.JsonWebTokens.JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
|
||||||
new Claim(JwtIRaCISClaimType.Id, user.Id.ToString()),
|
new Claim(JwtIRaCISClaimType.IdentityUserId, user.IdentityUserId.ToString()),
|
||||||
new Claim(JwtIRaCISClaimType.Name, user.FullName),
|
new Claim(JwtIRaCISClaimType.UserRoleId, user.UserRoleId.ToString()),
|
||||||
new Claim(JwtIRaCISClaimType.RealName, user.RealName),
|
new Claim(JwtIRaCISClaimType.UserName, user.UserName),
|
||||||
new Claim(JwtIRaCISClaimType.Code,user.Code),
|
new Claim(JwtIRaCISClaimType.FullName, user.FullName),
|
||||||
new Claim(JwtIRaCISClaimType.UserTypeId,user.UserTypeId.ToString()),
|
new Claim(JwtIRaCISClaimType.UserTypeId,user.UserTypeId.ToString()),
|
||||||
new Claim(JwtIRaCISClaimType.UserTypeEnum,user.UserTypeEnum.ToString()),
|
new Claim(JwtIRaCISClaimType.UserTypeEnum,user.UserTypeEnum.ToString()),
|
||||||
new Claim(JwtIRaCISClaimType.UserTypeEnumInt,((int)user.UserTypeEnum).ToString()),
|
new Claim(JwtIRaCISClaimType.UserTypeEnumInt,((int)user.UserTypeEnum).ToString()),
|
||||||
|
@ -47,13 +49,27 @@ namespace IRaCIS.Core.Application.Auth
|
||||||
signingCredentials: _jwtSetting.Credentials,
|
signingCredentials: _jwtSetting.Credentials,
|
||||||
claims: claims,
|
claims: claims,
|
||||||
notBefore: DateTime.Now,
|
notBefore: DateTime.Now,
|
||||||
expires: DateTime.Now.AddDays(_jwtSetting.TokenExpireDays)
|
expires: DateTime.Now.AddMinutes(_jwtSetting.TokenExpireMinute)
|
||||||
);
|
);
|
||||||
|
|
||||||
string jwtToken = new JwtSecurityTokenHandler().WriteToken(token);
|
string jwtToken = new JwtSecurityTokenHandler().WriteToken(token);
|
||||||
return jwtToken;
|
return jwtToken;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool IsTokenExpired(string token)
|
||||||
|
{
|
||||||
|
var handler = new JwtSecurityTokenHandler();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var jwtToken = handler.ReadJwtToken(token);
|
||||||
|
return jwtToken.ValidTo < DateTime.UtcNow;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return true; // 无效 Token 也视为已过期
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
using IRaCIS.Application.Contracts;
|
||||||
|
using IRaCIS.Core.Domain.Share;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.Application.Auth
|
||||||
|
{
|
||||||
|
public class UserTokenInfo
|
||||||
|
{
|
||||||
|
public Guid IdentityUserId { get; set; }
|
||||||
|
|
||||||
|
public Guid UserRoleId { get; set; }
|
||||||
|
|
||||||
|
public Guid UserTypeId { get; set; }
|
||||||
|
|
||||||
|
public UserTypeEnum UserTypeEnum { get; set; }
|
||||||
|
|
||||||
|
public string UserName { get; set; } = string.Empty;
|
||||||
|
public string FullName { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public string PermissionStr { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public bool IsTestUser { get; set; }
|
||||||
|
|
||||||
|
public bool IsZhiZhun { get; set; }
|
||||||
|
|
||||||
|
public string UserTypeShortName { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
|
}
|
|
@ -48,7 +48,7 @@ public class LimitUserRequestAuthorization(
|
||||||
|
|
||||||
|
|
||||||
//2、在这里取缓存 进行比较 看是否有其他人进行了登陆,如果其他人登陆了,就把之前用户挤掉
|
//2、在这里取缓存 进行比较 看是否有其他人进行了登陆,如果其他人登陆了,就把之前用户挤掉
|
||||||
var cacheUserToken = await _fusionCache.GetOrDefaultAsync<string>(CacheKeys.UserToken(_userInfo.Id));
|
var cacheUserToken = await _fusionCache.GetOrDefaultAsync<string>(CacheKeys.UserToken(_userInfo.IdentityUserId));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -58,17 +58,17 @@ public class LimitUserRequestAuthorization(
|
||||||
cacheUserToken = _userInfo.UserToken;
|
cacheUserToken = _userInfo.UserToken;
|
||||||
|
|
||||||
//设置当前用户最新Token
|
//设置当前用户最新Token
|
||||||
await _fusionCache.SetAsync(CacheKeys.UserToken(_userInfo.Id), _userInfo.UserToken, TimeSpan.FromDays(7));
|
await _fusionCache.SetAsync(CacheKeys.UserToken(_userInfo.IdentityUserId), _userInfo.UserToken, TimeSpan.FromDays(7));
|
||||||
|
|
||||||
//重启应用程序,所有人续期,不一下子踢出所有人
|
//重启应用程序,所有人续期,不一下子踢出所有人
|
||||||
await _fusionCache.SetAsync(CacheKeys.UserAutoLoginOut(_userInfo.Id), DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), TimeSpan.FromMinutes(minutes));
|
await _fusionCache.SetAsync(CacheKeys.UserAutoLoginOut(_userInfo.IdentityUserId), DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), TimeSpan.FromMinutes(minutes));
|
||||||
|
|
||||||
}
|
}
|
||||||
//是同一个人
|
//是同一个人
|
||||||
else if (cacheUserToken == _userInfo.UserToken)
|
else if (cacheUserToken == _userInfo.UserToken)
|
||||||
{
|
{
|
||||||
|
|
||||||
var cacheTime = await _fusionCache.GetOrDefaultAsync<string>(CacheKeys.UserAutoLoginOut(_userInfo.Id));
|
var cacheTime = await _fusionCache.GetOrDefaultAsync<string>(CacheKeys.UserAutoLoginOut(_userInfo.IdentityUserId));
|
||||||
|
|
||||||
//过期了 需要自动退出
|
//过期了 需要自动退出
|
||||||
if (string.IsNullOrEmpty(cacheTime))
|
if (string.IsNullOrEmpty(cacheTime))
|
||||||
|
@ -80,7 +80,7 @@ public class LimitUserRequestAuthorization(
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await _fusionCache.SetAsync(CacheKeys.UserAutoLoginOut(_userInfo.Id), DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), TimeSpan.FromMinutes(minutes));
|
await _fusionCache.SetAsync(CacheKeys.UserAutoLoginOut(_userInfo.IdentityUserId), DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), TimeSpan.FromMinutes(minutes));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -93,6 +93,21 @@ public class LimitUserRequestAuthorization(
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//用户或者角色禁用,那么操作的人退出
|
||||||
|
|
||||||
|
var isDisable = await _fusionCache.GetOrDefaultAsync<bool>(CacheKeys.UserDisable(_userInfo.IdentityUserId),false);
|
||||||
|
|
||||||
|
var isRoleDisable = await _fusionCache.GetOrDefaultAsync<bool>(CacheKeys.UserRoleDisable(_userInfo.UserRoleId),false);
|
||||||
|
|
||||||
|
if (isDisable == true || isRoleDisable == true)
|
||||||
|
{
|
||||||
|
context.HttpContext.Response.ContentType = "application/json";
|
||||||
|
context.HttpContext.Response.StatusCode = StatusCodes.Status403Forbidden;
|
||||||
|
// 用户或者角色被禁用。
|
||||||
|
|
||||||
|
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["LimitUser_AccountOrRoleDisable"], ApiResponseCodeEnum.AutoLoginOut));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -41,19 +41,19 @@ public class LimitUserRequestAuthorizationEndpointFilter(
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取缓存中的用户 token
|
// 获取缓存中的用户 token
|
||||||
var cacheUserToken = await _fusionCache.GetOrDefaultAsync<string>(CacheKeys.UserToken(_userInfo.Id));
|
var cacheUserToken = await _fusionCache.GetOrDefaultAsync<string>(CacheKeys.UserToken(_userInfo.IdentityUserId));
|
||||||
|
|
||||||
// 缓存中没有取到 token
|
// 缓存中没有取到 token
|
||||||
if (string.IsNullOrWhiteSpace(cacheUserToken))
|
if (string.IsNullOrWhiteSpace(cacheUserToken))
|
||||||
{
|
{
|
||||||
// 设置当前用户最新 token
|
// 设置当前用户最新 token
|
||||||
await _fusionCache.SetAsync(CacheKeys.UserToken(_userInfo.Id), _userInfo.UserToken, TimeSpan.FromDays(7));
|
await _fusionCache.SetAsync(CacheKeys.UserToken(_userInfo.IdentityUserId), _userInfo.UserToken, TimeSpan.FromDays(7));
|
||||||
await _fusionCache.SetAsync(CacheKeys.UserAutoLoginOut(_userInfo.Id), DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), TimeSpan.FromMinutes(minutes));
|
await _fusionCache.SetAsync(CacheKeys.UserAutoLoginOut(_userInfo.IdentityUserId), DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), TimeSpan.FromMinutes(minutes));
|
||||||
}
|
}
|
||||||
// 如果是同一个用户
|
// 如果是同一个用户
|
||||||
else if (cacheUserToken == _userInfo.UserToken)
|
else if (cacheUserToken == _userInfo.UserToken)
|
||||||
{
|
{
|
||||||
var cacheTime = await _fusionCache.GetOrDefaultAsync<string>(CacheKeys.UserAutoLoginOut(_userInfo.Id));
|
var cacheTime = await _fusionCache.GetOrDefaultAsync<string>(CacheKeys.UserAutoLoginOut(_userInfo.IdentityUserId));
|
||||||
|
|
||||||
// 如果过期,自动登出
|
// 如果过期,自动登出
|
||||||
if (string.IsNullOrEmpty(cacheTime))
|
if (string.IsNullOrEmpty(cacheTime))
|
||||||
|
@ -63,7 +63,7 @@ public class LimitUserRequestAuthorizationEndpointFilter(
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await _fusionCache.SetAsync(CacheKeys.UserAutoLoginOut(_userInfo.Id), DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), TimeSpan.FromMinutes(minutes));
|
await _fusionCache.SetAsync(CacheKeys.UserAutoLoginOut(_userInfo.IdentityUserId), DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), TimeSpan.FromMinutes(minutes));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
@ -34,6 +34,8 @@ public class ServiceVerifyConfigOption
|
||||||
[Description("修改密码的天数")]
|
[Description("修改密码的天数")]
|
||||||
public int ChangePassWordDays { get; set; }
|
public int ChangePassWordDays { get; set; }
|
||||||
|
|
||||||
|
public SysTemplateType TemplateType { get; set; }
|
||||||
|
|
||||||
public string ThirdPdfUrl { get; set; }
|
public string ThirdPdfUrl { get; set; }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -51,6 +53,8 @@ public class SystemEmailSendConfig
|
||||||
|
|
||||||
public string SiteUrl { get; set; } = string.Empty;
|
public string SiteUrl { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public string SystemShortName { get; set; } = string.Empty;
|
||||||
|
|
||||||
public string OrganizationName { get; set; } = string.Empty;
|
public string OrganizationName { get; set; } = string.Empty;
|
||||||
public string OrganizationNameCN { get; set; } = string.Empty;
|
public string OrganizationNameCN { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
@ -71,6 +75,7 @@ public class SystemEmailSendConfig
|
||||||
|
|
||||||
public class SystemEmailSendConfigView
|
public class SystemEmailSendConfigView
|
||||||
{
|
{
|
||||||
|
public string SystemShortName { get; set; } = string.Empty;
|
||||||
public string CompanyName { get; set; } = string.Empty;
|
public string CompanyName { get; set; } = string.Empty;
|
||||||
|
|
||||||
public string CompanyNameCN { get; set; } = string.Empty;
|
public string CompanyNameCN { get; set; } = string.Empty;
|
||||||
|
@ -140,7 +145,7 @@ public static class AppSettings
|
||||||
case nameof(Doctor):
|
case nameof(Doctor):
|
||||||
return IRaCISBasicConfig.DoctorCodePrefix + codeInt.ToString("D4");
|
return IRaCISBasicConfig.DoctorCodePrefix + codeInt.ToString("D4");
|
||||||
|
|
||||||
case nameof(User):
|
case nameof(IdentityUser):
|
||||||
return IRaCISBasicConfig.UserCodePrefix + codeInt.ToString("D4");
|
return IRaCISBasicConfig.UserCodePrefix + codeInt.ToString("D4");
|
||||||
|
|
||||||
case nameof(QCChallenge):
|
case nameof(QCChallenge):
|
||||||
|
|
|
@ -10,5 +10,6 @@ global using Microsoft.Extensions.Localization;
|
||||||
global using AutoMapper;
|
global using AutoMapper;
|
||||||
global using IRaCIS.Core.Domain.Share;
|
global using IRaCIS.Core.Domain.Share;
|
||||||
global using IRaCIS.Core.Application.BusinessFilter;
|
global using IRaCIS.Core.Application.BusinessFilter;
|
||||||
|
global using IdentityUser = IRaCIS.Core.Domain.Models.IdentityUser;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,11 @@ public static class CacheKeys
|
||||||
//超时没请求接口自动退出
|
//超时没请求接口自动退出
|
||||||
public static string UserAutoLoginOut(Guid userId) => $"UserAutoLoginOut:{userId}";
|
public static string UserAutoLoginOut(Guid userId) => $"UserAutoLoginOut:{userId}";
|
||||||
|
|
||||||
|
|
||||||
|
public static string UserDisable(Guid userId) => $"UserDisable:{userId}";
|
||||||
|
|
||||||
|
public static string UserRoleDisable(Guid userRoleId) => $"UserRoleDisable:{userRoleId}";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 用户登录错误 限制登录
|
/// 用户登录错误 限制登录
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -11,6 +11,12 @@ public static class SendEmailHelper
|
||||||
|
|
||||||
public static async Task SendEmailAsync(MimeMessage messageToSend, SystemEmailSendConfig _systemEmailConfig, EventHandler<MessageSentEventArgs>? messageSentSuccess = null)
|
public static async Task SendEmailAsync(MimeMessage messageToSend, SystemEmailSendConfig _systemEmailConfig, EventHandler<MessageSentEventArgs>? messageSentSuccess = null)
|
||||||
{
|
{
|
||||||
|
//没有收件人 那么不发送
|
||||||
|
if (messageToSend.To.Count == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using (var smtp = new MailKit.Net.Smtp.SmtpClient())
|
using (var smtp = new MailKit.Net.Smtp.SmtpClient())
|
||||||
|
|
|
@ -64,7 +64,7 @@ public static class ExcelExportHelper
|
||||||
foreach (var key in dic.Keys)
|
foreach (var key in dic.Keys)
|
||||||
{
|
{
|
||||||
//是数组 那么找到对应的属性 进行翻译
|
//是数组 那么找到对应的属性 进行翻译
|
||||||
if (dic[key]!=null && dic[key].GetType().GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IList<>)))
|
if (dic[key] != null && dic[key].GetType().GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IList<>)))
|
||||||
{
|
{
|
||||||
|
|
||||||
var newObjList = new List<object>();
|
var newObjList = new List<object>();
|
||||||
|
@ -243,6 +243,7 @@ public static class ExcelExportHelper
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public class DynamicColumnConfig
|
public class DynamicColumnConfig
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -255,13 +256,19 @@ public static class ExcelExportHelper
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int AutoColumnTitleRowIndex { get; set; }
|
public int AutoColumnTitleRowIndex { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 模板列最后的索引
|
||||||
|
/// </summary>
|
||||||
public int TempalteLastColumnIndex { get; set; }
|
public int TempalteLastColumnIndex { get; set; }
|
||||||
|
|
||||||
|
public bool IsCDISCExport { get; set; }=false;
|
||||||
|
|
||||||
|
//public List<string> CDISCList { get; set; } = new List<string>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 动态的列名
|
/// 动态的列名 如果Id 重复,那么就按照名称填充,否则就按照Id 填充列数据
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public List<string> ColumnNameList { get; set; } = new List<string>();
|
public List<ColumItem> ColumnIdNameList { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 动态翻译的字典名
|
/// 动态翻译的字典名
|
||||||
|
@ -288,7 +295,31 @@ public static class ExcelExportHelper
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string DynamicItemTitleName { get; set; }
|
public string DynamicItemTitleName { get; set; }
|
||||||
|
|
||||||
|
public string DynamicItemTitleId { get; set; }
|
||||||
|
|
||||||
public List<int> RemoveColunmIndexList { get; set; } = new List<int>();
|
public List<int> RemoveColunmIndexList { get; set; } = new List<int>();
|
||||||
|
|
||||||
|
public class ColumItem
|
||||||
|
{
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
public string CDISCCode { get; set; }
|
||||||
|
|
||||||
|
public override bool Equals(object obj)
|
||||||
|
{
|
||||||
|
if (obj is not ColumItem other) return false;
|
||||||
|
return Id == other.Id && Name == other.Name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
return HashCode.Combine(Id, Name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<string> ColumnIdList => ColumnIdNameList == null ? new List<string>() : ColumnIdNameList.Select(t => t.Id.ToString()).ToList();
|
||||||
|
public List<string> ColumnNameList => ColumnIdNameList == null ? new List<string>() : ColumnIdNameList.Select(t => t.Name).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -421,7 +452,7 @@ public static class ExcelExportHelper
|
||||||
workbook.RemoveSheetAt(1);
|
workbook.RemoveSheetAt(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
//中文替换项目术语
|
#region 中文替换项目术语
|
||||||
if (data.TrialObjectNameList?.Count > 0)
|
if (data.TrialObjectNameList?.Count > 0)
|
||||||
{
|
{
|
||||||
var replaceObjectList = data.TrialObjectNameList;
|
var replaceObjectList = data.TrialObjectNameList;
|
||||||
|
@ -457,21 +488,378 @@ public static class ExcelExportHelper
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (dynamicColumnConfig != null)
|
if (dynamicColumnConfig != null)
|
||||||
{
|
{
|
||||||
|
//var isCdics = dynamicColumnConfig.CDISCList.Count > 0;
|
||||||
|
|
||||||
|
var isCdics = dynamicColumnConfig.IsCDISCExport;
|
||||||
|
|
||||||
var sheet = workbook.GetSheetAt(0);
|
var sheet = workbook.GetSheetAt(0);
|
||||||
|
|
||||||
|
var cdicsRow = sheet.GetRow(dynamicColumnConfig.AutoColumnTitleRowIndex - 1);
|
||||||
var titelRow = sheet.GetRow(dynamicColumnConfig.AutoColumnTitleRowIndex);
|
var titelRow = sheet.GetRow(dynamicColumnConfig.AutoColumnTitleRowIndex);
|
||||||
var templateRow = sheet.GetRow(dynamicColumnConfig.AutoColumnTitleRowIndex + 1);
|
var templateRow = sheet.GetRow(dynamicColumnConfig.AutoColumnTitleRowIndex + 1);
|
||||||
|
|
||||||
//动态移除列的数量
|
//动态移除列的数量
|
||||||
var dynamicRemoveColunmCount = dynamicColumnConfig.RemoveColunmIndexList.Count();
|
var dynamicRemoveColunmCount = dynamicColumnConfig.RemoveColunmIndexList.Count();
|
||||||
|
|
||||||
|
//在动态列开始前移除的数量
|
||||||
var beforeDynamicRemoveCount = dynamicColumnConfig.RemoveColunmIndexList.Where(t => t < dynamicColumnConfig.AutoColumnStartIndex).Count();
|
var beforeDynamicRemoveCount = dynamicColumnConfig.RemoveColunmIndexList.Where(t => t < dynamicColumnConfig.AutoColumnStartIndex).Count();
|
||||||
|
|
||||||
//动态添加列的数量
|
//动态添加列的数量
|
||||||
var needAddCount = dynamicColumnConfig.ColumnNameList.Count;
|
var needAddCount = dynamicColumnConfig.ColumnIdNameList.Count;
|
||||||
|
|
||||||
|
//原始表 最终索引
|
||||||
|
var originTotalEndIndex = dynamicColumnConfig.TempalteLastColumnIndex;
|
||||||
|
|
||||||
|
//减去动态移除后原始结束索引
|
||||||
|
var originRemoveEndIndex = originTotalEndIndex - dynamicRemoveColunmCount;
|
||||||
|
|
||||||
|
//最终表 动态列开始索引
|
||||||
|
var dynamicColunmStartIndex = dynamicColumnConfig.AutoColumnStartIndex - beforeDynamicRemoveCount;
|
||||||
|
|
||||||
|
//最终表 动态列的终止索引
|
||||||
|
var dynamicColunmEndIndex = dynamicColunmStartIndex + needAddCount - 1;
|
||||||
|
|
||||||
|
//最终表 最终索引
|
||||||
|
var totalColunmEndIndex = originTotalEndIndex + needAddCount - dynamicRemoveColunmCount;
|
||||||
|
|
||||||
|
|
||||||
|
//动态列后需要移动的数量
|
||||||
|
var backMoveCount = totalColunmEndIndex - dynamicColunmEndIndex;
|
||||||
|
|
||||||
|
//删除需要动态删除的列 从大到小移除,否则索引会变
|
||||||
|
foreach (var removeIndex in dynamicColumnConfig.RemoveColunmIndexList.OrderByDescending(t => t))
|
||||||
|
{
|
||||||
|
//将后面的列向前移动
|
||||||
|
for (var i = 0; i < originTotalEndIndex - removeIndex; i++)
|
||||||
|
{
|
||||||
|
Console.WriteLine(titelRow.GetCell(removeIndex + i + 1).StringCellValue);
|
||||||
|
titelRow.GetCell(removeIndex + i).SetCellValue(titelRow.GetCell(removeIndex + i + 1).StringCellValue);
|
||||||
|
templateRow.GetCell(removeIndex + i).SetCellValue(templateRow.GetCell(removeIndex + i + 1).StringCellValue);
|
||||||
|
|
||||||
|
//后面的数据要清空
|
||||||
|
titelRow.GetCell(removeIndex + i + 1).SetCellValue("");
|
||||||
|
templateRow.GetCell(removeIndex + i + 1).SetCellValue("");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//创建新的列
|
||||||
|
for (int i = originRemoveEndIndex; i < originRemoveEndIndex + needAddCount; i++)
|
||||||
|
{
|
||||||
|
|
||||||
|
titelRow.CreateCell(i + 1);
|
||||||
|
templateRow.CreateCell(i + 1);
|
||||||
|
|
||||||
|
if (isCdics)
|
||||||
|
{
|
||||||
|
cdicsRow.CreateCell(i + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//移动Title 和下面的模板标识
|
||||||
|
|
||||||
|
var gap = totalColunmEndIndex - originRemoveEndIndex;
|
||||||
|
|
||||||
|
for (int i = totalColunmEndIndex; i > dynamicColunmEndIndex; i--)
|
||||||
|
{
|
||||||
|
|
||||||
|
titelRow.GetCell(i).SetCellValue(titelRow.GetCell(i - gap).StringCellValue);
|
||||||
|
|
||||||
|
templateRow.GetCell(i).SetCellValue(templateRow.GetCell(i - gap).StringCellValue);
|
||||||
|
|
||||||
|
if (isCdics)
|
||||||
|
{
|
||||||
|
cdicsRow.GetCell(i).SetCellValue(cdicsRow.GetCell(i - gap).StringCellValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//设置动态Tilte
|
||||||
|
|
||||||
|
for (int i = dynamicColunmStartIndex; i < dynamicColunmStartIndex + needAddCount; i++)
|
||||||
|
{
|
||||||
|
|
||||||
|
var name = dynamicColumnConfig.ColumnIdNameList[i - dynamicColunmStartIndex].Name;
|
||||||
|
|
||||||
|
titelRow.GetCell(i).SetCellValue(name);
|
||||||
|
templateRow.GetCell(i).SetCellValue("");
|
||||||
|
|
||||||
|
if (isCdics)
|
||||||
|
{
|
||||||
|
var cdicsCode = dynamicColumnConfig.ColumnIdNameList[i - dynamicColunmStartIndex].CDISCCode;
|
||||||
|
|
||||||
|
cdicsRow.GetCell(i).SetCellValue(cdicsCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
using (var memoryStream2 = new MemoryStream())
|
||||||
|
{
|
||||||
|
workbook.Write(memoryStream2, true);
|
||||||
|
|
||||||
|
memoryStream2.Seek(0, SeekOrigin.Begin);
|
||||||
|
|
||||||
|
templateStream = memoryStream2;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region MiniExcel
|
||||||
|
|
||||||
|
var memoryStream = new MemoryStream();
|
||||||
|
|
||||||
|
var config = new OpenXmlConfiguration()
|
||||||
|
{
|
||||||
|
IgnoreTemplateParameterMissing = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
//await MiniExcel.SaveAsByTemplateAsync("testmini.xlsx", templateStream.ToArray(), translateData);
|
||||||
|
|
||||||
|
await MiniExcel.SaveAsByTemplateAsync(memoryStream, templateStream.ToArray(), translateData, config);
|
||||||
|
|
||||||
|
|
||||||
|
memoryStream.Seek(0, SeekOrigin.Begin);
|
||||||
|
|
||||||
|
if (dynamicColumnConfig != null)
|
||||||
|
{
|
||||||
|
//Excel 列是按照名称填充 还是Id 填充
|
||||||
|
var isExcelAddDataWithName = dynamicColumnConfig.ColumnIdNameList.Select(t => t.Id).Distinct().Count() == 1;
|
||||||
|
|
||||||
|
var dynamicTranslateDataList = await _dictionaryService.GetBasicDataSelect(dynamicColumnConfig.TranslateDicNameList.ToArray());
|
||||||
|
|
||||||
|
// 使用NPOI 进行二次处理
|
||||||
|
var wb = new XSSFWorkbook(memoryStream);
|
||||||
|
var sheet = wb.GetSheetAt(0);
|
||||||
|
|
||||||
|
var list = translatedDic["List"] as IList;
|
||||||
|
|
||||||
|
foreach (var itemResult in list)
|
||||||
|
{
|
||||||
|
var index = list.IndexOf(itemResult);
|
||||||
|
|
||||||
|
//从第四行开始处理动态列
|
||||||
|
var row = sheet.GetRow(index + dynamicColumnConfig.AutoColumnTitleRowIndex + 1);
|
||||||
|
|
||||||
|
var itemDic = itemResult.ToDictionary();
|
||||||
|
|
||||||
|
var itemList = itemDic[dynamicColumnConfig.DynamicListName] as IList;
|
||||||
|
|
||||||
|
//这个数组是动态的,有的多,有的少,所以在此对比Title 一致才赋值
|
||||||
|
foreach (var itemObj in itemList)
|
||||||
|
{
|
||||||
|
|
||||||
|
var iteObjDic = itemObj.ToDictionary();
|
||||||
|
|
||||||
|
var itemDicName = iteObjDic[dynamicColumnConfig.DynamicItemDicName]?.ToString();
|
||||||
|
var itemValue = iteObjDic[dynamicColumnConfig.DynamicItemValueName]?.ToString();
|
||||||
|
|
||||||
|
//var writeIndex = itemList.IndexOf(itemObj) + dynamicColumnConfig.AutoColumnStartIndex;
|
||||||
|
|
||||||
|
var writeIndex = 0;
|
||||||
|
|
||||||
|
if (isExcelAddDataWithName)
|
||||||
|
{
|
||||||
|
writeIndex = dynamicColumnConfig.ColumnNameList.IndexOf(iteObjDic[dynamicColumnConfig.DynamicItemTitleName].ToString()) + dynamicColumnConfig.AutoColumnStartIndex;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
writeIndex = dynamicColumnConfig.ColumnIdList.IndexOf(iteObjDic[dynamicColumnConfig.DynamicItemTitleId].ToString()) + dynamicColumnConfig.AutoColumnStartIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (itemDicName.IsNotNullOrEmpty())
|
||||||
|
{
|
||||||
|
|
||||||
|
var translatedItemData = dynamicTranslateDataList[itemDicName].Where(t => t.Code.ToLower() == itemValue?.ToLower()).Select(t => isEn_US ? t.Value : t.ValueCN).FirstOrDefault() ?? String.Empty;
|
||||||
|
|
||||||
|
row.GetCell(writeIndex).SetCellValue(translatedItemData);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
row.GetCell(writeIndex).SetCellValue(itemValue);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var memoryStream2 = new MemoryStream();
|
||||||
|
wb.Write(memoryStream2, true);
|
||||||
|
memoryStream2.Seek(0, SeekOrigin.Begin);
|
||||||
|
|
||||||
|
memoryStream = memoryStream2;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return (memoryStream, fileName);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 暂时废弃--合并到上面
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="code"></param>
|
||||||
|
/// <param name="data"></param>
|
||||||
|
/// <param name="_commonDocumentRepository"></param>
|
||||||
|
/// <param name="_hostEnvironment"></param>
|
||||||
|
/// <param name="_dictionaryService"></param>
|
||||||
|
/// <param name="translateType"></param>
|
||||||
|
/// <param name="criterionType"></param>
|
||||||
|
/// <param name="dynamicColumnConfig"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static async Task<(MemoryStream, string)> CDISC_DataExport_Async(string code, ExcelExportInfo data, IRepository<CommonDocument> _commonDocumentRepository, IWebHostEnvironment _hostEnvironment, IDictionaryService? _dictionaryService = null, Type? translateType = null, CriterionType? criterionType = null, DynamicColumnConfig? dynamicColumnConfig = null)
|
||||||
|
{
|
||||||
|
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
|
||||||
|
//判断是否有字典翻译
|
||||||
|
|
||||||
|
object translateData = data;
|
||||||
|
|
||||||
|
Dictionary<string, object> translatedDic = default;
|
||||||
|
|
||||||
|
if (_dictionaryService != null && translateType != null)
|
||||||
|
{
|
||||||
|
|
||||||
|
//一个值 对应不同的字典翻译
|
||||||
|
var needTranslatePropertyList = translateType.GetProperties().Where(t => t.IsDefined(typeof(DictionaryTranslateAttribute), true))
|
||||||
|
.SelectMany(c =>
|
||||||
|
c.GetCustomAttributes(typeof(DictionaryTranslateAttribute), false).Select(f => (DictionaryTranslateAttribute?)f).Where(t => t.CriterionType == criterionType || t.CriterionType == null)
|
||||||
|
.Select(k => new { c.Name, k.DicParentCode, k.IsTranslateDenpendOtherProperty, k.DependPropertyName, k.DependPropertyValueStr })
|
||||||
|
).ToList();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//字典表查询出所有需要翻译的数据
|
||||||
|
|
||||||
|
var translateDataList = await _dictionaryService.GetBasicDataSelect(needTranslatePropertyList.Select(t => t.DicParentCode).Distinct().ToArray());
|
||||||
|
|
||||||
|
var dic = data.ConvertToDictionary();
|
||||||
|
//var dic = (JsonConvert.DeserializeObject<IDictionary<string, object>>(data.ToJsonNotIgnoreNull())).IfNullThrowException();
|
||||||
|
|
||||||
|
foreach (var key in dic.Keys)
|
||||||
|
{
|
||||||
|
//是数组 那么找到对应的属性 进行翻译
|
||||||
|
if (dic[key] != null && dic[key].GetType().GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IList<>)))
|
||||||
|
//if (dic[key].GetType().IsAssignableFrom(typeof(JArray)))
|
||||||
|
{
|
||||||
|
|
||||||
|
var newObjList = new List<object>();
|
||||||
|
var no = 1;
|
||||||
|
foreach (var item in dic[key] as IList)
|
||||||
|
//foreach (var item in dic[key] as JArray)
|
||||||
|
{
|
||||||
|
//var itemDic = JsonConvert.DeserializeObject<IDictionary<string, object>>(item.ToJsonNotIgnoreNull());
|
||||||
|
var itemDic = item.ConvertToDictionary();
|
||||||
|
|
||||||
|
foreach (var needTranslateProperty in needTranslatePropertyList)
|
||||||
|
{
|
||||||
|
if (itemDic.Keys.Any(t => t == needTranslateProperty.Name))
|
||||||
|
{
|
||||||
|
//翻译的属性依赖其他属性
|
||||||
|
if (needTranslateProperty.IsTranslateDenpendOtherProperty)
|
||||||
|
{
|
||||||
|
if (itemDic[needTranslateProperty.DependPropertyName]?.ToString().ToLower() == needTranslateProperty.DependPropertyValueStr.ToLower())
|
||||||
|
{
|
||||||
|
var beforeValue = itemDic[needTranslateProperty.Name]?.ToString();
|
||||||
|
|
||||||
|
itemDic[needTranslateProperty.Name] = translateDataList[needTranslateProperty.DicParentCode].Where(t => t.Code.ToLower() == beforeValue?.ToLower()).Select(t => isEn_US ? t.Value : t.ValueCN).FirstOrDefault() ?? String.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//普通翻译 或者某一标准翻译
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var beforeValue = itemDic[needTranslateProperty.Name]?.ToString();
|
||||||
|
|
||||||
|
|
||||||
|
itemDic[needTranslateProperty.Name] = translateDataList[needTranslateProperty.DicParentCode].Where(t => t.Code.ToLower() == beforeValue?.ToLower()).Select(t => isEn_US ? t.Value : t.ValueCN).FirstOrDefault() ?? String.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
itemDic.Add("No", no++);
|
||||||
|
newObjList.Add(itemDic);
|
||||||
|
}
|
||||||
|
|
||||||
|
dic[key] = newObjList;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//data = dic;
|
||||||
|
translateData = dic;
|
||||||
|
translatedDic = dic;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var (physicalPath, fileName) = await FileStoreHelper.GetCommonDocPhysicalFilePathAsync(_hostEnvironment, _commonDocumentRepository, code);
|
||||||
|
|
||||||
|
|
||||||
|
//模板路径
|
||||||
|
var tplPath = physicalPath;
|
||||||
|
|
||||||
|
#region 根据中英文 删除模板sheet
|
||||||
|
|
||||||
|
// 打开模板文件
|
||||||
|
var templateFile = new FileStream(tplPath, FileMode.Open, FileAccess.Read);
|
||||||
|
|
||||||
|
// 获取文件流
|
||||||
|
var templateStream = new MemoryStream();
|
||||||
|
templateFile.CopyTo(templateStream);
|
||||||
|
templateStream.Seek(0, SeekOrigin.Begin);
|
||||||
|
|
||||||
|
var workbook = new XSSFWorkbook(templateStream);
|
||||||
|
|
||||||
|
int sheetCount = workbook.NumberOfSheets;
|
||||||
|
|
||||||
|
if (sheetCount == 2)
|
||||||
|
{
|
||||||
|
if (isEn_US)
|
||||||
|
{
|
||||||
|
workbook.RemoveSheetAt(0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
workbook.RemoveSheetAt(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dynamicColumnConfig != null)
|
||||||
|
{
|
||||||
|
var sheet = workbook.GetSheetAt(0);
|
||||||
|
|
||||||
|
|
||||||
|
var cdicsRow = sheet.GetRow(dynamicColumnConfig.AutoColumnTitleRowIndex - 1);
|
||||||
|
var titelRow = sheet.GetRow(dynamicColumnConfig.AutoColumnTitleRowIndex);
|
||||||
|
var templateRow = sheet.GetRow(dynamicColumnConfig.AutoColumnTitleRowIndex + 1);
|
||||||
|
|
||||||
|
//动态移除列的数量
|
||||||
|
var dynamicRemoveColunmCount = dynamicColumnConfig.RemoveColunmIndexList.Count();
|
||||||
|
|
||||||
|
//在动态列开始前移除的数量
|
||||||
|
var beforeDynamicRemoveCount = dynamicColumnConfig.RemoveColunmIndexList.Where(t => t < dynamicColumnConfig.AutoColumnStartIndex).Count();
|
||||||
|
|
||||||
|
//动态添加列的数量
|
||||||
|
var needAddCount = dynamicColumnConfig.ColumnIdNameList.Count;
|
||||||
|
|
||||||
//原始表 最终索引
|
//原始表 最终索引
|
||||||
var originTotalEndIndex = dynamicColumnConfig.TempalteLastColumnIndex;
|
var originTotalEndIndex = dynamicColumnConfig.TempalteLastColumnIndex;
|
||||||
|
@ -513,6 +901,7 @@ public static class ExcelExportHelper
|
||||||
//创建新的列
|
//创建新的列
|
||||||
for (int i = originTotalEndIndex; i < originTotalEndIndex + needAddCount; i++)
|
for (int i = originTotalEndIndex; i < originTotalEndIndex + needAddCount; i++)
|
||||||
{
|
{
|
||||||
|
cdicsRow.CreateCell(i + 1);
|
||||||
titelRow.CreateCell(i + 1);
|
titelRow.CreateCell(i + 1);
|
||||||
templateRow.CreateCell(i + 1);
|
templateRow.CreateCell(i + 1);
|
||||||
}
|
}
|
||||||
|
@ -534,8 +923,11 @@ public static class ExcelExportHelper
|
||||||
|
|
||||||
for (int i = dynamicColunmStartIndex; i < dynamicColunmStartIndex + needAddCount; i++)
|
for (int i = dynamicColunmStartIndex; i < dynamicColunmStartIndex + needAddCount; i++)
|
||||||
{
|
{
|
||||||
var name = dynamicColumnConfig.ColumnNameList[i - dynamicColunmStartIndex];
|
var name = dynamicColumnConfig.ColumnIdNameList[i - dynamicColunmStartIndex].Name;
|
||||||
|
|
||||||
|
var cdicsCode = dynamicColumnConfig.ColumnIdNameList[i - dynamicColunmStartIndex].CDISCCode;
|
||||||
|
|
||||||
|
cdicsRow.GetCell(i).SetCellValue(cdicsCode);
|
||||||
titelRow.GetCell(i).SetCellValue(name);
|
titelRow.GetCell(i).SetCellValue(name);
|
||||||
templateRow.GetCell(i).SetCellValue("");
|
templateRow.GetCell(i).SetCellValue("");
|
||||||
}
|
}
|
||||||
|
@ -569,6 +961,8 @@ public static class ExcelExportHelper
|
||||||
|
|
||||||
if (dynamicColumnConfig != null)
|
if (dynamicColumnConfig != null)
|
||||||
{
|
{
|
||||||
|
var isExcelAddDataWithName = dynamicColumnConfig.ColumnIdNameList.Select(t => t.Id).Count() == 1;
|
||||||
|
|
||||||
var dynamicTranslateDataList = await _dictionaryService.GetBasicDataSelect(dynamicColumnConfig.TranslateDicNameList.ToArray());
|
var dynamicTranslateDataList = await _dictionaryService.GetBasicDataSelect(dynamicColumnConfig.TranslateDicNameList.ToArray());
|
||||||
|
|
||||||
// 使用NPOI 进行二次处理
|
// 使用NPOI 进行二次处理
|
||||||
|
@ -599,7 +993,15 @@ public static class ExcelExportHelper
|
||||||
|
|
||||||
//var writeIndex = itemList.IndexOf(itemObj) + dynamicColumnConfig.AutoColumnStartIndex;
|
//var writeIndex = itemList.IndexOf(itemObj) + dynamicColumnConfig.AutoColumnStartIndex;
|
||||||
|
|
||||||
var writeIndex = dynamicColumnConfig.ColumnNameList.IndexOf(iteObjDic[dynamicColumnConfig.DynamicItemTitleName].ToString()) + dynamicColumnConfig.AutoColumnStartIndex;
|
var writeIndex = 0;
|
||||||
|
if (isExcelAddDataWithName)
|
||||||
|
{
|
||||||
|
writeIndex = dynamicColumnConfig.ColumnNameList.IndexOf(iteObjDic[dynamicColumnConfig.DynamicItemTitleName].ToString()) + dynamicColumnConfig.AutoColumnStartIndex;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
writeIndex = dynamicColumnConfig.ColumnIdList.IndexOf(iteObjDic[dynamicColumnConfig.DynamicItemTitleId].ToString()) + dynamicColumnConfig.AutoColumnStartIndex;
|
||||||
|
}
|
||||||
|
|
||||||
if (itemDicName.IsNotNullOrEmpty())
|
if (itemDicName.IsNotNullOrEmpty())
|
||||||
{
|
{
|
||||||
|
@ -635,6 +1037,7 @@ public static class ExcelExportHelper
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 导出文件模板
|
/// 导出文件模板
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -69,6 +69,15 @@ public static class FileStoreHelper
|
||||||
return rootFolder;
|
return rootFolder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string GetDonwnloadImageFolder(IWebHostEnvironment _hostEnvironment)
|
||||||
|
{
|
||||||
|
var rootPath = GetIRaCISRootPath(_hostEnvironment);
|
||||||
|
|
||||||
|
var rootFolder = Path.Combine(rootPath, StaticData.Folder.DownloadIamgeFolder);
|
||||||
|
|
||||||
|
return rootFolder;
|
||||||
|
}
|
||||||
|
|
||||||
//根据相对路径 获取具体文件物理地址
|
//根据相对路径 获取具体文件物理地址
|
||||||
public static string GetPhysicalFilePath(IWebHostEnvironment _hostEnvironment, string relativePath)
|
public static string GetPhysicalFilePath(IWebHostEnvironment _hostEnvironment, string relativePath)
|
||||||
{
|
{
|
||||||
|
|
|
@ -20,12 +20,12 @@ namespace IRaCIS.Core.Application.Helper
|
||||||
//添加 或者更新定时任务 Id 要唯一标识一个定义任务
|
//添加 或者更新定时任务 Id 要唯一标识一个定义任务
|
||||||
public static void AddOrUpdateCronJob<T>(string jobId, Expression<Action<T>> methodCall, string cron, string queueName = "default")
|
public static void AddOrUpdateCronJob<T>(string jobId, Expression<Action<T>> methodCall, string cron, string queueName = "default")
|
||||||
{
|
{
|
||||||
RecurringJob.AddOrUpdate<T>(jobId, queueName, methodCall, cron);
|
RecurringJob.AddOrUpdate<T>(jobId, queueName, methodCall, cron, new RecurringJobOptions() { TimeZone = TimeZoneInfo.Local });
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void AddOrUpdateInitCronJob<T>(string jobId, Expression<Action<T>> methodCall, string cron)
|
public static void AddOrUpdateInitCronJob<T>(string jobId, Expression<Action<T>> methodCall, string cron)
|
||||||
{
|
{
|
||||||
RecurringJob.AddOrUpdate<T>(jobId, "sys_init", methodCall, cron);
|
RecurringJob.AddOrUpdate<T>(jobId, "sys_init", methodCall, cron, new RecurringJobOptions() { TimeZone = TimeZoneInfo.Local });
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void RemoveCronJob(string jobId)
|
public static void RemoveCronJob(string jobId)
|
||||||
|
@ -73,17 +73,36 @@ namespace IRaCIS.Core.Application.Helper
|
||||||
case EmailBusinessScenario.QCToCRCImageQuestion:
|
case EmailBusinessScenario.QCToCRCImageQuestion:
|
||||||
HangfireJobHelper.AddOrUpdateCronJob<IMediator>(jobId, t => t.Send(new QCImageQuestionRecurringEvent() { TrialId = trialId }, default), emailCron);
|
HangfireJobHelper.AddOrUpdateCronJob<IMediator>(jobId, t => t.Send(new QCImageQuestionRecurringEvent() { TrialId = trialId }, default), emailCron);
|
||||||
break;
|
break;
|
||||||
//加急阅片 10分钟
|
//加急阅片 10分钟
|
||||||
case EmailBusinessScenario.ExpeditedReading:
|
case EmailBusinessScenario.ExpeditedReading:
|
||||||
HangfireJobHelper.AddOrUpdateCronJob<IMediator>(jobId, t => t.Send(new UrgentIRUnReadTaskRecurringEvent() { TrialId = trialId }, default), emailCron);
|
HangfireJobHelper.AddOrUpdateCronJob<IMediator>(jobId, t => t.Send(new UrgentIRUnReadTaskRecurringEvent() { TrialId = trialId }, default), emailCron);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void AddOrUpdateTimingCronJob(string jobId, EmailBusinessScenario businessScenario, string emailCron)
|
||||||
|
{
|
||||||
|
switch (businessScenario)
|
||||||
|
{
|
||||||
|
|
||||||
|
case EmailBusinessScenario.GeneralTraining_ExpirationNotification:
|
||||||
|
|
||||||
|
|
||||||
|
HangfireJobHelper.AddOrUpdateCronJob<IMediator>(jobId, t => t.Send(new SystemDocumentErverDayEvent() { }, default), emailCron);
|
||||||
|
break;
|
||||||
|
case EmailBusinessScenario.TrialTraining_ExpirationNotification:
|
||||||
|
|
||||||
|
Console.WriteLine("更新项目到期job");
|
||||||
|
HangfireJobHelper.AddOrUpdateCronJob<IMediator>(jobId, t => t.Send(new TrialDocumentErverDayEvent() { }, default), emailCron);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
using IRaCIS.Core.Domain.Models;
|
||||||
|
using MaxMind.GeoIP2;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.Application.Helper
|
||||||
|
{
|
||||||
|
public static class IPCityHelper
|
||||||
|
{
|
||||||
|
|
||||||
|
public static string GetCityResponse(string ip)
|
||||||
|
{
|
||||||
|
var path = Path.Combine(AppContext.BaseDirectory, StaticData.Folder.Resources, "GeoLite2-City.mmdb");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var reader = new DatabaseReader(path))
|
||||||
|
{
|
||||||
|
|
||||||
|
var city = reader.City(ip);
|
||||||
|
|
||||||
|
//Console.WriteLine(city.Country.IsoCode); // 'US' 'CN'
|
||||||
|
//Console.WriteLine(city.Country.Name); // 'United States' 'China'
|
||||||
|
////Console.WriteLine(city.Country.Names["zh-CN"]); // '美国'
|
||||||
|
//Console.WriteLine(city.MostSpecificSubdivision.Name); // 'Minnesota' 'Hubei'
|
||||||
|
//Console.WriteLine(city.City.Name); // 'Minneapolis' 'WUHan'
|
||||||
|
|
||||||
|
return $"{city.Country.Name} | {city.MostSpecificSubdivision.Name} | {city.City.Name}";
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
|
||||||
|
return $"UN | UN | {ip}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
using AlibabaCloud.SDK.Sts20150401;
|
using AlibabaCloud.SDK.Sts20150401;
|
||||||
using Aliyun.OSS;
|
using Aliyun.OSS;
|
||||||
|
using Aliyun.OSS.Common;
|
||||||
using Amazon;
|
using Amazon;
|
||||||
using Amazon.Runtime;
|
using Amazon.Runtime;
|
||||||
using Amazon.S3;
|
using Amazon.S3;
|
||||||
|
@ -9,9 +10,12 @@ using Amazon.SecurityToken.Model;
|
||||||
using IRaCIS.Core.Infrastructure;
|
using IRaCIS.Core.Infrastructure;
|
||||||
using IRaCIS.Core.Infrastructure.NewtonsoftJson;
|
using IRaCIS.Core.Infrastructure.NewtonsoftJson;
|
||||||
using MassTransit;
|
using MassTransit;
|
||||||
|
using MassTransit.Caching.Internals;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using Minio;
|
using Minio;
|
||||||
|
using Minio.DataModel;
|
||||||
using Minio.DataModel.Args;
|
using Minio.DataModel.Args;
|
||||||
|
using Minio.Exceptions;
|
||||||
using System.Reactive.Linq;
|
using System.Reactive.Linq;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
@ -59,7 +63,7 @@ public class AliyunOSSOptions
|
||||||
|
|
||||||
public int DurationSeconds { get; set; }
|
public int DurationSeconds { get; set; }
|
||||||
|
|
||||||
|
public string PreviewEndpoint { get; set; }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,6 +109,7 @@ public class AliyunOSSTempToken
|
||||||
public string SecurityToken { get; set; }
|
public string SecurityToken { get; set; }
|
||||||
public DateTime Expiration { get; set; }
|
public DateTime Expiration { get; set; }
|
||||||
|
|
||||||
|
public string PreviewEndpoint { get; set; }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,9 +148,17 @@ public interface IOSSService
|
||||||
|
|
||||||
public Task<string> GetSignedUrl(string ossRelativePath);
|
public Task<string> GetSignedUrl(string ossRelativePath);
|
||||||
|
|
||||||
public Task DeleteFromPrefix(string prefix);
|
public Task DeleteFromPrefix(string prefix, bool isCache = false);
|
||||||
|
|
||||||
|
public Task DeleteObjects(List<string> objectKeys);
|
||||||
|
|
||||||
|
List<string> GetRootFolderNames();
|
||||||
|
|
||||||
public ObjectStoreDTO GetObjectStoreTempToken();
|
public ObjectStoreDTO GetObjectStoreTempToken();
|
||||||
|
|
||||||
|
public Task MoveObject(string sourcePath, string destPath, bool overwrite = true);
|
||||||
|
|
||||||
|
public Task<long> GetObjectSizeAsync(string sourcePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -347,7 +360,11 @@ public class OSSService : IOSSService
|
||||||
|
|
||||||
public async Task DownLoadFromOSSAsync(string ossRelativePath, string localFilePath)
|
public async Task DownLoadFromOSSAsync(string ossRelativePath, string localFilePath)
|
||||||
{
|
{
|
||||||
GetObjectStoreTempToken();
|
if (isFirstCall)
|
||||||
|
{
|
||||||
|
GetObjectStoreTempToken();
|
||||||
|
isFirstCall = false;
|
||||||
|
}
|
||||||
|
|
||||||
ossRelativePath = ossRelativePath.TrimStart('/');
|
ossRelativePath = ossRelativePath.TrimStart('/');
|
||||||
try
|
try
|
||||||
|
@ -526,12 +543,240 @@ public class OSSService : IOSSService
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 移动OSS文件到新路径
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sourcePath">原文件路径(格式:bucket/key)</param>
|
||||||
|
/// <param name="destPath">新文件路径(格式:bucket/key)</param>
|
||||||
|
/// <param name="overwrite">是否覆盖已存在的目标文件(默认true)</param>
|
||||||
|
public async Task MoveObject(string sourcePath, string destPath, bool overwrite = true)
|
||||||
|
{
|
||||||
|
GetObjectStoreTempToken();
|
||||||
|
|
||||||
|
switch (ObjectStoreServiceOptions.ObjectStoreUse)
|
||||||
|
{
|
||||||
|
case "AliyunOSS":
|
||||||
|
#region 阿里云
|
||||||
|
var aliConfig = ObjectStoreServiceOptions.AliyunOSS;
|
||||||
|
var client = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.EndPoint : aliConfig.InternalEndpoint, AliyunOSSTempToken.AccessKeyId, AliyunOSSTempToken.AccessKeySecret, AliyunOSSTempToken.SecurityToken);
|
||||||
|
if (sourcePath.StartsWith("/"))
|
||||||
|
{
|
||||||
|
sourcePath = sourcePath.Substring(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (destPath.StartsWith("/"))
|
||||||
|
{
|
||||||
|
destPath = destPath.Substring(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
var sourceBucket = aliConfig.BucketName;
|
||||||
|
var sourceKey = sourcePath;
|
||||||
|
|
||||||
|
var destBucket = aliConfig.BucketName;
|
||||||
|
|
||||||
|
var destKey = destPath;
|
||||||
|
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// 检查目标是否存在(当不允许覆盖时)
|
||||||
|
if (!overwrite && client.DoesObjectExist(destBucket, destKey))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("File Exist");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//var copyRequest = new Aliyun.OSS.CopyObjectRequest(sourceBucket, sourceKey, sourceBucket, destKey);
|
||||||
|
//var result = client.CopyObject(copyRequest);
|
||||||
|
|
||||||
|
//// 2. 删除原文件(可选,根据是否需要保留原文件)
|
||||||
|
//client.DeleteObject(sourceBucket, sourceKey);
|
||||||
|
|
||||||
|
// 执行复制
|
||||||
|
var copyRequestAli = new Aliyun.OSS.CopyObjectRequest(
|
||||||
|
sourceBucket, sourceKey,
|
||||||
|
destBucket, destKey);
|
||||||
|
|
||||||
|
// 保持原文件元数据
|
||||||
|
copyRequestAli.NewObjectMetadata = new ObjectMetadata
|
||||||
|
{
|
||||||
|
ContentType = client.GetObjectMetadata(sourceBucket, sourceKey).ContentType
|
||||||
|
};
|
||||||
|
|
||||||
|
var result = client.CopyObject(copyRequestAli);
|
||||||
|
|
||||||
|
// 删除原文件(仅在复制成功后)
|
||||||
|
client.DeleteObject(sourceBucket, sourceKey);
|
||||||
|
}
|
||||||
|
catch (OssException ex)
|
||||||
|
{
|
||||||
|
throw new Exception($"[{ex.ErrorCode}] {ex.Message}", ex);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
break;
|
||||||
|
case "MinIO":
|
||||||
|
#region MinIO
|
||||||
|
var minIOConfig = ObjectStoreServiceOptions.MinIO;
|
||||||
|
var minioClient = new MinioClient()
|
||||||
|
.WithEndpoint($"{minIOConfig.EndPoint}:{minIOConfig.Port}")
|
||||||
|
.WithCredentials(minIOConfig.AccessKeyId, minIOConfig.SecretAccessKey)
|
||||||
|
.WithSSL(minIOConfig.UseSSL)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
// 定义源路径和目标路径
|
||||||
|
string destinationKey = "b路径/文件名";
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// 1. 复制文件到新路径[2,5](@ref)
|
||||||
|
using (var memoryStream = new MemoryStream())
|
||||||
|
{
|
||||||
|
// 下载源文件流
|
||||||
|
await minioClient.GetObjectAsync(new GetObjectArgs()
|
||||||
|
.WithBucket(minIOConfig.BucketName)
|
||||||
|
.WithObject(sourcePath)
|
||||||
|
.WithCallbackStream(stream => stream.CopyTo(memoryStream)));
|
||||||
|
|
||||||
|
memoryStream.Position = 0; // 重置流位置
|
||||||
|
|
||||||
|
// 上传到新路径
|
||||||
|
await minioClient.PutObjectAsync(new PutObjectArgs()
|
||||||
|
.WithBucket(minIOConfig.BucketName)
|
||||||
|
.WithObject(destinationKey)
|
||||||
|
.WithStreamData(memoryStream)
|
||||||
|
.WithObjectSize(memoryStream.Length));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 删除原文件[1,6](@ref)
|
||||||
|
await minioClient.RemoveObjectAsync(new RemoveObjectArgs()
|
||||||
|
.WithBucket(minIOConfig.BucketName)
|
||||||
|
.WithObject(sourcePath));
|
||||||
|
}
|
||||||
|
catch (MinioException ex)
|
||||||
|
{
|
||||||
|
// 处理异常(例如:记录日志或抛出)
|
||||||
|
throw new Exception();
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
break;
|
||||||
|
case "AWS":
|
||||||
|
#region AWS
|
||||||
|
var awsConfig = ObjectStoreServiceOptions.AWS;
|
||||||
|
|
||||||
|
var credentials = new SessionAWSCredentials(
|
||||||
|
AWSTempToken.AccessKeyId,
|
||||||
|
AWSTempToken.SecretAccessKey,
|
||||||
|
AWSTempToken.SessionToken
|
||||||
|
);
|
||||||
|
|
||||||
|
var clientConfig = new AmazonS3Config
|
||||||
|
{
|
||||||
|
RegionEndpoint = RegionEndpoint.USEast1,
|
||||||
|
UseHttp = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
|
||||||
|
|
||||||
|
// 定义原路径和目标路径
|
||||||
|
|
||||||
|
|
||||||
|
// 1. 复制对象到新路径
|
||||||
|
var copyRequest = new Amazon.S3.Model.CopyObjectRequest
|
||||||
|
{
|
||||||
|
SourceBucket = awsConfig.BucketName,
|
||||||
|
SourceKey = sourcePath,
|
||||||
|
DestinationBucket = awsConfig.BucketName,
|
||||||
|
DestinationKey = destPath
|
||||||
|
};
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// 执行复制操作
|
||||||
|
await amazonS3Client.CopyObjectAsync(copyRequest);
|
||||||
|
|
||||||
|
// 2. 删除原对象
|
||||||
|
var deleteRequest = new Amazon.S3.Model.DeleteObjectRequest
|
||||||
|
{
|
||||||
|
BucketName = awsConfig.BucketName,
|
||||||
|
Key = sourcePath
|
||||||
|
};
|
||||||
|
await amazonS3Client.DeleteObjectAsync(deleteRequest);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (AmazonS3Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"ERROR: {ex.Message}");
|
||||||
|
// 可根据异常类型细化处理(如文件不存在、权限问题等)
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new BusinessValidationFailedException("ERROR");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取所有根目录名称
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public List<string> GetRootFolderNames()
|
||||||
|
{
|
||||||
|
GetObjectStoreTempToken();
|
||||||
|
var aliConfig = ObjectStoreServiceOptions.AliyunOSS;
|
||||||
|
var _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.EndPoint : aliConfig.InternalEndpoint,
|
||||||
|
AliyunOSSTempToken.AccessKeyId,
|
||||||
|
AliyunOSSTempToken.AccessKeySecret,
|
||||||
|
AliyunOSSTempToken.SecurityToken);
|
||||||
|
|
||||||
|
List<string> rootFolders = new List<string>();
|
||||||
|
string nextMarker = null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ObjectListing objectListing = null;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
// 列出根目录下的对象和文件夹
|
||||||
|
objectListing = _ossClient.ListObjects(new Aliyun.OSS.ListObjectsRequest(aliConfig.BucketName)
|
||||||
|
{
|
||||||
|
|
||||||
|
MaxKeys = 1000,
|
||||||
|
Marker = nextMarker,
|
||||||
|
Delimiter = "/" // 使用分隔符来模拟文件夹
|
||||||
|
});
|
||||||
|
|
||||||
|
// 遍历 CommonPrefixes 获取根文件夹名称
|
||||||
|
foreach (var prefix in objectListing.CommonPrefixes)
|
||||||
|
{
|
||||||
|
rootFolders.Add(prefix.TrimEnd('/')); // 去掉末尾的斜杠
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置 NextMarker 以获取下一页的数据
|
||||||
|
nextMarker = objectListing.NextMarker;
|
||||||
|
|
||||||
|
} while (objectListing.IsTruncated);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Error: {ex.Message}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return rootFolders;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 删除某个目录的文件
|
/// 删除某个目录的文件
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="prefix"></param>
|
/// <param name="prefix"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task DeleteFromPrefix(string prefix)
|
public async Task DeleteFromPrefix(string prefix, bool isCache = false)
|
||||||
{
|
{
|
||||||
GetObjectStoreTempToken();
|
GetObjectStoreTempToken();
|
||||||
|
|
||||||
|
@ -541,6 +786,21 @@ public class OSSService : IOSSService
|
||||||
|
|
||||||
var _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.EndPoint : aliConfig.InternalEndpoint, AliyunOSSTempToken.AccessKeyId, AliyunOSSTempToken.AccessKeySecret, AliyunOSSTempToken.SecurityToken);
|
var _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.EndPoint : aliConfig.InternalEndpoint, AliyunOSSTempToken.AccessKeyId, AliyunOSSTempToken.AccessKeySecret, AliyunOSSTempToken.SecurityToken);
|
||||||
|
|
||||||
|
var bucketName = string.Empty;
|
||||||
|
|
||||||
|
if (isCache)
|
||||||
|
{
|
||||||
|
Uri uri = new Uri(aliConfig.ViewEndpoint);
|
||||||
|
string host = uri.Host; // 获取 "zy-irc-test-dev-cache.oss-cn-shanghai.aliyuncs.com"
|
||||||
|
string[] parts = host.Split('.');
|
||||||
|
bucketName = parts[0];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bucketName = aliConfig.BucketName;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -549,7 +809,7 @@ public class OSSService : IOSSService
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
// 使用 prefix 模拟目录结构,设置 MaxKeys 和 NextMarker
|
// 使用 prefix 模拟目录结构,设置 MaxKeys 和 NextMarker
|
||||||
objectListing = _ossClient.ListObjects(new Aliyun.OSS.ListObjectsRequest(aliConfig.BucketName)
|
objectListing = _ossClient.ListObjects(new Aliyun.OSS.ListObjectsRequest(bucketName)
|
||||||
{
|
{
|
||||||
Prefix = prefix,
|
Prefix = prefix,
|
||||||
MaxKeys = 1000,
|
MaxKeys = 1000,
|
||||||
|
@ -561,7 +821,7 @@ public class OSSService : IOSSService
|
||||||
// 删除获取到的文件
|
// 删除获取到的文件
|
||||||
if (keys.Count > 0)
|
if (keys.Count > 0)
|
||||||
{
|
{
|
||||||
_ossClient.DeleteObjects(new Aliyun.OSS.DeleteObjectsRequest(aliConfig.BucketName, keys, false));
|
_ossClient.DeleteObjects(new Aliyun.OSS.DeleteObjectsRequest(bucketName, keys, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设置 NextMarker 以获取下一页的数据
|
// 设置 NextMarker 以获取下一页的数据
|
||||||
|
@ -618,7 +878,6 @@ public class OSSService : IOSSService
|
||||||
|
|
||||||
var awsConfig = ObjectStoreServiceOptions.AWS;
|
var awsConfig = ObjectStoreServiceOptions.AWS;
|
||||||
|
|
||||||
|
|
||||||
// 提供awsAccessKeyId和awsSecretAccessKey构造凭证
|
// 提供awsAccessKeyId和awsSecretAccessKey构造凭证
|
||||||
var credentials = new SessionAWSCredentials(AWSTempToken.AccessKeyId, AWSTempToken.SecretAccessKey, AWSTempToken.SessionToken);
|
var credentials = new SessionAWSCredentials(AWSTempToken.AccessKeyId, AWSTempToken.SecretAccessKey, AWSTempToken.SessionToken);
|
||||||
|
|
||||||
|
@ -670,6 +929,159 @@ public class OSSService : IOSSService
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task DeleteObjects(List<string> objectKeys)
|
||||||
|
{
|
||||||
|
GetObjectStoreTempToken();
|
||||||
|
|
||||||
|
if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS")
|
||||||
|
{
|
||||||
|
var aliConfig = ObjectStoreServiceOptions.AliyunOSS;
|
||||||
|
|
||||||
|
var _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.EndPoint : aliConfig.InternalEndpoint, AliyunOSSTempToken.AccessKeyId, AliyunOSSTempToken.AccessKeySecret, AliyunOSSTempToken.SecurityToken);
|
||||||
|
|
||||||
|
if (objectKeys.Count > 0)
|
||||||
|
{
|
||||||
|
var result = _ossClient.DeleteObjects(new Aliyun.OSS.DeleteObjectsRequest(aliConfig.BucketName, objectKeys, false));
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (ObjectStoreServiceOptions.ObjectStoreUse == "MinIO")
|
||||||
|
{
|
||||||
|
var minIOConfig = ObjectStoreServiceOptions.MinIO;
|
||||||
|
|
||||||
|
|
||||||
|
var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.EndPoint}:{minIOConfig.Port}")
|
||||||
|
.WithCredentials(minIOConfig.AccessKeyId, minIOConfig.SecretAccessKey).WithSSL(minIOConfig.UseSSL)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
|
||||||
|
if (objectKeys.Count > 0)
|
||||||
|
{
|
||||||
|
var objArgs = new RemoveObjectsArgs()
|
||||||
|
.WithBucket(minIOConfig.BucketName)
|
||||||
|
.WithObjects(objectKeys);
|
||||||
|
|
||||||
|
// 删除对象
|
||||||
|
await minioClient.RemoveObjectsAsync(objArgs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS")
|
||||||
|
{
|
||||||
|
|
||||||
|
var awsConfig = ObjectStoreServiceOptions.AWS;
|
||||||
|
|
||||||
|
|
||||||
|
// 提供awsAccessKeyId和awsSecretAccessKey构造凭证
|
||||||
|
var credentials = new SessionAWSCredentials(AWSTempToken.AccessKeyId, AWSTempToken.SecretAccessKey, AWSTempToken.SessionToken);
|
||||||
|
|
||||||
|
//提供awsEndPoint(域名)进行访问配置
|
||||||
|
var clientConfig = new AmazonS3Config
|
||||||
|
{
|
||||||
|
RegionEndpoint = RegionEndpoint.USEast1,
|
||||||
|
UseHttp = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
|
||||||
|
|
||||||
|
if (objectKeys.Count > 0)
|
||||||
|
{
|
||||||
|
// 准备删除请求
|
||||||
|
var deleteObjectsRequest = new Amazon.S3.Model.DeleteObjectsRequest
|
||||||
|
{
|
||||||
|
BucketName = awsConfig.BucketName,
|
||||||
|
Objects = objectKeys.Select(t => new KeyVersion() { Key = t }).ToList()
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// 批量删除对象
|
||||||
|
var deleteObjectsResponse = await amazonS3Client.DeleteObjectsAsync(deleteObjectsRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new BusinessValidationFailedException("未定义的存储介质类型");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool isFirstCall = true;
|
||||||
|
public async Task<long> GetObjectSizeAsync(string sourcePath)
|
||||||
|
{
|
||||||
|
if (isFirstCall)
|
||||||
|
{
|
||||||
|
GetObjectStoreTempToken();
|
||||||
|
isFirstCall = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var objectkey = sourcePath.Trim('/');
|
||||||
|
|
||||||
|
if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS")
|
||||||
|
{
|
||||||
|
var aliConfig = ObjectStoreServiceOptions.AliyunOSS;
|
||||||
|
|
||||||
|
var _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.EndPoint : aliConfig.InternalEndpoint, AliyunOSSTempToken.AccessKeyId, AliyunOSSTempToken.AccessKeySecret, AliyunOSSTempToken.SecurityToken);
|
||||||
|
|
||||||
|
var metadata = _ossClient.GetObjectMetadata(aliConfig.BucketName, objectkey);
|
||||||
|
|
||||||
|
long fileSize = metadata.ContentLength; // 文件大小(字节)
|
||||||
|
|
||||||
|
return fileSize;
|
||||||
|
}
|
||||||
|
else if (ObjectStoreServiceOptions.ObjectStoreUse == "MinIO")
|
||||||
|
{
|
||||||
|
var minIOConfig = ObjectStoreServiceOptions.MinIO;
|
||||||
|
|
||||||
|
|
||||||
|
var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.EndPoint}:{minIOConfig.Port}")
|
||||||
|
.WithCredentials(minIOConfig.AccessKeyId, minIOConfig.SecretAccessKey).WithSSL(minIOConfig.UseSSL)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
|
||||||
|
var stat = await minioClient.StatObjectAsync(new Minio.DataModel.Args.StatObjectArgs()
|
||||||
|
.WithBucket(minIOConfig.BucketName)
|
||||||
|
.WithObject(objectkey));
|
||||||
|
|
||||||
|
return stat.Size; // 文件大小(字节)
|
||||||
|
}
|
||||||
|
else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS")
|
||||||
|
{
|
||||||
|
|
||||||
|
var awsConfig = ObjectStoreServiceOptions.AWS;
|
||||||
|
|
||||||
|
|
||||||
|
// 提供awsAccessKeyId和awsSecretAccessKey构造凭证
|
||||||
|
var credentials = new SessionAWSCredentials(AWSTempToken.AccessKeyId, AWSTempToken.SecretAccessKey, AWSTempToken.SessionToken);
|
||||||
|
|
||||||
|
//提供awsEndPoint(域名)进行访问配置
|
||||||
|
var clientConfig = new AmazonS3Config
|
||||||
|
{
|
||||||
|
RegionEndpoint = RegionEndpoint.USEast1,
|
||||||
|
UseHttp = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
var request = new Amazon.S3.Model.GetObjectMetadataRequest
|
||||||
|
{
|
||||||
|
BucketName = awsConfig.BucketName,
|
||||||
|
Key = objectkey
|
||||||
|
};
|
||||||
|
|
||||||
|
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
|
||||||
|
|
||||||
|
var response = await amazonS3Client.GetObjectMetadataAsync(request);
|
||||||
|
|
||||||
|
long fileSize = response.ContentLength; // 文件大小(字节)
|
||||||
|
|
||||||
|
return fileSize;
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new BusinessValidationFailedException("未定义的存储介质类型");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public ObjectStoreDTO GetObjectStoreTempToken()
|
public ObjectStoreDTO GetObjectStoreTempToken()
|
||||||
{
|
{
|
||||||
|
@ -713,6 +1125,7 @@ public class OSSService : IOSSService
|
||||||
BucketName = ossOptions.BucketName,
|
BucketName = ossOptions.BucketName,
|
||||||
EndPoint = ossOptions.EndPoint,
|
EndPoint = ossOptions.EndPoint,
|
||||||
ViewEndpoint = ossOptions.ViewEndpoint,
|
ViewEndpoint = ossOptions.ViewEndpoint,
|
||||||
|
PreviewEndpoint = ossOptions.PreviewEndpoint
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -15,14 +15,20 @@ public static class ImageHelper
|
||||||
|
|
||||||
fileStorePath = fileStorePath ?? filePath + ".preview.jpeg";
|
fileStorePath = fileStorePath ?? filePath + ".preview.jpeg";
|
||||||
|
|
||||||
using (var image = SixLabors.ImageSharp.Image.Load(filePath))
|
// 读取 DICOM 文件
|
||||||
{
|
var dicomImage = new DicomImage(filePath);
|
||||||
|
|
||||||
|
// 渲染 DICOM 图像到 ImageSharp 格式
|
||||||
|
using (var image = dicomImage.RenderImage().AsSharpImage())
|
||||||
|
{
|
||||||
|
// 生成缩略图(调整大小)
|
||||||
image.Mutate(x => x.Resize(500, 500));
|
image.Mutate(x => x.Resize(500, 500));
|
||||||
|
|
||||||
image.Save(fileStorePath);
|
// 保存缩略图为 JPEG
|
||||||
|
image.Save(fileStorePath, new JpegEncoder());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -33,37 +33,38 @@
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="IdentityModel.OidcClient" Version="6.0.0" />
|
<PackageReference Include="IdentityModel.OidcClient" Version="6.0.0" />
|
||||||
<PackageReference Include="AlibabaCloud.SDK.Sts20150401" Version="1.1.4" />
|
<PackageReference Include="AlibabaCloud.SDK.Sts20150401" Version="1.1.5" />
|
||||||
<PackageReference Include="AWSSDK.SecurityToken" Version="3.7.400.36" />
|
<PackageReference Include="AWSSDK.SecurityToken" Version="3.7.401.81" />
|
||||||
<PackageReference Include="Aliyun.OSS.SDK.NetCore" Version="2.14.1" />
|
<PackageReference Include="Aliyun.OSS.SDK.NetCore" Version="2.14.1" />
|
||||||
<PackageReference Include="AWSSDK.S3" Version="3.7.405" />
|
<PackageReference Include="AWSSDK.S3" Version="3.7.416.8" />
|
||||||
<PackageReference Include="DocX" Version="3.0.1" />
|
<PackageReference Include="DocX" Version="4.0.25105.5786" />
|
||||||
<PackageReference Include="FreeSpire.Doc" Version="12.2.0" />
|
<PackageReference Include="FreeSpire.Doc" Version="12.2.0" />
|
||||||
<PackageReference Include="Hangfire.Core" Version="1.8.14" />
|
<PackageReference Include="Hangfire.Core" Version="1.8.18" />
|
||||||
<PackageReference Include="ExcelDataReader" Version="3.7.0" />
|
<PackageReference Include="ExcelDataReader" Version="3.7.0" />
|
||||||
<PackageReference Include="ExcelDataReader.DataSet" Version="3.7.0" />
|
<PackageReference Include="ExcelDataReader.DataSet" Version="3.7.0" />
|
||||||
<PackageReference Include="DistributedLock.Redis" Version="1.0.3" />
|
<PackageReference Include="DistributedLock.Redis" Version="1.0.3" />
|
||||||
<PackageReference Include="DistributedLock.SqlServer" Version="1.0.5" />
|
<PackageReference Include="DistributedLock.SqlServer" Version="1.0.6" />
|
||||||
<PackageReference Include="fo-dicom" Version="5.1.3" />
|
<PackageReference Include="fo-dicom" Version="5.2.1" />
|
||||||
<PackageReference Include="fo-dicom.Imaging.ImageSharp" Version="5.1.3" />
|
<PackageReference Include="fo-dicom.Imaging.ImageSharp" Version="5.2.1" />
|
||||||
<PackageReference Include="fo-dicom.Codecs" Version="5.15.1" />
|
<PackageReference Include="fo-dicom.Codecs" Version="5.16.1" />
|
||||||
<PackageReference Include="IP2Region.Net" Version="2.0.2" />
|
<PackageReference Include="IP2Region.Net" Version="2.0.2" />
|
||||||
<PackageReference Include="MailKit" Version="4.7.1.1" />
|
<PackageReference Include="MailKit" Version="4.11.0" />
|
||||||
<PackageReference Include="Masa.Contrib.Service.MinimalAPIs" Version="1.0.0" />
|
<PackageReference Include="Masa.Contrib.Service.MinimalAPIs" Version="1.0.0" />
|
||||||
|
<PackageReference Include="MaxMind.GeoIP2" Version="5.2.0" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.10" />
|
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.10" />
|
||||||
<PackageReference Include="MimeKit" Version="4.7.1" />
|
<PackageReference Include="MimeKit" Version="4.11.0" />
|
||||||
<PackageReference Include="MiniExcel" Version="1.34.2" />
|
<PackageReference Include="MiniExcel" Version="1.41.2" />
|
||||||
<PackageReference Include="Minio" Version="6.0.3" />
|
<PackageReference Include="Minio" Version="6.0.4" />
|
||||||
<PackageReference Include="MiniWord" Version="0.8.0" />
|
<PackageReference Include="MiniWord" Version="0.9.2" />
|
||||||
<PackageReference Include="My.Extensions.Localization.Json" Version="3.3.0">
|
<PackageReference Include="My.Extensions.Localization.Json" Version="3.3.0">
|
||||||
<TreatAsUsed>true</TreatAsUsed>
|
<TreatAsUsed>true</TreatAsUsed>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="NPOI" Version="2.7.1" />
|
<PackageReference Include="NPOI" Version="2.7.3" />
|
||||||
<PackageReference Include="Panda.DynamicWebApi" Version="1.2.2" />
|
<PackageReference Include="Panda.DynamicWebApi" Version="1.2.2" />
|
||||||
<PackageReference Include="RestSharp" Version="112.1.0" />
|
<PackageReference Include="RestSharp" Version="112.1.0" />
|
||||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.5" />
|
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.7" />
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore.Filters" Version="8.0.2" />
|
<PackageReference Include="Swashbuckle.AspNetCore.Filters" Version="8.0.2" />
|
||||||
<PackageReference Include="ZiggyCreatures.FusionCache" Version="1.4.0" />
|
<PackageReference Include="ZiggyCreatures.FusionCache" Version="2.1.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -14,19 +14,20 @@ namespace IRaCIS.Core.Application.MassTransit.Consumer;
|
||||||
|
|
||||||
public static class CommonEmailHelper
|
public static class CommonEmailHelper
|
||||||
{
|
{
|
||||||
public static async Task<EmailNoticeConfig> GetEmailSubejctAndHtmlInfoAndBuildAsync(IRepository<EmailNoticeConfig> _emailNoticeConfigrepository, EmailBusinessScenario scenario, MimeMessage messageToSend,
|
|
||||||
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailFunc)
|
/// <summary>
|
||||||
|
/// 系统默认邮件 + 项目默认邮件 (不用添加到项目邮件配置中,才发送)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="configInfo"></param>
|
||||||
|
/// <param name="messageToSend"></param>
|
||||||
|
/// <param name="emailFunc"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <exception cref="BusinessValidationFailedException"></exception>
|
||||||
|
public static async Task<EmailNoticeConfig> GetEmailSubejctAndHtmlInfoAndBuildAsync(EmailNoticeConfig configInfo, MimeMessage messageToSend,
|
||||||
|
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailFunc)
|
||||||
{
|
{
|
||||||
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
|
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
|
||||||
|
|
||||||
var configInfo = await _emailNoticeConfigrepository.Where(t => t.BusinessScenarioEnum == scenario).FirstOrDefaultAsync();
|
|
||||||
|
|
||||||
if (configInfo == null)
|
|
||||||
{
|
|
||||||
throw new BusinessValidationFailedException("系统未找到当前场景邮件配置信息,请联系运维人员核查");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
var (topicStr, htmlBodyStr) = isEn_US ? (configInfo.EmailTopic, configInfo.EmailHtmlContent) : (configInfo.EmailTopicCN, configInfo.EmailHtmlContentCN);
|
var (topicStr, htmlBodyStr) = isEn_US ? (configInfo.EmailTopic, configInfo.EmailHtmlContent) : (configInfo.EmailTopicCN, configInfo.EmailHtmlContentCN);
|
||||||
|
|
||||||
try
|
try
|
||||||
|
@ -36,8 +37,9 @@ public static class CommonEmailHelper
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
//"邮件模板内容有误,填充内容出现问题,请联系运维人员核查"
|
||||||
|
|
||||||
throw new BusinessValidationFailedException("邮件模板内容有误,填充内容出现问题,请联系运维人员核查");
|
throw new BusinessValidationFailedException(I18n.T("CommonEmail_ConfigError"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -52,6 +54,49 @@ public static class CommonEmailHelper
|
||||||
return configInfo;
|
return configInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 项目手动邮件 (需要添加到项目邮件配置中,才发送)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="configInfo"></param>
|
||||||
|
/// <param name="messageToSend"></param>
|
||||||
|
/// <param name="emailFunc"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <exception cref="BusinessValidationFailedException"></exception>
|
||||||
|
public static async Task<TrialEmailNoticeConfig> GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(TrialEmailNoticeConfig configInfo, MimeMessage messageToSend,
|
||||||
|
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailFunc)
|
||||||
|
{
|
||||||
|
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
|
||||||
|
|
||||||
|
|
||||||
|
var (topicStr, htmlBodyStr) = isEn_US ? (configInfo.EmailTopic, configInfo.EmailHtmlContent) : (configInfo.EmailTopicCN, configInfo.EmailHtmlContentCN);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
//每个场景修改主题 和body的逻辑不一样
|
||||||
|
(topicStr, htmlBodyStr) = emailFunc((topicStr, htmlBodyStr));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
//"邮件模板内容有误,填充内容出现问题,请联系运维人员核查"
|
||||||
|
|
||||||
|
throw new BusinessValidationFailedException(I18n.T("CommonEmail_ConfigError"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
messageToSend.Subject = topicStr;
|
||||||
|
|
||||||
|
var builder = new BodyBuilder();
|
||||||
|
|
||||||
|
builder.HtmlBody = htmlBodyStr;
|
||||||
|
|
||||||
|
messageToSend.Body = builder.ToMessageBody();
|
||||||
|
|
||||||
|
return configInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static string ReplaceCompanyName(SystemEmailSendConfig _systemEmailConfig, string needDealtxt)
|
public static string ReplaceCompanyName(SystemEmailSendConfig _systemEmailConfig, string needDealtxt)
|
||||||
{
|
{
|
||||||
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
|
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
|
||||||
|
@ -74,12 +119,13 @@ public static class CommonEmailHelper
|
||||||
var enumValueList = inDto.DictionaryList.Select(x => x.EnumValue).ToList();
|
var enumValueList = inDto.DictionaryList.Select(x => x.EnumValue).ToList();
|
||||||
|
|
||||||
|
|
||||||
var dicList =await inDto.DictionaryRepository.Where(x => dictionaryCodelist.Contains(x.Parent.Code) && enumValueList.Contains(x.Code)).Select(x=>new DictionaryData() {
|
var dicList = await inDto.DictionaryRepository.Where(x => dictionaryCodelist.Contains(x.Parent.Code) && enumValueList.Contains(x.Code)).Select(x => new DictionaryData()
|
||||||
|
{
|
||||||
DictionaryCode=x.Parent.Code,
|
|
||||||
EnumValue=x.Code,
|
DictionaryCode = x.Parent.Code,
|
||||||
Value=x.Value,
|
EnumValue = x.Code,
|
||||||
ValueCN=x.ValueCN
|
Value = x.Value,
|
||||||
|
ValueCN = x.ValueCN
|
||||||
}).ToListAsync();
|
}).ToListAsync();
|
||||||
|
|
||||||
|
|
||||||
|
@ -88,7 +134,7 @@ public static class CommonEmailHelper
|
||||||
inDto.DictionaryList.ForEach(x =>
|
inDto.DictionaryList.ForEach(x =>
|
||||||
{
|
{
|
||||||
|
|
||||||
var dic = dicList.Where(y => y.EnumValue == x.EnumValue && y.DictionaryCode == x.DictionaryCode).FirstOrDefault();
|
var dic = dicList.Where(y => y.EnumValue == x.EnumValue && y.DictionaryCode.Equals(x.DictionaryCode,StringComparison.OrdinalIgnoreCase)).FirstOrDefault();
|
||||||
result.Add(dic == null ? string.Empty : (inDto.IsEn_US ? dic.Value : dic.ValueCN));
|
result.Add(dic == null ? string.Empty : (inDto.IsEn_US ? dic.Value : dic.ValueCN));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
using AutoMapper;
|
using AutoMapper;
|
||||||
|
using IRaCIS.Application.Interfaces;
|
||||||
using IRaCIS.Core.Application.Contracts.DTO;
|
using IRaCIS.Core.Application.Contracts.DTO;
|
||||||
using IRaCIS.Core.Application.MassTransit.Command;
|
using IRaCIS.Core.Application.MassTransit.Command;
|
||||||
using IRaCIS.Core.Domain.Share;
|
using IRaCIS.Core.Domain.Share;
|
||||||
using MassTransit;
|
using MassTransit;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.Extensions.Localization;
|
using Microsoft.Extensions.Localization;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
|
@ -21,6 +24,9 @@ namespace IRaCIS.Core.Application.MassTransit.Consumer
|
||||||
private readonly IRepository<NoneDicomStudy> _noneDicomStudyRepository;
|
private readonly IRepository<NoneDicomStudy> _noneDicomStudyRepository;
|
||||||
public IStringLocalizer _localizer { get; set; }
|
public IStringLocalizer _localizer { get; set; }
|
||||||
|
|
||||||
|
private readonly SystemEmailSendConfig _systemEmailConfig;
|
||||||
|
|
||||||
|
private readonly IRepository<Dictionary> _dictionaryRepository;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 构造函数注入
|
/// 构造函数注入
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -28,7 +34,7 @@ namespace IRaCIS.Core.Application.MassTransit.Consumer
|
||||||
public ConsistencyCheckConsumer(IRepository<DicomStudy> studyRepository, IUserInfo userInfo,
|
public ConsistencyCheckConsumer(IRepository<DicomStudy> studyRepository, IUserInfo userInfo,
|
||||||
IRepository<Subject> subjectRepository, IRepository<SubjectVisit> subjectVisitRepository,
|
IRepository<Subject> subjectRepository, IRepository<SubjectVisit> subjectVisitRepository,
|
||||||
IRepository<TrialSite> trialSiteRepository, IRepository<NoneDicomStudy> noneDicomStudyRepository,
|
IRepository<TrialSite> trialSiteRepository, IRepository<NoneDicomStudy> noneDicomStudyRepository,
|
||||||
IMapper mapper, IStringLocalizer localizer)
|
IMapper mapper, IStringLocalizer localizer, IOptionsMonitor<SystemEmailSendConfig> systemEmailConfig, IRepository<Dictionary> dictionaryRepository)
|
||||||
{
|
{
|
||||||
_noneDicomStudyRepository = noneDicomStudyRepository;
|
_noneDicomStudyRepository = noneDicomStudyRepository;
|
||||||
_studyRepository = studyRepository;
|
_studyRepository = studyRepository;
|
||||||
|
@ -38,6 +44,9 @@ namespace IRaCIS.Core.Application.MassTransit.Consumer
|
||||||
_trialSiteRepository = trialSiteRepository;
|
_trialSiteRepository = trialSiteRepository;
|
||||||
_mapper = mapper;
|
_mapper = mapper;
|
||||||
_localizer = localizer;
|
_localizer = localizer;
|
||||||
|
|
||||||
|
_systemEmailConfig = systemEmailConfig.CurrentValue;
|
||||||
|
_dictionaryRepository = dictionaryRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -47,6 +56,9 @@ namespace IRaCIS.Core.Application.MassTransit.Consumer
|
||||||
|
|
||||||
var trialId = context.Message.TrialId;
|
var trialId = context.Message.TrialId;
|
||||||
|
|
||||||
|
//系统定义MRI 到底是MR 还是MRI
|
||||||
|
var mriModality = await _dictionaryRepository.Where(t => t.Parent.Code == "Modality").Where(t => t.Code == "MRI").Select(t => t.Value).FirstNotNullAsync();
|
||||||
|
|
||||||
//处理Excel大小写
|
//处理Excel大小写
|
||||||
context.Message.ETCList.ForEach(t => { t.Modality = t.Modality.ToUpper().Trim(); t.StudyDate = Convert.ToDateTime(t.StudyDate).ToString("yyyy-MM-dd"); t.SiteCode = t.SiteCode.ToUpper().Trim(); t.VisitName = t.VisitName.ToUpper().Trim(); t.SubjectCode = t.SubjectCode.ToUpper().Trim(); });
|
context.Message.ETCList.ForEach(t => { t.Modality = t.Modality.ToUpper().Trim(); t.StudyDate = Convert.ToDateTime(t.StudyDate).ToString("yyyy-MM-dd"); t.SiteCode = t.SiteCode.ToUpper().Trim(); t.VisitName = t.VisitName.ToUpper().Trim(); t.SubjectCode = t.SubjectCode.ToUpper().Trim(); });
|
||||||
var etcList = context.Message.ETCList;
|
var etcList = context.Message.ETCList;
|
||||||
|
@ -115,6 +127,16 @@ namespace IRaCIS.Core.Application.MassTransit.Consumer
|
||||||
var etcVisitStudyList = etcList.Where(t => t.SubjectCode == sv.SubjectCode && t.SiteCode == sv.SiteCode && t.VisitName == sv.VisitName).ToList();
|
var etcVisitStudyList = etcList.Where(t => t.SubjectCode == sv.SubjectCode && t.SiteCode == sv.SiteCode && t.VisitName == sv.VisitName).ToList();
|
||||||
|
|
||||||
|
|
||||||
|
//以我们系统数据库为准 ,判断是MR 还是MRI, 把Excel 里的MR 或者MRI 处理成一致,MR MRI 是一致的
|
||||||
|
foreach (var item in etcVisitStudyList)
|
||||||
|
{
|
||||||
|
if (item.Modality == "MR" || item.Modality == "MRI")
|
||||||
|
{
|
||||||
|
item.Modality = mriModality;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
StringBuilder dialogMsg = new StringBuilder();
|
StringBuilder dialogMsg = new StringBuilder();
|
||||||
//---您好,根据本系统自动识别,该受试者当前访视在IRC系统中已提交的影像检查情况如下:
|
//---您好,根据本系统自动识别,该受试者当前访视在IRC系统中已提交的影像检查情况如下:
|
||||||
|
@ -186,17 +208,17 @@ namespace IRaCIS.Core.Application.MassTransit.Consumer
|
||||||
{
|
{
|
||||||
dialogMsg.AppendLine($"<br/>");
|
dialogMsg.AppendLine($"<br/>");
|
||||||
//---核对EDC数据,完全一致, 审核通过
|
//---核对EDC数据,完全一致, 审核通过
|
||||||
dialogMsg.AppendLine(_localizer["ConsistencyVerification_EDCA"]);
|
dialogMsg.AppendLine(_localizer["ConsistencyVerification_EDCA", _systemEmailConfig.SystemShortName]);
|
||||||
|
|
||||||
|
|
||||||
// dialogMsg.AppendLine(@$"<br>说明:为高效解决/处理以上全部质疑问题,麻烦您准确核实实际影像检查情况。请注意影像日期与实际检查的日期可能会不一致,部分检查(如PET -CT)可能同时存在多种模态影像。准确核实后,请回复该访视正确的影像检查情况。");
|
// dialogMsg.AppendLine(@$"<br>说明:为高效解决/处理以上全部质疑问题,麻烦您准确核实实际影像检查情况。请注意影像日期与实际检查的日期可能会不一致,部分检查(如PET -CT)可能同时存在多种模态影像。准确核实后,请回复该访视正确的影像检查情况。");
|
||||||
dbSV.CheckState = CheckStateEnum.CVPassed;
|
dbSV.CheckState = CheckStateEnum.CVPassed;
|
||||||
dbSV.CheckUserId = _userInfo.Id;
|
dbSV.CheckUserId = _userInfo.UserRoleId;
|
||||||
dbSV.CheckPassedTime = DateTime.Now;
|
dbSV.CheckPassedTime = DateTime.Now;
|
||||||
dbSV.CheckChallengeState = CheckChanllengeTypeEnum.Closed;
|
dbSV.CheckChallengeState = CheckChanllengeTypeEnum.Closed;
|
||||||
|
|
||||||
//---核对EDC数据,完全一致
|
//---核对EDC数据,完全一致
|
||||||
dbSV.CheckResult = _localizer["ConsistencyVerification_EDCB"];
|
dbSV.CheckResult = _localizer["ConsistencyVerification_EDCB", _systemEmailConfig.SystemShortName];
|
||||||
//---自动核查通过
|
//---自动核查通过
|
||||||
dbSV.ManualPassReason = _localizer["ConsistencyVerification_Auto"];
|
dbSV.ManualPassReason = _localizer["ConsistencyVerification_Auto"];
|
||||||
|
|
||||||
|
@ -217,14 +239,14 @@ namespace IRaCIS.Core.Application.MassTransit.Consumer
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//"Problems are as follows:
|
//"Problems are as follows:
|
||||||
dialogMsg.AppendLine($"<br/><div style='color: red'>{_localizer["ConsistencyVerification_Prob"]}</div>");
|
dialogMsg.AppendLine($"<br/><div>{_localizer["ConsistencyVerification_Prob"]}</div>");
|
||||||
|
|
||||||
num = 0;
|
num = 0;
|
||||||
foreach (var item in dbExceptExcel)
|
foreach (var item in dbExceptExcel)
|
||||||
{
|
{
|
||||||
num++;
|
num++;
|
||||||
//影像检查(EDC 缺少) ConsistencyVerification_EdcL
|
//影像检查(EDC 缺少) ConsistencyVerification_EdcL
|
||||||
dialogMsg.AppendLine($"<br/><div style='text-indent: 20px;'>{num}. {_localizer["ConsistencyVerification_EdcL", item.StudyDate, item.Modality]}</div>");
|
dialogMsg.AppendLine($"<br/><div style='text-indent: 20px;'>{num}. {_localizer["ConsistencyVerification_EdcL", item.StudyDate, item.Modality/*, _systemEmailConfig.SystemShortName*/]}</div>");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -232,15 +254,15 @@ namespace IRaCIS.Core.Application.MassTransit.Consumer
|
||||||
{
|
{
|
||||||
num++;
|
num++;
|
||||||
// 影像检查(IRC 缺少)
|
// 影像检查(IRC 缺少)
|
||||||
dialogMsg.AppendLine($"<br/><div style='text-indent: 20px;'>{num}. {_localizer["ConsistencyVerification_IrcLi", item.StudyDate, item.Modality]}</div>");
|
dialogMsg.AppendLine($"<br/><div style='text-indent: 20px;'>{num}. {_localizer["ConsistencyVerification_IrcLi", item.StudyDate, item.Modality, _systemEmailConfig.SystemShortName]}</div>");
|
||||||
}
|
}
|
||||||
|
|
||||||
dialogMsg.AppendLine($"<br/>");
|
dialogMsg.AppendLine($"<br/>");
|
||||||
dialogMsg.AppendLine(@$"<div>{_localizer["ConsistencyVerification_Desc"]}</div>");
|
dialogMsg.AppendLine(@$"<div>{_localizer["ConsistencyVerification_Desc"]}</div>");
|
||||||
|
|
||||||
dbSV.CheckResult = _localizer["ConsistencyVerification_Conf"] +
|
dbSV.CheckResult =
|
||||||
String.Join(" | ", dbExceptExcel.Select(t => $"{_localizer["ConsistencyVerification_EdcL", t.StudyDate, t.Modality]}")) + " | "
|
String.Join(" | ", dbExceptExcel.Select(t => $"{_localizer["ConsistencyVerification_EdcL", t.StudyDate, t.Modality/*, _systemEmailConfig.SystemShortName*/]}")) + " | "
|
||||||
+ String.Join(" | ", excelExceptDB.Select(t => $"{_localizer["ConsistencyVerification_IrcLi", t.StudyDate, t.Modality]}"));
|
+ String.Join(" | ", excelExceptDB.Select(t => $"{_localizer["ConsistencyVerification_IrcLi", t.StudyDate, t.Modality, _systemEmailConfig.SystemShortName]}"));
|
||||||
|
|
||||||
//新增一致性核查质疑记录
|
//新增一致性核查质疑记录
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.Application.MassTransit.Consumer.Dto
|
||||||
|
{
|
||||||
|
public class SendImageReuploadEmailInDto
|
||||||
|
{
|
||||||
|
public EmailNoticeConfig EmailNoticeConfig { get; set; }
|
||||||
|
|
||||||
|
public EmailBusinessScenario Scenario { get; set; }
|
||||||
|
|
||||||
|
public SubjectVisit SubjectVisit { get; set; }
|
||||||
|
|
||||||
|
public Guid TrialId { get; set; }
|
||||||
|
public List<UserTypeEnum> UserTypes { get; set; } = null;
|
||||||
|
|
||||||
|
public ImageBackStateEnum ImageBackStateEnum { get; set; } = ImageBackStateEnum.None;
|
||||||
|
|
||||||
|
public RequestBackStateEnum RequestBackStateEnum { get; set; } = RequestBackStateEnum.NotRequest;
|
||||||
|
|
||||||
|
public ReReadingApplyState ReReadingApplyState { get; set; } = ReReadingApplyState.Default;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,522 @@
|
||||||
|
using IRaCIS.Core.Application.Helper;
|
||||||
|
using IRaCIS.Core.Application.MassTransit.Command;
|
||||||
|
using IRaCIS.Core.Application.MassTransit.Consumer.Dto;
|
||||||
|
using IRaCIS.Core.Application.Service.Reading.Dto;
|
||||||
|
using IRaCIS.Core.Domain;
|
||||||
|
using IRaCIS.Core.Domain._DomainEvent;
|
||||||
|
using IRaCIS.Core.Domain.BaseModel;
|
||||||
|
using IRaCIS.Core.Domain.Models;
|
||||||
|
using IRaCIS.Core.Domain.Share;
|
||||||
|
using IRaCIS.Core.Infra.EFCore.Common;
|
||||||
|
using MassTransit;
|
||||||
|
using MassTransit.Mediator;
|
||||||
|
using MassTransit.Scheduling;
|
||||||
|
using Medallion.Threading;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.StaticFiles;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using MimeKit;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Panda.DynamicWebApi.Attributes;
|
||||||
|
using RestSharp;
|
||||||
|
using SkiaSharp;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.Application.MassTransit.Consumer;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 影像重传和阅片人筛选相关邮件发送
|
||||||
|
/// </summary>
|
||||||
|
public class ImageConsumer(
|
||||||
|
IRepository<UserRole> _userRoleRepository,
|
||||||
|
IRepository<TrialUserRole> _trialUseRoleRepository,
|
||||||
|
IRepository<SubjectVisit> _subjectVisitRepository,
|
||||||
|
IRepository<Trial> _trialRepository,
|
||||||
|
IRepository<Enroll> _enrollRepository,
|
||||||
|
IRepository<VisitTask> _visitTaskRepository,
|
||||||
|
IRepository<ReadModule> _readModuleRepository,
|
||||||
|
IRepository<EmailNoticeUserType> _emailNoticeUserTypeRepository,
|
||||||
|
IRepository<TrialSite> _trialSiteRepository,
|
||||||
|
IRepository<Dictionary> _dictionaryRepository,
|
||||||
|
IRepository<EmailNoticeConfig> _emailNoticeConfigrepository,
|
||||||
|
IOptionsMonitor<SystemEmailSendConfig> systemEmailConfig) :
|
||||||
|
IConsumer<DirectApplyReupdloadEvent>,
|
||||||
|
IConsumer<CheckReuploadEvent>,
|
||||||
|
IConsumer<QCChanllengeReuploadEvent>,
|
||||||
|
IConsumer<HaveReadVisitTaskReReading>,
|
||||||
|
IConsumer<ReviewerPMApplyEvent>,
|
||||||
|
IConsumer<ReviewerSPMApprovedEvent>
|
||||||
|
{
|
||||||
|
private readonly SystemEmailSendConfig _systemEmailConfig = systemEmailConfig.CurrentValue;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 处理直接申请流程的重传事件
|
||||||
|
/// </summary>
|
||||||
|
public async Task Consume(ConsumeContext<DirectApplyReupdloadEvent> context)
|
||||||
|
{
|
||||||
|
Console.WriteLine("发送【影像重传-直接申请流程】邮件!!!");
|
||||||
|
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
|
||||||
|
|
||||||
|
var subjectVisitId = context.Message.SubjectVisitId;
|
||||||
|
var subjectVisit = await _subjectVisitRepository.Where(x => x.Id == subjectVisitId).Include(x => x.Subject).FirstNotNullAsync();
|
||||||
|
var trialId = subjectVisit.TrialId;
|
||||||
|
List<UserTypeEnum> userTypes = null;
|
||||||
|
// 根据申请角色和状态确定邮件场景
|
||||||
|
EmailBusinessScenario scenario;
|
||||||
|
if (context.Message.ImageBackState == ImageBackStateEnum.None)
|
||||||
|
{
|
||||||
|
switch (context.Message.ApplyUserRole)
|
||||||
|
{
|
||||||
|
case ImageBackApplyEnum.IQCRequestBack:
|
||||||
|
scenario = EmailBusinessScenario.ImageQC_IQCApplyRe_Uploading; // 64
|
||||||
|
break;
|
||||||
|
case ImageBackApplyEnum.CRCRequestBack:
|
||||||
|
scenario = EmailBusinessScenario.ImageUploadCRCApplyRe_Uploading; // 65
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
scenario = EmailBusinessScenario.ImageQC_IQCApplyRe_Uploading; // 64
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
scenario = EmailBusinessScenario.ImageReUpload_PMApprovalRe_Uploading; // 66
|
||||||
|
if (context.Message.ApplyUserRole == ImageBackApplyEnum.IQCRequestBack)
|
||||||
|
{
|
||||||
|
userTypes = new List<UserTypeEnum>() { UserTypeEnum.IQC, UserTypeEnum.ClinicalResearchCoordinator };
|
||||||
|
}
|
||||||
|
else if (context.Message.ApplyUserRole == ImageBackApplyEnum.CRCRequestBack)
|
||||||
|
{
|
||||||
|
userTypes = new List<UserTypeEnum>() { UserTypeEnum.ClinicalResearchCoordinator };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var emailConfig = _emailNoticeConfigrepository.Where(t => t.BusinessScenarioEnum == scenario && t.IsEnable && t.IsAutoSend).FirstOrDefault();
|
||||||
|
|
||||||
|
if (emailConfig != null)
|
||||||
|
{
|
||||||
|
var inDto = new SendImageReuploadEmailInDto
|
||||||
|
{
|
||||||
|
EmailNoticeConfig = emailConfig,
|
||||||
|
Scenario = scenario,
|
||||||
|
SubjectVisit = subjectVisit,
|
||||||
|
TrialId = trialId,
|
||||||
|
UserTypes = userTypes,
|
||||||
|
ImageBackStateEnum = context.Message.ImageBackState
|
||||||
|
};
|
||||||
|
await SendImageReuploadEmail(inDto);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 处理一致性核查申请重传事件
|
||||||
|
/// </summary>
|
||||||
|
public async Task Consume(ConsumeContext<CheckReuploadEvent> context)
|
||||||
|
{
|
||||||
|
Console.WriteLine("发送【一致性核查-影像重传】邮件!!!");
|
||||||
|
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
|
||||||
|
|
||||||
|
var subjectVisitId = context.Message.SubjectVisitId;
|
||||||
|
var subjectVisit = await _subjectVisitRepository.Where(x => x.Id == subjectVisitId).Include(x => x.Subject).FirstNotNullAsync();
|
||||||
|
var trialId = subjectVisit.TrialId;
|
||||||
|
|
||||||
|
// 根据状态确定邮件场景
|
||||||
|
EmailBusinessScenario scenario;
|
||||||
|
switch (context.Message.RequestBackState)
|
||||||
|
{
|
||||||
|
case RequestBackStateEnum.CRC_RequestBack:
|
||||||
|
scenario = EmailBusinessScenario.DataReconciliation_CRCApplyRe_Uploading; // 62
|
||||||
|
break;
|
||||||
|
case RequestBackStateEnum.PM_AgressBack:
|
||||||
|
case RequestBackStateEnum.PM_NotAgree:
|
||||||
|
scenario = EmailBusinessScenario.DataReconciliation_PMApprovalRe_Uploading; // 63
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return; // 其他状态不发送邮件
|
||||||
|
}
|
||||||
|
|
||||||
|
var emailConfig = _emailNoticeConfigrepository.Where(t => t.BusinessScenarioEnum == scenario && t.IsEnable && t.IsAutoSend).FirstOrDefault();
|
||||||
|
|
||||||
|
if (emailConfig != null)
|
||||||
|
{
|
||||||
|
var inDto = new SendImageReuploadEmailInDto
|
||||||
|
{
|
||||||
|
EmailNoticeConfig = emailConfig,
|
||||||
|
Scenario = scenario,
|
||||||
|
SubjectVisit = subjectVisit,
|
||||||
|
TrialId = trialId,
|
||||||
|
RequestBackStateEnum = context.Message.RequestBackState,
|
||||||
|
};
|
||||||
|
await SendImageReuploadEmail(inDto);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 处理质疑CRC申请重传事件
|
||||||
|
/// </summary>
|
||||||
|
public async Task Consume(ConsumeContext<QCChanllengeReuploadEvent> context)
|
||||||
|
{
|
||||||
|
Console.WriteLine("发送【影像质疑-影像重传】邮件!!!");
|
||||||
|
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
|
||||||
|
|
||||||
|
var subjectVisitId = context.Message.SubjectVisitId;
|
||||||
|
var subjectVisit = await _subjectVisitRepository.Where(x => x.Id == subjectVisitId).Include(x => x.Subject).FirstNotNullAsync();
|
||||||
|
var trialId = subjectVisit.TrialId;
|
||||||
|
|
||||||
|
// 根据状态确定邮件场景
|
||||||
|
EmailBusinessScenario scenario;
|
||||||
|
switch (context.Message.ReuploadEnum)
|
||||||
|
{
|
||||||
|
case QCChanllengeReuploadEnum.CRCRequestReupload:
|
||||||
|
scenario = EmailBusinessScenario.ImageQueryCRCApplyRe_Uploading; // 60
|
||||||
|
break;
|
||||||
|
case QCChanllengeReuploadEnum.QCAgreeUpload:
|
||||||
|
scenario = EmailBusinessScenario.ImageQuery_IQCApprovalRe_Uploading; // 61
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return; // 其他状态不发送邮件
|
||||||
|
}
|
||||||
|
|
||||||
|
var emailConfig = _emailNoticeConfigrepository.Where(t => t.BusinessScenarioEnum == scenario && t.IsEnable && t.IsAutoSend).FirstOrDefault();
|
||||||
|
|
||||||
|
if (emailConfig != null)
|
||||||
|
{
|
||||||
|
var inDto = new SendImageReuploadEmailInDto
|
||||||
|
{
|
||||||
|
EmailNoticeConfig = emailConfig,
|
||||||
|
Scenario = scenario,
|
||||||
|
SubjectVisit = subjectVisit,
|
||||||
|
TrialId = trialId
|
||||||
|
};
|
||||||
|
await SendImageReuploadEmail(inDto);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 处理已阅任务重阅申请事件
|
||||||
|
/// </summary>
|
||||||
|
public async Task Consume(ConsumeContext<HaveReadVisitTaskReReading> context)
|
||||||
|
{
|
||||||
|
Console.WriteLine("发送【重阅-影像重传】邮件!!!");
|
||||||
|
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
|
||||||
|
|
||||||
|
var visitTaskId = context.Message.VisitTaskId;
|
||||||
|
// 获取VisitTask关联的SubjectVisit
|
||||||
|
var visitTask = await _visitTaskRepository.Where(x => x.Id == visitTaskId).Include(x => x.Subject).Include(x => x.SourceSubjectVisit).FirstNotNullAsync();
|
||||||
|
var subjectVisit = new SubjectVisit() { };
|
||||||
|
if (visitTask.SourceSubjectVisitId != null)
|
||||||
|
{
|
||||||
|
subjectVisit = visitTask.SourceSubjectVisit;
|
||||||
|
}
|
||||||
|
else if (visitTask.SouceReadModuleId != null)
|
||||||
|
{
|
||||||
|
var readModule = await _readModuleRepository.Where(x => x.Id == visitTask.SouceReadModuleId).Include(x => x.SubjectVisit).FirstOrDefaultAsync();
|
||||||
|
subjectVisit = readModule.SubjectVisit;
|
||||||
|
}
|
||||||
|
|
||||||
|
var trialId = subjectVisit.TrialId;
|
||||||
|
|
||||||
|
// 根据状态确定邮件场景
|
||||||
|
EmailBusinessScenario scenario;
|
||||||
|
switch (context.Message.ReReadingApplyState)
|
||||||
|
{
|
||||||
|
case ReReadingApplyState.TrialGroupHaveApplyed:
|
||||||
|
scenario = EmailBusinessScenario.ReadTracking_PMApplyRe_Reading; // 67
|
||||||
|
break;
|
||||||
|
case ReReadingApplyState.Agree:
|
||||||
|
case ReReadingApplyState.Reject:
|
||||||
|
scenario = EmailBusinessScenario.Re_ReadApproval_SPMCPMApprovalRe_Reading; // 68
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return; // 其他状态不发送邮件
|
||||||
|
}
|
||||||
|
|
||||||
|
var emailConfig = _emailNoticeConfigrepository.Where(t => t.BusinessScenarioEnum == scenario && t.IsEnable && t.IsAutoSend).FirstOrDefault();
|
||||||
|
|
||||||
|
if (emailConfig != null)
|
||||||
|
{
|
||||||
|
var inDto = new SendImageReuploadEmailInDto
|
||||||
|
{
|
||||||
|
EmailNoticeConfig = emailConfig,
|
||||||
|
Scenario = scenario,
|
||||||
|
SubjectVisit = subjectVisit,
|
||||||
|
TrialId = trialId,
|
||||||
|
ReReadingApplyState= context.Message.ReReadingApplyState,
|
||||||
|
};
|
||||||
|
await SendImageReuploadEmail(inDto);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 发送影像重传相关邮件的通用方法
|
||||||
|
/// </summary>
|
||||||
|
private async Task SendImageReuploadEmail(SendImageReuploadEmailInDto inDto)
|
||||||
|
{
|
||||||
|
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
|
||||||
|
|
||||||
|
inDto.SubjectVisit = await _subjectVisitRepository.Where(x => x.Id == inDto.SubjectVisit.Id).Include(x => x.Subject).FirstNotNullAsync();
|
||||||
|
// 获取项目信息
|
||||||
|
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == inDto.TrialId);
|
||||||
|
|
||||||
|
// 根据不同场景获取不同角色的用户
|
||||||
|
var trialUser = await _trialUseRoleRepository.Where(x => x.TrialId == inDto.TrialId && !x.TrialUser.IsDeleted).Include(x => x.UserRole).Select(x => x.UserRole).ToListAsync();
|
||||||
|
|
||||||
|
// 根据场景确定收件人
|
||||||
|
List<UserRole> toUserList = new List<UserRole>();
|
||||||
|
List<UserRole> ccUserList = new List<UserRole>();
|
||||||
|
|
||||||
|
var emailNoticeUserList = await _emailNoticeUserTypeRepository.Where(x => x.EmailNoticeConfigId == inDto.EmailNoticeConfig.Id).ToListAsync();
|
||||||
|
var userTypeEnum = emailNoticeUserList.Select(x => x.UserType).ToList();
|
||||||
|
if (inDto.UserTypes != null)
|
||||||
|
{
|
||||||
|
userTypeEnum = inDto.UserTypes;
|
||||||
|
}
|
||||||
|
var crcUserList = new List<UserRole>() { };
|
||||||
|
if (userTypeEnum.Contains(UserTypeEnum.ClinicalResearchCoordinator))
|
||||||
|
{
|
||||||
|
crcUserList = await _trialSiteRepository.Where(x => x.Id == inDto.SubjectVisit.TrialSiteId).SelectMany(x => x.CRCUserList.Select(y => y.UserRole)).ToListAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inDto.UserTypes == null)
|
||||||
|
{
|
||||||
|
var toList = emailNoticeUserList.Where(x => x.EmailUserType == EmailUserType.To && x.UserType != UserTypeEnum.ClinicalResearchCoordinator).Select(x => x.UserType).ToList();
|
||||||
|
|
||||||
|
toUserList = trialUser.Where(x => toList.Contains(x.UserTypeEnum)).ToList();
|
||||||
|
|
||||||
|
if (emailNoticeUserList.Any(x => x.EmailUserType == EmailUserType.To && x.UserType == UserTypeEnum.ClinicalResearchCoordinator))
|
||||||
|
{
|
||||||
|
toUserList.AddRange(crcUserList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var toList = inDto.UserTypes.Where(x => x != UserTypeEnum.ClinicalResearchCoordinator).ToList();
|
||||||
|
|
||||||
|
toUserList = trialUser.Where(x => toList.Contains(x.UserTypeEnum)).ToList();
|
||||||
|
|
||||||
|
if (inDto.UserTypes.Any(x => x == UserTypeEnum.ClinicalResearchCoordinator))
|
||||||
|
{
|
||||||
|
toUserList.AddRange(crcUserList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var ccList = emailNoticeUserList.Where(x => x.EmailUserType == EmailUserType.Copy && x.UserType != UserTypeEnum.ClinicalResearchCoordinator).Select(x => x.UserType).ToList();
|
||||||
|
|
||||||
|
ccUserList = trialUser.Where(x => ccList.Contains(x.UserTypeEnum)).ToList();
|
||||||
|
|
||||||
|
if (emailNoticeUserList.Any(x => x.EmailUserType == EmailUserType.Copy && x.UserType == UserTypeEnum.ClinicalResearchCoordinator))
|
||||||
|
{
|
||||||
|
ccUserList.AddRange(crcUserList);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果没有收件人,则不发送邮件
|
||||||
|
if (toUserList.Count == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
List<DictionaryDto> dictionaryDtos = new List<DictionaryDto>() {
|
||||||
|
new DictionaryDto (){DictionaryCode= "YesOrNo",EnumValue=inDto.SubjectVisit.IsUrgent.ToString().ToLower(), }, //是否加急
|
||||||
|
};
|
||||||
|
switch (inDto.Scenario)
|
||||||
|
{
|
||||||
|
case EmailBusinessScenario.Re_ReadApproval_SPMCPMApprovalRe_Reading:
|
||||||
|
dictionaryDtos.Add(new DictionaryDto() { DictionaryCode = "ReReadingApplyState", EnumValue = inDto.ReReadingApplyState.GetEnumInt(), });
|
||||||
|
break;
|
||||||
|
case EmailBusinessScenario.DataReconciliation_PMApprovalRe_Uploading:
|
||||||
|
dictionaryDtos.Add(new DictionaryDto() { DictionaryCode = "RequestBackState", EnumValue = inDto.RequestBackStateEnum.GetEnumInt(), });
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
dictionaryDtos.Add(new DictionaryDto() { DictionaryCode = "ImageBackStateEnum", EnumValue = inDto.ImageBackStateEnum.GetEnumInt(), });//审批结果
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
var dictionValue = await CommonEmailHelper.TranslationDictionary(new TranslationDictionaryDto()
|
||||||
|
{
|
||||||
|
|
||||||
|
DictionaryRepository = _dictionaryRepository,
|
||||||
|
IsEn_US = isEn_US,
|
||||||
|
DictionaryList = dictionaryDtos
|
||||||
|
});
|
||||||
|
|
||||||
|
foreach (var userinfo in toUserList)
|
||||||
|
{
|
||||||
|
var messageToSend = new MimeMessage();
|
||||||
|
// 发件地址
|
||||||
|
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
|
||||||
|
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
|
||||||
|
|
||||||
|
// 添加抄送
|
||||||
|
foreach (var ccUser in ccUserList)
|
||||||
|
{
|
||||||
|
messageToSend.Cc.Add(new MailboxAddress(String.Empty, ccUser.EMail));
|
||||||
|
}
|
||||||
|
// 格式化邮件内容
|
||||||
|
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
|
||||||
|
{
|
||||||
|
var subjectCode = inDto.SubjectVisit.Subject.Code;
|
||||||
|
var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo, subjectCode, inDto.SubjectVisit.VisitName);
|
||||||
|
var htmlBodyStr = string.Format(
|
||||||
|
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
|
||||||
|
userinfo.FullName, // 用户名 {0}
|
||||||
|
trialInfo.ExperimentName, // 项目 {1}
|
||||||
|
subjectCode, // 受试者 {2}
|
||||||
|
inDto.SubjectVisit.VisitName, // 访视 {3}
|
||||||
|
dictionValue[0], // 是否加急 {4}
|
||||||
|
dictionValue[1], // 审批结果 {5}
|
||||||
|
_systemEmailConfig.SiteUrl // 链接 {6}
|
||||||
|
);
|
||||||
|
|
||||||
|
return (topicStr, htmlBodyStr);
|
||||||
|
};
|
||||||
|
|
||||||
|
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(inDto.EmailNoticeConfig, messageToSend, emailConfigFunc);
|
||||||
|
|
||||||
|
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 处理PM申请阅片人筛选事件
|
||||||
|
/// </summary>
|
||||||
|
public async Task Consume(ConsumeContext<ReviewerPMApplyEvent> context)
|
||||||
|
{
|
||||||
|
Console.WriteLine("发送【阅片人筛选-PM申请审批】邮件!!!");
|
||||||
|
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
|
||||||
|
|
||||||
|
// 获取项目信息
|
||||||
|
var enrollIdList = context.Message.EnrollIdList;
|
||||||
|
if (enrollIdList == null || !enrollIdList.Any())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取第一个Enroll的TrialId
|
||||||
|
var enrollId = enrollIdList.First();
|
||||||
|
var enroll = await _enrollRepository.Where(x => x.Id == enrollId).FirstNotNullAsync();
|
||||||
|
var trialId = enroll.TrialId;
|
||||||
|
|
||||||
|
// 设置邮件场景
|
||||||
|
var scenario = EmailBusinessScenario.ReviewerSelection_PMApplyApproving; // 69
|
||||||
|
|
||||||
|
var emailConfig = _emailNoticeConfigrepository.Where(t => t.BusinessScenarioEnum == scenario && t.IsEnable && t.IsAutoSend).FirstOrDefault();
|
||||||
|
|
||||||
|
if (emailConfig != null)
|
||||||
|
{
|
||||||
|
await SendReviewerSelectionEmail(emailConfig, trialId, enrollIdList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 处理SPM审批阅片人筛选事件
|
||||||
|
/// </summary>
|
||||||
|
public async Task Consume(ConsumeContext<ReviewerSPMApprovedEvent> context)
|
||||||
|
{
|
||||||
|
Console.WriteLine("发送【阅片人筛选-SPM审批】邮件!!!");
|
||||||
|
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
|
||||||
|
|
||||||
|
// 获取项目信息
|
||||||
|
var enrollIdList = context.Message.EnrollIdList;
|
||||||
|
if (enrollIdList == null || !enrollIdList.Any())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取第一个Enroll的TrialId
|
||||||
|
var enrollId = enrollIdList.First();
|
||||||
|
var enroll = await _enrollRepository.Where(x => x.Id == enrollId).FirstNotNullAsync();
|
||||||
|
var trialId = enroll.TrialId;
|
||||||
|
|
||||||
|
// 设置邮件场景
|
||||||
|
var scenario = EmailBusinessScenario.ReviewerSelection_SPMCPMApproval; // 70
|
||||||
|
|
||||||
|
var emailConfig = _emailNoticeConfigrepository.Where(t => t.BusinessScenarioEnum == scenario && t.IsEnable && t.IsAutoSend).FirstOrDefault();
|
||||||
|
|
||||||
|
if (emailConfig != null)
|
||||||
|
{
|
||||||
|
await SendReviewerSelectionEmail(emailConfig, trialId, enrollIdList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 发送阅片人筛选相关邮件的通用方法
|
||||||
|
/// </summary>
|
||||||
|
private async Task SendReviewerSelectionEmail(EmailNoticeConfig emailNoticeConfig, Guid trialId, List<Guid> enrollIdList)
|
||||||
|
{
|
||||||
|
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
|
||||||
|
|
||||||
|
// 获取项目信息
|
||||||
|
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == trialId);
|
||||||
|
|
||||||
|
// 根据不同场景获取不同角色的用户
|
||||||
|
var trialUser = await _trialUseRoleRepository.Where(x => x.TrialId == trialId && !x.TrialUser.IsDeleted).Include(x => x.UserRole).Select(x => x.UserRole).ToListAsync();
|
||||||
|
|
||||||
|
// 根据场景确定收件人
|
||||||
|
List<UserRole> toUserList = new List<UserRole>();
|
||||||
|
List<UserRole> ccUserList = new List<UserRole>();
|
||||||
|
|
||||||
|
var emailNoticeUserList = await _emailNoticeUserTypeRepository.Where(x => x.EmailNoticeConfigId == emailNoticeConfig.Id).ToListAsync();
|
||||||
|
|
||||||
|
var toList = emailNoticeUserList.Where(x => x.EmailUserType == EmailUserType.To).Select(x => x.UserType).ToList();
|
||||||
|
|
||||||
|
toUserList = trialUser.Where(x => toList.Contains(x.UserTypeEnum)).ToList();
|
||||||
|
|
||||||
|
var ccList = emailNoticeUserList.Where(x => x.EmailUserType == EmailUserType.Copy && x.UserType != UserTypeEnum.ClinicalResearchCoordinator).Select(x => x.UserType).ToList();
|
||||||
|
|
||||||
|
ccUserList = trialUser.Where(x => ccList.Contains(x.UserTypeEnum)).ToList();
|
||||||
|
|
||||||
|
|
||||||
|
// 如果没有收件人,则不发送邮件
|
||||||
|
if (toUserList.Count == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var userinfo in toUserList)
|
||||||
|
{
|
||||||
|
var messageToSend = new MimeMessage();
|
||||||
|
// 发件地址
|
||||||
|
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
|
||||||
|
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
|
||||||
|
|
||||||
|
// 添加抄送
|
||||||
|
foreach (var ccUser in ccUserList)
|
||||||
|
{
|
||||||
|
messageToSend.Cc.Add(new MailboxAddress(String.Empty, ccUser.EMail));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 格式化邮件内容
|
||||||
|
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
|
||||||
|
{
|
||||||
|
var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo);
|
||||||
|
var htmlBodyStr = string.Format(
|
||||||
|
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
|
||||||
|
userinfo.FullName, // 用户名 {0}
|
||||||
|
trialInfo.ExperimentName, // 项目 {1}
|
||||||
|
"", // 受试者 {2} - 阅片人筛选不涉及受试者
|
||||||
|
"", // 访视 {3} - 阅片人筛选不涉及访视
|
||||||
|
"", // 是否加急 {4}
|
||||||
|
"", // 预留 {5}
|
||||||
|
_systemEmailConfig.SiteUrl // 链接 {6}
|
||||||
|
);
|
||||||
|
|
||||||
|
return (topicStr, htmlBodyStr);
|
||||||
|
};
|
||||||
|
|
||||||
|
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(emailNoticeConfig, messageToSend, emailConfigFunc);
|
||||||
|
|
||||||
|
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,14 +22,14 @@ namespace IRaCIS.Core.Application.MassTransit.Consumer;
|
||||||
/// 加急的医学反馈任务 通知MIM
|
/// 加急的医学反馈任务 通知MIM
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class UrgentMedicalReviewAddedEventConsumer(
|
public class UrgentMedicalReviewAddedEventConsumer(
|
||||||
IRepository<User> _userRepository,
|
IRepository<UserRole> _userRoleRepository,
|
||||||
IRepository<TaskMedicalReview> _taskMedicalReviewRepository,
|
IRepository<TaskMedicalReview> _taskMedicalReviewRepository,
|
||||||
IRepository<Trial> _trialRepository,
|
IRepository<Trial> _trialRepository,
|
||||||
IRepository<SubjectVisit> _subjectVisitRepository,
|
IRepository<SubjectVisit> _subjectVisitRepository,
|
||||||
IRepository<ReadingQuestionCriterionTrial> _readingQuestionCriterionTrialRepository,
|
IRepository<ReadingQuestionCriterionTrial> _readingQuestionCriterionTrialRepository,
|
||||||
IRepository<VisitTask> _visitTaskRepository,
|
IRepository<VisitTask> _visitTaskRepository,
|
||||||
IRepository<Dictionary> _dictionaryRepository,
|
IRepository<Dictionary> _dictionaryRepository,
|
||||||
IRepository<EmailNoticeConfig> _emailNoticeConfigrepository,
|
IRepository<TrialEmailNoticeConfig> _trialEmailNoticeConfigrepository,
|
||||||
IOptionsMonitor<SystemEmailSendConfig> systemEmailConfig
|
IOptionsMonitor<SystemEmailSendConfig> systemEmailConfig
|
||||||
) : IConsumer<UrgentMedicalReviewAddedEvent>
|
) : IConsumer<UrgentMedicalReviewAddedEvent>
|
||||||
{
|
{
|
||||||
|
@ -48,52 +48,62 @@ public class UrgentMedicalReviewAddedEventConsumer(
|
||||||
|
|
||||||
var medicalReviewId = context.Message.MedicalReviewId;
|
var medicalReviewId = context.Message.MedicalReviewId;
|
||||||
|
|
||||||
var medicalReview = await _taskMedicalReviewRepository.Where(x => x.Id == medicalReviewId).Include(x => x.VisitTask).Include(x=>x.MedicalManagerUser).FirstOrDefaultAsync();
|
var medicalReview = await _taskMedicalReviewRepository.Where(x => x.Id == medicalReviewId).Include(x => x.VisitTask).Include(x => x.MedicalManagerUser).FirstOrDefaultAsync();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (medicalReview.MedicalManagerUserId == null)
|
if (medicalReview.MedicalManagerUserId == null)
|
||||||
{
|
{
|
||||||
Console.WriteLine("MIMId为空 ID"+ medicalReviewId);
|
Console.WriteLine("MIMId为空 ID" + medicalReviewId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var criterion = await _readingQuestionCriterionTrialRepository.FirstOrDefaultAsync(x => x.Id == medicalReview.VisitTask.TrialReadingCriterionId);
|
|
||||||
var userinfo = await _userRepository.Where(x => x.Id == medicalReview.MedicalManagerUserId).FirstOrDefaultAsync();
|
|
||||||
|
|
||||||
var taskInfo = await _visitTaskRepository.Where(x => x.Id == medicalReview.VisitTaskId).Include(x=>x.SourceSubjectVisit).Include(x => x.ReadModule).Include(x => x.Subject).FirstNotNullAsync();
|
var trialId = medicalReview.TrialId;
|
||||||
|
|
||||||
|
var scenario = context.Message.IsPD ? EmailBusinessScenario.PDVerification_MedicalQC : EmailBusinessScenario.EligibilityVerification_MedicalQC;
|
||||||
|
|
||||||
var visitid = taskInfo.SourceSubjectVisit == null ? taskInfo.ReadModule.SubjectVisitId : taskInfo.SourceSubjectVisitId;
|
var trialEmailConfig = _trialEmailNoticeConfigrepository.Where(t => t.TrialId == trialId && t.BusinessScenarioEnum == scenario && t.IsAutoSend && t.IsEnable).FirstOrDefault();
|
||||||
|
|
||||||
|
if (trialEmailConfig != null)
|
||||||
var subjectVisit = await _subjectVisitRepository.Where(x => x.Id == visitid).FirstOrDefaultAsync();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var messageToSend = new MimeMessage();
|
|
||||||
//发件地址
|
|
||||||
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
|
|
||||||
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
|
|
||||||
|
|
||||||
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == medicalReview.TrialId);
|
|
||||||
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
|
|
||||||
|
|
||||||
|
|
||||||
var dictionValue = await CommonEmailHelper.TranslationDictionary(new TranslationDictionaryDto()
|
|
||||||
{
|
{
|
||||||
|
|
||||||
DictionaryRepository = _dictionaryRepository,
|
|
||||||
IsEn_US = isEn_US,
|
var criterion = await _readingQuestionCriterionTrialRepository.FirstOrDefaultAsync(x => x.Id == medicalReview.VisitTask.TrialReadingCriterionId);
|
||||||
|
var userinfo = await _userRoleRepository.Where(x => x.Id == medicalReview.MedicalManagerUserId).FirstOrDefaultAsync();
|
||||||
|
|
||||||
|
var taskInfo = await _visitTaskRepository.Where(x => x.Id == medicalReview.VisitTaskId).Include(x => x.SourceSubjectVisit).Include(x => x.ReadModule).Include(x => x.Subject).FirstNotNullAsync();
|
||||||
|
|
||||||
|
|
||||||
DictionaryList = new List<DictionaryDto>()
|
var visitid = taskInfo.SourceSubjectVisit == null ? taskInfo.ReadModule.SubjectVisitId : taskInfo.SourceSubjectVisitId;
|
||||||
{
|
|
||||||
|
|
||||||
|
var subjectVisit = await _subjectVisitRepository.Where(x => x.Id == visitid).FirstOrDefaultAsync();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var messageToSend = new MimeMessage();
|
||||||
|
//发件地址
|
||||||
|
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
|
||||||
|
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
|
||||||
|
|
||||||
|
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == medicalReview.TrialId);
|
||||||
|
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
|
||||||
|
|
||||||
|
|
||||||
|
var dictionValue = await CommonEmailHelper.TranslationDictionary(new TranslationDictionaryDto()
|
||||||
|
{
|
||||||
|
|
||||||
|
DictionaryRepository = _dictionaryRepository,
|
||||||
|
IsEn_US = isEn_US,
|
||||||
|
|
||||||
|
|
||||||
|
DictionaryList = new List<DictionaryDto>()
|
||||||
|
{
|
||||||
new DictionaryDto (){DictionaryCode= "ReadingCategory",EnumValue=taskInfo.ReadingCategory.GetEnumInt(), },
|
new DictionaryDto (){DictionaryCode= "ReadingCategory",EnumValue=taskInfo.ReadingCategory.GetEnumInt(), },
|
||||||
new DictionaryDto (){DictionaryCode= "MedicalReviewDoctorUserIdea",EnumValue=medicalReview.DoctorUserIdeaEnum.GetEnumInt(), },
|
new DictionaryDto (){DictionaryCode= "MedicalReviewDoctorUserIdea",EnumValue=medicalReview.DoctorUserIdeaEnum.GetEnumInt(), },
|
||||||
new DictionaryDto (){DictionaryCode= "MedicalReviewAuditState",EnumValue=medicalReview.AuditState.GetEnumInt(), },
|
new DictionaryDto (){DictionaryCode= "MedicalReviewAuditState",EnumValue=medicalReview.AuditState.GetEnumInt(), },
|
||||||
new DictionaryDto (){DictionaryCode= "YesOrNo",EnumValue=taskInfo.IsUrgent.ToString().ToLower(), }
|
new DictionaryDto (){DictionaryCode= "YesOrNo",EnumValue=taskInfo.IsUrgent.ToString().ToLower(), }
|
||||||
}
|
}
|
||||||
}) ;
|
});
|
||||||
|
|
||||||
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
|
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
|
||||||
{
|
{
|
||||||
|
@ -107,7 +117,7 @@ public class UrgentMedicalReviewAddedEventConsumer(
|
||||||
subjectName, // 受试者 {3}
|
subjectName, // 受试者 {3}
|
||||||
taskInfo.TaskBlindName, // 访视 {4}
|
taskInfo.TaskBlindName, // 访视 {4}
|
||||||
dictionValue[0], // 任务类型 {5}
|
dictionValue[0], // 任务类型 {5}
|
||||||
//dictionValue[1], // 阅片人是否同意 {6}
|
//dictionValue[1], // 阅片人是否同意 {6}
|
||||||
dictionValue[2], // 审核状态 {6}
|
dictionValue[2], // 审核状态 {6}
|
||||||
dictionValue[3], // 是否加急 {7}
|
dictionValue[3], // 是否加急 {7}
|
||||||
_systemEmailConfig.SiteUrl // 链接 {8}
|
_systemEmailConfig.SiteUrl // 链接 {8}
|
||||||
|
@ -116,26 +126,25 @@ public class UrgentMedicalReviewAddedEventConsumer(
|
||||||
return (topicStr, htmlBodyStr);
|
return (topicStr, htmlBodyStr);
|
||||||
};
|
};
|
||||||
|
|
||||||
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(_emailNoticeConfigrepository,
|
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc);
|
||||||
|
|
||||||
context.Message.IsPD ? EmailBusinessScenario.PDVerification_MedicalQC : EmailBusinessScenario.EligibilityVerification_MedicalQC,
|
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
|
||||||
|
|
||||||
messageToSend, emailConfigFunc);
|
}
|
||||||
|
|
||||||
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//IR 回复医学反馈通知MIM
|
//IR 回复医学反馈通知MIM
|
||||||
public class UrgentIRRepliedMedicalReviewConsumer(
|
public class UrgentIRRepliedMedicalReviewConsumer(
|
||||||
IRepository<User> _userRepository,
|
IRepository<UserRole> _userRoleRepository,
|
||||||
IRepository<TaskMedicalReview> _taskMedicalReviewRepository,
|
IRepository<TaskMedicalReview> _taskMedicalReviewRepository,
|
||||||
IRepository<Trial> _trialRepository,
|
IRepository<Trial> _trialRepository,
|
||||||
IRepository<SubjectVisit> _subjectVisitRepository,
|
IRepository<SubjectVisit> _subjectVisitRepository,
|
||||||
IRepository<ReadingQuestionCriterionTrial> _readingQuestionCriterionTrialRepository,
|
IRepository<ReadingQuestionCriterionTrial> _readingQuestionCriterionTrialRepository,
|
||||||
IRepository<VisitTask> _visitTaskRepository,
|
IRepository<VisitTask> _visitTaskRepository,
|
||||||
IRepository<Dictionary> _dictionaryRepository,
|
IRepository<Dictionary> _dictionaryRepository,
|
||||||
IRepository<EmailNoticeConfig> _emailNoticeConfigrepository,
|
IRepository<TrialEmailNoticeConfig> _trialEmailNoticeConfigrepository,
|
||||||
IOptionsMonitor<SystemEmailSendConfig> systemEmailConfig) : IConsumer<UrgentIRRepliedMedicalReview>
|
IOptionsMonitor<SystemEmailSendConfig> systemEmailConfig) : IConsumer<UrgentIRRepliedMedicalReview>
|
||||||
{
|
{
|
||||||
private readonly SystemEmailSendConfig _systemEmailConfig = systemEmailConfig.CurrentValue;
|
private readonly SystemEmailSendConfig _systemEmailConfig = systemEmailConfig.CurrentValue;
|
||||||
|
@ -151,79 +160,85 @@ public class UrgentIRRepliedMedicalReviewConsumer(
|
||||||
|
|
||||||
var medicalReview = await _taskMedicalReviewRepository.Where(x => x.Id == medicalReviewId).Include(x => x.VisitTask).Include(x => x.MedicalManagerUser).FirstOrDefaultAsync();
|
var medicalReview = await _taskMedicalReviewRepository.Where(x => x.Id == medicalReviewId).Include(x => x.VisitTask).Include(x => x.MedicalManagerUser).FirstOrDefaultAsync();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (medicalReview.MedicalManagerUserId == null)
|
if (medicalReview.MedicalManagerUserId == null)
|
||||||
{
|
{
|
||||||
Console.WriteLine("MIMId为空 ID" + medicalReviewId);
|
Console.WriteLine("MIMId为空 ID" + medicalReviewId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var criterion = await _readingQuestionCriterionTrialRepository.FirstOrDefaultAsync(x => x.Id == medicalReview.VisitTask.TrialReadingCriterionId);
|
var trialId = medicalReview.TrialId;
|
||||||
var userinfo = await _userRepository.Where(x => x.Id == medicalReview.MedicalManagerUserId).FirstOrDefaultAsync();
|
|
||||||
|
|
||||||
var taskInfo = await _visitTaskRepository.Where(x => x.Id == medicalReview.VisitTaskId).Include(x => x.SourceSubjectVisit).Include(x => x.ReadModule).Include(x => x.Subject).FirstNotNullAsync();
|
var scenario = context.Message.IsPD ? EmailBusinessScenario.PDVerification_ExpeditedMedicalQCResponse : EmailBusinessScenario.EligibilityVerification_ExpeditedMedicalQCResponse;
|
||||||
|
|
||||||
|
var trialEmailConfig = _trialEmailNoticeConfigrepository.Where(t => t.TrialId == trialId && t.BusinessScenarioEnum == scenario && t.IsAutoSend && t.IsEnable).FirstOrDefault();
|
||||||
|
|
||||||
var visitid = taskInfo.SourceSubjectVisit == null ? taskInfo.ReadModule.SubjectVisitId : taskInfo.SourceSubjectVisitId;
|
if (trialEmailConfig != null)
|
||||||
|
|
||||||
|
|
||||||
var subjectVisit = await _subjectVisitRepository.Where(x => x.Id == visitid).FirstOrDefaultAsync();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var messageToSend = new MimeMessage();
|
|
||||||
//发件地址
|
|
||||||
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
|
|
||||||
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
|
|
||||||
|
|
||||||
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == medicalReview.TrialId);
|
|
||||||
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
|
|
||||||
|
|
||||||
|
|
||||||
var dictionValue = await CommonEmailHelper.TranslationDictionary(new TranslationDictionaryDto()
|
|
||||||
{
|
{
|
||||||
|
|
||||||
DictionaryRepository = _dictionaryRepository,
|
|
||||||
IsEn_US = isEn_US,
|
|
||||||
|
var criterion = await _readingQuestionCriterionTrialRepository.FirstOrDefaultAsync(x => x.Id == medicalReview.VisitTask.TrialReadingCriterionId);
|
||||||
|
var userinfo = await _userRoleRepository.Where(x => x.Id == medicalReview.MedicalManagerUserId).FirstOrDefaultAsync();
|
||||||
|
|
||||||
|
var taskInfo = await _visitTaskRepository.Where(x => x.Id == medicalReview.VisitTaskId).Include(x => x.SourceSubjectVisit).Include(x => x.ReadModule).Include(x => x.Subject).FirstNotNullAsync();
|
||||||
|
|
||||||
|
|
||||||
DictionaryList = new List<DictionaryDto>()
|
var visitid = taskInfo.SourceSubjectVisit == null ? taskInfo.ReadModule.SubjectVisitId : taskInfo.SourceSubjectVisitId;
|
||||||
|
|
||||||
|
|
||||||
|
var subjectVisit = await _subjectVisitRepository.Where(x => x.Id == visitid).FirstOrDefaultAsync();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var messageToSend = new MimeMessage();
|
||||||
|
//发件地址
|
||||||
|
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
|
||||||
|
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
|
||||||
|
|
||||||
|
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == medicalReview.TrialId);
|
||||||
|
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
|
||||||
|
|
||||||
|
|
||||||
|
var dictionValue = await CommonEmailHelper.TranslationDictionary(new TranslationDictionaryDto()
|
||||||
|
{
|
||||||
|
|
||||||
|
DictionaryRepository = _dictionaryRepository,
|
||||||
|
IsEn_US = isEn_US,
|
||||||
|
|
||||||
|
|
||||||
|
DictionaryList = new List<DictionaryDto>()
|
||||||
{
|
{
|
||||||
new DictionaryDto (){DictionaryCode= "ReadingCategory",EnumValue=taskInfo.ReadingCategory.GetEnumInt(), },
|
new DictionaryDto (){DictionaryCode= "ReadingCategory",EnumValue=taskInfo.ReadingCategory.GetEnumInt(), },
|
||||||
new DictionaryDto (){DictionaryCode= "MedicalReviewDoctorUserIdea",EnumValue=medicalReview.DoctorUserIdeaEnum.GetEnumInt(), },
|
new DictionaryDto (){DictionaryCode= "MedicalReviewDoctorUserIdea",EnumValue=medicalReview.DoctorUserIdeaEnum.GetEnumInt(), },
|
||||||
new DictionaryDto (){DictionaryCode= "MedicalReviewAuditState",EnumValue=medicalReview.AuditState.GetEnumInt(), },
|
new DictionaryDto (){DictionaryCode= "MedicalReviewAuditState",EnumValue=medicalReview.AuditState.GetEnumInt(), },
|
||||||
new DictionaryDto (){DictionaryCode= "YesOrNo",EnumValue=taskInfo.IsUrgent.ToString().ToLower(), }
|
new DictionaryDto (){DictionaryCode= "YesOrNo",EnumValue=taskInfo.IsUrgent.ToString().ToLower(), }
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
|
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
|
||||||
{
|
{
|
||||||
var subjectCode = taskInfo.BlindSubjectCode.IsNullOrEmpty() ? taskInfo.Subject.Code : taskInfo.BlindSubjectCode;
|
var subjectCode = taskInfo.BlindSubjectCode.IsNullOrEmpty() ? taskInfo.Subject.Code : taskInfo.BlindSubjectCode;
|
||||||
var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo, subjectCode, taskInfo.TaskBlindName);
|
var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo, subjectCode, taskInfo.TaskBlindName);
|
||||||
var htmlBodyStr = string.Format(
|
var htmlBodyStr = string.Format(
|
||||||
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
|
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
|
||||||
userinfo.FullName, // 用户名 {0}
|
userinfo.FullName, // 用户名 {0}
|
||||||
trialInfo.ExperimentName, // 项目 {1}
|
trialInfo.ExperimentName, // 项目 {1}
|
||||||
criterion.CriterionName, // 阅片标准 {2}
|
criterion.CriterionName, // 阅片标准 {2}
|
||||||
subjectCode, // 受试者 {3}
|
subjectCode, // 受试者 {3}
|
||||||
taskInfo.TaskBlindName, // 访视 {4}
|
taskInfo.TaskBlindName, // 访视 {4}
|
||||||
dictionValue[0], // 任务类型 {5}
|
dictionValue[0], // 任务类型 {5}
|
||||||
dictionValue[1], // 阅片人是否同意 {6}
|
dictionValue[1], // 阅片人是否同意 {6}
|
||||||
dictionValue[2], // 审核状态 {7}
|
dictionValue[2], // 审核状态 {7}
|
||||||
dictionValue[3], // 是否加急 {8}
|
dictionValue[3], // 是否加急 {8}
|
||||||
_systemEmailConfig.SiteUrl // 链接 {9}
|
_systemEmailConfig.SiteUrl // 链接 {9}
|
||||||
);
|
);
|
||||||
|
|
||||||
return (topicStr, htmlBodyStr);
|
return (topicStr, htmlBodyStr);
|
||||||
};
|
};
|
||||||
|
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc);
|
||||||
|
|
||||||
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(_emailNoticeConfigrepository,
|
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
|
||||||
|
}
|
||||||
|
|
||||||
context.Message.IsPD ? EmailBusinessScenario.PDVerification_ExpeditedMedicalQCResponse : EmailBusinessScenario.EligibilityVerification_ExpeditedMedicalQCResponse,
|
|
||||||
|
|
||||||
messageToSend, emailConfigFunc);
|
|
||||||
|
|
||||||
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -231,14 +246,14 @@ public class UrgentIRRepliedMedicalReviewConsumer(
|
||||||
/// MIM 回复医学返回通知IR
|
/// MIM 回复医学返回通知IR
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class UrgentMIMRepliedMedicalReviewConsumer(
|
public class UrgentMIMRepliedMedicalReviewConsumer(
|
||||||
IRepository<User> _userRepository,
|
IRepository<UserRole> _userRoleRepository,
|
||||||
IRepository<TaskMedicalReview> _taskMedicalReviewRepository,
|
IRepository<TaskMedicalReview> _taskMedicalReviewRepository,
|
||||||
IRepository<Trial> _trialRepository,
|
IRepository<Trial> _trialRepository,
|
||||||
IRepository<SubjectVisit> _subjectVisitRepository,
|
IRepository<SubjectVisit> _subjectVisitRepository,
|
||||||
IRepository<ReadingQuestionCriterionTrial> _readingQuestionCriterionTrialRepository,
|
IRepository<ReadingQuestionCriterionTrial> _readingQuestionCriterionTrialRepository,
|
||||||
IRepository<VisitTask> _visitTaskRepository,
|
IRepository<VisitTask> _visitTaskRepository,
|
||||||
IRepository<Dictionary> _dictionaryRepository,
|
IRepository<Dictionary> _dictionaryRepository,
|
||||||
IRepository<EmailNoticeConfig> _emailNoticeConfigrepository,
|
IRepository<TrialEmailNoticeConfig> _trialEmailNoticeConfigrepository,
|
||||||
IOptionsMonitor<SystemEmailSendConfig> systemEmailConfig) : IConsumer<UrgentMIMRepliedMedicalReview>
|
IOptionsMonitor<SystemEmailSendConfig> systemEmailConfig) : IConsumer<UrgentMIMRepliedMedicalReview>
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -254,146 +269,48 @@ public class UrgentMIMRepliedMedicalReviewConsumer(
|
||||||
Console.WriteLine("发送(022) 【加急医学反馈】邮件!!!");
|
Console.WriteLine("发送(022) 【加急医学反馈】邮件!!!");
|
||||||
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
|
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
|
||||||
|
|
||||||
|
|
||||||
var medicalReviewId = context.Message.MedicalReviewId;
|
var medicalReviewId = context.Message.MedicalReviewId;
|
||||||
|
|
||||||
var medicalReview = await _taskMedicalReviewRepository.Where(x => x.Id == medicalReviewId).Include(x => x.VisitTask).Include(x => x.MedicalManagerUser).FirstOrDefaultAsync();
|
var medicalReview = await _taskMedicalReviewRepository.Where(x => x.Id == medicalReviewId).Include(x => x.VisitTask).Include(x => x.MedicalManagerUser).FirstOrDefaultAsync();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (medicalReview.MedicalManagerUserId == null)
|
if (medicalReview.MedicalManagerUserId == null)
|
||||||
{
|
{
|
||||||
Console.WriteLine("MIMId为空 ID" + medicalReviewId);
|
Console.WriteLine("MIMId为空 ID" + medicalReviewId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var criterion = await _readingQuestionCriterionTrialRepository.FirstOrDefaultAsync(x => x.Id == medicalReview.VisitTask.TrialReadingCriterionId);
|
|
||||||
var taskInfo = await _visitTaskRepository.Where(x => x.Id == medicalReview.VisitTaskId).Include(x => x.SourceSubjectVisit).Include(x => x.ReadModule).Include(x => x.Subject).FirstNotNullAsync();
|
|
||||||
|
|
||||||
var userinfo = await _userRepository.Where(x => x.Id == taskInfo.DoctorUserId).FirstOrDefaultAsync();
|
var trialId = medicalReview.TrialId;
|
||||||
|
|
||||||
|
var scenario = EmailBusinessScenario.ExpeditedMedicalQCToIR;
|
||||||
|
|
||||||
|
var trialEmailConfig = _trialEmailNoticeConfigrepository.Where(t => t.TrialId == trialId && t.BusinessScenarioEnum == scenario && t.IsAutoSend && t.IsEnable).FirstOrDefault();
|
||||||
|
|
||||||
var visitid = taskInfo.SourceSubjectVisit == null ? taskInfo.ReadModule.SubjectVisitId : taskInfo.SourceSubjectVisitId;
|
if (trialEmailConfig != null)
|
||||||
|
|
||||||
|
|
||||||
var subjectVisit = await _subjectVisitRepository.Where(x => x.Id == visitid).FirstOrDefaultAsync();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var messageToSend = new MimeMessage();
|
|
||||||
//发件地址
|
|
||||||
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
|
|
||||||
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
|
|
||||||
|
|
||||||
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == medicalReview.TrialId);
|
|
||||||
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
|
|
||||||
|
|
||||||
|
|
||||||
var dictionValue = await CommonEmailHelper.TranslationDictionary(new TranslationDictionaryDto()
|
|
||||||
{
|
{
|
||||||
|
|
||||||
DictionaryRepository = _dictionaryRepository,
|
|
||||||
IsEn_US = isEn_US,
|
var criterion = await _readingQuestionCriterionTrialRepository.FirstOrDefaultAsync(x => x.Id == medicalReview.VisitTask.TrialReadingCriterionId);
|
||||||
|
var taskInfo = await _visitTaskRepository.Where(x => x.Id == medicalReview.VisitTaskId).Include(x => x.SourceSubjectVisit).Include(x => x.ReadModule).Include(x => x.Subject).FirstNotNullAsync();
|
||||||
|
|
||||||
|
var userinfo = await _userRoleRepository.Where(x => x.Id == taskInfo.DoctorUserId).FirstOrDefaultAsync();
|
||||||
|
|
||||||
|
|
||||||
DictionaryList = new List<DictionaryDto>()
|
|
||||||
{
|
|
||||||
new DictionaryDto (){DictionaryCode= "ReadingCategory",EnumValue=taskInfo.ReadingCategory.GetEnumInt(), },
|
|
||||||
new DictionaryDto (){DictionaryCode= "IsPass",EnumValue=medicalReview.IsHaveQuestion.ToString().ToLower(), },// 审核结论
|
|
||||||
new DictionaryDto (){DictionaryCode= "AuditAdvice",EnumValue=medicalReview.AuditAdviceEnum.GetEnumInt(), },// 审核建议
|
|
||||||
new DictionaryDto (){DictionaryCode= "YesOrNo",EnumValue=taskInfo.IsUrgent.ToString().ToLower(), }, //是否加急
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
|
|
||||||
{
|
|
||||||
var subjectCode = taskInfo.BlindSubjectCode.IsNullOrEmpty() ? taskInfo.Subject.Code : taskInfo.BlindSubjectCode;
|
|
||||||
var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo, subjectCode, taskInfo.TaskBlindName);
|
|
||||||
var htmlBodyStr = string.Format(
|
|
||||||
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
|
|
||||||
userinfo.FullName, // 用户名 {0}
|
|
||||||
trialInfo.ExperimentName, // 项目 {1}
|
|
||||||
subjectCode, // 受试者 {2}
|
|
||||||
taskInfo.TaskBlindName, // 访视 {3}
|
|
||||||
criterion.CriterionName, // 阅片标准 {4}
|
|
||||||
dictionValue[0], // 任务类型 {5}
|
|
||||||
dictionValue[1], // 审核结论 {6}
|
|
||||||
dictionValue[2], // 医学审核建议 {7}
|
|
||||||
dictionValue[3], // 是否加急 {8}
|
|
||||||
|
|
||||||
_systemEmailConfig.SiteUrl // 链接 {9}
|
|
||||||
);
|
|
||||||
|
|
||||||
return (topicStr, htmlBodyStr);
|
|
||||||
};
|
|
||||||
|
|
||||||
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(_emailNoticeConfigrepository,
|
|
||||||
|
|
||||||
EmailBusinessScenario.ExpeditedMedicalQCToIR,
|
|
||||||
|
|
||||||
messageToSend, emailConfigFunc);
|
|
||||||
|
|
||||||
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 加急阅片 IR 申请重阅 或者PM 申请重阅
|
|
||||||
/// </summary>
|
|
||||||
public class UrgentIRApplyedReReadingConsumer(
|
|
||||||
IRepository<User> _userRepository,
|
|
||||||
IRepository<TrialUser> _trialUseRepository,
|
|
||||||
IRepository<TaskMedicalReview> _taskMedicalReviewRepository,
|
|
||||||
IRepository<Trial> _trialRepository,
|
|
||||||
IRepository<SubjectVisit> _subjectVisitRepository,
|
|
||||||
IRepository<ReadingQuestionCriterionTrial> _readingQuestionCriterionTrialRepository,
|
|
||||||
IRepository<VisitTask> _visitTaskRepository,
|
|
||||||
IRepository<Dictionary> _dictionaryRepository,
|
|
||||||
IRepository<EmailNoticeConfig> _emailNoticeConfigrepository,
|
|
||||||
IOptionsMonitor<SystemEmailSendConfig> systemEmailConfig) : IConsumer<UrgentApplyedReReading>
|
|
||||||
{
|
|
||||||
|
|
||||||
private readonly SystemEmailSendConfig _systemEmailConfig = systemEmailConfig.CurrentValue;
|
|
||||||
public async Task Consume(ConsumeContext<UrgentApplyedReReading> context)
|
|
||||||
{
|
|
||||||
Console.WriteLine("发送(024,025) 【加急医学反馈】邮件!!!");
|
|
||||||
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
|
|
||||||
|
|
||||||
var visitTaskId = context.Message.VisitTaskId;
|
|
||||||
|
|
||||||
var taskInfo = await _visitTaskRepository.Where(x => x.Id == visitTaskId).Include(x => x.SourceSubjectVisit).Include(x => x.ReadModule).Include(x => x.Subject).FirstNotNullAsync();
|
|
||||||
var criterion = await _readingQuestionCriterionTrialRepository.FirstOrDefaultAsync(x => x.Id == taskInfo.TrialReadingCriterionId);
|
|
||||||
|
|
||||||
|
|
||||||
var doctorInfo = await _userRepository.Where(x => x.Id == taskInfo.DoctorUserId).FirstOrDefaultAsync();
|
|
||||||
|
|
||||||
var trialUser = await _trialUseRepository.Where(x => x.TrialId == taskInfo.TrialId).Include(x => x.User).Select(x=>x.User).ToListAsync();
|
|
||||||
|
|
||||||
var userinfoList = trialUser.Where(x => x.UserTypeEnum == UserTypeEnum.ProjectManager||x.UserTypeEnum==UserTypeEnum.APM).ToList();
|
|
||||||
if (context.Message.ReReadingApplyState == ReReadingApplyState.TrialGroupHaveApplyed)
|
|
||||||
{
|
|
||||||
userinfoList = trialUser.Where(x => x.UserTypeEnum == UserTypeEnum.CPM || x.UserTypeEnum == UserTypeEnum.SPM).ToList();
|
|
||||||
}
|
|
||||||
|
|
||||||
var visitid = taskInfo.SourceSubjectVisit == null ? taskInfo.ReadModule.SubjectVisitId : taskInfo.SourceSubjectVisitId;
|
|
||||||
|
|
||||||
|
|
||||||
var subjectVisit = await _subjectVisitRepository.Where(x => x.Id == visitid).FirstOrDefaultAsync();
|
var visitid = taskInfo.SourceSubjectVisit == null ? taskInfo.ReadModule.SubjectVisitId : taskInfo.SourceSubjectVisitId;
|
||||||
|
|
||||||
|
|
||||||
|
var subjectVisit = await _subjectVisitRepository.Where(x => x.Id == visitid).FirstOrDefaultAsync();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
foreach (var userinfo in userinfoList)
|
|
||||||
{
|
|
||||||
var messageToSend = new MimeMessage();
|
var messageToSend = new MimeMessage();
|
||||||
//发件地址
|
//发件地址
|
||||||
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
|
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
|
||||||
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
|
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
|
||||||
|
|
||||||
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == taskInfo.TrialId);
|
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == medicalReview.TrialId);
|
||||||
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
|
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
|
||||||
|
|
||||||
|
|
||||||
|
@ -402,10 +319,14 @@ public class UrgentIRApplyedReReadingConsumer(
|
||||||
|
|
||||||
DictionaryRepository = _dictionaryRepository,
|
DictionaryRepository = _dictionaryRepository,
|
||||||
IsEn_US = isEn_US,
|
IsEn_US = isEn_US,
|
||||||
|
|
||||||
|
|
||||||
DictionaryList = new List<DictionaryDto>()
|
DictionaryList = new List<DictionaryDto>()
|
||||||
{
|
{
|
||||||
new DictionaryDto (){DictionaryCode= "ReadingCategory",EnumValue=taskInfo.ReadingCategory.GetEnumInt(), }, //任务类型
|
new DictionaryDto (){DictionaryCode= "ReadingCategory",EnumValue=taskInfo.ReadingCategory.GetEnumInt(), },
|
||||||
new DictionaryDto (){DictionaryCode= "RequestReReadingResult",EnumValue="0", }, //审批结果 都是待审批
|
new DictionaryDto (){DictionaryCode= "IsPass",EnumValue=medicalReview.IsHaveQuestion.ToString().ToLower(), },// 审核结论
|
||||||
|
new DictionaryDto (){DictionaryCode= "AuditAdvice",EnumValue=medicalReview.AuditAdviceEnum.GetEnumInt(), },// 审核建议
|
||||||
|
new DictionaryDto (){DictionaryCode= "YesOrNo",EnumValue=taskInfo.IsUrgent.ToString().ToLower(), }, //是否加急
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -419,30 +340,134 @@ public class UrgentIRApplyedReReadingConsumer(
|
||||||
trialInfo.ExperimentName, // 项目 {1}
|
trialInfo.ExperimentName, // 项目 {1}
|
||||||
subjectCode, // 受试者 {2}
|
subjectCode, // 受试者 {2}
|
||||||
taskInfo.TaskBlindName, // 访视 {3}
|
taskInfo.TaskBlindName, // 访视 {3}
|
||||||
dictionValue[0], // 任务类型 {4}
|
criterion.CriterionName, // 阅片标准 {4}
|
||||||
doctorInfo.FullName, // 阅片人 {5}
|
dictionValue[0], // 任务类型 {5}
|
||||||
criterion.CriterionName, // 阅片标准 {6}
|
dictionValue[1], // 审核结论 {6}
|
||||||
dictionValue[1], // 审批结果 {7}
|
dictionValue[2], // 医学审核建议 {7}
|
||||||
_systemEmailConfig.SiteUrl // 链接 {8}
|
dictionValue[3], // 是否加急 {8}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
_systemEmailConfig.SiteUrl // 链接 {9}
|
||||||
);
|
);
|
||||||
|
|
||||||
return (topicStr, htmlBodyStr);
|
return (topicStr, htmlBodyStr);
|
||||||
};
|
};
|
||||||
|
|
||||||
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(_emailNoticeConfigrepository,
|
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc);
|
||||||
|
|
||||||
context.Message.ReReadingApplyState == ReReadingApplyState.TrialGroupHaveApplyed? EmailBusinessScenario.ReReadFromPMApproval: EmailBusinessScenario.ReReadFromIRApproval,
|
|
||||||
|
|
||||||
messageToSend, emailConfigFunc);
|
|
||||||
|
|
||||||
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
|
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 加急阅片 IR 申请重阅 或者PM 申请重阅
|
||||||
|
/// </summary>
|
||||||
|
public class UrgentIRApplyedReReadingConsumer(
|
||||||
|
IRepository<UserRole> _userRoleRepository,
|
||||||
|
IRepository<TrialUserRole> _trialUseRoleRepository,
|
||||||
|
IRepository<TaskMedicalReview> _taskMedicalReviewRepository,
|
||||||
|
IRepository<Trial> _trialRepository,
|
||||||
|
IRepository<SubjectVisit> _subjectVisitRepository,
|
||||||
|
IRepository<ReadingQuestionCriterionTrial> _readingQuestionCriterionTrialRepository,
|
||||||
|
IRepository<VisitTask> _visitTaskRepository,
|
||||||
|
IRepository<Dictionary> _dictionaryRepository,
|
||||||
|
IRepository<TrialEmailNoticeConfig> _trialEmailNoticeConfigrepository,
|
||||||
|
IOptionsMonitor<SystemEmailSendConfig> systemEmailConfig) : IConsumer<UrgentApplyedReReading>
|
||||||
|
{
|
||||||
|
|
||||||
|
private readonly SystemEmailSendConfig _systemEmailConfig = systemEmailConfig.CurrentValue;
|
||||||
|
public async Task Consume(ConsumeContext<UrgentApplyedReReading> context)
|
||||||
|
{
|
||||||
|
Console.WriteLine("发送(024,025) 【加急医学反馈】邮件!!!");
|
||||||
|
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
|
||||||
|
|
||||||
|
var visitTaskId = context.Message.VisitTaskId;
|
||||||
|
|
||||||
|
var taskInfo = await _visitTaskRepository.Where(x => x.Id == visitTaskId).Include(x => x.SourceSubjectVisit).Include(x => x.ReadModule).Include(x => x.Subject).FirstNotNullAsync();
|
||||||
|
|
||||||
|
var trialId=taskInfo.TrialId;
|
||||||
|
|
||||||
|
var scenario = context.Message.ReReadingApplyState == ReReadingApplyState.TrialGroupHaveApplyed ? EmailBusinessScenario.ReReadFromPMApproval : EmailBusinessScenario.ReReadFromIRApproval;
|
||||||
|
|
||||||
|
var trialEmailConfig = _trialEmailNoticeConfigrepository.Where(t => t.TrialId == trialId && t.BusinessScenarioEnum == scenario && t.IsAutoSend && t.IsEnable).FirstOrDefault();
|
||||||
|
|
||||||
|
if (trialEmailConfig != null)
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
var criterion = await _readingQuestionCriterionTrialRepository.FirstOrDefaultAsync(x => x.Id == taskInfo.TrialReadingCriterionId);
|
||||||
|
|
||||||
|
|
||||||
|
var doctorInfo = await _userRoleRepository.Where(x => x.Id == taskInfo.DoctorUserId).FirstOrDefaultAsync();
|
||||||
|
|
||||||
|
var trialUserList = await _trialUseRoleRepository.Where(x => x.TrialId == taskInfo.TrialId && x.TrialUser.IsDeleted==false).Include(x => x.UserRole).Select(x => x.UserRole).ToListAsync();
|
||||||
|
|
||||||
|
var userinfoList = trialUserList.Where(x => x.UserTypeEnum == UserTypeEnum.ProjectManager || x.UserTypeEnum == UserTypeEnum.APM).ToList();
|
||||||
|
if (context.Message.ReReadingApplyState == ReReadingApplyState.TrialGroupHaveApplyed)
|
||||||
|
{
|
||||||
|
userinfoList = trialUserList.Where(x => x.UserTypeEnum == UserTypeEnum.CPM || x.UserTypeEnum == UserTypeEnum.SPM).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
var visitid = taskInfo.SourceSubjectVisit == null ? taskInfo.ReadModule.SubjectVisitId : taskInfo.SourceSubjectVisitId;
|
||||||
|
|
||||||
|
|
||||||
|
var subjectVisit = await _subjectVisitRepository.Where(x => x.Id == visitid).FirstOrDefaultAsync();
|
||||||
|
|
||||||
|
|
||||||
|
foreach (var userinfo in userinfoList)
|
||||||
|
{
|
||||||
|
var messageToSend = new MimeMessage();
|
||||||
|
//发件地址
|
||||||
|
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
|
||||||
|
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
|
||||||
|
|
||||||
|
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == taskInfo.TrialId);
|
||||||
|
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
|
||||||
|
|
||||||
|
|
||||||
|
var dictionValue = await CommonEmailHelper.TranslationDictionary(new TranslationDictionaryDto()
|
||||||
|
{
|
||||||
|
|
||||||
|
DictionaryRepository = _dictionaryRepository,
|
||||||
|
IsEn_US = isEn_US,
|
||||||
|
DictionaryList = new List<DictionaryDto>()
|
||||||
|
{
|
||||||
|
new DictionaryDto (){DictionaryCode= "ReadingCategory",EnumValue=taskInfo.ReadingCategory.GetEnumInt(), }, //任务类型
|
||||||
|
new DictionaryDto (){DictionaryCode= "RequestReReadingResult",EnumValue="0", }, //审批结果 都是待审批
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
|
||||||
|
{
|
||||||
|
var subjectCode = taskInfo.BlindSubjectCode.IsNullOrEmpty() ? taskInfo.Subject.Code : taskInfo.BlindSubjectCode;
|
||||||
|
var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo, subjectCode, taskInfo.TaskBlindName);
|
||||||
|
var htmlBodyStr = string.Format(
|
||||||
|
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
|
||||||
|
userinfo.FullName, // 用户名 {0}
|
||||||
|
trialInfo.ExperimentName, // 项目 {1}
|
||||||
|
subjectCode, // 受试者 {2}
|
||||||
|
taskInfo.TaskBlindName, // 访视 {3}
|
||||||
|
dictionValue[0], // 任务类型 {4}
|
||||||
|
doctorInfo.FullName, // 阅片人 {5}
|
||||||
|
criterion.CriterionName, // 阅片标准 {6}
|
||||||
|
dictionValue[1], // 审批结果 {7}
|
||||||
|
_systemEmailConfig.SiteUrl // 链接 {8}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
return (topicStr, htmlBodyStr);
|
||||||
|
};
|
||||||
|
|
||||||
|
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc);
|
||||||
|
|
||||||
|
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,12 +25,12 @@ using System.Threading.Tasks;
|
||||||
namespace IRaCIS.Core.Application.MassTransit.Consumer;
|
namespace IRaCIS.Core.Application.MassTransit.Consumer;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 用户提交 发送邮件 通知SPM 或者PM
|
/// 用户提交 发送邮件 通知SPM 或者PM 项目默认邮件,不用添加到项目中,直接发送的
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class UserSiteSurveySubmitedEventConsumer(
|
public class UserSiteSurveySubmitedEventConsumer(
|
||||||
IRepository<Trial> _trialRepository,
|
IRepository<Trial> _trialRepository,
|
||||||
IRepository<TrialSite> _trialSiteRepository,
|
IRepository<TrialSite> _trialSiteRepository,
|
||||||
IRepository<TrialUser> _trialUserRepository,
|
IRepository<TrialUserRole> _trialUserRoleRepository,
|
||||||
IRepository<TrialSiteSurvey> _trialSiteSurveyRepository,
|
IRepository<TrialSiteSurvey> _trialSiteSurveyRepository,
|
||||||
IRepository<EmailNoticeConfig> _emailNoticeConfigrepository,
|
IRepository<EmailNoticeConfig> _emailNoticeConfigrepository,
|
||||||
IOptionsMonitor<SystemEmailSendConfig> systemEmailConfig
|
IOptionsMonitor<SystemEmailSendConfig> systemEmailConfig
|
||||||
|
@ -47,75 +47,83 @@ public class UserSiteSurveySubmitedEventConsumer(
|
||||||
|
|
||||||
var trialId = siteSurveyInfo.TrialId;
|
var trialId = siteSurveyInfo.TrialId;
|
||||||
|
|
||||||
var trialUserList = await _trialUserRepository.Where(t => t.TrialId == siteSurveyInfo.TrialId)
|
|
||||||
.Where(t => t.User.UserTypeEnum == UserTypeEnum.SPM || t.User.UserTypeEnum == UserTypeEnum.CPM || t.User.UserTypeEnum == UserTypeEnum.ProjectManager || t.User.UserTypeEnum == UserTypeEnum.APM)
|
|
||||||
.Select(t => new { t.User.FullName, t.User.EMail, t.User.UserTypeEnum }).ToListAsync();
|
|
||||||
|
|
||||||
var sPMOrCPMList = trialUserList.Where(t => t.UserTypeEnum == UserTypeEnum.SPM || t.UserTypeEnum == UserTypeEnum.CPM).ToList();
|
|
||||||
var pmAndAPMList = trialUserList.Where(t => t.UserTypeEnum == UserTypeEnum.ProjectManager || t.UserTypeEnum == UserTypeEnum.APM).ToList();
|
|
||||||
|
|
||||||
var messageToSend = new MimeMessage();
|
|
||||||
|
|
||||||
var toUserName = string.Empty;
|
|
||||||
|
|
||||||
//有SPM
|
|
||||||
if (sPMOrCPMList.Count > 0)
|
|
||||||
{
|
|
||||||
foreach (var user in sPMOrCPMList)
|
|
||||||
{
|
|
||||||
messageToSend.To.Add(new MailboxAddress(user.FullName, user.EMail));
|
|
||||||
}
|
|
||||||
|
|
||||||
toUserName = string.Join('、', sPMOrCPMList.Select(t => t.FullName));
|
|
||||||
|
|
||||||
foreach (var user in pmAndAPMList)
|
|
||||||
{
|
|
||||||
messageToSend.Cc.Add(new MailboxAddress(user.FullName, user.EMail));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
foreach (var user in pmAndAPMList)
|
|
||||||
{
|
|
||||||
messageToSend.To.Add(new MailboxAddress(user.FullName, user.EMail));
|
|
||||||
}
|
|
||||||
|
|
||||||
toUserName = string.Join('、', pmAndAPMList.Select(t => t.FullName));
|
|
||||||
}
|
|
||||||
|
|
||||||
//发件地址
|
|
||||||
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
|
|
||||||
|
|
||||||
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == trialId);
|
|
||||||
|
|
||||||
var siteInfo = await _trialSiteRepository.FirstOrDefaultAsync(t => t.TrialId == trialId && t.Id == siteSurveyInfo.TrialSiteId, true);
|
|
||||||
|
|
||||||
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
|
|
||||||
|
|
||||||
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
|
|
||||||
{
|
|
||||||
var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo);
|
|
||||||
var htmlBodyStr = string.Format(CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
|
|
||||||
toUserName,
|
|
||||||
siteInfo.TrialSiteCode,
|
|
||||||
siteInfo.TrialSiteAliasName,
|
|
||||||
|
|
||||||
siteSurveyInfo.UserName,
|
|
||||||
siteSurveyInfo.Email,
|
|
||||||
siteSurveyInfo.Phone,
|
|
||||||
_systemEmailConfig.SiteUrl
|
|
||||||
);
|
|
||||||
|
|
||||||
return (topicStr, htmlBodyStr);
|
|
||||||
};
|
|
||||||
|
|
||||||
var scenario = _trialSiteSurveyRepository.Where(t => t.Id != trialSiteSurveyId && t.TrialId == siteSurveyInfo.TrialId && t.TrialSiteId == siteSurveyInfo.TrialSiteId).IgnoreQueryFilters().Any() ?
|
var scenario = _trialSiteSurveyRepository.Where(t => t.Id != trialSiteSurveyId && t.TrialId == siteSurveyInfo.TrialId && t.TrialSiteId == siteSurveyInfo.TrialSiteId).IgnoreQueryFilters().Any() ?
|
||||||
EmailBusinessScenario.Approval_UpdateSiteSurvey : EmailBusinessScenario.Approval_SubmitSiteSurvey;
|
EmailBusinessScenario.Approval_UpdateSiteSurvey : EmailBusinessScenario.Approval_SubmitSiteSurvey;
|
||||||
|
|
||||||
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(_emailNoticeConfigrepository, scenario, messageToSend, emailConfigFunc);
|
|
||||||
|
|
||||||
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
|
var emailConfig = _emailNoticeConfigrepository.Where(t => t.BusinessScenarioEnum == scenario && t.IsAutoSend && t.IsEnable).FirstOrDefault();
|
||||||
|
|
||||||
|
if (emailConfig != null)
|
||||||
|
{
|
||||||
|
|
||||||
|
var trialUserList = await _trialUserRoleRepository.Where(t => t.TrialId == siteSurveyInfo.TrialId && t.TrialUser.IsDeleted == false)
|
||||||
|
.Where(t => t.UserRole.UserTypeEnum == UserTypeEnum.SPM || t.UserRole.UserTypeEnum == UserTypeEnum.CPM || t.UserRole.UserTypeEnum == UserTypeEnum.ProjectManager || t.UserRole.UserTypeEnum == UserTypeEnum.APM)
|
||||||
|
.Select(t => new { t.UserRole.FullName, t.UserRole.IdentityUser.EMail, t.UserRole.UserTypeEnum }).ToListAsync();
|
||||||
|
|
||||||
|
var sPMOrCPMList = trialUserList.Where(t => t.UserTypeEnum == UserTypeEnum.SPM || t.UserTypeEnum == UserTypeEnum.CPM).ToList();
|
||||||
|
var pmAndAPMList = trialUserList.Where(t => t.UserTypeEnum == UserTypeEnum.ProjectManager || t.UserTypeEnum == UserTypeEnum.APM).ToList();
|
||||||
|
|
||||||
|
var messageToSend = new MimeMessage();
|
||||||
|
|
||||||
|
var toUserName = string.Empty;
|
||||||
|
|
||||||
|
//有SPM
|
||||||
|
if (sPMOrCPMList.Count > 0)
|
||||||
|
{
|
||||||
|
foreach (var user in sPMOrCPMList)
|
||||||
|
{
|
||||||
|
messageToSend.To.Add(new MailboxAddress(user.FullName, user.EMail));
|
||||||
|
}
|
||||||
|
|
||||||
|
toUserName = string.Join('、', sPMOrCPMList.Select(t => t.FullName));
|
||||||
|
|
||||||
|
foreach (var user in pmAndAPMList)
|
||||||
|
{
|
||||||
|
messageToSend.Cc.Add(new MailboxAddress(user.FullName, user.EMail));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach (var user in pmAndAPMList)
|
||||||
|
{
|
||||||
|
messageToSend.To.Add(new MailboxAddress(user.FullName, user.EMail));
|
||||||
|
}
|
||||||
|
|
||||||
|
toUserName = string.Join('、', pmAndAPMList.Select(t => t.FullName));
|
||||||
|
}
|
||||||
|
|
||||||
|
//发件地址
|
||||||
|
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
|
||||||
|
|
||||||
|
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == trialId);
|
||||||
|
|
||||||
|
var siteInfo = await _trialSiteRepository.FirstOrDefaultAsync(t => t.TrialId == trialId && t.Id == siteSurveyInfo.TrialSiteId, true);
|
||||||
|
|
||||||
|
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
|
||||||
|
|
||||||
|
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
|
||||||
|
{
|
||||||
|
var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo);
|
||||||
|
var htmlBodyStr = string.Format(CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
|
||||||
|
toUserName,
|
||||||
|
siteInfo.TrialSiteCode,
|
||||||
|
siteInfo.TrialSiteAliasName,
|
||||||
|
|
||||||
|
siteSurveyInfo.UserName,
|
||||||
|
siteSurveyInfo.Email,
|
||||||
|
siteSurveyInfo.Phone,
|
||||||
|
_systemEmailConfig.SiteUrl
|
||||||
|
);
|
||||||
|
|
||||||
|
return (topicStr, htmlBodyStr);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(emailConfig, messageToSend, emailConfigFunc);
|
||||||
|
|
||||||
|
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -125,7 +133,7 @@ public class UserSiteSurveySubmitedEventConsumer(
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class SiteSurveySPMSubmitedEventConsumer(
|
public class SiteSurveySPMSubmitedEventConsumer(
|
||||||
IRepository<Trial> _trialRepository,
|
IRepository<Trial> _trialRepository,
|
||||||
IRepository<TrialUser> _trialUserRepository,
|
IRepository<TrialUserRole> _trialUserRoleRepository,
|
||||||
IRepository<TrialSite> _trialSiteRepository,
|
IRepository<TrialSite> _trialSiteRepository,
|
||||||
IRepository<TrialSiteSurvey> _trialSiteSurveyRepository,
|
IRepository<TrialSiteSurvey> _trialSiteSurveyRepository,
|
||||||
IRepository<EmailNoticeConfig> _emailNoticeConfigrepository,
|
IRepository<EmailNoticeConfig> _emailNoticeConfigrepository,
|
||||||
|
@ -143,55 +151,65 @@ public class SiteSurveySPMSubmitedEventConsumer(
|
||||||
|
|
||||||
var trialId = siteSurveyInfo.TrialId;
|
var trialId = siteSurveyInfo.TrialId;
|
||||||
|
|
||||||
var messageToSend = new MimeMessage();
|
var scenario = EmailBusinessScenario.Approval_SubmitSiteSurvey;
|
||||||
|
|
||||||
var trialUserList = _trialUserRepository.Where(t => t.TrialId == trialId)
|
var emailConfig = _emailNoticeConfigrepository.Where(t => t.BusinessScenarioEnum == scenario && t.IsAutoSend && t.IsEnable).FirstOrDefault();
|
||||||
.Where(t => t.User.UserTypeEnum == UserTypeEnum.SPM || t.User.UserTypeEnum == UserTypeEnum.CPM || t.User.UserTypeEnum == UserTypeEnum.ProjectManager || t.User.UserTypeEnum == UserTypeEnum.APM)
|
|
||||||
.Select(t => new { t.User.EMail, t.User.FullName, t.User.UserTypeEnum }).ToList();
|
|
||||||
|
|
||||||
var sPMOrCPMList = trialUserList.Where(t => t.UserTypeEnum == UserTypeEnum.SPM || t.UserTypeEnum == UserTypeEnum.CPM).ToList();
|
if (emailConfig != null)
|
||||||
var pmAndAPMList = trialUserList.Where(t => t.UserTypeEnum == UserTypeEnum.ProjectManager || t.UserTypeEnum == UserTypeEnum.APM).ToList();
|
|
||||||
|
|
||||||
var toUserName = string.Empty;
|
|
||||||
|
|
||||||
foreach (var item in pmAndAPMList)
|
|
||||||
{
|
{
|
||||||
messageToSend.To.Add(new MailboxAddress(item.FullName, item.EMail));
|
var messageToSend = new MimeMessage();
|
||||||
|
|
||||||
toUserName = string.Join('、', pmAndAPMList.Select(t => t.FullName));
|
var trialUserList = _trialUserRoleRepository.Where(t => t.TrialId == trialId && t.TrialUser.IsDeleted == false)
|
||||||
|
.Where(t => t.UserRole.UserTypeEnum == UserTypeEnum.SPM || t.UserRole.UserTypeEnum == UserTypeEnum.CPM || t.UserRole.UserTypeEnum == UserTypeEnum.ProjectManager || t.UserRole.UserTypeEnum == UserTypeEnum.APM)
|
||||||
|
.Select(t => new { t.UserRole.IdentityUser.EMail, t.UserRole.FullName, t.UserRole.UserTypeEnum }).ToList();
|
||||||
|
|
||||||
|
var sPMOrCPMList = trialUserList.Where(t => t.UserTypeEnum == UserTypeEnum.SPM || t.UserTypeEnum == UserTypeEnum.CPM).ToList();
|
||||||
|
var pmAndAPMList = trialUserList.Where(t => t.UserTypeEnum == UserTypeEnum.ProjectManager || t.UserTypeEnum == UserTypeEnum.APM).ToList();
|
||||||
|
|
||||||
|
var toUserName = string.Empty;
|
||||||
|
|
||||||
|
foreach (var item in pmAndAPMList)
|
||||||
|
{
|
||||||
|
messageToSend.To.Add(new MailboxAddress(item.FullName, item.EMail));
|
||||||
|
|
||||||
|
toUserName = string.Join('、', pmAndAPMList.Select(t => t.FullName));
|
||||||
|
}
|
||||||
|
|
||||||
|
//发件地址
|
||||||
|
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
|
||||||
|
|
||||||
|
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == trialId);
|
||||||
|
|
||||||
|
var siteInfo = await _trialSiteRepository.FirstOrDefaultAsync(t => t.TrialId == trialId && t.Id == siteSurveyInfo.TrialSiteId, true);
|
||||||
|
|
||||||
|
|
||||||
|
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
|
||||||
|
|
||||||
|
|
||||||
|
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
|
||||||
|
{
|
||||||
|
var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo);
|
||||||
|
var htmlBodyStr = string.Format(CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
|
||||||
|
toUserName,
|
||||||
|
siteInfo.TrialSiteCode, //中心编号
|
||||||
|
siteInfo.TrialSiteAliasName,//中心名称
|
||||||
|
|
||||||
|
siteSurveyInfo.UserName, //联系人
|
||||||
|
siteSurveyInfo.Email, //联系邮箱
|
||||||
|
siteSurveyInfo.Phone, //联系电话
|
||||||
|
_systemEmailConfig.SiteUrl
|
||||||
|
);
|
||||||
|
|
||||||
|
return (topicStr, htmlBodyStr);
|
||||||
|
};
|
||||||
|
|
||||||
|
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(emailConfig, messageToSend, emailConfigFunc);
|
||||||
|
|
||||||
|
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
//发件地址
|
|
||||||
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
|
|
||||||
|
|
||||||
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == trialId);
|
|
||||||
|
|
||||||
var siteInfo = await _trialSiteRepository.FirstOrDefaultAsync(t => t.TrialId == trialId && t.Id == siteSurveyInfo.TrialSiteId, true);
|
|
||||||
|
|
||||||
|
|
||||||
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
|
|
||||||
|
|
||||||
|
|
||||||
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
|
|
||||||
{
|
|
||||||
var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo);
|
|
||||||
var htmlBodyStr = string.Format(CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
|
|
||||||
toUserName,
|
|
||||||
siteInfo.TrialSiteCode,
|
|
||||||
siteInfo.TrialSiteAliasName,
|
|
||||||
|
|
||||||
siteSurveyInfo.UserName,
|
|
||||||
siteSurveyInfo.Email,
|
|
||||||
siteSurveyInfo.Phone,
|
|
||||||
_systemEmailConfig.SiteUrl
|
|
||||||
);
|
|
||||||
|
|
||||||
return (topicStr, htmlBodyStr);
|
|
||||||
};
|
|
||||||
|
|
||||||
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(_emailNoticeConfigrepository, EmailBusinessScenario.Approval_SubmitSiteSurvey, messageToSend, emailConfigFunc);
|
|
||||||
|
|
||||||
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,11 +218,11 @@ public class SiteSurveySPMSubmitedEventConsumer(
|
||||||
/// 调研表驳回发送邮件 之前已有,需要迁移过来
|
/// 调研表驳回发送邮件 之前已有,需要迁移过来
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class SiteSurverRejectedEventConsumer(
|
public class SiteSurverRejectedEventConsumer(
|
||||||
IRepository<TrialUser> _trialUserRepository,
|
IRepository<TrialUserRole> _trialUserRoleRepository,
|
||||||
IRepository<Trial> _trialRepository,
|
IRepository<Trial> _trialRepository,
|
||||||
IRepository<TrialSite> _trialSiteRepository,
|
IRepository<TrialSite> _trialSiteRepository,
|
||||||
IRepository<TrialSiteSurvey> _trialSiteSurveyRepository,
|
IRepository<TrialSiteSurvey> _trialSiteSurveyRepository,
|
||||||
IRepository<EmailNoticeConfig> _emailNoticeConfigrepository,
|
IRepository<EmailNoticeConfig> _emailNoticeConfigrepository,
|
||||||
IOptionsMonitor<SystemEmailSendConfig> systemEmailConfig
|
IOptionsMonitor<SystemEmailSendConfig> systemEmailConfig
|
||||||
) : IConsumer<SiteSurverRejectedEvent>
|
) : IConsumer<SiteSurverRejectedEvent>
|
||||||
{
|
{
|
||||||
|
@ -216,83 +234,95 @@ public class SiteSurverRejectedEventConsumer(
|
||||||
|
|
||||||
var trialSiteSurveyId = context.Message.TrialSiteSurveyId;
|
var trialSiteSurveyId = context.Message.TrialSiteSurveyId;
|
||||||
|
|
||||||
var siteSurveyInfo = _trialSiteSurveyRepository.Where(t => t.Id == trialSiteSurveyId).FirstOrDefault().IfNullThrowException();
|
var siteSurveyInfo = _trialSiteSurveyRepository.Where(t => t.Id == trialSiteSurveyId ,ignoreQueryFilters:true).FirstOrDefault().IfNullThrowException();
|
||||||
|
|
||||||
var trialId = siteSurveyInfo.TrialId;
|
var trialId = siteSurveyInfo.TrialId;
|
||||||
|
|
||||||
var messageToSend = new MimeMessage();
|
var scenario = EmailBusinessScenario.SiteSurveyReject;
|
||||||
|
|
||||||
var toUserName = siteSurveyInfo.UserName;
|
var emailConfig = _emailNoticeConfigrepository.Where(t => t.BusinessScenarioEnum == scenario && t.IsAutoSend && t.IsEnable).FirstOrDefault();
|
||||||
|
|
||||||
if (context.Message.IsHaveSPMOrCPM)
|
if (emailConfig != null)
|
||||||
{
|
{
|
||||||
//PM 驳回到SPM
|
|
||||||
if (siteSurveyInfo.State == TrialSiteSurveyEnum.CRCSubmitted)
|
|
||||||
|
var messageToSend = new MimeMessage();
|
||||||
|
|
||||||
|
var toUserName = siteSurveyInfo.UserName;
|
||||||
|
|
||||||
|
if (context.Message.IsHaveSPMOrCPM)
|
||||||
{
|
{
|
||||||
//var user = await _userRepository.FirstOrDefaultAsync(t => t.Id == siteSurveyInfo.PreliminaryUserId);
|
//PM 驳回到SPM
|
||||||
|
if (siteSurveyInfo.State == TrialSiteSurveyEnum.CRCSubmitted)
|
||||||
//name = user.FullName;
|
|
||||||
|
|
||||||
var sPMOrCPMList = _trialUserRepository.Where(t => t.TrialId == trialId)
|
|
||||||
.Where(t => t.User.UserTypeEnum == UserTypeEnum.SPM || t.User.UserTypeEnum == UserTypeEnum.CPM)
|
|
||||||
.Select(t => new { t.User.EMail, t.User.FullName, t.User.UserTypeEnum }).ToList();
|
|
||||||
|
|
||||||
|
|
||||||
foreach (var user in sPMOrCPMList)
|
|
||||||
{
|
{
|
||||||
messageToSend.To.Add(new MailboxAddress(user.FullName, user.EMail));
|
//var user = await _userRoleRepository.FirstOrDefaultAsync(t => t.Id == siteSurveyInfo.PreliminaryUserId);
|
||||||
|
|
||||||
|
//name = user.FullName;
|
||||||
|
|
||||||
|
var sPMOrCPMList = _trialUserRoleRepository.Where(t => t.TrialId == trialId && t.TrialUser.IsDeleted == false)
|
||||||
|
.Where(t => t.UserRole.UserTypeEnum == UserTypeEnum.SPM || t.UserRole.UserTypeEnum == UserTypeEnum.CPM)
|
||||||
|
.Select(t => new { t.UserRole.IdentityUser.EMail, t.UserRole.FullName, t.UserRole.UserTypeEnum }).ToList();
|
||||||
|
|
||||||
|
|
||||||
|
foreach (var user in sPMOrCPMList)
|
||||||
|
{
|
||||||
|
messageToSend.To.Add(new MailboxAddress(user.FullName, user.EMail));
|
||||||
|
}
|
||||||
|
|
||||||
|
toUserName = string.Join('、', sPMOrCPMList.Select(t => t.FullName));
|
||||||
|
|
||||||
|
}
|
||||||
|
//SPM 驳回到CRC
|
||||||
|
else if (siteSurveyInfo.State == TrialSiteSurveyEnum.ToSubmit)
|
||||||
|
{
|
||||||
|
messageToSend.To.Add(new MailboxAddress(toUserName, siteSurveyInfo.Email));
|
||||||
}
|
}
|
||||||
|
|
||||||
toUserName = string.Join('、', sPMOrCPMList.Select(t => t.FullName));
|
|
||||||
|
|
||||||
}
|
}
|
||||||
//SPM 驳回到CRC
|
else
|
||||||
else if (siteSurveyInfo.State == TrialSiteSurveyEnum.ToSubmit)
|
|
||||||
{
|
{
|
||||||
messageToSend.To.Add(new MailboxAddress(toUserName, siteSurveyInfo.Email));
|
//没有SPM PM驳回到CRC
|
||||||
|
|
||||||
|
messageToSend.To.Add(new MailboxAddress(String.Empty, siteSurveyInfo.Email));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
//发件地址
|
||||||
else
|
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
|
||||||
{
|
|
||||||
//没有SPM PM驳回到CRC
|
|
||||||
|
|
||||||
messageToSend.To.Add(new MailboxAddress(String.Empty, siteSurveyInfo.Email));
|
|
||||||
}
|
|
||||||
|
|
||||||
//发件地址
|
|
||||||
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
|
|
||||||
|
|
||||||
|
|
||||||
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == trialId);
|
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == trialId);
|
||||||
|
|
||||||
var siteInfo = await _trialSiteRepository.FirstOrDefaultAsync(t => t.TrialId == trialId && t.Id == siteSurveyInfo.TrialSiteId, true);
|
var siteInfo = await _trialSiteRepository.FirstOrDefaultAsync(t => t.TrialId == trialId && t.Id == siteSurveyInfo.TrialSiteId, true);
|
||||||
|
|
||||||
|
|
||||||
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
|
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
|
||||||
|
|
||||||
|
|
||||||
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
|
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
|
||||||
{
|
{
|
||||||
var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo);
|
var topicStr = string.Format(input.topicStr, companyName,trialInfo.ResearchProgramNo);
|
||||||
var htmlBodyStr = string.Format(CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
|
var htmlBodyStr = string.Format(CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
|
||||||
toUserName,
|
toUserName,
|
||||||
trialInfo.TrialCode,
|
trialInfo.TrialCode,
|
||||||
trialInfo.ResearchProgramNo,
|
trialInfo.ResearchProgramNo,
|
||||||
trialInfo.ExperimentName,
|
trialInfo.ExperimentName,
|
||||||
siteInfo.TrialSiteCode,
|
|
||||||
siteInfo.TrialSiteAliasName,
|
|
||||||
siteSurveyInfo.LatestBackReason,
|
|
||||||
_systemEmailConfig.SiteUrl,
|
|
||||||
(siteSurveyInfo.State == TrialSiteSurveyEnum.ToSubmit ? "inline - block" : "none")
|
|
||||||
);
|
|
||||||
|
|
||||||
return (topicStr, htmlBodyStr);
|
siteInfo.TrialSiteCode, //中心编号
|
||||||
};
|
siteInfo.TrialSiteAliasName,//中心名称
|
||||||
|
siteSurveyInfo.LatestBackReason, //驳回原因
|
||||||
|
_systemEmailConfig.SiteUrl, //链接
|
||||||
|
(siteSurveyInfo.State == TrialSiteSurveyEnum.ToSubmit ? "inline - block" : "none")
|
||||||
|
);
|
||||||
|
|
||||||
|
return (topicStr, htmlBodyStr);
|
||||||
|
};
|
||||||
|
|
||||||
|
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(emailConfig, messageToSend, emailConfigFunc);
|
||||||
|
|
||||||
|
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
|
||||||
|
}
|
||||||
|
|
||||||
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(_emailNoticeConfigrepository, EmailBusinessScenario.SiteSurveyReject, messageToSend, emailConfigFunc);
|
|
||||||
|
|
||||||
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -30,13 +30,13 @@ public class MasstransiTestCommand
|
||||||
/// publish 请求流不会先到消费者,发布后,直接执行后续代码
|
/// publish 请求流不会先到消费者,发布后,直接执行后续代码
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="_userRepository"></param>
|
/// <param name="_userRoleRepository"></param>
|
||||||
public class MasstransitTestConsumer(IRepository<User> _userRepository) : IConsumer<MasstransiTestCommand>
|
public class MasstransitTestConsumer(IRepository<UserRole> _userRoleRepository) : IConsumer<MasstransiTestCommand>
|
||||||
{
|
{
|
||||||
public async Task Consume(ConsumeContext<MasstransiTestCommand> context)
|
public async Task Consume(ConsumeContext<MasstransiTestCommand> context)
|
||||||
{
|
{
|
||||||
|
|
||||||
Console.WriteLine(_userRepository._dbContext.GetHashCode());
|
Console.WriteLine(_userRoleRepository._dbContext.GetHashCode());
|
||||||
Console.WriteLine("Now is " + DateTime.Now.ToString());
|
Console.WriteLine("Now is " + DateTime.Now.ToString());
|
||||||
Console.WriteLine($"MassTransit.Consumer :{context.Message.value}");
|
Console.WriteLine($"MassTransit.Consumer :{context.Message.value}");
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using DocumentFormat.OpenXml;
|
using DocumentFormat.OpenXml;
|
||||||
|
using DocumentFormat.OpenXml.Spreadsheet;
|
||||||
using IRaCIS.Application.Contracts;
|
using IRaCIS.Application.Contracts;
|
||||||
using IRaCIS.Core.Application.Contracts;
|
using IRaCIS.Core.Application.Contracts;
|
||||||
using IRaCIS.Core.Application.Helper;
|
using IRaCIS.Core.Application.Helper;
|
||||||
|
@ -26,8 +27,8 @@ namespace IRaCIS.Core.Application.MassTransit.Recurring
|
||||||
IRepository<ReadingQuestionCriterionTrial> _trialReadingCriterionRepository,
|
IRepository<ReadingQuestionCriterionTrial> _trialReadingCriterionRepository,
|
||||||
IRepository<VisitTask> _visitTaskRepository,
|
IRepository<VisitTask> _visitTaskRepository,
|
||||||
IRepository<Dictionary> _dictionaryRepository,
|
IRepository<Dictionary> _dictionaryRepository,
|
||||||
IRepository<TrialUser> _trialUserRepository,
|
IRepository<TrialUserRole> _trialUserRoleRepository,
|
||||||
IRepository<EmailNoticeConfig> _emailNoticeConfigrepository,
|
IRepository<TrialEmailNoticeConfig> _trialEmailNoticeConfigrepository,
|
||||||
IOptionsMonitor<SystemEmailSendConfig> systemEmailConfig) : IConsumer<UrgentIRUnReadTaskRecurringEvent>
|
IOptionsMonitor<SystemEmailSendConfig> systemEmailConfig) : IConsumer<UrgentIRUnReadTaskRecurringEvent>
|
||||||
{
|
{
|
||||||
private readonly SystemEmailSendConfig _systemEmailConfig = systemEmailConfig.CurrentValue;
|
private readonly SystemEmailSendConfig _systemEmailConfig = systemEmailConfig.CurrentValue;
|
||||||
|
@ -35,144 +36,151 @@ namespace IRaCIS.Core.Application.MassTransit.Recurring
|
||||||
public async Task Consume(ConsumeContext<UrgentIRUnReadTaskRecurringEvent> context)
|
public async Task Consume(ConsumeContext<UrgentIRUnReadTaskRecurringEvent> context)
|
||||||
{
|
{
|
||||||
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
|
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
|
||||||
|
|
||||||
var trialId = context.Message.TrialId;
|
var trialId = context.Message.TrialId;
|
||||||
|
|
||||||
var dictionValue = await CommonEmailHelper.TranslationDictionary(new TranslationDictionaryDto()
|
var scenario = EmailBusinessScenario.ExpeditedReading;
|
||||||
|
var trialEmailConfig = _trialEmailNoticeConfigrepository.Where(t => t.TrialId == trialId && t.BusinessScenarioEnum == scenario && t.IsAutoSend && t.IsEnable).FirstOrDefault();
|
||||||
|
|
||||||
|
if (trialEmailConfig != null)
|
||||||
{
|
{
|
||||||
DictionaryRepository = _dictionaryRepository,
|
|
||||||
IsEn_US = isEn_US,
|
|
||||||
DictionaryList = new List<DictionaryDto>()
|
var dictionValue = await CommonEmailHelper.TranslationDictionary(new TranslationDictionaryDto()
|
||||||
|
{
|
||||||
|
DictionaryRepository = _dictionaryRepository,
|
||||||
|
IsEn_US = isEn_US,
|
||||||
|
DictionaryList = new List<DictionaryDto>()
|
||||||
{
|
{
|
||||||
new DictionaryDto (){DictionaryCode= "YesOrNo",EnumValue="true" }
|
new DictionaryDto (){DictionaryCode= "YesOrNo",EnumValue="true" }
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
//找到该项目所有的IR 并且有加急 和Pd 未读的任务
|
//找到该项目所有的IR 并且有加急 和Pd 未读的任务
|
||||||
|
|
||||||
Expression<Func<VisitTask, bool>> comonTaskFilter = t => t.TrialId == trialId && t.TaskState == TaskState.Effect && t.ReadingTaskState != ReadingTaskState.HaveSigned && t.TaskAllocationState == TaskAllocationState.Allocated;
|
Expression<Func<VisitTask, bool>> comonTaskFilter = t => t.TrialId == trialId && t.TaskState == TaskState.Effect && t.ReadingTaskState != ReadingTaskState.HaveSigned && t.TaskAllocationState == TaskAllocationState.Allocated;
|
||||||
|
|
||||||
var trialUserList = _trialUserRepository.Where(t => t.TrialId == trialId).Select(t => new
|
var trialUserList = _trialUserRoleRepository.Where(t => t.TrialId == trialId && t.TrialUser.JoinTime != null).Select(t => new
|
||||||
{
|
|
||||||
t.UserId,
|
|
||||||
t.User.FullName,
|
|
||||||
t.User.EMail,
|
|
||||||
t.User.UserName,
|
|
||||||
t.Trial.TrialCode,
|
|
||||||
t.Trial.ResearchProgramNo
|
|
||||||
//TrialReadingCriterionList = t.Trial.TrialReadingCriterionList.Select(t => new { t.CriterionName, TrialReadingCriterionId = t.Id }).ToList()
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
foreach (var trialUser in trialUserList)
|
|
||||||
{
|
|
||||||
|
|
||||||
var userId = trialUser.UserId;
|
|
||||||
|
|
||||||
var doctorCriterionList = await _trialReadingCriterionRepository.Where(t => t.IsSigned && t.IsConfirm && t.TrialId == trialId && t.Trial.TrialUserList.Any(t => t.UserId == userId))
|
|
||||||
.Select(c => new
|
|
||||||
{
|
|
||||||
|
|
||||||
TrialReadingCriterionId = c.Id,
|
|
||||||
CriterionName = c.CriterionName,
|
|
||||||
|
|
||||||
UnReadList = c.VisitTaskList.Where(t => t.ExpetidEmailNoticeTime == null) //没有被通知
|
|
||||||
.Where(t => t.DoctorUserId == userId && t.ReadingTaskState != ReadingTaskState.HaveSigned && t.TaskState == TaskState.Effect)
|
|
||||||
// 前序 不存在 未一致性核查未通过的
|
|
||||||
.Where(t => !t.Subject.SubjectVisitList.Any(sv => sv.CheckState != CheckStateEnum.CVPassed && t.VisitTaskNum > sv.VisitNum))
|
|
||||||
//前序 不存在 未生成任务的访视
|
|
||||||
.Where(t => c.IsAutoCreate == false ? !t.Subject.SubjectCriteriaEvaluationVisitFilterList.Where(d => d.TrialReadingCriterionId == t.TrialReadingCriterionId).Any(f => f.IsGeneratedTask == false && t.VisitTaskNum > f.SubjectVisit.VisitNum) : true)
|
|
||||||
|
|
||||||
.Where(y => y.IsFrontTaskNeedSignButNotSign == false && (y.IsNeedClinicalDataSign == false || y.IsClinicalDataSign == true))
|
|
||||||
|
|
||||||
.Where(t => t.SourceSubjectVisit.PDState == PDStateEnum.PDProgress || t.SourceSubjectVisit.IsEnrollmentConfirm ||
|
|
||||||
t.ReadModule.SubjectVisit.PDState == PDStateEnum.PDProgress || t.ReadModule.SubjectVisit.IsEnrollmentConfirm)
|
|
||||||
.Select(c => new
|
|
||||||
{
|
|
||||||
SubejctCode = c.IsAnalysisCreate ? c.BlindSubjectCode : c.Subject.Code,
|
|
||||||
c.TaskBlindName,
|
|
||||||
c.TaskName,
|
|
||||||
VisitTaskId = c.Id
|
|
||||||
}).ToList()
|
|
||||||
|
|
||||||
}).ToListAsync();
|
|
||||||
|
|
||||||
|
|
||||||
var toTalUnreadCount = doctorCriterionList.SelectMany(t => t.UnReadList).Count();
|
|
||||||
|
|
||||||
if (toTalUnreadCount > 0)
|
|
||||||
{
|
{
|
||||||
var messageToSend = new MimeMessage();
|
t.UserId,
|
||||||
|
t.UserRole.FullName,
|
||||||
|
t.UserRole.IdentityUser.EMail,
|
||||||
|
t.UserRole.IdentityUser.UserName,
|
||||||
|
t.Trial.TrialCode,
|
||||||
|
t.Trial.ResearchProgramNo
|
||||||
|
//TrialReadingCriterionList = t.Trial.TrialReadingCriterionList.Select(t => new { t.CriterionName, TrialReadingCriterionId = t.Id }).ToList()
|
||||||
|
});
|
||||||
|
|
||||||
//发件地址
|
|
||||||
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
|
|
||||||
messageToSend.To.Add(new MailboxAddress(trialUser.FullName, trialUser.EMail));
|
|
||||||
|
|
||||||
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
|
foreach (var trialUser in trialUserList)
|
||||||
|
{
|
||||||
|
|
||||||
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
|
var userId = trialUser.UserId;
|
||||||
|
|
||||||
|
var doctorCriterionList = await _trialReadingCriterionRepository.Where(t => t.IsSigned && t.IsConfirm && t.TrialId == trialId && t.Trial.TrialUserRoleList.Any(t => t.UserId == userId))
|
||||||
|
.Select(c => new
|
||||||
|
{
|
||||||
|
|
||||||
|
TrialReadingCriterionId = c.Id,
|
||||||
|
CriterionName = c.CriterionName,
|
||||||
|
|
||||||
|
UnReadList = c.VisitTaskList.Where(t => t.ExpetidEmailNoticeTime == null) //没有被通知
|
||||||
|
.Where(t => t.DoctorUserId == userId && t.ReadingTaskState != ReadingTaskState.HaveSigned && t.TaskState == TaskState.Effect)
|
||||||
|
// 前序 不存在 未一致性核查未通过的
|
||||||
|
.Where(t => !t.Subject.SubjectVisitList.Any(sv => sv.CheckState != CheckStateEnum.CVPassed && t.VisitTaskNum > sv.VisitNum))
|
||||||
|
//前序 不存在 未生成任务的访视
|
||||||
|
.Where(t => c.IsAutoCreate == false ? !t.Subject.SubjectCriteriaEvaluationVisitFilterList.Where(d => d.TrialReadingCriterionId == t.TrialReadingCriterionId).Any(f => f.IsGeneratedTask == false && t.VisitTaskNum > f.SubjectVisit.VisitNum) : true)
|
||||||
|
|
||||||
|
.Where(y => y.IsFrontTaskNeedSignButNotSign == false && (y.IsNeedClinicalDataSign == false || y.IsClinicalDataSign == true))
|
||||||
|
|
||||||
|
.Where(t => t.SourceSubjectVisit.PDState == PDStateEnum.PDProgress || t.SourceSubjectVisit.IsEnrollmentConfirm ||
|
||||||
|
t.ReadModule.SubjectVisit.PDState == PDStateEnum.PDProgress || t.ReadModule.SubjectVisit.IsEnrollmentConfirm)
|
||||||
|
.Select(c => new
|
||||||
|
{
|
||||||
|
SubejctCode = c.IsAnalysisCreate ? c.BlindSubjectCode : c.Subject.Code,
|
||||||
|
c.TaskBlindName,
|
||||||
|
c.TaskName,
|
||||||
|
VisitTaskId = c.Id
|
||||||
|
}).ToList()
|
||||||
|
|
||||||
|
}).ToListAsync();
|
||||||
|
|
||||||
|
|
||||||
|
var toTalUnreadCount = doctorCriterionList.SelectMany(t => t.UnReadList).Count();
|
||||||
|
|
||||||
|
if (toTalUnreadCount > 0)
|
||||||
{
|
{
|
||||||
var topicStr = string.Format(input.topicStr, trialUser.ResearchProgramNo);
|
var messageToSend = new MimeMessage();
|
||||||
|
|
||||||
var template = input.htmlBodyStr;
|
//发件地址
|
||||||
|
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
|
||||||
|
messageToSend.To.Add(new MailboxAddress(trialUser.FullName, trialUser.EMail));
|
||||||
|
|
||||||
//正则提取循环的部分 {%for%}(.*?){%end for%}
|
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
|
||||||
string pattern = @"{%for%}(.*?){%end for%}";
|
|
||||||
var match = Regex.Match(template, pattern, RegexOptions.Singleline);
|
|
||||||
|
|
||||||
string criteriaTemplate = match.Groups[1].Value; // 提取循环模板
|
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
|
||||||
|
|
||||||
// 构建循环内容
|
|
||||||
string criteriaDetails = "";
|
|
||||||
foreach (var criteria in doctorCriterionList)
|
|
||||||
{
|
{
|
||||||
if (criteria.UnReadList.Count() > 0)
|
var topicStr = string.Format(input.topicStr, trialUser.ResearchProgramNo);
|
||||||
|
|
||||||
|
var template = input.htmlBodyStr;
|
||||||
|
|
||||||
|
//正则提取循环的部分 {%for%}(.*?){%end for%}
|
||||||
|
string pattern = @"{%for%}(.*?){%end for%}";
|
||||||
|
var match = Regex.Match(template, pattern, RegexOptions.Singleline);
|
||||||
|
|
||||||
|
string criteriaTemplate = match.Groups[1].Value; // 提取循环模板
|
||||||
|
|
||||||
|
// 构建循环内容
|
||||||
|
string criteriaDetails = "";
|
||||||
|
foreach (var criteria in doctorCriterionList)
|
||||||
{
|
{
|
||||||
criteriaDetails += string.Format(criteriaTemplate, criteria.CriterionName, criteria.UnReadList.Select(t => t.SubejctCode).Distinct().Count(), criteria.UnReadList.Count());
|
if (criteria.UnReadList.Count() > 0)
|
||||||
|
{
|
||||||
|
criteriaDetails += string.Format(criteriaTemplate, criteria.CriterionName, criteria.UnReadList.Select(t => t.SubejctCode).Distinct().Count(), criteria.UnReadList.Count());
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// 用循环内容替换原始模板中的循环部分
|
// 用循环内容替换原始模板中的循环部分
|
||||||
string emailContent = template.Replace(criteriaTemplate, criteriaDetails).Replace("{%for%}", "").Replace("{%end for%}", "");
|
string emailContent = template.Replace(criteriaTemplate, criteriaDetails).Replace("{%for%}", "").Replace("{%end for%}", "");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var htmlBodyStr = string.Format(CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, emailContent),
|
var htmlBodyStr = string.Format(CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, emailContent),
|
||||||
trialUser.FullName,
|
trialUser.FullName,
|
||||||
|
|
||||||
DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
|
DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
|
||||||
toTalUnreadCount,
|
toTalUnreadCount,
|
||||||
|
|
||||||
trialUser.ResearchProgramNo,
|
trialUser.ResearchProgramNo,
|
||||||
dictionValue[0],
|
dictionValue[0],
|
||||||
_systemEmailConfig.SiteUrl
|
_systemEmailConfig.SiteUrl
|
||||||
);
|
);
|
||||||
|
|
||||||
return (topicStr, htmlBodyStr);
|
return (topicStr, htmlBodyStr);
|
||||||
};
|
};
|
||||||
|
|
||||||
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(_emailNoticeConfigrepository, EmailBusinessScenario.ExpeditedReading, messageToSend, emailConfigFunc);
|
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc);
|
||||||
|
|
||||||
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
|
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
|
||||||
|
|
||||||
//处理标记已通知的任务
|
//处理标记已通知的任务
|
||||||
|
|
||||||
|
var visitTaskIdList = doctorCriterionList.Where(t => t.UnReadList.Count() > 0).SelectMany(t => t.UnReadList.Select(u => u.VisitTaskId)).ToList();
|
||||||
|
|
||||||
|
await _visitTaskRepository.BatchUpdateNoTrackingAsync(t => visitTaskIdList.Contains(t.Id), u => new VisitTask() { ExpetidEmailNoticeTime = DateTime.Now });
|
||||||
|
}
|
||||||
|
|
||||||
var visitTaskIdList = doctorCriterionList.Where(t => t.UnReadList.Count() > 0).SelectMany(t => t.UnReadList.Select(u => u.VisitTaskId)).ToList();
|
|
||||||
|
|
||||||
await _visitTaskRepository.BatchUpdateNoTrackingAsync(t => visitTaskIdList.Contains(t.Id), u => new VisitTask() { ExpetidEmailNoticeTime = DateTime.Now });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,16 +22,16 @@ public static class OldRecurringEmailHelper
|
||||||
public static async Task SendTrialEmailAsync(
|
public static async Task SendTrialEmailAsync(
|
||||||
IRepository<TrialEmailNoticeConfig> _trialEmailNoticeConfigRepository,
|
IRepository<TrialEmailNoticeConfig> _trialEmailNoticeConfigRepository,
|
||||||
IRepository<Trial> _trialRepository,
|
IRepository<Trial> _trialRepository,
|
||||||
IRepository<TrialUser> _trialUserRepository,
|
IRepository<TrialUserRole> _trialUserRoleRepository,
|
||||||
IRepository<VisitTask> _visitTaskRepository,
|
IRepository<VisitTask> _visitTaskRepository,
|
||||||
IRepository<TrialSiteUser> _trialSiteUserRepository,
|
IRepository<TrialSiteUserRole> _trialSiteUserRoleRepository,
|
||||||
|
|
||||||
Guid trialId, EmailBusinessScenario businessScenario,
|
Guid trialId, EmailBusinessScenario businessScenario,
|
||||||
Func<TrialEmailNoticeConfig, (string topicStr, string htmlBodyStr, bool isEn_us, Guid? onlyToUserId)> topicAndHtmlFunc,
|
Func<TrialEmailNoticeConfig, (string topicStr, string htmlBodyStr, bool isEn_us, Guid? onlyToUserId)> topicAndHtmlFunc,
|
||||||
Guid? trialSiteId = null, Guid? trialReadingCriterionId = null)
|
Guid? trialSiteId = null)
|
||||||
{
|
{
|
||||||
//找到配置
|
//找到配置
|
||||||
var trialEmailConfig = await _trialEmailNoticeConfigRepository.Where(t => t.TrialId == trialId && t.TrialReadingCriterionId == trialReadingCriterionId && t.BusinessScenarioEnum == businessScenario, ignoreQueryFilters: true)
|
var trialEmailConfig = await _trialEmailNoticeConfigRepository.Where(t => t.TrialId == trialId && t.BusinessScenarioEnum == businessScenario, ignoreQueryFilters: true)
|
||||||
.Include(t => t.TrialEmailNoticeUserList).Include(t => t.TrialEmailBlackUserList).FirstOrDefaultAsync();
|
.Include(t => t.TrialEmailNoticeUserList).Include(t => t.TrialEmailBlackUserList).FirstOrDefaultAsync();
|
||||||
|
|
||||||
|
|
||||||
|
@ -59,7 +59,7 @@ public static class OldRecurringEmailHelper
|
||||||
|
|
||||||
var allUserTypeEnumList = toUserTypeEnumList.Union(copyUserTypeEnumList).Distinct().ToList();
|
var allUserTypeEnumList = toUserTypeEnumList.Union(copyUserTypeEnumList).Distinct().ToList();
|
||||||
|
|
||||||
var allUserList = await _trialUserRepository.Where(t => t.TrialId == trialId && allUserTypeEnumList.Contains(t.User.UserTypeEnum)).Select(t => new { t.UserId, t.User.EMail, t.User.FullName, t.User.UserTypeEnum }).ToListAsync();
|
var allUserList = await _trialUserRoleRepository.Where(t => t.TrialId == trialId && allUserTypeEnumList.Contains(t.UserRole.UserTypeEnum)).Select(t => new { t.UserId, t.UserRole.IdentityUser.EMail, t.UserRole.FullName, t.UserRole.UserTypeEnum }).ToListAsync();
|
||||||
|
|
||||||
|
|
||||||
var toUserList = allUserList.Where(t => toUserTypeEnumList.Contains(t.UserTypeEnum))
|
var toUserList = allUserList.Where(t => toUserTypeEnumList.Contains(t.UserTypeEnum))
|
||||||
|
@ -72,7 +72,7 @@ public static class OldRecurringEmailHelper
|
||||||
}
|
}
|
||||||
if (trialSiteId != null && toUserTypeEnumList.Any(t => t == UserTypeEnum.ClinicalResearchCoordinator || t == UserTypeEnum.CRA))
|
if (trialSiteId != null && toUserTypeEnumList.Any(t => t == UserTypeEnum.ClinicalResearchCoordinator || t == UserTypeEnum.CRA))
|
||||||
{
|
{
|
||||||
var curentSiteUserIdList = _trialSiteUserRepository.Where(t => t.TrialId == trialId && t.TrialSiteId == trialSiteId).Select(t => t.UserId).ToList();
|
var curentSiteUserIdList = _trialSiteUserRoleRepository.Where(t => t.TrialId == trialId && t.TrialSiteId == trialSiteId).Select(t => t.UserId).ToList();
|
||||||
|
|
||||||
toUserList = toUserList.Where(t => (t.UserTypeEnum != UserTypeEnum.CRA && t.UserTypeEnum != UserTypeEnum.ClinicalResearchCoordinator) || curentSiteUserIdList.Contains(t.UserId)).ToList();
|
toUserList = toUserList.Where(t => (t.UserTypeEnum != UserTypeEnum.CRA && t.UserTypeEnum != UserTypeEnum.ClinicalResearchCoordinator) || curentSiteUserIdList.Contains(t.UserId)).ToList();
|
||||||
}
|
}
|
||||||
|
@ -86,7 +86,7 @@ public static class OldRecurringEmailHelper
|
||||||
|
|
||||||
if (trialSiteId != null && copyUserTypeEnumList.Any(t => t == UserTypeEnum.ClinicalResearchCoordinator || t == UserTypeEnum.CRA))
|
if (trialSiteId != null && copyUserTypeEnumList.Any(t => t == UserTypeEnum.ClinicalResearchCoordinator || t == UserTypeEnum.CRA))
|
||||||
{
|
{
|
||||||
var curentSiteUserIdList = _trialSiteUserRepository.Where(t => t.TrialId == trialId && t.TrialSiteId == trialSiteId).Select(t => t.UserId).ToList();
|
var curentSiteUserIdList = _trialSiteUserRoleRepository.Where(t => t.TrialId == trialId && t.TrialSiteId == trialSiteId).Select(t => t.UserId).ToList();
|
||||||
|
|
||||||
copyUserList = copyUserList.Where(t => (t.UserTypeEnum != UserTypeEnum.CRA && t.UserTypeEnum != UserTypeEnum.ClinicalResearchCoordinator) || curentSiteUserIdList.Contains(t.UserId)).ToList();
|
copyUserList = copyUserList.Where(t => (t.UserTypeEnum != UserTypeEnum.CRA && t.UserTypeEnum != UserTypeEnum.ClinicalResearchCoordinator) || curentSiteUserIdList.Contains(t.UserId)).ToList();
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,9 +23,9 @@ namespace IRaCIS.Core.Application.MassTransit.Consumer;
|
||||||
|
|
||||||
public class QCImageQuestionRecurringEventConsumer(IRepository<Trial> _trialRepository,
|
public class QCImageQuestionRecurringEventConsumer(IRepository<Trial> _trialRepository,
|
||||||
IRepository<TrialEmailNoticeConfig> _trialEmailNoticeConfigRepository,
|
IRepository<TrialEmailNoticeConfig> _trialEmailNoticeConfigRepository,
|
||||||
IRepository<TrialUser> _trialUserRepository,
|
IRepository<TrialUserRole> _trialUserRoleRepository,
|
||||||
IRepository<VisitTask> _visitTaskRepository,
|
IRepository<VisitTask> _visitTaskRepository,
|
||||||
IRepository<TrialSiteUser> _trialSiteUserRepository,
|
IRepository<TrialSiteUserRole> _trialSiteUserRoleRepository,
|
||||||
IOptionsMonitor<SystemEmailSendConfig> _SystemEmailSendConfig) : IConsumer<QCImageQuestionRecurringEvent>
|
IOptionsMonitor<SystemEmailSendConfig> _SystemEmailSendConfig) : IConsumer<QCImageQuestionRecurringEvent>
|
||||||
{
|
{
|
||||||
public async Task Consume(ConsumeContext<QCImageQuestionRecurringEvent> context)
|
public async Task Consume(ConsumeContext<QCImageQuestionRecurringEvent> context)
|
||||||
|
@ -37,7 +37,7 @@ public class QCImageQuestionRecurringEventConsumer(IRepository<Trial> _trialRepo
|
||||||
var trialInfo = await _trialRepository.Where(t => t.Id == trialId).Select(t => new { t.ResearchProgramNo, t.ExperimentName, t.TrialCode, t.TrialStatusStr }).FirstNotNullAsync();
|
var trialInfo = await _trialRepository.Where(t => t.Id == trialId).Select(t => new { t.ResearchProgramNo, t.ExperimentName, t.TrialCode, t.TrialStatusStr }).FirstNotNullAsync();
|
||||||
|
|
||||||
//找到 该项目的CRC 用户Id
|
//找到 该项目的CRC 用户Id
|
||||||
var userList = await _trialUserRepository.Where(t => t.TrialId == trialId).Where(t => t.User.UserTypeEnum == UserTypeEnum.ClinicalResearchCoordinator).Select(t => new { t.UserId, t.User.FullName }).ToListAsync();
|
var userList = await _trialUserRoleRepository.Where(t => t.TrialId == trialId).Where(t => t.UserRole.UserTypeEnum == UserTypeEnum.ClinicalResearchCoordinator).Select(t => new { t.UserId, t.UserRole.FullName }).ToListAsync();
|
||||||
|
|
||||||
//判断是否任务可以领取 ,可以的话 发送邮件
|
//判断是否任务可以领取 ,可以的话 发送邮件
|
||||||
|
|
||||||
|
@ -73,9 +73,9 @@ public class QCImageQuestionRecurringEventConsumer(IRepository<Trial> _trialRepo
|
||||||
await OldRecurringEmailHelper.SendTrialEmailAsync(
|
await OldRecurringEmailHelper.SendTrialEmailAsync(
|
||||||
_trialEmailNoticeConfigRepository,
|
_trialEmailNoticeConfigRepository,
|
||||||
_trialRepository,
|
_trialRepository,
|
||||||
_trialUserRepository,
|
_trialUserRoleRepository,
|
||||||
_visitTaskRepository,
|
_visitTaskRepository,
|
||||||
_trialSiteUserRepository,
|
_trialSiteUserRoleRepository,
|
||||||
trialId, EmailBusinessScenario.QCToCRCImageQuestion, topicAndHtmlFunc);
|
trialId, EmailBusinessScenario.QCToCRCImageQuestion, topicAndHtmlFunc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -88,9 +88,9 @@ public class QCImageQuestionRecurringEventConsumer(IRepository<Trial> _trialRepo
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class CRCImageQuestionRecurringEventConsumer(IRepository<Trial> _trialRepository,
|
public class CRCImageQuestionRecurringEventConsumer(IRepository<Trial> _trialRepository,
|
||||||
IRepository<TrialEmailNoticeConfig> _trialEmailNoticeConfigRepository,
|
IRepository<TrialEmailNoticeConfig> _trialEmailNoticeConfigRepository,
|
||||||
IRepository<TrialUser> _trialUserRepository,
|
IRepository<TrialUserRole> _trialUserRoleRepository,
|
||||||
IRepository<VisitTask> _visitTaskRepository,
|
IRepository<VisitTask> _visitTaskRepository,
|
||||||
IRepository<TrialSiteUser> _trialSiteUserRepository,
|
IRepository<TrialSiteUserRole> _trialSiteUserRoleRepository,
|
||||||
IOptionsMonitor<SystemEmailSendConfig> _SystemEmailSendConfig) : IConsumer<CRCImageQuestionRecurringEvent>
|
IOptionsMonitor<SystemEmailSendConfig> _SystemEmailSendConfig) : IConsumer<CRCImageQuestionRecurringEvent>
|
||||||
{
|
{
|
||||||
public async Task Consume(ConsumeContext<CRCImageQuestionRecurringEvent> context)
|
public async Task Consume(ConsumeContext<CRCImageQuestionRecurringEvent> context)
|
||||||
|
@ -101,7 +101,7 @@ public class CRCImageQuestionRecurringEventConsumer(IRepository<Trial> _trialRep
|
||||||
var trialInfo = await _trialRepository.Where(t => t.Id == trialId).Select(t => new { t.ResearchProgramNo, t.ExperimentName, t.TrialCode, t.TrialStatusStr, t.DeclarationTypeEnumList }).FirstNotNullAsync();
|
var trialInfo = await _trialRepository.Where(t => t.Id == trialId).Select(t => new { t.ResearchProgramNo, t.ExperimentName, t.TrialCode, t.TrialStatusStr, t.DeclarationTypeEnumList }).FirstNotNullAsync();
|
||||||
|
|
||||||
//找到 该项目的IQC 用户Id
|
//找到 该项目的IQC 用户Id
|
||||||
var userList = await _trialUserRepository.Where(t => t.TrialId == trialId).Where(t => t.User.UserTypeEnum == UserTypeEnum.IQC).Select(t => new { t.UserId, t.User.FullName }).ToListAsync();
|
var userList = await _trialUserRoleRepository.Where(t => t.TrialId == trialId).Where(t => t.UserRole.UserTypeEnum == UserTypeEnum.IQC).Select(t => new { t.UserId, t.UserRole.FullName }).ToListAsync();
|
||||||
|
|
||||||
//判断是否任务可以领取 ,可以的话 发送邮件
|
//判断是否任务可以领取 ,可以的话 发送邮件
|
||||||
|
|
||||||
|
@ -141,9 +141,9 @@ public class CRCImageQuestionRecurringEventConsumer(IRepository<Trial> _trialRep
|
||||||
|
|
||||||
await OldRecurringEmailHelper.SendTrialEmailAsync(_trialEmailNoticeConfigRepository,
|
await OldRecurringEmailHelper.SendTrialEmailAsync(_trialEmailNoticeConfigRepository,
|
||||||
_trialRepository,
|
_trialRepository,
|
||||||
_trialUserRepository,
|
_trialUserRoleRepository,
|
||||||
_visitTaskRepository,
|
_visitTaskRepository,
|
||||||
_trialSiteUserRepository,
|
_trialSiteUserRoleRepository,
|
||||||
trialId, EmailBusinessScenario.CRCToQCQuestion, topicAndHtmlFunc);
|
trialId, EmailBusinessScenario.CRCToQCQuestion, topicAndHtmlFunc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -155,9 +155,9 @@ public class CRCImageQuestionRecurringEventConsumer(IRepository<Trial> _trialRep
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class ImageQCRecurringEventConsumer(IRepository<Trial> _trialRepository,
|
public class ImageQCRecurringEventConsumer(IRepository<Trial> _trialRepository,
|
||||||
IRepository<TrialEmailNoticeConfig> _trialEmailNoticeConfigRepository,
|
IRepository<TrialEmailNoticeConfig> _trialEmailNoticeConfigRepository,
|
||||||
IRepository<TrialUser> _trialUserRepository,
|
IRepository<TrialUserRole> _trialUserRoleRepository,
|
||||||
IRepository<VisitTask> _visitTaskRepository,
|
IRepository<VisitTask> _visitTaskRepository,
|
||||||
IRepository<TrialSiteUser> _trialSiteUserRepository,
|
IRepository<TrialSiteUserRole> _trialSiteUserRoleRepository,
|
||||||
IOptionsMonitor<SystemEmailSendConfig> _SystemEmailSendConfig) : IConsumer<ImageQCRecurringEvent>
|
IOptionsMonitor<SystemEmailSendConfig> _SystemEmailSendConfig) : IConsumer<ImageQCRecurringEvent>
|
||||||
{
|
{
|
||||||
public async Task Consume(ConsumeContext<ImageQCRecurringEvent> context)
|
public async Task Consume(ConsumeContext<ImageQCRecurringEvent> context)
|
||||||
|
@ -169,7 +169,7 @@ public class ImageQCRecurringEventConsumer(IRepository<Trial> _trialRepository,
|
||||||
var trialInfo = await _trialRepository.Where(t => t.Id == trialId).Select(t => new { t.ResearchProgramNo, t.ExperimentName, t.TrialCode, t.TrialStatusStr }).FirstNotNullAsync();
|
var trialInfo = await _trialRepository.Where(t => t.Id == trialId).Select(t => new { t.ResearchProgramNo, t.ExperimentName, t.TrialCode, t.TrialStatusStr }).FirstNotNullAsync();
|
||||||
|
|
||||||
//找到 该项目的IQC 用户Id
|
//找到 该项目的IQC 用户Id
|
||||||
var userList = await _trialUserRepository.Where(t => t.TrialId == trialId).Where(t => t.User.UserTypeEnum == UserTypeEnum.IQC).Select(t => new { t.UserId, t.User.FullName }).ToListAsync();
|
var userList = await _trialUserRoleRepository.Where(t => t.TrialId == trialId).Where(t => t.UserRole.UserTypeEnum == UserTypeEnum.IQC).Select(t => new { t.UserId, t.UserRole.FullName }).ToListAsync();
|
||||||
|
|
||||||
//判断是否任务可以领取 ,可以的话 发送邮件
|
//判断是否任务可以领取 ,可以的话 发送邮件
|
||||||
var userIdList = userList.Select(t => t.UserId).ToList();
|
var userIdList = userList.Select(t => t.UserId).ToList();
|
||||||
|
@ -209,9 +209,9 @@ public class ImageQCRecurringEventConsumer(IRepository<Trial> _trialRepository,
|
||||||
await OldRecurringEmailHelper.SendTrialEmailAsync(
|
await OldRecurringEmailHelper.SendTrialEmailAsync(
|
||||||
_trialEmailNoticeConfigRepository,
|
_trialEmailNoticeConfigRepository,
|
||||||
_trialRepository,
|
_trialRepository,
|
||||||
_trialUserRepository,
|
_trialUserRoleRepository,
|
||||||
_visitTaskRepository,
|
_visitTaskRepository,
|
||||||
_trialSiteUserRepository,
|
_trialSiteUserRoleRepository,
|
||||||
trialId, EmailBusinessScenario.QCTask, topicAndHtmlFunc);
|
trialId, EmailBusinessScenario.QCTask, topicAndHtmlFunc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,4 +37,45 @@ public class UrgentIRUnReadTaskRecurringEvent : DomainEvent
|
||||||
public Guid TrialId { get; set; }
|
public Guid TrialId { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 定时提醒
|
||||||
|
/// </summary>
|
||||||
|
public class SystemDocumentErverDayEvent : DomainEvent
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public class SystemDocumentPublishEvent : DomainEvent
|
||||||
|
{
|
||||||
|
public List<Guid> Ids { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 新增的需要发送邮件的用户角色ID列表
|
||||||
|
/// 如果为null或空,则发送给所有相关角色
|
||||||
|
/// </summary>
|
||||||
|
public List<Guid> NewUserTypeIds { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 定时提醒
|
||||||
|
/// </summary>
|
||||||
|
public class TrialDocumentErverDayEvent : DomainEvent
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public class TrialDocumentPublishEvent : DomainEvent
|
||||||
|
{
|
||||||
|
public List<Guid> Ids { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 新增的需要发送邮件的用户角色ID列表
|
||||||
|
/// 如果为null或空,则发送给所有相关角色
|
||||||
|
/// </summary>
|
||||||
|
public List<Guid> NewUserTypeIds { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,277 @@
|
||||||
|
using DocumentFormat.OpenXml;
|
||||||
|
using IRaCIS.Application.Contracts;
|
||||||
|
using IRaCIS.Core.Application.Contracts;
|
||||||
|
using IRaCIS.Core.Application.Helper;
|
||||||
|
using IRaCIS.Core.Application.MassTransit.Consumer;
|
||||||
|
using IRaCIS.Core.Application.Service.Reading.Dto;
|
||||||
|
using IRaCIS.Core.Domain.Models;
|
||||||
|
using MassTransit;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using MimeKit;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reactive.Joins;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.Application.MassTransit.Recurring
|
||||||
|
{
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 定时过期提醒
|
||||||
|
/// </summary>
|
||||||
|
public class SystemDocumentErverDayEventConsumer(
|
||||||
|
IRepository<ReadingQuestionCriterionTrial> _trialReadingCriterionRepository,
|
||||||
|
IRepository<VisitTask> _visitTaskRepository,
|
||||||
|
IRepository<SystemDocument> _systemDocumentRepository,
|
||||||
|
IRepository<IdentityUser> _identityUserRepository,
|
||||||
|
IRepository<SystemDocConfirmedIdentityUser> _systemDocConfirmedUserRepository,
|
||||||
|
IRepository<Dictionary> _dictionaryRepository,
|
||||||
|
IRepository<TrialUserRole> _trialUserRoleRepository,
|
||||||
|
IRepository<EmailNoticeConfig> _emailNoticeConfigrepository,
|
||||||
|
|
||||||
|
IOptionsMonitor<SystemEmailSendConfig> systemEmailConfig) : IConsumer<SystemDocumentErverDayEvent>
|
||||||
|
{
|
||||||
|
private readonly SystemEmailSendConfig _systemEmailConfig = systemEmailConfig.CurrentValue;
|
||||||
|
|
||||||
|
public async Task Consume(ConsumeContext<SystemDocumentErverDayEvent> context)
|
||||||
|
{
|
||||||
|
|
||||||
|
DateTime now = DateTime.Now;
|
||||||
|
Console.WriteLine("发送定时过期提醒");
|
||||||
|
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
|
||||||
|
var systemDocQuery =
|
||||||
|
from sysDoc in _systemDocumentRepository.AsQueryable(false)
|
||||||
|
from identityUser in _identityUserRepository.AsQueryable(false).Where(t => t.UserRoleList.Where(t => t.IsUserRoleDisabled == false).Any(t => sysDoc.NeedConfirmedUserTypeList.AsQueryable().Any(c => c.NeedConfirmUserTypeId == t.UserTypeId)))
|
||||||
|
join confirmc in _systemDocConfirmedUserRepository.Where() on new { ConfirmUserId = identityUser.Id, SystemDocumentId = sysDoc.Id } equals new { confirmc.ConfirmUserId, confirmc.SystemDocumentId } into cc
|
||||||
|
from confirm in cc.DefaultIfEmpty()
|
||||||
|
select new UnionDocumentWithConfirmInfoView()
|
||||||
|
{
|
||||||
|
IsSystemDoc = true,
|
||||||
|
Id = sysDoc.Id,
|
||||||
|
CreateTime = sysDoc.CreateTime,
|
||||||
|
IsDeleted = sysDoc.IsDeleted,
|
||||||
|
SignViewMinimumMinutes = sysDoc.SignViewMinimumMinutes,
|
||||||
|
Name = sysDoc.Name,
|
||||||
|
Path = sysDoc.Path,
|
||||||
|
FileTypeId = sysDoc.FileTypeId,
|
||||||
|
UpdateTime = sysDoc.UpdateTime,
|
||||||
|
ConfirmUserId = identityUser.Id,
|
||||||
|
ConfirmTime = confirm.ConfirmTime,
|
||||||
|
UserCreateTime = identityUser.CreateTime,
|
||||||
|
CurrentStaffTrainDays = sysDoc.CurrentStaffTrainDays,
|
||||||
|
NewStaffTrainDays = sysDoc.NewStaffTrainDays,
|
||||||
|
RealName = identityUser.FullName,
|
||||||
|
UserName = identityUser.UserName,
|
||||||
|
|
||||||
|
|
||||||
|
IsNeedSendEmial = identityUser.IsZhiZhun || (!identityUser.IsZhiZhun && sysDoc.DocUserSignType == DocUserSignType.InnerAndOuter),
|
||||||
|
FullFilePath = sysDoc.Path
|
||||||
|
};
|
||||||
|
var datalist = await systemDocQuery.IgnoreQueryFilters().Where(t => t.IsDeleted == false && t.ConfirmTime == null)
|
||||||
|
.ToListAsync();
|
||||||
|
datalist = datalist.Where(x => x.SuggestFinishTime != null && x.SuggestFinishTime.Value.Date == DateTime.Now.Date)
|
||||||
|
.Where(x => x.IsNeedSendEmial).ToList();
|
||||||
|
var confirmUserIdList = datalist.Select(t => t.ConfirmUserId).Distinct().ToList();
|
||||||
|
var userinfoList = await _identityUserRepository.Where(x => confirmUserIdList.Contains(x.Id)).ToListAsync();
|
||||||
|
|
||||||
|
Console.WriteLine("发送定时过期提醒:人员数量" + userinfoList.Count);
|
||||||
|
int index = 1;
|
||||||
|
foreach (var userinfo in userinfoList)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Console.WriteLine($"{index}发送定时过期提醒,邮箱:{userinfo.EMail},姓名{userinfo.UserName}");
|
||||||
|
index++;
|
||||||
|
var messageToSend = new MimeMessage();
|
||||||
|
//发件地址
|
||||||
|
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
|
||||||
|
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
|
||||||
|
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
|
||||||
|
{
|
||||||
|
var topicStr = string.Format(input.topicStr, companyName);
|
||||||
|
|
||||||
|
var htmlBodyStr = string.Format(
|
||||||
|
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
|
||||||
|
userinfo.UserName, // 用户名 {0}
|
||||||
|
_systemEmailConfig.SiteUrl
|
||||||
|
);
|
||||||
|
|
||||||
|
return (topicStr, htmlBodyStr);
|
||||||
|
};
|
||||||
|
|
||||||
|
var scenario = EmailBusinessScenario.GeneralTraining_ExpirationNotification;
|
||||||
|
|
||||||
|
var emailConfig = _emailNoticeConfigrepository.Where(t => t.BusinessScenarioEnum == scenario && t.IsAutoSend && t.IsEnable).FirstOrDefault();
|
||||||
|
|
||||||
|
if (emailConfig != null)
|
||||||
|
{
|
||||||
|
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(emailConfig, messageToSend, emailConfigFunc);
|
||||||
|
|
||||||
|
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 生效通知
|
||||||
|
/// </summary>
|
||||||
|
public class SystemDocumentPublishEventConsumer(
|
||||||
|
IRepository<ReadingQuestionCriterionTrial> _trialReadingCriterionRepository,
|
||||||
|
IRepository<VisitTask> _visitTaskRepository,
|
||||||
|
IRepository<SystemDocument> _systemDocumentRepository,
|
||||||
|
IRepository<IdentityUser> _identityUserRepository,
|
||||||
|
IRepository<SystemDocConfirmedIdentityUser> _systemDocConfirmedUserRepository,
|
||||||
|
IRepository<Dictionary> _dictionaryRepository,
|
||||||
|
IRepository<TrialUserRole> _trialUserRoleRepository,
|
||||||
|
IRepository<EmailNoticeConfig> _emailNoticeConfigrepository,
|
||||||
|
|
||||||
|
IOptionsMonitor<SystemEmailSendConfig> systemEmailConfig) : IConsumer<SystemDocumentPublishEvent>
|
||||||
|
{
|
||||||
|
private readonly SystemEmailSendConfig _systemEmailConfig = systemEmailConfig.CurrentValue;
|
||||||
|
|
||||||
|
public async Task Consume(ConsumeContext<SystemDocumentPublishEvent> context)
|
||||||
|
{
|
||||||
|
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
|
||||||
|
|
||||||
|
// 记录是否只发送给新增角色的日志
|
||||||
|
if (context.Message.NewUserTypeIds != null && context.Message.NewUserTypeIds.Any())
|
||||||
|
{
|
||||||
|
Console.WriteLine($"只发送给新增的角色,角色数量: {context.Message.NewUserTypeIds.Count}");
|
||||||
|
}
|
||||||
|
// 构建查询
|
||||||
|
IQueryable<UnionDocumentWithConfirmInfoView> systemDocQuery;
|
||||||
|
|
||||||
|
if (context.Message.NewUserTypeIds != null && context.Message.NewUserTypeIds.Any())
|
||||||
|
{
|
||||||
|
// 只查询新增角色的用户
|
||||||
|
systemDocQuery =
|
||||||
|
from sysDoc in _systemDocumentRepository.AsQueryable(false).Where(x => context.Message.Ids.Contains(x.Id))
|
||||||
|
from identityUser in _identityUserRepository.AsQueryable(false)
|
||||||
|
.Where(t => t.Status == UserStateEnum.Enable &&
|
||||||
|
t.UserRoleList.Where(t => t.IsUserRoleDisabled == false)
|
||||||
|
.Any(t => context.Message.NewUserTypeIds.Contains(t.UserTypeId) &&
|
||||||
|
sysDoc.NeedConfirmedUserTypeList.AsQueryable().Any(c => c.NeedConfirmUserTypeId == t.UserTypeId)))
|
||||||
|
select new UnionDocumentWithConfirmInfoView()
|
||||||
|
{
|
||||||
|
IsSystemDoc = true,
|
||||||
|
Id = sysDoc.Id,
|
||||||
|
CreateTime = sysDoc.CreateTime,
|
||||||
|
IsDeleted = sysDoc.IsDeleted,
|
||||||
|
SignViewMinimumMinutes = sysDoc.SignViewMinimumMinutes,
|
||||||
|
Name = sysDoc.Name,
|
||||||
|
Path = sysDoc.Path,
|
||||||
|
FileTypeId = sysDoc.FileTypeId,
|
||||||
|
UpdateTime = sysDoc.UpdateTime,
|
||||||
|
ConfirmUserId = identityUser.Id,
|
||||||
|
RealName = identityUser.FullName,
|
||||||
|
UserName = identityUser.UserName,
|
||||||
|
IsNeedSendEmial = identityUser.IsZhiZhun || (!identityUser.IsZhiZhun && sysDoc.DocUserSignType == DocUserSignType.InnerAndOuter),
|
||||||
|
FullFilePath = sysDoc.Path
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 查询所有相关角色的用户
|
||||||
|
systemDocQuery =
|
||||||
|
from sysDoc in _systemDocumentRepository.AsQueryable(false).Where(x => context.Message.Ids.Contains(x.Id))
|
||||||
|
from identityUser in _identityUserRepository.AsQueryable(false)
|
||||||
|
.Where(t => t.Status == UserStateEnum.Enable &&
|
||||||
|
t.UserRoleList.Where(t => t.IsUserRoleDisabled == false)
|
||||||
|
.Any(t => sysDoc.NeedConfirmedUserTypeList.AsQueryable().Any(c => c.NeedConfirmUserTypeId == t.UserTypeId)))
|
||||||
|
select new UnionDocumentWithConfirmInfoView()
|
||||||
|
{
|
||||||
|
IsSystemDoc = true,
|
||||||
|
Id = sysDoc.Id,
|
||||||
|
CreateTime = sysDoc.CreateTime,
|
||||||
|
IsDeleted = sysDoc.IsDeleted,
|
||||||
|
SignViewMinimumMinutes = sysDoc.SignViewMinimumMinutes,
|
||||||
|
Name = sysDoc.Name,
|
||||||
|
Path = sysDoc.Path,
|
||||||
|
FileTypeId = sysDoc.FileTypeId,
|
||||||
|
UpdateTime = sysDoc.UpdateTime,
|
||||||
|
ConfirmUserId = identityUser.Id,
|
||||||
|
RealName = identityUser.FullName,
|
||||||
|
UserName = identityUser.UserName,
|
||||||
|
IsNeedSendEmial = identityUser.IsZhiZhun || (!identityUser.IsZhiZhun && sysDoc.DocUserSignType == DocUserSignType.InnerAndOuter),
|
||||||
|
FullFilePath = sysDoc.Path
|
||||||
|
};
|
||||||
|
}
|
||||||
|
var datalist = await systemDocQuery.IgnoreQueryFilters().Where(x => x.IsNeedSendEmial).ToListAsync();
|
||||||
|
|
||||||
|
var confirmUserIdList = datalist.Select(t => t.ConfirmUserId).Distinct().ToList();
|
||||||
|
var userinfoList = await _identityUserRepository.Where(x => confirmUserIdList.Contains(x.Id)).ToListAsync();
|
||||||
|
int index = 1;
|
||||||
|
foreach (var userinfo in userinfoList)
|
||||||
|
{
|
||||||
|
string msg = $"{index}生效通知,邮箱:{userinfo.EMail},姓名{userinfo.UserName},";
|
||||||
|
index++;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
|
||||||
|
var messageToSend = new MimeMessage();
|
||||||
|
//发件地址
|
||||||
|
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
|
||||||
|
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
|
||||||
|
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
|
||||||
|
{
|
||||||
|
var topicStr = string.Format(input.topicStr, companyName);
|
||||||
|
|
||||||
|
var htmlBodyStr = string.Format(
|
||||||
|
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
|
||||||
|
userinfo.UserName, // 用户名 {0}
|
||||||
|
_systemEmailConfig.SiteUrl
|
||||||
|
);
|
||||||
|
|
||||||
|
return (topicStr, htmlBodyStr);
|
||||||
|
};
|
||||||
|
|
||||||
|
var scenario = EmailBusinessScenario.GeneralTraining_EffectiveNotification;
|
||||||
|
|
||||||
|
var emailConfig = _emailNoticeConfigrepository.Where(t => t.BusinessScenarioEnum == scenario && t.IsAutoSend && t.IsEnable).FirstOrDefault();
|
||||||
|
|
||||||
|
if (emailConfig != null)
|
||||||
|
{
|
||||||
|
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(emailConfig, messageToSend, emailConfigFunc);
|
||||||
|
|
||||||
|
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
|
||||||
|
msg += "发送成功";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
msg += "发送失败";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Console.WriteLine(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,307 @@
|
||||||
|
using DocumentFormat.OpenXml;
|
||||||
|
using IRaCIS.Application.Contracts;
|
||||||
|
using IRaCIS.Core.Application.Contracts;
|
||||||
|
using IRaCIS.Core.Application.Helper;
|
||||||
|
using IRaCIS.Core.Application.MassTransit.Consumer;
|
||||||
|
using IRaCIS.Core.Application.Service.Reading.Dto;
|
||||||
|
using IRaCIS.Core.Domain.Models;
|
||||||
|
using MassTransit;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using MimeKit;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reactive.Joins;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.Application.MassTransit.Recurring
|
||||||
|
{
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 定时过期提醒
|
||||||
|
/// </summary>
|
||||||
|
public class TrialDocumentErverDayEventConsumer(
|
||||||
|
IRepository<ReadingQuestionCriterionTrial> _trialReadingCriterionRepository,
|
||||||
|
IRepository<VisitTask> _visitTaskRepository,
|
||||||
|
IRepository<SystemDocument> _systemDocumentRepository,
|
||||||
|
IRepository<IdentityUser> _identityUserRepository,
|
||||||
|
IRepository<SystemDocConfirmedIdentityUser> _systemDocConfirmedUserRepository,
|
||||||
|
IRepository<Dictionary> _dictionaryRepository,
|
||||||
|
IRepository<TrialUserRole> _trialUserRoleRepository, IRepository<TrialDocument> _trialDocumentRepository,
|
||||||
|
IRepository<Trial> _trialRepository,
|
||||||
|
ISystemDocumentService _systemDocumentService,
|
||||||
|
IRepository<SystemDocNeedConfirmedUserType> _systemDocNeedConfirmedUserTypeRepository,
|
||||||
|
IRepository<TrialDocNeedConfirmedUserType> _trialDocNeedConfirmedUserTypeRepository,
|
||||||
|
IServiceScopeFactory serviceScopeFactory,
|
||||||
|
IRepository<TrialIdentityUser> _trialIdentityUserRepository,
|
||||||
|
IRepository<TrialDocConfirmedIdentityUser> _trialDocConfirmedUserRepository,
|
||||||
|
IRepository<ReadingQuestionCriterionTrial> _readingQuestionCriterionTrialRepository,
|
||||||
|
IRepository<EmailNoticeConfig> _emailNoticeConfigrepository,
|
||||||
|
|
||||||
|
IOptionsMonitor<SystemEmailSendConfig> systemEmailConfig) : IConsumer<TrialDocumentErverDayEvent>
|
||||||
|
{
|
||||||
|
private readonly SystemEmailSendConfig _systemEmailConfig = systemEmailConfig.CurrentValue;
|
||||||
|
|
||||||
|
public async Task Consume(ConsumeContext<TrialDocumentErverDayEvent> context)
|
||||||
|
{
|
||||||
|
|
||||||
|
DateTime now = DateTime.Now;
|
||||||
|
Console.WriteLine("发送定时项目过期提醒");
|
||||||
|
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
|
||||||
|
var trialDocQuery =
|
||||||
|
from trialDoc in _trialDocumentRepository.AsQueryable(true)
|
||||||
|
|
||||||
|
join trialIdentityUser in _trialIdentityUserRepository.Where(x => x.IsDeleted == false) on trialDoc.TrialId equals trialIdentityUser.TrialId
|
||||||
|
join trialUserRole in _trialUserRoleRepository.Where(x => x.IsDeleted == false) on trialIdentityUser.Id equals trialUserRole.TrialUserId
|
||||||
|
from identityUser in _identityUserRepository.AsQueryable(false)
|
||||||
|
|
||||||
|
.Where(t => t.Status == UserStateEnum.Enable &&
|
||||||
|
t.Id == trialIdentityUser.IdentityUserId &&
|
||||||
|
t.UserRoleList.Where(t => t.IsUserRoleDisabled == false)
|
||||||
|
.Any(t => trialDoc.NeedConfirmedUserTypeList.AsQueryable().Any(c => c.NeedConfirmUserTypeId == t.UserTypeId)))
|
||||||
|
|
||||||
|
join confirm in _trialDocConfirmedUserRepository.Where() on
|
||||||
|
new { trialIdentityUser.IdentityUserId, TrialDocumentId = trialDoc.Id } equals new { IdentityUserId = confirm.ConfirmUserId, confirm.TrialDocumentId } into cc
|
||||||
|
|
||||||
|
from confirm in cc.DefaultIfEmpty()
|
||||||
|
select new TrialSignDocView()
|
||||||
|
{
|
||||||
|
TrialCode = trialDoc.Trial.TrialCode,
|
||||||
|
ResearchProgramNo = trialDoc.Trial.ResearchProgramNo,
|
||||||
|
ExperimentName = trialDoc.Trial.ExperimentName,
|
||||||
|
CurrentStaffTrainDays = trialDoc.CurrentStaffTrainDays,
|
||||||
|
NewStaffTrainDays = trialDoc.NewStaffTrainDays,
|
||||||
|
Id = trialDoc.Id,
|
||||||
|
IsSystemDoc = false,
|
||||||
|
CreateTime = trialDoc.CreateTime,
|
||||||
|
FullFilePath = trialDoc.Path,
|
||||||
|
IsDeleted = trialDoc.IsDeleted,
|
||||||
|
Name = trialDoc.Name,
|
||||||
|
Path = trialDoc.Path,
|
||||||
|
FileTypeId = trialDoc.FileTypeId,
|
||||||
|
UpdateTime = trialDoc.UpdateTime,
|
||||||
|
SignViewMinimumMinutes = trialDoc.SignViewMinimumMinutes,
|
||||||
|
//IsConfirmed = confirm.ConfirmTime != null,
|
||||||
|
ConfirmUserId = identityUser.Id,
|
||||||
|
ConfirmTime = confirm.ConfirmTime,
|
||||||
|
RealName = trialIdentityUser.IdentityUser.FullName,
|
||||||
|
UserName = trialIdentityUser.IdentityUser.UserName,
|
||||||
|
UserCreateTime= trialIdentityUser.CreateTime,
|
||||||
|
IdentityUserTypeList = trialIdentityUser.TrialUserRoleList.Select(t => t.UserRole.UserTypeRole.UserTypeShortName).ToList(),
|
||||||
|
|
||||||
|
DocNeedSignUserTypeList = trialDoc.NeedConfirmedUserTypeList.Select(t => t.UserTypeRole.UserTypeShortName).ToList(),
|
||||||
|
};
|
||||||
|
var datalist = await trialDocQuery.IgnoreQueryFilters().Where(t => t.IsDeleted == false && t.ConfirmTime == null&&t.ConfirmTime==null)
|
||||||
|
.ToListAsync();
|
||||||
|
datalist = datalist.Where(x => x.SuggestFinishTime != null && x.SuggestFinishTime.Value.Date == DateTime.Now.Date)
|
||||||
|
.ToList();
|
||||||
|
var confirmUserIdList = datalist.Select(t => t.ConfirmUserId).Distinct().ToList();
|
||||||
|
var userinfoList = await _identityUserRepository.Where(x => confirmUserIdList.Contains(x.Id)).ToListAsync();
|
||||||
|
|
||||||
|
Console.WriteLine("发送定时项目过期提醒:人员数量" + userinfoList.Count);
|
||||||
|
int index = 1;
|
||||||
|
foreach (var userinfo in userinfoList)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Console.WriteLine($"{index}发送定时过期提醒,邮箱:{userinfo.EMail},姓名{userinfo.UserName}");
|
||||||
|
index++;
|
||||||
|
var messageToSend = new MimeMessage();
|
||||||
|
//发件地址
|
||||||
|
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
|
||||||
|
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
|
||||||
|
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
|
||||||
|
{
|
||||||
|
var topicStr = string.Format(input.topicStr, companyName);
|
||||||
|
|
||||||
|
var htmlBodyStr = string.Format(
|
||||||
|
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
|
||||||
|
userinfo.UserName, // 用户名 {0}
|
||||||
|
_systemEmailConfig.SiteUrl
|
||||||
|
);
|
||||||
|
|
||||||
|
return (topicStr, htmlBodyStr);
|
||||||
|
};
|
||||||
|
|
||||||
|
var scenario = EmailBusinessScenario.TrialTraining_ExpirationNotification;
|
||||||
|
|
||||||
|
var emailConfig = _emailNoticeConfigrepository.Where(t => t.BusinessScenarioEnum == scenario && t.IsAutoSend && t.IsEnable).FirstOrDefault();
|
||||||
|
|
||||||
|
if (emailConfig != null)
|
||||||
|
{
|
||||||
|
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(emailConfig, messageToSend, emailConfigFunc);
|
||||||
|
|
||||||
|
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 生效通知
|
||||||
|
/// </summary>
|
||||||
|
public class TrialDocumentPublishEventConsumer(
|
||||||
|
IRepository<ReadingQuestionCriterionTrial> _trialReadingCriterionRepository,
|
||||||
|
IRepository<VisitTask> _visitTaskRepository,
|
||||||
|
IRepository<TrialDocument> _trialDocumentRepository,
|
||||||
|
IRepository<IdentityUser> _identityUserRepository,
|
||||||
|
IRepository<TrialIdentityUser> _trialIdentityUserRepository,
|
||||||
|
IRepository<Dictionary> _dictionaryRepository,
|
||||||
|
IRepository<TrialUserRole> _trialUserRoleRepository,
|
||||||
|
IRepository<EmailNoticeConfig> _emailNoticeConfigrepository,
|
||||||
|
|
||||||
|
IOptionsMonitor<SystemEmailSendConfig> systemEmailConfig) : IConsumer<TrialDocumentPublishEvent>
|
||||||
|
{
|
||||||
|
private readonly SystemEmailSendConfig _systemEmailConfig = systemEmailConfig.CurrentValue;
|
||||||
|
|
||||||
|
public async Task Consume(ConsumeContext<TrialDocumentPublishEvent> context)
|
||||||
|
{
|
||||||
|
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
|
||||||
|
|
||||||
|
// 记录是否只发送给新增角色的日志
|
||||||
|
if (context.Message.NewUserTypeIds != null && context.Message.NewUserTypeIds.Any())
|
||||||
|
{
|
||||||
|
Console.WriteLine($"只发送给新增项目的角色,角色数量: {context.Message.NewUserTypeIds.Count}");
|
||||||
|
}
|
||||||
|
// 构建查询
|
||||||
|
IQueryable<UnionDocumentWithConfirmInfoView> systemDocQuery;
|
||||||
|
|
||||||
|
if (context.Message.NewUserTypeIds != null && context.Message.NewUserTypeIds.Any())
|
||||||
|
{
|
||||||
|
// 只查询新增角色的用户
|
||||||
|
systemDocQuery =
|
||||||
|
from trialDoc in _trialDocumentRepository.AsQueryable(false).Where(x => context.Message.Ids.Contains(x.Id))
|
||||||
|
join trialIdentityUser in _trialIdentityUserRepository.Where(x=>x.IsDeleted==false) on trialDoc.TrialId equals trialIdentityUser.TrialId
|
||||||
|
join trialUserRole in _trialUserRoleRepository.Where(x => x.IsDeleted == false) on trialIdentityUser.Id equals trialUserRole.TrialUserId
|
||||||
|
from identityUser in _identityUserRepository.AsQueryable(false)
|
||||||
|
.Where(t => t.Status == UserStateEnum.Enable &&
|
||||||
|
t.Id == trialIdentityUser.IdentityUserId &&
|
||||||
|
t.UserRoleList.Where(t => t.IsUserRoleDisabled == false)
|
||||||
|
.Any(t => context.Message.NewUserTypeIds.Contains(t.UserTypeId) &&
|
||||||
|
trialDoc.NeedConfirmedUserTypeList.AsQueryable().Any(c => c.NeedConfirmUserTypeId == t.UserTypeId)))
|
||||||
|
select new UnionDocumentWithConfirmInfoView()
|
||||||
|
{
|
||||||
|
IsSystemDoc = true,
|
||||||
|
Id = trialDoc.Id,
|
||||||
|
CreateTime = trialDoc.CreateTime,
|
||||||
|
IsDeleted = trialDoc.IsDeleted,
|
||||||
|
SignViewMinimumMinutes = trialDoc.SignViewMinimumMinutes,
|
||||||
|
Name = trialDoc.Name,
|
||||||
|
Path = trialDoc.Path,
|
||||||
|
FileTypeId = trialDoc.FileTypeId,
|
||||||
|
UpdateTime = trialDoc.UpdateTime,
|
||||||
|
ConfirmUserId = identityUser.Id,
|
||||||
|
RealName = identityUser.FullName,
|
||||||
|
UserName = identityUser.UserName,
|
||||||
|
IsNeedSendEmial = identityUser.IsZhiZhun,
|
||||||
|
FullFilePath = trialDoc.Path
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 查询所有相关角色的用户
|
||||||
|
systemDocQuery =
|
||||||
|
from trialDoc in _trialDocumentRepository.AsQueryable(false).Where(x => context.Message.Ids.Contains(x.Id))
|
||||||
|
join trialIdentityUser in _trialIdentityUserRepository.Where(x => x.IsDeleted == false) on trialDoc.TrialId equals trialIdentityUser.TrialId
|
||||||
|
join trialUserRole in _trialUserRoleRepository.Where(x=>x.IsDeleted==false) on trialIdentityUser.Id equals trialUserRole.TrialUserId
|
||||||
|
from identityUser in _identityUserRepository.AsQueryable(false)
|
||||||
|
.Where(t => t.Status == UserStateEnum.Enable &&
|
||||||
|
t.Id== trialIdentityUser.IdentityUserId&&
|
||||||
|
t.UserRoleList.Where(t => t.IsUserRoleDisabled == false)
|
||||||
|
.Any(t => trialDoc.NeedConfirmedUserTypeList.AsQueryable().Any(c => c.NeedConfirmUserTypeId == t.UserTypeId)))
|
||||||
|
select new UnionDocumentWithConfirmInfoView()
|
||||||
|
{
|
||||||
|
IsSystemDoc = false,
|
||||||
|
Id = trialDoc.Id,
|
||||||
|
CreateTime = trialDoc.CreateTime,
|
||||||
|
IsDeleted = trialDoc.IsDeleted,
|
||||||
|
SignViewMinimumMinutes = trialDoc.SignViewMinimumMinutes,
|
||||||
|
Name = trialDoc.Name,
|
||||||
|
Path = trialDoc.Path,
|
||||||
|
FileTypeId = trialDoc.FileTypeId,
|
||||||
|
UpdateTime = trialDoc.UpdateTime,
|
||||||
|
ConfirmUserId = identityUser.Id,
|
||||||
|
RealName = identityUser.FullName,
|
||||||
|
UserName = identityUser.UserName,
|
||||||
|
IsNeedSendEmial = identityUser.IsZhiZhun ,
|
||||||
|
FullFilePath = trialDoc.Path
|
||||||
|
};
|
||||||
|
}
|
||||||
|
var datalist = await systemDocQuery.IgnoreQueryFilters().ToListAsync();
|
||||||
|
|
||||||
|
var confirmUserIdList = datalist.Select(t => t.ConfirmUserId).Distinct().ToList();
|
||||||
|
var userinfoList = await _identityUserRepository.Where(x => confirmUserIdList.Contains(x.Id)).ToListAsync();
|
||||||
|
int index = 1;
|
||||||
|
foreach (var userinfo in userinfoList)
|
||||||
|
{
|
||||||
|
string msg = $"{index}项目生效通知,邮箱:{userinfo.EMail},姓名{userinfo.UserName},";
|
||||||
|
index++;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
|
||||||
|
var messageToSend = new MimeMessage();
|
||||||
|
//发件地址
|
||||||
|
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
|
||||||
|
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
|
||||||
|
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
|
||||||
|
{
|
||||||
|
var topicStr = string.Format(input.topicStr, companyName);
|
||||||
|
|
||||||
|
var htmlBodyStr = string.Format(
|
||||||
|
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
|
||||||
|
userinfo.UserName, // 用户名 {0}
|
||||||
|
_systemEmailConfig.SiteUrl
|
||||||
|
);
|
||||||
|
|
||||||
|
return (topicStr, htmlBodyStr);
|
||||||
|
};
|
||||||
|
|
||||||
|
var scenario = EmailBusinessScenario.TrialTraining_EffectiveNotification;
|
||||||
|
|
||||||
|
var emailConfig = _emailNoticeConfigrepository.Where(t => t.BusinessScenarioEnum == scenario && t.IsEnable).FirstOrDefault();
|
||||||
|
|
||||||
|
if (emailConfig != null)
|
||||||
|
{
|
||||||
|
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(emailConfig, messageToSend, emailConfigFunc);
|
||||||
|
|
||||||
|
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
|
||||||
|
msg += "发送成功";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
msg += "发送失败";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Console.WriteLine(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -214,6 +214,10 @@ namespace IRaCIS.Core.Application.ViewModel
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsManualGeneration { get; set; }
|
public bool IsManualGeneration { get; set; }
|
||||||
//public bool IsAfterConvertedTask { get; set; }
|
//public bool IsAfterConvertedTask { get; set; }
|
||||||
|
|
||||||
|
public string PMBackReason { get; set; }
|
||||||
|
|
||||||
|
public int? RandomOrder { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -233,7 +237,7 @@ namespace IRaCIS.Core.Application.ViewModel
|
||||||
//重阅原始编号
|
//重阅原始编号
|
||||||
//public string ReReadingOriginalTaskCode { get; set; }
|
//public string ReReadingOriginalTaskCode { get; set; }
|
||||||
|
|
||||||
|
public string ApplicantName { get; set; }
|
||||||
|
|
||||||
public Guid Id { get; set; }
|
public Guid Id { get; set; }
|
||||||
|
|
||||||
|
@ -339,6 +343,16 @@ namespace IRaCIS.Core.Application.ViewModel
|
||||||
|
|
||||||
public int UrgentCount { get; set; }
|
public int UrgentCount { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否存在未处理的反馈
|
||||||
|
/// </summary>
|
||||||
|
public bool IsExistUnprocessedFeedback
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return UnReadCanReadTaskList.Any(t => t.IsExistUnprocessedFeedback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public List<IRUnreadTaskView> UnReadCanReadTaskList { get; set; } = new List<IRUnreadTaskView>();
|
public List<IRUnreadTaskView> UnReadCanReadTaskList { get; set; } = new List<IRUnreadTaskView>();
|
||||||
|
|
||||||
|
@ -355,6 +369,11 @@ namespace IRaCIS.Core.Application.ViewModel
|
||||||
/// 是否是一致性分析产生
|
/// 是否是一致性分析产生
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsAnalysisCreate { get; set; }
|
public bool IsAnalysisCreate { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否存在未处理的反馈
|
||||||
|
/// </summary>
|
||||||
|
public bool IsExistUnprocessedFeedback { get; set; }
|
||||||
|
|
||||||
public bool IsUrgent { get; set; }
|
public bool IsUrgent { get; set; }
|
||||||
|
|
||||||
|
@ -465,6 +484,9 @@ namespace IRaCIS.Core.Application.ViewModel
|
||||||
public string? RequestReReadingReason { get; set; }
|
public string? RequestReReadingReason { get; set; }
|
||||||
|
|
||||||
public ExportResult? ReadingExportType { get; set; }
|
public ExportResult? ReadingExportType { get; set; }
|
||||||
|
|
||||||
|
public int? RandomOrder { get; set; }
|
||||||
|
public bool? IsRandomOrderList { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -891,6 +913,30 @@ namespace IRaCIS.Core.Application.ViewModel
|
||||||
CancelAssign = 4,
|
CancelAssign = 4,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public class SetRandomTaskOrderCommand
|
||||||
|
{
|
||||||
|
[NotDefault]
|
||||||
|
public Guid TrialId { get; set; }
|
||||||
|
|
||||||
|
[NotDefault]
|
||||||
|
public Guid TrialReadingCriterionId { get; set; }
|
||||||
|
|
||||||
|
[NotDefault]
|
||||||
|
public Guid DoctorUserId { get; set; }
|
||||||
|
|
||||||
|
public bool IsAutoSet { get; set; }
|
||||||
|
|
||||||
|
public List<VisitTaskOrderCommand> SetList { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class VisitTaskOrderCommand
|
||||||
|
{
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
|
||||||
|
public int? RandomOrder { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ namespace IRaCIS.Core.Application.Service
|
||||||
{
|
{
|
||||||
Task<IResponseOutput> ApplyReReading(ApplyReReadingCommand applyReReadingCommand);
|
Task<IResponseOutput> ApplyReReading(ApplyReReadingCommand applyReReadingCommand);
|
||||||
|
|
||||||
Task<IResponseOutput> ConfirmReReading(ConfirmReReadingCommand agreeReReadingCommand, [FromServices] IVisitTaskHelpeService _visitTaskCommonService);
|
Task<IResponseOutput> ConfirmReReading(ConfirmReReadingCommand agreeReReadingCommand);
|
||||||
|
|
||||||
Task<PageOutput<IRUnReadSubjectView>> GetSubjectReadingIQueryable(GetReadingIQueryableInDto inDto);
|
Task<PageOutput<IRUnReadSubjectView>> GetSubjectReadingIQueryable(GetReadingIQueryableInDto inDto);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,10 +4,12 @@
|
||||||
// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。
|
// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。
|
||||||
//--------------------------------------------------------------------
|
//--------------------------------------------------------------------
|
||||||
|
|
||||||
|
using DocumentFormat.OpenXml.Spreadsheet;
|
||||||
using IRaCIS.Core.Application.Contracts;
|
using IRaCIS.Core.Application.Contracts;
|
||||||
using IRaCIS.Core.Application.Filter;
|
using IRaCIS.Core.Application.Filter;
|
||||||
using IRaCIS.Core.Application.Interfaces;
|
using IRaCIS.Core.Application.Interfaces;
|
||||||
using IRaCIS.Core.Application.ViewModel;
|
using IRaCIS.Core.Application.ViewModel;
|
||||||
|
using IRaCIS.Core.Domain.Models;
|
||||||
using IRaCIS.Core.Domain.Share;
|
using IRaCIS.Core.Domain.Share;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
@ -18,7 +20,7 @@ namespace IRaCIS.Core.Application.Service
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ApiExplorerSettings(GroupName = "Trial")]
|
[ApiExplorerSettings(GroupName = "Trial")]
|
||||||
public class TaskAllocationRuleService(IRepository<TaskAllocationRule> _taskAllocationRuleRepository,
|
public class TaskAllocationRuleService(IRepository<TaskAllocationRule> _taskAllocationRuleRepository,
|
||||||
IRepository<User> _userRepository,
|
IRepository<UserRole> _userRoleRepository,
|
||||||
IRepository<Enroll> _enrollRepository,
|
IRepository<Enroll> _enrollRepository,
|
||||||
IRepository<ReadingQuestionCriterionTrial> _readingQuestionCriterionTrialRepository,
|
IRepository<ReadingQuestionCriterionTrial> _readingQuestionCriterionTrialRepository,
|
||||||
IRepository<SubjectCanceDoctor> _subjectCanceDoctorRepository,
|
IRepository<SubjectCanceDoctor> _subjectCanceDoctorRepository,
|
||||||
|
@ -124,15 +126,15 @@ namespace IRaCIS.Core.Application.Service
|
||||||
public async Task<List<TrialDoctorUserSelectView>> GetDoctorUserSelectList(Guid trialId, [FromServices] IRepository<Enroll> _enrollRepository)
|
public async Task<List<TrialDoctorUserSelectView>> GetDoctorUserSelectList(Guid trialId, [FromServices] IRepository<Enroll> _enrollRepository)
|
||||||
{
|
{
|
||||||
var query = from enroll in _enrollRepository.Where(t => t.TrialId == trialId && t.EnrollStatus >= EnrollStatus.ConfirmIntoGroup)
|
var query = from enroll in _enrollRepository.Where(t => t.TrialId == trialId && t.EnrollStatus >= EnrollStatus.ConfirmIntoGroup)
|
||||||
join user in _userRepository.AsQueryable() on enroll.DoctorId equals user.DoctorId
|
join user in _userRoleRepository.AsQueryable() on enroll.DoctorId equals user.DoctorId
|
||||||
select new TrialDoctorUserSelectView()
|
select new TrialDoctorUserSelectView()
|
||||||
{
|
{
|
||||||
TrialId = enroll.TrialId,
|
TrialId = enroll.TrialId,
|
||||||
//ReadingType = enroll.Trial.ReadingType,
|
//ReadingType = enroll.Trial.ReadingType,
|
||||||
DoctorUserId = user.Id,
|
DoctorUserId = user.Id,
|
||||||
FullName = user.FullName,
|
FullName = user.IdentityUser.FullName,
|
||||||
UserCode = user.UserCode,
|
UserCode = user.IdentityUser.UserCode,
|
||||||
UserName = user.UserName,
|
UserName = user.IdentityUser.UserName,
|
||||||
UserTypeEnum = user.UserTypeRole.UserTypeEnum,
|
UserTypeEnum = user.UserTypeRole.UserTypeEnum,
|
||||||
ReadingCategoryList = enroll.EnrollReadingCategoryList.Select(t => t.ReadingCategory).ToList()
|
ReadingCategoryList = enroll.EnrollReadingCategoryList.Select(t => t.ReadingCategory).ToList()
|
||||||
|
|
||||||
|
@ -151,14 +153,14 @@ namespace IRaCIS.Core.Application.Service
|
||||||
.WhereIf(selectQuery.TrialReadingCriterionId != null && selectQuery.ReadingCategory == null, t => t.Enroll.EnrollReadingCategoryList.Any(t => t.TrialReadingCriterionId == selectQuery.TrialReadingCriterionId))
|
.WhereIf(selectQuery.TrialReadingCriterionId != null && selectQuery.ReadingCategory == null, t => t.Enroll.EnrollReadingCategoryList.Any(t => t.TrialReadingCriterionId == selectQuery.TrialReadingCriterionId))
|
||||||
.WhereIf(selectQuery.TrialReadingCriterionId != null && selectQuery.ReadingCategory != null,
|
.WhereIf(selectQuery.TrialReadingCriterionId != null && selectQuery.ReadingCategory != null,
|
||||||
t => t.Enroll.EnrollReadingCategoryList.Any(t => t.TrialReadingCriterionId == selectQuery.TrialReadingCriterionId && t.ReadingCategory == selectQuery.ReadingCategory))
|
t => t.Enroll.EnrollReadingCategoryList.Any(t => t.TrialReadingCriterionId == selectQuery.TrialReadingCriterionId && t.ReadingCategory == selectQuery.ReadingCategory))
|
||||||
join user in _userRepository.AsQueryable() on allocationRule.DoctorUserId equals user.Id
|
join user in _userRoleRepository.AsQueryable() on allocationRule.DoctorUserId equals user.Id
|
||||||
select new TrialDoctorUserSelectView()
|
select new TrialDoctorUserSelectView()
|
||||||
{
|
{
|
||||||
TrialId = allocationRule.TrialId,
|
TrialId = allocationRule.TrialId,
|
||||||
DoctorUserId = user.Id,
|
DoctorUserId = user.Id,
|
||||||
FullName = user.FullName,
|
FullName = user.IdentityUser.FullName,
|
||||||
UserCode = user.UserCode,
|
UserCode = user.IdentityUser.UserCode,
|
||||||
UserName = user.UserName,
|
UserName = user.IdentityUser.UserName,
|
||||||
UserTypeEnum = user.UserTypeRole.UserTypeEnum,
|
UserTypeEnum = user.UserTypeRole.UserTypeEnum,
|
||||||
|
|
||||||
ReadingCategoryList = selectQuery.TrialReadingCriterionId == null ?
|
ReadingCategoryList = selectQuery.TrialReadingCriterionId == null ?
|
||||||
|
|
|
@ -30,7 +30,7 @@ namespace IRaCIS.Core.Application.Service
|
||||||
IRepository<Trial> _trialRepository,
|
IRepository<Trial> _trialRepository,
|
||||||
IRepository<TrialSite> _trialSiteRepository,
|
IRepository<TrialSite> _trialSiteRepository,
|
||||||
IRepository<Enroll> _enrollRepository,
|
IRepository<Enroll> _enrollRepository,
|
||||||
IRepository<User> _userRepository,
|
IRepository<UserRole> _userRoleRepository,
|
||||||
IRepository<TrialVirtualSiteCodeUpdate> _trialVirtualSiteCodeUpdateRepository,
|
IRepository<TrialVirtualSiteCodeUpdate> _trialVirtualSiteCodeUpdateRepository,
|
||||||
IVisitTaskHelpeService _visitTaskCommonService, IMapper _mapper, IUserInfo _userInfo, IStringLocalizer _localizer) : BaseService, ITaskConsistentRuleService
|
IVisitTaskHelpeService _visitTaskCommonService, IMapper _mapper, IUserInfo _userInfo, IStringLocalizer _localizer) : BaseService, ITaskConsistentRuleService
|
||||||
{
|
{
|
||||||
|
@ -369,7 +369,7 @@ namespace IRaCIS.Core.Application.Service
|
||||||
|
|
||||||
var doctorUserIdQuery = from enroll in _enrollRepository.Where(t => t.TrialId == trialId).Where(t => t.EnrollReadingCategoryList.Where(t => t.TrialReadingCriterionId == trialReadingCriterionId).Any(c => c.ReadingCategory == ReadingCategory.Global || c.ReadingCategory == ReadingCategory.Visit))
|
var doctorUserIdQuery = from enroll in _enrollRepository.Where(t => t.TrialId == trialId).Where(t => t.EnrollReadingCategoryList.Where(t => t.TrialReadingCriterionId == trialReadingCriterionId).Any(c => c.ReadingCategory == ReadingCategory.Global || c.ReadingCategory == ReadingCategory.Visit))
|
||||||
.Where(t => t.EnrollReadingCriteriaList.Any(t => t.TrialReadingCriterionId == trialReadingCriterionId && t.IsJoinAnalysis == true))
|
.Where(t => t.EnrollReadingCriteriaList.Any(t => t.TrialReadingCriterionId == trialReadingCriterionId && t.IsJoinAnalysis == true))
|
||||||
join user in _userRepository.Where() on enroll.DoctorId equals user.DoctorId
|
join user in _userRoleRepository.Where() on enroll.DoctorId equals user.DoctorId
|
||||||
select user.Id;
|
select user.Id;
|
||||||
|
|
||||||
var configDoctorUserIdList = await doctorUserIdQuery.ToListAsync();
|
var configDoctorUserIdList = await doctorUserIdQuery.ToListAsync();
|
||||||
|
@ -707,9 +707,9 @@ namespace IRaCIS.Core.Application.Service
|
||||||
DoctorUserList = t.SubjectDoctorList.Where(t => t.TrialReadingCriterionId == trialReadingCriterionId && t.IsConfirmed && t.ArmEnum <= Arm.DoubleReadingArm2).Select(t => new UserSimpleInfo()
|
DoctorUserList = t.SubjectDoctorList.Where(t => t.TrialReadingCriterionId == trialReadingCriterionId && t.IsConfirmed && t.ArmEnum <= Arm.DoubleReadingArm2).Select(t => new UserSimpleInfo()
|
||||||
{
|
{
|
||||||
UserId = t.Id,
|
UserId = t.Id,
|
||||||
FullName = t.DoctorUser.FullName,
|
FullName = t.DoctorUser.IdentityUser.FullName,
|
||||||
UserCode = t.DoctorUser.UserCode,
|
UserCode = t.DoctorUser.IdentityUser.UserCode,
|
||||||
UserName = t.DoctorUser.UserName
|
UserName = t.DoctorUser.IdentityUser.UserName
|
||||||
}).ToList(),
|
}).ToList(),
|
||||||
|
|
||||||
ValidVisitCount = t.SubjectVisitTaskList.AsQueryable().Where(visitTaskFilter).GroupBy(t => new { t.SubjectId, t.VisitTaskNum }).Where(g => g.Count() == 2).Count(),
|
ValidVisitCount = t.SubjectVisitTaskList.AsQueryable().Where(visitTaskFilter).GroupBy(t => new { t.SubjectId, t.VisitTaskNum }).Where(g => g.Count() == 2).Count(),
|
||||||
|
@ -784,7 +784,7 @@ namespace IRaCIS.Core.Application.Service
|
||||||
|
|
||||||
var taskConsistentRuleQueryable = from enroll in _enrollRepository.Where(t => t.TrialId == trialId && t.EnrollStatus == EnrollStatus.ConfirmIntoGroup
|
var taskConsistentRuleQueryable = from enroll in _enrollRepository.Where(t => t.TrialId == trialId && t.EnrollStatus == EnrollStatus.ConfirmIntoGroup
|
||||||
&& t.EnrollReadingCriteriaList.Any(c => c.TrialReadingCriterionId == inQuery.TrialReadingCriterionId && c.IsJoinAnalysis))
|
&& t.EnrollReadingCriteriaList.Any(c => c.TrialReadingCriterionId == inQuery.TrialReadingCriterionId && c.IsJoinAnalysis))
|
||||||
join user in _userRepository.Where() on enroll.DoctorUserId equals user.Id
|
join user in _userRoleRepository.Where() on enroll.DoctorUserId equals user.Id
|
||||||
join taskConsistentRule in _taskConsistentRuleRepository.Where(t => t.TrialId == trialId && t.TrialReadingCriterionId == inQuery.TrialReadingCriterionId && t.IsSelfAnalysis) on enroll.TrialId equals taskConsistentRule.TrialId
|
join taskConsistentRule in _taskConsistentRuleRepository.Where(t => t.TrialId == trialId && t.TrialReadingCriterionId == inQuery.TrialReadingCriterionId && t.IsSelfAnalysis) on enroll.TrialId equals taskConsistentRule.TrialId
|
||||||
select new TaskConsistentRuleView()
|
select new TaskConsistentRuleView()
|
||||||
{
|
{
|
||||||
|
@ -830,9 +830,9 @@ namespace IRaCIS.Core.Application.Service
|
||||||
AnalysisDoctorUser = new UserSimpleInfo()
|
AnalysisDoctorUser = new UserSimpleInfo()
|
||||||
{
|
{
|
||||||
UserId = user.Id,
|
UserId = user.Id,
|
||||||
UserCode = user.UserCode,
|
UserCode = user.IdentityUser.UserCode,
|
||||||
FullName = user.FullName,
|
FullName = user.IdentityUser.FullName,
|
||||||
UserName = user.UserName
|
UserName = user.IdentityUser.UserName
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ namespace IRaCIS.Core.Application.Service
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ApiExplorerSettings(GroupName = "Trial")]
|
[ApiExplorerSettings(GroupName = "Trial")]
|
||||||
public class TaskMedicalReviewRuleService(IRepository<TaskMedicalReviewRule> _taskMedicalReviewRuleRepository,
|
public class TaskMedicalReviewRuleService(IRepository<TaskMedicalReviewRule> _taskMedicalReviewRuleRepository,
|
||||||
IRepository<TrialUser> _trialUserRepository, IMapper _mapper, IUserInfo _userInfo, IStringLocalizer _localizer) : BaseService, ITaskMedicalReviewRuleService
|
IRepository<TrialUserRole> _trialUserRoleRepository, IMapper _mapper, IUserInfo _userInfo, IStringLocalizer _localizer) : BaseService, ITaskMedicalReviewRuleService
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ namespace IRaCIS.Core.Application.Service
|
||||||
var taskTaskMedicalReviewRuleQueryable = _taskMedicalReviewRuleRepository.Where(t => t.TrialId == inQuery.TrialId)
|
var taskTaskMedicalReviewRuleQueryable = _taskMedicalReviewRuleRepository.Where(t => t.TrialId == inQuery.TrialId)
|
||||||
.ProjectTo<TaskMedicalReviewRuleView>(_mapper.ConfigurationProvider);
|
.ProjectTo<TaskMedicalReviewRuleView>(_mapper.ConfigurationProvider);
|
||||||
|
|
||||||
var isHaveMIM = await _trialUserRepository.AnyAsync(t => t.User.UserTypeEnum == Domain.Share.UserTypeEnum.MIM && t.TrialId == inQuery.TrialId);
|
var isHaveMIM = await _trialUserRoleRepository.AnyAsync(t => t.UserRole.UserTypeEnum == Domain.Share.UserTypeEnum.MIM && t.TrialId == inQuery.TrialId);
|
||||||
|
|
||||||
return (await taskTaskMedicalReviewRuleQueryable.ToListAsync(), new { IsHaveMIM = isHaveMIM });
|
return (await taskTaskMedicalReviewRuleQueryable.ToListAsync(), new { IsHaveMIM = isHaveMIM });
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ namespace IRaCIS.Core.Application.Service
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ApiExplorerSettings(GroupName = "Trial")]
|
[ApiExplorerSettings(GroupName = "Trial")]
|
||||||
public class TaskMedicalReviewService(IRepository<TaskMedicalReview> _taskMedicalReviewRepository,
|
public class TaskMedicalReviewService(IRepository<TaskMedicalReview> _taskMedicalReviewRepository,
|
||||||
IRepository<TrialUser> _trialUserRepository,
|
IRepository<TrialUserRole> _trialUserRoleRepository,
|
||||||
IRepository<ReadingQuestionCriterionTrial> _readingQuestionCriterionTrialRepository,
|
IRepository<ReadingQuestionCriterionTrial> _readingQuestionCriterionTrialRepository,
|
||||||
IRepository<VisitTask> _visitTaskRepository, IMapper _mapper, IUserInfo _userInfo, IStringLocalizer _localizer) : BaseService, ITaskMedicalReviewService
|
IRepository<VisitTask> _visitTaskRepository, IMapper _mapper, IUserInfo _userInfo, IStringLocalizer _localizer) : BaseService, ITaskMedicalReviewService
|
||||||
{
|
{
|
||||||
|
@ -196,7 +196,7 @@ namespace IRaCIS.Core.Application.Service
|
||||||
public async Task<IResponseOutput<PageOutput<TaskMedicalReviewView>>> GetMIMMedicalReviewTaskList(TaskMedicalReviewQuery inQuery)
|
public async Task<IResponseOutput<PageOutput<TaskMedicalReviewView>>> GetMIMMedicalReviewTaskList(TaskMedicalReviewQuery inQuery)
|
||||||
{
|
{
|
||||||
|
|
||||||
var taskMedicalReviewQueryable = _taskMedicalReviewRepository.Where(t => t.VisitTask.TrialId == inQuery.TrialId && t.MedicalManagerUserId == _userInfo.Id && t.VisitTask.TrialReadingCriterionId == inQuery.TrialReadingCriterionId)
|
var taskMedicalReviewQueryable = _taskMedicalReviewRepository.Where(t => t.VisitTask.TrialId == inQuery.TrialId && t.MedicalManagerUserId == _userInfo.UserRoleId && t.VisitTask.TrialReadingCriterionId == inQuery.TrialReadingCriterionId)
|
||||||
|
|
||||||
.WhereIf(inQuery.IsGetNextMedicalReviewTask,
|
.WhereIf(inQuery.IsGetNextMedicalReviewTask,
|
||||||
x => (
|
x => (
|
||||||
|
@ -328,7 +328,7 @@ namespace IRaCIS.Core.Application.Service
|
||||||
[HttpGet("{trialId:guid}")]
|
[HttpGet("{trialId:guid}")]
|
||||||
public async Task<List<UserSimpleInfo>> GetMIMUserList(Guid trialId)
|
public async Task<List<UserSimpleInfo>> GetMIMUserList(Guid trialId)
|
||||||
{
|
{
|
||||||
var query = _trialUserRepository.Where(t => (t.User.UserTypeEnum == Domain.Share.UserTypeEnum.MIM || t.User.UserTypeEnum == Domain.Share.UserTypeEnum.MC) && t.TrialId == trialId).Select(t => t.User).ProjectTo<UserSimpleInfo>(_mapper.ConfigurationProvider);
|
var query = _trialUserRoleRepository.Where(t => (t.UserRole.UserTypeEnum == Domain.Share.UserTypeEnum.MIM || t.UserRole.UserTypeEnum == Domain.Share.UserTypeEnum.MC) && t.TrialId == trialId).Select(t => t.UserRole).ProjectTo<UserSimpleInfo>(_mapper.ConfigurationProvider);
|
||||||
|
|
||||||
return await query.ToListAsync();
|
return await query.ToListAsync();
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,7 +35,7 @@ namespace IRaCIS.Core.Application.Service
|
||||||
IRepository<ReadingQuestionCriterionTrial> _trialReadingCriterionRepository,
|
IRepository<ReadingQuestionCriterionTrial> _trialReadingCriterionRepository,
|
||||||
IRepository<ClinicalDataTrialSet> _trialClinicalDataSetRepository,
|
IRepository<ClinicalDataTrialSet> _trialClinicalDataSetRepository,
|
||||||
IRepository<ReadingClinicalData> _readingClinicalDataRepository,
|
IRepository<ReadingClinicalData> _readingClinicalDataRepository,
|
||||||
IRepository<ReadingConsistentClinicalData> _readingConsistentClinicalDataRepository,
|
IRepository<ReadingConsistentClinicalData> _readingConsistentClinicalDataRepository,
|
||||||
IMapper _mapper, IUserInfo _userInfo, IStringLocalizer _localizer, IWebHostEnvironment _hostEnvironment, IFusionCache _fusionCache) : BaseService, IVisitTaskHelpeService
|
IMapper _mapper, IUserInfo _userInfo, IStringLocalizer _localizer, IWebHostEnvironment _hostEnvironment, IFusionCache _fusionCache) : BaseService, IVisitTaskHelpeService
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -47,16 +47,16 @@ namespace IRaCIS.Core.Application.Service
|
||||||
/// <param name="inDto"></param>
|
/// <param name="inDto"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
|
|
||||||
public async Task<FileResult> ExportTemplateAsync(ExportTemplateAsyncDto inDto)
|
public async Task<FileResult> ExportTemplateAsync(ExportTemplateAsyncDto inDto)
|
||||||
{
|
{
|
||||||
return await ExcelExportHelper.ExportTemplateAsync(new ExportTemplateServiceDto()
|
return await ExcelExportHelper.ExportTemplateAsync(new ExportTemplateServiceDto()
|
||||||
{
|
{
|
||||||
Data=inDto.Data,
|
Data = inDto.Data,
|
||||||
commonDocumentRepository= _commonDocumentRepository,
|
commonDocumentRepository = _commonDocumentRepository,
|
||||||
TemplateCode=inDto.TemplateCode,
|
TemplateCode = inDto.TemplateCode,
|
||||||
ExportFileName=inDto.ExportFileName,
|
ExportFileName = inDto.ExportFileName,
|
||||||
hostEnvironment=_hostEnvironment,
|
hostEnvironment = _hostEnvironment,
|
||||||
IsEnglish=_userInfo.IsEn_Us,
|
IsEnglish = _userInfo.IsEn_Us,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -757,248 +757,30 @@ namespace IRaCIS.Core.Application.Service
|
||||||
|
|
||||||
if (trialReadingCriterionConfig.TaskAllocateObjEnum == TaskAllocateObj.Subject)
|
if (trialReadingCriterionConfig.TaskAllocateObjEnum == TaskAllocateObj.Subject)
|
||||||
{
|
{
|
||||||
var allocateSubjectArmList = _visitTaskRepository.Where(t => t.TrialReadingCriterionId == trialReadingCriterionConfig.TrialReadingCriterionId && t.SubjectId == subjectVisit.SubjectId && t.TrialId == trialId && t.DoctorUserId != null && t.ArmEnum != Arm.JudgeArm)
|
|
||||||
.Select(t => new { t.DoctorUserId, t.ArmEnum }).Distinct().ToList();
|
|
||||||
|
|
||||||
//当前任务没有分配医生,初次分配 不处理 只生成任务,后续根据生成的任务 再进行分配
|
|
||||||
if (allocateSubjectArmList.Count == 0)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
if (trialReadingCriterionConfig.IsReadingTaskViewInOrder == ReadingOrder.InOrder)
|
||||||
else
|
|
||||||
{
|
{
|
||||||
//并且配置了医生
|
#region 当前访视处理
|
||||||
if (assignConfigList.Count > 0 && trialReadingCriterionConfig.IsFollowVisitAutoAssign)
|
|
||||||
|
//配置了医生
|
||||||
|
if (assignConfigList.Count > 0)
|
||||||
{
|
{
|
||||||
|
//之前有回退到影像上传的访视 那么当前访视一致性核查通过的时候,当前访视生成但是不分配出去(排除失访的)
|
||||||
|
|
||||||
#region 后续访视 未分配的进行再次分配,重置的或者失效的 需要重新生成新的任务 (PM 有序退回 或者PM 有序 申请重阅)
|
var beforeBackVisitTask = await _visitTaskRepository.Where(t => t.TrialId == trialId && t.TrialReadingCriterionId == trialReadingCriterionConfig.TrialReadingCriterionId && t.SubjectId == subjectVisit.SubjectId && t.VisitTaskNum < subjectVisit.VisitNum && t.ReadingCategory == ReadingCategory.Visit && t.SourceSubjectVisit.CheckState != CheckStateEnum.CVPassed && t.SourceSubjectVisit.IsLostVisit == false).OrderBy(t => t.VisitTaskNum).FirstOrDefaultAsync();
|
||||||
|
|
||||||
|
//之前有回退的,那么当前访视任务生成但是不分配
|
||||||
if (trialReadingCriterionConfig.IsReadingTaskViewInOrder == ReadingOrder.InOrder)
|
if (beforeBackVisitTask != null)
|
||||||
{
|
{
|
||||||
//之前有回退到影像上传的访视 那么当前访视一致性核查通过的时候,当前访视生成但是不分配出去(排除失访的)
|
//不用进行额外处理
|
||||||
|
|
||||||
var beforeBackVisitTask = await _visitTaskRepository.Where(t => t.TrialId == trialId && t.TrialReadingCriterionId == trialReadingCriterionConfig.TrialReadingCriterionId && t.SubjectId == subjectVisit.SubjectId && t.VisitTaskNum < subjectVisit.VisitNum && t.ReadingCategory == ReadingCategory.Visit && t.SourceSubjectVisit.CheckState != CheckStateEnum.CVPassed && t.SourceSubjectVisit.IsLostVisit == false).OrderBy(t => t.VisitTaskNum).FirstOrDefaultAsync();
|
|
||||||
|
|
||||||
//之前有回退的,那么当前访视任务生成但是不分配
|
|
||||||
if (beforeBackVisitTask != null)
|
|
||||||
{
|
|
||||||
//不用进行额外处理
|
|
||||||
|
|
||||||
//访视2 PM 回退 基线回退 访视2先一致性核查通过,生成访视2任务,但是不分配
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
#region 当前访视根据配置规则分配出去
|
|
||||||
|
|
||||||
var defaultState = trialReadingCriterionConfig.FollowVisitAutoAssignDefaultState == TaskAllocateDefaultState.InitAllocated ? TaskAllocationState.InitAllocated : TaskAllocationState.Allocated;
|
|
||||||
|
|
||||||
if (assignConfigList.Any(t => t.ArmEnum == Arm.DoubleReadingArm1) && task1 != null)
|
|
||||||
{
|
|
||||||
task1.TaskAllocationState = defaultState;
|
|
||||||
//分配给对应Arm的人
|
|
||||||
task1.DoctorUserId = assignConfigList.FirstOrDefault(t => t.ArmEnum == Arm.DoubleReadingArm1)!.DoctorUserId;
|
|
||||||
task1.AllocateTime = DateTime.Now;
|
|
||||||
|
|
||||||
task1.SuggesteFinishedTime = GetSuggessFinishTime(true, UrgentType.NotUrget);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (assignConfigList.Any(t => t.ArmEnum == Arm.DoubleReadingArm2) && task2 != null)
|
|
||||||
{
|
|
||||||
task2.TaskAllocationState = defaultState;
|
|
||||||
task2.DoctorUserId = assignConfigList.FirstOrDefault(t => t.ArmEnum == Arm.DoubleReadingArm2)!.DoctorUserId;
|
|
||||||
task2.AllocateTime = DateTime.Now;
|
|
||||||
task2.SuggesteFinishedTime = GetSuggessFinishTime(true, UrgentType.NotUrget);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
|
|
||||||
//后续最近的未一致性核查通过的访视任务
|
|
||||||
var followBackVisitTask = await _visitTaskRepository.Where(t => t.TrialId == trialId && t.TrialReadingCriterionId == trialReadingCriterionConfig.TrialReadingCriterionId && t.SubjectId == subjectVisit.SubjectId && t.VisitTaskNum > subjectVisit.VisitNum && t.ReadingCategory == ReadingCategory.Visit && t.SourceSubjectVisit.CheckState != CheckStateEnum.CVPassed && t.SourceSubjectVisit.IsLostVisit == false).OrderBy(t => t.VisitTaskNum).FirstOrDefaultAsync();
|
|
||||||
|
|
||||||
//大于当前访视 同时小于最近的未一致性核查通过的访视任务分配 或者生成
|
|
||||||
|
|
||||||
//存在退回访视1 又退回基线 这种情况 生成任务 考虑基线先一致性核查通过,但是访视1还未通过时 生成任务
|
|
||||||
var followVisitTaskList = await _visitTaskRepository
|
|
||||||
.Where(t => t.TrialId == trialId && t.TrialReadingCriterionId == trialReadingCriterionConfig.TrialReadingCriterionId && t.SubjectId == subjectVisit.SubjectId && t.VisitTaskNum > subjectVisit.VisitNum && t.SourceSubjectVisit.CheckState == CheckStateEnum.CVPassed && t.ReadingCategory == ReadingCategory.Visit && t.IsAnalysisCreate == false, true)
|
|
||||||
.WhereIf(followBackVisitTask != null, t => t.VisitTaskNum < followBackVisitTask!.VisitTaskNum)
|
|
||||||
.ToListAsync();
|
|
||||||
|
|
||||||
var followVisitGroup = followVisitTaskList.GroupBy(t => t.VisitTaskNum);
|
|
||||||
|
|
||||||
//每个访视去判断 是分配还是生成(因为影响哪里有些是取消分配,有些是重阅重置需要重新生成)
|
|
||||||
foreach (var visitGroup in followVisitGroup)
|
|
||||||
{
|
|
||||||
|
|
||||||
var visit = await _subjectVisitRepository.Where(x => x.Id == visitGroup.First().SourceSubjectVisitId).Select(x => new
|
|
||||||
{
|
|
||||||
x.PDState,
|
|
||||||
x.IsEnrollmentConfirm,
|
|
||||||
x.IsUrgent,
|
|
||||||
}).FirstNotNullAsync();
|
|
||||||
|
|
||||||
|
|
||||||
TaskUrgentType? urgentType = null;
|
|
||||||
|
|
||||||
if (subjectVisitInfo.PDState == PDStateEnum.PDProgress)
|
|
||||||
{
|
|
||||||
urgentType = TaskUrgentType.PDProgress;
|
|
||||||
}
|
|
||||||
else if (subjectVisitInfo.IsEnrollmentConfirm)
|
|
||||||
{
|
|
||||||
urgentType = TaskUrgentType.EnrollmentConfirm;
|
|
||||||
}
|
|
||||||
else if (subjectVisitInfo.IsUrgent)
|
|
||||||
{
|
|
||||||
urgentType = TaskUrgentType.VisitUrgent;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isCanEdit = urgentType == TaskUrgentType.EnrollmentConfirm || urgentType == TaskUrgentType.PDProgress ? false : true;
|
|
||||||
|
|
||||||
//如果后续访视已分配有效 就不用处理
|
|
||||||
if (visitGroup.Any(t => t.TaskState == TaskState.Effect && t.TaskAllocationState == TaskAllocationState.Allocated && t.ArmEnum == Arm.DoubleReadingArm1))
|
|
||||||
{
|
|
||||||
//不做处理
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var arm1 = visitGroup.FirstOrDefault(t => t.TaskState == TaskState.Effect && t.TaskAllocationState == TaskAllocationState.NotAllocate && t.DoctorUserId == null && t.ArmEnum == Arm.DoubleReadingArm1);
|
|
||||||
|
|
||||||
if (arm1 != null)
|
|
||||||
{
|
|
||||||
|
|
||||||
//有可能仅仅只分配了一个Subject 未分配 那么
|
|
||||||
if (assignConfigList.Any(t => t.ArmEnum == Arm.DoubleReadingArm1) && task1 != null)
|
|
||||||
{
|
|
||||||
arm1.IsUrgent = visit.IsUrgent;
|
|
||||||
arm1.TaskUrgentType = urgentType;
|
|
||||||
arm1.IsCanEditUrgentState = isCanEdit;
|
|
||||||
arm1.TaskAllocationState = TaskAllocationState.Allocated;
|
|
||||||
arm1.AllocateTime = DateTime.Now;
|
|
||||||
arm1.DoctorUserId = task1.DoctorUserId;
|
|
||||||
arm1.SuggesteFinishedTime = GetSuggessFinishTime(true, UrgentType.NotUrget);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var latestTask = visitGroup.Where(t => t.ArmEnum == Arm.DoubleReadingArm1).OrderByDescending(t => t.CreateTime).First();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var taskOne = await _visitTaskRepository.AddAsync(new VisitTask()
|
|
||||||
{
|
|
||||||
TrialId = trialId,
|
|
||||||
SubjectId = subjectVisit.SubjectId,
|
|
||||||
IsUrgent = visit.IsUrgent,
|
|
||||||
TaskUrgentType = urgentType,
|
|
||||||
IsCanEditUrgentState = isCanEdit,
|
|
||||||
ArmEnum = Arm.DoubleReadingArm1,//特殊
|
|
||||||
Code = currentMaxCodeInt + 1,
|
|
||||||
TaskCode = AppSettings.GetCodeStr(currentMaxCodeInt + 1, nameof(VisitTask)),
|
|
||||||
ReadingCategory = ReadingCategory.Visit,
|
|
||||||
|
|
||||||
SourceSubjectVisitId = latestTask.SourceSubjectVisitId,
|
|
||||||
VisitTaskNum = latestTask.VisitTaskNum,
|
|
||||||
TaskBlindName = visitBlindConfig.BlindFollowUpPrefix + " " + visitNumList.IndexOf(latestTask.VisitTaskNum),
|
|
||||||
TaskName = latestTask.TaskName,
|
|
||||||
|
|
||||||
BlindSubjectCode = latestTask.BlindSubjectCode,
|
|
||||||
BlindTrialSiteCode = latestTask.BlindTrialSiteCode,
|
|
||||||
IsAnalysisCreate = latestTask.IsAnalysisCreate,
|
|
||||||
IsSelfAnalysis = latestTask.IsSelfAnalysis,
|
|
||||||
TaskAllocationState = TaskAllocationState.Allocated,
|
|
||||||
AllocateTime = DateTime.Now,
|
|
||||||
DoctorUserId = task1!.DoctorUserId,
|
|
||||||
SuggesteFinishedTime = GetSuggessFinishTime(true, UrgentType.NotUrget),
|
|
||||||
TrialReadingCriterionId = latestTask.TrialReadingCriterionId,
|
|
||||||
IsNeedClinicalDataSign = latestTask.IsNeedClinicalDataSign,
|
|
||||||
IsClinicalDataSign = latestTask.IsClinicalDataSign
|
|
||||||
});
|
|
||||||
|
|
||||||
currentMaxCodeInt = currentMaxCodeInt + 1;
|
|
||||||
|
|
||||||
_fusionCache.Set<int>(CacheKeys.TrialStudyMaxCode(trialId), currentMaxCodeInt, TimeSpan.FromMinutes(30));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//如果后续访视已分配有效 就不用处理
|
|
||||||
if (visitGroup.Any(t => t.TaskState == TaskState.Effect && t.TaskAllocationState == TaskAllocationState.Allocated && t.ArmEnum == Arm.DoubleReadingArm2))
|
|
||||||
{
|
|
||||||
//不做处理
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var arm2 = visitGroup.FirstOrDefault(t => t.TaskState == TaskState.Effect && t.TaskAllocationState == TaskAllocationState.NotAllocate && t.DoctorUserId == null && t.ArmEnum == Arm.DoubleReadingArm2);
|
|
||||||
if (arm2 != null)
|
|
||||||
{
|
|
||||||
//有可能仅仅只分配了一个Subject
|
|
||||||
if (assignConfigList.Any(t => t.ArmEnum == Arm.DoubleReadingArm2) && task2 != null)
|
|
||||||
{
|
|
||||||
arm2.IsUrgent = visit.IsUrgent;
|
|
||||||
arm2.TaskUrgentType = urgentType;
|
|
||||||
arm2.IsCanEditUrgentState = isCanEdit;
|
|
||||||
arm2.TaskAllocationState = TaskAllocationState.Allocated;
|
|
||||||
arm2.AllocateTime = DateTime.Now;
|
|
||||||
arm2.DoctorUserId = task2.DoctorUserId;
|
|
||||||
arm2.SuggesteFinishedTime = GetSuggessFinishTime(true, UrgentType.NotUrget);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var latestTask = visitGroup.Where(t => t.ArmEnum == Arm.DoubleReadingArm2).OrderByDescending(t => t.CreateTime).First();
|
|
||||||
|
|
||||||
var taskTwo = await _visitTaskRepository.AddAsync(new VisitTask()
|
|
||||||
{
|
|
||||||
TrialId = trialId,
|
|
||||||
SubjectId = subjectVisit.SubjectId,
|
|
||||||
IsUrgent = visit.IsUrgent,
|
|
||||||
TaskUrgentType = urgentType,
|
|
||||||
IsCanEditUrgentState = isCanEdit,
|
|
||||||
|
|
||||||
//CheckPassedTime = subjectVisit.CheckPassedTime,
|
|
||||||
ArmEnum = Arm.DoubleReadingArm2,//特殊
|
|
||||||
Code = currentMaxCodeInt + 1,
|
|
||||||
TaskCode = AppSettings.GetCodeStr(currentMaxCodeInt + 1, nameof(VisitTask)),
|
|
||||||
ReadingCategory = ReadingCategory.Visit,
|
|
||||||
|
|
||||||
SourceSubjectVisitId = latestTask.SourceSubjectVisitId,
|
|
||||||
VisitTaskNum = latestTask.VisitTaskNum,
|
|
||||||
TaskBlindName = visitBlindConfig.BlindFollowUpPrefix + " " + visitNumList.IndexOf(latestTask.VisitTaskNum),
|
|
||||||
TaskName = latestTask.TaskName,
|
|
||||||
|
|
||||||
BlindSubjectCode = latestTask.BlindSubjectCode,
|
|
||||||
BlindTrialSiteCode = latestTask.BlindTrialSiteCode,
|
|
||||||
IsAnalysisCreate = latestTask.IsAnalysisCreate,
|
|
||||||
IsSelfAnalysis = latestTask.IsSelfAnalysis,
|
|
||||||
TaskAllocationState = TaskAllocationState.Allocated,
|
|
||||||
|
|
||||||
AllocateTime = DateTime.Now,
|
|
||||||
DoctorUserId = task2!.DoctorUserId,
|
|
||||||
SuggesteFinishedTime = GetSuggessFinishTime(true, UrgentType.NotUrget),
|
|
||||||
|
|
||||||
TrialReadingCriterionId = latestTask.TrialReadingCriterionId,
|
|
||||||
IsNeedClinicalDataSign = latestTask.IsNeedClinicalDataSign,
|
|
||||||
IsClinicalDataSign = latestTask.IsClinicalDataSign
|
|
||||||
});
|
|
||||||
|
|
||||||
currentMaxCodeInt = currentMaxCodeInt + 1;
|
|
||||||
|
|
||||||
_fusionCache.Set<int>(CacheKeys.TrialStudyMaxCode(trialId), currentMaxCodeInt, TimeSpan.FromMinutes(30));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
//访视2 PM 回退 基线回退 访视2先一致性核查通过,生成访视2任务,但是不分配
|
||||||
}
|
}
|
||||||
//无序的时候 生成任务并分配出去
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
#region 当前访视根据配置规则分配出去
|
||||||
|
|
||||||
var defaultState = trialReadingCriterionConfig.FollowVisitAutoAssignDefaultState == TaskAllocateDefaultState.InitAllocated ? TaskAllocationState.InitAllocated : TaskAllocationState.Allocated;
|
var defaultState = trialReadingCriterionConfig.FollowVisitAutoAssignDefaultState == TaskAllocateDefaultState.InitAllocated ? TaskAllocationState.InitAllocated : TaskAllocationState.Allocated;
|
||||||
|
|
||||||
if (assignConfigList.Any(t => t.ArmEnum == Arm.DoubleReadingArm1) && task1 != null)
|
if (assignConfigList.Any(t => t.ArmEnum == Arm.DoubleReadingArm1) && task1 != null)
|
||||||
|
@ -1019,21 +801,237 @@ namespace IRaCIS.Core.Application.Service
|
||||||
task2.SuggesteFinishedTime = GetSuggessFinishTime(true, UrgentType.NotUrget);
|
task2.SuggesteFinishedTime = GetSuggessFinishTime(true, UrgentType.NotUrget);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
#endregion
|
||||||
//后续访视不自动分配,或者配置的医生数量不足,就不进行分配
|
|
||||||
|
#region 后续访视处理
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//后续最近的未一致性核查通过的访视任务
|
||||||
|
var followBackVisitTask = await _visitTaskRepository.Where(t => t.TrialId == trialId && t.TrialReadingCriterionId == trialReadingCriterionConfig.TrialReadingCriterionId && t.SubjectId == subjectVisit.SubjectId && t.VisitTaskNum > subjectVisit.VisitNum && t.ReadingCategory == ReadingCategory.Visit && t.SourceSubjectVisit.CheckState != CheckStateEnum.CVPassed && t.SourceSubjectVisit.IsLostVisit == false).OrderBy(t => t.VisitTaskNum).FirstOrDefaultAsync();
|
||||||
|
|
||||||
|
//大于当前访视 同时小于最近的未一致性核查通过的访视任务分配 或者生成
|
||||||
|
|
||||||
|
//存在退回访视1 又退回基线 这种情况 生成任务 考虑基线先一致性核查通过,但是访视1还未通过时 生成任务
|
||||||
|
var followVisitTaskList = await _visitTaskRepository
|
||||||
|
.Where(t => t.TrialId == trialId && t.TrialReadingCriterionId == trialReadingCriterionConfig.TrialReadingCriterionId && t.SubjectId == subjectVisit.SubjectId && t.VisitTaskNum > subjectVisit.VisitNum && t.SourceSubjectVisit.CheckState == CheckStateEnum.CVPassed && t.ReadingCategory == ReadingCategory.Visit && t.IsAnalysisCreate == false, true)
|
||||||
|
.WhereIf(followBackVisitTask != null, t => t.VisitTaskNum < followBackVisitTask!.VisitTaskNum)
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
var followVisitGroup = followVisitTaskList.GroupBy(t => t.VisitTaskNum);
|
||||||
|
|
||||||
|
//每个访视去判断 是分配还是生成(因为影响哪里有些是取消分配,有些是重阅重置需要重新生成)
|
||||||
|
foreach (var visitGroup in followVisitGroup)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
var visit = await _subjectVisitRepository.Where(x => x.Id == visitGroup.First().SourceSubjectVisitId).Select(x => new
|
||||||
|
{
|
||||||
|
x.PDState,
|
||||||
|
x.IsEnrollmentConfirm,
|
||||||
|
x.IsUrgent,
|
||||||
|
}).FirstNotNullAsync();
|
||||||
|
|
||||||
|
|
||||||
|
TaskUrgentType? urgentType = null;
|
||||||
|
|
||||||
|
if (subjectVisitInfo.PDState == PDStateEnum.PDProgress)
|
||||||
|
{
|
||||||
|
urgentType = TaskUrgentType.PDProgress;
|
||||||
|
}
|
||||||
|
else if (subjectVisitInfo.IsEnrollmentConfirm)
|
||||||
|
{
|
||||||
|
urgentType = TaskUrgentType.EnrollmentConfirm;
|
||||||
|
}
|
||||||
|
else if (subjectVisitInfo.IsUrgent)
|
||||||
|
{
|
||||||
|
urgentType = TaskUrgentType.VisitUrgent;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isCanEdit = urgentType == TaskUrgentType.EnrollmentConfirm || urgentType == TaskUrgentType.PDProgress ? false : true;
|
||||||
|
|
||||||
|
//如果后续访视已分配有效 就不用处理
|
||||||
|
if (visitGroup.Any(t => t.TaskState == TaskState.Effect && t.TaskAllocationState == TaskAllocationState.Allocated && t.ArmEnum == Arm.DoubleReadingArm1))
|
||||||
|
{
|
||||||
|
//不做处理
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var arm1 = visitGroup.FirstOrDefault(t => t.TaskState == TaskState.Effect && t.TaskAllocationState == TaskAllocationState.NotAllocate && t.DoctorUserId == null && t.ArmEnum == Arm.DoubleReadingArm1);
|
||||||
|
|
||||||
|
if (arm1 != null)
|
||||||
|
{
|
||||||
|
arm1.IsUrgent = visit.IsUrgent;
|
||||||
|
arm1.TaskUrgentType = urgentType;
|
||||||
|
arm1.IsCanEditUrgentState = isCanEdit;
|
||||||
|
|
||||||
|
//有可能仅仅只分配了一个Subject 未分配 那么
|
||||||
|
if (assignConfigList.Any(t => t.ArmEnum == Arm.DoubleReadingArm1) && task1 != null)
|
||||||
|
{
|
||||||
|
arm1.TaskAllocationState = TaskAllocationState.Allocated;
|
||||||
|
arm1.AllocateTime = DateTime.Now;
|
||||||
|
arm1.DoctorUserId = task1.DoctorUserId;
|
||||||
|
arm1.SuggesteFinishedTime = GetSuggessFinishTime(true, UrgentType.NotUrget);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var latestTask = visitGroup.Where(t => t.ArmEnum == Arm.DoubleReadingArm1).OrderByDescending(t => t.CreateTime).First();
|
||||||
|
|
||||||
|
var taskOne = await _visitTaskRepository.AddAsync(new VisitTask()
|
||||||
|
{
|
||||||
|
TrialId = trialId,
|
||||||
|
SubjectId = subjectVisit.SubjectId,
|
||||||
|
IsUrgent = visit.IsUrgent,
|
||||||
|
TaskUrgentType = urgentType,
|
||||||
|
IsCanEditUrgentState = isCanEdit,
|
||||||
|
ArmEnum = Arm.DoubleReadingArm1,//特殊
|
||||||
|
Code = currentMaxCodeInt + 1,
|
||||||
|
TaskCode = AppSettings.GetCodeStr(currentMaxCodeInt + 1, nameof(VisitTask)),
|
||||||
|
ReadingCategory = ReadingCategory.Visit,
|
||||||
|
|
||||||
|
SourceSubjectVisitId = latestTask.SourceSubjectVisitId,
|
||||||
|
VisitTaskNum = latestTask.VisitTaskNum,
|
||||||
|
TaskBlindName = visitBlindConfig.BlindFollowUpPrefix + " " + visitNumList.IndexOf(latestTask.VisitTaskNum),
|
||||||
|
TaskName = latestTask.TaskName,
|
||||||
|
|
||||||
|
BlindSubjectCode = latestTask.BlindSubjectCode,
|
||||||
|
BlindTrialSiteCode = latestTask.BlindTrialSiteCode,
|
||||||
|
IsAnalysisCreate = latestTask.IsAnalysisCreate,
|
||||||
|
IsSelfAnalysis = latestTask.IsSelfAnalysis,
|
||||||
|
|
||||||
|
TrialReadingCriterionId = latestTask.TrialReadingCriterionId,
|
||||||
|
IsNeedClinicalDataSign = latestTask.IsNeedClinicalDataSign,
|
||||||
|
IsClinicalDataSign = latestTask.IsClinicalDataSign
|
||||||
|
});
|
||||||
|
|
||||||
|
if (assignConfigList.Any(t => t.ArmEnum == Arm.DoubleReadingArm1) && task1 != null)
|
||||||
|
{
|
||||||
|
taskOne.TaskAllocationState = TaskAllocationState.Allocated;
|
||||||
|
taskOne.AllocateTime = DateTime.Now;
|
||||||
|
taskOne. DoctorUserId = task1!.DoctorUserId;
|
||||||
|
taskOne. SuggesteFinishedTime = GetSuggessFinishTime(true, UrgentType.NotUrget);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
currentMaxCodeInt = currentMaxCodeInt + 1;
|
||||||
|
|
||||||
|
_fusionCache.Set<int>(CacheKeys.TrialStudyMaxCode(trialId), currentMaxCodeInt, TimeSpan.FromMinutes(30));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//如果后续访视已分配有效 就不用处理
|
||||||
|
if (visitGroup.Any(t => t.TaskState == TaskState.Effect && t.TaskAllocationState == TaskAllocationState.Allocated && t.ArmEnum == Arm.DoubleReadingArm2))
|
||||||
|
{
|
||||||
|
//不做处理
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var arm2 = visitGroup.FirstOrDefault(t => t.TaskState == TaskState.Effect && t.TaskAllocationState == TaskAllocationState.NotAllocate && t.DoctorUserId == null && t.ArmEnum == Arm.DoubleReadingArm2);
|
||||||
|
if (arm2 != null)
|
||||||
|
{
|
||||||
|
arm2.IsUrgent = visit.IsUrgent;
|
||||||
|
arm2.TaskUrgentType = urgentType;
|
||||||
|
arm2.IsCanEditUrgentState = isCanEdit;
|
||||||
|
|
||||||
|
//有可能仅仅只分配了一个Subject
|
||||||
|
if (assignConfigList.Any(t => t.ArmEnum == Arm.DoubleReadingArm2) && task2 != null)
|
||||||
|
{
|
||||||
|
arm2.TaskAllocationState = TaskAllocationState.Allocated;
|
||||||
|
arm2.AllocateTime = DateTime.Now;
|
||||||
|
arm2.DoctorUserId = task2.DoctorUserId;
|
||||||
|
arm2.SuggesteFinishedTime = GetSuggessFinishTime(true, UrgentType.NotUrget);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var latestTask = visitGroup.Where(t => t.ArmEnum == Arm.DoubleReadingArm2).OrderByDescending(t => t.CreateTime).First();
|
||||||
|
|
||||||
|
var taskTwo = await _visitTaskRepository.AddAsync(new VisitTask()
|
||||||
|
{
|
||||||
|
TrialId = trialId,
|
||||||
|
SubjectId = subjectVisit.SubjectId,
|
||||||
|
IsUrgent = visit.IsUrgent,
|
||||||
|
TaskUrgentType = urgentType,
|
||||||
|
IsCanEditUrgentState = isCanEdit,
|
||||||
|
|
||||||
|
//CheckPassedTime = subjectVisit.CheckPassedTime,
|
||||||
|
ArmEnum = Arm.DoubleReadingArm2,//特殊
|
||||||
|
Code = currentMaxCodeInt + 1,
|
||||||
|
TaskCode = AppSettings.GetCodeStr(currentMaxCodeInt + 1, nameof(VisitTask)),
|
||||||
|
ReadingCategory = ReadingCategory.Visit,
|
||||||
|
|
||||||
|
SourceSubjectVisitId = latestTask.SourceSubjectVisitId,
|
||||||
|
VisitTaskNum = latestTask.VisitTaskNum,
|
||||||
|
TaskBlindName = visitBlindConfig.BlindFollowUpPrefix + " " + visitNumList.IndexOf(latestTask.VisitTaskNum),
|
||||||
|
TaskName = latestTask.TaskName,
|
||||||
|
|
||||||
|
BlindSubjectCode = latestTask.BlindSubjectCode,
|
||||||
|
BlindTrialSiteCode = latestTask.BlindTrialSiteCode,
|
||||||
|
IsAnalysisCreate = latestTask.IsAnalysisCreate,
|
||||||
|
IsSelfAnalysis = latestTask.IsSelfAnalysis,
|
||||||
|
|
||||||
|
TrialReadingCriterionId = latestTask.TrialReadingCriterionId,
|
||||||
|
IsNeedClinicalDataSign = latestTask.IsNeedClinicalDataSign,
|
||||||
|
IsClinicalDataSign = latestTask.IsClinicalDataSign
|
||||||
|
});
|
||||||
|
|
||||||
|
if (assignConfigList.Any(t => t.ArmEnum == Arm.DoubleReadingArm2) && task2 != null)
|
||||||
|
{
|
||||||
|
taskTwo.TaskAllocationState = TaskAllocationState.Allocated;
|
||||||
|
taskTwo.AllocateTime = DateTime.Now;
|
||||||
|
taskTwo.DoctorUserId = task2!.DoctorUserId;
|
||||||
|
taskTwo.SuggesteFinishedTime = GetSuggessFinishTime(true, UrgentType.NotUrget);
|
||||||
|
}
|
||||||
|
|
||||||
|
currentMaxCodeInt = currentMaxCodeInt + 1;
|
||||||
|
|
||||||
|
_fusionCache.Set<int>(CacheKeys.TrialStudyMaxCode(trialId), currentMaxCodeInt, TimeSpan.FromMinutes(30));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//无序的时候 生成任务并分配出去
|
||||||
|
if (assignConfigList.Count > 0 && trialReadingCriterionConfig.IsFollowVisitAutoAssign)
|
||||||
|
{
|
||||||
|
var defaultState = trialReadingCriterionConfig.FollowVisitAutoAssignDefaultState == TaskAllocateDefaultState.InitAllocated ? TaskAllocationState.InitAllocated : TaskAllocationState.Allocated;
|
||||||
|
|
||||||
|
if (assignConfigList.Any(t => t.ArmEnum == Arm.DoubleReadingArm1) && task1 != null)
|
||||||
|
{
|
||||||
|
task1.TaskAllocationState = defaultState;
|
||||||
|
//分配给对应Arm的人
|
||||||
|
task1.DoctorUserId = assignConfigList.FirstOrDefault(t => t.ArmEnum == Arm.DoubleReadingArm1)!.DoctorUserId;
|
||||||
|
task1.AllocateTime = DateTime.Now;
|
||||||
|
|
||||||
|
task1.SuggesteFinishedTime = GetSuggessFinishTime(true, UrgentType.NotUrget);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (assignConfigList.Any(t => t.ArmEnum == Arm.DoubleReadingArm2) && task2 != null)
|
||||||
|
{
|
||||||
|
task2.TaskAllocationState = defaultState;
|
||||||
|
task2.DoctorUserId = assignConfigList.FirstOrDefault(t => t.ArmEnum == Arm.DoubleReadingArm2)!.DoctorUserId;
|
||||||
|
task2.AllocateTime = DateTime.Now;
|
||||||
|
task2.SuggesteFinishedTime = GetSuggessFinishTime(true, UrgentType.NotUrget);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -17,9 +17,9 @@ namespace IRaCIS.Core.Application.Service
|
||||||
|
|
||||||
|
|
||||||
CreateMap<TaskAllocationRule, TaskAllocationRuleView>()
|
CreateMap<TaskAllocationRule, TaskAllocationRuleView>()
|
||||||
.ForMember(o => o.UserCode, t => t.MapFrom(u => u.DoctorUser.UserCode))
|
.ForMember(o => o.UserCode, t => t.MapFrom(u => u.DoctorUser.IdentityUser.UserCode))
|
||||||
.ForMember(o => o.UserName, t => t.MapFrom(u => u.DoctorUser.UserName))
|
.ForMember(o => o.UserName, t => t.MapFrom(u => u.DoctorUser.IdentityUser.UserName))
|
||||||
.ForMember(o => o.FullName, t => t.MapFrom(u => u.DoctorUser.FullName))
|
.ForMember(o => o.FullName, t => t.MapFrom(u => u.DoctorUser.IdentityUser.FullName))
|
||||||
.ForMember(o => o.UserTypeShortName, t => t.MapFrom(u => u.DoctorUser.UserTypeRole.UserTypeShortName));
|
.ForMember(o => o.UserTypeShortName, t => t.MapFrom(u => u.DoctorUser.UserTypeRole.UserTypeShortName));
|
||||||
|
|
||||||
List<Guid> subjectIdList = new List<Guid>();
|
List<Guid> subjectIdList = new List<Guid>();
|
||||||
|
@ -27,9 +27,9 @@ namespace IRaCIS.Core.Application.Service
|
||||||
|
|
||||||
|
|
||||||
CreateMap<TaskAllocationRule, DoctorVisitTaskStatView>()
|
CreateMap<TaskAllocationRule, DoctorVisitTaskStatView>()
|
||||||
.ForMember(o => o.UserCode, t => t.MapFrom(u => u.DoctorUser.UserCode))
|
.ForMember(o => o.UserCode, t => t.MapFrom(u => u.DoctorUser.IdentityUser.UserCode))
|
||||||
.ForMember(o => o.UserName, t => t.MapFrom(u => u.DoctorUser.UserName))
|
.ForMember(o => o.UserName, t => t.MapFrom(u => u.DoctorUser.IdentityUser.UserName))
|
||||||
.ForMember(o => o.FullName, t => t.MapFrom(u => u.DoctorUser.FullName))
|
.ForMember(o => o.FullName, t => t.MapFrom(u => u.DoctorUser.IdentityUser.FullName))
|
||||||
.ForMember(o => o.UserTypeShortName, t => t.MapFrom(u => u.DoctorUser.UserTypeRole.UserTypeShortName))
|
.ForMember(o => o.UserTypeShortName, t => t.MapFrom(u => u.DoctorUser.UserTypeRole.UserTypeShortName))
|
||||||
//.ForMember(o => o.ArmList, t => t.MapFrom(u => u.DoctorVisitTaskList.Where(c => c.TrialId == u.TrialId).Select(t => t.ArmEnum).Distinct()))
|
//.ForMember(o => o.ArmList, t => t.MapFrom(u => u.DoctorVisitTaskList.Where(c => c.TrialId == u.TrialId).Select(t => t.ArmEnum).Distinct()))
|
||||||
.ForMember(o => o.TotalTaskCount, t => t.MapFrom(u => u.Trial.VisitTaskList.Count()))
|
.ForMember(o => o.TotalTaskCount, t => t.MapFrom(u => u.Trial.VisitTaskList.Count()))
|
||||||
|
@ -127,9 +127,9 @@ namespace IRaCIS.Core.Application.Service
|
||||||
.ForMember(o => o.SubjectCode, t => t.MapFrom(u => /*u.IsAnalysisCreate == true ? u.BlindSubjectCode :*/ u.Subject.Code))
|
.ForMember(o => o.SubjectCode, t => t.MapFrom(u => /*u.IsAnalysisCreate == true ? u.BlindSubjectCode :*/ u.Subject.Code))
|
||||||
.ForMember(o => o.MedicalNo, t => t.MapFrom(u => u.Subject.MedicalNo))
|
.ForMember(o => o.MedicalNo, t => t.MapFrom(u => u.Subject.MedicalNo))
|
||||||
|
|
||||||
.ForMember(o => o.UserCode, t => t.MapFrom(u => u.DoctorUser.UserCode))
|
.ForMember(o => o.UserCode, t => t.MapFrom(u => u.DoctorUser.IdentityUser.UserCode))
|
||||||
.ForMember(o => o.UserName, t => t.MapFrom(u => u.DoctorUser.UserName))
|
.ForMember(o => o.UserName, t => t.MapFrom(u => u.DoctorUser.IdentityUser.UserName))
|
||||||
.ForMember(o => o.FullName, t => t.MapFrom(u => u.DoctorUser.FullName))
|
.ForMember(o => o.FullName, t => t.MapFrom(u => u.DoctorUser.IdentityUser.FullName))
|
||||||
.ForMember(o => o.UserTypeShortName, t => t.MapFrom(u => u.DoctorUser.UserTypeRole.UserTypeShortName))
|
.ForMember(o => o.UserTypeShortName, t => t.MapFrom(u => u.DoctorUser.UserTypeRole.UserTypeShortName))
|
||||||
//.ForMember(o => o.IsClinicalDataSigned, t => t.MapFrom(u => u.Subject.ClinicalDataList.Any(c => c.IsSign && (c.ReadingId == u.SouceReadModuleId || c.ReadingId == u.SourceSubjectVisitId))))
|
//.ForMember(o => o.IsClinicalDataSigned, t => t.MapFrom(u => u.Subject.ClinicalDataList.Any(c => c.IsSign && (c.ReadingId == u.SouceReadModuleId || c.ReadingId == u.SourceSubjectVisitId))))
|
||||||
;
|
;
|
||||||
|
@ -160,6 +160,7 @@ namespace IRaCIS.Core.Application.Service
|
||||||
|
|
||||||
|
|
||||||
CreateMap<VisitTaskReReading, ReReadingTaskView>()
|
CreateMap<VisitTaskReReading, ReReadingTaskView>()
|
||||||
|
.ForMember(o => o.ApplicantName, t => t.MapFrom(u => u.CreateUserRole.IdentityUser.FullName))
|
||||||
.ForMember(o => o.ReReadingNewTaskCode, t => t.MapFrom(u => u.NewReReadingTask.TaskCode))
|
.ForMember(o => o.ReReadingNewTaskCode, t => t.MapFrom(u => u.NewReReadingTask.TaskCode))
|
||||||
.ForMember(o => o.OriginalReReadingTask, t => t.MapFrom(u => u.OriginalReReadingTask))
|
.ForMember(o => o.OriginalReReadingTask, t => t.MapFrom(u => u.OriginalReReadingTask))
|
||||||
;
|
;
|
||||||
|
@ -176,9 +177,9 @@ namespace IRaCIS.Core.Application.Service
|
||||||
|
|
||||||
|
|
||||||
CreateMap<VisitTask, HistoryReadingDoctorUser>()
|
CreateMap<VisitTask, HistoryReadingDoctorUser>()
|
||||||
.ForMember(o => o.UserCode, t => t.MapFrom(u => u.DoctorUser.UserCode))
|
.ForMember(o => o.UserCode, t => t.MapFrom(u => u.DoctorUser.IdentityUser.UserCode))
|
||||||
.ForMember(o => o.UserName, t => t.MapFrom(u => u.DoctorUser.UserName))
|
.ForMember(o => o.UserName, t => t.MapFrom(u => u.DoctorUser.IdentityUser.UserName))
|
||||||
.ForMember(o => o.FullName, t => t.MapFrom(u => u.DoctorUser.FullName))
|
.ForMember(o => o.FullName, t => t.MapFrom(u => u.DoctorUser.IdentityUser.FullName))
|
||||||
.ForMember(o => o.JudgeTaskCode, t => t.MapFrom(u => u.TaskCode))
|
.ForMember(o => o.JudgeTaskCode, t => t.MapFrom(u => u.TaskCode))
|
||||||
.ForMember(o => o.DoctorUserId, t => t.MapFrom(u => u.DoctorUser.Id));
|
.ForMember(o => o.DoctorUserId, t => t.MapFrom(u => u.DoctorUser.Id));
|
||||||
|
|
||||||
|
@ -195,9 +196,9 @@ namespace IRaCIS.Core.Application.Service
|
||||||
|
|
||||||
CreateMap<SubjectUser, AssignDoctorView>()
|
CreateMap<SubjectUser, AssignDoctorView>()
|
||||||
//.ForMember(o => o.AssignTime, t => t.MapFrom(u => u.AssignTime))
|
//.ForMember(o => o.AssignTime, t => t.MapFrom(u => u.AssignTime))
|
||||||
.ForMember(o => o.UserCode, t => t.MapFrom(u => u.DoctorUser.UserCode))
|
.ForMember(o => o.UserCode, t => t.MapFrom(u => u.DoctorUser.IdentityUser.UserCode))
|
||||||
.ForMember(o => o.UserName, t => t.MapFrom(u => u.DoctorUser.UserName))
|
.ForMember(o => o.UserName, t => t.MapFrom(u => u.DoctorUser.IdentityUser.UserName))
|
||||||
.ForMember(o => o.FullName, t => t.MapFrom(u => u.DoctorUser.FullName))
|
.ForMember(o => o.FullName, t => t.MapFrom(u => u.DoctorUser.IdentityUser.FullName))
|
||||||
.ForMember(o => o.UserTypeShortName, t => t.MapFrom(u => u.DoctorUser.UserTypeRole.UserTypeShortName));
|
.ForMember(o => o.UserTypeShortName, t => t.MapFrom(u => u.DoctorUser.UserTypeRole.UserTypeShortName));
|
||||||
|
|
||||||
|
|
||||||
|
@ -205,7 +206,7 @@ namespace IRaCIS.Core.Application.Service
|
||||||
CreateMap<TaskAllocationRuleAddOrEdit, TaskAllocationRule>();
|
CreateMap<TaskAllocationRuleAddOrEdit, TaskAllocationRule>();
|
||||||
|
|
||||||
|
|
||||||
CreateMap<User, TrialDoctorUserSelectView>()
|
CreateMap<UserRole, TrialDoctorUserSelectView>()
|
||||||
.ForMember(o => o.DoctorUserId, t => t.MapFrom(u => u.Id))
|
.ForMember(o => o.DoctorUserId, t => t.MapFrom(u => u.Id))
|
||||||
.ForMember(o => o.FullName, t => t.MapFrom(u => u.FullName));
|
.ForMember(o => o.FullName, t => t.MapFrom(u => u.FullName));
|
||||||
|
|
||||||
|
@ -250,12 +251,12 @@ namespace IRaCIS.Core.Application.Service
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
CreateMap<User, UserSimpleInfo>()
|
CreateMap<UserRole, UserSimpleInfo>()
|
||||||
|
|
||||||
.ForMember(o => o.UserId, t => t.MapFrom(u => u.Id))
|
.ForMember(o => o.UserId, t => t.MapFrom(u => u.Id))
|
||||||
.ForMember(o => o.UserCode, t => t.MapFrom(u => u.UserCode))
|
.ForMember(o => o.UserCode, t => t.MapFrom(u => u.IdentityUser.UserCode))
|
||||||
.ForMember(o => o.UserName, t => t.MapFrom(u => u.UserName))
|
.ForMember(o => o.UserName, t => t.MapFrom(u => u.IdentityUser.UserName))
|
||||||
.ForMember(o => o.FullName, t => t.MapFrom(u => u.FullName));
|
.ForMember(o => o.FullName, t => t.MapFrom(u => u.IdentityUser.FullName));
|
||||||
//.ForMember(o => o.UserTypeShortName, t => t.MapFrom(u => u.UserTypeRole.UserTypeShortName));
|
//.ForMember(o => o.UserTypeShortName, t => t.MapFrom(u => u.UserTypeRole.UserTypeShortName));
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,7 @@ namespace IRaCIS.Core.Application.Service
|
||||||
.WhereIf(queryCommonDocument.BusinessScenarioEnum != null, t => t.BusinessScenarioEnum == queryCommonDocument.BusinessScenarioEnum)
|
.WhereIf(queryCommonDocument.BusinessScenarioEnum != null, t => t.BusinessScenarioEnum == queryCommonDocument.BusinessScenarioEnum)
|
||||||
.WhereIf(!string.IsNullOrEmpty(queryCommonDocument.Code), t => t.Code.Contains(queryCommonDocument.Code))
|
.WhereIf(!string.IsNullOrEmpty(queryCommonDocument.Code), t => t.Code.Contains(queryCommonDocument.Code))
|
||||||
.WhereIf(!string.IsNullOrEmpty(queryCommonDocument.Name), t => t.Name.Contains(queryCommonDocument.Name) || t.NameCN.Contains(queryCommonDocument.Name))
|
.WhereIf(!string.IsNullOrEmpty(queryCommonDocument.Name), t => t.Name.Contains(queryCommonDocument.Name) || t.NameCN.Contains(queryCommonDocument.Name))
|
||||||
.ProjectTo<CommonDocumentView>(_mapper.ConfigurationProvider, new { token = _userInfo.UserToken, userId = _userInfo.Id });
|
.ProjectTo<CommonDocumentView>(_mapper.ConfigurationProvider, new { token = _userInfo.UserToken, userId = _userInfo.UserRoleId });
|
||||||
|
|
||||||
return await commonDocumentQueryable.ToPagedListAsync(queryCommonDocument);
|
return await commonDocumentQueryable.ToPagedListAsync(queryCommonDocument);
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,10 @@ namespace IRaCIS.Application.Contracts
|
||||||
public string ValueCN { get; set; } = string.Empty;
|
public string ValueCN { get; set; } = string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class ChildInQuery:SortInput
|
||||||
|
{
|
||||||
|
public Guid ParentId { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
public class AddOrEditBasicDic
|
public class AddOrEditBasicDic
|
||||||
{
|
{
|
||||||
|
|
|
@ -96,9 +96,6 @@ namespace IRaCIS.Core.Application.Contracts
|
||||||
public bool IsDeleted { get; set; }
|
public bool IsDeleted { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public CriterionType? CriterionTypeEnum { get; set; }
|
|
||||||
|
|
||||||
/// <summary> 业务模块 /// </summary>
|
/// <summary> 业务模块 /// </summary>
|
||||||
public int BusinessModuleEnum { get; set; }
|
public int BusinessModuleEnum { get; set; }
|
||||||
|
|
||||||
|
@ -140,6 +137,24 @@ namespace IRaCIS.Core.Application.Contracts
|
||||||
|
|
||||||
public int? EmailDelaySeconds { get; set; }
|
public int? EmailDelaySeconds { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[Comment("邮件配置的多个标准")]
|
||||||
|
public List<CriterionType>? CriterionTypeList { get; set; }
|
||||||
|
|
||||||
|
//public CriterionType? CriterionTypeEnum { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public class BatchUpdateEmailTopicCommand
|
||||||
|
{
|
||||||
|
[NotDefault]
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
|
||||||
|
public string EmailTopic { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public string EmailTopicCN { get; set; } = string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
// 生成时间 2022-03-28 16:43:52
|
// 生成时间 2022-03-28 16:43:52
|
||||||
// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。
|
// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。
|
||||||
//--------------------------------------------------------------------
|
//--------------------------------------------------------------------
|
||||||
|
using DocumentFormat.OpenXml.Wordprocessing;
|
||||||
using IRaCIS.Core.Infra.EFCore.Common;
|
using IRaCIS.Core.Infra.EFCore.Common;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
@ -63,6 +64,26 @@ namespace IRaCIS.Core.Application.ViewModel
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public class ModuleTypeData
|
||||||
|
{
|
||||||
|
|
||||||
|
public bool IsShow { get; set; }
|
||||||
|
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
public Guid? ParentId { get; set; }
|
||||||
|
public Guid DictionaryId { get; set; }
|
||||||
|
|
||||||
|
public int ShowOrder { get; set; }
|
||||||
|
public string DictionaryValue { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GetModuleTypeListInDto
|
||||||
|
{
|
||||||
|
public Guid TrialId { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 复制其他对象到当前对象
|
/// 复制其他对象到当前对象
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -148,6 +169,11 @@ namespace IRaCIS.Core.Application.ViewModel
|
||||||
public Guid UpdateUserId { get; set; }
|
public Guid UpdateUserId { get; set; }
|
||||||
public string Code { get; set; } = string.Empty;
|
public string Code { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否默认选择
|
||||||
|
/// </summary>
|
||||||
|
public bool IsDefaultChoice { get; set; } = false;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 字段的英文值
|
/// 字段的英文值
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -96,6 +96,8 @@ namespace IRaCIS.Core.Application.ViewModel
|
||||||
|
|
||||||
public int State { get; set; }
|
public int State { get; set; }
|
||||||
|
|
||||||
|
public Guid? PublishLogId { get; set; }
|
||||||
|
|
||||||
public List<BatchAddInternationalizationDto> AddList { get; set; }
|
public List<BatchAddInternationalizationDto> AddList { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,6 +112,10 @@ namespace IRaCIS.Core.Application.ViewModel
|
||||||
|
|
||||||
public string Module { get; set; } = string.Empty;
|
public string Module { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public int? State { get; set; }
|
||||||
|
|
||||||
|
public int InternationalizationType { get; set; }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class BatchAddInternationalizationDto : BatchInternationalizationDto
|
public class BatchAddInternationalizationDto : BatchInternationalizationDto
|
||||||
|
@ -119,6 +125,9 @@ namespace IRaCIS.Core.Application.ViewModel
|
||||||
|
|
||||||
public class InternationalizationSimpleDto : BatchInternationalizationDto
|
public class InternationalizationSimpleDto : BatchInternationalizationDto
|
||||||
{
|
{
|
||||||
|
public string Version { get; set; }
|
||||||
|
|
||||||
|
public DateTime CreateTime { get; set; }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -182,13 +182,12 @@ namespace IRaCIS.Core.Application.Service
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取子项数组
|
/// 获取子项数组
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="parentId"></param>
|
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpGet("{parentId:guid}")]
|
[HttpPost]
|
||||||
public async Task<List<BasicDicView>> GetChildList(Guid parentId)
|
public async Task<List<BasicDicView>> GetChildList(ChildInQuery inQuery)
|
||||||
{
|
{
|
||||||
return await _dicRepository.Where(t => t.ParentId == parentId)
|
return await _dicRepository.Where(t => t.ParentId == inQuery.ParentId)
|
||||||
.OrderBy(t => t.ShowOrder).ProjectTo<BasicDicView>(_mapper.ConfigurationProvider).ToListAsync();
|
.ProjectTo<BasicDicView>(_mapper.ConfigurationProvider).SortToListAsync(inQuery);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -4,8 +4,10 @@
|
||||||
// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。
|
// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。
|
||||||
//--------------------------------------------------------------------
|
//--------------------------------------------------------------------
|
||||||
|
|
||||||
|
using IRaCIS.Core.Application.Helper;
|
||||||
using IRaCIS.Core.Domain.Share;
|
using IRaCIS.Core.Domain.Share;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using NPOI.SS.Formula.Functions;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
namespace IRaCIS.Core.Application.Contracts
|
namespace IRaCIS.Core.Application.Contracts
|
||||||
|
@ -26,7 +28,7 @@ namespace IRaCIS.Core.Application.Contracts
|
||||||
.WhereIf(inQuery.SystemLevel != null, t => t.SystemLevel == inQuery.SystemLevel)
|
.WhereIf(inQuery.SystemLevel != null, t => t.SystemLevel == inQuery.SystemLevel)
|
||||||
.WhereIf(inQuery.IsDistinguishCriteria != null, t => t.IsDistinguishCriteria == inQuery.IsDistinguishCriteria)
|
.WhereIf(inQuery.IsDistinguishCriteria != null, t => t.IsDistinguishCriteria == inQuery.IsDistinguishCriteria)
|
||||||
.WhereIf(inQuery.BusinessLevelEnum != null, t => t.BusinessLevelEnum == inQuery.BusinessLevelEnum)
|
.WhereIf(inQuery.BusinessLevelEnum != null, t => t.BusinessLevelEnum == inQuery.BusinessLevelEnum)
|
||||||
.WhereIf(inQuery.CriterionTypeEnum != null, t => t.CriterionTypeEnum == inQuery.CriterionTypeEnum)
|
.WhereIf(inQuery.CriterionTypeEnum != null, t => t.CriterionTypeList.Any(t=>t==inQuery.CriterionTypeEnum))
|
||||||
.WhereIf(inQuery.BusinessModuleEnum != null, t => t.BusinessModuleEnum == inQuery.BusinessModuleEnum)
|
.WhereIf(inQuery.BusinessModuleEnum != null, t => t.BusinessModuleEnum == inQuery.BusinessModuleEnum)
|
||||||
.WhereIf(inQuery.BusinessScenarioEnum != null, t => t.BusinessScenarioEnum == inQuery.BusinessScenarioEnum)
|
.WhereIf(inQuery.BusinessScenarioEnum != null, t => t.BusinessScenarioEnum == inQuery.BusinessScenarioEnum)
|
||||||
.WhereIf(inQuery.IsReturnRequired != null, t => t.IsReturnRequired == inQuery.IsReturnRequired)
|
.WhereIf(inQuery.IsReturnRequired != null, t => t.IsReturnRequired == inQuery.IsReturnRequired)
|
||||||
|
@ -39,14 +41,83 @@ namespace IRaCIS.Core.Application.Contracts
|
||||||
return await emailNoticeConfigQueryable.ToPagedListAsync(inQuery);
|
return await emailNoticeConfigQueryable.ToPagedListAsync(inQuery);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 批量更新邮件主题中英文
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="inCommandList"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<IResponseOutput> BatchUpdateEmail(List<BatchUpdateEmailTopicCommand> inCommandList)
|
||||||
|
{
|
||||||
|
var findIdList = inCommandList.Select(x => x.Id).ToList();
|
||||||
|
|
||||||
|
var regex = new Regex(@"\{\s*\d+\s*\}");
|
||||||
|
|
||||||
|
foreach (var inCommand in inCommandList)
|
||||||
|
{
|
||||||
|
if (regex.Matches($"{inCommand.EmailTopic}{inCommand.EmailTopicCN}")
|
||||||
|
.Any(t => t.Value.Contains(" ")))
|
||||||
|
{
|
||||||
|
//邮件模板占位符不允许有空格,请核查占位符的地方
|
||||||
|
return ResponseOutput.NotOk(I18n.T("EmailNoticeConfig_ContainEmpty"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var list = _emailNoticeConfigrepository.Where(t => findIdList.Contains(t.Id), true).ToList();
|
||||||
|
|
||||||
|
foreach (var item in list)
|
||||||
|
{
|
||||||
|
var exist = inCommandList.FirstOrDefault(t => t.Id == item.Id);
|
||||||
|
if (exist != null)
|
||||||
|
{
|
||||||
|
item.EmailTopic = exist.EmailTopic;
|
||||||
|
item.EmailTopicCN = exist.EmailTopicCN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await _emailNoticeConfigrepository.SaveChangesAsync();
|
||||||
|
|
||||||
|
|
||||||
|
return ResponseOutput.Ok();
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<IResponseOutput> AddOrUpdateEmailNoticeConfig(EmailNoticeConfigAddOrEdit addOrEditEmailNoticeConfig)
|
public async Task<IResponseOutput> AddOrUpdateEmailNoticeConfig(EmailNoticeConfigAddOrEdit addOrEditEmailNoticeConfig)
|
||||||
{
|
{
|
||||||
var verifyExp1 = new EntityVerifyExp<EmailNoticeConfig>()
|
//var verifyExp1 = new EntityVerifyExp<EmailNoticeConfig>()
|
||||||
{
|
//{
|
||||||
VerifyExp = t => t.BusinessScenarioEnum == addOrEditEmailNoticeConfig.BusinessScenarioEnum && t.CriterionTypeEnum== addOrEditEmailNoticeConfig.CriterionTypeEnum,
|
// VerifyExp = t => t.BusinessScenarioEnum == addOrEditEmailNoticeConfig.BusinessScenarioEnum && t.CriterionTypeEnum == addOrEditEmailNoticeConfig.CriterionTypeEnum,
|
||||||
|
|
||||||
VerifyMsg = _localizer["EmailNoticeConfig_RepeatEmailScenario"]
|
// VerifyMsg = _localizer["EmailNoticeConfig_RepeatEmailScenario"]
|
||||||
|
|
||||||
|
//};
|
||||||
|
|
||||||
|
var criterionList = _emailNoticeConfigrepository.Where(t => t.BusinessScenarioEnum == addOrEditEmailNoticeConfig.BusinessScenarioEnum && t.IsEnable == true)
|
||||||
|
.Where(t => t.CriterionTypeList != null)
|
||||||
|
.WhereIf(addOrEditEmailNoticeConfig.Id != null, t => t.Id != addOrEditEmailNoticeConfig.Id)
|
||||||
|
.Select(t => t.CriterionTypeList).ToList();//不能使用selectMany 会当成关联对象,不能当成字符串
|
||||||
|
|
||||||
|
|
||||||
|
if (addOrEditEmailNoticeConfig.CriterionTypeList != null)
|
||||||
|
{
|
||||||
|
foreach (var item in addOrEditEmailNoticeConfig.CriterionTypeList)
|
||||||
|
{
|
||||||
|
foreach (var itemList in criterionList)
|
||||||
|
{
|
||||||
|
if (itemList.Any(t => t == item))
|
||||||
|
{
|
||||||
|
return ResponseOutput.NotOk(_localizer["EmailNoticeConfig_RepeatEmailScenario"]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var verifyExp2 = new EntityVerifyExp<EmailNoticeConfig>()
|
||||||
|
{
|
||||||
|
VerifyExp = t => t.Code == addOrEditEmailNoticeConfig.Code && t.IsDistinguishCriteria == addOrEditEmailNoticeConfig.IsDistinguishCriteria && t.SystemLevel == addOrEditEmailNoticeConfig.SystemLevel,
|
||||||
|
|
||||||
|
VerifyMsg = _localizer["EmailNoticeConfig_RepeatCode"]
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -61,10 +132,14 @@ namespace IRaCIS.Core.Application.Contracts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var entity = new EmailNoticeConfig() { };
|
||||||
|
|
||||||
if (addOrEditEmailNoticeConfig.Id == null)
|
if (addOrEditEmailNoticeConfig.Id == null)
|
||||||
{
|
{
|
||||||
|
|
||||||
var entity = _mapper.Map<EmailNoticeConfig>(addOrEditEmailNoticeConfig);
|
entity = _mapper.Map<EmailNoticeConfig>(addOrEditEmailNoticeConfig);
|
||||||
|
|
||||||
|
|
||||||
foreach (var item in addOrEditEmailNoticeConfig.ToUserTypeList)
|
foreach (var item in addOrEditEmailNoticeConfig.ToUserTypeList)
|
||||||
|
@ -80,9 +155,9 @@ namespace IRaCIS.Core.Application.Contracts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
await _emailNoticeConfigrepository.AddAsync(entity, true, verifyExp1);
|
await _emailNoticeConfigrepository.AddAsync(entity, true, verifyExp2);
|
||||||
|
|
||||||
|
|
||||||
return ResponseOutput.Ok(entity.Id.ToString());
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -103,14 +178,32 @@ namespace IRaCIS.Core.Application.Contracts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
var entity = await _emailNoticeConfigrepository.UpdateFromDTOAsync(addOrEditEmailNoticeConfig, true, false, verifyExp1);
|
entity = await _emailNoticeConfigrepository.UpdateFromDTOAsync(addOrEditEmailNoticeConfig, true, false, verifyExp2);
|
||||||
|
|
||||||
|
|
||||||
return ResponseOutput.Ok(entity.Id.ToString());
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (addOrEditEmailNoticeConfig.EmailCron != string.Empty)
|
||||||
|
{
|
||||||
|
var jobId = $"{entity.Id}_({addOrEditEmailNoticeConfig.BusinessScenarioEnum})";
|
||||||
|
|
||||||
|
HangfireJobHelper.RemoveCronJob(jobId);
|
||||||
|
|
||||||
|
//有的job 可能编辑控制直接不发,需要移除已存在的
|
||||||
|
if (entity.IsAutoSend && entity.IsEnable)
|
||||||
|
{
|
||||||
|
HangfireJobHelper.AddOrUpdateTimingCronJob(jobId, addOrEditEmailNoticeConfig.BusinessScenarioEnum, addOrEditEmailNoticeConfig.EmailCron);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return ResponseOutput.Ok(entity.Id.ToString());
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -25,10 +25,10 @@ namespace IRaCIS.Core.Application.Service
|
||||||
var exploreRecommendQueryable =
|
var exploreRecommendQueryable =
|
||||||
|
|
||||||
_exploreRecommendRepository.Where().IgnoreQueryFilters()
|
_exploreRecommendRepository.Where().IgnoreQueryFilters()
|
||||||
.WhereIf(string.IsNullOrEmpty(inQuery.Title), t => t.Title.Contains(inQuery.Title))
|
.WhereIf(!string.IsNullOrEmpty(inQuery.Title), t => t.Title.Contains(inQuery.Title))
|
||||||
.WhereIf(string.IsNullOrEmpty(inQuery.FileName), t => t.Title.Contains(inQuery.FileName))
|
.WhereIf(!string.IsNullOrEmpty(inQuery.FileName), t => t.Title.Contains(inQuery.FileName))
|
||||||
.WhereIf(string.IsNullOrEmpty(inQuery.DownloadUrl), t => t.Title.Contains(inQuery.DownloadUrl))
|
.WhereIf(!string.IsNullOrEmpty(inQuery.DownloadUrl), t => t.Title.Contains(inQuery.DownloadUrl))
|
||||||
.WhereIf(string.IsNullOrEmpty(inQuery.Version), t => t.Title.Contains(inQuery.Version))
|
.WhereIf(!string.IsNullOrEmpty(inQuery.Version), t => t.Title.Contains(inQuery.Version))
|
||||||
.WhereIf(inQuery.IsDeleted != null, t => t.IsDeleted == t.IsDeleted)
|
.WhereIf(inQuery.IsDeleted != null, t => t.IsDeleted == t.IsDeleted)
|
||||||
.ProjectTo<ExploreRecommendView>(_mapper.ConfigurationProvider);
|
.ProjectTo<ExploreRecommendView>(_mapper.ConfigurationProvider);
|
||||||
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue