Compare commits
611 Commits
Test_IRC_N
...
Test_HIR_N
| Author | SHA1 | Date |
|---|---|---|
|
|
1033ff665c | |
|
|
f8478887ce | |
|
|
763984ca62 | |
|
|
efbaae2a97 | |
|
|
49e4980fd0 | |
|
|
b2bcc37dab | |
|
|
32ee818be8 | |
|
|
d042be7828 | |
|
|
2017d781bf | |
|
|
fccf6686eb | |
|
|
88f40125e1 | |
|
|
facb318180 | |
|
|
62352738e8 | |
|
|
a22b7802b7 | |
|
|
6aaeeeaeb9 | |
|
|
7b606aee05 | |
|
|
841f1892d4 | |
|
|
106916f7ad | |
|
|
46ab1fc7bc | |
|
|
3328c18279 | |
|
|
aed778bbc6 | |
|
|
064e3836ca | |
|
|
3407f2f00d | |
|
|
e496cf1117 | |
|
|
e5f6685719 | |
|
|
837a3f66d5 | |
|
|
0201c2f50f | |
|
|
a7ed38f03b | |
|
|
a436571364 | |
|
|
34e6e1583e | |
|
|
400504db74 | |
|
|
88e7febec5 | |
|
|
02a49b2239 | |
|
|
d72b28afc2 | |
|
|
2ba9e6e2d4 | |
|
|
1d38ee3f49 | |
|
|
9879625781 | |
|
|
a0f5e7b7e5 | |
|
|
337d0fa05f | |
|
|
5d3df0b1f5 | |
|
|
dd4cd5d330 | |
|
|
3c28be9bf0 | |
|
|
6e75404175 | |
|
|
5b08640a02 | |
|
|
9b3e4194a9 | |
|
|
c4eabde7a9 | |
|
|
66e5570d82 | |
|
|
d1be23a69e | |
|
|
0ef8604da0 | |
|
|
05124428ad | |
|
|
17ce41209f | |
|
|
a1afbc2487 | |
|
|
3f6e006a95 | |
|
|
380546ca3c | |
|
|
e4cf4cc8b8 | |
|
|
ed580eab74 | |
|
|
2530843931 | |
|
|
c4b588376d | |
|
|
e63d6ba652 | |
|
|
64ef4ea0f2 | |
|
|
b293559225 | |
|
|
7214626850 | |
|
|
a4ef209fb1 | |
|
|
e4cc044219 | |
|
|
db6c0457ae | |
|
|
e745271671 | |
|
|
34686b10cc | |
|
|
711c4c5888 | |
|
|
3d19ee7b04 | |
|
|
72fd347c01 | |
|
|
4c1da73c33 | |
|
|
2b01c777a7 | |
|
|
d1ae500735 | |
|
|
214ea985d9 | |
|
|
95fe063706 | |
|
|
b6ceca9744 | |
|
|
42e9c58d7a | |
|
|
8356fff9ce | |
|
|
e6f91b9c4b | |
|
|
ba9c88d836 | |
|
|
237260dbd0 | |
|
|
5a2136f205 | |
|
|
f9d69995d3 | |
|
|
32d78f556c | |
|
|
d11afb10de | |
|
|
67e7eaaa96 | |
|
|
af95166782 | |
|
|
29093e5617 | |
|
|
b68c00c1b4 | |
|
|
2d06b12368 | |
|
|
0f7b80ff63 | |
|
|
96c8b5d7f8 | |
|
|
4ec15c2333 | |
|
|
11d5722f46 | |
|
|
a079451951 | |
|
|
eb7db8478f | |
|
|
444d5c8005 | |
|
|
d97edf6688 | |
|
|
359a6bf602 | |
|
|
bab20666af | |
|
|
05244cdaf7 | |
|
|
ef1b76f98c | |
|
|
f887699341 | |
|
|
6b97d1ce68 | |
|
|
334e51d9fd | |
|
|
8b43474734 | |
|
|
149cca011a | |
|
|
aa63ca2def | |
|
|
9dcb63bdf2 | |
|
|
323534e52b | |
|
|
b432002266 | |
|
|
e03884227e | |
|
|
0580b11e39 | |
|
|
90a3021938 | |
|
|
bc3f5c9394 | |
|
|
9e3eafa925 | |
|
|
98f0e9e1a1 | |
|
|
b66b3fcc6c | |
|
|
608f6387ae | |
|
|
4f7e73c990 | |
|
|
8baa8b7421 | |
|
|
bb38899cee | |
|
|
64f04a3953 | |
|
|
38a42dc44f | |
|
|
4c396dc4ae | |
|
|
e17d973fad | |
|
|
1efe15a776 | |
|
|
d7f87f77ab | |
|
|
7eff5fd1bb | |
|
|
2fe3441c3f | |
|
|
98741d9a3b | |
|
|
9386b89110 | |
|
|
32ca60156e | |
|
|
b2022a912f | |
|
|
ffedc3f378 | |
|
|
6d042459e3 | |
|
|
0a5164682f | |
|
|
62037c0d43 | |
|
|
ec45d80b9b | |
|
|
4187673b7a | |
|
|
ad62ad7ae0 | |
|
|
87709f0122 | |
|
|
5ade2135ee | |
|
|
f0a38ebec9 | |
|
|
15b50e39f1 | |
|
|
5f71d7f1b2 | |
|
|
f4f94f1f48 | |
|
|
21cafcab53 | |
|
|
c37903b2f4 | |
|
|
1fc70b1340 | |
|
|
36844c291a | |
|
|
9b96259e59 | |
|
|
4c3a96a0f6 | |
|
|
154c12eb93 | |
|
|
c09f27bf7d | |
|
|
f80ac02562 | |
|
|
f49d2ce06b | |
|
|
eff5920568 | |
|
|
05be768188 | |
|
|
c9387463ba | |
|
|
b230139f67 | |
|
|
d833093c33 | |
|
|
c62fb1df92 | |
|
|
8c1a25aae3 | |
|
|
758e0aedcb | |
|
|
563be56d27 | |
|
|
db2bd6b725 | |
|
|
81d9fcae2e | |
|
|
ea87692f49 | |
|
|
cee9d92a72 | |
|
|
bbe89ade16 | |
|
|
16e348a996 | |
|
|
b6e3032767 | |
|
|
690c759fa1 | |
|
|
56a15f3b61 | |
|
|
589e9868a8 | |
|
|
82afae93dc | |
|
|
6875cf3188 | |
|
|
076d99c6ba | |
|
|
79d39125ff | |
|
|
aafc1cd064 | |
|
|
26e7305bcf | |
|
|
d1e08b0fc0 | |
|
|
df87c56544 | |
|
|
a3b69d45ae | |
|
|
ef95cdd4ec | |
|
|
4d7d42753f | |
|
|
9e9db251c8 | |
|
|
8b1626e292 | |
|
|
a5a2dcdac9 | |
|
|
93ea0cf8d1 | |
|
|
b91ea2c176 | |
|
|
75be9eba74 | |
|
|
335535d6d3 | |
|
|
8b61b288fc | |
|
|
f08a5b0d46 | |
|
|
7bc3378e0c | |
|
|
b87e531920 | |
|
|
d5234db48a | |
|
|
7f6933df35 | |
|
|
70bff24348 | |
|
|
a8a1198c52 | |
|
|
bd83153743 | |
|
|
deeffe034b | |
|
|
39f579c10b | |
|
|
77eac6c1d0 | |
|
|
5cc192b317 | |
|
|
ba12a2977b | |
|
|
1a7576039b | |
|
|
780f6451c3 | |
|
|
b576478eea | |
|
|
8cbc5c5c26 | |
|
|
0245aaf7ad | |
|
|
d954d13f81 | |
|
|
c9e12993a6 | |
|
|
088a0b6fd5 | |
|
|
6b18e5eced | |
|
|
5673fcad5f | |
|
|
697ac6f57b | |
|
|
e9b3b012f9 | |
|
|
d5d3d6e1c9 | |
|
|
712d87a4dd | |
|
|
f082217e99 | |
|
|
16e5a0567f | |
|
|
5fa12786aa | |
|
|
4350aa6459 | |
|
|
897dd2118d | |
|
|
f84a9c481a | |
|
|
3cb0ed4cd5 | |
|
|
e346bfc527 | |
|
|
831ae8a591 | |
|
|
b359508ab4 | |
|
|
7750b1ba12 | |
|
|
f5f4639cc8 | |
|
|
32e659c26b | |
|
|
2f69df7a83 | |
|
|
ff0662867e | |
|
|
3ee9703dca | |
|
|
217df5dc2f | |
|
|
7fe61af141 | |
|
|
a37a77949a | |
|
|
eb885cc568 | |
|
|
c78a96eed0 | |
|
|
f8b23604c2 | |
|
|
26d60857bf | |
|
|
4c9b3e1a23 | |
|
|
5526a2dd53 | |
|
|
f69ad1b4e3 | |
|
|
b869693d63 | |
|
|
6f8f6000ba | |
|
|
ed5f8e8d9c | |
|
|
550cdff28f | |
|
|
6e363601a6 | |
|
|
6a63ffd1f2 | |
|
|
03c8cdd1d2 | |
|
|
99e5b102b7 | |
|
|
b331799b3f | |
|
|
20014e66d0 | |
|
|
8be56c5a4d | |
|
|
fa3df97dab | |
|
|
dfd4cd533b | |
|
|
0d3d1a7f70 | |
|
|
c418b7e474 | |
|
|
ab79eff346 | |
|
|
c6e8ec0149 | |
|
|
e143cb0e73 | |
|
|
5d1499025b | |
|
|
87c22f1033 | |
|
|
be5fc58ef7 | |
|
|
d769d43d23 | |
|
|
9de6a3c813 | |
|
|
e38219a0ee | |
|
|
874d94cefb | |
|
|
8e3f7d4ba4 | |
|
|
1d918909f0 | |
|
|
13b4c90518 | |
|
|
2d2f399601 | |
|
|
f49d8d81b1 | |
|
|
450f6c00ce | |
|
|
07d4df81f6 | |
|
|
2b696855cc | |
|
|
64bf0fb622 | |
|
|
68fa7ce02e | |
|
|
bd8cf9ee42 | |
|
|
224382bf62 | |
|
|
a19ba3c215 | |
|
|
5975004858 | |
|
|
baffb0bb35 | |
|
|
6ed0ea76da | |
|
|
55f864c16c | |
|
|
59ecaaa5a7 | |
|
|
4945daa0d4 | |
|
|
59339969da | |
|
|
fdbdda71eb | |
|
|
b7c85d6b56 | |
|
|
e3e884393f | |
|
|
70c744498b | |
|
|
a3f8730aa8 | |
|
|
d0352590ed | |
|
|
fd04315f17 | |
|
|
6482316195 | |
|
|
525cae7232 | |
|
|
afaba95ff2 | |
|
|
5258ae5d4b | |
|
|
54e30f008c | |
|
|
9569cdab5f | |
|
|
e189daf569 | |
|
|
9b60114476 | |
|
|
97c123677e | |
|
|
a3b57e2a04 | |
|
|
f80c1ec86e | |
|
|
32d613b146 | |
|
|
649b02d5d3 | |
|
|
e622f601a4 | |
|
|
fce4fd52de | |
|
|
ed2a777fd4 | |
|
|
4de4e36e73 | |
|
|
17937783e9 | |
|
|
d1f79cd06b | |
|
|
3f075ec367 | |
|
|
77add17984 | |
|
|
06eae02b7a | |
|
|
d7b5066683 | |
|
|
046ad55b57 | |
|
|
9782c5ee04 | |
|
|
5f7c4dda97 | |
|
|
4ad3d30b1d | |
|
|
0f272ad935 | |
|
|
68ac4c80af | |
|
|
66095bccb1 | |
|
|
1fd2c01612 | |
|
|
98547ef145 | |
|
|
efa9e511c3 | |
|
|
b4e2af4c63 | |
|
|
25425eab93 | |
|
|
e7261b1c7e | |
|
|
02f3bfbfdd | |
|
|
9995a1b519 | |
|
|
10b47de079 | |
|
|
ebf0c6b086 | |
|
|
621da36150 | |
|
|
34f52d8218 | |
|
|
0c1dcd4610 | |
|
|
ebaa2af196 | |
|
|
552509ad9e | |
|
|
806f829e08 | |
|
|
75f0f5e3a5 | |
|
|
652d90af98 | |
|
|
c4d1c652a3 | |
|
|
01b61f8fb6 | |
|
|
7542c20bcc | |
|
|
3a65046c48 | |
|
|
659aadbc7f | |
|
|
8b72566b91 | |
|
|
ce25556a9f | |
|
|
1847e6f76f | |
|
|
e6bf9697a5 | |
|
|
d2eec7676d | |
|
|
7525c14e7d | |
|
|
5644a2acd7 | |
|
|
d48ffc5051 | |
|
|
48bb5ce47a | |
|
|
8693e3169c | |
|
|
f73529bfc1 | |
|
|
df6b89b3be | |
|
|
893effcefe | |
|
|
77444e35fa | |
|
|
53007ccb6a | |
|
|
b301d51b2d | |
|
|
b5d259f907 | |
|
|
721826e346 | |
|
|
c479574905 | |
|
|
291801efe2 | |
|
|
13e907f876 | |
|
|
bee3084bbf | |
|
|
9300f5756d | |
|
|
7b3cac1f63 | |
|
|
0acdcbae0e | |
|
|
2c3483fa4e | |
|
|
1b6d1dd4cb | |
|
|
a9b907b717 | |
|
|
ce5ca145ed | |
|
|
15c9a370ee | |
|
|
006afc56d5 | |
|
|
29da1ab464 | |
|
|
a7db0c2887 | |
|
|
f409c6468e | |
|
|
ade2bdf181 | |
|
|
9ecbfb37aa | |
|
|
c44c861086 | |
|
|
a2deb93b6c | |
|
|
5527f51880 | |
|
|
b8d9cf1630 | |
|
|
73f1f2938f | |
|
|
5ea6d221e3 | |
|
|
30edf87bc0 | |
|
|
93a6bee01b | |
|
|
17a7634a37 | |
|
|
4048c28628 | |
|
|
816d81b555 | |
|
|
dedb1c152f | |
|
|
083090ca05 | |
|
|
31c86c5d4a | |
|
|
bf785156f4 | |
|
|
15af716651 | |
|
|
a2bd2857b2 | |
|
|
d0351ef405 | |
|
|
552a77ecd4 | |
|
|
9f10af6654 | |
|
|
55c87d673c | |
|
|
2607a05104 | |
|
|
4f2dc949a5 | |
|
|
3695c5d596 | |
|
|
91cf1ab48c | |
|
|
2b2c5e3d77 | |
|
|
94c4a81974 | |
|
|
b5956ef6d2 | |
|
|
3861e7642a | |
|
|
1bfe23f9ec | |
|
|
9f0647e156 | |
|
|
7f6222a0bc | |
|
|
39c07ecc4f | |
|
|
28c9b72e35 | |
|
|
2778ecb74a | |
|
|
40cbd73226 | |
|
|
7b456c9008 | |
|
|
011a75714e | |
|
|
38f408d1de | |
|
|
006d1bd167 | |
|
|
83e64fd4aa | |
|
|
7c7ce6632a | |
|
|
787bc250c1 | |
|
|
3b5ac96a98 | |
|
|
3d4dc0c1f4 | |
|
|
2990420361 | |
|
|
2521147579 | |
|
|
4f860cca07 | |
|
|
bea324aa56 | |
|
|
c78cbbb737 | |
|
|
e197f6c2bb | |
|
|
c7d8e38cd3 | |
|
|
8a241754ca | |
|
|
b3e77ec422 | |
|
|
daf593f76a | |
|
|
5c4388a2e7 | |
|
|
0da73ce1db | |
|
|
1d2ef4561f | |
|
|
dbf75535c5 | |
|
|
aa34c96a39 | |
|
|
e6b6a24b3b | |
|
|
0b958f21a5 | |
|
|
e6310a4533 | |
|
|
49c3a0d494 | |
|
|
213cb69332 | |
|
|
e43dce220c | |
|
|
52dc6e05d4 | |
|
|
31e76a2e97 | |
|
|
19d9ca44fb | |
|
|
c8b0681dfa | |
|
|
eed17feae0 | |
|
|
c0478ef96e | |
|
|
4713d2a09b | |
|
|
ea72e6cf65 | |
|
|
9f5aee9258 | |
|
|
e56d1b9573 | |
|
|
6d5a7bd3a2 | |
|
|
a73ef7108b | |
|
|
507500f051 | |
|
|
913084cc8d | |
|
|
5874223866 | |
|
|
d5609560e2 | |
|
|
a5b4ab6531 | |
|
|
a91705b35d | |
|
|
65369c73bf | |
|
|
eb9bb75825 | |
|
|
69216ae75d | |
|
|
309ea7b1d1 | |
|
|
5beacacc0f | |
|
|
008a909f5b | |
|
|
bdfc65c07b | |
|
|
5ee7a70c65 | |
|
|
c9590829b2 | |
|
|
80805bde06 | |
|
|
20718bca5d | |
|
|
3d58e91644 | |
|
|
ade2a25fc4 | |
|
|
dcddf88a66 | |
|
|
89574e05bc | |
|
|
95fe14fece | |
|
|
cfaf14ed65 | |
|
|
a50f82c42a | |
|
|
1c0e1e01ce | |
|
|
5ec3fc3d13 | |
|
|
9bd080b566 | |
|
|
f7cdb5b858 | |
|
|
00c3ad8f9e | |
|
|
d4b3038f6d | |
|
|
dde513a976 | |
|
|
8378e8f2d6 | |
|
|
374f024f85 | |
|
|
32aadceaac | |
|
|
285e742136 | |
|
|
704a876cf0 | |
|
|
7184ea9565 | |
|
|
6da50126e1 | |
|
|
965c8a56c9 | |
|
|
0611c1d9fe | |
|
|
642acc9cba | |
|
|
b87fb98c6c | |
|
|
d0d249655b | |
|
|
a847bb1785 | |
|
|
445ee00840 | |
|
|
ecd33d969b | |
|
|
470420410e | |
|
|
2952b9e59f | |
|
|
7e8995a7b2 | |
|
|
e7046173f8 | |
|
|
f1a39e3b9d | |
|
|
2838f232f3 | |
|
|
9ac4b262f3 | |
|
|
582b8b3c07 | |
|
|
91b20e07f2 | |
|
|
5a99f476f9 | |
|
|
e06b47c8b3 | |
|
|
613cf6441b | |
|
|
57dd964db2 | |
|
|
42dd5d384f | |
|
|
f2138623f4 | |
|
|
c69b2569ce | |
|
|
6f8b88422b | |
|
|
4c4d19b0d6 | |
|
|
91e0f0b787 | |
|
|
c7f64ea0fe | |
|
|
fdb0b74296 | |
|
|
0d7a04f1e8 | |
|
|
0a72c15dfd | |
|
|
282c0b40c2 | |
|
|
5783f5836a | |
|
|
aff1f5b599 | |
|
|
b12079f8d9 | |
|
|
99de20ef76 | |
|
|
c187b5fb94 | |
|
|
d852de4a5b | |
|
|
d10020d944 | |
|
|
f7c17546da | |
|
|
c267bb34b4 | |
|
|
98b0b36e3b | |
|
|
468224cae9 | |
|
|
6b385b619a | |
|
|
88669ccbff | |
|
|
5f338de0cd | |
|
|
c266c68be8 | |
|
|
364b84fd1c | |
|
|
982bd80898 | |
|
|
0050ae8d0a | |
|
|
8d11115fab | |
|
|
355ca22134 | |
|
|
d568964c64 | |
|
|
81443e5e36 | |
|
|
1c4d513f5b | |
|
|
d0a3ce9a09 | |
|
|
0a368c0607 | |
|
|
1104c1481e | |
|
|
7318dd4812 | |
|
|
1ee30d047d | |
|
|
39e7218b52 | |
|
|
959fa1c40c | |
|
|
fb074048df | |
|
|
0b20c42513 | |
|
|
7c0e53db73 | |
|
|
d77a6ebda0 | |
|
|
42f7bf9c97 | |
|
|
b9d9552ae8 | |
|
|
ba81a842b7 | |
|
|
28b3f16ef0 | |
|
|
37bf4905bb | |
|
|
0ab9cebe45 | |
|
|
a8c9587572 | |
|
|
48380aa7ea | |
|
|
b1831bfba0 | |
|
|
b9f866807e | |
|
|
8896450fcc | |
|
|
2dc0cc3525 | |
|
|
a3f635894b | |
|
|
542ead68a7 | |
|
|
7c25809ad5 | |
|
|
1f14adec2a | |
|
|
8173e6a8f2 | |
|
|
0baf4700f1 | |
|
|
e3f1fe5569 | |
|
|
9aa5c27f60 | |
|
|
a1479420c8 | |
|
|
0f368f0a5e | |
|
|
5d0c6417a4 | |
|
|
12d8944ca7 | |
|
|
521a4ad3f0 | |
|
|
040777ec87 | |
|
|
b68d3a5d99 | |
|
|
0af3c5ab8b | |
|
|
6ca1e81b6a | |
|
|
068b3fa8d7 | |
|
|
976b89e970 | |
|
|
47e85ebbdf | |
|
|
44f33fb7f0 | |
|
|
2c5ba280e9 | |
|
|
5250cd524a | |
|
|
787dd9aa5f | |
|
|
e059dcb2b2 | |
|
|
75bf9b394b | |
|
|
c158d53a39 | |
|
|
8e6125796b |
|
|
@ -0,0 +1,42 @@
|
|||
using Serilog;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace IRaCIS.Core.Application.Helper
|
||||
{
|
||||
public static class SafeBussinessHelper
|
||||
{
|
||||
public static async Task<bool> RunAsync(Func<Task> func, [CallerMemberName] string caller = "", string errorMsgTitle = "")
|
||||
{
|
||||
try
|
||||
{
|
||||
await func();
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Logger.Error($"【{errorMsgTitle}失败 - {caller}】: {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<(bool Success, T? Result)> RunAsync<T>(Func<Task<T>> func, [CallerMemberName] string caller = "", string errorMsgTitle = "")
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await func();
|
||||
return (true, result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Logger.Error($"【{errorMsgTitle}失败 - {caller}】: {ex.Message}");
|
||||
return (false, default);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -27,8 +27,7 @@ namespace IRaCIS.Core.SCP
|
|||
//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.UseSqlServer(configuration.GetSection("ConnectionStrings:RemoteNew").Value);
|
||||
|
||||
options.UseLoggerFactory(logFactory);
|
||||
|
||||
|
|
|
|||
|
|
@ -7,28 +7,29 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AlibabaCloud.SDK.Sts20150401" Version="1.1.4" />
|
||||
<PackageReference Include="AlibabaCloud.SDK.Sts20150401" Version="1.1.6" />
|
||||
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="10.0.0" />
|
||||
<PackageReference Include="Aliyun.OSS.SDK.NetCore" Version="2.14.1" />
|
||||
<PackageReference Include="AWSSDK.S3" Version="3.7.405" />
|
||||
<PackageReference Include="AWSSDK.SecurityToken" Version="3.7.400.36" />
|
||||
<PackageReference Include="DistributedLock.Core" Version="1.0.7" />
|
||||
<PackageReference Include="DistributedLock.SqlServer" Version="1.0.5" />
|
||||
<PackageReference Include="fo-dicom" Version="5.1.3" />
|
||||
<PackageReference Include="fo-dicom.Codecs" Version="5.15.1" />
|
||||
<PackageReference Include="fo-dicom.Imaging.ImageSharp" Version="5.1.3" />
|
||||
<PackageReference Include="AWSSDK.S3" Version="4.0.6.9" />
|
||||
<PackageReference Include="AWSSDK.SecurityToken" Version="4.0.2" />
|
||||
<PackageReference Include="DistributedLock.Core" Version="1.0.8" />
|
||||
<PackageReference Include="DistributedLock.SqlServer" Version="1.0.6" />
|
||||
<PackageReference Include="fo-dicom" Version="5.2.2" />
|
||||
<PackageReference Include="fo-dicom.Codecs" Version="5.16.4" />
|
||||
<PackageReference Include="fo-dicom.Imaging.ImageSharp" Version="5.2.2" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.10" />
|
||||
<PackageReference Include="AutoMapper" Version="13.0.1" />
|
||||
<PackageReference Include="Minio" Version="6.0.3" />
|
||||
<PackageReference Include="Minio" Version="7.0.0" />
|
||||
<PackageReference Include="My.Extensions.Localization.Json" Version="3.3.0">
|
||||
<TreatAsUsed>true</TreatAsUsed>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Panda.DynamicWebApi" Version="1.2.2" />
|
||||
<PackageReference Include="Serilog.Enrichers.ClientInfo" Version="2.1.2" />
|
||||
<PackageReference Include="Serilog.Extensions.Hosting" Version="8.0.0" />
|
||||
<PackageReference Include="Serilog.Enrichers.ClientInfo" Version="2.4.0" />
|
||||
<PackageReference Include="Serilog.Extensions.Hosting" Version="9.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.9.0" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="7.0.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="9.0.3" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.11" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
@ -36,4 +37,12 @@
|
|||
<ProjectReference Include="..\IRaCIS.Core.Infra.EFCore\IRaCIS.Core.Infra.EFCore.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Update="appsettings.Prod_HIR_SCP.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
|
||||
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ using MassTransit;
|
|||
using MassTransit.NewIdProviders;
|
||||
using Microsoft.AspNetCore.HttpOverrides;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Identity.Client;
|
||||
using Panda.DynamicWebApi;
|
||||
using Serilog;
|
||||
using Serilog.Events;
|
||||
|
|
@ -82,7 +83,6 @@ builder.Services.AddControllers(options =>
|
|||
.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"));
|
||||
|
||||
|
|
@ -126,19 +126,25 @@ builder.Services.Configure<ForwardedHeadersOptions>(options =>
|
|||
ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
|
||||
});
|
||||
|
||||
//Dicom影像渲染图片 跨平台
|
||||
//builder.Services.AddDicomSetup();
|
||||
new DicomSetupBuilder()
|
||||
.RegisterServices(s =>
|
||||
s.AddFellowOakDicom()
|
||||
.AddTranscoderManager<NativeTranscoderManager>()
|
||||
builder.Services.AddFellowOakDicom().AddTranscoderManager<NativeTranscoderManager>()
|
||||
//.AddTranscoderManager<FellowOakDicom.Imaging.NativeCodec.NativeTranscoderManager>()
|
||||
.AddImageManager<ImageSharpImageManager>())
|
||||
.SkipValidation()
|
||||
.Build();
|
||||
.AddImageManager<ImageSharpImageManager>();
|
||||
|
||||
|
||||
|
||||
////Dicom影像渲染图片 跨平台
|
||||
////builder.Services.AddDicomSetup();
|
||||
//new DicomSetupBuilder()
|
||||
// .RegisterServices(s =>
|
||||
// s.AddFellowOakDicom()
|
||||
// .AddTranscoderManager<NativeTranscoderManager>()
|
||||
// //.AddTranscoderManager<FellowOakDicom.Imaging.NativeCodec.NativeTranscoderManager>()
|
||||
// .AddImageManager<ImageSharpImageManager>())
|
||||
// .SkipValidation()
|
||||
|
||||
// .Build();
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
// Add services to the container.
|
||||
|
|
@ -211,9 +217,17 @@ else
|
|||
|
||||
#endregion
|
||||
|
||||
DicomSetupBuilder.UseServiceProvider(app.Services);
|
||||
|
||||
// Program.cs
|
||||
IRCAppConfig.Configuration = builder.Configuration;
|
||||
|
||||
|
||||
var logger = app.Services.GetService<Microsoft.Extensions.Logging.ILogger<Program>>();
|
||||
|
||||
var server = DicomServerFactory.Create<CStoreSCPService>(_configuration.GetSection("DicomSCPServiceConfig").GetValue<int>("ServerPort"), userState: app.Services,logger: logger);
|
||||
|
||||
|
||||
|
||||
|
||||
app.Run();
|
||||
|
|
|
|||
|
|
@ -1,24 +1,34 @@
|
|||
{
|
||||
"$schema": "http://json.schemastore.org/launchsettings.json",
|
||||
"profiles": {
|
||||
"Test_IRC_SCP": {
|
||||
"Test_HIR_SCP": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "swagger",
|
||||
"applicationUrl": "http://localhost:6200",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Test_IRC_SCP"
|
||||
"ASPNETCORE_ENVIRONMENT": "Test_HIR_SCP"
|
||||
}
|
||||
},
|
||||
"Uat_IRC_SCP": {
|
||||
"Uat_HIR_SCP": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "swagger",
|
||||
"applicationUrl": "http://localhost:6200",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Uat_IRC_SCP"
|
||||
"ASPNETCORE_ENVIRONMENT": "Uat_HIR_SCP"
|
||||
}
|
||||
},
|
||||
"Prod_HIR_SCP": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "swagger",
|
||||
"applicationUrl": "http://localhost:6200",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Prod_HIR_SCP"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,31 +21,71 @@ using FellowOakDicom.Imaging;
|
|||
using SharpCompress.Common;
|
||||
using SixLabors.ImageSharp.Formats.Jpeg;
|
||||
using IRaCIS.Core.Infrastructure;
|
||||
using IRaCIS.Core.Infrastructure.Extention;
|
||||
using Newtonsoft.Json;
|
||||
using FellowOakDicom.Imaging.Codec;
|
||||
using FellowOakDicom.IO.Buffer;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using FellowOakDicom.Network.Client;
|
||||
using MassTransit.Futures.Contracts;
|
||||
using Microsoft.Identity.Client;
|
||||
|
||||
namespace IRaCIS.Core.SCP.Service
|
||||
{
|
||||
|
||||
public class DicomSCPServiceOption
|
||||
{
|
||||
public bool IsSupportThirdService { get; set; }
|
||||
|
||||
public bool IsForwardImageMultiThread { get; set; }
|
||||
|
||||
|
||||
|
||||
public List<string> CalledAEList { get; set; }
|
||||
|
||||
public string ServerPort { get; set; }
|
||||
}
|
||||
|
||||
public class ThirdDestinationAE
|
||||
{
|
||||
public int Port { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public string IP { get; set; }
|
||||
}
|
||||
|
||||
public static class IRCAppConfig
|
||||
{
|
||||
public static IConfiguration Configuration { get; set; }
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public class CStoreSCPService : DicomService, IDicomServiceProvider, IDicomCStoreProvider, IDicomCEchoProvider
|
||||
{
|
||||
//private IServiceProvider _injectServiceProvider { get; set; }
|
||||
private IServiceProvider _serviceProvider { get; set; }
|
||||
|
||||
private List<Guid> _SCPStudyIdList { get; set; } = new List<Guid>();
|
||||
private List<Guid> _SCPStudyIdList => _ImageUploadList.Where(t => t.SCPStudyId != Guid.Empty).Select(t => t.SCPStudyId).ToList();
|
||||
|
||||
private List<ImageUploadInfo> _ImageUploadList { get; set; } = new List<ImageUploadInfo>();
|
||||
|
||||
private SCPImageUpload _upload { get; set; }
|
||||
|
||||
private Guid _trialId { get; set; }
|
||||
private DicomSCPServiceOption DicomSCPServiceConfig { get; set; }
|
||||
|
||||
public HospitalGroup CurrentHospitalGroup { get; set; }
|
||||
|
||||
private List<Guid> HospitalGroupIdList { get; set; }
|
||||
|
||||
private bool _releasedNormally = false;
|
||||
|
||||
private bool _isCurrentThirdForward = false;
|
||||
|
||||
private List<ThirdDestinationAE> ThirdDestinationAEList { get; set; }
|
||||
|
||||
private Guid _trialSiteId { get; set; }
|
||||
|
||||
|
||||
|
||||
|
|
@ -58,27 +98,44 @@ namespace IRaCIS.Core.SCP.Service
|
|||
|
||||
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
|
||||
// Lossless
|
||||
DicomTransferSyntax.JPEGLSLossless,
|
||||
DicomTransferSyntax.JPEG2000Lossless,
|
||||
DicomTransferSyntax.JPEGProcess14SV1,
|
||||
DicomTransferSyntax.JPEGProcess14,
|
||||
DicomTransferSyntax.RLELossless,
|
||||
// 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
|
||||
DicomTransferSyntax.JPEGLSNearLossless,
|
||||
DicomTransferSyntax.JPEG2000Lossy,
|
||||
DicomTransferSyntax.JPEGProcess1,
|
||||
DicomTransferSyntax.JPEGProcess2_4,
|
||||
// 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
|
||||
DicomTransferSyntax.ExplicitVRLittleEndian,
|
||||
DicomTransferSyntax.ExplicitVRBigEndian,
|
||||
DicomTransferSyntax.ImplicitVRLittleEndian
|
||||
};
|
||||
|
||||
// 定义一个静态信号量,控制同时最多 N 个转发线程
|
||||
|
||||
public CStoreSCPService(INetworkStream stream, Encoding fallbackEncoding, Microsoft.Extensions.Logging.ILogger log, DicomServiceDependencies dependencies)
|
||||
private static SemaphoreSlim _forwardLimiter;
|
||||
|
||||
// ✅ 静态构造函数(只执行一次,全局初始化)
|
||||
static CStoreSCPService()
|
||||
{
|
||||
// 默认单线程
|
||||
|
||||
var maxThreads = IRCAppConfig.Configuration.GetValue<int>("DicomSCPServiceConfig:MultiThreadCount", 1);
|
||||
|
||||
_forwardLimiter = new SemaphoreSlim(maxThreads);
|
||||
|
||||
Log.Logger.Information($"初始化 DICOM 转发线程限制为: {maxThreads}");
|
||||
}
|
||||
|
||||
|
||||
public CStoreSCPService(INetworkStream stream, Encoding fallbackEncoding, Microsoft.Extensions.Logging.ILogger log, DicomServiceDependencies dependencies, IServiceProvider injectServiceProvider)
|
||||
: base(stream, fallbackEncoding, log, dependencies)
|
||||
{
|
||||
_serviceProvider = injectServiceProvider.CreateScope().ServiceProvider;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -87,55 +144,42 @@ namespace IRaCIS.Core.SCP.Service
|
|||
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 option = _serviceProvider.GetService<IOptionsMonitor<DicomSCPServiceOption>>().CurrentValue;
|
||||
|
||||
DicomSCPServiceConfig = option;
|
||||
|
||||
var _hospitalGroupRepository = _serviceProvider.GetService<IRepository<HospitalGroup>>();
|
||||
|
||||
var _dicomAERepository = _serviceProvider.GetService<IRepository<DicomAE>>();
|
||||
|
||||
|
||||
ThirdDestinationAEList = _dicomAERepository.Where(t => t.PacsTypeEnum == PacsType.Destination).Select(t => new ThirdDestinationAE() { IP = t.IP, Port = t.Port, Name = t.CalledAE }).ToList();
|
||||
|
||||
|
||||
var aeList = _dicomAERepository/*.Where(t => t.PacsTypeEnum == PacsType.PacsServer)*/.Select(t => t.CalledAE).ToList();
|
||||
|
||||
var list = _hospitalGroupRepository.Where(t => t.IsEnable).ToList();
|
||||
|
||||
CurrentHospitalGroup = list.FirstOrDefault(t => t.CallingAE == association.CallingAE);
|
||||
|
||||
|
||||
var unionAEList = aeList.Union(list.Select(t => t.CallingAE)).ToList();
|
||||
|
||||
|
||||
|
||||
var _trialDicomAERepository = _serviceProvider.GetService<IRepository<TrialDicomAE>>();
|
||||
var calledAEList = option.CalledAEList;
|
||||
|
||||
|
||||
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)
|
||||
if (!calledAEList.Contains(association.CalledAE) || !unionAEList.Any(t => t == association.CallingAE))
|
||||
{
|
||||
|
||||
Log.Logger.Warning($"拒绝CallingAE:{association.CallingAE} CalledAE:{association.CalledAE}的连接");
|
||||
Log.Logger.Warning($"拒绝CalledAE:{association.CalledAE} CallingAE:{association.CallingAE}连接");
|
||||
|
||||
return SendAssociationRejectAsync(
|
||||
DicomRejectResult.Permanent,
|
||||
|
|
@ -155,53 +199,82 @@ namespace IRaCIS.Core.SCP.Service
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
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);
|
||||
if (_isCurrentThirdForward == false)
|
||||
{
|
||||
var _distributedLockProvider = _serviceProvider.GetService<IDistributedLockProvider>();
|
||||
|
||||
|
||||
var _studyRepository = _serviceProvider.GetService<IRepository<SCPStudy>>();
|
||||
//将检查设置为传输结束
|
||||
await _studyRepository.BatchUpdateNoTrackingAsync(t => _SCPStudyIdList.Contains(t.Id), u => new SCPStudy() { IsUploadFinished = true });
|
||||
var @lock = _distributedLockProvider.CreateLock($"{_upload.CallingAE}");
|
||||
|
||||
using (await @lock.AcquireAsync())
|
||||
{
|
||||
|
||||
await DataMaintenanceAsaync();
|
||||
|
||||
await AddUploadLogAsync();
|
||||
|
||||
_releasedNormally = true;
|
||||
|
||||
Log.Logger.Information($"进入释放连接请求 {_releasedNormally}");
|
||||
}
|
||||
}
|
||||
|
||||
await _studyRepository.SaveChangesAndClearAllTrackingAsync();
|
||||
|
||||
await SendAssociationReleaseResponseAsync();
|
||||
}
|
||||
|
||||
|
||||
private async Task AddUploadLogAsync()
|
||||
{
|
||||
//转发第三方,那么不记录日志
|
||||
if (_isCurrentThirdForward == false)
|
||||
{
|
||||
//记录监控
|
||||
|
||||
var _SCPImageUploadRepository = _serviceProvider.GetService<IRepository<SCPImageUpload>>();
|
||||
|
||||
_upload.EndTime = DateTime.Now;
|
||||
_upload.StudyCount = _ImageUploadList.Count;
|
||||
|
||||
_upload.UploadJsonStr = (new SCPImageLog() { UploadList = _ImageUploadList }).ToJsonStr();
|
||||
|
||||
|
||||
if (_upload.FileCount > 0)
|
||||
{
|
||||
//可能是测试echo 导致记录了
|
||||
await _SCPImageUploadRepository.AddAsync(_upload, true);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private async Task DataMaintenanceAsaync()
|
||||
{
|
||||
Log.Logger.Warning($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE}传输结束:开始维护数据,处理检查Modality");
|
||||
Log.Logger.Warning($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE}传输结束:开始维护数据,处理检查Modality 以及自动创建访视,绑定检查");
|
||||
|
||||
var patientStudyService = _serviceProvider.GetService<IPatientStudyService>();
|
||||
|
||||
await patientStudyService.AutoBindingPatientStudyVisitAsync(_SCPStudyIdList);
|
||||
|
||||
//处理检查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 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, t.StudyInstanceUid }).ToList();
|
||||
|
||||
foreach (var g in seriesModalityList.GroupBy(t => new { t.SCPStudyId, t.StudyInstanceUid }))
|
||||
{
|
||||
var modality = string.Join('、', g.Select(t => t.Modality).Distinct().ToList());
|
||||
|
||||
|
|
@ -222,7 +295,9 @@ namespace IRaCIS.Core.SCP.Service
|
|||
modalityForEdit = "PET-CT";
|
||||
}
|
||||
|
||||
await _studyRepository.BatchUpdateNoTrackingAsync(t => t.Id == g.Key, u => new SCPStudy() { Modalities = modality, ModalityForEdit = modalityForEdit });
|
||||
await _studyRepository.BatchUpdateNoTrackingAsync(t => t.Id == g.Key.SCPStudyId, u => new SCPStudy() { Modalities = modality, ModalityForEdit = modalityForEdit });
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -238,47 +313,152 @@ namespace IRaCIS.Core.SCP.Service
|
|||
|
||||
public async void OnConnectionClosed(Exception exception)
|
||||
{
|
||||
/* nothing to do here */
|
||||
|
||||
//奇怪的bug 上传的时候,用王捷修改的影像,会关闭,重新连接,导致检查id 丢失,然后状态不一致
|
||||
if (exception == null)
|
||||
if (_isCurrentThirdForward == false)
|
||||
{
|
||||
//var _studyRepository = _serviceProvider.GetService<IRepository<SCPStudy>>();
|
||||
////将检查设置为传输结束
|
||||
//await _studyRepository.BatchUpdateNoTrackingAsync(t => _SCPStudyIdList.Contains(t.Id), u => new SCPStudy() { IsUploadFinished = true });
|
||||
var _studyRepository = _serviceProvider.GetService<IRepository<SCPStudy>>();
|
||||
|
||||
//await _studyRepository.SaveChangesAndClearAllTrackingAsync();
|
||||
|
||||
if (exception != null || _releasedNormally == false)
|
||||
{
|
||||
//客户端断网,恢复后,也是没有异常的,估计是超时走了关闭
|
||||
|
||||
await _studyRepository.BatchUpdateNoTrackingAsync(t => _SCPStudyIdList.Contains(t.Id), u => new SCPStudy() { IsUploadFinished = true, IsUploadFaild = true });
|
||||
//记录日志
|
||||
await AddUploadLogAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
//将检查设置为传输结束
|
||||
await _studyRepository.BatchUpdateNoTrackingAsync(t => _SCPStudyIdList.Contains(t.Id), u => new SCPStudy() { IsUploadFinished = true, IsUploadFaild = false });
|
||||
}
|
||||
|
||||
|
||||
await _studyRepository.SaveChangesAndClearAllTrackingAsync();
|
||||
}
|
||||
|
||||
Log.Logger.Warning($"连接关闭 {exception?.Message} {exception?.InnerException?.Message}");
|
||||
Log.Logger.Warning($"连接关闭 {_releasedNormally} {exception?.Message} {exception?.InnerException?.Message}");
|
||||
}
|
||||
|
||||
|
||||
private async Task<DicomStatus> ForwardToThirdPartyAsync(DicomCStoreRequest request, ThirdDestinationAE findDestination)
|
||||
{
|
||||
await _forwardLimiter.WaitAsync(); // 限制并发数量
|
||||
try
|
||||
{
|
||||
var forwardRequest = new DicomCStoreRequest(request.File.Clone());
|
||||
|
||||
var client = DicomClientFactory.Create(
|
||||
findDestination.IP,
|
||||
findDestination.Port,
|
||||
false,
|
||||
DicomSCPServiceConfig.CalledAEList.First(),
|
||||
findDestination.Name);
|
||||
|
||||
DicomStatus finalStatus = DicomStatus.Success;
|
||||
|
||||
forwardRequest.OnResponseReceived += (rq, rp) =>
|
||||
{
|
||||
Log.Logger.Information($"Forwarded C-STORE Response: {rq.SOPInstanceUID} {rp.Status}");
|
||||
finalStatus = rp.Status; // 记录目标 PACS 返回状态
|
||||
};
|
||||
|
||||
await client.AddRequestAsync(forwardRequest);
|
||||
await client.SendAsync();
|
||||
|
||||
return finalStatus; // 返回实际状态
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Logger.Error("Error forwarding C-STORE: " + ex.Message);
|
||||
return DicomStatus.ProcessingFailure; // 出错返回失败状态
|
||||
}
|
||||
finally
|
||||
{
|
||||
_forwardLimiter.Release();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
string studyInstanceUid = request.Dataset.GetSingleValueOrDefault(DicomTag.StudyInstanceUID, string.Empty);
|
||||
string seriesInstanceUid = request.Dataset.GetSingleValueOrDefault(DicomTag.SeriesInstanceUID, string.Empty);
|
||||
string sopInstanceUid = request.Dataset.GetSingleValueOrDefault(DicomTag.SOPInstanceUID, string.Empty);
|
||||
string patientIdStr = request.Dataset.GetSingleValueOrDefault(DicomTag.PatientID, string.Empty);
|
||||
|
||||
//Guid studyId = IdentifierHelper.CreateGuid(studyInstanceUid, trialId.ToString());
|
||||
Guid seriesId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid, _trialId.ToString());
|
||||
Guid instanceId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid, sopInstanceUid, _trialId.ToString());
|
||||
if (studyInstanceUid.IsNullOrEmpty() || seriesInstanceUid.IsNullOrEmpty() || sopInstanceUid.IsNullOrEmpty())
|
||||
{
|
||||
Log.Logger.Error($"接收数据读取StudyInstanceUID:{studyInstanceUid}、SeriesInstanceUID:{seriesInstanceUid}、SOPInstanceUID:{sopInstanceUid}有空 ");
|
||||
|
||||
return new DicomCStoreResponse(request, DicomStatus.Success);
|
||||
}
|
||||
|
||||
var _cmoveStudyRepository = _serviceProvider.GetService<IRepository<CmoveStudy>>();
|
||||
|
||||
#region 判断是否转发第三方影像
|
||||
|
||||
if (DicomSCPServiceConfig.IsSupportThirdService)
|
||||
{
|
||||
var cmoveInfo = _cmoveStudyRepository.Where(t => t.StudyInstanceUIDList.Any(c => c == studyInstanceUid)).OrderByDescending(t => t.CreateTime).FirstOrDefault();
|
||||
|
||||
//确定是第三方请求
|
||||
if (cmoveInfo != null && ThirdDestinationAEList.Any(t => t.Name == cmoveInfo.DestinationAE))
|
||||
{
|
||||
_isCurrentThirdForward = true;
|
||||
|
||||
var findDestination = ThirdDestinationAEList.FirstOrDefault(t => t.Name == cmoveInfo.DestinationAE);
|
||||
|
||||
if (DicomSCPServiceConfig.IsForwardImageMultiThread)
|
||||
{
|
||||
// 多线程模式,异步执行
|
||||
_ = Task.Run(() => ForwardToThirdPartyAsync(request, findDestination));
|
||||
|
||||
// 立即返回 Success
|
||||
return new DicomCStoreResponse(request, DicomStatus.Success);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 单线程模式,同步等待完成
|
||||
var responseStatus = await ForwardToThirdPartyAsync(request, findDestination);
|
||||
|
||||
return new DicomCStoreResponse(request, responseStatus);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
//确保来了影像集合存在
|
||||
if (!_ImageUploadList.Any(t => t.StudyInstanceUid == studyInstanceUid))
|
||||
{
|
||||
_ImageUploadList.Add(new ImageUploadInfo() { StudyInstanceUid = studyInstanceUid });
|
||||
}
|
||||
|
||||
Guid seriesId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid);
|
||||
Guid instanceId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid, sopInstanceUid);
|
||||
|
||||
|
||||
var ossService = _serviceProvider.GetService<IOSSService>();
|
||||
var dicomArchiveService = _serviceProvider.GetService<IDicomArchiveService>();
|
||||
var _seriesRepository = _serviceProvider.GetService<IRepository<SCPSeries>>();
|
||||
var _studyGroupRepository = _serviceProvider.GetService<IRepository<SCPStudyHospitalGroup>>();
|
||||
|
||||
|
||||
|
||||
var _distributedLockProvider = _serviceProvider.GetService<IDistributedLockProvider>();
|
||||
|
||||
var storeRelativePath = string.Empty;
|
||||
var ossFolderPath = $"{_trialId}/Image/PACS/{_trialSiteId}{studyInstanceUid}";
|
||||
var ossFolderPath = $"Dicom/{studyInstanceUid}";
|
||||
|
||||
|
||||
long fileSize = 0;
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
|
|
@ -286,6 +466,66 @@ namespace IRaCIS.Core.SCP.Service
|
|||
{
|
||||
await request.File.SaveAsync(ms);
|
||||
|
||||
#region 1帧拆成多个固定大小的,方便移动端浏览
|
||||
|
||||
// 回到开头,读取 dicom
|
||||
ms.Position = 0;
|
||||
var dicomFile = DicomFile.Open(ms);
|
||||
|
||||
var pixelData = DicomPixelData.Create(dicomFile.Dataset);
|
||||
var syntax = pixelData.Syntax;
|
||||
|
||||
// 每个 fragment 固定大小 (64KB 示例,可以自己调整)
|
||||
int fragmentSize = 20 * 1024;
|
||||
|
||||
if (syntax.IsEncapsulated)
|
||||
{
|
||||
var newFragments = new DicomOtherByteFragment(DicomTag.PixelData);
|
||||
|
||||
for (int n = 0; n < pixelData.NumberOfFrames; n++)
|
||||
{
|
||||
var frameData = pixelData.GetFrame(n); // 获取完整一帧
|
||||
var data = frameData.Data;
|
||||
int offset = 0;
|
||||
|
||||
while (offset < data.Length)
|
||||
{
|
||||
int size = Math.Min(fragmentSize, data.Length - offset);
|
||||
var buffer = new byte[size];
|
||||
Buffer.BlockCopy(data, offset, buffer, 0, size);
|
||||
|
||||
newFragments.Fragments.Add(new MemoryByteBuffer(buffer));
|
||||
offset += size;
|
||||
}
|
||||
}
|
||||
|
||||
// 替换原 PixelData
|
||||
dicomFile.Dataset.AddOrUpdate(newFragments);
|
||||
|
||||
|
||||
// 重新保存 dicom 到流
|
||||
ms.SetLength(0);
|
||||
dicomFile.Save(ms);
|
||||
}
|
||||
|
||||
|
||||
ms.Position = 0;
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region 本地测试
|
||||
//// --- 保存到本地文件测试 ---
|
||||
//var localPath = @"D:\TestDicom.dcm";
|
||||
//using (var fs = new FileStream(localPath, FileMode.Create, FileAccess.Write))
|
||||
//{
|
||||
// ms.CopyTo(fs);
|
||||
//}
|
||||
//return new DicomCStoreResponse(request, DicomStatus.Success);
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
//irc 从路径最后一截取Guid
|
||||
storeRelativePath = await ossService.UploadToOSSAsync(ms, ossFolderPath, instanceId.ToString(), false);
|
||||
|
||||
|
|
@ -308,12 +548,7 @@ namespace IRaCIS.Core.SCP.Service
|
|||
{
|
||||
try
|
||||
{
|
||||
var scpStudyId = await dicomArchiveService.ArchiveDicomFileAsync(request.Dataset, _trialId, _trialSiteId, storeRelativePath, Association.CallingAE, Association.CalledAE,fileSize);
|
||||
|
||||
if (!_SCPStudyIdList.Contains(scpStudyId))
|
||||
{
|
||||
_SCPStudyIdList.Add(scpStudyId);
|
||||
}
|
||||
var scpStudyId = await dicomArchiveService.ArchiveDicomFileAsync(request.File, storeRelativePath, Association.CallingAE, Association.CalledAE, fileSize);
|
||||
|
||||
var series = await _seriesRepository.FirstOrDefaultAsync(t => t.Id == seriesId);
|
||||
|
||||
|
|
@ -337,32 +572,97 @@ namespace IRaCIS.Core.SCP.Service
|
|||
|
||||
series.ImageResizePath = seriesPath;
|
||||
|
||||
|
||||
|
||||
//await _seriesRepository.BatchUpdateNoTrackingAsync(t => t.Id == seriesId, u => new SCPSeries() { ImageResizePath = seriesPath });
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
await _seriesRepository.SaveChangesAsync();
|
||||
|
||||
if (_ImageUploadList.Any(t => t.StudyInstanceUid == studyInstanceUid))
|
||||
{
|
||||
var find = _ImageUploadList.FirstOrDefault(t => t.StudyInstanceUid.Equals(studyInstanceUid));
|
||||
|
||||
|
||||
find.SuccessImageCount++;
|
||||
|
||||
if (!find.PatientNameList.Any(t => t == patientIdStr) && patientIdStr.IsNotNullOrEmpty())
|
||||
{
|
||||
find.PatientNameList.Add(patientIdStr);
|
||||
}
|
||||
|
||||
//首次 (默认是Guid 空,数据库归档出了Id)
|
||||
if (find.SCPStudyId != scpStudyId)
|
||||
{
|
||||
find.SCPStudyId = scpStudyId;
|
||||
|
||||
#region 给检查打课题组标签
|
||||
|
||||
//添加课题组标签
|
||||
if (CurrentHospitalGroup != null)
|
||||
{
|
||||
if (!_studyGroupRepository.Any(t => t.SCPStudyId == scpStudyId && t.HospitalGroupId == CurrentHospitalGroup.Id))
|
||||
{
|
||||
await _studyGroupRepository.AddAsync(new SCPStudyHospitalGroup() { SCPStudyId = scpStudyId, HospitalGroupId = CurrentHospitalGroup.Id });
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var findCmoveInfo = _cmoveStudyRepository.Where(t => t.StudyInstanceUIDList.Any(c => c == studyInstanceUid)).OrderByDescending(t => t.CreateTime).FirstOrDefault();
|
||||
|
||||
if (findCmoveInfo != null)
|
||||
{
|
||||
foreach (var item in findCmoveInfo.HopitalGroupIdList)
|
||||
{
|
||||
if (!_studyGroupRepository.Any(t => t.SCPStudyId == scpStudyId && t.HospitalGroupId == item))
|
||||
{
|
||||
await _studyGroupRepository.AddAsync(new SCPStudyHospitalGroup() { SCPStudyId = scpStudyId, HospitalGroupId = item });
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Logger.Warning($"未找到{studyInstanceUid}的Cmove记录");
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
Log.Logger.Warning($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE} 传输处理异常:{ex.ToString()}");
|
||||
|
||||
if (_ImageUploadList.Any(t => t.StudyInstanceUid == studyInstanceUid))
|
||||
{
|
||||
var find = _ImageUploadList.FirstOrDefault(t => t.StudyInstanceUid.Equals(studyInstanceUid));
|
||||
|
||||
find.FailedImageCount++;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
//监控信息设置
|
||||
_upload.FileCount++;
|
||||
_upload.FileSize = _upload.FileSize + fileSize;
|
||||
return new DicomCStoreResponse(request, DicomStatus.Success);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//监控信息设置
|
||||
_upload.FileCount++;
|
||||
_upload.FileSize = _upload.FileSize + fileSize;
|
||||
return new DicomCStoreResponse(request, DicomStatus.Success);
|
||||
}
|
||||
|
||||
|
||||
public Task OnCStoreRequestExceptionAsync(string tempFileName, Exception e)
|
||||
{
|
||||
Log.Logger.Warning($"CStoreRequestException {tempFileName} {e?.Message} {e?.InnerException?.Message}");
|
||||
|
||||
// let library handle logging and error response
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@ using FellowOakDicom.Network;
|
|||
using IRaCIS.Core.SCP.Service;
|
||||
using IRaCIS.Core.Infra.EFCore;
|
||||
using MassTransit;
|
||||
using System.Runtime.Intrinsics.X86;
|
||||
using Serilog.Sinks.File;
|
||||
using IRaCIS.Core.Infrastructure.Extention;
|
||||
|
||||
namespace IRaCIS.Core.SCP.Service
|
||||
{
|
||||
|
|
@ -52,18 +52,21 @@ namespace IRaCIS.Core.SCP.Service
|
|||
/// <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)
|
||||
public async Task<Guid> ArchiveDicomFileAsync(DicomFile dicomFile, string fileRelativePath, string callingAE, string calledAE, long fileSize)
|
||||
{
|
||||
var dataset = dicomFile.Dataset;
|
||||
|
||||
|
||||
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);
|
||||
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());
|
||||
Guid studyId = IdentifierHelper.CreateGuid(studyInstanceUid);
|
||||
Guid seriesId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid);
|
||||
Guid instanceId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid, sopInstanceUid);
|
||||
|
||||
var isStudyNeedAdd = false;
|
||||
var isSeriesNeedAdd = false;
|
||||
|
|
@ -74,15 +77,16 @@ namespace IRaCIS.Core.SCP.Service
|
|||
|
||||
//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 findPatient = await _patientRepository.FirstOrDefaultAsync(t => t.PatientIdStr == patientIdStr);
|
||||
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)
|
||||
//先传输了,修改了患者编号的,又传输了没有修改患者编号的,导致后传输的没有修改患者编号的下面的检查为0
|
||||
if (findPatient == null /*&& findStudy == null*/)
|
||||
{
|
||||
isPatientNeedAdd = true;
|
||||
|
||||
|
|
@ -90,8 +94,6 @@ namespace IRaCIS.Core.SCP.Service
|
|||
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),
|
||||
|
|
@ -117,20 +119,6 @@ namespace IRaCIS.Core.SCP.Service
|
|||
{
|
||||
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
|
||||
{
|
||||
|
|
@ -151,6 +139,32 @@ namespace IRaCIS.Core.SCP.Service
|
|||
}
|
||||
|
||||
findPatient.LatestPushTime = DateTime.Now;
|
||||
findPatient.PatientName = dataset.GetSingleValueOrDefault(DicomTag.PatientName, string.Empty);
|
||||
findPatient.PatientSex = dataset.GetSingleValueOrDefault(DicomTag.PatientSex, string.Empty);
|
||||
findPatient.PatientAge = dataset.GetSingleValueOrDefault(DicomTag.PatientAge, string.Empty);
|
||||
findPatient.PatientBirthDate = dataset.GetSingleValueOrDefault(DicomTag.PatientBirthDate, string.Empty);
|
||||
|
||||
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;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
findPatient.PatientBirthDate = string.Empty;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (findStudy == null)
|
||||
|
|
@ -163,10 +177,12 @@ namespace IRaCIS.Core.SCP.Service
|
|||
|
||||
PatientId = findPatient.Id,
|
||||
Id = studyId,
|
||||
TrialId = trialId,
|
||||
TrialSiteId = trialSiteId,
|
||||
StudyInstanceUid = studyInstanceUid,
|
||||
StudyTime = studyTime,
|
||||
DicomStudyDate = dataset.GetSingleValueOrDefault(DicomTag.StudyDate, string.Empty),
|
||||
DicomStudyTime = dataset.GetSingleValueOrDefault(DicomTag.StudyTime, string.Empty),
|
||||
|
||||
|
||||
Modalities = dataset.GetSingleValueOrDefault(DicomTag.Modality, string.Empty),
|
||||
//ModalityForEdit = modalityForEdit,
|
||||
Description = dataset.GetSingleValueOrDefault(DicomTag.StudyDescription, string.Empty),
|
||||
|
|
@ -201,6 +217,19 @@ namespace IRaCIS.Core.SCP.Service
|
|||
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]}";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
findStudy.DicomStudyDate = dataset.GetSingleValueOrDefault(DicomTag.StudyDate, string.Empty);
|
||||
findStudy.DicomStudyTime = dataset.GetSingleValueOrDefault(DicomTag.StudyTime, string.Empty);
|
||||
findStudy.CalledAE = calledAE;
|
||||
findStudy.CallingAE = callingAE;
|
||||
findStudy.PatientName = dataset.GetSingleValueOrDefault(DicomTag.PatientName, string.Empty);
|
||||
findStudy.PatientSex = dataset.GetSingleValueOrDefault(DicomTag.PatientSex, string.Empty);
|
||||
findStudy.PatientAge = dataset.GetSingleValueOrDefault(DicomTag.PatientAge, string.Empty);
|
||||
findStudy.UpdateTime = DateTime.Now;
|
||||
|
||||
await _patientRepository.BatchUpdateNoTrackingAsync(t => t.Id == findStudy.PatientId, u => new SCPPatient() { LatestPushTime = DateTime.Now });
|
||||
}
|
||||
|
||||
|
||||
if (findSerice == null)
|
||||
|
|
@ -218,6 +247,9 @@ namespace IRaCIS.Core.SCP.Service
|
|||
//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),
|
||||
DicomSeriesDate = dataset.GetSingleValueOrDefault(DicomTag.SeriesDate, string.Empty),
|
||||
DicomSeriesTime = dataset.GetSingleValueOrDefault(DicomTag.SeriesTime, string.Empty),
|
||||
|
||||
Modality = dataset.GetSingleValueOrDefault(DicomTag.Modality, string.Empty),
|
||||
Description = dataset.GetSingleValueOrDefault(DicomTag.SeriesDescription, string.Empty),
|
||||
SliceThickness = dataset.GetSingleValueOrDefault(DicomTag.SliceThickness, string.Empty),
|
||||
|
|
@ -239,7 +271,20 @@ namespace IRaCIS.Core.SCP.Service
|
|||
|
||||
++findStudy.SeriesCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
findSerice.DicomSeriesDate = dataset.GetSingleValueOrDefault(DicomTag.SeriesDate, string.Empty);
|
||||
findSerice.DicomSeriesTime = dataset.GetSingleValueOrDefault(DicomTag.SeriesTime, string.Empty);
|
||||
findSerice.UpdateTime = DateTime.Now;
|
||||
}
|
||||
|
||||
var transferSyntaxUID = dicomFile.FileMetaInfo.GetSingleValueOrDefault(DicomTag.TransferSyntaxUID, string.Empty);
|
||||
|
||||
var isEncapsulated = false;
|
||||
if (transferSyntaxUID.IsNotNullOrEmpty())
|
||||
{
|
||||
isEncapsulated = DicomTransferSyntax.Lookup(DicomUID.Parse(transferSyntaxUID)).IsEncapsulated;
|
||||
}
|
||||
|
||||
if (findInstance == null)
|
||||
{
|
||||
|
|
@ -253,6 +298,12 @@ namespace IRaCIS.Core.SCP.Service
|
|||
SeriesInstanceUid = findSerice.SeriesInstanceUid,
|
||||
|
||||
SopInstanceUid = sopInstanceUid,
|
||||
SOPClassUID = dataset.GetSingleValueOrDefault(DicomTag.SOPClassUID, string.Empty),
|
||||
MediaStorageSOPClassUID = dicomFile.FileMetaInfo.GetSingleValueOrDefault(DicomTag.MediaStorageSOPClassUID, string.Empty),
|
||||
TransferSyntaxUID = transferSyntaxUID,
|
||||
MediaStorageSOPInstanceUID = dicomFile.FileMetaInfo.GetSingleValueOrDefault(DicomTag.MediaStorageSOPInstanceUID, string.Empty),
|
||||
IsEncapsulated = isEncapsulated,
|
||||
|
||||
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,
|
||||
|
|
@ -273,13 +324,21 @@ namespace IRaCIS.Core.SCP.Service
|
|||
|
||||
Path = fileRelativePath,
|
||||
|
||||
FileSize= fileSize,
|
||||
|
||||
FileSize = fileSize,
|
||||
};
|
||||
|
||||
++findStudy.InstanceCount;
|
||||
++findSerice.InstanceCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
findInstance.SOPClassUID = dataset.GetSingleValueOrDefault(DicomTag.SOPClassUID, string.Empty);
|
||||
findInstance.MediaStorageSOPClassUID = dicomFile.FileMetaInfo.GetSingleValueOrDefault(DicomTag.MediaStorageSOPClassUID, string.Empty);
|
||||
findInstance.TransferSyntaxUID = transferSyntaxUID;
|
||||
findInstance.MediaStorageSOPInstanceUID = dicomFile.FileMetaInfo.GetSingleValueOrDefault(DicomTag.MediaStorageSOPInstanceUID, string.Empty);
|
||||
findInstance.IsEncapsulated = isEncapsulated;
|
||||
findInstance.UpdateTime = DateTime.Now;
|
||||
}
|
||||
|
||||
if (isPatientNeedAdd)
|
||||
{
|
||||
|
|
@ -298,13 +357,14 @@ namespace IRaCIS.Core.SCP.Service
|
|||
{
|
||||
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 _instanceRepository.BatchUpdateNoTrackingAsync(t => t.Id == instanceId, u => new SCPInstance() { Path = fileRelativePath, FileSize = fileSize });
|
||||
}
|
||||
|
||||
await _studyRepository.SaveChangesAsync();
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ 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);
|
||||
Task<Guid> ArchiveDicomFileAsync(DicomFile dicomFile,string fileRelativePath,string callingAE,string calledAE,long fileSize);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ using MassTransit;
|
|||
using Microsoft.Extensions.Options;
|
||||
using Minio;
|
||||
using Minio.DataModel.Args;
|
||||
using System.Net;
|
||||
using System.Reactive.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
|
|
@ -118,7 +119,7 @@ public class AWSTempToken
|
|||
public string SecretAccessKey { get; set; }
|
||||
public string BucketName { get; set; }
|
||||
public string ViewEndpoint { get; set; }
|
||||
public DateTime Expiration { get; set; }
|
||||
public DateTime? Expiration { get; set; }
|
||||
}
|
||||
|
||||
public enum ObjectStoreUse
|
||||
|
|
@ -216,9 +217,19 @@ public class OSSService : IOSSService
|
|||
.WithBucket(minIOConfig.BucketName)
|
||||
.WithObject(ossRelativePath)
|
||||
.WithStreamData(memoryStream)
|
||||
|
||||
.WithObjectSize(memoryStream.Length);
|
||||
|
||||
await minioClient.PutObjectAsync(putObjectArgs);
|
||||
var putResponse = await minioClient.PutObjectAsync(putObjectArgs);
|
||||
|
||||
if (putResponse.ResponseStatusCode == HttpStatusCode.OK)
|
||||
{
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new BusinessValidationFailedException($"上传失败ResponseStatusCode:{putResponse.ResponseStatusCode}{putResponse.ResponseContent}");
|
||||
}
|
||||
}
|
||||
else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS")
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,268 @@
|
|||
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 Microsoft.AspNetCore.Mvc;
|
||||
using IRaCIS.Core.Infrastructure.Extention;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace IRaCIS.Core.SCP.Service
|
||||
{
|
||||
public interface IPatientStudyService
|
||||
{
|
||||
Task<IResponseOutput> AutoBindingPatientStudyVisitAsync(List<Guid> scpStudyIdList);
|
||||
}
|
||||
|
||||
[ApiExplorerSettings(GroupName = "Trial")]
|
||||
public class PatientStudyService : BaseService, IPatientStudyService
|
||||
{
|
||||
private readonly IRepository<SCPStudySubjectVisit> _studySubjectVisitRepository;
|
||||
private readonly IRepository<SubjectPatient> _subjectPatientRepository;
|
||||
private readonly IRepository<Trial> _trialRepository;
|
||||
private readonly IRepository<SCPPatient> _patientRepository;
|
||||
private readonly IRepository<SCPStudy> _studyRepository;
|
||||
private readonly IRepository<Subject> _subjectRepository;
|
||||
private readonly IRepository<SubjectVisit> _subjectVisitRepository;
|
||||
private readonly IDistributedLockProvider _distributedLockProvider;
|
||||
|
||||
public PatientStudyService(IRepository<SCPStudySubjectVisit> studySubjectVisitRepository, IRepository<SCPStudy> studyRepository, IRepository<SubjectPatient> subjectPatientRepository, IRepository<Trial> trialRepository, IRepository<SCPPatient> patientRepository, IRepository<Subject> subjectRepository, IRepository<SubjectVisit> subjectVisitRepository, IDistributedLockProvider distributedLockProvider)
|
||||
{
|
||||
_studySubjectVisitRepository = studySubjectVisitRepository;
|
||||
_studyRepository = studyRepository;
|
||||
_subjectPatientRepository = subjectPatientRepository;
|
||||
_trialRepository = trialRepository;
|
||||
_patientRepository = patientRepository;
|
||||
_subjectRepository = subjectRepository;
|
||||
_subjectVisitRepository = subjectVisitRepository;
|
||||
_distributedLockProvider = distributedLockProvider;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public class AuToBindingStudyInfo
|
||||
{
|
||||
public Guid SCPStudyId { get; set; }
|
||||
public DateTime? StudyTime { get; set; }
|
||||
}
|
||||
|
||||
private async Task DealAutoBindingStudyAsync(Guid trialId, Guid subjectId, List<AuToBindingStudyInfo> studyList, decimal? startBindVisitNum = null)
|
||||
{
|
||||
|
||||
//自动创建访视 和检查绑定
|
||||
|
||||
//1. 查询已存在的访视
|
||||
var subjectAllVisitList = await _subjectVisitRepository.Where(t => t.SubjectId == subjectId)
|
||||
.Select(t => new
|
||||
{
|
||||
t.SubjectId,
|
||||
SubjectVisitId = t.Id,
|
||||
t.SubmitState,
|
||||
VisitNum = t.VisitNum,
|
||||
MaxStudyTime = t.SCPStudySubjectVisitList.Max(t => t.SCPStudy.StudyTime),
|
||||
MinStudyTime = t.SCPStudySubjectVisitList.Min(t => t.SCPStudy.StudyTime)
|
||||
})
|
||||
.ToListAsync();
|
||||
|
||||
|
||||
//2、获取项目配置
|
||||
var trialconfig = _trialRepository.Where(t => t.Id == trialId).Select(t => new { t.BlindBaseLineName, t.BlindFollowUpPrefix }).FirstOrDefault();
|
||||
|
||||
//3、 未提交的最小的访视号 从这个访视开始绑定
|
||||
|
||||
decimal subjectMaxVisitNum = 0;
|
||||
|
||||
if (startBindVisitNum == null)
|
||||
{
|
||||
if (subjectAllVisitList.Any(t => t.SubmitState != SubmitStateEnum.Submitted))
|
||||
{
|
||||
subjectMaxVisitNum = subjectAllVisitList.Where(t => t.SubmitState != SubmitStateEnum.Submitted).MinOrDefault(t => t.VisitNum);
|
||||
}
|
||||
else
|
||||
{
|
||||
//没有未提交的,那么开始绑定的就是已提交的加1
|
||||
subjectMaxVisitNum = subjectAllVisitList.Any() ? subjectAllVisitList.Last().VisitNum + 1 : 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
subjectMaxVisitNum = startBindVisitNum.Value;
|
||||
}
|
||||
|
||||
//var subjectMaxVisitNum = startBindVisitNum == null ? subjectAllVisitList.Where(t => t.SubmitState != SubmitStateEnum.Submitted).MinOrDefault(t => t.VisitNum) : startBindVisitNum.Value;
|
||||
|
||||
|
||||
List<(int VisitCount, Guid SCPStudyId)> visits = new List<(int, Guid)>();
|
||||
|
||||
int visitCount = 0;
|
||||
|
||||
DateTime? lastVisitTime = null;
|
||||
|
||||
foreach (var study in studyList)
|
||||
{
|
||||
if (lastVisitTime == null || (study.StudyTime - lastVisitTime.Value).Value.TotalDays >= 15)
|
||||
{
|
||||
// 当前时间点与上一个访视时间点间隔大于等于 15 天,需要建立一个新的访视
|
||||
visitCount++;
|
||||
|
||||
visits.Add((visitCount, study.SCPStudyId));
|
||||
}
|
||||
else
|
||||
{
|
||||
visits.Add((visitCount, study.SCPStudyId));
|
||||
}
|
||||
|
||||
lastVisitTime = study.StudyTime;
|
||||
}
|
||||
|
||||
//4、生成访视 并且绑定
|
||||
|
||||
for (int i = 0; i < visitCount; i++)
|
||||
{
|
||||
|
||||
var bindSubjectVisitId = Guid.Empty;
|
||||
|
||||
var bindVisitNum = i + subjectMaxVisitNum;
|
||||
|
||||
var existSubjectVisit = subjectAllVisitList.FirstOrDefault(t => t.SubjectId == subjectId && t.VisitNum == bindVisitNum);
|
||||
|
||||
if (existSubjectVisit == null)
|
||||
{
|
||||
bindSubjectVisitId = NewId.NextGuid();
|
||||
|
||||
//基线
|
||||
if (bindVisitNum == 0)
|
||||
{
|
||||
|
||||
await _subjectVisitRepository.AddAsync(new SubjectVisit() { TrialId = trialId, SubjectId = subjectId, VisitName = trialconfig.BlindBaseLineName, VisitNum = bindVisitNum, Id = bindSubjectVisitId, SubmitState = SubmitStateEnum.ToSubmit, IsBaseLine = true });
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
await _subjectVisitRepository.AddAsync(new SubjectVisit() { TrialId = trialId, SubjectId = subjectId, VisitName = trialconfig.BlindFollowUpPrefix + $" {(int)bindVisitNum}", VisitNum = bindVisitNum, Id = bindSubjectVisitId, SubmitState = SubmitStateEnum.ToSubmit });
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
bindSubjectVisitId = existSubjectVisit.SubjectVisitId;
|
||||
}
|
||||
|
||||
|
||||
|
||||
var currentVisitStudyList = visits.Where(t => t.VisitCount == (i + 1)).ToList();
|
||||
|
||||
foreach (var item in currentVisitStudyList)
|
||||
{
|
||||
//访视状态为未提交才绑定
|
||||
if (!subjectAllVisitList.Any(t => t.SubjectId == subjectId && t.SubjectVisitId == bindSubjectVisitId && t.SubmitState == SubmitStateEnum.Submitted))
|
||||
{
|
||||
var find = await _subjectVisitRepository.FindAsync(bindSubjectVisitId);
|
||||
find.SubmitState = SubmitStateEnum.ToSubmit;
|
||||
await _studySubjectVisitRepository.AddAsync(new SCPStudySubjectVisit() { TrialId = trialId, SubjectVisitId = bindSubjectVisitId, SCPStudyId = item.SCPStudyId, SubjectId = subjectId });
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
await _subjectPatientRepository.SaveChangesAsync();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 传输完成后,自动给检查绑定访视
|
||||
/// </summary>
|
||||
/// <param name="inCommand"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost]
|
||||
public async Task<IResponseOutput> AutoBindingPatientStudyVisitAsync(List<Guid> scpStudyIdList)
|
||||
{
|
||||
//一个检查 可能绑定到不同的项目的不同subject 有的该检查已绑定访视,有的该检查绑定了访视
|
||||
|
||||
var query = from scpStudy in _studyRepository.Where(t => scpStudyIdList.Contains(t.Id))
|
||||
join subjectPatient in _subjectPatientRepository.AsQueryable()
|
||||
on scpStudy.PatientId equals subjectPatient.PatientId
|
||||
select new
|
||||
{
|
||||
subjectPatient.Subject.Status,
|
||||
subjectPatient.Subject.TrialId,
|
||||
subjectPatient.SubjectId,
|
||||
subjectPatient.PatientId,
|
||||
SCPStudyId = scpStudy.Id,
|
||||
scpStudy.StudyTime
|
||||
};
|
||||
|
||||
var list = query.ToList();
|
||||
|
||||
if (list.Count > 0)
|
||||
{
|
||||
|
||||
|
||||
var subjectIdList = list.Select(t => t.SubjectId).ToList();
|
||||
|
||||
var allSubjectVisitList = await _subjectVisitRepository.Where(t => subjectIdList.Contains(t.SubjectId))
|
||||
.Select(t => new { t.SubjectId, SubjectVisitId = t.Id, t.SubmitState, VisitNum = t.VisitNum, MaxStudyTime = t.SCPStudySubjectVisitList.Max(t => t.SCPStudy.StudyTime), MinStudyTime = t.SCPStudySubjectVisitList.Min(t => t.SCPStudy.StudyTime) })
|
||||
.ToListAsync();
|
||||
|
||||
foreach (var g in list.GroupBy(t => new { t.SubjectId, t.TrialId, t.Status }))
|
||||
{
|
||||
var subjectId = g.Key.SubjectId;
|
||||
var trialId = g.Key.TrialId;
|
||||
|
||||
//访视结束,那么就不处理
|
||||
if (g.Key.Status == SubjectStatus.EndOfVisit)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// 预先处理1: 数据库可能有已存在的subject 患者绑定,在这里要一起考虑绑定
|
||||
|
||||
var dbPatientIdList = _subjectPatientRepository.Where(t => t.SubjectId == subjectId).Select(t => t.PatientId).ToList();
|
||||
|
||||
// 预先处理2: 删除未提交的所有绑定的检查记录,所有检查一起考虑绑定
|
||||
|
||||
await _studySubjectVisitRepository.BatchDeleteNoTrackingAsync(t => t.SubjectId == subjectId && t.SubjectVisit.SubmitState != SubmitStateEnum.Submitted);
|
||||
|
||||
//预处理3 找到该subjecct 已提交的访视的最大检查时间,绑定的检查时间要比这个时间要大
|
||||
|
||||
var maxStudyTime = allSubjectVisitList.Where(t => t.SubjectId == subjectId && t.SubmitState == SubmitStateEnum.Submitted).MaxOrDefault(t => t.MaxStudyTime);
|
||||
|
||||
|
||||
// 预处理4: 处理需要绑定的检查
|
||||
//获取 该受试者绑定患者已存在的检查,考虑要生成多少个访视,去除已提交的检查
|
||||
|
||||
var studyList = await _studyRepository.Where(t => dbPatientIdList.Contains(t.PatientId)
|
||||
&& !t.SCPStudySubjectVisitList.Any(t => t.SubjectId == subjectId && t.SubjectVisit.SubmitState == SubmitStateEnum.Submitted))
|
||||
.WhereIf(maxStudyTime != null, t => t.StudyTime > maxStudyTime)
|
||||
.Select(t => new AuToBindingStudyInfo { SCPStudyId = t.Id, StudyTime = t.StudyTime }).OrderBy(t => t.StudyTime).ToListAsync();
|
||||
|
||||
await DealAutoBindingStudyAsync(trialId, subjectId, studyList);
|
||||
|
||||
|
||||
}
|
||||
|
||||
await _subjectVisitRepository.SaveChangesAsync();
|
||||
}
|
||||
|
||||
//将检查设置为传输结束
|
||||
await _studyRepository.BatchUpdateNoTrackingAsync(t => scpStudyIdList.Contains(t.Id), u => new SCPStudy() { IsUploadFinished = true });
|
||||
|
||||
return ResponseOutput.Ok();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
"ObjectStoreService": {
|
||||
"ObjectStoreUse": "MinIO",
|
||||
"MinIO": {
|
||||
"EndPoint": "47.102.87.183",
|
||||
"Port": "9000",
|
||||
"UseSSL": false,
|
||||
"AccessKeyId": "fbStsVYCIPKHQneeqMwD",
|
||||
"SecretAccessKey": "TzgvyA3zGXMUnpilJNUlyMYHfosl1hBMl6lxPmjy",
|
||||
"BucketName": "hir-images",
|
||||
"ViewEndpoint": "http://47.102.87.18:9001/hir-images/"
|
||||
}
|
||||
},
|
||||
|
||||
"ConnectionStrings": {
|
||||
"RemoteNew": "Server=47.102.87.183,1434;Database=HIR_Prod;User ID=sa;Password=xc_123456;TrustServerCertificate=true",
|
||||
"Hangfire": "Server=47.102.87.183,1434;Database=HIR_Prod_Hangfire;User ID=sa;Password=xc_123456;TrustServerCertificate=true"
|
||||
},
|
||||
"DicomSCPServiceConfig": {
|
||||
"IsSupportThirdService": true,
|
||||
"IsForwardImageMultiThread": true,
|
||||
"MultiThreadCount": 10,
|
||||
"CalledAEList": [
|
||||
"HIRAE",
|
||||
"STORESCP"
|
||||
],
|
||||
"ServerPort": 11115
|
||||
}
|
||||
}
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
"ObjectStoreService": {
|
||||
"ObjectStoreUse": "AliyunOSS",
|
||||
"AliyunOSS": {
|
||||
"RegionId": "cn-shanghai",
|
||||
"InternalEndpoint": "https://oss-cn-shanghai-internal.aliyuncs.com",
|
||||
"EndPoint": "https://oss-cn-shanghai.aliyuncs.com",
|
||||
"AccessKeyId": "LTAI5tNRTsqL6aWmHkDmTwoH",
|
||||
"AccessKeySecret": "7mtGz3qrYWI6JMMBZiLeC119VWicZH",
|
||||
"RoleArn": "acs:ram::1899121822495495:role/irc-oss-access",
|
||||
"BucketName": "zy-irc-store",
|
||||
"ViewEndpoint": "https://zy-irc-cache.oss-cn-shanghai.aliyuncs.com",
|
||||
"Region": "oss-cn-shanghai",
|
||||
"DurationSeconds": 7200
|
||||
}
|
||||
},
|
||||
|
||||
"ConnectionStrings": {
|
||||
"RemoteNew": "Server=prod_mssql_standard,1433;Database=Prod_IRC;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true",
|
||||
"Hangfire": "Server=prod_mssql_standard,1433;Database=Prod_IRC_Hangfire;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true"
|
||||
},
|
||||
|
||||
"DicomSCPServiceConfig": {
|
||||
"CalledAEList": [
|
||||
"STORESCP"
|
||||
],
|
||||
"ServerPort": 11112
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
"ObjectStoreService": {
|
||||
"ObjectStoreUse": "MinIO",
|
||||
"MinIO": {
|
||||
"EndPoint": "192.168.40.99",
|
||||
"Port": "9000",
|
||||
"UseSSL": false,
|
||||
"AccessKeyId": "Jnywl9aIw83yewZIJKod",
|
||||
"SecretAccessKey": "N83bTzoJGkg4OLW8x54IZRwwSvdxcdYi9UZ2BYII",
|
||||
"BucketName": "tj-hir",
|
||||
"ViewEndpoint": "http://192.168.40.99:9000/tj-hir"
|
||||
}
|
||||
},
|
||||
|
||||
"ConnectionStrings": {
|
||||
"RemoteNew": "Server=192.168.40.98,1433;Database=TJ_Prod_HIR;User ID=sa;Password=xc_123456;TrustServerCertificate=true",
|
||||
"Hangfire": "Server=192.168.40.98,1433;Database=TJ_Prod_HIR_Hangfire;User ID=sa;Password=xc_123456;TrustServerCertificate=true"
|
||||
},
|
||||
"DicomSCPServiceConfig": {
|
||||
"CalledAEList": [
|
||||
"STORESCP",
|
||||
"HIRAE",
|
||||
"Value2",
|
||||
"Value3"
|
||||
],
|
||||
"ServerPort": 11112
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
"ObjectStoreService": {
|
||||
"ObjectStoreUse": "MinIO",
|
||||
"MinIO": {
|
||||
"EndPoint": "106.14.89.110",
|
||||
"Port": "9001",
|
||||
"UseSSL": false,
|
||||
"AccessKeyId": "fbStsVYCIPKHQneeqMwD",
|
||||
"SecretAccessKey": "TzgvyA3zGXMUnpilJNUlyMYHfosl1hBMl6lxPmjy",
|
||||
"BucketName": "hir-test",
|
||||
"ViewEndpoint": "http://106.14.89.110:9001/hir-test/"
|
||||
}
|
||||
},
|
||||
|
||||
"ConnectionStrings": {
|
||||
"RemoteNew": "Server=106.14.89.110,1435;Database=Test_HIR_New;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
||||
"Hangfire": "Server=106.14.89.110,1435;Database=Test_HIR_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
|
||||
},
|
||||
"DicomSCPServiceConfig": {
|
||||
"IsSupportThirdService": true,
|
||||
"IsForwardImageMultiThread": true,
|
||||
"MultiThreadCount":10,
|
||||
"CalledAEList": [
|
||||
"HIRAE",
|
||||
"STORESCP"
|
||||
],
|
||||
"ServerPort": 11115
|
||||
}
|
||||
}
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
"ObjectStoreService": {
|
||||
"ObjectStoreUse": "AliyunOSS",
|
||||
"AliyunOSS": {
|
||||
"RegionId": "cn-shanghai",
|
||||
"InternalEndpoint": "https://oss-cn-shanghai-internal.aliyuncs.com",
|
||||
"EndPoint": "https://oss-cn-shanghai.aliyuncs.com",
|
||||
"AccessKeyId": "LTAI5tRRZehUp2V9pyTPtAJm",
|
||||
"AccessKeySecret": "FLizxkHsMm4CGYHtkV8E3PNJJZU7oV",
|
||||
"RoleArn": "acs:ram::1899121822495495:role/dev-oss-access",
|
||||
"BucketName": "zy-irc-test-store",
|
||||
"ViewEndpoint": "https://zy-irc-test-store.oss-cn-shanghai.aliyuncs.com",
|
||||
"Region": "oss-cn-shanghai",
|
||||
"DurationSeconds": 7200
|
||||
},
|
||||
|
||||
"MinIO": {
|
||||
"endPoint": "106.14.89.110",
|
||||
"port": "9001",
|
||||
"useSSL": false,
|
||||
"accessKey": "fbStsVYCIPKHQneeqMwD",
|
||||
"secretKey": "TzgvyA3zGXMUnpilJNUlyMYHfosl1hBMl6lxPmjy",
|
||||
"bucketName": "hir-test",
|
||||
"viewEndpoint": "http://106.14.89.110:9001/hir-test/"
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
"ConnectionStrings": {
|
||||
"RemoteNew": "Server=106.14.89.110,1435;Database=Test_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
||||
"Hangfire": "Server=106.14.89.110,1435;Database=Test_IRC_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
|
||||
},
|
||||
|
||||
"DicomSCPServiceConfig": {
|
||||
"CalledAEList": [
|
||||
"STORESCP"
|
||||
],
|
||||
"ServerPort": 11112
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
"ObjectStoreService": {
|
||||
"ObjectStoreUse": "MinIO",
|
||||
"MinIO": {
|
||||
"EndPoint": "3.226.182.187",
|
||||
"Port": "9001",
|
||||
"UseSSL": false,
|
||||
"AccessKeyId": "7rvVIHs7D6pbyscRcJhz",
|
||||
"SecretAccessKey": "DQsCQldHFL3QRjlnaLWV7oM4E9PtsO21QPC2h9BD",
|
||||
"BucketName": "hir-us",
|
||||
"ViewEndpoint": "http://hir-minio.uat.elevateimaging.ai/hir-us"
|
||||
}
|
||||
},
|
||||
|
||||
"ConnectionStrings": {
|
||||
"RemoteNew": "Server=3.226.182.187,1435;Database=US_HIR;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
||||
"Hangfire": "Server=3.226.182.187,1435;Database=US_HIR_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
|
||||
},
|
||||
"DicomSCPServiceConfig": {
|
||||
"IsSupportThirdService": true,
|
||||
"IsForwardImageMultiThread": true,
|
||||
"MultiThreadCount": 10,
|
||||
"CalledAEList": [
|
||||
"STORESCP",
|
||||
"HIRAE"
|
||||
],
|
||||
"ServerPort": 11112
|
||||
}
|
||||
}
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
"ObjectStoreService": {
|
||||
"ObjectStoreUse": "AWS",
|
||||
"AWS": {
|
||||
"Region": "us-east-1",
|
||||
"EndPoint": "s3.us-east-1.amazonaws.com",
|
||||
"UseSSL": true,
|
||||
"RoleArn": "arn:aws:iam::471112624751:role/lili_s3_access",
|
||||
"AccessKeyId": "AKIAW3MEAFJXZ2TZK7GM",
|
||||
"SecretAccessKey": "9MLQCQ1HifEVW1gf068zBRAOb4wNnfrOkvBVByth",
|
||||
"BucketName": "ei-med-s3-lili-store",
|
||||
"ViewEndpoint": "https://ei-med-s3-lili-store.s3.amazonaws.com",
|
||||
"DurationSeconds": 7200
|
||||
}
|
||||
},
|
||||
"ConnectionStrings": {
|
||||
"RemoteNew": "Server=us-prod-mssql-service,1433;Database=US_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
||||
"Hangfire": "Server=us-prod-mssql-service,1433;Database=US_IRC_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
|
||||
},
|
||||
"DicomSCPServiceConfig": {
|
||||
"CalledAEList": [
|
||||
"STORESCP"
|
||||
],
|
||||
"ServerPort": 11112
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
"ObjectStoreService": {
|
||||
"ObjectStoreUse": "AWS",
|
||||
"AWS": {
|
||||
"Region": "us-east-1",
|
||||
"EndPoint": "s3.us-east-1.amazonaws.com",
|
||||
"UseSSL": true,
|
||||
"RoleArn": "arn:aws:iam::471112624751:role/uat_s3_access",
|
||||
"AccessKeyId": "AKIAW3MEAFJX7IPXISP4",
|
||||
"SecretAccessKey": "Pgrg3le5jPxZQ7MR1yYNS30J0XRyJeKVyIIjElXc",
|
||||
"BucketName": "ei-med-s3-lili-uat-store",
|
||||
"ViewEndpoint": "https://ei-med-s3-lili-uat-store.s3.amazonaws.com/",
|
||||
"DurationSeconds": 7200
|
||||
}
|
||||
},
|
||||
"ConnectionStrings": {
|
||||
"RemoteNew": "Server=us-mssql-service,1433;Database=US_Uat_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
||||
"Hangfire": "Server=us-mssql-service,1433;Database=US_Uat_IRC_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
|
||||
},
|
||||
"DicomSCPServiceConfig": {
|
||||
"CalledAEList": [
|
||||
"STORESCP"
|
||||
],
|
||||
"ServerPort": 11112
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
"ObjectStoreService": {
|
||||
"ObjectStoreUse": "MinIO",
|
||||
"MinIO": {
|
||||
"EndPoint": "101.132.253.119",
|
||||
"Port": "9001",
|
||||
"UseSSL": false,
|
||||
"AccessKeyId": "ylWQa99fDdVdTfnj47ll",
|
||||
"SecretAccessKey": "kVpy2RIYN0GmyFsU2qAWhbKDf4Nskt23tEqd6sob",
|
||||
"BucketName": "hir-uat",
|
||||
"ViewEndpoint": "http://101.132.253.119:9001/hir-uat/"
|
||||
}
|
||||
},
|
||||
|
||||
"ConnectionStrings": {
|
||||
"RemoteNew": "Server=101.132.253.119,1435;Database=Uat_HIR_New;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
||||
"Hangfire": "Server=101.132.253.119,1435;Database=Uat_HIR_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
|
||||
},
|
||||
"DicomSCPServiceConfig": {
|
||||
"IsSupportThirdService": true,
|
||||
"IsForwardImageMultiThread": true,
|
||||
"MultiThreadCount": 10,
|
||||
"CalledAEList": [
|
||||
"STORESCP",
|
||||
"HIRAE"
|
||||
],
|
||||
"ServerPort": 11112
|
||||
}
|
||||
}
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
"ObjectStoreService": {
|
||||
"ObjectStoreUse": "AliyunOSS",
|
||||
"AliyunOSS": {
|
||||
"RegionId": "cn-shanghai",
|
||||
"InternalEndpoint": "https://oss-cn-shanghai-internal.aliyuncs.com",
|
||||
"EndPoint": "https://oss-cn-shanghai.aliyuncs.com",
|
||||
"AccessKeyId": "LTAI5tRRZehUp2V9pyTPtAJm",
|
||||
"AccessKeySecret": "FLizxkHsMm4CGYHtkV8E3PNJJZU7oV",
|
||||
"RoleArn": "acs:ram::1899121822495495:role/dev-oss-access",
|
||||
"BucketName": "zy-irc-uat-store",
|
||||
"ViewEndpoint": "https://zy-irc-uat-store.oss-cn-shanghai.aliyuncs.com",
|
||||
"Region": "oss-cn-shanghai",
|
||||
"DurationSeconds": 7200
|
||||
}
|
||||
},
|
||||
"ConnectionStrings": {
|
||||
"RemoteNew": "Server=47.117.164.182,1434;Database=Uat_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
||||
"Hangfire": "Server=47.117.164.182,1434;Database=Uat_IRC.Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
|
||||
},
|
||||
"DicomSCPServiceConfig": {
|
||||
"CalledAEList": [
|
||||
"STORESCP"
|
||||
],
|
||||
"ServerPort": 11112
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
using Amazon.Auth.AccessControlPolicy;
|
||||
using AlibabaCloud.SDK.Sts20150401;
|
||||
using Amazon.Auth.AccessControlPolicy;
|
||||
using Amazon.SecurityToken;
|
||||
using Azure.Core;
|
||||
using IdentityModel.Client;
|
||||
|
|
@ -24,6 +25,7 @@ using RestSharp;
|
|||
using RestSharp.Authenticators;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
|
|
@ -100,201 +102,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]
|
||||
[HttpGet, Route("user/getPublicKey")]
|
||||
public IResponseOutput GetPublicKey([FromServices] IOptionsMonitor<IRCEncreptOption> _IRCEncreptOption)
|
||||
|
|
@ -309,22 +116,18 @@ namespace IRaCIS.Api.Controllers
|
|||
[AllowAnonymous]
|
||||
public IResponseOutput ShareImage([FromServices] ITokenService _tokenService)
|
||||
{
|
||||
var token = _tokenService.GetToken(IRaCISClaims.Create(new UserBasicInfo()
|
||||
var token = _tokenService.GetToken(new UserTokenInfo()
|
||||
{
|
||||
Id = Guid.Empty,
|
||||
IsReviewer = false,
|
||||
IsAdmin = false,
|
||||
RealName = "Share001",
|
||||
IdentityUserId = Guid.NewGuid(),
|
||||
UserName = "Share001",
|
||||
Sex = 0,
|
||||
//UserType = "ShareType",
|
||||
UserTypeEnum = UserTypeEnum.ShareImage,
|
||||
Code = "ShareCode001",
|
||||
}));
|
||||
|
||||
});
|
||||
return ResponseOutput.Ok("/showdicom?studyId=f7b67793-8155-0223-2f15-118f2642efb8&type=Share&token=" + token);
|
||||
}
|
||||
|
||||
[HttpGet("user/GetObjectStoreToken")]
|
||||
[AllowAnonymous]
|
||||
public async Task<IResponseOutput> GetObjectStoreTokenAsync([FromServices] IOptionsMonitor<ObjectStoreServiceOptions> options, [FromServices] IOSSService _oSSService)
|
||||
{
|
||||
|
||||
|
|
@ -374,12 +177,58 @@ namespace IRaCIS.Api.Controllers
|
|||
return tempToken;
|
||||
}
|
||||
|
||||
#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;
|
||||
|
||||
|
||||
|
||||
// 返回STS令牌信息给前端
|
||||
var stsToken = new
|
||||
{
|
||||
AccessKeyId = credentials.AccessKeyId,
|
||||
AccessKeySecret = credentials.AccessKeySecret,
|
||||
SecurityToken = credentials.SecurityToken,
|
||||
Expiration = credentials.Expiration,
|
||||
|
||||
Region = ossOptions.Region,
|
||||
BucketName = ossOptions.BucketName,
|
||||
ViewEndpoint = ossOptions.ViewEndpoint,
|
||||
|
||||
};
|
||||
|
||||
return ResponseOutput.Ok(stsToken);
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
[HttpGet("User/UserRedirect")]
|
||||
[AllowAnonymous]
|
||||
public async Task<IActionResult> UserRedirect([FromServices] IRepository<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);
|
||||
|
|
@ -394,10 +243,22 @@ namespace IRaCIS.Api.Controllers
|
|||
|
||||
var errorUrl = domainStrList[0] + "//" + domainStrList[2] + "/error";
|
||||
|
||||
|
||||
if (!await _userRepository.AnyAsync(t => t.Id == Guid.Parse(userId) && t.EmailToken == token && t.IsFirstAdd))
|
||||
if (lang == "zh")
|
||||
{
|
||||
decodeUrl = errorUrl + $"?lang={lang}&ErrorMessage={System.Web.HttpUtility.UrlEncode(lang == "zh" ? "您的初始化链接已过期" : "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);
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ namespace IRaCIS.Core.API.Controllers
|
|||
[ApiController, ApiExplorerSettings(GroupName = "Reviewer")]
|
||||
[UnitOfWork]
|
||||
public class InspectionController(
|
||||
ITrialDocumentService _trialDocumentService,
|
||||
IReadingImageTaskService _iReadingImageTaskService,
|
||||
ITrialConfigService _trialConfigService,
|
||||
IClinicalAnswerService _clinicalAnswerService,
|
||||
|
|
@ -497,21 +496,7 @@ namespace IRaCIS.Core.API.Controllers
|
|||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 用户 签名某个文档
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpPost, Route("Inspection/TrialDocument/userConfirm")]
|
||||
[TrialGlobalLimit( "BeforeOngoingCantOpt", "SignSystemDocNoTrialId", "AfterStopCannNotOpt" )]
|
||||
[UnitOfWork]
|
||||
public async Task<IResponseOutput> UserConfirm(DataInspectionDto<UserConfirmCommand> opt)
|
||||
{
|
||||
var singid = await _inspectionService.RecordSing(opt.SignInfo);
|
||||
opt.Data.SignText = opt.SignInfo.SignText;
|
||||
var result = await _trialDocumentService.UserConfirm(opt.Data);
|
||||
await _inspectionService.CompletedSign(singid, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -370,7 +370,7 @@ namespace IRaCIS.Core.API.Controllers
|
|||
//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;
|
||||
|
||||
|
|
@ -797,116 +797,6 @@ namespace IRaCIS.Core.API.Controllers
|
|||
_userInfo = userInfo;
|
||||
}
|
||||
|
||||
[HttpPost, Route("TrialSiteSurvey/UploadTrialSiteSurveyUser")]
|
||||
[DisableFormValueModelBinding]
|
||||
[UnitOfWork]
|
||||
public async Task<IResponseOutput> UploadTrialSiteSurveyUser(Guid trialId, string baseUrl, string routeUrl,
|
||||
[FromServices] IRepository<TrialSite> _trialSiteRepository,
|
||||
[FromServices] IRepository<UserType> _usertypeRepository,
|
||||
[FromServices] ITrialSiteSurveyService _trialSiteSurveyService,
|
||||
[FromServices] IOSSService oSSService,
|
||||
[FromServices] IRepository<InspectionFile> _inspectionFileRepository)
|
||||
{
|
||||
var templateFileStream = new MemoryStream();
|
||||
|
||||
|
||||
await FileUploadToOSSAsync(async (realFileName, fileStream) =>
|
||||
{
|
||||
await fileStream.CopyToAsync(templateFileStream);
|
||||
templateFileStream.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
if (!realFileName.EndsWith(".xlsx", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// 请用提供格式的模板excel上传需要处理的数据
|
||||
throw new BusinessValidationFailedException(_localizer["UploadDownLoad_TemplateUploadData"]);
|
||||
}
|
||||
|
||||
var ossRelativePath = await oSSService.UploadToOSSAsync(fileStream, "InspectionUpload/SiteSurvey", realFileName);
|
||||
|
||||
await _inspectionFileRepository.AddAsync(new InspectionFile() { FileName = realFileName, RelativePath = ossRelativePath, TrialId = trialId });
|
||||
|
||||
|
||||
|
||||
return ossRelativePath;
|
||||
|
||||
});
|
||||
|
||||
//去掉空白行
|
||||
var excelList = MiniExcel.Query<SiteSurveyUserImportDto>(templateFileStream, excelType: ExcelType.XLSX).ToList()
|
||||
.Where(t => !(string.IsNullOrWhiteSpace(t.TrialSiteCode) && string.IsNullOrWhiteSpace(t.FirstName) && string.IsNullOrWhiteSpace(t.LastName) && string.IsNullOrWhiteSpace(t.Email)
|
||||
&& string.IsNullOrWhiteSpace(t.Phone) && string.IsNullOrWhiteSpace(t.UserTypeStr) && string.IsNullOrWhiteSpace(t.OrganizationName))).ToList();
|
||||
|
||||
if (excelList.Any(t => string.IsNullOrWhiteSpace(t.TrialSiteCode) || string.IsNullOrWhiteSpace(t.FirstName) || string.IsNullOrWhiteSpace(t.LastName) || string.IsNullOrWhiteSpace(t.Email) || string.IsNullOrWhiteSpace(t.UserTypeStr)))
|
||||
{
|
||||
//请确保Excel中 每一行的 中心编号,姓名,邮箱,用户类型数据记录完整再进行上传
|
||||
throw new BusinessValidationFailedException(_localizer["UploadDownLoad_EnsureCompleteData"]);
|
||||
}
|
||||
|
||||
var siteCodeList = excelList.Select(t => t.TrialSiteCode.Trim().ToUpper()).Distinct().ToList();
|
||||
|
||||
if (_trialSiteRepository.Where(t => t.TrialId == trialId && siteCodeList.Contains(t.TrialSiteCode.ToUpper())).Count() != siteCodeList.Count)
|
||||
{
|
||||
//在项目中未找到该Excel中部分或全部中心
|
||||
throw new BusinessValidationFailedException(_localizer["UploadDownLoad_InvalidCenters"]);
|
||||
}
|
||||
|
||||
if (excelList.GroupBy(t => new { t.TrialSiteCode, t.UserTypeStr, t.Email }).Any(g => g.Count() > 1))
|
||||
{
|
||||
// 同一邮箱,同一用户类型,只能生成一个账户,请核查Excel数据
|
||||
|
||||
throw new BusinessValidationFailedException(_localizer["UploadDownLoad_CheckDuplicateAccounts"]);
|
||||
}
|
||||
|
||||
if (excelList.Any(t => !t.Email.Contains("@")))
|
||||
{
|
||||
//有邮箱不符合邮箱格式,请核查Excel数据
|
||||
throw new BusinessValidationFailedException(_localizer["UploadDownLoad_InvalidEmail"]);
|
||||
}
|
||||
var generateUserTypeList = new List<string>() { "CRC", "CRA" };
|
||||
|
||||
//if (excelList.Any(t => !generateUserTypeList.Contains(t.UserTypeStr.ToUpper())))
|
||||
//{
|
||||
// //用户类型仅能为 CRC,SR,CRA 请核查Excel数据
|
||||
// throw new BusinessValidationFailedException(_localizer["UploadDownLoad_InvalidUserType"]);
|
||||
//}
|
||||
if (excelList.Count == 0)
|
||||
{
|
||||
throw new BusinessValidationFailedException(_localizer["UploadDownLoad_NoValiddata"]);
|
||||
}
|
||||
//处理好 用户类型 和用户类型枚举
|
||||
var sysUserTypeList = _usertypeRepository.Where(t => t.UserTypeEnum == UserTypeEnum.CRA || t.UserTypeEnum == UserTypeEnum.ClinicalResearchCoordinator).Select(t => new { UserTypeId = t.Id, t.UserTypeEnum }).ToList();
|
||||
var siteList = _trialSiteRepository.Where(t => t.TrialId == trialId && siteCodeList.Contains(t.TrialSiteCode)).Select(t => new { t.TrialSiteCode, TrialSiteId = t.Id }).ToList();
|
||||
|
||||
foreach (var item in excelList)
|
||||
{
|
||||
switch (item.UserTypeStr.ToUpper())
|
||||
{
|
||||
case "CRC":
|
||||
|
||||
item.UserTypeId = sysUserTypeList.FirstOrDefault(t => t.UserTypeEnum == UserTypeEnum.ClinicalResearchCoordinator).UserTypeId;
|
||||
item.UserTypeEnum = UserTypeEnum.ClinicalResearchCoordinator;
|
||||
break;
|
||||
|
||||
case "CRA":
|
||||
item.UserTypeId = sysUserTypeList.FirstOrDefault(t => t.UserTypeEnum == UserTypeEnum.CRA).UserTypeId;
|
||||
item.UserTypeEnum = UserTypeEnum.CRA;
|
||||
break;
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
item.TrialSiteId = siteList.FirstOrDefault(t => t.TrialSiteCode.ToUpper() == item.TrialSiteCode.ToUpper()).TrialSiteId;
|
||||
}
|
||||
|
||||
var list = excelList.Where(t => t.UserTypeEnum == UserTypeEnum.ClinicalResearchCoordinator || t.UserTypeEnum == UserTypeEnum.CRA).ToList();
|
||||
|
||||
await _trialSiteSurveyService.ImportGenerateAccountAndJoinTrialAsync(trialId, baseUrl, routeUrl, list);
|
||||
|
||||
return ResponseOutput.Ok();
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// <summary> 通用文件下载 </summary>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,363 @@
|
|||
using AutoMapper.Execution;
|
||||
using DocumentFormat.OpenXml.Bibliography;
|
||||
using FellowOakDicom;
|
||||
using FellowOakDicom.Network;
|
||||
using FellowOakDicom.Network.Client;
|
||||
using IRaCIS.Core.Application.Contracts;
|
||||
using IRaCIS.Core.Domain.Models;
|
||||
using IRaCIS.Core.Infra.EFCore;
|
||||
using IRaCIS.Core.Infrastructure.Extention;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Org.BouncyCastle.Bcpg;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Channels;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace IRaCIS.Core.API.HostService
|
||||
{
|
||||
|
||||
public class DicomSCPService : DicomService, IDicomServiceProvider, IDicomCFindProvider, IDicomCEchoProvider, IDicomCMoveProvider
|
||||
{
|
||||
|
||||
private static readonly DicomTransferSyntax[] _acceptedTransferSyntaxes = new DicomTransferSyntax[]
|
||||
{
|
||||
DicomTransferSyntax.ExplicitVRLittleEndian,
|
||||
DicomTransferSyntax.ExplicitVRBigEndian,
|
||||
DicomTransferSyntax.ImplicitVRLittleEndian
|
||||
};
|
||||
|
||||
|
||||
private static readonly DicomTransferSyntax[] _acceptedImageTransferSyntaxes = new DicomTransferSyntax[]
|
||||
{
|
||||
// Lossless
|
||||
DicomTransferSyntax.JPEGLSLossless,
|
||||
DicomTransferSyntax.JPEG2000Lossless,
|
||||
DicomTransferSyntax.JPEGProcess14SV1,
|
||||
DicomTransferSyntax.JPEGProcess14,
|
||||
DicomTransferSyntax.RLELossless,
|
||||
|
||||
// Lossy
|
||||
DicomTransferSyntax.JPEGLSNearLossless,
|
||||
DicomTransferSyntax.JPEG2000Lossy,
|
||||
DicomTransferSyntax.JPEGProcess1,
|
||||
DicomTransferSyntax.JPEGProcess2_4,
|
||||
|
||||
// Uncompressed
|
||||
DicomTransferSyntax.ExplicitVRLittleEndian,
|
||||
DicomTransferSyntax.ExplicitVRBigEndian,
|
||||
DicomTransferSyntax.ImplicitVRLittleEndian
|
||||
};
|
||||
|
||||
|
||||
|
||||
private IServiceProvider _serviceProvider { get; set; }
|
||||
|
||||
private DicomSCPServiceOption DicomSCPServiceConfig { get; set; }
|
||||
|
||||
|
||||
public string CallingAE { get; protected set; }
|
||||
public string CalledAE { get; protected set; }
|
||||
|
||||
public DicomSCPService(INetworkStream stream, Encoding fallbackEncoding, Microsoft.Extensions.Logging.ILogger log, DicomServiceDependencies dependencies, IServiceProvider injectServiceProvider)
|
||||
: base(stream, fallbackEncoding, log, dependencies)
|
||||
{
|
||||
_serviceProvider = injectServiceProvider.CreateScope().ServiceProvider;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void OnConnectionClosed(Exception exception)
|
||||
{
|
||||
if (exception != null)
|
||||
{
|
||||
Logger.LogError($"Closed, exception is {exception.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
public void OnReceiveAbort(DicomAbortSource source, DicomAbortReason reason)
|
||||
{
|
||||
Logger.LogError($"Received abort from {source}, reason is {reason}");
|
||||
}
|
||||
|
||||
public Task OnReceiveAssociationReleaseRequestAsync()
|
||||
{
|
||||
return SendAssociationReleaseResponseAsync();
|
||||
}
|
||||
|
||||
public Task OnReceiveAssociationRequestAsync(DicomAssociation association)
|
||||
{
|
||||
CallingAE = association.CallingAE;
|
||||
CalledAE = association.CalledAE;
|
||||
|
||||
Logger.LogInformation($"Received association request from AE: {CallingAE} with IP: {association.RemoteHost} ");
|
||||
|
||||
DicomSCPServiceConfig = _serviceProvider.GetService<IOptionsMonitor<DicomSCPServiceOption>>().CurrentValue;
|
||||
var calledAEList = DicomSCPServiceConfig.CalledAEList;
|
||||
|
||||
//不支持三方服务 或者CallAE不对,那么拒绝连接
|
||||
if (!calledAEList.Contains(CalledAE) || DicomSCPServiceConfig.IsSupportThirdService == false || CallingAE != DicomSCPServiceConfig.ThirdCallningAE)
|
||||
{
|
||||
Logger.LogError($"Association with {CallingAE} rejected since called aet {CalledAE} is unknown");
|
||||
return SendAssociationRejectAsync(DicomRejectResult.Permanent, DicomRejectSource.ServiceUser, DicomRejectReason.CalledAENotRecognized);
|
||||
}
|
||||
|
||||
foreach (var pc in association.PresentationContexts)
|
||||
{
|
||||
if (pc.AbstractSyntax == DicomUID.Verification
|
||||
|| pc.AbstractSyntax == DicomUID.PatientRootQueryRetrieveInformationModelFind
|
||||
|| pc.AbstractSyntax == DicomUID.PatientRootQueryRetrieveInformationModelMove
|
||||
|| pc.AbstractSyntax == DicomUID.StudyRootQueryRetrieveInformationModelFind
|
||||
|| pc.AbstractSyntax == DicomUID.StudyRootQueryRetrieveInformationModelMove)
|
||||
{
|
||||
pc.AcceptTransferSyntaxes(_acceptedTransferSyntaxes);
|
||||
}
|
||||
else if (pc.AbstractSyntax == DicomUID.PatientRootQueryRetrieveInformationModelGet
|
||||
|| pc.AbstractSyntax == DicomUID.StudyRootQueryRetrieveInformationModelGet)
|
||||
{
|
||||
pc.AcceptTransferSyntaxes(_acceptedImageTransferSyntaxes);
|
||||
}
|
||||
else if (pc.AbstractSyntax.StorageCategory != DicomStorageCategory.None)
|
||||
{
|
||||
pc.AcceptTransferSyntaxes(_acceptedImageTransferSyntaxes);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.LogWarning($"Requested abstract syntax {pc.AbstractSyntax} from {CallingAE} not supported");
|
||||
pc.SetResult(DicomPresentationContextResult.RejectAbstractSyntaxNotSupported);
|
||||
}
|
||||
}
|
||||
|
||||
Logger.LogInformation($"Accepted association request from {CallingAE}");
|
||||
return SendAssociationAcceptAsync(association);
|
||||
}
|
||||
|
||||
public Task<DicomCEchoResponse> OnCEchoRequestAsync(DicomCEchoRequest request)
|
||||
{
|
||||
Logger.LogInformation("Received verification request from AE {0} with IP: {1}", CallingAE, Association.RemoteHost);
|
||||
return Task.FromResult(new DicomCEchoResponse(request, DicomStatus.Success));
|
||||
}
|
||||
public async IAsyncEnumerable<DicomCFindResponse> OnCFindRequestAsync(DicomCFindRequest request)
|
||||
{
|
||||
Console.WriteLine("Received C-FIND request, forwarding to real PACS...");
|
||||
|
||||
var cts = new CancellationTokenSource();
|
||||
|
||||
var _dicomAERepository = _serviceProvider.GetService<IRepository<DicomAE>>();
|
||||
|
||||
var find = await _dicomAERepository.FirstOrDefaultAsync(t => t.PacsTypeEnum == PacsType.PacsServer && t.CalledAE == DicomSCPServiceConfig.ThirdSearchPacsAE);
|
||||
|
||||
var hirClient = await _dicomAERepository.FirstOrDefaultAsync(t => t.PacsTypeEnum == PacsType.HIRClient);
|
||||
|
||||
|
||||
if (find == null || hirClient == null)
|
||||
{
|
||||
Logger.LogInformation("客户端和Pacs配置未查询到");
|
||||
|
||||
yield return new DicomCFindResponse(request, DicomStatus.ProcessingFailure);
|
||||
yield break;
|
||||
}
|
||||
|
||||
string patientID = request.Dataset.GetSingleValueOrDefault(DicomTag.PatientID, string.Empty);
|
||||
string patientName = request.Dataset.GetSingleValueOrDefault(DicomTag.PatientName, string.Empty);
|
||||
string studyDate = request.Dataset.GetSingleValueOrDefault(DicomTag.StudyDate, string.Empty);
|
||||
string studyInstanceUID = request.Dataset.GetSingleValueOrDefault(DicomTag.StudyInstanceUID, string.Empty);
|
||||
string accessionNumber = request.Dataset.GetSingleValueOrDefault(DicomTag.AccessionNumber, string.Empty);
|
||||
|
||||
|
||||
if (patientID.IsNullOrEmpty() && patientName.IsNullOrEmpty() && studyInstanceUID.IsNullOrEmpty() && studyDate.IsNullOrEmpty() && accessionNumber.IsNullOrEmpty())
|
||||
{
|
||||
yield return new DicomCFindResponse(request, DicomStatus.MissingAttribute);
|
||||
yield break;
|
||||
}
|
||||
|
||||
|
||||
// 创建 channel 用于异步传递响应
|
||||
var channel = Channel.CreateUnbounded<DicomCFindResponse>();
|
||||
|
||||
// 克隆 dataset 避免线程/状态冲突
|
||||
var clonedDataset = request.Dataset?.Clone() ?? new DicomDataset();
|
||||
var forward = new DicomCFindRequest(request.SOPClassUID, request.Level)
|
||||
{
|
||||
Dataset = clonedDataset
|
||||
};
|
||||
|
||||
var receivedCount = 0;
|
||||
|
||||
// 标记是否已收到 final 状态(Success/Failure/Cancel)
|
||||
var finalReceived = false;
|
||||
|
||||
// 当远端 PACS 返回响应时,异步写入 channel
|
||||
forward.OnResponseReceived += (rq, rp) =>
|
||||
{
|
||||
|
||||
#region 取消,现在不行
|
||||
|
||||
////100条的时候直接取消
|
||||
//if (receivedCount >= 10)
|
||||
//{
|
||||
// rp.Status = DicomStatus.Cancel;
|
||||
|
||||
// cts.Cancel(); // 触发取消
|
||||
|
||||
// Logger.LogWarning("超过100条,剩余的取消!");
|
||||
//}
|
||||
|
||||
//receivedCount++;
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
var dsCopy = rp.Dataset?.Clone();
|
||||
var proxyResp = new DicomCFindResponse(request, rp.Status)
|
||||
{
|
||||
Dataset = dsCopy
|
||||
};
|
||||
channel.Writer.TryWrite(proxyResp);
|
||||
|
||||
if (!rp.Status.Equals(DicomStatus.Pending))
|
||||
{
|
||||
finalReceived = true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// 异步发送到真实 PACS
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var client = DicomClientFactory.Create(find.IP, find.Port, false, hirClient.CalledAE, find.CalledAE);
|
||||
await client.AddRequestAsync(forward);
|
||||
await client.SendAsync(cancellationToken: cts.Token);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine("Error forwarding C-FIND: " + ex.Message);
|
||||
}
|
||||
finally
|
||||
{
|
||||
channel.Writer.Complete();
|
||||
}
|
||||
});
|
||||
|
||||
// 异步 yield 返回给上游
|
||||
await foreach (var resp in channel.Reader.ReadAllAsync())
|
||||
{
|
||||
yield return resp;
|
||||
}
|
||||
|
||||
// 兜底:如果没有 final 响应,返回 Success
|
||||
if (!finalReceived)
|
||||
{
|
||||
yield return new DicomCFindResponse(request, DicomStatus.Success);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public async IAsyncEnumerable<DicomCMoveResponse> OnCMoveRequestAsync(DicomCMoveRequest request)
|
||||
{
|
||||
Console.WriteLine("Received C-Move request, forwarding to real PACS...");
|
||||
|
||||
var _dicomAERepository = _serviceProvider.GetService<IRepository<DicomAE>>();
|
||||
var _cmoveStudyRepository = _serviceProvider.GetService<IRepository<CmoveStudy>>();
|
||||
|
||||
var find = await _dicomAERepository.FirstOrDefaultAsync(t => t.PacsTypeEnum == PacsType.PacsServer && t.CalledAE == DicomSCPServiceConfig.ThirdSearchPacsAE);
|
||||
|
||||
|
||||
var hirServer = await _dicomAERepository.FirstOrDefaultAsync(t => t.PacsTypeEnum == PacsType.HIRServer);
|
||||
|
||||
var hirClient = await _dicomAERepository.FirstOrDefaultAsync(t => t.PacsTypeEnum == PacsType.HIRClient);
|
||||
|
||||
|
||||
if (find == null || hirClient == null || hirServer == null)
|
||||
{
|
||||
Logger.LogInformation("客户端和Pacs配置未查询到");
|
||||
}
|
||||
|
||||
var studyInstanceUid = request.Dataset?.GetSingleValueOrDefault(DicomTag.StudyInstanceUID, string.Empty);
|
||||
|
||||
if (studyInstanceUid.IsNotNullOrEmpty())
|
||||
{
|
||||
await _cmoveStudyRepository.AddAsync(new CmoveStudy() { CallingAE = CallingAE, CalledAE = CalledAE, DestinationAE = request.DestinationAE, StudyInstanceUIDList = new List<string>() { studyInstanceUid }, HopitalGroupIdList = new List<Guid>() }, true);
|
||||
|
||||
}
|
||||
|
||||
|
||||
var channel = Channel.CreateUnbounded<DicomCMoveResponse>();
|
||||
var clonedDataset = request.Dataset?.Clone() ?? new DicomDataset();
|
||||
|
||||
var forward = new DicomCMoveRequest(hirServer.CalledAE, studyInstanceUid)
|
||||
{
|
||||
Dataset = clonedDataset
|
||||
};
|
||||
|
||||
bool finalReceived = false;
|
||||
|
||||
// PACS 返回响应时写入 channel
|
||||
forward.OnResponseReceived += (rq, rp) =>
|
||||
{
|
||||
var dsCopy = rp.Dataset?.Clone();
|
||||
var proxyResp = new DicomCMoveResponse(request, rp.Status)
|
||||
{
|
||||
Dataset = dsCopy,
|
||||
Remaining = rp.Remaining,
|
||||
Completed = rp.Completed,
|
||||
Failures = rp.Failures,
|
||||
Warnings = rp.Warnings,
|
||||
|
||||
};
|
||||
|
||||
|
||||
Logger.LogInformation($"Completed:{rp.Completed}");
|
||||
|
||||
|
||||
channel.Writer.TryWrite(proxyResp);
|
||||
|
||||
if (!rp.Status.Equals(DicomStatus.Pending))
|
||||
{
|
||||
finalReceived = true;
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
// 异步发送到真实 PACS
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var client = DicomClientFactory.Create(find.IP, find.Port, false, hirClient.CalledAE, find.CalledAE);
|
||||
await client.AddRequestAsync(forward);
|
||||
await client.SendAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine("Error forwarding C-MOVE: " + ex.Message);
|
||||
}
|
||||
finally
|
||||
{
|
||||
channel.Writer.Complete();
|
||||
}
|
||||
});
|
||||
|
||||
// 异步 yield 回上游
|
||||
await foreach (var resp in channel.Reader.ReadAllAsync())
|
||||
{
|
||||
yield return resp;
|
||||
}
|
||||
|
||||
// 兜底
|
||||
if (!finalReceived)
|
||||
{
|
||||
yield return new DicomCMoveResponse(request, DicomStatus.Success);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -69,24 +69,22 @@
|
|||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AspNetCoreRateLimit" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.19" />
|
||||
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.10">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="ConfigMapFileProvider" Version="2.0.1" />
|
||||
<PackageReference Include="Hangfire.AspNetCore" Version="1.8.14" />
|
||||
<PackageReference Include="Hangfire.AspNetCore" Version="1.8.20" />
|
||||
<PackageReference Include="Hangfire.Dashboard.BasicAuthorization" Version="1.0.2" />
|
||||
<PackageReference Include="Hangfire.InMemory" Version="1.0.0" />
|
||||
<PackageReference Include="Hangfire.SqlServer" Version="1.8.14" />
|
||||
<PackageReference Include="Invio.Extensions.Authentication.JwtBearer" Version="2.0.1" />
|
||||
<PackageReference Include="LogDashboard" Version="1.4.8" />
|
||||
<PackageReference Include="Hangfire.SqlServer" Version="1.8.20" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.10" />
|
||||
<PackageReference Include="Serilog.AspNetCore" Version="8.0.3" />
|
||||
<PackageReference Include="Serilog.Formatting.Compact" Version="3.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.Email" Version="4.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="6.9.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="9.0.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
@ -112,6 +110,60 @@
|
|||
<Content Update="Resources\zh-CN.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="wwwroot\ReadReoprtTemplate\downLoad\file.txt">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="wwwroot\ReadReoprtTemplate\ReportTemplate_IRECIST_CN_V1.docx">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="wwwroot\ReadReoprtTemplate\ReportTemplate_PCWG3_CN_V1.docx">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="wwwroot\ReadReoprtTemplate\ReportTemplate_RECIST1.1_CN_V1.docx">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="wwwroot\ReadReoprtTemplate\TumorEvaluation_IRECIST1.1_CN_V1.docx">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="wwwroot\ReadReoprtTemplate\TumorEvaluation_IRECIST1.1_EN_V1.docx">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="wwwroot\ReadReoprtTemplate\TumorEvaluation_RECIST1.1_CN_V1.docx">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="wwwroot\ReadReoprtTemplate\医院模板\协和\ReportTemplate_IRECIST_CN_V1.docx">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="wwwroot\ReadReoprtTemplate\医院模板\协和\ReportTemplate_PCWG3_CN_V1.docx">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="wwwroot\ReadReoprtTemplate\医院模板\协和\ReportTemplate_RECIST1.1_CN_V1.docx">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="wwwroot\ReadReoprtTemplate\医院模板\同济\ReportTemplate_IRECIST_CN_V1.docx">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="wwwroot\ReadReoprtTemplate\医院模板\同济\ReportTemplate_PCWG3_CN_V1.docx">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="wwwroot\ReadReoprtTemplate\医院模板\同济\ReportTemplate_RECIST1.1_CN_V1.docx">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="wwwroot\ReadReoprtTemplate\医院模板\复旦大学肿瘤附属医院\ReportTemplate_IRECIST_CN_V1.docx">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="wwwroot\ReadReoprtTemplate\医院模板\复旦大学肿瘤附属医院\ReportTemplate_PCWG3_CN_V1.docx">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="wwwroot\ReadReoprtTemplate\医院模板\复旦大学肿瘤附属医院\ReportTemplate_RECIST1.1_CN_V1.docx">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="Resources\GeoLite2-City.mmdb">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<ProjectExtensions>
|
||||
|
|
|
|||
|
|
@ -34,9 +34,6 @@
|
|||
<param name="doctorId"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Api.Controllers.ExtraController.Login(IRaCIS.Application.Contracts.UserLoginDTO,ZiggyCreatures.Caching.Fusion.IFusionCache,IRaCIS.Core.Application.Service.IUserService,IRaCIS.Core.Application.Auth.ITokenService,IRaCIS.Core.Application.Contracts.IReadingImageTaskService,Microsoft.Extensions.Options.IOptionsMonitor{IRaCIS.Core.Domain.Share.ServiceVerifyConfigOption},Microsoft.Extensions.Options.IOptionsMonitor{IRaCIS.Core.Domain.Share.SystemEmailSendConfig},IRaCIS.Core.Application.Service.IMailVerificationService)">
|
||||
<summary> 系统用户登录接口[New] </summary>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Api.Controllers.ExtraController.OAuthCallBack(System.String,System.String)">
|
||||
<summary>
|
||||
回调到前端,前端调用后端的接口
|
||||
|
|
@ -278,12 +275,6 @@
|
|||
<param name="opt"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Core.API.Controllers.InspectionController.UserConfirm(IRaCIS.Core.Application.Service.Inspection.DTO.DataInspectionDto{IRaCIS.Core.Application.Contracts.UserConfirmCommand})">
|
||||
<summary>
|
||||
用户 签名某个文档
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</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)">
|
||||
<summary>
|
||||
重阅同意
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
using IRaCIS.Core.API;
|
||||
using FellowOakDicom.Network;
|
||||
using FellowOakDicom;
|
||||
using IRaCIS.Core.API;
|
||||
using IRaCIS.Core.API.HostService;
|
||||
using IRaCIS.Core.Application.BusinessFilter;
|
||||
using IRaCIS.Core.Application.Filter;
|
||||
|
|
@ -7,7 +9,6 @@ using IRaCIS.Core.Application.Service;
|
|||
using IRaCIS.Core.Application.Service.BusinessFilter;
|
||||
using IRaCIS.Core.Infra.EFCore;
|
||||
using IRaCIS.Core.Infrastructure.Extention;
|
||||
using LogDashboard;
|
||||
using MassTransit;
|
||||
using MassTransit.NewIdProviders;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
|
|
@ -25,6 +26,9 @@ using System.Collections.Generic;
|
|||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using FellowOakDicom.Imaging.NativeCodec;
|
||||
using FellowOakDicom.Imaging;
|
||||
using IRaCIS.Core.Application.Contracts;
|
||||
|
||||
AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);
|
||||
AppContext.SetSwitch("Npgsql.DisableDateTimeInfinityConversions", true);
|
||||
|
|
@ -100,6 +104,8 @@ builder.Services.AddControllers(options =>
|
|||
})
|
||||
.AddNewtonsoftJsonSetup(builder.Services); // NewtonsoftJson 序列化 处理
|
||||
|
||||
builder.Services.AddOptions().Configure<DicomSCPServiceOption>(_configuration.GetSection("DicomSCPServiceConfig"));
|
||||
|
||||
// Panda动态WebApi + UnifiedApiResultFilter + 省掉控制器代码
|
||||
builder.Services.AddDynamicWebApiSetup();
|
||||
//MinimalAPI
|
||||
|
|
@ -125,11 +131,17 @@ builder.Services.AddFusionCache();
|
|||
// hangfire 定时任务框架 有界面,更友好~
|
||||
builder.Services.AddhangfireSetup(_configuration);
|
||||
|
||||
//Serilog 日志可视化 LogDashboard日志
|
||||
builder.Services.AddLogDashboardSetup();
|
||||
|
||||
//Dicom影像渲染图片 跨平台
|
||||
builder.Services.AddDicomSetup();
|
||||
|
||||
builder.Services.AddFellowOakDicom().AddTranscoderManager<NativeTranscoderManager>()
|
||||
//.AddTranscoderManager<FellowOakDicom.Imaging.NativeCodec.NativeTranscoderManager>()
|
||||
.AddImageManager<ImageSharpImageManager>();
|
||||
//builder.Services.AddDicomSetup();
|
||||
|
||||
new DicomSetupBuilder()
|
||||
.SkipValidation()
|
||||
.Build();
|
||||
|
||||
// 实时应用
|
||||
builder.Services.AddSignalR();
|
||||
|
|
@ -144,6 +156,7 @@ builder.Services.AddSignalR();
|
|||
|
||||
//builder.Services.AddAntiforgery();
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
var app = builder.Build();
|
||||
|
|
@ -210,8 +223,6 @@ app.UseResponseCompression();
|
|||
//不需要 token 访问的静态文件 wwwroot css, JavaScript, and images don't require authentication.
|
||||
app.UseStaticFiles();
|
||||
|
||||
//LogDashboard
|
||||
app.UseLogDashboard("/LogDashboard");
|
||||
|
||||
//hangfire
|
||||
app.UseHangfireConfig(env);
|
||||
|
|
@ -277,6 +288,13 @@ try
|
|||
//Log.Logger.Warning($"ContentRootPath——xx:{Path.GetDirectoryName(Path.GetDirectoryName(env.ContentRootPath))}");
|
||||
#endregion
|
||||
|
||||
|
||||
DicomSetupBuilder.UseServiceProvider(app.Services);
|
||||
|
||||
var logger = app.Services.GetService<Microsoft.Extensions.Logging.ILogger<Program>>();
|
||||
|
||||
var server = DicomServerFactory.Create<DicomSCPService>(_configuration.GetSection("DicomSCPServiceConfig").GetValue<int>("ServerPort"), userState: app.Services, logger: logger);
|
||||
|
||||
app.Run();
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -16,60 +16,39 @@
|
|||
"ASPNETCORE_ENVIRONMENT": "Test_IRC"
|
||||
}
|
||||
},
|
||||
"IRaCIS.Test_IRC": {
|
||||
"Uat_HIR": {
|
||||
"commandName": "Project",
|
||||
"launchBrowser": true,
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Test_IRC"
|
||||
"ASPNETCORE_ENVIRONMENT": "Uat_HIR"
|
||||
},
|
||||
"applicationUrl": "http://localhost:6100"
|
||||
},
|
||||
"IRaCIS.Test_IRC_PGSQL": {
|
||||
"Prod_HIR": {
|
||||
"commandName": "Project",
|
||||
"launchBrowser": true,
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Test_IRC_PGSQL"
|
||||
"ASPNETCORE_ENVIRONMENT": "Prod_HIR"
|
||||
},
|
||||
"applicationUrl": "http://localhost:6100"
|
||||
},
|
||||
"IRaCIS.Event_IRC": {
|
||||
"US_HIR": {
|
||||
"commandName": "Project",
|
||||
"launchBrowser": true,
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Event_IRC"
|
||||
"ASPNETCORE_ENVIRONMENT": "US_HIR"
|
||||
},
|
||||
"applicationUrl": "http://localhost:6100"
|
||||
},
|
||||
"Docker": {
|
||||
"commandName": "Docker",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}",
|
||||
"publishAllPorts": true
|
||||
},
|
||||
"IRaCIS.Uat_IRC": {
|
||||
"Test_HIR": {
|
||||
"commandName": "Project",
|
||||
"launchBrowser": true,
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Uat_IRC"
|
||||
},
|
||||
"applicationUrl": "http://localhost:6100"
|
||||
},
|
||||
"IRaCIS.Prod_IRC": {
|
||||
"commandName": "Project",
|
||||
"launchBrowser": true,
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Prod_IRC"
|
||||
},
|
||||
"applicationUrl": "http://localhost:6100"
|
||||
},
|
||||
"IRaCIS.US_Uat_IRC": {
|
||||
"commandName": "Project",
|
||||
"launchBrowser": true,
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "US_Uat_IRC"
|
||||
"ASPNETCORE_ENVIRONMENT": "Test_HIR"
|
||||
},
|
||||
"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)
|
||||
{
|
||||
return connection.User?.FindFirst(JwtIRaCISClaimType.Id)?.Value!;
|
||||
return connection.User?.FindFirst(JwtIRaCISClaimType.IdentityUserId)?.Value!;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,15 +0,0 @@
|
|||
using LogDashboard;
|
||||
using LogDashboard.Authorization;
|
||||
|
||||
namespace IRaCIS.Core.API.Filter
|
||||
{
|
||||
|
||||
public class LogDashBoardAuthFilter : ILogDashboardAuthorizationFilter
|
||||
{
|
||||
//在此可以利用 本系统的UerTypeEnum 判断
|
||||
public bool Authorization(LogDashboardContext context)
|
||||
{
|
||||
return context.HttpContext.User.Identity.IsAuthenticated;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -21,6 +21,7 @@ namespace IRaCIS.Core.API
|
|||
=>
|
||||
{
|
||||
|
||||
|
||||
opts.MessageTemplate = "{FullName} {UserType} {UserIp} {Host} {RequestMethod} {RequestPath} {RequestBody} responded {StatusCode} in {Elapsed:0.0000} ms";
|
||||
|
||||
opts.EnrichDiagnosticContext = (diagnosticContext, httpContext) =>
|
||||
|
|
@ -38,7 +39,7 @@ namespace IRaCIS.Core.API
|
|||
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);
|
||||
|
||||
|
|
|
|||
|
|
@ -46,6 +46,8 @@ namespace IRaCIS.Core.API
|
|||
OnMessageReceived = (context) =>
|
||||
{
|
||||
|
||||
|
||||
|
||||
if (context.Request.Query.TryGetValue("access_token", out StringValues values))
|
||||
{
|
||||
var queryToken = values.FirstOrDefault();
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using FellowOakDicom;
|
||||
using FellowOakDicom.Imaging;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace IRaCIS.Core.API
|
||||
{
|
||||
|
|
@ -9,12 +10,14 @@ namespace IRaCIS.Core.API
|
|||
public static void AddDicomSetup(this IServiceCollection services)
|
||||
{
|
||||
new DicomSetupBuilder()
|
||||
.RegisterServices(s => s.AddFellowOakDicom()
|
||||
.AddTranscoderManager<FellowOakDicom.Imaging.NativeCodec.NativeTranscoderManager>()
|
||||
.AddImageManager<ImageSharpImageManager>()
|
||||
.RegisterServices(s => s.AddFellowOakDicom().AddLogging(config => config.AddConsole())
|
||||
.AddTranscoderManager<FellowOakDicom.Imaging.NativeCodec.NativeTranscoderManager>()
|
||||
//.AddImageManager<ImageSharpImageManager>()
|
||||
)
|
||||
.SkipValidation()
|
||||
.Build();
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using EntityFramework.Exceptions.SqlServer;
|
||||
using IRaCIS.Core.Application.Triggers;
|
||||
using IRaCIS.Core.Application.Triggers.AfterSaveTrigger;
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using IRaCIS.Core.Infra.EFCore;
|
||||
using IRaCIS.Core.Infra.EFCore.Interceptor;
|
||||
|
|
@ -80,8 +81,15 @@ namespace IRaCIS.Core.API
|
|||
triggerOptions.AddTrigger<UserLogTrigger>();
|
||||
|
||||
triggerOptions.AddTrigger<UserAddTrigger>();
|
||||
|
||||
triggerOptions.AddTrigger<IdenttiyUserRoleInfoTrigger>();
|
||||
|
||||
triggerOptions.AddTrigger<UserLogAfterTrigger>();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,25 +0,0 @@
|
|||
|
||||
using LogDashboard;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace IRaCIS.Core.API
|
||||
{
|
||||
public static class LogDashboardSetup
|
||||
{
|
||||
public static void AddLogDashboardSetup(this IServiceCollection services)
|
||||
{
|
||||
//IIS 配置虚拟路径部署,会出现IIS静态文件404
|
||||
services.AddLogDashboard(opt =>
|
||||
{
|
||||
//opt.PathMatch = "/api/LogDashboard";
|
||||
opt.PathMatch = "/LogDashboard";
|
||||
|
||||
//opt.AddAuthorizationFilter(new LogDashboardBasicAuthFilter("admin", "zhizhun2018"));
|
||||
|
||||
//opt.AddAuthorizationFilter(new LogDashBoardAuthFilter());
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -22,6 +22,9 @@ namespace IRaCIS.Core.API
|
|||
var config = new LoggerConfiguration()
|
||||
.MinimumLevel.Information()
|
||||
.MinimumLevel.Override("Microsoft", LogEventLevel.Information)
|
||||
.MinimumLevel.Override("MassTransit", LogEventLevel.Warning)
|
||||
//https://github.com/ZiggyCreatures/FusionCache/blob/main/docs/Logging.md
|
||||
.MinimumLevel.Override("ZiggyCreatures.Caching.Fusion", LogEventLevel.Warning)
|
||||
// Filter out ASP.NET Core infrastructre logs that are Information and below 日志太多了 一个请求 记录好几条
|
||||
.MinimumLevel.Override("Microsoft.AspNetCore", LogEventLevel.Warning)
|
||||
.MinimumLevel.Override("Microsoft.AspNetCore.Hosting", LogEventLevel.Warning)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using IP2Region.Net.Abstractions;
|
||||
using IP2Region.Net.XDB;
|
||||
using IRaCIS.Application.Contracts;
|
||||
using IRaCIS.Core.Application.BackGroundJob;
|
||||
using IRaCIS.Core.Application.Helper;
|
||||
using IRaCIS.Core.Application.Service;
|
||||
|
|
|
|||
|
|
@ -16,8 +16,12 @@ namespace IRaCIS.Core.API;
|
|||
|
||||
public enum SwaggerVersion
|
||||
{
|
||||
[Description("HIR修改")]
|
||||
HIR = -1,
|
||||
|
||||
[Description("医生模块")]
|
||||
Reviewer = 1,
|
||||
|
||||
[Description("项目模块")]
|
||||
Trial = 2,
|
||||
[Description("入组模块")]
|
||||
|
|
@ -33,11 +37,11 @@ public enum SwaggerVersion
|
|||
[Description("财务模块")]
|
||||
Financial = 8,
|
||||
[Description("管理模块")]
|
||||
Management =9,
|
||||
Management = 9,
|
||||
[Description("影像模块")]
|
||||
Image =10,
|
||||
Image = 10,
|
||||
[Description("读片模块")]
|
||||
Reading =11
|
||||
Reading = 11
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,72 +0,0 @@
|
|||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
"ConnectionStrings": {
|
||||
"RemoteNew": "Server=47.117.164.182,1434;Database=Event_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
||||
"Hangfire": "Server=47.117.164.182,1434;Database=Event_IRC_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
|
||||
},
|
||||
"ObjectStoreService": {
|
||||
"ObjectStoreUse": "AliyunOSS",
|
||||
"AliyunOSS": {
|
||||
"RegionId": "cn-shanghai",
|
||||
"InternalEndpoint": "https://oss-cn-shanghai-internal.aliyuncs.com",
|
||||
"EndPoint": "https://oss-cn-shanghai.aliyuncs.com",
|
||||
"AccessKeyId": "LTAI5tNRTsqL6aWmHkDmTwoH",
|
||||
"AccessKeySecret": "7mtGz3qrYWI6JMMBZiLeC119VWicZH",
|
||||
"RoleArn": "acs:ram::1899121822495495:role/irc-oss-access",
|
||||
"BucketName": "zy-irc-store",
|
||||
"ViewEndpoint": "https://zy-irc-cache.oss-cn-shanghai.aliyuncs.com",
|
||||
"Region": "oss-cn-shanghai",
|
||||
"DurationSeconds": 7200
|
||||
},
|
||||
"MinIO": {
|
||||
"endpoint": "http://192.168.3.68",
|
||||
"port": "8001",
|
||||
"useSSL": false,
|
||||
"accessKey": "IDFkwEpWej0b4DtiuThL",
|
||||
"secretKey": "Lhuu83yMhVwu7c1SnjvGY6lq74jzpYqifK6Qtj4h",
|
||||
"bucketName": "test"
|
||||
}
|
||||
},
|
||||
|
||||
"BasicSystemConfig": {
|
||||
|
||||
"OpenUserComplexPassword": true,
|
||||
|
||||
"OpenSignDocumentBeforeWork": true,
|
||||
|
||||
"OpenLoginLimit": true,
|
||||
"LoginMaxFailCount": 5,
|
||||
|
||||
"LoginFailLockMinutes": 30,
|
||||
"AutoLoginOutMinutes": 60,
|
||||
|
||||
"ContinuousReadingTimeMin": 120,
|
||||
|
||||
"ReadingRestTimeMin": 10,
|
||||
|
||||
"IsNeedChangePassWord": true,
|
||||
|
||||
"ChangePassWordDays": 90
|
||||
},
|
||||
"SystemEmailSendConfig": {
|
||||
"Port": 465,
|
||||
"Host": "smtp.qiye.aliyun.com",
|
||||
"FromEmail": "uat@extimaging.com",
|
||||
"FromName": "UAT_IRC",
|
||||
"AuthorizationCode": "SHzyyl2021",
|
||||
"SiteUrl": "http://irc.event.extimaging.com/login",
|
||||
"CompanyName": "Extensive Imaging",
|
||||
"CompanyNameCN": "上海展影医疗科技有限公司",
|
||||
"CompanyShortName": "Extensive Imaging",
|
||||
"CompanyShortNameCN": "展影医疗",
|
||||
"IsEnv_US": false
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
"ObjectStoreService": {
|
||||
"ObjectStoreUse": "MinIO",
|
||||
"MinIO": {
|
||||
"EndPoint": "hir-oss.tres.extimaging.com",
|
||||
"Port": "443",
|
||||
"UseSSL": true,
|
||||
"AccessKeyId": "fbStsVYCIPKHQneeqMwD",
|
||||
"SecretAccessKey": "TzgvyA3zGXMUnpilJNUlyMYHfosl1hBMl6lxPmjy",
|
||||
"BucketName": "hir-images",
|
||||
"ViewEndpoint": "https://hir-oss.tres.extimaging.com/hir-images"
|
||||
}
|
||||
|
||||
},
|
||||
"ConnectionStrings": {
|
||||
"RemoteNew": "Server=mssql,1433;Database=HIR_Prod;User ID=sa;Password=xc_123456;TrustServerCertificate=true",
|
||||
"Hangfire": "Server=mssql,1433;Database=HIR_Prod_Hangfire;User ID=sa;Password=xc_123456;TrustServerCertificate=true"
|
||||
},
|
||||
"BasicSystemConfig": {
|
||||
"OpenUserComplexPassword": true,
|
||||
"OpenSignDocumentBeforeWork": true,
|
||||
"OpenTrialRelationDelete": true,
|
||||
"OpenLoginLimit": false,
|
||||
"LoginMaxFailCount": 5,
|
||||
"LoginFailLockMinutes": 30,
|
||||
"AutoLoginOutMinutes": 120,
|
||||
"AESKey": "HIR_System_AES_Key_Info",
|
||||
"CmoveIntervalMinutes": 1,
|
||||
"CmoveInstanceIntervalMinutes": 1,
|
||||
// 是否强制用户定期修改密码
|
||||
"IsNeedChangePassWord": true,
|
||||
// 密码有效期(天),到期后必须修改
|
||||
"ChangePassWordDays": 1000,
|
||||
"OpenImageShare": true
|
||||
},
|
||||
|
||||
"SystemEmailSendConfig": {
|
||||
"Port": 465,
|
||||
"Host": "smtp.qiye.aliyun.com",
|
||||
"FromEmail": "test@extimaging.com",
|
||||
"FromName": "BCTOP",
|
||||
"AuthorizationCode": "SHzyyl2021",
|
||||
"SiteUrl": "https://bctop.tres.extimaging.com/login",
|
||||
"CompanyName": "BCTOP",
|
||||
"CompanyNameCN": "BCTOP",
|
||||
"CompanyShortName": "BCTOP",
|
||||
"CompanyShortNameCN": "BCTOP",
|
||||
"EmailRegexStr": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$"
|
||||
},
|
||||
"DicomSCPServiceConfig": {
|
||||
"IsSupportThirdService": true,
|
||||
"ThirdSearchPacsAE": "XCPACS",
|
||||
"ThirdCallningAE": "LYAE",
|
||||
"CalledAEList": [
|
||||
"HIRSCUAE",
|
||||
"HIRSCPAE"
|
||||
],
|
||||
"ServerPort": 11112
|
||||
}
|
||||
}
|
||||
|
|
@ -1,82 +0,0 @@
|
|||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
"ConnectionStrings": {
|
||||
//"RemoteNew": "Server=101.132.193.237,1434;Database=Prod_IRC;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true",
|
||||
//"Hangfire": "Server=101.132.193.237,1434;Database=Prod_IRC_Hangfire;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true"
|
||||
"RemoteNew": "Server=prod_mssql_standard,1433;Database=Prod_IRC;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true",
|
||||
"Hangfire": "Server=prod_mssql_standard,1433;Database=Prod_IRC_Hangfire;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true"
|
||||
},
|
||||
"ObjectStoreService": {
|
||||
"ObjectStoreUse": "AliyunOSS",
|
||||
"AliyunOSS": {
|
||||
"RegionId": "cn-shanghai",
|
||||
"InternalEndpoint": "https://oss-cn-shanghai-internal.aliyuncs.com",
|
||||
"EndPoint": "https://oss-cn-shanghai.aliyuncs.com",
|
||||
"AccessKeyId": "LTAI5tNRTsqL6aWmHkDmTwoH",
|
||||
"AccessKeySecret": "7mtGz3qrYWI6JMMBZiLeC119VWicZH",
|
||||
"RoleArn": "acs:ram::1899121822495495:role/irc-oss-access",
|
||||
"BucketName": "zy-irc-store",
|
||||
"ViewEndpoint": "https://zy-irc-cache.oss-cn-shanghai.aliyuncs.com",
|
||||
"Region": "oss-cn-shanghai",
|
||||
"DurationSeconds": 7200
|
||||
},
|
||||
"MinIO": {
|
||||
"endpoint": "http://192.168.3.68",
|
||||
"port": "8001",
|
||||
"useSSL": false,
|
||||
"accessKey": "IDFkwEpWej0b4DtiuThL",
|
||||
"secretKey": "Lhuu83yMhVwu7c1SnjvGY6lq74jzpYqifK6Qtj4h",
|
||||
"bucketName": "test"
|
||||
}
|
||||
},
|
||||
"BasicSystemConfig": {
|
||||
|
||||
"OpenUserComplexPassword": true,
|
||||
"OpenSignDocumentBeforeWork": true,
|
||||
|
||||
"OpenLoginLimit": true,
|
||||
"LoginMaxFailCount": 5,
|
||||
"LoginFailLockMinutes": 30,
|
||||
|
||||
"AutoLoginOutMinutes": 360,
|
||||
|
||||
"OpenLoginMFA": false,
|
||||
|
||||
"ContinuousReadingTimeMin": 120,
|
||||
|
||||
"ReadingRestTimeMin": 10,
|
||||
|
||||
"IsNeedChangePassWord": true,
|
||||
|
||||
"ChangePassWordDays": 90
|
||||
},
|
||||
|
||||
"SystemEmailSendConfig": {
|
||||
"Port": 465,
|
||||
"Host": "smtp.qiye.aliyun.com",
|
||||
"FromEmail": "IRC@extimaging.com",
|
||||
"FromName": "IRC",
|
||||
"AuthorizationCode": "ExtImg@2022",
|
||||
"SiteUrl": "http://irc.extimaging.com/login",
|
||||
"OrganizationName": "Extlmaging",
|
||||
"OrganizationNameCN": "Extlmaging",
|
||||
"CompanyName": "Extensive Imaging",
|
||||
"CompanyNameCN": "上海展影医疗科技有限公司",
|
||||
"CompanyShortName": "Extensive Imaging",
|
||||
"CompanyShortNameCN": "展影医疗",
|
||||
"IsEnv_US": false,
|
||||
"IsOpenErrorNoticeEmail": true,
|
||||
"ErrorNoticeEmailList": [ "872297557@qq.com" ]
|
||||
},
|
||||
"SystemPacsConfig": {
|
||||
"Port": "11113",
|
||||
"IP": "101.132.193.237"
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
"ObjectStoreService": {
|
||||
"ObjectStoreUse": "MinIO",
|
||||
"MinIO": {
|
||||
"EndPoint": "192.168.40.99",
|
||||
"Port": "9000",
|
||||
"UseSSL": false,
|
||||
"AccessKeyId": "Jnywl9aIw83yewZIJKod",
|
||||
"SecretAccessKey": "N83bTzoJGkg4OLW8x54IZRwwSvdxcdYi9UZ2BYII",
|
||||
"BucketName": "tj-hir",
|
||||
"ViewEndpoint": "http://192.168.40.99:9000/tj-hir"
|
||||
}
|
||||
|
||||
},
|
||||
"ConnectionStrings": {
|
||||
"RemoteNew": "Server=192.168.40.98,1433;Database=TJ_Prod_HIR;User ID=sa;Password=xc_123456;TrustServerCertificate=true",
|
||||
"Hangfire": "Server=192.168.40.98,1433;Database=TJ_Prod_HIR_Hangfire;User ID=sa;Password=xc_123456;TrustServerCertificate=true"
|
||||
},
|
||||
"BasicSystemConfig": {
|
||||
"OpenUserComplexPassword": true,
|
||||
"OpenSignDocumentBeforeWork": false,
|
||||
"OpenTrialRelationDelete": false,
|
||||
"OpenLoginLimit": true,
|
||||
"LoginMaxFailCount": 5,
|
||||
"LoginFailLockMinutes": 30,
|
||||
"AutoLoginOutMinutes": 120,
|
||||
"AESKey": "HIR_System_AES_Key_Info",
|
||||
"CmoveIntervalMinutes": 1,
|
||||
"CmoveInstanceIntervalMinutes": 1
|
||||
},
|
||||
"SystemHospitalConfig": {
|
||||
"HospitalCode": "EI",
|
||||
"HospitalLogoPath": "/System/GeneralDocuments/1716453306898_图片2.png",
|
||||
"TrialKeepCount": 60,
|
||||
"HospitalName": "上海展影医疗科技有限公司",
|
||||
"HospitalAliasName": "展影医疗",
|
||||
"Country": "中国",
|
||||
"City": "上海",
|
||||
"Province": "上海",
|
||||
"Address": "上海市杨浦区国泰路复旦科技园",
|
||||
"Phone": "021-60702575",
|
||||
"IsCanConnectInternet": false
|
||||
},
|
||||
"SystemEmailSendConfig": {
|
||||
"Port": 465,
|
||||
"Host": "smtp.qiye.aliyun.com",
|
||||
"FromEmail": "test-study@extimaging.com",
|
||||
"FromName": "Test_HIR",
|
||||
"AuthorizationCode": "zhanying123",
|
||||
"SiteUrl": "http://hir.test.extimaging.com/login"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
"ObjectStoreService": {
|
||||
"ObjectStoreUse": "MinIO",
|
||||
"MinIO": {
|
||||
"EndPoint": "hir-oss.test.extimaging.com",
|
||||
"Port": "443",
|
||||
"UseSSL": true,
|
||||
//"endPoint": "106.14.89.110",
|
||||
//"port": "9001",
|
||||
//"useSSL": false,
|
||||
"AccessKeyId": "fbStsVYCIPKHQneeqMwD",
|
||||
"SecretAccessKey": "TzgvyA3zGXMUnpilJNUlyMYHfosl1hBMl6lxPmjy",
|
||||
"BucketName": "hir-test",
|
||||
//"viewEndpoint": "https://hir.test.extimaging.com/oss/hir-test"
|
||||
"ViewEndpoint": "https://hir-oss.test.extimaging.com/hir-test"
|
||||
}
|
||||
|
||||
},
|
||||
"ConnectionStrings": {
|
||||
"RemoteNew": "Server=106.14.89.110,1435;Database=Test_HIR_New;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
||||
"Hangfire": "Server=106.14.89.110,1435;Database=Test_HIR_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
|
||||
},
|
||||
"BasicSystemConfig": {
|
||||
"OpenUserComplexPassword": true,
|
||||
"OpenSignDocumentBeforeWork": true,
|
||||
"OpenTrialRelationDelete": true,
|
||||
"OpenLoginLimit": false,
|
||||
"LoginMaxFailCount": 5,
|
||||
"LoginFailLockMinutes": 30,
|
||||
"AutoLoginOutMinutes": 120,
|
||||
"AESKey": "HIR_System_AES_Key_Info",
|
||||
"CmoveIntervalMinutes": 1,
|
||||
"CmoveInstanceIntervalMinutes": 1,
|
||||
// 是否强制用户定期修改密码
|
||||
"IsNeedChangePassWord": true,
|
||||
// 密码有效期(天),到期后必须修改
|
||||
"ChangePassWordDays": 1000,
|
||||
"OpenImageShare": true
|
||||
},
|
||||
|
||||
"SystemEmailSendConfig": {
|
||||
"Port": 465,
|
||||
"Host": "smtp.qiye.aliyun.com",
|
||||
"FromEmail": "test-study@extimaging.com",
|
||||
"FromName": "Test_HIR",
|
||||
"AuthorizationCode": "zhanying123",
|
||||
"SiteUrl": "http://hir.test.extimaging.com/login",
|
||||
"CompanyName": "Extensive Imaging",
|
||||
"CompanyNameCN": "上海展影医疗科技有限公司",
|
||||
"CompanyShortName": "Extensive Imaging",
|
||||
"CompanyShortNameCN": "展影医疗",
|
||||
"EmailRegexStr": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$"
|
||||
},
|
||||
"DicomSCPServiceConfig": {
|
||||
"IsSupportThirdService": true,
|
||||
"ThirdSearchPacsAE": "XCPACS",
|
||||
"ThirdCallningAE": "LYAE",
|
||||
"CalledAEList": [
|
||||
"HIRSCUAE",
|
||||
"HIRSCPAE"
|
||||
],
|
||||
"ServerPort": 11112
|
||||
}
|
||||
}
|
||||
|
|
@ -1,95 +0,0 @@
|
|||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
"ConnectionStrings": {
|
||||
"Db_Type": "pgsql",
|
||||
"RemoteNew": "Host=106.14.89.110;Port=5432;Username=sa;Password=pgsql_pwd;Database=Test2_PG",
|
||||
"Hangfire": "Server=106.14.89.110,1435;Database=Test_IRC_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
|
||||
},
|
||||
|
||||
"ObjectStoreService": {
|
||||
|
||||
"ObjectStoreUse": "AliyunOSS",
|
||||
|
||||
"AliyunOSS": {
|
||||
"regionId": "cn-shanghai",
|
||||
"internalEndpoint": "https://oss-cn-shanghai-internal.aliyuncs.com",
|
||||
"endPoint": "https://oss-cn-shanghai.aliyuncs.com",
|
||||
"accessKeyId": "LTAI5tKvzs7ed3UfSpNk3xwQ",
|
||||
"accessKeySecret": "zTIceGEShlZDGnLrCFfIGFE7TXVRio",
|
||||
"bucketName": "zy-irc-test-store",
|
||||
"roleArn": "acs:ram::1899121822495495:role/oss-upload",
|
||||
"viewEndpoint": "https://zy-irc-test-store.oss-cn-shanghai.aliyuncs.com",
|
||||
"region": "oss-cn-shanghai"
|
||||
},
|
||||
"MinIO": {
|
||||
"endPoint": "hir-oss.test.extimaging.com",
|
||||
"port": "443",
|
||||
"useSSL": true,
|
||||
"accessKey": "fbStsVYCIPKHQneeqMwD",
|
||||
"secretKey": "TzgvyA3zGXMUnpilJNUlyMYHfosl1hBMl6lxPmjy",
|
||||
"bucketName": "irc-test",
|
||||
"viewEndpoint": "https://hir-oss.test.extimaging.com/irc-test"
|
||||
},
|
||||
"AWS": {
|
||||
"endPoint": "s3.us-east-1.amazonaws.com",
|
||||
"useSSL": true,
|
||||
"accessKey": "AKIAZQ3DRSOHFPJJ6FEU",
|
||||
"secretKey": "l+yjtvV7Z4jiwm/7xCYv30UeUj/SvuqqYzAwjJHf",
|
||||
"bucketName": "ei-irc-test-store",
|
||||
"viewEndpoint": "https://ei-irc-test-store.s3.amazonaws.com/"
|
||||
}
|
||||
},
|
||||
|
||||
"BasicSystemConfig": {
|
||||
|
||||
"OpenUserComplexPassword": false,
|
||||
|
||||
"OpenSignDocumentBeforeWork": false,
|
||||
|
||||
"OpenTrialRelationDelete": true,
|
||||
|
||||
"OpenLoginLimit": false,
|
||||
|
||||
"LoginMaxFailCount": 5,
|
||||
|
||||
"LoginFailLockMinutes": 1,
|
||||
|
||||
"AutoLoginOutMinutes": 1,
|
||||
|
||||
"OpenLoginMFA": false,
|
||||
|
||||
"ContinuousReadingTimeMin": 120,
|
||||
|
||||
"ReadingRestTimeMin": 10,
|
||||
"IsNeedChangePassWord": true,
|
||||
|
||||
"ChangePassWordDays": 90
|
||||
},
|
||||
|
||||
"SystemEmailSendConfig": {
|
||||
"Port": 465,
|
||||
"Host": "smtp.qiye.aliyun.com",
|
||||
"FromEmail": "test@extimaging.com",
|
||||
"FromName": "Test_IRC",
|
||||
"AuthorizationCode": "SHzyyl2021",
|
||||
"SiteUrl": "http://irc.test.extimaging.com/login",
|
||||
|
||||
"OrganizationName": "Extlmaging",
|
||||
"OrganizationNameCN": "Extlmaging",
|
||||
"CompanyName": "Extensive Imaging",
|
||||
"CompanyNameCN": "上海展影医疗科技有限公司",
|
||||
"CompanyShortName": "Extensive Imaging",
|
||||
"CompanyShortNameCN": "展影医疗"
|
||||
},
|
||||
|
||||
"SystemPacsConfig": {
|
||||
"Port": "11113",
|
||||
"IP": "106.14.89.110"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
"ObjectStoreService": {
|
||||
"ObjectStoreUse": "MinIO",
|
||||
"MinIO": {
|
||||
"EndPoint": "hir-minio.uat.elevateimaging.ai",
|
||||
"Port": "443",
|
||||
"UseSSL": true,
|
||||
"AccessKeyId": "7rvVIHs7D6pbyscRcJhz",
|
||||
"SecretAccessKey": "DQsCQldHFL3QRjlnaLWV7oM4E9PtsO21QPC2h9BD",
|
||||
"BucketName": "hir-us",
|
||||
//"viewEndpoint": "https://hir.test.extimaging.com/oss/hir-test"
|
||||
"ViewEndpoint": "https://hir-minio.uat.elevateimaging.ai/hir-us"
|
||||
}
|
||||
|
||||
},
|
||||
"ConnectionStrings": {
|
||||
"RemoteNew": "Server=3.226.182.187,1435;Database=US_HIR;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
||||
"Hangfire": "Server=3.226.182.187,1435;Database=US_HIR_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
|
||||
},
|
||||
"BasicSystemConfig": {
|
||||
"OpenUserComplexPassword": true,
|
||||
"OpenSignDocumentBeforeWork": true,
|
||||
"OpenTrialRelationDelete": true,
|
||||
"OpenLoginLimit": true,
|
||||
"LoginMaxFailCount": 5,
|
||||
"LoginFailLockMinutes": 30,
|
||||
"AutoLoginOutMinutes": 120,
|
||||
"AESKey": "HIR_System_AES_Key_Info",
|
||||
"CmoveIntervalMinutes": 1,
|
||||
"CmoveInstanceIntervalMinutes": 1,
|
||||
// 是否强制用户定期修改密码
|
||||
"IsNeedChangePassWord": true,
|
||||
// 密码有效期(天),到期后必须修改
|
||||
"ChangePassWordDays": 1000,
|
||||
"OpenImageShare": true
|
||||
},
|
||||
|
||||
"SystemEmailSendConfig": {
|
||||
"Port": 465,
|
||||
"Host": "smtp.qiye.aliyun.com",
|
||||
"FromEmail": "uat-study@extimaging.com",
|
||||
"FromName": "Uat_HIR",
|
||||
"AuthorizationCode": "zhanying123",
|
||||
"SiteUrl": "https://hir.uat.elevateimaging.ai/login",
|
||||
"CompanyName": "Extensive Imaging",
|
||||
"CompanyNameCN": "上海展影医疗科技有限公司",
|
||||
"CompanyShortName": "Extensive Imaging",
|
||||
"CompanyShortNameCN": "展影医疗",
|
||||
"EmailRegexStr": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$"
|
||||
},
|
||||
"DicomSCPServiceConfig": {
|
||||
"IsSupportThirdService": true,
|
||||
"ThirdSearchPacsAE": "XCPACS",
|
||||
"ThirdCallningAE": "LYAE",
|
||||
"CalledAEList": [
|
||||
"HIRSCUAE",
|
||||
"HIRSCPAE"
|
||||
],
|
||||
"ServerPort": 11112
|
||||
}
|
||||
}
|
||||
|
|
@ -1,87 +0,0 @@
|
|||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
"ConnectionStrings": {
|
||||
"RemoteNew": "Server=us-prod-mssql-service,1433;Database=US_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
||||
"Hangfire": "Server=us-prod-mssql-service,1433;Database=US_IRC_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
|
||||
//"RemoteNew": "Server=44.210.231.169,1435;Database=US_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
||||
//"Hangfire": "Server=44.210.231.169,1435;Database=US_IRC_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
||||
},
|
||||
|
||||
"ObjectStoreService": {
|
||||
"ObjectStoreUse": "AWS",
|
||||
"MinIO": {
|
||||
"endPoint": "44.210.231.169",
|
||||
"port": "9001",
|
||||
"useSSL": false,
|
||||
"accessKey": "e9bT1isTOqSAUxb6wd4n",
|
||||
"secretKey": "b5TaDzNdQCBtCvfm8eZ3dR6yY7tfZu2JYze2Po1i",
|
||||
"bucketName": "prod-irc-us",
|
||||
"viewEndpoint": "http://44.210.231.169:9001/prod-irc-us/"
|
||||
},
|
||||
|
||||
"AWS": {
|
||||
"Region": "us-east-1",
|
||||
"EndPoint": "s3.us-east-1.amazonaws.com",
|
||||
"UseSSL": true,
|
||||
"RoleArn": "arn:aws:iam::471112624751:role/lili_s3_access",
|
||||
"AccessKeyId": "AKIAW3MEAFJXZ2TZK7GM",
|
||||
"SecretAccessKey": "9MLQCQ1HifEVW1gf068zBRAOb4wNnfrOkvBVByth",
|
||||
"BucketName": "ei-med-s3-lili-store",
|
||||
"ViewEndpoint": "https://ei-med-s3-lili-store.s3.amazonaws.com",
|
||||
"DurationSeconds": 7200
|
||||
}
|
||||
},
|
||||
"BasicSystemConfig": {
|
||||
|
||||
"OpenUserComplexPassword": false,
|
||||
|
||||
"OpenSignDocumentBeforeWork": false,
|
||||
|
||||
"OpenTrialRelationDelete": true,
|
||||
|
||||
"OpenLoginLimit": false,
|
||||
|
||||
"LoginMaxFailCount": 5,
|
||||
|
||||
"LoginFailLockMinutes": 30,
|
||||
"AutoLoginOutMinutes": 60,
|
||||
|
||||
"ContinuousReadingTimeMin": 120,
|
||||
|
||||
"ReadingRestTimeMin": 10,
|
||||
"IsNeedChangePassWord": true,
|
||||
|
||||
"ChangePassWordDays": 90
|
||||
},
|
||||
|
||||
"SystemEmailSendConfig": {
|
||||
"Port": 587,
|
||||
"Host": "smtp-mail.outlook.com",
|
||||
"FromEmail": "donotreply@elevateimaging.ai",
|
||||
"FromName": "LiLi",
|
||||
"AuthorizationCode": "Q#669869497420ul",
|
||||
|
||||
"OrganizationName": "Elevate Imaging",
|
||||
"OrganizationNameCN": "Elevate Imaging",
|
||||
"CompanyName": "Elevate Imaging Inc.",
|
||||
"CompanyNameCN": "上海展影医疗科技有限公司",
|
||||
"CompanyShortName": "Elevate Imaging",
|
||||
"CompanyShortNameCN": "展影医疗",
|
||||
"SiteUrl": "https://lili.elevateimaging.ai/login",
|
||||
"IsEnv_US": true,
|
||||
"IsOpenErrorNoticeEmail": true,
|
||||
"ErrorNoticeEmailList": [ "872297557@qq.com" ]
|
||||
},
|
||||
|
||||
"SystemPacsConfig": {
|
||||
"Port": "104",
|
||||
"IP": "44.210.231.169"
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,95 +0,0 @@
|
|||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
"ConnectionStrings": {
|
||||
"RemoteNew": "Server=106.14.89.110,1435;Database=Test_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
||||
"Hangfire": "Server=106.14.89.110,1435;Database=Test_IRC_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
|
||||
},
|
||||
|
||||
"ObjectStoreService": {
|
||||
|
||||
"ObjectStoreUse": "AWS",
|
||||
"MinIO": {
|
||||
//"endPoint": "hir-oss.uat.extimaging.com",
|
||||
//"port": "443",
|
||||
//"useSSL": true,
|
||||
//"viewEndpoint": "https://hir-oss.uat.extimaging.com/hir-uat",
|
||||
|
||||
"endPoint": "47.117.164.182",
|
||||
"port": "9001",
|
||||
"useSSL": false,
|
||||
"viewEndpoint": "http://47.117.164.182:9001/test-irc-us",
|
||||
|
||||
"accessKey": "b9Ul0e98xPzt6PwRXA1Q",
|
||||
"secretKey": "DzMaU2L4OXl90uytwOmDXF2encN0Jf4Nxu2XkYqQ",
|
||||
"bucketName": "test-irc-us"
|
||||
|
||||
},
|
||||
|
||||
"AWS": {
|
||||
"Region": "us-east-1",
|
||||
"EndPoint": "s3.us-east-1.amazonaws.com",
|
||||
"UseSSL": true,
|
||||
"RoleArn": "arn:aws:iam::471112624751:role/uat_s3_access",
|
||||
"AccessKeyId": "AKIAW3MEAFJX7IPXISP4",
|
||||
"SecretAccessKey": "Pgrg3le5jPxZQ7MR1yYNS30J0XRyJeKVyIIjElXc",
|
||||
"BucketName": "ei-med-s3-lili-uat-store",
|
||||
"ViewEndpoint": "https://ei-med-s3-lili-uat-store.s3.amazonaws.com",
|
||||
"DurationSeconds": 7200
|
||||
}
|
||||
},
|
||||
"BasicSystemConfig": {
|
||||
|
||||
"OpenUserComplexPassword": true,
|
||||
|
||||
"OpenSignDocumentBeforeWork": true,
|
||||
|
||||
"OpenTrialRelationDelete": true,
|
||||
|
||||
"OpenLoginLimit": true,
|
||||
|
||||
"LoginMaxFailCount": 5,
|
||||
|
||||
"LoginFailLockMinutes": 30,
|
||||
"AutoLoginOutMinutes": 60,
|
||||
|
||||
"ContinuousReadingTimeMin": 120,
|
||||
|
||||
"ReadingRestTimeMin": 10,
|
||||
"IsNeedChangePassWord": true,
|
||||
|
||||
"ChangePassWordDays": 90,
|
||||
|
||||
"OpenLoginMFA": true
|
||||
},
|
||||
|
||||
"SystemEmailSendConfig": {
|
||||
"Port": 587,
|
||||
"Host": "smtp-mail.outlook.com",
|
||||
"FromEmail": "donotreply@elevateimaging.ai",
|
||||
"FromName": "LiLi",
|
||||
"AuthorizationCode": "Q#669869497420ul",
|
||||
|
||||
"OrganizationName": "Elevate Imaging",
|
||||
"OrganizationNameCN": "Elevate Imaging",
|
||||
"CompanyName": "Elevate Imaging Inc.",
|
||||
"CompanyNameCN": "上海展影医疗科技有限公司",
|
||||
"CompanyShortName": "Elevate Imaging",
|
||||
"CompanyShortNameCN": "展影医疗",
|
||||
"SiteUrl": "https://lili.test.elevateimaging.ai/login",
|
||||
"IsEnv_US": true,
|
||||
"IsOpenErrorNoticeEmail": false,
|
||||
"ErrorNoticeEmailList": [ "872297557@qq.com" ]
|
||||
},
|
||||
|
||||
"SystemPacsConfig": {
|
||||
"Port": "104",
|
||||
"IP": "3.226.182.187"
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,99 +0,0 @@
|
|||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
"ConnectionStrings": {
|
||||
//"RemoteNew": "Server=us-mssql-service,1433;Database=US_Uat_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
||||
//"Hangfire": "Server=us-mssql-service,1433;Database=US_Uat_IRC_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
|
||||
|
||||
"RemoteNew": "Server=3.226.182.187,1435;Database=US_Uat_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
||||
"Hangfire": "Server=3.226.182.187,1435;Database=US_Uat_IRC_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
|
||||
},
|
||||
|
||||
"ObjectStoreService": {
|
||||
|
||||
"ObjectStoreUse": "AWS",
|
||||
|
||||
"MinIO": {
|
||||
//"endPoint": "hir-minio.uat.elevateimaging.ai",
|
||||
//"port": "443",
|
||||
//"useSSL": true,
|
||||
//"viewEndpoint": "https://hir-minio.uat.elevateimaging.ai/uat-irc-us",
|
||||
|
||||
"endPoint": "3.226.182.187",
|
||||
"port": "9001",
|
||||
"useSSL": false,
|
||||
"viewEndpoint": "http://44.218.11.19:9001/uat-irc-us",
|
||||
|
||||
"accessKey": "lH8DkKskLuDqPaiubuSQ",
|
||||
"secretKey": "pdPdicvvLeH7xAC5yFUrI7odMyBfOXxvVWMvKYV4",
|
||||
"bucketName": "uat-irc-us"
|
||||
|
||||
},
|
||||
|
||||
"AWS": {
|
||||
"Region": "us-east-1",
|
||||
"EndPoint": "s3.us-east-1.amazonaws.com",
|
||||
"UseSSL": true,
|
||||
"RoleArn": "arn:aws:iam::471112624751:role/uat_s3_access",
|
||||
"AccessKeyId": "AKIAW3MEAFJX7IPXISP4",
|
||||
"SecretAccessKey": "Pgrg3le5jPxZQ7MR1yYNS30J0XRyJeKVyIIjElXc",
|
||||
"BucketName": "ei-med-s3-lili-uat-store",
|
||||
"ViewEndpoint": "https://ei-med-s3-lili-uat-store.s3.amazonaws.com",
|
||||
"DurationSeconds": 7200
|
||||
}
|
||||
},
|
||||
"BasicSystemConfig": {
|
||||
|
||||
"OpenUserComplexPassword": true,
|
||||
|
||||
"OpenSignDocumentBeforeWork": true,
|
||||
|
||||
"OpenTrialRelationDelete": true,
|
||||
|
||||
"OpenLoginLimit": false,
|
||||
|
||||
"LoginMaxFailCount": 5,
|
||||
|
||||
"LoginFailLockMinutes": 30,
|
||||
"AutoLoginOutMinutes": 60,
|
||||
|
||||
"ContinuousReadingTimeMin": 120,
|
||||
|
||||
"ReadingRestTimeMin": 10,
|
||||
"IsNeedChangePassWord": true,
|
||||
|
||||
"ChangePassWordDays": 90,
|
||||
|
||||
"OpenLoginMFA": false
|
||||
},
|
||||
|
||||
"SystemEmailSendConfig": {
|
||||
"Port": 587,
|
||||
"Host": "smtp-mail.outlook.com",
|
||||
"FromEmail": "donotreply@elevateimaging.ai",
|
||||
"FromName": "LiLi",
|
||||
"AuthorizationCode": "Q#669869497420ul",
|
||||
|
||||
"OrganizationName": "Elevate Imaging",
|
||||
"OrganizationNameCN": "Elevate Imaging",
|
||||
"CompanyName": "Elevate Imaging Inc.",
|
||||
"CompanyNameCN": "上海展影医疗科技有限公司",
|
||||
"CompanyShortName": "Elevate Imaging",
|
||||
"CompanyShortNameCN": "展影医疗",
|
||||
"SiteUrl": "https://lili.test.elevateimaging.ai/login",
|
||||
"IsEnv_US": true,
|
||||
"IsOpenErrorNoticeEmail": false,
|
||||
"ErrorNoticeEmailList": [ "872297557@qq.com" ]
|
||||
},
|
||||
|
||||
"SystemPacsConfig": {
|
||||
"Port": "104",
|
||||
"IP": "3.226.182.187"
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
"ObjectStoreService": {
|
||||
"ObjectStoreUse": "MinIO",
|
||||
"MinIO": {
|
||||
"EndPoint": "hir-oss.uat.extimaging.com",
|
||||
"Port": "443",
|
||||
"UseSSL": true,
|
||||
//"endPoint": "106.14.89.110",
|
||||
//"port": "9001",
|
||||
//"useSSL": false,
|
||||
"AccessKeyId": "L6owzRVeDJJw3PcRmK2c",
|
||||
"SecretAccessKey": "2XvFDYSH7EyHQNtpDCgk4efgdsdarQmRKgx1LlOI",
|
||||
"BucketName": "hir-uat",
|
||||
"ViewEndpoint": "https://hir-oss.uat.extimaging.com/hir-uat"
|
||||
}
|
||||
|
||||
},
|
||||
"ConnectionStrings": {
|
||||
"RemoteNew": "Server=101.132.253.119,1435;Database=Uat_HIR_New;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
||||
"Hangfire": "Server=101.132.253.119,1435;Database=Uat_HIR_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
|
||||
},
|
||||
"BasicSystemConfig": {
|
||||
"OpenUserComplexPassword": true,
|
||||
"OpenSignDocumentBeforeWork": false,
|
||||
"OpenTrialRelationDelete": false,
|
||||
"OpenLoginLimit": true,
|
||||
"LoginMaxFailCount": 5,
|
||||
"LoginFailLockMinutes": 30,
|
||||
"AutoLoginOutMinutes": 120,
|
||||
"AESKey": "HIR_System_AES_Key_Info",
|
||||
"CmoveIntervalMinutes": 1,
|
||||
"CmoveInstanceIntervalMinutes": 1,
|
||||
// 是否强制用户定期修改密码
|
||||
"IsNeedChangePassWord": true,
|
||||
// 密码有效期(天),到期后必须修改
|
||||
"ChangePassWordDays": 1000,
|
||||
"OpenImageShare": true
|
||||
},
|
||||
"SystemEmailSendConfig": {
|
||||
"Port": 465,
|
||||
"Host": "smtp.qiye.aliyun.com",
|
||||
"FromEmail": "test-study@extimaging.com",
|
||||
"FromName": "Test_HIR",
|
||||
"AuthorizationCode": "zhanying123",
|
||||
"SiteUrl": "http://hir.test.extimaging.com/login",
|
||||
"CompanyName": "Extensive Imaging",
|
||||
"CompanyNameCN": "上海展影医疗科技有限公司",
|
||||
"CompanyShortName": "Extensive Imaging",
|
||||
"CompanyShortNameCN": "展影医疗",
|
||||
"EmailRegexStr": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$"
|
||||
},
|
||||
"DicomSCPServiceConfig": {
|
||||
"IsSupportThirdService": true,
|
||||
"ThirdSearchPacsAE": "XCPACS",
|
||||
"ThirdCallningAE": "LYAE",
|
||||
"CalledAEList": [
|
||||
"HIRSCUAE",
|
||||
"HIRSCPAE"
|
||||
],
|
||||
"ServerPort": 11112
|
||||
}
|
||||
}
|
||||
|
|
@ -1,100 +0,0 @@
|
|||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
"ConnectionStrings": {
|
||||
"RemoteNew": "Server=47.117.164.182,1434;Database=Uat_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
||||
"Hangfire": "Server=47.117.164.182,1434;Database=Uat_IRC.Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
|
||||
},
|
||||
"ObjectStoreService": {
|
||||
|
||||
"ObjectStoreUse": "AliyunOSS",
|
||||
|
||||
"AliyunOSS": {
|
||||
"RegionId": "cn-shanghai",
|
||||
"InternalEndpoint": "https://oss-cn-shanghai-internal.aliyuncs.com",
|
||||
"EndPoint": "https://oss-cn-shanghai.aliyuncs.com",
|
||||
"AccessKeyId": "LTAI5tRRZehUp2V9pyTPtAJm",
|
||||
"AccessKeySecret": "FLizxkHsMm4CGYHtkV8E3PNJJZU7oV",
|
||||
"RoleArn": "acs:ram::1899121822495495:role/dev-oss-access",
|
||||
"BucketName": "zy-irc-uat-store",
|
||||
"ViewEndpoint": "https://zy-irc-uat-store.oss-cn-shanghai.aliyuncs.com",
|
||||
"Region": "oss-cn-shanghai",
|
||||
"DurationSeconds": 7200
|
||||
},
|
||||
|
||||
"MinIO": {
|
||||
"endPoint": "hir-oss.uat.extimaging.com",
|
||||
"port": "80",
|
||||
"useSSL": false,
|
||||
"viewEndpoint": "http://hir-oss.uat.extimaging.com/irc-uat",
|
||||
//"port": "443",
|
||||
//"useSSL": true,
|
||||
//"viewEndpoint": "https://hir-oss.uat.extimaging.com/irc-uat",
|
||||
"accessKey": "b9Ul0e98xPzt6PwRXA1Q",
|
||||
"secretKey": "DzMaU2L4OXl90uytwOmDXF2encN0Jf4Nxu2XkYqQ",
|
||||
"bucketName": "irc-uat"
|
||||
|
||||
},
|
||||
"AWS": {
|
||||
"Region": "us-east-1",
|
||||
"EndPoint": "s3.us-east-1.amazonaws.com",
|
||||
"UseSSL": true,
|
||||
"RoleArn": "arn:aws:iam::471112624751:role/sts_s3_upload",
|
||||
"AccessKeyId": "AKIAW3MEAFJXWRCGSX5Z",
|
||||
"SecretAccessKey": "miais4jQGSd37A+TfBEP11AQM5u/CvotSmznJd8k",
|
||||
"BucketName": "ei-med-s3-lili-uat-store",
|
||||
"ViewEndpoint": "https://ei-med-s3-lili-uat-store.s3.amazonaws.com/",
|
||||
"DurationSeconds": 7200
|
||||
}
|
||||
},
|
||||
|
||||
"BasicSystemConfig": {
|
||||
|
||||
"OpenUserComplexPassword": true,
|
||||
|
||||
"OpenSignDocumentBeforeWork": true,
|
||||
|
||||
"OpenLoginLimit": true,
|
||||
"LoginMaxFailCount": 5,
|
||||
|
||||
"LoginFailLockMinutes": 30,
|
||||
"AutoLoginOutMinutes": 60,
|
||||
"OpenLoginMFA": false,
|
||||
|
||||
"ContinuousReadingTimeMin": 120,
|
||||
|
||||
"ReadingRestTimeMin": 10,
|
||||
"IsNeedChangePassWord": true,
|
||||
|
||||
"ChangePassWordDays": 90
|
||||
|
||||
},
|
||||
"SystemEmailSendConfig": {
|
||||
"Port": 465,
|
||||
"Host": "smtp.qiye.aliyun.com",
|
||||
"FromEmail": "uat@extimaging.com",
|
||||
"FromName": "UAT_IRC",
|
||||
"AuthorizationCode": "SHzyyl2021",
|
||||
"SiteUrl": "http://irc.uat.extimaging.com/login",
|
||||
"OrganizationName": "Extlmaging",
|
||||
"OrganizationNameCN": "Extlmaging",
|
||||
"CompanyName": "Extensive Imaging",
|
||||
"CompanyNameCN": "上海展影医疗科技有限公司",
|
||||
"CompanyShortName": "Extensive Imaging",
|
||||
"CompanyShortNameCN": "展影医疗",
|
||||
"IsEnv_US": false,
|
||||
"IsOpenErrorNoticeEmail": false,
|
||||
"ErrorNoticeEmailList": [ "872297557@qq.com" ]
|
||||
},
|
||||
|
||||
"SystemPacsConfig": {
|
||||
"Port": "11113",
|
||||
"IP": "47.117.164.182"
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
"SecurityKey": "ShangHaiZhanYing_SecurityKey_SHzyyl@2021",
|
||||
"Issuer": "Extimaging",
|
||||
"Audience": "EICS",
|
||||
"TokenExpireDays": "7"
|
||||
"TokenExpireMinute": "10080" //7天
|
||||
},
|
||||
"IpRateLimiting": {
|
||||
"EnableEndpointRateLimiting": true,
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -268,12 +268,12 @@ var abp = abp || {};
|
|||
}
|
||||
|
||||
function loginUserInternal(tenantId, callback) {
|
||||
|
||||
|
||||
var usernameOrEmailAddress = document.getElementById('userName').value;
|
||||
if (!usernameOrEmailAddress) {
|
||||
alert('UserName Can Not Be Null');
|
||||
return false;
|
||||
}
|
||||
// if (!usernameOrEmailAddress) {
|
||||
// alert('UserName Can Not Be Null');
|
||||
// return false;
|
||||
// }
|
||||
|
||||
var password = document.getElementById('password').value;
|
||||
var pwdMd5 = document.getElementById('pwdMd5').value;
|
||||
|
|
@ -286,12 +286,12 @@ var abp = abp || {};
|
|||
var xhr = new XMLHttpRequest();
|
||||
|
||||
xhr.onreadystatechange = function () {
|
||||
|
||||
|
||||
if (xhr.readyState === XMLHttpRequest.DONE) {
|
||||
debugger;
|
||||
// debugger;
|
||||
if (xhr.status === 200) {
|
||||
debugger;
|
||||
|
||||
// debugger;
|
||||
|
||||
var resultdata = JSON.parse(xhr.responseText);
|
||||
if (resultdata.ErrorMessage != '') {
|
||||
alert(resultdata.ErrorMessage);
|
||||
|
|
@ -303,18 +303,23 @@ var abp = abp || {};
|
|||
else {
|
||||
var responseJSON = JSON.parse(xhr.responseText);
|
||||
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);
|
||||
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 {
|
||||
alert('Login failed !');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
xhr.open('POST', '/user/login', true);
|
||||
|
||||
xhr.open('POST', '/User/getUserLoginRoleList', true);
|
||||
xhr.setRequestHeader('Abp.TenantId', tenantId);
|
||||
xhr.setRequestHeader('Content-type', 'application/json');
|
||||
var parm = {
|
||||
|
|
@ -330,7 +335,63 @@ var abp = abp || {};
|
|||
//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) {
|
||||
//Get TenantId first
|
||||
var tenancyName = document.getElementById('tenancyName').value;
|
||||
|
|
@ -356,6 +417,31 @@ var abp = abp || {};
|
|||
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.auth.clearToken();
|
||||
|
|
@ -419,9 +505,10 @@ var abp = abp || {};
|
|||
|
||||
//Inputs
|
||||
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, 'pwdMd5', 'PwdMd5', 'text', '');
|
||||
createSelect(modalUxContent, 'roleSelect', 'role', [])
|
||||
|
||||
//Buttons
|
||||
var authBtnWrapper = document.createElement('div');
|
||||
|
|
@ -444,9 +531,18 @@ var abp = abp || {};
|
|||
abp.swagger.login(loginCallback);
|
||||
};
|
||||
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');
|
||||
wrapper.className = 'wrapper';
|
||||
if (id == "tenancyName") {
|
||||
|
|
@ -470,6 +566,31 @@ var abp = abp || {};
|
|||
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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,42 +1,39 @@
|
|||
using Microsoft.IdentityModel.Tokens;
|
||||
using System.Text;
|
||||
|
||||
namespace IRaCIS.Core.Application.Auth
|
||||
public class JwtSetting
|
||||
{
|
||||
public class JwtSetting
|
||||
/// <summary>
|
||||
/// 颁发者
|
||||
/// </summary>
|
||||
public string Issuer { get; set; } = String.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 接收者
|
||||
/// </summary>
|
||||
public string Audience { get; set; } = String.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 令牌密码
|
||||
/// </summary>
|
||||
public string SecurityKey { get; set; } = String.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 过期时间
|
||||
/// </summary>
|
||||
public int TokenExpireMinute { get; set; }
|
||||
|
||||
//public Dictionary<string, object> Claims { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 签名
|
||||
/// </summary>
|
||||
public SigningCredentials Credentials
|
||||
{
|
||||
/// <summary>
|
||||
/// 颁发者
|
||||
/// </summary>
|
||||
public string Issuer { get; set; } = String.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 接收者
|
||||
/// </summary>
|
||||
public string Audience { get; set; } = String.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 令牌密码
|
||||
/// </summary>
|
||||
public string SecurityKey { get; set; } = String.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 过期时间
|
||||
/// </summary>
|
||||
public int TokenExpireDays { get; set; }
|
||||
|
||||
//public Dictionary<string, object> Claims { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 签名
|
||||
/// </summary>
|
||||
public SigningCredentials Credentials
|
||||
get
|
||||
{
|
||||
get
|
||||
{
|
||||
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(SecurityKey));
|
||||
return new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
|
||||
}
|
||||
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(SecurityKey));
|
||||
return new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -8,7 +8,9 @@ namespace IRaCIS.Core.Application.Auth
|
|||
|
||||
public interface ITokenService
|
||||
{
|
||||
string GetToken(IRaCISClaims user);
|
||||
string GetToken(UserTokenInfo user);
|
||||
|
||||
bool IsTokenExpired(string token);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -21,23 +23,24 @@ namespace IRaCIS.Core.Application.Auth
|
|||
_jwtSetting = option.Value;
|
||||
}
|
||||
|
||||
public string GetToken(IRaCISClaims user)
|
||||
public string GetToken(UserTokenInfo user)
|
||||
{
|
||||
//创建用户身份标识,可按需要添加更多信息
|
||||
var claims = new Claim[]
|
||||
{
|
||||
new Claim(Microsoft.IdentityModel.JsonWebTokens.JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
|
||||
new Claim(JwtIRaCISClaimType.Id, user.Id.ToString()),
|
||||
new Claim(JwtIRaCISClaimType.Name, user.FullName),
|
||||
new Claim(JwtIRaCISClaimType.RealName, user.RealName),
|
||||
new Claim(JwtIRaCISClaimType.Code,user.Code),
|
||||
new Claim(JwtIRaCISClaimType.IdentityUserId, user.IdentityUserId.ToString()),
|
||||
new Claim(JwtIRaCISClaimType.UserRoleId, user.UserRoleId.ToString()),
|
||||
new Claim(JwtIRaCISClaimType.UserName, user.UserName),
|
||||
new Claim(JwtIRaCISClaimType.FullName, user.FullName),
|
||||
new Claim(JwtIRaCISClaimType.UserTypeId,user.UserTypeId.ToString()),
|
||||
new Claim(JwtIRaCISClaimType.UserTypeEnum,user.UserTypeEnum.ToString()),
|
||||
new Claim(JwtIRaCISClaimType.UserTypeEnumInt,((int)user.UserTypeEnum).ToString()),
|
||||
new Claim(JwtIRaCISClaimType.UserTypeShortName,user.UserTypeShortName),
|
||||
new Claim(JwtIRaCISClaimType.PermissionStr,user.PermissionStr),
|
||||
new Claim(JwtIRaCISClaimType.IsZhiZhun,user.IsZhiZhun.ToString()),
|
||||
new Claim(JwtIRaCISClaimType.IsTestUser,user.IsTestUser.ToString())
|
||||
new Claim(JwtIRaCISClaimType.IsTestUser,user.IsTestUser.ToString()),
|
||||
new Claim(JwtIRaCISClaimType.HospitalGroupList,user.HospitalGroupList.ToJsonStr())
|
||||
};
|
||||
|
||||
////创建令牌
|
||||
|
|
@ -47,13 +50,27 @@ namespace IRaCIS.Core.Application.Auth
|
|||
signingCredentials: _jwtSetting.Credentials,
|
||||
claims: claims,
|
||||
notBefore: DateTime.Now,
|
||||
expires: DateTime.Now.AddDays(_jwtSetting.TokenExpireDays)
|
||||
expires: DateTime.Now.AddMinutes(_jwtSetting.TokenExpireMinute)
|
||||
);
|
||||
|
||||
string jwtToken = new JwtSecurityTokenHandler().WriteToken(token);
|
||||
return jwtToken;
|
||||
|
||||
}
|
||||
|
||||
public bool IsTokenExpired(string token)
|
||||
{
|
||||
var handler = new JwtSecurityTokenHandler();
|
||||
try
|
||||
{
|
||||
var jwtToken = handler.ReadJwtToken(token);
|
||||
return jwtToken.ValidTo < DateTime.UtcNow;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return true; // 无效 Token 也视为已过期
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,29 @@
|
|||
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;
|
||||
|
||||
public List<HospitalGroupInfo> HospitalGroupList { get; set; } = new List<HospitalGroupInfo>();
|
||||
}
|
||||
}
|
||||
|
|
@ -50,7 +50,7 @@ public class EncryptionRequestMiddleware
|
|||
{
|
||||
try
|
||||
{
|
||||
var decryptedSegment = AesEncryption.Decrypt(pathSegments[i], decryptedSymmetricKey);
|
||||
var decryptedSegment = Infrastructure.Encryption.AesEncryption.Decrypt(pathSegments[i], decryptedSymmetricKey);
|
||||
pathSegments[i] = decryptedSegment;
|
||||
}
|
||||
catch
|
||||
|
|
@ -74,7 +74,7 @@ public class EncryptionRequestMiddleware
|
|||
foreach (var param in queryParams)
|
||||
{
|
||||
var encryptedValue = param.Value;
|
||||
var decryptedValue = AesEncryption.Decrypt(encryptedValue, decryptedSymmetricKey);
|
||||
var decryptedValue = Infrastructure.Encryption.AesEncryption.Decrypt(encryptedValue, decryptedSymmetricKey);
|
||||
decryptedQueryParams[param.Key] = decryptedValue;
|
||||
}
|
||||
|
||||
|
|
@ -104,7 +104,7 @@ public class EncryptionRequestMiddleware
|
|||
foreach (var property in encryptedJson.Properties())
|
||||
{
|
||||
var encryptedValue = property.Value.ToString();
|
||||
var decryptedValue = AesEncryption.Decrypt(encryptedValue, decryptedSymmetricKey);
|
||||
var decryptedValue = Infrastructure.Encryption.AesEncryption.Decrypt(encryptedValue, decryptedSymmetricKey);
|
||||
decryptedJson[property.Name] = decryptedValue;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ public class LimitUserRequestAuthorization(
|
|||
|
||||
|
||||
//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;
|
||||
|
||||
//设置当前用户最新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)
|
||||
{
|
||||
|
||||
var cacheTime = await _fusionCache.GetOrDefaultAsync<string>(CacheKeys.UserAutoLoginOut(_userInfo.Id));
|
||||
var cacheTime = await _fusionCache.GetOrDefaultAsync<string>(CacheKeys.UserAutoLoginOut(_userInfo.IdentityUserId));
|
||||
|
||||
//过期了 需要自动退出
|
||||
if (string.IsNullOrEmpty(cacheTime))
|
||||
|
|
@ -80,7 +80,7 @@ public class LimitUserRequestAuthorization(
|
|||
}
|
||||
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
|
||||
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,12 @@
|
|||
using IRaCIS.Core.Application.BusinessFilter;
|
||||
using IRaCIS.Application.Contracts;
|
||||
using IRaCIS.Core.Application.BusinessFilter;
|
||||
using IRaCIS.Core.Application.Helper;
|
||||
using IRaCIS.Core.Infrastructure.Encryption;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
|
@ -13,7 +17,10 @@ using static IRaCIS.Core.Domain.Share.StaticData;
|
|||
|
||||
namespace IRaCIS.Core.Application.Filter;
|
||||
|
||||
public class TrialGlobalLimitActionFilter(IFusionCache _fusionCache, IUserInfo _userInfo, IRepository<Trial> _trialRepository) : IAsyncActionFilter
|
||||
public class TrialGlobalLimitActionFilter(IFusionCache _fusionCache, IUserInfo _userInfo, IRepository<Trial> _trialRepository,
|
||||
IOptionsMonitor<ServiceVerifyConfigOption> _basicSystemConfigConfig,
|
||||
IRepository<HIRHospital> _hirHospitalRepository,
|
||||
IStringLocalizer _localizer) : IAsyncActionFilter
|
||||
{
|
||||
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
|
||||
{
|
||||
|
|
@ -113,9 +120,9 @@ public class TrialGlobalLimitActionFilter(IFusionCache _fusionCache, IUserInfo _
|
|||
|
||||
trialIdStr = matchResult.Value;
|
||||
|
||||
var trialStatusStr = await _fusionCache.GetOrSetAsync(CacheKeys.Trial(trialIdStr), _ => CacheHelper.GetTrialStatusAsync(Guid.Parse(trialIdStr), _trialRepository), TimeSpan.FromDays(7));
|
||||
var trialInfo = await _fusionCache.GetOrSetAsync(CacheKeys.Trial(trialIdStr), _ => CacheHelper.GetTrialStatusAsync(Guid.Parse(trialIdStr), _trialRepository), TimeSpan.FromDays(7));
|
||||
|
||||
if (string.IsNullOrWhiteSpace(trialStatusStr))
|
||||
if (string.IsNullOrWhiteSpace(trialInfo?.TrialStatusStr))
|
||||
{
|
||||
|
||||
//数据库 检查该项目Id不对
|
||||
|
|
@ -145,22 +152,83 @@ public class TrialGlobalLimitActionFilter(IFusionCache _fusionCache, IUserInfo _
|
|||
//通过path 或者body 找到trialId 了
|
||||
if (!string.IsNullOrWhiteSpace(trialIdStr))
|
||||
{
|
||||
var trialStatusStr = await _fusionCache.GetOrSetAsync(CacheKeys.Trial(trialIdStr), _ => CacheHelper.GetTrialStatusAsync(Guid.Parse(trialIdStr), _trialRepository), TimeSpan.FromDays(7));
|
||||
var trialInfo = await _fusionCache.GetOrSetAsync(CacheKeys.Trial(trialIdStr), _ => CacheHelper.GetTrialStatusAsync(Guid.Parse(trialIdStr), _trialRepository), TimeSpan.FromDays(7));
|
||||
|
||||
// 这里是统一拦截 项目有关的操作允许情况(特殊的地方,比如项目配置(有的在多种状态(初始化,ongoing)都可以操作,有的仅仅在Initializing)还有 项目添加和更新,不走这里,特殊处理,不然在这里显得很乱,判断是哪个接口)
|
||||
if (trialStatusStr == StaticData.TrialState.TrialOngoing || optActions.Any(t => t == TrialOpt.BeforeOngoingCantOpt))
|
||||
var hospitalInfo = await _fusionCache.GetOrSetAsync(CacheKeys.Hospital, _ => CacheHelper.GetHospitalCode(_hirHospitalRepository), TimeSpan.FromDays(7));
|
||||
|
||||
var trialStatusStr = string.Empty;
|
||||
|
||||
if (trialInfo != null)
|
||||
{
|
||||
trialStatusStr = trialInfo.TrialStatusStr;
|
||||
|
||||
await next();
|
||||
var activationCode = trialInfo.AuthorizationEncrypt;
|
||||
|
||||
if (string.IsNullOrEmpty(activationCode))
|
||||
{
|
||||
//context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["项目未进行授权之前,不能进行操作"]));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
var decodedText = string.Empty;
|
||||
try
|
||||
{
|
||||
//解析加密信息
|
||||
decodedText = AesEncryption.Decrypt(activationCode, _basicSystemConfigConfig.CurrentValue.AESKey, "Trial_AuthorizationEncrypt");
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
//该授权码与该项目不匹配(无法解密)
|
||||
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["TrialGlobalLimit_AuthorizationCodeError"]));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
var hospitalCode = hospitalInfo.HospitalCode;
|
||||
var authInfo = JsonConvert.DeserializeObject<TrialAuthorizationInfo>(decodedText);
|
||||
|
||||
if (authInfo != null)
|
||||
{
|
||||
if (authInfo.TrialCode != trialInfo.TrialCode || authInfo.CreateUserId != trialInfo.CreateUserId || authInfo.TrialId != trialInfo.TrialId
|
||||
|| authInfo.HospitalCode != hospitalCode || trialInfo.CriterionTypeList.Except(authInfo.CriterionTypeList).Count() != 0)
|
||||
{
|
||||
// 您的操作被禁止,系统检测到该项目授权码与该项目授权配置信息不一致,请还原项目授权配置信息!
|
||||
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["TrialGlobalLimit_AuthorizationCodeInfoError"]));
|
||||
return;
|
||||
}
|
||||
|
||||
if (DateTime.Now > authInfo.AuthorizationDeadLineDate.Value.AddDays(15))
|
||||
{
|
||||
//当前时间已经超过项目授权截止时间半个月,请重新获取项目授权后再进行操作!
|
||||
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["TrialGlobalLimit_AuthorizationCodeAfterDeadLine15Days"]));
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["该授权码与该项目不匹配"]));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 这里是统一拦截 项目有关的操作允许情况(特殊的地方,比如项目配置(有的在多种状态(初始化,ongoing)都可以操作,有的仅仅在Initializing)还有 项目添加和更新,不走这里,特殊处理,不然在这里显得很乱,判断是哪个接口)
|
||||
if (trialStatusStr == StaticData.TrialState.TrialOngoing || optActions.Any(t => t == TrialOpt.BeforeOngoingCantOpt))
|
||||
{
|
||||
|
||||
await next();
|
||||
|
||||
}
|
||||
// 项目停止、或者完成 不允许操作
|
||||
else
|
||||
{
|
||||
//---本次请求被配置规则拦截:项目状态处于进行中时,才允许操作,若此处逻辑有误,请联系开发人员修改
|
||||
context.Result = new JsonResult(ResponseOutput.NotOk(I18n.T("TrialResource_InterceptedProjectStatusRule")));
|
||||
|
||||
}
|
||||
}
|
||||
// 项目停止、或者完成 不允许操作
|
||||
else
|
||||
{
|
||||
//---本次请求被配置规则拦截:项目状态处于进行中时,才允许操作,若此处逻辑有误,请联系开发人员修改
|
||||
context.Result = new JsonResult(ResponseOutput.NotOk(I18n.T("TrialResource_InterceptedProjectStatusRule")));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
//添加项目 签名系统文档的时候 不做拦截 但是更新项目 签名项目文档的时候需要拦截
|
||||
|
|
|
|||
|
|
@ -1,196 +0,0 @@
|
|||
using IRaCIS.Core.Application.Helper;
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using System.Text.RegularExpressions;
|
||||
using ZiggyCreatures.Caching.Fusion;
|
||||
using static IRaCIS.Core.Domain.Share.StaticData;
|
||||
|
||||
namespace IRaCIS.Core.Application.Filter;
|
||||
|
||||
/// <summary>
|
||||
/// 主要为了 处理项目结束 锁库,不允许操作
|
||||
/// </summary>
|
||||
public class TrialResourceFilter : Attribute, IAsyncResourceFilter
|
||||
{
|
||||
private readonly IUserInfo _userInfo;
|
||||
private readonly IFusionCache _fusionCache;
|
||||
public IStringLocalizer _localizer;
|
||||
private readonly IRepository<Trial> _trialRepository;
|
||||
private readonly List<string> _trialOptList = new List<string>();
|
||||
|
||||
|
||||
public TrialResourceFilter(IFusionCache fusionCache, IRepository<Trial> trialRepository, IStringLocalizer localizer, IUserInfo userInfo, string trialOpt = null, string trialOpt2 = null, string trialOpt3 = null)
|
||||
{
|
||||
_fusionCache = fusionCache;
|
||||
_userInfo = userInfo;
|
||||
_localizer = localizer;
|
||||
_trialRepository = trialRepository;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(trialOpt)) _trialOptList.Add(trialOpt.Trim());
|
||||
if (!string.IsNullOrWhiteSpace(trialOpt2)) _trialOptList.Add(trialOpt2.Trim());
|
||||
if (!string.IsNullOrWhiteSpace(trialOpt3)) _trialOptList.Add(trialOpt3.Trim());
|
||||
|
||||
}
|
||||
|
||||
//优先选择异步的方法
|
||||
public async Task OnResourceExecutionAsync(ResourceExecutingContext context, ResourceExecutionDelegate next)
|
||||
{
|
||||
// var typeFilter = context.ActionDescriptor.EndpointMetadata.Where(t => t.GetType() == typeof(TypeFilterAttribute)).Select(t => (TypeFilterAttribute)t).ToList().FirstOrDefault();
|
||||
//var _trialOptList= typeFilter.Arguments.Select(t => t.ToString()).ToList();
|
||||
|
||||
// 获取当前请求的 Host 信息
|
||||
var requestHost = context.HttpContext.Request.Host;
|
||||
|
||||
// 检查请求是否来自 localhost:6100
|
||||
if (requestHost.Host == "localhost" && (requestHost.Port == 6100|| requestHost.Port==3305))
|
||||
{
|
||||
await next.Invoke();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#region 处理新的用户类型,不能操作项目相关接口
|
||||
|
||||
// 后期列举出具体的类型,其他任何用户类型,都不允许操作
|
||||
if (_userInfo.UserTypeEnumInt == (int)UserTypeEnum.CRA && _userInfo.RequestUrl.ToLower() != "TrialDocument/userConfirm".ToLower())
|
||||
{
|
||||
//---对不起,您的账户没有操作权限。
|
||||
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["TrialResource_NoAccessPermission"]));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
//TrialId 传递的途径多种,可能在path 可能在body 可能在数组中,也可能在对象中,可能就在url
|
||||
var trialIdStr = string.Empty;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(context.HttpContext.Request.Query["trialId"]))
|
||||
{
|
||||
trialIdStr = context.HttpContext.Request.Query["trialId"];
|
||||
}
|
||||
|
||||
//先尝试从path中取TrialId
|
||||
else if (context.RouteData.Values.Keys.Any(t => t.Contains("trialId")))
|
||||
{
|
||||
var index = context.RouteData.Values.Keys.ToList().IndexOf("trialId");
|
||||
trialIdStr = context.RouteData.Values.Values.ToList()[index] as string;
|
||||
}
|
||||
else if (context.HttpContext.Request.Headers["Referer"].ToString().Contains("trialId"))
|
||||
{
|
||||
var headerStr = context.HttpContext.Request.Headers["Referer"].ToString();
|
||||
|
||||
var trialIdIndex = headerStr.IndexOf("trialId");
|
||||
|
||||
|
||||
var matchResult = Regex.Match(headerStr.Substring(trialIdIndex), @"[0-9a-fA-F]{8}(-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}");
|
||||
|
||||
if (matchResult.Success)
|
||||
{
|
||||
trialIdStr = matchResult.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
//---正则取请求Refer 中trialId 失败,请联系开发人员核查
|
||||
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["TrialResource_ReferTrialIdFailed"]));
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
#region body 中取数据
|
||||
|
||||
//设置可以多次读
|
||||
context.HttpContext.Request.EnableBuffering();
|
||||
var reader = new StreamReader(context.HttpContext.Request.Body);
|
||||
var contentFromBody = await reader.ReadToEndAsync();
|
||||
//读取后,流的位置还原
|
||||
context.HttpContext.Request.Body.Seek(0, SeekOrigin.Begin);
|
||||
//context.HttpContext.Request.Body.Position = 0;
|
||||
|
||||
//找到参数位置在字符串中的索引
|
||||
var trialIdIndex = contentFromBody.IndexOf("\"TrialId\"", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
if (trialIdIndex > -1)
|
||||
{
|
||||
// (?<="trialId" *: *").*?(?=",)
|
||||
|
||||
//使用正则 [0-9a-fA-F]{8}(-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}
|
||||
|
||||
var matchResult = Regex.Match(contentFromBody.Substring(trialIdIndex), @"[0-9a-fA-F]{8}(-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}");
|
||||
|
||||
if (matchResult.Success)
|
||||
{
|
||||
//有可能匹配错误 "trialId":"","documentId":"b8180000-3e2c-0016-9fe0-08da33f96236" 从缓存里面验证下
|
||||
|
||||
trialIdStr = matchResult.Value;
|
||||
|
||||
var trialStatusStr = await _fusionCache.GetOrSetAsync(CacheKeys.Trial(trialIdStr), _ => CacheHelper.GetTrialStatusAsync(Guid.Parse(trialIdStr), _trialRepository), TimeSpan.FromDays(7));
|
||||
|
||||
if (string.IsNullOrWhiteSpace(trialStatusStr))
|
||||
{
|
||||
|
||||
//数据库 检查该项目Id不对
|
||||
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["TrialResource_ReferTrialIdFailed"]));
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//---正则取请求Refer 中trialId 失败,请联系开发人员核查
|
||||
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["TrialResource_ReferTrialIdFailed"]));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//使用字符串取 如果是swagger 可能有时取的不对 因为空格的原因
|
||||
//trialIdStr = contentFromBody.Substring(trialIdIndex + "TrialId".Length + 4, 3
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
//通过path 或者body 找到trialId 了
|
||||
if (!string.IsNullOrWhiteSpace(trialIdStr))
|
||||
{
|
||||
var trialStatusStr = await _fusionCache.GetOrSetAsync(CacheKeys.Trial(trialIdStr), _ => CacheHelper.GetTrialStatusAsync(Guid.Parse(trialIdStr), _trialRepository), TimeSpan.FromDays(7));
|
||||
|
||||
// 这里是统一拦截 项目有关的操作允许情况(特殊的地方,比如项目配置(有的在多种状态(初始化,ongoing)都可以操作,有的仅仅在Initializing)还有 项目添加和更新,不走这里,特殊处理,不然在这里显得很乱,判断是哪个接口)
|
||||
if (trialStatusStr == StaticData.TrialState.TrialOngoing || _trialOptList.Any(t => t == TrialOpt.BeforeOngoingCantOpt))
|
||||
{
|
||||
|
||||
await next.Invoke();
|
||||
|
||||
}
|
||||
// 项目停止、或者完成 不允许操作
|
||||
else
|
||||
{
|
||||
//---本次请求被配置规则拦截:项目状态处于进行中时,才允许操作,若此处逻辑有误,请联系开发人员修改
|
||||
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["TrialResource_InterceptedProjectStatusRule"]));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
//添加项目 签名系统文档的时候 不做拦截 但是更新项目 签名项目文档的时候需要拦截
|
||||
else if (_trialOptList.Any(t => t == TrialOpt.AddOrUpdateTrial || t == TrialOpt.SignSystemDocNoTrialId))
|
||||
{
|
||||
await next.Invoke();
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
//如果项目相关接口没有传递trialId 会来到这里,提醒,以便修改
|
||||
|
||||
//---该接口参数中,没有传递项目编号,请核对。
|
||||
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["TrialResource_MissingProjectNumber"]));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Http;
|
|||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using ILogger = Microsoft.Extensions.Logging.ILogger;
|
||||
|
||||
namespace IRaCIS.Core.Application.Service.BusinessFilter;
|
||||
|
||||
|
|
|
|||
|
|
@ -41,19 +41,19 @@ public class LimitUserRequestAuthorizationEndpointFilter(
|
|||
}
|
||||
|
||||
// 获取缓存中的用户 token
|
||||
var cacheUserToken = await _fusionCache.GetOrDefaultAsync<string>(CacheKeys.UserToken(_userInfo.Id));
|
||||
var cacheUserToken = await _fusionCache.GetOrDefaultAsync<string>(CacheKeys.UserToken(_userInfo.IdentityUserId));
|
||||
|
||||
// 缓存中没有取到 token
|
||||
if (string.IsNullOrWhiteSpace(cacheUserToken))
|
||||
{
|
||||
// 设置当前用户最新 token
|
||||
await _fusionCache.SetAsync(CacheKeys.UserToken(_userInfo.Id), _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.UserToken(_userInfo.IdentityUserId), _userInfo.UserToken, TimeSpan.FromDays(7));
|
||||
await _fusionCache.SetAsync(CacheKeys.UserAutoLoginOut(_userInfo.IdentityUserId), DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), TimeSpan.FromMinutes(minutes));
|
||||
}
|
||||
// 如果是同一个用户
|
||||
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))
|
||||
|
|
@ -63,7 +63,7 @@ public class LimitUserRequestAuthorizationEndpointFilter(
|
|||
}
|
||||
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
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
using IRaCIS.Core.Application.Helper;
|
||||
using IRaCIS.Application.Contracts;
|
||||
using IRaCIS.Core.Application.Helper;
|
||||
using IRaCIS.Core.Infrastructure.Encryption;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
|
@ -24,7 +28,10 @@ public class TrialGlobalLimitAttribute : Attribute
|
|||
|
||||
|
||||
|
||||
public class TrialGlobalLimitEndpointFilter(IFusionCache _fusionCache, IUserInfo _userInfo, IRepository<Trial> _trialRepository) : IEndpointFilter
|
||||
public class TrialGlobalLimitEndpointFilter(IFusionCache _fusionCache, IUserInfo _userInfo, IRepository<Trial> _trialRepository,
|
||||
IOptionsMonitor<ServiceVerifyConfigOption> _basicSystemConfigConfig,
|
||||
IRepository<HIRHospital> _hirHospitalRepository,
|
||||
IStringLocalizer _localizer) : IEndpointFilter
|
||||
|
||||
{
|
||||
public async ValueTask<object?> InvokeAsync(EndpointFilterInvocationContext context, EndpointFilterDelegate next)
|
||||
|
|
@ -121,9 +128,9 @@ public class TrialGlobalLimitEndpointFilter(IFusionCache _fusionCache, IUserInfo
|
|||
|
||||
trialIdStr = matchResult.Value;
|
||||
|
||||
var trialStatusStr = await _fusionCache.GetOrSetAsync(CacheKeys.Trial(trialIdStr), _ => CacheHelper.GetTrialStatusAsync(Guid.Parse(trialIdStr), _trialRepository), TimeSpan.FromDays(7));
|
||||
var trialInfo = await _fusionCache.GetOrSetAsync(CacheKeys.Trial(trialIdStr), _ => CacheHelper.GetTrialStatusAsync(Guid.Parse(trialIdStr), _trialRepository), TimeSpan.FromDays(7));
|
||||
|
||||
if (string.IsNullOrWhiteSpace(trialStatusStr))
|
||||
if (string.IsNullOrWhiteSpace(trialInfo?.TrialStatusStr))
|
||||
{
|
||||
|
||||
//数据库 检查该项目Id不对
|
||||
|
|
@ -151,22 +158,81 @@ public class TrialGlobalLimitEndpointFilter(IFusionCache _fusionCache, IUserInfo
|
|||
//通过path 或者body 找到trialId 了
|
||||
if (!string.IsNullOrWhiteSpace(trialIdStr))
|
||||
{
|
||||
var trialStatusStr = await _fusionCache.GetOrSetAsync(CacheKeys.Trial(trialIdStr), _ => CacheHelper.GetTrialStatusAsync(Guid.Parse(trialIdStr), _trialRepository), TimeSpan.FromDays(7));
|
||||
var trialInfo = await _fusionCache.GetOrSetAsync(CacheKeys.Trial(trialIdStr), _ => CacheHelper.GetTrialStatusAsync(Guid.Parse(trialIdStr), _trialRepository), TimeSpan.FromDays(7));
|
||||
|
||||
// 这里是统一拦截 项目有关的操作允许情况(特殊的地方,比如项目配置(有的在多种状态(初始化,ongoing)都可以操作,有的仅仅在Initializing)还有 项目添加和更新,不走这里,特殊处理,不然在这里显得很乱,判断是哪个接口)
|
||||
if (trialStatusStr == StaticData.TrialState.TrialOngoing || optActions.Any(t => t == TrialOpt.BeforeOngoingCantOpt))
|
||||
var hospitalInfo = await _fusionCache.GetOrSetAsync(CacheKeys.Hospital, _ => CacheHelper.GetHospitalCode(_hirHospitalRepository), TimeSpan.FromDays(7));
|
||||
|
||||
var trialStatusStr = string.Empty;
|
||||
|
||||
|
||||
if (trialInfo != null)
|
||||
{
|
||||
trialStatusStr = trialInfo.TrialStatusStr;
|
||||
|
||||
return await next(context);
|
||||
var activationCode = trialInfo.AuthorizationEncrypt;
|
||||
|
||||
if (string.IsNullOrEmpty(activationCode))
|
||||
{
|
||||
//context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["项目未进行授权之前,不能进行操作"]));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
var decodedText = string.Empty;
|
||||
try
|
||||
{
|
||||
//解析加密信息
|
||||
decodedText = AesEncryption.Decrypt(activationCode, _basicSystemConfigConfig.CurrentValue.AESKey, "Trial_AuthorizationEncrypt");
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
|
||||
return Results.Json(ResponseOutput.NotOk(_localizer["该授权码与该项目不匹配"]));
|
||||
}
|
||||
|
||||
|
||||
var hospitalCode = hospitalInfo.HospitalCode;
|
||||
var authInfo = JsonConvert.DeserializeObject<TrialAuthorizationInfo>(decodedText);
|
||||
|
||||
if (authInfo != null)
|
||||
{
|
||||
if (authInfo.TrialCode != trialInfo.TrialCode || authInfo.CreateUserId != trialInfo.CreateUserId || authInfo.TrialId != trialInfo.TrialId
|
||||
|| authInfo.HospitalCode != hospitalCode || trialInfo.CriterionTypeList.Except(authInfo.CriterionTypeList).Count() != 0)
|
||||
{
|
||||
return Results.Json(ResponseOutput.NotOk(_localizer["您的操作被禁止,系统检测到该项目授权码与该项目授权配置信息不一致,请还原项目授权配置信息!"]));
|
||||
}
|
||||
|
||||
if (DateTime.Now > authInfo.AuthorizationDeadLineDate.Value.AddDays(15))
|
||||
{
|
||||
return Results.Json(ResponseOutput.NotOk(_localizer["当前时间已经超过项目授权截止时间半个月,请重新获取项目授权后再进行操作!"]));
|
||||
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["该授权码与该项目不匹配"]));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 这里是统一拦截 项目有关的操作允许情况(特殊的地方,比如项目配置(有的在多种状态(初始化,ongoing)都可以操作,有的仅仅在Initializing)还有 项目添加和更新,不走这里,特殊处理,不然在这里显得很乱,判断是哪个接口)
|
||||
if (trialStatusStr == StaticData.TrialState.TrialOngoing || optActions.Any(t => t == TrialOpt.BeforeOngoingCantOpt))
|
||||
{
|
||||
|
||||
return await next(context);
|
||||
|
||||
}
|
||||
// 项目停止、或者完成 不允许操作
|
||||
else
|
||||
{
|
||||
//---本次请求被配置规则拦截:项目状态处于进行中时,才允许操作,若此处逻辑有误,请联系开发人员修改
|
||||
return Results.Json(ResponseOutput.NotOk(I18n.T("TrialResource_InterceptedProjectStatusRule")));
|
||||
|
||||
}
|
||||
}
|
||||
// 项目停止、或者完成 不允许操作
|
||||
else
|
||||
{
|
||||
//---本次请求被配置规则拦截:项目状态处于进行中时,才允许操作,若此处逻辑有误,请联系开发人员修改
|
||||
return Results.Json(ResponseOutput.NotOk(I18n.T("TrialResource_InterceptedProjectStatusRule")));
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
//添加项目 签名系统文档的时候 不做拦截 但是更新项目 签名项目文档的时候需要拦截
|
||||
|
|
|
|||
|
|
@ -36,6 +36,15 @@ public class ServiceVerifyConfigOption
|
|||
|
||||
public string ThirdPdfUrl { get; set; }
|
||||
|
||||
public string AESKey { get; set; }
|
||||
|
||||
|
||||
public int CmoveIntervalMinutes { get; set; }
|
||||
|
||||
public int CmoveInstanceIntervalMinutes { get; set; }
|
||||
|
||||
|
||||
public bool OpenImageShare { get; set; }
|
||||
}
|
||||
|
||||
public class SystemEmailSendConfig
|
||||
|
|
@ -51,6 +60,8 @@ public class SystemEmailSendConfig
|
|||
|
||||
public string SiteUrl { get; set; } = string.Empty;
|
||||
|
||||
public string SystemShortName { get; set; } = string.Empty;
|
||||
|
||||
public string OrganizationName { get; set; } = string.Empty;
|
||||
public string OrganizationNameCN { get; set; } = string.Empty;
|
||||
|
||||
|
|
@ -66,11 +77,14 @@ public class SystemEmailSendConfig
|
|||
|
||||
public bool IsOpenErrorNoticeEmail { get; set; }
|
||||
|
||||
public List<string> ErrorNoticeEmailList { get; set; } =new List<string>();
|
||||
public string EmailRegexStr { get; set; }
|
||||
|
||||
public List<string> ErrorNoticeEmailList { get; set; } = new List<string>();
|
||||
}
|
||||
|
||||
public class SystemEmailSendConfigView
|
||||
{
|
||||
public string SystemShortName { get; set; } = string.Empty;
|
||||
public string CompanyName { get; set; } = string.Empty;
|
||||
|
||||
public string CompanyNameCN { get; set; } = string.Empty;
|
||||
|
|
@ -78,6 +92,10 @@ public class SystemEmailSendConfigView
|
|||
public string CompanyShortName { get; set; } = string.Empty;
|
||||
|
||||
public string CompanyShortNameCN { get; set; } = string.Empty;
|
||||
|
||||
public string EmailRegexStr { get; set; }
|
||||
|
||||
public bool OpenImageShare { get; set; }
|
||||
}
|
||||
|
||||
public class SystemPacsConfig
|
||||
|
|
@ -140,7 +158,7 @@ public static class AppSettings
|
|||
case nameof(Doctor):
|
||||
return IRaCISBasicConfig.DoctorCodePrefix + codeInt.ToString("D4");
|
||||
|
||||
case nameof(User):
|
||||
case nameof(IdentityUser):
|
||||
return IRaCISBasicConfig.UserCodePrefix + codeInt.ToString("D4");
|
||||
|
||||
case nameof(QCChallenge):
|
||||
|
|
|
|||
|
|
@ -10,5 +10,6 @@ global using Microsoft.Extensions.Localization;
|
|||
global using AutoMapper;
|
||||
global using IRaCIS.Core.Domain.Share;
|
||||
global using IRaCIS.Core.Application.BusinessFilter;
|
||||
global using Serilog;
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
namespace IRaCIS.Core.Application.Helper;
|
||||
using IRaCIS.Application.Contracts;
|
||||
|
||||
namespace IRaCIS.Core.Application.Helper;
|
||||
|
||||
|
||||
public static class CacheKeys
|
||||
|
|
@ -27,6 +29,11 @@ public static class CacheKeys
|
|||
//超时没请求接口自动退出
|
||||
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>
|
||||
|
|
@ -56,13 +63,20 @@ public static class CacheKeys
|
|||
/// <returns></returns>
|
||||
public static string StartRestTime(Guid userId) => $"{userId}StartRestTime";
|
||||
|
||||
|
||||
|
||||
public static string CmoveStudyId(string studyIdStr) => $"CmoveStudyId:{studyIdStr}";
|
||||
|
||||
public static string Hospital => $"Hospital";
|
||||
|
||||
}
|
||||
|
||||
public static class CacheHelper
|
||||
{
|
||||
public static async Task<string?> GetTrialStatusAsync(Guid trialId, IRepository<Trial> _trialRepository)
|
||||
public static async Task<TrialCacheInfo> GetTrialStatusAsync(Guid trialId, IRepository<Trial> _trialRepository)
|
||||
{
|
||||
var statusStr = await _trialRepository.Where(t => t.Id == trialId, ignoreQueryFilters: true).Select(t => t.TrialStatusStr).FirstOrDefaultAsync();
|
||||
var statusStr = await _trialRepository.Where(t => t.Id == trialId, ignoreQueryFilters: true).Select(t => new TrialCacheInfo { TrialId = t.Id, TrialStatusStr = t.TrialStatusStr, CriterionTypes = t.CriterionTypes, AuthorizationEncrypt = t.AuthorizationEncrypt, AuthorizationDate = t.AuthorizationDate, CreateUserId = t.CreateUserId, TrialCode = t.TrialCode })
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
return statusStr;
|
||||
}
|
||||
|
|
@ -73,4 +87,10 @@ public static class CacheHelper
|
|||
|
||||
return list;
|
||||
}
|
||||
|
||||
public static async Task<HIRHospital> GetHospitalCode(IRepository<HIRHospital> _hirHospitalRepository)
|
||||
{
|
||||
return await _hirHospitalRepository.Where(t => t.IsDefault == true).FirstNotNullAsync();
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,190 @@
|
|||
using FellowOakDicom;
|
||||
using FellowOakDicom.Media;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace IRaCIS.Core.Application.Helper
|
||||
{
|
||||
|
||||
public class StudyDIRInfo
|
||||
{
|
||||
public Guid SubjectVisitId { get; set; }
|
||||
// Study
|
||||
public Guid DicomStudyId { get; set; }
|
||||
|
||||
public string PatientId { get; set; }
|
||||
public string PatientName { get; set; }
|
||||
public string PatientBirthDate { get; set; }
|
||||
public string PatientSex { get; set; }
|
||||
|
||||
public string StudyInstanceUid { get; set; }
|
||||
public string StudyId { get; set; }
|
||||
public string DicomStudyDate { get; set; }
|
||||
public string DicomStudyTime { get; set; }
|
||||
public string AccessionNumber { get; set; }
|
||||
public string StudyDescription { get; set; }
|
||||
|
||||
// Series
|
||||
public string SeriesInstanceUid { get; set; }
|
||||
public string Modality { get; set; }
|
||||
public string DicomSeriesDate { get; set; }
|
||||
public string DicomSeriesTime { get; set; }
|
||||
public int SeriesNumber { get; set; }
|
||||
public string SeriesDescription { get; set; }
|
||||
|
||||
// Instance
|
||||
public Guid InstanceId { get; set; }
|
||||
public string SopInstanceUid { get; set; }
|
||||
public string SOPClassUID { get; set; }
|
||||
public int InstanceNumber { get; set; }
|
||||
public string MediaStorageSOPClassUID { get; set; }
|
||||
public string MediaStorageSOPInstanceUID { get; set; }
|
||||
public string TransferSyntaxUID { get; set; }
|
||||
}
|
||||
public static class DicomDIRHelper
|
||||
{
|
||||
|
||||
public static async Task GenerateStudyDIRAndUploadAsync(List<StudyDIRInfo> list, Dictionary<string, string> dic, string ossFolder, IOSSService _oSSService)
|
||||
{
|
||||
var mappings = new List<string>();
|
||||
int index = 1;
|
||||
// 全局关闭所有 VR 字段验证
|
||||
|
||||
var dicomDir = new DicomDirectory();
|
||||
|
||||
foreach (var item in list.OrderBy(t => t.SeriesNumber).ThenBy(t => t.InstanceNumber))
|
||||
{
|
||||
var dicomUid = DicomUID.Enumerate().FirstOrDefault(uid => uid.UID == item.TransferSyntaxUID);
|
||||
|
||||
if (dicomUid != null)
|
||||
{
|
||||
var ts = DicomTransferSyntax.Query(dicomUid);
|
||||
|
||||
var dataset = new DicomDataset(ts)
|
||||
{
|
||||
{ DicomTag.PatientID, item.PatientId ?? string.Empty },
|
||||
{ DicomTag.PatientName, item.PatientName ?? string.Empty },
|
||||
{ DicomTag.PatientBirthDate, item.PatientBirthDate?.Replace("-","") ?? string.Empty },
|
||||
{ DicomTag.PatientSex, item.PatientSex ?? string.Empty },
|
||||
|
||||
{ DicomTag.StudyInstanceUID, item.StudyInstanceUid ?? string.Empty },
|
||||
{ DicomTag.StudyID, item.StudyId ?? string.Empty },
|
||||
{ DicomTag.StudyDate, item.DicomStudyDate ?? string.Empty },
|
||||
{ DicomTag.StudyTime, item.DicomStudyTime ?? string.Empty },
|
||||
{ DicomTag.AccessionNumber, item.AccessionNumber ?? string.Empty },
|
||||
{ DicomTag.StudyDescription, item.StudyDescription ?? string.Empty },
|
||||
|
||||
{ DicomTag.SeriesInstanceUID, item.SeriesInstanceUid ?? string.Empty },
|
||||
{ DicomTag.Modality, item.Modality ?? string.Empty },
|
||||
{ DicomTag.SeriesDate, item.DicomSeriesDate ?? string.Empty },
|
||||
{ DicomTag.SeriesTime, item.DicomSeriesTime ?? string.Empty },
|
||||
{ DicomTag.SeriesNumber, item.SeriesNumber.ToString() ?? string.Empty },
|
||||
{ DicomTag.SeriesDescription, item.SeriesDescription ?? string.Empty },
|
||||
|
||||
{ DicomTag.SOPInstanceUID, item.SopInstanceUid ?? string.Empty },
|
||||
{ DicomTag.SOPClassUID, item.SOPClassUID ?? string.Empty },
|
||||
{ DicomTag.InstanceNumber, item.InstanceNumber.ToString() ?? string.Empty },
|
||||
{ DicomTag.MediaStorageSOPClassUID, item.MediaStorageSOPClassUID ?? string.Empty },
|
||||
{ DicomTag.MediaStorageSOPInstanceUID, item.MediaStorageSOPInstanceUID ?? string.Empty },
|
||||
{ DicomTag.TransferSyntaxUID, item.TransferSyntaxUID ?? string.Empty },
|
||||
};
|
||||
|
||||
var dicomFile = new DicomFile(dataset);
|
||||
|
||||
// 文件名递增格式:IM_00001, IM_00002, ...
|
||||
string filename = $@"IMAGE/IM_{index:D5}"; // :D5 表示补足5位
|
||||
|
||||
mappings.Add($"{filename} => {item.InstanceId}");
|
||||
|
||||
dic.Add(item.InstanceId.ToString(), Path.GetFileName(filename));
|
||||
|
||||
dicomDir.AddFile(dicomFile, filename);
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
//有实际的文件
|
||||
if (mappings.Count > 0)
|
||||
{
|
||||
#region 写入临时路径
|
||||
var tempFilePath = Path.GetTempFileName();
|
||||
|
||||
// 保存 DICOMDIR 到临时文件 不能直接写入到流种
|
||||
await dicomDir.SaveAsync(tempFilePath);
|
||||
|
||||
using (var memoryStream = new MemoryStream(File.ReadAllBytes(tempFilePath)))
|
||||
{
|
||||
// 重置流位置
|
||||
memoryStream.Position = 0;
|
||||
|
||||
await _oSSService.UploadToOSSAsync(memoryStream, ossFolder, "DICOMDIR", false);
|
||||
}
|
||||
|
||||
//清理临时文件
|
||||
File.Delete(tempFilePath);
|
||||
|
||||
#endregion
|
||||
|
||||
#region 映射上传
|
||||
|
||||
// 将映射写入内存流
|
||||
var mappingText = string.Join(Environment.NewLine, mappings);
|
||||
await using var mappingStream = new MemoryStream(Encoding.UTF8.GetBytes(mappingText));
|
||||
|
||||
await _oSSService.UploadToOSSAsync(mappingStream, ossFolder, $"Download_{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}", false);
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static StudyDIRInfo ReadDicomDIRInfo(DicomFile dicomFile)
|
||||
{
|
||||
var dataset = dicomFile.Dataset;
|
||||
|
||||
|
||||
var info = new StudyDIRInfo
|
||||
{
|
||||
PatientId = dataset.GetSingleValueOrDefault(DicomTag.PatientID, string.Empty),
|
||||
PatientName = dataset.GetSingleValueOrDefault(DicomTag.PatientName, string.Empty),
|
||||
PatientBirthDate = dataset.GetSingleValueOrDefault(DicomTag.PatientBirthDate, string.Empty),
|
||||
PatientSex = dataset.GetSingleValueOrDefault(DicomTag.PatientSex, string.Empty),
|
||||
|
||||
StudyInstanceUid = dataset.GetSingleValueOrDefault(DicomTag.StudyInstanceUID, string.Empty),
|
||||
StudyId = dataset.GetSingleValueOrDefault(DicomTag.StudyID, string.Empty),
|
||||
DicomStudyDate = dataset.GetSingleValueOrDefault(DicomTag.StudyDate, string.Empty),
|
||||
DicomStudyTime = dataset.GetSingleValueOrDefault(DicomTag.StudyTime, string.Empty),
|
||||
AccessionNumber = dataset.GetSingleValueOrDefault(DicomTag.AccessionNumber, string.Empty),
|
||||
StudyDescription = dataset.GetSingleValueOrDefault(DicomTag.StudyDescription, string.Empty),
|
||||
|
||||
SeriesInstanceUid = dataset.GetSingleValueOrDefault(DicomTag.SeriesInstanceUID, string.Empty),
|
||||
Modality = dataset.GetSingleValueOrDefault(DicomTag.Modality, string.Empty),
|
||||
DicomSeriesDate = dataset.GetSingleValueOrDefault(DicomTag.SeriesDate, string.Empty),
|
||||
DicomSeriesTime = dataset.GetSingleValueOrDefault(DicomTag.SeriesTime, string.Empty),
|
||||
SeriesNumber = dataset.GetSingleValueOrDefault(DicomTag.SeriesNumber, 1),
|
||||
SeriesDescription = dataset.GetSingleValueOrDefault(DicomTag.SeriesDescription, string.Empty),
|
||||
|
||||
SopInstanceUid = dataset.GetSingleValueOrDefault(DicomTag.SOPInstanceUID, string.Empty),
|
||||
SOPClassUID = dataset.GetSingleValueOrDefault(DicomTag.SOPClassUID, string.Empty),
|
||||
InstanceNumber = dataset.GetSingleValueOrDefault(DicomTag.InstanceNumber, 1),
|
||||
|
||||
MediaStorageSOPClassUID = dicomFile.FileMetaInfo.GetSingleValueOrDefault(DicomTag.MediaStorageSOPClassUID, string.Empty),
|
||||
MediaStorageSOPInstanceUID = dicomFile.FileMetaInfo.GetSingleValueOrDefault(DicomTag.MediaStorageSOPInstanceUID, string.Empty),
|
||||
|
||||
TransferSyntaxUID = dicomFile.FileMetaInfo.GetSingleValueOrDefault(DicomTag.TransferSyntaxUID, string.Empty)
|
||||
};
|
||||
|
||||
return info;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -64,7 +64,7 @@ public static class ExcelExportHelper
|
|||
foreach (var key in dic.Keys)
|
||||
{
|
||||
//是数组 那么找到对应的属性 进行翻译
|
||||
if (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>();
|
||||
|
|
@ -74,14 +74,15 @@ public static class ExcelExportHelper
|
|||
{
|
||||
var itemDic = item.ConvertToDictionary();
|
||||
|
||||
//处理集合里面时间类型,根据当前语言将时间转变为字符串
|
||||
foreach (var itemValuePair in itemDic)
|
||||
{
|
||||
if (DateTime.TryParse(itemValuePair.Value?.ToString(), out DateTime result))
|
||||
{
|
||||
itemDic[itemValuePair.Key] = ExportExcelConverterDate.DateTimeInternationalToString(result);
|
||||
}
|
||||
}
|
||||
////处理集合里面时间类型,根据当前语言将时间转变为字符串
|
||||
//foreach (var itemValuePair in itemDic)
|
||||
//{
|
||||
// // 临床数据 1,1 会变成2024-01-01
|
||||
// if (itemValuePair.Value?.ToString().Length > 8 && DateTime.TryParse(itemValuePair.Value?.ToString(), out DateTime result))
|
||||
// {
|
||||
// itemDic[itemValuePair.Key] = ExportExcelConverterDate.DateTimeInternationalToString(result);
|
||||
// }
|
||||
//}
|
||||
|
||||
|
||||
foreach (var needTranslateProperty in needTranslatePropertyList)
|
||||
|
|
@ -165,7 +166,7 @@ public static class ExcelExportHelper
|
|||
}
|
||||
|
||||
//中文替换项目术语
|
||||
if (isEn_US == false && data.TrialObjectNameList.Count > 0)
|
||||
if (data.TrialObjectNameList?.Count > 0)
|
||||
{
|
||||
var replaceObjectList = data.TrialObjectNameList;
|
||||
|
||||
|
|
@ -176,24 +177,30 @@ public static class ExcelExportHelper
|
|||
for (int rowIndex = 0; rowIndex < rowCount; rowIndex++)
|
||||
{
|
||||
var row = sheet.GetRow(rowIndex);
|
||||
var colums = row.LastCellNum;
|
||||
|
||||
for (int colIndex = 0; colIndex < colums; colIndex++)
|
||||
if (row != null)
|
||||
{
|
||||
var cell = row.GetCell(colIndex);
|
||||
|
||||
// 只处理字符串类型的单元格
|
||||
if (cell != null)
|
||||
var colums = row.LastCellNum;
|
||||
|
||||
for (int colIndex = 0; colIndex < colums; colIndex++)
|
||||
{
|
||||
var cellValue = cell.StringCellValue;
|
||||
var cell = row.GetCell(colIndex);
|
||||
|
||||
var find = replaceObjectList.FirstOrDefault(t => t.Name == cellValue);
|
||||
if (find != null)
|
||||
// 只处理字符串类型的单元格
|
||||
if (cell != null)
|
||||
{
|
||||
cell.SetCellValue(find.TrialName);
|
||||
var cellValue = cell.StringCellValue;
|
||||
|
||||
var find = replaceObjectList.FirstOrDefault(t => t.Name == cellValue);
|
||||
if (find != null)
|
||||
{
|
||||
cell.SetCellValue(find.TrialName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -236,6 +243,7 @@ public static class ExcelExportHelper
|
|||
|
||||
}
|
||||
|
||||
|
||||
public class DynamicColumnConfig
|
||||
{
|
||||
/// <summary>
|
||||
|
|
@ -248,13 +256,19 @@ public static class ExcelExportHelper
|
|||
/// </summary>
|
||||
public int AutoColumnTitleRowIndex { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 模板列最后的索引
|
||||
/// </summary>
|
||||
public int TempalteLastColumnIndex { get; set; }
|
||||
|
||||
public bool IsCDISCExport { get; set; } = false;
|
||||
|
||||
//public List<string> CDISCList { get; set; } = new List<string>();
|
||||
|
||||
/// <summary>
|
||||
/// 动态的列名
|
||||
/// 动态的列名 如果Id 重复,那么就按照名称填充,否则就按照Id 填充列数据
|
||||
/// </summary>
|
||||
public List<string> ColumnNameList { get; set; } = new List<string>();
|
||||
public List<ColumItem> ColumnIdNameList { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 动态翻译的字典名
|
||||
|
|
@ -281,7 +295,31 @@ public static class ExcelExportHelper
|
|||
/// </summary>
|
||||
public string DynamicItemTitleName { get; set; }
|
||||
|
||||
public string DynamicItemTitleId { get; set; }
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -316,7 +354,7 @@ public static class ExcelExportHelper
|
|||
foreach (var key in dic.Keys)
|
||||
{
|
||||
//是数组 那么找到对应的属性 进行翻译
|
||||
if (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<>)))
|
||||
//if (dic[key].GetType().IsAssignableFrom(typeof(JArray)))
|
||||
{
|
||||
|
||||
|
|
@ -328,14 +366,14 @@ public static class ExcelExportHelper
|
|||
//var itemDic = JsonConvert.DeserializeObject<IDictionary<string, object>>(item.ToJsonNotIgnoreNull());
|
||||
var itemDic = item.ConvertToDictionary();
|
||||
|
||||
//处理集合里面时间类型,根据当前语言将时间转变为字符串
|
||||
foreach (var itemValuePair in itemDic)
|
||||
{
|
||||
if (DateTime.TryParse(itemValuePair.Value?.ToString(), out DateTime result))
|
||||
{
|
||||
itemDic[itemValuePair.Key] = ExportExcelConverterDate.DateTimeInternationalToString(result);
|
||||
}
|
||||
}
|
||||
////处理集合里面时间类型,根据当前语言将时间转变为字符串
|
||||
//foreach (var itemValuePair in itemDic)
|
||||
//{
|
||||
// if (itemValuePair.Value?.ToString().Length > 8 && DateTime.TryParse(itemValuePair.Value?.ToString(), out DateTime result))
|
||||
// {
|
||||
// itemDic[itemValuePair.Key] = ExportExcelConverterDate.DateTimeInternationalToString(result);
|
||||
// }
|
||||
//}
|
||||
|
||||
foreach (var needTranslateProperty in needTranslatePropertyList)
|
||||
{
|
||||
|
|
@ -414,8 +452,8 @@ public static class ExcelExportHelper
|
|||
workbook.RemoveSheetAt(1);
|
||||
}
|
||||
|
||||
//中文替换项目术语
|
||||
if (isEn_US == false && data.TrialObjectNameList.Count > 0)
|
||||
#region 中文替换项目术语
|
||||
if (data.TrialObjectNameList?.Count > 0)
|
||||
{
|
||||
var replaceObjectList = data.TrialObjectNameList;
|
||||
|
||||
|
|
@ -426,41 +464,402 @@ public static class ExcelExportHelper
|
|||
for (int rowIndex = 0; rowIndex < rowCount; rowIndex++)
|
||||
{
|
||||
var row = sheet.GetRow(rowIndex);
|
||||
var colums = row.LastCellNum;
|
||||
|
||||
for (int colIndex = 0; colIndex < colums; colIndex++)
|
||||
if (row != null)
|
||||
{
|
||||
var cell = row.GetCell(colIndex);
|
||||
|
||||
// 只处理字符串类型的单元格
|
||||
if (cell != null)
|
||||
var colums = row.LastCellNum;
|
||||
|
||||
for (int colIndex = 0; colIndex < colums; colIndex++)
|
||||
{
|
||||
var cellValue = cell.StringCellValue;
|
||||
var cell = row.GetCell(colIndex);
|
||||
|
||||
var find = replaceObjectList.FirstOrDefault(t => t.Name == cellValue);
|
||||
if (find != null)
|
||||
// 只处理字符串类型的单元格
|
||||
if (cell != null)
|
||||
{
|
||||
cell.SetCellValue(find.TrialName);
|
||||
var cellValue = cell.StringCellValue;
|
||||
|
||||
var find = replaceObjectList.FirstOrDefault(t => t.Name == cellValue);
|
||||
if (find != null)
|
||||
{
|
||||
cell.SetCellValue(find.TrialName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
if (dynamicColumnConfig != null)
|
||||
{
|
||||
//var isCdics = dynamicColumnConfig.CDISCList.Count > 0;
|
||||
|
||||
var isCdics = dynamicColumnConfig.IsCDISCExport;
|
||||
|
||||
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.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.ContainsKey(dynamicColumnConfig.DynamicItemDicName) ? 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;
|
||||
|
|
@ -502,6 +901,7 @@ public static class ExcelExportHelper
|
|||
//创建新的列
|
||||
for (int i = originTotalEndIndex; i < originTotalEndIndex + needAddCount; i++)
|
||||
{
|
||||
cdicsRow.CreateCell(i + 1);
|
||||
titelRow.CreateCell(i + 1);
|
||||
templateRow.CreateCell(i + 1);
|
||||
}
|
||||
|
|
@ -523,8 +923,11 @@ public static class ExcelExportHelper
|
|||
|
||||
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);
|
||||
templateRow.GetCell(i).SetCellValue("");
|
||||
}
|
||||
|
|
@ -558,6 +961,8 @@ public static class ExcelExportHelper
|
|||
|
||||
if (dynamicColumnConfig != null)
|
||||
{
|
||||
var isExcelAddDataWithName = dynamicColumnConfig.ColumnIdNameList.Select(t => t.Id).Count() == 1;
|
||||
|
||||
var dynamicTranslateDataList = await _dictionaryService.GetBasicDataSelect(dynamicColumnConfig.TranslateDicNameList.ToArray());
|
||||
|
||||
// 使用NPOI 进行二次处理
|
||||
|
|
@ -588,7 +993,15 @@ public static class ExcelExportHelper
|
|||
|
||||
//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())
|
||||
{
|
||||
|
|
@ -624,6 +1037,7 @@ public static class ExcelExportHelper
|
|||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 导出文件模板
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -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 Aliyun.OSS;
|
||||
using Aliyun.OSS.Common;
|
||||
using Amazon;
|
||||
using Amazon.Runtime;
|
||||
using Amazon.S3;
|
||||
|
|
@ -9,11 +10,16 @@ using Amazon.SecurityToken.Model;
|
|||
using IRaCIS.Core.Infrastructure;
|
||||
using IRaCIS.Core.Infrastructure.NewtonsoftJson;
|
||||
using MassTransit;
|
||||
using MassTransit.Caching.Internals;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Minio;
|
||||
using Minio.DataModel;
|
||||
using Minio.DataModel.Args;
|
||||
using Minio.Exceptions;
|
||||
using System.Net.Http;
|
||||
using System.Reactive.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Web;
|
||||
|
||||
namespace IRaCIS.Core.Application.Helper;
|
||||
|
||||
|
|
@ -59,7 +65,7 @@ public class AliyunOSSOptions
|
|||
|
||||
public int DurationSeconds { get; set; }
|
||||
|
||||
|
||||
public string PreviewEndpoint { get; set; }
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -105,6 +111,9 @@ public class AliyunOSSTempToken
|
|||
public string SecurityToken { get; set; }
|
||||
public DateTime Expiration { get; set; }
|
||||
|
||||
public string PreviewEndpoint { get; set; }
|
||||
|
||||
public string DownloadEndPoint => EndPoint.Insert(EndPoint.IndexOf("//") + 2, BucketName + ".");
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -118,7 +127,7 @@ public class AWSTempToken
|
|||
public string SecretAccessKey { get; set; }
|
||||
public string BucketName { get; set; }
|
||||
public string ViewEndpoint { get; set; }
|
||||
public DateTime Expiration { get; set; }
|
||||
public DateTime? Expiration { get; set; }
|
||||
}
|
||||
|
||||
public enum ObjectStoreUse
|
||||
|
|
@ -139,13 +148,23 @@ public interface IOSSService
|
|||
|
||||
public Task DownLoadFromOSSAsync(string ossRelativePath, string localFilePath);
|
||||
|
||||
public Task<Stream> GetStreamFromOSSAsync(string ossRelativePath);
|
||||
|
||||
public ObjectStoreServiceOptions ObjectStoreServiceOptions { get; set; }
|
||||
|
||||
public Task<string> GetSignedUrl(string ossRelativePath);
|
||||
|
||||
public Task DeleteFromPrefix(string prefix);
|
||||
public Task DeleteFromPrefix(string prefix, bool isCache = false);
|
||||
|
||||
public Task DeleteObjects(List<string> objectKeys);
|
||||
|
||||
List<string> GetRootFolderNames();
|
||||
|
||||
public ObjectStoreDTO GetObjectStoreTempToken();
|
||||
|
||||
public Task MoveObject(string sourcePath, string destPath, bool overwrite = true);
|
||||
|
||||
public Task<long> GetObjectSizeAsync(string sourcePath);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -176,7 +195,7 @@ public class OSSService : IOSSService
|
|||
/// <returns></returns>
|
||||
public async Task<string> UploadToOSSAsync(Stream fileStream, string oosFolderPath, string fileRealName, bool isFileNameAddGuid = true)
|
||||
{
|
||||
GetObjectStoreTempToken();
|
||||
BackBatchGetToken();
|
||||
|
||||
var ossRelativePath = isFileNameAddGuid ? $"{oosFolderPath}/{Guid.NewGuid()}_{fileRealName}" : $"{oosFolderPath}/{fileRealName}";
|
||||
|
||||
|
|
@ -208,8 +227,14 @@ public class OSSService : IOSSService
|
|||
var minIOConfig = ObjectStoreServiceOptions.MinIO;
|
||||
|
||||
|
||||
var httpClientHandler = new HttpClientHandler
|
||||
{
|
||||
ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator
|
||||
};
|
||||
|
||||
var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.EndPoint}:{minIOConfig.Port}")
|
||||
.WithCredentials(minIOConfig.AccessKeyId, minIOConfig.SecretAccessKey).WithSSL(minIOConfig.UseSSL)
|
||||
.WithHttpClient(new HttpClient(httpClientHandler))
|
||||
.Build();
|
||||
|
||||
var putObjectArgs = new PutObjectArgs()
|
||||
|
|
@ -266,6 +291,37 @@ public class OSSService : IOSSService
|
|||
}
|
||||
|
||||
|
||||
//后端批量上传 或者下载,不每个文件获取临时token
|
||||
private void BackBatchGetToken()
|
||||
{
|
||||
if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS")
|
||||
{
|
||||
if (AliyunOSSTempToken == null)
|
||||
{
|
||||
GetObjectStoreTempToken();
|
||||
}
|
||||
//token 过期了
|
||||
else if (AliyunOSSTempToken.Expiration.AddSeconds(10) <= DateTime.Now)
|
||||
{
|
||||
GetObjectStoreTempToken();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS")
|
||||
{
|
||||
if (AWSTempToken == null)
|
||||
{
|
||||
GetObjectStoreTempToken();
|
||||
}
|
||||
//token 过期了
|
||||
else if (AWSTempToken.Expiration?.AddSeconds(10) <= DateTime.Now)
|
||||
{
|
||||
GetObjectStoreTempToken();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// oosFolderPath 不要 "/ "开头 应该: TempFolder/ChildFolder
|
||||
|
|
@ -277,7 +333,7 @@ public class OSSService : IOSSService
|
|||
/// <exception cref="BusinessValidationFailedException"></exception>
|
||||
public async Task<string> UploadToOSSAsync(string localFilePath, string oosFolderPath, bool isFileNameAddGuid = true)
|
||||
{
|
||||
GetObjectStoreTempToken();
|
||||
BackBatchGetToken();
|
||||
|
||||
var localFileName = Path.GetFileName(localFilePath);
|
||||
|
||||
|
|
@ -298,9 +354,14 @@ public class OSSService : IOSSService
|
|||
{
|
||||
var minIOConfig = ObjectStoreServiceOptions.MinIO;
|
||||
|
||||
var httpClientHandler = new HttpClientHandler
|
||||
{
|
||||
ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator
|
||||
};
|
||||
|
||||
var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.EndPoint}:{minIOConfig.Port}")
|
||||
.WithCredentials(minIOConfig.AccessKeyId, minIOConfig.SecretAccessKey).WithSSL(minIOConfig.UseSSL)
|
||||
.WithHttpClient(new HttpClient(httpClientHandler))
|
||||
.Build();
|
||||
|
||||
var putObjectArgs = new PutObjectArgs()
|
||||
|
|
@ -347,7 +408,7 @@ public class OSSService : IOSSService
|
|||
|
||||
public async Task DownLoadFromOSSAsync(string ossRelativePath, string localFilePath)
|
||||
{
|
||||
GetObjectStoreTempToken();
|
||||
BackBatchGetToken();
|
||||
|
||||
ossRelativePath = ossRelativePath.TrimStart('/');
|
||||
try
|
||||
|
|
@ -360,23 +421,29 @@ public class OSSService : IOSSService
|
|||
|
||||
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();
|
||||
await result.Content.CopyToAsync(fs);
|
||||
}
|
||||
|
||||
}
|
||||
else if (ObjectStoreServiceOptions.ObjectStoreUse == "MinIO")
|
||||
{
|
||||
var httpClientHandler = new HttpClientHandler
|
||||
{
|
||||
ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator
|
||||
};
|
||||
|
||||
var minIOConfig = ObjectStoreServiceOptions.MinIO;
|
||||
|
||||
//Console.WriteLine(minIOConfig.ToJsonStr() +$"ossRelativePath:{ossRelativePath} localFilePath:{localFilePath} ");
|
||||
|
||||
var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.EndPoint}:{minIOConfig.Port}")
|
||||
.WithCredentials(minIOConfig.AccessKeyId, minIOConfig.SecretAccessKey).WithSSL(minIOConfig.UseSSL)
|
||||
.WithHttpClient(new HttpClient(httpClientHandler))
|
||||
.Build();
|
||||
|
||||
var getObjectArgs = new GetObjectArgs()
|
||||
|
|
@ -431,6 +498,103 @@ public class OSSService : IOSSService
|
|||
|
||||
}
|
||||
|
||||
public async Task<Stream> GetStreamFromOSSAsync(string ossRelativePath)
|
||||
{
|
||||
BackBatchGetToken();
|
||||
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);
|
||||
|
||||
// 将OSS返回的流复制到内存流中并返回
|
||||
var memoryStream = new MemoryStream();
|
||||
await result.Content.CopyToAsync(memoryStream);
|
||||
memoryStream.Position = 0; // 重置位置以便读取
|
||||
return memoryStream;
|
||||
}
|
||||
else if (ObjectStoreServiceOptions.ObjectStoreUse == "MinIO")
|
||||
{
|
||||
var minIOConfig = ObjectStoreServiceOptions.MinIO;
|
||||
|
||||
|
||||
var httpClientHandler = new HttpClientHandler
|
||||
{
|
||||
ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator
|
||||
};
|
||||
|
||||
var minioClient = new MinioClient()
|
||||
.WithEndpoint($"{minIOConfig.EndPoint}:{minIOConfig.Port}")
|
||||
.WithCredentials(minIOConfig.AccessKeyId, minIOConfig.SecretAccessKey)
|
||||
.WithSSL(minIOConfig.UseSSL)
|
||||
.WithHttpClient(new HttpClient(httpClientHandler))
|
||||
.Build();
|
||||
|
||||
var memoryStream = new MemoryStream();
|
||||
|
||||
var getObjectArgs = new GetObjectArgs()
|
||||
.WithBucket(minIOConfig.BucketName)
|
||||
.WithObject(ossRelativePath)
|
||||
.WithCallbackStream(stream => stream.CopyToAsync(memoryStream));
|
||||
|
||||
await minioClient.GetObjectAsync(getObjectArgs);
|
||||
memoryStream.Position = 0;
|
||||
return memoryStream;
|
||||
}
|
||||
else if (ObjectStoreServiceOptions.ObjectStoreUse == "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);
|
||||
|
||||
var getObjectRequest = new Amazon.S3.Model.GetObjectRequest
|
||||
{
|
||||
BucketName = awsConfig.BucketName,
|
||||
Key = ossRelativePath
|
||||
};
|
||||
|
||||
var response = await amazonS3Client.GetObjectAsync(getObjectRequest);
|
||||
|
||||
var memoryStream = new MemoryStream();
|
||||
await response.ResponseStream.CopyToAsync(memoryStream);
|
||||
memoryStream.Position = 0;
|
||||
return memoryStream;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new BusinessValidationFailedException("未定义的存储介质类型");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new BusinessValidationFailedException("oss流获取失败! " + ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public async Task<string> GetSignedUrl(string ossRelativePath)
|
||||
{
|
||||
GetObjectStoreTempToken();
|
||||
|
|
@ -526,12 +690,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>
|
||||
/// <param name="prefix"></param>
|
||||
/// <returns></returns>
|
||||
public async Task DeleteFromPrefix(string prefix)
|
||||
public async Task DeleteFromPrefix(string prefix, bool isCache = false)
|
||||
{
|
||||
GetObjectStoreTempToken();
|
||||
|
||||
|
|
@ -541,6 +933,21 @@ public class OSSService : IOSSService
|
|||
|
||||
var _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.EndPoint : aliConfig.InternalEndpoint, AliyunOSSTempToken.AccessKeyId, AliyunOSSTempToken.AccessKeySecret, AliyunOSSTempToken.SecurityToken);
|
||||
|
||||
var bucketName = string.Empty;
|
||||
|
||||
if (isCache)
|
||||
{
|
||||
Uri uri = new Uri(aliConfig.ViewEndpoint);
|
||||
string host = uri.Host; // 获取 "zy-irc-test-dev-cache.oss-cn-shanghai.aliyuncs.com"
|
||||
string[] parts = host.Split('.');
|
||||
bucketName = parts[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
bucketName = aliConfig.BucketName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
|
|
@ -549,7 +956,7 @@ public class OSSService : IOSSService
|
|||
do
|
||||
{
|
||||
// 使用 prefix 模拟目录结构,设置 MaxKeys 和 NextMarker
|
||||
objectListing = _ossClient.ListObjects(new Aliyun.OSS.ListObjectsRequest(aliConfig.BucketName)
|
||||
objectListing = _ossClient.ListObjects(new Aliyun.OSS.ListObjectsRequest(bucketName)
|
||||
{
|
||||
Prefix = prefix,
|
||||
MaxKeys = 1000,
|
||||
|
|
@ -561,7 +968,7 @@ public class OSSService : IOSSService
|
|||
// 删除获取到的文件
|
||||
if (keys.Count > 0)
|
||||
{
|
||||
_ossClient.DeleteObjects(new Aliyun.OSS.DeleteObjectsRequest(aliConfig.BucketName, keys, false));
|
||||
_ossClient.DeleteObjects(new Aliyun.OSS.DeleteObjectsRequest(bucketName, keys, false));
|
||||
}
|
||||
|
||||
// 设置 NextMarker 以获取下一页的数据
|
||||
|
|
@ -581,8 +988,14 @@ public class OSSService : IOSSService
|
|||
var minIOConfig = ObjectStoreServiceOptions.MinIO;
|
||||
|
||||
|
||||
var httpClientHandler = new HttpClientHandler
|
||||
{
|
||||
ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator
|
||||
};
|
||||
|
||||
var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.EndPoint}:{minIOConfig.Port}")
|
||||
.WithCredentials(minIOConfig.AccessKeyId, minIOConfig.SecretAccessKey).WithSSL(minIOConfig.UseSSL)
|
||||
.WithHttpClient(new HttpClient(httpClientHandler))
|
||||
.Build();
|
||||
|
||||
|
||||
|
|
@ -618,7 +1031,6 @@ public class OSSService : IOSSService
|
|||
|
||||
var awsConfig = ObjectStoreServiceOptions.AWS;
|
||||
|
||||
|
||||
// 提供awsAccessKeyId和awsSecretAccessKey构造凭证
|
||||
var credentials = new SessionAWSCredentials(AWSTempToken.AccessKeyId, AWSTempToken.SecretAccessKey, AWSTempToken.SessionToken);
|
||||
|
||||
|
|
@ -670,6 +1082,169 @@ 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 httpClientHandler = new HttpClientHandler
|
||||
{
|
||||
ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator
|
||||
};
|
||||
|
||||
|
||||
var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.EndPoint}:{minIOConfig.Port}")
|
||||
.WithCredentials(minIOConfig.AccessKeyId, minIOConfig.SecretAccessKey).WithSSL(minIOConfig.UseSSL)
|
||||
.WithHttpClient(new HttpClient(httpClientHandler))
|
||||
.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("未定义的存储介质类型");
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<long> GetObjectSizeAsync(string sourcePath)
|
||||
{
|
||||
BackBatchGetToken();
|
||||
|
||||
|
||||
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 key = HttpUtility.UrlDecode(objectkey);
|
||||
var metadata = _ossClient.GetObjectMetadata(aliConfig.BucketName, key);
|
||||
|
||||
long fileSize = metadata?.ContentLength ?? 0; // 文件大小(字节)
|
||||
|
||||
return fileSize;
|
||||
}
|
||||
else if (ObjectStoreServiceOptions.ObjectStoreUse == "MinIO")
|
||||
{
|
||||
var minIOConfig = ObjectStoreServiceOptions.MinIO;
|
||||
|
||||
|
||||
var httpClientHandler = new HttpClientHandler
|
||||
{
|
||||
ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator
|
||||
};
|
||||
|
||||
|
||||
var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.EndPoint}:{minIOConfig.Port}")
|
||||
.WithCredentials(minIOConfig.AccessKeyId, minIOConfig.SecretAccessKey).WithSSL(minIOConfig.UseSSL)
|
||||
.WithHttpClient(new HttpClient(httpClientHandler))
|
||||
.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()
|
||||
{
|
||||
|
|
@ -713,6 +1288,7 @@ public class OSSService : IOSSService
|
|||
BucketName = ossOptions.BucketName,
|
||||
EndPoint = ossOptions.EndPoint,
|
||||
ViewEndpoint = ossOptions.ViewEndpoint,
|
||||
PreviewEndpoint = ossOptions.PreviewEndpoint
|
||||
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,41 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace IRaCIS.Core.Application.Helper
|
||||
{
|
||||
public static class SafeBussinessHelper
|
||||
{
|
||||
public static async Task<bool> RunAsync(Func<Task> func, [CallerMemberName] string caller = "", string errorMsgTitle = "")
|
||||
{
|
||||
try
|
||||
{
|
||||
await func();
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Logger.Error($"【{errorMsgTitle}失败 - {caller}】: {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<(bool Success, T? Result)> RunAsync<T>(Func<Task<T>> func, [CallerMemberName] string caller = "", string errorMsgTitle = "")
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await func();
|
||||
return (true, result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Logger.Error($"【{errorMsgTitle}失败 - {caller}】: {ex.Message}");
|
||||
return (false, default);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -32,38 +32,39 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MaxMind.GeoIP2" Version="5.3.0" />
|
||||
<PackageReference Include="IdentityModel.OidcClient" Version="6.0.0" />
|
||||
<PackageReference Include="AlibabaCloud.SDK.Sts20150401" Version="1.1.4" />
|
||||
<PackageReference Include="AWSSDK.SecurityToken" Version="3.7.400.36" />
|
||||
<PackageReference Include="AlibabaCloud.SDK.Sts20150401" Version="1.1.6" />
|
||||
<PackageReference Include="AWSSDK.SecurityToken" Version="4.0.2" />
|
||||
<PackageReference Include="Aliyun.OSS.SDK.NetCore" Version="2.14.1" />
|
||||
<PackageReference Include="AWSSDK.S3" Version="3.7.405" />
|
||||
<PackageReference Include="DocX" Version="3.0.1" />
|
||||
<PackageReference Include="AWSSDK.S3" Version="4.0.6.8" />
|
||||
<PackageReference Include="DocX" Version="4.0.25105.5786" />
|
||||
<PackageReference Include="FreeSpire.Doc" Version="12.2.0" />
|
||||
<PackageReference Include="Hangfire.Core" Version="1.8.14" />
|
||||
<PackageReference Include="ExcelDataReader" Version="3.7.0" />
|
||||
<PackageReference Include="ExcelDataReader.DataSet" Version="3.7.0" />
|
||||
<PackageReference Include="DistributedLock.Redis" Version="1.0.3" />
|
||||
<PackageReference Include="DistributedLock.SqlServer" Version="1.0.5" />
|
||||
<PackageReference Include="fo-dicom" Version="5.1.3" />
|
||||
<PackageReference Include="fo-dicom.Imaging.ImageSharp" Version="5.1.3" />
|
||||
<PackageReference Include="fo-dicom.Codecs" Version="5.15.1" />
|
||||
<PackageReference Include="DistributedLock.Redis" Version="1.1.0" />
|
||||
<PackageReference Include="DistributedLock.SqlServer" Version="1.0.6" />
|
||||
<PackageReference Include="fo-dicom" Version="5.2.2" />
|
||||
<PackageReference Include="fo-dicom.Imaging.ImageSharp" Version="5.2.2" />
|
||||
<PackageReference Include="fo-dicom.Codecs" Version="5.16.4" />
|
||||
<PackageReference Include="IP2Region.Net" Version="2.0.2" />
|
||||
<PackageReference Include="MailKit" Version="4.7.1.1" />
|
||||
<PackageReference Include="MailKit" Version="4.13.0" />
|
||||
<PackageReference Include="Masa.Contrib.Service.MinimalAPIs" Version="1.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.10" />
|
||||
<PackageReference Include="MimeKit" Version="4.7.1" />
|
||||
<PackageReference Include="MiniExcel" Version="1.34.2" />
|
||||
<PackageReference Include="MimeKit" Version="4.13.0" />
|
||||
<PackageReference Include="MiniExcel" Version="1.41.3" />
|
||||
<PackageReference Include="Minio" Version="6.0.3" />
|
||||
<PackageReference Include="MiniWord" Version="0.8.0" />
|
||||
<PackageReference Include="MiniWord" Version="0.9.2" />
|
||||
<PackageReference Include="My.Extensions.Localization.Json" Version="3.3.0">
|
||||
<TreatAsUsed>true</TreatAsUsed>
|
||||
</PackageReference>
|
||||
<PackageReference Include="NPOI" Version="2.7.1" />
|
||||
<PackageReference Include="NPOI" Version="2.7.4" />
|
||||
<PackageReference Include="Panda.DynamicWebApi" Version="1.2.2" />
|
||||
<PackageReference Include="RestSharp" Version="112.1.0" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.5" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.Filters" Version="8.0.2" />
|
||||
<PackageReference Include="ZiggyCreatures.FusionCache" Version="1.4.0" />
|
||||
<PackageReference Include="Serilog.AspNetCore" Version="9.0.0" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.11" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.Filters" Version="9.0.0" />
|
||||
<PackageReference Include="ZiggyCreatures.FusionCache" Version="2.4.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -191,7 +191,7 @@ namespace IRaCIS.Core.Application.MassTransit.Consumer
|
|||
|
||||
// dialogMsg.AppendLine(@$"<br>说明:为高效解决/处理以上全部质疑问题,麻烦您准确核实实际影像检查情况。请注意影像日期与实际检查的日期可能会不一致,部分检查(如PET -CT)可能同时存在多种模态影像。准确核实后,请回复该访视正确的影像检查情况。");
|
||||
dbSV.CheckState = CheckStateEnum.CVPassed;
|
||||
dbSV.CheckUserId = _userInfo.Id;
|
||||
dbSV.CheckUserId = _userInfo.UserRoleId;
|
||||
dbSV.CheckPassedTime = DateTime.Now;
|
||||
dbSV.CheckChallengeState = CheckChanllengeTypeEnum.Closed;
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ namespace IRaCIS.Core.Application.MassTransit.Consumer;
|
|||
/// 加急的医学反馈任务 通知MIM
|
||||
/// </summary>
|
||||
public class UrgentMedicalReviewAddedEventConsumer(
|
||||
IRepository<User> _userRepository,
|
||||
IRepository<UserRole> _userRoleRepository,
|
||||
IRepository<TaskMedicalReview> _taskMedicalReviewRepository,
|
||||
IRepository<Trial> _trialRepository,
|
||||
IRepository<SubjectVisit> _subjectVisitRepository,
|
||||
|
|
@ -58,7 +58,7 @@ public class UrgentMedicalReviewAddedEventConsumer(
|
|||
return;
|
||||
}
|
||||
var criterion = await _readingQuestionCriterionTrialRepository.FirstOrDefaultAsync(x => x.Id == medicalReview.VisitTask.TrialReadingCriterionId);
|
||||
var userinfo = await _userRepository.Where(x => x.Id == medicalReview.MedicalManagerUserId).FirstOrDefaultAsync();
|
||||
var userinfo = await _userRoleRepository.Where(x => x.Id == medicalReview.MedicalManagerUserId).FirstOrDefaultAsync();
|
||||
|
||||
var taskInfo = await _visitTaskRepository.Where(x => x.Id == medicalReview.VisitTaskId).Include(x=>x.SourceSubjectVisit).Include(x => x.ReadModule).Include(x => x.Subject).FirstNotNullAsync();
|
||||
|
||||
|
|
@ -128,7 +128,7 @@ public class UrgentMedicalReviewAddedEventConsumer(
|
|||
|
||||
//IR 回复医学反馈通知MIM
|
||||
public class UrgentIRRepliedMedicalReviewConsumer(
|
||||
IRepository<User> _userRepository,
|
||||
IRepository<UserRole> _userRoleRepository,
|
||||
IRepository<TaskMedicalReview> _taskMedicalReviewRepository,
|
||||
IRepository<Trial> _trialRepository,
|
||||
IRepository<SubjectVisit> _subjectVisitRepository,
|
||||
|
|
@ -159,7 +159,7 @@ public class UrgentIRRepliedMedicalReviewConsumer(
|
|||
return;
|
||||
}
|
||||
var criterion = await _readingQuestionCriterionTrialRepository.FirstOrDefaultAsync(x => x.Id == medicalReview.VisitTask.TrialReadingCriterionId);
|
||||
var userinfo = await _userRepository.Where(x => x.Id == medicalReview.MedicalManagerUserId).FirstOrDefaultAsync();
|
||||
var userinfo = await _userRoleRepository.Where(x => x.Id == medicalReview.MedicalManagerUserId).FirstOrDefaultAsync();
|
||||
|
||||
var taskInfo = await _visitTaskRepository.Where(x => x.Id == medicalReview.VisitTaskId).Include(x => x.SourceSubjectVisit).Include(x => x.ReadModule).Include(x => x.Subject).FirstNotNullAsync();
|
||||
|
||||
|
|
@ -231,7 +231,7 @@ public class UrgentIRRepliedMedicalReviewConsumer(
|
|||
/// MIM 回复医学返回通知IR
|
||||
/// </summary>
|
||||
public class UrgentMIMRepliedMedicalReviewConsumer(
|
||||
IRepository<User> _userRepository,
|
||||
IRepository<UserRole> _userRoleRepository,
|
||||
IRepository<TaskMedicalReview> _taskMedicalReviewRepository,
|
||||
IRepository<Trial> _trialRepository,
|
||||
IRepository<SubjectVisit> _subjectVisitRepository,
|
||||
|
|
@ -268,7 +268,7 @@ public class UrgentMIMRepliedMedicalReviewConsumer(
|
|||
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 userinfo = await _userRoleRepository.Where(x => x.Id == taskInfo.DoctorUserId).FirstOrDefaultAsync();
|
||||
|
||||
|
||||
|
||||
|
|
@ -346,8 +346,8 @@ public class UrgentMIMRepliedMedicalReviewConsumer(
|
|||
/// 加急阅片 IR 申请重阅 或者PM 申请重阅
|
||||
/// </summary>
|
||||
public class UrgentIRApplyedReReadingConsumer(
|
||||
IRepository<User> _userRepository,
|
||||
IRepository<TrialUser> _trialUseRepository,
|
||||
IRepository<UserRole> _userRoleRepository,
|
||||
IRepository<TrialUserRole> _trialUseRoleRepository,
|
||||
IRepository<TaskMedicalReview> _taskMedicalReviewRepository,
|
||||
IRepository<Trial> _trialRepository,
|
||||
IRepository<SubjectVisit> _subjectVisitRepository,
|
||||
|
|
@ -370,9 +370,9 @@ public class UrgentIRApplyedReReadingConsumer(
|
|||
var criterion = await _readingQuestionCriterionTrialRepository.FirstOrDefaultAsync(x => x.Id == taskInfo.TrialReadingCriterionId);
|
||||
|
||||
|
||||
var doctorInfo = await _userRepository.Where(x => x.Id == taskInfo.DoctorUserId).FirstOrDefaultAsync();
|
||||
var doctorInfo = await _userRoleRepository.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 trialUser = await _trialUseRoleRepository.Where(x => x.TrialId == taskInfo.TrialId).Include(x => x.UserRole).Select(x=>x.UserRole).ToListAsync();
|
||||
|
||||
var userinfoList = trialUser.Where(x => x.UserTypeEnum == UserTypeEnum.ProjectManager||x.UserTypeEnum==UserTypeEnum.APM).ToList();
|
||||
if (context.Message.ReReadingApplyState == ReReadingApplyState.TrialGroupHaveApplyed)
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue