Compare commits
1162 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 431d2e7a97 | |||
| 7caac94d97 | |||
| ceedafb6b9 | |||
| 04e7cd5463 | |||
| ba37700812 | |||
| cdadad9ec5 | |||
| 795e128ef9 | |||
| 658f9c7c40 | |||
| 5bce1cafa3 | |||
| f539496f9f | |||
| fac077d2b7 | |||
| 874c8718b1 | |||
| fea009bf0c | |||
| 5ae837ac19 | |||
| eb55fafbc3 | |||
| 88893c9825 | |||
| c6a18aa18a | |||
| 8829723c19 | |||
| 8768fe0437 | |||
| cb7098830c | |||
| c69c139e00 | |||
| acb7e798e0 | |||
| 63d7bd5607 | |||
| 99e3610be6 | |||
| 50712aefc6 | |||
| d5d728fea6 | |||
| 9e71732061 | |||
| c24adcd09c | |||
| 0da842871c | |||
| 03702477d6 | |||
| e4a36f4689 | |||
| cb6271daef | |||
| 11fa028cd4 | |||
| 5a1a710d5a | |||
| e9e4b45823 | |||
| 7693c72802 | |||
| 1bacc222be | |||
| c5355e976f | |||
| 95a84bb330 | |||
| 6a60e4696b | |||
| 7ff77554ff | |||
| ba23950cd5 | |||
| 8ec2c676f1 | |||
| b183622e13 | |||
| cd9f7f8812 | |||
| 513d773be3 | |||
| cf24583974 | |||
| f5fbf96772 | |||
| 8dc2301184 | |||
| 7e135367b2 | |||
| 8626a6b453 | |||
| 870dda49dd | |||
| 171b8bac34 | |||
| 7c19ecc3f4 | |||
| 3e375ea6d1 | |||
| 416a94e302 | |||
| 7760b0736e | |||
| aba7dfdec9 | |||
| 1dabadfba4 | |||
| b8f5f32596 | |||
| b6459d2f9f | |||
| dded4f2d7c | |||
| fadb7a9d0e | |||
| 55f261b080 | |||
| ccf154e5e5 | |||
| 069ee25d18 | |||
| 64fd39ceb4 | |||
| 19cf8c7559 | |||
| 3c0199cebd | |||
| 62965e5aa5 | |||
| 654fb7116b | |||
| da6746beea | |||
| 2461646d5f | |||
| 02db483119 | |||
| b5e756ffa3 | |||
| f3c29d3530 | |||
| f2d9c6fe38 | |||
| 0d4d984135 | |||
| f9c155a378 | |||
| a9a67c1db2 | |||
| 3d6d64175b | |||
| 45c74694f2 | |||
| 033c47435f | |||
| ea48967492 | |||
| 8ec35d90fb | |||
| 53f3bb5d6d | |||
| e6628e952f | |||
| 1b26ed1b65 | |||
| d0018a3ec3 | |||
| a423c7c73a | |||
| 8eacc969ed | |||
| 33b02be6ed | |||
| 9834335767 | |||
| d4d5ed0d9a | |||
| 3fc7b72a45 | |||
| e525965d89 | |||
| af988e7276 | |||
| 627f3bf4a9 | |||
| aca261ace2 | |||
| 328a8549c6 | |||
| ff71e925ca | |||
| f46bbea98e | |||
| 1d5c274e38 | |||
| e32195c322 | |||
| 935b498ab5 | |||
| 21925f6d88 | |||
| 54b75dd3d5 | |||
| 7ed54e3be3 | |||
| 3a3184a5ac | |||
| 41153e42d6 | |||
| 3ca9be0a29 | |||
| 6c5f669ab6 | |||
| 282ba96c61 | |||
| fe80604331 | |||
| 19fdd02429 | |||
| c2f72d677f | |||
| 6713fff0e7 | |||
| ff8fa94341 | |||
| 82f2185825 | |||
| 5fdf9e0606 | |||
| cf9823c682 | |||
| 73e7ed11b1 | |||
| 9adab8cef1 | |||
| 3d382635cc | |||
| e389c1e581 | |||
| 1a3ebe1fc4 | |||
| fb6f12903e | |||
| 1da4fbe23b | |||
| 4d206ebf9d | |||
| 9c4ada3e66 | |||
| 0bc0421ee3 | |||
| a79aebe772 | |||
| d3ff114b6d | |||
| 50a45e2260 | |||
| 8090668650 | |||
| d27b4af793 | |||
| b097ca1db3 | |||
| 609742c914 | |||
| 79d76dcbaf | |||
| 721ab8dfd3 | |||
| b2d90005f8 | |||
| c58d880e28 | |||
| 183fc6143f | |||
| 9a71e1fa0f | |||
| 5977ab34e9 | |||
| c71b58e0e9 | |||
| 1fcb871de2 | |||
| d7a76bc110 | |||
| 78b76da7ed | |||
| a6c04d8fbb | |||
| 303cb54e7a | |||
| 92e1a3f90b | |||
| 8c51ae350c | |||
| 91fa8e75e1 | |||
| 893bdaf0f2 | |||
| 7051e68c09 | |||
| e9bb71f5c9 | |||
| 3ee0c2ccc7 | |||
| 40c8a51a31 | |||
| d3e3ed79ba | |||
| 83d3a547d1 | |||
| 3d428af85b | |||
| c5b8743064 | |||
| 69d390da81 | |||
| ae0f601727 | |||
| d509bc1649 | |||
| 23ffe2b143 | |||
| 718ddefe97 | |||
| 7524d39213 | |||
| 7e705ccf37 | |||
| 7fc2de5eb3 | |||
| b90f5f2d85 | |||
| 97b28fd920 | |||
| 3bdd5af113 | |||
| 6dcfbb8717 | |||
| 103d4b42a3 | |||
| fc30cb39ee | |||
| 115017197b | |||
| 32931e155c | |||
| 0a1ce59670 | |||
| 42e4fe59ec | |||
| 19f4385055 | |||
| d789c48004 | |||
| 32427f0f0c | |||
| 01ade2d62f | |||
| cec038fb92 | |||
| b5d117f6fa | |||
| b98df9dae8 | |||
| 4e7ce826c8 | |||
| 7df247d5e7 | |||
| fb067534e7 | |||
| de274e00d6 | |||
| 3501f2acdf | |||
| 2dd86652f3 | |||
| bf39613789 | |||
| a64cec987d | |||
| 5b2b5fead9 | |||
| 0216665d5b | |||
| f1fadebfba | |||
| 19b647fb4a | |||
| 65e47d9918 | |||
| ab68a63bdd | |||
| 5990477fdc | |||
| 961ca49f76 | |||
| 581e412106 | |||
| 3813158b00 | |||
| 010fdf6e2c | |||
| f7c669a7d7 | |||
| 68dc09d969 | |||
| a6a6765d9b | |||
| 5931a67825 | |||
| f26441719f | |||
| 67a930a208 | |||
| f189f9f361 | |||
| b6d0791f18 | |||
| 7285a9d672 | |||
| c3daa75e0e | |||
| 564ac4d931 | |||
| e065e44ab0 | |||
| bbc737811a | |||
| d507e625c6 | |||
| e997cb2a8f | |||
| bfa2d8afce | |||
| 3840627ce7 | |||
| 8fed969bec | |||
| d50091ed6b | |||
| 86864ae575 | |||
| 0fdcc7c56e | |||
| 849f48db25 | |||
| b31821848d | |||
| e5be375308 | |||
| 095db5786b | |||
| dcfe28d950 | |||
| f9f3a1422f | |||
| 8dcd1f2f56 | |||
| 7d39bf43fe | |||
| 8b8b52e0cc | |||
| 0cdc489278 | |||
| d4cef1148b | |||
| 40d4806f22 | |||
| 48427f2740 | |||
| a3296a1ab9 | |||
| 7404fabcda | |||
| 29842f484b | |||
| daac69c0d1 | |||
| 603f6e095c | |||
| c9b1e5f92a | |||
| 6fd9bd94df | |||
| fd6673c339 | |||
| 6fe4aa60d4 | |||
| df1665307a | |||
| 1ccb31fcf2 | |||
| 55f98d735e | |||
| 4642e4e00e | |||
| c6d74309f4 | |||
| 2abada285b | |||
| d600862fb0 | |||
| 4048be622f | |||
| eec0156fe7 | |||
| 85d1930730 | |||
| 7420fdeda5 | |||
| 4e2eb7b0a0 | |||
| 68e87a6c0e | |||
| 19f9af8af8 | |||
| 34faaaf231 | |||
| 136a9e54ca | |||
| 5b63e58c1d | |||
| 70c381831d | |||
| 2bf562d027 | |||
| c7e3403cae | |||
| 2f41c21f75 | |||
| 1688392216 | |||
| 6a1a56f0bc | |||
| 0f495a4568 | |||
| 81abcf39fc | |||
| 247b11cec4 | |||
| 21ded0d466 | |||
| 36c354c8f0 | |||
| cb6f93ec0a | |||
| da8812868c | |||
| 14ec78a2fd | |||
| eccb5dc562 | |||
| c11ff02a82 | |||
| b9b095a19c | |||
| 8678c3602f | |||
| 510a35e2b4 | |||
| 5e1ec1ffb2 | |||
| 3f38cf25c5 | |||
| b32225b8bb | |||
| ecbc27fbb8 | |||
| d3f13e844d | |||
| 405454f71f | |||
| f7dd5117a0 | |||
| 60330fbd93 | |||
| 6b7542b11f | |||
| e012384dfa | |||
| 2aa63a6132 | |||
| e12935d9aa | |||
| 21cb074469 | |||
| 204cb19d96 | |||
| 238fde7d01 | |||
| b59b46f938 | |||
| 0fbfe6c0e1 | |||
| 31e2aa24b8 | |||
| 5b52a3e67f | |||
| bb5844a8e8 | |||
| bc5956ed66 | |||
| 624d89563c | |||
| f6bcff7e13 | |||
| 36acbc1df8 | |||
| ca9ce6e4c5 | |||
| 08edc735e9 | |||
| 5b420a453b | |||
| 6a6411dfb8 | |||
| 360bcd6bfe | |||
| 16107dc8a9 | |||
| 578281b707 | |||
| 3730d955dd | |||
| 3dbf329a31 | |||
| afa2b50951 | |||
| b219e4a055 | |||
| 6e38f3f3df | |||
| 34370a36af | |||
| 1dfb27f08e | |||
| 3bf616667e | |||
| 15a3fd17df | |||
| 092a9c2dc6 | |||
| 6e1bf8265f | |||
| d4389e5c35 | |||
| d30b1853de | |||
| 532d7d155b | |||
| 9cff6b9c87 | |||
| e5f8b3dc71 | |||
| 8ac2de3434 | |||
| 74501c3584 | |||
| 757ccc3eb6 | |||
| 36fb3f2d53 | |||
| 8092b0680b | |||
| 42de7737cb | |||
| 273d2a4b88 | |||
| fe4b48f4bc | |||
| 444d3c3a59 | |||
| c8596c6815 | |||
| e2dd4b2466 | |||
| 429ca4dd6d | |||
| 55e0624dc6 | |||
| fff6ef5fe2 | |||
| 2127c47d00 | |||
| 564477b728 | |||
| c7e80401e1 | |||
| 7fee1abdb5 | |||
| 243fae4b19 | |||
| 6e2a58c3d7 | |||
| 2d864de128 | |||
| bee09217a2 | |||
| bd0e039e7a | |||
| f1423fba4f | |||
| c439e1bb42 | |||
| 6a17c2a9a8 | |||
| 0b987f937d | |||
| cd62a8741a | |||
| 67a8f10b03 | |||
| 27749ec3cd | |||
| 19220a1cbd | |||
| f4df76cccd | |||
| 6c443ca089 | |||
| 68be1d8583 | |||
| a965cf429f | |||
| c1c5add001 | |||
| a8049f2a60 | |||
| 9d01b557ce | |||
| 89b3bc94a2 | |||
| 7a7fde5fc0 | |||
| 632712cfee | |||
| 98be30449f | |||
| a7db67adb6 | |||
| c6fdae8879 | |||
| f925f8969a | |||
| f1601e7017 | |||
| caf5946b9e | |||
| 7dce99e045 | |||
| 0e59617691 | |||
| f0f688a1a3 | |||
| 753941204f | |||
| 6fd239cf70 | |||
| a8f6594142 | |||
| 0d5d5105ce | |||
| c078d496be | |||
| bceffcad6a | |||
| ccbf1f0544 | |||
| e5a8a06bbb | |||
| af1d85e65c | |||
| 7ace6c3593 | |||
| b3e0cf8ec1 | |||
| 4f77d1bfb3 | |||
| 72e54fe0d9 | |||
| 735083999e | |||
| 006a5729d6 | |||
| e4c3521b04 | |||
| dd13eddfe3 | |||
| 706cb888c8 | |||
| bfe5379206 | |||
| 2ffd1a2520 | |||
| 784e453eaa | |||
| 084b3d8822 | |||
| c03fd57c25 | |||
| 34d5f809ee | |||
| fd0d91faa4 | |||
| 2450602227 | |||
| 48ea90ce97 | |||
| 640ebdb835 | |||
| 5d59d729d7 | |||
| afadc5e2be | |||
| dc3c3f7557 | |||
| 61cbfe231d | |||
| 9ee2dd477e | |||
| 7a0e1eda0c | |||
| a14472358b | |||
| 51f02e79c1 | |||
| 0202a61b07 | |||
| 6cc4c32625 | |||
| 3d304961d3 | |||
| f4426dd280 | |||
| 457544adc3 | |||
| f426df9d7f | |||
| 61780a3fe8 | |||
| 35d7202fc3 | |||
| cec6174bb6 | |||
| 52ba183846 | |||
| b08a0aa4f1 | |||
| 327874d396 | |||
| eae6be5fb4 | |||
| d49350a8cd | |||
| 72f0fd3193 | |||
| 0a581db6d1 | |||
| a2dd550a72 | |||
| 1e91c08ae3 | |||
| 96462aa4ba | |||
| dfba116268 | |||
| 1072049741 | |||
| 16bf3e9cd8 | |||
| 1861374c31 | |||
| 320ede4d7b | |||
| 15414365ec | |||
| a5ef0fc902 | |||
| 01ee530e38 | |||
| e8db8a892c | |||
| 1e0af11110 | |||
| 5f6b174dd9 | |||
| 4d2589c112 | |||
| 47706913ca | |||
| 1bbd11bbcd | |||
| 5d946d820e | |||
| 1e67d97169 | |||
| 84f4343791 | |||
| bd4e4e0072 | |||
| 5afe0be277 | |||
| 71fad05411 | |||
| 8afd215957 | |||
| 42e3b0df45 | |||
| facdb3264d | |||
| 9732b65017 | |||
| 80a60dff38 | |||
| 6276e0611a | |||
| 5ccfe70f97 | |||
| 7dc40fbc5c | |||
| a340167bfb | |||
| 0b7b96dff1 | |||
| 7b7286c2b3 | |||
| 5598385a5c | |||
| d5023c412f | |||
| b1d6d20800 | |||
| 4f5006560e | |||
| 43b302b727 | |||
| d3611ef310 | |||
| 0e0cf881be | |||
| a6ccd959fc | |||
| 4fb7f6818d | |||
| f902e44303 | |||
| 2939290915 | |||
| a55ba549eb | |||
| cdd8250a0c | |||
| d56d1142cd | |||
| e4b0210981 | |||
| 4026a34fcf | |||
| 7001639125 | |||
| 8fae551cc0 | |||
| cd00efe96f | |||
| 176f50e836 | |||
| 2cd78ab94e | |||
| 9f4e43e20a | |||
| 2caa7933ee | |||
| 3b6da279f8 | |||
| e87185a906 | |||
| 7a496e114b | |||
| fa2a0e7482 | |||
| 3f88a9df46 | |||
| 049b14ae97 | |||
| f14d4bf373 | |||
| a9afb684c9 | |||
| 9c447dbd68 | |||
| 0d446e6fcd | |||
| c8f04a878a | |||
| 633abdf0f4 | |||
| 8bd9f9a772 | |||
| 7164a53267 | |||
| f52b09e5e8 | |||
| 0acb7ca987 | |||
| 1778d45481 | |||
| 952655bf84 | |||
| c0921942ec | |||
| 7e67f4c7cf | |||
| 4d14b31fb5 | |||
| 003064429d | |||
| 34934762f0 | |||
| d1f542f03d | |||
| fbc7513f19 | |||
| e42c7407e3 | |||
| cbfae4126c | |||
| c712bee677 | |||
| f732edfc6f | |||
| 2898c0380f | |||
| a43ef376c4 | |||
| 7867a82e6e | |||
| e81e270d69 | |||
| 45b14abe63 | |||
| 22b1a839ba | |||
| 47ec4a5e7e | |||
| 4319982be9 | |||
| f0943ca9a4 | |||
| 8a42ed74bc | |||
| d9f60ded4a | |||
| 15fe8db248 | |||
| 7db339d876 | |||
| dd07057ca5 | |||
| a43abebc6e | |||
| 6a535e72d4 | |||
| 1535f1096f | |||
| 88fcddfd8b | |||
| d58cd79d28 | |||
| e5c35f7fab | |||
| 0f45729c94 | |||
| a78c4de654 | |||
| 4dfe5a1dce | |||
| ecaa72c12b | |||
| 95728c668c | |||
| 9356e6ed7e | |||
| 7a6cfc8978 | |||
| f744124f17 | |||
| 30e71b11e8 | |||
| 2de504005e | |||
| 6a41c33d34 | |||
| 3b48dfae31 | |||
| c7fd9a6bcb | |||
| 169ef180ff | |||
| f33d3d474b | |||
| 63e1331bd1 | |||
| 418cc9b4b9 | |||
| 71fb1eac6b | |||
| 0804a05925 | |||
| de6098013d | |||
| f8cd361415 | |||
| 0b1a640c3e | |||
| aa9df5838c | |||
| 2edacc9ff5 | |||
| a9a0a9d307 | |||
| 1937804e47 | |||
| 8e8b55fa46 | |||
| 3096671595 | |||
| a6102490c8 | |||
| 9e1cc1521f | |||
| a579c26ada | |||
| 754da2c7fc | |||
| 995bdee1f6 | |||
| a70f661e86 | |||
| 8312bfb78b | |||
| 7194796841 | |||
| c6918cca46 | |||
| be7b38c6ce | |||
| a7ebd50536 | |||
| 05f9b81e04 | |||
| 1f9eb9e36b | |||
| af46eb2d65 | |||
| d1e54629e7 | |||
| 78dbe23d0e | |||
| 4bf93d10b0 | |||
| 00827e9975 | |||
| 021b32587a | |||
| 5ffe1675db | |||
| d34eca2cb8 | |||
| 12ac7af11e | |||
| a19eb2abca | |||
| 4c9a8aadab | |||
| 034058999d | |||
| 34ea39e764 | |||
| 7caf00bd75 | |||
| aabe604e18 | |||
| f2bc281e2b | |||
| 4187a568f8 | |||
| 1611554c00 | |||
| 60fc8f9bce | |||
| 48eb20fa66 | |||
| 9e7bc09f84 | |||
| ec9e35180b | |||
| c757212c07 | |||
| 8415dd2be1 | |||
| 17d734ba11 | |||
| 476387b468 | |||
| 3a2cb138ab | |||
| fa763e6341 | |||
| c988ca0bca | |||
| c257dd4171 | |||
| ff026f45fc | |||
| cd2166f11b | |||
| 33cfacec84 | |||
| 5c2bebf96c | |||
| ef5ae76907 | |||
| 7546ba412c | |||
| 6b536f4a4f | |||
| a540326031 | |||
| 45dd2e90c5 | |||
| e3188c0419 | |||
| 423fa4001b | |||
| 3701d8bcd9 | |||
| 0689f731b7 | |||
| d97025aeca | |||
| fa929ebde4 | |||
| 32c4d634ed | |||
| 8d4a791a1c | |||
| 59f376e720 | |||
| a5017c1571 | |||
| 8767911a16 | |||
| f1170244da | |||
| 921c66140f | |||
| 0f2a73cb3c | |||
| a16f567ade | |||
| 5e8b5eed9f | |||
| 682e40febd | |||
| 568ef07438 | |||
| cafdbaac57 | |||
| 505184b200 | |||
| 1d7e1be641 | |||
| 8ba22a343a | |||
| ab34c947ca | |||
| 062387e6a8 | |||
| 161213f2af | |||
| c1fcb4f6e3 | |||
| a9d81f5af5 | |||
| 2900f2fa7f | |||
| 4e44d163f3 | |||
| ffa6d62d3d | |||
| d9ba6a7a89 | |||
| 8e0105b71f | |||
| c45461937d | |||
| b9c9088de2 | |||
| a6cfe19139 | |||
| 1f11de4a20 | |||
| 871333b9e9 | |||
| 47d8ec7576 | |||
| 46a304b5a6 | |||
| 098ade3c8a | |||
| 86489b2117 | |||
| 64b5206f86 | |||
| ea0b7f1eca | |||
| 7e8dd1c6a7 | |||
| d3b8716a66 | |||
| af77baba60 | |||
| 2f1cfc3e67 | |||
| 37d15736e2 | |||
| 5f8045c611 | |||
| d62562149c | |||
| 63e2ca07b5 | |||
| ed94423f32 | |||
| e705e12656 | |||
| 035ce2ae4e | |||
| a1778d836b | |||
| cebcf908f9 | |||
| 202a3c26d1 | |||
| fbbda8c0d1 | |||
| 7954bdeca8 | |||
| d8a0c80ca4 | |||
| 100faa3b9f | |||
| 7894a4991c | |||
| 0405ba4b13 | |||
| ca6e96929f | |||
| b832a66abf | |||
| 168c7447bb | |||
| 891c442da4 | |||
| 74098641f6 | |||
| eedfa2add8 | |||
| 2838f09268 | |||
| ab800d09ca | |||
| 44e4fe3a29 | |||
| d279bad2d0 | |||
| a5737073c4 | |||
| 4a82812b4e | |||
| 8ec67d72f1 | |||
| 405db77dea | |||
| 8b8ed1fee3 | |||
| c1e496530d | |||
| a43ba04bf3 | |||
| 7b78e4b399 | |||
| e92ea3ea0c | |||
| 2bb4630351 | |||
| ef63c19c93 | |||
| cda134e92c | |||
| 75d162b353 | |||
| 9cbfb2c7e4 | |||
| a47e20ff74 | |||
| 36a63d7628 | |||
| 2217ae0353 | |||
| 31a444508b | |||
| 37c898ea0c | |||
| 03c2a67fe4 | |||
| 795caf19e7 | |||
| 99ef28be99 | |||
| 97a5eda8f8 | |||
| 2d8cb0c818 | |||
| 2d1325ca52 | |||
| 9f5b8e9858 | |||
| 4eaf070243 | |||
| c353ae645b | |||
| e1038c27f6 | |||
| 994a770fef | |||
| 1875b56b37 | |||
| 99eef31202 | |||
| 826f8a670d | |||
| cb53dbefcf | |||
| 9b4e29a812 | |||
| fa9c9de12c | |||
| c98db93ecb | |||
| eb1dcc9f0a | |||
| 63c6c2f67b | |||
| 62f37a4bb4 | |||
| 02a8289ad4 | |||
| 7c5f3323de | |||
| 2149bfa782 | |||
| 3561df7574 | |||
| 9cd4e272c7 | |||
| 7069a17d8e | |||
| 3e5d0b650d | |||
| a6b8db30c3 | |||
| 613f2d8c6c | |||
| b8e0c74d45 | |||
| 13acd950ad | |||
| 8ec86017f0 | |||
| ee43a5dc9d | |||
| e3ee1a42fb | |||
| 9fa24a35ef | |||
| 0e631e6f29 | |||
| 622d598392 | |||
| 3ab411578e | |||
| d1e21a9f4b | |||
| cd2ebe700b | |||
| 7f5a3bffd1 | |||
| cdbe74f1d9 | |||
| de01da975e | |||
| aae243e36e | |||
| d3f7c199c7 | |||
| ca41fac53d | |||
| 6ccbfc466f | |||
| 81aa7fb8ef | |||
| da102dfed2 | |||
| 63c225fb9b | |||
| f0f6f0bd23 | |||
| 52b60338c3 | |||
| d3e7b3a8d8 | |||
| 48cba94995 | |||
| a2ca3a8245 | |||
| d602238f82 | |||
| 5f141212f4 | |||
| 8b05c01e42 | |||
| 307ed2ca7f | |||
| e9a90a4c3a | |||
| a9407c3290 | |||
| f9fc79d3aa | |||
| ee4b4a62ac | |||
| 7d7027a753 | |||
| e36dd04d3e | |||
| fd9af47606 | |||
| 633507d4cd | |||
| 8c94e17c14 | |||
| 942f3adfe0 | |||
| 3b92aaf61c | |||
| 4f5d89732d | |||
| 55bb70179c | |||
| 6c9c8ee5dc | |||
| 73e9e3239a | |||
| 8d67a3be5f | |||
| 5175c857ec | |||
| d2a9f478e4 | |||
| 9f32b92af3 | |||
| 880525b015 | |||
| 0536a57075 | |||
| abb3b02e74 | |||
| cad7132b0a | |||
| c6e5d31244 | |||
| b0302d0e6d | |||
| 9a958276db | |||
| 44bcdf7973 | |||
| 6311884a5c | |||
| de5fc7c277 | |||
| 3414c50975 | |||
| 39736c3ce4 | |||
| 14fb158e13 | |||
| 81f55a9e07 | |||
| 94e942e344 | |||
| 95fbe6085a | |||
| 4f4dda7a09 | |||
| 8021d6a860 | |||
| 3cdfd3a771 | |||
| 76792a127f | |||
| fc36cbcb75 | |||
| 3fab377454 | |||
| 5246c10e32 | |||
| 63530b7847 | |||
| 99be08dc20 | |||
| 9898673dbf | |||
| d7f0fdd0e7 | |||
| 795cb0aba2 | |||
| 32ba46206b | |||
| 22c9386101 | |||
| ff4136e02d | |||
| 04cccf1174 | |||
| ab9203eef0 | |||
| 924ba90384 | |||
| 353b2c99cb | |||
| 0b3542e9c3 | |||
| 425bd7bf9a | |||
| e0aa9fb1d9 | |||
| c65a484b13 | |||
| d9fc5e3646 | |||
| d72f3e89a2 | |||
| 85a303171c | |||
| 4026eca656 | |||
| 74aec46a94 | |||
| 49e21a6342 | |||
| 0e0dd0c7d3 | |||
| 8fedcdaf00 | |||
| 9924375f82 | |||
| a60661aaa5 | |||
| 5041ebd58b | |||
| 594574c846 | |||
| 6e7545be65 | |||
| b77d2d9160 | |||
| 0c94ef86b0 | |||
| e0c6e9aabf | |||
| 814f1e576a | |||
| d62d2770f4 | |||
| cec678a24b | |||
| 7baf301785 | |||
| ded0dbaa2d | |||
| 8c16bc2ac5 | |||
| 0827ede4b6 | |||
| 239e0b84c2 | |||
| 73b77a477a | |||
| c032c42321 | |||
| cb7140ad38 | |||
| 29ea2a0ef1 | |||
| 46af71e3df | |||
| 67819fc85d | |||
| 4b15bcdc55 | |||
| 0ba439e5bd | |||
| c25c855b46 | |||
| 003b227f7d | |||
| 461976f6d3 | |||
| 7636c82b11 | |||
| d1e4863e01 | |||
| d483aca73b | |||
| cf45169de2 | |||
| 0075c18cd8 | |||
| e55bc5fe7a | |||
| e6bd7ad0ea | |||
| a98bc37045 | |||
| 06209a945d | |||
| d5c628d916 | |||
| 905fdd9861 | |||
| 3ff95ede2c | |||
| f9734dc5cc | |||
| c32ec2f132 | |||
| 28c3adaf64 | |||
| 6435746660 | |||
| c6531e9249 | |||
| b1be0e2a98 | |||
| 31d22e2b4f | |||
| 1e5f01fdeb | |||
| 213787bbcf | |||
| 933a43f4ea | |||
| 6264d105ab | |||
| 0c7495870b | |||
| a615b5cefd | |||
| 9f66a932fc | |||
| d094f22ab2 | |||
| 5cc41b3b0f | |||
| 27b99eb3be | |||
| e324850811 | |||
| 7c3b0e9e72 | |||
| 709529da87 | |||
| 2354ee9174 | |||
| f158f092ce | |||
| f446d400a1 | |||
| 5b0026f30f | |||
| 1efd41e7ac | |||
| 76c6e8442a | |||
| f92ace1894 | |||
| 4585b7b732 | |||
| ae2907ad5d | |||
| 1a3ad4f602 | |||
| 9c95928add | |||
| 10380994a4 | |||
| 49993edb37 | |||
| 3efc40e9ae | |||
| 4922ffa5c3 | |||
| c7935ee90f | |||
| a91a706e0b | |||
| a16fbc187a | |||
| 4a314f2070 | |||
| 27d9104607 | |||
| acad258319 | |||
| ba8f22f260 | |||
| f524aa5e57 | |||
| 5c9664ec7d | |||
| be27376dde | |||
| 452535b561 | |||
| 9048963ea5 | |||
| d3ab051a39 | |||
| 73edbe8fd3 | |||
| de70f68640 | |||
| 97b97a164d | |||
| fb87f4d06a | |||
| 20092d6757 | |||
| 96c4165cda | |||
| a4226d4e2f | |||
| f97565d9d8 | |||
| ab84e66fe4 | |||
| 1f9d29e005 | |||
| 8521b0c28c | |||
| 26a62426f0 | |||
| 1246413e30 | |||
| f50b5819c0 | |||
| 6db8d61b7c | |||
| 58250fe993 | |||
| 6cae7a1ae4 | |||
| 73541efbf4 | |||
| f4bd891deb | |||
| a9831657cc | |||
| 500a9fbaca | |||
| 5f11c3a602 | |||
| 7680e90d65 | |||
| 06a57cd056 | |||
| 7626d17540 | |||
| 344b92c427 | |||
| 4719177009 | |||
| c1461633ce | |||
| 6be7aa1ad9 | |||
| 4da7c2f960 | |||
| 47a45df610 | |||
| 1a44e48b2d | |||
| c566a703eb | |||
| 001c248cb8 | |||
| 654462fe6b | |||
| ab6642ce33 | |||
| 946386ad43 | |||
| cf9c79a928 | |||
| 493e4e8dca | |||
| ecbe8434f3 | |||
| 644fe1bd3c | |||
| 870fca2e93 | |||
| 6207fb5006 | |||
| ae913d8213 | |||
| ce969822a0 | |||
| 4fc311d4a5 | |||
| 2b424ff45c | |||
| e5d9cfc50e | |||
| ac3552818b | |||
| c004741f95 | |||
| 2bdc0c7704 | |||
| 35d90d56dd | |||
| f14525ea95 | |||
| c2350f1418 | |||
| 4b8b02fa55 | |||
| 62102b8b03 | |||
| 0655318d2d | |||
| 0bd2a10091 | |||
| ce11788878 | |||
| 0cb74296c3 | |||
| def08b72ca | |||
| 1ef8d2913b | |||
| 7e18b1287e | |||
| fdcb6ad12f | |||
| 988bf0e92c | |||
| f6612286d9 | |||
| c21845c60d | |||
| 867ef0ef97 | |||
| f27bee83b7 | |||
| e3b6feb4da | |||
| d727703075 | |||
| c49cf0ed12 | |||
| f324d207dd | |||
| c9bda275f5 | |||
| 62ed1a408d | |||
| 25efb34f1f | |||
| eb0f13c075 | |||
| 7ca92a7675 | |||
| e313f88bca | |||
| 4823eb29d5 | |||
| 43ec332f61 | |||
| cac0652120 | |||
| cc088900b6 | |||
| 824b165723 | |||
| 443b94b332 | |||
| dc116d3a6f | |||
| bbc8859a16 | |||
| 09e388b01a | |||
| e5fcb75b9f | |||
| 8e104e133f | |||
| e2a77537f0 | |||
| a73a79b627 | |||
| e79c3a8f37 | |||
| ca9a2dad18 | |||
| cd03f5f31d | |||
| 33e25de801 | |||
| 9cd60e9575 | |||
| 4da4a4de89 | |||
| ec0a88a5ec | |||
| a52a00e58c | |||
| 0a6ffbba07 | |||
| 8cb5423b0b | |||
| 5a6411c6c3 | |||
| b97a8b5a68 | |||
| 91794266e7 | |||
| 2f86e2dae1 | |||
| fb76c5af6c | |||
| 3f6ec5b7ea | |||
| c95ea8c547 | |||
| facd9e0e5f | |||
| 9d0c3d3fc5 | |||
| 907d0d39a1 | |||
| 4d689100ee | |||
| 818d9d6f62 | |||
| 9a2059726f | |||
| 8bcac405e5 | |||
| 19661c5e82 | |||
| 9875d9622d | |||
| 7573ffe941 | |||
| 47166ab2eb | |||
| 0295b7d919 | |||
| 3653072497 | |||
| 0e7f203a1b | |||
| ab99265c88 | |||
| f774d95f5c | |||
| 9e1e32f16d | |||
| 0accb453e5 | |||
| e2b285c792 | |||
| 935f95fa3b | |||
| a4d20d9f08 | |||
| 0e23719c7b | |||
| eb1f06658d | |||
| 1e177a2530 | |||
| 49031b0e48 | |||
| ca5bbb3064 | |||
| b338188c5f | |||
| 9cbe4cc36e | |||
| 6f5f521803 | |||
| a6db30280b | |||
| 4db3860ade | |||
| 57837cfe1d | |||
| b51fdc5769 | |||
| 5038d7ad53 | |||
| c1d618326b | |||
| db1a0195fe | |||
| 238def8089 | |||
| 59e969c2a8 | |||
| 029a288be2 | |||
| 11e5448acf | |||
| 71577d0346 | |||
| 681e559cb4 | |||
| 862390ae78 | |||
| f210799c4d | |||
| d7700c3c77 | |||
| 44fa4d3fc4 | |||
| f0c57c643b | |||
| 5e48ed1e23 | |||
| a3f12b2a53 | |||
| cc00cce704 | |||
| 9c244c6750 | |||
| ffb5d46ca3 | |||
| 7376732b1a | |||
| 1caf4ff8e6 | |||
| 22147483c1 | |||
| c7dbf7ba92 | |||
| fd3f76d35c | |||
| e10bca6892 | |||
| 4a01960426 | |||
| 318364e70b | |||
| 383064a7b7 | |||
| 78bbfb5f41 | |||
| 9aec1f5f50 | |||
| 66beb43daf | |||
| b02589a957 | |||
| d4a1d83851 | |||
| 53aee1020d | |||
| 010c106879 | |||
| fb43e38c7d | |||
| ca96c67e96 | |||
| 1fbf635582 | |||
| 44d62b187f | |||
| 57600cbad1 | |||
| 311d6507ce | |||
| 8e520ce579 | |||
| 5a97e15885 | |||
| bb36c67159 | |||
| 7a33edc7a6 | |||
| a7d6303da4 | |||
| 704c201c33 | |||
| c69d3eec36 | |||
| 6e5a3a0078 | |||
| ed62de9135 | |||
| a8b218ce95 | |||
| f6d2b1e7f1 | |||
| 85439213b2 | |||
| 2933d100f7 | |||
| ac799688d3 | |||
| a4b9c44cfa | |||
| af282d2bb8 | |||
| a93bbebb61 | |||
| 4552a24caa | |||
| fc22ec0672 | |||
| 963fbcb5e8 | |||
| 3da3b9de45 | |||
| a055d1c668 | |||
| 0b130bbc01 | |||
| 9f9bc9c747 | |||
| 75367ae727 | |||
| bd9d78622f | |||
| 6b807f419b | |||
| f58ee6f63a | |||
| 7510d86a52 | |||
| 65fba6ca41 | |||
| 3e2198b7ba | |||
| 535c0fc419 | |||
| 1c5d30b673 | |||
| 3bf26dbbef | |||
| 329437411d | |||
| 090cdaf2e4 | |||
| 7c257f261c | |||
| 477bc7baf7 | |||
| 32d6ebef9c | |||
| 51e0676ce5 | |||
| 53156e210f | |||
| 84f76c09f7 | |||
| 74e0765d7a | |||
| 51cfadbc77 | |||
| 01e047f639 | |||
| 4050d7e4a3 | |||
| adab66172d | |||
| 7c850c969e | |||
| f55fa98dec | |||
| 7f28c984e1 | |||
| 6220e53d8f | |||
| 5bb0231354 | |||
| 58eec48af7 |
@@ -0,0 +1,4 @@
|
|||||||
|
FROM registry.cn-shanghai.aliyuncs.com/extimaging/aspnetcore:v8.2
|
||||||
|
WORKDIR /app
|
||||||
|
COPY publish .
|
||||||
|
ENTRYPOINT ["dotnet", "IRaCIS.Core.API.dll"]
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
using IRaCIS.Core.Infrastructure.Extention;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
|
using Microsoft.Extensions.Localization;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.SCP.Filter
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
public class ModelActionFilter : ActionFilterAttribute, IActionFilter
|
||||||
|
{
|
||||||
|
public IStringLocalizer _localizer;
|
||||||
|
public ModelActionFilter(IStringLocalizer localizer)
|
||||||
|
{
|
||||||
|
_localizer = localizer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnActionExecuting(ActionExecutingContext context)
|
||||||
|
{
|
||||||
|
if (!context.ModelState.IsValid)
|
||||||
|
{
|
||||||
|
|
||||||
|
var validationErrors = context.ModelState
|
||||||
|
.Keys
|
||||||
|
.SelectMany(k => context.ModelState[k]!.Errors)
|
||||||
|
.Select(e => e.ErrorMessage)
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
//---提供给接口的参数无效。
|
||||||
|
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["ModelAction_InvalidAPIParameter"] + JsonConvert.SerializeObject( validationErrors)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
using IRaCIS.Core.Infrastructure;
|
||||||
|
using IRaCIS.Core.Infrastructure.Extention;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
|
using Microsoft.Extensions.Localization;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.SCP.Filter
|
||||||
|
{
|
||||||
|
public class ProjectExceptionFilter : Attribute, IExceptionFilter
|
||||||
|
{
|
||||||
|
private readonly ILogger<ProjectExceptionFilter> _logger;
|
||||||
|
|
||||||
|
public IStringLocalizer _localizer;
|
||||||
|
|
||||||
|
public ProjectExceptionFilter(IStringLocalizer localizer, ILogger<ProjectExceptionFilter> logger)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
_localizer = localizer;
|
||||||
|
}
|
||||||
|
public void OnException(ExceptionContext context)
|
||||||
|
{
|
||||||
|
//context.ExceptionHandled;//记录当前这个异常是否已经被处理过了
|
||||||
|
|
||||||
|
if (!context.ExceptionHandled)
|
||||||
|
{
|
||||||
|
if (context.Exception.GetType().Name == "DbUpdateConcurrencyException")
|
||||||
|
{
|
||||||
|
//---并发更新,当前不允许该操作
|
||||||
|
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["ProjectException_ConcurrentUpdateNotAllowed"] + context.Exception.Message));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (context.Exception.GetType() == typeof(BusinessValidationFailedException))
|
||||||
|
{
|
||||||
|
var error = context.Exception as BusinessValidationFailedException;
|
||||||
|
|
||||||
|
context.Result = new JsonResult(ResponseOutput.NotOk(context.Exception.Message, error!.Code));
|
||||||
|
}
|
||||||
|
else if(context.Exception.GetType() == typeof(QueryBusinessObjectNotExistException))
|
||||||
|
{
|
||||||
|
context.Result = new JsonResult(ResponseOutput.NotOk( context.Exception.Message, ApiResponseCodeEnum.DataNotExist));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["Project_ExceptionContactDeveloper"] + (context.Exception.InnerException is null ? (context.Exception.Message /*+ context.Exception.StackTrace*/)
|
||||||
|
: (context.Exception.InnerException?.Message /*+ context.Exception.InnerException?.StackTrace*/)), ApiResponseCodeEnum.ProgramException));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_logger.LogError(context.Exception.InnerException is null ? (context.Exception.Message + context.Exception.StackTrace) : (context.Exception.InnerException?.Message + context.Exception.InnerException?.StackTrace));
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//继续
|
||||||
|
}
|
||||||
|
context.ExceptionHandled = true;//标记当前异常已经被处理过了
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using IRaCIS.Core.Domain.Share;
|
||||||
|
using IRaCIS.Core.Infrastructure.Extention;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.Application.Service.BusinessFilter
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 统一返回前端数据包装,之前在控制器包装,现在修改为动态Api 在ResultFilter这里包装,减少重复冗余代码
|
||||||
|
/// by zhouhang 2021.09.12 周末
|
||||||
|
/// </summary>
|
||||||
|
public class UnifiedApiResultFilter : Attribute, IAsyncResultFilter
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 异步版本
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context"></param>
|
||||||
|
/// <param name="next"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (context.Result is ObjectResult objectResult)
|
||||||
|
{
|
||||||
|
var statusCode = objectResult.StatusCode ?? context.HttpContext.Response.StatusCode;
|
||||||
|
|
||||||
|
//是200 并且没有包装 那么包装结果
|
||||||
|
if (statusCode == 200 && !(objectResult.Value is IResponseOutput))
|
||||||
|
{
|
||||||
|
//if (objectResult.Value == null)
|
||||||
|
//{
|
||||||
|
// var apiResponse = ResponseOutput.DBNotExist();
|
||||||
|
|
||||||
|
// objectResult.Value = apiResponse;
|
||||||
|
// objectResult.DeclaredType = apiResponse.GetType();
|
||||||
|
//}
|
||||||
|
//else
|
||||||
|
//{
|
||||||
|
|
||||||
|
var type = objectResult.Value?.GetType();
|
||||||
|
|
||||||
|
|
||||||
|
if ( type!=null&& type.IsGenericType&&(type.GetGenericTypeDefinition()==typeof(ValueTuple<,>)|| type.GetGenericTypeDefinition()==typeof(Tuple<,>)))
|
||||||
|
{
|
||||||
|
|
||||||
|
//报错
|
||||||
|
//var tuple = (object, object))objectResult.Value;
|
||||||
|
|
||||||
|
//var (val1, val2) = ((dynamic, dynamic))objectResult.Value;
|
||||||
|
//var apiResponse = ResponseOutput.Ok(val1, val2);
|
||||||
|
|
||||||
|
//OK
|
||||||
|
var tuple = (dynamic)objectResult.Value;
|
||||||
|
var apiResponse = ResponseOutput.Ok(tuple.Item1, tuple.Item2);
|
||||||
|
|
||||||
|
|
||||||
|
objectResult.Value = apiResponse;
|
||||||
|
objectResult.DeclaredType = apiResponse.GetType();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var apiResponse = ResponseOutput.Ok(objectResult.Value);
|
||||||
|
|
||||||
|
objectResult.Value = apiResponse;
|
||||||
|
objectResult.DeclaredType = apiResponse.GetType();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//}
|
||||||
|
|
||||||
|
}
|
||||||
|
//如果不是200 是IResponseOutput 不处理
|
||||||
|
else if (statusCode != 200 && (objectResult.Value is IResponseOutput))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
else if(statusCode != 200&&!(objectResult.Value is IResponseOutput))
|
||||||
|
{
|
||||||
|
//---程序错误,请联系开发人员。
|
||||||
|
var apiResponse = ResponseOutput.NotOk(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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
using Autofac;
|
||||||
|
using IRaCIS.Core.Infra.EFCore;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Panda.DynamicWebApi;
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using IRaCIS.Core.Domain.Models;
|
||||||
|
using IRaCIS.Core.Domain.Share;
|
||||||
|
using IRaCIS.Core.Application.Service;
|
||||||
|
using AutoMapper;
|
||||||
|
using IRaCIS.Core.SCP.Service;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.SCP
|
||||||
|
{
|
||||||
|
// ReSharper disable once IdentifierTypo
|
||||||
|
public class AutofacModuleSetup : Autofac.Module
|
||||||
|
{
|
||||||
|
protected override void Load(ContainerBuilder containerBuilder)
|
||||||
|
{
|
||||||
|
|
||||||
|
#region byzhouhang 20210917 此处注册泛型仓储 可以减少Domain层 和Infra.EFcore 两层 空的仓储接口定义和 仓储文件定义
|
||||||
|
|
||||||
|
containerBuilder.RegisterGeneric(typeof(Repository<>))
|
||||||
|
.As(typeof(IRepository<>)).InstancePerLifetimeScope();//注册泛型仓储
|
||||||
|
|
||||||
|
containerBuilder.RegisterType<Repository>().As<IRepository>().InstancePerLifetimeScope();
|
||||||
|
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 指定控制器也由autofac 来进行实例获取 https://www.cnblogs.com/xwhqwer/p/15320838.html
|
||||||
|
|
||||||
|
//获取所有控制器类型并使用属性注入
|
||||||
|
containerBuilder.RegisterAssemblyTypes(typeof(BaseService).Assembly)
|
||||||
|
.Where(type => typeof(IDynamicWebApi).IsAssignableFrom(type))
|
||||||
|
.PropertiesAutowired();
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Assembly application = Assembly.LoadFrom(AppDomain.CurrentDomain.BaseDirectory + typeof(BaseService).Assembly.GetName().Name+".dll");
|
||||||
|
containerBuilder.RegisterAssemblyTypes(application).Where(t => t.FullName.Contains("Service"))
|
||||||
|
.PropertiesAutowired().AsImplementedInterfaces();
|
||||||
|
|
||||||
|
|
||||||
|
//containerBuilder.RegisterType<HttpContextAccessor>().As<IHttpContextAccessor>().SingleInstance();
|
||||||
|
//containerBuilder.RegisterType<UserInfo>().As<IUserInfo>().InstancePerLifetimeScope();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
using EntityFramework.Exceptions.SqlServer;
|
||||||
|
using IRaCIS.Core.Domain.Share;
|
||||||
|
using IRaCIS.Core.Infra.EFCore;
|
||||||
|
using Medallion.Threading;
|
||||||
|
using Medallion.Threading.SqlServer;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Diagnostics;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.SCP
|
||||||
|
{
|
||||||
|
public static class EFSetup
|
||||||
|
{
|
||||||
|
public static void AddEFSetup( this IServiceCollection services, IConfiguration configuration)
|
||||||
|
{
|
||||||
|
services.AddHttpContextAccessor();
|
||||||
|
services.AddScoped<IUserInfo, UserInfo>();
|
||||||
|
services.AddScoped<ISaveChangesInterceptor, AuditEntityInterceptor>();
|
||||||
|
|
||||||
|
|
||||||
|
//这个注入没有成功--注入是没问题的,构造函数也只是支持参数就好,错在注入的地方不能写DbContext
|
||||||
|
//Web程序中通过重用池中DbContext实例可提高高并发场景下的吞吐量, 这在概念上类似于ADO.NET Provider原生的连接池操作方式,具有节省DbContext实例化成本的优点
|
||||||
|
services.AddDbContext<IRaCISDBContext>((sp, options) =>
|
||||||
|
{
|
||||||
|
// 在控制台
|
||||||
|
//public static readonly ILoggerFactory MyLoggerFactory = LoggerFactory.Create(builder => { builder.AddConsole(); });
|
||||||
|
var logFactory = LoggerFactory.Create(builder => { builder.AddDebug(); });
|
||||||
|
|
||||||
|
options.UseSqlServer(configuration.GetSection("ConnectionStrings:RemoteNew").Value,
|
||||||
|
contextOptionsBuilder => contextOptionsBuilder.EnableRetryOnFailure());
|
||||||
|
|
||||||
|
options.UseLoggerFactory(logFactory);
|
||||||
|
|
||||||
|
options.UseExceptionProcessor();
|
||||||
|
|
||||||
|
options.EnableSensitiveDataLogging();
|
||||||
|
|
||||||
|
options.AddInterceptors(new QueryWithNoLockDbCommandInterceptor());
|
||||||
|
options.AddInterceptors(sp.GetServices<ISaveChangesInterceptor>());
|
||||||
|
|
||||||
|
options.UseProjectables();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
//// Register an additional context factory as a Scoped service, which gets a pooled context from the Singleton factory we registered above,
|
||||||
|
//services.AddScoped<IRaCISDBScopedFactory>();
|
||||||
|
|
||||||
|
//// Finally, arrange for a context to get injected from our Scoped factory:
|
||||||
|
//services.AddScoped(sp => sp.GetRequiredService<IRaCISDBScopedFactory>().CreateDbContext());
|
||||||
|
|
||||||
|
//注意区分 easy caching 也有 IDistributedLockProvider
|
||||||
|
services.AddSingleton<IDistributedLockProvider>(sp =>
|
||||||
|
{
|
||||||
|
//var connection = ConnectionMultiplexer.Connect(configuration["Redis:Configuration"]!);
|
||||||
|
|
||||||
|
return new SqlDistributedSynchronizationProvider(configuration.GetSection("ConnectionStrings:RemoteNew").Value);
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
|
||||||
|
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Converters;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.SCP
|
||||||
|
{
|
||||||
|
public static class NewtonsoftJsonSetup
|
||||||
|
{
|
||||||
|
public static void AddNewtonsoftJsonSetup(this IMvcBuilder builder, IServiceCollection services)
|
||||||
|
{
|
||||||
|
services.AddHttpContextAccessor();
|
||||||
|
services.AddScoped<IOSSService,OSSService>();
|
||||||
|
|
||||||
|
builder.AddNewtonsoftJson(options =>
|
||||||
|
{
|
||||||
|
//options.SerializerSettings.PreserveReferencesHandling = PreserveReferencesHandling.Objects;
|
||||||
|
// 忽略循环引用
|
||||||
|
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
|
||||||
|
//options.SerializerSettings.TypeNameHandling = TypeNameHandling.All;
|
||||||
|
|
||||||
|
//处理返回给前端 可空类型 给出默认值 比如in? 为null 设置 默认值0
|
||||||
|
options.SerializerSettings.ContractResolver = new NullToEmptyStringResolver(); //new DefaultContractResolver();// new NullToEmptyStringResolver();
|
||||||
|
// 设置时间格式
|
||||||
|
options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss";
|
||||||
|
|
||||||
|
options.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.RoundtripKind;
|
||||||
|
|
||||||
|
//options.SerializerSettings.Converters.Add(new JSONCustomDateConverter()) ;
|
||||||
|
|
||||||
|
//options.SerializerSettings.Converters.Add(services.BuildServiceProvider().GetService<JSONTimeZoneConverter>());
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
})
|
||||||
|
.AddControllersAsServices()//动态webApi属性注入需要
|
||||||
|
.ConfigureApiBehaviorOptions(o =>
|
||||||
|
{
|
||||||
|
o.SuppressModelStateInvalidFilter = true; //自己写验证
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
Newtonsoft.Json.JsonSerializerSettings setting = new Newtonsoft.Json.JsonSerializerSettings();
|
||||||
|
JsonConvert.DefaultSettings = new Func<JsonSerializerSettings>(() =>
|
||||||
|
{
|
||||||
|
//日期类型默认格式化处理
|
||||||
|
setting.DateFormatString = "yyyy-MM-dd HH:mm:ss";
|
||||||
|
setting.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
|
||||||
|
|
||||||
|
return setting;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Serialization;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.SCP
|
||||||
|
{
|
||||||
|
public class NullToEmptyStringResolver : DefaultContractResolver
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
|
||||||
|
{
|
||||||
|
IList<JsonProperty> properties = base.CreateProperties(type, memberSerialization);
|
||||||
|
|
||||||
|
var list= type.GetProperties()
|
||||||
|
.Select(p =>
|
||||||
|
{
|
||||||
|
var jp = base.CreateProperty(p, memberSerialization);
|
||||||
|
jp.ValueProvider = new NullToEmptyStringValueProvider(p);
|
||||||
|
return jp;
|
||||||
|
}).ToList();
|
||||||
|
|
||||||
|
var uu = list.Select(t => t.PropertyName).ToList();
|
||||||
|
|
||||||
|
//获取复杂对象属性
|
||||||
|
properties = properties.TakeWhile(t => !uu.Contains(t.PropertyName)).ToList();
|
||||||
|
|
||||||
|
list.AddRange(properties);
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
using Newtonsoft.Json.Serialization;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.SCP
|
||||||
|
{
|
||||||
|
|
||||||
|
public class NullToEmptyStringValueProvider : IValueProvider
|
||||||
|
{
|
||||||
|
PropertyInfo _MemberInfo;
|
||||||
|
public NullToEmptyStringValueProvider(PropertyInfo memberInfo)
|
||||||
|
{
|
||||||
|
_MemberInfo = memberInfo;
|
||||||
|
}
|
||||||
|
public object GetValue(object target)
|
||||||
|
{
|
||||||
|
object result = _MemberInfo.GetValue(target);
|
||||||
|
if (_MemberInfo.PropertyType == typeof(string) && result == null) result = "";
|
||||||
|
else if (_MemberInfo.PropertyType == typeof(String[]) && result == null) result = new string[] { };
|
||||||
|
//else if (_MemberInfo.PropertyType == typeof(Nullable<Int32>) && result == null) result = 0;
|
||||||
|
else if (_MemberInfo.PropertyType == typeof(Nullable<Decimal>) && result == null) result = 0.00M;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
public void SetValue(object target, object value)
|
||||||
|
{
|
||||||
|
|
||||||
|
if(_MemberInfo.PropertyType == typeof(string))
|
||||||
|
{
|
||||||
|
//去掉前后空格
|
||||||
|
_MemberInfo.SetValue(target, value==null?string.Empty: value.ToString()==string.Empty? value:value.ToString().Trim());
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_MemberInfo.SetValue(target, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="AlibabaCloud.SDK.Sts20150401" Version="1.1.4" />
|
||||||
|
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="10.0.0" />
|
||||||
|
<PackageReference Include="Aliyun.OSS.SDK.NetCore" Version="2.14.1" />
|
||||||
|
<PackageReference Include="AWSSDK.S3" Version="3.7.402.7" />
|
||||||
|
<PackageReference Include="AWSSDK.SecurityToken" Version="3.7.400.16" />
|
||||||
|
<PackageReference Include="DistributedLock.Core" Version="1.0.7" />
|
||||||
|
<PackageReference Include="DistributedLock.SqlServer" Version="1.0.5" />
|
||||||
|
<PackageReference Include="fo-dicom" Version="5.1.3" />
|
||||||
|
<PackageReference Include="fo-dicom.Codecs" Version="5.14.5" />
|
||||||
|
<PackageReference Include="fo-dicom.Imaging.ImageSharp" Version="5.1.3" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.8" />
|
||||||
|
<PackageReference Include="AutoMapper" Version="13.0.1" />
|
||||||
|
<PackageReference Include="Minio" Version="6.0.3" />
|
||||||
|
<PackageReference Include="My.Extensions.Localization.Json" Version="3.3.0">
|
||||||
|
<TreatAsUsed>true</TreatAsUsed>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="Panda.DynamicWebApi" Version="1.2.2" />
|
||||||
|
<PackageReference Include="Serilog.Enrichers.ClientInfo" Version="2.1.1" />
|
||||||
|
<PackageReference Include="Serilog.Extensions.Hosting" Version="8.0.0" />
|
||||||
|
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
|
||||||
|
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
|
||||||
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.7.3" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\IRaCIS.Core.Domain\IRaCIS.Core.Domain.csproj" />
|
||||||
|
<ProjectReference Include="..\IRaCIS.Core.Infra.EFCore\IRaCIS.Core.Infra.EFCore.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,219 @@
|
|||||||
|
|
||||||
|
using Autofac;
|
||||||
|
using Autofac.Extensions.DependencyInjection;
|
||||||
|
using AutoMapper.EquivalencyExpression;
|
||||||
|
using FellowOakDicom;
|
||||||
|
using FellowOakDicom.Imaging;
|
||||||
|
using FellowOakDicom.Imaging.NativeCodec;
|
||||||
|
using FellowOakDicom.Network;
|
||||||
|
using IRaCIS.Core.Infra.EFCore;
|
||||||
|
using IRaCIS.Core.SCP;
|
||||||
|
using IRaCIS.Core.SCP.Filter;
|
||||||
|
using IRaCIS.Core.SCP.Service;
|
||||||
|
using MassTransit;
|
||||||
|
using MassTransit.NewIdProviders;
|
||||||
|
using Microsoft.AspNetCore.HttpOverrides;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Panda.DynamicWebApi;
|
||||||
|
using Serilog;
|
||||||
|
using Serilog.Events;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
|
||||||
|
//以配置文件为准,否则 从url中取环境值(服务以命令行传递参数启动,配置文件配置了就不需要传递环境参数)
|
||||||
|
var config = new ConfigurationBuilder()
|
||||||
|
.AddEnvironmentVariables()
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
var enviromentName = config["ASPNETCORE_ENVIRONMENT"];
|
||||||
|
|
||||||
|
var builder = WebApplication.CreateBuilder(new WebApplicationOptions
|
||||||
|
{
|
||||||
|
EnvironmentName = enviromentName
|
||||||
|
});
|
||||||
|
|
||||||
|
#region 兼容windows 服务命令行的方式
|
||||||
|
|
||||||
|
int urlsIndex = Array.FindIndex(args, arg => arg != null && arg.StartsWith("--port"));
|
||||||
|
|
||||||
|
if (urlsIndex > -1)
|
||||||
|
{
|
||||||
|
var port = args[urlsIndex].Substring("--port=".Length);
|
||||||
|
Console.WriteLine(port);
|
||||||
|
builder.WebHost.UseUrls($"http://0.0.0.0:{port}");
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 主机配置
|
||||||
|
|
||||||
|
NewId.SetProcessIdProvider(new CurrentProcessIdProvider());
|
||||||
|
|
||||||
|
builder.Configuration.AddJsonFile("appsettings.json", false, true)
|
||||||
|
.AddJsonFile($"appsettings.{enviromentName}.json", false, true);
|
||||||
|
builder.Host
|
||||||
|
.UseServiceProviderFactory(new AutofacServiceProviderFactory())
|
||||||
|
.ConfigureContainer<ContainerBuilder>(containerBuilder =>
|
||||||
|
{
|
||||||
|
containerBuilder.RegisterModule<AutofacModuleSetup>();
|
||||||
|
})
|
||||||
|
.UseSerilog();
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 配置服务
|
||||||
|
var _configuration = builder.Configuration;
|
||||||
|
|
||||||
|
//健康检查
|
||||||
|
builder.Services.AddHealthChecks();
|
||||||
|
|
||||||
|
//本地化
|
||||||
|
builder.Services.AddJsonLocalization(options => options.ResourcesPath = "Resources");
|
||||||
|
|
||||||
|
|
||||||
|
// 异常、参数统一验证过滤器、Json序列化配置、字符串参数绑型统一Trim()
|
||||||
|
builder.Services.AddControllers(options =>
|
||||||
|
{
|
||||||
|
options.Filters.Add<ModelActionFilter>();
|
||||||
|
options.Filters.Add<ProjectExceptionFilter>();
|
||||||
|
options.Filters.Add<UnitOfWorkFilter>();
|
||||||
|
|
||||||
|
|
||||||
|
})
|
||||||
|
.AddNewtonsoftJsonSetup(builder.Services); // NewtonsoftJson 序列化 处理
|
||||||
|
|
||||||
|
|
||||||
|
builder.Services.AddOptions().Configure<AliyunOSSOptions>(_configuration.GetSection("AliyunOSS"));
|
||||||
|
builder.Services.AddOptions().Configure<ObjectStoreServiceOptions>(_configuration.GetSection("ObjectStoreService"));
|
||||||
|
builder.Services.AddOptions().Configure<DicomSCPServiceOption>(_configuration.GetSection("DicomSCPServiceConfig"));
|
||||||
|
|
||||||
|
|
||||||
|
//动态WebApi + UnifiedApiResultFilter 省掉控制器代码
|
||||||
|
//动态webApi 目前存在的唯一小坑是生成api上服务上的动态代理AOP失效 间接掉用不影响
|
||||||
|
builder.Services
|
||||||
|
.AddDynamicWebApi(dynamicWebApiOption =>
|
||||||
|
{
|
||||||
|
//默认是 api
|
||||||
|
dynamicWebApiOption.DefaultApiPrefix = "";
|
||||||
|
//首字母小写
|
||||||
|
dynamicWebApiOption.GetRestFulActionName = (actionName) => char.ToLower(actionName[0]) + actionName.Substring(1);
|
||||||
|
//删除 Service后缀
|
||||||
|
dynamicWebApiOption.RemoveControllerPostfixes.Add("Service");
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
//AutoMapper
|
||||||
|
builder.Services.AddAutoMapper(automapper =>
|
||||||
|
{
|
||||||
|
|
||||||
|
automapper.AddCollectionMappers();
|
||||||
|
|
||||||
|
|
||||||
|
}, typeof(BaseService).Assembly);
|
||||||
|
|
||||||
|
//EF ORM QueryWithNoLock
|
||||||
|
builder.Services.AddEFSetup(_configuration);
|
||||||
|
|
||||||
|
builder.Services.AddMediator(cfg =>
|
||||||
|
{
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
//转发头设置 获取真实IP
|
||||||
|
builder.Services.Configure<ForwardedHeadersOptions>(options =>
|
||||||
|
{
|
||||||
|
options.ForwardedHeaders =
|
||||||
|
ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
|
||||||
|
});
|
||||||
|
|
||||||
|
//Dicom影像渲染图片 跨平台
|
||||||
|
//builder.Services.AddDicomSetup();
|
||||||
|
new DicomSetupBuilder()
|
||||||
|
.RegisterServices(s =>
|
||||||
|
s.AddFellowOakDicom()
|
||||||
|
.AddTranscoderManager<NativeTranscoderManager>()
|
||||||
|
//.AddTranscoderManager<FellowOakDicom.Imaging.NativeCodec.NativeTranscoderManager>()
|
||||||
|
.AddImageManager<ImageSharpImageManager>())
|
||||||
|
.SkipValidation()
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
// Add services to the container.
|
||||||
|
|
||||||
|
builder.Services.AddControllers();
|
||||||
|
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
|
||||||
|
builder.Services.AddEndpointsApiExplorer();
|
||||||
|
builder.Services.AddSwaggerGen();
|
||||||
|
|
||||||
|
var app = builder.Build();
|
||||||
|
|
||||||
|
// Configure the HTTP request pipeline.
|
||||||
|
//if (app.Environment.IsDevelopment())
|
||||||
|
//{
|
||||||
|
app.UseSwagger();
|
||||||
|
app.UseSwaggerUI();
|
||||||
|
//}
|
||||||
|
|
||||||
|
app.UseAuthorization();
|
||||||
|
|
||||||
|
app.MapControllers();
|
||||||
|
|
||||||
|
#region 日志
|
||||||
|
|
||||||
|
//Log.Logger = new LoggerConfiguration()
|
||||||
|
// .MinimumLevel.Information()
|
||||||
|
// .MinimumLevel.Override("Microsoft", LogEventLevel.Information)
|
||||||
|
// // Filter out ASP.NET Core infrastructre logs that are Information and below 日志太多了 一个请求 记录好几条
|
||||||
|
// .MinimumLevel.Override("Microsoft.AspNetCore", LogEventLevel.Warning)
|
||||||
|
// .MinimumLevel.Override("Hangfire", LogEventLevel.Warning)
|
||||||
|
// .MinimumLevel.Override("System.Net.Http.HttpClient.HttpReports", LogEventLevel.Warning)
|
||||||
|
// .Enrich.WithClientIp()
|
||||||
|
|
||||||
|
// .Enrich.FromLogContext()
|
||||||
|
|
||||||
|
// //控制台 方便调试 问题 我们显示记录日志 时 获取上下文的ip 和用户名 用户类型
|
||||||
|
// .WriteTo.Console(restrictedToMinimumLevel: LogEventLevel.Warning,
|
||||||
|
// outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {SourceContext:l} || {Message} || {Exception} ||end {NewLine}")
|
||||||
|
|
||||||
|
// .WriteTo.File($"{AppContext.BaseDirectory}Serilogs/.log", rollingInterval: RollingInterval.Day,
|
||||||
|
// outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {SourceContext:l} || {Message} || {Exception} ||end {NewLine}")
|
||||||
|
// .CreateLogger();
|
||||||
|
|
||||||
|
Log.Logger = new LoggerConfiguration()
|
||||||
|
//.MinimumLevel.Information()
|
||||||
|
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
|
||||||
|
.WriteTo.Console()
|
||||||
|
.WriteTo.File($"{AppContext.BaseDirectory}Serilogs/.log", rollingInterval: RollingInterval.Day)
|
||||||
|
.CreateLogger();
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
#region 运行环境 部署平台
|
||||||
|
|
||||||
|
Log.Logger.Warning($"当前环境:{enviromentName}");
|
||||||
|
|
||||||
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||||
|
{
|
||||||
|
Log.Logger.Warning($"当前部署平台环境:windows");
|
||||||
|
}
|
||||||
|
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
||||||
|
{
|
||||||
|
Log.Logger.Warning($"当前部署平台环境:linux");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log.Logger.Warning($"当前部署平台环境:OSX or FreeBSD");
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
var logger = app.Services.GetService<Microsoft.Extensions.Logging.ILogger<Program>>();
|
||||||
|
|
||||||
|
var server = DicomServerFactory.Create<CStoreSCPService>(_configuration.GetSection("DicomSCPServiceConfig").GetValue<int>("ServerPort"), userState: app.Services,logger: logger);
|
||||||
|
|
||||||
|
|
||||||
|
app.Run();
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"$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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,111 @@
|
|||||||
|
using AutoMapper;
|
||||||
|
using IRaCIS.Core.Application.Service.BusinessFilter;
|
||||||
|
using IRaCIS.Core.Infra.EFCore;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.Extensions.Localization;
|
||||||
|
using Panda.DynamicWebApi;
|
||||||
|
using Panda.DynamicWebApi.Attributes;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using IRaCIS.Core.Domain.Share;
|
||||||
|
using IRaCIS.Core.Infrastructure.Extention;
|
||||||
|
using IRaCIS.Core.Domain.Models;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.SCP.Service
|
||||||
|
{
|
||||||
|
|
||||||
|
#pragma warning disable CS8618
|
||||||
|
|
||||||
|
|
||||||
|
#region 非泛型版本
|
||||||
|
|
||||||
|
[Authorize, DynamicWebApi, UnifiedApiResultFilter]
|
||||||
|
public class BaseService : IBaseService, IDynamicWebApi
|
||||||
|
{
|
||||||
|
public IMapper _mapper { get; set; }
|
||||||
|
|
||||||
|
public IUserInfo _userInfo { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
public IStringLocalizer _localizer { get; set; }
|
||||||
|
|
||||||
|
public IWebHostEnvironment _hostEnvironment { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static IResponseOutput Null404NotFound<TEntity>(TEntity? businessObject) where TEntity : class
|
||||||
|
{
|
||||||
|
return new ResponseOutput<string>()
|
||||||
|
.NotOk($"The query object {typeof(TEntity).Name} does not exist , or was deleted by someone else, or an incorrect parameter query caused", code: ApiResponseCodeEnum.DataNotExist);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public interface IBaseService
|
||||||
|
{
|
||||||
|
[MemberNotNull(nameof(_mapper))]
|
||||||
|
public IMapper _mapper { get; set; }
|
||||||
|
|
||||||
|
[MemberNotNull(nameof(_userInfo))]
|
||||||
|
public IUserInfo _userInfo { get; set; }
|
||||||
|
|
||||||
|
[MemberNotNull(nameof(_localizer))]
|
||||||
|
public IStringLocalizer _localizer { get; set; }
|
||||||
|
|
||||||
|
[MemberNotNull(nameof(_hostEnvironment))]
|
||||||
|
public IWebHostEnvironment _hostEnvironment { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
#region 泛型版本测试
|
||||||
|
|
||||||
|
|
||||||
|
public interface IBaseServiceTest<T> where T : Entity
|
||||||
|
{
|
||||||
|
[MemberNotNull(nameof(_mapper))]
|
||||||
|
public IMapper _mapper { get; set; }
|
||||||
|
|
||||||
|
[MemberNotNull(nameof(_userInfo))]
|
||||||
|
public IUserInfo _userInfo { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[MemberNotNull(nameof(_localizer))]
|
||||||
|
public IStringLocalizer _localizer { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Authorize, DynamicWebApi, UnifiedApiResultFilter]
|
||||||
|
public class BaseServiceTest<T> : IBaseServiceTest<T>, IDynamicWebApi where T : Entity
|
||||||
|
{
|
||||||
|
public IMapper _mapper { get; set; }
|
||||||
|
|
||||||
|
public IUserInfo _userInfo { get; set; }
|
||||||
|
|
||||||
|
public IStringLocalizer _localizer { get; set; }
|
||||||
|
|
||||||
|
public static IResponseOutput Null404NotFound<TEntity>(TEntity? businessObject) where TEntity : class
|
||||||
|
{
|
||||||
|
return new ResponseOutput<string>()
|
||||||
|
.NotOk($"The query object {typeof(TEntity).Name} does not exist , or was deleted by someone else, or an incorrect parameter query caused", code: ApiResponseCodeEnum.DataNotExist);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,377 @@
|
|||||||
|
using FellowOakDicom.Network;
|
||||||
|
using FellowOakDicom;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using IRaCIS.Core.SCP.Service;
|
||||||
|
using IRaCIS.Core.Domain.Models;
|
||||||
|
using IRaCIS.Core.Infra.EFCore;
|
||||||
|
using Medallion.Threading;
|
||||||
|
using IRaCIS.Core.Domain.Share;
|
||||||
|
using Serilog;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion.Internal;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using System.Data;
|
||||||
|
using FellowOakDicom.Imaging;
|
||||||
|
using SharpCompress.Common;
|
||||||
|
using SixLabors.ImageSharp.Formats.Jpeg;
|
||||||
|
using IRaCIS.Core.Infrastructure;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.SCP.Service
|
||||||
|
{
|
||||||
|
|
||||||
|
public class DicomSCPServiceOption
|
||||||
|
{
|
||||||
|
public List<string> CalledAEList { get; set; }
|
||||||
|
|
||||||
|
public string ServerPort { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public class CStoreSCPService : DicomService, IDicomServiceProvider, IDicomCStoreProvider, IDicomCEchoProvider
|
||||||
|
{
|
||||||
|
private IServiceProvider _serviceProvider { get; set; }
|
||||||
|
|
||||||
|
private List<Guid> _SCPStudyIdList { get; set; } = new List<Guid>();
|
||||||
|
|
||||||
|
private SCPImageUpload _upload { get; set; }
|
||||||
|
|
||||||
|
private Guid _trialId { get; set; }
|
||||||
|
|
||||||
|
private Guid _trialSiteId { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private static readonly DicomTransferSyntax[] _acceptedTransferSyntaxes = new DicomTransferSyntax[]
|
||||||
|
{
|
||||||
|
DicomTransferSyntax.ExplicitVRLittleEndian,
|
||||||
|
DicomTransferSyntax.ExplicitVRBigEndian,
|
||||||
|
DicomTransferSyntax.ImplicitVRLittleEndian
|
||||||
|
};
|
||||||
|
|
||||||
|
private static readonly DicomTransferSyntax[] _acceptedImageTransferSyntaxes = new DicomTransferSyntax[]
|
||||||
|
{
|
||||||
|
// Lossless
|
||||||
|
DicomTransferSyntax.JPEGLSLossless, //1.2.840.10008.1.2.4.80
|
||||||
|
DicomTransferSyntax.JPEG2000Lossless, //1.2.840.10008.1.2.4.90
|
||||||
|
DicomTransferSyntax.JPEGProcess14SV1, //1.2.840.10008.1.2.4.70
|
||||||
|
DicomTransferSyntax.JPEGProcess14, //1.2.840.10008.1.2.4.57 JPEG Lossless, Non-Hierarchical (Process 14)
|
||||||
|
DicomTransferSyntax.RLELossless, //1.2.840.10008.1.2.5
|
||||||
|
// Lossy
|
||||||
|
DicomTransferSyntax.JPEGLSNearLossless,//1.2.840.10008.1.2.4.81"
|
||||||
|
DicomTransferSyntax.JPEG2000Lossy, //1.2.840.10008.1.2.4.91
|
||||||
|
DicomTransferSyntax.JPEGProcess1, //1.2.840.10008.1.2.4.50
|
||||||
|
DicomTransferSyntax.JPEGProcess2_4, //1.2.840.10008.1.2.4.51
|
||||||
|
// Uncompressed
|
||||||
|
DicomTransferSyntax.ExplicitVRLittleEndian, //1.2.840.10008.1.2.1
|
||||||
|
DicomTransferSyntax.ExplicitVRBigEndian, //1.2.840.10008.1.2.2
|
||||||
|
DicomTransferSyntax.ImplicitVRLittleEndian //1.2.840.10008.1.2
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
public CStoreSCPService(INetworkStream stream, Encoding fallbackEncoding, Microsoft.Extensions.Logging.ILogger log, DicomServiceDependencies dependencies)
|
||||||
|
: base(stream, fallbackEncoding, log, dependencies)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public Task OnReceiveAssociationRequestAsync(DicomAssociation association)
|
||||||
|
{
|
||||||
|
|
||||||
|
_upload = new SCPImageUpload() { StartTime = DateTime.Now, CallingAE = association.CallingAE, CalledAE = association.CalledAE, CallingAEIP = association.RemoteHost };
|
||||||
|
|
||||||
|
|
||||||
|
Log.Logger.Warning($"接收到来自{association.CallingAE}的连接");
|
||||||
|
|
||||||
|
_serviceProvider = (IServiceProvider)this.UserState;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var _trialDicomAERepository = _serviceProvider.GetService<IRepository<TrialDicomAE>>();
|
||||||
|
|
||||||
|
|
||||||
|
var trialDicomAEList = _trialDicomAERepository.Select(t => new { t.CalledAE, t.TrialId }).ToList();
|
||||||
|
var trialCalledAEList = trialDicomAEList.Select(t => t.CalledAE).ToList();
|
||||||
|
|
||||||
|
Log.Logger.Information("当前系统配置:", string.Join('|', trialDicomAEList));
|
||||||
|
|
||||||
|
var findCalledAE = trialDicomAEList.Where(t => t.CalledAE == association.CalledAE).FirstOrDefault();
|
||||||
|
|
||||||
|
var isCanReceiveIamge = false;
|
||||||
|
|
||||||
|
if (findCalledAE != null)
|
||||||
|
{
|
||||||
|
_trialId = findCalledAE.TrialId;
|
||||||
|
|
||||||
|
var _trialSiteDicomAERepository = _serviceProvider.GetService<IRepository<TrialSiteDicomAE>>();
|
||||||
|
|
||||||
|
|
||||||
|
var findTrialSiteAE = _trialSiteDicomAERepository.Where(t => t.CallingAE == association.CallingAE && t.TrialId==_trialId).FirstOrDefault();
|
||||||
|
|
||||||
|
if (findTrialSiteAE != null)
|
||||||
|
{
|
||||||
|
_trialSiteId = findTrialSiteAE.TrialSiteId;
|
||||||
|
|
||||||
|
isCanReceiveIamge = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (association.CallingAE == "test-callingAE")
|
||||||
|
{
|
||||||
|
isCanReceiveIamge = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!trialCalledAEList.Contains(association.CalledAE) || isCanReceiveIamge == false)
|
||||||
|
{
|
||||||
|
|
||||||
|
Log.Logger.Warning($"拒绝CallingAE:{association.CallingAE} CalledAE:{association.CalledAE}的连接");
|
||||||
|
|
||||||
|
return SendAssociationRejectAsync(
|
||||||
|
DicomRejectResult.Permanent,
|
||||||
|
DicomRejectSource.ServiceUser,
|
||||||
|
DicomRejectReason.CalledAENotRecognized);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var pc in association.PresentationContexts)
|
||||||
|
{
|
||||||
|
if (pc.AbstractSyntax == DicomUID.Verification)
|
||||||
|
{
|
||||||
|
pc.AcceptTransferSyntaxes(_acceptedTransferSyntaxes);
|
||||||
|
}
|
||||||
|
else if (pc.AbstractSyntax.StorageCategory != DicomStorageCategory.None)
|
||||||
|
{
|
||||||
|
pc.AcceptTransferSyntaxes(_acceptedImageTransferSyntaxes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return SendAssociationAcceptAsync(association);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public async Task OnReceiveAssociationReleaseRequestAsync()
|
||||||
|
{
|
||||||
|
await DataMaintenanceAsaync();
|
||||||
|
|
||||||
|
//记录监控
|
||||||
|
|
||||||
|
var _SCPImageUploadRepository = _serviceProvider.GetService<IRepository<SCPImageUpload>>();
|
||||||
|
|
||||||
|
_upload.EndTime = DateTime.Now;
|
||||||
|
_upload.StudyCount = _SCPStudyIdList.Count;
|
||||||
|
_upload.TrialId = _trialId;
|
||||||
|
_upload.TrialSiteId = _trialSiteId;
|
||||||
|
|
||||||
|
await _SCPImageUploadRepository.AddAsync(_upload, true);
|
||||||
|
|
||||||
|
|
||||||
|
var _studyRepository = _serviceProvider.GetService<IRepository<SCPStudy>>();
|
||||||
|
//将检查设置为传输结束
|
||||||
|
await _studyRepository.BatchUpdateNoTrackingAsync(t => _SCPStudyIdList.Contains(t.Id), u => new SCPStudy() { IsUploadFinished = true });
|
||||||
|
|
||||||
|
await _studyRepository.SaveChangesAndClearAllTrackingAsync();
|
||||||
|
|
||||||
|
await SendAssociationReleaseResponseAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private async Task DataMaintenanceAsaync()
|
||||||
|
{
|
||||||
|
Log.Logger.Warning($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE}传输结束:开始维护数据,处理检查Modality");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//处理检查Modality
|
||||||
|
var _dictionaryRepository = _serviceProvider.GetService<IRepository<Dictionary>>();
|
||||||
|
var _seriesRepository = _serviceProvider.GetService<IRepository<SCPSeries>>();
|
||||||
|
var _studyRepository = _serviceProvider.GetService<IRepository<SCPStudy>>();
|
||||||
|
|
||||||
|
var dicModalityList = _dictionaryRepository.Where(t => t.Code == "Modality").SelectMany(t => t.ChildList.Select(c => c.Value)).ToList();
|
||||||
|
var seriesModalityList = _seriesRepository.Where(t => _SCPStudyIdList.Contains(t.StudyId)).Select(t => new { SCPStudyId = t.StudyId, t.Modality }).ToList();
|
||||||
|
|
||||||
|
foreach (var g in seriesModalityList.GroupBy(t => t.SCPStudyId))
|
||||||
|
{
|
||||||
|
var modality = string.Join('、', g.Select(t => t.Modality).Distinct().ToList());
|
||||||
|
|
||||||
|
//特殊逻辑
|
||||||
|
var modalityForEdit = dicModalityList.Contains(modality) ? modality : String.Empty;
|
||||||
|
|
||||||
|
if (modality == "MR")
|
||||||
|
{
|
||||||
|
modalityForEdit = "MRI";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (modality == "PT")
|
||||||
|
{
|
||||||
|
modalityForEdit = "PET";
|
||||||
|
}
|
||||||
|
if (modality == "PT、CT" || modality == "CT、PT")
|
||||||
|
{
|
||||||
|
modalityForEdit = "PET-CT";
|
||||||
|
}
|
||||||
|
|
||||||
|
await _studyRepository.BatchUpdateNoTrackingAsync(t => t.Id == g.Key, u => new SCPStudy() { Modalities = modality, ModalityForEdit = modalityForEdit });
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.Logger.Warning($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE}维护数据结束");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnReceiveAbort(DicomAbortSource source, DicomAbortReason reason)
|
||||||
|
{
|
||||||
|
Log.Logger.Warning($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE}接收中断,中断原因:{source.ToString() + reason.ToString()}");
|
||||||
|
/* nothing to do here */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public async void OnConnectionClosed(Exception exception)
|
||||||
|
{
|
||||||
|
/* nothing to do here */
|
||||||
|
|
||||||
|
//奇怪的bug 上传的时候,用王捷修改的影像,会关闭,重新连接,导致检查id 丢失,然后状态不一致
|
||||||
|
if (exception == null)
|
||||||
|
{
|
||||||
|
//var _studyRepository = _serviceProvider.GetService<IRepository<SCPStudy>>();
|
||||||
|
////将检查设置为传输结束
|
||||||
|
//await _studyRepository.BatchUpdateNoTrackingAsync(t => _SCPStudyIdList.Contains(t.Id), u => new SCPStudy() { IsUploadFinished = true });
|
||||||
|
|
||||||
|
//await _studyRepository.SaveChangesAndClearAllTrackingAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.Logger.Warning($"连接关闭 {exception?.Message} {exception?.InnerException?.Message}");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public async Task<DicomCStoreResponse> OnCStoreRequestAsync(DicomCStoreRequest request)
|
||||||
|
{
|
||||||
|
|
||||||
|
string studyInstanceUid = request.Dataset.GetString(DicomTag.StudyInstanceUID);
|
||||||
|
string seriesInstanceUid = request.Dataset.GetString(DicomTag.SeriesInstanceUID);
|
||||||
|
string sopInstanceUid = request.Dataset.GetString(DicomTag.SOPInstanceUID);
|
||||||
|
|
||||||
|
//Guid studyId = IdentifierHelper.CreateGuid(studyInstanceUid, trialId.ToString());
|
||||||
|
Guid seriesId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid, _trialId.ToString());
|
||||||
|
Guid instanceId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid, sopInstanceUid, _trialId.ToString());
|
||||||
|
|
||||||
|
|
||||||
|
var ossService = _serviceProvider.GetService<IOSSService>();
|
||||||
|
var dicomArchiveService = _serviceProvider.GetService<IDicomArchiveService>();
|
||||||
|
var _seriesRepository = _serviceProvider.GetService<IRepository<SCPSeries>>();
|
||||||
|
|
||||||
|
var _distributedLockProvider = _serviceProvider.GetService<IDistributedLockProvider>();
|
||||||
|
|
||||||
|
var storeRelativePath = string.Empty;
|
||||||
|
var ossFolderPath = $"{_trialId}/Image/PACS/{_trialSiteId}{studyInstanceUid}";
|
||||||
|
|
||||||
|
|
||||||
|
long fileSize = 0;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
|
||||||
|
using (MemoryStream ms = new MemoryStream())
|
||||||
|
{
|
||||||
|
await request.File.SaveAsync(ms);
|
||||||
|
|
||||||
|
//irc 从路径最后一截取Guid
|
||||||
|
storeRelativePath = await ossService.UploadToOSSAsync(ms, ossFolderPath, instanceId.ToString(), false);
|
||||||
|
|
||||||
|
fileSize = ms.Length;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.Logger.Information($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE} {request.SOPInstanceUID} 上传完成 ");
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (Exception ec)
|
||||||
|
{
|
||||||
|
Log.Logger.Warning($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE} 上传异常 {ec.Message}");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var @lock = _distributedLockProvider.CreateLock($"{studyInstanceUid}");
|
||||||
|
|
||||||
|
using (await @lock.AcquireAsync())
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var scpStudyId = await dicomArchiveService.ArchiveDicomFileAsync(request.Dataset, _trialId, _trialSiteId, storeRelativePath, Association.CallingAE, Association.CalledAE,fileSize);
|
||||||
|
|
||||||
|
if (!_SCPStudyIdList.Contains(scpStudyId))
|
||||||
|
{
|
||||||
|
_SCPStudyIdList.Add(scpStudyId);
|
||||||
|
}
|
||||||
|
|
||||||
|
var series = await _seriesRepository.FindAsync(seriesId);
|
||||||
|
|
||||||
|
//没有缩略图
|
||||||
|
if (series != null && string.IsNullOrEmpty(series.ImageResizePath))
|
||||||
|
{
|
||||||
|
|
||||||
|
// 生成缩略图
|
||||||
|
using (var memoryStream = new MemoryStream())
|
||||||
|
{
|
||||||
|
DicomImage image = new DicomImage(request.Dataset);
|
||||||
|
|
||||||
|
var sharpimage = image.RenderImage().AsSharpImage();
|
||||||
|
sharpimage.Save(memoryStream, new JpegEncoder());
|
||||||
|
|
||||||
|
// 上传缩略图到 OSS
|
||||||
|
|
||||||
|
var seriesPath = await ossService.UploadToOSSAsync(memoryStream, ossFolderPath, seriesId.ToString() + ".preview.jpg", false);
|
||||||
|
|
||||||
|
Console.WriteLine(seriesPath + " Id: " + seriesId);
|
||||||
|
|
||||||
|
series.ImageResizePath = seriesPath;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
await _seriesRepository.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
|
||||||
|
Log.Logger.Warning($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE} 传输处理异常:{ex.ToString()}");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//监控信息设置
|
||||||
|
_upload.FileCount++;
|
||||||
|
_upload.FileSize = _upload.FileSize + fileSize;
|
||||||
|
return new DicomCStoreResponse(request, DicomStatus.Success);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Task OnCStoreRequestExceptionAsync(string tempFileName, Exception e)
|
||||||
|
{
|
||||||
|
// let library handle logging and error response
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Task<DicomCEchoResponse> OnCEchoRequestAsync(DicomCEchoRequest request)
|
||||||
|
{
|
||||||
|
return Task.FromResult(new DicomCEchoResponse(request, DicomStatus.Success));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,356 @@
|
|||||||
|
using IRaCIS.Core.Domain.Share;
|
||||||
|
using System.Text;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using IRaCIS.Core.Infrastructure;
|
||||||
|
using Medallion.Threading;
|
||||||
|
using FellowOakDicom;
|
||||||
|
using FellowOakDicom.Imaging.Codec;
|
||||||
|
using System.Data;
|
||||||
|
using IRaCIS.Core.Domain.Models;
|
||||||
|
using FellowOakDicom.Network;
|
||||||
|
using IRaCIS.Core.SCP.Service;
|
||||||
|
using IRaCIS.Core.Infra.EFCore;
|
||||||
|
using MassTransit;
|
||||||
|
using System.Runtime.Intrinsics.X86;
|
||||||
|
using Serilog.Sinks.File;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.SCP.Service
|
||||||
|
{
|
||||||
|
public class DicomArchiveService : BaseService, IDicomArchiveService
|
||||||
|
{
|
||||||
|
private readonly IRepository<SCPPatient> _patientRepository;
|
||||||
|
private readonly IRepository<SCPStudy> _studyRepository;
|
||||||
|
private readonly IRepository<SCPSeries> _seriesRepository;
|
||||||
|
private readonly IRepository<SCPInstance> _instanceRepository;
|
||||||
|
private readonly IRepository<Dictionary> _dictionaryRepository;
|
||||||
|
private readonly IDistributedLockProvider _distributedLockProvider;
|
||||||
|
|
||||||
|
|
||||||
|
private List<Guid> _instanceIdList = new List<Guid>();
|
||||||
|
|
||||||
|
public DicomArchiveService(IRepository<SCPPatient> patientRepository, IRepository<SCPStudy> studyRepository,
|
||||||
|
IRepository<SCPSeries> seriesRepository,
|
||||||
|
IRepository<SCPInstance> instanceRepository,
|
||||||
|
IRepository<Dictionary> dictionaryRepository,
|
||||||
|
IDistributedLockProvider distributedLockProvider)
|
||||||
|
{
|
||||||
|
_distributedLockProvider = distributedLockProvider;
|
||||||
|
_studyRepository = studyRepository;
|
||||||
|
_patientRepository = patientRepository;
|
||||||
|
_seriesRepository = seriesRepository;
|
||||||
|
_instanceRepository = instanceRepository;
|
||||||
|
_dictionaryRepository = dictionaryRepository;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 单个文件接收 归档
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dataset"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <exception cref="NotImplementedException"></exception>
|
||||||
|
public async Task<Guid> ArchiveDicomFileAsync(DicomDataset dataset, Guid trialId, Guid trialSiteId, string fileRelativePath, string callingAE, string calledAE,long fileSize)
|
||||||
|
{
|
||||||
|
string studyInstanceUid = dataset.GetString(DicomTag.StudyInstanceUID);
|
||||||
|
string seriesInstanceUid = dataset.GetString(DicomTag.SeriesInstanceUID);
|
||||||
|
string sopInstanceUid = dataset.GetString(DicomTag.SOPInstanceUID);
|
||||||
|
|
||||||
|
string patientIdStr = dataset.GetSingleValueOrDefault(DicomTag.PatientID,string.Empty);
|
||||||
|
|
||||||
|
//Guid patientId= IdentifierHelper.CreateGuid(patientIdStr);
|
||||||
|
Guid studyId = IdentifierHelper.CreateGuid(studyInstanceUid,trialId.ToString());
|
||||||
|
Guid seriesId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid, trialId.ToString());
|
||||||
|
Guid instanceId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid, sopInstanceUid, trialId.ToString());
|
||||||
|
|
||||||
|
var isStudyNeedAdd = false;
|
||||||
|
var isSeriesNeedAdd = false;
|
||||||
|
var isInstanceNeedAdd = false;
|
||||||
|
var isPatientNeedAdd = false;
|
||||||
|
|
||||||
|
//var @lock = _distributedLockProvider.CreateLock($"{studyInstanceUid}");
|
||||||
|
|
||||||
|
//using (@lock.Acquire())
|
||||||
|
{
|
||||||
|
var findPatient = await _patientRepository.FirstOrDefaultAsync(t => t.PatientIdStr == patientIdStr && t.TrialSiteId==trialSiteId );
|
||||||
|
var findStudy = await _studyRepository.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);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
using FellowOakDicom;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion.Internal;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.SCP.Service
|
||||||
|
{
|
||||||
|
public interface IDicomArchiveService
|
||||||
|
{
|
||||||
|
Task<Guid> ArchiveDicomFileAsync(DicomDataset dicomDataset,Guid trialId,Guid trialSiteId, string fileRelativePath,string callingAE,string calledAE,long fileSize);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,770 @@
|
|||||||
|
using AlibabaCloud.SDK.Sts20150401;
|
||||||
|
using Aliyun.OSS;
|
||||||
|
using Amazon;
|
||||||
|
using Amazon.Runtime;
|
||||||
|
using Amazon.S3;
|
||||||
|
using Amazon.S3.Model;
|
||||||
|
using Amazon.SecurityToken;
|
||||||
|
using Amazon.SecurityToken.Model;
|
||||||
|
using IRaCIS.Core.Infrastructure;
|
||||||
|
using IRaCIS.Core.Infrastructure.NewtonsoftJson;
|
||||||
|
using MassTransit;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using Minio;
|
||||||
|
using Minio.DataModel.Args;
|
||||||
|
using System.Reactive.Linq;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.SCP;
|
||||||
|
|
||||||
|
#region 绑定和返回模型
|
||||||
|
|
||||||
|
[LowerCamelCaseJson]
|
||||||
|
public class MinIOOptions : AWSOptions
|
||||||
|
{
|
||||||
|
public int Port { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public class AWSOptions
|
||||||
|
{
|
||||||
|
public string EndPoint { get; set; }
|
||||||
|
public bool UseSSL { get; set; }
|
||||||
|
public string AccessKeyId { get; set; }
|
||||||
|
public string RoleArn { get; set; }
|
||||||
|
public string SecretAccessKey { get; set; }
|
||||||
|
public string BucketName { get; set; }
|
||||||
|
public string ViewEndpoint { get; set; }
|
||||||
|
public int DurationSeconds { get; set; }
|
||||||
|
public string Region { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AliyunOSSOptions
|
||||||
|
{
|
||||||
|
public string RegionId { get; set; }
|
||||||
|
public string AccessKeyId { get; set; }
|
||||||
|
public string AccessKeySecret { get; set; }
|
||||||
|
|
||||||
|
public string InternalEndpoint { get; set; }
|
||||||
|
|
||||||
|
public string EndPoint { get; set; }
|
||||||
|
public string BucketName { get; set; }
|
||||||
|
|
||||||
|
public string RoleArn { get; set; }
|
||||||
|
|
||||||
|
public string Region { get; set; }
|
||||||
|
|
||||||
|
public string ViewEndpoint { get; set; }
|
||||||
|
|
||||||
|
public int DurationSeconds { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ObjectStoreServiceOptions
|
||||||
|
{
|
||||||
|
public string ObjectStoreUse { get; set; }
|
||||||
|
|
||||||
|
public AliyunOSSOptions AliyunOSS { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
public MinIOOptions MinIO { get; set; }
|
||||||
|
|
||||||
|
public AWSOptions AWS { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ObjectStoreDTO
|
||||||
|
{
|
||||||
|
public string ObjectStoreUse { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
public AliyunOSSTempToken AliyunOSS { get; set; }
|
||||||
|
|
||||||
|
public MinIOOptions MinIO { get; set; }
|
||||||
|
|
||||||
|
public AWSTempToken AWS { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
[LowerCamelCaseJson]
|
||||||
|
public class AliyunOSSTempToken
|
||||||
|
{
|
||||||
|
public string AccessKeyId { get; set; }
|
||||||
|
public string AccessKeySecret { get; set; }
|
||||||
|
|
||||||
|
public string EndPoint { get; set; }
|
||||||
|
public string BucketName { get; set; }
|
||||||
|
|
||||||
|
public string Region { get; set; }
|
||||||
|
|
||||||
|
public string ViewEndpoint { get; set; }
|
||||||
|
|
||||||
|
public string SecurityToken { get; set; }
|
||||||
|
public DateTime Expiration { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
[LowerCamelCaseJson]
|
||||||
|
public class AWSTempToken
|
||||||
|
{
|
||||||
|
public string Region { get; set; }
|
||||||
|
public string SessionToken { get; set; }
|
||||||
|
public string EndPoint { get; set; }
|
||||||
|
public string AccessKeyId { get; set; }
|
||||||
|
public string SecretAccessKey { get; set; }
|
||||||
|
public string BucketName { get; set; }
|
||||||
|
public string ViewEndpoint { get; set; }
|
||||||
|
public DateTime Expiration { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum ObjectStoreUse
|
||||||
|
{
|
||||||
|
AliyunOSS = 0,
|
||||||
|
MinIO = 1,
|
||||||
|
AWS = 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
// aws 参考链接 https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/dotnetv3/S3/S3_Basics
|
||||||
|
|
||||||
|
public interface IOSSService
|
||||||
|
{
|
||||||
|
public Task<string> UploadToOSSAsync(Stream fileStream, string oosFolderPath, string fileRealName, bool isFileNameAddGuid = true);
|
||||||
|
public Task<string> UploadToOSSAsync(string localFilePath, string oosFolderPath, bool isFileNameAddGuid = true);
|
||||||
|
|
||||||
|
public Task DownLoadFromOSSAsync(string ossRelativePath, string localFilePath);
|
||||||
|
|
||||||
|
public ObjectStoreServiceOptions ObjectStoreServiceOptions { get; set; }
|
||||||
|
|
||||||
|
public Task<string> GetSignedUrl(string ossRelativePath);
|
||||||
|
|
||||||
|
public Task DeleteFromPrefix(string prefix);
|
||||||
|
|
||||||
|
public ObjectStoreDTO GetObjectStoreTempToken();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public class OSSService : IOSSService
|
||||||
|
{
|
||||||
|
public ObjectStoreServiceOptions ObjectStoreServiceOptions { get; set; }
|
||||||
|
|
||||||
|
private AliyunOSSTempToken AliyunOSSTempToken { get; set; }
|
||||||
|
|
||||||
|
private AWSTempToken AWSTempToken { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
public OSSService(IOptionsMonitor<ObjectStoreServiceOptions> options)
|
||||||
|
{
|
||||||
|
ObjectStoreServiceOptions = options.CurrentValue;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// oosFolderPath 不要 "/ "开头 应该: TempFolder/ChildFolder
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="fileStream"></param>
|
||||||
|
/// <param name="oosFolderPath"></param>
|
||||||
|
/// <param name="fileRealName"></param>
|
||||||
|
/// <param name="isFileNameAddGuid"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<string> UploadToOSSAsync(Stream fileStream, string oosFolderPath, string fileRealName, bool isFileNameAddGuid = true)
|
||||||
|
{
|
||||||
|
GetObjectStoreTempToken();
|
||||||
|
|
||||||
|
var ossRelativePath = isFileNameAddGuid ? $"{oosFolderPath}/{Guid.NewGuid()}_{fileRealName}" : $"{oosFolderPath}/{fileRealName}";
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var memoryStream = new MemoryStream())
|
||||||
|
{
|
||||||
|
fileStream.Seek(0, SeekOrigin.Begin);
|
||||||
|
|
||||||
|
fileStream.CopyTo(memoryStream);
|
||||||
|
|
||||||
|
memoryStream.Seek(0, SeekOrigin.Begin);
|
||||||
|
|
||||||
|
|
||||||
|
if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS")
|
||||||
|
{
|
||||||
|
var aliConfig = ObjectStoreServiceOptions.AliyunOSS;
|
||||||
|
|
||||||
|
var _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.EndPoint : aliConfig.InternalEndpoint, AliyunOSSTempToken.AccessKeyId, AliyunOSSTempToken.AccessKeySecret, AliyunOSSTempToken.SecurityToken);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 上传文件
|
||||||
|
var result = _ossClient.PutObject(aliConfig.BucketName, ossRelativePath, memoryStream);
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (ObjectStoreServiceOptions.ObjectStoreUse == "MinIO")
|
||||||
|
{
|
||||||
|
var minIOConfig = ObjectStoreServiceOptions.MinIO;
|
||||||
|
|
||||||
|
|
||||||
|
var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.EndPoint}:{minIOConfig.Port}")
|
||||||
|
.WithCredentials(minIOConfig.AccessKeyId, minIOConfig.SecretAccessKey).WithSSL(minIOConfig.UseSSL)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
var putObjectArgs = new PutObjectArgs()
|
||||||
|
.WithBucket(minIOConfig.BucketName)
|
||||||
|
.WithObject(ossRelativePath)
|
||||||
|
.WithStreamData(memoryStream)
|
||||||
|
.WithObjectSize(memoryStream.Length);
|
||||||
|
|
||||||
|
await minioClient.PutObjectAsync(putObjectArgs);
|
||||||
|
}
|
||||||
|
else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS")
|
||||||
|
{
|
||||||
|
var awsConfig = ObjectStoreServiceOptions.AWS;
|
||||||
|
|
||||||
|
var credentials = new SessionAWSCredentials(AWSTempToken.AccessKeyId, AWSTempToken.SecretAccessKey, AWSTempToken.SessionToken);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//提供awsEndPoint(域名)进行访问配置
|
||||||
|
var clientConfig = new AmazonS3Config
|
||||||
|
{
|
||||||
|
RegionEndpoint = RegionEndpoint.USEast1,
|
||||||
|
UseHttp = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
|
||||||
|
|
||||||
|
var putObjectRequest = new Amazon.S3.Model.PutObjectRequest()
|
||||||
|
{
|
||||||
|
BucketName = awsConfig.BucketName,
|
||||||
|
InputStream = memoryStream,
|
||||||
|
Key = ossRelativePath,
|
||||||
|
};
|
||||||
|
|
||||||
|
await amazonS3Client.PutObjectAsync(putObjectRequest);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new BusinessValidationFailedException("未定义的存储介质类型");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
|
||||||
|
throw new BusinessValidationFailedException($"上传发生异常:{ex.Message}");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return "/" + ossRelativePath;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// oosFolderPath 不要 "/ "开头 应该: TempFolder/ChildFolder
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="localFilePath"></param>
|
||||||
|
/// <param name="oosFolderPath"></param>
|
||||||
|
/// <param name="isFileNameAddGuid"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <exception cref="BusinessValidationFailedException"></exception>
|
||||||
|
public async Task<string> UploadToOSSAsync(string localFilePath, string oosFolderPath, bool isFileNameAddGuid = true)
|
||||||
|
{
|
||||||
|
GetObjectStoreTempToken();
|
||||||
|
|
||||||
|
var localFileName = Path.GetFileName(localFilePath);
|
||||||
|
|
||||||
|
var ossRelativePath = isFileNameAddGuid ? $"{oosFolderPath}/{Guid.NewGuid()}_{localFileName}" : $"{oosFolderPath}/{localFileName}";
|
||||||
|
|
||||||
|
|
||||||
|
if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS")
|
||||||
|
{
|
||||||
|
var aliConfig = ObjectStoreServiceOptions.AliyunOSS;
|
||||||
|
|
||||||
|
var _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.EndPoint : aliConfig.InternalEndpoint, AliyunOSSTempToken.AccessKeyId, AliyunOSSTempToken.AccessKeySecret, AliyunOSSTempToken.SecurityToken);
|
||||||
|
|
||||||
|
// 上传文件
|
||||||
|
var result = _ossClient.PutObject(aliConfig.BucketName, ossRelativePath, localFilePath);
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (ObjectStoreServiceOptions.ObjectStoreUse == "MinIO")
|
||||||
|
{
|
||||||
|
var minIOConfig = ObjectStoreServiceOptions.MinIO;
|
||||||
|
|
||||||
|
|
||||||
|
var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.EndPoint}:{minIOConfig.Port}")
|
||||||
|
.WithCredentials(minIOConfig.AccessKeyId, minIOConfig.SecretAccessKey).WithSSL(minIOConfig.UseSSL)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
var putObjectArgs = new PutObjectArgs()
|
||||||
|
.WithBucket(minIOConfig.BucketName)
|
||||||
|
.WithObject(ossRelativePath)
|
||||||
|
.WithFileName(localFilePath);
|
||||||
|
|
||||||
|
await minioClient.PutObjectAsync(putObjectArgs);
|
||||||
|
}
|
||||||
|
else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS")
|
||||||
|
{
|
||||||
|
var awsConfig = ObjectStoreServiceOptions.AWS;
|
||||||
|
|
||||||
|
// 提供awsAccessKeyId和awsSecretAccessKey构造凭证
|
||||||
|
var credentials = new SessionAWSCredentials(AWSTempToken.AccessKeyId, AWSTempToken.SecretAccessKey, AWSTempToken.SessionToken);
|
||||||
|
|
||||||
|
//提供awsEndPoint(域名)进行访问配置
|
||||||
|
var clientConfig = new AmazonS3Config
|
||||||
|
{
|
||||||
|
RegionEndpoint = RegionEndpoint.USEast1,
|
||||||
|
UseHttp = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
|
||||||
|
|
||||||
|
var putObjectRequest = new Amazon.S3.Model.PutObjectRequest()
|
||||||
|
{
|
||||||
|
BucketName = awsConfig.BucketName,
|
||||||
|
FilePath = localFilePath,
|
||||||
|
Key = ossRelativePath,
|
||||||
|
};
|
||||||
|
|
||||||
|
await amazonS3Client.PutObjectAsync(putObjectRequest);
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new BusinessValidationFailedException("未定义的存储介质类型");
|
||||||
|
}
|
||||||
|
return "/" + ossRelativePath;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public async Task DownLoadFromOSSAsync(string ossRelativePath, string localFilePath)
|
||||||
|
{
|
||||||
|
GetObjectStoreTempToken();
|
||||||
|
|
||||||
|
ossRelativePath = ossRelativePath.TrimStart('/');
|
||||||
|
try
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS")
|
||||||
|
{
|
||||||
|
var aliConfig = ObjectStoreServiceOptions.AliyunOSS;
|
||||||
|
|
||||||
|
var _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.EndPoint : aliConfig.InternalEndpoint, AliyunOSSTempToken.AccessKeyId, AliyunOSSTempToken.AccessKeySecret, AliyunOSSTempToken.SecurityToken);
|
||||||
|
|
||||||
|
// 上传文件
|
||||||
|
var result = _ossClient.GetObject(aliConfig.BucketName, ossRelativePath);
|
||||||
|
|
||||||
|
// 将下载的文件流保存到本地文件
|
||||||
|
using (var fs = File.OpenWrite(localFilePath))
|
||||||
|
{
|
||||||
|
result.Content.CopyTo(fs);
|
||||||
|
fs.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (ObjectStoreServiceOptions.ObjectStoreUse == "MinIO")
|
||||||
|
{
|
||||||
|
var minIOConfig = ObjectStoreServiceOptions.MinIO;
|
||||||
|
|
||||||
|
var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.EndPoint}:{minIOConfig.Port}")
|
||||||
|
.WithCredentials(minIOConfig.AccessKeyId, minIOConfig.SecretAccessKey).WithSSL(minIOConfig.UseSSL)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
var getObjectArgs = new GetObjectArgs()
|
||||||
|
.WithBucket(minIOConfig.BucketName)
|
||||||
|
.WithObject(ossRelativePath)
|
||||||
|
.WithFile(localFilePath);
|
||||||
|
|
||||||
|
await minioClient.GetObjectAsync(getObjectArgs);
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS")
|
||||||
|
{
|
||||||
|
var awsConfig = ObjectStoreServiceOptions.AWS;
|
||||||
|
|
||||||
|
// 提供awsAccessKeyId和awsSecretAccessKey构造凭证
|
||||||
|
var credentials = new SessionAWSCredentials(AWSTempToken.AccessKeyId, AWSTempToken.SecretAccessKey, AWSTempToken.SessionToken);
|
||||||
|
|
||||||
|
//提供awsEndPoint(域名)进行访问配置
|
||||||
|
var clientConfig = new AmazonS3Config
|
||||||
|
{
|
||||||
|
RegionEndpoint = RegionEndpoint.USEast1,
|
||||||
|
UseHttp = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
|
||||||
|
|
||||||
|
var getObjectArgs = new Amazon.S3.Model.GetObjectRequest()
|
||||||
|
{
|
||||||
|
BucketName = awsConfig.BucketName,
|
||||||
|
Key = ossRelativePath,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
await (await amazonS3Client.GetObjectAsync(getObjectArgs)).WriteResponseStreamToFileAsync(localFilePath, true, CancellationToken.None);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new BusinessValidationFailedException("未定义的存储介质类型");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
|
||||||
|
throw new BusinessValidationFailedException("oss下载失败!" + ex.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<string> GetSignedUrl(string ossRelativePath)
|
||||||
|
{
|
||||||
|
GetObjectStoreTempToken();
|
||||||
|
|
||||||
|
ossRelativePath = ossRelativePath.TrimStart('/');
|
||||||
|
try
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS")
|
||||||
|
{
|
||||||
|
var aliConfig = ObjectStoreServiceOptions.AliyunOSS;
|
||||||
|
|
||||||
|
var _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.EndPoint : aliConfig.InternalEndpoint, AliyunOSSTempToken.AccessKeyId, AliyunOSSTempToken.AccessKeySecret, AliyunOSSTempToken.SecurityToken);
|
||||||
|
|
||||||
|
// 生成签名URL。
|
||||||
|
var req = new GeneratePresignedUriRequest(aliConfig.BucketName, ossRelativePath, SignHttpMethod.Get)
|
||||||
|
{
|
||||||
|
// 设置签名URL过期时间,默认值为3600秒。
|
||||||
|
Expiration = DateTime.Now.AddHours(1),
|
||||||
|
};
|
||||||
|
var uri = _ossClient.GeneratePresignedUri(req);
|
||||||
|
|
||||||
|
return uri.PathAndQuery;
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (ObjectStoreServiceOptions.ObjectStoreUse == "MinIO")
|
||||||
|
{
|
||||||
|
var minIOConfig = ObjectStoreServiceOptions.MinIO;
|
||||||
|
|
||||||
|
var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.EndPoint}:{minIOConfig.Port}")
|
||||||
|
.WithCredentials(minIOConfig.AccessKeyId, minIOConfig.SecretAccessKey).WithSSL(minIOConfig.UseSSL)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
|
||||||
|
var args = new PresignedGetObjectArgs()
|
||||||
|
.WithBucket(minIOConfig.BucketName)
|
||||||
|
.WithObject(ossRelativePath)
|
||||||
|
.WithExpiry(3600)
|
||||||
|
/*.WithHeaders(reqParams)*/;
|
||||||
|
|
||||||
|
var presignedUrl = await minioClient.PresignedGetObjectAsync(args);
|
||||||
|
|
||||||
|
Uri uri = new Uri(presignedUrl);
|
||||||
|
|
||||||
|
string relativePath = uri.PathAndQuery;
|
||||||
|
|
||||||
|
|
||||||
|
return relativePath;
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS")
|
||||||
|
{
|
||||||
|
var awsConfig = ObjectStoreServiceOptions.AWS;
|
||||||
|
|
||||||
|
|
||||||
|
// 提供awsAccessKeyId和awsSecretAccessKey构造凭证
|
||||||
|
var credentials = new SessionAWSCredentials(AWSTempToken.AccessKeyId, AWSTempToken.SecretAccessKey, AWSTempToken.SessionToken);
|
||||||
|
|
||||||
|
//提供awsEndPoint(域名)进行访问配置
|
||||||
|
var clientConfig = new AmazonS3Config
|
||||||
|
{
|
||||||
|
RegionEndpoint = RegionEndpoint.USEast1,
|
||||||
|
UseHttp = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
|
||||||
|
|
||||||
|
var presignedUrl = await amazonS3Client.GetPreSignedURLAsync(new GetPreSignedUrlRequest()
|
||||||
|
{
|
||||||
|
BucketName = awsConfig.BucketName,
|
||||||
|
Key = ossRelativePath,
|
||||||
|
Expires = DateTime.UtcNow.AddMinutes(120)
|
||||||
|
});
|
||||||
|
|
||||||
|
Uri uri = new Uri(presignedUrl);
|
||||||
|
|
||||||
|
string relativePath = uri.PathAndQuery;
|
||||||
|
|
||||||
|
|
||||||
|
return relativePath;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new BusinessValidationFailedException("未定义的存储介质类型");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
|
||||||
|
throw new BusinessValidationFailedException("oss授权url失败!" + ex.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 删除某个目录的文件
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="prefix"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task DeleteFromPrefix(string prefix)
|
||||||
|
{
|
||||||
|
GetObjectStoreTempToken();
|
||||||
|
|
||||||
|
if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS")
|
||||||
|
{
|
||||||
|
var aliConfig = ObjectStoreServiceOptions.AliyunOSS;
|
||||||
|
|
||||||
|
var _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.EndPoint : aliConfig.InternalEndpoint, AliyunOSSTempToken.AccessKeyId, AliyunOSSTempToken.AccessKeySecret, AliyunOSSTempToken.SecurityToken);
|
||||||
|
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ObjectListing objectListing = null;
|
||||||
|
string nextMarker = null;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
// 使用 prefix 模拟目录结构,设置 MaxKeys 和 NextMarker
|
||||||
|
objectListing = _ossClient.ListObjects(new Aliyun.OSS.ListObjectsRequest(aliConfig.BucketName)
|
||||||
|
{
|
||||||
|
Prefix = prefix,
|
||||||
|
MaxKeys = 1000,
|
||||||
|
Marker = nextMarker
|
||||||
|
});
|
||||||
|
|
||||||
|
List<string> keys = objectListing.ObjectSummaries.Select(t => t.Key).ToList();
|
||||||
|
|
||||||
|
// 删除获取到的文件
|
||||||
|
if (keys.Count > 0)
|
||||||
|
{
|
||||||
|
_ossClient.DeleteObjects(new Aliyun.OSS.DeleteObjectsRequest(aliConfig.BucketName, keys, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置 NextMarker 以获取下一页的数据
|
||||||
|
nextMarker = objectListing.NextMarker;
|
||||||
|
|
||||||
|
} while (objectListing.IsTruncated);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Error: {ex.Message}");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (ObjectStoreServiceOptions.ObjectStoreUse == "MinIO")
|
||||||
|
{
|
||||||
|
var minIOConfig = ObjectStoreServiceOptions.MinIO;
|
||||||
|
|
||||||
|
|
||||||
|
var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.EndPoint}:{minIOConfig.Port}")
|
||||||
|
.WithCredentials(minIOConfig.AccessKeyId, minIOConfig.SecretAccessKey).WithSSL(minIOConfig.UseSSL)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
|
||||||
|
var listArgs = new ListObjectsArgs().WithBucket(minIOConfig.BucketName).WithPrefix(prefix).WithRecursive(true);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 创建一个空列表用于存储对象键
|
||||||
|
var objects = new List<string>();
|
||||||
|
|
||||||
|
// 使用 await foreach 来异步迭代对象列表
|
||||||
|
await foreach (var item in minioClient.ListObjectsEnumAsync(listArgs))
|
||||||
|
{
|
||||||
|
objects.Add(item.Key);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (objects.Count > 0)
|
||||||
|
{
|
||||||
|
var objArgs = new RemoveObjectsArgs()
|
||||||
|
.WithBucket(minIOConfig.BucketName)
|
||||||
|
.WithObjects(objects);
|
||||||
|
|
||||||
|
// 删除对象
|
||||||
|
await minioClient.RemoveObjectsAsync(objArgs);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS")
|
||||||
|
{
|
||||||
|
|
||||||
|
var awsConfig = ObjectStoreServiceOptions.AWS;
|
||||||
|
|
||||||
|
|
||||||
|
// 提供awsAccessKeyId和awsSecretAccessKey构造凭证
|
||||||
|
var credentials = new SessionAWSCredentials(AWSTempToken.AccessKeyId, AWSTempToken.SecretAccessKey, AWSTempToken.SessionToken);
|
||||||
|
|
||||||
|
//提供awsEndPoint(域名)进行访问配置
|
||||||
|
var clientConfig = new AmazonS3Config
|
||||||
|
{
|
||||||
|
RegionEndpoint = RegionEndpoint.USEast1,
|
||||||
|
UseHttp = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
|
||||||
|
|
||||||
|
// 列出指定前缀下的所有对象
|
||||||
|
var listObjectsRequest = new ListObjectsV2Request
|
||||||
|
{
|
||||||
|
BucketName = awsConfig.BucketName,
|
||||||
|
Prefix = prefix
|
||||||
|
};
|
||||||
|
|
||||||
|
var listObjectsResponse = await amazonS3Client.ListObjectsV2Async(listObjectsRequest);
|
||||||
|
|
||||||
|
if (listObjectsResponse.S3Objects.Count > 0)
|
||||||
|
{
|
||||||
|
// 准备删除请求
|
||||||
|
var deleteObjectsRequest = new Amazon.S3.Model.DeleteObjectsRequest
|
||||||
|
{
|
||||||
|
BucketName = awsConfig.BucketName,
|
||||||
|
Objects = new List<KeyVersion>()
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (var s3Object in listObjectsResponse.S3Objects)
|
||||||
|
{
|
||||||
|
deleteObjectsRequest.Objects.Add(new KeyVersion
|
||||||
|
{
|
||||||
|
Key = s3Object.Key
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量删除对象
|
||||||
|
var deleteObjectsResponse = await amazonS3Client.DeleteObjectsAsync(deleteObjectsRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new BusinessValidationFailedException("未定义的存储介质类型");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public ObjectStoreDTO GetObjectStoreTempToken()
|
||||||
|
{
|
||||||
|
|
||||||
|
var ossOptions = ObjectStoreServiceOptions.AliyunOSS;
|
||||||
|
|
||||||
|
if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS")
|
||||||
|
{
|
||||||
|
var client = new Client(new AlibabaCloud.OpenApiClient.Models.Config()
|
||||||
|
{
|
||||||
|
AccessKeyId = ossOptions.AccessKeyId,
|
||||||
|
AccessKeySecret = ossOptions.AccessKeySecret,
|
||||||
|
//AccessKeyId = "LTAI5tJV76pYX5yPg1N9QVE8",
|
||||||
|
//AccessKeySecret = "roRNLa9YG1of4pYruJGCNKBXEWTAWa",
|
||||||
|
|
||||||
|
Endpoint = "sts.cn-hangzhou.aliyuncs.com"
|
||||||
|
});
|
||||||
|
|
||||||
|
var assumeRoleRequest = new AlibabaCloud.SDK.Sts20150401.Models.AssumeRoleRequest();
|
||||||
|
// 将<YOUR_ROLE_SESSION_NAME>设置为自定义的会话名称,例如oss-role-session。
|
||||||
|
assumeRoleRequest.RoleSessionName = $"session-name-{NewId.NextGuid()}";
|
||||||
|
// 将<YOUR_ROLE_ARN>替换为拥有上传文件到指定OSS Bucket权限的RAM角色的ARN。
|
||||||
|
assumeRoleRequest.RoleArn = ossOptions.RoleArn;
|
||||||
|
//assumeRoleRequest.RoleArn = "acs:ram::1899121822495495:role/webdirect";
|
||||||
|
assumeRoleRequest.DurationSeconds = ossOptions.DurationSeconds;
|
||||||
|
var runtime = new AlibabaCloud.TeaUtil.Models.RuntimeOptions();
|
||||||
|
var response = client.AssumeRoleWithOptions(assumeRoleRequest, runtime);
|
||||||
|
var credentials = response.Body.Credentials;
|
||||||
|
|
||||||
|
var tempToken = new AliyunOSSTempToken()
|
||||||
|
{
|
||||||
|
AccessKeyId = credentials.AccessKeyId,
|
||||||
|
AccessKeySecret = credentials.AccessKeySecret,
|
||||||
|
|
||||||
|
//转为服务器时区,最后统一转为客户端时区
|
||||||
|
Expiration = TimeZoneInfo.ConvertTimeFromUtc(DateTime.Parse(credentials.Expiration), TimeZoneInfo.Local),
|
||||||
|
SecurityToken = credentials.SecurityToken,
|
||||||
|
|
||||||
|
|
||||||
|
Region = ossOptions.Region,
|
||||||
|
BucketName = ossOptions.BucketName,
|
||||||
|
EndPoint = ossOptions.EndPoint,
|
||||||
|
ViewEndpoint = ossOptions.ViewEndpoint,
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
AliyunOSSTempToken = tempToken;
|
||||||
|
|
||||||
|
return new ObjectStoreDTO() { ObjectStoreUse = ObjectStoreServiceOptions.ObjectStoreUse, AliyunOSS = tempToken };
|
||||||
|
}
|
||||||
|
else if (ObjectStoreServiceOptions.ObjectStoreUse == "MinIO")
|
||||||
|
{
|
||||||
|
return new ObjectStoreDTO() { ObjectStoreUse = ObjectStoreServiceOptions.ObjectStoreUse, MinIO = ObjectStoreServiceOptions.MinIO };
|
||||||
|
}
|
||||||
|
else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS")
|
||||||
|
{
|
||||||
|
var awsOptions = ObjectStoreServiceOptions.AWS;
|
||||||
|
|
||||||
|
//aws 临时凭证
|
||||||
|
// 创建 STS 客户端
|
||||||
|
var stsClient = new AmazonSecurityTokenServiceClient(awsOptions.AccessKeyId, awsOptions.SecretAccessKey);
|
||||||
|
|
||||||
|
// 使用 AssumeRole 请求临时凭证
|
||||||
|
var assumeRoleRequest = new AssumeRoleRequest
|
||||||
|
{
|
||||||
|
|
||||||
|
RoleArn = awsOptions.RoleArn, // 角色 ARN
|
||||||
|
RoleSessionName = $"session-name-{NewId.NextGuid()}",
|
||||||
|
DurationSeconds = awsOptions.DurationSeconds // 临时凭证有效期
|
||||||
|
};
|
||||||
|
|
||||||
|
var assumeRoleResponse = stsClient.AssumeRoleAsync(assumeRoleRequest).Result;
|
||||||
|
|
||||||
|
var credentials = assumeRoleResponse.Credentials;
|
||||||
|
|
||||||
|
var tempToken = new AWSTempToken()
|
||||||
|
{
|
||||||
|
AccessKeyId = credentials.AccessKeyId,
|
||||||
|
SecretAccessKey = credentials.SecretAccessKey,
|
||||||
|
SessionToken = credentials.SessionToken,
|
||||||
|
Expiration = credentials.Expiration,
|
||||||
|
Region = awsOptions.Region,
|
||||||
|
BucketName = awsOptions.BucketName,
|
||||||
|
EndPoint = awsOptions.EndPoint,
|
||||||
|
ViewEndpoint = awsOptions.ViewEndpoint,
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
AWSTempToken = tempToken;
|
||||||
|
return new ObjectStoreDTO() { ObjectStoreUse = ObjectStoreServiceOptions.ObjectStoreUse, AWS = tempToken };
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new BusinessValidationFailedException("未定义的存储介质类型");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft": "Warning",
|
||||||
|
"Microsoft.Hosting.Lifetime": "Information"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ObjectStoreService": {
|
||||||
|
"ObjectStoreUse": "AliyunOSS",
|
||||||
|
"AliyunOSS": {
|
||||||
|
"RegionId": "cn-shanghai",
|
||||||
|
"InternalEndpoint": "https://oss-cn-shanghai-internal.aliyuncs.com",
|
||||||
|
"EndPoint": "https://oss-cn-shanghai.aliyuncs.com",
|
||||||
|
"AccessKeyId": "LTAI5tNRTsqL6aWmHkDmTwoH",
|
||||||
|
"AccessKeySecret": "7mtGz3qrYWI6JMMBZiLeC119VWicZH",
|
||||||
|
"RoleArn": "acs:ram::1899121822495495:role/irc-oss-access",
|
||||||
|
"BucketName": "zy-irc-store",
|
||||||
|
"ViewEndpoint": "https://zy-irc-cache.oss-cn-shanghai.aliyuncs.com",
|
||||||
|
"Region": "oss-cn-shanghai",
|
||||||
|
"DurationSeconds": 7200
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"ConnectionStrings": {
|
||||||
|
"RemoteNew": "Server=prod_mssql_standard,1433;Database=Prod_IRC;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true",
|
||||||
|
"Hangfire": "Server=prod_mssql_standard,1433;Database=Prod_IRC_Hangfire;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true"
|
||||||
|
},
|
||||||
|
|
||||||
|
"DicomSCPServiceConfig": {
|
||||||
|
"CalledAEList": [
|
||||||
|
"STORESCP"
|
||||||
|
],
|
||||||
|
"ServerPort": 11112
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft": "Warning",
|
||||||
|
"Microsoft.Hosting.Lifetime": "Information"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ObjectStoreService": {
|
||||||
|
"ObjectStoreUse": "AliyunOSS",
|
||||||
|
"AliyunOSS": {
|
||||||
|
"RegionId": "cn-shanghai",
|
||||||
|
"InternalEndpoint": "https://oss-cn-shanghai-internal.aliyuncs.com",
|
||||||
|
"EndPoint": "https://oss-cn-shanghai.aliyuncs.com",
|
||||||
|
"AccessKeyId": "LTAI5tRRZehUp2V9pyTPtAJm",
|
||||||
|
"AccessKeySecret": "FLizxkHsMm4CGYHtkV8E3PNJJZU7oV",
|
||||||
|
"RoleArn": "acs:ram::1899121822495495:role/dev-oss-access",
|
||||||
|
"BucketName": "zy-irc-test-store",
|
||||||
|
"ViewEndpoint": "https://zy-irc-test-store.oss-cn-shanghai.aliyuncs.com",
|
||||||
|
"Region": "oss-cn-shanghai",
|
||||||
|
"DurationSeconds": 7200
|
||||||
|
},
|
||||||
|
|
||||||
|
"MinIO": {
|
||||||
|
"endPoint": "106.14.89.110",
|
||||||
|
"port": "9001",
|
||||||
|
"useSSL": false,
|
||||||
|
"accessKey": "fbStsVYCIPKHQneeqMwD",
|
||||||
|
"secretKey": "TzgvyA3zGXMUnpilJNUlyMYHfosl1hBMl6lxPmjy",
|
||||||
|
"bucketName": "hir-test",
|
||||||
|
"viewEndpoint": "http://106.14.89.110:9001/hir-test/"
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
"ConnectionStrings": {
|
||||||
|
"RemoteNew": "Server=106.14.89.110,1435;Database=Test_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
||||||
|
"Hangfire": "Server=106.14.89.110,1435;Database=Test_IRC_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
|
||||||
|
},
|
||||||
|
|
||||||
|
"DicomSCPServiceConfig": {
|
||||||
|
"CalledAEList": [
|
||||||
|
"STORESCP"
|
||||||
|
],
|
||||||
|
"ServerPort": 11112
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft": "Warning",
|
||||||
|
"Microsoft.Hosting.Lifetime": "Information"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ObjectStoreService": {
|
||||||
|
"ObjectStoreUse": "AWS",
|
||||||
|
"AWS": {
|
||||||
|
"Region": "us-east-1",
|
||||||
|
"EndPoint": "s3.us-east-1.amazonaws.com",
|
||||||
|
"UseSSL": true,
|
||||||
|
"RoleArn": "arn:aws:iam::471112624751:role/lili_s3_access",
|
||||||
|
"AccessKeyId": "AKIAW3MEAFJXZ2TZK7GM",
|
||||||
|
"SecretAccessKey": "9MLQCQ1HifEVW1gf068zBRAOb4wNnfrOkvBVByth",
|
||||||
|
"BucketName": "ei-med-s3-lili-store",
|
||||||
|
"ViewEndpoint": "https://ei-med-s3-lili-store.s3.amazonaws.com",
|
||||||
|
"DurationSeconds": 7200
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ConnectionStrings": {
|
||||||
|
"RemoteNew": "Server=us-prod-mssql-service,1433;Database=US_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
||||||
|
"Hangfire": "Server=us-prod-mssql-service,1433;Database=US_IRC_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
|
||||||
|
},
|
||||||
|
"DicomSCPServiceConfig": {
|
||||||
|
"CalledAEList": [
|
||||||
|
"STORESCP"
|
||||||
|
],
|
||||||
|
"ServerPort": 11112
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft": "Warning",
|
||||||
|
"Microsoft.Hosting.Lifetime": "Information"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ObjectStoreService": {
|
||||||
|
"ObjectStoreUse": "AWS",
|
||||||
|
"AWS": {
|
||||||
|
"Region": "us-east-1",
|
||||||
|
"EndPoint": "s3.us-east-1.amazonaws.com",
|
||||||
|
"UseSSL": true,
|
||||||
|
"RoleArn": "arn:aws:iam::471112624751:role/uat_s3_access",
|
||||||
|
"AccessKeyId": "AKIAW3MEAFJX7IPXISP4",
|
||||||
|
"SecretAccessKey": "Pgrg3le5jPxZQ7MR1yYNS30J0XRyJeKVyIIjElXc",
|
||||||
|
"BucketName": "ei-med-s3-lili-uat-store",
|
||||||
|
"ViewEndpoint": "https://ei-med-s3-lili-uat-store.s3.amazonaws.com/",
|
||||||
|
"DurationSeconds": 7200
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ConnectionStrings": {
|
||||||
|
"RemoteNew": "Server=us-mssql-service,1433;Database=US_Uat_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
||||||
|
"Hangfire": "Server=us-mssql-service,1433;Database=US_Uat_IRC_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
|
||||||
|
},
|
||||||
|
"DicomSCPServiceConfig": {
|
||||||
|
"CalledAEList": [
|
||||||
|
"STORESCP"
|
||||||
|
],
|
||||||
|
"ServerPort": 11112
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft": "Warning",
|
||||||
|
"Microsoft.Hosting.Lifetime": "Information"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ObjectStoreService": {
|
||||||
|
"ObjectStoreUse": "AliyunOSS",
|
||||||
|
"AliyunOSS": {
|
||||||
|
"RegionId": "cn-shanghai",
|
||||||
|
"InternalEndpoint": "https://oss-cn-shanghai-internal.aliyuncs.com",
|
||||||
|
"EndPoint": "https://oss-cn-shanghai.aliyuncs.com",
|
||||||
|
"AccessKeyId": "LTAI5tRRZehUp2V9pyTPtAJm",
|
||||||
|
"AccessKeySecret": "FLizxkHsMm4CGYHtkV8E3PNJJZU7oV",
|
||||||
|
"RoleArn": "acs:ram::1899121822495495:role/dev-oss-access",
|
||||||
|
"BucketName": "zy-irc-uat-store",
|
||||||
|
"ViewEndpoint": "https://zy-irc-uat-store.oss-cn-shanghai.aliyuncs.com",
|
||||||
|
"Region": "oss-cn-shanghai",
|
||||||
|
"DurationSeconds": 7200
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ConnectionStrings": {
|
||||||
|
"RemoteNew": "Server=47.117.164.182,1434;Database=Uat_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
||||||
|
"Hangfire": "Server=47.117.164.182,1434;Database=Uat_IRC.Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
|
||||||
|
},
|
||||||
|
"DicomSCPServiceConfig": {
|
||||||
|
"CalledAEList": [
|
||||||
|
"STORESCP"
|
||||||
|
],
|
||||||
|
"ServerPort": 11112
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft.AspNetCore": "Warning"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"AllowedHosts": "*"
|
||||||
|
}
|
||||||
@@ -17,6 +17,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IRaCIS.Core.Infra.EFCore",
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IRaCIS.Core.Infrastructure", "IRaCIS.Core.Infrastructure\IRaCIS.Core.Infrastructure.csproj", "{07EED0F8-08E6-46F3-ACBE-17BC1391BD4C}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IRaCIS.Core.Infrastructure", "IRaCIS.Core.Infrastructure\IRaCIS.Core.Infrastructure.csproj", "{07EED0F8-08E6-46F3-ACBE-17BC1391BD4C}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IRC.Core.SCP", "IRC.Core.SCP\IRC.Core.SCP.csproj", "{ECD08F47-DC1A-484E-BB91-6CDDC8823CC5}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@@ -51,6 +53,10 @@ Global
|
|||||||
{07EED0F8-08E6-46F3-ACBE-17BC1391BD4C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{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.ActiveCfg = Release|Any CPU
|
||||||
{07EED0F8-08E6-46F3-ACBE-17BC1391BD4C}.Release|Any CPU.Build.0 = 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
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
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; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using EasyCaching.Core;
|
|
||||||
using IRaCIS.Application.Interfaces;
|
using IRaCIS.Application.Interfaces;
|
||||||
using IRaCIS.Application.Contracts;
|
using IRaCIS.Application.Contracts;
|
||||||
using IRaCIS.Core.Application.Auth;
|
using IRaCIS.Core.Application.Auth;
|
||||||
@@ -15,7 +14,7 @@ using System.Text;
|
|||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using IRaCIS.Core.Application.Interfaces;
|
using IRaCIS.Core.Application.Interfaces;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using IRaCIS.Application.Services;
|
using IRaCIS.Core.Application.Service;
|
||||||
using IRaCIS.Core.Infra.EFCore;
|
using IRaCIS.Core.Infra.EFCore;
|
||||||
using IRaCIS.Core.Domain.Models;
|
using IRaCIS.Core.Domain.Models;
|
||||||
using IRaCIS.Core.Infrastructure;
|
using IRaCIS.Core.Infrastructure;
|
||||||
@@ -23,13 +22,22 @@ using System.Linq;
|
|||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using MassTransit;
|
using MassTransit;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Aliyun.Acs.Core.Profile;
|
|
||||||
using Aliyun.Acs.Core.Auth.Sts;
|
|
||||||
using Aliyun.Acs.Core;
|
|
||||||
using IRaCIS.Core.Application.Helper;
|
using IRaCIS.Core.Application.Helper;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using IRaCIS.Core.Application.Contracts;
|
using IRaCIS.Core.Application.Contracts;
|
||||||
using LoginReturnDTO = IRaCIS.Application.Contracts.LoginReturnDTO;
|
using LoginReturnDTO = IRaCIS.Application.Contracts.LoginReturnDTO;
|
||||||
|
using DocumentFormat.OpenXml.Spreadsheet;
|
||||||
|
using AutoMapper.QueryableExtensions;
|
||||||
|
using NetTopologySuite.Algorithm;
|
||||||
|
using ZiggyCreatures.Caching.Fusion;
|
||||||
|
using AlibabaCloud.SDK.Sts20150401;
|
||||||
|
using AlibabaCloud.SDK.Sts20150401.Models;
|
||||||
|
using Org.BouncyCastle.Tls;
|
||||||
|
using Amazon.SecurityToken.Model;
|
||||||
|
using Amazon.SecurityToken;
|
||||||
|
using Amazon;
|
||||||
|
using AssumeRoleRequest = Amazon.SecurityToken.Model.AssumeRoleRequest;
|
||||||
|
using AutoMapper;
|
||||||
|
|
||||||
namespace IRaCIS.Api.Controllers
|
namespace IRaCIS.Api.Controllers
|
||||||
{
|
{
|
||||||
@@ -53,8 +61,8 @@ namespace IRaCIS.Api.Controllers
|
|||||||
/// <param name="doctorId"></param>
|
/// <param name="doctorId"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpGet, Route("doctor/getDetail/{doctorId:guid}")]
|
[HttpGet, Route("doctor/getDetail/{doctorId:guid}")]
|
||||||
|
|
||||||
public async Task<IResponseOutput<DoctorDetailDTO>> GetDoctorDetail([FromServices] IAttachmentService attachmentService, [FromServices] IDoctorService _doctorService,
|
public async Task<IResponseOutput<DoctorDetailDTO>> GetDoctorDetail([FromServices] IAttachmentService attachmentService, [FromServices] IDoctorService _doctorService,
|
||||||
[FromServices] IEducationService _educationService, [FromServices] ITrialExperienceService _trialExperienceService,
|
[FromServices] IEducationService _educationService, [FromServices] ITrialExperienceService _trialExperienceService,
|
||||||
|
|
||||||
[FromServices] IResearchPublicationService _researchPublicationService, [FromServices] IVacationService _vacationService, Guid doctorId)
|
[FromServices] IResearchPublicationService _researchPublicationService, [FromServices] IVacationService _vacationService, Guid doctorId)
|
||||||
@@ -66,7 +74,7 @@ namespace IRaCIS.Api.Controllers
|
|||||||
|
|
||||||
var doctorDetail = new DoctorDetailDTO
|
var doctorDetail = new DoctorDetailDTO
|
||||||
{
|
{
|
||||||
AuditView =await _doctorService.GetAuditState(doctorId),
|
AuditView = await _doctorService.GetAuditState(doctorId),
|
||||||
BasicInfoView = await _doctorService.GetBasicInfo(doctorId),
|
BasicInfoView = await _doctorService.GetBasicInfo(doctorId),
|
||||||
EmploymentView = await _doctorService.GetEmploymentInfo(doctorId),
|
EmploymentView = await _doctorService.GetEmploymentInfo(doctorId),
|
||||||
AttachmentList = await attachmentService.GetAttachments(doctorId),
|
AttachmentList = await attachmentService.GetAttachments(doctorId),
|
||||||
@@ -77,7 +85,7 @@ namespace IRaCIS.Api.Controllers
|
|||||||
TrialExperienceView = await _trialExperienceService.GetTrialExperience(doctorId),
|
TrialExperienceView = await _trialExperienceService.GetTrialExperience(doctorId),
|
||||||
ResearchPublicationView = await _researchPublicationService.GetResearchPublication(doctorId),
|
ResearchPublicationView = await _researchPublicationService.GetResearchPublication(doctorId),
|
||||||
|
|
||||||
SpecialtyView =await _doctorService.GetSpecialtyInfo(doctorId),
|
SpecialtyView = await _doctorService.GetSpecialtyInfo(doctorId),
|
||||||
InHoliday = (await _vacationService.OnVacation(doctorId)).IsSuccess,
|
InHoliday = (await _vacationService.OnVacation(doctorId)).IsSuccess,
|
||||||
IntoGroupInfo = _doctorService.GetDoctorIntoGroupInfo(doctorId),
|
IntoGroupInfo = _doctorService.GetDoctorIntoGroupInfo(doctorId),
|
||||||
SowList = sowList,
|
SowList = sowList,
|
||||||
@@ -96,80 +104,35 @@ namespace IRaCIS.Api.Controllers
|
|||||||
/// <summary> 系统用户登录接口[New] </summary>
|
/// <summary> 系统用户登录接口[New] </summary>
|
||||||
[HttpPost, Route("user/login")]
|
[HttpPost, Route("user/login")]
|
||||||
[AllowAnonymous]
|
[AllowAnonymous]
|
||||||
public async Task<IResponseOutput<LoginReturnDTO>> Login(UserLoginDTO loginUser, [FromServices] IEasyCachingProvider provider, [FromServices] IUserService _userService,
|
public async Task<IResponseOutput> Login(UserLoginDTO loginUser,
|
||||||
|
[FromServices] IFusionCache _fusionCache,
|
||||||
|
[FromServices] IUserService _userService,
|
||||||
[FromServices] ITokenService _tokenService,
|
[FromServices] ITokenService _tokenService,
|
||||||
|
[FromServices] IReadingImageTaskService readingImageTaskService,
|
||||||
|
[FromServices] IOptionsMonitor<ServiceVerifyConfigOption> _verifyConfig,
|
||||||
|
[FromServices] IOptionsMonitor<SystemEmailSendConfig> _emailConfig,
|
||||||
|
|
||||||
[FromServices] IReadingImageTaskService readingImageTaskService,
|
[FromServices] IMailVerificationService _mailVerificationService)
|
||||||
[FromServices] IConfiguration configuration)
|
|
||||||
{
|
{
|
||||||
|
var emailConfig = _emailConfig.CurrentValue;
|
||||||
|
var companyInfo = new SystemEmailSendConfigView() { CompanyName = emailConfig.CompanyName, CompanyNameCN = emailConfig.CompanyNameCN, CompanyShortName = emailConfig.CompanyShortName, CompanyShortNameCN = emailConfig.CompanyShortNameCN };
|
||||||
|
|
||||||
|
//MFA 邮箱验证 前端传递用户Id 和MFACode
|
||||||
var returnModel = await _userService.Login(loginUser.UserName, loginUser.Password);
|
if (loginUser.UserId != null && _verifyConfig.CurrentValue.OpenLoginMFA)
|
||||||
|
|
||||||
if (returnModel.IsSuccess)
|
|
||||||
{
|
{
|
||||||
#region GRPC 调用鉴权中心,因为服务器IIS问题 http/2 故而没法使用
|
Guid userId = (Guid)loginUser.UserId;
|
||||||
|
|
||||||
////重试策略
|
//验证MFA 编码是否有问题 ,前端要拆开,自己调用验证的逻辑
|
||||||
//var defaultMethodConfig = new MethodConfig
|
//await _userService.VerifyMFACodeAsync(userId, loginUser.MFACode);
|
||||||
//{
|
|
||||||
// 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 loginUser = await _userRepository.Where(u => u.UserName.Equals(userName) && u.Password == password).ProjectTo<UserBasicInfo>(_mapper.ConfigurationProvider).FirstOrDefaultAsync();
|
||||||
|
|
||||||
//var httpHandler = new HttpClientHandler();
|
var basicInfo = await _userService.GetUserBasicInfo(userId, loginUser.Password);
|
||||||
//// Return `true` to allow certificates that are untrusted/invalid
|
|
||||||
//httpHandler.ServerCertificateCustomValidationCallback =
|
|
||||||
// HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
|
|
||||||
|
|
||||||
|
var loginReturn = new LoginReturnDTO() { BasicInfo = basicInfo };
|
||||||
|
|
||||||
//////这一句是让grpc支持本地 http 如果本地访问部署在服务器上,那么是访问不成功的
|
loginReturn.JWTStr = _tokenService.GetToken(IRaCISClaims.Create(loginReturn.BasicInfo));
|
||||||
//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 的属性
|
// 创建一个 CookieOptions 对象,用于设置 Cookie 的属性
|
||||||
var option = new CookieOptions
|
var option = new CookieOptions
|
||||||
@@ -180,18 +143,162 @@ namespace IRaCIS.Api.Controllers
|
|||||||
Secure = false // 确保 cookie 只能通过 HTTPS 访问
|
Secure = false // 确保 cookie 只能通过 HTTPS 访问
|
||||||
};
|
};
|
||||||
|
|
||||||
HttpContext.Response.Cookies.Append("access_token", returnModel.Data.JWTStr, option);
|
HttpContext.Response.Cookies.Append("access_token", loginReturn.JWTStr, option);
|
||||||
|
|
||||||
|
// 验证阅片休息时间
|
||||||
|
await readingImageTaskService.ResetReadingRestTime(userId);
|
||||||
|
|
||||||
|
await _fusionCache.SetAsync(CacheKeys.UserToken(userId), loginReturn.JWTStr, TimeSpan.FromDays(7));
|
||||||
|
|
||||||
|
await _fusionCache.SetAsync(CacheKeys.UserAutoLoginOut(userId), DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), TimeSpan.FromMinutes(_verifyConfig.CurrentValue.AutoLoginOutMinutes));
|
||||||
|
|
||||||
|
loginReturn.CompanyInfo = companyInfo;
|
||||||
|
return ResponseOutput.Ok(loginReturn);
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var returnModel = await _userService.Login(loginUser.UserName, loginUser.Password);
|
||||||
|
|
||||||
|
if (returnModel.IsSuccess)
|
||||||
|
{
|
||||||
|
#region GRPC 调用鉴权中心,因为服务器IIS问题 http/2 故而没法使用
|
||||||
|
|
||||||
|
////重试策略
|
||||||
|
//var defaultMethodConfig = new MethodConfig
|
||||||
|
//{
|
||||||
|
// Names = { MethodName.Default },
|
||||||
|
// RetryPolicy = new RetryPolicy
|
||||||
|
// {
|
||||||
|
// MaxAttempts = 3,
|
||||||
|
// InitialBackoff = TimeSpan.FromSeconds(1),
|
||||||
|
// MaxBackoff = TimeSpan.FromSeconds(5),
|
||||||
|
// BackoffMultiplier = 1.5,
|
||||||
|
// RetryableStatusCodes = { Grpc.Core.StatusCode.Unavailable }
|
||||||
|
// }
|
||||||
|
//};
|
||||||
|
|
||||||
|
//#region unable to trust the certificate then the gRPC client can be configured to ignore the invalid certificate
|
||||||
|
|
||||||
|
//var httpHandler = new HttpClientHandler();
|
||||||
|
//// Return `true` to allow certificates that are untrusted/invalid
|
||||||
|
//httpHandler.ServerCertificateCustomValidationCallback =
|
||||||
|
// HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
|
||||||
|
|
||||||
|
|
||||||
|
//////这一句是让grpc支持本地 http 如果本地访问部署在服务器上,那么是访问不成功的
|
||||||
|
//AppContext.SetSwitch(
|
||||||
|
// "System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
|
||||||
|
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//var grpcAdress = configuration.GetValue<string>("GrpcAddress");
|
||||||
|
////var grpcAdress = "http://localhost:7200";
|
||||||
|
|
||||||
|
//var channel = GrpcChannel.ForAddress(grpcAdress, new GrpcChannelOptions
|
||||||
|
//{
|
||||||
|
// HttpHandler = httpHandler,
|
||||||
|
// ServiceConfig = new ServiceConfig { MethodConfigs = { defaultMethodConfig } }
|
||||||
|
|
||||||
|
//});
|
||||||
|
////var channel = GrpcChannel.ForAddress(grpcAdress);
|
||||||
|
//var grpcClient = new TokenGrpcService.TokenGrpcServiceClient(channel);
|
||||||
|
|
||||||
|
//var userInfo = returnModel.Data.BasicInfo;
|
||||||
|
|
||||||
|
//var tokenResponse = grpcClient.GetUserToken(new GetTokenReuqest()
|
||||||
|
//{
|
||||||
|
// Id = userInfo.Id.ToString(),
|
||||||
|
// ReviewerCode = userInfo.ReviewerCode,
|
||||||
|
// IsAdmin = userInfo.IsAdmin,
|
||||||
|
// RealName = userInfo.RealName,
|
||||||
|
// UserTypeEnumInt = (int)userInfo.UserTypeEnum,
|
||||||
|
// UserTypeShortName = userInfo.UserTypeShortName,
|
||||||
|
// UserName = userInfo.UserName
|
||||||
|
//});
|
||||||
|
|
||||||
|
//returnModel.Data.JWTStr = tokenResponse.Token;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
var userId = returnModel.Data.BasicInfo.Id;
|
||||||
|
|
||||||
|
if (_verifyConfig.CurrentValue.OpenLoginMFA)
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//MFA 发送邮件
|
||||||
|
|
||||||
|
returnModel.Data.IsMFA = true;
|
||||||
|
|
||||||
|
var email = returnModel.Data.BasicInfo.EMail;
|
||||||
|
|
||||||
|
var hiddenEmail = IRCEmailPasswordHelper.MaskEmail(email);
|
||||||
|
|
||||||
|
returnModel.Data.BasicInfo.EMail = hiddenEmail;
|
||||||
|
|
||||||
|
//修改密码
|
||||||
|
if (returnModel.Data.BasicInfo.IsFirstAdd || returnModel.Data.BasicInfo.LoginState == 1)
|
||||||
|
{
|
||||||
|
returnModel.Data.JWTStr = _tokenService.GetToken(IRaCISClaims.Create(returnModel.Data.BasicInfo));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//正常登录才发送邮件
|
||||||
|
await _userService.SendMFAEmail(userId);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
returnModel.Data.JWTStr = _tokenService.GetToken(IRaCISClaims.Create(returnModel.Data.BasicInfo));
|
||||||
|
|
||||||
|
// 创建一个 CookieOptions 对象,用于设置 Cookie 的属性
|
||||||
|
var option = new CookieOptions
|
||||||
|
{
|
||||||
|
Expires = DateTime.Now.AddMonths(1), // 设置过期时间为 30 分钟之后
|
||||||
|
HttpOnly = false, // 确保 cookie 只能通过 HTTP 访问
|
||||||
|
SameSite = Microsoft.AspNetCore.Http.SameSiteMode.None, // 设置 SameSite 属性
|
||||||
|
Secure = false // 确保 cookie 只能通过 HTTPS 访问
|
||||||
|
};
|
||||||
|
|
||||||
|
HttpContext.Response.Cookies.Append("access_token", returnModel.Data.JWTStr, option);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 验证阅片休息时间
|
||||||
|
await readingImageTaskService.ResetReadingRestTime(returnModel.Data.BasicInfo.Id);
|
||||||
|
|
||||||
|
await _fusionCache.SetAsync(CacheKeys.UserToken(userId), returnModel.Data.JWTStr, TimeSpan.FromDays(7));
|
||||||
|
|
||||||
|
await _fusionCache.SetAsync(CacheKeys.UserAutoLoginOut(userId), DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), TimeSpan.FromMinutes(_verifyConfig.CurrentValue.AutoLoginOutMinutes));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
returnModel.Data.CompanyInfo = companyInfo;
|
||||||
|
return returnModel;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var userId = returnModel.Data.BasicInfo.Id.ToString();
|
|
||||||
//provider.Set(userId, userId, TimeSpan.FromMinutes(AppSettings.LoginExpiredTimeSpan));
|
|
||||||
// 验证阅片休息时间
|
|
||||||
await readingImageTaskService.ResetReadingRestTime(returnModel.Data.BasicInfo.Id);
|
|
||||||
await provider.SetAsync(userId.ToString(), returnModel.Data.JWTStr, TimeSpan.FromDays(7));
|
|
||||||
return returnModel;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[AllowAnonymous]
|
||||||
|
[HttpGet, Route("user/getPublicKey")]
|
||||||
|
public IResponseOutput GetPublicKey([FromServices] IOptionsMonitor<IRCEncreptOption> _IRCEncreptOption)
|
||||||
|
{
|
||||||
|
//var pemPublicKey = Encoding.UTF8.GetString(Convert.FromBase64String(_IRCEncreptOption.CurrentValue.Base64RSAPublicKey));
|
||||||
|
|
||||||
|
return ResponseOutput.Ok(_IRCEncreptOption.CurrentValue.Base64RSAPublicKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
[HttpGet, Route("imageShare/ShareImage")]
|
[HttpGet, Route("imageShare/ShareImage")]
|
||||||
@@ -214,116 +321,109 @@ namespace IRaCIS.Api.Controllers
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("user/GetObjectStoreToken")]
|
[HttpGet("user/GetObjectStoreToken")]
|
||||||
public IResponseOutput GetObjectStoreToken([FromServices] IOptionsMonitor<ObjectStoreServiceOptions> options)
|
public async Task<IResponseOutput> GetObjectStoreTokenAsync([FromServices] IOptionsMonitor<ObjectStoreServiceOptions> options, [FromServices] IOSSService _oSSService)
|
||||||
{
|
{
|
||||||
var serviceOption = options.CurrentValue;
|
|
||||||
|
|
||||||
if (Enum.TryParse<ObjectStoreUse>(serviceOption.ObjectStoreUse, out var parsedEnum) && parsedEnum == ObjectStoreUse.AliyunOSS)
|
var result = _oSSService.GetObjectStoreTempToken();
|
||||||
{
|
|
||||||
|
|
||||||
var ossOptions = serviceOption.AliyunOSS;
|
//result.AWS = await GetAWSTemToken(options.CurrentValue);
|
||||||
|
|
||||||
return ResponseOutput.Ok(new ObjectStoreDTO() { ObjectStoreUse = serviceOption.ObjectStoreUse, MinIO = serviceOption.MinIO ,AliyunOSS= serviceOption.AliyunOSS,AWS=serviceOption.AWS });
|
return ResponseOutput.Ok(result);
|
||||||
|
|
||||||
#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
|
|
||||||
{
|
|
||||||
return ResponseOutput.Ok(new ObjectStoreDTO() { ObjectStoreUse = serviceOption.ObjectStoreUse, MinIO = serviceOption.MinIO, AWS = serviceOption.AWS });
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("user/GenerateSTS")]
|
|
||||||
public IResponseOutput GenerateSTS([FromServices]IOptionsMonitor<AliyunOSSOptions> options )
|
private async Task<AWSTempToken> GetAWSTemToken(ObjectStoreServiceOptions serviceOption)
|
||||||
{
|
{
|
||||||
var ossOptions = options.CurrentValue;
|
var awsOptions = serviceOption.AWS;
|
||||||
|
|
||||||
|
//aws 临时凭证
|
||||||
|
// 创建 STS 客户端
|
||||||
|
var stsClient = new AmazonSecurityTokenServiceClient(awsOptions.AccessKeyId, awsOptions.SecretAccessKey);
|
||||||
|
|
||||||
IClientProfile profile = DefaultProfile.GetProfile(ossOptions.regionId, ossOptions.accessKeyId, ossOptions.accessKeySecret);
|
// 使用 AssumeRole 请求临时凭证
|
||||||
DefaultAcsClient client = new DefaultAcsClient(profile);
|
var assumeRoleRequest = new AssumeRoleRequest
|
||||||
|
|
||||||
|
|
||||||
// 创建一个STS请求
|
|
||||||
AssumeRoleRequest request = new AssumeRoleRequest
|
|
||||||
{
|
{
|
||||||
RoleArn = ossOptions.roleArn, // 角色ARN,需要替换为你的角色ARN
|
|
||||||
RoleSessionName = $"session-name-{NewId.NextGuid()}", // 角色会话名称,可自定义
|
RoleArn = awsOptions.RoleArn, // 角色 ARN
|
||||||
DurationSeconds = 900, // 令牌有效期(单位:秒),这里设置为1小时
|
RoleSessionName = $"session-name-{NewId.NextGuid()}",
|
||||||
|
DurationSeconds = awsOptions.DurationSeconds // 临时凭证有效期
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var assumeRoleResponse = await stsClient.AssumeRoleAsync(assumeRoleRequest);
|
||||||
|
|
||||||
AssumeRoleResponse response = client.GetAcsResponse(request);
|
var credentials = assumeRoleResponse.Credentials;
|
||||||
|
|
||||||
// 返回STS令牌信息给前端
|
var tempToken = new AWSTempToken()
|
||||||
var stsToken = new
|
|
||||||
{
|
{
|
||||||
AccessKeyId = response.Credentials.AccessKeyId,
|
AccessKeyId = credentials.AccessKeyId,
|
||||||
AccessKeySecret = response.Credentials.AccessKeySecret,
|
SecretAccessKey = credentials.SecretAccessKey,
|
||||||
SecurityToken = response.Credentials.SecurityToken,
|
SessionToken = credentials.SessionToken,
|
||||||
Expiration = response.Credentials.Expiration,
|
Expiration = credentials.Expiration,
|
||||||
|
Region = awsOptions.Region,
|
||||||
Region = ossOptions.region ,
|
BucketName = awsOptions.BucketName,
|
||||||
BucketName = ossOptions.bucketName ,
|
EndPoint = awsOptions.EndPoint,
|
||||||
ViewEndpoint = ossOptions.viewEndpoint ,
|
ViewEndpoint = awsOptions.ViewEndpoint,
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return ResponseOutput.Ok(stsToken);
|
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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[HttpGet("User/UserRedirect")]
|
[HttpGet("User/UserRedirect")]
|
||||||
[AllowAnonymous]
|
[AllowAnonymous]
|
||||||
public async Task<IActionResult> UserRedirect([FromServices] IRepository<User> _userRepository, string url ,[FromServices]ILogger<ExtraController> _logger)
|
public async Task<IActionResult> UserRedirect([FromServices] IRepository<User> _userRepository, string url, [FromServices] ILogger<ExtraController> _logger)
|
||||||
{
|
{
|
||||||
|
|
||||||
var decodeUrl = System.Web.HttpUtility.UrlDecode(url);
|
var decodeUrl = System.Web.HttpUtility.UrlDecode(url);
|
||||||
|
|
||||||
var userId = decodeUrl.Substring(decodeUrl.IndexOf("UserId=") + "UserId=".Length , 36) ;
|
var userId = decodeUrl.Substring(decodeUrl.IndexOf("UserId=") + "UserId=".Length, 36);
|
||||||
|
|
||||||
var token = decodeUrl.Substring(decodeUrl.IndexOf("access_token=") + "access_token=".Length);
|
var token = decodeUrl.Substring(decodeUrl.IndexOf("access_token=") + "access_token=".Length);
|
||||||
|
|
||||||
@@ -331,12 +431,12 @@ namespace IRaCIS.Api.Controllers
|
|||||||
|
|
||||||
var domainStrList = decodeUrl.Split("/").ToList().Take(3).ToList();
|
var domainStrList = decodeUrl.Split("/").ToList().Take(3).ToList();
|
||||||
|
|
||||||
var errorUrl = domainStrList[0]+"//"+ domainStrList[2]+ "/error";
|
var errorUrl = domainStrList[0] + "//" + domainStrList[2] + "/error";
|
||||||
|
|
||||||
|
|
||||||
if (!await _userRepository.AnyAsync(t => t.Id == Guid.Parse(userId) && t.EmailToken == token && t.IsFirstAdd))
|
if (!await _userRepository.AnyAsync(t => t.Id == Guid.Parse(userId) && t.EmailToken == token && t.IsFirstAdd))
|
||||||
{
|
{
|
||||||
decodeUrl = errorUrl+ $"?lang={lang}&ErrorMessage={System.Web.HttpUtility.UrlEncode(lang=="zh"? "您的初始化链接已过期": "Error!The initialization link has expired. Return")} ";
|
decodeUrl = errorUrl + $"?lang={lang}&ErrorMessage={System.Web.HttpUtility.UrlEncode(lang == "zh" ? "您的初始化链接已过期" : "Error!The initialization link has expired. Return")} ";
|
||||||
}
|
}
|
||||||
|
|
||||||
return Redirect(decodeUrl);
|
return Redirect(decodeUrl);
|
||||||
@@ -348,7 +448,7 @@ namespace IRaCIS.Api.Controllers
|
|||||||
|
|
||||||
[HttpGet, Route("ip")]
|
[HttpGet, Route("ip")]
|
||||||
[AllowAnonymous]
|
[AllowAnonymous]
|
||||||
public IResponseOutput Get([FromServices] IHttpContextAccessor _context/*, [FromServices] IUserService _userService*/)
|
public IResponseOutput Get([FromServices] IHttpContextAccessor _context)
|
||||||
{
|
{
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
@@ -369,7 +469,7 @@ namespace IRaCIS.Api.Controllers
|
|||||||
|
|
||||||
[HttpGet, Route("ip2")]
|
[HttpGet, Route("ip2")]
|
||||||
[AllowAnonymous]
|
[AllowAnonymous]
|
||||||
public IResponseOutput Get2([FromServices] IHttpContextAccessor _context, [FromServices] IRepository _userService)
|
public IResponseOutput Get2([FromServices] IHttpContextAccessor _context)
|
||||||
{
|
{
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ using IRaCIS.Core.Application.Filter;
|
|||||||
using IRaCIS.Core.Infrastructure.Extention;
|
using IRaCIS.Core.Infrastructure.Extention;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using IRaCIS.Application.Services;
|
using IRaCIS.Core.Application.Service;
|
||||||
using IRaCIS.Core.Application.Service.Inspection.DTO;
|
using IRaCIS.Core.Application.Service.Inspection.DTO;
|
||||||
using IRaCIS.Core.Infra.EFCore;
|
using IRaCIS.Core.Infra.EFCore;
|
||||||
using IRaCIS.Core.Domain.Models;
|
using IRaCIS.Core.Domain.Models;
|
||||||
@@ -19,18 +19,12 @@ namespace IRaCIS.Core.API.Controllers.Special
|
|||||||
//谨慎修改 涉及到财务模块
|
//谨慎修改 涉及到财务模块
|
||||||
|
|
||||||
[ApiController, Authorize, ApiExplorerSettings(GroupName = "Financial")]
|
[ApiController, Authorize, ApiExplorerSettings(GroupName = "Financial")]
|
||||||
public class FinancialChangeController : ControllerBase
|
public class FinancialChangeController(
|
||||||
|
ITrialService _trialService,
|
||||||
|
ICalculateService _calculateService,
|
||||||
|
IStringLocalizer _localizer) : ControllerBase
|
||||||
{
|
{
|
||||||
private readonly ITrialService _trialService;
|
|
||||||
private readonly ICalculateService _calculateService;
|
|
||||||
private IStringLocalizer _localizer { get; set; }
|
|
||||||
public FinancialChangeController(ITrialService trialService, ICalculateService calculateService, IStringLocalizer localizer
|
|
||||||
)
|
|
||||||
{
|
|
||||||
_localizer = localizer;
|
|
||||||
_trialService = trialService;
|
|
||||||
_calculateService = calculateService;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//[TrialAudit(AuditType.TrialAudit, AuditOptType.AddOrUpdateTrial)]
|
//[TrialAudit(AuditType.TrialAudit, AuditOptType.AddOrUpdateTrial)]
|
||||||
|
|||||||
@@ -26,78 +26,19 @@ namespace IRaCIS.Core.API.Controllers
|
|||||||
|
|
||||||
[ApiController, ApiExplorerSettings(GroupName = "Reviewer")]
|
[ApiController, ApiExplorerSettings(GroupName = "Reviewer")]
|
||||||
[UnitOfWork]
|
[UnitOfWork]
|
||||||
public class InspectionController : ControllerBase
|
public class InspectionController(
|
||||||
|
ITrialDocumentService _trialDocumentService,
|
||||||
|
IReadingImageTaskService _iReadingImageTaskService,
|
||||||
|
ITrialConfigService _trialConfigService,
|
||||||
|
IClinicalAnswerService _clinicalAnswerService,
|
||||||
|
IReadingClinicalDataService _readingClinicalDataService,
|
||||||
|
IQCOperationService _qCOperationService,
|
||||||
|
IInspectionService _inspectionService,
|
||||||
|
IReadingMedicalReviewService _readingMedicalReviewService,
|
||||||
|
IReadingMedicineQuestionService _readingMedicineQuestionService
|
||||||
|
) : 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 获取稽查数据
|
#region 获取稽查数据
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取稽查数据
|
/// 获取稽查数据
|
||||||
@@ -308,11 +249,28 @@ namespace IRaCIS.Core.API.Controllers
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 提交结构化录入并签名
|
/// PM签名一致性分析临床数据
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="opt"></param>
|
/// <param name="opt"></param>
|
||||||
/// <returns></returns>
|
/// <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>
|
||||||
[HttpPost, Route("Inspection/ClinicalAnswer/SubmitClinicalFormAndSign")]
|
[HttpPost, Route("Inspection/ClinicalAnswer/SubmitClinicalFormAndSign")]
|
||||||
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
||||||
|
|
||||||
@@ -402,6 +360,19 @@ namespace IRaCIS.Core.API.Controllers
|
|||||||
return result;
|
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>
|
||||||
/// 签名确认
|
/// 签名确认
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
using AutoMapper;
|
using AutoMapper;
|
||||||
using DocumentFormat.OpenXml.Drawing;
|
using DocumentFormat.OpenXml.Drawing;
|
||||||
using EasyCaching.Core;
|
|
||||||
using ExcelDataReader;
|
using ExcelDataReader;
|
||||||
using IRaCIS.Application.Contracts;
|
using IRaCIS.Application.Contracts;
|
||||||
using IRaCIS.Application.Interfaces;
|
using IRaCIS.Application.Interfaces;
|
||||||
@@ -10,8 +9,7 @@ using IRaCIS.Core.Application.Contracts.Dicom;
|
|||||||
using IRaCIS.Core.Application.Contracts.Dicom.DTO;
|
using IRaCIS.Core.Application.Contracts.Dicom.DTO;
|
||||||
using IRaCIS.Core.Application.Filter;
|
using IRaCIS.Core.Application.Filter;
|
||||||
using IRaCIS.Core.Application.Helper;
|
using IRaCIS.Core.Application.Helper;
|
||||||
using IRaCIS.Core.Application.MediatR.CommandAndQueries;
|
using IRaCIS.Core.Application.MassTransit.Command;
|
||||||
using IRaCIS.Core.Application.MediatR.Handlers;
|
|
||||||
using IRaCIS.Core.Application.Service;
|
using IRaCIS.Core.Application.Service;
|
||||||
using IRaCIS.Core.Application.Service.ImageAndDoc;
|
using IRaCIS.Core.Application.Service.ImageAndDoc;
|
||||||
using IRaCIS.Core.Application.Service.Reading.Dto;
|
using IRaCIS.Core.Application.Service.Reading.Dto;
|
||||||
@@ -20,9 +18,8 @@ using IRaCIS.Core.Domain.Share;
|
|||||||
using IRaCIS.Core.Infra.EFCore;
|
using IRaCIS.Core.Infra.EFCore;
|
||||||
using IRaCIS.Core.Infrastructure;
|
using IRaCIS.Core.Infrastructure;
|
||||||
using IRaCIS.Core.Infrastructure.Extention;
|
using IRaCIS.Core.Infrastructure.Extention;
|
||||||
using Magicodes.ExporterAndImporter.Excel;
|
|
||||||
using MassTransit;
|
using MassTransit;
|
||||||
using MediatR;
|
using MassTransit.Mediator;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Cors.Infrastructure;
|
using Microsoft.AspNetCore.Cors.Infrastructure;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
@@ -39,15 +36,16 @@ using Microsoft.Net.Http.Headers;
|
|||||||
using MiniExcelLibs;
|
using MiniExcelLibs;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using SharpCompress.Archives;
|
using SharpCompress.Archives;
|
||||||
using SkiaSharp;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.Data;
|
using System.Data;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using ZiggyCreatures.Caching.Fusion;
|
||||||
using Path = System.IO.Path;
|
using Path = System.IO.Path;
|
||||||
|
|
||||||
namespace IRaCIS.Core.API.Controllers
|
namespace IRaCIS.Core.API.Controllers
|
||||||
@@ -230,70 +228,17 @@ namespace IRaCIS.Core.API.Controllers
|
|||||||
|
|
||||||
[ApiExplorerSettings(GroupName = "Image")]
|
[ApiExplorerSettings(GroupName = "Image")]
|
||||||
[ApiController]
|
[ApiController]
|
||||||
public class StudyController : UploadBaseController
|
public class StudyController(
|
||||||
|
IMediator _mediator,
|
||||||
|
QCCommon _qCCommon,
|
||||||
|
IUserInfo _userInfo,
|
||||||
|
IRepository<SubjectVisit> _subjectVisitRepository,
|
||||||
|
IStringLocalizer _localizer) : UploadBaseController
|
||||||
{
|
{
|
||||||
public IMapper _mapper { get; set; }
|
|
||||||
public IUserInfo _userInfo { get; set; }
|
|
||||||
private readonly IMediator _mediator;
|
|
||||||
|
|
||||||
|
|
||||||
public IStringLocalizer _localizer { get; set; }
|
|
||||||
|
|
||||||
|
|
||||||
private readonly IWebHostEnvironment _hostEnvironment;
|
|
||||||
|
|
||||||
private readonly IRepository _repository;
|
|
||||||
|
|
||||||
private readonly IEasyCachingProvider _provider;
|
|
||||||
private readonly QCCommon _qCCommon;
|
|
||||||
public StudyController(IMapper mapper, IStringLocalizer localizer, IUserInfo userInfo, IWebHostEnvironment hostEnvironment, IMediator mediator, IEasyCachingProvider provider,
|
|
||||||
QCCommon qCCommon,
|
|
||||||
IRepository repository)
|
|
||||||
{
|
|
||||||
_qCCommon = qCCommon;
|
|
||||||
_provider = provider;
|
|
||||||
_hostEnvironment = hostEnvironment;
|
|
||||||
_mediator = mediator;
|
|
||||||
_localizer = localizer;
|
|
||||||
_mapper = mapper;
|
|
||||||
_userInfo = userInfo;
|
|
||||||
_repository = repository;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
[HttpPost, Route("Study/PreArchiveStudy")]
|
|
||||||
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
|
||||||
public async Task<IResponseOutput> PreArchiveStudy(PreArchiveStudyCommand preArchiveStudyCommand,
|
|
||||||
[FromServices] IStudyService _studyService,
|
|
||||||
[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,
|
|
||||||
|
|
||||||
IsSuccess = false,
|
|
||||||
UploadStartTime = DateTime.Now,
|
|
||||||
IsDicom = preArchiveStudyCommand.IsDicom,
|
|
||||||
IP = _userInfo.IP
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
var addEntity = await _studyMonitorRepository.AddAsync(studyMonitor, true);
|
|
||||||
|
|
||||||
return ResponseOutput.Ok(addEntity.Id);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>Dicom 归档</summary>
|
/// <summary>Dicom 归档</summary>
|
||||||
@@ -301,9 +246,8 @@ namespace IRaCIS.Core.API.Controllers
|
|||||||
[DisableFormValueModelBinding]
|
[DisableFormValueModelBinding]
|
||||||
[DisableRequestSizeLimit]
|
[DisableRequestSizeLimit]
|
||||||
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
||||||
public async Task<IResponseOutput> ArchiveStudyNew(/*[FromForm] ArchiveStudyCommand archiveStudyCommand,*/ Guid trialId, Guid subjectVisitId, string studyInstanceUid, Guid? abandonStudyId, Guid studyMonitorId,
|
public async Task<IResponseOutput> ArchiveStudyNew(Guid trialId, Guid subjectVisitId, string studyInstanceUid, Guid? abandonStudyId, Guid studyMonitorId,
|
||||||
[FromServices] ILogger<UploadDownLoadController> _logger,
|
[FromServices] ILogger<UploadDownLoadController> _logger,
|
||||||
[FromServices] IEasyCachingProvider _provider,
|
|
||||||
[FromServices] IStudyService _studyService,
|
[FromServices] IStudyService _studyService,
|
||||||
[FromServices] IHubContext<UploadHub, IUploadClient> _uploadHub,
|
[FromServices] IHubContext<UploadHub, IUploadClient> _uploadHub,
|
||||||
[FromServices] IDicomArchiveService _dicomArchiveService,
|
[FromServices] IDicomArchiveService _dicomArchiveService,
|
||||||
@@ -328,22 +272,6 @@ namespace IRaCIS.Core.API.Controllers
|
|||||||
var startTime = DateTime.Now;
|
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));
|
|
||||||
//}
|
|
||||||
|
|
||||||
//到了接口,代表上传结束了
|
//到了接口,代表上传结束了
|
||||||
|
|
||||||
@@ -407,15 +335,14 @@ namespace IRaCIS.Core.API.Controllers
|
|||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (Exception )
|
catch (Exception)
|
||||||
{
|
{
|
||||||
_provider.Remove($"StudyUid_{trialId}_{archiveStudyCommand.StudyInstanceUid}");
|
|
||||||
|
|
||||||
//---请求异常,请重试!
|
//---请求异常,请重试!
|
||||||
throw new BusinessValidationFailedException(_localizer["UploadDownLoad_RequestError"]);
|
throw new BusinessValidationFailedException(_localizer["UploadDownLoad_RequestError"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
studyMonitor.FileSize = (decimal)HttpContext.Request.ContentLength;
|
studyMonitor.FileSize = (long)HttpContext.Request.ContentLength;
|
||||||
studyMonitor.FileCount = archiveResult.ReceivedFileCount;
|
studyMonitor.FileCount = archiveResult.ReceivedFileCount;
|
||||||
studyMonitor.FailedFileCount = archiveResult.ErrorFiles.Count;
|
studyMonitor.FailedFileCount = archiveResult.ErrorFiles.Count;
|
||||||
studyMonitor.IsDicomReUpload = archiveStudyCommand.AbandonStudyId != null;
|
studyMonitor.IsDicomReUpload = archiveStudyCommand.AbandonStudyId != null;
|
||||||
@@ -448,7 +375,6 @@ namespace IRaCIS.Core.API.Controllers
|
|||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
_provider.Remove($"StudyUid_{trialId}_{archiveStudyCommand.StudyInstanceUid}");
|
|
||||||
|
|
||||||
studyMonitor.StudyId = archiveResult.ArchivedDicomStudies.FirstOrDefault()?.Id ?? Guid.Empty;
|
studyMonitor.StudyId = archiveResult.ArchivedDicomStudies.FirstOrDefault()?.Id ?? Guid.Empty;
|
||||||
studyMonitor.StudyCode = archiveResult.ArchivedDicomStudies.FirstOrDefault()?.StudyCode;
|
studyMonitor.StudyCode = archiveResult.ArchivedDicomStudies.FirstOrDefault()?.StudyCode;
|
||||||
@@ -468,12 +394,26 @@ namespace IRaCIS.Core.API.Controllers
|
|||||||
|
|
||||||
public class UploadNoneDicomFileCommand
|
public class UploadNoneDicomFileCommand
|
||||||
{
|
{
|
||||||
public Guid TrialId { get; set; }
|
[NotDefault]
|
||||||
public Guid SubjectVisitId { get; set; }
|
public Guid SubjectVisitId { get; set; }
|
||||||
public Guid NoneDicomStudyId { get; set; }
|
|
||||||
|
|
||||||
|
[NotDefault]
|
||||||
public Guid StudyMonitorId { get; set; }
|
public Guid StudyMonitorId { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
public Guid? NoneDicomStudyId { get; set; }
|
||||||
|
|
||||||
|
//IR 上传的时候跟任务绑定
|
||||||
|
public Guid? VisitTaskId { get; set; }
|
||||||
|
|
||||||
|
public string RecordPath { get; set; }
|
||||||
|
|
||||||
|
public int FailedFileCount { get; set; }
|
||||||
|
|
||||||
|
public long FileSize { get; set; }
|
||||||
|
|
||||||
|
|
||||||
public List<OSSFileDTO> UploadedFileList { get; set; } = new List<OSSFileDTO>();
|
public List<OSSFileDTO> UploadedFileList { get; set; } = new List<OSSFileDTO>();
|
||||||
|
|
||||||
|
|
||||||
@@ -482,21 +422,61 @@ namespace IRaCIS.Core.API.Controllers
|
|||||||
public string FilePath { get; set; }
|
public string FilePath { get; set; }
|
||||||
public string FileName { get; set; }
|
public string FileName { get; set; }
|
||||||
public int FileFize { get; set; }
|
public int FileFize { get; set; }
|
||||||
|
|
||||||
|
public string FileType { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 非dicom 上传预上传接口
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="preArchiveStudyCommand"></param>
|
||||||
|
/// <param name="_studyService"></param>
|
||||||
|
/// <param name="_studyMonitorRepository"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpPost, Route("Study/PreArchiveStudy")]
|
||||||
|
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
||||||
|
public async Task<IResponseOutput> PreArchiveStudy(PreArchiveStudyCommand preArchiveStudyCommand,
|
||||||
|
[FromServices] IStudyService _studyService,
|
||||||
|
[FromServices] IRepository<StudyMonitor> _studyMonitorRepository)
|
||||||
|
{
|
||||||
|
|
||||||
|
var savedInfo = _studyService.GetSaveToDicomInfo(preArchiveStudyCommand.SubjectVisitId);
|
||||||
|
|
||||||
|
var studyMonitor = new StudyMonitor()
|
||||||
|
{
|
||||||
|
TrialId = savedInfo.TrialId,
|
||||||
|
SubjectId = savedInfo.SubjectId,
|
||||||
|
SubjectVisitId = savedInfo.SubjectVisitId,
|
||||||
|
|
||||||
|
IsSuccess = false,
|
||||||
|
UploadStartTime = DateTime.Now,
|
||||||
|
FileCount=preArchiveStudyCommand.FileCount,
|
||||||
|
IsDicom = preArchiveStudyCommand.IsDicom,
|
||||||
|
IP = _userInfo.IP
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
var addEntity = await _studyMonitorRepository.AddAsync(studyMonitor, true);
|
||||||
|
|
||||||
|
return ResponseOutput.Ok(addEntity.Id);
|
||||||
|
|
||||||
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 上传非Dicom 文件 支持压缩包 多文件上传
|
/// 上传非Dicom 文件 支持压缩包 多文件上传
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="incommand"></param>
|
/// <param name="incommand"></param>
|
||||||
/// <param name="_noneDicomStudyRepository"></param>
|
/// <param name="_noneDicomStudyRepository"></param>
|
||||||
/// <param name="_studyMonitorRepository"></param>
|
/// <param name="_studyMonitorRepository"></param>
|
||||||
|
/// <param name="_noneDicomStudyFileRepository"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost("NoneDicomStudy/UploadNoneDicomFile")]
|
[HttpPost("NoneDicomStudy/UploadNoneDicomFile")]
|
||||||
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
||||||
|
|
||||||
public async Task<IResponseOutput> UploadNoneDicomFile(UploadNoneDicomFileCommand incommand,
|
public async Task<IResponseOutput> UploadNoneDicomFile(UploadNoneDicomFileCommand incommand,
|
||||||
[FromServices] IRepository<NoneDicomStudy> _noneDicomStudyRepository, [FromServices] IRepository<StudyMonitor> _studyMonitorRepository)
|
[FromServices] IRepository<NoneDicomStudy> _noneDicomStudyRepository,
|
||||||
|
[FromServices] IRepository<StudyMonitor> _studyMonitorRepository,
|
||||||
|
[FromServices] IRepository<NoneDicomStudyFile> _noneDicomStudyFileRepository)
|
||||||
{
|
{
|
||||||
|
|
||||||
var subjectVisitId = incommand.SubjectVisitId;
|
var subjectVisitId = incommand.SubjectVisitId;
|
||||||
@@ -504,9 +484,9 @@ namespace IRaCIS.Core.API.Controllers
|
|||||||
var noneDicomStudyId = incommand.NoneDicomStudyId;
|
var noneDicomStudyId = incommand.NoneDicomStudyId;
|
||||||
|
|
||||||
|
|
||||||
await _qCCommon.VerifyIsCRCSubmmitAsync(_repository, _userInfo, subjectVisitId);
|
await _qCCommon.VerifyIsCRCSubmmitAsync(_subjectVisitRepository, _userInfo, subjectVisitId);
|
||||||
|
|
||||||
var sv = (await _repository.Where<SubjectVisit>(t => t.Id == subjectVisitId).Select(t => new { t.TrialId, t.SiteId, t.SubjectId }).FirstOrDefaultAsync()).IfNullThrowConvertException();
|
var sv = (await _subjectVisitRepository.Where(t => t.Id == subjectVisitId).Select(t => new { t.TrialId, t.TrialSiteId, t.SubjectId }).FirstOrDefaultAsync()).IfNullThrowConvertException();
|
||||||
|
|
||||||
var studyMonitor = await _studyMonitorRepository.FirstOrDefaultAsync(t => t.Id == studyMonitorId);
|
var studyMonitor = await _studyMonitorRepository.FirstOrDefaultAsync(t => t.Id == studyMonitorId);
|
||||||
|
|
||||||
@@ -514,25 +494,39 @@ namespace IRaCIS.Core.API.Controllers
|
|||||||
|
|
||||||
foreach (var item in incommand.UploadedFileList)
|
foreach (var item in incommand.UploadedFileList)
|
||||||
{
|
{
|
||||||
await _repository.AddAsync(new NoneDicomStudyFile() { FileName = item.FileName, Path = item.FilePath, NoneDicomStudyId = noneDicomStudyId });
|
//如果是跟任务绑,那么NoneDicomStudyId 设置为空,不影响之前的检查,同时设置 OriginNoneDicomStudyId 保证关系
|
||||||
|
|
||||||
|
if (incommand.VisitTaskId != null && incommand.VisitTaskId != Guid.Empty)
|
||||||
|
{
|
||||||
|
await _noneDicomStudyFileRepository.AddAsync(new NoneDicomStudyFile() { FileName = item.FileName, Path = item.FilePath, OriginNoneDicomStudyId = noneDicomStudyId.Value, VisitTaskId = incommand.VisitTaskId, FileType = item.FileType, FileSize = item.FileFize });
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await _noneDicomStudyFileRepository.AddAsync(new NoneDicomStudyFile() { FileName = item.FileName, Path = item.FilePath, NoneDicomStudyId = noneDicomStudyId.Value, FileType = item.FileType, FileSize = item.FileFize });
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
var uploadFinishedTime = DateTime.Now;
|
var uploadFinishedTime = DateTime.Now;
|
||||||
|
|
||||||
var noneDicomStudy = await _noneDicomStudyRepository.FirstOrDefaultAsync((t => t.Id == noneDicomStudyId));
|
var noneDicomStudy = await _noneDicomStudyRepository.FirstOrDefaultAsync((t => t.Id == noneDicomStudyId));
|
||||||
|
|
||||||
noneDicomStudy.FileCount = noneDicomStudy.FileCount + incommand.UploadedFileList.Count;
|
noneDicomStudy.FileCount = noneDicomStudy.FileCount + (incommand.VisitTaskId != null ? 0 : incommand.UploadedFileList.Count);
|
||||||
|
|
||||||
studyMonitor.FileCount = incommand.UploadedFileList.Count;
|
studyMonitor.RecordPath = incommand.RecordPath;
|
||||||
|
studyMonitor.FailedFileCount = incommand.FailedFileCount;
|
||||||
|
studyMonitor.IsSuccess = incommand.FailedFileCount == 0;
|
||||||
studyMonitor.FileSize = incommand.UploadedFileList.Sum(t => t.FileFize);
|
studyMonitor.FileSize = incommand.UploadedFileList.Sum(t => t.FileFize);
|
||||||
studyMonitor.IsDicom = false;
|
studyMonitor.IsDicom = false;
|
||||||
studyMonitor.IsDicomReUpload = false;
|
studyMonitor.IsDicomReUpload = false;
|
||||||
studyMonitor.StudyId = noneDicomStudyId;
|
studyMonitor.StudyId = noneDicomStudyId.Value;
|
||||||
studyMonitor.StudyCode = noneDicomStudy.StudyCode;
|
studyMonitor.StudyCode = noneDicomStudy.StudyCode;
|
||||||
studyMonitor.ArchiveFinishedTime = DateTime.Now;
|
studyMonitor.ArchiveFinishedTime = DateTime.Now;
|
||||||
studyMonitor.IP = _userInfo.IP;
|
studyMonitor.IP = _userInfo.IP;
|
||||||
|
|
||||||
await _repository.SaveChangesAsync();
|
await _noneDicomStudyRepository.SaveChangesAsync();
|
||||||
|
|
||||||
return ResponseOutput.Ok();
|
return ResponseOutput.Ok();
|
||||||
}
|
}
|
||||||
@@ -546,12 +540,13 @@ namespace IRaCIS.Core.API.Controllers
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="trialId"></param>
|
/// <param name="trialId"></param>
|
||||||
/// <param name="oSSService"></param>
|
/// <param name="oSSService"></param>
|
||||||
|
/// <param name="_inspectionFileRepository"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
/// <exception cref="BusinessValidationFailedException"></exception>
|
/// <exception cref="BusinessValidationFailedException"></exception>
|
||||||
[HttpPost("QCOperation/UploadVisitCheckExcel/{trialId:guid}")]
|
[HttpPost("QCOperation/UploadVisitCheckExcel/{trialId:guid}")]
|
||||||
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
||||||
|
|
||||||
public async Task<IResponseOutput> UploadVisitCheckExcel(Guid trialId, [FromServices] IOSSService oSSService)
|
public async Task<IResponseOutput> UploadVisitCheckExcel(Guid trialId, [FromServices] IOSSService oSSService, [FromServices] IRepository<InspectionFile> _inspectionFileRepository)
|
||||||
{
|
{
|
||||||
|
|
||||||
var fileName = string.Empty;
|
var fileName = string.Empty;
|
||||||
@@ -561,9 +556,9 @@ namespace IRaCIS.Core.API.Controllers
|
|||||||
{
|
{
|
||||||
fileName = realFileName;
|
fileName = realFileName;
|
||||||
|
|
||||||
if (!fileName.EndsWith(".xlsx") && !fileName.EndsWith(".csv") && !fileName.EndsWith(".xls"))
|
if (!fileName.EndsWith(".xlsx") && !fileName.EndsWith(".xls"))
|
||||||
{
|
{
|
||||||
//---支持.xlsx、.xls、.csv格式的文件上传。
|
//---支持.xlsx、.xls格式的文件上传。
|
||||||
throw new BusinessValidationFailedException(_localizer["UploadDownLoad_SupportedFormats"]);
|
throw new BusinessValidationFailedException(_localizer["UploadDownLoad_SupportedFormats"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -573,7 +568,7 @@ namespace IRaCIS.Core.API.Controllers
|
|||||||
|
|
||||||
var ossRelativePath = await oSSService.UploadToOSSAsync(fileStream, "InspectionUpload/Check", realFileName);
|
var ossRelativePath = await oSSService.UploadToOSSAsync(fileStream, "InspectionUpload/Check", realFileName);
|
||||||
|
|
||||||
await _repository.AddAsync(new InspectionFile() { FileName = realFileName, RelativePath = ossRelativePath, TrialId = trialId });
|
await _inspectionFileRepository.AddAsync(new InspectionFile() { FileName = realFileName, RelativePath = ossRelativePath, TrialId = trialId });
|
||||||
|
|
||||||
return ossRelativePath;
|
return ossRelativePath;
|
||||||
|
|
||||||
@@ -587,6 +582,20 @@ namespace IRaCIS.Core.API.Controllers
|
|||||||
|
|
||||||
#region MiniExcel 需要自己验证数据格式规范
|
#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"))
|
//if (fileName.EndsWith(".csv"))
|
||||||
//{
|
//{
|
||||||
// //因为csv 需要加配置文件 不然都是null
|
// //因为csv 需要加配置文件 不然都是null
|
||||||
@@ -602,67 +611,72 @@ namespace IRaCIS.Core.API.Controllers
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region 升级net8 导入有问题
|
||||||
|
|
||||||
|
|
||||||
//Magicodes 支持自定义特性验证
|
//Magicodes 支持自定义特性验证
|
||||||
if (fileName.EndsWith(".xlsx"))
|
// if (fileName.EndsWith(".xlsx"))
|
||||||
{
|
//{
|
||||||
var Importer = new ExcelImporter();
|
// 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(_localizer["UploadDownLoad_TemplateErrors"]);
|
||||||
|
|
||||||
etcCheckList = import.Data.ToList();
|
// etcCheckList = import.Data.ToList();
|
||||||
}
|
//}
|
||||||
else if (fileName.EndsWith(".csv"))
|
//else if (fileName.EndsWith(".csv"))
|
||||||
{
|
//{
|
||||||
#region 临时方案 MiniExcel读取 然后保存为xlsx 再用 Magicodes验证数据
|
// #region 临时方案 MiniExcel读取 然后保存为xlsx 再用 Magicodes验证数据
|
||||||
|
|
||||||
//因为csv 需要加配置文件 不然都是null
|
// //因为csv 需要加配置文件 不然都是null
|
||||||
etcCheckList = MiniExcel.Query<CheckViewModel>(templateFileStream, null, configuration: new MiniExcelLibs.Csv.CsvConfiguration()
|
// etcCheckList = MiniExcel.Query<CheckViewModel>(templateFileStream, null, configuration: new MiniExcelLibs.Csv.CsvConfiguration()
|
||||||
{
|
// {
|
||||||
StreamReaderFunc = (stream) => new StreamReader(stream, Encoding.GetEncoding("gb2312"))
|
// StreamReaderFunc = (stream) => new StreamReader(stream, Encoding.GetEncoding("gb2312"))
|
||||||
}).ToList();
|
// }).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编码格式
|
// #region 导入组件有问题 excel编码格式
|
||||||
//var Importer = new CsvImporter();
|
// //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();
|
// //etcCheckList = import.Data.ToList();
|
||||||
#endregion
|
// #endregion
|
||||||
|
|
||||||
}
|
//}
|
||||||
|
|
||||||
|
#endregion
|
||||||
//ExcelReaderFactory 需要自己验证数据 并且从固定列取数据
|
//ExcelReaderFactory 需要自己验证数据 并且从固定列取数据
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -724,11 +738,16 @@ namespace IRaCIS.Core.API.Controllers
|
|||||||
//---请保证上传数据符合模板文件中的样式,且存在有效数据。
|
//---请保证上传数据符合模板文件中的样式,且存在有效数据。
|
||||||
return ResponseOutput.NotOk(_localizer["UploadDownLoad_InvalidData"]);
|
return ResponseOutput.NotOk(_localizer["UploadDownLoad_InvalidData"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
await _mediator.Send(new ConsistencyVerificationRequest() { ETCList = etcCheckList, TrialId = trialId });
|
|
||||||
|
// 适合获取结果的
|
||||||
|
//var client = _mediator.CreateRequestClient<ConsistenCheckCommand>();
|
||||||
|
//await client.GetResponse<ConsistenCheckResult>(new ConsistenCheckCommand() { ETCList = etcCheckList, TrialId = trialId });
|
||||||
|
|
||||||
|
//不获取结果,不用定义返回类型
|
||||||
|
await _mediator.Send(new ConsistenCheckCommand() { ETCList = etcCheckList, TrialId = trialId });
|
||||||
|
|
||||||
return ResponseOutput.Ok();
|
return ResponseOutput.Ok();
|
||||||
|
|
||||||
@@ -870,89 +889,6 @@ namespace IRaCIS.Core.API.Controllers
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
#region 废弃
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 下载多个医生的所有附件
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="doctorIds"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
[Obsolete]
|
|
||||||
[HttpPost, Route("file/downloadDoctorAttachments")]
|
|
||||||
public async Task<IResponseOutput<UploadFileInfoDTO>> DownloadAttachment(Guid[] doctorIds)
|
|
||||||
{
|
|
||||||
|
|
||||||
var path = await _fileService.CreateDoctorsAllAttachmentZip(doctorIds);
|
|
||||||
|
|
||||||
return ResponseOutput.Ok(new UploadFileInfoDTO
|
|
||||||
{
|
|
||||||
FilePath = path,
|
|
||||||
FullFilePath = path + "?access_token=" + HttpContext.Request.Headers["Authorization"].ToString().Substring(7)
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 下载指定医生的指定附件
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="doctorId">医生Id</param>
|
|
||||||
/// <param name="attachmentIds">要下载的附件Id</param>
|
|
||||||
/// <returns></returns>
|
|
||||||
[HttpPost, Route("file/downloadByAttachmentId/{doctorId}")]
|
|
||||||
[Obsolete]
|
|
||||||
public async Task<IResponseOutput<UploadFileInfoDTO>> DownloadAttachmentById(Guid doctorId, Guid[] attachmentIds)
|
|
||||||
{
|
|
||||||
var path = await _fileService.CreateZipPackageByAttachment(doctorId, attachmentIds);
|
|
||||||
return ResponseOutput.Ok(new UploadFileInfoDTO
|
|
||||||
{
|
|
||||||
FilePath = await _fileService.CreateZipPackageByAttachment(doctorId, attachmentIds),
|
|
||||||
FullFilePath = path + "?access_token=" + HttpContext.Request.Headers["Authorization"].ToString().Substring(7)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 下载医生官方简历 首页 区分 中文和英文
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="language"></param>
|
|
||||||
/// <param name="doctorIds"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
[HttpPost, Route("file/downloadOfficialCV/{language}")]
|
|
||||||
[Obsolete]
|
|
||||||
public async Task<IResponseOutput<UploadFileInfoDTO>> DownloadOfficialResume(int language, Guid[] doctorIds)
|
|
||||||
{
|
|
||||||
|
|
||||||
var path = await _fileService.CreateDoctorsAllAttachmentZip(doctorIds);
|
|
||||||
return ResponseOutput.Ok(new UploadFileInfoDTO
|
|
||||||
{
|
|
||||||
FilePath = await _fileService.CreateOfficialResumeZip(language, doctorIds),
|
|
||||||
FullFilePath = path + "?access_token=" + HttpContext.Request.Headers["Authorization"].ToString().Substring(7)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 入组 项目下载简历
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="language"></param>
|
|
||||||
/// <param name="trialId"></param>
|
|
||||||
/// <param name="doctorIdArray"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
[HttpPost, Route("enroll/downloadResume/{trialId:guid}/{language}")]
|
|
||||||
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
|
||||||
[AllowAnonymous]
|
|
||||||
[Obsolete]
|
|
||||||
public async Task<IResponseOutput<string>> DownloadResume(int language, Guid trialId, Guid[] doctorIdArray)
|
|
||||||
{
|
|
||||||
var zipPath = await _fileService.CreateOfficialResumeZip(language, doctorIdArray);
|
|
||||||
|
|
||||||
return ResponseOutput.Ok(zipPath);
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@@ -966,14 +902,14 @@ namespace IRaCIS.Core.API.Controllers
|
|||||||
public IMapper _mapper { get; set; }
|
public IMapper _mapper { get; set; }
|
||||||
public IUserInfo _userInfo { get; set; }
|
public IUserInfo _userInfo { get; set; }
|
||||||
private readonly IMediator _mediator;
|
private readonly IMediator _mediator;
|
||||||
public IStringLocalizer _localizer { get; set; }
|
public IStringLocalizer _localizer { get; set; }
|
||||||
private readonly IWebHostEnvironment _hostEnvironment;
|
private readonly IWebHostEnvironment _hostEnvironment;
|
||||||
|
|
||||||
public UploadDownLoadController(IMapper mapper, IUserInfo userInfo, IStringLocalizer localizer, IMediator mediator, IWebHostEnvironment hostEnvironment)
|
public UploadDownLoadController(IMapper mapper, IUserInfo userInfo, IStringLocalizer localizer, IMediator mediator, IWebHostEnvironment hostEnvironment)
|
||||||
{
|
{
|
||||||
_hostEnvironment = hostEnvironment;
|
_hostEnvironment = hostEnvironment;
|
||||||
_localizer = localizer;
|
_localizer = localizer;
|
||||||
_mediator = mediator;
|
_mediator = mediator;
|
||||||
_mapper = mapper;
|
_mapper = mapper;
|
||||||
_userInfo = userInfo;
|
_userInfo = userInfo;
|
||||||
}
|
}
|
||||||
@@ -1045,18 +981,18 @@ namespace IRaCIS.Core.API.Controllers
|
|||||||
}
|
}
|
||||||
var generateUserTypeList = new List<string>() { "CRC", "CRA" };
|
var generateUserTypeList = new List<string>() { "CRC", "CRA" };
|
||||||
|
|
||||||
if (excelList.Any(t => !generateUserTypeList.Contains(t.UserTypeStr.ToUpper())))
|
//if (excelList.Any(t => !generateUserTypeList.Contains(t.UserTypeStr.ToUpper())))
|
||||||
{
|
//{
|
||||||
//用户类型仅能为 CRC,SR,CRA 请核查Excel数据
|
// //用户类型仅能为 CRC,SR,CRA 请核查Excel数据
|
||||||
throw new BusinessValidationFailedException(_localizer["UploadDownLoad_InvalidUserType"]);
|
// throw new BusinessValidationFailedException(_localizer["UploadDownLoad_InvalidUserType"]);
|
||||||
}
|
//}
|
||||||
if (excelList.Count == 0)
|
if (excelList.Count == 0)
|
||||||
{
|
{
|
||||||
throw new BusinessValidationFailedException(_localizer["UploadDownLoad_NoValiddata"]);
|
throw new BusinessValidationFailedException(_localizer["UploadDownLoad_NoValiddata"]);
|
||||||
}
|
}
|
||||||
//处理好 用户类型 和用户类型枚举
|
//处理好 用户类型 和用户类型枚举
|
||||||
var sysUserTypeList = _usertypeRepository.Where(t => t.UserTypeEnum == UserTypeEnum.CRA || t.UserTypeEnum == UserTypeEnum.ClinicalResearchCoordinator).Select(t => new { UserTypeId = t.Id, t.UserTypeEnum }).ToList();
|
var 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, t.SiteId }).ToList();
|
var siteList = _trialSiteRepository.Where(t => t.TrialId == trialId && siteCodeList.Contains(t.TrialSiteCode)).Select(t => new { t.TrialSiteCode, TrialSiteId = t.Id }).ToList();
|
||||||
|
|
||||||
foreach (var item in excelList)
|
foreach (var item in excelList)
|
||||||
{
|
{
|
||||||
@@ -1077,12 +1013,12 @@ namespace IRaCIS.Core.API.Controllers
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
item.SiteId = siteList.FirstOrDefault(t => t.TrialSiteCode.ToUpper() == item.TrialSiteCode.ToUpper()).SiteId;
|
item.TrialSiteId = siteList.FirstOrDefault(t => t.TrialSiteCode.ToUpper() == item.TrialSiteCode.ToUpper()).TrialSiteId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var list = excelList.Where(t => t.UserTypeEnum == UserTypeEnum.ClinicalResearchCoordinator || t.UserTypeEnum == UserTypeEnum.CRA).ToList();
|
||||||
|
|
||||||
|
await _trialSiteSurveyService.ImportGenerateAccountAndJoinTrialAsync(trialId, baseUrl, routeUrl, list);
|
||||||
await _trialSiteSurveyService.ImportGenerateAccountAndJoinTrialAsync(trialId, baseUrl, routeUrl, excelList.ToList());
|
|
||||||
|
|
||||||
return ResponseOutput.Ok();
|
return ResponseOutput.Ok();
|
||||||
|
|
||||||
|
|||||||
@@ -1,107 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
|
||||||
<SignAssembly>false</SignAssembly>
|
|
||||||
<UserSecretsId>354572d4-9e15-4099-807c-63a2d29ff9f2</UserSecretsId>
|
|
||||||
<LangVersion>default</LangVersion>
|
|
||||||
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
|
||||||
<DocumentationFile>.\IRaCIS.Core.API.xml</DocumentationFile>
|
|
||||||
<NoWarn>1701;1702;1591;</NoWarn>
|
|
||||||
<OutputPath>..\bin\</OutputPath>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
|
||||||
<DocumentationFile>bin\Release\IRaCIS.Core.API.xml</DocumentationFile>
|
|
||||||
<OutputPath>bin\Release\</OutputPath>
|
|
||||||
<NoWarn>1701;1702;1591</NoWarn>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Compile Remove="Controllers\ReviewerApi\**" />
|
|
||||||
<Compile Remove="UploadFile\**" />
|
|
||||||
<Content Remove="Controllers\ReviewerApi\**" />
|
|
||||||
<Content Remove="UploadFile\**" />
|
|
||||||
<EmbeddedResource Remove="Controllers\ReviewerApi\**" />
|
|
||||||
<EmbeddedResource Remove="UploadFile\**" />
|
|
||||||
<None Remove="Controllers\ReviewerApi\**" />
|
|
||||||
<None Remove="UploadFile\**" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Content Remove="web.config" />
|
|
||||||
<Content Remove="wwwroot\swagger\ui\abp.js" />
|
|
||||||
<Content Remove="wwwroot\swagger\ui\abp.swagger.js" />
|
|
||||||
<Content Remove="wwwroot\swagger\ui\Index.html" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<None Remove=".preview.jpg" />
|
|
||||||
<None Remove="GrpcToken.proto" />
|
|
||||||
<None Remove="IRaCIS.Core.API.xml" />
|
|
||||||
<None Remove="Protos\GrpcToken.proto" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ApplicationDefinition Include="GrpcToken.proto" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<EmbeddedResource Include="wwwroot\swagger\ui\abp.js" />
|
|
||||||
<EmbeddedResource Include="wwwroot\swagger\ui\abp.swagger.js" />
|
|
||||||
<EmbeddedResource Include="wwwroot\swagger\ui\Index.html" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Protobuf Include="Protos\GrpcToken.proto">
|
|
||||||
<GrpcServices>Client</GrpcServices>
|
|
||||||
</Protobuf>
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="AspNetCoreRateLimit" Version="4.0.1" />
|
|
||||||
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="7.2.0" />
|
|
||||||
<PackageReference Include="EasyCaching.InMemory" Version="1.4.1" />
|
|
||||||
<PackageReference Include="EasyCaching.Interceptor.Castle" Version="1.4.1" />
|
|
||||||
<PackageReference Include="Google.Protobuf" Version="3.19.1" />
|
|
||||||
<PackageReference Include="Grpc.Net.Client" Version="2.41.0" />
|
|
||||||
<PackageReference Include="Grpc.Tools" Version="2.42.0">
|
|
||||||
<PrivateAssets>all</PrivateAssets>
|
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
|
||||||
</PackageReference>
|
|
||||||
<PackageReference Include="Hangfire.Tags.SqlServer" Version="1.8.0" />
|
|
||||||
<PackageReference Include="Invio.Extensions.Authentication.JwtBearer" Version="2.0.1" />
|
|
||||||
<PackageReference Include="LogDashboard" Version="1.4.8" />
|
|
||||||
<PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="9.0.0" />
|
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="6.0.1" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="6.0.0" />
|
|
||||||
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.14.0" />
|
|
||||||
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="6.0.1" />
|
|
||||||
<PackageReference Include="Serilog.AspNetCore" Version="4.1.0" />
|
|
||||||
<PackageReference Include="Serilog.Enrichers.ClientInfo" Version="1.1.4" />
|
|
||||||
<PackageReference Include="Serilog.Sinks.Email" Version="2.4.0" />
|
|
||||||
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\IRaCIS.Core.Application\IRaCIS.Core.Application.csproj" />
|
|
||||||
<ProjectReference Include="..\IRaCIS.Core.Infra.EFCore\IRaCIS.Core.Infra.EFCore.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Folder Include="Properties\PublishProfiles\" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Content Update="NLog.config">
|
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
|
||||||
</Content>
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ProjectExtensions><VisualStudio><UserProperties anonymizetagsetting_1json__JsonSchema="http://json.schemastore.org/jovo-language-model" /></VisualStudio></ProjectExtensions>
|
|
||||||
|
|
||||||
|
|
||||||
</Project>
|
|
||||||
@@ -1,160 +1,112 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<SignAssembly>false</SignAssembly>
|
<SignAssembly>false</SignAssembly>
|
||||||
<UserSecretsId>354572d4-9e15-4099-807c-63a2d29ff9f2</UserSecretsId>
|
<UserSecretsId>354572d4-9e15-4099-807c-63a2d29ff9f2</UserSecretsId>
|
||||||
<LangVersion>default</LangVersion>
|
<LangVersion>default</LangVersion>
|
||||||
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||||
<Version>1.0.1.001</Version>
|
<Version>1.0.1.001</Version>
|
||||||
<Company>上海展影医疗科技有限公司</Company>
|
<Company>上海展影医疗科技有限公司</Company>
|
||||||
<Product>IRC影像系统 (EICS)</Product>
|
<Product>IRC影像系统 (EICS)</Product>
|
||||||
<Copyright>上海展影医疗科技有限公司版权所有</Copyright>
|
<Copyright>上海展影医疗科技有限公司版权所有</Copyright>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||||
<DocumentationFile>.\IRaCIS.Core.API.xml</DocumentationFile>
|
<DocumentationFile>.\IRaCIS.Core.API.xml</DocumentationFile>
|
||||||
<NoWarn>1701;1702;1591;1570;</NoWarn>
|
<NoWarn>1701;1702;1591;1570;</NoWarn>
|
||||||
<OutputPath>..\bin\</OutputPath>
|
<OutputPath>..\bin\</OutputPath>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||||
<DocumentationFile>bin\Release\IRaCIS.Core.API.xml</DocumentationFile>
|
<DocumentationFile>bin\Release\IRaCIS.Core.API.xml</DocumentationFile>
|
||||||
<OutputPath>bin\Release\</OutputPath>
|
<OutputPath>bin\Release\</OutputPath>
|
||||||
<NoWarn>1701;1702;1591</NoWarn>
|
<NoWarn>1701;1702;1591</NoWarn>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Content Remove="web.config" />
|
<Content Remove="web.config" />
|
||||||
<Content Remove="wwwroot\swagger\ui\abp.js" />
|
<Content Remove="wwwroot\swagger\ui\abp.js" />
|
||||||
<Content Remove="wwwroot\swagger\ui\abp.swagger.js" />
|
<Content Remove="wwwroot\swagger\ui\abp.swagger.js" />
|
||||||
<Content Remove="wwwroot\swagger\ui\Index.html" />
|
<Content Remove="wwwroot\swagger\ui\Index.html" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Remove=".preview.jpg" />
|
<None Remove=".preview.jpg" />
|
||||||
<None Remove="GrpcToken.proto" />
|
<None Remove="GrpcToken.proto" />
|
||||||
<None Remove="IRaCIS.Core.API.xml" />
|
<None Remove="IRaCIS.Core.API.xml" />
|
||||||
<None Remove="Protos\GrpcToken.proto" />
|
<None Remove="Protos\GrpcToken.proto" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ApplicationDefinition Include="GrpcToken.proto" />
|
<ApplicationDefinition Include="GrpcToken.proto" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<EmbeddedResource Include="wwwroot\swagger\ui\abp.js">
|
<EmbeddedResource Include="wwwroot\swagger\ui\abp.js">
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
</EmbeddedResource>
|
</EmbeddedResource>
|
||||||
<EmbeddedResource Include="wwwroot\swagger\ui\abp.swagger.js">
|
<EmbeddedResource Include="wwwroot\swagger\ui\abp.swagger.js">
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
</EmbeddedResource>
|
</EmbeddedResource>
|
||||||
<EmbeddedResource Include="wwwroot\swagger\ui\Index.html">
|
<EmbeddedResource Include="wwwroot\swagger\ui\Index.html">
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
</EmbeddedResource>
|
</EmbeddedResource>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Protobuf Include="Protos\GrpcToken.proto">
|
<Protobuf Include="Protos\GrpcToken.proto">
|
||||||
<GrpcServices>Client</GrpcServices>
|
<GrpcServices>Client</GrpcServices>
|
||||||
</Protobuf>
|
</Protobuf>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="aliyun-net-sdk-sts" Version="3.1.2" />
|
<PackageReference Include="AspNetCoreRateLimit" Version="5.0.0" />
|
||||||
<PackageReference Include="AspNetCoreRateLimit" Version="5.0.0" />
|
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="10.0.0" />
|
||||||
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="9.0.0" />
|
|
||||||
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.1" />
|
|
||||||
<PackageReference Include="EasyCaching.Interceptor.Castle" Version="1.9.2" />
|
|
||||||
<PackageReference Include="EasyCaching.Serialization.MessagePack" Version="1.9.2">
|
|
||||||
<TreatAsUsed>true</TreatAsUsed>
|
|
||||||
</PackageReference>
|
|
||||||
<PackageReference Include="Hangfire.AspNetCore" Version="1.8.11" />
|
|
||||||
<PackageReference Include="Hangfire.Dashboard.BasicAuthorization" Version="1.0.2" />
|
|
||||||
<PackageReference Include="Hangfire.SqlServer" Version="1.8.11" />
|
|
||||||
<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.3" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="8.0.0" />
|
|
||||||
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.19.6" />
|
|
||||||
<PackageReference Include="Serilog.AspNetCore" Version="8.0.1" />
|
|
||||||
<PackageReference Include="Serilog.Enrichers.ClientInfo" Version="2.0.3" />
|
|
||||||
<PackageReference Include="Serilog.Sinks.Email" Version="3.0.0" />
|
|
||||||
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
|
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="6.5.0" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.8">
|
||||||
<ProjectReference Include="..\IRaCIS.Core.Application\IRaCIS.Core.Application.csproj" />
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<ProjectReference Include="..\IRaCIS.Core.Infra.EFCore\IRaCIS.Core.Infra.EFCore.csproj" />
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</ItemGroup>
|
</PackageReference>
|
||||||
|
<PackageReference Include="ConfigMapFileProvider" Version="2.0.1" />
|
||||||
|
<PackageReference Include="Hangfire.AspNetCore" Version="1.8.14" />
|
||||||
|
<PackageReference Include="Hangfire.Dashboard.BasicAuthorization" Version="1.0.2" />
|
||||||
|
<PackageReference Include="Hangfire.InMemory" Version="0.10.3" />
|
||||||
|
<PackageReference Include="Hangfire.SqlServer" Version="1.8.14" />
|
||||||
|
<PackageReference Include="Invio.Extensions.Authentication.JwtBearer" Version="2.0.1" />
|
||||||
|
<PackageReference Include="LogDashboard" Version="1.4.8" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.8" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="8.0.0" />
|
||||||
|
<PackageReference Include="Serilog.AspNetCore" Version="8.0.2" />
|
||||||
|
<PackageReference Include="Serilog.Enrichers.ClientInfo" Version="2.1.1" />
|
||||||
|
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
|
||||||
|
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="6.7.3" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="..\.editorconfig" Link=".editorconfig" />
|
<ProjectReference Include="..\IRaCIS.Core.Application\IRaCIS.Core.Application.csproj" />
|
||||||
</ItemGroup>
|
<ProjectReference Include="..\IRaCIS.Core.Infra.EFCore\IRaCIS.Core.Infra.EFCore.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Content Update="wwwroot\EmailTemplate\AdminAddUser_US.html">
|
<None Include="..\.editorconfig" Link=".editorconfig" />
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
</ItemGroup>
|
||||||
</Content>
|
|
||||||
<Content Update="wwwroot\EmailTemplate\AdminResetUser_US.html">
|
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
|
||||||
</Content>
|
|
||||||
<Content Update="wwwroot\EmailTemplate\AdminResetUser.html">
|
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
|
||||||
</Content>
|
|
||||||
<Content Update="wwwroot\EmailTemplate\SubjectEnrollConfirmOrPDProgress_US.html">
|
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
|
||||||
</Content>
|
|
||||||
<Content Update="wwwroot\EmailTemplate\TrialDoctorExistJoin_US.html">
|
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
|
||||||
</Content>
|
|
||||||
<Content Update="wwwroot\EmailTemplate\TrialDoctorFirstJoin.html">
|
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
|
||||||
</Content>
|
|
||||||
<Content Update="wwwroot\EmailTemplate\TrialSiteSurveyReject.html">
|
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
|
||||||
</Content>
|
|
||||||
<Content Update="wwwroot\EmailTemplate\TrialSiteSurveyReject_US.html">
|
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
|
||||||
</Content>
|
|
||||||
<Content Update="wwwroot\EmailTemplate\TrialDoctorExistJoin.html">
|
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
|
||||||
</Content>
|
|
||||||
<Content Update="wwwroot\EmailTemplate\TrialUserExistJoin_US.html">
|
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
|
||||||
</Content>
|
|
||||||
<Content Update="wwwroot\EmailTemplate\TrialUserExistJoin.html">
|
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
|
||||||
</Content>
|
|
||||||
<Content Update="wwwroot\EmailTemplate\AdminAddUser.html">
|
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
|
||||||
</Content>
|
|
||||||
<Content Update="wwwroot\EmailTemplate\TrialDoctorFirstJoin_US.html">
|
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
|
||||||
</Content>
|
|
||||||
<Content Update="wwwroot\EmailTemplate\SubjectEnrollConfirmOrPDProgress.html">
|
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
|
||||||
</Content>
|
|
||||||
<Content Update="wwwroot\EmailTemplate\TrialUserFirstJoin_US.html">
|
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
|
||||||
</Content>
|
|
||||||
<Content Update="wwwroot\EmailTemplate\UserOptCommon_US.html">
|
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
|
||||||
</Content>
|
|
||||||
<Content Update="wwwroot\EmailTemplate\UserOptCommon.html">
|
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
|
||||||
</Content>
|
|
||||||
<Content Update="wwwroot\EmailTemplate\TrialUserFirstJoin.html">
|
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
|
||||||
</Content>
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Folder Include="Properties\PublishProfiles\" />
|
<Folder Include="Properties\PublishProfiles\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ProjectExtensions><VisualStudio><UserProperties properties_4launchsettings_1json__JsonSchema="" /></VisualStudio></ProjectExtensions>
|
<ItemGroup>
|
||||||
|
<Content Update="appsettings.json">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ProjectExtensions>
|
||||||
|
<VisualStudio>
|
||||||
|
<UserProperties properties_4launchsettings_1json__JsonSchema="" />
|
||||||
|
</VisualStudio>
|
||||||
|
</ProjectExtensions>
|
||||||
|
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
<param name="doctorId"></param>
|
<param name="doctorId"></param>
|
||||||
<returns></returns>
|
<returns></returns>
|
||||||
</member>
|
</member>
|
||||||
<member name="M:IRaCIS.Api.Controllers.ExtraController.Login(IRaCIS.Application.Contracts.UserLoginDTO,EasyCaching.Core.IEasyCachingProvider,IRaCIS.Application.Services.IUserService,IRaCIS.Core.Application.Auth.ITokenService,IRaCIS.Core.Application.Contracts.IReadingImageTaskService,Microsoft.Extensions.Configuration.IConfiguration)">
|
<member name="M:IRaCIS.Api.Controllers.ExtraController.Login(IRaCIS.Application.Contracts.UserLoginDTO,ZiggyCreatures.Caching.Fusion.IFusionCache,IRaCIS.Core.Application.Service.IUserService,IRaCIS.Core.Application.Auth.ITokenService,IRaCIS.Core.Application.Contracts.IReadingImageTaskService,Microsoft.Extensions.Options.IOptionsMonitor{IRaCIS.Core.Domain.Share.ServiceVerifyConfigOption},Microsoft.Extensions.Options.IOptionsMonitor{IRaCIS.Core.Domain.Share.SystemEmailSendConfig},IRaCIS.Core.Application.Service.IMailVerificationService)">
|
||||||
<summary> 系统用户登录接口[New] </summary>
|
<summary> 系统用户登录接口[New] </summary>
|
||||||
</member>
|
</member>
|
||||||
<member name="M:IRaCIS.Core.API.Controllers.Special.FinancialChangeController.AddOrUpdateTrialInspection(IRaCIS.Core.Application.Service.Inspection.DTO.DataInspectionDto{IRaCIS.Application.Contracts.TrialCommand})">
|
<member name="M:IRaCIS.Core.API.Controllers.Special.FinancialChangeController.AddOrUpdateTrialInspection(IRaCIS.Core.Application.Service.Inspection.DTO.DataInspectionDto{IRaCIS.Application.Contracts.TrialCommand})">
|
||||||
@@ -41,7 +41,7 @@
|
|||||||
<param name="param"></param>
|
<param name="param"></param>
|
||||||
<returns>新记录Id</returns>
|
<returns>新记录Id</returns>
|
||||||
</member>
|
</member>
|
||||||
<member name="M:IRaCIS.Core.API.Controllers.Special.FinancialChangeController.WorkLoadAddOrUpdate(IRaCIS.Application.Services.IDoctorWorkloadService,IRaCIS.Application.Contracts.WorkloadCommand)">
|
<member name="M:IRaCIS.Core.API.Controllers.Special.FinancialChangeController.WorkLoadAddOrUpdate(IRaCIS.Core.Application.Service.IDoctorWorkloadService,IRaCIS.Application.Contracts.WorkloadCommand)">
|
||||||
<summary>
|
<summary>
|
||||||
添加或更新工作量[AUTH]
|
添加或更新工作量[AUTH]
|
||||||
</summary>
|
</summary>
|
||||||
@@ -167,6 +167,13 @@
|
|||||||
<param name="opt"></param>
|
<param name="opt"></param>
|
||||||
<returns></returns>
|
<returns></returns>
|
||||||
</member>
|
</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})">
|
<member name="M:IRaCIS.Core.API.Controllers.InspectionController.SubmitClinicalFormAndSign(IRaCIS.Core.Application.Service.Inspection.DTO.DataInspectionDto{IRaCIS.Core.Application.Service.Reading.Dto.SubmitClinicalFormInDto})">
|
||||||
<summary>
|
<summary>
|
||||||
提交结构化录入并签名
|
提交结构化录入并签名
|
||||||
@@ -271,24 +278,35 @@
|
|||||||
<member name="M:IRaCIS.Core.API.Controllers.UploadBaseController.DicomFileUploadAsync(System.Func{System.String,System.IO.Stream,System.Int32,System.Threading.Tasks.Task},System.String)">
|
<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>
|
<summary> 流式上传 Dicom上传 </summary>
|
||||||
</member>
|
</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},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})">
|
<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})">
|
||||||
<summary>Dicom 归档</summary>
|
<summary>Dicom 归档</summary>
|
||||||
</member>
|
</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})">
|
<member name="M:IRaCIS.Core.API.Controllers.StudyController.PreArchiveStudy(IRaCIS.Core.Application.Contracts.PreArchiveStudyCommand,IRaCIS.Core.Application.Contracts.IStudyService,IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.StudyMonitor})">
|
||||||
|
<summary>
|
||||||
|
非dicom 上传预上传接口
|
||||||
|
</summary>
|
||||||
|
<param name="preArchiveStudyCommand"></param>
|
||||||
|
<param name="_studyService"></param>
|
||||||
|
<param name="_studyMonitorRepository"></param>
|
||||||
|
<returns></returns>
|
||||||
|
</member>
|
||||||
|
<member name="M:IRaCIS.Core.API.Controllers.StudyController.UploadNoneDicomFile(IRaCIS.Core.API.Controllers.StudyController.UploadNoneDicomFileCommand,IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.NoneDicomStudy},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.StudyMonitor},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.NoneDicomStudyFile})">
|
||||||
<summary>
|
<summary>
|
||||||
上传非Dicom 文件 支持压缩包 多文件上传
|
上传非Dicom 文件 支持压缩包 多文件上传
|
||||||
</summary>
|
</summary>
|
||||||
<param name="incommand"></param>
|
<param name="incommand"></param>
|
||||||
<param name="_noneDicomStudyRepository"></param>
|
<param name="_noneDicomStudyRepository"></param>
|
||||||
<param name="_studyMonitorRepository"></param>
|
<param name="_studyMonitorRepository"></param>
|
||||||
|
<param name="_noneDicomStudyFileRepository"></param>
|
||||||
<returns></returns>
|
<returns></returns>
|
||||||
</member>
|
</member>
|
||||||
<member name="M:IRaCIS.Core.API.Controllers.StudyController.UploadVisitCheckExcel(System.Guid,IRaCIS.Core.Application.Helper.IOSSService)">
|
<member name="M:IRaCIS.Core.API.Controllers.StudyController.UploadVisitCheckExcel(System.Guid,IRaCIS.Core.Application.Helper.IOSSService,IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.InspectionFile})">
|
||||||
<summary>
|
<summary>
|
||||||
一致性核查 excel上传 支持三种格式
|
一致性核查 excel上传 支持三种格式
|
||||||
</summary>
|
</summary>
|
||||||
<param name="trialId"></param>
|
<param name="trialId"></param>
|
||||||
<param name="oSSService"></param>
|
<param name="oSSService"></param>
|
||||||
|
<param name="_inspectionFileRepository"></param>
|
||||||
<returns></returns>
|
<returns></returns>
|
||||||
<exception cref="T:IRaCIS.Core.Infrastructure.BusinessValidationFailedException"></exception>
|
<exception cref="T:IRaCIS.Core.Infrastructure.BusinessValidationFailedException"></exception>
|
||||||
</member>
|
</member>
|
||||||
@@ -319,38 +337,6 @@
|
|||||||
<param name="_attachmentrepository"></param>
|
<param name="_attachmentrepository"></param>
|
||||||
<returns></returns>
|
<returns></returns>
|
||||||
</member>
|
</member>
|
||||||
<member name="M:IRaCIS.Core.API.Controllers.FileController.DownloadAttachment(System.Guid[])">
|
|
||||||
<summary>
|
|
||||||
下载多个医生的所有附件
|
|
||||||
</summary>
|
|
||||||
<param name="doctorIds"></param>
|
|
||||||
<returns></returns>
|
|
||||||
</member>
|
|
||||||
<member name="M:IRaCIS.Core.API.Controllers.FileController.DownloadAttachmentById(System.Guid,System.Guid[])">
|
|
||||||
<summary>
|
|
||||||
下载指定医生的指定附件
|
|
||||||
</summary>
|
|
||||||
<param name="doctorId">医生Id</param>
|
|
||||||
<param name="attachmentIds">要下载的附件Id</param>
|
|
||||||
<returns></returns>
|
|
||||||
</member>
|
|
||||||
<member name="M:IRaCIS.Core.API.Controllers.FileController.DownloadOfficialResume(System.Int32,System.Guid[])">
|
|
||||||
<summary>
|
|
||||||
下载医生官方简历 首页 区分 中文和英文
|
|
||||||
</summary>
|
|
||||||
<param name="language"></param>
|
|
||||||
<param name="doctorIds"></param>
|
|
||||||
<returns></returns>
|
|
||||||
</member>
|
|
||||||
<member name="M:IRaCIS.Core.API.Controllers.FileController.DownloadResume(System.Int32,System.Guid,System.Guid[])">
|
|
||||||
<summary>
|
|
||||||
入组 项目下载简历
|
|
||||||
</summary>
|
|
||||||
<param name="language"></param>
|
|
||||||
<param name="trialId"></param>
|
|
||||||
<param name="doctorIdArray"></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})">
|
<member name="M:IRaCIS.Core.API.Controllers.UploadDownLoadController.DownloadCommonFile(System.String,IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.CommonDocument})">
|
||||||
<summary> 通用文件下载 </summary>
|
<summary> 通用文件下载 </summary>
|
||||||
</member>
|
</member>
|
||||||
@@ -376,6 +362,11 @@
|
|||||||
序列化,反序列化的时候,处理时间 时区转换
|
序列化,反序列化的时候,处理时间 时区转换
|
||||||
</summary>
|
</summary>
|
||||||
</member>
|
</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)">
|
<member name="M:IRaCIS.Core.API.NullToEmptyStringResolver.CreateProperties(System.Type,Newtonsoft.Json.MemberSerialization)">
|
||||||
<summary>
|
<summary>
|
||||||
创建属性
|
创建属性
|
||||||
@@ -384,18 +375,6 @@
|
|||||||
<param name="memberSerialization">序列化成员</param>
|
<param name="memberSerialization">序列化成员</param>
|
||||||
<returns></returns>
|
<returns></returns>
|
||||||
</member>
|
</member>
|
||||||
<member name="M:IRaCIS.WX.CoreApi.Auth.AuthMiddleware.Invoke(Microsoft.AspNetCore.Http.HttpContext)">
|
|
||||||
<summary>
|
|
||||||
为了前端 一段时间无操作,需要重新登陆
|
|
||||||
</summary>
|
|
||||||
<param name="httpContext"></param>
|
|
||||||
<returns></returns>
|
|
||||||
</member>
|
|
||||||
<member name="T:TimeZoneAdjustmentMiddleware">
|
|
||||||
<summary>
|
|
||||||
废弃,没用
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="T:ZhaoXi._001.NET5Demo.Practice.WebApi.Utility.Jwt.CustomHSJWTService">
|
<member name="T:ZhaoXi._001.NET5Demo.Practice.WebApi.Utility.Jwt.CustomHSJWTService">
|
||||||
<summary>
|
<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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
+128
-30
@@ -4,8 +4,6 @@ using Microsoft.AspNetCore.Hosting;
|
|||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
using MediatR;
|
|
||||||
using IRaCIS.Core.Application.MediatR.Handlers;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MassTransit;
|
using MassTransit;
|
||||||
using MassTransit.NewIdProviders;
|
using MassTransit.NewIdProviders;
|
||||||
@@ -22,13 +20,24 @@ using Microsoft.AspNetCore.SignalR;
|
|||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using IRaCIS.Core.Application.Filter;
|
using IRaCIS.Core.Application.Filter;
|
||||||
using Microsoft.AspNetCore.HttpOverrides;
|
using Microsoft.AspNetCore.HttpOverrides;
|
||||||
using IRaCIS.Application.Services.BackGroundJob;
|
using IRaCIS.Core.Application.Service.BackGroundJob;
|
||||||
using LogDashboard;
|
using LogDashboard;
|
||||||
using OfficeOpenXml.Utils;
|
|
||||||
using FellowOakDicom.Network;
|
using FellowOakDicom.Network;
|
||||||
using IRaCIS.Core.Application.Service.ImageAndDoc;
|
using IRaCIS.Core.Application.Service.ImageAndDoc;
|
||||||
|
using IP2Region.Net.Abstractions;
|
||||||
|
using IP2Region.Net.XDB;
|
||||||
|
using IRaCIS.Core.Application.BusinessFilter;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using IRaCIS.Core.Infrastructure.Extention;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Microsoft.AspNetCore.Diagnostics;
|
||||||
|
using IRaCIS.Core.Application.MassTransit.Command;
|
||||||
|
using IRaCIS.Core.Application.MassTransit.Consumer;
|
||||||
|
using DocumentFormat.OpenXml.InkML;
|
||||||
|
using IRaCIS.Core.Domain;
|
||||||
|
|
||||||
|
AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);
|
||||||
|
AppContext.SetSwitch("Npgsql.DisableDateTimeInfinityConversions", true);
|
||||||
#region 获取环境变量
|
#region 获取环境变量
|
||||||
//以配置文件为准,否则 从url中取环境值(服务以命令行传递参数启动,配置文件配置了就不需要传递环境参数)
|
//以配置文件为准,否则 从url中取环境值(服务以命令行传递参数启动,配置文件配置了就不需要传递环境参数)
|
||||||
var config = new ConfigurationBuilder()
|
var config = new ConfigurationBuilder()
|
||||||
@@ -75,8 +84,8 @@ if (urlsIndex > -1)
|
|||||||
|
|
||||||
NewId.SetProcessIdProvider(new CurrentProcessIdProvider());
|
NewId.SetProcessIdProvider(new CurrentProcessIdProvider());
|
||||||
|
|
||||||
builder.Configuration.AddJsonFile("appsettings.json", false, true)
|
builder.Configuration.AddJsonFile(ConfigMapFileProvider.FromRelativePath(""), "appsettings.json", false, true)
|
||||||
.AddJsonFile($"appsettings.{enviromentName}.json", false, true);
|
.AddJsonFile(ConfigMapFileProvider.FromRelativePath(""), $"appsettings.{enviromentName}.json", false, true);
|
||||||
|
|
||||||
builder.Host
|
builder.Host
|
||||||
.UseServiceProviderFactory(new AutofacServiceProviderFactory())
|
.UseServiceProviderFactory(new AutofacServiceProviderFactory())
|
||||||
@@ -103,11 +112,9 @@ builder.Services.AddControllers(options =>
|
|||||||
options.Filters.Add<ModelActionFilter>();
|
options.Filters.Add<ModelActionFilter>();
|
||||||
options.Filters.Add<ProjectExceptionFilter>();
|
options.Filters.Add<ProjectExceptionFilter>();
|
||||||
options.Filters.Add<UnitOfWorkFilter>();
|
options.Filters.Add<UnitOfWorkFilter>();
|
||||||
|
//options.Filters.Add<EncreptApiResultFilter>(10);
|
||||||
|
options.Filters.Add<LimitUserRequestAuthorization>();
|
||||||
|
|
||||||
if (_configuration.GetSection("BasicSystemConfig").GetValue<bool>("OpenLoginLimit"))
|
|
||||||
{
|
|
||||||
options.Filters.Add<LimitUserRequestAuthorization>();
|
|
||||||
}
|
|
||||||
|
|
||||||
})
|
})
|
||||||
.AddNewtonsoftJsonSetup(builder.Services); // NewtonsoftJson 序列化 处理
|
.AddNewtonsoftJsonSetup(builder.Services); // NewtonsoftJson 序列化 处理
|
||||||
@@ -116,6 +123,10 @@ builder.Services.AddOptions().Configure<SystemEmailSendConfig>(_configuration.Ge
|
|||||||
builder.Services.AddOptions().Configure<ServiceVerifyConfigOption>(_configuration.GetSection("BasicSystemConfig"));
|
builder.Services.AddOptions().Configure<ServiceVerifyConfigOption>(_configuration.GetSection("BasicSystemConfig"));
|
||||||
builder.Services.AddOptions().Configure<AliyunOSSOptions>(_configuration.GetSection("AliyunOSS"));
|
builder.Services.AddOptions().Configure<AliyunOSSOptions>(_configuration.GetSection("AliyunOSS"));
|
||||||
builder.Services.AddOptions().Configure<ObjectStoreServiceOptions>(_configuration.GetSection("ObjectStoreService"));
|
builder.Services.AddOptions().Configure<ObjectStoreServiceOptions>(_configuration.GetSection("ObjectStoreService"));
|
||||||
|
builder.Services.AddOptions().Configure<IRCEncreptOption>(_configuration.GetSection("EncrypteResponseConfig"));
|
||||||
|
builder.Services.AddOptions().Configure<SystemPacsConfig>(_configuration.GetSection("SystemPacsConfig"));
|
||||||
|
|
||||||
|
builder.Services.Configure<IRaCISBasicConfigOption>(_configuration.GetSection("IRaCISBasicConfig"));
|
||||||
|
|
||||||
|
|
||||||
//动态WebApi + UnifiedApiResultFilter 省掉控制器代码
|
//动态WebApi + UnifiedApiResultFilter 省掉控制器代码
|
||||||
@@ -123,7 +134,7 @@ builder.Services.AddDynamicWebApiSetup();
|
|||||||
//AutoMapper
|
//AutoMapper
|
||||||
builder.Services.AddAutoMapperSetup();
|
builder.Services.AddAutoMapperSetup();
|
||||||
//EF ORM QueryWithNoLock
|
//EF ORM QueryWithNoLock
|
||||||
builder.Services.AddEFSetup(_configuration);
|
builder.Services.AddEFSetup(_configuration, enviromentName);
|
||||||
//Http 响应压缩
|
//Http 响应压缩
|
||||||
builder.Services.AddResponseCompressionSetup();
|
builder.Services.AddResponseCompressionSetup();
|
||||||
//Swagger Api 文档
|
//Swagger Api 文档
|
||||||
@@ -132,9 +143,45 @@ builder.Services.AddSwaggerSetup();
|
|||||||
builder.Services.AddJWTAuthSetup(_configuration);
|
builder.Services.AddJWTAuthSetup(_configuration);
|
||||||
|
|
||||||
// MediatR 进程内消息 事件解耦 从程序集中 注册命令和handler对应关系
|
// MediatR 进程内消息 事件解耦 从程序集中 注册命令和handler对应关系
|
||||||
builder.Services.AddMediatR(cfg => cfg.RegisterServicesFromAssemblyContaining<ConsistencyVerificationHandler>());
|
//builder.Services.AddMediatR(cfg => cfg.RegisterServicesFromAssemblyContaining<ConsistencyVerificationHandler>());
|
||||||
|
|
||||||
|
#region MassTransit
|
||||||
|
//masstransit组件 也支持MediatR 中介者模式,但是支持分布式,考虑后续,所以在次替代MediatR
|
||||||
|
//参考链接:https://masstransit.io/documentation/concepts/mediator#scoped-mediator
|
||||||
|
builder.Services.AddMediator(cfg =>
|
||||||
|
{
|
||||||
|
cfg.AddConsumer<ConsistencyCheckConsumer>();
|
||||||
|
cfg.AddConsumer<AddSubjectTriggerConsumer>();
|
||||||
|
cfg.AddConsumer<AddSubjectTriggerConsumer2>();
|
||||||
|
//cfg.ConfigureMediator((context, cfg) => cfg.UseHttpContextScopeFilter(context));
|
||||||
|
});
|
||||||
|
|
||||||
|
//添加 MassTransit 和 InMemory 传输
|
||||||
|
builder.Services.AddMassTransit(cfg =>
|
||||||
|
{
|
||||||
|
// 注册消费者
|
||||||
|
cfg.AddConsumer<AddSubjectTriggerConsumer>(); // 替换为你的消费者类
|
||||||
|
cfg.AddConsumer<AddSubjectTriggerConsumer2>();
|
||||||
|
|
||||||
|
// 使用 InMemory 作为消息传递机制
|
||||||
|
cfg.UsingInMemory((context, cfg) =>
|
||||||
|
{
|
||||||
|
// 这里可以进行额外的配置
|
||||||
|
cfg.ConfigureEndpoints(context); // 自动配置所有消费者的端点
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
#region FusionCache
|
||||||
|
|
||||||
|
builder.Services.AddFusionCache();
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
// EasyCaching 缓存
|
// EasyCaching 缓存
|
||||||
builder.Services.AddEasyCachingSetup(_configuration);
|
//builder.Services.AddEasyCachingSetup(_configuration);
|
||||||
|
|
||||||
// hangfire 定时任务框架 有界面,更友好~
|
// hangfire 定时任务框架 有界面,更友好~
|
||||||
builder.Services.AddhangfireSetup(_configuration);
|
builder.Services.AddhangfireSetup(_configuration);
|
||||||
@@ -160,7 +207,10 @@ builder.Services.AddSignalR();
|
|||||||
builder.Services.AddSingleton<IUserIdProvider, IRaCISUserIdProvider>();
|
builder.Services.AddSingleton<IUserIdProvider, IRaCISUserIdProvider>();
|
||||||
|
|
||||||
|
|
||||||
|
builder.Services.AddSingleton<ISearcher>(new Searcher(CachePolicy.Content, Path.Combine(AppContext.BaseDirectory, StaticData.Folder.Resources, "ip2region.xdb")));
|
||||||
|
|
||||||
|
//builder.Services.AddExceptionHandler<GlobalExceptionHandler>();
|
||||||
|
//builder.Services.AddProblemDetails();
|
||||||
|
|
||||||
|
|
||||||
#region 历史废弃配置
|
#region 历史废弃配置
|
||||||
@@ -185,9 +235,60 @@ var env = app.Environment;
|
|||||||
|
|
||||||
#region 配置中间件
|
#region 配置中间件
|
||||||
|
|
||||||
|
app.UseMiddleware<EncryptionRequestMiddleware>();
|
||||||
|
|
||||||
// Configure the HTTP request pipeline.
|
// Configure the HTTP request pipeline.
|
||||||
|
|
||||||
|
#region 异常处理 全局业务异常已统一处理了,非业务错误会来到这里 400 -500状态码
|
||||||
|
|
||||||
|
|
||||||
|
//app.UseStatusCodePagesWithReExecute("/Error/{0}");
|
||||||
|
|
||||||
|
app.UseStatusCodePages(async context =>
|
||||||
|
{
|
||||||
|
var code = context.HttpContext.Response.StatusCode;
|
||||||
|
context.HttpContext.Response.ContentType = "application/json";
|
||||||
|
if (code < 500)
|
||||||
|
{
|
||||||
|
await context.HttpContext.Response.WriteAsync(JsonConvert.SerializeObject(ResponseOutput.NotOk($"Client error, actual request error status code({code})")));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//ResultFilter 里面的异常并不会到这里
|
||||||
|
await context.HttpContext.Response.WriteAsync(JsonConvert.SerializeObject((ResponseOutput.NotOk($"Server error , actual request error status code({code})"))));
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
//app.UseExceptionHandler(o => { });
|
||||||
|
|
||||||
|
//这里没生效,原因未知,官方文档也是这种写法,也用了GlobalExceptionHandler 尝试,还是不行,怀疑框架bug
|
||||||
|
//app.UseExceptionHandler(configure =>
|
||||||
|
//{
|
||||||
|
// configure.Run(async context =>
|
||||||
|
// {
|
||||||
|
// var exceptionHandlerPathFeature = context.Features.Get<IExceptionHandlerPathFeature>();
|
||||||
|
//var ex = exceptionHandlerPathFeature?.Error;
|
||||||
|
//context.Response.ContentType = "application/json";
|
||||||
|
|
||||||
|
//if (ex != null)
|
||||||
|
//{
|
||||||
|
// var errorInfo = $"Exception: {ex.Message}[{ex.StackTrace}]" + (ex.InnerException != null ? $" InnerException: {ex.InnerException.Message}[{ex.InnerException.StackTrace}]" : "");
|
||||||
|
|
||||||
|
// await context.Response.WriteAsync(JsonConvert.SerializeObject(ResponseOutput.NotOk($"{ex?.Message}")));
|
||||||
|
|
||||||
|
// Log.Logger.Error(errorInfo);
|
||||||
|
|
||||||
|
|
||||||
|
//}
|
||||||
|
|
||||||
|
// });
|
||||||
|
//});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
//本地化
|
//本地化
|
||||||
app.UseLocalization();
|
app.UseLocalization();
|
||||||
|
|
||||||
@@ -196,12 +297,11 @@ app.UseForwardedHeaders();
|
|||||||
//响应压缩
|
//响应压缩
|
||||||
app.UseResponseCompression();
|
app.UseResponseCompression();
|
||||||
|
|
||||||
//app.UseCors(t => t.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader());
|
|
||||||
|
|
||||||
//不需要 token 访问的静态文件 wwwroot css, JavaScript, and images don't require authentication.
|
//不需要 token 访问的静态文件 wwwroot css, JavaScript, and images don't require authentication.
|
||||||
app.UseStaticFiles();
|
app.UseStaticFiles();
|
||||||
|
|
||||||
app.UseMiddleware<MultiDiskStaticFilesMiddleware>();
|
|
||||||
|
|
||||||
//LogDashboard
|
//LogDashboard
|
||||||
app.UseLogDashboard("/LogDashboard");
|
app.UseLogDashboard("/LogDashboard");
|
||||||
@@ -209,22 +309,24 @@ app.UseLogDashboard("/LogDashboard");
|
|||||||
//hangfire
|
//hangfire
|
||||||
app.UseHangfireConfig(env);
|
app.UseHangfireConfig(env);
|
||||||
|
|
||||||
|
#region 暂时废弃
|
||||||
|
|
||||||
|
//app.UseMiddleware<MultiDiskStaticFilesMiddleware>();
|
||||||
////限流 中间件
|
////限流 中间件
|
||||||
//app.UseIpRateLimiting();
|
//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);
|
SwaggerSetup.Configure(app, env);
|
||||||
|
|
||||||
@@ -294,10 +396,6 @@ try
|
|||||||
//Log.Logger.Warning($"ContentRootPath——xx:{Path.GetDirectoryName(Path.GetDirectoryName(env.ContentRootPath))}");
|
//Log.Logger.Warning($"ContentRootPath——xx:{Path.GetDirectoryName(Path.GetDirectoryName(env.ContentRootPath))}");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var server = DicomServerFactory.Create<CStoreSCPService>(11112,userState: app.Services);
|
|
||||||
|
|
||||||
app.Run();
|
app.Run();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,13 +24,21 @@
|
|||||||
},
|
},
|
||||||
"applicationUrl": "http://localhost:6100"
|
"applicationUrl": "http://localhost:6100"
|
||||||
},
|
},
|
||||||
|
"IRaCIS.Test_IRC_PGSQL": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"launchBrowser": true,
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Test_IRC_PGSQL"
|
||||||
|
},
|
||||||
|
"applicationUrl": "http://localhost:6100"
|
||||||
|
},
|
||||||
"IRaCIS.Event_IRC": {
|
"IRaCIS.Event_IRC": {
|
||||||
"commandName": "Project",
|
"commandName": "Project",
|
||||||
"launchBrowser": true,
|
"launchBrowser": true,
|
||||||
"environmentVariables": {
|
"environmentVariables": {
|
||||||
"ASPNETCORE_ENVIRONMENT": "Event_IRC"
|
"ASPNETCORE_ENVIRONMENT": "Event_IRC"
|
||||||
},
|
},
|
||||||
"applicationUrl": "http://localhost:6101"
|
"applicationUrl": "http://localhost:6100"
|
||||||
},
|
},
|
||||||
"Docker": {
|
"Docker": {
|
||||||
"commandName": "Docker",
|
"commandName": "Docker",
|
||||||
@@ -54,11 +62,11 @@
|
|||||||
},
|
},
|
||||||
"applicationUrl": "http://localhost:6100"
|
"applicationUrl": "http://localhost:6100"
|
||||||
},
|
},
|
||||||
"IRaCIS.US_IRC": {
|
"IRaCIS.US_Uat_IRC": {
|
||||||
"commandName": "Project",
|
"commandName": "Project",
|
||||||
"launchBrowser": true,
|
"launchBrowser": true,
|
||||||
"environmentVariables": {
|
"environmentVariables": {
|
||||||
"ASPNETCORE_ENVIRONMENT": "US_IRC"
|
"ASPNETCORE_ENVIRONMENT": "US_Uat_IRC"
|
||||||
},
|
},
|
||||||
"applicationUrl": "http://localhost:6100"
|
"applicationUrl": "http://localhost:6100"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using EasyCaching.Core;
|
using IRaCIS.Core.Domain.Share;
|
||||||
using IRaCIS.Core.Domain.Share;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Cors;
|
using Microsoft.AspNetCore.Cors;
|
||||||
using Microsoft.AspNetCore.SignalR;
|
using Microsoft.AspNetCore.SignalR;
|
||||||
|
|||||||
@@ -1,66 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
using Hangfire;
|
using Hangfire;
|
||||||
using Hangfire.Dashboard;
|
using Hangfire.Dashboard;
|
||||||
using Hangfire.Dashboard.BasicAuthorization;
|
using Hangfire.Dashboard.BasicAuthorization;
|
||||||
using IRaCIS.Application.Services.BackGroundJob;
|
using IRaCIS.Core.Application.Service.BackGroundJob;
|
||||||
using IRaCIS.Core.API.Filter;
|
using IRaCIS.Core.API.Filter;
|
||||||
using IRaCIS.Core.Application.Helper;
|
using IRaCIS.Core.Application.Helper;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Microsoft.AspNetCore.Builder;
|
using IRaCIS.Core.Domain.Share;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Localization;
|
using Microsoft.AspNetCore.Localization;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
@@ -12,8 +13,9 @@ namespace IRaCIS.Core.API
|
|||||||
{
|
{
|
||||||
var supportedCultures = new List<CultureInfo>
|
var supportedCultures = new List<CultureInfo>
|
||||||
{
|
{
|
||||||
new CultureInfo("en-US"),
|
|
||||||
new CultureInfo("zh-CN")
|
new CultureInfo(StaticData.CultureInfo.en_US),
|
||||||
|
new CultureInfo(StaticData.CultureInfo.zh_CN)
|
||||||
};
|
};
|
||||||
|
|
||||||
var options = new RequestLocalizationOptions
|
var options = new RequestLocalizationOptions
|
||||||
|
|||||||
@@ -20,18 +20,20 @@ namespace IRaCIS.Core.API
|
|||||||
// Set all the common properties available for every request
|
// Set all the common properties available for every request
|
||||||
diagnosticContext.Set("Host", request.Host);
|
diagnosticContext.Set("Host", request.Host);
|
||||||
|
|
||||||
|
diagnosticContext.Set("Protocol", request.Protocol);
|
||||||
|
diagnosticContext.Set("Scheme", request.Scheme);
|
||||||
|
|
||||||
|
#region old 未用
|
||||||
//这种获取的Ip不准 配置服务才行
|
//这种获取的Ip不准 配置服务才行
|
||||||
//diagnosticContext.Set("RequestIP", httpContext.Connection.RemoteIpAddress.ToString());
|
//diagnosticContext.Set("RequestIP", httpContext.Connection.RemoteIpAddress.ToString());
|
||||||
|
|
||||||
//这种方式可以,但是serilog提供了 就不用了
|
//这种方式可以,但是serilog提供了 就不用了
|
||||||
//diagnosticContext.Set("TestIP", httpContext.GetUserIp());
|
//diagnosticContext.Set("TestIP", httpContext.GetUserIp());
|
||||||
|
|
||||||
diagnosticContext.Set("Protocol", request.Protocol);
|
|
||||||
diagnosticContext.Set("Scheme", request.Scheme);
|
|
||||||
|
|
||||||
//这种方式不行 读取的body为空字符串 必须在中间件中读取
|
//这种方式不行 读取的body为空字符串 必须在中间件中读取
|
||||||
//diagnosticContext.Set("RequestBody", await ReadRequestBody(httpContext.Request));
|
//diagnosticContext.Set("RequestBody", await ReadRequestBody(httpContext.Request));
|
||||||
//diagnosticContext.Set("RequestBody", RequestPayload);
|
//diagnosticContext.Set("RequestBody", RequestPayload);
|
||||||
|
#endregion
|
||||||
|
|
||||||
// Only set it if available. You're not sending sensitive data in a querystring right?!
|
// Only set it if available. You're not sending sensitive data in a querystring right?!
|
||||||
if (request.QueryString.HasValue)
|
if (request.QueryString.HasValue)
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using AutoMapper.EquivalencyExpression;
|
using AutoMapper.EquivalencyExpression;
|
||||||
using IRaCIS.Core.Application.Service;
|
using IRaCIS.Core.Application.Service;
|
||||||
|
using IRaCIS.Core.Domain.Models;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
namespace IRaCIS.Core.API
|
namespace IRaCIS.Core.API
|
||||||
@@ -14,11 +15,16 @@ namespace IRaCIS.Core.API
|
|||||||
//AutoMapper.Collection.EntityFrameworkCore
|
//AutoMapper.Collection.EntityFrameworkCore
|
||||||
automapper.AddCollectionMappers();
|
automapper.AddCollectionMappers();
|
||||||
|
|
||||||
|
|
||||||
|
// 全局忽略 DomainEvents 属性
|
||||||
|
automapper.AddGlobalIgnore(nameof(Entity.DomainEvents));
|
||||||
|
automapper.AddGlobalIgnore(nameof(Entity.DomainCommands));
|
||||||
|
|
||||||
#region 会使 IncludeMembers 失效 不能全局使用
|
#region 会使 IncludeMembers 失效 不能全局使用
|
||||||
//mapping an EntityFramework Core DbContext-object.
|
//mapping an EntityFramework Core DbContext-object.
|
||||||
//automapper.UseEntityFrameworkCoreModel<IRaCISDBContext>(services);
|
//automapper.UseEntityFrameworkCoreModel<IRaCISDBContext>(services);
|
||||||
|
|
||||||
|
|
||||||
//automapper.ForAllMaps((a, b) => b.ForAllMembers(opt => opt.Condition((src, dest, srcMember, desMenber) =>
|
//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
|
// //// Can test When Guid? -> Guid if source is null will change to Guid.Empty
|
||||||
@@ -27,7 +33,7 @@ namespace IRaCIS.Core.API
|
|||||||
// // not want to map a null Guid? value to db Guid value
|
// // not want to map a null Guid? value to db Guid value
|
||||||
//})));
|
//})));
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
}, typeof(QCConfig).Assembly);
|
}, typeof(QCConfig).Assembly);
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using Autofac;
|
using Autofac;
|
||||||
using Autofac.Extras.DynamicProxy;
|
|
||||||
using IRaCIS.Core.Application;
|
using IRaCIS.Core.Application;
|
||||||
using IRaCIS.Core.Application.BackGroundJob;
|
using IRaCIS.Core.Application.BackGroundJob;
|
||||||
using IRaCIS.Core.Infra.EFCore;
|
using IRaCIS.Core.Infra.EFCore;
|
||||||
@@ -10,8 +9,7 @@ using System.Linq;
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using IRaCIS.Core.Domain.Models;
|
using IRaCIS.Core.Domain.Models;
|
||||||
using IRaCIS.Core.Domain.Share;
|
using IRaCIS.Core.Domain.Share;
|
||||||
using MediatR;
|
using IRaCIS.Core.Application.Service;
|
||||||
using IRaCIS.Application.Services;
|
|
||||||
using IRaCIS.Application.Interfaces;
|
using IRaCIS.Application.Interfaces;
|
||||||
using AutoMapper;
|
using AutoMapper;
|
||||||
|
|
||||||
@@ -51,12 +49,12 @@ namespace IRaCIS.Core.API
|
|||||||
|
|
||||||
Assembly application = Assembly.LoadFrom(AppDomain.CurrentDomain.BaseDirectory + "IRaCIS.Core.Application.dll");
|
Assembly application = Assembly.LoadFrom(AppDomain.CurrentDomain.BaseDirectory + "IRaCIS.Core.Application.dll");
|
||||||
containerBuilder.RegisterAssemblyTypes(application).Where(t => t.FullName.Contains("Service"))
|
containerBuilder.RegisterAssemblyTypes(application).Where(t => t.FullName.Contains("Service"))
|
||||||
.PropertiesAutowired().AsImplementedInterfaces().EnableClassInterceptors();
|
.PropertiesAutowired().AsImplementedInterfaces();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
containerBuilder.RegisterType<HttpContextAccessor>().As<IHttpContextAccessor>().SingleInstance();
|
//containerBuilder.RegisterType<HttpContextAccessor>().As<IHttpContextAccessor>().SingleInstance();
|
||||||
containerBuilder.RegisterType<UserInfo>().As<IUserInfo>().InstancePerLifetimeScope();
|
//containerBuilder.RegisterType<UserInfo>().As<IUserInfo>().InstancePerLifetimeScope();
|
||||||
|
|
||||||
|
|
||||||
//注册hangfire任务 依赖注入
|
//注册hangfire任务 依赖注入
|
||||||
|
|||||||
@@ -1,31 +1,68 @@
|
|||||||
using Hangfire.SqlServer;
|
using EntityFramework.Exceptions.SqlServer;
|
||||||
|
using Hangfire.SqlServer;
|
||||||
using IRaCIS.Core.Application.Triggers;
|
using IRaCIS.Core.Application.Triggers;
|
||||||
|
using IRaCIS.Core.Domain.Share;
|
||||||
using IRaCIS.Core.Infra.EFCore;
|
using IRaCIS.Core.Infra.EFCore;
|
||||||
|
using IRaCIS.Core.Infra.EFCore.Interceptor;
|
||||||
using Medallion.Threading;
|
using Medallion.Threading;
|
||||||
using Medallion.Threading.SqlServer;
|
using Medallion.Threading.SqlServer;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Diagnostics;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
using StackExchange.Redis;
|
using StackExchange.Redis;
|
||||||
|
|
||||||
namespace IRaCIS.Core.API
|
namespace IRaCIS.Core.API
|
||||||
{
|
{
|
||||||
public static class EFSetup
|
public static class EFSetup
|
||||||
{
|
{
|
||||||
public static void AddEFSetup( this IServiceCollection services, IConfiguration configuration)
|
public static void AddEFSetup( this IServiceCollection services, IConfiguration configuration,string envName)
|
||||||
{
|
{
|
||||||
//services.AddScoped<DbContext, IRaCISDBContext>();
|
|
||||||
|
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:
|
||||||
|
|
||||||
//这个注入没有成功--注入是没问题的,构造函数也只是支持参数就好,错在注入的地方不能写DbContext
|
//这个注入没有成功--注入是没问题的,构造函数也只是支持参数就好,错在注入的地方不能写DbContext
|
||||||
//Web程序中通过重用池中DbContext实例可提高高并发场景下的吞吐量, 这在概念上类似于ADO.NET Provider原生的连接池操作方式,具有节省DbContext实例化成本的优点
|
//Web程序中通过重用池中DbContext实例可提高高并发场景下的吞吐量, 这在概念上类似于ADO.NET Provider原生的连接池操作方式,具有节省DbContext实例化成本的优点
|
||||||
services.AddDbContext<IRaCISDBContext>(options =>
|
services.AddDbContext<IRaCISDBContext>((sp, options) =>
|
||||||
{
|
{
|
||||||
options.UseSqlServer(configuration.GetSection("ConnectionStrings:RemoteNew").Value,
|
|
||||||
contextOptionsBuilder => contextOptionsBuilder.EnableRetryOnFailure());
|
// 在控制台
|
||||||
|
//public static readonly ILoggerFactory MyLoggerFactory = LoggerFactory.Create(builder => { builder.AddConsole(); });
|
||||||
|
var logFactory = LoggerFactory.Create(builder => { builder.AddDebug(); });
|
||||||
|
|
||||||
|
var dbType = configuration.GetSection("ConnectionStrings:Db_Type").Value ;
|
||||||
|
if (!string.IsNullOrWhiteSpace(dbType) && dbType == "pgsql")
|
||||||
|
{
|
||||||
|
options.UseNpgsql(configuration.GetSection("ConnectionStrings:RemoteNew").Value, contextOptionsBuilder => contextOptionsBuilder.EnableRetryOnFailure());
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
options.UseSqlServer(configuration.GetSection("ConnectionStrings:RemoteNew").Value, contextOptionsBuilder => contextOptionsBuilder.EnableRetryOnFailure());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//迁移的时候,不生成外键
|
||||||
|
options.ReplaceService<IMigrationsSqlGenerator, NoForeignKeyMigrationsSqlGenerator>();
|
||||||
|
|
||||||
|
|
||||||
|
options.UseLoggerFactory(logFactory);
|
||||||
|
|
||||||
|
options.UseExceptionProcessor();
|
||||||
|
|
||||||
options.EnableSensitiveDataLogging();
|
options.EnableSensitiveDataLogging();
|
||||||
|
|
||||||
options.AddInterceptors(new QueryWithNoLockDbCommandInterceptor());
|
options.AddInterceptors(new QueryWithNoLockDbCommandInterceptor());
|
||||||
|
options.AddInterceptors(sp.GetServices<ISaveChangesInterceptor>());
|
||||||
|
|
||||||
options.UseProjectables();
|
options.UseProjectables();
|
||||||
|
|
||||||
@@ -38,20 +75,17 @@ namespace IRaCIS.Core.API
|
|||||||
|
|
||||||
options.UseTriggers(triggerOptions =>
|
options.UseTriggers(triggerOptions =>
|
||||||
{
|
{
|
||||||
triggerOptions.AddTrigger<AddSubjectTrigger>();
|
triggerOptions.AddTrigger<SubjectTrigger>();
|
||||||
triggerOptions.AddTrigger<ChallengeStateTrigger>();
|
triggerOptions.AddTrigger<ChallengeStateTrigger>();
|
||||||
triggerOptions.AddTrigger<SubjectStateTrigger>();
|
|
||||||
triggerOptions.AddTrigger<AddCRCCliniaclDataTrigger>();
|
|
||||||
triggerOptions.AddTrigger<SubjectVisitCheckPassedTrigger>();
|
|
||||||
triggerOptions.AddTrigger<SubjectVisitFinalVisitTrigger>();
|
|
||||||
triggerOptions.AddTrigger<SubjectVisitTrigger>();
|
triggerOptions.AddTrigger<SubjectVisitTrigger>();
|
||||||
|
triggerOptions.AddTrigger<SubjectVisitScanDateTrigger>();
|
||||||
triggerOptions.AddTrigger<TrialCriterionSignTrigger>();
|
triggerOptions.AddTrigger<TrialCriterionSignTrigger>();
|
||||||
triggerOptions.AddTrigger<TableQuestionRowTrigger>();
|
triggerOptions.AddTrigger<TableQuestionRowTrigger>();
|
||||||
//triggerOptions.AddTrigger<AddlTrialUserTrigger>();
|
//triggerOptions.AddTrigger<AddlTrialUserTrigger>();
|
||||||
triggerOptions.AddTrigger<VisitTaskIsFrontTaskNeedSignButNotSignTrigger>();
|
triggerOptions.AddTrigger<VisitTaskIsFrontTaskNeedSignButNotSignTrigger>();
|
||||||
triggerOptions.AddTrigger<VisitTaskIbeforeTrigger>();
|
triggerOptions.AddTrigger<JudgeVisitTaskTrigger>();
|
||||||
|
|
||||||
|
|
||||||
|
triggerOptions.AddTrigger<UserLogTrigger>();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -62,6 +96,12 @@ namespace IRaCIS.Core.API
|
|||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 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
|
//注意区分 easy caching 也有 IDistributedLockProvider
|
||||||
services.AddSingleton<IDistributedLockProvider>(sp =>
|
services.AddSingleton<IDistributedLockProvider>(sp =>
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
using EasyCaching.Core;
|
using Microsoft.Extensions.Configuration;
|
||||||
using EasyCaching.Core.Configurations;
|
|
||||||
using EasyCaching.Interceptor.Castle;
|
|
||||||
using Microsoft.Extensions.Configuration;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
namespace IRaCIS.Core.API
|
namespace IRaCIS.Core.API
|
||||||
@@ -10,18 +7,18 @@ namespace IRaCIS.Core.API
|
|||||||
{
|
{
|
||||||
public static void AddEasyCachingSetup(this IServiceCollection services, IConfiguration configuration)
|
public static void AddEasyCachingSetup(this IServiceCollection services, IConfiguration configuration)
|
||||||
{
|
{
|
||||||
services.AddEasyCaching(options =>
|
//services.AddEasyCaching(options =>
|
||||||
{
|
//{
|
||||||
options.UseInMemory();
|
// 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,19 +11,41 @@ namespace IRaCIS.Core.API
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 序列化,反序列化的时候,处理时间 时区转换
|
/// 序列化,反序列化的时候,处理时间 时区转换
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class JSONTimeZoneConverter : DateTimeConverterBase
|
public class JSONTimeZoneConverter(IHttpContextAccessor _httpContextAccessor) : DateTimeConverterBase
|
||||||
{
|
{
|
||||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
|
||||||
|
|
||||||
private readonly TimeZoneInfo _clientTimeZone;
|
private TimeZoneInfo _clientTimeZone;
|
||||||
public JSONTimeZoneConverter(IHttpContextAccessor httpContextAccessor)
|
|
||||||
|
private string _dateFormat;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public override bool CanConvert(Type objectType)
|
||||||
{
|
{
|
||||||
_httpContextAccessor = httpContextAccessor;
|
#region 设置语言格式化方式,放在构造函数里面做不到动态切换
|
||||||
|
|
||||||
|
|
||||||
|
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
|
||||||
|
|
||||||
|
if (!isEn_US)
|
||||||
|
{
|
||||||
|
// Chinese date format
|
||||||
|
_dateFormat = "yyyy-MM-dd HH:mm:ss";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Default or English date format
|
||||||
|
//_dateFormat = "MM/dd/yyyy HH:mm:ss";
|
||||||
|
|
||||||
|
_dateFormat = "yyyy-MM-dd HH:mm:ss";
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 获取当前请求的客户端时区
|
||||||
|
|
||||||
//默认是UTC
|
|
||||||
//var timeZoneId = "Etc/UTC";
|
//var timeZoneId = "Etc/UTC";
|
||||||
var timeZoneId = "Asia/Shanghai";
|
var timeZoneId = "Asia/Shanghai";
|
||||||
|
|
||||||
var timeZoneIdHeader = _httpContextAccessor?.HttpContext?.Request?.Headers["TimeZoneId"];
|
var timeZoneIdHeader = _httpContextAccessor?.HttpContext?.Request?.Headers["TimeZoneId"];
|
||||||
|
|
||||||
if (timeZoneIdHeader is not null && !string.IsNullOrEmpty(timeZoneIdHeader.Value))
|
if (timeZoneIdHeader is not null && !string.IsNullOrEmpty(timeZoneIdHeader.Value))
|
||||||
@@ -33,12 +55,12 @@ namespace IRaCIS.Core.API
|
|||||||
|
|
||||||
_clientTimeZone = TimeZoneInfo.FindSystemTimeZoneById(timeZoneId);
|
_clientTimeZone = TimeZoneInfo.FindSystemTimeZoneById(timeZoneId);
|
||||||
|
|
||||||
}
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
public override bool CanConvert(Type objectType)
|
|
||||||
{
|
|
||||||
// 仅支持 DateTime 类型的转换
|
// 仅支持 DateTime 类型的转换
|
||||||
return objectType == typeof(DateTime)|| objectType == typeof(DateTime?);
|
return objectType == typeof(DateTime)|| objectType == typeof(DateTime?);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||||
@@ -91,7 +113,10 @@ namespace IRaCIS.Core.API
|
|||||||
{
|
{
|
||||||
//第一个参数默认使用系统本地时区 也就是应用服务器的时区
|
//第一个参数默认使用系统本地时区 也就是应用服务器的时区
|
||||||
DateTime clientZoneTime = TimeZoneInfo.ConvertTime(nullableDateTime.Value, _clientTimeZone);
|
DateTime clientZoneTime = TimeZoneInfo.ConvertTime(nullableDateTime.Value, _clientTimeZone);
|
||||||
writer.WriteValue(clientZoneTime);
|
|
||||||
|
//writer.WriteValue(clientZoneTime);
|
||||||
|
|
||||||
|
writer.WriteValue(clientZoneTime.ToString(_dateFormat));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,99 @@
|
|||||||
|
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,10 +1,16 @@
|
|||||||
|
|
||||||
using IRaCIS.Core.API._ServiceExtensions.NewtonsoftJson;
|
using IRaCIS.Core.API._ServiceExtensions.NewtonsoftJson;
|
||||||
using IRaCIS.Core.Application.Helper;
|
using IRaCIS.Core.Application.Helper;
|
||||||
|
using IRaCIS.Core.Domain.Share;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Converters;
|
using Newtonsoft.Json.Converters;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Text.Json;
|
||||||
|
using Newtonsoft.Json.Serialization;
|
||||||
|
|
||||||
namespace IRaCIS.Core.API
|
namespace IRaCIS.Core.API
|
||||||
{
|
{
|
||||||
@@ -12,34 +18,43 @@ namespace IRaCIS.Core.API
|
|||||||
{
|
{
|
||||||
public static void AddNewtonsoftJsonSetup(this IMvcBuilder builder, IServiceCollection services)
|
public static void AddNewtonsoftJsonSetup(this IMvcBuilder builder, IServiceCollection services)
|
||||||
{
|
{
|
||||||
|
|
||||||
services.AddHttpContextAccessor();
|
services.AddHttpContextAccessor();
|
||||||
services.AddScoped<JSONTimeZoneConverter>();
|
services.AddScoped<JSONTimeZoneConverter>();
|
||||||
services.AddScoped<CustomStringConverter>();
|
services.AddScoped<ObjectStorePathConvert>();
|
||||||
services.AddScoped<IOSSService,OSSService>();
|
services.AddScoped<IOSSService, OSSService>();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
builder.AddNewtonsoftJson(options =>
|
builder.AddNewtonsoftJson(options =>
|
||||||
{
|
{
|
||||||
//options.SerializerSettings.PreserveReferencesHandling = PreserveReferencesHandling.Objects;
|
|
||||||
|
//大驼峰
|
||||||
|
//options.SerializerSettings.ContractResolver = new DefaultContractResolver();
|
||||||
|
//小驼峰
|
||||||
|
//options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 忽略循环引用
|
// 忽略循环引用
|
||||||
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
|
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
|
||||||
//options.SerializerSettings.TypeNameHandling = TypeNameHandling.All;
|
|
||||||
|
|
||||||
//处理返回给前端 可空类型 给出默认值 比如in? 为null 设置 默认值0
|
//处理返回给前端 可空类型 给出默认值 比如in? 为null 设置 默认值0
|
||||||
options.SerializerSettings.ContractResolver = new NullToEmptyStringResolver(); //new DefaultContractResolver();// new NullToEmptyStringResolver();
|
options.SerializerSettings.ContractResolver = 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.DateTimeZoneHandling = DateTimeZoneHandling.RoundtripKind;
|
||||||
|
|
||||||
//options.SerializerSettings.Converters.Add(new JSONCustomDateConverter()) ;
|
|
||||||
|
|
||||||
|
//二者只能取其一
|
||||||
|
//options.SerializerSettings.Converters.Add(new MyDateTimeConverter());
|
||||||
|
//options.SerializerSettings.Converters.Add(new MyNullableDateTimeConverter());
|
||||||
|
|
||||||
|
//必须放在后面
|
||||||
options.SerializerSettings.Converters.Add(services.BuildServiceProvider().GetService<JSONTimeZoneConverter>());
|
options.SerializerSettings.Converters.Add(services.BuildServiceProvider().GetService<JSONTimeZoneConverter>());
|
||||||
|
|
||||||
//options.SerializerSettings.Converters.Add(services.BuildServiceProvider().GetService<CustomStringConverter>());
|
|
||||||
|
|
||||||
|
|
||||||
//IsoDateTimeConverter
|
|
||||||
//options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore;
|
|
||||||
|
|
||||||
})
|
})
|
||||||
.AddControllersAsServices()//动态webApi属性注入需要
|
.AddControllersAsServices()//动态webApi属性注入需要
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
using Newtonsoft.Json;
|
using IRaCIS.Core.Infrastructure.NewtonsoftJson;
|
||||||
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Serialization;
|
using Newtonsoft.Json.Serialization;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
namespace IRaCIS.Core.API
|
namespace IRaCIS.Core.API
|
||||||
{
|
{
|
||||||
@@ -30,8 +32,20 @@ namespace IRaCIS.Core.API
|
|||||||
|
|
||||||
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
|
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);
|
IList<JsonProperty> properties = base.CreateProperties(type, memberSerialization);
|
||||||
|
|
||||||
|
|
||||||
var list= type.GetProperties()
|
var list= type.GetProperties()
|
||||||
.Select(p =>
|
.Select(p =>
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -9,15 +9,13 @@ using System.Text.RegularExpressions;
|
|||||||
|
|
||||||
namespace IRaCIS.Core.API._ServiceExtensions.NewtonsoftJson
|
namespace IRaCIS.Core.API._ServiceExtensions.NewtonsoftJson
|
||||||
{
|
{
|
||||||
public class CustomStringConverter : JsonConverter<string>
|
public class ObjectStorePathConvert : JsonConverter<string>
|
||||||
{
|
{
|
||||||
//private readonly IHttpContextAccessor _httpContextAccessor;
|
|
||||||
private readonly IOSSService _oSSService;
|
private readonly IOSSService _oSSService;
|
||||||
|
|
||||||
// 构造函数
|
// 构造函数
|
||||||
public CustomStringConverter(/*IHttpContextAccessor httpContextAccessor*/ IOSSService oSSService)
|
public ObjectStorePathConvert( IOSSService oSSService)
|
||||||
{
|
{
|
||||||
//_httpContextAccessor = httpContextAccessor;
|
|
||||||
_oSSService = oSSService;
|
_oSSService = oSSService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +1,30 @@
|
|||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.ResponseCompression;
|
using Microsoft.AspNetCore.ResponseCompression;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using System.IO.Compression;
|
||||||
|
|
||||||
namespace IRaCIS.Core.API
|
namespace IRaCIS.Core.API
|
||||||
{
|
{
|
||||||
public static class ResponseCompressionSetup
|
public static class ResponseCompressionSetup
|
||||||
{
|
{
|
||||||
public static void AddResponseCompressionSetup(this IServiceCollection services)
|
public static void AddResponseCompressionSetup(this IServiceCollection services)
|
||||||
{
|
{
|
||||||
services.AddResponseCompression(options =>
|
services.AddResponseCompression(options =>
|
||||||
{
|
{
|
||||||
|
options.EnableForHttps = true;
|
||||||
options.Providers.Add<BrotliCompressionProvider>();
|
options.Providers.Add<BrotliCompressionProvider>();
|
||||||
options.Providers.Add<GzipCompressionProvider>();
|
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("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("RequestBody", await ReadRequestBody(httpContext.Request)));
|
||||||
//logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("RequestIP", IPHelper.GetIP(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("Referer", httpContext.Request.Headers["Referer"].ToString()));
|
||||||
//logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("request_path", httpContext.Request.Path));
|
//logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("request_path", httpContext.Request.Path));
|
||||||
//logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("request_method", httpContext.Request.Method));
|
//logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("request_method", httpContext.Request.Method));
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
using Serilog.Events;
|
using Serilog.Events;
|
||||||
using Serilog.Sinks.Email;
|
//using Serilog.Sinks.Email;
|
||||||
using System;
|
using System;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using Hangfire.SqlServer;
|
|||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace IRaCIS.Core.API
|
namespace IRaCIS.Core.API
|
||||||
{
|
{
|
||||||
@@ -14,19 +15,29 @@ namespace IRaCIS.Core.API
|
|||||||
|
|
||||||
services.AddHangfire(hangFireConfig =>
|
services.AddHangfire(hangFireConfig =>
|
||||||
{
|
{
|
||||||
|
//本地window 调试 使用内存,服务器部署使用数据库,防止服务器任务调度到本地
|
||||||
//hangFireConfig.UseInMemoryStorage();
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||||
|
|
||||||
//指定存储介质
|
|
||||||
hangFireConfig.UseSqlServerStorage(hangFireConnStr, new SqlServerStorageOptions()
|
|
||||||
{
|
{
|
||||||
SchemaName = "dbo",
|
hangFireConfig.UseInMemoryStorage();
|
||||||
CommandBatchMaxTimeout = TimeSpan.FromMinutes(5),
|
|
||||||
SlidingInvisibilityTimeout = TimeSpan.FromMinutes(5),
|
}
|
||||||
QueuePollInterval = TimeSpan.Zero,
|
else
|
||||||
UseRecommendedIsolationLevel = true,
|
{
|
||||||
DisableGlobalLocks = true
|
//指定存储介质
|
||||||
});
|
hangFireConfig.UseSqlServerStorage(hangFireConnStr, new SqlServerStorageOptions()
|
||||||
|
{
|
||||||
|
SchemaName = "dbo",
|
||||||
|
CommandBatchMaxTimeout = TimeSpan.FromMinutes(5),
|
||||||
|
SlidingInvisibilityTimeout = TimeSpan.FromMinutes(5),
|
||||||
|
QueuePollInterval = TimeSpan.Zero,
|
||||||
|
UseRecommendedIsolationLevel = true,
|
||||||
|
DisableGlobalLocks = true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//hangFireConfig.UseTagsWithSql(); //nuget引入Hangfire.Tags.SqlServer
|
//hangFireConfig.UseTagsWithSql(); //nuget引入Hangfire.Tags.SqlServer
|
||||||
//.UseHangfireHttpJob();
|
//.UseHangfireHttpJob();
|
||||||
|
|||||||
@@ -13,14 +13,16 @@
|
|||||||
"ObjectStoreService": {
|
"ObjectStoreService": {
|
||||||
"ObjectStoreUse": "AliyunOSS",
|
"ObjectStoreUse": "AliyunOSS",
|
||||||
"AliyunOSS": {
|
"AliyunOSS": {
|
||||||
"regionId": "cn-shanghai",
|
"RegionId": "cn-shanghai",
|
||||||
"endpoint": "https://oss-cn-shanghai.aliyuncs.com",
|
"InternalEndpoint": "https://oss-cn-shanghai-internal.aliyuncs.com",
|
||||||
"accessKeyId": "LTAI5tKvzs7ed3UfSpNk3xwQ",
|
"EndPoint": "https://oss-cn-shanghai.aliyuncs.com",
|
||||||
"accessKeySecret": "zTIceGEShlZDGnLrCFfIGFE7TXVRio",
|
"AccessKeyId": "LTAI5tNRTsqL6aWmHkDmTwoH",
|
||||||
"bucketName": "zy-irc-store",
|
"AccessKeySecret": "7mtGz3qrYWI6JMMBZiLeC119VWicZH",
|
||||||
"roleArn": "acs:ram::1899121822495495:role/oss-upload",
|
"RoleArn": "acs:ram::1899121822495495:role/irc-oss-access",
|
||||||
"viewEndpoint": "https://zy-irc-cache.oss-cn-shanghai.aliyuncs.com",
|
"BucketName": "zy-irc-store",
|
||||||
"region": "oss-cn-shanghai"
|
"ViewEndpoint": "https://zy-irc-cache.oss-cn-shanghai.aliyuncs.com",
|
||||||
|
"Region": "oss-cn-shanghai",
|
||||||
|
"DurationSeconds": 7200
|
||||||
},
|
},
|
||||||
"MinIO": {
|
"MinIO": {
|
||||||
"endpoint": "http://192.168.3.68",
|
"endpoint": "http://192.168.3.68",
|
||||||
@@ -42,8 +44,15 @@
|
|||||||
"LoginMaxFailCount": 5,
|
"LoginMaxFailCount": 5,
|
||||||
|
|
||||||
"LoginFailLockMinutes": 30,
|
"LoginFailLockMinutes": 30,
|
||||||
"AutoLoginOutMinutes": 60
|
"AutoLoginOutMinutes": 60,
|
||||||
|
|
||||||
|
"ContinuousReadingTimeMin": 120,
|
||||||
|
|
||||||
|
"ReadingRestTimeMin": 10,
|
||||||
|
|
||||||
|
"IsNeedChangePassWord": true,
|
||||||
|
|
||||||
|
"ChangePassWordDays": 90
|
||||||
},
|
},
|
||||||
"SystemEmailSendConfig": {
|
"SystemEmailSendConfig": {
|
||||||
"Port": 465,
|
"Port": 465,
|
||||||
@@ -51,7 +60,12 @@
|
|||||||
"FromEmail": "uat@extimaging.com",
|
"FromEmail": "uat@extimaging.com",
|
||||||
"FromName": "UAT_IRC",
|
"FromName": "UAT_IRC",
|
||||||
"AuthorizationCode": "SHzyyl2021",
|
"AuthorizationCode": "SHzyyl2021",
|
||||||
"SiteUrl": "http://irc.event.extimaging.com/login"
|
"SiteUrl": "http://irc.event.extimaging.com/login",
|
||||||
|
"CompanyName": "Extensive Imaging",
|
||||||
|
"CompanyNameCN": "上海展影医疗科技有限公司",
|
||||||
|
"CompanyShortName": "Extensive Imaging",
|
||||||
|
"CompanyShortNameCN": "展影医疗",
|
||||||
|
"IsEnv_US": false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -7,22 +7,24 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ConnectionStrings": {
|
"ConnectionStrings": {
|
||||||
// "RemoteNew": "Server=47.117.165.18,1434;Database=Prod_IRC;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true",
|
//"RemoteNew": "Server=101.132.193.237,1434;Database=Prod_IRC;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true",
|
||||||
// "Hangfire": "Server=47.117.165.18,1434;Database=Prod_IRC_Hangfire;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true"
|
//"Hangfire": "Server=101.132.193.237,1434;Database=Prod_IRC_Hangfire;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true"
|
||||||
"RemoteNew": "Server=prod_mssql_standard,1433;Database=Prod_IRC;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true",
|
"RemoteNew": "Server=prod_mssql_standard,1433;Database=Prod_IRC;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true",
|
||||||
"Hangfire": "Server=prod_mssql_standard,1433;Database=Prod_IRC_Hangfire;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true"
|
"Hangfire": "Server=prod_mssql_standard,1433;Database=Prod_IRC_Hangfire;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true"
|
||||||
},
|
},
|
||||||
"ObjectStoreService": {
|
"ObjectStoreService": {
|
||||||
"ObjectStoreUse": "AliyunOSS",
|
"ObjectStoreUse": "AliyunOSS",
|
||||||
"AliyunOSS": {
|
"AliyunOSS": {
|
||||||
"regionId": "cn-shanghai",
|
"RegionId": "cn-shanghai",
|
||||||
"endpoint": "https://oss-cn-shanghai.aliyuncs.com",
|
"InternalEndpoint": "https://oss-cn-shanghai-internal.aliyuncs.com",
|
||||||
"accessKeyId": "LTAI5tKvzs7ed3UfSpNk3xwQ",
|
"EndPoint": "https://oss-cn-shanghai.aliyuncs.com",
|
||||||
"accessKeySecret": "zTIceGEShlZDGnLrCFfIGFE7TXVRio",
|
"AccessKeyId": "LTAI5tNRTsqL6aWmHkDmTwoH",
|
||||||
"bucketName": "zy-irc-store",
|
"AccessKeySecret": "7mtGz3qrYWI6JMMBZiLeC119VWicZH",
|
||||||
"roleArn": "acs:ram::1899121822495495:role/oss-upload",
|
"RoleArn": "acs:ram::1899121822495495:role/irc-oss-access",
|
||||||
"viewEndpoint": "https://zy-irc-cache.oss-cn-shanghai.aliyuncs.com",
|
"BucketName": "zy-irc-store",
|
||||||
"region": "oss-cn-shanghai"
|
"ViewEndpoint": "https://zy-irc-cache.oss-cn-shanghai.aliyuncs.com",
|
||||||
|
"Region": "oss-cn-shanghai",
|
||||||
|
"DurationSeconds": 7200
|
||||||
},
|
},
|
||||||
"MinIO": {
|
"MinIO": {
|
||||||
"endpoint": "http://192.168.3.68",
|
"endpoint": "http://192.168.3.68",
|
||||||
@@ -36,14 +38,22 @@
|
|||||||
"BasicSystemConfig": {
|
"BasicSystemConfig": {
|
||||||
|
|
||||||
"OpenUserComplexPassword": true,
|
"OpenUserComplexPassword": true,
|
||||||
|
|
||||||
"OpenSignDocumentBeforeWork": true,
|
"OpenSignDocumentBeforeWork": true,
|
||||||
|
|
||||||
"OpenLoginLimit": true,
|
"OpenLoginLimit": true,
|
||||||
"LoginMaxFailCount": 5,
|
"LoginMaxFailCount": 5,
|
||||||
|
|
||||||
"LoginFailLockMinutes": 30,
|
"LoginFailLockMinutes": 30,
|
||||||
"AutoLoginOutMinutes": 60
|
|
||||||
|
"AutoLoginOutMinutes": 360,
|
||||||
|
|
||||||
|
"OpenLoginMFA": false,
|
||||||
|
|
||||||
|
"ContinuousReadingTimeMin": 120,
|
||||||
|
|
||||||
|
"ReadingRestTimeMin": 10,
|
||||||
|
"IsNeedChangePassWord": true,
|
||||||
|
|
||||||
|
"ChangePassWordDays": 90
|
||||||
},
|
},
|
||||||
|
|
||||||
"SystemEmailSendConfig": {
|
"SystemEmailSendConfig": {
|
||||||
@@ -53,9 +63,17 @@
|
|||||||
"FromName": "IRC",
|
"FromName": "IRC",
|
||||||
"AuthorizationCode": "ExtImg@2022",
|
"AuthorizationCode": "ExtImg@2022",
|
||||||
"SiteUrl": "http://irc.extimaging.com/login",
|
"SiteUrl": "http://irc.extimaging.com/login",
|
||||||
|
"OrganizationName": "Extlmaging",
|
||||||
|
"OrganizationNameCN": "Extlmaging",
|
||||||
"CompanyName": "Extensive Imaging",
|
"CompanyName": "Extensive Imaging",
|
||||||
"CompanyNameCN": "上海展影医疗科技有限公司",
|
"CompanyNameCN": "上海展影医疗科技有限公司",
|
||||||
"CompanyShortName": "Extensive Imaging",
|
"CompanyShortName": "Extensive Imaging",
|
||||||
"CompanyShortNameCN": "展影医疗"
|
"CompanyShortNameCN": "展影医疗",
|
||||||
|
"IsEnv_US": false
|
||||||
|
},
|
||||||
|
"SystemPacsConfig": {
|
||||||
|
"Port": "11113",
|
||||||
|
"IP": "101.132.193.237"
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,9 +7,8 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ConnectionStrings": {
|
"ConnectionStrings": {
|
||||||
"RemoteNew": "Server=123.56.94.154,1435;Database=Test_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
"RemoteNew": "Server=106.14.89.110,1435;Database=Test_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
||||||
"Hangfire": "Server=123.56.94.154,1435;Database=Test_IRC_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
|
"Hangfire": "Server=106.14.89.110,1435;Database=Test_IRC_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
"ObjectStoreService": {
|
"ObjectStoreService": {
|
||||||
@@ -17,33 +16,36 @@
|
|||||||
"ObjectStoreUse": "AliyunOSS",
|
"ObjectStoreUse": "AliyunOSS",
|
||||||
|
|
||||||
"AliyunOSS": {
|
"AliyunOSS": {
|
||||||
"regionId": "cn-shanghai",
|
"RegionId": "cn-shanghai",
|
||||||
"endPoint": "https://oss-cn-shanghai.aliyuncs.com",
|
"InternalEndpoint": "https://oss-cn-shanghai-internal.aliyuncs.com",
|
||||||
"accessKeyId": "LTAI5tKvzs7ed3UfSpNk3xwQ",
|
"EndPoint": "https://oss-cn-shanghai.aliyuncs.com",
|
||||||
"accessKeySecret": "zTIceGEShlZDGnLrCFfIGFE7TXVRio",
|
"AccessKeyId": "LTAI5tRRZehUp2V9pyTPtAJm",
|
||||||
"bucketName": "zy-irc-test-store",
|
"AccessKeySecret": "FLizxkHsMm4CGYHtkV8E3PNJJZU7oV",
|
||||||
"roleArn": "acs:ram::1899121822495495:role/oss-upload",
|
"RoleArn": "acs:ram::1899121822495495:role/dev-oss-access",
|
||||||
"viewEndpoint": "https://zy-irc-test-store.oss-cn-shanghai.aliyuncs.com",
|
"BucketName": "zy-irc-test-store",
|
||||||
"region": "oss-cn-shanghai"
|
"ViewEndpoint": "https://zy-irc-test-store.oss-cn-shanghai.aliyuncs.com",
|
||||||
|
"Region": "oss-cn-shanghai",
|
||||||
|
"DurationSeconds": 7200
|
||||||
},
|
},
|
||||||
|
|
||||||
"MinIO": {
|
"MinIO": {
|
||||||
"endPoint": "192.168.3.68",
|
"EndPoint": "hir-oss.test.extimaging.com",
|
||||||
"port": "8001",
|
"Port": "443",
|
||||||
"useSSL": false,
|
"UseSSL": true,
|
||||||
"accessKey": "IDFkwEpWej0b4DtiuThL",
|
"AccessKey": "fbStsVYCIPKHQneeqMwD",
|
||||||
"secretKey": "Lhuu83yMhVwu7c1SnjvGY6lq74jzpYqifK6Qtj4h",
|
"SecretKey": "TzgvyA3zGXMUnpilJNUlyMYHfosl1hBMl6lxPmjy",
|
||||||
"bucketName": "test",
|
"BucketName": "irc-test",
|
||||||
"viewEndpoint": "http://192.168.3.68:8001/test/"
|
"ViewEndpoint": "https://hir-oss.test.extimaging.com/irc-test"
|
||||||
},
|
},
|
||||||
|
|
||||||
"AWS": {
|
"AWS": {
|
||||||
"endPoint": "s3.us-east-1.amazonaws.com",
|
"Region": "us-east-1",
|
||||||
"useSSL": true,
|
"EndPoint": "s3.us-east-1.amazonaws.com",
|
||||||
"accessKey": "AKIAZQ3DRSOHFPJJ6FEU",
|
"UseSSL": true,
|
||||||
"secretKey": "l+yjtvV7Z4jiwm/7xCYv30UeUj/SvuqqYzAwjJHf",
|
"RoleArn": "arn:aws:iam::471112624751:role/uat_s3_access",
|
||||||
"bucketName": "ei-irc-test-store",
|
"AccessKeyId": "AKIAW3MEAFJX7IPXISP4",
|
||||||
"viewEndpoint": "https://ei-irc-test-store.s3.amazonaws.com/"
|
"SecretAccessKey": "Pgrg3le5jPxZQ7MR1yYNS30J0XRyJeKVyIIjElXc",
|
||||||
|
"BucketName": "ei-med-s3-lili-uat-store",
|
||||||
|
"ViewEndpoint": "https://ei-med-s3-lili-uat-store.s3.amazonaws.com",
|
||||||
|
"DurationSeconds": 7200
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -60,7 +62,17 @@
|
|||||||
"LoginMaxFailCount": 5,
|
"LoginMaxFailCount": 5,
|
||||||
|
|
||||||
"LoginFailLockMinutes": 1,
|
"LoginFailLockMinutes": 1,
|
||||||
"AutoLoginOutMinutes": 10
|
|
||||||
|
"AutoLoginOutMinutes": 1,
|
||||||
|
|
||||||
|
"OpenLoginMFA": false,
|
||||||
|
|
||||||
|
"ContinuousReadingTimeMin": 120,
|
||||||
|
|
||||||
|
"ReadingRestTimeMin": 10,
|
||||||
|
"IsNeedChangePassWord": true,
|
||||||
|
|
||||||
|
"ChangePassWordDays": 90
|
||||||
},
|
},
|
||||||
|
|
||||||
"SystemEmailSendConfig": {
|
"SystemEmailSendConfig": {
|
||||||
@@ -69,13 +81,19 @@
|
|||||||
"FromEmail": "test@extimaging.com",
|
"FromEmail": "test@extimaging.com",
|
||||||
"FromName": "Test_IRC",
|
"FromName": "Test_IRC",
|
||||||
"AuthorizationCode": "SHzyyl2021",
|
"AuthorizationCode": "SHzyyl2021",
|
||||||
|
|
||||||
"SiteUrl": "http://irc.test.extimaging.com/login",
|
"SiteUrl": "http://irc.test.extimaging.com/login",
|
||||||
|
|
||||||
|
"OrganizationName": "Extlmaging",
|
||||||
|
"OrganizationNameCN": "Extlmaging",
|
||||||
"CompanyName": "Extensive Imaging",
|
"CompanyName": "Extensive Imaging",
|
||||||
"CompanyNameCN": "上海展影医疗科技有限公司",
|
"CompanyNameCN": "上海展影医疗科技有限公司",
|
||||||
"CompanyShortName": "Extensive Imaging",
|
"CompanyShortName": "Extensive Imaging",
|
||||||
"CompanyShortNameCN": "展影医疗"
|
"CompanyShortNameCN": "展影医疗",
|
||||||
}
|
"IsEnv_US": false
|
||||||
|
},
|
||||||
|
|
||||||
|
"SystemPacsConfig": {
|
||||||
|
"Port": "11113",
|
||||||
|
"IP": "106.14.89.110"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+38
-22
@@ -7,19 +7,18 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ConnectionStrings": {
|
"ConnectionStrings": {
|
||||||
"RemoteNew": "Server=us-mssql-service,1433;Database=Prod_US_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
"Db_Type": "pgsql",
|
||||||
"Hangfire": "Server=us-mssql-service,1433;Database=Prod_US_IRC_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
|
"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"
|
||||||
//"RemoteNew": "Server=44.218.11.19,1435;Database=Prod_US_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
|
||||||
//"Hangfire": "Server=44.218.11.19,1435;Database=Prod_US_IRC_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
|
|
||||||
},
|
},
|
||||||
|
|
||||||
"ObjectStoreService": {
|
"ObjectStoreService": {
|
||||||
|
|
||||||
"ObjectStoreUse": "AWS",
|
"ObjectStoreUse": "AliyunOSS",
|
||||||
|
|
||||||
"AliyunOSS": {
|
"AliyunOSS": {
|
||||||
"regionId": "cn-shanghai",
|
"regionId": "cn-shanghai",
|
||||||
|
"internalEndpoint": "https://oss-cn-shanghai-internal.aliyuncs.com",
|
||||||
"endPoint": "https://oss-cn-shanghai.aliyuncs.com",
|
"endPoint": "https://oss-cn-shanghai.aliyuncs.com",
|
||||||
"accessKeyId": "LTAI5tKvzs7ed3UfSpNk3xwQ",
|
"accessKeyId": "LTAI5tKvzs7ed3UfSpNk3xwQ",
|
||||||
"accessKeySecret": "zTIceGEShlZDGnLrCFfIGFE7TXVRio",
|
"accessKeySecret": "zTIceGEShlZDGnLrCFfIGFE7TXVRio",
|
||||||
@@ -28,17 +27,15 @@
|
|||||||
"viewEndpoint": "https://zy-irc-test-store.oss-cn-shanghai.aliyuncs.com",
|
"viewEndpoint": "https://zy-irc-test-store.oss-cn-shanghai.aliyuncs.com",
|
||||||
"region": "oss-cn-shanghai"
|
"region": "oss-cn-shanghai"
|
||||||
},
|
},
|
||||||
|
|
||||||
"MinIO": {
|
"MinIO": {
|
||||||
"endPoint": "192.168.3.68",
|
"endPoint": "hir-oss.test.extimaging.com",
|
||||||
"port": "8001",
|
"port": "443",
|
||||||
"useSSL": false,
|
"useSSL": true,
|
||||||
"accessKey": "IDFkwEpWej0b4DtiuThL",
|
"accessKey": "fbStsVYCIPKHQneeqMwD",
|
||||||
"secretKey": "Lhuu83yMhVwu7c1SnjvGY6lq74jzpYqifK6Qtj4h",
|
"secretKey": "TzgvyA3zGXMUnpilJNUlyMYHfosl1hBMl6lxPmjy",
|
||||||
"bucketName": "test",
|
"bucketName": "irc-test",
|
||||||
"viewEndpoint": "http://192.168.3.68:8001/test/"
|
"viewEndpoint": "https://hir-oss.test.extimaging.com/irc-test"
|
||||||
},
|
},
|
||||||
|
|
||||||
"AWS": {
|
"AWS": {
|
||||||
"endPoint": "s3.us-east-1.amazonaws.com",
|
"endPoint": "s3.us-east-1.amazonaws.com",
|
||||||
"useSSL": true,
|
"useSSL": true,
|
||||||
@@ -48,6 +45,7 @@
|
|||||||
"viewEndpoint": "https://ei-irc-test-store.s3.amazonaws.com/"
|
"viewEndpoint": "https://ei-irc-test-store.s3.amazonaws.com/"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
"BasicSystemConfig": {
|
"BasicSystemConfig": {
|
||||||
|
|
||||||
"OpenUserComplexPassword": false,
|
"OpenUserComplexPassword": false,
|
||||||
@@ -60,8 +58,18 @@
|
|||||||
|
|
||||||
"LoginMaxFailCount": 5,
|
"LoginMaxFailCount": 5,
|
||||||
|
|
||||||
"LoginFailLockMinutes": 30,
|
"LoginFailLockMinutes": 1,
|
||||||
"AutoLoginOutMinutes": 60
|
|
||||||
|
"AutoLoginOutMinutes": 1,
|
||||||
|
|
||||||
|
"OpenLoginMFA": false,
|
||||||
|
|
||||||
|
"ContinuousReadingTimeMin": 120,
|
||||||
|
|
||||||
|
"ReadingRestTimeMin": 10,
|
||||||
|
"IsNeedChangePassWord": true,
|
||||||
|
|
||||||
|
"ChangePassWordDays": 90
|
||||||
},
|
},
|
||||||
|
|
||||||
"SystemEmailSendConfig": {
|
"SystemEmailSendConfig": {
|
||||||
@@ -70,10 +78,18 @@
|
|||||||
"FromEmail": "test@extimaging.com",
|
"FromEmail": "test@extimaging.com",
|
||||||
"FromName": "Test_IRC",
|
"FromName": "Test_IRC",
|
||||||
"AuthorizationCode": "SHzyyl2021",
|
"AuthorizationCode": "SHzyyl2021",
|
||||||
"CompanyName": "Elevate Imaging",
|
"SiteUrl": "http://irc.test.extimaging.com/login",
|
||||||
"CompanyNameCN": "上海展影医疗科技有限公司",
|
|
||||||
"CompanyShortName": "Elevate Imaging",
|
|
||||||
"CompanyShortNameCN": "展影医疗"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
"OrganizationName": "Extlmaging",
|
||||||
|
"OrganizationNameCN": "Extlmaging",
|
||||||
|
"CompanyName": "Extensive Imaging",
|
||||||
|
"CompanyNameCN": "上海展影医疗科技有限公司",
|
||||||
|
"CompanyShortName": "Extensive Imaging",
|
||||||
|
"CompanyShortNameCN": "展影医疗"
|
||||||
|
},
|
||||||
|
|
||||||
|
"SystemPacsConfig": {
|
||||||
|
"Port": "11113",
|
||||||
|
"IP": "106.14.89.110"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft": "Warning",
|
||||||
|
"Microsoft.Hosting.Lifetime": "Information"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ConnectionStrings": {
|
||||||
|
"RemoteNew": "Server=us-prod-mssql-service,1433;Database=US_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
||||||
|
"Hangfire": "Server=us-prod-mssql-service,1433;Database=US_IRC_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
|
||||||
|
//"RemoteNew": "Server=44.210.231.169,1435;Database=US_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
||||||
|
//"Hangfire": "Server=44.210.231.169,1435;Database=US_IRC_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
||||||
|
},
|
||||||
|
|
||||||
|
"ObjectStoreService": {
|
||||||
|
"ObjectStoreUse": "AWS",
|
||||||
|
"MinIO": {
|
||||||
|
"endPoint": "44.210.231.169",
|
||||||
|
"port": "9001",
|
||||||
|
"useSSL": false,
|
||||||
|
"accessKey": "e9bT1isTOqSAUxb6wd4n",
|
||||||
|
"secretKey": "b5TaDzNdQCBtCvfm8eZ3dR6yY7tfZu2JYze2Po1i",
|
||||||
|
"bucketName": "prod-irc-us",
|
||||||
|
"viewEndpoint": "http://44.210.231.169:9001/prod-irc-us/"
|
||||||
|
},
|
||||||
|
|
||||||
|
"AWS": {
|
||||||
|
"Region": "us-east-1",
|
||||||
|
"EndPoint": "s3.us-east-1.amazonaws.com",
|
||||||
|
"UseSSL": true,
|
||||||
|
"RoleArn": "arn:aws:iam::471112624751:role/lili_s3_access",
|
||||||
|
"AccessKeyId": "AKIAW3MEAFJXZ2TZK7GM",
|
||||||
|
"SecretAccessKey": "9MLQCQ1HifEVW1gf068zBRAOb4wNnfrOkvBVByth",
|
||||||
|
"BucketName": "ei-med-s3-lili-store",
|
||||||
|
"ViewEndpoint": "https://ei-med-s3-lili-store.s3.amazonaws.com",
|
||||||
|
"DurationSeconds": 7200
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"BasicSystemConfig": {
|
||||||
|
|
||||||
|
"OpenUserComplexPassword": false,
|
||||||
|
|
||||||
|
"OpenSignDocumentBeforeWork": false,
|
||||||
|
|
||||||
|
"OpenTrialRelationDelete": true,
|
||||||
|
|
||||||
|
"OpenLoginLimit": false,
|
||||||
|
|
||||||
|
"LoginMaxFailCount": 5,
|
||||||
|
|
||||||
|
"LoginFailLockMinutes": 30,
|
||||||
|
"AutoLoginOutMinutes": 60,
|
||||||
|
|
||||||
|
"ContinuousReadingTimeMin": 120,
|
||||||
|
|
||||||
|
"ReadingRestTimeMin": 10,
|
||||||
|
"IsNeedChangePassWord": true,
|
||||||
|
|
||||||
|
"ChangePassWordDays": 90
|
||||||
|
},
|
||||||
|
|
||||||
|
"SystemEmailSendConfig": {
|
||||||
|
"Port": 587,
|
||||||
|
"Host": "smtp-mail.outlook.com",
|
||||||
|
"FromEmail": "donotreply@elevateimaging.ai",
|
||||||
|
"FromName": "LiLi",
|
||||||
|
"AuthorizationCode": "Q#669869497420ul",
|
||||||
|
|
||||||
|
"OrganizationName": "Elevate Imaging",
|
||||||
|
"OrganizationNameCN": "Elevate Imaging",
|
||||||
|
"CompanyName": "Elevate Imaging Inc.",
|
||||||
|
"CompanyNameCN": "上海展影医疗科技有限公司",
|
||||||
|
"CompanyShortName": "Elevate Imaging",
|
||||||
|
"CompanyShortNameCN": "展影医疗",
|
||||||
|
"SiteUrl": "https://lili.elevateimaging.ai/login",
|
||||||
|
"IsEnv_US": true
|
||||||
|
},
|
||||||
|
|
||||||
|
"SystemPacsConfig": {
|
||||||
|
"Port": "104",
|
||||||
|
"IP": "44.210.231.169"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft": "Warning",
|
||||||
|
"Microsoft.Hosting.Lifetime": "Information"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ConnectionStrings": {
|
||||||
|
"RemoteNew": "Server=106.14.89.110,1435;Database=Test_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
||||||
|
"Hangfire": "Server=106.14.89.110,1435;Database=Test_IRC_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
|
||||||
|
},
|
||||||
|
|
||||||
|
"ObjectStoreService": {
|
||||||
|
|
||||||
|
"ObjectStoreUse": "AWS",
|
||||||
|
"MinIO": {
|
||||||
|
//"endPoint": "hir-oss.uat.extimaging.com",
|
||||||
|
//"port": "443",
|
||||||
|
//"useSSL": true,
|
||||||
|
//"viewEndpoint": "https://hir-oss.uat.extimaging.com/hir-uat",
|
||||||
|
|
||||||
|
"endPoint": "47.117.164.182",
|
||||||
|
"port": "9001",
|
||||||
|
"useSSL": false,
|
||||||
|
"viewEndpoint": "http://47.117.164.182:9001/test-irc-us",
|
||||||
|
|
||||||
|
"accessKey": "b9Ul0e98xPzt6PwRXA1Q",
|
||||||
|
"secretKey": "DzMaU2L4OXl90uytwOmDXF2encN0Jf4Nxu2XkYqQ",
|
||||||
|
"bucketName": "test-irc-us"
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
"AWS": {
|
||||||
|
"Region": "us-east-1",
|
||||||
|
"EndPoint": "s3.us-east-1.amazonaws.com",
|
||||||
|
"UseSSL": true,
|
||||||
|
"RoleArn": "arn:aws:iam::471112624751:role/uat_s3_access",
|
||||||
|
"AccessKeyId": "AKIAW3MEAFJX7IPXISP4",
|
||||||
|
"SecretAccessKey": "Pgrg3le5jPxZQ7MR1yYNS30J0XRyJeKVyIIjElXc",
|
||||||
|
"BucketName": "ei-med-s3-lili-uat-store",
|
||||||
|
"ViewEndpoint": "https://ei-med-s3-lili-uat-store.s3.amazonaws.com",
|
||||||
|
"DurationSeconds": 7200
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"BasicSystemConfig": {
|
||||||
|
|
||||||
|
"OpenUserComplexPassword": true,
|
||||||
|
|
||||||
|
"OpenSignDocumentBeforeWork": true,
|
||||||
|
|
||||||
|
"OpenTrialRelationDelete": true,
|
||||||
|
|
||||||
|
"OpenLoginLimit": true,
|
||||||
|
|
||||||
|
"LoginMaxFailCount": 5,
|
||||||
|
|
||||||
|
"LoginFailLockMinutes": 30,
|
||||||
|
"AutoLoginOutMinutes": 60,
|
||||||
|
|
||||||
|
"ContinuousReadingTimeMin": 120,
|
||||||
|
|
||||||
|
"ReadingRestTimeMin": 10,
|
||||||
|
"IsNeedChangePassWord": true,
|
||||||
|
|
||||||
|
"ChangePassWordDays": 90,
|
||||||
|
|
||||||
|
"OpenLoginMFA": true
|
||||||
|
},
|
||||||
|
|
||||||
|
"SystemEmailSendConfig": {
|
||||||
|
"Port": 587,
|
||||||
|
"Host": "smtp-mail.outlook.com",
|
||||||
|
"FromEmail": "donotreply@elevateimaging.ai",
|
||||||
|
"FromName": "LiLi",
|
||||||
|
"AuthorizationCode": "Q#669869497420ul",
|
||||||
|
|
||||||
|
"OrganizationName": "Elevate Imaging",
|
||||||
|
"OrganizationNameCN": "Elevate Imaging",
|
||||||
|
"CompanyName": "Elevate Imaging Inc.",
|
||||||
|
"CompanyNameCN": "上海展影医疗科技有限公司",
|
||||||
|
"CompanyShortName": "Elevate Imaging",
|
||||||
|
"CompanyShortNameCN": "展影医疗",
|
||||||
|
"SiteUrl": "https://lili.test.elevateimaging.ai/login",
|
||||||
|
"IsEnv_US": true
|
||||||
|
},
|
||||||
|
|
||||||
|
"SystemPacsConfig": {
|
||||||
|
"Port": "104",
|
||||||
|
"IP": "3.226.182.187"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft": "Warning",
|
||||||
|
"Microsoft.Hosting.Lifetime": "Information"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ConnectionStrings": {
|
||||||
|
//"RemoteNew": "Server=us-mssql-service,1433;Database=US_Uat_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
||||||
|
//"Hangfire": "Server=us-mssql-service,1433;Database=US_Uat_IRC_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
|
||||||
|
|
||||||
|
"RemoteNew": "Server=3.226.182.187,1435;Database=US_Uat_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
||||||
|
"Hangfire": "Server=3.226.182.187,1435;Database=US_Uat_IRC_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
|
||||||
|
},
|
||||||
|
|
||||||
|
"ObjectStoreService": {
|
||||||
|
|
||||||
|
"ObjectStoreUse": "AWS",
|
||||||
|
|
||||||
|
"MinIO": {
|
||||||
|
//"endPoint": "hir-minio.uat.elevateimaging.ai",
|
||||||
|
//"port": "443",
|
||||||
|
//"useSSL": true,
|
||||||
|
//"viewEndpoint": "https://hir-minio.uat.elevateimaging.ai/uat-irc-us",
|
||||||
|
|
||||||
|
"endPoint": "3.226.182.187",
|
||||||
|
"port": "9001",
|
||||||
|
"useSSL": false,
|
||||||
|
"viewEndpoint": "http://44.218.11.19:9001/uat-irc-us",
|
||||||
|
|
||||||
|
"accessKey": "lH8DkKskLuDqPaiubuSQ",
|
||||||
|
"secretKey": "pdPdicvvLeH7xAC5yFUrI7odMyBfOXxvVWMvKYV4",
|
||||||
|
"bucketName": "uat-irc-us"
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
"AWS": {
|
||||||
|
"Region": "us-east-1",
|
||||||
|
"EndPoint": "s3.us-east-1.amazonaws.com",
|
||||||
|
"UseSSL": true,
|
||||||
|
"RoleArn": "arn:aws:iam::471112624751:role/uat_s3_access",
|
||||||
|
"AccessKeyId": "AKIAW3MEAFJX7IPXISP4",
|
||||||
|
"SecretAccessKey": "Pgrg3le5jPxZQ7MR1yYNS30J0XRyJeKVyIIjElXc",
|
||||||
|
"BucketName": "ei-med-s3-lili-uat-store",
|
||||||
|
"ViewEndpoint": "https://ei-med-s3-lili-uat-store.s3.amazonaws.com/",
|
||||||
|
"DurationSeconds": 7200
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"BasicSystemConfig": {
|
||||||
|
|
||||||
|
"OpenUserComplexPassword": true,
|
||||||
|
|
||||||
|
"OpenSignDocumentBeforeWork": true,
|
||||||
|
|
||||||
|
"OpenTrialRelationDelete": true,
|
||||||
|
|
||||||
|
"OpenLoginLimit": false,
|
||||||
|
|
||||||
|
"LoginMaxFailCount": 5,
|
||||||
|
|
||||||
|
"LoginFailLockMinutes": 30,
|
||||||
|
"AutoLoginOutMinutes": 60,
|
||||||
|
|
||||||
|
"ContinuousReadingTimeMin": 120,
|
||||||
|
|
||||||
|
"ReadingRestTimeMin": 10,
|
||||||
|
"IsNeedChangePassWord": true,
|
||||||
|
|
||||||
|
"ChangePassWordDays": 90,
|
||||||
|
|
||||||
|
"OpenLoginMFA": false
|
||||||
|
},
|
||||||
|
|
||||||
|
"SystemEmailSendConfig": {
|
||||||
|
"Port": 587,
|
||||||
|
"Host": "smtp-mail.outlook.com",
|
||||||
|
"FromEmail": "donotreply@elevateimaging.ai",
|
||||||
|
"FromName": "LiLi",
|
||||||
|
"AuthorizationCode": "Q#669869497420ul",
|
||||||
|
|
||||||
|
"OrganizationName": "Elevate Imaging",
|
||||||
|
"OrganizationNameCN": "Elevate Imaging",
|
||||||
|
"CompanyName": "Elevate Imaging Inc.",
|
||||||
|
"CompanyNameCN": "上海展影医疗科技有限公司",
|
||||||
|
"CompanyShortName": "Elevate Imaging",
|
||||||
|
"CompanyShortNameCN": "展影医疗",
|
||||||
|
"SiteUrl": "https://lili.test.elevateimaging.ai/login",
|
||||||
|
"IsEnv_US": true
|
||||||
|
},
|
||||||
|
|
||||||
|
"SystemPacsConfig": {
|
||||||
|
"Port": "104",
|
||||||
|
"IP": "3.226.182.187"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -15,23 +15,41 @@
|
|||||||
"ObjectStoreUse": "AliyunOSS",
|
"ObjectStoreUse": "AliyunOSS",
|
||||||
|
|
||||||
"AliyunOSS": {
|
"AliyunOSS": {
|
||||||
"regionId": "cn-shanghai",
|
"RegionId": "cn-shanghai",
|
||||||
"endpoint": "https://oss-cn-shanghai.aliyuncs.com",
|
"InternalEndpoint": "https://oss-cn-shanghai-internal.aliyuncs.com",
|
||||||
"accessKeyId": "LTAI5tKvzs7ed3UfSpNk3xwQ",
|
"EndPoint": "https://oss-cn-shanghai.aliyuncs.com",
|
||||||
"accessKeySecret": "zTIceGEShlZDGnLrCFfIGFE7TXVRio",
|
"AccessKeyId": "LTAI5tRRZehUp2V9pyTPtAJm",
|
||||||
"bucketName": "zy-irc-uat-store",
|
"AccessKeySecret": "FLizxkHsMm4CGYHtkV8E3PNJJZU7oV",
|
||||||
"roleArn": "acs:ram::1899121822495495:role/oss-upload",
|
"RoleArn": "acs:ram::1899121822495495:role/dev-oss-access",
|
||||||
"viewEndpoint": "https://zy-irc-uat-store.oss-cn-shanghai.aliyuncs.com",
|
"BucketName": "zy-irc-uat-store",
|
||||||
"region": "oss-cn-shanghai"
|
"ViewEndpoint": "https://zy-irc-uat-store.oss-cn-shanghai.aliyuncs.com",
|
||||||
|
"Region": "oss-cn-shanghai",
|
||||||
|
"DurationSeconds": 7200
|
||||||
},
|
},
|
||||||
|
|
||||||
"MinIO": {
|
"MinIO": {
|
||||||
"endpoint": "http://192.168.3.68",
|
"endPoint": "hir-oss.uat.extimaging.com",
|
||||||
"port": "8001",
|
"port": "80",
|
||||||
"useSSL": false,
|
"useSSL": false,
|
||||||
"accessKey": "IDFkwEpWej0b4DtiuThL",
|
"viewEndpoint": "http://hir-oss.uat.extimaging.com/irc-uat",
|
||||||
"secretKey": "Lhuu83yMhVwu7c1SnjvGY6lq74jzpYqifK6Qtj4h",
|
//"port": "443",
|
||||||
"bucketName": "test"
|
//"useSSL": true,
|
||||||
|
//"viewEndpoint": "https://hir-oss.uat.extimaging.com/irc-uat",
|
||||||
|
"accessKey": "b9Ul0e98xPzt6PwRXA1Q",
|
||||||
|
"secretKey": "DzMaU2L4OXl90uytwOmDXF2encN0Jf4Nxu2XkYqQ",
|
||||||
|
"bucketName": "irc-uat"
|
||||||
|
|
||||||
|
},
|
||||||
|
"AWS": {
|
||||||
|
"Region": "us-east-1",
|
||||||
|
"EndPoint": "s3.us-east-1.amazonaws.com",
|
||||||
|
"UseSSL": true,
|
||||||
|
"RoleArn": "arn:aws:iam::471112624751:role/sts_s3_upload",
|
||||||
|
"AccessKeyId": "AKIAW3MEAFJXWRCGSX5Z",
|
||||||
|
"SecretAccessKey": "miais4jQGSd37A+TfBEP11AQM5u/CvotSmznJd8k",
|
||||||
|
"BucketName": "ei-med-s3-lili-uat-store",
|
||||||
|
"ViewEndpoint": "https://ei-med-s3-lili-uat-store.s3.amazonaws.com/",
|
||||||
|
"DurationSeconds": 7200
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -45,7 +63,15 @@
|
|||||||
"LoginMaxFailCount": 5,
|
"LoginMaxFailCount": 5,
|
||||||
|
|
||||||
"LoginFailLockMinutes": 30,
|
"LoginFailLockMinutes": 30,
|
||||||
"AutoLoginOutMinutes": 60
|
"AutoLoginOutMinutes": 60,
|
||||||
|
"OpenLoginMFA": false,
|
||||||
|
|
||||||
|
"ContinuousReadingTimeMin": 120,
|
||||||
|
|
||||||
|
"ReadingRestTimeMin": 10,
|
||||||
|
"IsNeedChangePassWord": true,
|
||||||
|
|
||||||
|
"ChangePassWordDays": 90
|
||||||
|
|
||||||
},
|
},
|
||||||
"SystemEmailSendConfig": {
|
"SystemEmailSendConfig": {
|
||||||
@@ -55,11 +81,18 @@
|
|||||||
"FromName": "UAT_IRC",
|
"FromName": "UAT_IRC",
|
||||||
"AuthorizationCode": "SHzyyl2021",
|
"AuthorizationCode": "SHzyyl2021",
|
||||||
"SiteUrl": "http://irc.uat.extimaging.com/login",
|
"SiteUrl": "http://irc.uat.extimaging.com/login",
|
||||||
|
"OrganizationName": "Extlmaging",
|
||||||
|
"OrganizationNameCN": "Extlmaging",
|
||||||
"CompanyName": "Extensive Imaging",
|
"CompanyName": "Extensive Imaging",
|
||||||
"CompanyNameCN": "上海展影医疗科技有限公司",
|
"CompanyNameCN": "上海展影医疗科技有限公司",
|
||||||
"CompanyShortName": "Extensive Imaging",
|
"CompanyShortName": "Extensive Imaging",
|
||||||
"CompanyShortNameCN": "展影医疗"
|
"CompanyShortNameCN": "展影医疗",
|
||||||
|
"IsEnv_US": false
|
||||||
|
},
|
||||||
|
|
||||||
|
"SystemPacsConfig": {
|
||||||
|
"Port": "11113",
|
||||||
|
"IP": "47.117.164.182"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"JwtSetting": {
|
"JwtSetting": {
|
||||||
"SecurityKey": "SecurityKey_SHzyyl@2021",
|
"SecurityKey": "ShangHaiZhanYing_SecurityKey_SHzyyl@2021",
|
||||||
"Issuer": "Extimaging",
|
"Issuer": "Extimaging",
|
||||||
"Audience": "EICS",
|
"Audience": "EICS",
|
||||||
"TokenExpireDays": "7"
|
"TokenExpireDays": "7"
|
||||||
@@ -43,40 +43,6 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"easycaching": {
|
|
||||||
"inmemory": {
|
|
||||||
"MaxRdSecond": 120,
|
|
||||||
"EnableLogging": false,
|
|
||||||
"LockMs": 5000,
|
|
||||||
"SleepMs": 300,
|
|
||||||
"DBConfig": {
|
|
||||||
"SizeLimit": 10000,
|
|
||||||
"ExpirationScanFrequency": 60,
|
|
||||||
"EnableReadDeepClone": true,
|
|
||||||
"EnableWriteDeepClone": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"redis": {
|
|
||||||
"MaxRdSecond": 120,
|
|
||||||
"EnableLogging": false,
|
|
||||||
"LockMs": 5000,
|
|
||||||
"SleepMs": 300,
|
|
||||||
"dbconfig": {
|
|
||||||
"Password": "xc@123456",
|
|
||||||
"IsSsl": false,
|
|
||||||
"SslHost": null,
|
|
||||||
"ConnectionTimeout": 5000,
|
|
||||||
"AllowAdmin": true,
|
|
||||||
"Endpoints": [
|
|
||||||
{
|
|
||||||
"Host": "47.117.164.182",
|
|
||||||
"Port": 6379
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"Database": 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"IRaCISImageStore": {
|
"IRaCISImageStore": {
|
||||||
"SwitchingMode": "RemainingDiskCapacity",
|
"SwitchingMode": "RemainingDiskCapacity",
|
||||||
"SwitchingRatio": 80,
|
"SwitchingRatio": 80,
|
||||||
@@ -88,9 +54,17 @@
|
|||||||
"QCChallengeCodePrefix": "Q",
|
"QCChallengeCodePrefix": "Q",
|
||||||
"NoneDicomStudyCodePrefix": "NST",
|
"NoneDicomStudyCodePrefix": "NST",
|
||||||
"DicomStudyCodePrefix": "ST",
|
"DicomStudyCodePrefix": "ST",
|
||||||
|
"TaskStudyCodePrefix": "P-ST",
|
||||||
"SystemSiteCodePrefix": "S",
|
"SystemSiteCodePrefix": "S",
|
||||||
"DefaultPassword": "123456",
|
"DefaultPassword": "123456",
|
||||||
"DefaultInternalOrganizationName": "ExtImaging",
|
|
||||||
"ImageShareExpireDays": 10
|
"ImageShareExpireDays": 10
|
||||||
|
},
|
||||||
|
"EncrypteResponseConfig": {
|
||||||
|
"Base64RSAPublicKey": "LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0NCk1JSUJJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBUThBTUlJQkNnS0NBUUVBaHp3T1hYTWYyaEFkS1ZoWHczYUYNCmNaT3QycE1lcmdEaFVrOUdQK2s4VDBrUjFTRVlGVGtzNlkzaEVvL0dRTExqMHZFYVV3bTNhSFNuTTl5NmdLRWoNCmY5cTN6dkoyZzRSQjE4Z0UrTnNWWi9DMkVRZ3k5OWFiWGc5TitGREVlT0NmSjlSRTJPV3JBQ2s0V0RPbFFUdXYNCnhvR2JmcnkwVElSaFBrOGtuYkFmVkZ1and1VXJGblpJZ0ExYXhKZVZ6aDhwcmV1SEgreW1jdHp6NVo4V1pSV3kNCi9ISURHUy90dkg2NUMra2l6cUxRYUpKNHYwODMrRGpaVTBmNzNCdkk5eWt1dW1saXFzY1pvU2preDFOUFJwSkUNCkFtMVFNQ0hMRCtFdzlHT2Vsc2Mwa1ZxdjdaeEF1TkFrMkZuUURNRk1BUmZuUFd0aGVhOGZYVTJsMW9ROWs3WDcNCmN3SURBUUFCDQotLS0tLUVORCBQVUJMSUMgS0VZLS0tLS0NCg==",
|
||||||
|
"Base64RSAPrivateKey": "LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQ0KTUlJRW9nSUJBQUtDQVFFQWh6d09YWE1mMmhBZEtWaFh3M2FGY1pPdDJwTWVyZ0RoVWs5R1ArazhUMGtSMVNFWQ0KRlRrczZZM2hFby9HUUxMajB2RWFVd20zYUhTbk05eTZnS0VqZjlxM3p2SjJnNFJCMThnRStOc1ZaL0MyRVFneQ0KOTlhYlhnOU4rRkRFZU9DZko5UkUyT1dyQUNrNFdET2xRVHV2eG9HYmZyeTBUSVJoUGs4a25iQWZWRnVqd3VVcg0KRm5aSWdBMWF4SmVWemg4cHJldUhIK3ltY3R6ejVaOFdaUld5L0hJREdTL3R2SDY1QytraXpxTFFhSko0djA4Mw0KK0RqWlUwZjczQnZJOXlrdXVtbGlxc2Nab1Nqa3gxTlBScEpFQW0xUU1DSExEK0V3OUdPZWxzYzBrVnF2N1p4QQ0KdU5BazJGblFETUZNQVJmblBXdGhlYThmWFUybDFvUTlrN1g3Y3dJREFRQUJBb0lCQUNDRFoxNi9XWWFYZmpOZA0KQ29pemU2VFJZU2llVzI5eFJic0ExVDV6YUcwVmY4U1NsdFF2Y1dWYmw2UGJUa3BxMkF4MHJDUVB2M2xOSm8vNA0KL3h3QzRlS1E1c1ZLRlFWTXJIbmhISlRxTTJ6UWVpMkJINlBuaEdZcVh0QVhOdzFxejhrSEoyQlFZM3IvN2d5Qw0KcWpZVFVCRDFRem5HeThCanlXOXVIcnNNeDVPRHRRZWxBM3B1TFd1bXZNb3Z1L2JhaDZvTGtOSHY4b0VTdzhGSQ0KTllyTUtscHhFTjZaWUdwSTl2VTZnYUhuTmhEa2ExTHlvUnZ5NnA2dTRLR0FsRTc1VXk4T0dsdncydU5uay9sdg0KSEMyYnY5TnlCRGJpSFNDY0MyK1JXUXMrN0tNWFlwYnBvTVFCR0hqV01GRHVBODFaUCs1TWYxUm9yQUpRNGxrRw0KQnRDQThva0NnWUVBeWY4alBjcFIvQ3dJNU5MYnlwb3ZUWEFHVkFKMmtWRlJVUC9HT2ZOMkFyYWMvL20wdDJ5NA0KemNYVkJZc0pJeHkvVWYxRTFJNFg3VFg3V3NBNGxVNGlPTkwzTnN4dDBEbk1PV2tKUlBPZlF1bW1JaWw5QVRiYQ0KTnRVWFNlTmRoUFFGMGlCb21acFFJYWpSN1RmUnVBbzR6dmpTLzkzeXZZY0lIWU1zM0tjR00ya0NnWUVBcTJPbg0KZlp0RmhLTElGanVlRzRrNklGTWdjbytEUUh5TTFWSUp5Tk05K1o2TEgybzI5eDJCaSs2Qm82M2NKUjQ2STZncQ0KNWUrSTBvdzZRYmNQeTZUNHFSQ0o3Ujd6MllkdEw4eXdJTkNYS3U0cC9qaFNqNWJ1TzFJWlI3ZStSV29CakNtUQ0KWFd0ZVBCbldqWVlLdVRCazROc2FXV09GTXg1QndKdUp2MjBnQ0hzQ2dZQlV4QnFYM1lWV0cyeUlDZXh1TXhIUw0KbjBZb2p2Z090MTgyYkg5VTVsUUpnM1NTL3NqVmlHeHMvYTROSzNGa0tMWW93KzNVZk9TUmlPdTRBNTQ3R1pURw0KMzlFYVQrTnRWRFBkaTdSMkdQNG1hRUp0WjVlcm9NY2w1M3BrYVdOZlhiL3JrK29STzI2UkVYVTI1UXUrL1pzbA0KVDhuTDBlb0JtdDdPODdNcHpYV09zUUtCZ0ZxVGFQSGx2RUNUY3FEbFV2S0VmRmFXOTkvelhrOFhRNnA5RjdTdA0KaHVSRDJJeDZxcC9BVlRWcGo5Tzd6MHRDaFVGUTM1THpHMkVDUU10My9uNEdLbS9XMEwyakRRWWFIeWNTeXNZYw0KMXJjV2ROVG9XU0dQaDBtTVl0WFhFbFJHNkpoMVl0a3NJL29wUVkwN21MRTBGU3dNUHdtY29jbFpKVEN3UW9VTA0KRzlHL0FvR0FWM25kcWcydnUyR0x4TlRUbm1UTWRJNFB3UzBmN0V4VnVNUnRicGZyOWhKNzlDakVNTGFXQ1FsNg0KeE43TElMTnArNVQwQW1DZVBqcitkbnJkRUNidFZPTDFybDc3Z0krRkwzaVVqYmZmMVZqa0N3M0x6K3cyb1FFdA0KbGE4aTZrL1NRK01iYkRPaWRJOVczdlN6MmlJRlZobWJiK1Q2SlZwakxqNjlkblM3eUxZPQ0KLS0tLS1FTkQgUlNBIFBSSVZBVEUgS0VZLS0tLS0NCg==",
|
||||||
|
"IsResponseEncreptEnable": true,
|
||||||
|
"ApiPathList": [
|
||||||
|
"/test/get"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang='en'>
|
|
||||||
<head>
|
|
||||||
<meta charset='UTF-8'>
|
|
||||||
<title>Title</title>
|
|
||||||
</head>
|
|
||||||
<body style='font-family: 微软雅黑;padding: 0;margin: 0;'>
|
|
||||||
<div style='padding-left: 40px;background: #f6f6f6'>
|
|
||||||
<div style='padding-top: 20px;'>
|
|
||||||
<div style='line-height: 40px;font-size: 18px'>
|
|
||||||
尊敬的 {0} ,您好:
|
|
||||||
</div>
|
|
||||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
|
|
||||||
{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;'>
|
|
||||||
<div>
|
|
||||||
用户名: {1}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
角色: {2}
|
|
||||||
</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>
|
|
||||||
</div>
|
|
||||||
</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>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang='en'>
|
|
||||||
<head>
|
|
||||||
<meta charset='UTF-8'>
|
|
||||||
<title>Title</title>
|
|
||||||
</head>
|
|
||||||
<body style='font-family: 微软雅黑;padding: 0;margin: 0;'>
|
|
||||||
<div style='padding-left: 40px;background: #f6f6f6'>
|
|
||||||
<div style='padding-top: 20px;'>
|
|
||||||
<div style='line-height: 40px;font-size: 18px'>
|
|
||||||
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>
|
|
||||||
<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>
|
|
||||||
User ID: {1}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
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
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div style='line-height: 24px;font-size: 14px;color:#333;margin-top: 20px;padding-bottom: 40px;'>
|
|
||||||
<div>Best regards,</div>
|
|
||||||
<div style="font-size: 14px;">{company}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang='en'>
|
|
||||||
<head>
|
|
||||||
<meta charset='UTF-8'>
|
|
||||||
<title>Title</title>
|
|
||||||
</head>
|
|
||||||
<body style='font-family: 微软雅黑;padding: 0;margin: 0;'>
|
|
||||||
<div style='padding-left: 40px;background: #f6f6f6'>
|
|
||||||
<div style='padding-top: 20px;'>
|
|
||||||
<div style='line-height: 40px;font-size: 18px'>
|
|
||||||
尊敬的 {0} ,您好:
|
|
||||||
</div>
|
|
||||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
|
|
||||||
{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;'>
|
|
||||||
<div>
|
|
||||||
用户名: {1}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
角色: {2}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
密码: {3}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div style='line-height: 24px;font-size: 14px;color:#333;margin-top: 20px;padding-bottom: 40px;'>
|
|
||||||
<div>祝您顺利!</div>
|
|
||||||
<div style="font-size: 14px;">{company}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang='en'>
|
|
||||||
<head>
|
|
||||||
<meta charset='UTF-8'>
|
|
||||||
<title>Title</title>
|
|
||||||
</head>
|
|
||||||
<body style='font-family: 微软雅黑;padding: 0;margin: 0;'>
|
|
||||||
<div style='padding-left: 40px;background: #f6f6f6'>
|
|
||||||
<div style='padding-top: 20px;'>
|
|
||||||
<div style='line-height: 40px;font-size: 18px'>
|
|
||||||
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>
|
|
||||||
<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>
|
|
||||||
User ID: {1}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
Role: {2}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
Password: {3}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div style='line-height: 24px;font-size: 14px;color:#333;margin-top: 20px;padding-bottom: 40px;'>
|
|
||||||
<div>Best regards,</div>
|
|
||||||
<div style="font-size: 14px;">{company}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang='en'>
|
|
||||||
<head>
|
|
||||||
<meta charset='UTF-8'>
|
|
||||||
<title>Title</title>
|
|
||||||
</head>
|
|
||||||
<body style='font-family: 微软雅黑;padding: 0;margin: 0;'>
|
|
||||||
<div style='padding-left: 40px;background: #f6f6f6'>
|
|
||||||
<div style='padding-top: 20px;'>
|
|
||||||
<div style='line-height: 40px;font-size: 18px'>
|
|
||||||
您好:
|
|
||||||
</div>
|
|
||||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
|
|
||||||
感谢您使用展影云平台。
|
|
||||||
</div>
|
|
||||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
|
|
||||||
{0}。
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style='line-height: 24px;font-size: 14px;color:#333;margin-top: 20px;padding-bottom: 40px;'>
|
|
||||||
<div>祝您顺利!</div>
|
|
||||||
<div style="font-size: 14px;">{company}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang='en'>
|
|
||||||
<head>
|
|
||||||
<meta charset='UTF-8'>
|
|
||||||
<title>Title</title>
|
|
||||||
</head>
|
|
||||||
<body style='font-family: 微软雅黑;padding: 0;margin: 0;'>
|
|
||||||
<div style='padding-left: 40px;background: #f6f6f6'>
|
|
||||||
<div style='padding-top: 20px;'>
|
|
||||||
<div style='line-height: 40px;font-size: 18px'>
|
|
||||||
Dear:
|
|
||||||
</div>
|
|
||||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
|
|
||||||
Thank you for using {company abbreviation} Cloud System.
|
|
||||||
</div>
|
|
||||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
|
|
||||||
{0}。
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style='line-height: 24px;font-size: 14px;color:#333;margin-top: 20px;padding-bottom: 40px;'>
|
|
||||||
<div>Best regards,</div>
|
|
||||||
<div style="font-size: 14px;">{company}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang='en'>
|
|
||||||
<head>
|
|
||||||
<meta charset='UTF-8'>
|
|
||||||
<title>Title</title>
|
|
||||||
</head>
|
|
||||||
<body style='font-family: 微软雅黑;padding: 0;margin: 0;'>
|
|
||||||
<div style='padding-left: 40px;background: #f6f6f6'>
|
|
||||||
<div style='padding-top: 20px;'>
|
|
||||||
<div style='line-height: 40px;font-size: 18px'>
|
|
||||||
尊敬的 {0} ,您好:
|
|
||||||
</div>
|
|
||||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
|
|
||||||
{company abbreviation}作为 [{1} (试验方案号:{2 })] 项目的供应商,诚邀您参加该项目阅片相关工作。
|
|
||||||
</div>
|
|
||||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
|
|
||||||
该项目采用电子化工作流,系统及您的账号信息如下:
|
|
||||||
</div>
|
|
||||||
<span style="color: #00D1B2"></span>
|
|
||||||
<div style='border: 1px solid #eee;box-sizing:border-box;width: 50%;background: #fff;padding: 20px;line-height: 40px;font-size: 14px;border-radius: 5px;margin-left: 60px;margin-bottom: 30px;'>
|
|
||||||
<div>
|
|
||||||
项目编号: {3}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
试验方案号: {2}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
试验名称: {1}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
用户名: {4}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
角色: {5}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
系统登录地址:
|
|
||||||
<a href='{6}' style='margin-left:1px;font-size:14px;text-decoration: none;display: inline-block;color:#00D1B2;border-radius: 5px;line-height: 40px;text-align: center;'>
|
|
||||||
点击跳转
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style='line-height: 24px;font-size: 14px;color:#333;margin-top: 20px;padding-bottom: 40px;'>
|
|
||||||
<div>祝您顺利!</div>
|
|
||||||
<div style="font-size: 14px;">{company}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang='en'>
|
|
||||||
<head>
|
|
||||||
<meta charset='UTF-8'>
|
|
||||||
<title>Title</title>
|
|
||||||
</head>
|
|
||||||
<body style='font-family: 微软雅黑;padding: 0;margin: 0;'>
|
|
||||||
<div style='padding-left: 40px;background: #f6f6f6'>
|
|
||||||
<div style='padding-top: 20px;'>
|
|
||||||
<div style='line-height: 40px;font-size: 18px'>
|
|
||||||
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}).
|
|
||||||
</div>
|
|
||||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
|
|
||||||
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>
|
|
||||||
Trial ID: {3}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
Protocol ID: {2}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
Study Name: {1}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
User ID: {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
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div style='line-height: 24px;font-size: 14px;color:#333;margin-top: 20px;padding-bottom: 40px;'>
|
|
||||||
<div>Best regards,</div>
|
|
||||||
<div style="font-size: 14px;">{company}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang='en'>
|
|
||||||
<head>
|
|
||||||
<meta charset='UTF-8'>
|
|
||||||
<title>Title</title>
|
|
||||||
</head>
|
|
||||||
<body style='font-family: 微软雅黑;padding: 0;margin: 0;'>
|
|
||||||
<div style='padding-left: 40px;background: #f6f6f6'>
|
|
||||||
<div style='padding-top: 20px;'>
|
|
||||||
<div style='line-height: 40px;font-size: 18px'>
|
|
||||||
尊敬的 {0} ,您好:
|
|
||||||
</div>
|
|
||||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
|
|
||||||
{company abbreviation}作为 [{1} (试验方案号:{2 })] 项目的IRC供应商,诚邀您参加该项目IRC阅片相关工作,欢迎您提供指导和建议,非常感谢!
|
|
||||||
</div>
|
|
||||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
|
|
||||||
该项目采用电子化工作流,系统及您的账号信息如下:
|
|
||||||
</div>
|
|
||||||
<span style="color: #00D1B2"></span>
|
|
||||||
<div style='border: 1px solid #eee;box-sizing:border-box;width: 50%;background: #fff;padding: 20px;line-height: 40px;font-size: 14px;border-radius: 5px;margin-left: 60px;margin-bottom: 30px;'>
|
|
||||||
<div>
|
|
||||||
项目编号: {3}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
试验方案号: {2}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
试验名称: {1}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
用户名: {4}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
角色: {5}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
首次登陆前,请通过该链接修改您的账户信息:
|
|
||||||
<a href='{6}' style='margin-left:1px;font-size:14px;text-decoration: none;display: inline-block;color:#00D1B2;border-radius: 5px;line-height: 40px;text-align: center;'>
|
|
||||||
初始化账号信息
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div style='line-height: 24px;font-size: 14px;color:#333;margin-top: 20px;padding-bottom: 40px;'>
|
|
||||||
<div>祝您顺利!</div>
|
|
||||||
<div style="font-size: 14px;">{company}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang='en'>
|
|
||||||
<head>
|
|
||||||
<meta charset='UTF-8'>
|
|
||||||
<title>Title</title>
|
|
||||||
</head>
|
|
||||||
<body style='font-family: 微软雅黑;padding: 0;margin: 0;'>
|
|
||||||
<div style='padding-left: 40px;background: #f6f6f6'>
|
|
||||||
<div style='padding-top: 20px;'>
|
|
||||||
<div style='line-height: 40px;font-size: 18px'>
|
|
||||||
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>
|
|
||||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
|
|
||||||
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>
|
|
||||||
Trial ID: {3}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
Protocol ID: {2}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
Study Name: {1}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
User ID: {4}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
Role: {5}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
Before logging in for the first time, please modify 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
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div style='line-height: 24px;font-size: 14px;color:#333;margin-top: 20px;padding-bottom: 40px;'>
|
|
||||||
<div>Best regards,</div>
|
|
||||||
<div style="font-size: 14px;">{company}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang='en'>
|
|
||||||
<head>
|
|
||||||
<meta charset='UTF-8'>
|
|
||||||
<title>Title</title>
|
|
||||||
</head>
|
|
||||||
<body style='font-family: 微软雅黑;padding: 0;margin: 0;'>
|
|
||||||
<div style='padding-left: 40px;background: #f6f6f6'>
|
|
||||||
<div style='padding-top: 20px;'>
|
|
||||||
<div style='line-height: 40px;font-size: 18px'>
|
|
||||||
尊敬的 {0} ,您好:
|
|
||||||
</div>
|
|
||||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
|
|
||||||
您填写的中心调研表被驳回,详细信息如下:
|
|
||||||
</div>
|
|
||||||
<span style="color: #00D1B2"></span>
|
|
||||||
<div style='border: 1px solid #eee;box-sizing:border-box;width: 50%;background: #fff;padding: 20px;line-height: 40px;font-size: 14px;border-radius: 5px;margin-left: 60px;margin-bottom: 30px;'>
|
|
||||||
<div>
|
|
||||||
项目编号: {1}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
试验方案号: {2}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
试验名称: {3}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
中心编号: {4}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
中心名称: {5}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
驳回原因: {6}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<a href='{7}' style='margin-left:60px;font-size:14px;padding: 10px;text-decoration: none;display:{8} ;height: 40px;width: 140px;background: #00D1B2;color:#fff;border-radius: 5px;line-height: 40px;text-align: center;margin-bottom: 100px;'>
|
|
||||||
登陆并查看
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<div style='line-height: 24px;font-size: 14px;color:#333;margin-top: 20px;padding-bottom: 40px;'>
|
|
||||||
<div>祝您顺利!</div>
|
|
||||||
<div style="font-size: 14px;">{company}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang='en'>
|
|
||||||
<head>
|
|
||||||
<meta charset='UTF-8'>
|
|
||||||
<title>Title</title>
|
|
||||||
</head>
|
|
||||||
<body style='font-family: 微软雅黑;padding: 0;margin: 0;'>
|
|
||||||
<div style='padding-left: 40px;background: #f6f6f6'>
|
|
||||||
<div style='padding-top: 20px;'>
|
|
||||||
<div style='line-height: 40px;font-size: 18px'>
|
|
||||||
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>
|
|
||||||
<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>
|
|
||||||
Trial ID: {1}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
Protocol ID: {2}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
Study Name: {3}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
Site ID: {4}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
Site name: {5}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
Reason for rejection: {6}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<a href='{7}' style='margin-left:60px;font-size: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>
|
|
||||||
|
|
||||||
<div style='line-height: 24px;font-size: 14px;color:#333;margin-top: 20px;padding-bottom: 40px;'>
|
|
||||||
<div>Best regards,</div>
|
|
||||||
<div style="font-size: 14px;">{company}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang='en'>
|
|
||||||
<head>
|
|
||||||
<meta charset='UTF-8'>
|
|
||||||
<title>Title</title>
|
|
||||||
</head>
|
|
||||||
<body style='font-family: 微软雅黑;padding: 0;margin: 0;'>
|
|
||||||
<div style='padding-left: 40px;background: #f6f6f6'>
|
|
||||||
<div style='padding-top: 20px;'>
|
|
||||||
<div style='line-height: 40px;font-size: 18px'>
|
|
||||||
尊敬的 {0} ,您好:
|
|
||||||
</div>
|
|
||||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
|
|
||||||
{company abbreviation}作为 [{1} (试验方案号:{2 })] 项目的供应商,诚邀您参加该项目相关工作,欢迎您提供指导和建议,非常感谢!
|
|
||||||
</div>
|
|
||||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
|
|
||||||
该项目采用电子化工作流,系统及您的账号信息如下:
|
|
||||||
</div>
|
|
||||||
<span style="color: #00D1B2"></span>
|
|
||||||
<div style='border: 1px solid #eee;box-sizing:border-box;width: 50%;background: #fff;padding: 20px;line-height: 40px;font-size: 14px;border-radius: 5px;margin-left: 60px;margin-bottom: 30px;'>
|
|
||||||
<div>
|
|
||||||
项目编号: {3}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
试验方案号: {2}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
试验名称: {1}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
用户名: {4}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
角色: {5}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
系统登录地址:
|
|
||||||
<a href='{6}' style='margin-left:5px;font-size:14px;text-decoration: none;display: inline-block;color:#00D1B2;border-radius: 5px;line-height: 40px;text-align: center;'>
|
|
||||||
点击跳转
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div style='line-height: 24px;font-size: 14px;color:#333;margin-top: 20px;padding-bottom: 40px;'>
|
|
||||||
<div>祝您顺利!</div>
|
|
||||||
<div style="font-size: 14px;">{company}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang='en'>
|
|
||||||
<head>
|
|
||||||
<meta charset='UTF-8'>
|
|
||||||
<title>Title</title>
|
|
||||||
</head>
|
|
||||||
<body style='font-family: 微软雅黑;padding: 0;margin: 0;'>
|
|
||||||
<div style='padding-left: 40px;background: #f6f6f6'>
|
|
||||||
<div style='padding-top: 20px;'>
|
|
||||||
<div style='line-height: 40px;font-size: 18px'>
|
|
||||||
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}).
|
|
||||||
</div>
|
|
||||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
|
|
||||||
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>
|
|
||||||
Trial ID: {3}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
Protocol ID: {2}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
Study Name: {1}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
User ID: {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
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div style='line-height: 24px;font-size: 14px;color:#333;margin-top: 20px;padding-bottom: 40px;'>
|
|
||||||
<div>Best regards,</div>
|
|
||||||
<div style="font-size: 14px;">{company}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang='en'>
|
|
||||||
<head>
|
|
||||||
<meta charset='UTF-8'>
|
|
||||||
<title>Title</title>
|
|
||||||
</head>
|
|
||||||
<body style='font-family: 微软雅黑;padding: 0;margin: 0;'>
|
|
||||||
<div style='padding-left: 40px;background: #f6f6f6'>
|
|
||||||
<div style='padding-top: 20px;'>
|
|
||||||
<div style='line-height: 40px;font-size: 18px'>
|
|
||||||
尊敬的 {0} ,您好:
|
|
||||||
</div>
|
|
||||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
|
|
||||||
{company abbreviation}作为 [{1} (试验方案号:{2 })] 项目的IRC供应商,诚邀您参加该项目IRC相关工作,欢迎您提供指导和建议,非常感谢!
|
|
||||||
</div>
|
|
||||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
|
|
||||||
该项目采用电子化工作流,系统及您的账号信息如下:
|
|
||||||
</div>
|
|
||||||
<span style="color: #00D1B2"></span>
|
|
||||||
<div style='border: 1px solid #eee;box-sizing:border-box;width: 50%;background: #fff;padding: 20px;line-height: 40px;font-size: 14px;border-radius: 5px;margin-left: 60px;margin-bottom: 30px;'>
|
|
||||||
<div>
|
|
||||||
项目编号: {3}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
试验方案号: {2}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
试验名称: {1}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
用户名: {4}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
角色: {5}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
首次登陆前,请通过该链接修改您的账户信息:
|
|
||||||
<a href='{6}' style='margin-left:1px;font-size:14px;text-decoration: none;display: inline-block;color:#00D1B2;border-radius: 5px;line-height: 40px;text-align: center;'>
|
|
||||||
初始化账号信息
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div style='line-height: 24px;font-size: 14px;color:#333;margin-top: 20px;padding-bottom: 40px;'>
|
|
||||||
<div>祝您顺利!</div>
|
|
||||||
<div style="font-size: 14px;">{company}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang='en'>
|
|
||||||
<head>
|
|
||||||
<meta charset='UTF-8'>
|
|
||||||
<title>Title</title>
|
|
||||||
</head>
|
|
||||||
<body style='font-family: 微软雅黑;padding: 0;margin: 0;'>
|
|
||||||
<div style='padding-left: 40px;background: #f6f6f6'>
|
|
||||||
<div style='padding-top: 20px;'>
|
|
||||||
<div style='line-height: 40px;font-size: 18px'>
|
|
||||||
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>
|
|
||||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
|
|
||||||
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>
|
|
||||||
Trial ID: {3}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
Protocol ID: {2}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
Study Name: {1}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
User ID: {4}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
Role: {5}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
Before logging in for the first time, please modify 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
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div style='line-height: 24px;font-size: 14px;color:#333;margin-top: 20px;padding-bottom: 40px;'>
|
|
||||||
<div>Best regards,</div>
|
|
||||||
<div style="font-size: 14px;">{company}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang='en'>
|
|
||||||
<head>
|
|
||||||
<meta charset='UTF-8'>
|
|
||||||
<title>Title</title>
|
|
||||||
</head>
|
|
||||||
<body style='font-family: 微软雅黑;padding: 0;margin: 0;'>
|
|
||||||
<div style='padding-left: 40px;background: #f6f6f6'>
|
|
||||||
<div style='padding-top: 20px;'>
|
|
||||||
<div style='line-height: 40px;font-size: 18px'>
|
|
||||||
{0}您好,
|
|
||||||
</div>
|
|
||||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
|
|
||||||
感谢您使用展影云平台。
|
|
||||||
</div>
|
|
||||||
<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分钟内输入该验证码,进行后续操作
|
|
||||||
</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>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang='en'>
|
|
||||||
<head>
|
|
||||||
<meta charset='UTF-8'>
|
|
||||||
<title>Title</title>
|
|
||||||
</head>
|
|
||||||
<body style='font-family: 微软雅黑;padding: 0;margin: 0;'>
|
|
||||||
<div style='padding-left: 40px;background: #f6f6f6'>
|
|
||||||
<div style='padding-top: 20px;'>
|
|
||||||
<div style='line-height: 40px;font-size: 18px'>
|
|
||||||
Dear {0},
|
|
||||||
</div>
|
|
||||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
|
|
||||||
{1}
|
|
||||||
</div>
|
|
||||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
|
|
||||||
The verification code is <span style="font-weight:bold">{2}</span>.
|
|
||||||
</div>
|
|
||||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
|
|
||||||
This code will expire in 3 minutes.
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style='line-height: 24px;font-size: 14px;color:#333;margin-top: 20px;padding-bottom: 40px;'>
|
|
||||||
<div>Best regards,</div>
|
|
||||||
<div style="font-size: 14px;">{company}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -31,9 +31,9 @@ namespace IRaCIS.Core.Application.Auth
|
|||||||
Id = user.Id,
|
Id = user.Id,
|
||||||
FullName = user.UserName,
|
FullName = user.UserName,
|
||||||
RealName = user.RealName,
|
RealName = user.RealName,
|
||||||
UserTypeEnum=user.UserTypeEnum,
|
UserTypeEnum = user.UserTypeEnum,
|
||||||
UserTypeId=user.UserTypeId,
|
UserTypeId = user.UserTypeId,
|
||||||
IsTestUser=user.IsTestUser,
|
IsTestUser = user.IsTestUser,
|
||||||
Code = user.Code,
|
Code = user.Code,
|
||||||
PermissionStr = user.PermissionStr,
|
PermissionStr = user.PermissionStr,
|
||||||
|
|
||||||
@@ -46,7 +46,7 @@ namespace IRaCIS.Core.Application.Auth
|
|||||||
{
|
{
|
||||||
Id = doctor.Id,
|
Id = doctor.Id,
|
||||||
FullName = doctor.FirstName + doctor.LastName,
|
FullName = doctor.FirstName + doctor.LastName,
|
||||||
Phone = doctor.Phone,
|
Phone = doctor.Phone,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,4 @@
|
|||||||
using System;
|
namespace IRaCIS.Core.Application.Auth
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace IRaCIS.Core.Application.Auth
|
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
@@ -13,7 +7,7 @@ namespace IRaCIS.Core.Application.Auth
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
public const string CRC = "CRC";
|
public const string CRC = "CRC";
|
||||||
|
|
||||||
public const string PM = "PM";
|
public const string PM = "PM";
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
using System.Collections.Generic;
|
using Microsoft.IdentityModel.Tokens;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Microsoft.IdentityModel.Tokens;
|
|
||||||
|
|
||||||
namespace IRaCIS.Core.Application.Auth
|
namespace IRaCIS.Core.Application.Auth
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
using System.IdentityModel.Tokens.Jwt;
|
using IRaCIS.Core.Domain.Share;
|
||||||
using System.Security.Claims;
|
|
||||||
using IRaCIS.Core.Domain.Share;
|
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
using System.IdentityModel.Tokens.Jwt;
|
||||||
|
using System.Security.Claims;
|
||||||
|
|
||||||
namespace IRaCIS.Core.Application.Auth
|
namespace IRaCIS.Core.Application.Auth
|
||||||
{
|
{
|
||||||
@@ -56,5 +56,5 @@ namespace IRaCIS.Core.Application.Auth
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,187 +1,59 @@
|
|||||||
using EasyCaching.Core;
|
using Hangfire;
|
||||||
using Hangfire;
|
|
||||||
using Hangfire.Storage;
|
using Hangfire.Storage;
|
||||||
using IRaCIS.Core.Application.Helper;
|
using IRaCIS.Core.Application.Helper;
|
||||||
using IRaCIS.Core.Application.Service;
|
|
||||||
using IRaCIS.Core.Domain.Share;
|
using IRaCIS.Core.Domain.Share;
|
||||||
using IRaCIS.Core.Infrastructure;
|
|
||||||
using Microsoft.Extensions.Configuration;
|
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Newtonsoft.Json;
|
|
||||||
using SharpCompress.Common;
|
|
||||||
|
|
||||||
namespace IRaCIS.Application.Services.BackGroundJob
|
namespace IRaCIS.Core.Application.Service.BackGroundJob
|
||||||
{
|
{
|
||||||
|
|
||||||
public interface IIRaCISHangfireJob
|
public interface IIRaCISHangfireJob
|
||||||
{
|
{
|
||||||
|
|
||||||
Task MemoryCacheTrialStatusAsync();
|
//Task MemoryCacheTrialStatusAsync();
|
||||||
|
|
||||||
Task InitHangfireJobTaskAsync();
|
Task InitHangfireJobTaskAsync();
|
||||||
|
|
||||||
}
|
}
|
||||||
public class IRaCISCHangfireJob : IIRaCISHangfireJob
|
public class IRaCISCHangfireJob(ILogger<IRaCISCHangfireJob> _logger,
|
||||||
|
IRepository<Internationalization> _internationalizationRepository,
|
||||||
|
IRepository<TrialEmailNoticeConfig> _trialEmailNoticeConfigRepository
|
||||||
|
) : IIRaCISHangfireJob
|
||||||
{
|
{
|
||||||
public static string JsonFileFolder = Path.Combine(AppContext.BaseDirectory, StaticData.Folder.Resources);
|
public static string JsonFileFolder = Path.Combine(AppContext.BaseDirectory, StaticData.Folder.Resources);
|
||||||
|
|
||||||
public static FileSystemWatcher FileSystemWatcher_US { get; set; }
|
|
||||||
public static FileSystemWatcher FileSystemWatcher_CN { get; set; }
|
|
||||||
private readonly IRepository<Trial> _trialRepository;
|
|
||||||
private readonly IEasyCachingProvider _provider;
|
|
||||||
private readonly ILogger<IRaCISCHangfireJob> _logger;
|
|
||||||
private readonly IRepository<SystemAnonymization> _systemAnonymizationRepository;
|
|
||||||
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, IRepository<SystemAnonymization> systemAnonymizationRepository)
|
|
||||||
{
|
|
||||||
_trialRepository = trialRepository;
|
|
||||||
_provider = provider;
|
|
||||||
_logger = logger;
|
|
||||||
_trialEmailNoticeConfigRepository = trialEmailNoticeConfigRepository;
|
|
||||||
_internationalizationRepository = internationalizationRepository;
|
|
||||||
_systemAnonymizationRepository = systemAnonymizationRepository;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task InitHangfireJobTaskAsync()
|
public async Task InitHangfireJobTaskAsync()
|
||||||
{
|
{
|
||||||
_logger.LogInformation("项目启动 hangfire 任务初始化 执行开始~");
|
_logger.LogInformation("项目启动 hangfire 任务初始化 执行开始~");
|
||||||
|
|
||||||
|
//初始化国际化
|
||||||
|
|
||||||
//项目状态 立即加载到缓存中
|
await InternationalizationHelper.InitInternationlizationDataAndWatchJsonFileAsync(_internationalizationRepository);
|
||||||
await MemoryCacheTrialStatusAsync();
|
|
||||||
|
|
||||||
await MemoryCacheAnonymizeData();
|
|
||||||
|
|
||||||
|
|
||||||
//创建项目缓存 定时任务
|
|
||||||
HangfireJobHelper.AddOrUpdateInitCronJob<IIRaCISHangfireJob>("RecurringJob_Cache_TrialState", t => t.MemoryCacheTrialStatusAsync(), Cron.Daily());
|
|
||||||
|
|
||||||
//初始化
|
|
||||||
|
|
||||||
await InitInternationlizationDataAndWatchJsonFileAsync();
|
|
||||||
|
|
||||||
//创建邮件定时任务
|
//创建邮件定时任务
|
||||||
await InitSysAndTrialCronJobAsync();
|
await InitSysAndTrialCronJobAsync();
|
||||||
|
|
||||||
|
#region 废弃
|
||||||
|
////项目状态 立即加载到缓存中
|
||||||
|
//await MemoryCacheTrialStatusAsync();
|
||||||
|
|
||||||
|
////await MemoryCacheAnonymizeData();
|
||||||
|
|
||||||
|
|
||||||
|
////创建项目缓存 定时任务
|
||||||
|
//HangfireJobHelper.AddOrUpdateInitCronJob<IIRaCISHangfireJob>("RecurringJob_Cache_TrialState", t => t.MemoryCacheTrialStatusAsync(), Cron.Daily());
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
_logger.LogInformation("项目启动 hangfire 任务初始化 执行结束");
|
_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)));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task MemoryCacheAnonymizeData()
|
|
||||||
{
|
|
||||||
var systemAnonymizationList = await _systemAnonymizationRepository.Where(t => t.IsEnable).ToListAsync();
|
|
||||||
|
|
||||||
_provider.Set(StaticData.Anonymize.Anonymize_AddFixedFiled, systemAnonymizationList.Where(t => t.IsAdd && t.IsFixed).ToList(), TimeSpan.FromDays(7));
|
|
||||||
_provider.Set(StaticData.Anonymize.Anonymize_AddIRCInfoFiled, systemAnonymizationList.Where(t => t.IsAdd && t.IsFixed == false).ToList(), TimeSpan.FromDays(7));
|
|
||||||
_provider.Set(StaticData.Anonymize.Anonymize_FixedField, systemAnonymizationList.Where(t => t.IsAdd == false && t.IsFixed).ToList(), TimeSpan.FromDays(7));
|
|
||||||
_provider.Set(StaticData.Anonymize.Anonymize_IRCInfoField, systemAnonymizationList.Where(t => t.IsAdd == false && t.IsFixed == false).ToList(), 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文件变更 实时刷新数据
|
|
||||||
|
|
||||||
if (!File.Exists(usJsonPath)|| !File.Exists(cnJsonPath))
|
|
||||||
{
|
|
||||||
throw new BusinessValidationFailedException(StaticData.International("IRaCISCHangfireJob_FileNotFound"));
|
|
||||||
}
|
|
||||||
|
|
||||||
FileSystemWatcher_US = new FileSystemWatcher
|
|
||||||
{
|
|
||||||
Path = Path.GetDirectoryName(usJsonPath)!,
|
|
||||||
NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.Size,
|
|
||||||
Filter = Path.GetFileName(usJsonPath),
|
|
||||||
EnableRaisingEvents = true,
|
|
||||||
|
|
||||||
};
|
|
||||||
// 添加文件更改事件的处理程序
|
|
||||||
FileSystemWatcher_US.Changed += (sender, e) => LoadJsonFile(usJsonPath);
|
|
||||||
|
|
||||||
|
|
||||||
FileSystemWatcher_CN = new FileSystemWatcher
|
|
||||||
{
|
|
||||||
Path = Path.GetDirectoryName(cnJsonPath)!,
|
|
||||||
NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.Size,
|
|
||||||
Filter = Path.GetFileName(cnJsonPath),
|
|
||||||
EnableRaisingEvents = true,
|
|
||||||
|
|
||||||
};
|
|
||||||
FileSystemWatcher_CN.Changed += (sender, e) => LoadJsonFile(cnJsonPath);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private void LoadJsonFile(string filePath)
|
|
||||||
{
|
|
||||||
Console.WriteLine("刷新json内存数据");
|
|
||||||
IConfigurationBuilder builder = new ConfigurationBuilder().AddJsonFile(filePath,false,true);
|
|
||||||
|
|
||||||
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()
|
public async Task InitSysAndTrialCronJobAsync()
|
||||||
{
|
{
|
||||||
@@ -223,6 +95,9 @@ namespace IRaCIS.Application.Services.BackGroundJob
|
|||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
using IRaCIS.Core.Infra.EFCore;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
|
|
||||||
namespace IRaCIS.Core.Application.BackGroundJob
|
namespace IRaCIS.Core.Application.BackGroundJob
|
||||||
{
|
{
|
||||||
public interface IObtainTaskAutoCancelJob
|
public interface IObtainTaskAutoCancelJob
|
||||||
{
|
{
|
||||||
Task CancelQCObtaion(Guid subjectVisitId,DateTime startTime);
|
Task CancelQCObtaion(Guid subjectVisitId, DateTime startTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ObtainTaskAutoCancelJob : IObtainTaskAutoCancelJob
|
public class ObtainTaskAutoCancelJob : IObtainTaskAutoCancelJob
|
||||||
@@ -13,7 +12,7 @@ namespace IRaCIS.Core.Application.BackGroundJob
|
|||||||
private readonly IRepository<SubjectVisit> _subjectVisitRepository;
|
private readonly IRepository<SubjectVisit> _subjectVisitRepository;
|
||||||
private readonly ILogger<ObtainTaskAutoCancelJob> _logger;
|
private readonly ILogger<ObtainTaskAutoCancelJob> _logger;
|
||||||
|
|
||||||
public ObtainTaskAutoCancelJob(IRepository<SubjectVisit> subjectVisitRepository, ILogger<ObtainTaskAutoCancelJob> logger)
|
public ObtainTaskAutoCancelJob(IRepository<SubjectVisit> subjectVisitRepository, ILogger<ObtainTaskAutoCancelJob> logger)
|
||||||
{
|
{
|
||||||
_subjectVisitRepository = subjectVisitRepository;
|
_subjectVisitRepository = subjectVisitRepository;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
@@ -30,7 +29,7 @@ namespace IRaCIS.Core.Application.BackGroundJob
|
|||||||
|
|
||||||
var success = await _subjectVisitRepository.SaveChangesAsync();
|
var success = await _subjectVisitRepository.SaveChangesAsync();
|
||||||
|
|
||||||
_logger.LogWarning($"任务建立时间:{startTime} 取消时间:{DateTime.Now} 取消 受试者访视:{ subjectVisitId }success:{success}");
|
_logger.LogWarning($"任务建立时间:{startTime} 取消时间:{DateTime.Now} 取消 受试者访视:{subjectVisitId}success:{success}");
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
using AutoMapper;
|
using AutoMapper;
|
||||||
using IRaCIS.Application.Services.BusinessFilter;
|
using IRaCIS.Core.Application.Service.BusinessFilter;
|
||||||
using IRaCIS.Core.Infra.EFCore;
|
using IRaCIS.Core.Domain.Share;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.Extensions.Localization;
|
using Microsoft.Extensions.Localization;
|
||||||
using Panda.DynamicWebApi;
|
using Panda.DynamicWebApi;
|
||||||
using Panda.DynamicWebApi.Attributes;
|
using Panda.DynamicWebApi.Attributes;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using IRaCIS.Core.Domain.Share;
|
using ZiggyCreatures.Caching.Fusion;
|
||||||
|
|
||||||
namespace IRaCIS.Core.Application
|
namespace IRaCIS.Core.Application.Service
|
||||||
{
|
{
|
||||||
|
|
||||||
#pragma warning disable CS8618
|
#pragma warning disable CS8618
|
||||||
@@ -17,20 +18,21 @@ namespace IRaCIS.Core.Application
|
|||||||
|
|
||||||
#region 非泛型版本
|
#region 非泛型版本
|
||||||
|
|
||||||
[Authorize, DynamicWebApi, UnifiedApiResultFilter]
|
[TypeFilter(typeof(UnifiedApiResultFilter))]
|
||||||
|
[Authorize, DynamicWebApi]
|
||||||
public class BaseService : IBaseService, IDynamicWebApi
|
public class BaseService : IBaseService, IDynamicWebApi
|
||||||
{
|
{
|
||||||
public IMapper _mapper { get; set; }
|
public IMapper _mapper { get; set; }
|
||||||
|
|
||||||
public IUserInfo _userInfo { get; set; }
|
public IUserInfo _userInfo { get; set; }
|
||||||
|
|
||||||
public IRepository _repository { get; set; }
|
|
||||||
|
|
||||||
public IStringLocalizer _localizer { get; set; }
|
public IStringLocalizer _localizer { get; set; }
|
||||||
|
|
||||||
public IWebHostEnvironment _hostEnvironment { get; set; }
|
public IWebHostEnvironment _hostEnvironment { get; set; }
|
||||||
|
|
||||||
|
public IFusionCache _fusionCache { get; set; }
|
||||||
|
|
||||||
|
|
||||||
public static IResponseOutput Null404NotFound<TEntity>(TEntity? businessObject) where TEntity : class
|
public static IResponseOutput Null404NotFound<TEntity>(TEntity? businessObject) where TEntity : class
|
||||||
@@ -49,8 +51,6 @@ namespace IRaCIS.Core.Application
|
|||||||
[MemberNotNull(nameof(_userInfo))]
|
[MemberNotNull(nameof(_userInfo))]
|
||||||
public IUserInfo _userInfo { get; set; }
|
public IUserInfo _userInfo { get; set; }
|
||||||
|
|
||||||
[MemberNotNull(nameof(_repository))]
|
|
||||||
public IRepository _repository { get; set; }
|
|
||||||
|
|
||||||
[MemberNotNull(nameof(_localizer))]
|
[MemberNotNull(nameof(_localizer))]
|
||||||
public IStringLocalizer _localizer { get; set; }
|
public IStringLocalizer _localizer { get; set; }
|
||||||
@@ -58,6 +58,9 @@ namespace IRaCIS.Core.Application
|
|||||||
[MemberNotNull(nameof(_hostEnvironment))]
|
[MemberNotNull(nameof(_hostEnvironment))]
|
||||||
public IWebHostEnvironment _hostEnvironment { get; set; }
|
public IWebHostEnvironment _hostEnvironment { get; set; }
|
||||||
|
|
||||||
|
[MemberNotNull(nameof(_fusionCache))]
|
||||||
|
public IFusionCache _fusionCache { get; set; }
|
||||||
|
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@@ -73,28 +76,26 @@ namespace IRaCIS.Core.Application
|
|||||||
[MemberNotNull(nameof(_userInfo))]
|
[MemberNotNull(nameof(_userInfo))]
|
||||||
public IUserInfo _userInfo { get; set; }
|
public IUserInfo _userInfo { get; set; }
|
||||||
|
|
||||||
[MemberNotNull(nameof(_repository))]
|
|
||||||
public IRepository _repository { get; set; }
|
|
||||||
|
|
||||||
[MemberNotNull(nameof(_localizer))]
|
[MemberNotNull(nameof(_localizer))]
|
||||||
public IStringLocalizer _localizer { get; set; }
|
public IStringLocalizer _localizer { get; set; }
|
||||||
|
|
||||||
|
[MemberNotNull(nameof(_fusionCache))]
|
||||||
|
public IFusionCache _fusionCache { get; set; }
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[TypeFilter(typeof(UnifiedApiResultFilter))]
|
||||||
[Authorize, DynamicWebApi, UnifiedApiResultFilter]
|
[Authorize, DynamicWebApi]
|
||||||
public class BaseServiceTest<T> : IBaseServiceTest<T>, IDynamicWebApi where T : Entity
|
public class BaseServiceTest<T> : IBaseServiceTest<T>, IDynamicWebApi where T : Entity
|
||||||
{
|
{
|
||||||
public IMapper _mapper { get; set; }
|
public IMapper _mapper { get; set; }
|
||||||
|
|
||||||
public IUserInfo _userInfo { get; set; }
|
public IUserInfo _userInfo { get; set; }
|
||||||
|
|
||||||
public IRepository _repository { get; set; }
|
|
||||||
|
|
||||||
public IStringLocalizer _localizer { get; set; }
|
public IStringLocalizer _localizer { get; set; }
|
||||||
|
|
||||||
|
public IFusionCache _fusionCache { get; set; }
|
||||||
|
|
||||||
public static IResponseOutput Null404NotFound<TEntity>(TEntity? businessObject) where TEntity : class
|
public static IResponseOutput Null404NotFound<TEntity>(TEntity? businessObject) where TEntity : class
|
||||||
{
|
{
|
||||||
return new ResponseOutput<string>()
|
return new ResponseOutput<string>()
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
using IRaCIS.Core.Domain.Share;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.Application.BusinessFilter;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 测试加密API 返回的结果
|
||||||
|
/// </summary>
|
||||||
|
public class EncreptApiResultFilter : IAsyncResultFilter
|
||||||
|
{
|
||||||
|
private readonly IOptionsMonitor<IRCEncreptOption> _encreptResponseMonitor;
|
||||||
|
|
||||||
|
public EncreptApiResultFilter(IOptionsMonitor<IRCEncreptOption> encreptResponseMonitor)
|
||||||
|
{
|
||||||
|
_encreptResponseMonitor = encreptResponseMonitor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (_encreptResponseMonitor.CurrentValue.IsResponseEncreptEnable)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (context.Result is ObjectResult objectResult)
|
||||||
|
{
|
||||||
|
var statusCode = objectResult.StatusCode ?? context.HttpContext.Response.StatusCode;
|
||||||
|
|
||||||
|
var objectValue = objectResult.Value;
|
||||||
|
|
||||||
|
|
||||||
|
if (objectValue is IResponseOutput)
|
||||||
|
{
|
||||||
|
var responseOutput = objectValue as IResponseOutput<object>;
|
||||||
|
|
||||||
|
var path = context.HttpContext?.Request.Path.Value?.ToLower();
|
||||||
|
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(path) && path.Length > 5 && _encreptResponseMonitor.CurrentValue.ApiPathList.Contains(path.ToLower()))
|
||||||
|
{
|
||||||
|
|
||||||
|
if (responseOutput.IsSuccess)
|
||||||
|
{
|
||||||
|
responseOutput.Code = ApiResponseCodeEnum.ResultEncrepted;
|
||||||
|
responseOutput.Data = JsonConvert.SerializeObject(Convert.ToBase64String(Encoding.UTF8.GetBytes(responseOutput.Data.ToString())));
|
||||||
|
|
||||||
|
objectResult.Value = responseOutput;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await next.Invoke();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,130 @@
|
|||||||
|
using IRaCIS.Core.Domain.Share;
|
||||||
|
using IRaCIS.Core.Infrastructure.Encryption;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.Application.BusinessFilter;
|
||||||
|
|
||||||
|
public class EncryptionRequestMiddleware
|
||||||
|
{
|
||||||
|
private readonly RequestDelegate _next;
|
||||||
|
|
||||||
|
private readonly IRCEncreptOption _IRCEncreptOption;
|
||||||
|
|
||||||
|
public EncryptionRequestMiddleware(RequestDelegate next, IOptionsMonitor<IRCEncreptOption> encreptResponseMonitor)
|
||||||
|
{
|
||||||
|
_next = next;
|
||||||
|
|
||||||
|
_IRCEncreptOption = encreptResponseMonitor.CurrentValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task InvokeAsync(HttpContext context)
|
||||||
|
{
|
||||||
|
//模拟前端已经设置该请求头
|
||||||
|
//context.Request.Headers["X-Encrypted-Key"] = "hW8JuJocBzIPWSHURUnYJMZ0l68g6uP9rEdMho8ioFO8amD6GIH+R6RIX5jVFzOwO+lmgBi+4gaVJGGkWJMTcoDTfzzzT1EmfPV+UhFLE9HWvCkEXmDOLE9xCHrbG8TrR1I9+Qd3cvo9HLmrQ58n13cJsM82FBw+reUgiWeklPCkEWM9br2qc9VkCp6gKzimTldNTWBezV86S6j3qHbZpVZm0atdDtGb+zSC9LLA3+oHQwRLJAAGSOAi8CUiRmIQsygRd824wBndkaH2ImEeRjBMs7PqCeK3KsoDp13yHdj2AE751gsfNzf4SF547UPf72m0F2/rBFgrSNy+Jz0T9w==";
|
||||||
|
|
||||||
|
// 检查请求头中是否包含加密的对称密钥
|
||||||
|
if (context.Request.Headers.ContainsKey("X-Encrypted-Key"))
|
||||||
|
{
|
||||||
|
|
||||||
|
var encryptedSymmetricKeyStr = context.Request.Headers["X-Encrypted-Key"].ToString();
|
||||||
|
|
||||||
|
var pemSecretKey = Encoding.UTF8.GetString(Convert.FromBase64String(_IRCEncreptOption.Base64RSAPrivateKey));
|
||||||
|
|
||||||
|
// 使用私钥解密对称密钥
|
||||||
|
var decryptedSymmetricKey = RSAEncryption.Decrypt(pemSecretKey, encryptedSymmetricKeyStr);
|
||||||
|
|
||||||
|
|
||||||
|
#region 处理路径传递参数
|
||||||
|
|
||||||
|
// 处理路径参数解密
|
||||||
|
var path = context.Request.Path.Value;
|
||||||
|
|
||||||
|
// 假设加密的参数在路径中,按照顺序解密路径中的每个部分
|
||||||
|
var pathSegments = path.Split('/', StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
for (int i = 2; i < pathSegments.Length; i++)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(pathSegments[i]))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var decryptedSegment = AesEncryption.Decrypt(pathSegments[i], decryptedSymmetricKey);
|
||||||
|
pathSegments[i] = decryptedSegment;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// 如果不能解密该部分,保持原值
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将解密后的路径重新拼接并设置到请求的Path
|
||||||
|
context.Request.Path = "/" + string.Join("/", pathSegments);
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 处理url 传递参数
|
||||||
|
|
||||||
|
// 处理 URL 查询参数的解密
|
||||||
|
var queryParams = context.Request.Query.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.ToString());
|
||||||
|
var decryptedQueryParams = new Dictionary<string, string>();
|
||||||
|
|
||||||
|
foreach (var param in queryParams)
|
||||||
|
{
|
||||||
|
var encryptedValue = param.Value;
|
||||||
|
var decryptedValue = AesEncryption.Decrypt(encryptedValue, decryptedSymmetricKey);
|
||||||
|
decryptedQueryParams[param.Key] = decryptedValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构造解密后的查询字符串
|
||||||
|
var decryptedQueryString = new QueryString("?" + string.Join("&", decryptedQueryParams.Select(kvp => $"{kvp.Key}={kvp.Value}")));
|
||||||
|
context.Request.QueryString = decryptedQueryString;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 处理body传参
|
||||||
|
|
||||||
|
// 读取并解密请求体中的JSON数据
|
||||||
|
context.Request.EnableBuffering();
|
||||||
|
using (var reader = new StreamReader(context.Request.Body, Encoding.UTF8, leaveOpen: true))
|
||||||
|
{
|
||||||
|
var encryptedBody = await reader.ReadToEndAsync();
|
||||||
|
context.Request.Body.Position = 0;
|
||||||
|
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(encryptedBody))
|
||||||
|
{
|
||||||
|
// 尝试解析为JObject
|
||||||
|
var encryptedJson = JObject.Parse(encryptedBody);
|
||||||
|
var decryptedJson = new JObject();
|
||||||
|
|
||||||
|
// 解密每个字段的值
|
||||||
|
foreach (var property in encryptedJson.Properties())
|
||||||
|
{
|
||||||
|
var encryptedValue = property.Value.ToString();
|
||||||
|
var decryptedValue = AesEncryption.Decrypt(encryptedValue, decryptedSymmetricKey);
|
||||||
|
decryptedJson[property.Name] = decryptedValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将解密后的JSON对象转换回字符串,并替换原始请求体
|
||||||
|
var decryptedBody = decryptedJson.ToString();
|
||||||
|
var bodyStream = new MemoryStream(Encoding.UTF8.GetBytes(decryptedBody));
|
||||||
|
context.Request.Body = bodyStream;
|
||||||
|
context.Request.ContentLength = bodyStream.Length;
|
||||||
|
bodyStream.Seek(0, SeekOrigin.Begin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
// 调用下一个中间件
|
||||||
|
await _next(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
using Microsoft.AspNetCore.Diagnostics;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.Application.BusinessFilter;
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 不生效,不知道为啥
|
||||||
|
/// </summary>
|
||||||
|
public class GlobalExceptionHandler : IExceptionHandler
|
||||||
|
{
|
||||||
|
private readonly ILogger<GlobalExceptionHandler> _logger;
|
||||||
|
public GlobalExceptionHandler(ILogger<GlobalExceptionHandler> logger)
|
||||||
|
{
|
||||||
|
this._logger = logger;
|
||||||
|
}
|
||||||
|
public ValueTask<bool> TryHandleAsync(
|
||||||
|
HttpContext httpContext,
|
||||||
|
Exception exception,
|
||||||
|
CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
httpContext.Response.ContentType = "application/json";
|
||||||
|
|
||||||
|
|
||||||
|
var ex = exception;
|
||||||
|
var errorInfo = $"Exception: {ex.Message}[{ex.StackTrace}]" + (ex.InnerException != null ? $" InnerException: {ex.InnerException.Message}[{ex.InnerException.StackTrace}]" : "");
|
||||||
|
|
||||||
|
httpContext.Response.WriteAsJsonAsync(ResponseOutput.NotOk($"{ex?.Message}"));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
_logger.LogError(errorInfo);
|
||||||
|
|
||||||
|
// return true to signal that this exception is handled
|
||||||
|
return ValueTask.FromResult(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,13 +1,12 @@
|
|||||||
using EasyCaching.Core;
|
using IRaCIS.Core.Application.Helper;
|
||||||
using IRaCIS.Core.Domain.Share;
|
using IRaCIS.Core.Domain.Share;
|
||||||
using Microsoft.AspNetCore.Authentication;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.AspNetCore.Mvc.Filters;
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
using Microsoft.Extensions.Localization;
|
using Microsoft.Extensions.Localization;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using Newtonsoft.Json;
|
using ZiggyCreatures.Caching.Fusion;
|
||||||
|
|
||||||
namespace IRaCIS.Core.Application.Filter;
|
namespace IRaCIS.Core.Application.Filter;
|
||||||
|
|
||||||
@@ -18,15 +17,15 @@ public class LimitUserRequestAuthorization : IAsyncAuthorizationFilter
|
|||||||
{
|
{
|
||||||
public IStringLocalizer _localizer { get; set; }
|
public IStringLocalizer _localizer { get; set; }
|
||||||
|
|
||||||
private readonly IEasyCachingProvider _provider;
|
private readonly IFusionCache _fusionCache;
|
||||||
|
|
||||||
private readonly IUserInfo _userInfo;
|
private readonly IUserInfo _userInfo;
|
||||||
|
|
||||||
private readonly IOptionsMonitor<ServiceVerifyConfigOption> _verifyConfig;
|
private readonly IOptionsMonitor<ServiceVerifyConfigOption> _verifyConfig;
|
||||||
|
|
||||||
public LimitUserRequestAuthorization(IEasyCachingProvider provider, IUserInfo userInfo, IStringLocalizer localizer ,IOptionsMonitor<ServiceVerifyConfigOption> verifyConfig)
|
public LimitUserRequestAuthorization(IFusionCache fusionCache, IUserInfo userInfo, IStringLocalizer localizer, IOptionsMonitor<ServiceVerifyConfigOption> verifyConfig)
|
||||||
{
|
{
|
||||||
_provider = provider;
|
_fusionCache = fusionCache;
|
||||||
_userInfo = userInfo;
|
_userInfo = userInfo;
|
||||||
_verifyConfig = verifyConfig;
|
_verifyConfig = verifyConfig;
|
||||||
_localizer = localizer;
|
_localizer = localizer;
|
||||||
@@ -54,7 +53,7 @@ public class LimitUserRequestAuthorization : IAsyncAuthorizationFilter
|
|||||||
{
|
{
|
||||||
context.HttpContext.Response.ContentType = "application/json";
|
context.HttpContext.Response.ContentType = "application/json";
|
||||||
context.HttpContext.Response.StatusCode = StatusCodes.Status200OK;
|
context.HttpContext.Response.StatusCode = StatusCodes.Status200OK;
|
||||||
//---当前请求未从Header/Url取到用户Token,请联系开发者
|
//---当前请求未从Header/Url取到用户Token,请联系开发者
|
||||||
//await context.HttpContext.Response.WriteAsync(JsonConvert.SerializeObject(ResponseOutput.NotOk(_localizer["LimitUser_AuthTokenMissing"])));
|
//await context.HttpContext.Response.WriteAsync(JsonConvert.SerializeObject(ResponseOutput.NotOk(_localizer["LimitUser_AuthTokenMissing"])));
|
||||||
|
|
||||||
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["LimitUser_AuthTokenMissing"]));
|
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["LimitUser_AuthTokenMissing"]));
|
||||||
@@ -63,7 +62,7 @@ public class LimitUserRequestAuthorization : IAsyncAuthorizationFilter
|
|||||||
|
|
||||||
|
|
||||||
//2、在这里取缓存 进行比较 看是否有其他人进行了登陆,如果其他人登陆了,就把之前用户挤掉
|
//2、在这里取缓存 进行比较 看是否有其他人进行了登陆,如果其他人登陆了,就把之前用户挤掉
|
||||||
var cacheUserToken = (await _provider.GetAsync<string>(_userInfo.Id.ToString())).Value;
|
var cacheUserToken = await _fusionCache.GetOrDefaultAsync<string>(CacheKeys.UserToken(_userInfo.Id));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -73,50 +72,41 @@ public class LimitUserRequestAuthorization : IAsyncAuthorizationFilter
|
|||||||
cacheUserToken = _userInfo.UserToken;
|
cacheUserToken = _userInfo.UserToken;
|
||||||
|
|
||||||
//设置当前用户最新Token
|
//设置当前用户最新Token
|
||||||
await _provider.SetAsync(_userInfo.Id.ToString(), _userInfo.UserToken, TimeSpan.FromDays(7));
|
await _fusionCache.SetAsync(CacheKeys.UserToken(_userInfo.Id), _userInfo.UserToken, TimeSpan.FromDays(7));
|
||||||
|
|
||||||
//重启应用程序,所有人续期,不一下子踢出所有人
|
//重启应用程序,所有人续期,不一下子踢出所有人
|
||||||
await _provider.SetAsync($"{_userInfo.Id.ToString()}_Online", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), TimeSpan.FromMinutes(minutes));
|
await _fusionCache.SetAsync(CacheKeys.UserAutoLoginOut(_userInfo.Id), DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), TimeSpan.FromMinutes(minutes));
|
||||||
|
|
||||||
}
|
}
|
||||||
//是同一个人
|
//是同一个人
|
||||||
else if (cacheUserToken == _userInfo.UserToken)
|
else if (cacheUserToken == _userInfo.UserToken)
|
||||||
{
|
{
|
||||||
|
|
||||||
var cacheTime = (await _provider.GetAsync<string>($"{_userInfo.Id.ToString()}_Online")).Value;
|
var cacheTime = await _fusionCache.GetOrDefaultAsync<string>(CacheKeys.UserAutoLoginOut(_userInfo.Id));
|
||||||
|
|
||||||
//过期了 需要自动退出
|
//过期了 需要自动退出
|
||||||
if (string.IsNullOrEmpty(cacheTime))
|
if (string.IsNullOrEmpty(cacheTime))
|
||||||
{
|
{
|
||||||
context.HttpContext.Response.ContentType = "application/json";
|
context.HttpContext.Response.ContentType = "application/json";
|
||||||
context.HttpContext.Response.StatusCode = StatusCodes.Status403Forbidden;
|
context.HttpContext.Response.StatusCode = StatusCodes.Status403Forbidden;
|
||||||
context.Result = new JsonResult(ResponseOutput.NotOk("登录无操作超时自动退出", ApiResponseCodeEnum.AutoLoginOut));
|
|
||||||
|
|
||||||
|
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["LimitUser_AccountAuto_LoginOut"], ApiResponseCodeEnum.AutoLoginOut));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await _provider.SetAsync($"{_userInfo.Id.ToString()}_Online", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), TimeSpan.FromMinutes(minutes));
|
await _fusionCache.SetAsync(CacheKeys.UserAutoLoginOut(_userInfo.Id), DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), TimeSpan.FromMinutes(minutes));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
||||||
context.HttpContext.Response.ContentType = "application/json";
|
context.HttpContext.Response.ContentType = "application/json";
|
||||||
context.HttpContext.Response.StatusCode = StatusCodes.Status403Forbidden;
|
context.HttpContext.Response.StatusCode = StatusCodes.Status403Forbidden;
|
||||||
//---您的账户在其他地方已登陆,您被迫下线。
|
//---您的账户在其他地方已登陆,您被迫下线。
|
||||||
|
|
||||||
|
|
||||||
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["LimitUser_AccountLoggedInElsewhere"], ApiResponseCodeEnum.LoginInOtherPlace));
|
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["LimitUser_AccountLoggedInElsewhere"], ApiResponseCodeEnum.LoginInOtherPlace));
|
||||||
|
|
||||||
|
|
||||||
//await context.HttpContext.Response.WriteAsync(JsonConvert.SerializeObject(ResponseOutput.NotOk(_localizer["LimitUser_AccountLoggedInElsewhere"], ApiResponseCodeEnum.LoginInOtherPlace)));
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,52 +114,3 @@ public class LimitUserRequestAuthorization : IAsyncAuthorizationFilter
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//public class UserTypeRequirement : IAuthorizationRequirement
|
|
||||||
//{
|
|
||||||
//}
|
|
||||||
|
|
||||||
//public class UserTypeHandler : AuthorizationHandler<UserTypeRequirement>
|
|
||||||
//{
|
|
||||||
|
|
||||||
// private IUserInfo _userInfo;
|
|
||||||
|
|
||||||
// public UserTypeHandler(IUserInfo userInfo)
|
|
||||||
// {
|
|
||||||
// _userInfo = userInfo;
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
// protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, UserTypeRequirement requirement)
|
|
||||||
// {
|
|
||||||
|
|
||||||
// //if (context.User.Claims.Count() == 0)
|
|
||||||
// //{
|
|
||||||
// // return Task.CompletedTask;
|
|
||||||
// //}
|
|
||||||
|
|
||||||
// //string userId = context.User.Claims.First(c => c.Type == "Userid").Value;
|
|
||||||
// //string qq = context.User.Claims.First(c => c.Type == "QQ").Value;
|
|
||||||
|
|
||||||
// //if (_UserService.Validata(userId, qq))
|
|
||||||
// //{
|
|
||||||
// // context.Succeed(requirement); //验证通过了
|
|
||||||
// //}
|
|
||||||
// ////在这里就可以做验证
|
|
||||||
|
|
||||||
// return Task.CompletedTask;
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,37 +1,34 @@
|
|||||||
using IRaCIS.Core.Infrastructure.Extention;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.AspNetCore.Mvc.Filters;
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
using Microsoft.Extensions.Localization;
|
using Microsoft.Extensions.Localization;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
|
||||||
namespace IRaCIS.Core.Application.Filter
|
namespace IRaCIS.Core.Application.Filter;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public class ModelActionFilter : ActionFilterAttribute, IActionFilter
|
||||||
{
|
{
|
||||||
|
public IStringLocalizer _localizer;
|
||||||
|
public ModelActionFilter(IStringLocalizer localizer)
|
||||||
public class ModelActionFilter : ActionFilterAttribute, IActionFilter
|
|
||||||
{
|
{
|
||||||
public IStringLocalizer _localizer;
|
_localizer = 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)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 +1,26 @@
|
|||||||
using System;
|
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Mvc.Filters;
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||||
|
|
||||||
namespace IRaCIS.Core.Application.Filter
|
namespace IRaCIS.Core.Application.Filter;
|
||||||
|
|
||||||
|
#region snippet_DisableFormValueModelBindingAttribute
|
||||||
|
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
|
||||||
|
public class DisableFormValueModelBindingAttribute : Attribute, IResourceFilter
|
||||||
{
|
{
|
||||||
#region snippet_DisableFormValueModelBindingAttribute
|
public void OnResourceExecuting(ResourceExecutingContext context)
|
||||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
|
|
||||||
public class DisableFormValueModelBindingAttribute : Attribute, IResourceFilter
|
|
||||||
{
|
{
|
||||||
public void OnResourceExecuting(ResourceExecutingContext context)
|
|
||||||
{
|
|
||||||
|
|
||||||
|
|
||||||
var factories = context.ValueProviderFactories;
|
var factories = context.ValueProviderFactories;
|
||||||
//factories.RemoveType<FormValueProviderFactory>();
|
//factories.RemoveType<FormValueProviderFactory>();
|
||||||
factories.RemoveType<FormFileValueProviderFactory>();
|
factories.RemoveType<FormFileValueProviderFactory>();
|
||||||
factories.RemoveType<JQueryFormValueProviderFactory>();
|
factories.RemoveType<JQueryFormValueProviderFactory>();
|
||||||
context.HttpContext.Request.EnableBuffering();
|
context.HttpContext.Request.EnableBuffering();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnResourceExecuted(ResourceExecutedContext context)
|
public void OnResourceExecuted(ResourceExecutedContext context)
|
||||||
{
|
{
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#endregion
|
|
||||||
}
|
}
|
||||||
|
#endregion
|
||||||
|
|||||||
@@ -1,61 +1,75 @@
|
|||||||
using IRaCIS.Core.Infrastructure;
|
using IRaCIS.Core.Domain.Share;
|
||||||
using IRaCIS.Core.Infrastructure.Extention;
|
using IRaCIS.Core.Infrastructure;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.AspNetCore.Mvc.Filters;
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
using Microsoft.Extensions.Localization;
|
using Microsoft.Extensions.Localization;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace IRaCIS.Core.Application.Filter
|
namespace IRaCIS.Core.Application.Filter;
|
||||||
|
|
||||||
|
public class ProjectExceptionFilter : Attribute, IExceptionFilter
|
||||||
{
|
{
|
||||||
public class ProjectExceptionFilter : Attribute, IExceptionFilter
|
private readonly ILogger<ProjectExceptionFilter> _logger;
|
||||||
|
|
||||||
|
public IStringLocalizer _localizer;
|
||||||
|
|
||||||
|
public ProjectExceptionFilter(IStringLocalizer localizer, ILogger<ProjectExceptionFilter> logger)
|
||||||
{
|
{
|
||||||
private readonly ILogger<ProjectExceptionFilter> _logger;
|
_logger = logger;
|
||||||
|
_localizer = localizer;
|
||||||
|
}
|
||||||
|
public void OnException(ExceptionContext context)
|
||||||
|
{
|
||||||
|
//context.ExceptionHandled;//记录当前这个异常是否已经被处理过了
|
||||||
|
|
||||||
public IStringLocalizer _localizer;
|
if (!context.ExceptionHandled)
|
||||||
|
|
||||||
public ProjectExceptionFilter(IStringLocalizer localizer, ILogger<ProjectExceptionFilter> logger)
|
|
||||||
{
|
{
|
||||||
_logger = logger;
|
if (context.Exception.GetType().Name == "DbUpdateConcurrencyException")
|
||||||
_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));
|
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["ProjectException_ConcurrentUpdateNotAllowed"] + context.Exception.Message));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (context.Exception.GetType() == typeof(BusinessValidationFailedException))
|
if (context.Exception.GetType() == typeof(BusinessValidationFailedException))
|
||||||
|
{
|
||||||
|
var error = context.Exception as BusinessValidationFailedException;
|
||||||
|
|
||||||
|
var info = string.Empty;
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(error!.LocalizedKey) && StaticData.Log_Locoalize_Dic.ContainsKey(error!.LocalizedKey))
|
||||||
{
|
{
|
||||||
var error = context.Exception as BusinessValidationFailedException;
|
info = $"[{error!.LocalizedKey}]:{StaticData.Log_Locoalize_Dic[error!.LocalizedKey]}";
|
||||||
|
|
||||||
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));
|
context.Result = new JsonResult(ResponseOutput.NotOk(context.Exception.Message, "", error!.Code, localizedInfo: info));
|
||||||
|
|
||||||
|
|
||||||
|
//warning 级别记录
|
||||||
|
//_logger.LogWarning($"[{error!.LocalizedKey}]:{StaticData.Log_Locoalize_Dic[error!.LocalizedKey]}");
|
||||||
|
}
|
||||||
|
else if (context.Exception.GetType() == typeof(QueryBusinessObjectNotExistException))
|
||||||
|
{
|
||||||
|
context.Result = new JsonResult(ResponseOutput.NotOk(context.Exception.Message, ApiResponseCodeEnum.DataNotExist));
|
||||||
}
|
}
|
||||||
else
|
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));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
context.ExceptionHandled = true;//标记当前异常已经被处理过了
|
context.ExceptionHandled = true;//标记当前异常已经被处理过了
|
||||||
|
|
||||||
|
|
||||||
|
//throw new Exception("test-result-exceptioin");
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//继续
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,215 +1,196 @@
|
|||||||
using EasyCaching.Core;
|
using IRaCIS.Core.Application.Helper;
|
||||||
using IRaCIS.Core.Domain.Share;
|
using IRaCIS.Core.Domain.Share;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.AspNetCore.Mvc.Filters;
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
using Microsoft.Extensions.Localization;
|
using Microsoft.Extensions.Localization;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
|
using ZiggyCreatures.Caching.Fusion;
|
||||||
using static IRaCIS.Core.Domain.Share.StaticData;
|
using static IRaCIS.Core.Domain.Share.StaticData;
|
||||||
|
|
||||||
namespace IRaCIS.Core.Application.Filter
|
namespace IRaCIS.Core.Application.Filter;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 主要为了 处理项目结束 锁库,不允许操作
|
||||||
|
/// </summary>
|
||||||
|
public class TrialResourceFilter : Attribute, IAsyncResourceFilter
|
||||||
{
|
{
|
||||||
/// <summary>
|
private readonly IUserInfo _userInfo;
|
||||||
/// 主要为了 处理项目结束 锁库,不允许操作
|
private readonly IFusionCache _fusionCache;
|
||||||
/// </summary>
|
public IStringLocalizer _localizer;
|
||||||
public class TrialResourceFilter : Attribute, IAsyncResourceFilter
|
private readonly IRepository<Trial> _trialRepository;
|
||||||
|
private readonly List<string> _trialOptList = new List<string>();
|
||||||
|
|
||||||
|
|
||||||
|
public TrialResourceFilter(IFusionCache fusionCache, IRepository<Trial> trialRepository, IStringLocalizer localizer, IUserInfo userInfo, string trialOpt = null, string trialOpt2 = null, string trialOpt3 = null)
|
||||||
{
|
{
|
||||||
private readonly IEasyCachingProvider _provider;
|
_fusionCache = fusionCache;
|
||||||
private readonly IUserInfo _userInfo;
|
_userInfo = userInfo;
|
||||||
|
_localizer = localizer;
|
||||||
|
_trialRepository = trialRepository;
|
||||||
|
|
||||||
public IStringLocalizer _localizer;
|
if (!string.IsNullOrWhiteSpace(trialOpt)) _trialOptList.Add(trialOpt.Trim());
|
||||||
private readonly List<string> _trialOptList=new List<string>();
|
if (!string.IsNullOrWhiteSpace(trialOpt2)) _trialOptList.Add(trialOpt2.Trim());
|
||||||
|
if (!string.IsNullOrWhiteSpace(trialOpt3)) _trialOptList.Add(trialOpt3.Trim());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public TrialResourceFilter(IEasyCachingProvider provider, IStringLocalizer localizer , IUserInfo userInfo, string trialOpt = null, string trialOpt2 = null, string trialOpt3 = null)
|
//优先选择异步的方法
|
||||||
|
public async Task OnResourceExecutionAsync(ResourceExecutingContext context, ResourceExecutionDelegate next)
|
||||||
|
{
|
||||||
|
// var typeFilter = context.ActionDescriptor.EndpointMetadata.Where(t => t.GetType() == typeof(TypeFilterAttribute)).Select(t => (TypeFilterAttribute)t).ToList().FirstOrDefault();
|
||||||
|
//var _trialOptList= typeFilter.Arguments.Select(t => t.ToString()).ToList();
|
||||||
|
|
||||||
|
// 获取当前请求的 Host 信息
|
||||||
|
var requestHost = context.HttpContext.Request.Host;
|
||||||
|
|
||||||
|
// 检查请求是否来自 localhost:6100
|
||||||
|
if (requestHost.Host == "localhost" && (requestHost.Port == 6100|| requestHost.Port==3305))
|
||||||
{
|
{
|
||||||
_provider = provider;
|
await next.Invoke();
|
||||||
_userInfo = userInfo;
|
|
||||||
_localizer = localizer;
|
|
||||||
//_trialOpt = trialOpt;
|
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(trialOpt)) _trialOptList.Add(trialOpt.Trim());
|
return;
|
||||||
if (!string.IsNullOrWhiteSpace(trialOpt2)) _trialOptList.Add(trialOpt2.Trim());
|
|
||||||
if (!string.IsNullOrWhiteSpace(trialOpt3)) _trialOptList.Add(trialOpt3.Trim());
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region 处理新的用户类型,不能操作项目相关接口
|
||||||
|
|
||||||
|
// 后期列举出具体的类型,其他任何用户类型,都不允许操作
|
||||||
//public TrialResourceFilter(IEasyCachingProvider provider, IUserInfo userInfo)
|
if (_userInfo.UserTypeEnumInt == (int)UserTypeEnum.CRA && _userInfo.RequestUrl.ToLower() != "TrialDocument/userConfirm".ToLower())
|
||||||
//{
|
|
||||||
// _provider = provider;
|
|
||||||
// _userInfo = userInfo;
|
|
||||||
//}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//优先选择异步的方法
|
|
||||||
public async Task OnResourceExecutionAsync(ResourceExecutingContext context, ResourceExecutionDelegate next)
|
|
||||||
{
|
{
|
||||||
// var typeFilter = context.ActionDescriptor.EndpointMetadata.Where(t => t.GetType() == typeof(TypeFilterAttribute)).Select(t => (TypeFilterAttribute)t).ToList().FirstOrDefault();
|
//---对不起,您的账户没有操作权限。
|
||||||
//var _trialOptList= typeFilter.Arguments.Select(t => t.ToString()).ToList();
|
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["TrialResource_NoAccessPermission"]));
|
||||||
|
|
||||||
#region 处理新的用户类型,不能操作项目相关接口
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// 后期列举出具体的类型,其他任何用户类型,都不允许操作
|
#endregion
|
||||||
if (_userInfo.UserTypeEnumInt == (int)UserTypeEnum.CRA && _userInfo.RequestUrl.ToLower()!= "TrialDocument/userConfirm".ToLower())
|
|
||||||
|
|
||||||
|
|
||||||
|
//TrialId 传递的途径多种,可能在path 可能在body 可能在数组中,也可能在对象中,可能就在url
|
||||||
|
var trialIdStr = string.Empty;
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(context.HttpContext.Request.Query["trialId"]))
|
||||||
|
{
|
||||||
|
trialIdStr = context.HttpContext.Request.Query["trialId"];
|
||||||
|
}
|
||||||
|
|
||||||
|
//先尝试从path中取TrialId
|
||||||
|
else if (context.RouteData.Values.Keys.Any(t => t.Contains("trialId")))
|
||||||
|
{
|
||||||
|
var index = context.RouteData.Values.Keys.ToList().IndexOf("trialId");
|
||||||
|
trialIdStr = context.RouteData.Values.Values.ToList()[index] as string;
|
||||||
|
}
|
||||||
|
else if (context.HttpContext.Request.Headers["Referer"].ToString().Contains("trialId"))
|
||||||
|
{
|
||||||
|
var headerStr = context.HttpContext.Request.Headers["Referer"].ToString();
|
||||||
|
|
||||||
|
var trialIdIndex = headerStr.IndexOf("trialId");
|
||||||
|
|
||||||
|
|
||||||
|
var matchResult = Regex.Match(headerStr.Substring(trialIdIndex), @"[0-9a-fA-F]{8}(-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}");
|
||||||
|
|
||||||
|
if (matchResult.Success)
|
||||||
{
|
{
|
||||||
//---对不起,您的账户没有操作权限。
|
trialIdStr = matchResult.Value;
|
||||||
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["TrialResource_NoAccessPermission"]));
|
}
|
||||||
|
else
|
||||||
return;
|
{
|
||||||
|
//---正则取请求Refer 中trialId 失败,请联系开发人员核查
|
||||||
|
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["TrialResource_ReferTrialIdFailed"]));
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
#region body 中取数据
|
||||||
|
|
||||||
|
//设置可以多次读
|
||||||
|
context.HttpContext.Request.EnableBuffering();
|
||||||
|
var reader = new StreamReader(context.HttpContext.Request.Body);
|
||||||
|
var contentFromBody = await reader.ReadToEndAsync();
|
||||||
|
//读取后,流的位置还原
|
||||||
|
context.HttpContext.Request.Body.Seek(0, SeekOrigin.Begin);
|
||||||
|
//context.HttpContext.Request.Body.Position = 0;
|
||||||
|
|
||||||
|
//找到参数位置在字符串中的索引
|
||||||
|
var trialIdIndex = contentFromBody.IndexOf("\"TrialId\"", StringComparison.OrdinalIgnoreCase);
|
||||||
|
|
||||||
//TrialId 传递的途径多种,可能在path 可能在body 可能在数组中,也可能在对象中,可能就在url
|
if (trialIdIndex > -1)
|
||||||
var trialIdStr = string.Empty;
|
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(context.HttpContext.Request.Query["trialId"]))
|
|
||||||
{
|
{
|
||||||
trialIdStr = context.HttpContext.Request.Query["trialId"];
|
// (?<="trialId" *: *").*?(?=",)
|
||||||
}
|
|
||||||
|
|
||||||
//先尝试从path中取TrialId
|
//使用正则 [0-9a-fA-F]{8}(-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}
|
||||||
else if (context.RouteData.Values.Keys.Any(t => t.Contains("trialId")))
|
|
||||||
{
|
|
||||||
var index = context.RouteData.Values.Keys.ToList().IndexOf("trialId");
|
|
||||||
trialIdStr = context.RouteData.Values.Values.ToList()[index] as string;
|
|
||||||
}
|
|
||||||
else if (context.HttpContext.Request.Headers["Referer"].ToString().Contains("trialId"))
|
|
||||||
{
|
|
||||||
var headerStr = context.HttpContext.Request.Headers["Referer"].ToString();
|
|
||||||
|
|
||||||
var trialIdIndex = headerStr.IndexOf("trialId");
|
var matchResult = Regex.Match(contentFromBody.Substring(trialIdIndex), @"[0-9a-fA-F]{8}(-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}");
|
||||||
|
|
||||||
|
|
||||||
var matchResult = Regex.Match(headerStr.Substring(trialIdIndex), @"[0-9a-fA-F]{8}(-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}");
|
|
||||||
|
|
||||||
if (matchResult.Success)
|
if (matchResult.Success)
|
||||||
{
|
{
|
||||||
|
//有可能匹配错误 "trialId":"","documentId":"b8180000-3e2c-0016-9fe0-08da33f96236" 从缓存里面验证下
|
||||||
|
|
||||||
trialIdStr = matchResult.Value;
|
trialIdStr = matchResult.Value;
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//---正则取请求Refer 中trialId 失败,请联系开发人员核查
|
|
||||||
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["TrialResource_ReferTrialIdFailed"]));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
var trialStatusStr = await _fusionCache.GetOrSetAsync(CacheKeys.Trial(trialIdStr), _ => CacheHelper.GetTrialStatusAsync(Guid.Parse(trialIdStr), _trialRepository), TimeSpan.FromDays(7));
|
||||||
else
|
|
||||||
{
|
|
||||||
#region body 中取数据
|
|
||||||
|
|
||||||
//设置可以多次读
|
if (string.IsNullOrWhiteSpace(trialStatusStr))
|
||||||
context.HttpContext.Request.EnableBuffering();
|
|
||||||
var reader = new StreamReader(context.HttpContext.Request.Body);
|
|
||||||
var contentFromBody = await reader.ReadToEndAsync();
|
|
||||||
//读取后,流的位置还原
|
|
||||||
context.HttpContext.Request.Body.Seek(0, SeekOrigin.Begin);
|
|
||||||
//context.HttpContext.Request.Body.Position = 0;
|
|
||||||
|
|
||||||
//找到参数位置在字符串中的索引
|
|
||||||
var trialIdIndex = contentFromBody.IndexOf("\"TrialId\"", StringComparison.OrdinalIgnoreCase);
|
|
||||||
|
|
||||||
if (trialIdIndex > -1)
|
|
||||||
{
|
|
||||||
// (?<="trialId" *: *").*?(?=",)
|
|
||||||
|
|
||||||
//使用正则 [0-9a-fA-F]{8}(-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}
|
|
||||||
|
|
||||||
var matchResult = Regex.Match(contentFromBody.Substring(trialIdIndex), @"[0-9a-fA-F]{8}(-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}");
|
|
||||||
|
|
||||||
if (matchResult.Success)
|
|
||||||
{
|
{
|
||||||
//有可能匹配错误 "trialId":"","documentId":"b8180000-3e2c-0016-9fe0-08da33f96236" 从缓存里面验证下
|
|
||||||
var cacheResultDic = _provider.GetAll<string>(new[] { matchResult.Value });
|
|
||||||
|
|
||||||
var trialStatusStr = cacheResultDic[matchResult.Value.ToLower()].Value;
|
//数据库 检查该项目Id不对
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(trialStatusStr))
|
|
||||||
{
|
|
||||||
trialIdStr = matchResult.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//---正则取请求Refer 中trialId 失败,请联系开发人员核查
|
|
||||||
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["TrialResource_ReferTrialIdFailed"]));
|
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["TrialResource_ReferTrialIdFailed"]));
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//使用字符串取 如果是swagger 可能有时取的不对 因为空格的原因
|
|
||||||
//trialIdStr = contentFromBody.Substring(trialIdIndex + "TrialId".Length + 4, 3
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
|
|
||||||
//通过path 或者body 找到trialId 了
|
|
||||||
if ( !string.IsNullOrWhiteSpace(trialIdStr))
|
|
||||||
{
|
|
||||||
|
|
||||||
//如果没缓存数据,可能定时任务没执行或者缓存丢失 在此重新缓存
|
|
||||||
if (_provider.GetCount() == 0)
|
|
||||||
{
|
|
||||||
|
|
||||||
var _trialRepository = context.HttpContext.RequestServices.GetService(typeof(IRepository<Trial>)) as IRepository<Trial>;
|
|
||||||
|
|
||||||
var list = _trialRepository.IfNullThrowException().Select(t => new { TrialId = t.Id, TrialStatusStr = t.TrialStatusStr })
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
list.ForEach(t => _provider.Set(t.TrialId.ToString(), t.TrialStatusStr, TimeSpan.FromDays(7)));
|
|
||||||
}
|
|
||||||
|
|
||||||
var cacheResultDic = _provider.GetAll<string>(new[] { trialIdStr });
|
|
||||||
|
|
||||||
var trialStatusStr = cacheResultDic[trialIdStr].Value;
|
|
||||||
|
|
||||||
//意外 导致缓存过期,调整服务器时间,测试不想重启程序
|
|
||||||
if (string.IsNullOrWhiteSpace(trialStatusStr))
|
|
||||||
{
|
|
||||||
var trialRepository = context.HttpContext.RequestServices.GetService(typeof(IRepository<Trial>)) as IRepository<Trial>;
|
|
||||||
trialStatusStr = trialRepository?.Where(t => t.Id == Guid.Parse(trialIdStr)).Select(t => t.TrialStatusStr).FirstOrDefault();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 这里是统一拦截 项目有关的操作允许情况(特殊的地方,比如项目配置(有的在多种状态(初始化,ongoing)都可以操作,有的仅仅在Initializing)还有 项目添加和更新,不走这里,特殊处理,不然在这里显得很乱,判断是哪个接口)
|
|
||||||
if (trialStatusStr == StaticData.TrialState.TrialOngoing || _trialOptList.Any(t=>t== TrialOpt.BeforeOngoingCantOpt) )
|
|
||||||
{
|
|
||||||
|
|
||||||
await next.Invoke();
|
|
||||||
|
|
||||||
}
|
|
||||||
// 项目停止、或者完成 不允许操作
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//---本次请求被配置规则拦截:项目状态处于进行中时,才允许操作,若此处逻辑有误,请联系开发人员修改
|
//---正则取请求Refer 中trialId 失败,请联系开发人员核查
|
||||||
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["TrialResource_InterceptedProjectStatusRule"]));
|
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["TrialResource_ReferTrialIdFailed"]));
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
//使用字符串取 如果是swagger 可能有时取的不对 因为空格的原因
|
||||||
//添加项目 签名系统文档的时候 不做拦截 但是更新项目 签名项目文档的时候需要拦截
|
//trialIdStr = contentFromBody.Substring(trialIdIndex + "TrialId".Length + 4, 3
|
||||||
else if (_trialOptList.Any(t => t == TrialOpt.AddOrUpdateTrial ||t ==TrialOpt.SignSystemDocNoTrialId))
|
|
||||||
{
|
|
||||||
await next.Invoke();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
|
||||||
|
//通过path 或者body 找到trialId 了
|
||||||
|
if (!string.IsNullOrWhiteSpace(trialIdStr))
|
||||||
|
{
|
||||||
|
var trialStatusStr = await _fusionCache.GetOrSetAsync(CacheKeys.Trial(trialIdStr), _ => CacheHelper.GetTrialStatusAsync(Guid.Parse(trialIdStr), _trialRepository), TimeSpan.FromDays(7));
|
||||||
|
|
||||||
|
// 这里是统一拦截 项目有关的操作允许情况(特殊的地方,比如项目配置(有的在多种状态(初始化,ongoing)都可以操作,有的仅仅在Initializing)还有 项目添加和更新,不走这里,特殊处理,不然在这里显得很乱,判断是哪个接口)
|
||||||
|
if (trialStatusStr == StaticData.TrialState.TrialOngoing || _trialOptList.Any(t => t == TrialOpt.BeforeOngoingCantOpt))
|
||||||
|
{
|
||||||
|
|
||||||
|
await next.Invoke();
|
||||||
|
|
||||||
|
}
|
||||||
|
// 项目停止、或者完成 不允许操作
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//如果项目相关接口没有传递trialId 会来到这里,提醒,以便修改
|
//---本次请求被配置规则拦截:项目状态处于进行中时,才允许操作,若此处逻辑有误,请联系开发人员修改
|
||||||
|
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["TrialResource_InterceptedProjectStatusRule"]));
|
||||||
|
|
||||||
//---该接口参数中,没有传递项目编号,请核对。
|
|
||||||
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["TrialResource_MissingProjectNumber"]));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
//添加项目 签名系统文档的时候 不做拦截 但是更新项目 签名项目文档的时候需要拦截
|
||||||
|
else if (_trialOptList.Any(t => t == TrialOpt.AddOrUpdateTrial || t == TrialOpt.SignSystemDocNoTrialId))
|
||||||
|
{
|
||||||
|
await next.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//如果项目相关接口没有传递trialId 会来到这里,提醒,以便修改
|
||||||
|
|
||||||
|
//---该接口参数中,没有传递项目编号,请核对。
|
||||||
|
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["TrialResource_MissingProjectNumber"]));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,119 +1,122 @@
|
|||||||
using Microsoft.AspNetCore.Mvc.Filters;
|
using IRaCIS.Core.Domain.Share;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using IRaCIS.Core.Domain.Share;
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace IRaCIS.Application.Services.BusinessFilter
|
namespace IRaCIS.Core.Application.Service.BusinessFilter;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 统一返回前端数据包装,之前在控制器包装,现在修改为动态Api 在ResultFilter这里包装,减少重复冗余代码
|
||||||
|
/// by zhouhang 2021.09.12 周末
|
||||||
|
/// </summary>
|
||||||
|
public class UnifiedApiResultFilter : Attribute, IAsyncResultFilter
|
||||||
{
|
{
|
||||||
|
private readonly ILogger _logger;
|
||||||
|
|
||||||
|
public UnifiedApiResultFilter(ILogger<UnifiedApiResultFilter> logger)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 统一返回前端数据包装,之前在控制器包装,现在修改为动态Api 在ResultFilter这里包装,减少重复冗余代码
|
/// 异步版本
|
||||||
/// by zhouhang 2021.09.12 周末
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class UnifiedApiResultFilter : Attribute, IAsyncResultFilter
|
/// <param name="context"></param>
|
||||||
{
|
/// <param name="next"></param>
|
||||||
/// <summary>
|
/// <returns></returns>
|
||||||
/// 异步版本
|
public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next)
|
||||||
/// </summary>
|
{
|
||||||
/// <param name="context"></param>
|
|
||||||
/// <param name="next"></param>
|
if (context.Result is ObjectResult objectResult)
|
||||||
/// <returns></returns>
|
|
||||||
public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next)
|
|
||||||
{
|
{
|
||||||
|
var statusCode = objectResult.StatusCode ?? context.HttpContext.Response.StatusCode;
|
||||||
|
|
||||||
if (context.Result is ObjectResult objectResult)
|
//是200 并且没有包装 那么包装结果
|
||||||
|
if (statusCode == 200 && !(objectResult.Value is IResponseOutput))
|
||||||
{
|
{
|
||||||
var statusCode = objectResult.StatusCode ?? context.HttpContext.Response.StatusCode;
|
|
||||||
|
|
||||||
//是200 并且没有包装 那么包装结果
|
var type = objectResult.Value?.GetType();
|
||||||
if (statusCode == 200 && !(objectResult.Value is IResponseOutput))
|
|
||||||
|
|
||||||
|
if (type != null && type.IsGenericType && (type.GetGenericTypeDefinition() == typeof(ValueTuple<,>) || type.GetGenericTypeDefinition() == typeof(Tuple<,>)))
|
||||||
{
|
{
|
||||||
//if (objectResult.Value == null)
|
|
||||||
//{
|
|
||||||
// var apiResponse = ResponseOutput.DBNotExist();
|
|
||||||
|
|
||||||
// objectResult.Value = apiResponse;
|
//报错
|
||||||
// objectResult.DeclaredType = apiResponse.GetType();
|
//var tuple = (object, object))objectResult.Value;
|
||||||
//}
|
//var (val1, val2) = ((dynamic, dynamic))objectResult.Value;
|
||||||
//else
|
//var apiResponse = ResponseOutput.Ok(val1, val2);
|
||||||
//{
|
|
||||||
|
|
||||||
var type = objectResult.Value?.GetType();
|
//OK
|
||||||
|
var tuple = (dynamic)objectResult.Value;
|
||||||
|
var apiResponse = ResponseOutput.Ok(tuple!.Item1, tuple!.Item2);
|
||||||
|
|
||||||
|
|
||||||
if ( type!=null&& type.IsGenericType&&(type.GetGenericTypeDefinition()==typeof(ValueTuple<,>)|| type.GetGenericTypeDefinition()==typeof(Tuple<,>)))
|
objectResult.Value = apiResponse;
|
||||||
{
|
objectResult.DeclaredType = apiResponse.GetType();
|
||||||
|
|
||||||
//报错
|
|
||||||
//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
|
||||||
else if (statusCode != 200 && (objectResult.Value is IResponseOutput))
|
|
||||||
{
|
{
|
||||||
}
|
var apiResponse = ResponseOutput.Ok(objectResult.Value);
|
||||||
|
|
||||||
else if(statusCode != 200&&!(objectResult.Value is IResponseOutput))
|
|
||||||
{
|
|
||||||
//---程序错误,请联系开发人员。
|
|
||||||
var apiResponse = ResponseOutput.NotOk(StaticData.International("UnifiedAPI_ProgramError"));
|
|
||||||
|
|
||||||
objectResult.Value = apiResponse;
|
objectResult.Value = apiResponse;
|
||||||
objectResult.DeclaredType = apiResponse.GetType();
|
objectResult.DeclaredType = apiResponse.GetType();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
//如果是200 是IResponseOutput 记录下日志
|
||||||
await next.Invoke();
|
else if (statusCode == 200 && (objectResult.Value is IResponseOutput))
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
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 result = objectResult.Value as IResponseOutput;
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(result.LocalizedInfo))
|
||||||
{
|
{
|
||||||
var genType = type.GetGenericTypeDefinition();
|
//统一在这里记录国际化的日志信息
|
||||||
if (genType == typeof(Tuple<>)
|
_logger.LogWarning($"{result.LocalizedInfo}");
|
||||||
|| genType == typeof(Tuple<,>)
|
|
||||||
|| genType == typeof(Tuple<,>))
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!checkBaseTypes)
|
|
||||||
break;
|
|
||||||
|
|
||||||
type = type.BaseType;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,164 @@
|
|||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.Configuration.Json;
|
||||||
|
using System.ComponentModel;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.Domain.Share;
|
||||||
|
|
||||||
|
[Description("多环境 配置环境实体")]
|
||||||
|
public class ServiceVerifyConfigOption
|
||||||
|
{
|
||||||
|
public bool OpenUserComplexPassword { get; set; }
|
||||||
|
|
||||||
|
public bool OpenSignDocumentBeforeWork { get; set; }
|
||||||
|
|
||||||
|
public bool OpenTrialRelationDelete { get; set; }
|
||||||
|
|
||||||
|
public bool OpenLoginLimit { get; set; }
|
||||||
|
|
||||||
|
public int LoginMaxFailCount { get; set; }
|
||||||
|
|
||||||
|
public int LoginFailLockMinutes { get; set; }
|
||||||
|
|
||||||
|
public int AutoLoginOutMinutes { get; set; }
|
||||||
|
|
||||||
|
public bool OpenLoginMFA { get; set; }
|
||||||
|
|
||||||
|
[Description("连续阅片时间 (min)")]
|
||||||
|
public int ContinuousReadingTimeMin { get; set; }
|
||||||
|
|
||||||
|
[Description("休息时间 (min)")]
|
||||||
|
public int ReadingRestTimeMin { get; set; }
|
||||||
|
[Description("是否需要修改密码")]
|
||||||
|
public bool IsNeedChangePassWord { get; set; }
|
||||||
|
|
||||||
|
[Description("修改密码的天数")]
|
||||||
|
public int ChangePassWordDays { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SystemEmailSendConfig
|
||||||
|
{
|
||||||
|
public int Port { get; set; }
|
||||||
|
|
||||||
|
public string Host { get; set; } = string.Empty;
|
||||||
|
public string FromEmail { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public string FromName { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public string AuthorizationCode { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public string SiteUrl { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public string OrganizationName { get; set; } = string.Empty;
|
||||||
|
public string OrganizationNameCN { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public string CompanyName { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public string CompanyNameCN { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public string CompanyShortName { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public string CompanyShortNameCN { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public bool IsEnv_US { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SystemEmailSendConfigView
|
||||||
|
{
|
||||||
|
public string CompanyName { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public string CompanyNameCN { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public string CompanyShortName { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public string CompanyShortNameCN { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SystemPacsConfig
|
||||||
|
{
|
||||||
|
public int Port { get; set; }
|
||||||
|
|
||||||
|
public string IP { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
|
public class IRCEncreptOption
|
||||||
|
{
|
||||||
|
public string Base64RSAPublicKey { get; set; }
|
||||||
|
|
||||||
|
public string Base64RSAPrivateKey { get; set; }
|
||||||
|
public bool IsResponseEncreptEnable { get; set; }
|
||||||
|
|
||||||
|
public List<string> ApiPathList { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class IRaCISBasicConfigOption
|
||||||
|
{
|
||||||
|
public string DoctorCodePrefix { get; set; }
|
||||||
|
public string UserCodePrefix { get; set; }
|
||||||
|
public string QCChallengeCodePrefix { get; set; }
|
||||||
|
public string DicomStudyCodePrefix { get; set; }
|
||||||
|
public string TaskStudyCodePrefix { get; set; }
|
||||||
|
public string NoneDicomStudyCodePrefix { get; set; }
|
||||||
|
public int ImageShareExpireDays { get; set; }
|
||||||
|
public string SystemSiteCodePrefix { get; set; }
|
||||||
|
public string BlindTaskPrefix { get; set; }
|
||||||
|
public string DefaultPassword { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
[Description("项目基础配置规则")]
|
||||||
|
public static class AppSettings
|
||||||
|
{
|
||||||
|
|
||||||
|
public static IRaCISBasicConfigOption IRaCISBasicConfig { get; set; }
|
||||||
|
|
||||||
|
static AppSettings()
|
||||||
|
{
|
||||||
|
var configuration = new ConfigurationBuilder()
|
||||||
|
.Add(new JsonConfigurationSource
|
||||||
|
{
|
||||||
|
Path = "appsettings.json",
|
||||||
|
ReloadOnChange = true
|
||||||
|
})
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
// 手动绑定配置
|
||||||
|
var appSettings = new IRaCISBasicConfigOption();
|
||||||
|
configuration.GetSection("IRaCISBasicConfig").Bind(appSettings);
|
||||||
|
|
||||||
|
IRaCISBasicConfig = appSettings;
|
||||||
|
}
|
||||||
|
//获取实体编码字符串
|
||||||
|
public static string GetCodeStr(int codeInt, string typeStr)
|
||||||
|
{
|
||||||
|
switch (typeStr)
|
||||||
|
{
|
||||||
|
case nameof(Doctor):
|
||||||
|
return IRaCISBasicConfig.DoctorCodePrefix + codeInt.ToString("D4");
|
||||||
|
|
||||||
|
case nameof(User):
|
||||||
|
return IRaCISBasicConfig.UserCodePrefix + codeInt.ToString("D4");
|
||||||
|
|
||||||
|
case nameof(QCChallenge):
|
||||||
|
return IRaCISBasicConfig.QCChallengeCodePrefix + codeInt.ToString("D5");
|
||||||
|
|
||||||
|
case nameof(NoneDicomStudy):
|
||||||
|
return IRaCISBasicConfig.NoneDicomStudyCodePrefix + codeInt.ToString("D5");
|
||||||
|
|
||||||
|
case nameof(DicomStudy):
|
||||||
|
return IRaCISBasicConfig.DicomStudyCodePrefix + codeInt.ToString("D5");
|
||||||
|
|
||||||
|
case nameof(TaskStudy):
|
||||||
|
return IRaCISBasicConfig.TaskStudyCodePrefix + codeInt.ToString("D5");
|
||||||
|
|
||||||
|
case nameof(VisitTask):
|
||||||
|
return "W" + codeInt.ToString("D5");
|
||||||
|
|
||||||
|
case nameof(Site):
|
||||||
|
return IRaCISBasicConfig.SystemSiteCodePrefix + codeInt.ToString("D4");
|
||||||
|
|
||||||
|
default:
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
global using AutoMapper.QueryableExtensions;
|
||||||
|
global using IRaCIS.Core.Application.Service;
|
||||||
|
global using IRaCIS.Core.Domain.Models;
|
||||||
|
global using IRaCIS.Core.Infra.EFCore;
|
||||||
|
global using IRaCIS.Core.Infrastructure.Extention;
|
||||||
|
global using Microsoft.EntityFrameworkCore;
|
||||||
|
global using System.Linq.Expressions;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user