Compare commits
661 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 845c532134 | |||
| 8615c90ebe | |||
| 88f015183d | |||
| 4f9560c3a6 | |||
| 9abb78400a | |||
| 8f5e2c5b0c | |||
| efe96203d9 | |||
| 65de94079a | |||
| 6e706c4fc9 | |||
| 84e2e14b73 | |||
| 49d4a71f36 | |||
| 7dcf73176c | |||
| f9bf9d752c | |||
| acac7ec995 | |||
| 6cfcb9bfe1 | |||
| 9adf2ad52b | |||
| 403885ef3f | |||
| 7b1a0affbe | |||
| 85897f990d | |||
| be477818d7 | |||
| 41bbb75407 | |||
| fd9fdcb8be | |||
| 0e8ecdaf46 | |||
| 81b033885e | |||
| fca2a81c66 | |||
| 353f5df217 | |||
| 4874533804 | |||
| 025a8e7af1 | |||
| 79c5824051 | |||
| 2c47842e13 | |||
| 39d9cefda9 | |||
| 2c0d1cdc82 | |||
| af77d5be56 | |||
| f662a988e0 | |||
| b9cace17b2 | |||
| c8d0ef9043 | |||
| 2491fd1fff | |||
| 7cc9731edf | |||
| ff333219d4 | |||
| 0eae520f0d | |||
| 0a2513245c | |||
| f10cc0adbf | |||
| 58b2f9c7a9 | |||
| 472e744b6c | |||
| 699df338c1 | |||
| a64127537e | |||
| 2287c8869d | |||
| 0d8319feaa | |||
| 676c5b0e74 | |||
| a76654c210 | |||
| 0a29d431dc | |||
| 86562b39e3 | |||
| 86fe43aa93 | |||
| 082998da09 | |||
| d209a8de4d | |||
| b1608812ec | |||
| 20c18d957a | |||
| 7a7011e7ef | |||
| 24a395f8c8 | |||
| df2f179917 | |||
| 1bcc831937 | |||
| 75bf85997a | |||
| 074cba906c | |||
| 86db676df8 | |||
| bc67f16735 | |||
| 3e046e24e9 | |||
| c971161d31 | |||
| a63904c0bb | |||
| 572d4ea4b8 | |||
| ae4345cfcc | |||
| a536c33302 | |||
| be5c9b636e | |||
| c71bf65cd9 | |||
| 69c5ce9841 | |||
| 2139193df4 | |||
| 9aa2f23a67 | |||
| 531e0fa228 | |||
| 250c478687 | |||
| 09ba37ec6d | |||
| 913b673b72 | |||
| 5ee8a3db21 | |||
| 3c41789ba9 | |||
| d2268f0706 | |||
| e8496df81a | |||
| 3e24fe9461 | |||
| d771ab0f6f | |||
| 29dfa03d0f | |||
| 44e9b211c2 | |||
| b1154ea594 | |||
| 6dd12b419b | |||
| bce7c1cc99 | |||
| a660eb4f37 | |||
| 3ade13a922 | |||
| a5fed78c19 | |||
| 5207fef130 | |||
| b8ad8085cd | |||
| dc8451f55c | |||
| dd9ec3d8fe | |||
| 589cd83576 | |||
| 6c2a1e0a4b | |||
| 7a927b3133 | |||
| 3dbf6dc897 | |||
| bb5401f78c | |||
| 59e9d9df69 | |||
| 9a70193e7a | |||
| b44a9ecc63 | |||
| fb45173106 | |||
| bf2996bd1c | |||
| d840f14a2f | |||
| 4101c68d37 | |||
| 91503dc599 | |||
| debe22fdc3 | |||
| 8f4f9a91c2 | |||
| a53e306427 | |||
| 02183affc3 | |||
| e81c97037a | |||
| 1fd02cbbc5 | |||
| 55cbbffba6 | |||
| 4a1bf759cf | |||
| 703ab03b7f | |||
| 0582390b2c | |||
| cdf1119764 | |||
| 72cb407766 | |||
| 9f80386a19 | |||
| 3a96710210 | |||
| b8b63e33b8 | |||
| 98d09886de | |||
| 91671c5003 | |||
| 35ef014391 | |||
| 82fc9bfc75 | |||
| d6d47a6f85 | |||
| 32d6cce33e | |||
| 06ca624886 | |||
| 88231fffb4 | |||
| 12ab0d3eb4 | |||
| 6faeecebb0 | |||
| 48b778c95a | |||
| dee286cc90 | |||
| 2e96f7a936 | |||
| ebb33f45ba | |||
| 9d32be658c | |||
| 27fd819c9c | |||
| 937f8a5b66 | |||
| 53544001a7 | |||
| f08cd1c818 | |||
| 2c377d02f9 | |||
| 29f8648287 | |||
| f0725873e1 | |||
| dc12e57494 | |||
| d4a731d544 | |||
| 6e22515603 | |||
| abd5c8942d | |||
| 3decef7693 | |||
| ae83c753cb | |||
| 53ae0527ff | |||
| a234e0746d | |||
| c88d927c4b | |||
| 245898e22e | |||
| 93d3024b0f | |||
| 35be7f3144 | |||
| 62ac9418b5 | |||
| ea5de83364 | |||
| cf5a8b20e4 | |||
| ea95ece331 | |||
| 894b9eb67b | |||
| d7e5b3f57c | |||
| 29a47e7351 | |||
| 6d2cddfcfc | |||
| 60b2978deb | |||
| 79187772a6 | |||
| 105da818a8 | |||
| 584516f936 | |||
| a7429c1739 | |||
| 75f227e547 | |||
| 60c31b470c | |||
| 731f049fc0 | |||
| 201c24238c | |||
| 71e28f1a37 | |||
| 375e50bab7 | |||
| 39e2651edf | |||
| b461216d87 | |||
| dc6ae39130 | |||
| 4b65f1e922 | |||
| 99439a4809 | |||
| 419d5de7f1 | |||
| a56ffaee58 | |||
| e925d5bdb2 | |||
| 9ec97f7d37 | |||
| 3c532389d9 | |||
| 1094f318f9 | |||
| 56f261b8ce | |||
| 34b12b89e4 | |||
| bca8f2ddb9 | |||
| 4023a59097 | |||
| 8afcbf3d5e | |||
| 69c2cd339c | |||
| 4c7ce217da | |||
| d66209452c | |||
| 77352245fc | |||
| 17ebdce96e | |||
| 3962178c68 | |||
| 7c6dcbbe38 | |||
| 75ed6375c2 | |||
| 290578d824 | |||
| cd2f5aa1e1 | |||
| 79d150829a | |||
| 270de86658 | |||
| edefc8f3e7 | |||
| f1b3ec7d7c | |||
| 5171af975d | |||
| 33b9113993 | |||
| a53626b66c | |||
| a27dcd6399 | |||
| 14908c4eed | |||
| dfef88d663 | |||
| 1afe020e83 | |||
| 7544e0c2e9 | |||
| 68d72fce53 | |||
| 423f2fcb7c | |||
| 92b715de3a | |||
| 912bc2edb8 | |||
| a5f6c24aec | |||
| 8fc886a1db | |||
| 5832610d3d | |||
| e675542136 | |||
| 8f7a3ab713 | |||
| 02d98cfa75 | |||
| 8bb1e8cf43 | |||
| b3e37c7ea4 | |||
| 5bdb89df7d | |||
| 10d50f3b5a | |||
| bb6d36c8be | |||
| d91f4dc728 | |||
| f01e5335d6 | |||
| 3e8794b335 | |||
| 79375f7106 | |||
| 72b2047862 | |||
| 8b124e1c60 | |||
| 5b2449b607 | |||
| 3f4a5e3040 | |||
| 75a24b8aef | |||
| 493c46ed15 | |||
| 5c2467bdbb | |||
| 0777ea830c | |||
| b75223f8a0 | |||
| 6a4e42f9dc | |||
| f7eec6327a | |||
| 1a4301233c | |||
| b6b5a5d0de | |||
| 92c435cf23 | |||
| 8697ec952c | |||
| c6d3ae7c8b | |||
| 376c02f44c | |||
| da9a7affa9 | |||
| ac62f48a71 | |||
| 3d8cdd6ac6 | |||
| 9380b40c28 | |||
| cc823daee5 | |||
| b4594a4c47 | |||
| c8660c0426 | |||
| 375a724169 | |||
| 67375c3cbd | |||
| 1645aaf69a | |||
| 99b7d3a4f8 | |||
| eac77e45d2 | |||
| ebf05df568 | |||
| f464b25c2b | |||
| b0dbe1442c | |||
| 5ae3220e67 | |||
| f31a6408ac | |||
| 9234e7ebe9 | |||
| 609a7fb427 | |||
| a92bbbb275 | |||
| c2bf16e1ff | |||
| dce2509613 | |||
| 1ac336220e | |||
| a47f2fd496 | |||
| 45c0704b40 | |||
| 97cf5ce93c | |||
| 786367f9fe | |||
| f8c9cb1564 | |||
| ee11290037 | |||
| 42f3f6fb67 | |||
| 2a844b11bd | |||
| 5579fe4e95 | |||
| ee96435379 | |||
| 4fce96db0e | |||
| 14e7ecd171 | |||
| ffeece79fe | |||
| 5c86989dfa | |||
| c6a8c4d24f | |||
| 477691ec70 | |||
| 5518609d54 | |||
| 9f706d3123 | |||
| 5956d32c36 | |||
| a60e00159d | |||
| 47277086ac | |||
| c7571570e9 | |||
| 83d6859347 | |||
| a42986dcab | |||
| 13fbf36de0 | |||
| 4ba2aa4769 | |||
| 6b1a6a81eb | |||
| 62f8d5823f | |||
| 5cbefec0b0 | |||
| b154540885 | |||
| 9eda07b253 | |||
| 2995d2273a | |||
| 8fffdb244d | |||
| 657da0c554 | |||
| 1912e1cfcd | |||
| 3313c0223d | |||
| 3c9b87ba01 | |||
| 65b52a1836 | |||
| 684dbbd8e1 | |||
| 5a3127bb8e | |||
| 0b9cf600ba | |||
| b2f4fa3fb9 | |||
| ecae4f663a | |||
| 200da3375b | |||
| a3cedaa7b8 | |||
| 37fc48c64d | |||
| 1e4ee0a5e9 | |||
| bc383d9b23 | |||
| 27634b5aac | |||
| 2d479a15ec | |||
| a719ca752f | |||
| 089e329e4c | |||
| b84abaa0a9 | |||
| 6f4d85ea2b | |||
| 0f47a14e69 | |||
| 788c24a6f3 | |||
| a6c0bc7a21 | |||
| 661140ebf8 | |||
| 9692ae569e | |||
| f6d46bcf12 | |||
| 595635d93d | |||
| 89d92baf56 | |||
| 995605a2b6 | |||
| 749c123229 | |||
| f24f60cdef | |||
| 451ffec65b | |||
| 033ac555f2 | |||
| 14b57769d3 | |||
| 7c5b1b6fd1 | |||
| 14bc9f4429 | |||
| b72b13509b | |||
| daaf4540a4 | |||
| eee828ca46 | |||
| a084a6b49b | |||
| 1676056e43 | |||
| a601cf3ef8 | |||
| 3a7ddab1f3 | |||
| d1634d0813 | |||
| 31ccf4cefa | |||
| f528b7ff5e | |||
| d48333e71e | |||
| 7c0c096be4 | |||
| b23befe18c | |||
| ea48a57f95 | |||
| c7faf920db | |||
| 6f722a43f5 | |||
| cbe66ac397 | |||
| 0006f12151 | |||
| 8a3db78cd0 | |||
| 3dfe229024 | |||
| a8e4d6285d | |||
| bfe82bd322 | |||
| e0ebd083b9 | |||
| 6c6b18818a | |||
| 4338481f9f | |||
| e05f43b0b8 | |||
| a4e26f9895 | |||
| bc1e39450d | |||
| 87aa2cd8da | |||
| 9fa9896f0a | |||
| c4c034f2bb | |||
| aaed933eb9 | |||
| c7b541dec6 | |||
| e24933f0dd | |||
| e46b51d6a1 | |||
| f3f00e3d0f | |||
| 5a71e3eb25 | |||
| d9b108553a | |||
| b9a99cef90 | |||
| 2f366b55d9 | |||
| 13263cf39c | |||
| e03e93b0f5 | |||
| d62d1f2047 | |||
| ca3f3b6f9d | |||
| 96e1a2c393 | |||
| b6403580de | |||
| 8c5cca7ba5 | |||
| 4200daa1b9 | |||
| 1a5cfa8bbd | |||
| c371c4b127 | |||
| bd8123af59 | |||
| 00cb2fc67c | |||
| ea157cdb71 | |||
| aa343ade2e | |||
| 536e9ef262 | |||
| d200f12fa8 | |||
| c85bb7f22b | |||
| d575d8ac73 | |||
| 585b665b53 | |||
| 8ffe69d38e | |||
| 5e743b162d | |||
| b9ccf4bb12 | |||
| 4bc2c3107a | |||
| 008f5203c0 | |||
| 5d2acf44c1 | |||
| c04d3a3fe4 | |||
| 33ef76ad98 | |||
| c77cf1bf1e | |||
| f9274c8fda | |||
| 5ae02e0d1f | |||
| dcdf03ca8e | |||
| 775927c484 | |||
| bf230924d7 | |||
| d81fee17ba | |||
| 0a398bd712 | |||
| a7d41772f7 | |||
| 08754b9a24 | |||
| 133120e8c0 | |||
| ce3bfaeb5c | |||
| 7e518bf3ab | |||
| 106cbfacce | |||
| eb58745f19 | |||
| cf0fa32101 | |||
| d8894e92c0 | |||
| fc800672e1 | |||
| 462ef4671e | |||
| 212701fa8c | |||
| 75ccbe9bfb | |||
| 56e3bf64f2 | |||
| 228254db09 | |||
| 711c3f3639 | |||
| 5aa4c81c0d | |||
| db9ce69594 | |||
| 891044eaf7 | |||
| 2e84fb8063 | |||
| d0e6f06f38 | |||
| 32a7fc3239 | |||
| 29e4df9792 | |||
| 447bc6cb13 | |||
| dbb23132b3 | |||
| 179c283d75 | |||
| 427cb38f39 | |||
| ca9193438b | |||
| d3d991cb3a | |||
| d8ea93dcd6 | |||
| 3a649cfcfc | |||
| 446d47167b | |||
| 7b10424238 | |||
| 9bfca25044 | |||
| 2bde5d1309 | |||
| 93646d8f58 | |||
| 9162f6ec99 | |||
| e413191bf4 | |||
| 36cb770a86 | |||
| 7a383a3fa5 | |||
| c9342b6ad1 | |||
| aadbc191a8 | |||
| 4c9b46a20c | |||
| 0bde983ae9 | |||
| f55e295c01 | |||
| e06f564905 | |||
| 1a64de066e | |||
| 53251e53a0 | |||
| f0cde942fb | |||
| 7c9a07f384 | |||
| 96b9d264a9 | |||
| 0f3802474d | |||
| f4c8d6c999 | |||
| fd25c99609 | |||
| 79da2c0adc | |||
| 7ec682668c | |||
| 93df8b81b7 | |||
| a089807bf0 | |||
| f97956c181 | |||
| 8bc59c53b6 | |||
| a136ed3f87 | |||
| d903e76d7a | |||
| ac1c0e6baa | |||
| e57617c305 | |||
| 4d307bc6fa | |||
| f3f3eb463b | |||
| a8232341dd | |||
| 4e2b03000d | |||
| 4e777e05db | |||
| 1f7fe0d854 | |||
| a58d7c9314 | |||
| 22d66ce856 | |||
| 546d7e7280 | |||
| 9f2d241d87 | |||
| bcdd8b7e2b | |||
| 11ad8f8d5f | |||
| 24b2067f50 | |||
| f85c51f7de | |||
| 449098ad72 | |||
| d8c1f9ee6f | |||
| 05bc298cfc | |||
| d03c405d54 | |||
| be786c2cbe | |||
| 3a32825642 | |||
| 72e6bcc297 | |||
| 586000a98d | |||
| eae14095c0 | |||
| f7027d3fe6 | |||
| ca0a29b033 | |||
| 9e695dcfe8 | |||
| 4a8fd956ff | |||
| 284495700a | |||
| 1ba147009b | |||
| 2c541e68e1 | |||
| 95b39ea5e7 | |||
| 44e109cefa | |||
| 48763989d9 | |||
| f88bbee37b | |||
| fa3e1a6c35 | |||
| 6a5ce9aaa6 | |||
| bd7f840536 | |||
| 53fab12535 | |||
| 77745cfae6 | |||
| 73cb05956f | |||
| b3386c20ab | |||
| 83f419bff1 | |||
| c6a4a2dd0b | |||
| de3c59b7f5 | |||
| 28c89ce51d | |||
| a6fcf3421c | |||
| 2fcb2493d1 | |||
| 64b2b7850f | |||
| 429b53feba | |||
| a7042ba8a6 | |||
| f4c0413360 | |||
| 216b14663c | |||
| 895bda2e3f | |||
| 459866e924 | |||
| bdada76518 | |||
| 53129d2fcb | |||
| f174a555dd | |||
| 21da76f493 | |||
| 4d74de93ba | |||
| 92780b7152 | |||
| 0d73d19e44 | |||
| 689511a130 | |||
| d22d4d01c5 | |||
| fe691c2460 | |||
| f0d3c787d9 | |||
| 6b0945dc6f | |||
| d84358db3a | |||
| 5bb90e6f15 | |||
| 0840ac2c8b | |||
| 44a153ebb0 | |||
| 2214bdc1ad | |||
| c958e49118 | |||
| f7ae01ddc1 | |||
| 71b68f3354 | |||
| a7e2e56175 | |||
| 7f4e82c27c | |||
| 6e2013f0c7 | |||
| 438e0947ac | |||
| 2e2d912aeb | |||
| 44d0f67e18 | |||
| 22225dd841 | |||
| 650d1e9647 | |||
| 9265147e12 | |||
| 8358d67f3e | |||
| 2930c2b640 | |||
| 42e64d77cb | |||
| 440fc103b1 | |||
| 128e3c747e | |||
| a9439758c9 | |||
| 1247baaa36 | |||
| 59239060a6 | |||
| 42eae556e8 | |||
| a61b20ace6 | |||
| 9425fc521e | |||
| 47c629f9a1 | |||
| 11a669bf99 | |||
| 366aba68ce | |||
| 2f5a4d5515 | |||
| f7c41d1b9a | |||
| fbd24fd059 | |||
| cc6994e238 | |||
| 96c07f86b5 | |||
| e502721d13 | |||
| a3b52dc9ff | |||
| e07fe76e39 | |||
| a4ee90ca5c | |||
| c016187498 | |||
| b0d74d31de | |||
| a85255b5fd | |||
| 888dfbcafb | |||
| 5de4745303 | |||
| 6883e3b5fa | |||
| ef15b96f15 | |||
| 03810e0093 | |||
| bbbc9da76e | |||
| 11b4cf0351 | |||
| 6df64d68af | |||
| 3925fe74af | |||
| edb48d8364 | |||
| 1d6b67658e | |||
| bcd1476d2d | |||
| d2251e4cb2 | |||
| bd275539d1 | |||
| 6042909915 | |||
| 38fc05e0d9 | |||
| 3ddbd59995 | |||
| 0a2a5bdd7a | |||
| 088aa83fea | |||
| 0797b8b262 | |||
| 54a9db2acb | |||
| e9f88a9c92 | |||
| 311d651ba7 | |||
| 7789252f76 | |||
| c58e96bce7 | |||
| 2a0ee12780 | |||
| e7bc9168ae | |||
| 98e5cb4979 | |||
| 6fc6829bc8 | |||
| 516f0fa1b4 | |||
| 6f556c45df | |||
| e237d58906 | |||
| cc9807846e | |||
| df7f9dbaeb | |||
| dee350742f | |||
| 735f9a9534 | |||
| 859f10ee1f | |||
| a002eeed25 | |||
| c9c5e5658f | |||
| edfcb53491 | |||
| d158c13c7c | |||
| f2dc43b47f | |||
| e7cb246639 | |||
| 19d109b3bf | |||
| b2fc4b9886 | |||
| e6dd0f0abe | |||
| 6f4186ae07 | |||
| 44a3d03974 | |||
| 649f6897a4 | |||
| 9edc30cef0 | |||
| b0aaa2b791 | |||
| 108a4ee2bf | |||
| 1083b80849 | |||
| a2ff420f89 | |||
| 5d718f7621 | |||
| 63096d5919 | |||
| b80f1794e2 | |||
| 166c64798e | |||
| 150b51b327 | |||
| 878e9541da | |||
| 8b416b113c | |||
| b8dd85c88f | |||
| 7098b15d0d | |||
| 1bd796cd94 | |||
| cd41f8f060 | |||
| 2e117d6641 | |||
| 70513ff8b8 |
+1
-6
@@ -22,9 +22,4 @@
|
||||
**/secrets.dev.yaml
|
||||
**/values.dev.yaml
|
||||
LICENSE
|
||||
README.md
|
||||
!**/.gitignore
|
||||
!.git/HEAD
|
||||
!.git/config
|
||||
!.git/packed-refs
|
||||
!.git/refs/heads/**
|
||||
README.md
|
||||
+37
-5
@@ -126,14 +126,46 @@ csharp_style_deconstructed_variable_declaration = true:suggestion
|
||||
csharp_style_unused_value_assignment_preference = discard_variable:suggestion
|
||||
csharp_style_unused_value_expression_statement_preference = discard_variable:silent
|
||||
|
||||
# SA0001: XML comment analysis disabled
|
||||
dotnet_diagnostic.SA0001.severity = suggestion
|
||||
|
||||
dotnet_diagnostic.CS1570.severity = none
|
||||
dotnet_diagnostic.CS1587.severity = none
|
||||
dotnet_diagnostic.CS8619.severity = none
|
||||
dotnet_diagnostic.CS8620.severity = none
|
||||
dotnet_diagnostic.CS8632.severity = none
|
||||
# SA1002: Semicolons should be spaced correctly
|
||||
dotnet_diagnostic.SA1002.severity = suggestion
|
||||
|
||||
# SA1005: Single line comments should begin with single space
|
||||
dotnet_diagnostic.SA1005.severity = none
|
||||
dotnet_diagnostic.CS8618.severity = none
|
||||
dotnet_diagnostic.CS8604.severity = none
|
||||
dotnet_diagnostic.CS8600.severity = none
|
||||
dotnet_diagnostic.CS1570.severity = none
|
||||
dotnet_diagnostic.CS8601.severity = none
|
||||
dotnet_diagnostic.CS8632.severity = none
|
||||
dotnet_diagnostic.CS8625.severity = none
|
||||
|
||||
dotnet_diagnostic.CS8603.severity = none
|
||||
dotnet_diagnostic.CS8602.severity = none
|
||||
dotnet_diagnostic.CS1998.severity = none
|
||||
dotnet_diagnostic.CS0168.severity = none
|
||||
dotnet_diagnostic.CS0219.severity = none
|
||||
dotnet_diagnostic.CS0108.severity = none
|
||||
dotnet_diagnostic.CS0120.severity = none
|
||||
|
||||
dotnet_diagnostic.CS8620.severity = none
|
||||
dotnet_diagnostic.CS8619.severity = none
|
||||
dotnet_diagnostic.CS0162.severity = none
|
||||
dotnet_diagnostic.CS0472.severity = none
|
||||
|
||||
dotnet_diagnostic.CS8629.severity = none
|
||||
dotnet_diagnostic.CS8073.severity = none
|
||||
dotnet_diagnostic.CS0414.severity = none
|
||||
dotnet_diagnostic.CS1573.severity = none
|
||||
dotnet_diagnostic.CS0109.severity = none
|
||||
|
||||
csharp_style_prefer_method_group_conversion = true:silent
|
||||
csharp_style_prefer_top_level_statements = true:silent
|
||||
dotnet_diagnostic.SA1003.severity = silent
|
||||
dotnet_diagnostic.SA1108.severity = silent
|
||||
dotnet_diagnostic.SA1107.severity = silent
|
||||
|
||||
[*.vb]
|
||||
#### 命名样式 ####
|
||||
|
||||
+26
-4
@@ -1,4 +1,26 @@
|
||||
FROM registry.cn-shanghai.aliyuncs.com/extimaging/aspnetcore:v8.2
|
||||
WORKDIR /app
|
||||
COPY publish .
|
||||
ENTRYPOINT ["dotnet", "IRaCIS.Core.API.dll"]
|
||||
#See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging.
|
||||
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
|
||||
WORKDIR /app
|
||||
EXPOSE 80
|
||||
|
||||
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
|
||||
WORKDIR /src
|
||||
COPY ["IRaCIS.Core.API/IRaCIS.Core.API.csproj", "IRaCIS.Core.API/"]
|
||||
COPY ["IRaCIS.Core.Application/IRaCIS.Core.Application.csproj", "IRaCIS.Core.Application/"]
|
||||
COPY ["IRaCIS.Core.Infra.EFCore/IRaCIS.Core.Infra.EFCore.csproj", "IRaCIS.Core.Infra.EFCore/"]
|
||||
COPY ["IRaCIS.Core.Domain/IRaCIS.Core.Domain.csproj", "IRaCIS.Core.Domain/"]
|
||||
COPY ["IRaCIS.Core.Domain.Share/IRaCIS.Core.Domain.Share.csproj", "IRaCIS.Core.Domain.Share/"]
|
||||
COPY ["IRaCIS.Core.Infrastructure/IRaCIS.Core.Infrastructure.csproj", "IRaCIS.Core.Infrastructure/"]
|
||||
RUN dotnet restore "IRaCIS.Core.API/IRaCIS.Core.API.csproj"
|
||||
COPY . .
|
||||
WORKDIR "/src/IRaCIS.Core.API"
|
||||
RUN dotnet build "IRaCIS.Core.API.csproj" -c Release -o /app/build
|
||||
|
||||
FROM build AS publish
|
||||
RUN dotnet publish "IRaCIS.Core.API.csproj" -c Release -o /app/publish /p:UseAppHost=false
|
||||
|
||||
FROM base AS final
|
||||
WORKDIR /app
|
||||
COPY --from=publish /app/publish .
|
||||
ENTRYPOINT ["dotnet", "IRaCIS.Core.API.dll"]
|
||||
@@ -1,37 +0,0 @@
|
||||
using IRaCIS.Core.Infrastructure.Extention;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
|
||||
namespace IRaCIS.Core.SCP.Filter
|
||||
{
|
||||
|
||||
|
||||
public class ModelActionFilter : ActionFilterAttribute, IActionFilter
|
||||
{
|
||||
public IStringLocalizer _localizer;
|
||||
public ModelActionFilter(IStringLocalizer localizer)
|
||||
{
|
||||
_localizer = localizer;
|
||||
}
|
||||
|
||||
public override void OnActionExecuting(ActionExecutingContext context)
|
||||
{
|
||||
if (!context.ModelState.IsValid)
|
||||
{
|
||||
|
||||
var validationErrors = context.ModelState
|
||||
.Keys
|
||||
.SelectMany(k => context.ModelState[k]!.Errors)
|
||||
.Select(e => e.ErrorMessage)
|
||||
.ToArray();
|
||||
|
||||
//---提供给接口的参数无效。
|
||||
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["ModelAction_InvalidAPIParameter"] + JsonConvert.SerializeObject( validationErrors)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
|
||||
namespace IRaCIS.Core.SCP.Filter
|
||||
{
|
||||
#region snippet_DisableFormValueModelBindingAttribute
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
|
||||
public class DisableFormValueModelBindingAttribute : Attribute, IResourceFilter
|
||||
{
|
||||
public void OnResourceExecuting(ResourceExecutingContext context)
|
||||
{
|
||||
|
||||
|
||||
var factories = context.ValueProviderFactories;
|
||||
//factories.RemoveType<FormValueProviderFactory>();
|
||||
factories.RemoveType<FormFileValueProviderFactory>();
|
||||
factories.RemoveType<JQueryFormValueProviderFactory>();
|
||||
context.HttpContext.Request.EnableBuffering();
|
||||
}
|
||||
|
||||
public void OnResourceExecuted(ResourceExecutedContext context)
|
||||
{
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
using IRaCIS.Core.Infrastructure;
|
||||
using IRaCIS.Core.Infrastructure.Extention;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace IRaCIS.Core.SCP.Filter
|
||||
{
|
||||
public class ProjectExceptionFilter : Attribute, IExceptionFilter
|
||||
{
|
||||
private readonly ILogger<ProjectExceptionFilter> _logger;
|
||||
|
||||
public IStringLocalizer _localizer;
|
||||
|
||||
public ProjectExceptionFilter(IStringLocalizer localizer, ILogger<ProjectExceptionFilter> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
_localizer = localizer;
|
||||
}
|
||||
public void OnException(ExceptionContext context)
|
||||
{
|
||||
//context.ExceptionHandled;//记录当前这个异常是否已经被处理过了
|
||||
|
||||
if (!context.ExceptionHandled)
|
||||
{
|
||||
if (context.Exception.GetType().Name == "DbUpdateConcurrencyException")
|
||||
{
|
||||
//---并发更新,当前不允许该操作
|
||||
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["ProjectException_ConcurrentUpdateNotAllowed"] + context.Exception.Message));
|
||||
}
|
||||
|
||||
if (context.Exception.GetType() == typeof(BusinessValidationFailedException))
|
||||
{
|
||||
var error = context.Exception as BusinessValidationFailedException;
|
||||
|
||||
context.Result = new JsonResult(ResponseOutput.NotOk(context.Exception.Message, error!.Code));
|
||||
}
|
||||
else if(context.Exception.GetType() == typeof(QueryBusinessObjectNotExistException))
|
||||
{
|
||||
context.Result = new JsonResult(ResponseOutput.NotOk( context.Exception.Message, ApiResponseCodeEnum.DataNotExist));
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["Project_ExceptionContactDeveloper"] + (context.Exception.InnerException is null ? (context.Exception.Message /*+ context.Exception.StackTrace*/)
|
||||
: (context.Exception.InnerException?.Message /*+ context.Exception.InnerException?.StackTrace*/)), ApiResponseCodeEnum.ProgramException));
|
||||
}
|
||||
|
||||
|
||||
_logger.LogError(context.Exception.InnerException is null ? (context.Exception.Message + context.Exception.StackTrace) : (context.Exception.InnerException?.Message + context.Exception.InnerException?.StackTrace));
|
||||
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
//继续
|
||||
}
|
||||
context.ExceptionHandled = true;//标记当前异常已经被处理过了
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,120 +0,0 @@
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using IRaCIS.Core.Infrastructure.Extention;
|
||||
|
||||
namespace IRaCIS.Application.Services.BusinessFilter
|
||||
{
|
||||
/// <summary>
|
||||
/// 统一返回前端数据包装,之前在控制器包装,现在修改为动态Api 在ResultFilter这里包装,减少重复冗余代码
|
||||
/// by zhouhang 2021.09.12 周末
|
||||
/// </summary>
|
||||
public class UnifiedApiResultFilter : Attribute, IAsyncResultFilter
|
||||
{
|
||||
/// <summary>
|
||||
/// 异步版本
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <param name="next"></param>
|
||||
/// <returns></returns>
|
||||
public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next)
|
||||
{
|
||||
|
||||
if (context.Result is ObjectResult objectResult)
|
||||
{
|
||||
var statusCode = objectResult.StatusCode ?? context.HttpContext.Response.StatusCode;
|
||||
|
||||
//是200 并且没有包装 那么包装结果
|
||||
if (statusCode == 200 && !(objectResult.Value is IResponseOutput))
|
||||
{
|
||||
//if (objectResult.Value == null)
|
||||
//{
|
||||
// var apiResponse = ResponseOutput.DBNotExist();
|
||||
|
||||
// objectResult.Value = apiResponse;
|
||||
// objectResult.DeclaredType = apiResponse.GetType();
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
|
||||
var type = objectResult.Value?.GetType();
|
||||
|
||||
|
||||
if ( type!=null&& type.IsGenericType&&(type.GetGenericTypeDefinition()==typeof(ValueTuple<,>)|| type.GetGenericTypeDefinition()==typeof(Tuple<,>)))
|
||||
{
|
||||
|
||||
//报错
|
||||
//var tuple = (object, object))objectResult.Value;
|
||||
|
||||
//var (val1, val2) = ((dynamic, dynamic))objectResult.Value;
|
||||
//var apiResponse = ResponseOutput.Ok(val1, val2);
|
||||
|
||||
//OK
|
||||
var tuple = (dynamic)objectResult.Value;
|
||||
var apiResponse = ResponseOutput.Ok(tuple.Item1, tuple.Item2);
|
||||
|
||||
|
||||
objectResult.Value = apiResponse;
|
||||
objectResult.DeclaredType = apiResponse.GetType();
|
||||
}
|
||||
else
|
||||
{
|
||||
var apiResponse = ResponseOutput.Ok(objectResult.Value);
|
||||
|
||||
objectResult.Value = apiResponse;
|
||||
objectResult.DeclaredType = apiResponse.GetType();
|
||||
}
|
||||
|
||||
|
||||
//}
|
||||
|
||||
}
|
||||
//如果不是200 是IResponseOutput 不处理
|
||||
else if (statusCode != 200 && (objectResult.Value is IResponseOutput))
|
||||
{
|
||||
}
|
||||
|
||||
else if(statusCode != 200&&!(objectResult.Value is IResponseOutput))
|
||||
{
|
||||
//---程序错误,请联系开发人员。
|
||||
var apiResponse = ResponseOutput.NotOk(StaticData.International("UnifiedAPI_ProgramError"));
|
||||
|
||||
objectResult.Value = apiResponse;
|
||||
objectResult.DeclaredType = apiResponse.GetType();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
await next.Invoke();
|
||||
|
||||
}
|
||||
|
||||
public static bool IsTupleType(Type type, bool checkBaseTypes = false)
|
||||
{
|
||||
if (type == null)
|
||||
throw new ArgumentNullException(nameof(type));
|
||||
|
||||
if (type == typeof(Tuple))
|
||||
return true;
|
||||
|
||||
while (type != null)
|
||||
{
|
||||
if (type.IsGenericType)
|
||||
{
|
||||
var genType = type.GetGenericTypeDefinition();
|
||||
if (genType == typeof(Tuple<>)
|
||||
|| genType == typeof(Tuple<,>)
|
||||
|| genType == typeof(Tuple<,>))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!checkBaseTypes)
|
||||
break;
|
||||
|
||||
type = type.BaseType;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
|
||||
using FellowOakDicom.Imaging;
|
||||
using SixLabors.ImageSharp.Formats.Jpeg;
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.Processing;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace IRaCIS.Core.Application.Helper;
|
||||
|
||||
public static class ImageHelper
|
||||
{
|
||||
|
||||
// 采用ImageSharp组件 跨平台,不依赖windows 平台图像处理组件,这里大坑(net 6 下,之前由于Dicom处理最新组件 依赖了这个库的一个旧版本,导致缩略图bug,单独升级依赖组件才解决)
|
||||
public static void ResizeSave(string filePath, string? fileStorePath)
|
||||
{
|
||||
|
||||
fileStorePath = fileStorePath ?? filePath + ".preview.jpeg";
|
||||
|
||||
using (var image = SixLabors.ImageSharp.Image.Load(filePath))
|
||||
{
|
||||
|
||||
image.Mutate(x => x.Resize(500, 500));
|
||||
|
||||
image.Save(fileStorePath);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static Stream RenderPreviewJpeg(string filePath)
|
||||
{
|
||||
string jpegPath = filePath + ".preview.jpg";
|
||||
|
||||
if (!File.Exists(jpegPath))
|
||||
{
|
||||
using (Stream stream = new FileStream(jpegPath, FileMode.Create))
|
||||
{
|
||||
DicomImage image = new DicomImage(filePath);
|
||||
|
||||
var sharpimage = image.RenderImage().AsSharpImage();
|
||||
|
||||
sharpimage.Save(stream, new JpegEncoder());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return new FileStream(jpegPath, FileMode.Open);
|
||||
}
|
||||
|
||||
public static void RemovePreviewJpeg(string filePath)
|
||||
{
|
||||
string jpegPath = filePath + ".preview.jpg";
|
||||
if (File.Exists(jpegPath)) File.Delete(jpegPath);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
using Autofac;
|
||||
using IRaCIS.Core.Infra.EFCore;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Panda.DynamicWebApi;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using IRaCIS.Core.Domain.Models;
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using IRaCIS.Application.Services;
|
||||
using AutoMapper;
|
||||
using IRaCIS.Core.SCP.Service;
|
||||
|
||||
namespace IRaCIS.Core.SCP
|
||||
{
|
||||
// ReSharper disable once IdentifierTypo
|
||||
public class AutofacModuleSetup : Autofac.Module
|
||||
{
|
||||
protected override void Load(ContainerBuilder containerBuilder)
|
||||
{
|
||||
|
||||
#region byzhouhang 20210917 此处注册泛型仓储 可以减少Domain层 和Infra.EFcore 两层 空的仓储接口定义和 仓储文件定义
|
||||
|
||||
containerBuilder.RegisterGeneric(typeof(Repository<>))
|
||||
.As(typeof(IRepository<>)).InstancePerLifetimeScope();//注册泛型仓储
|
||||
|
||||
containerBuilder.RegisterType<Repository>().As<IRepository>().InstancePerLifetimeScope();
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region 指定控制器也由autofac 来进行实例获取 https://www.cnblogs.com/xwhqwer/p/15320838.html
|
||||
|
||||
//获取所有控制器类型并使用属性注入
|
||||
containerBuilder.RegisterAssemblyTypes(typeof(BaseService).Assembly)
|
||||
.Where(type => typeof(IDynamicWebApi).IsAssignableFrom(type))
|
||||
.PropertiesAutowired();
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
Assembly application = Assembly.LoadFrom(AppDomain.CurrentDomain.BaseDirectory + typeof(BaseService).Assembly.GetName().Name+".dll");
|
||||
containerBuilder.RegisterAssemblyTypes(application).Where(t => t.FullName.Contains("Service"))
|
||||
.PropertiesAutowired().AsImplementedInterfaces();
|
||||
|
||||
|
||||
//containerBuilder.RegisterType<HttpContextAccessor>().As<IHttpContextAccessor>().SingleInstance();
|
||||
//containerBuilder.RegisterType<UserInfo>().As<IUserInfo>().InstancePerLifetimeScope();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
using EntityFramework.Exceptions.SqlServer;
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using IRaCIS.Core.Infra.EFCore;
|
||||
using Medallion.Threading;
|
||||
using Medallion.Threading.SqlServer;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Diagnostics;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace IRaCIS.Core.SCP
|
||||
{
|
||||
public static class EFSetup
|
||||
{
|
||||
public static void AddEFSetup( this IServiceCollection services, IConfiguration configuration)
|
||||
{
|
||||
services.AddHttpContextAccessor();
|
||||
services.AddScoped<IUserInfo, UserInfo>();
|
||||
services.AddScoped<ISaveChangesInterceptor, AuditEntityInterceptor>();
|
||||
|
||||
|
||||
//这个注入没有成功--注入是没问题的,构造函数也只是支持参数就好,错在注入的地方不能写DbContext
|
||||
//Web程序中通过重用池中DbContext实例可提高高并发场景下的吞吐量, 这在概念上类似于ADO.NET Provider原生的连接池操作方式,具有节省DbContext实例化成本的优点
|
||||
services.AddDbContext<IRaCISDBContext>((sp, options) =>
|
||||
{
|
||||
// 在控制台
|
||||
//public static readonly ILoggerFactory MyLoggerFactory = LoggerFactory.Create(builder => { builder.AddConsole(); });
|
||||
var logFactory = LoggerFactory.Create(builder => { builder.AddDebug(); });
|
||||
|
||||
options.UseSqlServer(configuration.GetSection("ConnectionStrings:RemoteNew").Value,
|
||||
contextOptionsBuilder => contextOptionsBuilder.EnableRetryOnFailure());
|
||||
|
||||
options.UseLoggerFactory(logFactory);
|
||||
|
||||
options.UseExceptionProcessor();
|
||||
|
||||
options.EnableSensitiveDataLogging();
|
||||
|
||||
options.AddInterceptors(new QueryWithNoLockDbCommandInterceptor());
|
||||
options.AddInterceptors(sp.GetServices<ISaveChangesInterceptor>());
|
||||
|
||||
options.UseProjectables();
|
||||
|
||||
|
||||
|
||||
});
|
||||
|
||||
//// Register an additional context factory as a Scoped service, which gets a pooled context from the Singleton factory we registered above,
|
||||
//services.AddScoped<IRaCISDBScopedFactory>();
|
||||
|
||||
//// Finally, arrange for a context to get injected from our Scoped factory:
|
||||
//services.AddScoped(sp => sp.GetRequiredService<IRaCISDBScopedFactory>().CreateDbContext());
|
||||
|
||||
//注意区分 easy caching 也有 IDistributedLockProvider
|
||||
services.AddSingleton<IDistributedLockProvider>(sp =>
|
||||
{
|
||||
//var connection = ConnectionMultiplexer.Connect(configuration["Redis:Configuration"]!);
|
||||
|
||||
return new SqlDistributedSynchronizationProvider(configuration.GetSection("ConnectionStrings:RemoteNew").Value);
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
|
||||
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
using System;
|
||||
|
||||
namespace IRaCIS.Core.SCP
|
||||
{
|
||||
public static class NewtonsoftJsonSetup
|
||||
{
|
||||
public static void AddNewtonsoftJsonSetup(this IMvcBuilder builder, IServiceCollection services)
|
||||
{
|
||||
services.AddHttpContextAccessor();
|
||||
services.AddScoped<IOSSService,OSSService>();
|
||||
|
||||
builder.AddNewtonsoftJson(options =>
|
||||
{
|
||||
//options.SerializerSettings.PreserveReferencesHandling = PreserveReferencesHandling.Objects;
|
||||
// 忽略循环引用
|
||||
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
|
||||
//options.SerializerSettings.TypeNameHandling = TypeNameHandling.All;
|
||||
|
||||
//处理返回给前端 可空类型 给出默认值 比如in? 为null 设置 默认值0
|
||||
options.SerializerSettings.ContractResolver = new NullToEmptyStringResolver(); //new DefaultContractResolver();// new NullToEmptyStringResolver();
|
||||
// 设置时间格式
|
||||
options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss";
|
||||
|
||||
options.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.RoundtripKind;
|
||||
|
||||
//options.SerializerSettings.Converters.Add(new JSONCustomDateConverter()) ;
|
||||
|
||||
//options.SerializerSettings.Converters.Add(services.BuildServiceProvider().GetService<JSONTimeZoneConverter>());
|
||||
|
||||
|
||||
|
||||
})
|
||||
.AddControllersAsServices()//动态webApi属性注入需要
|
||||
.ConfigureApiBehaviorOptions(o =>
|
||||
{
|
||||
o.SuppressModelStateInvalidFilter = true; //自己写验证
|
||||
|
||||
});
|
||||
|
||||
|
||||
Newtonsoft.Json.JsonSerializerSettings setting = new Newtonsoft.Json.JsonSerializerSettings();
|
||||
JsonConvert.DefaultSettings = new Func<JsonSerializerSettings>(() =>
|
||||
{
|
||||
//日期类型默认格式化处理
|
||||
setting.DateFormatString = "yyyy-MM-dd HH:mm:ss";
|
||||
setting.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
|
||||
|
||||
return setting;
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace IRaCIS.Core.SCP
|
||||
{
|
||||
public class NullToEmptyStringResolver : DefaultContractResolver
|
||||
{
|
||||
|
||||
|
||||
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
|
||||
{
|
||||
IList<JsonProperty> properties = base.CreateProperties(type, memberSerialization);
|
||||
|
||||
var list= type.GetProperties()
|
||||
.Select(p =>
|
||||
{
|
||||
var jp = base.CreateProperty(p, memberSerialization);
|
||||
jp.ValueProvider = new NullToEmptyStringValueProvider(p);
|
||||
return jp;
|
||||
}).ToList();
|
||||
|
||||
var uu = list.Select(t => t.PropertyName).ToList();
|
||||
|
||||
//获取复杂对象属性
|
||||
properties = properties.TakeWhile(t => !uu.Contains(t.PropertyName)).ToList();
|
||||
|
||||
list.AddRange(properties);
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
|
||||
namespace IRaCIS.Core.SCP
|
||||
{
|
||||
|
||||
public class NullToEmptyStringValueProvider : IValueProvider
|
||||
{
|
||||
PropertyInfo _MemberInfo;
|
||||
public NullToEmptyStringValueProvider(PropertyInfo memberInfo)
|
||||
{
|
||||
_MemberInfo = memberInfo;
|
||||
}
|
||||
public object GetValue(object target)
|
||||
{
|
||||
object result = _MemberInfo.GetValue(target);
|
||||
if (_MemberInfo.PropertyType == typeof(string) && result == null) result = "";
|
||||
else if (_MemberInfo.PropertyType == typeof(String[]) && result == null) result = new string[] { };
|
||||
//else if (_MemberInfo.PropertyType == typeof(Nullable<Int32>) && result == null) result = 0;
|
||||
else if (_MemberInfo.PropertyType == typeof(Nullable<Decimal>) && result == null) result = 0.00M;
|
||||
|
||||
return result;
|
||||
}
|
||||
public void SetValue(object target, object value)
|
||||
{
|
||||
|
||||
if(_MemberInfo.PropertyType == typeof(string))
|
||||
{
|
||||
//去掉前后空格
|
||||
_MemberInfo.SetValue(target, value==null?string.Empty: value.ToString()==string.Empty? value:value.ToString().Trim());
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
_MemberInfo.SetValue(target, value);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Aliyun.OSS.SDK.NetCore" Version="2.14.1" />
|
||||
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="10.0.0" />
|
||||
<PackageReference Include="DistributedLock.Core" Version="1.0.7" />
|
||||
<PackageReference Include="DistributedLock.SqlServer" Version="1.0.5" />
|
||||
<PackageReference Include="fo-dicom" Version="5.1.3" />
|
||||
<PackageReference Include="fo-dicom.Codecs" Version="5.14.5" />
|
||||
<PackageReference Include="fo-dicom.Imaging.ImageSharp" Version="5.1.3" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.8" />
|
||||
<PackageReference Include="AutoMapper" Version="13.0.1" />
|
||||
<PackageReference Include="Minio" Version="6.0.3" />
|
||||
<PackageReference Include="My.Extensions.Localization.Json" Version="3.3.0">
|
||||
<TreatAsUsed>true</TreatAsUsed>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Panda.DynamicWebApi" Version="1.2.2" />
|
||||
<PackageReference Include="Serilog.Enrichers.ClientInfo" Version="2.1.1" />
|
||||
<PackageReference Include="Serilog.Extensions.Hosting" Version="8.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.7.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\IRaCIS.Core.Domain\IRaCIS.Core.Domain.csproj" />
|
||||
<ProjectReference Include="..\IRaCIS.Core.Infra.EFCore\IRaCIS.Core.Infra.EFCore.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,214 +0,0 @@
|
||||
|
||||
using Autofac;
|
||||
using Autofac.Extensions.DependencyInjection;
|
||||
using AutoMapper.EquivalencyExpression;
|
||||
using FellowOakDicom;
|
||||
using FellowOakDicom.Imaging;
|
||||
using FellowOakDicom.Imaging.NativeCodec;
|
||||
using FellowOakDicom.Network;
|
||||
using IRaCIS.Core.Infra.EFCore;
|
||||
using IRaCIS.Core.SCP;
|
||||
using IRaCIS.Core.SCP.Filter;
|
||||
using IRaCIS.Core.SCP.Service;
|
||||
using MassTransit;
|
||||
using MassTransit.NewIdProviders;
|
||||
using Microsoft.AspNetCore.HttpOverrides;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Panda.DynamicWebApi;
|
||||
using Serilog;
|
||||
using Serilog.Events;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
|
||||
//以配置文件为准,否则 从url中取环境值(服务以命令行传递参数启动,配置文件配置了就不需要传递环境参数)
|
||||
var config = new ConfigurationBuilder()
|
||||
.AddEnvironmentVariables()
|
||||
.Build();
|
||||
|
||||
var enviromentName = config["ASPNETCORE_ENVIRONMENT"];
|
||||
|
||||
var builder = WebApplication.CreateBuilder(new WebApplicationOptions
|
||||
{
|
||||
EnvironmentName = enviromentName
|
||||
});
|
||||
|
||||
#region 兼容windows 服务命令行的方式
|
||||
|
||||
int urlsIndex = Array.FindIndex(args, arg => arg != null && arg.StartsWith("--port"));
|
||||
|
||||
if (urlsIndex > -1)
|
||||
{
|
||||
var port = args[urlsIndex].Substring("--port=".Length);
|
||||
Console.WriteLine(port);
|
||||
builder.WebHost.UseUrls($"http://0.0.0.0:{port}");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 主机配置
|
||||
|
||||
NewId.SetProcessIdProvider(new CurrentProcessIdProvider());
|
||||
|
||||
builder.Configuration.AddJsonFile("appsettings.json", false, true)
|
||||
.AddJsonFile($"appsettings.{enviromentName}.json", false, true);
|
||||
builder.Host
|
||||
.UseServiceProviderFactory(new AutofacServiceProviderFactory())
|
||||
.ConfigureContainer<ContainerBuilder>(containerBuilder =>
|
||||
{
|
||||
containerBuilder.RegisterModule<AutofacModuleSetup>();
|
||||
})
|
||||
.UseSerilog();
|
||||
#endregion
|
||||
|
||||
#region 配置服务
|
||||
var _configuration = builder.Configuration;
|
||||
|
||||
//健康检查
|
||||
builder.Services.AddHealthChecks();
|
||||
|
||||
//本地化
|
||||
builder.Services.AddJsonLocalization(options => options.ResourcesPath = "Resources");
|
||||
|
||||
|
||||
// 异常、参数统一验证过滤器、Json序列化配置、字符串参数绑型统一Trim()
|
||||
builder.Services.AddControllers(options =>
|
||||
{
|
||||
options.Filters.Add<ModelActionFilter>();
|
||||
options.Filters.Add<ProjectExceptionFilter>();
|
||||
options.Filters.Add<UnitOfWorkFilter>();
|
||||
|
||||
|
||||
})
|
||||
.AddNewtonsoftJsonSetup(builder.Services); // NewtonsoftJson 序列化 处理
|
||||
|
||||
|
||||
builder.Services.AddOptions().Configure<AliyunOSSOptions>(_configuration.GetSection("AliyunOSS"));
|
||||
builder.Services.AddOptions().Configure<ObjectStoreServiceOptions>(_configuration.GetSection("ObjectStoreService"));
|
||||
builder.Services.AddOptions().Configure<DicomSCPServiceOption>(_configuration.GetSection("DicomSCPServiceConfig"));
|
||||
|
||||
|
||||
//动态WebApi + UnifiedApiResultFilter 省掉控制器代码
|
||||
//动态webApi 目前存在的唯一小坑是生成api上服务上的动态代理AOP失效 间接掉用不影响
|
||||
builder.Services
|
||||
.AddDynamicWebApi(dynamicWebApiOption =>
|
||||
{
|
||||
//默认是 api
|
||||
dynamicWebApiOption.DefaultApiPrefix = "";
|
||||
//首字母小写
|
||||
dynamicWebApiOption.GetRestFulActionName = (actionName) => char.ToLower(actionName[0]) + actionName.Substring(1);
|
||||
//删除 Service后缀
|
||||
dynamicWebApiOption.RemoveControllerPostfixes.Add("Service");
|
||||
|
||||
});
|
||||
|
||||
//AutoMapper
|
||||
builder.Services.AddAutoMapper(automapper =>
|
||||
{
|
||||
|
||||
automapper.AddCollectionMappers();
|
||||
|
||||
|
||||
}, typeof(BaseService).Assembly);
|
||||
|
||||
//EF ORM QueryWithNoLock
|
||||
builder.Services.AddEFSetup(_configuration);
|
||||
|
||||
|
||||
//转发头设置 获取真实IP
|
||||
builder.Services.Configure<ForwardedHeadersOptions>(options =>
|
||||
{
|
||||
options.ForwardedHeaders =
|
||||
ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
|
||||
});
|
||||
|
||||
//Dicom影像渲染图片 跨平台
|
||||
//builder.Services.AddDicomSetup();
|
||||
new DicomSetupBuilder()
|
||||
.RegisterServices(s =>
|
||||
s.AddFellowOakDicom()
|
||||
.AddTranscoderManager<NativeTranscoderManager>()
|
||||
//.AddTranscoderManager<FellowOakDicom.Imaging.NativeCodec.NativeTranscoderManager>()
|
||||
.AddImageManager<ImageSharpImageManager>())
|
||||
.SkipValidation()
|
||||
.Build();
|
||||
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
// Add services to the container.
|
||||
|
||||
builder.Services.AddControllers();
|
||||
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
|
||||
builder.Services.AddEndpointsApiExplorer();
|
||||
builder.Services.AddSwaggerGen();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
// Configure the HTTP request pipeline.
|
||||
//if (app.Environment.IsDevelopment())
|
||||
//{
|
||||
app.UseSwagger();
|
||||
app.UseSwaggerUI();
|
||||
//}
|
||||
|
||||
app.UseAuthorization();
|
||||
|
||||
app.MapControllers();
|
||||
|
||||
#region 日志
|
||||
|
||||
//Log.Logger = new LoggerConfiguration()
|
||||
// .MinimumLevel.Information()
|
||||
// .MinimumLevel.Override("Microsoft", LogEventLevel.Information)
|
||||
// // Filter out ASP.NET Core infrastructre logs that are Information and below 日志太多了 一个请求 记录好几条
|
||||
// .MinimumLevel.Override("Microsoft.AspNetCore", LogEventLevel.Warning)
|
||||
// .MinimumLevel.Override("Hangfire", LogEventLevel.Warning)
|
||||
// .MinimumLevel.Override("System.Net.Http.HttpClient.HttpReports", LogEventLevel.Warning)
|
||||
// .Enrich.WithClientIp()
|
||||
|
||||
// .Enrich.FromLogContext()
|
||||
|
||||
// //控制台 方便调试 问题 我们显示记录日志 时 获取上下文的ip 和用户名 用户类型
|
||||
// .WriteTo.Console(restrictedToMinimumLevel: LogEventLevel.Warning,
|
||||
// outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {SourceContext:l} || {Message} || {Exception} ||end {NewLine}")
|
||||
|
||||
// .WriteTo.File($"{AppContext.BaseDirectory}Serilogs/.log", rollingInterval: RollingInterval.Day,
|
||||
// outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {SourceContext:l} || {Message} || {Exception} ||end {NewLine}")
|
||||
// .CreateLogger();
|
||||
|
||||
Log.Logger = new LoggerConfiguration()
|
||||
//.MinimumLevel.Information()
|
||||
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
|
||||
.WriteTo.Console()
|
||||
.WriteTo.File($"{AppContext.BaseDirectory}Serilogs/.log", rollingInterval: RollingInterval.Day)
|
||||
.CreateLogger();
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region 运行环境 部署平台
|
||||
|
||||
Log.Logger.Warning($"当前环境:{enviromentName}");
|
||||
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
Log.Logger.Warning($"当前部署平台环境:windows");
|
||||
}
|
||||
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
||||
{
|
||||
Log.Logger.Warning($"当前部署平台环境:linux");
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Logger.Warning($"当前部署平台环境:OSX or FreeBSD");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
var logger = app.Services.GetService<Microsoft.Extensions.Logging.ILogger<Program>>();
|
||||
|
||||
var server = DicomServerFactory.Create<CStoreSCPService>(_configuration.GetSection("DicomSCPServiceConfig").GetValue<int>("ServerPort"), userState: app.Services,logger: logger);
|
||||
|
||||
|
||||
app.Run();
|
||||
@@ -1,25 +0,0 @@
|
||||
{
|
||||
"$schema": "http://json.schemastore.org/launchsettings.json",
|
||||
"profiles": {
|
||||
"Test_IRC_SCP": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "swagger",
|
||||
"applicationUrl": "http://localhost:6200",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Test_IRC_SCP"
|
||||
}
|
||||
},
|
||||
"Uat_IRC_SCP": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "swagger",
|
||||
"applicationUrl": "http://localhost:6200",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Uat_IRC_SCP"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
using AutoMapper;
|
||||
using IRaCIS.Application.Services.BusinessFilter;
|
||||
using IRaCIS.Core.Infra.EFCore;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Panda.DynamicWebApi;
|
||||
using Panda.DynamicWebApi.Attributes;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using IRaCIS.Core.Infrastructure.Extention;
|
||||
using IRaCIS.Core.Domain.Models;
|
||||
|
||||
namespace IRaCIS.Core.SCP.Service
|
||||
{
|
||||
|
||||
#pragma warning disable CS8618
|
||||
|
||||
|
||||
#region 非泛型版本
|
||||
|
||||
[Authorize, DynamicWebApi, UnifiedApiResultFilter]
|
||||
public class BaseService : IBaseService, IDynamicWebApi
|
||||
{
|
||||
public IMapper _mapper { get; set; }
|
||||
|
||||
public IUserInfo _userInfo { get; set; }
|
||||
|
||||
|
||||
public IStringLocalizer _localizer { get; set; }
|
||||
|
||||
public IWebHostEnvironment _hostEnvironment { get; set; }
|
||||
|
||||
|
||||
|
||||
|
||||
public static IResponseOutput Null404NotFound<TEntity>(TEntity? businessObject) where TEntity : class
|
||||
{
|
||||
return new ResponseOutput<string>()
|
||||
.NotOk($"The query object {typeof(TEntity).Name} does not exist , or was deleted by someone else, or an incorrect parameter query caused", code: ApiResponseCodeEnum.DataNotExist);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public interface IBaseService
|
||||
{
|
||||
[MemberNotNull(nameof(_mapper))]
|
||||
public IMapper _mapper { get; set; }
|
||||
|
||||
[MemberNotNull(nameof(_userInfo))]
|
||||
public IUserInfo _userInfo { get; set; }
|
||||
|
||||
[MemberNotNull(nameof(_localizer))]
|
||||
public IStringLocalizer _localizer { get; set; }
|
||||
|
||||
[MemberNotNull(nameof(_hostEnvironment))]
|
||||
public IWebHostEnvironment _hostEnvironment { get; set; }
|
||||
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
#region 泛型版本测试
|
||||
|
||||
|
||||
public interface IBaseServiceTest<T> where T : Entity
|
||||
{
|
||||
[MemberNotNull(nameof(_mapper))]
|
||||
public IMapper _mapper { get; set; }
|
||||
|
||||
[MemberNotNull(nameof(_userInfo))]
|
||||
public IUserInfo _userInfo { get; set; }
|
||||
|
||||
|
||||
|
||||
[MemberNotNull(nameof(_localizer))]
|
||||
public IStringLocalizer _localizer { get; set; }
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
[Authorize, DynamicWebApi, UnifiedApiResultFilter]
|
||||
public class BaseServiceTest<T> : IBaseServiceTest<T>, IDynamicWebApi where T : Entity
|
||||
{
|
||||
public IMapper _mapper { get; set; }
|
||||
|
||||
public IUserInfo _userInfo { get; set; }
|
||||
|
||||
public IStringLocalizer _localizer { get; set; }
|
||||
|
||||
public static IResponseOutput Null404NotFound<TEntity>(TEntity? businessObject) where TEntity : class
|
||||
{
|
||||
return new ResponseOutput<string>()
|
||||
.NotOk($"The query object {typeof(TEntity).Name} does not exist , or was deleted by someone else, or an incorrect parameter query caused", code: ApiResponseCodeEnum.DataNotExist);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -1,370 +0,0 @@
|
||||
using FellowOakDicom.Network;
|
||||
using FellowOakDicom;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using IRaCIS.Core.SCP.Service;
|
||||
using IRaCIS.Core.Domain.Models;
|
||||
using IRaCIS.Core.Infra.EFCore;
|
||||
using Medallion.Threading;
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using Serilog;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion.Internal;
|
||||
using Microsoft.Extensions.Options;
|
||||
using System.Data;
|
||||
using FellowOakDicom.Imaging;
|
||||
using SharpCompress.Common;
|
||||
using SixLabors.ImageSharp.Formats.Jpeg;
|
||||
using IRaCIS.Core.Infrastructure;
|
||||
|
||||
namespace IRaCIS.Core.SCP.Service
|
||||
{
|
||||
|
||||
public class DicomSCPServiceOption
|
||||
{
|
||||
public List<string> CalledAEList { get; set; }
|
||||
|
||||
public string ServerPort { get; set; }
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public class CStoreSCPService : DicomService, IDicomServiceProvider, IDicomCStoreProvider, IDicomCEchoProvider
|
||||
{
|
||||
private IServiceProvider _serviceProvider { get; set; }
|
||||
|
||||
private List<Guid> _SCPStudyIdList { get; set; } = new List<Guid>();
|
||||
|
||||
private SCPImageUpload _upload { get; set; }
|
||||
|
||||
private Guid _trialId { get; set; }
|
||||
|
||||
private Guid _trialSiteId { get; set; }
|
||||
|
||||
|
||||
|
||||
private static readonly DicomTransferSyntax[] _acceptedTransferSyntaxes = new DicomTransferSyntax[]
|
||||
{
|
||||
DicomTransferSyntax.ExplicitVRLittleEndian,
|
||||
DicomTransferSyntax.ExplicitVRBigEndian,
|
||||
DicomTransferSyntax.ImplicitVRLittleEndian
|
||||
};
|
||||
|
||||
private static readonly DicomTransferSyntax[] _acceptedImageTransferSyntaxes = new DicomTransferSyntax[]
|
||||
{
|
||||
// Lossless
|
||||
DicomTransferSyntax.JPEGLSLossless, //1.2.840.10008.1.2.4.80
|
||||
DicomTransferSyntax.JPEG2000Lossless, //1.2.840.10008.1.2.4.90
|
||||
DicomTransferSyntax.JPEGProcess14SV1, //1.2.840.10008.1.2.4.70
|
||||
DicomTransferSyntax.JPEGProcess14, //1.2.840.10008.1.2.4.57 JPEG Lossless, Non-Hierarchical (Process 14)
|
||||
DicomTransferSyntax.RLELossless, //1.2.840.10008.1.2.5
|
||||
// Lossy
|
||||
DicomTransferSyntax.JPEGLSNearLossless,//1.2.840.10008.1.2.4.81"
|
||||
DicomTransferSyntax.JPEG2000Lossy, //1.2.840.10008.1.2.4.91
|
||||
DicomTransferSyntax.JPEGProcess1, //1.2.840.10008.1.2.4.50
|
||||
DicomTransferSyntax.JPEGProcess2_4, //1.2.840.10008.1.2.4.51
|
||||
// Uncompressed
|
||||
DicomTransferSyntax.ExplicitVRLittleEndian, //1.2.840.10008.1.2.1
|
||||
DicomTransferSyntax.ExplicitVRBigEndian, //1.2.840.10008.1.2.2
|
||||
DicomTransferSyntax.ImplicitVRLittleEndian //1.2.840.10008.1.2
|
||||
};
|
||||
|
||||
|
||||
public CStoreSCPService(INetworkStream stream, Encoding fallbackEncoding, Microsoft.Extensions.Logging.ILogger log, DicomServiceDependencies dependencies)
|
||||
: base(stream, fallbackEncoding, log, dependencies)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public Task OnReceiveAssociationRequestAsync(DicomAssociation association)
|
||||
{
|
||||
|
||||
_upload = new SCPImageUpload() { StartTime = DateTime.Now, CallingAE = association.CallingAE, CalledAE = association.CalledAE, CallingAEIP = association.RemoteHost };
|
||||
|
||||
|
||||
Log.Logger.Warning($"接收到来自{association.CallingAE}的连接");
|
||||
|
||||
_serviceProvider = (IServiceProvider)this.UserState;
|
||||
|
||||
|
||||
|
||||
var _trialDicomAERepository = _serviceProvider.GetService<IRepository<TrialDicomAE>>();
|
||||
|
||||
|
||||
var trialDicomAEList = _trialDicomAERepository.Select(t => new { t.CalledAE, t.TrialId }).ToList();
|
||||
var trialCalledAEList = trialDicomAEList.Select(t => t.CalledAE).ToList();
|
||||
|
||||
Log.Logger.Information("当前系统配置:", string.Join('|', trialDicomAEList));
|
||||
|
||||
var findCalledAE = trialDicomAEList.Where(t => t.CalledAE == association.CalledAE).FirstOrDefault();
|
||||
|
||||
var isCanReceiveIamge = false;
|
||||
|
||||
if (findCalledAE != null)
|
||||
{
|
||||
_trialId = findCalledAE.TrialId;
|
||||
|
||||
var _trialSiteDicomAERepository = _serviceProvider.GetService<IRepository<TrialSiteDicomAE>>();
|
||||
|
||||
|
||||
var findTrialSiteAE = _trialSiteDicomAERepository.Where(t => t.CallingAE == association.CallingAE && t.TrialId==_trialId).FirstOrDefault();
|
||||
|
||||
if (findTrialSiteAE != null)
|
||||
{
|
||||
_trialSiteId = findTrialSiteAE.TrialSiteId;
|
||||
|
||||
isCanReceiveIamge = true;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
if (association.CallingAE == "test-callingAE")
|
||||
{
|
||||
isCanReceiveIamge = true;
|
||||
}
|
||||
|
||||
if (!trialCalledAEList.Contains(association.CalledAE) || isCanReceiveIamge == false)
|
||||
{
|
||||
|
||||
Log.Logger.Warning($"拒绝CallingAE:{association.CallingAE} CalledAE:{association.CalledAE}的连接");
|
||||
|
||||
return SendAssociationRejectAsync(
|
||||
DicomRejectResult.Permanent,
|
||||
DicomRejectSource.ServiceUser,
|
||||
DicomRejectReason.CalledAENotRecognized);
|
||||
}
|
||||
|
||||
foreach (var pc in association.PresentationContexts)
|
||||
{
|
||||
if (pc.AbstractSyntax == DicomUID.Verification)
|
||||
{
|
||||
pc.AcceptTransferSyntaxes(_acceptedTransferSyntaxes);
|
||||
}
|
||||
else if (pc.AbstractSyntax.StorageCategory != DicomStorageCategory.None)
|
||||
{
|
||||
pc.AcceptTransferSyntaxes(_acceptedImageTransferSyntaxes);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
return SendAssociationAcceptAsync(association);
|
||||
}
|
||||
|
||||
|
||||
public async Task OnReceiveAssociationReleaseRequestAsync()
|
||||
{
|
||||
await DataMaintenanceAsaync();
|
||||
|
||||
//记录监控
|
||||
|
||||
var _SCPImageUploadRepository = _serviceProvider.GetService<IRepository<SCPImageUpload>>();
|
||||
|
||||
_upload.EndTime = DateTime.Now;
|
||||
_upload.StudyCount = _SCPStudyIdList.Count;
|
||||
_upload.TrialId = _trialId;
|
||||
_upload.TrialSiteId = _trialSiteId;
|
||||
|
||||
await _SCPImageUploadRepository.AddAsync(_upload, true);
|
||||
|
||||
await SendAssociationReleaseResponseAsync();
|
||||
}
|
||||
|
||||
|
||||
private async Task DataMaintenanceAsaync()
|
||||
{
|
||||
Log.Logger.Warning($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE}传输结束:开始维护数据,处理检查Modality");
|
||||
|
||||
|
||||
|
||||
//处理检查Modality
|
||||
var _dictionaryRepository = _serviceProvider.GetService<IRepository<Dictionary>>();
|
||||
var _seriesRepository = _serviceProvider.GetService<IRepository<SCPSeries>>();
|
||||
var _studyRepository = _serviceProvider.GetService<IRepository<SCPStudy>>();
|
||||
|
||||
var dicModalityList = _dictionaryRepository.Where(t => t.Code == "Modality").SelectMany(t => t.ChildList.Select(c => c.Value)).ToList();
|
||||
var seriesModalityList = _seriesRepository.Where(t => _SCPStudyIdList.Contains(t.StudyId)).Select(t => new { SCPStudyId = t.StudyId, t.Modality }).ToList();
|
||||
|
||||
foreach (var g in seriesModalityList.GroupBy(t => t.SCPStudyId))
|
||||
{
|
||||
var modality = string.Join('、', g.Select(t => t.Modality).Distinct().ToList());
|
||||
|
||||
//特殊逻辑
|
||||
var modalityForEdit = dicModalityList.Contains(modality) ? modality : String.Empty;
|
||||
|
||||
if (modality == "MR")
|
||||
{
|
||||
modalityForEdit = "MRI";
|
||||
}
|
||||
|
||||
if (modality == "PT")
|
||||
{
|
||||
modalityForEdit = "PET";
|
||||
}
|
||||
if (modality == "PT、CT" || modality == "CT、PT")
|
||||
{
|
||||
modalityForEdit = "PET-CT";
|
||||
}
|
||||
|
||||
await _studyRepository.BatchUpdateNoTrackingAsync(t => t.Id == g.Key, u => new SCPStudy() { Modalities = modality, ModalityForEdit = modalityForEdit });
|
||||
|
||||
}
|
||||
|
||||
Log.Logger.Warning($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE}维护数据结束");
|
||||
}
|
||||
|
||||
public void OnReceiveAbort(DicomAbortSource source, DicomAbortReason reason)
|
||||
{
|
||||
Log.Logger.Warning($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE}接收中断,中断原因:{source.ToString() + reason.ToString()}");
|
||||
/* nothing to do here */
|
||||
}
|
||||
|
||||
|
||||
public async void OnConnectionClosed(Exception exception)
|
||||
{
|
||||
/* nothing to do here */
|
||||
|
||||
//奇怪的bug 上传的时候,用王捷修改的影像,会关闭,重新连接,导致检查id 丢失,然后状态不一致
|
||||
if (exception == null)
|
||||
{
|
||||
var _studyRepository = _serviceProvider.GetService<IRepository<SCPStudy>>();
|
||||
//将检查设置为传输结束
|
||||
await _studyRepository.BatchUpdateNoTrackingAsync(t => _SCPStudyIdList.Contains(t.Id), u => new SCPStudy() { IsUploadFinished = true });
|
||||
|
||||
await _studyRepository.SaveChangesAndClearAllTrackingAsync();
|
||||
}
|
||||
|
||||
Log.Logger.Warning($"连接关闭 {exception?.Message} {exception?.InnerException?.Message}");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public async Task<DicomCStoreResponse> OnCStoreRequestAsync(DicomCStoreRequest request)
|
||||
{
|
||||
|
||||
string studyInstanceUid = request.Dataset.GetString(DicomTag.StudyInstanceUID);
|
||||
string seriesInstanceUid = request.Dataset.GetString(DicomTag.SeriesInstanceUID);
|
||||
string sopInstanceUid = request.Dataset.GetString(DicomTag.SOPInstanceUID);
|
||||
|
||||
//Guid studyId = IdentifierHelper.CreateGuid(studyInstanceUid, trialId.ToString());
|
||||
Guid seriesId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid, _trialId.ToString());
|
||||
Guid instanceId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid, sopInstanceUid, _trialId.ToString());
|
||||
|
||||
|
||||
var ossService = _serviceProvider.GetService<IOSSService>();
|
||||
var dicomArchiveService = _serviceProvider.GetService<IDicomArchiveService>();
|
||||
var _seriesRepository = _serviceProvider.GetService<IRepository<SCPSeries>>();
|
||||
|
||||
var _distributedLockProvider = _serviceProvider.GetService<IDistributedLockProvider>();
|
||||
|
||||
var storeRelativePath = string.Empty;
|
||||
var ossFolderPath = $"{_trialId}/Image/PACS/{_trialSiteId}{studyInstanceUid}";
|
||||
|
||||
|
||||
long fileSize = 0;
|
||||
try
|
||||
{
|
||||
|
||||
using (MemoryStream ms = new MemoryStream())
|
||||
{
|
||||
await request.File.SaveAsync(ms);
|
||||
|
||||
//irc 从路径最后一截取Guid
|
||||
storeRelativePath = await ossService.UploadToOSSAsync(ms, ossFolderPath, instanceId.ToString(), false);
|
||||
|
||||
fileSize = ms.Length;
|
||||
}
|
||||
|
||||
Log.Logger.Information($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE} {request.SOPInstanceUID} 上传完成 ");
|
||||
|
||||
}
|
||||
catch (Exception ec)
|
||||
{
|
||||
Log.Logger.Warning($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE} 上传异常 {ec.Message}");
|
||||
}
|
||||
|
||||
|
||||
|
||||
var @lock = _distributedLockProvider.CreateLock($"{studyInstanceUid}");
|
||||
|
||||
using (await @lock.AcquireAsync())
|
||||
{
|
||||
try
|
||||
{
|
||||
var scpStudyId = await dicomArchiveService.ArchiveDicomFileAsync(request.Dataset, _trialId, _trialSiteId, storeRelativePath, Association.CallingAE, Association.CalledAE,fileSize);
|
||||
|
||||
if (!_SCPStudyIdList.Contains(scpStudyId))
|
||||
{
|
||||
_SCPStudyIdList.Add(scpStudyId);
|
||||
}
|
||||
|
||||
var series = await _seriesRepository.FindAsync(seriesId);
|
||||
|
||||
//没有缩略图
|
||||
if (series != null && string.IsNullOrEmpty(series.ImageResizePath))
|
||||
{
|
||||
|
||||
// 生成缩略图
|
||||
using (var memoryStream = new MemoryStream())
|
||||
{
|
||||
DicomImage image = new DicomImage(request.Dataset);
|
||||
|
||||
var sharpimage = image.RenderImage().AsSharpImage();
|
||||
sharpimage.Save(memoryStream, new JpegEncoder());
|
||||
|
||||
// 上传缩略图到 OSS
|
||||
|
||||
var seriesPath = await ossService.UploadToOSSAsync(memoryStream, ossFolderPath, seriesId.ToString() + ".preview.jpg", false);
|
||||
|
||||
Console.WriteLine(seriesPath + " Id: " + seriesId);
|
||||
|
||||
series.ImageResizePath = seriesPath;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
await _seriesRepository.SaveChangesAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
Log.Logger.Warning($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE} 传输处理异常:{ex.ToString()}");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
//监控信息设置
|
||||
_upload.FileCount++;
|
||||
_upload.FileSize = _upload.FileSize + fileSize;
|
||||
return new DicomCStoreResponse(request, DicomStatus.Success);
|
||||
}
|
||||
|
||||
|
||||
public Task OnCStoreRequestExceptionAsync(string tempFileName, Exception e)
|
||||
{
|
||||
// let library handle logging and error response
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
|
||||
public Task<DicomCEchoResponse> OnCEchoRequestAsync(DicomCEchoRequest request)
|
||||
{
|
||||
return Task.FromResult(new DicomCEchoResponse(request, DicomStatus.Success));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,356 +0,0 @@
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using System.Text;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using IRaCIS.Core.Infrastructure;
|
||||
using Medallion.Threading;
|
||||
using FellowOakDicom;
|
||||
using FellowOakDicom.Imaging.Codec;
|
||||
using System.Data;
|
||||
using IRaCIS.Core.Domain.Models;
|
||||
using FellowOakDicom.Network;
|
||||
using IRaCIS.Core.SCP.Service;
|
||||
using IRaCIS.Core.Infra.EFCore;
|
||||
using MassTransit;
|
||||
using System.Runtime.Intrinsics.X86;
|
||||
using Serilog.Sinks.File;
|
||||
|
||||
namespace IRaCIS.Core.SCP.Service
|
||||
{
|
||||
public class DicomArchiveService : BaseService, IDicomArchiveService
|
||||
{
|
||||
private readonly IRepository<SCPPatient> _patientRepository;
|
||||
private readonly IRepository<SCPStudy> _studyRepository;
|
||||
private readonly IRepository<SCPSeries> _seriesRepository;
|
||||
private readonly IRepository<SCPInstance> _instanceRepository;
|
||||
private readonly IRepository<Dictionary> _dictionaryRepository;
|
||||
private readonly IDistributedLockProvider _distributedLockProvider;
|
||||
|
||||
|
||||
private List<Guid> _instanceIdList = new List<Guid>();
|
||||
|
||||
public DicomArchiveService(IRepository<SCPPatient> patientRepository, IRepository<SCPStudy> studyRepository,
|
||||
IRepository<SCPSeries> seriesRepository,
|
||||
IRepository<SCPInstance> instanceRepository,
|
||||
IRepository<Dictionary> dictionaryRepository,
|
||||
IDistributedLockProvider distributedLockProvider)
|
||||
{
|
||||
_distributedLockProvider = distributedLockProvider;
|
||||
_studyRepository = studyRepository;
|
||||
_patientRepository = patientRepository;
|
||||
_seriesRepository = seriesRepository;
|
||||
_instanceRepository = instanceRepository;
|
||||
_dictionaryRepository = dictionaryRepository;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 单个文件接收 归档
|
||||
/// </summary>
|
||||
/// <param name="dataset"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="NotImplementedException"></exception>
|
||||
public async Task<Guid> ArchiveDicomFileAsync(DicomDataset dataset, Guid trialId, Guid trialSiteId, string fileRelativePath, string callingAE, string calledAE,long fileSize)
|
||||
{
|
||||
string studyInstanceUid = dataset.GetString(DicomTag.StudyInstanceUID);
|
||||
string seriesInstanceUid = dataset.GetString(DicomTag.SeriesInstanceUID);
|
||||
string sopInstanceUid = dataset.GetString(DicomTag.SOPInstanceUID);
|
||||
|
||||
string patientIdStr = dataset.GetSingleValueOrDefault(DicomTag.PatientID,string.Empty);
|
||||
|
||||
//Guid patientId= IdentifierHelper.CreateGuid(patientIdStr);
|
||||
Guid studyId = IdentifierHelper.CreateGuid(studyInstanceUid,trialId.ToString());
|
||||
Guid seriesId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid, trialId.ToString());
|
||||
Guid instanceId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid, sopInstanceUid, trialId.ToString());
|
||||
|
||||
var isStudyNeedAdd = false;
|
||||
var isSeriesNeedAdd = false;
|
||||
var isInstanceNeedAdd = false;
|
||||
var isPatientNeedAdd = false;
|
||||
|
||||
//var @lock = _distributedLockProvider.CreateLock($"{studyInstanceUid}");
|
||||
|
||||
//using (@lock.Acquire())
|
||||
{
|
||||
var findPatient = await _patientRepository.FirstOrDefaultAsync(t => t.PatientIdStr == patientIdStr && t.TrialSiteId==trialSiteId );
|
||||
var findStudy = await _studyRepository.FindAsync(studyId);
|
||||
var findSerice = await _seriesRepository.FindAsync(seriesId);
|
||||
var findInstance = await _instanceRepository.FindAsync(instanceId);
|
||||
|
||||
DateTime? studyTime = dataset.GetSingleValueOrDefault(DicomTag.StudyDate, string.Empty) == string.Empty ? null : dataset.GetSingleValue<DateTime>(DicomTag.StudyDate).Add(dataset.GetSingleValueOrDefault(DicomTag.StudyTime, string.Empty) == string.Empty ? TimeSpan.Zero : dataset.GetSingleValue<DateTime>(DicomTag.StudyTime).TimeOfDay);
|
||||
|
||||
//先传输了修改了患者编号的,又传输了没有修改患者编号的,导致后传输的没有修改患者编号的下面的检查为0
|
||||
if (findPatient == null && findStudy==null)
|
||||
{
|
||||
isPatientNeedAdd = true;
|
||||
|
||||
|
||||
findPatient = new SCPPatient()
|
||||
{
|
||||
Id = NewId.NextSequentialGuid(),
|
||||
TrialId=trialId,
|
||||
TrialSiteId=trialSiteId,
|
||||
PatientIdStr = dataset.GetSingleValueOrDefault(DicomTag.PatientID, string.Empty),
|
||||
PatientName = dataset.GetSingleValueOrDefault(DicomTag.PatientName, string.Empty),
|
||||
PatientAge = dataset.GetSingleValueOrDefault(DicomTag.PatientAge, string.Empty),
|
||||
PatientSex = dataset.GetSingleValueOrDefault(DicomTag.PatientSex, string.Empty),
|
||||
PatientBirthDate = dataset.GetSingleValueOrDefault(DicomTag.PatientBirthDate, string.Empty),
|
||||
|
||||
EarliestStudyTime = studyTime,
|
||||
LatestStudyTime = studyTime,
|
||||
LatestPushTime = DateTime.Now,
|
||||
};
|
||||
|
||||
if (findPatient.PatientBirthDate.Length == 8)
|
||||
{
|
||||
var birthDateStr = $"{findPatient.PatientBirthDate[0]}{findPatient.PatientBirthDate[1]}{findPatient.PatientBirthDate[2]}{findPatient.PatientBirthDate[3]}-{findPatient.PatientBirthDate[4]}{findPatient.PatientBirthDate[5]}-{findPatient.PatientBirthDate[6]}{findPatient.PatientBirthDate[7]}";
|
||||
|
||||
var yearStr = $"{findPatient.PatientBirthDate[0]}{findPatient.PatientBirthDate[1]}{findPatient.PatientBirthDate[2]}{findPatient.PatientBirthDate[3]}";
|
||||
|
||||
int year = 0;
|
||||
|
||||
var canParse = int.TryParse(yearStr, out year);
|
||||
|
||||
if (canParse && year > 1900)
|
||||
{
|
||||
findPatient.PatientBirthDate = birthDateStr;
|
||||
|
||||
DateTime birthDate;
|
||||
|
||||
if (findPatient.PatientAge == string.Empty && studyTime.HasValue && DateTime.TryParse(findPatient.PatientBirthDate,out birthDate))
|
||||
{
|
||||
var patientAge = studyTime.Value.Year - birthDate.Year;
|
||||
// 如果生日还未到,年龄减去一岁
|
||||
if (studyTime.Value < birthDate.AddYears(patientAge))
|
||||
{
|
||||
patientAge--;
|
||||
}
|
||||
|
||||
findPatient.PatientAge = patientAge.ToString();
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
findPatient.PatientBirthDate = string.Empty;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (studyTime < findPatient.EarliestStudyTime)
|
||||
{
|
||||
findPatient.EarliestStudyTime = studyTime;
|
||||
}
|
||||
if (studyTime > findPatient.LatestStudyTime)
|
||||
{
|
||||
findPatient.LatestStudyTime = studyTime;
|
||||
}
|
||||
|
||||
findPatient.LatestPushTime = DateTime.Now;
|
||||
}
|
||||
|
||||
if (findStudy == null)
|
||||
{
|
||||
isStudyNeedAdd = true;
|
||||
findStudy = new SCPStudy
|
||||
{
|
||||
CalledAE = calledAE,
|
||||
CallingAE = callingAE,
|
||||
|
||||
PatientId = findPatient.Id,
|
||||
Id = studyId,
|
||||
TrialId = trialId,
|
||||
TrialSiteId = trialSiteId,
|
||||
StudyInstanceUid = studyInstanceUid,
|
||||
StudyTime = studyTime,
|
||||
Modalities = dataset.GetSingleValueOrDefault(DicomTag.Modality, string.Empty),
|
||||
//ModalityForEdit = modalityForEdit,
|
||||
Description = dataset.GetSingleValueOrDefault(DicomTag.StudyDescription, string.Empty),
|
||||
InstitutionName = dataset.GetSingleValueOrDefault(DicomTag.InstitutionName, string.Empty),
|
||||
PatientIdStr = dataset.GetSingleValueOrDefault(DicomTag.PatientID, string.Empty),
|
||||
PatientName = dataset.GetSingleValueOrDefault(DicomTag.PatientName, string.Empty),
|
||||
PatientAge = dataset.GetSingleValueOrDefault(DicomTag.PatientAge, string.Empty),
|
||||
PatientSex = dataset.GetSingleValueOrDefault(DicomTag.PatientSex, string.Empty),
|
||||
BodyPartExamined = dataset.GetSingleValueOrDefault(DicomTag.BodyPartExamined, string.Empty),
|
||||
|
||||
StudyId = dataset.GetSingleValueOrDefault(DicomTag.StudyID, string.Empty),
|
||||
AccessionNumber = dataset.GetSingleValueOrDefault(DicomTag.AccessionNumber, string.Empty),
|
||||
|
||||
//需要特殊处理
|
||||
PatientBirthDate = dataset.GetSingleValueOrDefault(DicomTag.PatientBirthDate, string.Empty),
|
||||
|
||||
|
||||
AcquisitionTime = dataset.GetSingleValueOrDefault(DicomTag.AcquisitionTime, string.Empty),
|
||||
AcquisitionNumber = dataset.GetSingleValueOrDefault(DicomTag.AcquisitionNumber, string.Empty),
|
||||
TriggerTime = dataset.GetSingleValueOrDefault(DicomTag.TriggerTime, string.Empty),
|
||||
|
||||
|
||||
|
||||
//IsDoubleReview = addtionalInfo.IsDoubleReview,
|
||||
SeriesCount = 0,
|
||||
InstanceCount = 0
|
||||
};
|
||||
|
||||
|
||||
if (findStudy.PatientBirthDate.Length == 8)
|
||||
{
|
||||
findStudy.PatientBirthDate = $"{findStudy.PatientBirthDate[0]}{findStudy.PatientBirthDate[1]}{findStudy.PatientBirthDate[2]}{findStudy.PatientBirthDate[3]}-{findStudy.PatientBirthDate[4]}{findStudy.PatientBirthDate[5]}-{findStudy.PatientBirthDate[6]}{findStudy.PatientBirthDate[7]}";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (findSerice == null)
|
||||
{
|
||||
isSeriesNeedAdd = true;
|
||||
|
||||
findSerice = new SCPSeries
|
||||
{
|
||||
Id = seriesId,
|
||||
StudyId = findStudy.Id,
|
||||
|
||||
StudyInstanceUid = findStudy.StudyInstanceUid,
|
||||
SeriesInstanceUid = seriesInstanceUid,
|
||||
SeriesNumber = dataset.GetSingleValueOrDefault(DicomTag.SeriesNumber, 1),
|
||||
//SeriesTime = dataset.GetSingleValueOrDefault(DicomTag.SeriesDate, DateTime.Now).Add(dataset.GetSingleValueOrDefault(DicomTag.SeriesTime, DateTime.Now).TimeOfDay),
|
||||
//SeriesTime = DateTime.TryParse(dataset.GetSingleValue<string>(DicomTag.SeriesDate) + dataset.GetSingleValue<string>(DicomTag.SeriesTime), out DateTime dt) ? dt : null,
|
||||
SeriesTime = dataset.GetSingleValueOrDefault(DicomTag.SeriesDate, string.Empty) == string.Empty ? null : dataset.GetSingleValue<DateTime>(DicomTag.SeriesDate).Add(dataset.GetSingleValueOrDefault(DicomTag.SeriesTime, string.Empty) == string.Empty ? TimeSpan.Zero : dataset.GetSingleValue<DateTime>(DicomTag.SeriesTime).TimeOfDay),
|
||||
Modality = dataset.GetSingleValueOrDefault(DicomTag.Modality, string.Empty),
|
||||
Description = dataset.GetSingleValueOrDefault(DicomTag.SeriesDescription, string.Empty),
|
||||
SliceThickness = dataset.GetSingleValueOrDefault(DicomTag.SliceThickness, string.Empty),
|
||||
|
||||
ImagePositionPatient = dataset.GetSingleValueOrDefault(DicomTag.ImagePositionPatient, string.Empty),
|
||||
ImageOrientationPatient = dataset.GetSingleValueOrDefault(DicomTag.ImageOrientationPatient, string.Empty),
|
||||
BodyPartExamined = dataset.GetSingleValueOrDefault(DicomTag.BodyPartExamined, string.Empty),
|
||||
SequenceName = dataset.GetSingleValueOrDefault(DicomTag.SequenceName, string.Empty),
|
||||
ProtocolName = dataset.GetSingleValueOrDefault(DicomTag.ProtocolName, string.Empty),
|
||||
ImagerPixelSpacing = dataset.GetSingleValueOrDefault(DicomTag.ImagerPixelSpacing, string.Empty),
|
||||
|
||||
AcquisitionTime = dataset.GetSingleValueOrDefault(DicomTag.AcquisitionTime, string.Empty),
|
||||
AcquisitionNumber = dataset.GetSingleValueOrDefault(DicomTag.AcquisitionNumber, string.Empty),
|
||||
TriggerTime = dataset.GetSingleValueOrDefault(DicomTag.TriggerTime, string.Empty),
|
||||
|
||||
|
||||
InstanceCount = 0
|
||||
};
|
||||
|
||||
++findStudy.SeriesCount;
|
||||
}
|
||||
|
||||
|
||||
if (findInstance == null)
|
||||
{
|
||||
isInstanceNeedAdd = true;
|
||||
findInstance = new SCPInstance
|
||||
{
|
||||
Id = instanceId,
|
||||
StudyId = findStudy.Id,
|
||||
SeriesId = findSerice.Id,
|
||||
StudyInstanceUid = findStudy.StudyInstanceUid,
|
||||
SeriesInstanceUid = findSerice.SeriesInstanceUid,
|
||||
|
||||
SopInstanceUid = sopInstanceUid,
|
||||
InstanceNumber = dataset.GetSingleValueOrDefault(DicomTag.InstanceNumber, 1),
|
||||
InstanceTime = dataset.GetSingleValueOrDefault(DicomTag.ContentDate, string.Empty) == string.Empty ? null : dataset.GetSingleValue<DateTime>(DicomTag.ContentDate).Add(dataset.GetSingleValueOrDefault(DicomTag.ContentTime, string.Empty) == string.Empty ? TimeSpan.Zero : dataset.GetSingleValue<DateTime>(DicomTag.ContentTime).TimeOfDay),
|
||||
//InstanceTime = DateTime.TryParse(dataset.GetSingleValue<string>(DicomTag.ContentDate) + dataset.GetSingleValue<string>(DicomTag.ContentTime), out DateTime dt) ? dt : null,
|
||||
//InstanceTime = dataset.GetSingleValueOrDefault(DicomTag.ContentDate,(DateTime?)null)?.Add(dataset.GetSingleValueOrDefault(DicomTag.ContentTime, TimeSpan.Zero)),
|
||||
//dataset.GetSingleValueOrDefault(DicomTag.ContentDate,DateTime.Now);//, DicomTag.ContentTime)
|
||||
CPIStatus = false,
|
||||
ImageRows = dataset.GetSingleValueOrDefault(DicomTag.Rows, 0),
|
||||
ImageColumns = dataset.GetSingleValueOrDefault(DicomTag.Columns, 0),
|
||||
SliceLocation = dataset.GetSingleValueOrDefault(DicomTag.SliceLocation, 0),
|
||||
|
||||
SliceThickness = dataset.GetSingleValueOrDefault(DicomTag.SliceThickness, string.Empty),
|
||||
NumberOfFrames = dataset.GetSingleValueOrDefault(DicomTag.NumberOfFrames, 0),
|
||||
PixelSpacing = dataset.GetSingleValueOrDefault(DicomTag.PixelSpacing, string.Empty),
|
||||
ImagerPixelSpacing = dataset.GetSingleValueOrDefault(DicomTag.ImagerPixelSpacing, string.Empty),
|
||||
FrameOfReferenceUID = dataset.GetSingleValueOrDefault(DicomTag.FrameOfReferenceUID, string.Empty),
|
||||
WindowCenter = dataset.GetSingleValueOrDefault(DicomTag.WindowCenter, string.Empty),
|
||||
WindowWidth = dataset.GetSingleValueOrDefault(DicomTag.WindowWidth, string.Empty),
|
||||
|
||||
Path = fileRelativePath,
|
||||
|
||||
FileSize= fileSize,
|
||||
|
||||
};
|
||||
|
||||
++findStudy.InstanceCount;
|
||||
++findSerice.InstanceCount;
|
||||
}
|
||||
|
||||
if (isPatientNeedAdd)
|
||||
{
|
||||
var ss = await _patientRepository.AddAsync(findPatient);
|
||||
}
|
||||
if (isStudyNeedAdd)
|
||||
{
|
||||
var dd = await _studyRepository.AddAsync(findStudy);
|
||||
}
|
||||
else
|
||||
{
|
||||
await _studyRepository.BatchUpdateNoTrackingAsync(t => t.Id == findStudy.Id, t => new SCPStudy() { IsUploadFinished = false });
|
||||
}
|
||||
|
||||
if (isSeriesNeedAdd)
|
||||
{
|
||||
await _seriesRepository.AddAsync(findSerice);
|
||||
}
|
||||
if (isInstanceNeedAdd)
|
||||
{
|
||||
await _instanceRepository.AddAsync(findInstance);
|
||||
}
|
||||
else
|
||||
{
|
||||
await _instanceRepository.BatchUpdateNoTrackingAsync(t => t.Id == instanceId, u => new SCPInstance() { Path = fileRelativePath,FileSize=fileSize });
|
||||
}
|
||||
|
||||
//await _studyRepository.SaveChangesAsync();
|
||||
|
||||
return findStudy.Id;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// 从DICOM文件中获取使用的字符集
|
||||
private string GetEncodingVaulueFromDicomFile(DicomDataset dataset, DicomTag dicomTag)
|
||||
{
|
||||
|
||||
// 获取DICOM文件的特定元素,通常用于指示使用的字符集
|
||||
var charset = dataset.GetSingleValueOrDefault(DicomTag.SpecificCharacterSet, string.Empty);
|
||||
|
||||
var dicomEncoding = DicomEncoding.GetEncoding(charset);
|
||||
|
||||
|
||||
var dicomStringElement = dataset.GetDicomItem<DicomStringElement>(dicomTag);
|
||||
|
||||
var bytes = dicomStringElement.Buffer.Data;
|
||||
|
||||
|
||||
return dicomEncoding.GetString(bytes);
|
||||
|
||||
|
||||
//// 从DICOM文件中获取使用的字符集
|
||||
//string filePath = "C:\\Users\\hang\\Documents\\WeChat Files\\wxid_r2imdzb7j3q922\\FileStorage\\File\\2024-05\\1.2.840.113619.2.80.169103990.5390.1271401378.4.dcm";
|
||||
//DicomFile dicomFile = DicomFile.Open(filePath);
|
||||
|
||||
//// 获取DICOM文件的特定元素,通常用于指示使用的字符集
|
||||
//var charset = dicomFile.Dataset.GetSingleValueOrDefault(DicomTag.SpecificCharacterSet, string.Empty);
|
||||
|
||||
//var dicomEncoding = DicomEncoding.GetEncoding(charset);
|
||||
|
||||
//var value = dicomFile.Dataset.GetSingleValueOrDefault(DicomTag.PatientName, string.Empty);
|
||||
|
||||
//var dicomStringElement = dicomFile.Dataset.GetDicomItem<DicomStringElement>(DicomTag.PatientName);
|
||||
|
||||
//var bytes = dicomStringElement.Buffer.Data;
|
||||
|
||||
//var aa= dicomEncoding.GetString(bytes);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
using FellowOakDicom;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion.Internal;
|
||||
|
||||
namespace IRaCIS.Core.SCP.Service
|
||||
{
|
||||
public interface IDicomArchiveService
|
||||
{
|
||||
Task<Guid> ArchiveDicomFileAsync(DicomDataset dicomDataset,Guid trialId,Guid trialSiteId, string fileRelativePath,string callingAE,string calledAE,long fileSize);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,442 +0,0 @@
|
||||
using Aliyun.OSS;
|
||||
using IRaCIS.Core.Infrastructure;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Minio.DataModel.Args;
|
||||
using Minio;
|
||||
using SharpCompress.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.AccessControl;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace IRaCIS.Core.SCP
|
||||
{
|
||||
public class MinIOOptions : AWSOptions
|
||||
{
|
||||
public int port { get; set; }
|
||||
|
||||
}
|
||||
|
||||
public class AWSOptions
|
||||
{
|
||||
public string endPoint { get; set; }
|
||||
public bool useSSL { get; set; }
|
||||
public string accessKey { get; set; }
|
||||
public string secretKey { get; set; }
|
||||
public string bucketName { get; set; }
|
||||
public string viewEndpoint { get; set; }
|
||||
}
|
||||
|
||||
public class AliyunOSSOptions
|
||||
{
|
||||
public string regionId { get; set; }
|
||||
public string accessKeyId { get; set; }
|
||||
public string accessKeySecret { get; set; }
|
||||
|
||||
public string internalEndpoint { get; set; }
|
||||
|
||||
public string endPoint { get; set; }
|
||||
public string bucketName { get; set; }
|
||||
|
||||
public string roleArn { get; set; }
|
||||
|
||||
public string region { get; set; }
|
||||
|
||||
public string viewEndpoint { get; set; }
|
||||
|
||||
}
|
||||
|
||||
public class ObjectStoreServiceOptions
|
||||
{
|
||||
public string ObjectStoreUse { get; set; }
|
||||
public AliyunOSSOptions AliyunOSS { get; set; }
|
||||
public MinIOOptions MinIO { get; set; }
|
||||
|
||||
public AWSOptions AWS { get; set; }
|
||||
|
||||
}
|
||||
|
||||
public class ObjectStoreDTO
|
||||
{
|
||||
public string ObjectStoreUse { get; set; }
|
||||
|
||||
public AliyunOSSOptions AliyunOSS { get; set; }
|
||||
|
||||
public MinIOOptions MinIO { get; set; }
|
||||
|
||||
public AWSOptions AWS { get; set; }
|
||||
|
||||
}
|
||||
|
||||
public class AliyunOSSTempToken
|
||||
{
|
||||
public string AccessKeyId { get; set; }
|
||||
public string AccessKeySecret { get; set; }
|
||||
public string SecurityToken { get; set; }
|
||||
public string Expiration { get; set; }
|
||||
|
||||
public string Region { get; set; }
|
||||
public string BucketName { get; set; }
|
||||
public string ViewEndpoint { get; set; }
|
||||
}
|
||||
|
||||
|
||||
|
||||
public enum ObjectStoreUse
|
||||
{
|
||||
AliyunOSS = 0,
|
||||
MinIO = 1,
|
||||
AWS = 2,
|
||||
}
|
||||
|
||||
public interface IOSSService
|
||||
{
|
||||
public Task<string> UploadToOSSAsync(Stream fileStream, string oosFolderPath, string fileRealName, bool isFileNameAddGuid = true);
|
||||
public Task<string> UploadToOSSAsync(string localFilePath, string oosFolderPath, bool isFileNameAddGuid = true);
|
||||
|
||||
public Task DownLoadFromOSSAsync(string ossRelativePath, string localFilePath);
|
||||
|
||||
public ObjectStoreServiceOptions ObjectStoreServiceOptions { get; set; }
|
||||
|
||||
public Task<string> GetSignedUrl(string ossRelativePath);
|
||||
|
||||
}
|
||||
|
||||
|
||||
public class OSSService : IOSSService
|
||||
{
|
||||
public ObjectStoreServiceOptions ObjectStoreServiceOptions { get; set; }
|
||||
|
||||
|
||||
public OSSService(IOptionsMonitor<ObjectStoreServiceOptions> options)
|
||||
{
|
||||
ObjectStoreServiceOptions = options.CurrentValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// oosFolderPath 不要 "/ "开头 应该: TempFolder/ChildFolder
|
||||
/// </summary>
|
||||
/// <param name="fileStream"></param>
|
||||
/// <param name="oosFolderPath"></param>
|
||||
/// <param name="fileRealName"></param>
|
||||
/// <param name="isFileNameAddGuid"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<string> UploadToOSSAsync(Stream fileStream, string oosFolderPath, string fileRealName, bool isFileNameAddGuid = true)
|
||||
{
|
||||
var ossRelativePath = isFileNameAddGuid ? $"{oosFolderPath}/{Guid.NewGuid()}_{fileRealName}" : $"{oosFolderPath}/{fileRealName}";
|
||||
//var ossRelativePath = $"{oosFolderPath}/{Guid.NewGuid()}_{fileRealName}";
|
||||
//var ossRelativePath = oosFolderPath + "/" + fileRealName;
|
||||
|
||||
try
|
||||
{
|
||||
using (var memoryStream = new MemoryStream())
|
||||
{
|
||||
fileStream.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
fileStream.CopyTo(memoryStream);
|
||||
|
||||
memoryStream.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
|
||||
if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS")
|
||||
{
|
||||
var aliConfig = ObjectStoreServiceOptions.AliyunOSS;
|
||||
|
||||
var _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.endPoint : aliConfig.internalEndpoint, aliConfig.accessKeyId, aliConfig.accessKeySecret);
|
||||
|
||||
|
||||
|
||||
// 上传文件
|
||||
var result = _ossClient.PutObject(aliConfig.bucketName, ossRelativePath, memoryStream);
|
||||
|
||||
}
|
||||
else if (ObjectStoreServiceOptions.ObjectStoreUse == "MinIO")
|
||||
{
|
||||
var minIOConfig = ObjectStoreServiceOptions.MinIO;
|
||||
|
||||
|
||||
var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.endPoint}:{minIOConfig.port}")
|
||||
.WithCredentials(minIOConfig.accessKey, minIOConfig.secretKey).WithSSL(minIOConfig.useSSL)
|
||||
.Build();
|
||||
|
||||
var putObjectArgs = new PutObjectArgs()
|
||||
.WithBucket(minIOConfig.bucketName)
|
||||
.WithObject(ossRelativePath)
|
||||
.WithStreamData(memoryStream)
|
||||
.WithObjectSize(memoryStream.Length);
|
||||
|
||||
await minioClient.PutObjectAsync(putObjectArgs);
|
||||
}
|
||||
else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS")
|
||||
{
|
||||
var minIOConfig = ObjectStoreServiceOptions.AWS;
|
||||
|
||||
|
||||
var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.endPoint}")
|
||||
.WithCredentials(minIOConfig.accessKey, minIOConfig.secretKey).WithSSL(minIOConfig.useSSL)
|
||||
.Build();
|
||||
|
||||
var putObjectArgs = new PutObjectArgs()
|
||||
.WithBucket(minIOConfig.bucketName)
|
||||
.WithObject(ossRelativePath)
|
||||
.WithStreamData(memoryStream)
|
||||
.WithObjectSize(memoryStream.Length);
|
||||
|
||||
await minioClient.PutObjectAsync(putObjectArgs);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new BusinessValidationFailedException("未定义的存储介质类型");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
throw new BusinessValidationFailedException($"上传发生异常:{ex.Message}");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
return "/" + ossRelativePath;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// oosFolderPath 不要 "/ "开头 应该: TempFolder/ChildFolder
|
||||
/// </summary>
|
||||
/// <param name="localFilePath"></param>
|
||||
/// <param name="oosFolderPath"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="BusinessValidationFailedException"></exception>
|
||||
public async Task<string> UploadToOSSAsync(string localFilePath, string oosFolderPath, bool isFileNameAddGuid = true)
|
||||
{
|
||||
var localFileName = Path.GetFileName(localFilePath);
|
||||
|
||||
var ossRelativePath = isFileNameAddGuid ? $"{oosFolderPath}/{Guid.NewGuid()}_{localFileName}" : $"{oosFolderPath}/{localFileName}";
|
||||
|
||||
|
||||
//var ossRelativePath = oosFolderPath + "/" + localFileName;
|
||||
|
||||
|
||||
if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS")
|
||||
{
|
||||
var aliConfig = ObjectStoreServiceOptions.AliyunOSS;
|
||||
|
||||
var _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.endPoint : aliConfig.internalEndpoint, aliConfig.accessKeyId, aliConfig.accessKeySecret);
|
||||
|
||||
// 上传文件
|
||||
var result = _ossClient.PutObject(aliConfig.bucketName, ossRelativePath, localFilePath);
|
||||
|
||||
}
|
||||
else if (ObjectStoreServiceOptions.ObjectStoreUse == "MinIO")
|
||||
{
|
||||
var minIOConfig = ObjectStoreServiceOptions.MinIO;
|
||||
|
||||
|
||||
var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.endPoint}:{minIOConfig.port}")
|
||||
.WithCredentials(minIOConfig.accessKey, minIOConfig.secretKey).WithSSL(minIOConfig.useSSL)
|
||||
.Build();
|
||||
|
||||
var putObjectArgs = new PutObjectArgs()
|
||||
.WithBucket(minIOConfig.bucketName)
|
||||
.WithObject(ossRelativePath)
|
||||
.WithFileName(localFilePath);
|
||||
|
||||
await minioClient.PutObjectAsync(putObjectArgs);
|
||||
}
|
||||
else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS")
|
||||
{
|
||||
var minIOConfig = ObjectStoreServiceOptions.AWS;
|
||||
|
||||
|
||||
var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.endPoint}")
|
||||
.WithCredentials(minIOConfig.accessKey, minIOConfig.secretKey).WithSSL(minIOConfig.useSSL)
|
||||
.Build();
|
||||
|
||||
var putObjectArgs = new PutObjectArgs()
|
||||
.WithBucket(minIOConfig.bucketName)
|
||||
.WithObject(ossRelativePath)
|
||||
.WithFileName(localFilePath);
|
||||
|
||||
await minioClient.PutObjectAsync(putObjectArgs);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new BusinessValidationFailedException("未定义的存储介质类型");
|
||||
}
|
||||
return "/" + ossRelativePath;
|
||||
|
||||
}
|
||||
|
||||
|
||||
public async Task DownLoadFromOSSAsync(string ossRelativePath, string localFilePath)
|
||||
{
|
||||
|
||||
ossRelativePath = ossRelativePath.TrimStart('/');
|
||||
try
|
||||
{
|
||||
|
||||
|
||||
if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS")
|
||||
{
|
||||
var aliConfig = ObjectStoreServiceOptions.AliyunOSS;
|
||||
|
||||
var _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.endPoint : aliConfig.internalEndpoint, aliConfig.accessKeyId, aliConfig.accessKeySecret);
|
||||
|
||||
// 上传文件
|
||||
var result = _ossClient.GetObject(aliConfig.bucketName, ossRelativePath);
|
||||
|
||||
// 将下载的文件流保存到本地文件
|
||||
using (var fs = File.OpenWrite(localFilePath))
|
||||
{
|
||||
result.Content.CopyTo(fs);
|
||||
fs.Close();
|
||||
}
|
||||
|
||||
}
|
||||
else if (ObjectStoreServiceOptions.ObjectStoreUse == "MinIO")
|
||||
{
|
||||
var minIOConfig = ObjectStoreServiceOptions.MinIO;
|
||||
|
||||
var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.endPoint}:{minIOConfig.port}")
|
||||
.WithCredentials(minIOConfig.accessKey, minIOConfig.secretKey).WithSSL(minIOConfig.useSSL)
|
||||
.Build();
|
||||
|
||||
var getObjectArgs = new GetObjectArgs()
|
||||
.WithBucket(minIOConfig.bucketName)
|
||||
.WithObject(ossRelativePath)
|
||||
.WithFile(localFilePath);
|
||||
|
||||
await minioClient.GetObjectAsync(getObjectArgs);
|
||||
|
||||
}
|
||||
else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS")
|
||||
{
|
||||
var minIOConfig = ObjectStoreServiceOptions.AWS;
|
||||
|
||||
var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.endPoint}")
|
||||
.WithCredentials(minIOConfig.accessKey, minIOConfig.secretKey).WithSSL(minIOConfig.useSSL)
|
||||
.Build();
|
||||
|
||||
var getObjectArgs = new GetObjectArgs()
|
||||
.WithBucket(minIOConfig.bucketName)
|
||||
.WithObject(ossRelativePath)
|
||||
.WithFile(localFilePath);
|
||||
|
||||
await minioClient.GetObjectAsync(getObjectArgs);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new BusinessValidationFailedException("未定义的存储介质类型");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
throw new BusinessValidationFailedException("oss下载失败!" + ex.Message);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
public async Task<string> GetSignedUrl(string ossRelativePath)
|
||||
{
|
||||
ossRelativePath = ossRelativePath.TrimStart('/');
|
||||
try
|
||||
{
|
||||
|
||||
|
||||
if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS")
|
||||
{
|
||||
var aliConfig = ObjectStoreServiceOptions.AliyunOSS;
|
||||
|
||||
var _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.endPoint : aliConfig.internalEndpoint, aliConfig.accessKeyId, aliConfig.accessKeySecret);
|
||||
|
||||
// 生成签名URL。
|
||||
var req = new GeneratePresignedUriRequest(aliConfig.bucketName, ossRelativePath, SignHttpMethod.Get)
|
||||
{
|
||||
// 设置签名URL过期时间,默认值为3600秒。
|
||||
Expiration = DateTime.Now.AddHours(1),
|
||||
};
|
||||
var uri = _ossClient.GeneratePresignedUri(req);
|
||||
|
||||
return uri.PathAndQuery;
|
||||
|
||||
}
|
||||
else if (ObjectStoreServiceOptions.ObjectStoreUse == "MinIO")
|
||||
{
|
||||
var minIOConfig = ObjectStoreServiceOptions.MinIO;
|
||||
|
||||
var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.endPoint}:{minIOConfig.port}")
|
||||
.WithCredentials(minIOConfig.accessKey, minIOConfig.secretKey).WithSSL(minIOConfig.useSSL)
|
||||
.Build();
|
||||
|
||||
|
||||
var args = new PresignedGetObjectArgs()
|
||||
.WithBucket(minIOConfig.bucketName)
|
||||
.WithObject(ossRelativePath)
|
||||
.WithExpiry(3600)
|
||||
/*.WithHeaders(reqParams)*/;
|
||||
|
||||
var presignedUrl = await minioClient.PresignedGetObjectAsync(args);
|
||||
|
||||
Uri uri = new Uri(presignedUrl);
|
||||
|
||||
string relativePath = uri.PathAndQuery;
|
||||
|
||||
|
||||
return relativePath;
|
||||
|
||||
}
|
||||
else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS")
|
||||
{
|
||||
var minIOConfig = ObjectStoreServiceOptions.AWS;
|
||||
|
||||
var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.endPoint}")
|
||||
.WithCredentials(minIOConfig.accessKey, minIOConfig.secretKey).WithSSL(minIOConfig.useSSL)
|
||||
.Build();
|
||||
|
||||
var args = new PresignedGetObjectArgs()
|
||||
.WithBucket(minIOConfig.bucketName)
|
||||
.WithObject(ossRelativePath)
|
||||
.WithExpiry(3600);
|
||||
|
||||
var presignedUrl = await minioClient.PresignedGetObjectAsync(args);
|
||||
|
||||
Uri uri = new Uri(presignedUrl);
|
||||
|
||||
string relativePath = uri.PathAndQuery;
|
||||
|
||||
|
||||
return relativePath;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new BusinessValidationFailedException("未定义的存储介质类型");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
throw new BusinessValidationFailedException("oss授权url失败!" + ex.Message);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -1,44 +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": "LTAI5tKvzs7ed3UfSpNk3xwQ",
|
||||
"accessKeySecret": "zTIceGEShlZDGnLrCFfIGFE7TXVRio",
|
||||
"bucketName": "zy-irc-store",
|
||||
"roleArn": "acs:ram::1899121822495495:role/oss-upload",
|
||||
"viewEndpoint": "https://zy-irc-cache.oss-cn-shanghai.aliyuncs.com",
|
||||
"region": "oss-cn-shanghai"
|
||||
},
|
||||
"MinIO": {
|
||||
"endpoint": "http://192.168.3.68",
|
||||
"port": "8001",
|
||||
"useSSL": false,
|
||||
"accessKey": "IDFkwEpWej0b4DtiuThL",
|
||||
"secretKey": "Lhuu83yMhVwu7c1SnjvGY6lq74jzpYqifK6Qtj4h",
|
||||
"bucketName": "test"
|
||||
}
|
||||
},
|
||||
|
||||
"ConnectionStrings": {
|
||||
"RemoteNew": "Server=prod_mssql_standard,1433;Database=Prod_IRC;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true",
|
||||
"Hangfire": "Server=prod_mssql_standard,1433;Database=Prod_IRC_Hangfire;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true"
|
||||
},
|
||||
|
||||
"DicomSCPServiceConfig": {
|
||||
"CalledAEList": [
|
||||
"STORESCP"
|
||||
],
|
||||
"ServerPort": 11112
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,80 +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": "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": "106.14.89.110",
|
||||
"port": "9001",
|
||||
"useSSL": false,
|
||||
"accessKey": "fbStsVYCIPKHQneeqMwD",
|
||||
"secretKey": "TzgvyA3zGXMUnpilJNUlyMYHfosl1hBMl6lxPmjy",
|
||||
"bucketName": "hir-test",
|
||||
"viewEndpoint": "http://106.14.89.110:9001/hir-test/"
|
||||
},
|
||||
|
||||
"AWS": {
|
||||
"endPoint": "s3.us-east-1.amazonaws.com",
|
||||
"useSSL": false,
|
||||
"accessKey": "AKIAZQ3DRSOHFPJJ6FEU",
|
||||
"secretKey": "l+yjtvV7Z4jiwm/7xCYv30UeUj/SvuqqYzAwjJHf",
|
||||
"bucketName": "ei-irc-test-store",
|
||||
"viewEndpoint": "https://ei-irc-test-store.s3.amazonaws.com/"
|
||||
}
|
||||
},
|
||||
|
||||
"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"
|
||||
},
|
||||
"BasicSystemConfig": {
|
||||
|
||||
"OpenUserComplexPassword": false,
|
||||
|
||||
"OpenSignDocumentBeforeWork": false,
|
||||
|
||||
"OpenTrialRelationDelete": true,
|
||||
|
||||
"OpenLoginLimit": false,
|
||||
"LoginMaxFailCount": 5,
|
||||
|
||||
"LoginFailLockMinutes": 30
|
||||
|
||||
},
|
||||
|
||||
"SystemEmailSendConfig": {
|
||||
"Port": 465,
|
||||
"Host": "smtp.qiye.aliyun.com",
|
||||
"FromEmail": "test-study@extimaging.com",
|
||||
"FromName": "Test_Study",
|
||||
"AuthorizationCode": "zhanying123",
|
||||
|
||||
"SiteUrl": "http://study.test.extimaging.com/login"
|
||||
},
|
||||
"DicomSCPServiceConfig": {
|
||||
"CalledAEList": [
|
||||
"STORESCP",
|
||||
"Value1",
|
||||
"Value2",
|
||||
"Value3"
|
||||
],
|
||||
"ServerPort": 11112
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
}
|
||||
@@ -17,8 +17,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IRaCIS.Core.Infra.EFCore",
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IRaCIS.Core.Infrastructure", "IRaCIS.Core.Infrastructure\IRaCIS.Core.Infrastructure.csproj", "{07EED0F8-08E6-46F3-ACBE-17BC1391BD4C}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IRC.Core.SCP", "IRC.Core.SCP\IRC.Core.SCP.csproj", "{ECD08F47-DC1A-484E-BB91-6CDDC8823CC5}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -53,10 +51,6 @@ Global
|
||||
{07EED0F8-08E6-46F3-ACBE-17BC1391BD4C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{07EED0F8-08E6-46F3-ACBE-17BC1391BD4C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{07EED0F8-08E6-46F3-ACBE-17BC1391BD4C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{ECD08F47-DC1A-484E-BB91-6CDDC8823CC5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{ECD08F47-DC1A-484E-BB91-6CDDC8823CC5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{ECD08F47-DC1A-484E-BB91-6CDDC8823CC5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{ECD08F47-DC1A-484E-BB91-6CDDC8823CC5}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace EFSaving.Concurrency
|
||||
{
|
||||
public class Sample
|
||||
{
|
||||
public static void Run()
|
||||
{
|
||||
// Ensure database is created and has a person in it
|
||||
using (var setupContext = new PersonContext())
|
||||
{
|
||||
setupContext.Database.EnsureDeleted();
|
||||
setupContext.Database.EnsureCreated();
|
||||
|
||||
setupContext.People.Add(new Person { FirstName = "John", LastName = "Doe" });
|
||||
setupContext.SaveChanges();
|
||||
}
|
||||
|
||||
#region ConcurrencyHandlingCode
|
||||
using var context = new PersonContext();
|
||||
// Fetch a person from database and change phone number
|
||||
var person = context.People.Single(p => p.PersonId == 1);
|
||||
person.PhoneNumber = "555-555-5555";
|
||||
|
||||
// Change the person's name in the database to simulate a concurrency conflict
|
||||
context.Database.ExecuteSqlRaw(
|
||||
"UPDATE dbo.People SET FirstName = 'Jane' WHERE PersonId = 1");
|
||||
|
||||
var saved = false;
|
||||
while (!saved)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Attempt to save changes to the database
|
||||
context.SaveChanges();
|
||||
saved = true;
|
||||
}
|
||||
catch (DbUpdateConcurrencyException ex)
|
||||
{
|
||||
foreach (var entry in ex.Entries)
|
||||
{
|
||||
if (entry.Entity is Person)
|
||||
{
|
||||
var proposedValues = entry.CurrentValues;
|
||||
var databaseValues = entry.GetDatabaseValues();
|
||||
|
||||
foreach (var property in proposedValues.Properties)
|
||||
{
|
||||
var proposedValue = proposedValues[property];
|
||||
var databaseValue = databaseValues[property];
|
||||
|
||||
// TODO: decide which value should be written to database
|
||||
// proposedValues[property] = <value to be saved>;
|
||||
}
|
||||
|
||||
// Refresh original values to bypass next concurrency check
|
||||
entry.OriginalValues.SetValues(databaseValues);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotSupportedException(
|
||||
"Don't know how to handle concurrency conflicts for "
|
||||
+ entry.Metadata.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
public class PersonContext : DbContext
|
||||
{
|
||||
public DbSet<Person> People { get; set; }
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
{
|
||||
// Requires NuGet package Microsoft.EntityFrameworkCore.SqlServer
|
||||
optionsBuilder.UseSqlServer(
|
||||
@"Server=(localdb)\mssqllocaldb;Database=EFSaving.Concurrency;Trusted_Connection=True");
|
||||
}
|
||||
}
|
||||
|
||||
public class Person
|
||||
{
|
||||
public int PersonId { get; set; }
|
||||
|
||||
[ConcurrencyCheck]
|
||||
public string FirstName { get; set; }
|
||||
|
||||
[ConcurrencyCheck]
|
||||
public string LastName { get; set; }
|
||||
|
||||
public string PhoneNumber { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,231 @@
|
||||
//using Autofac;
|
||||
//using Autofac.Extensions.DependencyInjection;
|
||||
//using IRaCIS.Core.API;
|
||||
//using IRaCIS.Core.Application.Filter;
|
||||
//using IRaCIS.Core.Application.MediatR.Handlers;
|
||||
//using LogDashboard;
|
||||
//using MassTransit;
|
||||
//using MassTransit.NewIdProviders;
|
||||
//using MediatR;
|
||||
//using Microsoft.AspNetCore.Builder;
|
||||
//using Microsoft.AspNetCore.Http.Features;
|
||||
//using Microsoft.AspNetCore.HttpOverrides;
|
||||
//using Microsoft.AspNetCore.SignalR;
|
||||
//using Microsoft.Extensions.Configuration;
|
||||
//using Microsoft.Extensions.DependencyInjection;
|
||||
//using Microsoft.Extensions.Hosting;
|
||||
//using Serilog;
|
||||
//using System;
|
||||
|
||||
//var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
|
||||
// //以配置文件为准,否则 从url中取环境值(服务以命令行传递参数启动,配置文件配置了就不需要传递环境参数)
|
||||
// var config = new ConfigurationBuilder()
|
||||
// .AddEnvironmentVariables()
|
||||
// .Build();
|
||||
|
||||
// var enviromentName = config["ASPNETCORE_ENVIRONMENT"];
|
||||
|
||||
// if (string.IsNullOrWhiteSpace(enviromentName))
|
||||
// {
|
||||
|
||||
// var index = Array.IndexOf(args, "--env");
|
||||
// enviromentName = index > -1
|
||||
// ? args[index + 1]
|
||||
// : "Development";
|
||||
// }
|
||||
|
||||
|
||||
// NewId.SetProcessIdProvider(new CurrentProcessIdProvider());
|
||||
|
||||
|
||||
|
||||
|
||||
//builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory())
|
||||
// .ConfigureContainer<ContainerBuilder>(containerBuilder =>
|
||||
// {
|
||||
// containerBuilder.RegisterModule<AutofacModuleSetup>();
|
||||
// })
|
||||
// .UseWindowsService().UseSerilog();
|
||||
|
||||
|
||||
|
||||
//// Add services to the container.
|
||||
|
||||
////本地化
|
||||
//builder.Services.AddJsonLocalization(options => options.ResourcesPath = "Resources");
|
||||
|
||||
//// 异常、参数统一验证过滤器、Json序列化配置、字符串参数绑型统一Trim()
|
||||
//builder.Services.AddControllers(options =>
|
||||
//{
|
||||
// //options.Filters.Add<LogActionFilter>();
|
||||
// options.Filters.Add<ModelActionFilter>();
|
||||
// options.Filters.Add<ProjectExceptionFilter>();
|
||||
// //options.Filters.Add<UnitOfWorkFilter>();
|
||||
|
||||
// //if (_configuration.GetSection("BasicSystemConfig").GetValue<bool>("OpenLoginLimit"))
|
||||
// //{
|
||||
// // options.Filters.Add<LimitUserRequestAuthorization>();
|
||||
|
||||
// //}
|
||||
|
||||
|
||||
//}).AddNewtonsoftJsonSetup(); // NewtonsoftJson 序列化 处理
|
||||
|
||||
////动态WebApi + UnifiedApiResultFilter 省掉控制器代码
|
||||
//builder.Services.AddDynamicWebApiSetup();
|
||||
////AutoMapper
|
||||
//builder.Services.AddAutoMapperSetup();
|
||||
////EF ORM QueryWithNoLock
|
||||
//builder.Services.AddEFSetup(builder.Configuration);
|
||||
////Http 响应压缩
|
||||
//builder.Services.AddResponseCompressionSetup();
|
||||
////Swagger Api 文档
|
||||
//builder.Services.AddSwaggerSetup();
|
||||
////JWT Token 验证
|
||||
//builder.Services.AddJWTAuthSetup(builder.Configuration);
|
||||
//// MediatR 进程内消息 事件解耦 从程序集中 注册命令和handler对应关系
|
||||
//builder.Services.AddMediatR(typeof(ConsistencyVerificationHandler).Assembly);
|
||||
//// EasyCaching 缓存
|
||||
//builder.Services.AddEasyCachingSetup();
|
||||
|
||||
////services.AddDistributedMemoryCache();
|
||||
|
||||
////// hangfire 定时任务框架 有界面,更友好~
|
||||
//builder.Services.AddhangfireSetup(builder.Configuration);
|
||||
////// QuartZ 定时任务框架 使用了hangfire 暂时不用,后续需要可以打开,已经配好
|
||||
////builder.Services.AddQuartZSetup(_configuration);
|
||||
|
||||
//// 保护上传文件
|
||||
////services.AddStaticFileAuthorizationSetup();
|
||||
|
||||
|
||||
//////HttpReports 暂时废弃
|
||||
////services.AddHttpReports().AddHttpTransport();
|
||||
////Serilog 日志可视化 LogDashboard日志
|
||||
//builder.Services.AddLogDashboardSetup();
|
||||
////上传限制 配置
|
||||
//builder.Services.Configure<FormOptions>(options =>
|
||||
//{
|
||||
// options.MultipartBodyLengthLimit = int.MaxValue;
|
||||
// options.ValueCountLimit = int.MaxValue;
|
||||
// options.ValueLengthLimit = int.MaxValue;
|
||||
//});
|
||||
////IP 限流 可设置白名单 或者黑名单
|
||||
////services.AddIpPolicyRateLimitSetup(_configuration);
|
||||
////用户类型 策略授权
|
||||
//builder.Services.AddAuthorizationPolicySetup(builder.Configuration);
|
||||
|
||||
//builder.Services.AddJsonConfigSetup(builder.Configuration);
|
||||
|
||||
////转发头设置 获取真实IP
|
||||
//builder.Services.Configure<ForwardedHeadersOptions>(options =>
|
||||
//{
|
||||
// options.ForwardedHeaders =
|
||||
// ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
|
||||
//});
|
||||
////Dicom影像渲染图片 跨平台
|
||||
//builder.Services.AddDicomSetup();
|
||||
|
||||
//// 实时应用
|
||||
//builder.Services.AddSignalR();
|
||||
|
||||
|
||||
//builder.Services.AddSingleton<IUserIdProvider, IRaCISUserIdProvider>();
|
||||
|
||||
//builder.Services.AddControllers();
|
||||
//// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
|
||||
////builder.Services.AddEndpointsApiExplorer();
|
||||
////builder.Services.AddSwaggerGen();
|
||||
|
||||
////SerilogExtension.AddSerilogSetup(enviromentName, builder.Host.confi);
|
||||
|
||||
|
||||
//var app = builder.Build();
|
||||
|
||||
//// Configure the HTTP request pipeline.
|
||||
|
||||
////本地化
|
||||
//app.UseLocalization();
|
||||
|
||||
//app.UseForwardedHeaders();
|
||||
|
||||
|
||||
////不需要 token 访问的静态文件 wwwroot css, JavaScript, and images don't require authentication.
|
||||
//app.UseStaticFiles();
|
||||
|
||||
//app.UseIRacisHostStaticFileStore(app.Environment);
|
||||
|
||||
////LogDashboard
|
||||
//app.UseLogDashboard("/LogDashboard");
|
||||
|
||||
////hangfire
|
||||
////app.UseHangfireConfig(app.Environment);
|
||||
|
||||
//////暂时废弃
|
||||
////app.UseHttpReports();
|
||||
|
||||
//////限流 中间件
|
||||
////app.UseIpRateLimiting();
|
||||
|
||||
////响应压缩
|
||||
//app.UseResponseCompression();
|
||||
|
||||
//if (app.Environment.IsDevelopment())
|
||||
//{
|
||||
// app.UseDeveloperExceptionPage();
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
|
||||
// //app.UseHsts();
|
||||
//}
|
||||
|
||||
//SwaggerSetup.Configure(app, app.Environment);
|
||||
|
||||
//Console.WriteLine("当前环境: " + builder.Environment.EnvironmentName);
|
||||
|
||||
////app.UseMiddleware<AuthMiddleware>();
|
||||
|
||||
//// 特殊异常处理 比如 404
|
||||
//app.UseStatusCodePagesWithReExecute("/Error/{0}");
|
||||
|
||||
|
||||
|
||||
|
||||
//////serilog 记录请求的用户信息
|
||||
//app.UseSerilogConfig(app.Environment);
|
||||
|
||||
//app.UseRouting();
|
||||
|
||||
//app.UseCors(t => t.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader());
|
||||
|
||||
|
||||
//app.UseAuthentication();
|
||||
////app.UseJwtBearerQueryString();
|
||||
//app.UseAuthorization();
|
||||
|
||||
//////文件伺服 必须带Token 访问
|
||||
//////app.UseIRacisHostStaticFileStore(env);
|
||||
|
||||
|
||||
|
||||
////app.UseEndpoints(endpoints =>
|
||||
////{
|
||||
|
||||
|
||||
//// endpoints.MapControllers();
|
||||
|
||||
//// endpoints.MapHub<UploadHub>("/UploadHub")/*.RequireCors(t=>t.WithOrigins(new string[] {"null"}).AllowAnyMethod().AllowAnyHeader().AllowCredentials())*/;
|
||||
////});
|
||||
|
||||
|
||||
|
||||
//app.MapControllers();
|
||||
//app.MapHub<UploadHub>("/UploadHub")/*.RequireCors(t=>t.WithOrigins(new string[] {"null"}).AllowAnyMethod().AllowAnyHeader().AllowCredentials())*/;
|
||||
|
||||
|
||||
//app.Run();
|
||||
|
||||
//测试同步
|
||||
@@ -0,0 +1,106 @@
|
||||
{
|
||||
"needAnonymizeTag": [
|
||||
{ //PatientsName
|
||||
"Group": "0010",
|
||||
"Element": "0010",
|
||||
"ReplaceValue": "",
|
||||
"Enable": true
|
||||
},
|
||||
{ // PatientID
|
||||
"Group": "0010",
|
||||
"Element": "0020",
|
||||
"ReplaceValue": "",
|
||||
"Enable": true
|
||||
},
|
||||
{ // IssuerOfPatientID
|
||||
"Group": "0010",
|
||||
"Element": "0021",
|
||||
"ReplaceValue": "",
|
||||
"Enable": true
|
||||
},
|
||||
{ // PatientsBirthDate
|
||||
"Group": "0010",
|
||||
"Element": "0030",
|
||||
"ReplaceValue": "",
|
||||
"Enable": true
|
||||
},
|
||||
{ // PatientsBirthTime
|
||||
"Group": "0010",
|
||||
"Element": "0032",
|
||||
"ReplaceValue": "",
|
||||
"Enable": false
|
||||
},
|
||||
{ // PatientsSex
|
||||
"Group": "0010",
|
||||
"Element": "0040",
|
||||
"ReplaceValue": "",
|
||||
"Enable": false
|
||||
},
|
||||
{ // OtherPatientIDs
|
||||
"Group": "0010",
|
||||
"Element": "1000",
|
||||
"ReplaceValue": "",
|
||||
"Enable": false
|
||||
},
|
||||
{ // OtherPatientNames
|
||||
"Group": "0010",
|
||||
"Element": "1001",
|
||||
"ReplaceValue": "",
|
||||
"Enable": false
|
||||
},
|
||||
{ // OtherPatientNames
|
||||
"Group": "0010",
|
||||
"Element": "1005",
|
||||
"ReplaceValue": "",
|
||||
"Enable": true
|
||||
},
|
||||
{ // PatientBirthName
|
||||
"Group": "0010",
|
||||
"Element": "1005",
|
||||
"ReplaceValue": "",
|
||||
"Enable": true
|
||||
},
|
||||
{ // PatientsAge
|
||||
"Group": "0010",
|
||||
"Element": "1010",
|
||||
"ReplaceValue": "",
|
||||
"Enable": true
|
||||
},
|
||||
{ // PatientsAddress
|
||||
"Group": "0010",
|
||||
"Element": "1040",
|
||||
"ReplaceValue": "",
|
||||
"Enable": true
|
||||
},
|
||||
{ // PatientsMothersBirthName
|
||||
"Group": "0010",
|
||||
"Element": "1060",
|
||||
"ReplaceValue": "",
|
||||
"Enable": true
|
||||
},
|
||||
{
|
||||
"Group": "0010",
|
||||
"Element": "2150",
|
||||
"ReplaceValue": "",
|
||||
"Enable": true
|
||||
},
|
||||
{
|
||||
"Group": "0010",
|
||||
"Element": "2152",
|
||||
"ReplaceValue": "",
|
||||
"Enable": true
|
||||
},
|
||||
{
|
||||
"Group": "0010",
|
||||
"Element": "2154",
|
||||
"ReplaceValue": "",
|
||||
"Enable": true
|
||||
},
|
||||
{
|
||||
"Group": "0012",
|
||||
"Element": "0040",
|
||||
"ReplaceValue": "XXX",
|
||||
"Enable": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using EasyCaching.Core;
|
||||
using IRaCIS.Application.Interfaces;
|
||||
using IRaCIS.Application.Contracts;
|
||||
using IRaCIS.Core.Application.Auth;
|
||||
@@ -20,23 +21,13 @@ using IRaCIS.Core.Domain.Models;
|
||||
using IRaCIS.Core.Infrastructure;
|
||||
using System.Linq;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using MassTransit;
|
||||
using Aliyun.Acs.Core;
|
||||
using Aliyun.Acs.Core.Profile;
|
||||
using Aliyun.Acs.Sts.Model.V20150401;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using MassTransit;
|
||||
using IRaCIS.Core.Application.Helper;
|
||||
using Microsoft.Extensions.Options;
|
||||
using IRaCIS.Core.Application.Contracts;
|
||||
using LoginReturnDTO = IRaCIS.Application.Contracts.LoginReturnDTO;
|
||||
using DocumentFormat.OpenXml.Spreadsheet;
|
||||
using AutoMapper.QueryableExtensions;
|
||||
using NetTopologySuite.Algorithm;
|
||||
using ZiggyCreatures.Caching.Fusion;
|
||||
using AlibabaCloud.SDK.Sts20150401;
|
||||
using AlibabaCloud.SDK.Sts20150401.Models;
|
||||
using Org.BouncyCastle.Tls;
|
||||
using Amazon.SecurityToken.Model;
|
||||
using Amazon.SecurityToken;
|
||||
using Amazon;
|
||||
using AssumeRoleRequest = Amazon.SecurityToken.Model.AssumeRoleRequest;
|
||||
|
||||
namespace IRaCIS.Api.Controllers
|
||||
{
|
||||
@@ -48,231 +39,203 @@ namespace IRaCIS.Api.Controllers
|
||||
{
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 获取医生详情
|
||||
/// </summary>
|
||||
/// <param name="attachmentService"></param>
|
||||
/// <param name="_doctorService"></param>
|
||||
/// <param name="_educationService"></param>
|
||||
/// <param name="_trialExperienceService"></param>
|
||||
/// <param name="_researchPublicationService"></param>
|
||||
/// <param name="_vacationService"></param>
|
||||
/// <param name="doctorId"></param>
|
||||
/// <returns></returns>
|
||||
[HttpGet, Route("doctor/getDetail/{doctorId:guid}")]
|
||||
|
||||
public async Task<IResponseOutput<DoctorDetailDTO>> GetDoctorDetail([FromServices] IAttachmentService attachmentService, [FromServices] IDoctorService _doctorService,
|
||||
[FromServices] IEducationService _educationService, [FromServices] ITrialExperienceService _trialExperienceService,
|
||||
|
||||
[FromServices] IResearchPublicationService _researchPublicationService, [FromServices] IVacationService _vacationService, Guid doctorId)
|
||||
{
|
||||
var education = await _educationService.GetEducation(doctorId);
|
||||
|
||||
var sowList = _doctorService.GetDoctorSowList(doctorId);
|
||||
var ackSowList = _doctorService.GetDoctorAckSowList(doctorId);
|
||||
|
||||
var doctorDetail = new DoctorDetailDTO
|
||||
{
|
||||
AuditView = await _doctorService.GetAuditState(doctorId),
|
||||
BasicInfoView = await _doctorService.GetBasicInfo(doctorId),
|
||||
EmploymentView = await _doctorService.GetEmploymentInfo(doctorId),
|
||||
AttachmentList = await attachmentService.GetAttachments(doctorId),
|
||||
|
||||
EducationList = education.EducationList,
|
||||
PostgraduateList = education.PostgraduateList,
|
||||
|
||||
TrialExperienceView = await _trialExperienceService.GetTrialExperience(doctorId),
|
||||
ResearchPublicationView = await _researchPublicationService.GetResearchPublication(doctorId),
|
||||
|
||||
SpecialtyView = await _doctorService.GetSpecialtyInfo(doctorId),
|
||||
InHoliday = (await _vacationService.OnVacation(doctorId)).IsSuccess,
|
||||
IntoGroupInfo = _doctorService.GetDoctorIntoGroupInfo(doctorId),
|
||||
SowList = sowList,
|
||||
AckSowList = ackSowList
|
||||
};
|
||||
|
||||
return ResponseOutput.Ok(doctorDetail);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary> 系统用户登录接口[New] </summary>
|
||||
[HttpPost, Route("user/login")]
|
||||
[AllowAnonymous]
|
||||
public async Task<IResponseOutput> Login(UserLoginDTO loginUser,
|
||||
[FromServices] IFusionCache _fusionCache,
|
||||
[FromServices] IUserService _userService,
|
||||
[FromServices] ITokenService _tokenService,
|
||||
[FromServices] IReadingImageTaskService readingImageTaskService,
|
||||
[FromServices] IOptionsMonitor<ServiceVerifyConfigOption> _verifyConfig,
|
||||
[FromServices] IMailVerificationService _mailVerificationService)
|
||||
public async Task<IResponseOutput<LoginReturnDTO>> Login(UserLoginDTO loginUser, [FromServices] IEasyCachingProvider provider, [FromServices] IUserService _userService,
|
||||
[FromServices] ITokenService _tokenService, [FromServices] IConfiguration configuration)
|
||||
{
|
||||
|
||||
//MFA 邮箱验证 前端传递用户Id 和MFACode
|
||||
if (loginUser.UserId != null && _verifyConfig.CurrentValue.OpenLoginMFA)
|
||||
|
||||
var returnModel = await _userService.Login(loginUser.UserName, loginUser.Password);
|
||||
|
||||
if (returnModel.IsSuccess)
|
||||
{
|
||||
Guid userId = (Guid)loginUser.UserId;
|
||||
#region GRPC 调用鉴权中心,因为服务器IIS问题 http/2 故而没法使用
|
||||
|
||||
//验证MFA 编码是否有问题 ,前端要拆开,自己调用验证的逻辑
|
||||
//await _userService.VerifyMFACodeAsync(userId, loginUser.MFACode);
|
||||
////重试策略
|
||||
//var defaultMethodConfig = new MethodConfig
|
||||
//{
|
||||
// Names = { MethodName.Default },
|
||||
// RetryPolicy = new RetryPolicy
|
||||
// {
|
||||
// MaxAttempts = 3,
|
||||
// InitialBackoff = TimeSpan.FromSeconds(1),
|
||||
// MaxBackoff = TimeSpan.FromSeconds(5),
|
||||
// BackoffMultiplier = 1.5,
|
||||
// RetryableStatusCodes = { Grpc.Core.StatusCode.Unavailable }
|
||||
// }
|
||||
//};
|
||||
|
||||
//var loginUser = await _userRepository.Where(u => u.UserName.Equals(userName) && u.Password == password).ProjectTo<UserBasicInfo>(_mapper.ConfigurationProvider).FirstOrDefaultAsync();
|
||||
//#region unable to trust the certificate then the gRPC client can be configured to ignore the invalid certificate
|
||||
|
||||
var basicInfo = await _userService.GetUserBasicInfo(userId, loginUser.Password);
|
||||
//var httpHandler = new HttpClientHandler();
|
||||
//// Return `true` to allow certificates that are untrusted/invalid
|
||||
//httpHandler.ServerCertificateCustomValidationCallback =
|
||||
// HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
|
||||
|
||||
var loginReturn = new LoginReturnDTO() { BasicInfo = basicInfo };
|
||||
|
||||
loginReturn.JWTStr = _tokenService.GetToken(IRaCISClaims.Create(loginReturn.BasicInfo));
|
||||
//////这一句是让grpc支持本地 http 如果本地访问部署在服务器上,那么是访问不成功的
|
||||
//AppContext.SetSwitch(
|
||||
// "System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
|
||||
|
||||
//#endregion
|
||||
|
||||
|
||||
|
||||
//var grpcAdress = configuration.GetValue<string>("GrpcAddress");
|
||||
////var grpcAdress = "http://localhost:7200";
|
||||
|
||||
//var channel = GrpcChannel.ForAddress(grpcAdress, new GrpcChannelOptions
|
||||
//{
|
||||
// HttpHandler = httpHandler,
|
||||
// ServiceConfig = new ServiceConfig { MethodConfigs = { defaultMethodConfig } }
|
||||
|
||||
//});
|
||||
////var channel = GrpcChannel.ForAddress(grpcAdress);
|
||||
//var grpcClient = new TokenGrpcService.TokenGrpcServiceClient(channel);
|
||||
|
||||
//var userInfo = returnModel.Data.BasicInfo;
|
||||
|
||||
//var tokenResponse = grpcClient.GetUserToken(new GetTokenReuqest()
|
||||
//{
|
||||
// Id = userInfo.Id.ToString(),
|
||||
// ReviewerCode = userInfo.ReviewerCode,
|
||||
// IsAdmin = userInfo.IsAdmin,
|
||||
// RealName = userInfo.RealName,
|
||||
// UserTypeEnumInt = (int)userInfo.UserTypeEnum,
|
||||
// UserTypeShortName = userInfo.UserTypeShortName,
|
||||
// UserName = userInfo.UserName
|
||||
//});
|
||||
|
||||
//returnModel.Data.JWTStr = tokenResponse.Token;
|
||||
|
||||
#endregion
|
||||
|
||||
returnModel.Data.JWTStr = _tokenService.GetToken(IRaCISClaims.Create(returnModel.Data.BasicInfo));
|
||||
|
||||
// 创建一个 CookieOptions 对象,用于设置 Cookie 的属性
|
||||
var option = new CookieOptions
|
||||
{
|
||||
Expires = DateTime.Now.AddMonths(1), // 设置过期时间为 30 分钟之后
|
||||
HttpOnly = false, // 确保 cookie 只能通过 HTTP 访问
|
||||
SameSite = Microsoft.AspNetCore.Http.SameSiteMode.None, // 设置 SameSite 属性
|
||||
Expires = DateTime.Now.AddMonths(1),
|
||||
HttpOnly = true, // 确保 cookie 只能通过 HTTP 访问
|
||||
SameSite = Microsoft.AspNetCore.Http.SameSiteMode.Unspecified, // 设置 SameSite 属性
|
||||
Secure = false // 确保 cookie 只能通过 HTTPS 访问
|
||||
};
|
||||
|
||||
HttpContext.Response.Cookies.Append("access_token", loginReturn.JWTStr, option);
|
||||
HttpContext.Response.Cookies.Append("access_token", returnModel.Data.JWTStr, option);
|
||||
|
||||
// 验证阅片休息时间
|
||||
await readingImageTaskService.ResetReadingRestTime(userId);
|
||||
}
|
||||
|
||||
await _fusionCache.SetAsync(userId.ToString(), loginReturn.JWTStr, TimeSpan.FromDays(7));
|
||||
var userId = returnModel.Data.BasicInfo.Id.ToString();
|
||||
//provider.Set(userId, userId, TimeSpan.FromMinutes(AppSettings.LoginExpiredTimeSpan));
|
||||
|
||||
await _fusionCache.SetAsync($"{userId.ToString()}_Online", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), TimeSpan.FromMinutes(_verifyConfig.CurrentValue.AutoLoginOutMinutes));
|
||||
await provider.SetAsync(userId.ToString(), returnModel.Data.JWTStr, TimeSpan.FromDays(7));
|
||||
return returnModel;
|
||||
}
|
||||
|
||||
return ResponseOutput.Ok(loginReturn);
|
||||
[HttpGet("user/GetObjectStoreToken")]
|
||||
public IResponseOutput GetObjectStoreToken([FromServices] IOptionsMonitor<ObjectStoreServiceOptions> options)
|
||||
{
|
||||
var serviceOption = options.CurrentValue;
|
||||
|
||||
if (Enum.TryParse<ObjectStoreUse>(serviceOption.ObjectStoreUse, out var parsedEnum) && parsedEnum == ObjectStoreUse.AliyunOSS)
|
||||
{
|
||||
|
||||
var ossOptions = serviceOption.AliyunOSS;
|
||||
|
||||
return ResponseOutput.Ok(new ObjectStoreDTO() { ObjectStoreUse = serviceOption.ObjectStoreUse, MinIO = serviceOption.MinIO, AliyunOSS = serviceOption.AliyunOSS, AWS = serviceOption.AWS });
|
||||
|
||||
#region 临时token 屏蔽
|
||||
//IClientProfile profile = DefaultProfile.GetProfile(ossOptions.RegionId, ossOptions.AccessKeyId, ossOptions.AccessKeySecret);
|
||||
//DefaultAcsClient client = new DefaultAcsClient(profile);
|
||||
|
||||
|
||||
//// 创建一个STS请求
|
||||
//AssumeRoleRequest request = new AssumeRoleRequest
|
||||
//{
|
||||
// RoleArn = ossOptions.RoleArn, // 角色ARN,需要替换为你的角色ARN
|
||||
// RoleSessionName = $"session-name-{NewId.NextGuid()}", // 角色会话名称,可自定义
|
||||
// DurationSeconds = 900, // 令牌有效期(单位:秒),这里设置为1小时
|
||||
//};
|
||||
|
||||
|
||||
//AssumeRoleResponse response = client.GetAcsResponse(request);
|
||||
|
||||
//// 返回STS令牌信息给前端
|
||||
//var stsToken = new ObjectStoreDTO()
|
||||
//{
|
||||
// ObjectStoreUse = serviceOption.ObjectStoreUse,
|
||||
// AliyunOSS = new AliyunOSSTempToken()
|
||||
// {
|
||||
// AccessKeyId = response.Credentials.AccessKeyId,
|
||||
// AccessKeySecret = response.Credentials.AccessKeySecret,
|
||||
// SecurityToken = response.Credentials.SecurityToken,
|
||||
// Expiration = response.Credentials.Expiration,
|
||||
|
||||
// Region = ossOptions.Region,
|
||||
// BucketName = ossOptions.BucketName,
|
||||
// ViewEndpoint = ossOptions.ViewEndpoint,
|
||||
|
||||
// },
|
||||
// MinIO = serviceOption.MinIO
|
||||
//};
|
||||
//return ResponseOutput.Ok(stsToken);
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
else if (Enum.TryParse<ObjectStoreUse>(serviceOption.ObjectStoreUse, out var parsedValue) && parsedValue == ObjectStoreUse.MinIO)
|
||||
{
|
||||
return ResponseOutput.Ok(new ObjectStoreDTO() { ObjectStoreUse = serviceOption.ObjectStoreUse, MinIO = serviceOption.MinIO, AWS = serviceOption.AWS });
|
||||
}
|
||||
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)
|
||||
{
|
||||
//发版屏蔽
|
||||
|
||||
//returnModel.Data.JWTStr = _tokenService.GetToken(IRaCISClaims.Create(returnModel.Data.BasicInfo));
|
||||
|
||||
//MFA 发送邮件
|
||||
|
||||
returnModel.Data.IsMFA = true;
|
||||
|
||||
var email = returnModel.Data.BasicInfo.EMail;
|
||||
|
||||
var hiddenEmail = EmailMaskHelper.MaskEmail(email);
|
||||
|
||||
returnModel.Data.BasicInfo.EMail = hiddenEmail;
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
}
|
||||
return returnModel;
|
||||
|
||||
return ResponseOutput.Ok(new ObjectStoreDTO() { ObjectStoreUse = serviceOption.ObjectStoreUse, MinIO = serviceOption.MinIO, AWS = serviceOption.AWS });
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
[HttpGet("user/GenerateSTS")]
|
||||
public IResponseOutput GenerateSTS([FromServices] IOptionsMonitor<AliyunOSSOptions> options)
|
||||
{
|
||||
var ossOptions = options.CurrentValue;
|
||||
|
||||
|
||||
IClientProfile profile = DefaultProfile.GetProfile(ossOptions.regionId, ossOptions.accessKeyId, ossOptions.accessKeySecret);
|
||||
DefaultAcsClient client = new DefaultAcsClient(profile);
|
||||
|
||||
|
||||
// 创建一个STS请求
|
||||
AssumeRoleRequest request = new AssumeRoleRequest
|
||||
{
|
||||
RoleArn = ossOptions.roleArn, // 角色ARN,需要替换为你的角色ARN
|
||||
RoleSessionName = $"session-name-{NewId.NextGuid()}", // 角色会话名称,可自定义
|
||||
DurationSeconds = 900, // 令牌有效期(单位:秒),这里设置为1小时
|
||||
};
|
||||
|
||||
|
||||
AssumeRoleResponse response = client.GetAcsResponse(request);
|
||||
|
||||
// 返回STS令牌信息给前端
|
||||
var stsToken = new
|
||||
{
|
||||
AccessKeyId = response.Credentials.AccessKeyId,
|
||||
AccessKeySecret = response.Credentials.AccessKeySecret,
|
||||
SecurityToken = response.Credentials.SecurityToken,
|
||||
Expiration = response.Credentials.Expiration,
|
||||
|
||||
Region = ossOptions.region ,
|
||||
BucketName = ossOptions.bucketName ,
|
||||
ViewEndpoint = ossOptions.viewEndpoint ,
|
||||
|
||||
};
|
||||
|
||||
return ResponseOutput.Ok(stsToken);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -296,184 +259,6 @@ namespace IRaCIS.Api.Controllers
|
||||
return ResponseOutput.Ok("/showdicom?studyId=f7b67793-8155-0223-2f15-118f2642efb8&type=Share&token=" + token);
|
||||
}
|
||||
|
||||
[HttpGet("user/GetObjectStoreToken")]
|
||||
public async Task<IResponseOutput> GetObjectStoreTokenAsync([FromServices] IOptionsMonitor<ObjectStoreServiceOptions> options)
|
||||
{
|
||||
var serviceOption = options.CurrentValue;
|
||||
|
||||
if (Enum.TryParse<ObjectStoreUse>(serviceOption.ObjectStoreUse, out var parsedEnum) && parsedEnum == ObjectStoreUse.AliyunOSS)
|
||||
{
|
||||
|
||||
var ossOptions = serviceOption.AliyunOSS;
|
||||
|
||||
var client = new Client(new AlibabaCloud.OpenApiClient.Models.Config()
|
||||
{
|
||||
AccessKeyId = ossOptions.AccessKeyId,
|
||||
AccessKeySecret = ossOptions.AccessKeySecret,
|
||||
//AccessKeyId = "LTAI5tJV76pYX5yPg1N9QVE8",
|
||||
//AccessKeySecret = "roRNLa9YG1of4pYruJGCNKBXEWTAWa",
|
||||
|
||||
Endpoint = "sts.cn-hangzhou.aliyuncs.com"
|
||||
});
|
||||
|
||||
var assumeRoleRequest = new AlibabaCloud.SDK.Sts20150401.Models.AssumeRoleRequest();
|
||||
// 将<YOUR_ROLE_SESSION_NAME>设置为自定义的会话名称,例如oss-role-session。
|
||||
assumeRoleRequest.RoleSessionName = $"session-name-{NewId.NextGuid()}";
|
||||
// 将<YOUR_ROLE_ARN>替换为拥有上传文件到指定OSS Bucket权限的RAM角色的ARN。
|
||||
assumeRoleRequest.RoleArn = ossOptions.RoleArn;
|
||||
//assumeRoleRequest.RoleArn = "acs:ram::1899121822495495:role/webdirect";
|
||||
assumeRoleRequest.DurationSeconds = ossOptions.DurationSeconds;
|
||||
var runtime = new AlibabaCloud.TeaUtil.Models.RuntimeOptions();
|
||||
var response = client.AssumeRoleWithOptions(assumeRoleRequest, runtime);
|
||||
var credentials = response.Body.Credentials;
|
||||
|
||||
var tempToken = new AliyunOSSTempToken()
|
||||
{
|
||||
AccessKeyId = credentials.AccessKeyId,
|
||||
AccessKeySecret = credentials.AccessKeySecret,
|
||||
Expiration = credentials.Expiration,
|
||||
SecurityToken = credentials.SecurityToken,
|
||||
|
||||
|
||||
Region = ossOptions.Region,
|
||||
BucketName = ossOptions.BucketName,
|
||||
EndPoint = ossOptions.EndPoint,
|
||||
ViewEndpoint = ossOptions.ViewEndpoint,
|
||||
|
||||
};
|
||||
|
||||
var awstempToken = await GetAWSTemToken(serviceOption);
|
||||
|
||||
return ResponseOutput.Ok(new ObjectStoreDTO() { ObjectStoreUse = serviceOption.ObjectStoreUse, AliyunOSS = tempToken,AWS= awstempToken });
|
||||
|
||||
|
||||
}
|
||||
else if (Enum.TryParse<ObjectStoreUse>(serviceOption.ObjectStoreUse, out var parsedValue) && parsedValue == ObjectStoreUse.MinIO)
|
||||
{
|
||||
return ResponseOutput.Ok(new ObjectStoreDTO() { ObjectStoreUse = serviceOption.ObjectStoreUse, MinIO = serviceOption.MinIO});
|
||||
}
|
||||
else
|
||||
{
|
||||
var awsOptions = serviceOption.AWS;
|
||||
|
||||
//aws 临时凭证
|
||||
// 创建 STS 客户端
|
||||
var stsClient = new AmazonSecurityTokenServiceClient(awsOptions.AccessKeyId, awsOptions.SecretAccessKey);
|
||||
|
||||
// 使用 AssumeRole 请求临时凭证
|
||||
var assumeRoleRequest = new AssumeRoleRequest
|
||||
{
|
||||
|
||||
RoleArn = awsOptions.RoleArn, // 角色 ARN
|
||||
RoleSessionName = $"session-name-{NewId.NextGuid()}",
|
||||
DurationSeconds = awsOptions.DurationSeconds // 临时凭证有效期
|
||||
};
|
||||
|
||||
var assumeRoleResponse = await stsClient.AssumeRoleAsync(assumeRoleRequest);
|
||||
|
||||
var credentials = assumeRoleResponse.Credentials;
|
||||
|
||||
var tempToken = new AWSTempToken()
|
||||
{
|
||||
AccessKeyId = credentials.AccessKeyId,
|
||||
SecretAccessKey = credentials.SecretAccessKey,
|
||||
SessionToken= credentials.SessionToken,
|
||||
Expiration=credentials.Expiration,
|
||||
Region=awsOptions.Region,
|
||||
|
||||
BucketName = awsOptions.BucketName,
|
||||
EndPoint = awsOptions.EndPoint,
|
||||
ViewEndpoint = awsOptions.ViewEndpoint,
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
return ResponseOutput.Ok(new ObjectStoreDTO() { ObjectStoreUse = serviceOption.ObjectStoreUse, MinIO = serviceOption.MinIO, AWS = tempToken });
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private async Task<AWSTempToken> GetAWSTemToken(ObjectStoreServiceOptions serviceOption)
|
||||
{
|
||||
var awsOptions = serviceOption.AWS;
|
||||
|
||||
//aws 临时凭证
|
||||
// 创建 STS 客户端
|
||||
var stsClient = new AmazonSecurityTokenServiceClient(awsOptions.AccessKeyId, awsOptions.SecretAccessKey);
|
||||
|
||||
// 使用 AssumeRole 请求临时凭证
|
||||
var assumeRoleRequest = new AssumeRoleRequest
|
||||
{
|
||||
|
||||
RoleArn = awsOptions.RoleArn, // 角色 ARN
|
||||
RoleSessionName = $"session-name-{NewId.NextGuid()}",
|
||||
DurationSeconds = awsOptions.DurationSeconds // 临时凭证有效期
|
||||
};
|
||||
|
||||
var assumeRoleResponse = await stsClient.AssumeRoleAsync(assumeRoleRequest);
|
||||
|
||||
var credentials = assumeRoleResponse.Credentials;
|
||||
|
||||
var tempToken = new AWSTempToken()
|
||||
{
|
||||
AccessKeyId = credentials.AccessKeyId,
|
||||
SecretAccessKey = credentials.SecretAccessKey,
|
||||
SessionToken = credentials.SessionToken,
|
||||
Expiration = credentials.Expiration,
|
||||
Region = awsOptions.Region,
|
||||
BucketName = awsOptions.BucketName,
|
||||
EndPoint = awsOptions.EndPoint,
|
||||
ViewEndpoint = awsOptions.ViewEndpoint,
|
||||
|
||||
};
|
||||
|
||||
return tempToken;
|
||||
}
|
||||
|
||||
|
||||
#region aliyun-net-sdk-sts 之前
|
||||
//[HttpGet("user/GenerateSTS")]
|
||||
//public IResponseOutput GenerateSTS([FromServices] IOptionsMonitor<AliyunOSSOptions> options)
|
||||
//{
|
||||
// var ossOptions = options.CurrentValue;
|
||||
|
||||
|
||||
// IClientProfile profile = DefaultProfile.GetProfile(ossOptions.regionId, ossOptions.accessKeyId, ossOptions.accessKeySecret);
|
||||
// DefaultAcsClient client = new DefaultAcsClient(profile);
|
||||
|
||||
|
||||
// // 创建一个STS请求
|
||||
// AssumeRoleRequest request = new AssumeRoleRequest
|
||||
// {
|
||||
// RoleArn = ossOptions.roleArn, // 角色ARN,需要替换为你的角色ARN
|
||||
// RoleSessionName = $"session-name-{NewId.NextGuid()}", // 角色会话名称,可自定义
|
||||
// DurationSeconds = 900, // 令牌有效期(单位:秒),这里设置为1小时
|
||||
// };
|
||||
|
||||
|
||||
// AssumeRoleResponse response = client.GetAcsResponse(request);
|
||||
|
||||
// // 返回STS令牌信息给前端
|
||||
// var stsToken = new
|
||||
// {
|
||||
// AccessKeyId = response.Credentials.AccessKeyId,
|
||||
// AccessKeySecret = response.Credentials.AccessKeySecret,
|
||||
// SecurityToken = response.Credentials.SecurityToken,
|
||||
// Expiration = response.Credentials.Expiration,
|
||||
|
||||
// Region = ossOptions.region,
|
||||
// BucketName = ossOptions.bucketName,
|
||||
// ViewEndpoint = ossOptions.viewEndpoint,
|
||||
|
||||
// };
|
||||
|
||||
// return ResponseOutput.Ok(stsToken);
|
||||
|
||||
//}
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -510,7 +295,7 @@ namespace IRaCIS.Api.Controllers
|
||||
|
||||
[HttpGet, Route("ip")]
|
||||
[AllowAnonymous]
|
||||
public IResponseOutput Get([FromServices] IHttpContextAccessor _context)
|
||||
public IResponseOutput Get([FromServices] IHttpContextAccessor _context/*, [FromServices] IUserService _userService*/)
|
||||
{
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
@@ -531,7 +316,7 @@ namespace IRaCIS.Api.Controllers
|
||||
|
||||
[HttpGet, Route("ip2")]
|
||||
[AllowAnonymous]
|
||||
public IResponseOutput Get2([FromServices] IHttpContextAccessor _context)
|
||||
public IResponseOutput Get2([FromServices] IHttpContextAccessor _context, [FromServices] IRepository _userService)
|
||||
{
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
@@ -19,12 +19,18 @@ namespace IRaCIS.Core.API.Controllers.Special
|
||||
//谨慎修改 涉及到财务模块
|
||||
|
||||
[ApiController, Authorize, ApiExplorerSettings(GroupName = "Financial")]
|
||||
public class FinancialChangeController(
|
||||
ITrialService _trialService,
|
||||
ICalculateService _calculateService,
|
||||
IStringLocalizer _localizer) : ControllerBase
|
||||
public class FinancialChangeController : ControllerBase
|
||||
{
|
||||
|
||||
private readonly ITrialService _trialService;
|
||||
|
||||
private IStringLocalizer _localizer { get; set; }
|
||||
public FinancialChangeController(ITrialService trialService, IStringLocalizer localizer
|
||||
)
|
||||
{
|
||||
_localizer = localizer;
|
||||
_trialService = trialService;
|
||||
|
||||
}
|
||||
|
||||
|
||||
//[TrialAudit(AuditType.TrialAudit, AuditOptType.AddOrUpdateTrial)]
|
||||
@@ -54,268 +60,28 @@ namespace IRaCIS.Core.API.Controllers.Special
|
||||
var userId = Guid.Parse(User.FindFirst("id").Value);
|
||||
var result = await _trialService.AddOrUpdateTrial(param);
|
||||
|
||||
if (_trialService.TrialExpeditedChange)
|
||||
{
|
||||
var needCalReviewerIds = await _trialService.GetTrialEnrollmentReviewerIds(param.Id.Value);
|
||||
var calcList = await _calculateService.GetNeedCalculateReviewerList(Guid.Empty, string.Empty);
|
||||
|
||||
calcList.ForEach(t =>
|
||||
{
|
||||
if (needCalReviewerIds.Contains(t.DoctorId))
|
||||
{
|
||||
_calculateService.CalculateMonthlyPayment(new CalculateDoctorAndMonthDTO()
|
||||
{
|
||||
NeedCalculateReviewers = new List<Guid>()
|
||||
{
|
||||
t.DoctorId
|
||||
},
|
||||
CalculateMonth = DateTime.Parse(t.YearMonth)
|
||||
}, User.FindFirst("id").Value);
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 添加或更新工作量[AUTH]
|
||||
/// </summary>
|
||||
/// <param name="_trialWorkloadService"></param>
|
||||
/// <param name="workLoadAddOrUpdateModel"></param>
|
||||
/// <returns></returns>
|
||||
|
||||
[HttpPost, Route("doctorWorkload/workLoadAddOrUpdate")]
|
||||
[TypeFilter(typeof(TrialResourceFilter),Arguments = new object[] { "AfterStopCannNotOpt" })]
|
||||
public async Task<IResponseOutput> WorkLoadAddOrUpdate([FromServices] IDoctorWorkloadService _trialWorkloadService, WorkloadCommand workLoadAddOrUpdateModel)
|
||||
{
|
||||
var userId = Guid.Parse(User.FindFirst("id").Value);
|
||||
var result = await _trialWorkloadService.AddOrUpdateWorkload(workLoadAddOrUpdateModel, userId);
|
||||
if (result.IsSuccess && workLoadAddOrUpdateModel.DataFrom == 2)
|
||||
{
|
||||
await _calculateService.CalculateMonthlyPayment(new CalculateDoctorAndMonthDTO()
|
||||
{
|
||||
NeedCalculateReviewers = new List<Guid>()
|
||||
{
|
||||
workLoadAddOrUpdateModel.DoctorId
|
||||
},
|
||||
CalculateMonth = workLoadAddOrUpdateModel.WorkTime
|
||||
}, User.FindFirst("id").Value);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
[HttpDelete, Route("doctorWorkload/deleteWorkLoad/{id:guid}/{trialId:guid}")]
|
||||
[TypeFilter(typeof(TrialResourceFilter),Arguments = new object[] { "AfterStopCannNotOpt" })]
|
||||
public async Task<IResponseOutput> DeleteWorkLoad([FromServices] IDoctorWorkloadService _trialWorkloadService, Guid id)
|
||||
{
|
||||
//先判断该工作量的费用是否被锁定,如果被锁定,则不能删除
|
||||
var workload = await _trialWorkloadService.GetWorkloadDetailById(id);
|
||||
var yearMonth = workload.WorkTime.ToString("yyyy-MM");
|
||||
var isLock = await _calculateService.IsLock(workload.DoctorId, yearMonth);
|
||||
|
||||
if (isLock)
|
||||
{
|
||||
//---Expenses have been settled and workload can not be reset.
|
||||
return ResponseOutput.NotOk(_localizer["Financial_ChargeSettled"]);
|
||||
}
|
||||
|
||||
var deleteResult = await _trialWorkloadService.DeleteWorkload(id);
|
||||
if (workload.DataFrom == (int)Domain.Share.WorkLoadFromStatus.FinalConfirm)
|
||||
{
|
||||
await _calculateService.CalculateMonthlyPayment(new CalculateDoctorAndMonthDTO()
|
||||
{
|
||||
NeedCalculateReviewers = new List<Guid>()
|
||||
{
|
||||
workload.DoctorId
|
||||
},
|
||||
CalculateMonth = workload.WorkTime
|
||||
}, User.FindFirst("id").Value);
|
||||
}
|
||||
return deleteResult;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 添加或更新汇率(会触发没有对锁定的费用计算)
|
||||
/// </summary>
|
||||
|
||||
[HttpPost, Route("exchangeRate/addOrUpdateExchangeRate")]
|
||||
public async Task<IResponseOutput> AddOrUpdateExchangeRate([FromServices] IExchangeRateService _exchangeRateService, [FromServices] IPaymentAdjustmentService _costAdjustmentService, ExchangeRateCommand addOrUpdateModel)
|
||||
{
|
||||
var result = await _exchangeRateService.AddOrUpdateExchangeRate(addOrUpdateModel);
|
||||
var calcList = await _calculateService.GetNeedCalculateReviewerList(Guid.Empty, addOrUpdateModel.YearMonth);
|
||||
foreach (var item in calcList)
|
||||
{
|
||||
if (item != null)
|
||||
{
|
||||
await _calculateService.CalculateMonthlyPayment(new CalculateDoctorAndMonthDTO()
|
||||
{
|
||||
NeedCalculateReviewers = new List<Guid>()
|
||||
{
|
||||
item.DoctorId
|
||||
},
|
||||
CalculateMonth = DateTime.Parse(item.YearMonth)
|
||||
}, User.FindFirst("id").Value);
|
||||
}
|
||||
}
|
||||
await _costAdjustmentService.CalculateCNY(addOrUpdateModel.YearMonth, addOrUpdateModel.Rate);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 添加或更新 职称单价[AUTH]
|
||||
/// </summary>
|
||||
|
||||
[HttpPost, Route("rankPrice/addOrUpdateRankPrice")]
|
||||
public async Task<IResponseOutput> AddOrUpdateRankPrice([FromServices] IReviewerPayInfoService _reviewerPayInfoService, [FromServices] IRankPriceService _rankPriceService, RankPriceCommand addOrUpdateModel)
|
||||
{
|
||||
if (addOrUpdateModel.Id != Guid.Empty && addOrUpdateModel.Id != null)
|
||||
{
|
||||
var needCalReviewerIds =await _reviewerPayInfoService.GetReviewerIdByRankId(Guid.Parse(addOrUpdateModel.Id.ToString()));
|
||||
var calcList = await _calculateService.GetNeedCalculateReviewerList(Guid.Empty, string.Empty);
|
||||
|
||||
foreach (var item in calcList)
|
||||
{
|
||||
if (item != null && needCalReviewerIds.Contains(item.DoctorId))
|
||||
{
|
||||
await _calculateService.CalculateMonthlyPayment(new CalculateDoctorAndMonthDTO()
|
||||
{
|
||||
NeedCalculateReviewers = new List<Guid>()
|
||||
{
|
||||
item.DoctorId
|
||||
},
|
||||
CalculateMonth = DateTime.Parse(item.YearMonth)
|
||||
}, User.FindFirst("id").Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
var userId = Guid.Parse(User.FindFirst("id").Value);
|
||||
return await _rankPriceService.AddOrUpdateRankPrice(addOrUpdateModel, userId);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 添加或更新(替换)医生支付展信息[AUTH]
|
||||
/// </summary>
|
||||
|
||||
[HttpPost, Route("reviewerPayInfo/addOrUpdateReviewerPayInfo")]
|
||||
public async Task<IResponseOutput> AddOrUpdateReviewerPayInfo([FromServices] IReviewerPayInfoService _doctorPayInfoService, ReviewerPayInfoCommand addOrUpdateModel)
|
||||
{
|
||||
var userId = Guid.Parse(User.FindFirst("id").Value);
|
||||
var result =await _doctorPayInfoService.AddOrUpdateReviewerPayInfo(addOrUpdateModel, userId);
|
||||
var calcList = await _calculateService.GetNeedCalculateReviewerList(addOrUpdateModel.DoctorId, string.Empty);
|
||||
foreach (var item in calcList)
|
||||
{
|
||||
if (item != null)
|
||||
{
|
||||
await _calculateService.CalculateMonthlyPayment(new CalculateDoctorAndMonthDTO()
|
||||
{
|
||||
NeedCalculateReviewers = new List<Guid>()
|
||||
{
|
||||
item.DoctorId
|
||||
},
|
||||
CalculateMonth = DateTime.Parse(item.YearMonth)
|
||||
}, User.FindFirst("id").Value);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 保存(替换)项目支付价格信息(会触发没有被锁定的费用计算)[AUTH]
|
||||
/// </summary>
|
||||
|
||||
[HttpPost, Route("trialPaymentPrice/addOrUpdateTrialPaymentPrice")]
|
||||
public async Task<IResponseOutput> AddOrUpdateTrialPaymentPrice([FromServices] ITrialPaymentPriceService _trialPaymentPriceService, TrialPaymentPriceCommand addOrUpdateModel)
|
||||
{
|
||||
var userId = Guid.Parse(User.FindFirst("id").Value);
|
||||
var result =await _trialPaymentPriceService.AddOrUpdateTrialPaymentPrice(addOrUpdateModel);
|
||||
var needCalReviewerIds = await _trialService.GetTrialEnrollmentReviewerIds(addOrUpdateModel.TrialId);
|
||||
var calcList = await _calculateService.GetNeedCalculateReviewerList(Guid.Empty, string.Empty);
|
||||
|
||||
|
||||
foreach (var item in calcList)
|
||||
{
|
||||
if (item != null && needCalReviewerIds.Contains(item.DoctorId))
|
||||
{
|
||||
await _calculateService.CalculateMonthlyPayment(new CalculateDoctorAndMonthDTO()
|
||||
{
|
||||
NeedCalculateReviewers = new List<Guid>()
|
||||
{
|
||||
item.DoctorId
|
||||
},
|
||||
CalculateMonth = DateTime.Parse(item.YearMonth)
|
||||
}, User.FindFirst("id").Value);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 批量更新奖励费用[AUTH]
|
||||
/// </summary>
|
||||
|
||||
[HttpPost, Route("volumeReward/addOrUpdatevolumeRewardPriceList")]
|
||||
public async Task<IResponseOutput> AddOrUpdateAwardPriceList([FromServices] IVolumeRewardService _volumeRewardService, IEnumerable<AwardPriceCommand> addOrUpdateModel)
|
||||
{
|
||||
|
||||
var result =await _volumeRewardService.AddOrUpdateVolumeRewardPriceList(addOrUpdateModel);
|
||||
|
||||
var calcList = await _calculateService.GetNeedCalculateReviewerList(Guid.Empty, string.Empty);
|
||||
foreach (var item in calcList)
|
||||
{
|
||||
if (item != null)
|
||||
{
|
||||
await _calculateService.CalculateMonthlyPayment(new CalculateDoctorAndMonthDTO()
|
||||
{
|
||||
NeedCalculateReviewers = new List<Guid>()
|
||||
{
|
||||
item.DoctorId
|
||||
},
|
||||
CalculateMonth = DateTime.Parse(item.YearMonth)
|
||||
}, User.FindFirst("id").Value);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 计算医生月度费用,并将计算的结果存入费用表
|
||||
/// </summary>
|
||||
[HttpPost, Route("financial/calculateMonthlyPayment")]
|
||||
public async Task<IResponseOutput> CalculateMonthlyPayment(CalculateDoctorAndMonthDTO param)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
//---Invalid parameter.
|
||||
return ResponseOutput.NotOk(_localizer["Financial_InvalidParameter"]);
|
||||
}
|
||||
return await _calculateService.CalculateMonthlyPayment(param, User.FindFirst("id").Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Financials /Monthly Payment 列表查询接口
|
||||
/// </summary>
|
||||
[HttpPost, Route("financial/getMonthlyPaymentList")]
|
||||
public async Task<IResponseOutput<PaymentDTO>> GetMonthlyPaymentList([FromServices] IPaymentService _paymentService, [FromServices] IExchangeRateService _exchangeRateService, MonthlyPaymentQueryDTO queryParam)
|
||||
{
|
||||
return ResponseOutput.Ok(new PaymentDTO
|
||||
{
|
||||
CostList = await _paymentService.GetMonthlyPaymentList(queryParam),
|
||||
ExchangeRate = await _exchangeRateService.GetExchangeRateByMonth(queryParam.StatisticsDate.ToString("yyyy-MM"))
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,19 +26,78 @@ namespace IRaCIS.Core.API.Controllers
|
||||
|
||||
[ApiController, ApiExplorerSettings(GroupName = "Reviewer")]
|
||||
[UnitOfWork]
|
||||
public class InspectionController(
|
||||
ITrialDocumentService _trialDocumentService,
|
||||
IReadingImageTaskService _iReadingImageTaskService,
|
||||
ITrialConfigService _trialConfigService,
|
||||
IClinicalAnswerService _clinicalAnswerService,
|
||||
IReadingClinicalDataService _readingClinicalDataService,
|
||||
IQCOperationService _qCOperationService,
|
||||
IInspectionService _inspectionService,
|
||||
IReadingMedicalReviewService _readingMedicalReviewService,
|
||||
IReadingMedicineQuestionService _readingMedicineQuestionService
|
||||
) : ControllerBase
|
||||
public class InspectionController : ControllerBase
|
||||
{
|
||||
|
||||
private readonly IRepository _repository;
|
||||
private readonly IMapper _mapper;
|
||||
private readonly IUserInfo _userInfo;
|
||||
private readonly ITrialDocumentService _trialDocumentService;
|
||||
private readonly IQCListService _qCListService;
|
||||
private readonly IReadingImageTaskService _iReadingImageTaskService;
|
||||
private readonly IHttpContextAccessor _httpContext;
|
||||
private readonly ITrialConfigService _trialConfigService;
|
||||
private readonly INoneDicomStudyService _noneDicomStudyService;
|
||||
private readonly IClinicalAnswerService _clinicalAnswerService;
|
||||
private readonly ISubjectService _subjectService;
|
||||
private readonly IReadingClinicalDataService _readingClinicalDataService;
|
||||
private readonly ISubjectVisitService _subjectVisitService;
|
||||
private readonly IQCOperationService _qCOperationService;
|
||||
private readonly IClinicalDataService _clinicalDataService;
|
||||
private readonly IVisitPlanService _visitPlanService;
|
||||
|
||||
private readonly IInspectionService _inspectionService;
|
||||
private readonly IReadingMedicalReviewService _readingMedicalReviewService;
|
||||
private readonly IReadingMedicineQuestionService _readingMedicineQuestionService;
|
||||
private readonly IRepository<DataInspection> _dataInspectionRepository;
|
||||
private delegate Task<IResponseOutput> executionFun(dynamic data);
|
||||
|
||||
public InspectionController(IRepository repository,
|
||||
IRepository<DataInspection> _repositoryDataInspection,
|
||||
IMapper mapper, IUserInfo userInfo,
|
||||
ITrialDocumentService trialDocumentService,
|
||||
IRepository<DataInspection> dataInspectionRepository,
|
||||
IQCListService _qCListService,
|
||||
IReadingImageTaskService _iReadingImageTaskService,
|
||||
IHttpContextAccessor httpContext,
|
||||
IInspectionService sinspectionService,
|
||||
IReadingMedicalReviewService readingMedicalReviewService,
|
||||
IReadingMedicineQuestionService readingMedicineQuestionService,
|
||||
ITrialConfigService _trialConfigService,
|
||||
INoneDicomStudyService noneDicomStudyService,
|
||||
IClinicalAnswerService clinicalAnswerService,
|
||||
ISubjectService _subjectService,
|
||||
IReadingClinicalDataService _readingClinicalDataService,
|
||||
ISubjectVisitService subjectVisitService,
|
||||
IQCOperationService qCOperationService,
|
||||
IClinicalDataService clinicalDataService,
|
||||
IVisitPlanService visitPlanService
|
||||
)
|
||||
{
|
||||
this._repository = repository;
|
||||
this._mapper = mapper;
|
||||
this._userInfo = userInfo;
|
||||
this._inspectionService = sinspectionService;
|
||||
this._readingMedicalReviewService = readingMedicalReviewService;
|
||||
this._readingMedicineQuestionService = readingMedicineQuestionService;
|
||||
this._trialDocumentService = trialDocumentService;
|
||||
this._qCListService = _qCListService;
|
||||
this._iReadingImageTaskService = _iReadingImageTaskService;
|
||||
this._httpContext = httpContext;
|
||||
this._trialConfigService = _trialConfigService;
|
||||
this._noneDicomStudyService = noneDicomStudyService;
|
||||
this._clinicalAnswerService = clinicalAnswerService;
|
||||
this._subjectService = _subjectService;
|
||||
this._readingClinicalDataService = _readingClinicalDataService;
|
||||
this._subjectVisitService = subjectVisitService;
|
||||
this._qCOperationService = qCOperationService;
|
||||
this._clinicalDataService = clinicalDataService;
|
||||
this._visitPlanService = visitPlanService;
|
||||
this._dataInspectionRepository = dataInspectionRepository;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#region 获取稽查数据
|
||||
/// <summary>
|
||||
/// 获取稽查数据
|
||||
@@ -249,28 +308,11 @@ namespace IRaCIS.Core.API.Controllers
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// PM签名一致性分析临床数据
|
||||
/// </summary>
|
||||
/// <param name="opt"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost, Route("Inspection/ReadingClinicalData/SignConsistencyAnalysisReadingClinicalData")]
|
||||
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
||||
|
||||
[UnitOfWork]
|
||||
public async Task<IResponseOutput> SignConsistencyAnalysisReadingClinicalData(DataInspectionDto<SignConsistencyAnalysisReadingClinicalDataInDto> opt)
|
||||
{
|
||||
var singid = await _inspectionService.RecordSing(opt.SignInfo);
|
||||
var result = await _readingClinicalDataService.SignConsistencyAnalysisReadingClinicalData(opt.Data);
|
||||
await _inspectionService.CompletedSign(singid, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 提交结构化录入并签名
|
||||
/// </summary>
|
||||
/// <param name="opt"></param>
|
||||
/// <returns></returns>
|
||||
/// <summary>
|
||||
/// 提交结构化录入并签名
|
||||
/// </summary>
|
||||
/// <param name="opt"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost, Route("Inspection/ClinicalAnswer/SubmitClinicalFormAndSign")]
|
||||
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
||||
|
||||
@@ -360,19 +402,6 @@ namespace IRaCIS.Core.API.Controllers
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
[HttpPost, Route("Inspection/configTrialBasicInfo/ConfigTrialPACSInfoConfirm")]
|
||||
[UnitOfWork]
|
||||
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "BeforeOngoingCantOpt" })]
|
||||
public async Task<IResponseOutput> ConfigTrialPACSInfoConfirm(DataInspectionDto<TrialPACSConfig> opt)
|
||||
{
|
||||
opt.Data.IsTrialPACSConfirmed = true;
|
||||
var singid = await _inspectionService.RecordSing(opt.SignInfo);
|
||||
var result = await _trialConfigService.ConfigTrialPACSInfo(opt.Data);
|
||||
await _inspectionService.CompletedSign(singid, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 签名确认
|
||||
/// </summary>
|
||||
@@ -390,30 +419,13 @@ namespace IRaCIS.Core.API.Controllers
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 重置并同步项目阅片标准
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpPost, Route("Inspection/ReadingCriterion/ResetAndAsyncCriterion")]
|
||||
[UnitOfWork]
|
||||
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
||||
|
||||
public async Task<IResponseOutput> ResetAndAsyncCriterion(DataInspectionDto<ResetAndAsyncCriterionInDto> opt)
|
||||
{
|
||||
var singid = await _inspectionService.RecordSing(opt.SignInfo);
|
||||
var result = await _trialConfigService.ResetAndAsyncCriterion(opt.Data);
|
||||
await _inspectionService.CompletedSign(singid, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// CRC RequestToQC 批量提交
|
||||
/// </summary>
|
||||
/// <param name="opt"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost, Route("Inspection/QCOperation/CRCRequestToQC")]
|
||||
/// <summary>
|
||||
/// CRC RequestToQC 批量提交
|
||||
/// </summary>
|
||||
/// <param name="opt"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost, Route("Inspection/QCOperation/CRCRequestToQC")]
|
||||
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
||||
[UnitOfWork]
|
||||
public async Task<IResponseOutput> CRCRequestToQC(DataInspectionDto<CRCRequestToQCCommand> opt)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using AutoMapper;
|
||||
using DocumentFormat.OpenXml.Drawing;
|
||||
using EasyCaching.Core;
|
||||
using ExcelDataReader;
|
||||
using IRaCIS.Application.Contracts;
|
||||
using IRaCIS.Application.Interfaces;
|
||||
@@ -9,7 +10,8 @@ using IRaCIS.Core.Application.Contracts.Dicom;
|
||||
using IRaCIS.Core.Application.Contracts.Dicom.DTO;
|
||||
using IRaCIS.Core.Application.Filter;
|
||||
using IRaCIS.Core.Application.Helper;
|
||||
using IRaCIS.Core.Application.MassTransit.Command;
|
||||
using IRaCIS.Core.Application.MediatR.CommandAndQueries;
|
||||
using IRaCIS.Core.Application.MediatR.Handlers;
|
||||
using IRaCIS.Core.Application.Service;
|
||||
using IRaCIS.Core.Application.Service.ImageAndDoc;
|
||||
using IRaCIS.Core.Application.Service.Reading.Dto;
|
||||
@@ -18,8 +20,9 @@ using IRaCIS.Core.Domain.Share;
|
||||
using IRaCIS.Core.Infra.EFCore;
|
||||
using IRaCIS.Core.Infrastructure;
|
||||
using IRaCIS.Core.Infrastructure.Extention;
|
||||
using Magicodes.ExporterAndImporter.Excel;
|
||||
using MassTransit;
|
||||
using MassTransit.Mediator;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Cors.Infrastructure;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
@@ -30,12 +33,12 @@ using Microsoft.AspNetCore.SignalR;
|
||||
using Microsoft.AspNetCore.StaticFiles;
|
||||
using Microsoft.AspNetCore.WebUtilities;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
using MiniExcelLibs;
|
||||
using Newtonsoft.Json;
|
||||
using SharpCompress.Archives;
|
||||
using SkiaSharp;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
@@ -44,7 +47,6 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using ZiggyCreatures.Caching.Fusion;
|
||||
using Path = System.IO.Path;
|
||||
|
||||
namespace IRaCIS.Core.API.Controllers
|
||||
@@ -227,15 +229,31 @@ namespace IRaCIS.Core.API.Controllers
|
||||
|
||||
[ApiExplorerSettings(GroupName = "Image")]
|
||||
[ApiController]
|
||||
public class StudyController(
|
||||
IMediator _mediator,
|
||||
QCCommon _qCCommon,
|
||||
IUserInfo _userInfo,
|
||||
IRepository<SubjectVisit> _subjectVisitRepository,
|
||||
IStringLocalizer _localizer) : UploadBaseController
|
||||
public class StudyController : UploadBaseController
|
||||
{
|
||||
|
||||
public IMapper _mapper { get; set; }
|
||||
public IUserInfo _userInfo { get; set; }
|
||||
private readonly IMediator _mediator;
|
||||
|
||||
private readonly IWebHostEnvironment _hostEnvironment;
|
||||
|
||||
private readonly IRepository _repository;
|
||||
|
||||
private readonly IEasyCachingProvider _provider;
|
||||
private readonly QCCommon _qCCommon;
|
||||
public StudyController(IMapper mapper, IUserInfo userInfo, IWebHostEnvironment hostEnvironment, IMediator mediator, IEasyCachingProvider provider,
|
||||
QCCommon qCCommon,
|
||||
IRepository repository)
|
||||
{
|
||||
_qCCommon = qCCommon;
|
||||
_provider = provider;
|
||||
_hostEnvironment = hostEnvironment;
|
||||
_mediator = mediator;
|
||||
|
||||
_mapper = mapper;
|
||||
_userInfo = userInfo;
|
||||
_repository = repository;
|
||||
}
|
||||
|
||||
[HttpPost, Route("Study/PreArchiveStudy")]
|
||||
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
||||
@@ -244,11 +262,17 @@ namespace IRaCIS.Core.API.Controllers
|
||||
[FromServices] IRepository<StudyMonitor> _studyMonitorRepository)
|
||||
{
|
||||
|
||||
if (_provider.Get<List<SystemAnonymization>>(StaticData.Anonymize.Anonymize_AddFixedFiled).Value == null)
|
||||
{
|
||||
await _mediator.Send(new AnonymizeCacheRequest());
|
||||
}
|
||||
|
||||
var savedInfo = _studyService.GetSaveToDicomInfo(preArchiveStudyCommand.SubjectVisitId);
|
||||
|
||||
var studyMonitor = new StudyMonitor()
|
||||
{
|
||||
TrialId = savedInfo.TrialId,
|
||||
SiteId = savedInfo.SiteId,
|
||||
SubjectId = savedInfo.SubjectId,
|
||||
SubjectVisitId = savedInfo.SubjectVisitId,
|
||||
|
||||
@@ -271,8 +295,9 @@ namespace IRaCIS.Core.API.Controllers
|
||||
[DisableFormValueModelBinding]
|
||||
[DisableRequestSizeLimit]
|
||||
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
||||
public async Task<IResponseOutput> ArchiveStudyNew( Guid trialId, Guid subjectVisitId, string studyInstanceUid, Guid? abandonStudyId, Guid studyMonitorId,
|
||||
public async Task<IResponseOutput> ArchiveStudyNew(/*[FromForm] ArchiveStudyCommand archiveStudyCommand,*/ Guid trialId, Guid subjectVisitId, string studyInstanceUid, Guid? abandonStudyId, Guid studyMonitorId,
|
||||
[FromServices] ILogger<UploadDownLoadController> _logger,
|
||||
[FromServices] IEasyCachingProvider _provider,
|
||||
[FromServices] IStudyService _studyService,
|
||||
[FromServices] IHubContext<UploadHub, IUploadClient> _uploadHub,
|
||||
[FromServices] IDicomArchiveService _dicomArchiveService,
|
||||
@@ -287,7 +312,7 @@ namespace IRaCIS.Core.API.Controllers
|
||||
string.IsNullOrEmpty(mediaTypeHeader.Boundary.Value))
|
||||
{
|
||||
//---不支持的MediaType
|
||||
return ResponseOutput.NotOk(_localizer["UploadDownLoad_UnsupportedMedia"]);
|
||||
return ResponseOutput.NotOk(StaticData.International("UploadDownLoad_UnsupportedMedia"));
|
||||
}
|
||||
|
||||
var archiveStudyCommand = new ArchiveStudyCommand() { AbandonStudyId = abandonStudyId, StudyInstanceUid = studyInstanceUid, SubjectVisitId = subjectVisitId };
|
||||
@@ -297,6 +322,22 @@ namespace IRaCIS.Core.API.Controllers
|
||||
var startTime = DateTime.Now;
|
||||
|
||||
|
||||
if (_provider.Exists($"StudyUid_{trialId}_{archiveStudyCommand.StudyInstanceUid}") && _provider.Get<Guid>($"StudyUid_{trialId}_{studyInstanceUid}").Value == _userInfo.Id)
|
||||
{
|
||||
_provider.Set($"StudyUid_{trialId}_{archiveStudyCommand.StudyInstanceUid}", _userInfo.Id, TimeSpan.FromMinutes(30));
|
||||
}
|
||||
|
||||
|
||||
|
||||
//if (_provider.Exists($"StudyUid_{trialId}_{archiveStudyCommand.StudyInstanceUid}"))
|
||||
//{
|
||||
// //---当前已有人正在上传和归档该检查!
|
||||
// return ResponseOutput.NotOk(StaticData.International("UploadDownLoad_ArchiveInProgress"));
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
// _provider.Set($"StudyUid_{trialId}_{archiveStudyCommand.StudyInstanceUid}", _userInfo.Id, TimeSpan.FromMinutes(30));
|
||||
//}
|
||||
|
||||
//到了接口,代表上传结束了
|
||||
|
||||
@@ -360,14 +401,15 @@ namespace IRaCIS.Core.API.Controllers
|
||||
|
||||
|
||||
}
|
||||
catch (Exception)
|
||||
catch (Exception ex)
|
||||
{
|
||||
_provider.Remove($"StudyUid_{trialId}_{archiveStudyCommand.StudyInstanceUid}");
|
||||
|
||||
//---请求异常,请重试!
|
||||
throw new BusinessValidationFailedException(_localizer["UploadDownLoad_RequestError"]);
|
||||
throw new BusinessValidationFailedException(StaticData.International("UploadDownLoad_RequestError"));
|
||||
}
|
||||
|
||||
studyMonitor.FileSize = (long)HttpContext.Request.ContentLength;
|
||||
studyMonitor.FileSize = (decimal)HttpContext.Request.ContentLength;
|
||||
studyMonitor.FileCount = archiveResult.ReceivedFileCount;
|
||||
studyMonitor.FailedFileCount = archiveResult.ErrorFiles.Count;
|
||||
studyMonitor.IsDicomReUpload = archiveStudyCommand.AbandonStudyId != null;
|
||||
@@ -400,6 +442,7 @@ namespace IRaCIS.Core.API.Controllers
|
||||
}
|
||||
finally
|
||||
{
|
||||
_provider.Remove($"StudyUid_{trialId}_{archiveStudyCommand.StudyInstanceUid}");
|
||||
|
||||
studyMonitor.StudyId = archiveResult.ArchivedDicomStudies.FirstOrDefault()?.Id ?? Guid.Empty;
|
||||
studyMonitor.StudyCode = archiveResult.ArchivedDicomStudies.FirstOrDefault()?.StudyCode;
|
||||
@@ -417,6 +460,248 @@ namespace IRaCIS.Core.API.Controllers
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#region 废弃
|
||||
|
||||
|
||||
|
||||
///// <summary>
|
||||
///// 上传临床数据 多文件
|
||||
///// </summary>
|
||||
///// <param name="subjectVisitId"></param>
|
||||
///// <returns></returns>
|
||||
//[HttpPost("ClinicalData/UploadVisitClinicalData/{trialId:guid}/{subjectVisitId:guid}")]
|
||||
//[DisableRequestSizeLimit]
|
||||
////[Authorize(Policy = IRaCISPolicy.CRC)]
|
||||
//[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
||||
|
||||
//public async Task<IResponseOutput> UploadVisitClinicalData(Guid subjectVisitId)
|
||||
//{
|
||||
// await _qCCommon.VerifyIsCRCSubmmitAsync(_repository, _userInfo, subjectVisitId);
|
||||
// var sv = _repository.Where<SubjectVisit>(t => t.Id == subjectVisitId).Select(t => new { t.TrialId, t.SiteId, t.SubjectId }).FirstOrDefault().IfNullThrowException();
|
||||
|
||||
// await FileUploadAsync(async (fileName) =>
|
||||
// {
|
||||
// var (serverFilePath, relativePath, fileRealName) = FileStoreHelper.GetClinicalDataPath(_hostEnvironment, fileName, sv.TrialId, sv.SiteId, sv.SubjectId, subjectVisitId);
|
||||
// //插入临床pdf 路径
|
||||
// await _repository.AddAsync(new PreviousPDF()
|
||||
// {
|
||||
// SubjectVisitId = subjectVisitId,
|
||||
|
||||
// IsVisist = true,
|
||||
// DataType = ClinicalDataType.MedicalHistory,
|
||||
// UploadType = ClinicalUploadType.PDF,
|
||||
// SubjectId = sv.SubjectId,
|
||||
// TrialId = sv.TrialId,
|
||||
// ClinicalLevel = ClinicalLevel.Subject,
|
||||
// Path = relativePath,
|
||||
// FileName = fileRealName
|
||||
// });
|
||||
// return serverFilePath;
|
||||
// });
|
||||
// await _repository.SaveChangesAsync();
|
||||
// return ResponseOutput.Ok();
|
||||
|
||||
//}
|
||||
|
||||
///// <summary>
|
||||
///// 上传临床数据模板
|
||||
///// </summary>
|
||||
///// <param name="trialId"></param>
|
||||
///// <returns></returns>
|
||||
//[HttpPost("ClinicalData/UploadClinicalTemplate")]
|
||||
//[DisableRequestSizeLimit]
|
||||
//[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "BeforeOngoingCantOpt", "AfterStopCannNotOpt" })]
|
||||
|
||||
|
||||
//public async Task<IResponseOutput<List<FileDto>>> UploadClinicalTemplate(Guid? trialId)
|
||||
//{
|
||||
// if (trialId == null)
|
||||
// trialId = default(Guid);
|
||||
|
||||
// var filerelativePath = string.Empty;
|
||||
// List<FileDto> fileDtos = new List<FileDto>();
|
||||
// await FileUploadAsync(async (fileName) =>
|
||||
// {
|
||||
// var (serverFilePath, relativePath, fileRealName) = FileStoreHelper.GetClinicalTemplatePath(_hostEnvironment, fileName, trialId.Value);
|
||||
// //插入临床pdf 路径
|
||||
// filerelativePath = relativePath;
|
||||
|
||||
// fileDtos.Add(new FileDto()
|
||||
// {
|
||||
// FileName = fileName,
|
||||
// Path = relativePath
|
||||
|
||||
// });
|
||||
// await Task.CompletedTask;
|
||||
// return serverFilePath;
|
||||
// });
|
||||
|
||||
// return ResponseOutput.Ok(fileDtos);
|
||||
//}
|
||||
|
||||
|
||||
|
||||
///// <summary>
|
||||
///// 上传阅片临床数据
|
||||
///// </summary>
|
||||
///// <param name="trialId"></param>
|
||||
///// <param name="subjectId"></param>
|
||||
///// <param name="readingId"></param>
|
||||
///// <returns></returns>
|
||||
//[HttpPost("ClinicalData/UploadClinicalData/{trialId:guid}/{subjectId:guid}/{readingId:guid}")]
|
||||
//[DisableRequestSizeLimit]
|
||||
//[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
||||
|
||||
//public async Task<IResponseOutput<List<FileDto>>> UploadReadClinicalData(Guid trialId, Guid subjectId, Guid readingId)
|
||||
//{
|
||||
// var filerelativePath = string.Empty;
|
||||
// List<FileDto> fileDtos = new List<FileDto>();
|
||||
|
||||
// var siteid = await _repository.Where<Subject>(x => x.Id == subjectId).Select(x => x.SiteId).FirstOrDefaultAsync();
|
||||
|
||||
// await FileUploadAsync(async (fileName) =>
|
||||
// {
|
||||
// var (serverFilePath, relativePath, fileRealName) = FileStoreHelper.GetReadClinicalDataPath(_hostEnvironment, fileName, trialId, siteid, subjectId, readingId);
|
||||
// //插入临床pdf 路径
|
||||
// filerelativePath = relativePath;
|
||||
|
||||
// fileDtos.Add(new FileDto()
|
||||
// {
|
||||
// FileName = fileName,
|
||||
// Path = relativePath
|
||||
|
||||
// });
|
||||
// await Task.CompletedTask;
|
||||
// return serverFilePath;
|
||||
// });
|
||||
|
||||
// return ResponseOutput.Ok(fileDtos);
|
||||
|
||||
//}
|
||||
|
||||
|
||||
///// <summary>
|
||||
///// 上传截图
|
||||
///// </summary>
|
||||
///// <param name="subjectId"></param>
|
||||
///// <returns></returns>
|
||||
//[HttpPost("Printscreen/UploadPrintscreen/{subjectId:guid}")]
|
||||
//[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
||||
//public async Task<IResponseOutput<FileDto>> UploadPrintscreen(Guid subjectId)
|
||||
//{
|
||||
|
||||
// var subjectInfo = await this._repository.Where<Subject>(x => x.Id == subjectId).FirstNotNullAsync();
|
||||
|
||||
// FileDto fileDto = new FileDto();
|
||||
// await FileUploadAsync(async (fileName) =>
|
||||
// {
|
||||
// var (serverFilePath, relativePath, fileRealName) = FileStoreHelper.GetUploadPrintscreenFilePath(_hostEnvironment, fileName, subjectInfo.TrialId, subjectInfo.SiteId, subjectInfo.Id);
|
||||
// fileDto.Path = relativePath;
|
||||
// fileDto.FileName = fileName;
|
||||
// await Task.CompletedTask;
|
||||
// return serverFilePath;
|
||||
// });
|
||||
|
||||
|
||||
// return ResponseOutput.Ok(fileDto);
|
||||
//}
|
||||
|
||||
|
||||
///// <summary>
|
||||
///// 上传Reading问题的图像
|
||||
///// </summary>
|
||||
///// <param name="trialId"></param>
|
||||
///// <param name="visitTaskId"></param>
|
||||
///// <returns></returns>
|
||||
//[HttpPost("VisitTask/UploadReadingAnswerImage/{trialId:guid}/{visitTaskId:guid}")]
|
||||
//[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
||||
//public async Task<IResponseOutput<FileDto>> UploadReadingAnswerImage(Guid trialId, Guid visitTaskId)
|
||||
//{
|
||||
|
||||
// FileDto fileDto = new FileDto();
|
||||
// await FileUploadAsync(async (fileName) =>
|
||||
// {
|
||||
// var (serverFilePath, relativePath, fileRealName) = FileStoreHelper.GetFilePath(_hostEnvironment, fileName, trialId, visitTaskId, StaticData.Folder.JudgeTask);
|
||||
// fileDto.Path = relativePath;
|
||||
// fileDto.FileName = fileName;
|
||||
// await Task.CompletedTask;
|
||||
// return serverFilePath;
|
||||
// });
|
||||
|
||||
|
||||
// return ResponseOutput.Ok(fileDto);
|
||||
//}
|
||||
|
||||
///// <summary>
|
||||
///// 上传裁判任务的图像
|
||||
///// </summary>
|
||||
///// <param name="trialId"></param>
|
||||
///// <param name="visitTaskId"></param>
|
||||
///// <returns></returns>
|
||||
//[HttpPost("VisitTask/UploadJudgeTaskImage/{trialId:guid}/{visitTaskId:guid}")]
|
||||
//[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
||||
//public async Task<IResponseOutput<FileDto>> UploadJudgeTaskImage(Guid trialId, Guid visitTaskId)
|
||||
//{
|
||||
|
||||
// FileDto fileDto = new FileDto();
|
||||
// await FileUploadAsync(async (fileName) =>
|
||||
// {
|
||||
// var (serverFilePath, relativePath, fileRealName) = FileStoreHelper.GetFilePath(_hostEnvironment, fileName, trialId, visitTaskId, StaticData.Folder.JudgeTask);
|
||||
// fileDto.Path = relativePath;
|
||||
// fileDto.FileName = fileName;
|
||||
|
||||
// await Task.CompletedTask;
|
||||
|
||||
// return serverFilePath;
|
||||
// });
|
||||
|
||||
// return ResponseOutput.Ok(fileDto);
|
||||
//}
|
||||
|
||||
|
||||
|
||||
///// <summary>
|
||||
///// 上传医学审核图片
|
||||
///// </summary>
|
||||
///// <param name="trialId"></param>
|
||||
///// <param name="taskMedicalReviewId"></param>
|
||||
///// <returns></returns>
|
||||
//[HttpPost("TaskMedicalReview/UploadMedicalReviewImage/{trialId:guid}/{taskMedicalReviewId:guid}")]
|
||||
//[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
||||
//public async Task<IResponseOutput<FileDto>> UploadMedicalReviewImage(Guid trialId, Guid taskMedicalReviewId)
|
||||
//{
|
||||
// string path = string.Empty;
|
||||
// FileDto fileDto = new FileDto();
|
||||
// await FileUploadAsync(async (fileName) =>
|
||||
// {
|
||||
|
||||
// var (serverFilePath, relativePath, fileRealName) = FileStoreHelper.GetMedicalReviewImage(_hostEnvironment, fileName, trialId, taskMedicalReviewId);
|
||||
|
||||
|
||||
// //await _repository.UpdatePartialFromQueryAsync<TaskMedicalReview>(x => x.Id == taskMedicalReviewId, x => new TaskMedicalReview()
|
||||
// //{
|
||||
// // ImagePath = relativePath,
|
||||
// // FileName = fileName,
|
||||
// //});
|
||||
|
||||
// path = relativePath;
|
||||
// fileDto.Path = relativePath;
|
||||
// fileDto.FileName = fileName;
|
||||
// return serverFilePath;
|
||||
// });
|
||||
|
||||
// await _repository.SaveChangesAsync();
|
||||
// return ResponseOutput.Ok(fileDto);
|
||||
//}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
public class UploadNoneDicomFileCommand
|
||||
{
|
||||
public Guid TrialId { get; set; }
|
||||
@@ -433,26 +718,20 @@ namespace IRaCIS.Core.API.Controllers
|
||||
public string FilePath { get; set; }
|
||||
public string FileName { get; set; }
|
||||
public int FileFize { get; set; }
|
||||
|
||||
public string FileType { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 上传非Dicom 文件 支持压缩包 多文件上传
|
||||
/// </summary>
|
||||
/// <param name="incommand"></param>
|
||||
/// <param name="_noneDicomStudyRepository"></param>
|
||||
/// <param name="_studyMonitorRepository"></param>
|
||||
/// <param name="_noneDicomStudyFileRepository"></param>
|
||||
/// <returns></returns>
|
||||
//[DisableRequestSizeLimit]
|
||||
[HttpPost("NoneDicomStudy/UploadNoneDicomFile")]
|
||||
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
||||
|
||||
//[Authorize(Policy = IRaCISPolicy.CRC)]
|
||||
public async Task<IResponseOutput> UploadNoneDicomFile(UploadNoneDicomFileCommand incommand,
|
||||
[FromServices] IRepository<NoneDicomStudy> _noneDicomStudyRepository,
|
||||
[FromServices] IRepository<StudyMonitor> _studyMonitorRepository,
|
||||
[FromServices] IRepository<NoneDicomStudyFile> _noneDicomStudyFileRepository)
|
||||
[FromServices] IRepository<NoneDicomStudy> _noneDicomStudyRepository, [FromServices] IRepository<StudyMonitor> _studyMonitorRepository)
|
||||
{
|
||||
|
||||
var subjectVisitId = incommand.SubjectVisitId;
|
||||
@@ -460,9 +739,9 @@ namespace IRaCIS.Core.API.Controllers
|
||||
var noneDicomStudyId = incommand.NoneDicomStudyId;
|
||||
|
||||
|
||||
await _qCCommon.VerifyIsCRCSubmmitAsync(_subjectVisitRepository, _userInfo, subjectVisitId);
|
||||
await _qCCommon.VerifyIsCRCSubmmitAsync(_repository, _userInfo, subjectVisitId);
|
||||
|
||||
var sv = (await _subjectVisitRepository.Where(t => t.Id == subjectVisitId).Select(t => new { t.TrialId, t.TrialSiteId, t.SubjectId }).FirstOrDefaultAsync()).IfNullThrowConvertException();
|
||||
var sv = (await _repository.Where<SubjectVisit>(t => t.Id == subjectVisitId).Select(t => new { t.TrialId, t.SiteId, t.SubjectId }).FirstOrDefaultAsync()).IfNullThrowConvertException();
|
||||
|
||||
var studyMonitor = await _studyMonitorRepository.FirstOrDefaultAsync(t => t.Id == studyMonitorId);
|
||||
|
||||
@@ -470,7 +749,7 @@ namespace IRaCIS.Core.API.Controllers
|
||||
|
||||
foreach (var item in incommand.UploadedFileList)
|
||||
{
|
||||
await _noneDicomStudyFileRepository.AddAsync(new NoneDicomStudyFile() { FileName = item.FileName, Path = item.FilePath, NoneDicomStudyId = noneDicomStudyId,FileType=item.FileType,FileSize=item.FileFize });
|
||||
await _repository.AddAsync(new NoneDicomStudyFile() { FileName = item.FileName, Path = item.FilePath, NoneDicomStudyId = noneDicomStudyId });
|
||||
|
||||
}
|
||||
var uploadFinishedTime = DateTime.Now;
|
||||
@@ -487,29 +766,21 @@ namespace IRaCIS.Core.API.Controllers
|
||||
studyMonitor.StudyCode = noneDicomStudy.StudyCode;
|
||||
studyMonitor.ArchiveFinishedTime = DateTime.Now;
|
||||
studyMonitor.IP = _userInfo.IP;
|
||||
studyMonitor.IsSuccess = true;
|
||||
|
||||
await _noneDicomStudyRepository.SaveChangesAsync();
|
||||
await _repository.SaveChangesAsync();
|
||||
|
||||
return ResponseOutput.Ok();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 一致性核查 excel上传 支持三种格式
|
||||
/// </summary>
|
||||
/// <param name="trialId"></param>
|
||||
/// <param name="oSSService"></param>
|
||||
/// <param name="_inspectionFileRepository"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="BusinessValidationFailedException"></exception>
|
||||
[HttpPost("QCOperation/UploadVisitCheckExcel/{trialId:guid}")]
|
||||
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
||||
|
||||
public async Task<IResponseOutput> UploadVisitCheckExcel(Guid trialId, [FromServices] IOSSService oSSService, [FromServices] IRepository<InspectionFile> _inspectionFileRepository)
|
||||
//[Authorize(Policy = IRaCISPolicy.PM_APM)]
|
||||
public async Task<IResponseOutput> UploadVisitCheckExcel(Guid trialId, [FromServices] IOSSService oSSService)
|
||||
{
|
||||
|
||||
var fileName = string.Empty;
|
||||
@@ -519,19 +790,19 @@ namespace IRaCIS.Core.API.Controllers
|
||||
{
|
||||
fileName = realFileName;
|
||||
|
||||
if (!fileName.EndsWith(".xlsx") && !fileName.EndsWith(".xls"))
|
||||
if (!fileName.EndsWith(".xlsx") && !fileName.EndsWith(".csv") && !fileName.EndsWith(".xls"))
|
||||
{
|
||||
//---支持.xlsx、.xls格式的文件上传。
|
||||
throw new BusinessValidationFailedException(_localizer["UploadDownLoad_SupportedFormats"]);
|
||||
//---支持.xlsx、.xls、.csv格式的文件上传。
|
||||
throw new BusinessValidationFailedException(StaticData.International("UploadDownLoad_SupportedFormats"));
|
||||
}
|
||||
|
||||
fileStream.CopyTo(templateFileStream);
|
||||
templateFileStream.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
|
||||
var ossRelativePath = await oSSService.UploadToOSSAsync(fileStream, "InspectionUpload/Check", realFileName);
|
||||
var ossRelativePath = await oSSService.UploadToOSSAsync(fileStream, "InspectionUpload/Check", realFileName);
|
||||
|
||||
await _inspectionFileRepository.AddAsync(new InspectionFile() { FileName = realFileName, RelativePath = ossRelativePath, TrialId = trialId });
|
||||
await _repository.AddAsync(new InspectionFile() { FileName = realFileName, RelativePath = ossRelativePath, TrialId = trialId });
|
||||
|
||||
return ossRelativePath;
|
||||
|
||||
@@ -543,22 +814,9 @@ namespace IRaCIS.Core.API.Controllers
|
||||
var etcCheckList = new List<CheckViewModel>();
|
||||
|
||||
|
||||
|
||||
#region MiniExcel 需要自己验证数据格式规范
|
||||
|
||||
|
||||
if (fileName.EndsWith(".xlsx"))
|
||||
{
|
||||
etcCheckList = MiniExcel.Query<CheckViewModel>(templateFileStream, excelType: ExcelType.XLSX).ToList();
|
||||
}
|
||||
else if (fileName.EndsWith(".csv"))
|
||||
{
|
||||
//因为csv 需要加配置文件 不然都是null
|
||||
etcCheckList = MiniExcel.Query<CheckViewModel>(templateFileStream, null, configuration: new MiniExcelLibs.Csv.CsvConfiguration()
|
||||
{
|
||||
StreamReaderFunc = (stream) => new StreamReader(stream, Encoding.GetEncoding("gb2312"))
|
||||
}).ToList();
|
||||
|
||||
}
|
||||
//if (fileName.EndsWith(".csv"))
|
||||
//{
|
||||
// //因为csv 需要加配置文件 不然都是null
|
||||
@@ -574,72 +832,67 @@ namespace IRaCIS.Core.API.Controllers
|
||||
|
||||
#endregion
|
||||
|
||||
#region 升级net8 导入有问题
|
||||
|
||||
|
||||
//Magicodes 支持自定义特性验证
|
||||
// if (fileName.EndsWith(".xlsx"))
|
||||
//{
|
||||
// var Importer = new ExcelImporter();
|
||||
if (fileName.EndsWith(".xlsx"))
|
||||
{
|
||||
var Importer = new ExcelImporter();
|
||||
|
||||
// var import = await Importer.Import<CheckViewModel>(templateFileStream);
|
||||
var import = await Importer.Import<CheckViewModel>(templateFileStream);
|
||||
|
||||
// if (import.Exception != null) return ResponseOutput.NotOk(import.Exception.ToString());
|
||||
if (import.Exception != null) return ResponseOutput.NotOk(import.Exception.ToString());
|
||||
|
||||
// //if (import.RowErrors.Count > 0) return ResponseOutput.NotOk(JsonConvert.SerializeObject(import.RowErrors));
|
||||
//if (import.RowErrors.Count > 0) return ResponseOutput.NotOk(JsonConvert.SerializeObject(import.RowErrors));
|
||||
|
||||
// if (import.TemplateErrors.Count > 0) return ResponseOutput.NotOk(_localizer["UploadDownLoad_TemplateErrors"]);
|
||||
if (import.TemplateErrors.Count > 0) return ResponseOutput.NotOk(JsonConvert.SerializeObject(import.TemplateErrors));
|
||||
|
||||
// etcCheckList = import.Data.ToList();
|
||||
//}
|
||||
//else if (fileName.EndsWith(".csv"))
|
||||
//{
|
||||
// #region 临时方案 MiniExcel读取 然后保存为xlsx 再用 Magicodes验证数据
|
||||
etcCheckList = import.Data.ToList();
|
||||
}
|
||||
else if (fileName.EndsWith(".csv"))
|
||||
{
|
||||
#region 临时方案 MiniExcel读取 然后保存为xlsx 再用 Magicodes验证数据
|
||||
|
||||
// //因为csv 需要加配置文件 不然都是null
|
||||
// etcCheckList = MiniExcel.Query<CheckViewModel>(templateFileStream, null, configuration: new MiniExcelLibs.Csv.CsvConfiguration()
|
||||
// {
|
||||
// StreamReaderFunc = (stream) => new StreamReader(stream, Encoding.GetEncoding("gb2312"))
|
||||
// }).ToList();
|
||||
//因为csv 需要加配置文件 不然都是null
|
||||
etcCheckList = MiniExcel.Query<CheckViewModel>(templateFileStream, null, configuration: new MiniExcelLibs.Csv.CsvConfiguration()
|
||||
{
|
||||
StreamReaderFunc = (stream) => new StreamReader(stream, Encoding.GetEncoding("gb2312"))
|
||||
}).ToList();
|
||||
|
||||
// var (csVToXlsxPath, csVToXlsxRelativePath) = FileStoreHelper.GetTrialCheckFilePath(_hostEnvironment, Path.GetFileNameWithoutExtension(fileName) + ".xlsx", trialId);
|
||||
var (csVToXlsxPath, csVToXlsxRelativePath) = FileStoreHelper.GetTrialCheckFilePath(_hostEnvironment, Path.GetFileNameWithoutExtension(fileName) + ".xlsx", trialId);
|
||||
|
||||
|
||||
// await MiniExcel.SaveAsAsync(csVToXlsxPath, etcCheckList, excelType: ExcelType.XLSX);
|
||||
await MiniExcel.SaveAsAsync(csVToXlsxPath, etcCheckList, excelType: ExcelType.XLSX);
|
||||
|
||||
|
||||
// var Importer = new ExcelImporter();
|
||||
var Importer = new ExcelImporter();
|
||||
|
||||
// var import = await Importer.Import<CheckViewModel>(System.IO.File.OpenRead(csVToXlsxPath));
|
||||
var import = await Importer.Import<CheckViewModel>(System.IO.File.OpenRead(csVToXlsxPath));
|
||||
|
||||
// if (import.Exception != null) return ResponseOutput.NotOk(import.Exception.ToString());
|
||||
if (import.Exception != null) return ResponseOutput.NotOk(import.Exception.ToString());
|
||||
|
||||
// //if (import.RowErrors.Count > 0) return ResponseOutput.NotOk(JsonConvert.SerializeObject(import.RowErrors));
|
||||
//if (import.RowErrors.Count > 0) return ResponseOutput.NotOk(JsonConvert.SerializeObject(import.RowErrors));
|
||||
|
||||
// if (import.TemplateErrors.Count > 0) return ResponseOutput.NotOk(JsonConvert.SerializeObject(import.TemplateErrors));
|
||||
if (import.TemplateErrors.Count > 0) return ResponseOutput.NotOk(JsonConvert.SerializeObject(import.TemplateErrors));
|
||||
|
||||
// etcCheckList = import.Data.ToList();
|
||||
etcCheckList = import.Data.ToList();
|
||||
|
||||
// #endregion
|
||||
#endregion
|
||||
|
||||
|
||||
// #region 导入组件有问题 excel编码格式
|
||||
// //var Importer = new CsvImporter();
|
||||
#region 导入组件有问题 excel编码格式
|
||||
//var Importer = new CsvImporter();
|
||||
|
||||
// //var import = await Importer.Import<CheckViewModel>(File.OpenRead(filePath));
|
||||
//var import = await Importer.Import<CheckViewModel>(File.OpenRead(filePath));
|
||||
|
||||
// //if (import.Exception != null) return ResponseOutput.NotOk(import.Exception.ToString());
|
||||
//if (import.Exception != null) return ResponseOutput.NotOk(import.Exception.ToString());
|
||||
|
||||
// //if (import.RowErrors.Count > 0) return ResponseOutput.NotOk(JsonConvert.SerializeObject(import.RowErrors));
|
||||
//if (import.RowErrors.Count > 0) return ResponseOutput.NotOk(JsonConvert.SerializeObject(import.RowErrors));
|
||||
|
||||
// //if (import.TemplateErrors.Count > 0) return ResponseOutput.NotOk(JsonConvert.SerializeObject(import.TemplateErrors));
|
||||
//if (import.TemplateErrors.Count > 0) return ResponseOutput.NotOk(JsonConvert.SerializeObject(import.TemplateErrors));
|
||||
|
||||
// //etcCheckList = import.Data.ToList();
|
||||
// #endregion
|
||||
//etcCheckList = import.Data.ToList();
|
||||
#endregion
|
||||
|
||||
//}
|
||||
|
||||
#endregion
|
||||
}
|
||||
//ExcelReaderFactory 需要自己验证数据 并且从固定列取数据
|
||||
else
|
||||
{
|
||||
@@ -678,7 +931,7 @@ namespace IRaCIS.Core.API.Controllers
|
||||
if (etcCheckList == null || etcCheckList.Count == 0)
|
||||
{
|
||||
//---请保证上传数据符合模板文件中的样式,且存在有效数据。
|
||||
return ResponseOutput.NotOk(_localizer["UploadDownLoad_InvalidData"]);
|
||||
return ResponseOutput.NotOk(StaticData.International("UploadDownLoad_InvalidData"));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -699,18 +952,13 @@ namespace IRaCIS.Core.API.Controllers
|
||||
if (etcCheckList.Count == 0)
|
||||
{
|
||||
//---请保证上传数据符合模板文件中的样式,且存在有效数据。
|
||||
return ResponseOutput.NotOk(_localizer["UploadDownLoad_InvalidData"]);
|
||||
return ResponseOutput.NotOk(StaticData.International("UploadDownLoad_InvalidData"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 适合获取结果的
|
||||
//var client = _mediator.CreateRequestClient<ConsistenCheckCommand>();
|
||||
//await client.GetResponse<ConsistenCheckResult>(new ConsistenCheckCommand() { ETCList = etcCheckList, TrialId = trialId });
|
||||
|
||||
//不获取结果,不用定义返回类型
|
||||
await _mediator.Send(new ConsistenCheckCommand() { ETCList = etcCheckList, TrialId = trialId });
|
||||
await _mediator.Send(new ConsistencyVerificationRequest() { ETCList = etcCheckList, TrialId = trialId });
|
||||
|
||||
return ResponseOutput.Ok();
|
||||
|
||||
@@ -720,140 +968,8 @@ namespace IRaCIS.Core.API.Controllers
|
||||
|
||||
#endregion
|
||||
|
||||
#region 医生文件上传下载
|
||||
|
||||
|
||||
#region DTO
|
||||
public class DoctorDownloadInfo
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string ReviewerCode { get; set; }
|
||||
|
||||
public List<DownloadFileInfo> FileList { get; set; }
|
||||
}
|
||||
public class DownloadFileInfo
|
||||
{
|
||||
public string FileName { get; set; }
|
||||
|
||||
public string Path { get; set; }
|
||||
}
|
||||
|
||||
public class GetDoctorPathCommand
|
||||
{
|
||||
public int Language { get; set; }
|
||||
|
||||
public List<Guid> DoctorIdList { get; set; }
|
||||
}
|
||||
|
||||
public class GetDoctoreAttachPathCommand
|
||||
{
|
||||
public Guid DoctorId { get; set; }
|
||||
public List<Guid> AttachmentIdList { get; set; }
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
/// <summary>医生文件上传下载</summary>
|
||||
[ApiExplorerSettings(GroupName = "Common")]
|
||||
[ApiController]
|
||||
public class FileController : UploadBaseController
|
||||
{
|
||||
public IMapper _mapper { get; set; }
|
||||
public IUserInfo _userInfo { get; set; }
|
||||
|
||||
|
||||
private readonly IWebHostEnvironment _hostEnvironment;
|
||||
|
||||
private readonly IFileService _fileService;
|
||||
|
||||
|
||||
public FileController(IMapper mapper, IUserInfo userInfo, IWebHostEnvironment hostEnvironment, IFileService fileService)
|
||||
{
|
||||
_fileService = fileService;
|
||||
_hostEnvironment = hostEnvironment;
|
||||
_mapper = mapper;
|
||||
_userInfo = userInfo;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// New 医生首页 多选 获取多个医生信息+文件路径列表 医生压缩包名称 doctorCode + "_" + doctorName _(时间戳或者随机的Guid)
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpPost, Route("file/GetOfficialResume")]
|
||||
public async Task<IResponseOutput<List<DoctorDownloadInfo>>> GetOfficialResume(GetDoctorPathCommand command,
|
||||
[FromServices] IRepository<Attachment> _attachmentrepository)
|
||||
{
|
||||
|
||||
var list = await _attachmentrepository.Where(t => command.DoctorIdList.Contains(t.DoctorId) && command.Language == t.Language).GroupBy(t => new { Name = t.Doctor.FirstName + "_" + t.Doctor.LastName, ReviewerCode = t.Doctor.ReviewerCode, t.DoctorId })
|
||||
.Select(g => new DoctorDownloadInfo()
|
||||
{
|
||||
Id = g.Key.DoctorId,
|
||||
Name = g.Key.Name,
|
||||
ReviewerCode = g.Key.ReviewerCode,
|
||||
FileList = g.Select(t => new DownloadFileInfo() { FileName = t.FileName, Path = t.Path }).ToList()
|
||||
}).ToListAsync();
|
||||
|
||||
return ResponseOutput.Ok(list);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// New 项目入组 勾选获取简历路径
|
||||
/// </summary>
|
||||
/// <param name="command"></param>
|
||||
/// <param name="_doctorService"></param>
|
||||
/// <param name="_attachmentrepository"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost, Route("file/GetTrialDoctorOfficialResume")]
|
||||
public async Task<IResponseOutput<List<DoctorDownloadInfo>>> GetTrialDoctorOfficialResume(GetDoctorPathCommand command,
|
||||
[FromServices] IDoctorService _doctorService,
|
||||
[FromServices] IRepository<Attachment> _attachmentrepository)
|
||||
{
|
||||
|
||||
var list = await _attachmentrepository.Where(t => command.DoctorIdList.Contains(t.DoctorId) && command.Language == t.Language && t.IsOfficial && t.Type.Equals("Resume")).GroupBy(t => new { Name = t.Doctor.FirstName + "_" + t.Doctor.LastName, ReviewerCode = t.Doctor.ReviewerCode, t.DoctorId })
|
||||
.Select(g => new DoctorDownloadInfo()
|
||||
{
|
||||
Id = g.Key.DoctorId,
|
||||
Name = g.Key.Name,
|
||||
ReviewerCode = g.Key.ReviewerCode,
|
||||
FileList = g.Select(t => new DownloadFileInfo() { FileName = t.FileName, Path = t.Path }).ToList()
|
||||
}).ToListAsync();
|
||||
|
||||
return ResponseOutput.Ok(list);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// new 医生详情 勾选或者下载文件路径
|
||||
/// </summary>
|
||||
/// <param name="command"></param>
|
||||
/// <param name="_doctorService"></param>
|
||||
/// <param name="_attachmentrepository"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost, Route("file/GetDoctorAttachment")]
|
||||
public async Task<IResponseOutput<DoctorDownloadInfo>> GetDoctorAttachment(GetDoctoreAttachPathCommand command,
|
||||
[FromServices] IDoctorService _doctorService,
|
||||
[FromServices] IRepository<Attachment> _attachmentrepository)
|
||||
{
|
||||
|
||||
var find = await _attachmentrepository.Where(t => command.DoctorId == t.DoctorId && command.AttachmentIdList.Contains(t.Id)).GroupBy(t => new { Name = t.Doctor.FirstName + "_" + t.Doctor.LastName, ReviewerCode = t.Doctor.ReviewerCode, t.DoctorId })
|
||||
.Select(g => new DoctorDownloadInfo()
|
||||
{
|
||||
Id = g.Key.DoctorId,
|
||||
Name = g.Key.Name,
|
||||
ReviewerCode = g.Key.ReviewerCode,
|
||||
FileList = g.Select(t => new DownloadFileInfo() { FileName = t.FileName, Path = t.Path }).ToList()
|
||||
}).FirstOrDefaultAsync();
|
||||
|
||||
return ResponseOutput.Ok(find);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
#region 项目 系统 基本文件 上传 下载 预览
|
||||
@@ -865,18 +981,25 @@ namespace IRaCIS.Core.API.Controllers
|
||||
public IMapper _mapper { get; set; }
|
||||
public IUserInfo _userInfo { get; set; }
|
||||
private readonly IMediator _mediator;
|
||||
public IStringLocalizer _localizer { get; set; }
|
||||
|
||||
private readonly IWebHostEnvironment _hostEnvironment;
|
||||
|
||||
public UploadDownLoadController(IMapper mapper, IUserInfo userInfo, IStringLocalizer localizer, IMediator mediator, IWebHostEnvironment hostEnvironment)
|
||||
|
||||
|
||||
public UploadDownLoadController(IMapper mapper, IUserInfo userInfo, IMediator mediator, IWebHostEnvironment hostEnvironment)
|
||||
{
|
||||
_hostEnvironment = hostEnvironment;
|
||||
_localizer = localizer;
|
||||
_mediator = mediator;
|
||||
_mapper = mapper;
|
||||
_userInfo = userInfo;
|
||||
}
|
||||
|
||||
[HttpPost, Route("TrialSiteSurvey/TestOOS")]
|
||||
public string TestUploadFileToOOS(string path)
|
||||
{
|
||||
return FileStoreHelper.UploadOOS(path, "testc/test", true);
|
||||
}
|
||||
|
||||
[HttpPost, Route("TrialSiteSurvey/UploadTrialSiteSurveyUser")]
|
||||
[DisableFormValueModelBinding]
|
||||
[UnitOfWork]
|
||||
@@ -898,28 +1021,28 @@ namespace IRaCIS.Core.API.Controllers
|
||||
if (!realFileName.EndsWith(".xlsx", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// 请用提供格式的模板excel上传需要处理的数据
|
||||
throw new BusinessValidationFailedException(_localizer["UploadDownLoad_TemplateUploadData"]);
|
||||
throw new BusinessValidationFailedException(StaticData.International("UploadDownLoad_TemplateUploadData"));
|
||||
}
|
||||
|
||||
var ossRelativePath = await oSSService.UploadToOSSAsync(fileStream, "InspectionUpload/SiteSurvey", realFileName);
|
||||
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()
|
||||
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"]);
|
||||
throw new BusinessValidationFailedException(StaticData.International("UploadDownLoad_EnsureCompleteData"));
|
||||
}
|
||||
|
||||
var siteCodeList = excelList.Select(t => t.TrialSiteCode.Trim().ToUpper()).Distinct().ToList();
|
||||
@@ -927,35 +1050,35 @@ namespace IRaCIS.Core.API.Controllers
|
||||
if (_trialSiteRepository.Where(t => t.TrialId == trialId && siteCodeList.Contains(t.TrialSiteCode.ToUpper())).Count() != siteCodeList.Count)
|
||||
{
|
||||
//在项目中未找到该Excel中部分或全部中心
|
||||
throw new BusinessValidationFailedException(_localizer["UploadDownLoad_InvalidCenters"]);
|
||||
throw new BusinessValidationFailedException(StaticData.International("UploadDownLoad_InvalidCenters"));
|
||||
}
|
||||
|
||||
if (excelList.GroupBy(t => new { t.TrialSiteCode, t.UserTypeStr, t.Email }).Any(g => g.Count() > 1))
|
||||
if (excelList.GroupBy(t => new {t.TrialSiteCode, t.UserTypeStr, t.Email }).Any(g => g.Count() > 1))
|
||||
{
|
||||
// 同一邮箱,同一用户类型,只能生成一个账户,请核查Excel数据
|
||||
|
||||
throw new BusinessValidationFailedException(_localizer["UploadDownLoad_CheckDuplicateAccounts"]);
|
||||
throw new BusinessValidationFailedException(StaticData.International("UploadDownLoad_CheckDuplicateAccounts"));
|
||||
}
|
||||
|
||||
if (excelList.Any(t => !t.Email.Contains("@")))
|
||||
{
|
||||
//有邮箱不符合邮箱格式,请核查Excel数据
|
||||
throw new BusinessValidationFailedException(_localizer["UploadDownLoad_InvalidEmail"]);
|
||||
throw new BusinessValidationFailedException(StaticData.International("UploadDownLoad_InvalidEmail"));
|
||||
}
|
||||
var generateUserTypeList = new List<string>() { "CRC", "CRA" };
|
||||
var generateUserTypeList = new List<string>() { "CRC", "CRA","SR" };
|
||||
|
||||
//if (excelList.Any(t => !generateUserTypeList.Contains(t.UserTypeStr.ToUpper())))
|
||||
//{
|
||||
// //用户类型仅能为 CRC,SR,CRA 请核查Excel数据
|
||||
// throw new BusinessValidationFailedException(_localizer["UploadDownLoad_InvalidUserType"]);
|
||||
//}
|
||||
if (excelList.Any(t => !generateUserTypeList.Contains(t.UserTypeStr.ToUpper())))
|
||||
{
|
||||
//用户类型仅能为 CRC,SR,CRA 请核查Excel数据
|
||||
throw new BusinessValidationFailedException(StaticData.International("UploadDownLoad_InvalidUserType"));
|
||||
}
|
||||
if (excelList.Count == 0)
|
||||
{
|
||||
throw new BusinessValidationFailedException(_localizer["UploadDownLoad_NoValiddata"]);
|
||||
throw new BusinessValidationFailedException(StaticData.International("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();
|
||||
var sysUserTypeList = _usertypeRepository.Where(t => t.UserTypeEnum == UserTypeEnum.CRA || t.UserTypeEnum == UserTypeEnum.ClinicalResearchCoordinator || t.UserTypeEnum == UserTypeEnum.SR).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, t.SiteId }).ToList();
|
||||
|
||||
foreach (var item in excelList)
|
||||
{
|
||||
@@ -972,11 +1095,14 @@ namespace IRaCIS.Core.API.Controllers
|
||||
item.UserTypeEnum = UserTypeEnum.CRA;
|
||||
break;
|
||||
|
||||
|
||||
case "SR":
|
||||
item.UserTypeId = sysUserTypeList.FirstOrDefault(t => t.UserTypeEnum == UserTypeEnum.SR).UserTypeId;
|
||||
item.UserTypeEnum = UserTypeEnum.SR;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
item.TrialSiteId = siteList.FirstOrDefault(t => t.TrialSiteCode.ToUpper() == item.TrialSiteCode.ToUpper()).TrialSiteId;
|
||||
item.SiteId = siteList.FirstOrDefault(t => t.TrialSiteCode.ToUpper() == item.TrialSiteCode.ToUpper()).SiteId;
|
||||
}
|
||||
|
||||
|
||||
@@ -1011,12 +1137,12 @@ namespace IRaCIS.Core.API.Controllers
|
||||
[DisableFormValueModelBinding]
|
||||
public async Task<IResponseOutput> UploadCommonDoc()
|
||||
{
|
||||
|
||||
return await SingleFileUploadAsync((fileName) => FileStoreHelper.GetCommonDocPath(_hostEnvironment, fileName));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public enum UploadFileType
|
||||
{
|
||||
DataUpload = 1,
|
||||
@@ -1070,6 +1196,8 @@ namespace IRaCIS.Core.API.Controllers
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<SignAssembly>false</SignAssembly>
|
||||
<UserSecretsId>354572d4-9e15-4099-807c-63a2d29ff9f2</UserSecretsId>
|
||||
<LangVersion>default</LangVersion>
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<DocumentationFile>.\IRaCIS.Core.API.xml</DocumentationFile>
|
||||
<NoWarn>1701;1702;1591;1570;</NoWarn>
|
||||
<NoWarn>1701;1702;1591;</NoWarn>
|
||||
<OutputPath>..\bin\</OutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -24,7 +24,12 @@
|
||||
<NoWarn>1701;1702;1591</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="wwwroot\**" />
|
||||
<EmbeddedResource Remove="UploadFile\**" />
|
||||
<EmbeddedResource Remove="wwwroot\**" />
|
||||
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Remove="web.config" />
|
||||
@@ -61,23 +66,28 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AlibabaCloud.SDK.Sts20150401" Version="1.1.4" />
|
||||
<PackageReference Include="aliyun-net-sdk-sts" Version="3.1.2" />
|
||||
<PackageReference Include="AspNetCoreRateLimit" Version="5.0.0" />
|
||||
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="10.0.0" />
|
||||
<PackageReference Include="AWSSDK.SecurityToken" Version="3.7.400.13" />
|
||||
<PackageReference Include="ConfigMapFileProvider" Version="2.0.1" />
|
||||
<PackageReference Include="Hangfire.AspNetCore" Version="1.8.14" />
|
||||
<PackageReference Include="Hangfire.Dashboard.BasicAuthorization" Version="1.0.2" />
|
||||
<PackageReference Include="Hangfire.InMemory" Version="0.10.3" />
|
||||
<PackageReference Include="Hangfire.SqlServer" Version="1.8.14" />
|
||||
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="8.0.0" />
|
||||
<PackageReference Include="EasyCaching.Interceptor.Castle" Version="1.9.1" />
|
||||
<PackageReference Include="EasyCaching.Serialization.MessagePack" Version="1.9.1" />
|
||||
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.1" />
|
||||
<PackageReference Include="EasyCaching.InMemory" Version="1.9.1" />
|
||||
<PackageReference Include="Hangfire.AspNetCore" Version="1.8.5" />
|
||||
<PackageReference Include="Hangfire.Dashboard.BasicAuthorization" Version="1.0.2" />
|
||||
<PackageReference Include="Hangfire.SqlServer" Version="1.8.5" />
|
||||
<PackageReference Include="Invio.Extensions.Authentication.JwtBearer" Version="2.0.1" />
|
||||
<PackageReference Include="LogDashboard" Version="1.4.8" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.8" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="8.0.0" />
|
||||
<PackageReference Include="Serilog.AspNetCore" Version="8.0.2" />
|
||||
<PackageReference Include="Serilog.Enrichers.ClientInfo" Version="2.1.1" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="6.7.3" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="6.0.23" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="7.0.1" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.17.2" />
|
||||
<PackageReference Include="Quartz.Extensions.DependencyInjection" Version="3.7.0" />
|
||||
<PackageReference Include="Quartz.Extensions.Hosting" Version="3.7.0" />
|
||||
<PackageReference Include="Serilog.AspNetCore" Version="7.0.0" />
|
||||
<PackageReference Include="Serilog.Enrichers.ClientInfo" Version="2.0.1" />
|
||||
<PackageReference Include="Serilog.Sinks.Email" Version="2.4.0" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="6.5.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -90,6 +100,11 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Update="appsettings.Uat_Study.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
|
||||
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||
</Content>
|
||||
<Content Update="wwwroot\EmailTemplate\AdminAddUser_US.html">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
@@ -150,7 +165,15 @@
|
||||
<Folder Include="Properties\PublishProfiles\" />
|
||||
</ItemGroup>
|
||||
|
||||
<ProjectExtensions><VisualStudio><UserProperties properties_4launchsettings_1json__JsonSchema="" /></VisualStudio></ProjectExtensions>
|
||||
<ProjectExtensions><VisualStudio><UserProperties anonymizetagsetting_1json__JsonSchema="http://json.schemastore.org/jovo-language-model" properties_4launchsettings_1json__JsonSchema="" /></VisualStudio></ProjectExtensions>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="..\.dockerignore" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="..\.dockerignore" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -16,20 +16,7 @@
|
||||
医生基本信息 、工作信息 专业信息、审核状态
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Api.Controllers.ExtraController.GetDoctorDetail(IRaCIS.Application.Interfaces.IAttachmentService,IRaCIS.Application.Interfaces.IDoctorService,IRaCIS.Application.Interfaces.IEducationService,IRaCIS.Application.Interfaces.ITrialExperienceService,IRaCIS.Application.Interfaces.IResearchPublicationService,IRaCIS.Application.Interfaces.IVacationService,System.Guid)">
|
||||
<summary>
|
||||
获取医生详情
|
||||
</summary>
|
||||
<param name="attachmentService"></param>
|
||||
<param name="_doctorService"></param>
|
||||
<param name="_educationService"></param>
|
||||
<param name="_trialExperienceService"></param>
|
||||
<param name="_researchPublicationService"></param>
|
||||
<param name="_vacationService"></param>
|
||||
<param name="doctorId"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Api.Controllers.ExtraController.Login(IRaCIS.Application.Contracts.UserLoginDTO,ZiggyCreatures.Caching.Fusion.IFusionCache,IRaCIS.Application.Services.IUserService,IRaCIS.Core.Application.Auth.ITokenService,IRaCIS.Core.Application.Contracts.IReadingImageTaskService,Microsoft.Extensions.Options.IOptionsMonitor{IRaCIS.Core.Domain.Share.ServiceVerifyConfigOption},IRaCIS.Application.Services.IMailVerificationService)">
|
||||
<member name="M:IRaCIS.Api.Controllers.ExtraController.Login(IRaCIS.Application.Contracts.UserLoginDTO,EasyCaching.Core.IEasyCachingProvider,IRaCIS.Application.Services.IUserService,IRaCIS.Core.Application.Auth.ITokenService,Microsoft.Extensions.Configuration.IConfiguration)">
|
||||
<summary> 系统用户登录接口[New] </summary>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Core.API.Controllers.Special.FinancialChangeController.AddOrUpdateTrialInspection(IRaCIS.Core.Application.Service.Inspection.DTO.DataInspectionDto{IRaCIS.Application.Contracts.TrialCommand})">
|
||||
@@ -41,49 +28,6 @@
|
||||
<param name="param"></param>
|
||||
<returns>新记录Id</returns>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Core.API.Controllers.Special.FinancialChangeController.WorkLoadAddOrUpdate(IRaCIS.Application.Services.IDoctorWorkloadService,IRaCIS.Application.Contracts.WorkloadCommand)">
|
||||
<summary>
|
||||
添加或更新工作量[AUTH]
|
||||
</summary>
|
||||
<param name="_trialWorkloadService"></param>
|
||||
<param name="workLoadAddOrUpdateModel"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Core.API.Controllers.Special.FinancialChangeController.AddOrUpdateExchangeRate(IRaCIS.Application.Interfaces.IExchangeRateService,IRaCIS.Application.Interfaces.IPaymentAdjustmentService,IRaCIS.Application.Contracts.ExchangeRateCommand)">
|
||||
<summary>
|
||||
添加或更新汇率(会触发没有对锁定的费用计算)
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Core.API.Controllers.Special.FinancialChangeController.AddOrUpdateRankPrice(IRaCIS.Application.Interfaces.IReviewerPayInfoService,IRaCIS.Application.Interfaces.IRankPriceService,IRaCIS.Application.Contracts.RankPriceCommand)">
|
||||
<summary>
|
||||
添加或更新 职称单价[AUTH]
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Core.API.Controllers.Special.FinancialChangeController.AddOrUpdateReviewerPayInfo(IRaCIS.Application.Interfaces.IReviewerPayInfoService,IRaCIS.Application.Contracts.ReviewerPayInfoCommand)">
|
||||
<summary>
|
||||
添加或更新(替换)医生支付展信息[AUTH]
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Core.API.Controllers.Special.FinancialChangeController.AddOrUpdateTrialPaymentPrice(IRaCIS.Application.Interfaces.ITrialPaymentPriceService,IRaCIS.Application.Contracts.TrialPaymentPriceCommand)">
|
||||
<summary>
|
||||
保存(替换)项目支付价格信息(会触发没有被锁定的费用计算)[AUTH]
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Core.API.Controllers.Special.FinancialChangeController.AddOrUpdateAwardPriceList(IRaCIS.Application.Interfaces.IVolumeRewardService,System.Collections.Generic.IEnumerable{IRaCIS.Application.Interfaces.AwardPriceCommand})">
|
||||
<summary>
|
||||
批量更新奖励费用[AUTH]
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Core.API.Controllers.Special.FinancialChangeController.CalculateMonthlyPayment(IRaCIS.Application.Contracts.CalculateDoctorAndMonthDTO)">
|
||||
<summary>
|
||||
计算医生月度费用,并将计算的结果存入费用表
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Core.API.Controllers.Special.FinancialChangeController.GetMonthlyPaymentList(IRaCIS.Application.Interfaces.IPaymentService,IRaCIS.Application.Interfaces.IExchangeRateService,IRaCIS.Application.Contracts.MonthlyPaymentQueryDTO)">
|
||||
<summary>
|
||||
Financials /Monthly Payment 列表查询接口
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Core.API.Controllers.InspectionController.GetInspectionList(IRaCIS.Core.Application.Service.Inspection.DTO.GetDataInspectionDto)">
|
||||
<summary>
|
||||
获取稽查数据
|
||||
@@ -167,13 +111,6 @@
|
||||
<param name="opt"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Core.API.Controllers.InspectionController.SignConsistencyAnalysisReadingClinicalData(IRaCIS.Core.Application.Service.Inspection.DTO.DataInspectionDto{IRaCIS.Core.Application.Service.Reading.Dto.SignConsistencyAnalysisReadingClinicalDataInDto})">
|
||||
<summary>
|
||||
PM签名一致性分析临床数据
|
||||
</summary>
|
||||
<param name="opt"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Core.API.Controllers.InspectionController.SubmitClinicalFormAndSign(IRaCIS.Core.Application.Service.Inspection.DTO.DataInspectionDto{IRaCIS.Core.Application.Service.Reading.Dto.SubmitClinicalFormInDto})">
|
||||
<summary>
|
||||
提交结构化录入并签名
|
||||
@@ -215,12 +152,6 @@
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Core.API.Controllers.InspectionController.ResetAndAsyncCriterion(IRaCIS.Core.Application.Service.Inspection.DTO.DataInspectionDto{IRaCIS.Core.Application.Service.Reading.Dto.ResetAndAsyncCriterionInDto})">
|
||||
<summary>
|
||||
重置并同步项目阅片标准
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Core.API.Controllers.InspectionController.CRCRequestToQC(IRaCIS.Core.Application.Service.Inspection.DTO.DataInspectionDto{IRaCIS.Core.Application.Contracts.CRCRequestToQCCommand})">
|
||||
<summary>
|
||||
CRC RequestToQC 批量提交
|
||||
@@ -278,54 +209,21 @@
|
||||
<member name="M:IRaCIS.Core.API.Controllers.UploadBaseController.DicomFileUploadAsync(System.Func{System.String,System.IO.Stream,System.Int32,System.Threading.Tasks.Task},System.String)">
|
||||
<summary> 流式上传 Dicom上传 </summary>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Core.API.Controllers.StudyController.ArchiveStudyNew(System.Guid,System.Guid,System.String,System.Nullable{System.Guid},System.Guid,Microsoft.Extensions.Logging.ILogger{IRaCIS.Core.API.Controllers.UploadDownLoadController},IRaCIS.Core.Application.Contracts.IStudyService,Microsoft.AspNetCore.SignalR.IHubContext{IRaCIS.Core.API.UploadHub,IRaCIS.Core.API.IUploadClient},IRaCIS.Core.Application.Contracts.Dicom.IDicomArchiveService,IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.StudyMonitor})">
|
||||
<member name="M:IRaCIS.Core.API.Controllers.StudyController.ArchiveStudyNew(System.Guid,System.Guid,System.String,System.Nullable{System.Guid},System.Guid,Microsoft.Extensions.Logging.ILogger{IRaCIS.Core.API.Controllers.UploadDownLoadController},EasyCaching.Core.IEasyCachingProvider,IRaCIS.Core.Application.Contracts.IStudyService,Microsoft.AspNetCore.SignalR.IHubContext{IRaCIS.Core.API.UploadHub,IRaCIS.Core.API.IUploadClient},IRaCIS.Core.Application.Contracts.Dicom.IDicomArchiveService,IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.StudyMonitor})">
|
||||
<summary>Dicom 归档</summary>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Core.API.Controllers.StudyController.UploadNoneDicomFile(IRaCIS.Core.API.Controllers.StudyController.UploadNoneDicomFileCommand,IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.NoneDicomStudy},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.StudyMonitor},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.NoneDicomStudyFile})">
|
||||
<member name="M:IRaCIS.Core.API.Controllers.StudyController.UploadNoneDicomFile(IRaCIS.Core.API.Controllers.StudyController.UploadNoneDicomFileCommand,IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.NoneDicomStudy},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.StudyMonitor})">
|
||||
<summary>
|
||||
上传非Dicom 文件 支持压缩包 多文件上传
|
||||
</summary>
|
||||
<param name="incommand"></param>
|
||||
<param name="_noneDicomStudyRepository"></param>
|
||||
<param name="_studyMonitorRepository"></param>
|
||||
<param name="_noneDicomStudyFileRepository"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Core.API.Controllers.StudyController.UploadVisitCheckExcel(System.Guid,IRaCIS.Core.Application.Helper.IOSSService,IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.InspectionFile})">
|
||||
<member name="M:IRaCIS.Core.API.Controllers.StudyController.UploadVisitCheckExcel(System.Guid,IRaCIS.Core.Application.Helper.IOSSService)">
|
||||
<summary>
|
||||
一致性核查 excel上传 支持三种格式
|
||||
</summary>
|
||||
<param name="trialId"></param>
|
||||
<param name="oSSService"></param>
|
||||
<param name="_inspectionFileRepository"></param>
|
||||
<returns></returns>
|
||||
<exception cref="T:IRaCIS.Core.Infrastructure.BusinessValidationFailedException"></exception>
|
||||
</member>
|
||||
<member name="T:IRaCIS.Core.API.Controllers.FileController">
|
||||
<summary>医生文件上传下载</summary>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Core.API.Controllers.FileController.GetOfficialResume(IRaCIS.Core.API.Controllers.GetDoctorPathCommand,IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.Attachment})">
|
||||
<summary>
|
||||
New 医生首页 多选 获取多个医生信息+文件路径列表 医生压缩包名称 doctorCode + "_" + doctorName _(时间戳或者随机的Guid)
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Core.API.Controllers.FileController.GetTrialDoctorOfficialResume(IRaCIS.Core.API.Controllers.GetDoctorPathCommand,IRaCIS.Application.Interfaces.IDoctorService,IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.Attachment})">
|
||||
<summary>
|
||||
New 项目入组 勾选获取简历路径
|
||||
</summary>
|
||||
<param name="command"></param>
|
||||
<param name="_doctorService"></param>
|
||||
<param name="_attachmentrepository"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Core.API.Controllers.FileController.GetDoctorAttachment(IRaCIS.Core.API.Controllers.GetDoctoreAttachPathCommand,IRaCIS.Application.Interfaces.IDoctorService,IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.Attachment})">
|
||||
<summary>
|
||||
new 医生详情 勾选或者下载文件路径
|
||||
</summary>
|
||||
<param name="command"></param>
|
||||
<param name="_doctorService"></param>
|
||||
<param name="_attachmentrepository"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Core.API.Controllers.UploadDownLoadController.DownloadCommonFile(System.String,IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.CommonDocument})">
|
||||
@@ -348,16 +246,6 @@
|
||||
IPLimit限流 启动服务
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:IRaCIS.Core.API.JSONTimeZoneConverter">
|
||||
<summary>
|
||||
序列化,反序列化的时候,处理时间 时区转换
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Core.API.JSONTimeZoneConverter.#ctor(Microsoft.AspNetCore.Http.IHttpContextAccessor)">
|
||||
<summary>
|
||||
序列化,反序列化的时候,处理时间 时区转换
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Core.API.NullToEmptyStringResolver.CreateProperties(System.Type,Newtonsoft.Json.MemberSerialization)">
|
||||
<summary>
|
||||
创建属性
|
||||
@@ -366,10 +254,12 @@
|
||||
<param name="memberSerialization">序列化成员</param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="T:TimeZoneAdjustmentMiddleware">
|
||||
<summary>
|
||||
废弃,没用
|
||||
</summary>
|
||||
<member name="M:IRaCIS.WX.CoreApi.Auth.AuthMiddleware.Invoke(Microsoft.AspNetCore.Http.HttpContext)">
|
||||
<summary>
|
||||
为了前端 一段时间无操作,需要重新登陆
|
||||
</summary>
|
||||
<param name="httpContext"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="T:ZhaoXi._001.NET5Demo.Practice.WebApi.Utility.Jwt.CustomHSJWTService">
|
||||
<summary>
|
||||
|
||||
@@ -1,118 +0,0 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
/// <summary>
|
||||
/// 废弃,没用
|
||||
/// </summary>
|
||||
public class TimeZoneAdjustmentMiddleware
|
||||
{
|
||||
private readonly RequestDelegate _next;
|
||||
|
||||
public TimeZoneAdjustmentMiddleware(RequestDelegate next)
|
||||
{
|
||||
_next = next;
|
||||
}
|
||||
|
||||
public async Task Invoke(HttpContext context)
|
||||
{
|
||||
if (string.IsNullOrEmpty(context.Request.ContentType))
|
||||
{
|
||||
// 请求没有内容体,可能是一个没有请求体的请求,比如 GET 请求
|
||||
await _next(context);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
var timeZoneId = "Asia/Shanghai"; // 客户端默认时区
|
||||
|
||||
var timeZoneIdHeaderValue = context.Request.Headers["TimeZoneId"];
|
||||
|
||||
if (!string.IsNullOrEmpty(timeZoneIdHeaderValue))
|
||||
{
|
||||
timeZoneId = timeZoneIdHeaderValue;
|
||||
}
|
||||
|
||||
var timeZone = TimeZoneInfo.FindSystemTimeZoneById(timeZoneId);
|
||||
|
||||
|
||||
|
||||
// 处理 JSON 请求体中的时间字段
|
||||
if (context.Request.ContentType.StartsWith("application/json"))
|
||||
{
|
||||
var requestBody = await new StreamReader(context.Request.Body).ReadToEndAsync();
|
||||
|
||||
// 使用 JSON.NET 或 System.Text.Json 解析 JSON 请求体
|
||||
// 假设请求体中有一个名为 "dateTime" 的时间字段
|
||||
dynamic jsonData = JsonConvert.DeserializeObject(requestBody);
|
||||
|
||||
if (jsonData.dateTime != null)
|
||||
{
|
||||
if (DateTime.TryParse((string)jsonData.dateTime, out DateTime dateTime))
|
||||
{
|
||||
// 将 JSON 请求体中的时间字段转换为服务器时区的时间
|
||||
var serverTime = TimeZoneInfo.ConvertTime(dateTime, timeZone);
|
||||
jsonData.dateTime = serverTime;
|
||||
}
|
||||
}
|
||||
|
||||
// 将修改后的 JSON 请求体重新写入请求流中
|
||||
var jsonBytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(jsonData));
|
||||
context.Request.Body = new MemoryStream(jsonBytes);
|
||||
context.Request.ContentLength = jsonBytes.Length;
|
||||
}
|
||||
|
||||
|
||||
// 处理 URL 表单参数
|
||||
var modifiedQuery = new Dictionary<string, StringValues>();
|
||||
|
||||
foreach (var key in context.Request.Query.Keys)
|
||||
{
|
||||
if (DateTime.TryParse(context.Request.Query[key], out DateTime dateTime))
|
||||
{
|
||||
// 将 URL 表单参数中的时间转换为服务器时区的时间
|
||||
var serverTime = TimeZoneInfo.ConvertTime(dateTime, timeZone);
|
||||
modifiedQuery[key] = new StringValues(serverTime.ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
modifiedQuery[key] = context.Request.Query[key];
|
||||
}
|
||||
}
|
||||
|
||||
context.Request.Query = new QueryCollection(modifiedQuery);
|
||||
|
||||
// 处理Form请求体中的参数
|
||||
if (context.Request.HasFormContentType)
|
||||
{
|
||||
var modifiedForm = new Dictionary<string, StringValues>();
|
||||
|
||||
foreach (var key in context.Request.Form.Keys)
|
||||
{
|
||||
if (DateTime.TryParse(context.Request.Form[key], out DateTime dateTime))
|
||||
{
|
||||
// 将请求体中的时间转换为服务器时区的时间
|
||||
var serverTime = TimeZoneInfo.ConvertTime(dateTime, timeZone);
|
||||
modifiedForm[key] = new StringValues(serverTime.ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
modifiedForm[key] = context.Request.Form[key];
|
||||
}
|
||||
}
|
||||
|
||||
var newFormCollection = new FormCollection(modifiedForm);
|
||||
|
||||
// 将新的表单集合设置回请求对象
|
||||
context.Request.Form = newFormCollection;
|
||||
}
|
||||
|
||||
await _next(context);
|
||||
}
|
||||
|
||||
}
|
||||
+31
-113
@@ -4,6 +4,8 @@ using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Serilog;
|
||||
using MediatR;
|
||||
using IRaCIS.Core.Application.MediatR.Handlers;
|
||||
using System.Threading.Tasks;
|
||||
using MassTransit;
|
||||
using MassTransit.NewIdProviders;
|
||||
@@ -22,20 +24,10 @@ using IRaCIS.Core.Application.Filter;
|
||||
using Microsoft.AspNetCore.HttpOverrides;
|
||||
using IRaCIS.Application.Services.BackGroundJob;
|
||||
using LogDashboard;
|
||||
using FellowOakDicom.Network;
|
||||
using IRaCIS.Core.Application.Service.ImageAndDoc;
|
||||
using IP2Region.Net.Abstractions;
|
||||
using IP2Region.Net.XDB;
|
||||
using IRaCIS.Core.Application.BusinessFilter;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using IRaCIS.Core.Infrastructure.Extention;
|
||||
using Newtonsoft.Json;
|
||||
using Microsoft.AspNetCore.Diagnostics;
|
||||
using IRaCIS.Core.Application.MassTransit.Command;
|
||||
using IRaCIS.Core.Application.MassTransit.Consumer;
|
||||
using OfficeOpenXml.Utils;
|
||||
|
||||
|
||||
|
||||
AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);
|
||||
AppContext.SetSwitch("Npgsql.DisableDateTimeInfinityConversions", true);
|
||||
#region 获取环境变量
|
||||
//以配置文件为准,否则 从url中取环境值(服务以命令行传递参数启动,配置文件配置了就不需要传递环境参数)
|
||||
var config = new ConfigurationBuilder()
|
||||
@@ -82,8 +74,8 @@ if (urlsIndex > -1)
|
||||
|
||||
NewId.SetProcessIdProvider(new CurrentProcessIdProvider());
|
||||
|
||||
builder.Configuration.AddJsonFile(ConfigMapFileProvider.FromRelativePath(""), "appsettings.json", false, true)
|
||||
.AddJsonFile(ConfigMapFileProvider.FromRelativePath(""), $"appsettings.{enviromentName}.json", false, true);
|
||||
builder.Configuration.AddJsonFile("appsettings.json", false, true)
|
||||
.AddJsonFile($"appsettings.{enviromentName}.json", false, true);
|
||||
|
||||
builder.Host
|
||||
.UseServiceProviderFactory(new AutofacServiceProviderFactory())
|
||||
@@ -110,19 +102,19 @@ builder.Services.AddControllers(options =>
|
||||
options.Filters.Add<ModelActionFilter>();
|
||||
options.Filters.Add<ProjectExceptionFilter>();
|
||||
options.Filters.Add<UnitOfWorkFilter>();
|
||||
options.Filters.Add<EncreptApiResultFilter>(10);
|
||||
options.Filters.Add<LimitUserRequestAuthorization>();
|
||||
|
||||
if (_configuration.GetSection("BasicSystemConfig").GetValue<bool>("OpenLoginLimit"))
|
||||
{
|
||||
options.Filters.Add<LimitUserRequestAuthorization>();
|
||||
}
|
||||
|
||||
})
|
||||
.AddNewtonsoftJsonSetup(builder.Services); // NewtonsoftJson 序列化 处理
|
||||
.AddNewtonsoftJsonSetup(); // NewtonsoftJson 序列化 处理
|
||||
|
||||
builder.Services.AddOptions().Configure<SystemEmailSendConfig>(_configuration.GetSection("SystemEmailSendConfig"));
|
||||
builder.Services.AddOptions().Configure<ServiceVerifyConfigOption>(_configuration.GetSection("BasicSystemConfig"));
|
||||
builder.Services.AddOptions().Configure<AliyunOSSOptions>(_configuration.GetSection("AliyunOSS"));
|
||||
builder.Services.AddOptions().Configure<ObjectStoreServiceOptions>(_configuration.GetSection("ObjectStoreService"));
|
||||
builder.Services.AddOptions().Configure<EncreptResponseOption>(_configuration.GetSection("EncrypteResponseConfig"));
|
||||
builder.Services.AddOptions().Configure<SystemPacsConfig>(_configuration.GetSection("SystemPacsConfig"));
|
||||
|
||||
|
||||
//动态WebApi + UnifiedApiResultFilter 省掉控制器代码
|
||||
@@ -130,7 +122,7 @@ builder.Services.AddDynamicWebApiSetup();
|
||||
//AutoMapper
|
||||
builder.Services.AddAutoMapperSetup();
|
||||
//EF ORM QueryWithNoLock
|
||||
builder.Services.AddEFSetup(_configuration,enviromentName);
|
||||
builder.Services.AddEFSetup(_configuration);
|
||||
//Http 响应压缩
|
||||
builder.Services.AddResponseCompressionSetup();
|
||||
//Swagger Api 文档
|
||||
@@ -139,34 +131,15 @@ builder.Services.AddSwaggerSetup();
|
||||
builder.Services.AddJWTAuthSetup(_configuration);
|
||||
|
||||
// MediatR 进程内消息 事件解耦 从程序集中 注册命令和handler对应关系
|
||||
//builder.Services.AddMediatR(cfg => cfg.RegisterServicesFromAssemblyContaining<ConsistencyVerificationHandler>());
|
||||
|
||||
#region MassTransit
|
||||
//masstransit组件 也支持MediatR 中介者模式,但是支持分布式,考虑后续,所以在次替代MediatR
|
||||
builder.Services.AddMediator(cfg =>
|
||||
{
|
||||
cfg.AddConsumer<ConsistencyCheckConsumer>();
|
||||
});
|
||||
|
||||
builder.Services.AddMassTransit(cfg =>
|
||||
{
|
||||
cfg.UsingInMemory();
|
||||
});
|
||||
#endregion
|
||||
|
||||
|
||||
#region FusionCache
|
||||
|
||||
builder.Services.AddFusionCache();
|
||||
|
||||
#endregion
|
||||
|
||||
builder.Services.AddMediatR(cfg => cfg.RegisterServicesFromAssemblyContaining<ConsistencyVerificationHandler>());
|
||||
// EasyCaching 缓存
|
||||
//builder.Services.AddEasyCachingSetup(_configuration);
|
||||
builder.Services.AddEasyCachingSetup(_configuration);
|
||||
|
||||
// hangfire 定时任务框架 有界面,更友好~
|
||||
builder.Services.AddhangfireSetup(_configuration);
|
||||
|
||||
//
|
||||
builder.Services.AddQuartZSetup(_configuration);
|
||||
|
||||
//Serilog 日志可视化 LogDashboard日志
|
||||
builder.Services.AddLogDashboardSetup();
|
||||
@@ -187,14 +160,12 @@ builder.Services.AddSignalR();
|
||||
|
||||
builder.Services.AddSingleton<IUserIdProvider, IRaCISUserIdProvider>();
|
||||
|
||||
//builder.Services.AddMemoryCache();
|
||||
|
||||
builder.Services.AddSingleton<ISearcher>(new Searcher(CachePolicy.Content, Path.Combine(AppContext.BaseDirectory, StaticData.Folder.Resources, "ip2region.xdb")));
|
||||
|
||||
//builder.Services.AddExceptionHandler<GlobalExceptionHandler>();
|
||||
//builder.Services.AddProblemDetails();
|
||||
|
||||
#region 历史废弃配置
|
||||
//builder.Services.AddMemoryCache();
|
||||
|
||||
////上传限制 配置
|
||||
//builder.Services.Configure<FormOptions>(options =>
|
||||
//{
|
||||
@@ -218,56 +189,6 @@ var env = app.Environment;
|
||||
|
||||
// Configure the HTTP request pipeline.
|
||||
|
||||
#region 异常处理 全局业务异常已统一处理了,非业务错误会来到这里 400 -500状态码
|
||||
|
||||
|
||||
//app.UseStatusCodePagesWithReExecute("/Error/{0}");
|
||||
|
||||
app.UseStatusCodePages(async context =>
|
||||
{
|
||||
var code = context.HttpContext.Response.StatusCode;
|
||||
context.HttpContext.Response.ContentType = "application/json";
|
||||
if (code < 500)
|
||||
{
|
||||
await context.HttpContext.Response.WriteAsync(JsonConvert.SerializeObject(ResponseOutput.NotOk($"Client error, actual request error status code({code})")));
|
||||
}
|
||||
else
|
||||
{
|
||||
//ResultFilter 里面的异常并不会到这里
|
||||
await context.HttpContext.Response.WriteAsync(JsonConvert.SerializeObject((ResponseOutput.NotOk($"Server error , actual request error status code({code})"))));
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
//app.UseExceptionHandler(o => { });
|
||||
|
||||
//这里没生效,原因未知,官方文档也是这种写法,也用了GlobalExceptionHandler 尝试,还是不行,怀疑框架bug
|
||||
//app.UseExceptionHandler(configure =>
|
||||
//{
|
||||
// configure.Run(async context =>
|
||||
// {
|
||||
// var exceptionHandlerPathFeature = context.Features.Get<IExceptionHandlerPathFeature>();
|
||||
//var ex = exceptionHandlerPathFeature?.Error;
|
||||
//context.Response.ContentType = "application/json";
|
||||
|
||||
//if (ex != null)
|
||||
//{
|
||||
// var errorInfo = $"Exception: {ex.Message}[{ex.StackTrace}]" + (ex.InnerException != null ? $" InnerException: {ex.InnerException.Message}[{ex.InnerException.StackTrace}]" : "");
|
||||
|
||||
// await context.Response.WriteAsync(JsonConvert.SerializeObject(ResponseOutput.NotOk($"{ex?.Message}")));
|
||||
|
||||
// Log.Logger.Error(errorInfo);
|
||||
|
||||
|
||||
//}
|
||||
|
||||
// });
|
||||
//});
|
||||
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
//本地化
|
||||
app.UseLocalization();
|
||||
|
||||
@@ -276,11 +197,12 @@ app.UseForwardedHeaders();
|
||||
//响应压缩
|
||||
app.UseResponseCompression();
|
||||
|
||||
//app.UseCors(t => t.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader());
|
||||
|
||||
//不需要 token 访问的静态文件 wwwroot css, JavaScript, and images don't require authentication.
|
||||
app.UseStaticFiles();
|
||||
|
||||
|
||||
//app.UseMiddleware<MultiDiskStaticFilesMiddleware>();
|
||||
|
||||
//LogDashboard
|
||||
app.UseLogDashboard("/LogDashboard");
|
||||
@@ -288,24 +210,22 @@ app.UseLogDashboard("/LogDashboard");
|
||||
//hangfire
|
||||
app.UseHangfireConfig(env);
|
||||
|
||||
#region 暂时废弃
|
||||
|
||||
//app.UseMiddleware<MultiDiskStaticFilesMiddleware>();
|
||||
////限流 中间件
|
||||
//app.UseIpRateLimiting();
|
||||
//if (env.IsDevelopment())
|
||||
//{
|
||||
// app.UseDeveloperExceptionPage();
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
// //app.UseHsts();
|
||||
//}
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
if (env.IsDevelopment())
|
||||
{
|
||||
app.UseDeveloperExceptionPage();
|
||||
}
|
||||
else
|
||||
{
|
||||
//app.UseHsts();
|
||||
}
|
||||
|
||||
// 特殊异常处理 比如 404
|
||||
app.UseStatusCodePagesWithReExecute("/Error/{0}");
|
||||
|
||||
SwaggerSetup.Configure(app, env);
|
||||
|
||||
@@ -338,7 +258,6 @@ var hangfireJobService = app.Services.GetRequiredService<IIRaCISHangfireJob>();
|
||||
await hangfireJobService.InitHangfireJobTaskAsync();
|
||||
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
try
|
||||
@@ -374,7 +293,6 @@ try
|
||||
//Log.Logger.Warning($"ContentRootPath——GetParent:{Directory.GetParent(env.ContentRootPath).Parent.FullName}");
|
||||
//Log.Logger.Warning($"ContentRootPath——xx:{Path.GetDirectoryName(Path.GetDirectoryName(env.ContentRootPath))}");
|
||||
|
||||
|
||||
app.Run();
|
||||
|
||||
}
|
||||
|
||||
@@ -1,75 +1,21 @@
|
||||
{
|
||||
"$schema": "http://json.schemastore.org/launchsettings.json",
|
||||
"iisSettings": {
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:3305",
|
||||
"sslPort": 0
|
||||
}
|
||||
},
|
||||
"profiles": {
|
||||
"IIS Express": {
|
||||
"commandName": "IISExpress",
|
||||
"launchBrowser": true,
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Test_IRC"
|
||||
}
|
||||
},
|
||||
"IRaCIS.Test_IRC": {
|
||||
"Test_Study": {
|
||||
"commandName": "Project",
|
||||
"launchBrowser": true,
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Test_IRC"
|
||||
"ASPNETCORE_ENVIRONMENT": "Test_Study"
|
||||
},
|
||||
"applicationUrl": "http://localhost:6100"
|
||||
},
|
||||
"IRaCIS.Test_IRC_PGSQL": {
|
||||
"Uat_Study": {
|
||||
"commandName": "Project",
|
||||
"launchBrowser": true,
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Test_IRC_PGSQL"
|
||||
},
|
||||
"applicationUrl": "http://localhost:6100"
|
||||
},
|
||||
"IRaCIS.Event_IRC": {
|
||||
"commandName": "Project",
|
||||
"launchBrowser": true,
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Event_IRC"
|
||||
},
|
||||
"applicationUrl": "http://localhost:6100"
|
||||
},
|
||||
"Docker": {
|
||||
"commandName": "Docker",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}",
|
||||
"publishAllPorts": true
|
||||
},
|
||||
"IRaCIS.Uat_IRC": {
|
||||
"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": "Uat_Study"
|
||||
},
|
||||
"applicationUrl": "http://localhost:6100"
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using EasyCaching.Core;
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Cors;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
using System;
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using EasyCaching.Core;
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace IRaCIS.WX.CoreApi.Auth
|
||||
{
|
||||
public class AuthMiddleware
|
||||
{
|
||||
private readonly RequestDelegate _next;
|
||||
private readonly IEasyCachingProvider _provider;
|
||||
public AuthMiddleware(RequestDelegate next, IEasyCachingProvider provider)
|
||||
{
|
||||
_next = next;
|
||||
_provider = provider;
|
||||
}
|
||||
/// <summary>
|
||||
///为了前端 一段时间无操作,需要重新登陆
|
||||
/// </summary>
|
||||
/// <param name="httpContext"></param>
|
||||
/// <returns></returns>
|
||||
public async Task Invoke(HttpContext httpContext)
|
||||
{
|
||||
|
||||
var isLogin = httpContext.Request.Path.ToString().ToLower().Contains("login");
|
||||
|
||||
var result = await httpContext.AuthenticateAsync(JwtBearerDefaults.AuthenticationScheme);
|
||||
|
||||
if (!isLogin)
|
||||
{
|
||||
if (!result.Succeeded)
|
||||
{
|
||||
httpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
|
||||
await httpContext.Response.WriteAsync("Unauthorized");
|
||||
}
|
||||
else
|
||||
{
|
||||
var toekn = result.Properties.Items[".Token.access_token"];
|
||||
var jwtHandler = new JwtSecurityTokenHandler();
|
||||
JwtSecurityToken jwtToken = jwtHandler.ReadJwtToken(toekn);
|
||||
object userId;
|
||||
jwtToken.Payload.TryGetValue("id", out userId);
|
||||
|
||||
var cacheValueExist = await _provider.ExistsAsync(userId.ToString()); //Get<string>(userId.ToString()).ToString();
|
||||
if (!cacheValueExist)
|
||||
{
|
||||
httpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
|
||||
await httpContext.Response.WriteAsync("Unauthorized");
|
||||
}
|
||||
else
|
||||
{
|
||||
await _provider.SetAsync(userId.ToString(), userId.ToString(), TimeSpan.FromMinutes(15));
|
||||
httpContext.User = result.Principal;
|
||||
await _next.Invoke(httpContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
else await _next.Invoke(httpContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
using Hangfire.Dashboard;
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
|
||||
namespace IRaCIS.Core.API.Filter
|
||||
{
|
||||
|
||||
//从cookie 中取值
|
||||
public class hangfireAuthorizationFilter : IDashboardAuthorizationFilter
|
||||
{
|
||||
public bool Authorize(DashboardContext context)
|
||||
{
|
||||
var httpContext = context.GetHttpContext();
|
||||
|
||||
// Allow all authenticated users to see the Dashboard (potentially dangerous).
|
||||
//return httpContext.User.Identity.IsAuthenticated;
|
||||
|
||||
var jwtToken = httpContext.Request.Cookies["access_token"]?.ToString();
|
||||
|
||||
var handler = new JwtSecurityTokenHandler();
|
||||
|
||||
if (handler.CanReadToken(jwtToken))
|
||||
{
|
||||
|
||||
var jwtSecurityToken = handler.ReadJwtToken(jwtToken);
|
||||
|
||||
return jwtSecurityToken.Claims.Any(t => t.Type == JwtIRaCISClaimType.UserTypeEnum && (t.Value == UserTypeEnum.Admin.ToString()|| t.Value== UserTypeEnum.SuperAdmin.ToString()));
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
using Hangfire.Dashboard;
|
||||
|
||||
namespace IRaCIS.Core.API.Filter
|
||||
{
|
||||
public class hangfireAuthorizationFilter : IDashboardAuthorizationFilter
|
||||
{
|
||||
public bool Authorize(DashboardContext context)
|
||||
{
|
||||
var httpContext = context.GetHttpContext();
|
||||
|
||||
// Allow all authenticated users to see the Dashboard (potentially dangerous).
|
||||
return httpContext.User.Identity.IsAuthenticated;
|
||||
|
||||
//return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-12
@@ -1,11 +1,8 @@
|
||||
using Azure;
|
||||
using IRaCIS.Core.Application.Helper;
|
||||
using IRaCIS.Core.Application.Helper;
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using IRaCIS.Core.Infrastructure.Extention;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.StaticFiles;
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
using Microsoft.Extensions.FileProviders.Physical;
|
||||
@@ -14,7 +11,6 @@ using Microsoft.Extensions.Hosting.Internal;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.VisualBasic;
|
||||
using Newtonsoft.Json;
|
||||
using SharpCompress.Common;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
@@ -51,7 +47,7 @@ namespace IRaCIS.Core.API
|
||||
var path = context.Request.Path.Value;
|
||||
var isIRacisFile = path.StartsWith($"/{StaticData.Folder.IRaCISDataFolder}");
|
||||
|
||||
var isDicomFile = isIRacisFile && path.Contains($"{StaticData.Folder.DicomFolder}");
|
||||
var isDicomFile = path.Contains($"{StaticData.Folder.DicomFolder}");
|
||||
|
||||
|
||||
|
||||
@@ -151,12 +147,6 @@ namespace IRaCIS.Core.API
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没有找到文件,返回404
|
||||
context.Response.StatusCode = 404;
|
||||
context.Response.ContentType = "application/json";
|
||||
|
||||
await context.Response.WriteAsync(JsonConvert.SerializeObject(ResponseOutput.NotOk("File not found")));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Localization;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
@@ -13,9 +12,8 @@ namespace IRaCIS.Core.API
|
||||
{
|
||||
var supportedCultures = new List<CultureInfo>
|
||||
{
|
||||
|
||||
new CultureInfo(StaticData.CultureInfo.en_US),
|
||||
new CultureInfo(StaticData.CultureInfo.zh_CN)
|
||||
new CultureInfo("en-US"),
|
||||
new CultureInfo("zh-CN")
|
||||
};
|
||||
|
||||
var options = new RequestLocalizationOptions
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
using Hangfire;
|
||||
using Hangfire.Dashboard;
|
||||
using IRaCIS.Core.API._PipelineExtensions.Serilog;
|
||||
using IRaCIS.Core.API._PipelineExtensions.Serilog;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Serilog;
|
||||
|
||||
@@ -20,20 +20,18 @@ namespace IRaCIS.Core.API
|
||||
// Set all the common properties available for every request
|
||||
diagnosticContext.Set("Host", request.Host);
|
||||
|
||||
diagnosticContext.Set("Protocol", request.Protocol);
|
||||
diagnosticContext.Set("Scheme", request.Scheme);
|
||||
|
||||
#region old 未用
|
||||
//这种获取的Ip不准 配置服务才行
|
||||
//diagnosticContext.Set("RequestIP", httpContext.Connection.RemoteIpAddress.ToString());
|
||||
diagnosticContext.Set("RequestIP", httpContext.Connection.RemoteIpAddress.ToString());
|
||||
|
||||
//这种方式可以,但是serilog提供了 就不用了
|
||||
//diagnosticContext.Set("TestIP", httpContext.GetUserIp());
|
||||
|
||||
diagnosticContext.Set("Protocol", request.Protocol);
|
||||
diagnosticContext.Set("Scheme", request.Scheme);
|
||||
|
||||
//这种方式不行 读取的body为空字符串 必须在中间件中读取
|
||||
//diagnosticContext.Set("RequestBody", await ReadRequestBody(httpContext.Request));
|
||||
//diagnosticContext.Set("RequestBody", RequestPayload);
|
||||
#endregion
|
||||
|
||||
// Only set it if available. You're not sending sensitive data in a querystring right?!
|
||||
if (request.QueryString.HasValue)
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace IRaCIS.Core.API
|
||||
public IStringLocalizer _localizer;
|
||||
public ApiResponseHandler(IOptionsMonitor<AuthenticationSchemeOptions> options,
|
||||
IStringLocalizer localizer,
|
||||
ILoggerFactory logger, UrlEncoder encoder) : base(options, logger, encoder)
|
||||
ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock)
|
||||
{
|
||||
_localizer = localizer;
|
||||
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
using IRaCIS.Core.Application.Auth;
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace IRaCIS.Core.API
|
||||
{
|
||||
public static class AuthorizationPolicySetup
|
||||
{
|
||||
|
||||
public static void AddAuthorizationPolicySetup(this IServiceCollection services, IConfiguration configuration)
|
||||
{
|
||||
services.AddAuthorization(options =>
|
||||
{
|
||||
//影像质控策略 只允许 CRC IQC进行操作
|
||||
options.AddPolicy(IRaCISPolicy.CRC_IQC, policyBuilder =>
|
||||
{
|
||||
policyBuilder.RequireClaim("userTypeEnumInt", ((int)UserTypeEnum.ClinicalResearchCoordinator).ToString(), ((int)UserTypeEnum.IQC).ToString());
|
||||
});
|
||||
|
||||
//一致性核查策略 只允许 CRC PM APM 进行操作
|
||||
options.AddPolicy(IRaCISPolicy.PM_APM_CRC, policyBuilder =>
|
||||
{
|
||||
policyBuilder.RequireClaim("userTypeEnumInt", ((int)UserTypeEnum.ProjectManager).ToString(), ((int)UserTypeEnum.ClinicalResearchCoordinator).ToString(), ((int)UserTypeEnum.APM).ToString());
|
||||
});
|
||||
|
||||
|
||||
options.AddPolicy(IRaCISPolicy.PM_APM, policyBuilder =>
|
||||
{
|
||||
policyBuilder.RequireClaim("userTypeEnumInt", ((int)UserTypeEnum.ProjectManager).ToString(), ((int)UserTypeEnum.APM).ToString());
|
||||
});
|
||||
|
||||
options.AddPolicy(IRaCISPolicy.PM_IQC, policyBuilder =>
|
||||
{
|
||||
policyBuilder.RequireClaim("userTypeEnumInt", ((int)UserTypeEnum.ProjectManager).ToString(), ((int)UserTypeEnum.IQC).ToString());
|
||||
});
|
||||
|
||||
|
||||
options.AddPolicy(IRaCISPolicy.PM, policyBuilder =>
|
||||
{
|
||||
policyBuilder.RequireClaim("userTypeEnumInt", ((int)UserTypeEnum.ProjectManager).ToString());
|
||||
});
|
||||
|
||||
|
||||
options.AddPolicy(IRaCISPolicy.IQC, policyBuilder =>
|
||||
{
|
||||
policyBuilder.RequireClaim("userTypeEnumInt", ((int)UserTypeEnum.IQC).ToString());
|
||||
});
|
||||
|
||||
options.AddPolicy(IRaCISPolicy.CRC, policyBuilder =>
|
||||
{
|
||||
policyBuilder.RequireClaim("userTypeEnumInt", ((int)UserTypeEnum.ClinicalResearchCoordinator).ToString());
|
||||
});
|
||||
|
||||
options.AddPolicy(IRaCISPolicy.PM_APM_CRC_QC, policyBuilder =>
|
||||
{
|
||||
policyBuilder.RequireClaim("userTypeEnumInt", ((int)UserTypeEnum.ProjectManager).ToString(),((int)UserTypeEnum.ClinicalResearchCoordinator).ToString(), ((int)UserTypeEnum.APM).ToString(), ((int)UserTypeEnum.IQC).ToString());
|
||||
});
|
||||
|
||||
options.AddPolicy(IRaCISPolicy.SPM_CPM, policyBuilder =>
|
||||
{
|
||||
policyBuilder.RequireClaim("userTypeEnumInt", ((int)UserTypeEnum.SPM).ToString(), ((int)UserTypeEnum.CPM).ToString());
|
||||
});
|
||||
|
||||
options.AddPolicy(IRaCISPolicy.PM_APM_SPM_CPM, policyBuilder =>
|
||||
{
|
||||
policyBuilder.RequireClaim("userTypeEnumInt", ((int)UserTypeEnum.ProjectManager).ToString(), ((int)UserTypeEnum.APM).ToString(),((int)UserTypeEnum.SPM).ToString(), ((int)UserTypeEnum.CPM).ToString());
|
||||
});
|
||||
|
||||
options.AddPolicy(IRaCISPolicy.PM_APM_SPM_CPM_SMM_CMM, policyBuilder =>
|
||||
{
|
||||
policyBuilder.RequireClaim("userTypeEnumInt", ((int)UserTypeEnum.ProjectManager).ToString(), ((int)UserTypeEnum.APM).ToString(), ((int)UserTypeEnum.SPM).ToString(),
|
||||
((int)UserTypeEnum.CPM).ToString(), ((int)UserTypeEnum.SMM).ToString(), ((int)UserTypeEnum.CMM).ToString());
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
using AutoMapper.EquivalencyExpression;
|
||||
using IRaCIS.Core.Application.Service;
|
||||
using IRaCIS.Core.Domain.Models;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace IRaCIS.Core.API
|
||||
@@ -15,15 +14,11 @@ namespace IRaCIS.Core.API
|
||||
//AutoMapper.Collection.EntityFrameworkCore
|
||||
automapper.AddCollectionMappers();
|
||||
|
||||
|
||||
// 全局忽略 DomainEvents 属性
|
||||
automapper.AddGlobalIgnore(nameof(Entity.DomainEvents));
|
||||
|
||||
#region 会使 IncludeMembers 失效 不能全局使用
|
||||
//mapping an EntityFramework Core DbContext-object.
|
||||
//automapper.UseEntityFrameworkCoreModel<IRaCISDBContext>(services);
|
||||
|
||||
|
||||
|
||||
//automapper.ForAllMaps((a, b) => b.ForAllMembers(opt => opt.Condition((src, dest, srcMember, desMenber) =>
|
||||
//{
|
||||
// //// Can test When Guid? -> Guid if source is null will change to Guid.Empty
|
||||
@@ -32,7 +27,7 @@ namespace IRaCIS.Core.API
|
||||
// // not want to map a null Guid? value to db Guid value
|
||||
//})));
|
||||
#endregion
|
||||
|
||||
|
||||
}, typeof(QCConfig).Assembly);
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Autofac;
|
||||
using Autofac.Extras.DynamicProxy;
|
||||
using IRaCIS.Core.Application;
|
||||
using IRaCIS.Core.Application.BackGroundJob;
|
||||
using IRaCIS.Core.Infra.EFCore;
|
||||
@@ -9,9 +10,11 @@ using System.Linq;
|
||||
using System.Reflection;
|
||||
using IRaCIS.Core.Domain.Models;
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using MediatR;
|
||||
using IRaCIS.Application.Services;
|
||||
using IRaCIS.Application.Interfaces;
|
||||
using AutoMapper;
|
||||
using Quartz;
|
||||
|
||||
namespace IRaCIS.Core.API
|
||||
{
|
||||
@@ -28,6 +31,10 @@ namespace IRaCIS.Core.API
|
||||
|
||||
containerBuilder.RegisterType<Repository>().As<IRepository>().InstancePerLifetimeScope();
|
||||
|
||||
//containerBuilder.RegisterType<Mapper>().As<IMapper>().InstancePerLifetimeScope();
|
||||
|
||||
//containerBuilder.RegisterGeneric(typeof(EFUnitOfWork<>))
|
||||
// .As(typeof(IEFUnitOfWork<>)).InstancePerLifetimeScope();//注册仓储
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -43,21 +50,30 @@ namespace IRaCIS.Core.API
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
//containerBuilder.RegisterType<BaseService>().As<IBaseService>().PropertiesAutowired().InstancePerLifetimeScope();
|
||||
//containerBuilder.RegisterType<DictionaryService>().As<IDictionaryService>().InstancePerLifetimeScope();
|
||||
|
||||
|
||||
|
||||
Assembly application = Assembly.LoadFrom(AppDomain.CurrentDomain.BaseDirectory + "IRaCIS.Core.Application.dll");
|
||||
containerBuilder.RegisterAssemblyTypes(application).Where(t => t.FullName.Contains("Service"))
|
||||
.PropertiesAutowired().AsImplementedInterfaces();
|
||||
.PropertiesAutowired().AsImplementedInterfaces().EnableClassInterceptors();
|
||||
|
||||
//Assembly infrastructure = Assembly.Load("IRaCIS.Core.Infra.EFCore");
|
||||
//containerBuilder.RegisterAssemblyTypes(infrastructure).AsImplementedInterfaces();
|
||||
|
||||
containerBuilder.RegisterType<HttpContextAccessor>().As<IHttpContextAccessor>().SingleInstance();
|
||||
containerBuilder.RegisterType<UserInfo>().As<IUserInfo>().InstancePerLifetimeScope();
|
||||
|
||||
//containerBuilder.RegisterType<Dictionary>().InstancePerLifetimeScope();
|
||||
//Autofac 注册拦截器 需要注意的是生成api上服务上的动态代理AOP失效 间接掉用不影响
|
||||
//containerBuilder.RegisterType<TrialStatusAutofacAOP>();
|
||||
//containerBuilder.RegisterType<UserAddAOP>();
|
||||
//containerBuilder.RegisterType<QANoticeAOP>();
|
||||
//containerBuilder.RegisterType<LogService>().As<ILogService>().SingleInstance();
|
||||
|
||||
|
||||
|
||||
//containerBuilder.RegisterType<HttpContextAccessor>().As<IHttpContextAccessor>().SingleInstance();
|
||||
//containerBuilder.RegisterType<UserInfo>().As<IUserInfo>().InstancePerLifetimeScope();
|
||||
|
||||
|
||||
//注册hangfire任务 依赖注入
|
||||
//注册hangfire任务 依赖注入
|
||||
containerBuilder.RegisterType<ObtainTaskAutoCancelJob>().As<IObtainTaskAutoCancelJob>().InstancePerDependency();
|
||||
|
||||
|
||||
|
||||
@@ -1,65 +1,31 @@
|
||||
using EntityFramework.Exceptions.SqlServer;
|
||||
using Hangfire.SqlServer;
|
||||
using Hangfire.SqlServer;
|
||||
using IRaCIS.Core.Application.Triggers;
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using IRaCIS.Core.Infra.EFCore;
|
||||
using IRaCIS.Core.Infra.EFCore.Interceptor;
|
||||
using Medallion.Threading;
|
||||
using Medallion.Threading.SqlServer;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Diagnostics;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using StackExchange.Redis;
|
||||
|
||||
namespace IRaCIS.Core.API
|
||||
{
|
||||
public static class EFSetup
|
||||
{
|
||||
public static void AddEFSetup( this IServiceCollection services, IConfiguration configuration,string envName)
|
||||
public static void AddEFSetup( this IServiceCollection services, IConfiguration configuration)
|
||||
{
|
||||
|
||||
services.AddHttpContextAccessor();
|
||||
services.AddScoped<IUserInfo, UserInfo>();
|
||||
services.AddScoped<ISaveChangesInterceptor, AuditEntityInterceptor>();
|
||||
services.AddScoped<ISaveChangesInterceptor, DispatchDomainEventsInterceptor>();
|
||||
|
||||
|
||||
// First, register a pooling context factory as a Singleton service, as usual:
|
||||
//services.AddScoped<DbContext, IRaCISDBContext>();
|
||||
|
||||
//这个注入没有成功--注入是没问题的,构造函数也只是支持参数就好,错在注入的地方不能写DbContext
|
||||
//Web程序中通过重用池中DbContext实例可提高高并发场景下的吞吐量, 这在概念上类似于ADO.NET Provider原生的连接池操作方式,具有节省DbContext实例化成本的优点
|
||||
services.AddDbContext<IRaCISDBContext>((sp, options) =>
|
||||
services.AddDbContext<IRaCISDBContext>(options =>
|
||||
{
|
||||
|
||||
// 在控制台
|
||||
//public static readonly ILoggerFactory MyLoggerFactory = LoggerFactory.Create(builder => { builder.AddConsole(); });
|
||||
var logFactory = LoggerFactory.Create(builder => { builder.AddDebug(); });
|
||||
|
||||
var dbType = configuration.GetSection("ConnectionStrings:Db_Type").Value ;
|
||||
if (!string.IsNullOrWhiteSpace(dbType) && dbType == "pgsql")
|
||||
{
|
||||
options.UseNpgsql(configuration.GetSection("ConnectionStrings:RemoteNew").Value, contextOptionsBuilder => contextOptionsBuilder.EnableRetryOnFailure());
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
options.UseSqlServer(configuration.GetSection("ConnectionStrings:RemoteNew").Value, contextOptionsBuilder => contextOptionsBuilder.EnableRetryOnFailure());
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
options.UseLoggerFactory(logFactory);
|
||||
|
||||
options.UseExceptionProcessor();
|
||||
options.UseSqlServer(configuration.GetSection("ConnectionStrings:RemoteNew").Value,
|
||||
contextOptionsBuilder => contextOptionsBuilder.EnableRetryOnFailure());
|
||||
|
||||
options.EnableSensitiveDataLogging();
|
||||
|
||||
options.AddInterceptors(new QueryWithNoLockDbCommandInterceptor());
|
||||
options.AddInterceptors(sp.GetServices<ISaveChangesInterceptor>());
|
||||
|
||||
options.UseProjectables();
|
||||
|
||||
@@ -83,25 +49,13 @@ namespace IRaCIS.Core.API
|
||||
triggerOptions.AddTrigger<TableQuestionRowTrigger>();
|
||||
//triggerOptions.AddTrigger<AddlTrialUserTrigger>();
|
||||
triggerOptions.AddTrigger<VisitTaskIsFrontTaskNeedSignButNotSignTrigger>();
|
||||
triggerOptions.AddTrigger<VisitTaskIbeforeTrigger>();
|
||||
|
||||
triggerOptions.AddTrigger<UserLogTrigger>();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
triggerOptions.AddTrigger<VisitTaskIAfterSignTrigger>();
|
||||
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
// Register an additional context factory as a Scoped service, which gets a pooled context from the Singleton factory we registered above,
|
||||
//services.AddScoped<IRaCISDBScopedFactory>();
|
||||
|
||||
//// Finally, arrange for a context to get injected from our Scoped factory:
|
||||
//services.AddScoped(sp => sp.GetRequiredService<IRaCISDBScopedFactory>().CreateDbContext());
|
||||
|
||||
//注意区分 easy caching 也有 IDistributedLockProvider
|
||||
services.AddSingleton<IDistributedLockProvider>(sp =>
|
||||
{
|
||||
|
||||
@@ -1,24 +1,27 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using EasyCaching.Core;
|
||||
using EasyCaching.Core.Configurations;
|
||||
using EasyCaching.Interceptor.Castle;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace IRaCIS.Core.API
|
||||
{
|
||||
public static class EasyCachingSetup
|
||||
public static class EasyCachingSetup
|
||||
{
|
||||
public static void AddEasyCachingSetup(this IServiceCollection services, IConfiguration configuration)
|
||||
{
|
||||
//services.AddEasyCaching(options =>
|
||||
//{
|
||||
// options.UseInMemory();
|
||||
services.AddEasyCaching(options =>
|
||||
{
|
||||
options.UseInMemory();
|
||||
|
||||
// //options.UseRedis(configuration, EasyCachingConstValue.DefaultRedisName).WithMessagePack(EasyCachingConstValue.DefaultRedisName);
|
||||
//options.UseRedis(configuration, EasyCachingConstValue.DefaultRedisName).WithMessagePack(EasyCachingConstValue.DefaultRedisName);
|
||||
|
||||
|
||||
});
|
||||
|
||||
//});
|
||||
//services.ConfigureCastleInterceptor(options => options.CacheProviderName = EasyCachingConstValue.DefaultRedisName);
|
||||
|
||||
////services.ConfigureCastleInterceptor(options => options.CacheProviderName = EasyCachingConstValue.DefaultRedisName);
|
||||
|
||||
//services.ConfigureCastleInterceptor(options => options.CacheProviderName = EasyCachingConstValue.DefaultInMemoryName);
|
||||
services.ConfigureCastleInterceptor(options => options.CacheProviderName = EasyCachingConstValue.DefaultInMemoryName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,12 +13,14 @@ namespace IRaCIS.Core.API
|
||||
services.AddLogDashboard(opt =>
|
||||
{
|
||||
//opt.PathMatch = "/api/LogDashboard";
|
||||
opt.PathMatch = "/LogDashboard";
|
||||
opt.PathMatch = "/back/logs";
|
||||
|
||||
//opt.AddAuthorizationFilter(new LogDashboardBasicAuthFilter("admin", "zhizhun2018"));
|
||||
|
||||
//opt.AddAuthorizationFilter(new LogDashBoardAuthFilter());
|
||||
|
||||
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
//using System;
|
||||
//using Microsoft.Extensions.DependencyInjection;
|
||||
//using StackExchange.Profiling.Storage;
|
||||
|
||||
//namespace IRaCIS.Core.API
|
||||
//{
|
||||
// public class MiniProfilerConfigure
|
||||
// {
|
||||
// public static void ConfigureMiniProfiler(IServiceCollection services)
|
||||
// {
|
||||
|
||||
// services.AddMiniProfiler(options =>
|
||||
// {
|
||||
// // All of this is optional. You can simply call .AddMiniProfiler() for all defaults
|
||||
|
||||
// // (Optional) Path to use for profiler URLs, default is /mini-profiler-resources
|
||||
// options.RouteBasePath = "/profiler";
|
||||
|
||||
// //// (Optional) Control storage
|
||||
// //// (default is 30 minutes in MemoryCacheStorage)
|
||||
// (options.Storage as MemoryCacheStorage).CacheDuration = TimeSpan.FromMinutes(10);
|
||||
|
||||
// //// (Optional) Control which SQL formatter to use, InlineFormatter is the default
|
||||
// //options.SqlFormatter = new StackExchange.Profiling.SqlFormatters.InlineFormatter();
|
||||
|
||||
// //// (Optional) To control authorization, you can use the Func<HttpRequest, bool> options:
|
||||
// //// (default is everyone can access profilers)
|
||||
// //options.ResultsAuthorize = request => MyGetUserFunction(request).CanSeeMiniProfiler;
|
||||
// //options.ResultsListAuthorize = request => MyGetUserFunction(request).CanSeeMiniProfiler;
|
||||
// //// Or, there are async versions available:
|
||||
// //options.ResultsAuthorizeAsync = async request => (await MyGetUserFunctionAsync(request)).CanSeeMiniProfiler;
|
||||
// //options.ResultsAuthorizeListAsync = async request => (await MyGetUserFunctionAsync(request)).CanSeeMiniProfilerLists;
|
||||
|
||||
// //// (Optional) To control which requests are profiled, use the Func<HttpRequest, bool> option:
|
||||
// //// (default is everything should be profiled)
|
||||
// //options.ShouldProfile = request => MyShouldThisBeProfiledFunction(request);
|
||||
|
||||
// //// (Optional) Profiles are stored under a user ID, function to get it:
|
||||
// //// (default is null, since above methods don't use it by default)
|
||||
// //options.UserIdProvider = request => MyGetUserIdFunction(request);
|
||||
|
||||
// //// (Optional) Swap out the entire profiler provider, if you want
|
||||
// //// (default handles async and works fine for almost all applications)
|
||||
// //options.ProfilerProvider = new MyProfilerProvider();
|
||||
|
||||
// //// (Optional) You can disable "Connection Open()", "Connection Close()" (and async variant) tracking.
|
||||
// //// (defaults to true, and connection opening/closing is tracked)
|
||||
// //options.TrackConnectionOpenClose = true;
|
||||
|
||||
// //// (Optional) Use something other than the "light" color scheme.
|
||||
// //// (defaults to "light")
|
||||
// //options.ColorScheme = StackExchange.Profiling.ColorScheme.Auto;
|
||||
|
||||
// //// The below are newer options, available in .NET Core 3.0 and above:
|
||||
|
||||
// //// (Optional) You can disable MVC filter profiling
|
||||
// //// (defaults to true, and filters are profiled)
|
||||
// //options.EnableMvcFilterProfiling = true;
|
||||
// //// ...or only save filters that take over a certain millisecond duration (including their children)
|
||||
// //// (defaults to null, and all filters are profiled)
|
||||
// //// options.MvcFilterMinimumSaveMs = 1.0m;
|
||||
|
||||
// //// (Optional) You can disable MVC view profiling
|
||||
// //// (defaults to true, and views are profiled)
|
||||
// //options.EnableMvcViewProfiling = true;
|
||||
// //// ...or only save views that take over a certain millisecond duration (including their children)
|
||||
// //// (defaults to null, and all views are profiled)
|
||||
// //// options.MvcViewMinimumSaveMs = 1.0m;
|
||||
|
||||
// //// (Optional) listen to any errors that occur within MiniProfiler itself
|
||||
// //// options.OnInternalError = e => MyExceptionLogger(e);
|
||||
|
||||
// //// (Optional - not recommended) You can enable a heavy debug mode with stacks and tooltips when using memory storage
|
||||
// //// It has a lot of overhead vs. normal profiling and should only be used with that in mind
|
||||
// //// (defaults to false, debug/heavy mode is off)
|
||||
// ////options.EnableDebugMode = true;
|
||||
// });
|
||||
// //.AddEntityFramework();
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
@@ -0,0 +1,46 @@
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
using System;
|
||||
|
||||
namespace IRaCIS.Core.API
|
||||
{
|
||||
public class JSONCustomDateConverter : DateTimeConverterBase
|
||||
{
|
||||
private TimeZoneInfo _timeZoneInfo;
|
||||
private string _dateFormat;
|
||||
|
||||
private IUserInfo _userInfo;
|
||||
public JSONCustomDateConverter(string dateFormat, TimeZoneInfo timeZoneInfo, IUserInfo userInfo)
|
||||
{
|
||||
_dateFormat = dateFormat;
|
||||
_timeZoneInfo = timeZoneInfo;
|
||||
|
||||
_userInfo = userInfo;
|
||||
}
|
||||
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||
{
|
||||
var timeZoneId = _userInfo.TimeZoneId;
|
||||
|
||||
var needConvertUtcDateTime = Convert.ToDateTime(value);
|
||||
|
||||
|
||||
var tz = TimeZoneInfo.FindSystemTimeZoneById(timeZoneId);
|
||||
|
||||
|
||||
var dateTimeOffset = new DateTimeOffset(needConvertUtcDateTime);
|
||||
|
||||
var time = TimeZoneInfo.ConvertTimeFromUtc(needConvertUtcDateTime, tz).ToString(_dateFormat);
|
||||
|
||||
writer.WriteValue(time);
|
||||
writer.Flush();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
using StackExchange.Redis;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
|
||||
namespace IRaCIS.Core.API
|
||||
{
|
||||
/// <summary>
|
||||
/// 序列化,反序列化的时候,处理时间 时区转换
|
||||
/// </summary>
|
||||
public class JSONTimeZoneConverter(IHttpContextAccessor _httpContextAccessor) : DateTimeConverterBase
|
||||
{
|
||||
|
||||
private TimeZoneInfo _clientTimeZone;
|
||||
|
||||
private string _dateFormat;
|
||||
|
||||
|
||||
|
||||
public override bool CanConvert(Type objectType)
|
||||
{
|
||||
#region 设置语言格式化方式,放在构造函数里面做不到动态切换
|
||||
|
||||
|
||||
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
|
||||
|
||||
if (!isEn_US)
|
||||
{
|
||||
// Chinese date format
|
||||
_dateFormat = "yyyy-MM-dd HH:mm:ss";
|
||||
}
|
||||
else
|
||||
{
|
||||
// Default or English date format
|
||||
_dateFormat = "MM/dd/yyyy HH:mm:ss";
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 获取当前请求的客户端时区
|
||||
|
||||
//var timeZoneId = "Etc/UTC";
|
||||
var timeZoneId = "Asia/Shanghai";
|
||||
var timeZoneIdHeader = _httpContextAccessor?.HttpContext?.Request?.Headers["TimeZoneId"];
|
||||
|
||||
if (timeZoneIdHeader is not null && !string.IsNullOrEmpty(timeZoneIdHeader.Value))
|
||||
{
|
||||
timeZoneId = timeZoneIdHeader.Value;
|
||||
}
|
||||
|
||||
_clientTimeZone = TimeZoneInfo.FindSystemTimeZoneById(timeZoneId);
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
// 仅支持 DateTime 类型的转换
|
||||
return objectType == typeof(DateTime)|| objectType == typeof(DateTime?);
|
||||
|
||||
}
|
||||
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||
{
|
||||
if (reader.Value == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
DateTime dateTime;
|
||||
|
||||
if (reader.ValueType == typeof(DateTime) || reader.ValueType == typeof(DateTime?))
|
||||
{
|
||||
DateTime? nullableDateTime = reader.Value as DateTime?;
|
||||
|
||||
|
||||
if (nullableDateTime != null && nullableDateTime.HasValue)
|
||||
{
|
||||
dateTime = nullableDateTime.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (DateTime.TryParse((string)reader.Value, out dateTime) == false)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 将客户端时间转换为服务器时区的时间
|
||||
var serverZoneTime = TimeZoneInfo.ConvertTime(dateTime, _clientTimeZone, TimeZoneInfo.Local);
|
||||
|
||||
return serverZoneTime;
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||
{
|
||||
DateTime? nullableDateTime = value as DateTime?;
|
||||
|
||||
if (nullableDateTime != null && nullableDateTime.HasValue)
|
||||
{
|
||||
//第一个参数默认使用系统本地时区 也就是应用服务器的时区
|
||||
DateTime clientZoneTime = TimeZoneInfo.ConvertTime(nullableDateTime.Value, _clientTimeZone);
|
||||
|
||||
//writer.WriteValue(clientZoneTime);
|
||||
|
||||
writer.WriteValue(clientZoneTime.ToString(_dateFormat));
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.WriteNull();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
|
||||
namespace IRaCIS.Core.API
|
||||
{
|
||||
|
||||
|
||||
//public class CustomContractResolver : DefaultContractResolver
|
||||
//{
|
||||
// protected override JsonContract CreateContract(Type objectType)
|
||||
// {
|
||||
// var contract = base.CreateContract(objectType);
|
||||
|
||||
// // 检查类是否有 LowercaseJsonAttribute 标记
|
||||
// if (objectType.GetCustomAttribute<LowerCamelCaseJsonAttribute>() != null)
|
||||
// {
|
||||
// contract.NamingStrategy = new IRCCamelCaseNamingStrategy();
|
||||
// }
|
||||
|
||||
// return contract;
|
||||
// }
|
||||
//}
|
||||
|
||||
|
||||
|
||||
#region 废弃
|
||||
|
||||
public class MyDateTimeConverter : JsonConverter<DateTime>
|
||||
{
|
||||
public override DateTime ReadJson(JsonReader reader, Type objectType, DateTime existingValue, bool hasExistingValue, JsonSerializer serializer)
|
||||
{
|
||||
return reader.ReadAsDateTime().Value;
|
||||
}
|
||||
|
||||
public override void WriteJson(JsonWriter writer, DateTime value, JsonSerializer serializer)
|
||||
{
|
||||
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
|
||||
|
||||
string dateFormat;
|
||||
if (!isEn_US)
|
||||
{
|
||||
// Chinese date format
|
||||
dateFormat = "yyyy-MM-dd HH:mm:ss";
|
||||
}
|
||||
else
|
||||
{
|
||||
// Default or English date format
|
||||
dateFormat = "MM/dd/yyyy HH:mm:ss";
|
||||
}
|
||||
|
||||
writer.WriteValue(value.ToString(dateFormat));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public class MyNullableDateTimeConverter : JsonConverter<DateTime?>
|
||||
{
|
||||
public override DateTime? ReadJson(JsonReader reader, Type objectType, DateTime? existingValue, bool hasExistingValue, JsonSerializer serializer)
|
||||
{
|
||||
var val = reader.ReadAsDateTime();
|
||||
return val;
|
||||
}
|
||||
|
||||
public override void WriteJson(JsonWriter writer, DateTime? value, JsonSerializer serializer)
|
||||
{
|
||||
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
|
||||
|
||||
string dateFormat;
|
||||
if (!isEn_US)
|
||||
{
|
||||
// Chinese date format
|
||||
dateFormat = "yyyy-MM-dd HH:mm:ss";
|
||||
}
|
||||
else
|
||||
{
|
||||
// Default or English date format
|
||||
dateFormat = "MM/dd/yyyy HH:mm:ss";
|
||||
}
|
||||
|
||||
if (value.HasValue)
|
||||
{
|
||||
writer.WriteValue(value.Value.ToString(dateFormat));
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.WriteValue(default(DateTime?));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -1,60 +1,32 @@
|
||||
|
||||
using IRaCIS.Core.API._ServiceExtensions.NewtonsoftJson;
|
||||
using IRaCIS.Core.Application.Helper;
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Text.Json;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
|
||||
namespace IRaCIS.Core.API
|
||||
{
|
||||
public static class NewtonsoftJsonSetup
|
||||
{
|
||||
public static void AddNewtonsoftJsonSetup(this IMvcBuilder builder, IServiceCollection services)
|
||||
public static void AddNewtonsoftJsonSetup(this IMvcBuilder builder)
|
||||
{
|
||||
|
||||
services.AddHttpContextAccessor();
|
||||
services.AddScoped<JSONTimeZoneConverter>();
|
||||
services.AddScoped<ObjectStorePathConvert>();
|
||||
services.AddScoped<IOSSService, OSSService>();
|
||||
|
||||
|
||||
|
||||
builder.AddNewtonsoftJson(options =>
|
||||
{
|
||||
|
||||
//大驼峰
|
||||
//options.SerializerSettings.ContractResolver = new DefaultContractResolver();
|
||||
//小驼峰
|
||||
//options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
|
||||
|
||||
|
||||
|
||||
//options.SerializerSettings.PreserveReferencesHandling = PreserveReferencesHandling.Objects;
|
||||
// 忽略循环引用
|
||||
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
|
||||
//options.SerializerSettings.TypeNameHandling = TypeNameHandling.All;
|
||||
|
||||
//处理返回给前端 可空类型 给出默认值 比如in? 为null 设置 默认值0
|
||||
options.SerializerSettings.ContractResolver = new NullToEmptyStringResolver();
|
||||
options.SerializerSettings.ContractResolver = new NullToEmptyStringResolver(); //new DefaultContractResolver();// new NullToEmptyStringResolver();
|
||||
// 设置时间格式
|
||||
options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss";
|
||||
|
||||
// 设置时间格式 isEn_US? "MM/dd/yyyy HH:mm:ss" :
|
||||
//options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss";
|
||||
|
||||
options.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.RoundtripKind;
|
||||
|
||||
|
||||
//二者只能取其一
|
||||
//options.SerializerSettings.Converters.Add(new MyDateTimeConverter());
|
||||
//options.SerializerSettings.Converters.Add(new MyNullableDateTimeConverter());
|
||||
|
||||
//必须放在后面
|
||||
options.SerializerSettings.Converters.Add(services.BuildServiceProvider().GetService<JSONTimeZoneConverter>());
|
||||
//options.SerializerSettings.Converters.Add(new JSONCustomDateConverter()) ;
|
||||
|
||||
//IsoDateTimeConverter
|
||||
//options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore;
|
||||
|
||||
})
|
||||
.AddControllersAsServices()//动态webApi属性注入需要
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
using IRaCIS.Core.Infrastructure.NewtonsoftJson;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace IRaCIS.Core.API
|
||||
{
|
||||
@@ -32,20 +30,8 @@ namespace IRaCIS.Core.API
|
||||
|
||||
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
|
||||
{
|
||||
// 检查类是否有 LowerCamelCaseJsonAttribute 标记 有的话,属性名小写
|
||||
if (type.GetCustomAttribute<LowerCamelCaseJsonAttribute>() != null)
|
||||
{
|
||||
base.NamingStrategy= new LowerCamelCaseNamingStrategy();
|
||||
}
|
||||
else
|
||||
{
|
||||
base.NamingStrategy = null;
|
||||
}
|
||||
|
||||
|
||||
IList<JsonProperty> properties = base.CreateProperties(type, memberSerialization);
|
||||
|
||||
|
||||
var list= type.GetProperties()
|
||||
.Select(p =>
|
||||
{
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace IRaCIS.Core.API
|
||||
if(_MemberInfo.PropertyType == typeof(string))
|
||||
{
|
||||
//去掉前后空格
|
||||
_MemberInfo.SetValue(target, value==null?string.Empty: value.ToString()==string.Empty? value:value/*.ToString().Trim()*/);
|
||||
_MemberInfo.SetValue(target, value==null?string.Empty: value.ToString()==string.Empty? value:value.ToString().Trim());
|
||||
|
||||
}
|
||||
else
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
using IRaCIS.Core.Application.Helper;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace IRaCIS.Core.API._ServiceExtensions.NewtonsoftJson
|
||||
{
|
||||
public class ObjectStorePathConvert : JsonConverter<string>
|
||||
{
|
||||
private readonly IOSSService _oSSService;
|
||||
|
||||
// 构造函数
|
||||
public ObjectStorePathConvert( IOSSService oSSService)
|
||||
{
|
||||
_oSSService = oSSService;
|
||||
}
|
||||
|
||||
public override string ReadJson(JsonReader reader, Type objectType, string existingValue, bool hasExistingValue, JsonSerializer serializer)
|
||||
{
|
||||
if (reader.TokenType == JsonToken.String)
|
||||
{
|
||||
return (string)reader.Value;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public override void WriteJson(JsonWriter writer, string value, JsonSerializer serializer)
|
||||
{
|
||||
if (value != null)
|
||||
{
|
||||
// 在这里对字符串进行处理,例如转换大小写、去除空格等
|
||||
// 这里只是一个示例,您可以根据实际需求进行更改
|
||||
|
||||
// 获取当前正在序列化的属性名
|
||||
string propertyName = writer.Path.Split('.').Last();
|
||||
|
||||
Regex guidRegex = new Regex(@"[0-9a-fA-F]{8}(-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}");
|
||||
|
||||
if (propertyName.IndexOf("Path") > -1 && value.IndexOf('.')>-1 && guidRegex.IsMatch(value))
|
||||
{
|
||||
var tt= _oSSService.GetSignedUrl(value);
|
||||
writer.WriteValue(tt);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 将处理后的字符串写入到 JsonWriter 中
|
||||
writer.WriteValue(value);
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.WriteNull();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
|
||||
using IRaCIS.Application.Services.BackGroundJob;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Quartz;
|
||||
|
||||
namespace IRaCIS.Core.API
|
||||
{
|
||||
public static class QuartZSetup
|
||||
{
|
||||
public static void AddQuartZSetup(this IServiceCollection services, IConfiguration configuration)
|
||||
{
|
||||
services.AddTransient<CancelTaskQuartZJob>();
|
||||
|
||||
services.AddQuartz(q =>
|
||||
{
|
||||
// base quartz scheduler, job and trigger configuration
|
||||
|
||||
// as of 3.3.2 this also injects scoped services (like EF DbContext) without problems
|
||||
q.UseMicrosoftDependencyInjectionJobFactory();
|
||||
|
||||
// 基本Quartz调度器、作业和触发器配置
|
||||
//var jobKey = new JobKey("RegularTrialWork", "regularWorkGroup");
|
||||
//q.AddJob<CacheTrialStatusQuartZJob>(jobKey, j => j
|
||||
// .WithDescription("Trial regular work")
|
||||
//);
|
||||
|
||||
//q.AddTrigger(t => t
|
||||
// .WithIdentity("TrialStatusTrigger")
|
||||
// .ForJob(jobKey)
|
||||
// .WithCronSchedule("0 0 * * * ?")
|
||||
// .WithDescription("My regular trial work trigger")
|
||||
//);
|
||||
|
||||
|
||||
|
||||
|
||||
});
|
||||
|
||||
// ASP.NET Core hosting
|
||||
services.AddQuartzHostedService(options =>
|
||||
{
|
||||
// when shutting down we want jobs to complete gracefully
|
||||
options.WaitForJobsToComplete = true;
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,30 +1,18 @@
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.ResponseCompression;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System.IO.Compression;
|
||||
|
||||
namespace IRaCIS.Core.API
|
||||
{
|
||||
public static class ResponseCompressionSetup
|
||||
public static class ResponseCompressionSetup
|
||||
{
|
||||
public static void AddResponseCompressionSetup(this IServiceCollection services)
|
||||
{
|
||||
services.AddResponseCompression(options =>
|
||||
{
|
||||
options.EnableForHttps = true;
|
||||
options.Providers.Add<BrotliCompressionProvider>();
|
||||
options.Providers.Add<GzipCompressionProvider>();
|
||||
});
|
||||
|
||||
services.Configure<BrotliCompressionProviderOptions>(options =>
|
||||
{
|
||||
options.Level = CompressionLevel.Optimal;
|
||||
});
|
||||
|
||||
services.Configure<GzipCompressionProviderOptions>(options =>
|
||||
{
|
||||
options.Level = CompressionLevel.Optimal;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,13 +31,13 @@ namespace IRaCIS.Core.API
|
||||
|
||||
logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("LocalIP", httpContext.Connection.LocalIpAddress.MapToIPv4().ToString()));
|
||||
|
||||
logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("TokenUserRealName", httpContext?.User?.FindFirst(ClaimAttributes.RealName)?.Value));
|
||||
logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("TokenUserType", httpContext?.User?.FindFirst(JwtIRaCISClaimType.UserTypeShortName)?.Value));
|
||||
|
||||
|
||||
//这样读取没用
|
||||
//logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("RequestBody", await ReadRequestBody(httpContext.Request)));
|
||||
//logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("RequestIP", IPHelper.GetIP(httpContext.Request) ));
|
||||
logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("TokenUserRealName", httpContext?.User?.FindFirst(ClaimAttributes.RealName)?.Value));
|
||||
logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("TokenUserType", httpContext?.User?.FindFirst(JwtIRaCISClaimType.UserTypeShortName)?.Value));
|
||||
|
||||
//logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("Referer", httpContext.Request.Headers["Referer"].ToString()));
|
||||
//logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("request_path", httpContext.Request.Path));
|
||||
//logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("request_method", httpContext.Request.Method));
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Serilog;
|
||||
using Serilog.Events;
|
||||
//using Serilog.Sinks.Email;
|
||||
using Serilog.Sinks.Email;
|
||||
using System;
|
||||
using System.Net;
|
||||
|
||||
@@ -19,11 +19,11 @@ namespace IRaCIS.Core.API
|
||||
// Filter out ASP.NET Core infrastructre logs that are Information and below 日志太多了 一个请求 记录好几条
|
||||
.MinimumLevel.Override("Microsoft.AspNetCore", LogEventLevel.Warning)
|
||||
.MinimumLevel.Override("Hangfire", LogEventLevel.Warning)
|
||||
.MinimumLevel.Override("System.Net.Http.HttpClient.HttpReports", LogEventLevel.Warning)
|
||||
.MinimumLevel.Override("System.Net.Http.HttpClient.HttpReports", LogEventLevel.Warning)
|
||||
.Enrich.WithClientIp()
|
||||
.Enrich.FromLogContext()
|
||||
.Filter.ByExcluding(logEvent =>logEvent.Properties.ContainsKey("RequestPath") && logEvent.Properties["RequestPath"].ToString().Contains("/health"))
|
||||
|
||||
.Enrich.FromLogContext()
|
||||
|
||||
//控制台 方便调试 问题 我们显示记录日志 时 获取上下文的ip 和用户名 用户类型
|
||||
.WriteTo.Console(restrictedToMinimumLevel: LogEventLevel.Warning,
|
||||
outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {SourceContext:l} || {Message} || {Exception} ||end {NewLine}")
|
||||
|
||||
@@ -3,7 +3,6 @@ using Hangfire.SqlServer;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace IRaCIS.Core.API
|
||||
{
|
||||
@@ -15,29 +14,19 @@ namespace IRaCIS.Core.API
|
||||
|
||||
services.AddHangfire(hangFireConfig =>
|
||||
{
|
||||
//本地window 调试 使用内存,服务器部署使用数据库,防止服务器任务调度到本地
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
|
||||
//hangFireConfig.UseInMemoryStorage();
|
||||
|
||||
//指定存储介质
|
||||
hangFireConfig.UseSqlServerStorage(hangFireConnStr, new SqlServerStorageOptions()
|
||||
{
|
||||
hangFireConfig.UseInMemoryStorage();
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
//指定存储介质
|
||||
hangFireConfig.UseSqlServerStorage(hangFireConnStr, new SqlServerStorageOptions()
|
||||
{
|
||||
SchemaName = "dbo",
|
||||
CommandBatchMaxTimeout = TimeSpan.FromMinutes(5),
|
||||
SlidingInvisibilityTimeout = TimeSpan.FromMinutes(5),
|
||||
QueuePollInterval = TimeSpan.Zero,
|
||||
UseRecommendedIsolationLevel = true,
|
||||
DisableGlobalLocks = true
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
SchemaName = "dbo",
|
||||
CommandBatchMaxTimeout = TimeSpan.FromMinutes(5),
|
||||
SlidingInvisibilityTimeout = TimeSpan.FromMinutes(5),
|
||||
QueuePollInterval = TimeSpan.Zero,
|
||||
UseRecommendedIsolationLevel = true,
|
||||
DisableGlobalLocks = true
|
||||
});
|
||||
|
||||
//hangFireConfig.UseTagsWithSql(); //nuget引入Hangfire.Tags.SqlServer
|
||||
//.UseHangfireHttpJob();
|
||||
@@ -46,7 +35,7 @@ namespace IRaCIS.Core.API
|
||||
|
||||
services.AddHangfireServer(option =>
|
||||
{
|
||||
option.Queues = new[] { "immediately_once", "default", "sys_init", "not_immediately_once" };
|
||||
option.Queues = new[] { "immediately_once","default","sys_init" , "not_immediately_once" };
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@@ -1,77 +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": "LTAI5tKvzs7ed3UfSpNk3xwQ",
|
||||
"accessKeySecret": "zTIceGEShlZDGnLrCFfIGFE7TXVRio",
|
||||
"bucketName": "zy-irc-store",
|
||||
"roleArn": "acs:ram::1899121822495495:role/oss-upload",
|
||||
"viewEndpoint": "https://zy-irc-cache.oss-cn-shanghai.aliyuncs.com",
|
||||
"region": "oss-cn-shanghai"
|
||||
},
|
||||
"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": "展影医疗"
|
||||
},
|
||||
"SystemPacsConfig": {
|
||||
"Port": "11113",
|
||||
"IP": "101.132.193.237"
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
"ObjectStoreService": {
|
||||
"ObjectStoreUse": "AliyunOSS",
|
||||
"AliyunOSS": {
|
||||
"regionId": "cn-shanghai",
|
||||
"endpoint": "https://oss-cn-shanghai.aliyuncs.com",
|
||||
"accessKeyId": "LTAI5tKvzs7ed3UfSpNk3xwQ",
|
||||
"accessKeySecret": "zTIceGEShlZDGnLrCFfIGFE7TXVRio",
|
||||
"bucketName": "zy-sir-store",
|
||||
"roleArn": "acs:ram::1899121822495495:role/oss-upload",
|
||||
"viewEndpoint": "https://zy-sir-cache.oss-cn-shanghai.aliyuncs.com",
|
||||
"region": "oss-cn-shanghai"
|
||||
},
|
||||
|
||||
"MinIO": {
|
||||
"endpoint": "http://192.168.3.68",
|
||||
"port": "8001",
|
||||
"useSSL": false,
|
||||
"accessKey": "IDFkwEpWej0b4DtiuThL",
|
||||
"secretKey": "Lhuu83yMhVwu7c1SnjvGY6lq74jzpYqifK6Qtj4h",
|
||||
"bucketName": "test"
|
||||
}
|
||||
},
|
||||
|
||||
"ConnectionStrings": {
|
||||
"RemoteNew": "Server=101.132.193.237,1434;Database=Prod_Study;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true",
|
||||
"Hangfire": "Server=101.132.193.237,1434;Database=Prod_Study_Hangfire;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true"
|
||||
},
|
||||
|
||||
"BasicSystemConfig": {
|
||||
|
||||
"OpenUserComplexPassword": true,
|
||||
|
||||
"OpenSignDocumentBeforeWork": true,
|
||||
|
||||
"OpenTrialRelationDelete": true,
|
||||
|
||||
"OpenLoginLimit": true,
|
||||
"LoginMaxFailCount": 5,
|
||||
|
||||
"LoginFailLockMinutes": 30
|
||||
|
||||
},
|
||||
|
||||
"SystemEmailSendConfig": {
|
||||
"Port": 465,
|
||||
"Host": "smtp.qiye.aliyun.com",
|
||||
"FromEmail": "study@extimaging.com",
|
||||
"FromName": "研究单位阅片系统",
|
||||
"AuthorizationCode": "zhanying123",
|
||||
"SiteUrl": "https://study.extimaging.com/login"
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
"ConnectionStrings": {
|
||||
"RemoteNew": "Server=106.14.89.110,1435;Database=Test_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
||||
"Hangfire": "Server=106.14.89.110,1435;Database=Test_IRC_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
|
||||
},
|
||||
|
||||
"ObjectStoreService": {
|
||||
|
||||
"ObjectStoreUse": "AliyunOSS",
|
||||
|
||||
"AliyunOSS": {
|
||||
"RegionId": "cn-shanghai",
|
||||
"InternalEndpoint": "https://oss-cn-shanghai-internal.aliyuncs.com",
|
||||
"EndPoint": "https://oss-cn-shanghai.aliyuncs.com",
|
||||
"AccessKeyId": "LTAI5tJV76pYX5yPg1N9QVE8",
|
||||
"AccessKeySecret": "roRNLa9YG1of4pYruJGCNKBXEWTAWa",
|
||||
"BucketName": "zy-irc-test-store",
|
||||
"RoleArn": "acs:ram::1899121822495495:role/webdirect",
|
||||
"ViewEndpoint": "https://zy-irc-test-store.oss-cn-shanghai.aliyuncs.com",
|
||||
"Region": "oss-cn-shanghai",
|
||||
"DurationSeconds": 7200
|
||||
},
|
||||
"MinIO": {
|
||||
"EndPoint": "hir-oss.test.extimaging.com",
|
||||
"Port": "443",
|
||||
"UseSSL": true,
|
||||
"AccessKey": "fbStsVYCIPKHQneeqMwD",
|
||||
"SecretKey": "TzgvyA3zGXMUnpilJNUlyMYHfosl1hBMl6lxPmjy",
|
||||
"BucketName": "irc-test",
|
||||
"ViewEndpoint": "https://hir-oss.test.extimaging.com/irc-test"
|
||||
},
|
||||
"AWS": {
|
||||
"Region": "us-east-1",
|
||||
"EndPoint": "s3.us-east-1.amazonaws.com",
|
||||
"UseSSL": true,
|
||||
"RoleArn": "arn:aws:iam::471112624751:role/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": 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"
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
+21
-17
@@ -10,16 +10,25 @@
|
||||
"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-uat-store",
|
||||
"bucketName": "zy-sir-test-store",
|
||||
"roleArn": "acs:ram::1899121822495495:role/oss-upload",
|
||||
"viewEndpoint": "https://zy-irc-uat-store.oss-cn-shanghai.aliyuncs.com",
|
||||
"viewEndpoint": "https://zy-sir-test-store.oss-cn-shanghai.aliyuncs.com",
|
||||
"region": "oss-cn-shanghai"
|
||||
},
|
||||
|
||||
"MinIO": {
|
||||
"endPoint": "192.168.3.68",
|
||||
"port": "8001",
|
||||
"useSSL": false,
|
||||
"accessKey": "IDFkwEpWej0b4DtiuThL",
|
||||
"secretKey": "Lhuu83yMhVwu7c1SnjvGY6lq74jzpYqifK6Qtj4h",
|
||||
"bucketName": "test",
|
||||
"viewEndpoint": "http://192.168.3.68:8001/test/"
|
||||
},
|
||||
|
||||
"AWS": {
|
||||
"endPoint": "s3.us-east-1.amazonaws.com",
|
||||
"useSSL": false,
|
||||
@@ -29,9 +38,10 @@
|
||||
"viewEndpoint": "https://ei-irc-test-store.s3.amazonaws.com/"
|
||||
}
|
||||
},
|
||||
|
||||
"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"
|
||||
"RemoteNew": "Server=123.56.94.154,1435;Database=Test_Study;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
||||
"Hangfire": "Server=123.56.94.154,1435;Database=Test_Study_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
|
||||
},
|
||||
"BasicSystemConfig": {
|
||||
|
||||
@@ -45,23 +55,17 @@
|
||||
"LoginMaxFailCount": 5,
|
||||
|
||||
"LoginFailLockMinutes": 30
|
||||
|
||||
},
|
||||
|
||||
"SystemEmailSendConfig": {
|
||||
"Port": 465,
|
||||
"Host": "smtp.qiye.aliyun.com",
|
||||
"FromEmail": "uat-study@extimaging.com",
|
||||
"FromName": "Uat_Study",
|
||||
"FromEmail": "test-study@extimaging.com",
|
||||
"FromName": "Test_Study",
|
||||
"AuthorizationCode": "zhanying123",
|
||||
"SiteUrl": "http://study.uat.extimaging.com/login"
|
||||
},
|
||||
"DicomSCPServiceConfig": {
|
||||
"CalledAEList": [
|
||||
"STORESCP",
|
||||
"Value1",
|
||||
"Value2",
|
||||
"Value3"
|
||||
],
|
||||
"ServerPort": 11112
|
||||
|
||||
"SiteUrl": "http://study.test.extimaging.com/login"
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,88 +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",
|
||||
"AliyunOSS": {
|
||||
"regionId": "cn-shanghai",
|
||||
"internalEndpoint": "https://oss-cn-shanghai-internal.aliyuncs.com",
|
||||
"endPoint": "https://oss-cn-shanghai.aliyuncs.com",
|
||||
"accessKeyId": "",
|
||||
"accessKeySecret": "",
|
||||
"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": "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": {
|
||||
"endPoint": "s3.us-east-1.amazonaws.com",
|
||||
"useSSL": true,
|
||||
"accessKey": "AKIAW3MEAFJX5P32P6NA",
|
||||
"secretKey": "soKfYlzZE11Zi4RyTjXp0myXN0U3U+ka8rT49+B/",
|
||||
"bucketName": "ei-med-s3-lili-store",
|
||||
"viewEndpoint": "https://ei-med-s3-lili-store.s3.amazonaws.com/"
|
||||
}
|
||||
},
|
||||
"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"
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,94 +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": "MinIO",
|
||||
"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.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": {
|
||||
"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": 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.test.elevateimaging.ai/login"
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,88 +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": {
|
||||
"endPoint": "s3.us-east-1.amazonaws.com",
|
||||
"useSSL": true,
|
||||
"accessKey": "AKIAW3MEAFJXUO6XYFYN",
|
||||
"secretKey": "AeX5r4xHQH7tNJlTTFVv5/zBXie1Kj+mAayKrukp",
|
||||
"bucketName": "ei-med-s3-lili-uat-store",
|
||||
"viewEndpoint": "https://ei-med-s3-lili-uat-store.s3.amazonaws.com/"
|
||||
}
|
||||
},
|
||||
"BasicSystemConfig": {
|
||||
|
||||
"OpenUserComplexPassword": false,
|
||||
|
||||
"OpenSignDocumentBeforeWork": false,
|
||||
|
||||
"OpenTrialRelationDelete": true,
|
||||
|
||||
"OpenLoginLimit": false,
|
||||
|
||||
"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"
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,85 +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": "LTAI5tKvzs7ed3UfSpNk3xwQ",
|
||||
"accessKeySecret": "zTIceGEShlZDGnLrCFfIGFE7TXVRio",
|
||||
"bucketName": "zy-irc-uat-store",
|
||||
"roleArn": "acs:ram::1899121822495495:role/oss-upload",
|
||||
"viewEndpoint": "https://zy-irc-uat-store.oss-cn-shanghai.aliyuncs.com",
|
||||
"region": "oss-cn-shanghai"
|
||||
},
|
||||
|
||||
"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"
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
"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": "展影医疗"
|
||||
},
|
||||
|
||||
"SystemPacsConfig": {
|
||||
"Port": "11113",
|
||||
"IP": "47.117.164.182"
|
||||
}
|
||||
|
||||
}
|
||||
+18
-29
@@ -6,23 +6,19 @@
|
||||
"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": "LTAI5tKvzs7ed3UfSpNk3xwQ",
|
||||
"accessKeySecret": "zTIceGEShlZDGnLrCFfIGFE7TXVRio",
|
||||
"bucketName": "zy-irc-store",
|
||||
"bucketName": "zy-sir-uat-store",
|
||||
"roleArn": "acs:ram::1899121822495495:role/oss-upload",
|
||||
"viewEndpoint": "https://zy-irc-cache.oss-cn-shanghai.aliyuncs.com",
|
||||
"viewEndpoint": "https://zy-sir-uat-store.oss-cn-shanghai.aliyuncs.com",
|
||||
"region": "oss-cn-shanghai"
|
||||
},
|
||||
|
||||
"MinIO": {
|
||||
"endpoint": "http://192.168.3.68",
|
||||
"port": "8001",
|
||||
@@ -32,39 +28,32 @@
|
||||
"bucketName": "test"
|
||||
}
|
||||
},
|
||||
|
||||
"ConnectionStrings": {
|
||||
"RemoteNew": "Server=47.117.164.182,1434;Database=Uat.Study;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
||||
"Hangfire": "Server=47.117.164.182,1434;Database=Uat.Study.hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
|
||||
},
|
||||
"BasicSystemConfig": {
|
||||
|
||||
"OpenUserComplexPassword": true,
|
||||
"OpenUserComplexPassword": false,
|
||||
|
||||
"OpenSignDocumentBeforeWork": true,
|
||||
"OpenSignDocumentBeforeWork": false,
|
||||
|
||||
"OpenLoginLimit": true,
|
||||
"OpenTrialRelationDelete": true,
|
||||
|
||||
"OpenLoginLimit": false,
|
||||
"LoginMaxFailCount": 5,
|
||||
|
||||
"LoginFailLockMinutes": 30,
|
||||
"AutoLoginOutMinutes": 60,
|
||||
"LoginFailLockMinutes": 30
|
||||
|
||||
"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": "展影医疗"
|
||||
"FromEmail": "uat-study@extimaging.com",
|
||||
"FromName": "Uat_Study",
|
||||
"AuthorizationCode": "zhanying123",
|
||||
"SiteUrl": "http://study.uat.extimaging.com/login"
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,10 +1,11 @@
|
||||
{
|
||||
"JwtSetting": {
|
||||
"SecurityKey": "ShangHaiZhanYing_SecurityKey_SHzyyl@2021",
|
||||
"Issuer": "Extimaging",
|
||||
"Audience": "EICS",
|
||||
"SecurityKey": "3e6dbfa227234a03977a2f421bdb7f4f",
|
||||
"Issuer": "IRaCIS",
|
||||
"Audience": "ZhiZhun",
|
||||
"TokenExpireDays": "7"
|
||||
},
|
||||
//ip
|
||||
"IpRateLimiting": {
|
||||
"EnableEndpointRateLimiting": true,
|
||||
"StackBlockedRequests": false,
|
||||
@@ -19,6 +20,7 @@
|
||||
"EndpointWhitelist": [
|
||||
"post:/study/archivestudy/*"
|
||||
],
|
||||
|
||||
"IpWhitelist": [],
|
||||
"GeneralRules": [
|
||||
{
|
||||
@@ -90,13 +92,7 @@
|
||||
"DicomStudyCodePrefix": "ST",
|
||||
"SystemSiteCodePrefix": "S",
|
||||
"DefaultPassword": "123456",
|
||||
"DefaultInternalOrganizationName": "ExtImaging",
|
||||
"ImageShareExpireDays": 10
|
||||
},
|
||||
|
||||
"EncrypteResponseConfig": {
|
||||
"IsEnable": true,
|
||||
"ApiPathList": [
|
||||
"/test/get"
|
||||
]
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -11,7 +11,7 @@
|
||||
尊敬的 {0} ,您好:
|
||||
</div>
|
||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
|
||||
{company abbreviation}为您添加了账户,账户信息如下:
|
||||
展影医疗为您添加了账户,账户信息如下:
|
||||
</div>
|
||||
<span style="color: #00D1B2"></span>
|
||||
<div style='border: 1px solid #eee;box-sizing:border-box;width: 50%;background: #fff;padding: 20px;line-height: 40px;font-size: 14px;border-radius: 5px;margin-left: 60px;margin-bottom: 30px;'>
|
||||
@@ -23,7 +23,7 @@
|
||||
</div>
|
||||
<div>
|
||||
首次登陆前,请通过该链接修改您的账户信息:
|
||||
<a href='{3}' style='margin-left:1px;font-size:14px;text-decoration: none;display: inline-block;color:#00D1B2;border-radius: 5px;line-height: 40px;text-align: center;'>
|
||||
<a href='{3}' style='margin-left:30px;font-size:14px;text-decoration: none;display: inline-block;color:#00D1B2;border-radius: 5px;line-height: 40px;text-align: center;'>
|
||||
初始化账号信息
|
||||
</a>
|
||||
</div>
|
||||
@@ -32,7 +32,7 @@
|
||||
|
||||
<div style='line-height: 24px;font-size: 14px;color:#333;margin-top: 20px;padding-bottom: 40px;'>
|
||||
<div>祝您顺利!</div>
|
||||
<div style="font-size: 14px;">{company}</div>
|
||||
<div style="font-size: 14px;">展影医疗</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -4,17 +4,17 @@
|
||||
<meta charset='UTF-8'>
|
||||
<title>Title</title>
|
||||
</head>
|
||||
<body style='font-family: 微软雅黑;padding: 0;margin: 0;'>
|
||||
<div style='padding-left: 40px;background: #f6f6f6'>
|
||||
<body style='font-family: Times New Roman;padding: 0;margin: 0;'>
|
||||
<div style='padding-left: 40px;background: #f6f6f6;font-size: 16px'>
|
||||
<div style='padding-top: 20px;'>
|
||||
<div style='line-height: 40px;font-size: 18px'>
|
||||
<div style='line-height: 40px;font-size: 16px'>
|
||||
Dear {0} ,
|
||||
</div>
|
||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
|
||||
{company abbreviation} has created an account for you. The account information is as follows:
|
||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;font-size: 16px'>
|
||||
Extensive Imaging has generated a user account for you. <br>The account information is as follows:
|
||||
</div>
|
||||
<span style="color: #00D1B2"></span>
|
||||
<div style='border: 1px solid #eee;box-sizing:border-box;width: 50%;background: #fff;padding: 20px;line-height: 40px;font-size: 14px;border-radius: 5px;margin-left: 60px;margin-bottom: 30px;'>
|
||||
<div style='border: 1px solid #eee;box-sizing:border-box;width: 50%;background: #fff;padding: 20px;line-height: 40px;font-size: 14px;border-radius: 5px;margin-left: 60px;margin-bottom: 30px;font-size: 16px'>
|
||||
<div>
|
||||
User ID: {1}
|
||||
</div>
|
||||
@@ -22,17 +22,17 @@
|
||||
Role: {2}
|
||||
</div>
|
||||
<div>
|
||||
Before logging in for the first time, please modify the account information through this link:
|
||||
<a href='{3}' style='margin-left:1px;font-size:14px;text-decoration: none;display: inline-block;color:#00D1B2;border-radius: 5px;line-height: 40px;text-align: center;'>
|
||||
Initialize account information
|
||||
Before logging in for the first time, please initialize your account information through this link:
|
||||
<a href='{3}' style='margin-left:30px;font-size:12px;text-decoration: none;display: inline-block;color:#00D1B2;border-radius: 5px;line-height: 40px;text-align: center;font-size: 16px'>
|
||||
Initialize Account Information
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div style='line-height: 24px;font-size: 14px;color:#333;margin-top: 20px;padding-bottom: 40px;'>
|
||||
<div style='line-height: 24px;font-size: 16px;color:#333;margin-top: 20px;padding-bottom: 40px;'>
|
||||
<div>Best regards,</div>
|
||||
<div style="font-size: 14px;">{company}</div>
|
||||
<div style="font-size: 16px;">Extensive Imaging</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
尊敬的 {0} ,您好:
|
||||
</div>
|
||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
|
||||
{company abbreviation}将您的账户密码已重置,账户信息如下:
|
||||
展影医疗将您的账户密码已重置,账户信息如下:
|
||||
</div>
|
||||
<span style="color: #00D1B2"></span>
|
||||
<div style='border: 1px solid #eee;box-sizing:border-box;width: 50%;background: #fff;padding: 20px;line-height: 40px;font-size: 14px;border-radius: 5px;margin-left: 60px;margin-bottom: 30px;'>
|
||||
@@ -29,7 +29,7 @@
|
||||
|
||||
<div style='line-height: 24px;font-size: 14px;color:#333;margin-top: 20px;padding-bottom: 40px;'>
|
||||
<div>祝您顺利!</div>
|
||||
<div style="font-size: 14px;">{company}</div>
|
||||
<div style="font-size: 14px;">展影医疗</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -4,17 +4,17 @@
|
||||
<meta charset='UTF-8'>
|
||||
<title>Title</title>
|
||||
</head>
|
||||
<body style='font-family: 微软雅黑;padding: 0;margin: 0;'>
|
||||
<div style='padding-left: 40px;background: #f6f6f6'>
|
||||
<body style='font-family: Times New Roman;padding: 0;margin: 0;'>
|
||||
<div style='padding-left: 40px;background: #f6f6f6;font-size: 16px'>
|
||||
<div style='padding-top: 20px;'>
|
||||
<div style='line-height: 40px;font-size: 18px'>
|
||||
<div style='line-height: 40px;font-size: 16px'>
|
||||
Dear {0} ,
|
||||
</div>
|
||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
|
||||
{company abbreviation} has reset your account password, and the account information is as follows:
|
||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;font-size: 16px'>
|
||||
Extensive Imaging has reset your password.<br>The account information is as follows:
|
||||
</div>
|
||||
<span style="color: #00D1B2"></span>
|
||||
<div style='border: 1px solid #eee;box-sizing:border-box;width: 50%;background: #fff;padding: 20px;line-height: 40px;font-size: 14px;border-radius: 5px;margin-left: 60px;margin-bottom: 30px;'>
|
||||
<div style='border: 1px solid #eee;box-sizing:border-box;width: 50%;background: #fff;padding: 20px;line-height: 40px;font-size: 12px;border-radius: 5px;margin-left: 60px;margin-bottom: 30px;font-size: 16px'>
|
||||
<div>
|
||||
User ID: {1}
|
||||
</div>
|
||||
@@ -27,9 +27,9 @@
|
||||
</div>
|
||||
|
||||
|
||||
<div style='line-height: 24px;font-size: 14px;color:#333;margin-top: 20px;padding-bottom: 40px;'>
|
||||
<div style='line-height: 24px;font-size: 16px;color:#333;margin-top: 20px;padding-bottom: 40px;'>
|
||||
<div>Best regards,</div>
|
||||
<div style="font-size: 14px;">{company}</div>
|
||||
<div style="font-size: 16px;">Extensive Imaging</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
<div style='line-height: 24px;font-size: 14px;color:#333;margin-top: 20px;padding-bottom: 40px;'>
|
||||
<div>祝您顺利!</div>
|
||||
<div style="font-size: 14px;">{company}</div>
|
||||
<div style="font-size: 14px;">展影医疗</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -4,22 +4,22 @@
|
||||
<meta charset='UTF-8'>
|
||||
<title>Title</title>
|
||||
</head>
|
||||
<body style='font-family: 微软雅黑;padding: 0;margin: 0;'>
|
||||
<div style='padding-left: 40px;background: #f6f6f6'>
|
||||
<body style='font-family: Times New Roman;padding: 0;margin: 0;font-size: 16px'>
|
||||
<div style='padding-left: 40px;background: #f6f6f6;font-size: 16px'>
|
||||
<div style='padding-top: 20px;'>
|
||||
<div style='line-height: 40px;font-size: 18px'>
|
||||
Dear:
|
||||
<div style='line-height: 40px;font-size: 16px'>
|
||||
Dear Sir or Madam:
|
||||
</div>
|
||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
|
||||
Thank you for using {company abbreviation} Cloud System.
|
||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;font-size: 16px'>
|
||||
Thank you for using our IRC imaging system.
|
||||
</div>
|
||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
|
||||
{0}。
|
||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;font-size: 16px'>
|
||||
{0}.
|
||||
</div>
|
||||
|
||||
<div style='line-height: 24px;font-size: 14px;color:#333;margin-top: 20px;padding-bottom: 40px;'>
|
||||
<div style='line-height: 24px;font-size: 16px;color:#333;margin-top: 20px;padding-bottom: 40px;'>
|
||||
<div>Best regards,</div>
|
||||
<div style="font-size: 14px;">{company}</div>
|
||||
<div style="font-size: 16px;">Extensive Imaging</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
尊敬的 {0} ,您好:
|
||||
</div>
|
||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
|
||||
{company abbreviation}作为 [{1} (试验方案号:{2 })] 项目的供应商,诚邀您参加该项目阅片相关工作。
|
||||
展影医疗作为 [{1} (试验方案号:{2 })] 项目的供应商,诚邀您参加该项目阅片相关工作。
|
||||
</div>
|
||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
|
||||
该项目采用电子化工作流,系统及您的账号信息如下:
|
||||
@@ -35,7 +35,7 @@
|
||||
</div>
|
||||
<div>
|
||||
系统登录地址:
|
||||
<a href='{6}' style='margin-left:1px;font-size:14px;text-decoration: none;display: inline-block;color:#00D1B2;border-radius: 5px;line-height: 40px;text-align: center;'>
|
||||
<a href='{6}' style='margin-left:30px;font-size:14px;text-decoration: none;display: inline-block;color:#00D1B2;border-radius: 5px;line-height: 40px;text-align: center;'>
|
||||
点击跳转
|
||||
</a>
|
||||
</div>
|
||||
@@ -43,7 +43,7 @@
|
||||
|
||||
<div style='line-height: 24px;font-size: 14px;color:#333;margin-top: 20px;padding-bottom: 40px;'>
|
||||
<div>祝您顺利!</div>
|
||||
<div style="font-size: 14px;">{company}</div>
|
||||
<div style="font-size: 14px;">展影医疗</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -4,20 +4,20 @@
|
||||
<meta charset='UTF-8'>
|
||||
<title>Title</title>
|
||||
</head>
|
||||
<body style='font-family: 微软雅黑;padding: 0;margin: 0;'>
|
||||
<body style='font-family: Times New Roman;padding: 0;margin: 0;'>
|
||||
<div style='padding-left: 40px;background: #f6f6f6'>
|
||||
<div style='padding-top: 20px;'>
|
||||
<div style='line-height: 40px;font-size: 18px'>
|
||||
<div style='line-height: 40px;font-size: 16px'>
|
||||
Dear {0},
|
||||
</div>
|
||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;font-size: 16px'>
|
||||
{company abbreviation} invites you to participate in the IRC work of a trial ({1} , {2}).
|
||||
As the service provider of {1} trial(Protocol ID is {2}), Extensive Imaging invites you to participate in the independent assessment work of this trial.
|
||||
</div>
|
||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
|
||||
Your account information is as follows:
|
||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;font-size: 16px'>
|
||||
Digital workflow is implemented for this trial, and your account information is as follows:
|
||||
</div>
|
||||
<span style="color: #00D1B2"></span>
|
||||
<div style='border: 1px solid #eee;box-sizing:border-box;width: 50%;background: #fff;padding: 20px;line-height: 40px;font-size: 14px;border-radius: 5px;margin-left: 60px;margin-bottom: 30px;'>
|
||||
<div style='border: 1px solid #eee;box-sizing:border-box;width: 50%;background: #fff;padding: 20px;line-height: 40px;font-size: 14px;border-radius: 5px;margin-left: 60px;margin-bottom: 30px;font-size: 16px'>
|
||||
<div>
|
||||
Trial ID: {3}
|
||||
</div>
|
||||
@@ -28,23 +28,22 @@
|
||||
Study Name: {1}
|
||||
</div>
|
||||
<div>
|
||||
User ID: {4}
|
||||
User Name: {4}
|
||||
</div>
|
||||
<div>
|
||||
Role: {5}
|
||||
</div>
|
||||
<div>
|
||||
Login URL:
|
||||
<a href='{6}' style='margin-left:1px;font-size:14px;text-decoration: none;display: inline-block;color:#00D1B2;border-radius: 5px;line-height: 40px;text-align: center;'>
|
||||
Click to login
|
||||
Login URL:
|
||||
<a href='{6}' style='margin-left:30px;font-size:14px;text-decoration: none;display: inline-block;color:#00D1B2;border-radius: 5px;line-height: 40px;text-align: center;'>
|
||||
click redirect
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div style='line-height: 24px;font-size: 14px;color:#333;margin-top: 20px;padding-bottom: 40px;'>
|
||||
<div style='line-height: 24px;font-size: 16px;color:#333;margin-top: 20px;padding-bottom: 40px;'>
|
||||
<div>Best regards,</div>
|
||||
<div style="font-size: 14px;">{company}</div>
|
||||
<div style="font-size: 16px;">Extensive Imaging</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
尊敬的 {0} ,您好:
|
||||
</div>
|
||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
|
||||
{company abbreviation}作为 [{1} (试验方案号:{2 })] 项目的IRC供应商,诚邀您参加该项目IRC阅片相关工作,欢迎您提供指导和建议,非常感谢!
|
||||
展影医疗作为 [{1} (试验方案号:{2 })] 项目的供应商,诚邀您参加该项目阅片相关工作,欢迎您提供指导和建议,非常感谢!
|
||||
</div>
|
||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
|
||||
该项目采用电子化工作流,系统及您的账号信息如下:
|
||||
@@ -35,7 +35,7 @@
|
||||
</div>
|
||||
<div>
|
||||
首次登陆前,请通过该链接修改您的账户信息:
|
||||
<a href='{6}' style='margin-left:1px;font-size:14px;text-decoration: none;display: inline-block;color:#00D1B2;border-radius: 5px;line-height: 40px;text-align: center;'>
|
||||
<a href='{6}' style='margin-left:30px;font-size:14px;text-decoration: none;display: inline-block;color:#00D1B2;border-radius: 5px;line-height: 40px;text-align: center;'>
|
||||
初始化账号信息
|
||||
</a>
|
||||
</div>
|
||||
@@ -44,7 +44,7 @@
|
||||
|
||||
<div style='line-height: 24px;font-size: 14px;color:#333;margin-top: 20px;padding-bottom: 40px;'>
|
||||
<div>祝您顺利!</div>
|
||||
<div style="font-size: 14px;">{company}</div>
|
||||
<div style="font-size: 14px;">展影医疗</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -4,20 +4,21 @@
|
||||
<meta charset='UTF-8'>
|
||||
<title>Title</title>
|
||||
</head>
|
||||
<body style='font-family: 微软雅黑;padding: 0;margin: 0;'>
|
||||
<body style='font-family: Times New Roman;padding: 0;margin: 0;'>
|
||||
<div style='padding-left: 40px;background: #f6f6f6'>
|
||||
<div style='padding-top: 20px;'>
|
||||
<div style='line-height: 40px;font-size: 18px'>
|
||||
<div style='line-height: 40px;font-size: 16px'>
|
||||
Dear {0},
|
||||
</div>
|
||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
|
||||
{company abbreviation} invites you to participate in the IRC work of a trial ({1}, {2}).
|
||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;font-size: 16px'>
|
||||
As the service provider of {1}trial (Protocol ID is {2}), Extensive Imaging invites you to participate in the independent assessment work of this trial.
|
||||
</div>
|
||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
|
||||
Your account information is as follows:
|
||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;font-size: 16px'>
|
||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;font-size: 16px'>
|
||||
Digital workflow is implemented for this trial, and your account information is as follows:
|
||||
</div>
|
||||
<span style="color: #00D1B2"></span>
|
||||
<div style='border: 1px solid #eee;box-sizing:border-box;width: 50%;background: #fff;padding: 20px;line-height: 40px;font-size: 14px;border-radius: 5px;margin-left: 60px;margin-bottom: 30px;'>
|
||||
<div style='border: 1px solid #eee;box-sizing:border-box;width: 50%;background: #fff;padding: 20px;line-height: 40px;font-size: 14px;border-radius: 5px;margin-left: 60px;margin-bottom: 30px;font-size: 16px'>
|
||||
<div>
|
||||
Trial ID: {3}
|
||||
</div>
|
||||
@@ -34,17 +35,17 @@
|
||||
Role: {5}
|
||||
</div>
|
||||
<div>
|
||||
Before logging in for the first time, please modify the account information through this link:
|
||||
<a href='{6}' style='margin-left:1px;font-size:14px;text-decoration: none;display: inline-block;color:#00D1B2;border-radius: 5px;line-height: 40px;text-align: center;'>
|
||||
Initialize account information
|
||||
Before logging in for the first time, please modify your account information through this link:
|
||||
<a href='{6}' style='margin-left:30px;font-size:16px;text-decoration: none;display: inline-block;color:#00D1B2;border-radius: 5px;line-height: 40px;text-align: center;'>
|
||||
Initialize Account Information
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div style='line-height: 24px;font-size: 14px;color:#333;margin-top: 20px;padding-bottom: 40px;'>
|
||||
<div style='line-height: 24px;font-size: 14px;color:#333;margin-top: 20px;padding-bottom: 40px;font-size: 16px'>
|
||||
<div>Best regards,</div>
|
||||
<div style="font-size: 14px;">{company}</div>
|
||||
<div style="font-size: 16px;">Extensive Imaging</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
尊敬的 {0} ,您好:
|
||||
</div>
|
||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
|
||||
您填写的中心调研表被驳回,详细信息如下:
|
||||
您好, 您填写的中心调研表被驳回,详细信息如下:
|
||||
</div>
|
||||
<span style="color: #00D1B2"></span>
|
||||
<div style='border: 1px solid #eee;box-sizing:border-box;width: 50%;background: #fff;padding: 20px;line-height: 40px;font-size: 14px;border-radius: 5px;margin-left: 60px;margin-bottom: 30px;'>
|
||||
@@ -41,7 +41,7 @@
|
||||
|
||||
<div style='line-height: 24px;font-size: 14px;color:#333;margin-top: 20px;padding-bottom: 40px;'>
|
||||
<div>祝您顺利!</div>
|
||||
<div style="font-size: 14px;">{company}</div>
|
||||
<div style="font-size: 14px;">展影医疗</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -4,17 +4,17 @@
|
||||
<meta charset='UTF-8'>
|
||||
<title>Title</title>
|
||||
</head>
|
||||
<body style='font-family: 微软雅黑;padding: 0;margin: 0;'>
|
||||
<div style='padding-left: 40px;background: #f6f6f6'>
|
||||
<div style='padding-top: 20px;'>
|
||||
<div style='line-height: 40px;font-size: 18px'>
|
||||
Dear {0} ,
|
||||
<body style='font-family: Times New Roman;padding: 0;margin: 0;font-size: 16px'>
|
||||
<div style='padding-left: 40px;background: #f6f6f6;font-size: 16px'>
|
||||
<div style='padding-top: 20px;font-size: 16px'>
|
||||
<div style='line-height: 40px;font-size: 16px'>
|
||||
Dear {0} ,
|
||||
</div>
|
||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
|
||||
The site survey form you filled in has been rejected. The details are as follows:
|
||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;font-size: 16px'>
|
||||
The site survey questionnaire you filled in has been rejected. Details are as follows:
|
||||
</div>
|
||||
<span style="color: #00D1B2"></span>
|
||||
<div style='border: 1px solid #eee;box-sizing:border-box;width: 50%;background: #fff;padding: 20px;line-height: 40px;font-size: 14px;border-radius: 5px;margin-left: 60px;margin-bottom: 30px;'>
|
||||
<div style='border: 1px solid #eee;box-sizing:border-box;width: 50%;background: #fff;padding: 20px;line-height: 40px;font-size: 14px;border-radius: 5px;margin-left: 60px;margin-bottom: 30px;font-size: 16px'>
|
||||
<div>
|
||||
Trial ID: {1}
|
||||
</div>
|
||||
@@ -28,20 +28,20 @@
|
||||
Site ID: {4}
|
||||
</div>
|
||||
<div>
|
||||
Site name: {5}
|
||||
Site Name: {5}
|
||||
</div>
|
||||
<div>
|
||||
Reason for rejection: {6}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<a href='{7}' style='margin-left:60px;font-size:14px;padding: 10px;text-decoration: none;display:{8} ;height: 40px;width: 140px;background: #00D1B2;color:#fff;border-radius: 5px;line-height: 40px;text-align: center;margin-bottom: 100px;'>
|
||||
Login and view
|
||||
<a href='{7}' style='margin-left:60px;font-size:16px;padding: 10px;text-decoration: none;display:{8} ;height: 40px;width: 140px;background: #00D1B2;color:#fff;border-radius: 5px;line-height: 40px;text-align: center;margin-bottom: 100px;font-size: 16px'>
|
||||
Login & View
|
||||
</a>
|
||||
|
||||
<div style='line-height: 24px;font-size: 14px;color:#333;margin-top: 20px;padding-bottom: 40px;'>
|
||||
<div style='line-height: 24px;font-size: 16px;color:#333;margin-top: 20px;padding-bottom: 40px;'>
|
||||
<div>Best regards,</div>
|
||||
<div style="font-size: 14px;">{company}</div>
|
||||
<div style="font-size: 16px;">Extensive Imaging</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
尊敬的 {0} ,您好:
|
||||
</div>
|
||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
|
||||
{company abbreviation}作为 [{1} (试验方案号:{2 })] 项目的供应商,诚邀您参加该项目相关工作,欢迎您提供指导和建议,非常感谢!
|
||||
展影医疗作为 [{1} (试验方案号:{2 })] 项目的供应商,诚邀您参加该项目相关工作,欢迎您提供指导和建议,非常感谢!
|
||||
</div>
|
||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
|
||||
该项目采用电子化工作流,系统及您的账号信息如下:
|
||||
@@ -35,7 +35,7 @@
|
||||
</div>
|
||||
<div>
|
||||
系统登录地址:
|
||||
<a href='{6}' style='margin-left:5px;font-size:14px;text-decoration: none;display: inline-block;color:#00D1B2;border-radius: 5px;line-height: 40px;text-align: center;'>
|
||||
<a href='{6}' style='margin-left:30px;font-size:14px;text-decoration: none;display: inline-block;color:#00D1B2;border-radius: 5px;line-height: 40px;text-align: center;'>
|
||||
点击跳转
|
||||
</a>
|
||||
</div>
|
||||
@@ -44,7 +44,7 @@
|
||||
|
||||
<div style='line-height: 24px;font-size: 14px;color:#333;margin-top: 20px;padding-bottom: 40px;'>
|
||||
<div>祝您顺利!</div>
|
||||
<div style="font-size: 14px;">{company}</div>
|
||||
<div style="font-size: 14px;">展影医疗</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -4,20 +4,20 @@
|
||||
<meta charset='UTF-8'>
|
||||
<title>Title</title>
|
||||
</head>
|
||||
<body style='font-family: 微软雅黑;padding: 0;margin: 0;'>
|
||||
<div style='padding-left: 40px;background: #f6f6f6'>
|
||||
<div style='padding-top: 20px;'>
|
||||
<div style='line-height: 40px;font-size: 18px'>
|
||||
<body style='font-family: Times New Roman;padding: 0;margin: 0;font-size: 16px'>
|
||||
<div style='padding-left: 40px;background: #f6f6f6;font-size: 16px'>
|
||||
<div style='padding-top: 20px;font-size: 16px'>
|
||||
<div style='line-height: 40px;font-size: 16px'>
|
||||
Dear {0},
|
||||
</div>
|
||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;font-size: 16px'>
|
||||
{company abbreviation} invites you to participate in the IRC work of a trial ({1}, {2}).
|
||||
As the service provider of {1} trial (Protocol ID is {2}), Extensive Imaging invites you to participate in related work of this trial.
|
||||
</div>
|
||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
|
||||
Your account information is as follows:
|
||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;font-size: 16px'>
|
||||
Digital workflow is implemented for this trial, and your account information is as follows:
|
||||
</div>
|
||||
<span style="color: #00D1B2"></span>
|
||||
<div style='border: 1px solid #eee;box-sizing:border-box;width: 50%;background: #fff;padding: 20px;line-height: 40px;font-size: 14px;border-radius: 5px;margin-left: 60px;margin-bottom: 30px;'>
|
||||
<div style='border: 1px solid #eee;box-sizing:border-box;width: 50%;background: #fff;padding: 20px;line-height: 40px;font-size: 14px;border-radius: 5px;margin-left: 60px;margin-bottom: 30px;font-size: 16px'>
|
||||
<div>
|
||||
Trial ID: {3}
|
||||
</div>
|
||||
@@ -28,23 +28,23 @@
|
||||
Study Name: {1}
|
||||
</div>
|
||||
<div>
|
||||
User ID: {4}
|
||||
User Name: {4}
|
||||
</div>
|
||||
<div>
|
||||
Role: {5}
|
||||
</div>
|
||||
<div>
|
||||
Login URL:
|
||||
<a href='{6}' style='margin-left:1px;font-size:14px;text-decoration: none;display: inline-block;color:#00D1B2;border-radius: 5px;line-height: 40px;text-align: center;'>
|
||||
Click to login
|
||||
Login URL:
|
||||
<a href='{6}' style='margin-left:30px;font-size:14px;text-decoration: none;display: inline-block;color:#00D1B2;border-radius: 5px;line-height: 40px;text-align: center;'>
|
||||
click redirect
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div style='line-height: 24px;font-size: 14px;color:#333;margin-top: 20px;padding-bottom: 40px;'>
|
||||
<div style='line-height: 24px;font-size: 16px;color:#333;margin-top: 20px;padding-bottom: 40px;'>
|
||||
<div>Best regards,</div>
|
||||
<div style="font-size: 14px;">{company}</div>
|
||||
<div style="font-size: 16px;">Extensive Imaging</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
尊敬的 {0} ,您好:
|
||||
</div>
|
||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
|
||||
{company abbreviation}作为 [{1} (试验方案号:{2 })] 项目的IRC供应商,诚邀您参加该项目IRC相关工作,欢迎您提供指导和建议,非常感谢!
|
||||
展影医疗作为 [{1} (试验方案号:{2 })] 项目的供应商,诚邀您参加该项目相关工作。
|
||||
</div>
|
||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
|
||||
该项目采用电子化工作流,系统及您的账号信息如下:
|
||||
@@ -35,7 +35,7 @@
|
||||
</div>
|
||||
<div>
|
||||
首次登陆前,请通过该链接修改您的账户信息:
|
||||
<a href='{6}' style='margin-left:1px;font-size:14px;text-decoration: none;display: inline-block;color:#00D1B2;border-radius: 5px;line-height: 40px;text-align: center;'>
|
||||
<a href='{6}' style='margin-left:30px;font-size:14px;text-decoration: none;display: inline-block;color:#00D1B2;border-radius: 5px;line-height: 40px;text-align: center;'>
|
||||
初始化账号信息
|
||||
</a>
|
||||
</div>
|
||||
@@ -44,7 +44,7 @@
|
||||
|
||||
<div style='line-height: 24px;font-size: 14px;color:#333;margin-top: 20px;padding-bottom: 40px;'>
|
||||
<div>祝您顺利!</div>
|
||||
<div style="font-size: 14px;">{company}</div>
|
||||
<div style="font-size: 14px;">展影医疗</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -4,20 +4,20 @@
|
||||
<meta charset='UTF-8'>
|
||||
<title>Title</title>
|
||||
</head>
|
||||
<body style='font-family: 微软雅黑;padding: 0;margin: 0;'>
|
||||
<body style='font-family: Times New Roman;padding: 0;margin: 0;font-size: 16px'>
|
||||
<div style='padding-left: 40px;background: #f6f6f6'>
|
||||
<div style='padding-top: 20px;'>
|
||||
<div style='line-height: 40px;font-size: 18px'>
|
||||
<div style='line-height: 40px;font-size: 16px'>
|
||||
Dear {0},
|
||||
</div>
|
||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
|
||||
{company abbreviation} invites you to participate in the IRC work of a trial ({1}, {2}).
|
||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;font-size: 16px'>
|
||||
As the service provider of {1} trial (Protocol ID is {2}), Extensive Imaging invites you to participate in related work of this trial.
|
||||
</div>
|
||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
|
||||
Your account information is as follows:
|
||||
Digital workflow is implemented for this trial, and your account information is as follows:
|
||||
</div>
|
||||
<span style="color: #00D1B2"></span>
|
||||
<div style='border: 1px solid #eee;box-sizing:border-box;width: 50%;background: #fff;padding: 20px;line-height: 40px;font-size: 14px;border-radius: 5px;margin-left: 60px;margin-bottom: 30px;'>
|
||||
<div style='border: 1px solid #eee;box-sizing:border-box;width: 50%;background: #fff;padding: 20px;line-height: 40px;font-size: 14px;border-radius: 5px;margin-left: 60px;margin-bottom: 30px;font-size: 16px'>
|
||||
<div>
|
||||
Trial ID: {3}
|
||||
</div>
|
||||
@@ -28,23 +28,23 @@
|
||||
Study Name: {1}
|
||||
</div>
|
||||
<div>
|
||||
User ID: {4}
|
||||
User Name: {4}
|
||||
</div>
|
||||
<div>
|
||||
Role: {5}
|
||||
</div>
|
||||
<div>
|
||||
Before logging in for the first time, please modify the account information through this link:
|
||||
<a href='{6}' style='margin-left:1px;font-size:14px;text-decoration: none;display: inline-block;color:#00D1B2;border-radius: 5px;line-height: 40px;text-align: center;'>
|
||||
Initialize the account information
|
||||
Before logging in for the first time, please modify your account information through this link:
|
||||
<a href='{6}' style='margin-left:30px;font-size:16px;text-decoration: none;display: inline-block;color:#00D1B2;border-radius: 5px;line-height: 40px;text-align: center;'>
|
||||
Initialize Account Information
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div style='line-height: 24px;font-size: 14px;color:#333;margin-top: 20px;padding-bottom: 40px;'>
|
||||
<div style='line-height: 24px;font-size: 16px;color:#333;margin-top: 20px;padding-bottom: 40px;'>
|
||||
<div>Best regards,</div>
|
||||
<div style="font-size: 14px;">{company}</div>
|
||||
<div style="font-size: 16px;">Extensive Imaging</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -8,20 +8,18 @@
|
||||
<div style='padding-left: 40px;background: #f6f6f6'>
|
||||
<div style='padding-top: 20px;'>
|
||||
<div style='line-height: 40px;font-size: 18px'>
|
||||
{0}您好,
|
||||
亲爱的用户 ,
|
||||
</div>
|
||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
|
||||
感谢您使用展影云平台。
|
||||
</div>
|
||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
|
||||
{1},验证码是: <span style="font-weight:bold">{2}</span>。
|
||||
</div>
|
||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
|
||||
请在3分钟内输入该验证码,进行后续操作
|
||||
{0},验证码是: {1},请在3分钟内输入该验证码,进行后续操作。如非本人操作,请忽略该邮件。
|
||||
</div>
|
||||
|
||||
<div style='line-height: 24px;font-size: 14px;color:#333;margin-top: 20px;padding-bottom: 40px;'>
|
||||
<div>祝您顺利!</div>
|
||||
<div style="font-size: 14px;">{company}</div>
|
||||
<div style="font-size: 14px;">展影医疗</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -4,25 +4,25 @@
|
||||
<meta charset='UTF-8'>
|
||||
<title>Title</title>
|
||||
</head>
|
||||
<body style='font-family: 微软雅黑;padding: 0;margin: 0;'>
|
||||
<body style='font-family: Times New Roman;padding: 0;margin: 0;'>
|
||||
<div style='padding-left: 40px;background: #f6f6f6'>
|
||||
<div style='padding-top: 20px;'>
|
||||
<div style='line-height: 40px;font-size: 18px'>
|
||||
Dear {0},
|
||||
<div style='line-height: 40px;font-size: 16px'>
|
||||
Dear Sir or Madam:
|
||||
</div>
|
||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
|
||||
{1}
|
||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;font-size: 16px'>
|
||||
Thank you for using our imaging system.
|
||||
</div>
|
||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
|
||||
The verification code is <span style="font-weight:bold">{2}</span>.
|
||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;font-size: 16px'>
|
||||
{0}, the verification code is {1}. Please enter this code within 3 minutes for follow-up operations.
|
||||
</div>
|
||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
|
||||
This code will expire in 3 minutes.
|
||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;font-size: 16px'>
|
||||
If you are not the intended person, please ignore this email.
|
||||
</div>
|
||||
|
||||
<div style='line-height: 24px;font-size: 14px;color:#333;margin-top: 20px;padding-bottom: 40px;'>
|
||||
<div style='line-height: 24px;font-size: 14px;color:#333;margin-top: 20px;padding-bottom: 40px;font-size: 16px'>
|
||||
<div>Best regards,</div>
|
||||
<div style="font-size: 14px;">{company}</div>
|
||||
<div style="font-size: 16px;">Extensive Imaging</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Binary file not shown.
@@ -276,10 +276,8 @@ var abp = abp || {};
|
||||
}
|
||||
|
||||
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');
|
||||
if (!password) {
|
||||
alert('PassWord Can Not Be Null');
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -323,9 +321,6 @@ var abp = abp || {};
|
||||
UserName: usernameOrEmailAddress,
|
||||
Password: hex_md5(password),
|
||||
}
|
||||
if (pwdMd5 != '') {
|
||||
parm.Password = pwdMd5;
|
||||
}
|
||||
xhr.send(JSON.stringify(parm));
|
||||
//xhr.send("{" + "userName:'" + usernameOrEmailAddress + "'," + "passWord:'" + password + "'}");
|
||||
|
||||
@@ -420,8 +415,7 @@ var abp = abp || {};
|
||||
//Inputs
|
||||
createInput(modalUxContent, 'tenancyName', 'Tenancy Name (Leave empty for Host)');
|
||||
createInput(modalUxContent, 'userName', 'Username or email address','text','cyldev');
|
||||
createInput(modalUxContent, 'password', 'Password', 'password', '123456');
|
||||
createInput(modalUxContent, 'pwdMd5', 'PwdMd5', 'text', '');
|
||||
createInput(modalUxContent, 'password', 'Password','password','123456');
|
||||
|
||||
//Buttons
|
||||
var authBtnWrapper = document.createElement('div');
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using EasyCaching.Core;
|
||||
using IRaCIS.Core.Domain;
|
||||
using IRaCIS.Core.Infra.EFCore;
|
||||
using IRaCIS.Core.Domain.Models;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Quartz;
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using IRaCIS.Core.Application.ViewModel;
|
||||
using IRaCIS.Core.Application.Contracts;
|
||||
|
||||
namespace IRaCIS.Application.Services.BackGroundJob
|
||||
{
|
||||
|
||||
public class CancelTaskQuartZJob : IJob
|
||||
{
|
||||
|
||||
private readonly IRepository<Subject> _subjectRepository;
|
||||
private readonly ILogger<CancelTaskQuartZJob> _logger;
|
||||
private readonly IRepository<VisitTask> _visitTaskRepository;
|
||||
|
||||
public CancelTaskQuartZJob(IRepository<Subject> subjectRepository, IEasyCachingProvider provider, ILogger<CancelTaskQuartZJob> logger, IRepository<VisitTask> visitTaskRepository)
|
||||
{
|
||||
_subjectRepository = subjectRepository;
|
||||
_logger = logger;
|
||||
_visitTaskRepository = visitTaskRepository;
|
||||
}
|
||||
|
||||
public async Task Execute(IJobExecutionContext context)
|
||||
|
||||
{
|
||||
_logger.LogInformation($"开始执行QuartZ定时取消任务作业");
|
||||
|
||||
try
|
||||
{
|
||||
JobDataMap dataMap = context.JobDetail.JobDataMap;
|
||||
|
||||
bool isInOrder = (bool)dataMap.Get("IsInOrder");
|
||||
|
||||
if (isInOrder)
|
||||
{
|
||||
Guid subjectId = (Guid)dataMap.Get("SubjectId");
|
||||
|
||||
Guid trialReadingCriterionId = (Guid)dataMap.Get("TrialReadingCriterionId");
|
||||
|
||||
await _visitTaskRepository.BatchUpdateNoTrackingAsync(t => t.SubjectId == subjectId && t.TrialReadingCriterionId==trialReadingCriterionId, u => new VisitTask() { SubjectCriterionClaimUserId = null });
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
Guid id = (Guid)dataMap.Get("VisitTaskId");
|
||||
|
||||
await _visitTaskRepository.UpdatePartialFromQueryAsync(t => t.Id == id && t.ReadingTaskState!=ReadingTaskState.HaveSigned, u => new VisitTask() { DoctorUserId = null }, true);
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError($"QuartZ定时取消任务异常" + e.Message);
|
||||
}
|
||||
_logger.LogInformation("QuartZ定时取消任务作业结束");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,14 @@
|
||||
using Hangfire;
|
||||
using EasyCaching.Core;
|
||||
using Hangfire;
|
||||
using Hangfire.Storage;
|
||||
using IRaCIS.Core.Application.Helper;
|
||||
using IRaCIS.Core.Application.Service;
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using IRaCIS.Core.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.SqlServer.Query.Internal;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json;
|
||||
using SharpCompress.Common;
|
||||
using ZiggyCreatures.Caching.Fusion;
|
||||
|
||||
namespace IRaCIS.Application.Services.BackGroundJob
|
||||
{
|
||||
@@ -16,50 +16,143 @@ namespace IRaCIS.Application.Services.BackGroundJob
|
||||
public interface IIRaCISHangfireJob
|
||||
{
|
||||
|
||||
//Task MemoryCacheTrialStatusAsync();
|
||||
Task MemoryCacheTrialStatusAsync();
|
||||
|
||||
Task InitHangfireJobTaskAsync();
|
||||
|
||||
}
|
||||
public class IRaCISCHangfireJob(ILogger<IRaCISCHangfireJob> _logger,
|
||||
IRepository<Internationalization> _internationalizationRepository,
|
||||
IRepository<TrialEmailNoticeConfig> _trialEmailNoticeConfigRepository
|
||||
) : IIRaCISHangfireJob
|
||||
public class IRaCISCHangfireJob : IIRaCISHangfireJob
|
||||
{
|
||||
public static string JsonFileFolder = Path.Combine(AppContext.BaseDirectory, StaticData.Folder.Resources);
|
||||
|
||||
private readonly IRepository<Trial> _trialRepository;
|
||||
private readonly IEasyCachingProvider _provider;
|
||||
private readonly ILogger<IRaCISCHangfireJob> _logger;
|
||||
|
||||
private readonly IRepository<TrialEmailNoticeConfig> _trialEmailNoticeConfigRepository;
|
||||
private readonly IRepository<Internationalization> _internationalizationRepository;
|
||||
|
||||
|
||||
|
||||
public IRaCISCHangfireJob(IRepository<Trial> trialRepository, ILogger<IRaCISCHangfireJob> logger, IEasyCachingProvider provider, IRepository<TrialEmailNoticeConfig> trialEmailNoticeConfigRepository, IRepository<Internationalization> internationalizationRepository)
|
||||
{
|
||||
_trialRepository = trialRepository;
|
||||
_provider = provider;
|
||||
_logger = logger;
|
||||
_trialEmailNoticeConfigRepository = trialEmailNoticeConfigRepository;
|
||||
_internationalizationRepository = internationalizationRepository;
|
||||
}
|
||||
|
||||
public async Task InitHangfireJobTaskAsync()
|
||||
{
|
||||
_logger.LogInformation("项目启动 hangfire 任务初始化 执行开始~");
|
||||
|
||||
//初始化国际化
|
||||
|
||||
await InternationalizationHelper.InitInternationlizationDataAndWatchJsonFileAsync(_internationalizationRepository);
|
||||
//项目状态 立即加载到缓存中
|
||||
await MemoryCacheTrialStatusAsync();
|
||||
|
||||
|
||||
//创建项目缓存 定时任务
|
||||
HangfireJobHelper.AddOrUpdateInitCronJob<IIRaCISHangfireJob>("RecurringJob_Cache_TrialState", t => t.MemoryCacheTrialStatusAsync(), Cron.Daily());
|
||||
|
||||
//初始化
|
||||
|
||||
await InitInternationlizationDataAndWatchJsonFileAsync();
|
||||
|
||||
//创建邮件定时任务
|
||||
await InitSysAndTrialCronJobAsync();
|
||||
|
||||
#region 废弃
|
||||
////项目状态 立即加载到缓存中
|
||||
//await MemoryCacheTrialStatusAsync();
|
||||
|
||||
////await MemoryCacheAnonymizeData();
|
||||
|
||||
|
||||
////创建项目缓存 定时任务
|
||||
//HangfireJobHelper.AddOrUpdateInitCronJob<IIRaCISHangfireJob>("RecurringJob_Cache_TrialState", t => t.MemoryCacheTrialStatusAsync(), Cron.Daily());
|
||||
#endregion
|
||||
|
||||
|
||||
_logger.LogInformation("项目启动 hangfire 任务初始化 执行结束");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 缓存项目状态
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task MemoryCacheTrialStatusAsync()
|
||||
{
|
||||
|
||||
var list = await _trialRepository.Select(t => new { TrialId = t.Id, TrialStatusStr = t.TrialStatusStr })
|
||||
.ToListAsync();
|
||||
|
||||
list.ForEach(t => _provider.Set(t.TrialId.ToString(), t.TrialStatusStr, TimeSpan.FromDays(7)));
|
||||
|
||||
}
|
||||
|
||||
|
||||
#region 国际化 初始化
|
||||
|
||||
|
||||
public async Task InitInternationlizationDataAndWatchJsonFileAsync()
|
||||
{
|
||||
//查询数据库的数据
|
||||
var toJsonList = await _internationalizationRepository.Where(t => t.InternationalizationType == 1).Select(t => new
|
||||
{
|
||||
t.Code,
|
||||
t.Value,
|
||||
t.ValueCN
|
||||
}).ToListAsync();
|
||||
|
||||
//组织成json 文件
|
||||
|
||||
var usJsonPath = Path.Combine(JsonFileFolder, StaticData.En_US_Json);
|
||||
var cnJsonPath = Path.Combine(JsonFileFolder, StaticData.Zh_CN_Json);
|
||||
|
||||
|
||||
//本地静态文件国际化需要
|
||||
foreach (var tojsonItem in toJsonList)
|
||||
{
|
||||
StaticData.En_US_Dic[tojsonItem.Code] = tojsonItem.Value;
|
||||
StaticData.Zh_CN_Dic[tojsonItem.Code] = tojsonItem.ValueCN;
|
||||
}
|
||||
|
||||
File.WriteAllText(usJsonPath, JsonConvert.SerializeObject(StaticData.En_US_Dic));
|
||||
File.WriteAllText(cnJsonPath, JsonConvert.SerializeObject(StaticData.Zh_CN_Dic));
|
||||
|
||||
|
||||
//监测Json文件变更 实时刷新数据
|
||||
WatchJsonFile(usJsonPath);
|
||||
WatchJsonFile(cnJsonPath);
|
||||
|
||||
}
|
||||
|
||||
public void WatchJsonFile(string filePath)
|
||||
{
|
||||
if (!File.Exists(filePath))
|
||||
{
|
||||
throw new BusinessValidationFailedException(StaticData.International("IRaCISCHangfireJob_FileNotFound"));
|
||||
}
|
||||
|
||||
FileSystemWatcher watcher = new FileSystemWatcher(Path.GetDirectoryName(filePath), Path.GetFileName(filePath));
|
||||
watcher.Changed += (sender, e) => LoadJsonFile(filePath);
|
||||
watcher.EnableRaisingEvents = true;
|
||||
|
||||
}
|
||||
|
||||
|
||||
private void LoadJsonFile(string filePath)
|
||||
{
|
||||
|
||||
IConfigurationBuilder builder = new ConfigurationBuilder().AddJsonFile(filePath);
|
||||
|
||||
IConfigurationRoot enConfiguration = builder.Build();
|
||||
|
||||
foreach (IConfigurationSection section in enConfiguration.GetChildren())
|
||||
{
|
||||
if (filePath.Contains(StaticData.En_US_Json))
|
||||
{
|
||||
StaticData.En_US_Dic[section.Key] = section.Value;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
StaticData.Zh_CN_Dic[section.Key] = section.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public async Task InitSysAndTrialCronJobAsync()
|
||||
{
|
||||
@@ -101,9 +194,6 @@ namespace IRaCIS.Application.Services.BackGroundJob
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user