Compare commits
1112 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 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 | |||
| 65545dd3e5 | |||
| 5b92015c98 | |||
| 64c97b22d5 | |||
| 87adbe9bed | |||
| c6ed8b8318 | |||
| 4d2890976a | |||
| 9cfddf1327 | |||
| 84203d5aa2 | |||
| 4d19fffc5f | |||
| 18683390b7 | |||
| fb137ae840 | |||
| c8bc0c4a32 | |||
| 8ded82fa81 | |||
| acd1f47f4a | |||
| 054827bbae | |||
| a5ebe97dc5 | |||
| 02b2d8f754 | |||
| cf5da8f55a | |||
| 40947de05a | |||
| 662648691d | |||
| 29836a9c62 | |||
| a2b1a0d64b | |||
| 87fbd2cb59 | |||
| c91e21c23a | |||
| 0f25c7db0e | |||
| e5b5cbb8d8 | |||
| 9c1af1b8dc | |||
| 0da09da45e | |||
| 87aaefadf6 | |||
| b5b739a5cb | |||
| 1315d5dc29 | |||
| 72b70f5311 | |||
| fd15a62148 | |||
| e9bd625195 | |||
| c37fcf588c | |||
| 3f8d5027c8 | |||
| 972fc32eeb | |||
| 1ba9efe894 | |||
| f85ac8a813 | |||
| 7bf097c127 | |||
| fc579d9cf4 | |||
| b1c0933428 | |||
| bd7dd906fb | |||
| 3a8e400f33 | |||
| c6dd323b80 | |||
| dfb45d8391 | |||
| 0b0df00525 | |||
| 96fc5ae78c | |||
| a4b2d68995 | |||
| 70ababc2d9 | |||
| e57887eba5 | |||
| aef7fded18 | |||
| e0613c1f2d | |||
| f6d323cc31 | |||
| aa85562076 | |||
| 0165688799 | |||
| c2aefebedc | |||
| 1c5e4454da | |||
| 65e2a45d74 | |||
| 99026d6483 | |||
| 9190783ac4 | |||
| ae8f50dc14 | |||
| 2a51de03b7 | |||
| 2be9daa443 | |||
| 2778b3ea7b | |||
| 4cedb9dbcb | |||
| ed6a6fb844 | |||
| 8daf02246e | |||
| b3178e6f8d | |||
| 00cbde90c7 | |||
| 938175c49f | |||
| 146fc916d1 | |||
| 87776513fb | |||
| 60d462e5f9 | |||
| b39d8dd546 | |||
| b65635c41c | |||
| fe2539afba | |||
| d2ff216396 | |||
| 4c05da8335 | |||
| 6061cf5d93 | |||
| d313b7d8a6 | |||
| 71c21ccc00 | |||
| f415c03daa | |||
| 2bfef364ec | |||
| c822d20d0d | |||
| 3a996212ed | |||
| 5a3b6a5f93 | |||
| 3f7227ce32 | |||
| af0df80874 | |||
| a163d8a785 | |||
| 70fbf8d352 | |||
| d9a8692be8 | |||
| 56fb9c14d1 | |||
| 8bc0cee81f | |||
| dc86767184 | |||
| e1383445e2 | |||
| 0a30107859 | |||
| ba27925409 | |||
| 3a52021cdc | |||
| aba4fc70c2 | |||
| 5b89343192 | |||
| 20f9e90a69 | |||
| 5c58171ac3 | |||
| e98d80eea6 | |||
| c14d5be4ac | |||
| fea108371f | |||
| dd47bf05f8 | |||
| 3a160c1913 | |||
| c42c26d76a | |||
| f7117563f7 | |||
| 39968194b1 | |||
| 0d5f7e5673 | |||
| 7f4e111f7d | |||
| 0c92fcb011 | |||
| 2f4418e3ed | |||
| 75a3d7c80f | |||
| 4605053e50 | |||
| a8da0c0b55 | |||
| ba984f9263 | |||
| 451a311142 | |||
| f86bc219cf | |||
| 06bd63b6d1 | |||
| be088d8ba2 | |||
| c718ec8446 | |||
| 668e1019f0 | |||
| e2f5a7fae1 | |||
| f1a141c018 | |||
| 30a0f428bc | |||
| 7a8346621d | |||
| 6d93054d3d | |||
| 5c53e78aca | |||
| 1eeb832b0e | |||
| c8db9431b2 | |||
| ce8e0e3972 | |||
| ed4192838d | |||
| d900c368e2 | |||
| 6926bb877c | |||
| cb9b54a089 | |||
| 57c5d14f5a | |||
| 4f19d1638d | |||
| ace39a7f94 | |||
| e8e0540cc2 | |||
| a98799daaf | |||
| f2b53cd359 | |||
| 960f479b91 | |||
| d6dacb93dd | |||
| 8823656231 | |||
| 2c859e7186 | |||
| 99828dd82e | |||
| 855242e6fe | |||
| 90a1a920fa | |||
| c73968f490 | |||
| 282368b9e0 | |||
| 726a2be71e | |||
| 510104d0ff | |||
| 12318937f4 | |||
| 8f17d4b110 | |||
| 18496246b2 | |||
| 55f60e897a | |||
| dd2d142340 | |||
| 49a0cf3ece | |||
| f0d4a1c075 | |||
| 5e6db93250 | |||
| 12adf2f3a9 | |||
| b08908b1dc | |||
| 8f08746377 | |||
| 045e9346ad | |||
| 864ab17ff3 | |||
| 700d111011 | |||
| 7168d68997 | |||
| 40ec535e8a | |||
| 4dfaf29f3d | |||
| 702cd361fe | |||
| b776103328 | |||
| 381e7b5046 | |||
| ebdec8dd62 | |||
| 6677f2a01e | |||
| 5aaa9043ff | |||
| 48874adda3 | |||
| 0cf5a88d63 | |||
| 4b92874ca6 | |||
| 2d81b05fba | |||
| 78756aa2f4 | |||
| a15c91ebcb | |||
| 4b389c8293 | |||
| 2313b5e907 | |||
| e22941d947 | |||
| 3efc173056 | |||
| 6389919cfd | |||
| 984b840fa7 | |||
| 2bf01fbf0c | |||
| 116eeb753d | |||
| 82a6237323 | |||
| 6220a96666 | |||
| 259ab2c569 | |||
| efda4e0fdd | |||
| 968264fe9a | |||
| 57911cf567 | |||
| a61d9ae4d1 | |||
| 5735383cd7 | |||
| e3607895b4 | |||
| 16a2e0b53f | |||
| 41e5f9fd78 | |||
| 64b750b06e | |||
| ca90736366 | |||
| 664b308e31 | |||
| 8de0671d31 | |||
| 19d7192842 | |||
| c3ed54f05f | |||
| ef6655a014 | |||
| 234e549b22 | |||
| 903f72989a | |||
| 3e9900cc45 | |||
| 78d0cffd3d | |||
| 658e8d983c | |||
| 8174237a82 | |||
| 39a5e8eade | |||
| 54dea66f5f | |||
| 86fd9ddc0e | |||
| b071d1126b | |||
| 139e9daec0 | |||
| 4eeda2b3e7 | |||
| fa64b42a84 | |||
| ce67f4828f | |||
| 87e3d953d3 | |||
| 5d4668eaee | |||
| cfce8dba1f | |||
| c11e319472 | |||
| 7c7a0cd20b | |||
| a66bb42126 | |||
| 7d63f3f5cb | |||
| b102d23477 | |||
| e19c03412d | |||
| 56cba570bf | |||
| c7abcc153e | |||
| 967a77f371 | |||
| 2060f2c62e | |||
| 193c107539 | |||
| 2fe0851786 | |||
| 46b68ca2c9 | |||
| 396797dc0c | |||
| 5579f543de | |||
| ce31ab3097 | |||
| daa4764aac | |||
| a24c6775e8 | |||
| eb9de1769e | |||
| d675cc50b3 | |||
| 350d089c39 | |||
| 116969f4f2 | |||
| 112d9f9a63 | |||
| 5dae9c5d06 | |||
| c0a63ecfed | |||
| 881a5320a8 | |||
| 22a254890e | |||
| 0912909b68 | |||
| cd7318dd63 | |||
| 21cc610095 | |||
| cc8151caf8 | |||
| 8cce399430 | |||
| 99688df446 | |||
| de0a278cac | |||
| 7031ea1a04 | |||
| 541455a08b | |||
| 814efc4e35 | |||
| f85bbc41c4 | |||
| c00e8eb809 | |||
| 9dd00170af | |||
| fdba16de86 | |||
| 38f319b425 | |||
| d35046b277 | |||
| bdcaf2f2be | |||
| bba622da0e | |||
| 0d4f694fcf | |||
| a41824414e | |||
| c54b0abbd7 | |||
| d772ea527a | |||
| 62b76220d4 | |||
| 6aa8628c0c | |||
| 7d9f4864d0 | |||
| d149d5a5d5 | |||
| 7572bf9fb0 | |||
| 40ab925a4e | |||
| 75a989b4f1 | |||
| f98f89a51d | |||
| 423ea4bc6f | |||
| 3c60dd9ae8 | |||
| f9006be79b | |||
| 0492563838 | |||
| d58089b9ee | |||
| e17a33a16f | |||
| c935599fcf | |||
| efafdc50b8 | |||
| 8846c1d728 | |||
| 9bfd89cb9b | |||
| 35f01818dd | |||
| 875af7e9f7 | |||
| 7dfce2db55 | |||
| cab5772068 | |||
| 6319f1e82d | |||
| d42c8477aa | |||
| b559a7882b | |||
| 30e1d70099 | |||
| 84a8d18519 | |||
| 1fc20e5640 | |||
| d8f0fba8e2 | |||
| 2921404540 | |||
| df79b7b57b | |||
| 0a45638bac | |||
| 4f725af6cd | |||
| 44ae7bde56 | |||
| cd5e0405de | |||
| 5bbf2d3c82 | |||
| 09743f7153 | |||
| f88ef2f3fc | |||
| ef032b548e | |||
| db2f3cc85a | |||
| 7584f4a5b4 | |||
| aac559e221 | |||
| aee5c17591 | |||
| d8248a7973 | |||
| b68d060f85 | |||
| 74399df5eb | |||
| c3b62137d8 | |||
| 352bd23766 | |||
| 398344854b | |||
| e181c3cc82 | |||
| 24e4008b72 | |||
| 50d9ed7e3c | |||
| 9fd113c95f | |||
| 5683ced93d | |||
| 4360f4b2cb | |||
| 43a873defd | |||
| e5d08cdb3e | |||
| c78b80c679 | |||
| 9cd3ba1c67 | |||
| dd22b206dc | |||
| 12b668642c | |||
| 77fa881d69 | |||
| 05dcbfed2e | |||
| 6471041fad | |||
| 69ab56f878 | |||
| 97538333c0 | |||
| a094e1a2b7 | |||
| d0c8df1452 | |||
| 1b6a31e626 | |||
| 2c1cde9f69 | |||
| 1aa5e45694 | |||
| 9fc04193c7 | |||
| b685b9fa13 | |||
| 59f97d0c2d | |||
| 8f94460537 | |||
| 26fe947991 | |||
| 16829f9075 | |||
| 246314561b | |||
| 331a3c4178 | |||
| 35ba7861d1 | |||
| 59cb66331e | |||
| 782bfaa6c4 | |||
| 830af4a164 | |||
| 994cb73104 | |||
| 8e3dc73408 | |||
| 6792f9e916 | |||
| a350c35f49 | |||
| fb498b3723 | |||
| 7b9c90b77c | |||
| f4fd16d8fd | |||
| 5656e66c28 | |||
| 39882e43ce | |||
| e5f2d1cde9 | |||
| 4f978863e4 | |||
| f26aafa8cb | |||
| e444354cf7 | |||
| 0e4ae72016 | |||
| c1121a2d3f | |||
| 1737825506 | |||
| 437c569617 | |||
| 9580d77512 | |||
| f7fb89313c | |||
| 721a7d2341 | |||
| 3529ab6cb6 | |||
| 484bb3ca5b | |||
| 121b2d5df1 | |||
| 472887d627 | |||
| 03f514d5fe | |||
| 36d2203e4c | |||
| 4e11cc7db3 | |||
| f23963896d | |||
| fd2b13b391 | |||
| b90743ded1 | |||
| ee499d9352 | |||
| d12f0bddad | |||
| 9eabd9031b | |||
| 552e7a430d | |||
| b97f9f8f12 | |||
| 8be0cea193 | |||
| bce088bcdf | |||
| 6955c42c8b | |||
| 07b36279cf | |||
| 5c1dc0754b | |||
| 7eaa644028 | |||
| 1b29312bef | |||
| 930412bf21 | |||
| 0e1f4931eb | |||
| 2e2c4b4397 | |||
| 7665cc7f73 | |||
| c743335d13 | |||
| e9aeb1c918 | |||
| c092e9a8d0 | |||
| a55ec01992 | |||
| 302e503e02 | |||
| f82639cdf3 | |||
| 9172d8c5f8 | |||
| 69fd8ec123 | |||
| d9c7a512e9 | |||
| 38383f34ff | |||
| b7585fe022 | |||
| b7d9009ff4 | |||
| 9c26745c2a | |||
| 67f8e57ab6 | |||
| b2ef603729 | |||
| 110ea953c4 | |||
| cef07a5bff | |||
| a5899023b9 | |||
| 61530be208 | |||
| 292bcd7100 | |||
| 8f4a6fcf80 | |||
| dfdb3b5951 | |||
| 0cc7b373b7 | |||
| 7e5cbd92b4 | |||
| 2594ecd27e | |||
| edf8af3e4d | |||
| 5a76275571 | |||
| b147ee1c99 | |||
| 8b7cc8a220 | |||
| 88931a4a56 | |||
| df187bed58 | |||
| 8b86bc6b3f | |||
| f9ac64023a | |||
| 3a710e1e50 | |||
| 1d4f58f362 | |||
| abfd0a0a2e | |||
| 98839b15dc | |||
| 5b885e98cc | |||
| 58e29608e5 | |||
| db71cfd396 | |||
| 0de9cfb240 | |||
| 2a8efb246d | |||
| 071cf4eed8 | |||
| 87a8d8d490 | |||
| d3caa1dda9 | |||
| f832d44f3a | |||
| 103ab700fc | |||
| 6419fa6e61 | |||
| 98cf2adf92 | |||
| 5660d2e83b | |||
| 5e3e242981 | |||
| 65c31fb457 | |||
| 0f891075cc | |||
| 6d898367e4 | |||
| 003e63b7fb | |||
| 73c9e15976 | |||
| 1dc1d673dd | |||
| 46818d9a02 | |||
| d6a585c6cc | |||
| c54fcf920c | |||
| 467c7f77f5 | |||
| 3c51287178 | |||
| 9d8fdcfa12 | |||
| fadd8a7f79 | |||
| f2afb7ab1a | |||
| b52d8785bb | |||
| 70846525bf | |||
| abe77916a3 | |||
| 660846a0d8 | |||
| 8dec77f30a | |||
| 5812c8f939 | |||
| 1c673e11ac | |||
| bf54bcfd9b | |||
| 33a0b8f11f | |||
| 61ffdabc51 | |||
| 2f224047bc | |||
| 2747fa82e3 | |||
| 5dd80c91cf | |||
| 495e602599 | |||
| 545ddd4ec9 | |||
| 9fff53f137 | |||
| 1d38fc0879 | |||
| 48f44b8920 | |||
| e365818ef5 | |||
| c2bb3010cc | |||
| 4ca8003d24 | |||
| 6a212926e0 | |||
| dae5c0440f | |||
| 6b1d083faa | |||
| c8510bea2a | |||
| a00e84018a | |||
| 3435806020 | |||
| 27f0a0fcfa | |||
| 1f4bb0128a | |||
| e686b6b7d4 | |||
| b74d46b425 |
@@ -0,0 +1,30 @@
|
||||
**/.classpath
|
||||
**/.dockerignore
|
||||
**/.env
|
||||
**/.git
|
||||
**/.gitignore
|
||||
**/.project
|
||||
**/.settings
|
||||
**/.toolstarget
|
||||
**/.vs
|
||||
**/.vscode
|
||||
**/*.*proj.user
|
||||
**/*.dbmdl
|
||||
**/*.jfm
|
||||
**/azds.yaml
|
||||
**/bin
|
||||
**/charts
|
||||
**/docker-compose*
|
||||
**/Dockerfile*
|
||||
**/node_modules
|
||||
**/npm-debug.log
|
||||
**/obj
|
||||
**/secrets.dev.yaml
|
||||
**/values.dev.yaml
|
||||
LICENSE
|
||||
README.md
|
||||
!**/.gitignore
|
||||
!.git/HEAD
|
||||
!.git/config
|
||||
!.git/packed-refs
|
||||
!.git/refs/heads/**
|
||||
@@ -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.Application.Services.BusinessFilter
|
||||
{
|
||||
/// <summary>
|
||||
/// 统一返回前端数据包装,之前在控制器包装,现在修改为动态Api 在ResultFilter这里包装,减少重复冗余代码
|
||||
/// by zhouhang 2021.09.12 周末
|
||||
/// </summary>
|
||||
public class UnifiedApiResultFilter : Attribute, IAsyncResultFilter
|
||||
{
|
||||
/// <summary>
|
||||
/// 异步版本
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <param name="next"></param>
|
||||
/// <returns></returns>
|
||||
public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next)
|
||||
{
|
||||
|
||||
if (context.Result is ObjectResult objectResult)
|
||||
{
|
||||
var statusCode = objectResult.StatusCode ?? context.HttpContext.Response.StatusCode;
|
||||
|
||||
//是200 并且没有包装 那么包装结果
|
||||
if (statusCode == 200 && !(objectResult.Value is IResponseOutput))
|
||||
{
|
||||
//if (objectResult.Value == null)
|
||||
//{
|
||||
// var apiResponse = ResponseOutput.DBNotExist();
|
||||
|
||||
// objectResult.Value = apiResponse;
|
||||
// objectResult.DeclaredType = apiResponse.GetType();
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
|
||||
var type = objectResult.Value?.GetType();
|
||||
|
||||
|
||||
if ( type!=null&& type.IsGenericType&&(type.GetGenericTypeDefinition()==typeof(ValueTuple<,>)|| type.GetGenericTypeDefinition()==typeof(Tuple<,>)))
|
||||
{
|
||||
|
||||
//报错
|
||||
//var tuple = (object, object))objectResult.Value;
|
||||
|
||||
//var (val1, val2) = ((dynamic, dynamic))objectResult.Value;
|
||||
//var apiResponse = ResponseOutput.Ok(val1, val2);
|
||||
|
||||
//OK
|
||||
var tuple = (dynamic)objectResult.Value;
|
||||
var apiResponse = ResponseOutput.Ok(tuple.Item1, tuple.Item2);
|
||||
|
||||
|
||||
objectResult.Value = apiResponse;
|
||||
objectResult.DeclaredType = apiResponse.GetType();
|
||||
}
|
||||
else
|
||||
{
|
||||
var apiResponse = ResponseOutput.Ok(objectResult.Value);
|
||||
|
||||
objectResult.Value = apiResponse;
|
||||
objectResult.DeclaredType = apiResponse.GetType();
|
||||
}
|
||||
|
||||
|
||||
//}
|
||||
|
||||
}
|
||||
//如果不是200 是IResponseOutput 不处理
|
||||
else if (statusCode != 200 && (objectResult.Value is IResponseOutput))
|
||||
{
|
||||
}
|
||||
|
||||
else if(statusCode != 200&&!(objectResult.Value is IResponseOutput))
|
||||
{
|
||||
//---程序错误,请联系开发人员。
|
||||
var apiResponse = ResponseOutput.NotOk(StaticData.International("UnifiedAPI_ProgramError"));
|
||||
|
||||
objectResult.Value = apiResponse;
|
||||
objectResult.DeclaredType = apiResponse.GetType();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
await next.Invoke();
|
||||
|
||||
}
|
||||
|
||||
public static bool IsTupleType(Type type, bool checkBaseTypes = false)
|
||||
{
|
||||
if (type == null)
|
||||
throw new ArgumentNullException(nameof(type));
|
||||
|
||||
if (type == typeof(Tuple))
|
||||
return true;
|
||||
|
||||
while (type != null)
|
||||
{
|
||||
if (type.IsGenericType)
|
||||
{
|
||||
var genType = type.GetGenericTypeDefinition();
|
||||
if (genType == typeof(Tuple<>)
|
||||
|| genType == typeof(Tuple<,>)
|
||||
|| genType == typeof(Tuple<,>))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!checkBaseTypes)
|
||||
break;
|
||||
|
||||
type = type.BaseType;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.Application.Services;
|
||||
using AutoMapper;
|
||||
using IRaCIS.Core.SCP.Service;
|
||||
|
||||
namespace IRaCIS.Core.SCP
|
||||
{
|
||||
// ReSharper disable once IdentifierTypo
|
||||
public class AutofacModuleSetup : Autofac.Module
|
||||
{
|
||||
protected override void Load(ContainerBuilder containerBuilder)
|
||||
{
|
||||
|
||||
#region byzhouhang 20210917 此处注册泛型仓储 可以减少Domain层 和Infra.EFcore 两层 空的仓储接口定义和 仓储文件定义
|
||||
|
||||
containerBuilder.RegisterGeneric(typeof(Repository<>))
|
||||
.As(typeof(IRepository<>)).InstancePerLifetimeScope();//注册泛型仓储
|
||||
|
||||
containerBuilder.RegisterType<Repository>().As<IRepository>().InstancePerLifetimeScope();
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region 指定控制器也由autofac 来进行实例获取 https://www.cnblogs.com/xwhqwer/p/15320838.html
|
||||
|
||||
//获取所有控制器类型并使用属性注入
|
||||
containerBuilder.RegisterAssemblyTypes(typeof(BaseService).Assembly)
|
||||
.Where(type => typeof(IDynamicWebApi).IsAssignableFrom(type))
|
||||
.PropertiesAutowired();
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
Assembly application = Assembly.LoadFrom(AppDomain.CurrentDomain.BaseDirectory + typeof(BaseService).Assembly.GetName().Name+".dll");
|
||||
containerBuilder.RegisterAssemblyTypes(application).Where(t => t.FullName.Contains("Service"))
|
||||
.PropertiesAutowired().AsImplementedInterfaces();
|
||||
|
||||
|
||||
//containerBuilder.RegisterType<HttpContextAccessor>().As<IHttpContextAccessor>().SingleInstance();
|
||||
//containerBuilder.RegisterType<UserInfo>().As<IUserInfo>().InstancePerLifetimeScope();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,37 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Aliyun.OSS.SDK.NetCore" Version="2.13.0" />
|
||||
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="9.0.0" />
|
||||
<PackageReference Include="DistributedLock.Core" Version="1.0.6" />
|
||||
<PackageReference Include="DistributedLock.SqlServer" Version="1.0.4" />
|
||||
<PackageReference Include="fo-dicom" Version="5.1.2" />
|
||||
<PackageReference Include="fo-dicom.Codecs" Version="5.12.0" />
|
||||
<PackageReference Include="fo-dicom.Imaging.ImageSharp" Version="5.1.2" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.4" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.20.1" />
|
||||
<PackageReference Include="AutoMapper" Version="13.0.1" />
|
||||
<PackageReference Include="Minio" Version="6.0.2" />
|
||||
<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.0.3" />
|
||||
<PackageReference Include="Serilog.Extensions.Hosting" Version="8.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.Console" Version="5.0.1" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
|
||||
</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,214 @@
|
||||
|
||||
using Autofac;
|
||||
using Autofac.Extensions.DependencyInjection;
|
||||
using AutoMapper.EquivalencyExpression;
|
||||
using FellowOakDicom;
|
||||
using FellowOakDicom.Imaging;
|
||||
using FellowOakDicom.Imaging.NativeCodec;
|
||||
using FellowOakDicom.Network;
|
||||
using IRaCIS.Core.Infra.EFCore;
|
||||
using IRaCIS.Core.SCP;
|
||||
using IRaCIS.Core.SCP.Filter;
|
||||
using IRaCIS.Core.SCP.Service;
|
||||
using MassTransit;
|
||||
using MassTransit.NewIdProviders;
|
||||
using Microsoft.AspNetCore.HttpOverrides;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Panda.DynamicWebApi;
|
||||
using Serilog;
|
||||
using Serilog.Events;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
|
||||
//以配置文件为准,否则 从url中取环境值(服务以命令行传递参数启动,配置文件配置了就不需要传递环境参数)
|
||||
var config = new ConfigurationBuilder()
|
||||
.AddEnvironmentVariables()
|
||||
.Build();
|
||||
|
||||
var enviromentName = config["ASPNETCORE_ENVIRONMENT"];
|
||||
|
||||
var builder = WebApplication.CreateBuilder(new WebApplicationOptions
|
||||
{
|
||||
EnvironmentName = enviromentName
|
||||
});
|
||||
|
||||
#region 兼容windows 服务命令行的方式
|
||||
|
||||
int urlsIndex = Array.FindIndex(args, arg => arg != null && arg.StartsWith("--port"));
|
||||
|
||||
if (urlsIndex > -1)
|
||||
{
|
||||
var port = args[urlsIndex].Substring("--port=".Length);
|
||||
Console.WriteLine(port);
|
||||
builder.WebHost.UseUrls($"http://0.0.0.0:{port}");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 主机配置
|
||||
|
||||
NewId.SetProcessIdProvider(new CurrentProcessIdProvider());
|
||||
|
||||
builder.Configuration.AddJsonFile("appsettings.json", false, true)
|
||||
.AddJsonFile($"appsettings.{enviromentName}.json", false, true);
|
||||
builder.Host
|
||||
.UseServiceProviderFactory(new AutofacServiceProviderFactory())
|
||||
.ConfigureContainer<ContainerBuilder>(containerBuilder =>
|
||||
{
|
||||
containerBuilder.RegisterModule<AutofacModuleSetup>();
|
||||
})
|
||||
.UseSerilog();
|
||||
#endregion
|
||||
|
||||
#region 配置服务
|
||||
var _configuration = builder.Configuration;
|
||||
|
||||
//健康检查
|
||||
builder.Services.AddHealthChecks();
|
||||
|
||||
//本地化
|
||||
builder.Services.AddJsonLocalization(options => options.ResourcesPath = "Resources");
|
||||
|
||||
|
||||
// 异常、参数统一验证过滤器、Json序列化配置、字符串参数绑型统一Trim()
|
||||
builder.Services.AddControllers(options =>
|
||||
{
|
||||
options.Filters.Add<ModelActionFilter>();
|
||||
options.Filters.Add<ProjectExceptionFilter>();
|
||||
options.Filters.Add<UnitOfWorkFilter>();
|
||||
|
||||
|
||||
})
|
||||
.AddNewtonsoftJsonSetup(builder.Services); // NewtonsoftJson 序列化 处理
|
||||
|
||||
|
||||
builder.Services.AddOptions().Configure<AliyunOSSOptions>(_configuration.GetSection("AliyunOSS"));
|
||||
builder.Services.AddOptions().Configure<ObjectStoreServiceOptions>(_configuration.GetSection("ObjectStoreService"));
|
||||
builder.Services.AddOptions().Configure<DicomSCPServiceOption>(_configuration.GetSection("DicomSCPServiceConfig"));
|
||||
|
||||
|
||||
//动态WebApi + UnifiedApiResultFilter 省掉控制器代码
|
||||
//动态webApi 目前存在的唯一小坑是生成api上服务上的动态代理AOP失效 间接掉用不影响
|
||||
builder.Services
|
||||
.AddDynamicWebApi(dynamicWebApiOption =>
|
||||
{
|
||||
//默认是 api
|
||||
dynamicWebApiOption.DefaultApiPrefix = "";
|
||||
//首字母小写
|
||||
dynamicWebApiOption.GetRestFulActionName = (actionName) => char.ToLower(actionName[0]) + actionName.Substring(1);
|
||||
//删除 Service后缀
|
||||
dynamicWebApiOption.RemoveControllerPostfixes.Add("Service");
|
||||
|
||||
});
|
||||
|
||||
//AutoMapper
|
||||
builder.Services.AddAutoMapper(automapper =>
|
||||
{
|
||||
|
||||
automapper.AddCollectionMappers();
|
||||
|
||||
|
||||
}, typeof(BaseService).Assembly);
|
||||
|
||||
//EF ORM QueryWithNoLock
|
||||
builder.Services.AddEFSetup(_configuration);
|
||||
|
||||
|
||||
//转发头设置 获取真实IP
|
||||
builder.Services.Configure<ForwardedHeadersOptions>(options =>
|
||||
{
|
||||
options.ForwardedHeaders =
|
||||
ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
|
||||
});
|
||||
|
||||
//Dicom影像渲染图片 跨平台
|
||||
//builder.Services.AddDicomSetup();
|
||||
new DicomSetupBuilder()
|
||||
.RegisterServices(s =>
|
||||
s.AddFellowOakDicom()
|
||||
.AddTranscoderManager<NativeTranscoderManager>()
|
||||
//.AddTranscoderManager<FellowOakDicom.Imaging.NativeCodec.NativeTranscoderManager>()
|
||||
.AddImageManager<ImageSharpImageManager>())
|
||||
.SkipValidation()
|
||||
.Build();
|
||||
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
// Add services to the container.
|
||||
|
||||
builder.Services.AddControllers();
|
||||
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
|
||||
builder.Services.AddEndpointsApiExplorer();
|
||||
builder.Services.AddSwaggerGen();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
// Configure the HTTP request pipeline.
|
||||
//if (app.Environment.IsDevelopment())
|
||||
//{
|
||||
app.UseSwagger();
|
||||
app.UseSwaggerUI();
|
||||
//}
|
||||
|
||||
app.UseAuthorization();
|
||||
|
||||
app.MapControllers();
|
||||
|
||||
#region 日志
|
||||
|
||||
//Log.Logger = new LoggerConfiguration()
|
||||
// .MinimumLevel.Information()
|
||||
// .MinimumLevel.Override("Microsoft", LogEventLevel.Information)
|
||||
// // Filter out ASP.NET Core infrastructre logs that are Information and below 日志太多了 一个请求 记录好几条
|
||||
// .MinimumLevel.Override("Microsoft.AspNetCore", LogEventLevel.Warning)
|
||||
// .MinimumLevel.Override("Hangfire", LogEventLevel.Warning)
|
||||
// .MinimumLevel.Override("System.Net.Http.HttpClient.HttpReports", LogEventLevel.Warning)
|
||||
// .Enrich.WithClientIp()
|
||||
|
||||
// .Enrich.FromLogContext()
|
||||
|
||||
// //控制台 方便调试 问题 我们显示记录日志 时 获取上下文的ip 和用户名 用户类型
|
||||
// .WriteTo.Console(restrictedToMinimumLevel: LogEventLevel.Warning,
|
||||
// outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {SourceContext:l} || {Message} || {Exception} ||end {NewLine}")
|
||||
|
||||
// .WriteTo.File($"{AppContext.BaseDirectory}Serilogs/.log", rollingInterval: RollingInterval.Day,
|
||||
// outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {SourceContext:l} || {Message} || {Exception} ||end {NewLine}")
|
||||
// .CreateLogger();
|
||||
|
||||
Log.Logger = new LoggerConfiguration()
|
||||
//.MinimumLevel.Information()
|
||||
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
|
||||
.WriteTo.Console()
|
||||
.WriteTo.File($"{AppContext.BaseDirectory}Serilogs/.log", rollingInterval: RollingInterval.Day)
|
||||
.CreateLogger();
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region 运行环境 部署平台
|
||||
|
||||
Log.Logger.Warning($"当前环境:{enviromentName}");
|
||||
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
Log.Logger.Warning($"当前部署平台环境:windows");
|
||||
}
|
||||
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
||||
{
|
||||
Log.Logger.Warning($"当前部署平台环境:linux");
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Logger.Warning($"当前部署平台环境:OSX or FreeBSD");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
var logger = app.Services.GetService<Microsoft.Extensions.Logging.ILogger<Program>>();
|
||||
|
||||
var server = DicomServerFactory.Create<CStoreSCPService>(_configuration.GetSection("DicomSCPServiceConfig").GetValue<int>("ServerPort"), userState: app.Services,logger: logger);
|
||||
|
||||
|
||||
app.Run();
|
||||
@@ -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,118 @@
|
||||
using AutoMapper;
|
||||
using IRaCIS.Application.Services.BusinessFilter;
|
||||
using IRaCIS.Core.Infra.EFCore;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Panda.DynamicWebApi;
|
||||
using Panda.DynamicWebApi.Attributes;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using IRaCIS.Core.Infrastructure.Extention;
|
||||
using IRaCIS.Core.Domain.Models;
|
||||
|
||||
namespace IRaCIS.Core.SCP.Service
|
||||
{
|
||||
|
||||
#pragma warning disable CS8618
|
||||
|
||||
|
||||
#region 非泛型版本
|
||||
|
||||
[Authorize, DynamicWebApi, UnifiedApiResultFilter]
|
||||
public class BaseService : IBaseService, IDynamicWebApi
|
||||
{
|
||||
public IMapper _mapper { get; set; }
|
||||
|
||||
public IUserInfo _userInfo { get; set; }
|
||||
|
||||
public IRepository _repository { 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(_repository))]
|
||||
public IRepository _repository { 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(_repository))]
|
||||
public IRepository _repository { 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 IRepository _repository { 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,370 @@
|
||||
using FellowOakDicom.Network;
|
||||
using FellowOakDicom;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using IRaCIS.Core.SCP.Service;
|
||||
using IRaCIS.Core.Domain.Models;
|
||||
using IRaCIS.Core.Infra.EFCore;
|
||||
using Medallion.Threading;
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using Serilog;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion.Internal;
|
||||
using Microsoft.Extensions.Options;
|
||||
using System.Data;
|
||||
using FellowOakDicom.Imaging;
|
||||
using SharpCompress.Common;
|
||||
using SixLabors.ImageSharp.Formats.Jpeg;
|
||||
using IRaCIS.Core.Infrastructure;
|
||||
|
||||
namespace IRaCIS.Core.SCP.Service
|
||||
{
|
||||
|
||||
public class DicomSCPServiceOption
|
||||
{
|
||||
public List<string> CalledAEList { get; set; }
|
||||
|
||||
public string ServerPort { get; set; }
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public class CStoreSCPService : DicomService, IDicomServiceProvider, IDicomCStoreProvider, IDicomCEchoProvider
|
||||
{
|
||||
private IServiceProvider _serviceProvider { get; set; }
|
||||
|
||||
private List<Guid> _SCPStudyIdList { get; set; } = new List<Guid>();
|
||||
|
||||
private SCPImageUpload _upload { get; set; }
|
||||
|
||||
private Guid _trialId { get; set; }
|
||||
|
||||
private Guid _trialSiteId { get; set; }
|
||||
|
||||
|
||||
|
||||
private static readonly DicomTransferSyntax[] _acceptedTransferSyntaxes = new DicomTransferSyntax[]
|
||||
{
|
||||
DicomTransferSyntax.ExplicitVRLittleEndian,
|
||||
DicomTransferSyntax.ExplicitVRBigEndian,
|
||||
DicomTransferSyntax.ImplicitVRLittleEndian
|
||||
};
|
||||
|
||||
private static readonly DicomTransferSyntax[] _acceptedImageTransferSyntaxes = new DicomTransferSyntax[]
|
||||
{
|
||||
// Lossless
|
||||
DicomTransferSyntax.JPEGLSLossless, //1.2.840.10008.1.2.4.80
|
||||
DicomTransferSyntax.JPEG2000Lossless, //1.2.840.10008.1.2.4.90
|
||||
DicomTransferSyntax.JPEGProcess14SV1, //1.2.840.10008.1.2.4.70
|
||||
DicomTransferSyntax.JPEGProcess14, //1.2.840.10008.1.2.4.57 JPEG Lossless, Non-Hierarchical (Process 14)
|
||||
DicomTransferSyntax.RLELossless, //1.2.840.10008.1.2.5
|
||||
// Lossy
|
||||
DicomTransferSyntax.JPEGLSNearLossless,//1.2.840.10008.1.2.4.81"
|
||||
DicomTransferSyntax.JPEG2000Lossy, //1.2.840.10008.1.2.4.91
|
||||
DicomTransferSyntax.JPEGProcess1, //1.2.840.10008.1.2.4.50
|
||||
DicomTransferSyntax.JPEGProcess2_4, //1.2.840.10008.1.2.4.51
|
||||
// Uncompressed
|
||||
DicomTransferSyntax.ExplicitVRLittleEndian, //1.2.840.10008.1.2.1
|
||||
DicomTransferSyntax.ExplicitVRBigEndian, //1.2.840.10008.1.2.2
|
||||
DicomTransferSyntax.ImplicitVRLittleEndian //1.2.840.10008.1.2
|
||||
};
|
||||
|
||||
|
||||
public CStoreSCPService(INetworkStream stream, Encoding fallbackEncoding, Microsoft.Extensions.Logging.ILogger log, DicomServiceDependencies dependencies)
|
||||
: base(stream, fallbackEncoding, log, dependencies)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public Task OnReceiveAssociationRequestAsync(DicomAssociation association)
|
||||
{
|
||||
|
||||
_upload = new SCPImageUpload() { StartTime = DateTime.Now, CallingAE = association.CallingAE, CalledAE = association.CalledAE, CallingAEIP = association.RemoteHost };
|
||||
|
||||
|
||||
Log.Logger.Warning($"接收到来自{association.CallingAE}的连接");
|
||||
|
||||
_serviceProvider = (IServiceProvider)this.UserState;
|
||||
|
||||
|
||||
|
||||
var _trialDicomAERepository = _serviceProvider.GetService<IRepository<TrialDicomAE>>();
|
||||
|
||||
|
||||
var trialDicomAEList = _trialDicomAERepository.Select(t => new { t.CalledAE, t.TrialId }).ToList();
|
||||
var trialCalledAEList = trialDicomAEList.Select(t => t.CalledAE).ToList();
|
||||
|
||||
Log.Logger.Information("当前系统配置:", string.Join('|', trialDicomAEList));
|
||||
|
||||
var findCalledAE = trialDicomAEList.Where(t => t.CalledAE == association.CalledAE).FirstOrDefault();
|
||||
|
||||
var isCanReceiveIamge = false;
|
||||
|
||||
if (findCalledAE != null)
|
||||
{
|
||||
_trialId = findCalledAE.TrialId;
|
||||
|
||||
var _trialSiteDicomAERepository = _serviceProvider.GetService<IRepository<TrialSiteDicomAE>>();
|
||||
|
||||
|
||||
var findTrialSiteAE = _trialSiteDicomAERepository.Where(t => t.CallingAE == association.CallingAE && t.TrialId==_trialId).FirstOrDefault();
|
||||
|
||||
if (findTrialSiteAE != null)
|
||||
{
|
||||
_trialSiteId = findTrialSiteAE.TrialSiteId;
|
||||
|
||||
isCanReceiveIamge = true;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
if (association.CallingAE == "test-callingAE")
|
||||
{
|
||||
isCanReceiveIamge = true;
|
||||
}
|
||||
|
||||
if (!trialCalledAEList.Contains(association.CalledAE) || isCanReceiveIamge == false)
|
||||
{
|
||||
|
||||
Log.Logger.Warning($"拒绝CallingAE:{association.CallingAE} CalledAE:{association.CalledAE}的连接");
|
||||
|
||||
return SendAssociationRejectAsync(
|
||||
DicomRejectResult.Permanent,
|
||||
DicomRejectSource.ServiceUser,
|
||||
DicomRejectReason.CalledAENotRecognized);
|
||||
}
|
||||
|
||||
foreach (var pc in association.PresentationContexts)
|
||||
{
|
||||
if (pc.AbstractSyntax == DicomUID.Verification)
|
||||
{
|
||||
pc.AcceptTransferSyntaxes(_acceptedTransferSyntaxes);
|
||||
}
|
||||
else if (pc.AbstractSyntax.StorageCategory != DicomStorageCategory.None)
|
||||
{
|
||||
pc.AcceptTransferSyntaxes(_acceptedImageTransferSyntaxes);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
return SendAssociationAcceptAsync(association);
|
||||
}
|
||||
|
||||
|
||||
public async Task OnReceiveAssociationReleaseRequestAsync()
|
||||
{
|
||||
await DataMaintenanceAsaync();
|
||||
|
||||
//记录监控
|
||||
|
||||
var _SCPImageUploadRepository = _serviceProvider.GetService<IRepository<SCPImageUpload>>();
|
||||
|
||||
_upload.EndTime = DateTime.Now;
|
||||
_upload.StudyCount = _SCPStudyIdList.Count;
|
||||
_upload.TrialId = _trialId;
|
||||
_upload.TrialSiteId = _trialSiteId;
|
||||
|
||||
await _SCPImageUploadRepository.AddAsync(_upload, true);
|
||||
|
||||
await SendAssociationReleaseResponseAsync();
|
||||
}
|
||||
|
||||
|
||||
private async Task DataMaintenanceAsaync()
|
||||
{
|
||||
Log.Logger.Warning($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE}传输结束:开始维护数据,处理检查Modality");
|
||||
|
||||
|
||||
|
||||
//处理检查Modality
|
||||
var _dictionaryRepository = _serviceProvider.GetService<IRepository<Dictionary>>();
|
||||
var _seriesRepository = _serviceProvider.GetService<IRepository<SCPSeries>>();
|
||||
var _studyRepository = _serviceProvider.GetService<IRepository<SCPStudy>>();
|
||||
|
||||
var dicModalityList = _dictionaryRepository.Where(t => t.Code == "Modality").SelectMany(t => t.ChildList.Select(c => c.Value)).ToList();
|
||||
var seriesModalityList = _seriesRepository.Where(t => _SCPStudyIdList.Contains(t.StudyId)).Select(t => new { SCPStudyId = t.StudyId, t.Modality }).ToList();
|
||||
|
||||
foreach (var g in seriesModalityList.GroupBy(t => t.SCPStudyId))
|
||||
{
|
||||
var modality = string.Join('、', g.Select(t => t.Modality).Distinct().ToList());
|
||||
|
||||
//特殊逻辑
|
||||
var modalityForEdit = dicModalityList.Contains(modality) ? modality : String.Empty;
|
||||
|
||||
if (modality == "MR")
|
||||
{
|
||||
modalityForEdit = "MRI";
|
||||
}
|
||||
|
||||
if (modality == "PT")
|
||||
{
|
||||
modalityForEdit = "PET";
|
||||
}
|
||||
if (modality == "PT、CT" || modality == "CT、PT")
|
||||
{
|
||||
modalityForEdit = "PET-CT";
|
||||
}
|
||||
|
||||
await _studyRepository.BatchUpdateNoTrackingAsync(t => t.Id == g.Key, u => new SCPStudy() { Modalities = modality, ModalityForEdit = modalityForEdit });
|
||||
|
||||
}
|
||||
|
||||
Log.Logger.Warning($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE}维护数据结束");
|
||||
}
|
||||
|
||||
public void OnReceiveAbort(DicomAbortSource source, DicomAbortReason reason)
|
||||
{
|
||||
Log.Logger.Warning($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE}接收中断,中断原因:{source.ToString() + reason.ToString()}");
|
||||
/* nothing to do here */
|
||||
}
|
||||
|
||||
|
||||
public async void OnConnectionClosed(Exception exception)
|
||||
{
|
||||
/* nothing to do here */
|
||||
|
||||
//奇怪的bug 上传的时候,用王捷修改的影像,会关闭,重新连接,导致检查id 丢失,然后状态不一致
|
||||
if (exception == null)
|
||||
{
|
||||
var _studyRepository = _serviceProvider.GetService<IRepository<SCPStudy>>();
|
||||
//将检查设置为传输结束
|
||||
await _studyRepository.BatchUpdateNoTrackingAsync(t => _SCPStudyIdList.Contains(t.Id), u => new SCPStudy() { IsUploadFinished = true });
|
||||
|
||||
await _studyRepository.SaveChangesAndClearAllTrackingAsync();
|
||||
}
|
||||
|
||||
Log.Logger.Warning($"连接关闭 {exception?.Message} {exception?.InnerException?.Message}");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public async Task<DicomCStoreResponse> OnCStoreRequestAsync(DicomCStoreRequest request)
|
||||
{
|
||||
|
||||
string studyInstanceUid = request.Dataset.GetString(DicomTag.StudyInstanceUID);
|
||||
string seriesInstanceUid = request.Dataset.GetString(DicomTag.SeriesInstanceUID);
|
||||
string sopInstanceUid = request.Dataset.GetString(DicomTag.SOPInstanceUID);
|
||||
|
||||
//Guid studyId = IdentifierHelper.CreateGuid(studyInstanceUid, trialId.ToString());
|
||||
Guid seriesId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid, _trialId.ToString());
|
||||
Guid instanceId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid, sopInstanceUid, _trialId.ToString());
|
||||
|
||||
|
||||
var ossService = _serviceProvider.GetService<IOSSService>();
|
||||
var dicomArchiveService = _serviceProvider.GetService<IDicomArchiveService>();
|
||||
var _seriesRepository = _serviceProvider.GetService<IRepository<SCPSeries>>();
|
||||
|
||||
var _distributedLockProvider = _serviceProvider.GetService<IDistributedLockProvider>();
|
||||
|
||||
var storeRelativePath = string.Empty;
|
||||
var ossFolderPath = $"{_trialId}/Image/PACS/{_trialSiteId}{studyInstanceUid}";
|
||||
|
||||
|
||||
long fileSize = 0;
|
||||
try
|
||||
{
|
||||
|
||||
using (MemoryStream ms = new MemoryStream())
|
||||
{
|
||||
await request.File.SaveAsync(ms);
|
||||
|
||||
//irc 从路径最后一截取Guid
|
||||
storeRelativePath = await ossService.UploadToOSSAsync(ms, ossFolderPath, instanceId.ToString(), false);
|
||||
|
||||
fileSize = ms.Length;
|
||||
}
|
||||
|
||||
Log.Logger.Information($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE} {request.SOPInstanceUID} 上传完成 ");
|
||||
|
||||
}
|
||||
catch (Exception ec)
|
||||
{
|
||||
Log.Logger.Warning($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE} 上传异常 {ec.Message}");
|
||||
}
|
||||
|
||||
|
||||
|
||||
var @lock = _distributedLockProvider.CreateLock($"{studyInstanceUid}");
|
||||
|
||||
using (await @lock.AcquireAsync())
|
||||
{
|
||||
try
|
||||
{
|
||||
var scpStudyId = await dicomArchiveService.ArchiveDicomFileAsync(request.Dataset, _trialId, _trialSiteId, storeRelativePath, Association.CallingAE, Association.CalledAE);
|
||||
|
||||
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,352 @@
|
||||
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;
|
||||
|
||||
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)
|
||||
{
|
||||
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
|
||||
};
|
||||
|
||||
++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 });
|
||||
}
|
||||
|
||||
//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);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,442 @@
|
||||
using Aliyun.OSS;
|
||||
using IRaCIS.Core.Infrastructure;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Minio.DataModel.Args;
|
||||
using Minio;
|
||||
using SharpCompress.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.AccessControl;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace IRaCIS.Core.SCP
|
||||
{
|
||||
public class MinIOOptions : AWSOptions
|
||||
{
|
||||
public int port { get; set; }
|
||||
|
||||
}
|
||||
|
||||
public class AWSOptions
|
||||
{
|
||||
public string endPoint { get; set; }
|
||||
public bool useSSL { get; set; }
|
||||
public string accessKey { get; set; }
|
||||
public string secretKey { get; set; }
|
||||
public string bucketName { get; set; }
|
||||
public string viewEndpoint { get; set; }
|
||||
}
|
||||
|
||||
public class AliyunOSSOptions
|
||||
{
|
||||
public string regionId { get; set; }
|
||||
public string accessKeyId { get; set; }
|
||||
public string accessKeySecret { get; set; }
|
||||
|
||||
public string internalEndpoint { get; set; }
|
||||
|
||||
public string endPoint { get; set; }
|
||||
public string bucketName { get; set; }
|
||||
|
||||
public string roleArn { get; set; }
|
||||
|
||||
public string region { get; set; }
|
||||
|
||||
public string viewEndpoint { get; set; }
|
||||
|
||||
}
|
||||
|
||||
public class ObjectStoreServiceOptions
|
||||
{
|
||||
public string ObjectStoreUse { get; set; }
|
||||
public AliyunOSSOptions AliyunOSS { get; set; }
|
||||
public MinIOOptions MinIO { get; set; }
|
||||
|
||||
public AWSOptions AWS { get; set; }
|
||||
|
||||
}
|
||||
|
||||
public class ObjectStoreDTO
|
||||
{
|
||||
public string ObjectStoreUse { get; set; }
|
||||
|
||||
public AliyunOSSOptions AliyunOSS { get; set; }
|
||||
|
||||
public MinIOOptions MinIO { get; set; }
|
||||
|
||||
public AWSOptions AWS { get; set; }
|
||||
|
||||
}
|
||||
|
||||
public class AliyunOSSTempToken
|
||||
{
|
||||
public string AccessKeyId { get; set; }
|
||||
public string AccessKeySecret { get; set; }
|
||||
public string SecurityToken { get; set; }
|
||||
public string Expiration { get; set; }
|
||||
|
||||
public string Region { get; set; }
|
||||
public string BucketName { get; set; }
|
||||
public string ViewEndpoint { get; set; }
|
||||
}
|
||||
|
||||
|
||||
|
||||
public enum ObjectStoreUse
|
||||
{
|
||||
AliyunOSS = 0,
|
||||
MinIO = 1,
|
||||
AWS = 2,
|
||||
}
|
||||
|
||||
public interface IOSSService
|
||||
{
|
||||
public Task<string> UploadToOSSAsync(Stream fileStream, string oosFolderPath, string fileRealName, bool isFileNameAddGuid = true);
|
||||
public Task<string> UploadToOSSAsync(string localFilePath, string oosFolderPath, bool isFileNameAddGuid = true);
|
||||
|
||||
public Task DownLoadFromOSSAsync(string ossRelativePath, string localFilePath);
|
||||
|
||||
public ObjectStoreServiceOptions ObjectStoreServiceOptions { get; set; }
|
||||
|
||||
public Task<string> GetSignedUrl(string ossRelativePath);
|
||||
|
||||
}
|
||||
|
||||
|
||||
public class OSSService : IOSSService
|
||||
{
|
||||
public ObjectStoreServiceOptions ObjectStoreServiceOptions { get; set; }
|
||||
|
||||
|
||||
public OSSService(IOptionsMonitor<ObjectStoreServiceOptions> options)
|
||||
{
|
||||
ObjectStoreServiceOptions = options.CurrentValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// oosFolderPath 不要 "/ "开头 应该: TempFolder/ChildFolder
|
||||
/// </summary>
|
||||
/// <param name="fileStream"></param>
|
||||
/// <param name="oosFolderPath"></param>
|
||||
/// <param name="fileRealName"></param>
|
||||
/// <param name="isFileNameAddGuid"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<string> UploadToOSSAsync(Stream fileStream, string oosFolderPath, string fileRealName, bool isFileNameAddGuid = true)
|
||||
{
|
||||
var ossRelativePath = isFileNameAddGuid ? $"{oosFolderPath}/{Guid.NewGuid()}_{fileRealName}" : $"{oosFolderPath}/{fileRealName}";
|
||||
//var ossRelativePath = $"{oosFolderPath}/{Guid.NewGuid()}_{fileRealName}";
|
||||
//var ossRelativePath = oosFolderPath + "/" + fileRealName;
|
||||
|
||||
try
|
||||
{
|
||||
using (var memoryStream = new MemoryStream())
|
||||
{
|
||||
fileStream.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
fileStream.CopyTo(memoryStream);
|
||||
|
||||
memoryStream.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
|
||||
if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS")
|
||||
{
|
||||
var aliConfig = ObjectStoreServiceOptions.AliyunOSS;
|
||||
|
||||
var _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.endPoint : aliConfig.internalEndpoint, aliConfig.accessKeyId, aliConfig.accessKeySecret);
|
||||
|
||||
|
||||
|
||||
// 上传文件
|
||||
var result = _ossClient.PutObject(aliConfig.bucketName, ossRelativePath, memoryStream);
|
||||
|
||||
}
|
||||
else if (ObjectStoreServiceOptions.ObjectStoreUse == "MinIO")
|
||||
{
|
||||
var minIOConfig = ObjectStoreServiceOptions.MinIO;
|
||||
|
||||
|
||||
var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.endPoint}:{minIOConfig.port}")
|
||||
.WithCredentials(minIOConfig.accessKey, minIOConfig.secretKey).WithSSL(minIOConfig.useSSL)
|
||||
.Build();
|
||||
|
||||
var putObjectArgs = new PutObjectArgs()
|
||||
.WithBucket(minIOConfig.bucketName)
|
||||
.WithObject(ossRelativePath)
|
||||
.WithStreamData(memoryStream)
|
||||
.WithObjectSize(memoryStream.Length);
|
||||
|
||||
await minioClient.PutObjectAsync(putObjectArgs);
|
||||
}
|
||||
else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS")
|
||||
{
|
||||
var minIOConfig = ObjectStoreServiceOptions.AWS;
|
||||
|
||||
|
||||
var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.endPoint}")
|
||||
.WithCredentials(minIOConfig.accessKey, minIOConfig.secretKey).WithSSL(minIOConfig.useSSL)
|
||||
.Build();
|
||||
|
||||
var putObjectArgs = new PutObjectArgs()
|
||||
.WithBucket(minIOConfig.bucketName)
|
||||
.WithObject(ossRelativePath)
|
||||
.WithStreamData(memoryStream)
|
||||
.WithObjectSize(memoryStream.Length);
|
||||
|
||||
await minioClient.PutObjectAsync(putObjectArgs);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new BusinessValidationFailedException("未定义的存储介质类型");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
throw new BusinessValidationFailedException($"上传发生异常:{ex.Message}");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
return "/" + ossRelativePath;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// oosFolderPath 不要 "/ "开头 应该: TempFolder/ChildFolder
|
||||
/// </summary>
|
||||
/// <param name="localFilePath"></param>
|
||||
/// <param name="oosFolderPath"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="BusinessValidationFailedException"></exception>
|
||||
public async Task<string> UploadToOSSAsync(string localFilePath, string oosFolderPath, bool isFileNameAddGuid = true)
|
||||
{
|
||||
var localFileName = Path.GetFileName(localFilePath);
|
||||
|
||||
var ossRelativePath = isFileNameAddGuid ? $"{oosFolderPath}/{Guid.NewGuid()}_{localFileName}" : $"{oosFolderPath}/{localFileName}";
|
||||
|
||||
|
||||
//var ossRelativePath = oosFolderPath + "/" + localFileName;
|
||||
|
||||
|
||||
if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS")
|
||||
{
|
||||
var aliConfig = ObjectStoreServiceOptions.AliyunOSS;
|
||||
|
||||
var _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.endPoint : aliConfig.internalEndpoint, aliConfig.accessKeyId, aliConfig.accessKeySecret);
|
||||
|
||||
// 上传文件
|
||||
var result = _ossClient.PutObject(aliConfig.bucketName, ossRelativePath, localFilePath);
|
||||
|
||||
}
|
||||
else if (ObjectStoreServiceOptions.ObjectStoreUse == "MinIO")
|
||||
{
|
||||
var minIOConfig = ObjectStoreServiceOptions.MinIO;
|
||||
|
||||
|
||||
var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.endPoint}:{minIOConfig.port}")
|
||||
.WithCredentials(minIOConfig.accessKey, minIOConfig.secretKey).WithSSL(minIOConfig.useSSL)
|
||||
.Build();
|
||||
|
||||
var putObjectArgs = new PutObjectArgs()
|
||||
.WithBucket(minIOConfig.bucketName)
|
||||
.WithObject(ossRelativePath)
|
||||
.WithFileName(localFilePath);
|
||||
|
||||
await minioClient.PutObjectAsync(putObjectArgs);
|
||||
}
|
||||
else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS")
|
||||
{
|
||||
var minIOConfig = ObjectStoreServiceOptions.AWS;
|
||||
|
||||
|
||||
var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.endPoint}")
|
||||
.WithCredentials(minIOConfig.accessKey, minIOConfig.secretKey).WithSSL(minIOConfig.useSSL)
|
||||
.Build();
|
||||
|
||||
var putObjectArgs = new PutObjectArgs()
|
||||
.WithBucket(minIOConfig.bucketName)
|
||||
.WithObject(ossRelativePath)
|
||||
.WithFileName(localFilePath);
|
||||
|
||||
await minioClient.PutObjectAsync(putObjectArgs);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new BusinessValidationFailedException("未定义的存储介质类型");
|
||||
}
|
||||
return "/" + ossRelativePath;
|
||||
|
||||
}
|
||||
|
||||
|
||||
public async Task DownLoadFromOSSAsync(string ossRelativePath, string localFilePath)
|
||||
{
|
||||
|
||||
ossRelativePath = ossRelativePath.TrimStart('/');
|
||||
try
|
||||
{
|
||||
|
||||
|
||||
if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS")
|
||||
{
|
||||
var aliConfig = ObjectStoreServiceOptions.AliyunOSS;
|
||||
|
||||
var _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.endPoint : aliConfig.internalEndpoint, aliConfig.accessKeyId, aliConfig.accessKeySecret);
|
||||
|
||||
// 上传文件
|
||||
var result = _ossClient.GetObject(aliConfig.bucketName, ossRelativePath);
|
||||
|
||||
// 将下载的文件流保存到本地文件
|
||||
using (var fs = File.OpenWrite(localFilePath))
|
||||
{
|
||||
result.Content.CopyTo(fs);
|
||||
fs.Close();
|
||||
}
|
||||
|
||||
}
|
||||
else if (ObjectStoreServiceOptions.ObjectStoreUse == "MinIO")
|
||||
{
|
||||
var minIOConfig = ObjectStoreServiceOptions.MinIO;
|
||||
|
||||
var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.endPoint}:{minIOConfig.port}")
|
||||
.WithCredentials(minIOConfig.accessKey, minIOConfig.secretKey).WithSSL(minIOConfig.useSSL)
|
||||
.Build();
|
||||
|
||||
var getObjectArgs = new GetObjectArgs()
|
||||
.WithBucket(minIOConfig.bucketName)
|
||||
.WithObject(ossRelativePath)
|
||||
.WithFile(localFilePath);
|
||||
|
||||
await minioClient.GetObjectAsync(getObjectArgs);
|
||||
|
||||
}
|
||||
else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS")
|
||||
{
|
||||
var minIOConfig = ObjectStoreServiceOptions.AWS;
|
||||
|
||||
var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.endPoint}")
|
||||
.WithCredentials(minIOConfig.accessKey, minIOConfig.secretKey).WithSSL(minIOConfig.useSSL)
|
||||
.Build();
|
||||
|
||||
var getObjectArgs = new GetObjectArgs()
|
||||
.WithBucket(minIOConfig.bucketName)
|
||||
.WithObject(ossRelativePath)
|
||||
.WithFile(localFilePath);
|
||||
|
||||
await minioClient.GetObjectAsync(getObjectArgs);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new BusinessValidationFailedException("未定义的存储介质类型");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
throw new BusinessValidationFailedException("oss下载失败!" + ex.Message);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
public async Task<string> GetSignedUrl(string ossRelativePath)
|
||||
{
|
||||
ossRelativePath = ossRelativePath.TrimStart('/');
|
||||
try
|
||||
{
|
||||
|
||||
|
||||
if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS")
|
||||
{
|
||||
var aliConfig = ObjectStoreServiceOptions.AliyunOSS;
|
||||
|
||||
var _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.endPoint : aliConfig.internalEndpoint, aliConfig.accessKeyId, aliConfig.accessKeySecret);
|
||||
|
||||
// 生成签名URL。
|
||||
var req = new GeneratePresignedUriRequest(aliConfig.bucketName, ossRelativePath, SignHttpMethod.Get)
|
||||
{
|
||||
// 设置签名URL过期时间,默认值为3600秒。
|
||||
Expiration = DateTime.Now.AddHours(1),
|
||||
};
|
||||
var uri = _ossClient.GeneratePresignedUri(req);
|
||||
|
||||
return uri.PathAndQuery;
|
||||
|
||||
}
|
||||
else if (ObjectStoreServiceOptions.ObjectStoreUse == "MinIO")
|
||||
{
|
||||
var minIOConfig = ObjectStoreServiceOptions.MinIO;
|
||||
|
||||
var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.endPoint}:{minIOConfig.port}")
|
||||
.WithCredentials(minIOConfig.accessKey, minIOConfig.secretKey).WithSSL(minIOConfig.useSSL)
|
||||
.Build();
|
||||
|
||||
|
||||
var args = new PresignedGetObjectArgs()
|
||||
.WithBucket(minIOConfig.bucketName)
|
||||
.WithObject(ossRelativePath)
|
||||
.WithExpiry(3600)
|
||||
/*.WithHeaders(reqParams)*/;
|
||||
|
||||
var presignedUrl = await minioClient.PresignedGetObjectAsync(args);
|
||||
|
||||
Uri uri = new Uri(presignedUrl);
|
||||
|
||||
string relativePath = uri.PathAndQuery;
|
||||
|
||||
|
||||
return relativePath;
|
||||
|
||||
}
|
||||
else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS")
|
||||
{
|
||||
var minIOConfig = ObjectStoreServiceOptions.AWS;
|
||||
|
||||
var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.endPoint}")
|
||||
.WithCredentials(minIOConfig.accessKey, minIOConfig.secretKey).WithSSL(minIOConfig.useSSL)
|
||||
.Build();
|
||||
|
||||
var args = new PresignedGetObjectArgs()
|
||||
.WithBucket(minIOConfig.bucketName)
|
||||
.WithObject(ossRelativePath)
|
||||
.WithExpiry(3600);
|
||||
|
||||
var presignedUrl = await minioClient.PresignedGetObjectAsync(args);
|
||||
|
||||
Uri uri = new Uri(presignedUrl);
|
||||
|
||||
string relativePath = uri.PathAndQuery;
|
||||
|
||||
|
||||
return relativePath;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new BusinessValidationFailedException("未定义的存储介质类型");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
throw new BusinessValidationFailedException("oss授权url失败!" + ex.Message);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
"ObjectStoreService": {
|
||||
"ObjectStoreUse": "AliyunOSS",
|
||||
"AliyunOSS": {
|
||||
"regionId": "cn-shanghai",
|
||||
"internalEndpoint": "https://oss-cn-shanghai-internal.aliyuncs.com",
|
||||
"endpoint": "https://oss-cn-shanghai.aliyuncs.com",
|
||||
"accessKeyId": "LTAI5tKvzs7ed3UfSpNk3xwQ",
|
||||
"accessKeySecret": "zTIceGEShlZDGnLrCFfIGFE7TXVRio",
|
||||
"bucketName": "zy-irc-store",
|
||||
"roleArn": "acs:ram::1899121822495495:role/oss-upload",
|
||||
"viewEndpoint": "https://zy-irc-cache.oss-cn-shanghai.aliyuncs.com",
|
||||
"region": "oss-cn-shanghai"
|
||||
},
|
||||
|
||||
"MinIO": {
|
||||
"endpoint": "http://192.168.3.68",
|
||||
"port": "8001",
|
||||
"useSSL": false,
|
||||
"accessKey": "IDFkwEpWej0b4DtiuThL",
|
||||
"secretKey": "Lhuu83yMhVwu7c1SnjvGY6lq74jzpYqifK6Qtj4h",
|
||||
"bucketName": "test"
|
||||
}
|
||||
},
|
||||
|
||||
"ConnectionStrings": {
|
||||
"RemoteNew": "Server=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"
|
||||
},
|
||||
|
||||
"BasicSystemConfig": {
|
||||
|
||||
"OpenUserComplexPassword": true,
|
||||
|
||||
"OpenSignDocumentBeforeWork": true,
|
||||
|
||||
"OpenTrialRelationDelete": true,
|
||||
|
||||
"OpenLoginLimit": true,
|
||||
"LoginMaxFailCount": 5,
|
||||
|
||||
"LoginFailLockMinutes": 30
|
||||
|
||||
},
|
||||
|
||||
"SystemEmailSendConfig": {
|
||||
"Port": 465,
|
||||
"Host": "smtp.qiye.aliyun.com",
|
||||
"FromEmail": "study@extimaging.com",
|
||||
"FromName": "研究单位阅片系统",
|
||||
"AuthorizationCode": "zhanying123",
|
||||
"SiteUrl": "https://study.extimaging.com/login"
|
||||
}
|
||||
|
||||
}
|
||||
+29
-23
@@ -6,20 +6,11 @@
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
"ConnectionStrings": {
|
||||
"RemoteNew": "Server=us-mssql-service,1433;Database=Prod_US_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
||||
"Hangfire": "Server=us-mssql-service,1433;Database=Prod_US_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": {
|
||||
|
||||
"ObjectStoreUse": "AWS",
|
||||
|
||||
"ObjectStoreUse": "AliyunOSS",
|
||||
"AliyunOSS": {
|
||||
"regionId": "cn-shanghai",
|
||||
"internalEndpoint": "https://oss-cn-shanghai-internal.aliyuncs.com",
|
||||
"endPoint": "https://oss-cn-shanghai.aliyuncs.com",
|
||||
"accessKeyId": "LTAI5tKvzs7ed3UfSpNk3xwQ",
|
||||
"accessKeySecret": "zTIceGEShlZDGnLrCFfIGFE7TXVRio",
|
||||
@@ -30,24 +21,29 @@
|
||||
},
|
||||
|
||||
"MinIO": {
|
||||
"endPoint": "192.168.3.68",
|
||||
"port": "8001",
|
||||
"endPoint": "106.14.89.110",
|
||||
"port": "9001",
|
||||
"useSSL": false,
|
||||
"accessKey": "IDFkwEpWej0b4DtiuThL",
|
||||
"secretKey": "Lhuu83yMhVwu7c1SnjvGY6lq74jzpYqifK6Qtj4h",
|
||||
"bucketName": "test",
|
||||
"viewEndpoint": "http://192.168.3.68:8001/test/"
|
||||
"accessKey": "fbStsVYCIPKHQneeqMwD",
|
||||
"secretKey": "TzgvyA3zGXMUnpilJNUlyMYHfosl1hBMl6lxPmjy",
|
||||
"bucketName": "hir-test",
|
||||
"viewEndpoint": "http://106.14.89.110:9001/hir-test/"
|
||||
},
|
||||
|
||||
"AWS": {
|
||||
"endPoint": "s3.us-east-1.amazonaws.com",
|
||||
"useSSL": true,
|
||||
"useSSL": false,
|
||||
"accessKey": "AKIAZQ3DRSOHFPJJ6FEU",
|
||||
"secretKey": "l+yjtvV7Z4jiwm/7xCYv30UeUj/SvuqqYzAwjJHf",
|
||||
"bucketName": "ei-irc-test-store",
|
||||
"viewEndpoint": "https://ei-irc-test-store.s3.amazonaws.com/"
|
||||
}
|
||||
},
|
||||
|
||||
"ConnectionStrings": {
|
||||
"RemoteNew": "Server=106.14.89.110,1435;Database=Test_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
||||
"Hangfire": "Server=106.14.89.110,1435;Database=Test_IRC_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
|
||||
},
|
||||
"BasicSystemConfig": {
|
||||
|
||||
"OpenUserComplexPassword": false,
|
||||
@@ -57,18 +53,28 @@
|
||||
"OpenTrialRelationDelete": true,
|
||||
|
||||
"OpenLoginLimit": false,
|
||||
|
||||
"LoginMaxFailCount": 5,
|
||||
|
||||
"LoginFailLockMinutes": 30
|
||||
|
||||
},
|
||||
|
||||
"SystemEmailSendConfig": {
|
||||
"Port": 465,
|
||||
"Host": "smtp.qiye.aliyun.com",
|
||||
"FromEmail": "test@extimaging.com",
|
||||
"FromName": "Test_IRC",
|
||||
"AuthorizationCode": "SHzyyl2021"
|
||||
}
|
||||
"FromEmail": "test-study@extimaging.com",
|
||||
"FromName": "Test_Study",
|
||||
"AuthorizationCode": "zhanying123",
|
||||
|
||||
"SiteUrl": "http://study.test.extimaging.com/login"
|
||||
},
|
||||
"DicomSCPServiceConfig": {
|
||||
"CalledAEList": [
|
||||
"STORESCP",
|
||||
"Value1",
|
||||
"Value2",
|
||||
"Value3"
|
||||
],
|
||||
"ServerPort": 11112
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
"ObjectStoreService": {
|
||||
"ObjectStoreUse": "AliyunOSS",
|
||||
"AliyunOSS": {
|
||||
"regionId": "cn-shanghai",
|
||||
"internalEndpoint": "https://oss-cn-shanghai-internal.aliyuncs.com",
|
||||
"endpoint": "https://oss-cn-shanghai.aliyuncs.com",
|
||||
"accessKeyId": "LTAI5tKvzs7ed3UfSpNk3xwQ",
|
||||
"accessKeySecret": "zTIceGEShlZDGnLrCFfIGFE7TXVRio",
|
||||
"bucketName": "zy-irc-uat-store",
|
||||
"roleArn": "acs:ram::1899121822495495:role/oss-upload",
|
||||
"viewEndpoint": "https://zy-irc-uat-store.oss-cn-shanghai.aliyuncs.com",
|
||||
"region": "oss-cn-shanghai"
|
||||
},
|
||||
|
||||
"AWS": {
|
||||
"endPoint": "s3.us-east-1.amazonaws.com",
|
||||
"useSSL": false,
|
||||
"accessKey": "AKIAZQ3DRSOHFPJJ6FEU",
|
||||
"secretKey": "l+yjtvV7Z4jiwm/7xCYv30UeUj/SvuqqYzAwjJHf",
|
||||
"bucketName": "ei-irc-test-store",
|
||||
"viewEndpoint": "https://ei-irc-test-store.s3.amazonaws.com/"
|
||||
}
|
||||
},
|
||||
"ConnectionStrings": {
|
||||
"RemoteNew": "Server=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"
|
||||
},
|
||||
"BasicSystemConfig": {
|
||||
|
||||
"OpenUserComplexPassword": false,
|
||||
|
||||
"OpenSignDocumentBeforeWork": false,
|
||||
|
||||
"OpenTrialRelationDelete": true,
|
||||
|
||||
"OpenLoginLimit": false,
|
||||
"LoginMaxFailCount": 5,
|
||||
|
||||
"LoginFailLockMinutes": 30
|
||||
},
|
||||
|
||||
"SystemEmailSendConfig": {
|
||||
"Port": 465,
|
||||
"Host": "smtp.qiye.aliyun.com",
|
||||
"FromEmail": "uat-study@extimaging.com",
|
||||
"FromName": "Uat_Study",
|
||||
"AuthorizationCode": "zhanying123",
|
||||
"SiteUrl": "http://study.uat.extimaging.com/login"
|
||||
},
|
||||
"DicomSCPServiceConfig": {
|
||||
"CalledAEList": [
|
||||
"STORESCP",
|
||||
"Value1",
|
||||
"Value2",
|
||||
"Value3"
|
||||
],
|
||||
"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
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IRaCIS.Core.Infrastructure", "IRaCIS.Core.Infrastructure\IRaCIS.Core.Infrastructure.csproj", "{07EED0F8-08E6-46F3-ACBE-17BC1391BD4C}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IRC.Core.SCP", "IRC.Core.SCP\IRC.Core.SCP.csproj", "{ECD08F47-DC1A-484E-BB91-6CDDC8823CC5}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -51,6 +53,10 @@ Global
|
||||
{07EED0F8-08E6-46F3-ACBE-17BC1391BD4C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{07EED0F8-08E6-46F3-ACBE-17BC1391BD4C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{07EED0F8-08E6-46F3-ACBE-17BC1391BD4C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{ECD08F47-DC1A-484E-BB91-6CDDC8823CC5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{ECD08F47-DC1A-484E-BB91-6CDDC8823CC5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{ECD08F47-DC1A-484E-BB91-6CDDC8823CC5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{ECD08F47-DC1A-484E-BB91-6CDDC8823CC5}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
||||
@@ -30,6 +30,9 @@ using IRaCIS.Core.Application.Helper;
|
||||
using Microsoft.Extensions.Options;
|
||||
using IRaCIS.Core.Application.Contracts;
|
||||
using LoginReturnDTO = IRaCIS.Application.Contracts.LoginReturnDTO;
|
||||
using DocumentFormat.OpenXml.Spreadsheet;
|
||||
using AutoMapper.QueryableExtensions;
|
||||
using NetTopologySuite.Algorithm;
|
||||
|
||||
namespace IRaCIS.Api.Controllers
|
||||
{
|
||||
@@ -53,8 +56,8 @@ namespace IRaCIS.Api.Controllers
|
||||
/// <param name="doctorId"></param>
|
||||
/// <returns></returns>
|
||||
[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] IResearchPublicationService _researchPublicationService, [FromServices] IVacationService _vacationService, Guid doctorId)
|
||||
@@ -66,7 +69,7 @@ namespace IRaCIS.Api.Controllers
|
||||
|
||||
var doctorDetail = new DoctorDetailDTO
|
||||
{
|
||||
AuditView =await _doctorService.GetAuditState(doctorId),
|
||||
AuditView = await _doctorService.GetAuditState(doctorId),
|
||||
BasicInfoView = await _doctorService.GetBasicInfo(doctorId),
|
||||
EmploymentView = await _doctorService.GetEmploymentInfo(doctorId),
|
||||
AttachmentList = await attachmentService.GetAttachments(doctorId),
|
||||
@@ -77,7 +80,7 @@ namespace IRaCIS.Api.Controllers
|
||||
TrialExperienceView = await _trialExperienceService.GetTrialExperience(doctorId),
|
||||
ResearchPublicationView = await _researchPublicationService.GetResearchPublication(doctorId),
|
||||
|
||||
SpecialtyView =await _doctorService.GetSpecialtyInfo(doctorId),
|
||||
SpecialtyView = await _doctorService.GetSpecialtyInfo(doctorId),
|
||||
InHoliday = (await _vacationService.OnVacation(doctorId)).IsSuccess,
|
||||
IntoGroupInfo = _doctorService.GetDoctorIntoGroupInfo(doctorId),
|
||||
SowList = sowList,
|
||||
@@ -96,80 +99,31 @@ namespace IRaCIS.Api.Controllers
|
||||
/// <summary> 系统用户登录接口[New] </summary>
|
||||
[HttpPost, Route("user/login")]
|
||||
[AllowAnonymous]
|
||||
public async Task<IResponseOutput<LoginReturnDTO>> Login(UserLoginDTO loginUser, [FromServices] IEasyCachingProvider provider, [FromServices] IUserService _userService,
|
||||
[FromServices] ITokenService _tokenService,
|
||||
|
||||
[FromServices] IReadingImageTaskService readingImageTaskService,
|
||||
[FromServices] IConfiguration configuration)
|
||||
public async Task<IResponseOutput> Login(UserLoginDTO loginUser,
|
||||
[FromServices] IEasyCachingProvider provider,
|
||||
[FromServices] IUserService _userService,
|
||||
[FromServices] ITokenService _tokenService,
|
||||
[FromServices] IReadingImageTaskService readingImageTaskService,
|
||||
IOptionsMonitor<ServiceVerifyConfigOption> _verifyConfig,
|
||||
IMailVerificationService _mailVerificationService)
|
||||
{
|
||||
|
||||
|
||||
var returnModel = await _userService.Login(loginUser.UserName, loginUser.Password);
|
||||
|
||||
if (returnModel.IsSuccess)
|
||||
//MFA 邮箱验证 前端传递用户Id 和MFACode
|
||||
if (loginUser.UserId != null && _verifyConfig.CurrentValue.OpenLoginMFA)
|
||||
{
|
||||
#region GRPC 调用鉴权中心,因为服务器IIS问题 http/2 故而没法使用
|
||||
Guid userId = (Guid)loginUser.UserId;
|
||||
|
||||
////重试策略
|
||||
//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 }
|
||||
// }
|
||||
//};
|
||||
//验证MFA 编码是否有问题 ,前端要拆开,自己调用验证的逻辑
|
||||
//await _userService.VerifyMFACodeAsync(userId, loginUser.MFACode);
|
||||
|
||||
//#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();
|
||||
//// Return `true` to allow certificates that are untrusted/invalid
|
||||
//httpHandler.ServerCertificateCustomValidationCallback =
|
||||
// HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
|
||||
var basicInfo = await _userService.GetUserBasicInfo(userId, loginUser.Password);
|
||||
|
||||
var loginReturn = new LoginReturnDTO() { BasicInfo = basicInfo };
|
||||
|
||||
//////这一句是让grpc支持本地 http 如果本地访问部署在服务器上,那么是访问不成功的
|
||||
//AppContext.SetSwitch(
|
||||
// "System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
|
||||
loginReturn.JWTStr = _tokenService.GetToken(IRaCISClaims.Create(loginReturn.BasicInfo));
|
||||
|
||||
//#endregion
|
||||
|
||||
|
||||
|
||||
//var grpcAdress = configuration.GetValue<string>("GrpcAddress");
|
||||
////var grpcAdress = "http://localhost:7200";
|
||||
|
||||
//var channel = GrpcChannel.ForAddress(grpcAdress, new GrpcChannelOptions
|
||||
//{
|
||||
// HttpHandler = httpHandler,
|
||||
// ServiceConfig = new ServiceConfig { MethodConfigs = { defaultMethodConfig } }
|
||||
|
||||
//});
|
||||
////var channel = GrpcChannel.ForAddress(grpcAdress);
|
||||
//var grpcClient = new TokenGrpcService.TokenGrpcServiceClient(channel);
|
||||
|
||||
//var userInfo = returnModel.Data.BasicInfo;
|
||||
|
||||
//var tokenResponse = grpcClient.GetUserToken(new GetTokenReuqest()
|
||||
//{
|
||||
// Id = userInfo.Id.ToString(),
|
||||
// ReviewerCode = userInfo.ReviewerCode,
|
||||
// IsAdmin = userInfo.IsAdmin,
|
||||
// RealName = userInfo.RealName,
|
||||
// UserTypeEnumInt = (int)userInfo.UserTypeEnum,
|
||||
// UserTypeShortName = userInfo.UserTypeShortName,
|
||||
// UserName = userInfo.UserName
|
||||
//});
|
||||
|
||||
//returnModel.Data.JWTStr = tokenResponse.Token;
|
||||
|
||||
#endregion
|
||||
|
||||
returnModel.Data.JWTStr = _tokenService.GetToken(IRaCISClaims.Create(returnModel.Data.BasicInfo));
|
||||
|
||||
// 创建一个 CookieOptions 对象,用于设置 Cookie 的属性
|
||||
var option = new CookieOptions
|
||||
@@ -180,20 +134,145 @@ namespace IRaCIS.Api.Controllers
|
||||
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 provider.SetAsync(userId.ToString(), loginReturn.JWTStr, TimeSpan.FromDays(7));
|
||||
|
||||
await provider.SetAsync($"{userId.ToString()}_Online", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), TimeSpan.FromMinutes(_verifyConfig.CurrentValue.AutoLoginOutMinutes));
|
||||
|
||||
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)
|
||||
{
|
||||
//发版屏蔽
|
||||
|
||||
//returnModel.Data.JWTStr = _tokenService.GetToken(IRaCISClaims.Create(returnModel.Data.BasicInfo));
|
||||
|
||||
//MFA 发送邮件
|
||||
|
||||
returnModel.Data.IsMFA = true;
|
||||
|
||||
var email = returnModel.Data.BasicInfo.EMail;
|
||||
|
||||
var hiddenEmail = EmailMaskHelper.MaskEmail(email);
|
||||
|
||||
returnModel.Data.BasicInfo.EMail = hiddenEmail;
|
||||
|
||||
await _userService.SendMFAEmail(userId);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
returnModel.Data.JWTStr = _tokenService.GetToken(IRaCISClaims.Create(returnModel.Data.BasicInfo));
|
||||
|
||||
// 创建一个 CookieOptions 对象,用于设置 Cookie 的属性
|
||||
var option = new CookieOptions
|
||||
{
|
||||
Expires = DateTime.Now.AddMonths(1), // 设置过期时间为 30 分钟之后
|
||||
HttpOnly = false, // 确保 cookie 只能通过 HTTP 访问
|
||||
SameSite = Microsoft.AspNetCore.Http.SameSiteMode.None, // 设置 SameSite 属性
|
||||
Secure = false // 确保 cookie 只能通过 HTTPS 访问
|
||||
};
|
||||
|
||||
HttpContext.Response.Cookies.Append("access_token", returnModel.Data.JWTStr, option);
|
||||
|
||||
|
||||
|
||||
// 验证阅片休息时间
|
||||
await readingImageTaskService.ResetReadingRestTime(returnModel.Data.BasicInfo.Id);
|
||||
|
||||
await provider.SetAsync(userId.ToString(), returnModel.Data.JWTStr, TimeSpan.FromDays(7));
|
||||
|
||||
await provider.SetAsync($"{userId.ToString()}_Online", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), TimeSpan.FromMinutes(_verifyConfig.CurrentValue.AutoLoginOutMinutes));
|
||||
}
|
||||
|
||||
}
|
||||
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;
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
[HttpGet, Route("imageShare/ShareImage")]
|
||||
[AllowAnonymous]
|
||||
public IResponseOutput ShareImage([FromServices] ITokenService _tokenService)
|
||||
@@ -223,7 +302,7 @@ namespace IRaCIS.Api.Controllers
|
||||
|
||||
var ossOptions = serviceOption.AliyunOSS;
|
||||
|
||||
return ResponseOutput.Ok(new ObjectStoreDTO() { ObjectStoreUse = serviceOption.ObjectStoreUse, MinIO = serviceOption.MinIO ,AliyunOSS= serviceOption.AliyunOSS,AWS=serviceOption.AWS });
|
||||
return ResponseOutput.Ok(new ObjectStoreDTO() { ObjectStoreUse = serviceOption.ObjectStoreUse, MinIO = serviceOption.MinIO, AliyunOSS = serviceOption.AliyunOSS, AWS = serviceOption.AWS });
|
||||
|
||||
#region 临时token 屏蔽
|
||||
//IClientProfile profile = DefaultProfile.GetProfile(ossOptions.RegionId, ossOptions.AccessKeyId, ossOptions.AccessKeySecret);
|
||||
@@ -264,19 +343,19 @@ namespace IRaCIS.Api.Controllers
|
||||
#endregion
|
||||
|
||||
}
|
||||
else if(Enum.TryParse<ObjectStoreUse>(serviceOption.ObjectStoreUse, out var parsedValue) && parsedValue == ObjectStoreUse.MinIO)
|
||||
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 });
|
||||
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 )
|
||||
[HttpGet("user/GenerateSTS")]
|
||||
public IResponseOutput GenerateSTS([FromServices] IOptionsMonitor<AliyunOSSOptions> options)
|
||||
{
|
||||
var ossOptions = options.CurrentValue;
|
||||
|
||||
@@ -304,9 +383,9 @@ namespace IRaCIS.Api.Controllers
|
||||
SecurityToken = response.Credentials.SecurityToken,
|
||||
Expiration = response.Credentials.Expiration,
|
||||
|
||||
Region = ossOptions.region ,
|
||||
BucketName = ossOptions.bucketName ,
|
||||
ViewEndpoint = ossOptions.viewEndpoint ,
|
||||
Region = ossOptions.region,
|
||||
BucketName = ossOptions.bucketName,
|
||||
ViewEndpoint = ossOptions.viewEndpoint,
|
||||
|
||||
};
|
||||
|
||||
@@ -318,12 +397,12 @@ namespace IRaCIS.Api.Controllers
|
||||
|
||||
[HttpGet("User/UserRedirect")]
|
||||
[AllowAnonymous]
|
||||
public async Task<IActionResult> UserRedirect([FromServices] IRepository<User> _userRepository, string url ,[FromServices]ILogger<ExtraController> _logger)
|
||||
public async Task<IActionResult> UserRedirect([FromServices] IRepository<User> _userRepository, string url, [FromServices] ILogger<ExtraController> _logger)
|
||||
{
|
||||
|
||||
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);
|
||||
|
||||
@@ -331,12 +410,12 @@ namespace IRaCIS.Api.Controllers
|
||||
|
||||
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);
|
||||
|
||||
@@ -308,11 +308,28 @@ namespace IRaCIS.Core.API.Controllers
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 提交结构化录入并签名
|
||||
/// </summary>
|
||||
/// <param name="opt"></param>
|
||||
/// <returns></returns>
|
||||
/// <summary>
|
||||
/// PM签名一致性分析临床数据
|
||||
/// </summary>
|
||||
/// <param name="opt"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost, Route("Inspection/ReadingClinicalData/SignConsistencyAnalysisReadingClinicalData")]
|
||||
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
||||
|
||||
[UnitOfWork]
|
||||
public async Task<IResponseOutput> SignConsistencyAnalysisReadingClinicalData(DataInspectionDto<SignConsistencyAnalysisReadingClinicalDataInDto> opt)
|
||||
{
|
||||
var singid = await _inspectionService.RecordSing(opt.SignInfo);
|
||||
var result = await _readingClinicalDataService.SignConsistencyAnalysisReadingClinicalData(opt.Data);
|
||||
await _inspectionService.CompletedSign(singid, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 提交结构化录入并签名
|
||||
/// </summary>
|
||||
/// <param name="opt"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost, Route("Inspection/ClinicalAnswer/SubmitClinicalFormAndSign")]
|
||||
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
||||
|
||||
@@ -402,6 +419,19 @@ namespace IRaCIS.Core.API.Controllers
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
[HttpPost, Route("Inspection/configTrialBasicInfo/ConfigTrialPACSInfoConfirm")]
|
||||
[UnitOfWork]
|
||||
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "BeforeOngoingCantOpt" })]
|
||||
public async Task<IResponseOutput> ConfigTrialPACSInfoConfirm(DataInspectionDto<TrialPACSConfig> opt)
|
||||
{
|
||||
opt.Data.IsTrialPACSConfirmed = true;
|
||||
var singid = await _inspectionService.RecordSing(opt.SignInfo);
|
||||
var result = await _trialConfigService.ConfigTrialPACSInfo(opt.Data);
|
||||
await _inspectionService.CompletedSign(singid, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 签名确认
|
||||
/// </summary>
|
||||
@@ -419,13 +449,30 @@ namespace IRaCIS.Core.API.Controllers
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 重置并同步项目阅片标准
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpPost, Route("Inspection/ReadingCriterion/ResetAndAsyncCriterion")]
|
||||
[UnitOfWork]
|
||||
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
||||
|
||||
/// <summary>
|
||||
/// CRC RequestToQC 批量提交
|
||||
/// </summary>
|
||||
/// <param name="opt"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost, Route("Inspection/QCOperation/CRCRequestToQC")]
|
||||
public async Task<IResponseOutput> ResetAndAsyncCriterion(DataInspectionDto<ResetAndAsyncCriterionInDto> opt)
|
||||
{
|
||||
var singid = await _inspectionService.RecordSing(opt.SignInfo);
|
||||
var result = await _trialConfigService.ResetAndAsyncCriterion(opt.Data);
|
||||
await _inspectionService.CompletedSign(singid, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// CRC RequestToQC 批量提交
|
||||
/// </summary>
|
||||
/// <param name="opt"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost, Route("Inspection/QCOperation/CRCRequestToQC")]
|
||||
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
||||
[UnitOfWork]
|
||||
public async Task<IResponseOutput> CRCRequestToQC(DataInspectionDto<CRCRequestToQCCommand> opt)
|
||||
|
||||
@@ -10,8 +10,7 @@ using IRaCIS.Core.Application.Contracts.Dicom;
|
||||
using IRaCIS.Core.Application.Contracts.Dicom.DTO;
|
||||
using IRaCIS.Core.Application.Filter;
|
||||
using IRaCIS.Core.Application.Helper;
|
||||
using IRaCIS.Core.Application.MediatR.CommandAndQueries;
|
||||
using IRaCIS.Core.Application.MediatR.Handlers;
|
||||
using IRaCIS.Core.Application.MassTransit.Command;
|
||||
using IRaCIS.Core.Application.Service;
|
||||
using IRaCIS.Core.Application.Service.ImageAndDoc;
|
||||
using IRaCIS.Core.Application.Service.Reading.Dto;
|
||||
@@ -20,9 +19,8 @@ using IRaCIS.Core.Domain.Share;
|
||||
using IRaCIS.Core.Infra.EFCore;
|
||||
using IRaCIS.Core.Infrastructure;
|
||||
using IRaCIS.Core.Infrastructure.Extention;
|
||||
using Magicodes.ExporterAndImporter.Excel;
|
||||
using MassTransit;
|
||||
using MediatR;
|
||||
using MassTransit.Mediator;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Cors.Infrastructure;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
@@ -33,12 +31,12 @@ using Microsoft.AspNetCore.SignalR;
|
||||
using Microsoft.AspNetCore.StaticFiles;
|
||||
using Microsoft.AspNetCore.WebUtilities;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
using MiniExcelLibs;
|
||||
using Newtonsoft.Json;
|
||||
using SharpCompress.Archives;
|
||||
using SkiaSharp;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
@@ -233,15 +231,22 @@ namespace IRaCIS.Core.API.Controllers
|
||||
{
|
||||
public IMapper _mapper { get; set; }
|
||||
public IUserInfo _userInfo { get; set; }
|
||||
|
||||
|
||||
private readonly IMediator _mediator;
|
||||
|
||||
|
||||
|
||||
public IStringLocalizer _localizer { get; set; }
|
||||
|
||||
|
||||
private readonly IWebHostEnvironment _hostEnvironment;
|
||||
|
||||
private readonly IRepository _repository;
|
||||
|
||||
private readonly IEasyCachingProvider _provider;
|
||||
private readonly QCCommon _qCCommon;
|
||||
public StudyController(IMapper mapper, IUserInfo userInfo, IWebHostEnvironment hostEnvironment, IMediator mediator, IEasyCachingProvider provider,
|
||||
public StudyController(IMapper mapper, IStringLocalizer localizer, IUserInfo userInfo, IWebHostEnvironment hostEnvironment, IMediator mediator, IEasyCachingProvider provider,
|
||||
QCCommon qCCommon,
|
||||
IRepository repository)
|
||||
{
|
||||
@@ -249,7 +254,7 @@ namespace IRaCIS.Core.API.Controllers
|
||||
_provider = provider;
|
||||
_hostEnvironment = hostEnvironment;
|
||||
_mediator = mediator;
|
||||
|
||||
_localizer = localizer;
|
||||
_mapper = mapper;
|
||||
_userInfo = userInfo;
|
||||
_repository = repository;
|
||||
@@ -263,17 +268,11 @@ namespace IRaCIS.Core.API.Controllers
|
||||
[FromServices] IRepository<StudyMonitor> _studyMonitorRepository)
|
||||
{
|
||||
|
||||
if (_provider.Get<List<SystemAnonymization>>(StaticData.Anonymize.Anonymize_AddFixedFiled).Value == null)
|
||||
{
|
||||
await _mediator.Send(new AnonymizeCacheRequest());
|
||||
}
|
||||
|
||||
var savedInfo = _studyService.GetSaveToDicomInfo(preArchiveStudyCommand.SubjectVisitId);
|
||||
|
||||
var studyMonitor = new StudyMonitor()
|
||||
{
|
||||
TrialId = savedInfo.TrialId,
|
||||
SiteId = savedInfo.SiteId,
|
||||
SubjectId = savedInfo.SubjectId,
|
||||
SubjectVisitId = savedInfo.SubjectVisitId,
|
||||
|
||||
@@ -313,7 +312,7 @@ namespace IRaCIS.Core.API.Controllers
|
||||
string.IsNullOrEmpty(mediaTypeHeader.Boundary.Value))
|
||||
{
|
||||
//---不支持的MediaType
|
||||
return ResponseOutput.NotOk(StaticData.International("UploadDownLoad_UnsupportedMedia"));
|
||||
return ResponseOutput.NotOk(_localizer["UploadDownLoad_UnsupportedMedia"]);
|
||||
}
|
||||
|
||||
var archiveStudyCommand = new ArchiveStudyCommand() { AbandonStudyId = abandonStudyId, StudyInstanceUid = studyInstanceUid, SubjectVisitId = subjectVisitId };
|
||||
@@ -402,12 +401,12 @@ namespace IRaCIS.Core.API.Controllers
|
||||
|
||||
|
||||
}
|
||||
catch (Exception )
|
||||
catch (Exception)
|
||||
{
|
||||
_provider.Remove($"StudyUid_{trialId}_{archiveStudyCommand.StudyInstanceUid}");
|
||||
|
||||
//---请求异常,请重试!
|
||||
throw new BusinessValidationFailedException(StaticData.International("UploadDownLoad_RequestError"));
|
||||
throw new BusinessValidationFailedException(_localizer["UploadDownLoad_RequestError"]);
|
||||
}
|
||||
|
||||
studyMonitor.FileSize = (decimal)HttpContext.Request.ContentLength;
|
||||
@@ -477,6 +476,8 @@ namespace IRaCIS.Core.API.Controllers
|
||||
public string FilePath { get; set; }
|
||||
public string FileName { get; set; }
|
||||
public int FileFize { get; set; }
|
||||
|
||||
public string FileType { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -501,7 +502,7 @@ namespace IRaCIS.Core.API.Controllers
|
||||
|
||||
await _qCCommon.VerifyIsCRCSubmmitAsync(_repository, _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 _repository.Where<SubjectVisit>(t => t.Id == subjectVisitId).Select(t => new { t.TrialId, t.TrialSiteId, t.SubjectId }).FirstOrDefaultAsync()).IfNullThrowConvertException();
|
||||
|
||||
var studyMonitor = await _studyMonitorRepository.FirstOrDefaultAsync(t => t.Id == studyMonitorId);
|
||||
|
||||
@@ -509,7 +510,7 @@ namespace IRaCIS.Core.API.Controllers
|
||||
|
||||
foreach (var item in incommand.UploadedFileList)
|
||||
{
|
||||
await _repository.AddAsync(new NoneDicomStudyFile() { FileName = item.FileName, Path = item.FilePath, NoneDicomStudyId = noneDicomStudyId });
|
||||
await _repository.AddAsync(new NoneDicomStudyFile() { FileName = item.FileName, Path = item.FilePath, NoneDicomStudyId = noneDicomStudyId,FileType=item.FileType });
|
||||
|
||||
}
|
||||
var uploadFinishedTime = DateTime.Now;
|
||||
@@ -526,6 +527,7 @@ namespace IRaCIS.Core.API.Controllers
|
||||
studyMonitor.StudyCode = noneDicomStudy.StudyCode;
|
||||
studyMonitor.ArchiveFinishedTime = DateTime.Now;
|
||||
studyMonitor.IP = _userInfo.IP;
|
||||
studyMonitor.IsSuccess = true;
|
||||
|
||||
await _repository.SaveChangesAsync();
|
||||
|
||||
@@ -559,7 +561,7 @@ namespace IRaCIS.Core.API.Controllers
|
||||
if (!fileName.EndsWith(".xlsx") && !fileName.EndsWith(".csv") && !fileName.EndsWith(".xls"))
|
||||
{
|
||||
//---支持.xlsx、.xls、.csv格式的文件上传。
|
||||
throw new BusinessValidationFailedException(StaticData.International("UploadDownLoad_SupportedFormats"));
|
||||
throw new BusinessValidationFailedException(_localizer["UploadDownLoad_SupportedFormats"]);
|
||||
}
|
||||
|
||||
fileStream.CopyTo(templateFileStream);
|
||||
@@ -580,9 +582,22 @@ namespace IRaCIS.Core.API.Controllers
|
||||
var etcCheckList = new List<CheckViewModel>();
|
||||
|
||||
|
||||
|
||||
#region MiniExcel 需要自己验证数据格式规范
|
||||
|
||||
|
||||
if (fileName.EndsWith(".xlsx"))
|
||||
{
|
||||
etcCheckList = MiniExcel.Query<CheckViewModel>(templateFileStream, excelType: ExcelType.XLSX).ToList();
|
||||
}
|
||||
else if (fileName.EndsWith(".csv"))
|
||||
{
|
||||
//因为csv 需要加配置文件 不然都是null
|
||||
etcCheckList = MiniExcel.Query<CheckViewModel>(templateFileStream, null, configuration: new MiniExcelLibs.Csv.CsvConfiguration()
|
||||
{
|
||||
StreamReaderFunc = (stream) => new StreamReader(stream, Encoding.GetEncoding("gb2312"))
|
||||
}).ToList();
|
||||
|
||||
}
|
||||
//if (fileName.EndsWith(".csv"))
|
||||
//{
|
||||
// //因为csv 需要加配置文件 不然都是null
|
||||
@@ -598,67 +613,72 @@ namespace IRaCIS.Core.API.Controllers
|
||||
|
||||
#endregion
|
||||
|
||||
#region 升级net8 导入有问题
|
||||
|
||||
|
||||
//Magicodes 支持自定义特性验证
|
||||
if (fileName.EndsWith(".xlsx"))
|
||||
{
|
||||
var Importer = new ExcelImporter();
|
||||
// if (fileName.EndsWith(".xlsx"))
|
||||
//{
|
||||
// var Importer = new ExcelImporter();
|
||||
|
||||
var import = await Importer.Import<CheckViewModel>(templateFileStream);
|
||||
// var import = await Importer.Import<CheckViewModel>(templateFileStream);
|
||||
|
||||
if (import.Exception != null) return ResponseOutput.NotOk(import.Exception.ToString());
|
||||
// if (import.Exception != null) return ResponseOutput.NotOk(import.Exception.ToString());
|
||||
|
||||
//if (import.RowErrors.Count > 0) return ResponseOutput.NotOk(JsonConvert.SerializeObject(import.RowErrors));
|
||||
// //if (import.RowErrors.Count > 0) return ResponseOutput.NotOk(JsonConvert.SerializeObject(import.RowErrors));
|
||||
|
||||
if (import.TemplateErrors.Count > 0) return ResponseOutput.NotOk(JsonConvert.SerializeObject(import.TemplateErrors));
|
||||
// if (import.TemplateErrors.Count > 0) return ResponseOutput.NotOk(_localizer["UploadDownLoad_TemplateErrors"]);
|
||||
|
||||
etcCheckList = import.Data.ToList();
|
||||
}
|
||||
else if (fileName.EndsWith(".csv"))
|
||||
{
|
||||
#region 临时方案 MiniExcel读取 然后保存为xlsx 再用 Magicodes验证数据
|
||||
// etcCheckList = import.Data.ToList();
|
||||
//}
|
||||
//else if (fileName.EndsWith(".csv"))
|
||||
//{
|
||||
// #region 临时方案 MiniExcel读取 然后保存为xlsx 再用 Magicodes验证数据
|
||||
|
||||
//因为csv 需要加配置文件 不然都是null
|
||||
etcCheckList = MiniExcel.Query<CheckViewModel>(templateFileStream, null, configuration: new MiniExcelLibs.Csv.CsvConfiguration()
|
||||
{
|
||||
StreamReaderFunc = (stream) => new StreamReader(stream, Encoding.GetEncoding("gb2312"))
|
||||
}).ToList();
|
||||
// //因为csv 需要加配置文件 不然都是null
|
||||
// etcCheckList = MiniExcel.Query<CheckViewModel>(templateFileStream, null, configuration: new MiniExcelLibs.Csv.CsvConfiguration()
|
||||
// {
|
||||
// StreamReaderFunc = (stream) => new StreamReader(stream, Encoding.GetEncoding("gb2312"))
|
||||
// }).ToList();
|
||||
|
||||
var (csVToXlsxPath, csVToXlsxRelativePath) = FileStoreHelper.GetTrialCheckFilePath(_hostEnvironment, Path.GetFileNameWithoutExtension(fileName) + ".xlsx", trialId);
|
||||
// var (csVToXlsxPath, csVToXlsxRelativePath) = FileStoreHelper.GetTrialCheckFilePath(_hostEnvironment, Path.GetFileNameWithoutExtension(fileName) + ".xlsx", trialId);
|
||||
|
||||
|
||||
await MiniExcel.SaveAsAsync(csVToXlsxPath, etcCheckList, excelType: ExcelType.XLSX);
|
||||
// await MiniExcel.SaveAsAsync(csVToXlsxPath, etcCheckList, excelType: ExcelType.XLSX);
|
||||
|
||||
|
||||
var Importer = new ExcelImporter();
|
||||
// var Importer = new ExcelImporter();
|
||||
|
||||
var import = await Importer.Import<CheckViewModel>(System.IO.File.OpenRead(csVToXlsxPath));
|
||||
// var import = await Importer.Import<CheckViewModel>(System.IO.File.OpenRead(csVToXlsxPath));
|
||||
|
||||
if (import.Exception != null) return ResponseOutput.NotOk(import.Exception.ToString());
|
||||
// if (import.Exception != null) return ResponseOutput.NotOk(import.Exception.ToString());
|
||||
|
||||
//if (import.RowErrors.Count > 0) return ResponseOutput.NotOk(JsonConvert.SerializeObject(import.RowErrors));
|
||||
// //if (import.RowErrors.Count > 0) return ResponseOutput.NotOk(JsonConvert.SerializeObject(import.RowErrors));
|
||||
|
||||
if (import.TemplateErrors.Count > 0) return ResponseOutput.NotOk(JsonConvert.SerializeObject(import.TemplateErrors));
|
||||
// if (import.TemplateErrors.Count > 0) return ResponseOutput.NotOk(JsonConvert.SerializeObject(import.TemplateErrors));
|
||||
|
||||
etcCheckList = import.Data.ToList();
|
||||
// etcCheckList = import.Data.ToList();
|
||||
|
||||
#endregion
|
||||
// #endregion
|
||||
|
||||
|
||||
#region 导入组件有问题 excel编码格式
|
||||
//var Importer = new CsvImporter();
|
||||
// #region 导入组件有问题 excel编码格式
|
||||
// //var Importer = new CsvImporter();
|
||||
|
||||
//var import = await Importer.Import<CheckViewModel>(File.OpenRead(filePath));
|
||||
// //var import = await Importer.Import<CheckViewModel>(File.OpenRead(filePath));
|
||||
|
||||
//if (import.Exception != null) return ResponseOutput.NotOk(import.Exception.ToString());
|
||||
// //if (import.Exception != null) return ResponseOutput.NotOk(import.Exception.ToString());
|
||||
|
||||
//if (import.RowErrors.Count > 0) return ResponseOutput.NotOk(JsonConvert.SerializeObject(import.RowErrors));
|
||||
// //if (import.RowErrors.Count > 0) return ResponseOutput.NotOk(JsonConvert.SerializeObject(import.RowErrors));
|
||||
|
||||
//if (import.TemplateErrors.Count > 0) return ResponseOutput.NotOk(JsonConvert.SerializeObject(import.TemplateErrors));
|
||||
// //if (import.TemplateErrors.Count > 0) return ResponseOutput.NotOk(JsonConvert.SerializeObject(import.TemplateErrors));
|
||||
|
||||
//etcCheckList = import.Data.ToList();
|
||||
#endregion
|
||||
// //etcCheckList = import.Data.ToList();
|
||||
// #endregion
|
||||
|
||||
}
|
||||
//}
|
||||
|
||||
#endregion
|
||||
//ExcelReaderFactory 需要自己验证数据 并且从固定列取数据
|
||||
else
|
||||
{
|
||||
@@ -697,7 +717,7 @@ namespace IRaCIS.Core.API.Controllers
|
||||
if (etcCheckList == null || etcCheckList.Count == 0)
|
||||
{
|
||||
//---请保证上传数据符合模板文件中的样式,且存在有效数据。
|
||||
return ResponseOutput.NotOk(StaticData.International("UploadDownLoad_InvalidData"));
|
||||
return ResponseOutput.NotOk(_localizer["UploadDownLoad_InvalidData"]);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -718,13 +738,18 @@ namespace IRaCIS.Core.API.Controllers
|
||||
if (etcCheckList.Count == 0)
|
||||
{
|
||||
//---请保证上传数据符合模板文件中的样式,且存在有效数据。
|
||||
return ResponseOutput.NotOk(StaticData.International("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();
|
||||
|
||||
@@ -866,89 +891,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
|
||||
|
||||
@@ -962,12 +904,13 @@ namespace IRaCIS.Core.API.Controllers
|
||||
public IMapper _mapper { get; set; }
|
||||
public IUserInfo _userInfo { get; set; }
|
||||
private readonly IMediator _mediator;
|
||||
|
||||
public IStringLocalizer _localizer { get; set; }
|
||||
private readonly IWebHostEnvironment _hostEnvironment;
|
||||
|
||||
public UploadDownLoadController(IMapper mapper, IUserInfo userInfo, IMediator mediator, IWebHostEnvironment hostEnvironment)
|
||||
public UploadDownLoadController(IMapper mapper, IUserInfo userInfo, IStringLocalizer localizer, IMediator mediator, IWebHostEnvironment hostEnvironment)
|
||||
{
|
||||
_hostEnvironment = hostEnvironment;
|
||||
_localizer = localizer;
|
||||
_mediator = mediator;
|
||||
_mapper = mapper;
|
||||
_userInfo = userInfo;
|
||||
@@ -994,7 +937,7 @@ namespace IRaCIS.Core.API.Controllers
|
||||
if (!realFileName.EndsWith(".xlsx", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// 请用提供格式的模板excel上传需要处理的数据
|
||||
throw new BusinessValidationFailedException(StaticData.International("UploadDownLoad_TemplateUploadData"));
|
||||
throw new BusinessValidationFailedException(_localizer["UploadDownLoad_TemplateUploadData"]);
|
||||
}
|
||||
|
||||
var ossRelativePath = await oSSService.UploadToOSSAsync(fileStream, "InspectionUpload/SiteSurvey", realFileName);
|
||||
@@ -1015,7 +958,7 @@ namespace IRaCIS.Core.API.Controllers
|
||||
if (excelList.Any(t => string.IsNullOrWhiteSpace(t.TrialSiteCode) || string.IsNullOrWhiteSpace(t.FirstName) || string.IsNullOrWhiteSpace(t.LastName) || string.IsNullOrWhiteSpace(t.Email) || string.IsNullOrWhiteSpace(t.UserTypeStr)))
|
||||
{
|
||||
//请确保Excel中 每一行的 中心编号,姓名,邮箱,用户类型数据记录完整再进行上传
|
||||
throw new BusinessValidationFailedException(StaticData.International("UploadDownLoad_EnsureCompleteData"));
|
||||
throw new BusinessValidationFailedException(_localizer["UploadDownLoad_EnsureCompleteData"]);
|
||||
}
|
||||
|
||||
var siteCodeList = excelList.Select(t => t.TrialSiteCode.Trim().ToUpper()).Distinct().ToList();
|
||||
@@ -1023,35 +966,35 @@ namespace IRaCIS.Core.API.Controllers
|
||||
if (_trialSiteRepository.Where(t => t.TrialId == trialId && siteCodeList.Contains(t.TrialSiteCode.ToUpper())).Count() != siteCodeList.Count)
|
||||
{
|
||||
//在项目中未找到该Excel中部分或全部中心
|
||||
throw new BusinessValidationFailedException(StaticData.International("UploadDownLoad_InvalidCenters"));
|
||||
throw new BusinessValidationFailedException(_localizer["UploadDownLoad_InvalidCenters"]);
|
||||
}
|
||||
|
||||
if (excelList.GroupBy(t => new { t.TrialSiteCode, t.UserTypeStr, t.Email }).Any(g => g.Count() > 1))
|
||||
{
|
||||
// 同一邮箱,同一用户类型,只能生成一个账户,请核查Excel数据
|
||||
|
||||
throw new BusinessValidationFailedException(StaticData.International("UploadDownLoad_CheckDuplicateAccounts"));
|
||||
throw new BusinessValidationFailedException(_localizer["UploadDownLoad_CheckDuplicateAccounts"]);
|
||||
}
|
||||
|
||||
if (excelList.Any(t => !t.Email.Contains("@")))
|
||||
{
|
||||
//有邮箱不符合邮箱格式,请核查Excel数据
|
||||
throw new BusinessValidationFailedException(StaticData.International("UploadDownLoad_InvalidEmail"));
|
||||
throw new BusinessValidationFailedException(_localizer["UploadDownLoad_InvalidEmail"]);
|
||||
}
|
||||
var generateUserTypeList = new List<string>() { "CRC", "CRA" };
|
||||
|
||||
if (excelList.Any(t => !generateUserTypeList.Contains(t.UserTypeStr.ToUpper())))
|
||||
{
|
||||
//用户类型仅能为 CRC,SR,CRA 请核查Excel数据
|
||||
throw new BusinessValidationFailedException(StaticData.International("UploadDownLoad_InvalidUserType"));
|
||||
throw new BusinessValidationFailedException(_localizer["UploadDownLoad_InvalidUserType"]);
|
||||
}
|
||||
if (excelList.Count == 0)
|
||||
{
|
||||
throw new BusinessValidationFailedException(StaticData.International("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 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)
|
||||
{
|
||||
@@ -1072,7 +1015,7 @@ 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -63,25 +63,25 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="aliyun-net-sdk-sts" Version="3.1.2" />
|
||||
<PackageReference Include="AspNetCoreRateLimit" Version="5.0.0" />
|
||||
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="8.0.0" />
|
||||
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.1" />
|
||||
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="9.0.0" />
|
||||
<PackageReference Include="ConfigMapFileProvider" Version="2.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.7" />
|
||||
<PackageReference Include="Hangfire.AspNetCore" Version="1.8.14" />
|
||||
<PackageReference Include="Hangfire.Dashboard.BasicAuthorization" Version="1.0.2" />
|
||||
<PackageReference Include="Hangfire.SqlServer" Version="1.8.7" />
|
||||
<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.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.6" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.19.5" />
|
||||
<PackageReference Include="Serilog.AspNetCore" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.20.1" />
|
||||
<PackageReference Include="Serilog.AspNetCore" Version="8.0.1" />
|
||||
<PackageReference Include="Serilog.Enrichers.ClientInfo" Version="2.0.3" />
|
||||
<PackageReference Include="Serilog.Sinks.Email" Version="2.4.0" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="6.5.0" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="6.6.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
<param name="doctorId"></param>
|
||||
<returns></returns>
|
||||
</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,EasyCaching.Core.IEasyCachingProvider,IRaCIS.Application.Services.IUserService,IRaCIS.Core.Application.Auth.ITokenService,IRaCIS.Core.Application.Contracts.IReadingImageTaskService,Microsoft.Extensions.Options.IOptionsMonitor{IRaCIS.Core.Domain.Share.ServiceVerifyConfigOption},IRaCIS.Application.Services.IMailVerificationService)">
|
||||
<summary> 系统用户登录接口[New] </summary>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Core.API.Controllers.Special.FinancialChangeController.AddOrUpdateTrialInspection(IRaCIS.Core.Application.Service.Inspection.DTO.DataInspectionDto{IRaCIS.Application.Contracts.TrialCommand})">
|
||||
@@ -167,6 +167,13 @@
|
||||
<param name="opt"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Core.API.Controllers.InspectionController.SignConsistencyAnalysisReadingClinicalData(IRaCIS.Core.Application.Service.Inspection.DTO.DataInspectionDto{IRaCIS.Core.Application.Service.Reading.Dto.SignConsistencyAnalysisReadingClinicalDataInDto})">
|
||||
<summary>
|
||||
PM签名一致性分析临床数据
|
||||
</summary>
|
||||
<param name="opt"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Core.API.Controllers.InspectionController.SubmitClinicalFormAndSign(IRaCIS.Core.Application.Service.Inspection.DTO.DataInspectionDto{IRaCIS.Core.Application.Service.Reading.Dto.SubmitClinicalFormInDto})">
|
||||
<summary>
|
||||
提交结构化录入并签名
|
||||
@@ -208,6 +215,12 @@
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Core.API.Controllers.InspectionController.ResetAndAsyncCriterion(IRaCIS.Core.Application.Service.Inspection.DTO.DataInspectionDto{IRaCIS.Core.Application.Service.Reading.Dto.ResetAndAsyncCriterionInDto})">
|
||||
<summary>
|
||||
重置并同步项目阅片标准
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Core.API.Controllers.InspectionController.CRCRequestToQC(IRaCIS.Core.Application.Service.Inspection.DTO.DataInspectionDto{IRaCIS.Core.Application.Contracts.CRCRequestToQCCommand})">
|
||||
<summary>
|
||||
CRC RequestToQC 批量提交
|
||||
@@ -313,38 +326,6 @@
|
||||
<param name="_attachmentrepository"></param>
|
||||
<returns></returns>
|
||||
</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})">
|
||||
<summary> 通用文件下载 </summary>
|
||||
</member>
|
||||
@@ -365,6 +346,11 @@
|
||||
IPLimit限流 启动服务
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:IRaCIS.Core.API.JSONTimeZoneConverter">
|
||||
<summary>
|
||||
序列化,反序列化的时候,处理时间 时区转换
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Core.API.NullToEmptyStringResolver.CreateProperties(System.Type,Newtonsoft.Json.MemberSerialization)">
|
||||
<summary>
|
||||
创建属性
|
||||
@@ -380,6 +366,11 @@
|
||||
<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">
|
||||
<summary>
|
||||
对称可逆加密
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
+112
-25
@@ -4,8 +4,6 @@ using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Serilog;
|
||||
using MediatR;
|
||||
using IRaCIS.Core.Application.MediatR.Handlers;
|
||||
using System.Threading.Tasks;
|
||||
using MassTransit;
|
||||
using MassTransit.NewIdProviders;
|
||||
@@ -24,8 +22,17 @@ using IRaCIS.Core.Application.Filter;
|
||||
using Microsoft.AspNetCore.HttpOverrides;
|
||||
using IRaCIS.Application.Services.BackGroundJob;
|
||||
using LogDashboard;
|
||||
using OfficeOpenXml.Utils;
|
||||
|
||||
using FellowOakDicom.Network;
|
||||
using IRaCIS.Core.Application.Service.ImageAndDoc;
|
||||
using IP2Region.Net.Abstractions;
|
||||
using IP2Region.Net.XDB;
|
||||
using IRaCIS.Core.Application.BusinessFilter;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using IRaCIS.Core.Infrastructure.Extention;
|
||||
using Newtonsoft.Json;
|
||||
using Microsoft.AspNetCore.Diagnostics;
|
||||
using IRaCIS.Core.Application.MassTransit.Command;
|
||||
using IRaCIS.Core.Application.MassTransit.Consumer;
|
||||
|
||||
|
||||
#region 获取环境变量
|
||||
@@ -74,8 +81,8 @@ if (urlsIndex > -1)
|
||||
|
||||
NewId.SetProcessIdProvider(new CurrentProcessIdProvider());
|
||||
|
||||
builder.Configuration.AddJsonFile("appsettings.json", false, true)
|
||||
.AddJsonFile($"appsettings.{enviromentName}.json", false, true);
|
||||
builder.Configuration.AddJsonFile(ConfigMapFileProvider.FromRelativePath(""), "appsettings.json", false, true)
|
||||
.AddJsonFile(ConfigMapFileProvider.FromRelativePath(""), $"appsettings.{enviromentName}.json", false, true);
|
||||
|
||||
builder.Host
|
||||
.UseServiceProviderFactory(new AutofacServiceProviderFactory())
|
||||
@@ -102,19 +109,19 @@ builder.Services.AddControllers(options =>
|
||||
options.Filters.Add<ModelActionFilter>();
|
||||
options.Filters.Add<ProjectExceptionFilter>();
|
||||
options.Filters.Add<UnitOfWorkFilter>();
|
||||
options.Filters.Add<EncreptApiResultFilter>(10);
|
||||
options.Filters.Add<LimitUserRequestAuthorization>();
|
||||
|
||||
if (_configuration.GetSection("BasicSystemConfig").GetValue<bool>("OpenLoginLimit"))
|
||||
{
|
||||
options.Filters.Add<LimitUserRequestAuthorization>();
|
||||
}
|
||||
|
||||
})
|
||||
.AddNewtonsoftJsonSetup(); // NewtonsoftJson 序列化 处理
|
||||
.AddNewtonsoftJsonSetup(builder.Services); // NewtonsoftJson 序列化 处理
|
||||
|
||||
builder.Services.AddOptions().Configure<SystemEmailSendConfig>(_configuration.GetSection("SystemEmailSendConfig"));
|
||||
builder.Services.AddOptions().Configure<ServiceVerifyConfigOption>(_configuration.GetSection("BasicSystemConfig"));
|
||||
builder.Services.AddOptions().Configure<AliyunOSSOptions>(_configuration.GetSection("AliyunOSS"));
|
||||
builder.Services.AddOptions().Configure<ObjectStoreServiceOptions>(_configuration.GetSection("ObjectStoreService"));
|
||||
builder.Services.AddOptions().Configure<EncreptResponseOption>(_configuration.GetSection("EncrypteResponseConfig"));
|
||||
builder.Services.AddOptions().Configure<SystemPacsConfig>(_configuration.GetSection("SystemPacsConfig"));
|
||||
|
||||
|
||||
//动态WebApi + UnifiedApiResultFilter 省掉控制器代码
|
||||
@@ -131,7 +138,28 @@ builder.Services.AddSwaggerSetup();
|
||||
builder.Services.AddJWTAuthSetup(_configuration);
|
||||
|
||||
// MediatR 进程内消息 事件解耦 从程序集中 注册命令和handler对应关系
|
||||
builder.Services.AddMediatR(cfg => cfg.RegisterServicesFromAssemblyContaining<ConsistencyVerificationHandler>());
|
||||
//builder.Services.AddMediatR(cfg => cfg.RegisterServicesFromAssemblyContaining<ConsistencyVerificationHandler>());
|
||||
|
||||
#region MassTransit
|
||||
//masstransit组件 也支持MediatR 中介者模式,但是支持分布式,考虑后续,所以在次替代MediatR
|
||||
builder.Services.AddMediator(cfg =>
|
||||
{
|
||||
cfg.AddConsumer<ConsistencyCheckConsumer>();
|
||||
});
|
||||
|
||||
builder.Services.AddMassTransit(cfg =>
|
||||
{
|
||||
cfg.UsingInMemory();
|
||||
});
|
||||
#endregion
|
||||
|
||||
|
||||
#region FusionCache
|
||||
|
||||
builder.Services.AddFusionCache();
|
||||
|
||||
#endregion
|
||||
|
||||
// EasyCaching 缓存
|
||||
builder.Services.AddEasyCachingSetup(_configuration);
|
||||
|
||||
@@ -159,8 +187,10 @@ builder.Services.AddSignalR();
|
||||
builder.Services.AddSingleton<IUserIdProvider, IRaCISUserIdProvider>();
|
||||
|
||||
|
||||
builder.Services.AddSingleton<ISearcher>(new Searcher(CachePolicy.Content, Path.Combine(AppContext.BaseDirectory, StaticData.Folder.Resources, "ip2region.xdb")));
|
||||
|
||||
|
||||
//builder.Services.AddExceptionHandler<GlobalExceptionHandler>();
|
||||
//builder.Services.AddProblemDetails();
|
||||
|
||||
#region 历史废弃配置
|
||||
//builder.Services.AddMemoryCache();
|
||||
@@ -187,6 +217,56 @@ var env = app.Environment;
|
||||
|
||||
// Configure the HTTP request pipeline.
|
||||
|
||||
#region 异常处理 全局业务异常已统一处理了,非业务错误会来到这里 400 -500状态码
|
||||
|
||||
|
||||
//app.UseStatusCodePagesWithReExecute("/Error/{0}");
|
||||
|
||||
app.UseStatusCodePages(async context =>
|
||||
{
|
||||
var code = context.HttpContext.Response.StatusCode;
|
||||
context.HttpContext.Response.ContentType = "application/json";
|
||||
if (code < 500)
|
||||
{
|
||||
await context.HttpContext.Response.WriteAsync(JsonConvert.SerializeObject(ResponseOutput.NotOk($"Client error, actual request error status code({code})")));
|
||||
}
|
||||
else
|
||||
{
|
||||
//ResultFilter 里面的异常并不会到这里
|
||||
await context.HttpContext.Response.WriteAsync(JsonConvert.SerializeObject((ResponseOutput.NotOk($"Server error , actual request error status code({code})"))));
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
//app.UseExceptionHandler(o => { });
|
||||
|
||||
//这里没生效,原因未知,官方文档也是这种写法,也用了GlobalExceptionHandler 尝试,还是不行,怀疑框架bug
|
||||
//app.UseExceptionHandler(configure =>
|
||||
//{
|
||||
// configure.Run(async context =>
|
||||
// {
|
||||
// var exceptionHandlerPathFeature = context.Features.Get<IExceptionHandlerPathFeature>();
|
||||
//var ex = exceptionHandlerPathFeature?.Error;
|
||||
//context.Response.ContentType = "application/json";
|
||||
|
||||
//if (ex != null)
|
||||
//{
|
||||
// var errorInfo = $"Exception: {ex.Message}[{ex.StackTrace}]" + (ex.InnerException != null ? $" InnerException: {ex.InnerException.Message}[{ex.InnerException.StackTrace}]" : "");
|
||||
|
||||
// await context.Response.WriteAsync(JsonConvert.SerializeObject(ResponseOutput.NotOk($"{ex?.Message}")));
|
||||
|
||||
// Log.Logger.Error(errorInfo);
|
||||
|
||||
|
||||
//}
|
||||
|
||||
// });
|
||||
//});
|
||||
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
//本地化
|
||||
app.UseLocalization();
|
||||
|
||||
@@ -195,12 +275,11 @@ app.UseForwardedHeaders();
|
||||
//响应压缩
|
||||
app.UseResponseCompression();
|
||||
|
||||
//app.UseCors(t => t.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader());
|
||||
|
||||
//不需要 token 访问的静态文件 wwwroot css, JavaScript, and images don't require authentication.
|
||||
app.UseStaticFiles();
|
||||
|
||||
app.UseMiddleware<MultiDiskStaticFilesMiddleware>();
|
||||
|
||||
|
||||
//LogDashboard
|
||||
app.UseLogDashboard("/LogDashboard");
|
||||
@@ -208,22 +287,24 @@ app.UseLogDashboard("/LogDashboard");
|
||||
//hangfire
|
||||
app.UseHangfireConfig(env);
|
||||
|
||||
#region 暂时废弃
|
||||
|
||||
//app.UseMiddleware<MultiDiskStaticFilesMiddleware>();
|
||||
////限流 中间件
|
||||
//app.UseIpRateLimiting();
|
||||
//if (env.IsDevelopment())
|
||||
//{
|
||||
// app.UseDeveloperExceptionPage();
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
// //app.UseHsts();
|
||||
//}
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
if (env.IsDevelopment())
|
||||
{
|
||||
app.UseDeveloperExceptionPage();
|
||||
}
|
||||
else
|
||||
{
|
||||
//app.UseHsts();
|
||||
}
|
||||
|
||||
// 特殊异常处理 比如 404
|
||||
app.UseStatusCodePagesWithReExecute("/Error/{0}");
|
||||
|
||||
SwaggerSetup.Configure(app, env);
|
||||
|
||||
@@ -256,6 +337,7 @@ var hangfireJobService = app.Services.GetRequiredService<IIRaCISHangfireJob>();
|
||||
await hangfireJobService.InitHangfireJobTaskAsync();
|
||||
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
try
|
||||
@@ -291,6 +373,11 @@ try
|
||||
//Log.Logger.Warning($"ContentRootPath——GetParent:{Directory.GetParent(env.ContentRootPath).Parent.FullName}");
|
||||
//Log.Logger.Warning($"ContentRootPath——xx:{Path.GetDirectoryName(Path.GetDirectoryName(env.ContentRootPath))}");
|
||||
|
||||
|
||||
|
||||
|
||||
var server = DicomServerFactory.Create<CStoreSCPService>(11112, userState: app.Services);
|
||||
|
||||
app.Run();
|
||||
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Event_IRC"
|
||||
},
|
||||
"applicationUrl": "http://localhost:6101"
|
||||
"applicationUrl": "http://localhost:6100"
|
||||
},
|
||||
"Docker": {
|
||||
"commandName": "Docker",
|
||||
@@ -54,11 +54,11 @@
|
||||
},
|
||||
"applicationUrl": "http://localhost:6100"
|
||||
},
|
||||
"IRaCIS.US_IRC": {
|
||||
"IRaCIS.US_Uat_IRC": {
|
||||
"commandName": "Project",
|
||||
"launchBrowser": true,
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "US_IRC"
|
||||
"ASPNETCORE_ENVIRONMENT": "US_Uat_IRC"
|
||||
},
|
||||
"applicationUrl": "http://localhost:6100"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Localization;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
@@ -12,8 +13,9 @@ namespace IRaCIS.Core.API
|
||||
{
|
||||
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
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using AutoMapper.EquivalencyExpression;
|
||||
using IRaCIS.Core.Application.Service;
|
||||
using IRaCIS.Core.Domain.Models;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace IRaCIS.Core.API
|
||||
@@ -14,11 +15,15 @@ namespace IRaCIS.Core.API
|
||||
//AutoMapper.Collection.EntityFrameworkCore
|
||||
automapper.AddCollectionMappers();
|
||||
|
||||
|
||||
// 全局忽略 DomainEvents 属性
|
||||
automapper.AddGlobalIgnore(nameof(Entity.DomainEvents));
|
||||
|
||||
#region 会使 IncludeMembers 失效 不能全局使用
|
||||
//mapping an EntityFramework Core DbContext-object.
|
||||
//automapper.UseEntityFrameworkCoreModel<IRaCISDBContext>(services);
|
||||
|
||||
|
||||
|
||||
//automapper.ForAllMaps((a, b) => b.ForAllMembers(opt => opt.Condition((src, dest, srcMember, desMenber) =>
|
||||
//{
|
||||
// //// Can test When Guid? -> Guid if source is null will change to Guid.Empty
|
||||
@@ -27,7 +32,7 @@ namespace IRaCIS.Core.API
|
||||
// // not want to map a null Guid? value to db Guid value
|
||||
//})));
|
||||
#endregion
|
||||
|
||||
|
||||
}, typeof(QCConfig).Assembly);
|
||||
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@ using System.Linq;
|
||||
using System.Reflection;
|
||||
using IRaCIS.Core.Domain.Models;
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using MediatR;
|
||||
using IRaCIS.Application.Services;
|
||||
using IRaCIS.Application.Interfaces;
|
||||
using AutoMapper;
|
||||
@@ -55,8 +54,8 @@ namespace IRaCIS.Core.API
|
||||
|
||||
|
||||
|
||||
containerBuilder.RegisterType<HttpContextAccessor>().As<IHttpContextAccessor>().SingleInstance();
|
||||
containerBuilder.RegisterType<UserInfo>().As<IUserInfo>().InstancePerLifetimeScope();
|
||||
//containerBuilder.RegisterType<HttpContextAccessor>().As<IHttpContextAccessor>().SingleInstance();
|
||||
//containerBuilder.RegisterType<UserInfo>().As<IUserInfo>().InstancePerLifetimeScope();
|
||||
|
||||
|
||||
//注册hangfire任务 依赖注入
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
using Hangfire.SqlServer;
|
||||
using Castle.Core.Logging;
|
||||
using EntityFramework.Exceptions.SqlServer;
|
||||
using Hangfire.SqlServer;
|
||||
using IRaCIS.Core.Application.Triggers;
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using IRaCIS.Core.Infra.EFCore;
|
||||
using IRaCIS.Core.Infra.EFCore.Interceptor;
|
||||
using Medallion.Threading;
|
||||
using Medallion.Threading.SqlServer;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Diagnostics;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using StackExchange.Redis;
|
||||
|
||||
namespace IRaCIS.Core.API
|
||||
@@ -14,18 +20,35 @@ namespace IRaCIS.Core.API
|
||||
{
|
||||
public static void AddEFSetup( this IServiceCollection services, IConfiguration configuration)
|
||||
{
|
||||
//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
|
||||
//Web程序中通过重用池中DbContext实例可提高高并发场景下的吞吐量, 这在概念上类似于ADO.NET Provider原生的连接池操作方式,具有节省DbContext实例化成本的优点
|
||||
services.AddDbContext<IRaCISDBContext>(options =>
|
||||
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();
|
||||
|
||||
@@ -50,8 +73,8 @@ namespace IRaCIS.Core.API
|
||||
//triggerOptions.AddTrigger<AddlTrialUserTrigger>();
|
||||
triggerOptions.AddTrigger<VisitTaskIsFrontTaskNeedSignButNotSignTrigger>();
|
||||
triggerOptions.AddTrigger<VisitTaskIbeforeTrigger>();
|
||||
|
||||
|
||||
triggerOptions.AddTrigger<UserLogTrigger>();
|
||||
|
||||
|
||||
|
||||
@@ -62,6 +85,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
|
||||
services.AddSingleton<IDistributedLockProvider>(sp =>
|
||||
{
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
using System;
|
||||
|
||||
namespace IRaCIS.Core.API
|
||||
{
|
||||
public class JSONCustomDateConverter : DateTimeConverterBase
|
||||
{
|
||||
private TimeZoneInfo _timeZoneInfo;
|
||||
private string _dateFormat;
|
||||
|
||||
private IUserInfo _userInfo;
|
||||
public JSONCustomDateConverter(string dateFormat, TimeZoneInfo timeZoneInfo, IUserInfo userInfo)
|
||||
{
|
||||
_dateFormat = dateFormat;
|
||||
_timeZoneInfo = timeZoneInfo;
|
||||
|
||||
_userInfo = userInfo;
|
||||
}
|
||||
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||
{
|
||||
var timeZoneId = _userInfo.TimeZoneId;
|
||||
|
||||
var needConvertUtcDateTime = Convert.ToDateTime(value);
|
||||
|
||||
|
||||
var tz = TimeZoneInfo.FindSystemTimeZoneById(timeZoneId);
|
||||
|
||||
|
||||
var dateTimeOffset = new DateTimeOffset(needConvertUtcDateTime);
|
||||
|
||||
var time = TimeZoneInfo.ConvertTimeFromUtc(needConvertUtcDateTime, tz).ToString(_dateFormat);
|
||||
|
||||
writer.WriteValue(time);
|
||||
writer.Flush();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
using StackExchange.Redis;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
|
||||
namespace IRaCIS.Core.API
|
||||
{
|
||||
/// <summary>
|
||||
/// 序列化,反序列化的时候,处理时间 时区转换
|
||||
/// </summary>
|
||||
public class JSONTimeZoneConverter : DateTimeConverterBase
|
||||
{
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
|
||||
private readonly TimeZoneInfo _clientTimeZone;
|
||||
public JSONTimeZoneConverter(IHttpContextAccessor httpContextAccessor)
|
||||
{
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
|
||||
//默认是UTC
|
||||
//var timeZoneId = "Etc/UTC";
|
||||
var timeZoneId = "Asia/Shanghai";
|
||||
|
||||
var timeZoneIdHeader = _httpContextAccessor?.HttpContext?.Request?.Headers["TimeZoneId"];
|
||||
|
||||
if (timeZoneIdHeader is not null && !string.IsNullOrEmpty(timeZoneIdHeader.Value))
|
||||
{
|
||||
timeZoneId = timeZoneIdHeader.Value;
|
||||
}
|
||||
|
||||
_clientTimeZone = TimeZoneInfo.FindSystemTimeZoneById(timeZoneId);
|
||||
|
||||
}
|
||||
|
||||
public override bool CanConvert(Type objectType)
|
||||
{
|
||||
// 仅支持 DateTime 类型的转换
|
||||
return objectType == typeof(DateTime)|| objectType == typeof(DateTime?);
|
||||
}
|
||||
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||
{
|
||||
if (reader.Value == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
DateTime dateTime;
|
||||
|
||||
if (reader.ValueType == typeof(DateTime) || reader.ValueType == typeof(DateTime?))
|
||||
{
|
||||
DateTime? nullableDateTime = reader.Value as DateTime?;
|
||||
|
||||
|
||||
if (nullableDateTime != null && nullableDateTime.HasValue)
|
||||
{
|
||||
dateTime = nullableDateTime.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (DateTime.TryParse((string)reader.Value, out dateTime) == false)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 将客户端时间转换为服务器时区的时间
|
||||
var serverZoneTime = TimeZoneInfo.ConvertTime(dateTime, _clientTimeZone, TimeZoneInfo.Local);
|
||||
|
||||
return serverZoneTime;
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||
{
|
||||
DateTime? nullableDateTime = value as DateTime?;
|
||||
|
||||
if (nullableDateTime != null && nullableDateTime.HasValue)
|
||||
{
|
||||
//第一个参数默认使用系统本地时区 也就是应用服务器的时区
|
||||
DateTime clientZoneTime = TimeZoneInfo.ConvertTime(nullableDateTime.Value, _clientTimeZone);
|
||||
writer.WriteValue(clientZoneTime);
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.WriteNull();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
|
||||
using IRaCIS.Core.API._ServiceExtensions.NewtonsoftJson;
|
||||
using IRaCIS.Core.Application.Helper;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
@@ -8,8 +10,12 @@ namespace IRaCIS.Core.API
|
||||
{
|
||||
public static class NewtonsoftJsonSetup
|
||||
{
|
||||
public static void AddNewtonsoftJsonSetup(this IMvcBuilder builder)
|
||||
public static void AddNewtonsoftJsonSetup(this IMvcBuilder builder, IServiceCollection services)
|
||||
{
|
||||
services.AddHttpContextAccessor();
|
||||
services.AddScoped<JSONTimeZoneConverter>();
|
||||
services.AddScoped<CustomStringConverter>();
|
||||
services.AddScoped<IOSSService,OSSService>();
|
||||
|
||||
builder.AddNewtonsoftJson(options =>
|
||||
{
|
||||
@@ -23,8 +29,15 @@ namespace IRaCIS.Core.API
|
||||
// 设置时间格式
|
||||
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>());
|
||||
|
||||
//options.SerializerSettings.Converters.Add(services.BuildServiceProvider().GetService<CustomStringConverter>());
|
||||
|
||||
|
||||
//IsoDateTimeConverter
|
||||
//options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore;
|
||||
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
using IRaCIS.Core.Application.Helper;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace IRaCIS.Core.API._ServiceExtensions.NewtonsoftJson
|
||||
{
|
||||
public class CustomStringConverter : JsonConverter<string>
|
||||
{
|
||||
//private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
private readonly IOSSService _oSSService;
|
||||
|
||||
// 构造函数
|
||||
public CustomStringConverter(/*IHttpContextAccessor httpContextAccessor*/ IOSSService oSSService)
|
||||
{
|
||||
//_httpContextAccessor = httpContextAccessor;
|
||||
_oSSService = oSSService;
|
||||
}
|
||||
|
||||
public override string ReadJson(JsonReader reader, Type objectType, string existingValue, bool hasExistingValue, JsonSerializer serializer)
|
||||
{
|
||||
if (reader.TokenType == JsonToken.String)
|
||||
{
|
||||
return (string)reader.Value;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public override void WriteJson(JsonWriter writer, string value, JsonSerializer serializer)
|
||||
{
|
||||
if (value != null)
|
||||
{
|
||||
// 在这里对字符串进行处理,例如转换大小写、去除空格等
|
||||
// 这里只是一个示例,您可以根据实际需求进行更改
|
||||
|
||||
// 获取当前正在序列化的属性名
|
||||
string propertyName = writer.Path.Split('.').Last();
|
||||
|
||||
Regex guidRegex = new Regex(@"[0-9a-fA-F]{8}(-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}");
|
||||
|
||||
if (propertyName.IndexOf("Path") > -1 && value.IndexOf('.')>-1 && guidRegex.IsMatch(value))
|
||||
{
|
||||
var tt= _oSSService.GetSignedUrl(value);
|
||||
writer.WriteValue(tt);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 将处理后的字符串写入到 JsonWriter 中
|
||||
writer.WriteValue(value);
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.WriteNull();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,30 @@
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.ResponseCompression;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System.IO.Compression;
|
||||
|
||||
namespace IRaCIS.Core.API
|
||||
{
|
||||
public static class ResponseCompressionSetup
|
||||
public static class ResponseCompressionSetup
|
||||
{
|
||||
public static void AddResponseCompressionSetup(this IServiceCollection services)
|
||||
{
|
||||
services.AddResponseCompression(options =>
|
||||
{
|
||||
options.EnableForHttps = true;
|
||||
options.Providers.Add<BrotliCompressionProvider>();
|
||||
options.Providers.Add<GzipCompressionProvider>();
|
||||
});
|
||||
|
||||
services.Configure<BrotliCompressionProviderOptions>(options =>
|
||||
{
|
||||
options.Level = CompressionLevel.Optimal;
|
||||
});
|
||||
|
||||
services.Configure<GzipCompressionProviderOptions>(options =>
|
||||
{
|
||||
options.Level = CompressionLevel.Optimal;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Serilog;
|
||||
using Serilog.Events;
|
||||
using Serilog.Sinks.Email;
|
||||
//using Serilog.Sinks.Email;
|
||||
using System;
|
||||
using System.Net;
|
||||
|
||||
@@ -29,8 +29,7 @@ namespace IRaCIS.Core.API
|
||||
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}")
|
||||
.Filter.ByExcluding(logEvent => logEvent.Properties.ContainsKey("SourceContext") && logEvent.Properties["SourceContext"].ToString().Contains("health"));
|
||||
outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {SourceContext:l} || {Message} || {Exception} ||end {NewLine}");
|
||||
|
||||
//.WriteTo.MSSqlServer("Data Source=DESKTOP-4TU9A6M;Initial Catalog=CoreFrame;User ID=sa;Password=123456", "logs", autoCreateSqlTable: true, restrictedToMinimumLevel: LogEventLevel.Information)//从左至右四个参数分别是数据库连接字符串、表名、如果表不存在是否创建、最低等级。Serilog会默认创建一些列。
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ using Hangfire.SqlServer;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace IRaCIS.Core.API
|
||||
{
|
||||
@@ -14,19 +15,29 @@ namespace IRaCIS.Core.API
|
||||
|
||||
services.AddHangfire(hangFireConfig =>
|
||||
{
|
||||
|
||||
//hangFireConfig.UseInMemoryStorage();
|
||||
|
||||
//指定存储介质
|
||||
hangFireConfig.UseSqlServerStorage(hangFireConnStr, new SqlServerStorageOptions()
|
||||
//本地window 调试 使用内存,服务器部署使用数据库,防止服务器任务调度到本地
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
SchemaName = "dbo",
|
||||
CommandBatchMaxTimeout = TimeSpan.FromMinutes(5),
|
||||
SlidingInvisibilityTimeout = TimeSpan.FromMinutes(5),
|
||||
QueuePollInterval = TimeSpan.Zero,
|
||||
UseRecommendedIsolationLevel = true,
|
||||
DisableGlobalLocks = true
|
||||
});
|
||||
hangFireConfig.UseInMemoryStorage();
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
//指定存储介质
|
||||
hangFireConfig.UseSqlServerStorage(hangFireConnStr, new SqlServerStorageOptions()
|
||||
{
|
||||
SchemaName = "dbo",
|
||||
CommandBatchMaxTimeout = TimeSpan.FromMinutes(5),
|
||||
SlidingInvisibilityTimeout = TimeSpan.FromMinutes(5),
|
||||
QueuePollInterval = TimeSpan.Zero,
|
||||
UseRecommendedIsolationLevel = true,
|
||||
DisableGlobalLocks = true
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//hangFireConfig.UseTagsWithSql(); //nuget引入Hangfire.Tags.SqlServer
|
||||
//.UseHangfireHttpJob();
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"ObjectStoreUse": "AliyunOSS",
|
||||
"AliyunOSS": {
|
||||
"regionId": "cn-shanghai",
|
||||
"internalEndpoint": "https://oss-cn-shanghai-internal.aliyuncs.com",
|
||||
"endpoint": "https://oss-cn-shanghai.aliyuncs.com",
|
||||
"accessKeyId": "LTAI5tKvzs7ed3UfSpNk3xwQ",
|
||||
"accessKeySecret": "zTIceGEShlZDGnLrCFfIGFE7TXVRio",
|
||||
@@ -41,8 +42,16 @@
|
||||
"OpenLoginLimit": true,
|
||||
"LoginMaxFailCount": 5,
|
||||
|
||||
"LoginFailLockMinutes": 30
|
||||
"LoginFailLockMinutes": 30,
|
||||
"AutoLoginOutMinutes": 60,
|
||||
|
||||
"ContinuousReadingTimeMin": 120,
|
||||
|
||||
"ReadingRestTimeMin": 10,
|
||||
|
||||
"IsNeedChangePassWord": true,
|
||||
|
||||
"ChangePassWordDays": 90
|
||||
},
|
||||
"SystemEmailSendConfig": {
|
||||
"Port": 465,
|
||||
@@ -50,7 +59,11 @@
|
||||
"FromEmail": "uat@extimaging.com",
|
||||
"FromName": "UAT_IRC",
|
||||
"AuthorizationCode": "SHzyyl2021",
|
||||
"SiteUrl": "http://irc.event.extimaging.com/login"
|
||||
"SiteUrl": "http://irc.event.extimaging.com/login",
|
||||
"CompanyName": "Extensive Imaging",
|
||||
"CompanyNameCN": "上海展影医疗科技有限公司",
|
||||
"CompanyShortName": "Extensive Imaging",
|
||||
"CompanyShortNameCN": "展影医疗"
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -7,13 +7,16 @@
|
||||
}
|
||||
},
|
||||
"ConnectionStrings": {
|
||||
"RemoteNew": "Server=101.132.193.237,1434;Database=Prod_IRC;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true",
|
||||
"Hangfire": "Server=101.132.193.237,1434;Database=Prod_IRC_Hangfire;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true"
|
||||
//"RemoteNew": "Server=101.132.193.237,1434;Database=Prod_IRC;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true",
|
||||
//"Hangfire": "Server=101.132.193.237,1434;Database=Prod_IRC_Hangfire;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true"
|
||||
"RemoteNew": "Server=prod_mssql_standard,1433;Database=Prod_IRC;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true",
|
||||
"Hangfire": "Server=prod_mssql_standard,1433;Database=Prod_IRC_Hangfire;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true"
|
||||
},
|
||||
"ObjectStoreService": {
|
||||
"ObjectStoreUse": "AliyunOSS",
|
||||
"AliyunOSS": {
|
||||
"regionId": "cn-shanghai",
|
||||
"internalEndpoint": "https://oss-cn-shanghai-internal.aliyuncs.com",
|
||||
"endpoint": "https://oss-cn-shanghai.aliyuncs.com",
|
||||
"accessKeyId": "LTAI5tKvzs7ed3UfSpNk3xwQ",
|
||||
"accessKeySecret": "zTIceGEShlZDGnLrCFfIGFE7TXVRio",
|
||||
@@ -34,13 +37,22 @@
|
||||
"BasicSystemConfig": {
|
||||
|
||||
"OpenUserComplexPassword": true,
|
||||
|
||||
"OpenSignDocumentBeforeWork": true,
|
||||
|
||||
"OpenLoginLimit": true,
|
||||
"LoginMaxFailCount": 5,
|
||||
"LoginFailLockMinutes": 30,
|
||||
|
||||
"LoginFailLockMinutes": 30
|
||||
"AutoLoginOutMinutes": 360,
|
||||
|
||||
"OpenLoginMFA": false,
|
||||
|
||||
"ContinuousReadingTimeMin": 120,
|
||||
|
||||
"ReadingRestTimeMin": 10,
|
||||
"IsNeedChangePassWord": true,
|
||||
|
||||
"ChangePassWordDays": 90
|
||||
},
|
||||
|
||||
"SystemEmailSendConfig": {
|
||||
@@ -49,6 +61,12 @@
|
||||
"FromEmail": "IRC@extimaging.com",
|
||||
"FromName": "IRC",
|
||||
"AuthorizationCode": "ExtImg@2022",
|
||||
"SiteUrl": "http://irc.extimaging.com/login"
|
||||
"SiteUrl": "http://irc.extimaging.com/login",
|
||||
"OrganizationName": "Extlmaging",
|
||||
"OrganizationNameCN": "Extlmaging",
|
||||
"CompanyName": "Extensive Imaging",
|
||||
"CompanyNameCN": "上海展影医疗科技有限公司",
|
||||
"CompanyShortName": "Extensive Imaging",
|
||||
"CompanyShortNameCN": "展影医疗"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,9 +7,8 @@
|
||||
}
|
||||
},
|
||||
"ConnectionStrings": {
|
||||
"RemoteNew": "Server=123.56.94.154,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"
|
||||
|
||||
"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": {
|
||||
@@ -18,6 +17,7 @@
|
||||
|
||||
"AliyunOSS": {
|
||||
"regionId": "cn-shanghai",
|
||||
"internalEndpoint": "https://oss-cn-shanghai-internal.aliyuncs.com",
|
||||
"endPoint": "https://oss-cn-shanghai.aliyuncs.com",
|
||||
"accessKeyId": "LTAI5tKvzs7ed3UfSpNk3xwQ",
|
||||
"accessKeySecret": "zTIceGEShlZDGnLrCFfIGFE7TXVRio",
|
||||
@@ -26,17 +26,15 @@
|
||||
"viewEndpoint": "https://zy-irc-test-store.oss-cn-shanghai.aliyuncs.com",
|
||||
"region": "oss-cn-shanghai"
|
||||
},
|
||||
|
||||
"MinIO": {
|
||||
"endPoint": "192.168.3.68",
|
||||
"port": "8001",
|
||||
"useSSL": false,
|
||||
"accessKey": "IDFkwEpWej0b4DtiuThL",
|
||||
"secretKey": "Lhuu83yMhVwu7c1SnjvGY6lq74jzpYqifK6Qtj4h",
|
||||
"bucketName": "test",
|
||||
"viewEndpoint": "http://192.168.3.68:8001/test/"
|
||||
"endPoint": "hir-oss.test.extimaging.com",
|
||||
"port": "443",
|
||||
"useSSL": true,
|
||||
"accessKey": "fbStsVYCIPKHQneeqMwD",
|
||||
"secretKey": "TzgvyA3zGXMUnpilJNUlyMYHfosl1hBMl6lxPmjy",
|
||||
"bucketName": "irc-test",
|
||||
"viewEndpoint": "https://hir-oss.test.extimaging.com/irc-test"
|
||||
},
|
||||
|
||||
"AWS": {
|
||||
"endPoint": "s3.us-east-1.amazonaws.com",
|
||||
"useSSL": true,
|
||||
@@ -59,7 +57,18 @@
|
||||
|
||||
"LoginMaxFailCount": 5,
|
||||
|
||||
"LoginFailLockMinutes": 1
|
||||
"LoginFailLockMinutes": 1,
|
||||
|
||||
"AutoLoginOutMinutes": 1,
|
||||
|
||||
"OpenLoginMFA": false,
|
||||
|
||||
"ContinuousReadingTimeMin": 120,
|
||||
|
||||
"ReadingRestTimeMin": 10,
|
||||
"IsNeedChangePassWord": true,
|
||||
|
||||
"ChangePassWordDays": 90
|
||||
},
|
||||
|
||||
"SystemEmailSendConfig": {
|
||||
@@ -68,8 +77,18 @@
|
||||
"FromEmail": "test@extimaging.com",
|
||||
"FromName": "Test_IRC",
|
||||
"AuthorizationCode": "SHzyyl2021",
|
||||
"SiteUrl": "http://irc.test.extimaging.com/login",
|
||||
|
||||
"SiteUrl": "http://irc.test.extimaging.com/login"
|
||||
"OrganizationName": "Extlmaging",
|
||||
"OrganizationNameCN": "Extlmaging",
|
||||
"CompanyName": "Extensive Imaging",
|
||||
"CompanyNameCN": "上海展影医疗科技有限公司",
|
||||
"CompanyShortName": "Extensive Imaging",
|
||||
"CompanyShortNameCN": "展影医疗"
|
||||
},
|
||||
|
||||
"SystemPacsConfig": {
|
||||
"Port": "11113",
|
||||
"IP": "106.14.89.110"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
"ConnectionStrings": {
|
||||
"RemoteNew": "Server=us-prod-mssql-service,1433;Database=US_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
||||
"Hangfire": "Server=us-prod-mssql-service,1433;Database=US_IRC_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
|
||||
//"RemoteNew": "Server=44.210.231.169,1435;Database=US_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
||||
//"Hangfire": "Server=44.210.231.169,1435;Database=US_IRC_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
||||
},
|
||||
|
||||
"ObjectStoreService": {
|
||||
"ObjectStoreUse": "AWS",
|
||||
"AliyunOSS": {
|
||||
"regionId": "cn-shanghai",
|
||||
"internalEndpoint": "https://oss-cn-shanghai-internal.aliyuncs.com",
|
||||
"endPoint": "https://oss-cn-shanghai.aliyuncs.com",
|
||||
"accessKeyId": "",
|
||||
"accessKeySecret": "",
|
||||
"bucketName": "zy-irc-test-store",
|
||||
"roleArn": "acs:ram::1899121822495495:role/oss-upload",
|
||||
"viewEndpoint": "https://zy-irc-test-store.oss-cn-shanghai.aliyuncs.com",
|
||||
"region": "oss-cn-shanghai"
|
||||
},
|
||||
|
||||
"MinIO": {
|
||||
"endPoint": "44.210.231.169",
|
||||
"port": "9001",
|
||||
"useSSL": false,
|
||||
"accessKey": "e9bT1isTOqSAUxb6wd4n",
|
||||
"secretKey": "b5TaDzNdQCBtCvfm8eZ3dR6yY7tfZu2JYze2Po1i",
|
||||
"bucketName": "prod-irc-us",
|
||||
"viewEndpoint": "http://44.210.231.169:9001/prod-irc-us/"
|
||||
},
|
||||
|
||||
"AWS": {
|
||||
"endPoint": "s3.us-east-1.amazonaws.com",
|
||||
"useSSL": true,
|
||||
"accessKey": "AKIAW3MEAFJX5P32P6NA",
|
||||
"secretKey": "soKfYlzZE11Zi4RyTjXp0myXN0U3U+ka8rT49+B/",
|
||||
"bucketName": "ei-med-s3-lili-store",
|
||||
"viewEndpoint": "https://ei-med-s3-lili-store.s3.amazonaws.com/"
|
||||
}
|
||||
},
|
||||
"BasicSystemConfig": {
|
||||
|
||||
"OpenUserComplexPassword": false,
|
||||
|
||||
"OpenSignDocumentBeforeWork": false,
|
||||
|
||||
"OpenTrialRelationDelete": true,
|
||||
|
||||
"OpenLoginLimit": false,
|
||||
|
||||
"LoginMaxFailCount": 5,
|
||||
|
||||
"LoginFailLockMinutes": 30,
|
||||
"AutoLoginOutMinutes": 60,
|
||||
|
||||
"ContinuousReadingTimeMin": 120,
|
||||
|
||||
"ReadingRestTimeMin": 10,
|
||||
"IsNeedChangePassWord": true,
|
||||
|
||||
"ChangePassWordDays": 90
|
||||
},
|
||||
|
||||
"SystemEmailSendConfig": {
|
||||
"Port": 587,
|
||||
"Host": "smtp-mail.outlook.com",
|
||||
"FromEmail": "donotreply@elevateimaging.ai",
|
||||
"FromName": "LiLi",
|
||||
"AuthorizationCode": "Q#669869497420ul",
|
||||
|
||||
"OrganizationName": "Elevate Imaging",
|
||||
"OrganizationNameCN": "Elevate Imaging",
|
||||
"CompanyName": "Elevate Imaging Inc.",
|
||||
"CompanyNameCN": "上海展影医疗科技有限公司",
|
||||
"CompanyShortName": "Elevate Imaging",
|
||||
"CompanyShortNameCN": "展影医疗",
|
||||
"SiteUrl": "https://lili.elevateimaging.ai/login"
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
"ConnectionStrings": {
|
||||
"RemoteNew": "Server=106.14.89.110,1435;Database=Test_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
||||
"Hangfire": "Server=106.14.89.110,1435;Database=Test_IRC_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
|
||||
},
|
||||
|
||||
"ObjectStoreService": {
|
||||
|
||||
"ObjectStoreUse": "MinIO",
|
||||
"AliyunOSS": {
|
||||
"regionId": "cn-shanghai",
|
||||
"internalEndpoint": "https://oss-cn-shanghai-internal.aliyuncs.com",
|
||||
"endPoint": "https://oss-cn-shanghai.aliyuncs.com",
|
||||
"accessKeyId": "LTAI5tKvzs7ed3UfSpNk3xwQ",
|
||||
"accessKeySecret": "zTIceGEShlZDGnLrCFfIGFE7TXVRio",
|
||||
"bucketName": "zy-irc-test-store",
|
||||
"roleArn": "acs:ram::1899121822495495:role/oss-upload",
|
||||
"viewEndpoint": "https://zy-irc-test-store.oss-cn-shanghai.aliyuncs.com",
|
||||
"region": "oss-cn-shanghai"
|
||||
},
|
||||
|
||||
"MinIO": {
|
||||
//"endPoint": "hir-oss.uat.extimaging.com",
|
||||
//"port": "443",
|
||||
//"useSSL": true,
|
||||
//"viewEndpoint": "https://hir-oss.uat.extimaging.com/hir-uat",
|
||||
|
||||
"endPoint": "47.117.164.182",
|
||||
"port": "9001",
|
||||
"useSSL": false,
|
||||
"viewEndpoint": "http://47.117.164.182:9001/test-irc-us",
|
||||
|
||||
"accessKey": "b9Ul0e98xPzt6PwRXA1Q",
|
||||
"secretKey": "DzMaU2L4OXl90uytwOmDXF2encN0Jf4Nxu2XkYqQ",
|
||||
"bucketName": "test-irc-us"
|
||||
|
||||
},
|
||||
|
||||
"AWS": {
|
||||
"endPoint": "s3.us-east-1.amazonaws.com",
|
||||
"useSSL": true,
|
||||
"accessKey": "AKIAZQ3DRSOHFPJJ6FEU",
|
||||
"secretKey": "l+yjtvV7Z4jiwm/7xCYv30UeUj/SvuqqYzAwjJHf",
|
||||
"bucketName": "ei-irc-test-store",
|
||||
"viewEndpoint": "https://ei-irc-test-store.s3.amazonaws.com/"
|
||||
}
|
||||
},
|
||||
"BasicSystemConfig": {
|
||||
|
||||
"OpenUserComplexPassword": false,
|
||||
|
||||
"OpenSignDocumentBeforeWork": false,
|
||||
|
||||
"OpenTrialRelationDelete": true,
|
||||
|
||||
"OpenLoginLimit": false,
|
||||
|
||||
"LoginMaxFailCount": 5,
|
||||
|
||||
"LoginFailLockMinutes": 30,
|
||||
"AutoLoginOutMinutes": 60,
|
||||
|
||||
"ContinuousReadingTimeMin": 120,
|
||||
|
||||
"ReadingRestTimeMin": 10,
|
||||
"IsNeedChangePassWord": true,
|
||||
|
||||
"ChangePassWordDays": 90
|
||||
},
|
||||
|
||||
"SystemEmailSendConfig": {
|
||||
"Port": 587,
|
||||
"Host": "smtp-mail.outlook.com",
|
||||
"FromEmail": "donotreply@elevateimaging.ai",
|
||||
"FromName": "LiLi",
|
||||
"AuthorizationCode": "Q#669869497420ul",
|
||||
|
||||
"OrganizationName": "Elevate Imaging",
|
||||
"OrganizationNameCN": "Elevate Imaging",
|
||||
"CompanyName": "Elevate Imaging Inc.",
|
||||
"CompanyNameCN": "上海展影医疗科技有限公司",
|
||||
"CompanyShortName": "Elevate Imaging",
|
||||
"CompanyShortNameCN": "展影医疗",
|
||||
"SiteUrl": "https://lili.test.elevateimaging.ai/login"
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
"ConnectionStrings": {
|
||||
"RemoteNew": "Server=us-mssql-service,1433;Database=US_Uat_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
||||
"Hangfire": "Server=us-mssql-service,1433;Database=US_Uat_IRC_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
|
||||
|
||||
//"RemoteNew": "Server=3.226.182.187,1435;Database=US_Uat_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
||||
//"Hangfire": "Server=3.226.182.187,1435;Database=US_Uat_IRC_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
|
||||
},
|
||||
|
||||
"ObjectStoreService": {
|
||||
|
||||
"ObjectStoreUse": "AWS",
|
||||
|
||||
"MinIO": {
|
||||
//"endPoint": "hir-minio.uat.elevateimaging.ai",
|
||||
//"port": "443",
|
||||
//"useSSL": true,
|
||||
//"viewEndpoint": "https://hir-minio.uat.elevateimaging.ai/uat-irc-us",
|
||||
|
||||
"endPoint": "3.226.182.187",
|
||||
"port": "9001",
|
||||
"useSSL": false,
|
||||
"viewEndpoint": "http://44.218.11.19:9001/uat-irc-us",
|
||||
|
||||
"accessKey": "lH8DkKskLuDqPaiubuSQ",
|
||||
"secretKey": "pdPdicvvLeH7xAC5yFUrI7odMyBfOXxvVWMvKYV4",
|
||||
"bucketName": "uat-irc-us"
|
||||
|
||||
},
|
||||
|
||||
"AWS": {
|
||||
"endPoint": "s3.us-east-1.amazonaws.com",
|
||||
"useSSL": true,
|
||||
"accessKey": "AKIAW3MEAFJXUO6XYFYN",
|
||||
"secretKey": "AeX5r4xHQH7tNJlTTFVv5/zBXie1Kj+mAayKrukp",
|
||||
"bucketName": "ei-med-s3-lili-uat-store",
|
||||
"viewEndpoint": "https://ei-med-s3-lili-uat-store.s3.amazonaws.com/"
|
||||
}
|
||||
},
|
||||
"BasicSystemConfig": {
|
||||
|
||||
"OpenUserComplexPassword": false,
|
||||
|
||||
"OpenSignDocumentBeforeWork": false,
|
||||
|
||||
"OpenTrialRelationDelete": true,
|
||||
|
||||
"OpenLoginLimit": false,
|
||||
|
||||
"LoginMaxFailCount": 5,
|
||||
|
||||
"LoginFailLockMinutes": 30,
|
||||
"AutoLoginOutMinutes": 60,
|
||||
|
||||
"ContinuousReadingTimeMin": 120,
|
||||
|
||||
"ReadingRestTimeMin": 10,
|
||||
"IsNeedChangePassWord": true,
|
||||
|
||||
"ChangePassWordDays": 90,
|
||||
|
||||
"OpenLoginMFA": true
|
||||
},
|
||||
|
||||
"SystemEmailSendConfig": {
|
||||
"Port": 587,
|
||||
"Host": "smtp-mail.outlook.com",
|
||||
"FromEmail": "donotreply@elevateimaging.ai",
|
||||
"FromName": "LiLi",
|
||||
"AuthorizationCode": "Q#669869497420ul",
|
||||
|
||||
"OrganizationName": "Elevate Imaging",
|
||||
"OrganizationNameCN": "Elevate Imaging",
|
||||
"CompanyName": "Elevate Imaging Inc.",
|
||||
"CompanyNameCN": "上海展影医疗科技有限公司",
|
||||
"CompanyShortName": "Elevate Imaging",
|
||||
"CompanyShortNameCN": "展影医疗",
|
||||
"SiteUrl": "https://lili.test.elevateimaging.ai/login"
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
"AliyunOSS": {
|
||||
"regionId": "cn-shanghai",
|
||||
"internalEndpoint": "https://oss-cn-shanghai-internal.aliyuncs.com",
|
||||
"endpoint": "https://oss-cn-shanghai.aliyuncs.com",
|
||||
"accessKeyId": "LTAI5tKvzs7ed3UfSpNk3xwQ",
|
||||
"accessKeySecret": "zTIceGEShlZDGnLrCFfIGFE7TXVRio",
|
||||
@@ -26,12 +27,17 @@
|
||||
},
|
||||
|
||||
"MinIO": {
|
||||
"endpoint": "http://192.168.3.68",
|
||||
"port": "8001",
|
||||
"endPoint": "hir-oss.uat.extimaging.com",
|
||||
"port": "80",
|
||||
"useSSL": false,
|
||||
"accessKey": "IDFkwEpWej0b4DtiuThL",
|
||||
"secretKey": "Lhuu83yMhVwu7c1SnjvGY6lq74jzpYqifK6Qtj4h",
|
||||
"bucketName": "test"
|
||||
"viewEndpoint": "http://hir-oss.uat.extimaging.com/irc-uat",
|
||||
//"port": "443",
|
||||
//"useSSL": true,
|
||||
//"viewEndpoint": "https://hir-oss.uat.extimaging.com/irc-uat",
|
||||
"accessKey": "b9Ul0e98xPzt6PwRXA1Q",
|
||||
"secretKey": "DzMaU2L4OXl90uytwOmDXF2encN0Jf4Nxu2XkYqQ",
|
||||
"bucketName": "irc-uat"
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
@@ -44,7 +50,16 @@
|
||||
"OpenLoginLimit": true,
|
||||
"LoginMaxFailCount": 5,
|
||||
|
||||
"LoginFailLockMinutes": 30
|
||||
"LoginFailLockMinutes": 30,
|
||||
"AutoLoginOutMinutes": 60,
|
||||
"OpenLoginMFA": false,
|
||||
|
||||
"ContinuousReadingTimeMin": 120,
|
||||
|
||||
"ReadingRestTimeMin": 10,
|
||||
"IsNeedChangePassWord": true,
|
||||
|
||||
"ChangePassWordDays": 90
|
||||
|
||||
},
|
||||
"SystemEmailSendConfig": {
|
||||
@@ -53,8 +68,18 @@
|
||||
"FromEmail": "uat@extimaging.com",
|
||||
"FromName": "UAT_IRC",
|
||||
"AuthorizationCode": "SHzyyl2021",
|
||||
"SiteUrl": "http://irc.uat.extimaging.com/login"
|
||||
"SiteUrl": "http://irc.uat.extimaging.com/login",
|
||||
"OrganizationName": "Extlmaging",
|
||||
"OrganizationNameCN": "Extlmaging",
|
||||
"CompanyName": "Extensive Imaging",
|
||||
"CompanyNameCN": "上海展影医疗科技有限公司",
|
||||
"CompanyShortName": "Extensive Imaging",
|
||||
"CompanyShortNameCN": "展影医疗"
|
||||
},
|
||||
|
||||
"SystemPacsConfig": {
|
||||
"Port": "11113",
|
||||
"IP": "47.117.164.182"
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"JwtSetting": {
|
||||
"SecurityKey": "SecurityKey_SHzyyl@2021",
|
||||
"SecurityKey": "ShangHaiZhanYing_SecurityKey_SHzyyl@2021",
|
||||
"Issuer": "Extimaging",
|
||||
"Audience": "EICS",
|
||||
"TokenExpireDays": "7"
|
||||
@@ -90,7 +90,13 @@
|
||||
"DicomStudyCodePrefix": "ST",
|
||||
"SystemSiteCodePrefix": "S",
|
||||
"DefaultPassword": "123456",
|
||||
"DefaultInternalOrganizationName": "ExtImaging",
|
||||
"ImageShareExpireDays": 10
|
||||
},
|
||||
|
||||
"EncrypteResponseConfig": {
|
||||
"IsEnable": true,
|
||||
"ApiPathList": [
|
||||
"/test/get"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@
|
||||
尊敬的 {0} ,您好:
|
||||
</div>
|
||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
|
||||
展影医疗为您添加了账户,账户信息如下:
|
||||
{company abbreviation}为您添加了账户,账户信息如下:
|
||||
</div>
|
||||
<span style="color: #00D1B2"></span>
|
||||
<div style='border: 1px solid #eee;box-sizing:border-box;width: 50%;background: #fff;padding: 20px;line-height: 40px;font-size: 14px;border-radius: 5px;margin-left: 60px;margin-bottom: 30px;'>
|
||||
@@ -23,7 +23,7 @@
|
||||
</div>
|
||||
<div>
|
||||
首次登陆前,请通过该链接修改您的账户信息:
|
||||
<a href='{3}' style='margin-left:30px;font-size:14px;text-decoration: none;display: inline-block;color:#00D1B2;border-radius: 5px;line-height: 40px;text-align: center;'>
|
||||
<a href='{3}' style='margin-left:1px;font-size:14px;text-decoration: none;display: inline-block;color:#00D1B2;border-radius: 5px;line-height: 40px;text-align: center;'>
|
||||
初始化账号信息
|
||||
</a>
|
||||
</div>
|
||||
@@ -32,7 +32,7 @@
|
||||
|
||||
<div style='line-height: 24px;font-size: 14px;color:#333;margin-top: 20px;padding-bottom: 40px;'>
|
||||
<div>祝您顺利!</div>
|
||||
<div style="font-size: 14px;">上海展影医疗科技有限公司</div>
|
||||
<div style="font-size: 14px;">{company}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -11,20 +11,20 @@
|
||||
Dear {0} ,
|
||||
</div>
|
||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
|
||||
Extimaging has added an account for you. The account information is as follows:
|
||||
{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 name: {1}
|
||||
User ID: {1}
|
||||
</div>
|
||||
<div>
|
||||
Role: {2}
|
||||
</div>
|
||||
<div>
|
||||
Before logging in for the first time, please modify your account information by this link:
|
||||
<a href='{3}' style='margin-left:30px;font-size:14px;text-decoration: none;display: inline-block;color:#00D1B2;border-radius: 5px;line-height: 40px;text-align: center;'>
|
||||
Initialize the account information
|
||||
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>
|
||||
@@ -32,7 +32,7 @@
|
||||
|
||||
<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;">Extensive Imaging</div>
|
||||
<div style="font-size: 14px;">{company}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
尊敬的 {0} ,您好:
|
||||
</div>
|
||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
|
||||
展影医疗将您的账户密码已重置,账户信息如下:
|
||||
{company abbreviation}将您的账户密码已重置,账户信息如下:
|
||||
</div>
|
||||
<span style="color: #00D1B2"></span>
|
||||
<div style='border: 1px solid #eee;box-sizing:border-box;width: 50%;background: #fff;padding: 20px;line-height: 40px;font-size: 14px;border-radius: 5px;margin-left: 60px;margin-bottom: 30px;'>
|
||||
@@ -29,7 +29,7 @@
|
||||
|
||||
<div style='line-height: 24px;font-size: 14px;color:#333;margin-top: 20px;padding-bottom: 40px;'>
|
||||
<div>祝您顺利!</div>
|
||||
<div style="font-size: 14px;">上海展影医疗科技有限公司</div>
|
||||
<div style="font-size: 14px;">{company}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -11,12 +11,12 @@
|
||||
Dear {0} ,
|
||||
</div>
|
||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
|
||||
Extimaging has reset your account password, and the account information is as follows:
|
||||
{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 name: {1}
|
||||
User ID: {1}
|
||||
</div>
|
||||
<div>
|
||||
Role: {2}
|
||||
@@ -29,7 +29,7 @@
|
||||
|
||||
<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;">Extensive Imaging</div>
|
||||
<div style="font-size: 14px;">{company}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
<div style='line-height: 24px;font-size: 14px;color:#333;margin-top: 20px;padding-bottom: 40px;'>
|
||||
<div>祝您顺利!</div>
|
||||
<div style="font-size: 14px;">上海展影医疗科技有限公司</div>
|
||||
<div style="font-size: 14px;">{company}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
Dear:
|
||||
</div>
|
||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
|
||||
Thank you for using Extensive Imaging Cloud System.
|
||||
Thank you for using {company abbreviation} Cloud System.
|
||||
</div>
|
||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
|
||||
{0}。
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
<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;">Extensive Imaging</div>
|
||||
<div style="font-size: 14px;">{company}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
尊敬的 {0} ,您好:
|
||||
</div>
|
||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
|
||||
展影医疗作为 [{1} (试验方案号:{2 })] 项目的供应商,诚邀您参加该项目阅片相关工作。
|
||||
{company abbreviation}作为 [{1} (试验方案号:{2 })] 项目的供应商,诚邀您参加该项目阅片相关工作。
|
||||
</div>
|
||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
|
||||
该项目采用电子化工作流,系统及您的账号信息如下:
|
||||
@@ -35,7 +35,7 @@
|
||||
</div>
|
||||
<div>
|
||||
系统登录地址:
|
||||
<a href='{6}' style='margin-left:30px;font-size:14px;text-decoration: none;display: inline-block;color:#00D1B2;border-radius: 5px;line-height: 40px;text-align: center;'>
|
||||
<a href='{6}' style='margin-left:1px;font-size:14px;text-decoration: none;display: inline-block;color:#00D1B2;border-radius: 5px;line-height: 40px;text-align: center;'>
|
||||
点击跳转
|
||||
</a>
|
||||
</div>
|
||||
@@ -43,7 +43,7 @@
|
||||
|
||||
<div style='line-height: 24px;font-size: 14px;color:#333;margin-top: 20px;padding-bottom: 40px;'>
|
||||
<div>祝您顺利!</div>
|
||||
<div style="font-size: 14px;">上海展影医疗科技有限公司</div>
|
||||
<div style="font-size: 14px;">{company}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -11,10 +11,10 @@
|
||||
Dear {0},
|
||||
</div>
|
||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;font-size: 16px'>
|
||||
As the service provider of {1} trial(Protocol ID is {2}), Extensive Imaging invites you to participate in the independent assessment work of this trial.
|
||||
{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;'>
|
||||
This project adopts electronic workflow. The system and your account information are as follows:
|
||||
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;'>
|
||||
@@ -25,23 +25,26 @@
|
||||
Protocol ID: {2}
|
||||
</div>
|
||||
<div>
|
||||
Study name: {1}
|
||||
Study Name: {1}
|
||||
</div>
|
||||
<div>
|
||||
User name: {4}
|
||||
User ID: {4}
|
||||
</div>
|
||||
<div>
|
||||
Role: {5}
|
||||
</div>
|
||||
<div>
|
||||
Login URL: {6}
|
||||
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;">Extensive Imaging</div>
|
||||
<div style="font-size: 14px;">{company}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
尊敬的 {0} ,您好:
|
||||
</div>
|
||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
|
||||
展影医疗作为 [{1} (试验方案号:{2 })] 项目的IRC供应商,诚邀您参加该项目IRC阅片相关工作,欢迎您提供指导和建议,非常感谢!
|
||||
{company abbreviation}作为 [{1} (试验方案号:{2 })] 项目的IRC供应商,诚邀您参加该项目IRC阅片相关工作,欢迎您提供指导和建议,非常感谢!
|
||||
</div>
|
||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
|
||||
该项目采用电子化工作流,系统及您的账号信息如下:
|
||||
@@ -35,7 +35,7 @@
|
||||
</div>
|
||||
<div>
|
||||
首次登陆前,请通过该链接修改您的账户信息:
|
||||
<a href='{6}' style='margin-left:30px;font-size:14px;text-decoration: none;display: inline-block;color:#00D1B2;border-radius: 5px;line-height: 40px;text-align: center;'>
|
||||
<a 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>
|
||||
@@ -44,7 +44,7 @@
|
||||
|
||||
<div style='line-height: 24px;font-size: 14px;color:#333;margin-top: 20px;padding-bottom: 40px;'>
|
||||
<div>祝您顺利!</div>
|
||||
<div style="font-size: 14px;">上海展影医疗科技有限公司</div>
|
||||
<div style="font-size: 14px;">{company}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -11,10 +11,10 @@
|
||||
Dear {0},
|
||||
</div>
|
||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
|
||||
Hello, as the IRC supplier of [ {1} (Protocol ID is {2}) ] project,Extiming sincerely invites you to participate in the IRC related work of this project. Your guidance and suggestions are welcome.
|
||||
{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;'>
|
||||
This project adopts electronic workflow. The system and your account information are as follows:
|
||||
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;'>
|
||||
@@ -25,18 +25,18 @@
|
||||
Protocol ID: {2}
|
||||
</div>
|
||||
<div>
|
||||
Study name: {1}
|
||||
Study Name: {1}
|
||||
</div>
|
||||
<div>
|
||||
User name: {4}
|
||||
User ID: {4}
|
||||
</div>
|
||||
<div>
|
||||
Role: {5}
|
||||
</div>
|
||||
<div>
|
||||
Before logging in for the first time, please modify your account information by this link:
|
||||
<a href='{6}' style='margin-left:30px;font-size:14px;text-decoration: none;display: inline-block;color:#00D1B2;border-radius: 5px;line-height: 40px;text-align: center;'>
|
||||
Initialize the account information
|
||||
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>
|
||||
@@ -44,7 +44,7 @@
|
||||
|
||||
<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;">Extensive Imaging</div>
|
||||
<div style="font-size: 14px;">{company}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
尊敬的 {0} ,您好:
|
||||
</div>
|
||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
|
||||
您好, 您填写的中心调研表被驳回,详细信息如下:
|
||||
您填写的中心调研表被驳回,详细信息如下:
|
||||
</div>
|
||||
<span style="color: #00D1B2"></span>
|
||||
<div style='border: 1px solid #eee;box-sizing:border-box;width: 50%;background: #fff;padding: 20px;line-height: 40px;font-size: 14px;border-radius: 5px;margin-left: 60px;margin-bottom: 30px;'>
|
||||
@@ -41,7 +41,7 @@
|
||||
|
||||
<div style='line-height: 24px;font-size: 14px;color:#333;margin-top: 20px;padding-bottom: 40px;'>
|
||||
<div>祝您顺利!</div>
|
||||
<div style="font-size: 14px;">上海展影医疗科技有限公司</div>
|
||||
<div style="font-size: 14px;">{company}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
Dear {0} ,
|
||||
</div>
|
||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
|
||||
Hello, the center survey form you filled in has been rejected. The details are as follows:
|
||||
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;'>
|
||||
@@ -22,7 +22,7 @@
|
||||
Protocol ID: {2}
|
||||
</div>
|
||||
<div>
|
||||
Study name: {3}
|
||||
Study Name: {3}
|
||||
</div>
|
||||
<div>
|
||||
Site ID: {4}
|
||||
@@ -41,7 +41,7 @@
|
||||
|
||||
<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;">Extensive Imaging</div>
|
||||
<div style="font-size: 14px;">{company}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
尊敬的 {0} ,您好:
|
||||
</div>
|
||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
|
||||
展影医疗作为 [{1} (试验方案号:{2 })] 项目的供应商,诚邀您参加该项目相关工作,欢迎您提供指导和建议,非常感谢!
|
||||
{company abbreviation}作为 [{1} (试验方案号:{2 })] 项目的供应商,诚邀您参加该项目相关工作,欢迎您提供指导和建议,非常感谢!
|
||||
</div>
|
||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
|
||||
该项目采用电子化工作流,系统及您的账号信息如下:
|
||||
@@ -35,7 +35,7 @@
|
||||
</div>
|
||||
<div>
|
||||
系统登录地址:
|
||||
<a href='{6}' style='margin-left:30px;font-size:14px;text-decoration: none;display: inline-block;color:#00D1B2;border-radius: 5px;line-height: 40px;text-align: center;'>
|
||||
<a href='{6}' style='margin-left:5px;font-size:14px;text-decoration: none;display: inline-block;color:#00D1B2;border-radius: 5px;line-height: 40px;text-align: center;'>
|
||||
点击跳转
|
||||
</a>
|
||||
</div>
|
||||
@@ -44,7 +44,7 @@
|
||||
|
||||
<div style='line-height: 24px;font-size: 14px;color:#333;margin-top: 20px;padding-bottom: 40px;'>
|
||||
<div>祝您顺利!</div>
|
||||
<div style="font-size: 14px;">上海展影医疗科技有限公司</div>
|
||||
<div style="font-size: 14px;">{company}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -11,10 +11,10 @@
|
||||
Dear {0},
|
||||
</div>
|
||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;font-size: 16px'>
|
||||
As the service provider of {1} trial (Protocol ID is {2}), Extensive Imaging invites you to participate in related work of this trial.
|
||||
{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;'>
|
||||
This project adopts electronic workflow. The system and your account information are as follows:
|
||||
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;'>
|
||||
@@ -25,23 +25,26 @@
|
||||
Protocol ID: {2}
|
||||
</div>
|
||||
<div>
|
||||
Study name: {1}
|
||||
Study Name: {1}
|
||||
</div>
|
||||
<div>
|
||||
User name: {4}
|
||||
User ID: {4}
|
||||
</div>
|
||||
<div>
|
||||
Role: {5}
|
||||
</div>
|
||||
<div>
|
||||
Login URL: {6}
|
||||
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;">Extensive Imaging</div>
|
||||
<div style="font-size: 14px;">{company}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
尊敬的 {0} ,您好:
|
||||
</div>
|
||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
|
||||
展影医疗作为 [{1} (试验方案号:{2 })] 项目的IRC供应商,诚邀您参加该项目IRC相关工作,欢迎您提供指导和建议,非常感谢!
|
||||
{company abbreviation}作为 [{1} (试验方案号:{2 })] 项目的IRC供应商,诚邀您参加该项目IRC相关工作,欢迎您提供指导和建议,非常感谢!
|
||||
</div>
|
||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
|
||||
该项目采用电子化工作流,系统及您的账号信息如下:
|
||||
@@ -35,7 +35,7 @@
|
||||
</div>
|
||||
<div>
|
||||
首次登陆前,请通过该链接修改您的账户信息:
|
||||
<a href='{6}' style='margin-left:30px;font-size:14px;text-decoration: none;display: inline-block;color:#00D1B2;border-radius: 5px;line-height: 40px;text-align: center;'>
|
||||
<a 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>
|
||||
@@ -44,7 +44,7 @@
|
||||
|
||||
<div style='line-height: 24px;font-size: 14px;color:#333;margin-top: 20px;padding-bottom: 40px;'>
|
||||
<div>祝您顺利!</div>
|
||||
<div style="font-size: 14px;">上海展影医疗科技有限公司</div>
|
||||
<div style="font-size: 14px;">{company}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -11,10 +11,10 @@
|
||||
Dear {0},
|
||||
</div>
|
||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
|
||||
Hello, as the IRC supplier of [ {1} (Protocol ID is {2}) ] project,Extiming sincerely invites you to participate in the IRC related work of this project. Your guidance and suggestions are welcome.
|
||||
{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;'>
|
||||
This project adopts electronic workflow. The system and your account information are as follows:
|
||||
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;'>
|
||||
@@ -25,18 +25,18 @@
|
||||
Protocol ID: {2}
|
||||
</div>
|
||||
<div>
|
||||
Study name: {1}
|
||||
Study Name: {1}
|
||||
</div>
|
||||
<div>
|
||||
User name: {4}
|
||||
User ID: {4}
|
||||
</div>
|
||||
<div>
|
||||
Role: {5}
|
||||
</div>
|
||||
<div>
|
||||
Before logging in for the first time, please modify your account information by this link:
|
||||
<a href='{6}' style='margin-left:30px;font-size:14px;text-decoration: none;display: inline-block;color:#00D1B2;border-radius: 5px;line-height: 40px;text-align: center;'>
|
||||
Initialize the account information
|
||||
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>
|
||||
@@ -44,7 +44,7 @@
|
||||
|
||||
<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;">Extensive Imaging</div>
|
||||
<div style="font-size: 14px;">{company}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<div style='padding-left: 40px;background: #f6f6f6'>
|
||||
<div style='padding-top: 20px;'>
|
||||
<div style='line-height: 40px;font-size: 18px'>
|
||||
Hello {0},
|
||||
{0}您好,
|
||||
</div>
|
||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
|
||||
感谢您使用展影云平台。
|
||||
@@ -21,7 +21,7 @@
|
||||
</div>
|
||||
<div style='line-height: 24px;font-size: 14px;color:#333;margin-top: 20px;padding-bottom: 40px;'>
|
||||
<div>祝您顺利!</div>
|
||||
<div style="font-size: 14px;">上海展影医疗科技有限公司</div>
|
||||
<div style="font-size: 14px;">{company}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -8,21 +8,21 @@
|
||||
<div style='padding-left: 40px;background: #f6f6f6'>
|
||||
<div style='padding-top: 20px;'>
|
||||
<div style='line-height: 40px;font-size: 18px'>
|
||||
Hello {0},
|
||||
Dear {0},
|
||||
</div>
|
||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
|
||||
Thank you for using Extensive Imaging’s cloud system!
|
||||
{1}
|
||||
</div>
|
||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
|
||||
{1}, and the verification code is <span style="font-weight:bold">{2}</span>.
|
||||
The verification code is <span style="font-weight:bold">{2}</span>.
|
||||
</div>
|
||||
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;'>
|
||||
Please enter this verification code within 3 minutes for subsequent operations.
|
||||
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;">Extensive Imaging</div>
|
||||
<div style="font-size: 14px;">{company}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Binary file not shown.
@@ -8,6 +8,8 @@ using IRaCIS.Core.Infrastructure;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json;
|
||||
using SharpCompress.Common;
|
||||
using ZiggyCreatures.Caching.Fusion;
|
||||
|
||||
namespace IRaCIS.Application.Services.BackGroundJob
|
||||
{
|
||||
@@ -20,151 +22,48 @@ namespace IRaCIS.Application.Services.BackGroundJob
|
||||
Task InitHangfireJobTaskAsync();
|
||||
|
||||
}
|
||||
public class IRaCISCHangfireJob : IIRaCISHangfireJob
|
||||
public class IRaCISCHangfireJob(IRepository<Trial> _trialRepository,
|
||||
IEasyCachingProvider _provider,
|
||||
ILogger<IRaCISCHangfireJob> _logger,
|
||||
IRepository<SystemAnonymization> _systemAnonymizationRepository,
|
||||
IRepository<Internationalization> _internationalizationRepository,
|
||||
IRepository<TrialEmailNoticeConfig> _trialEmailNoticeConfigRepository
|
||||
) : IIRaCISHangfireJob
|
||||
{
|
||||
public static string JsonFileFolder = Path.Combine(AppContext.BaseDirectory, StaticData.Folder.Resources);
|
||||
|
||||
private readonly IRepository<Trial> _trialRepository;
|
||||
private readonly IEasyCachingProvider _provider;
|
||||
private readonly ILogger<IRaCISCHangfireJob> _logger;
|
||||
private readonly IRepository<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()
|
||||
{
|
||||
_logger.LogInformation("项目启动 hangfire 任务初始化 执行开始~");
|
||||
|
||||
//初始化国际化
|
||||
|
||||
//项目状态 立即加载到缓存中
|
||||
await MemoryCacheTrialStatusAsync();
|
||||
|
||||
await MemoryCacheAnonymizeData();
|
||||
|
||||
|
||||
//创建项目缓存 定时任务
|
||||
HangfireJobHelper.AddOrUpdateInitCronJob<IIRaCISHangfireJob>("RecurringJob_Cache_TrialState", t => t.MemoryCacheTrialStatusAsync(), Cron.Daily());
|
||||
|
||||
//初始化
|
||||
|
||||
await InitInternationlizationDataAndWatchJsonFileAsync();
|
||||
await InternationalizationHelper.InitInternationlizationDataAndWatchJsonFileAsync(_internationalizationRepository);
|
||||
|
||||
//创建邮件定时任务
|
||||
await InitSysAndTrialCronJobAsync();
|
||||
|
||||
#region 废弃
|
||||
////项目状态 立即加载到缓存中
|
||||
//await MemoryCacheTrialStatusAsync();
|
||||
|
||||
////await MemoryCacheAnonymizeData();
|
||||
|
||||
|
||||
////创建项目缓存 定时任务
|
||||
//HangfireJobHelper.AddOrUpdateInitCronJob<IIRaCISHangfireJob>("RecurringJob_Cache_TrialState", t => t.MemoryCacheTrialStatusAsync(), Cron.Daily());
|
||||
#endregion
|
||||
|
||||
|
||||
_logger.LogInformation("项目启动 hangfire 任务初始化 执行结束");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 缓存项目状态
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task MemoryCacheTrialStatusAsync()
|
||||
{
|
||||
|
||||
var list = await _trialRepository.Select(t => new { TrialId = t.Id, TrialStatusStr = t.TrialStatusStr })
|
||||
.ToListAsync();
|
||||
|
||||
list.ForEach(t => _provider.Set(t.TrialId.ToString(), t.TrialStatusStr, TimeSpan.FromDays(7)));
|
||||
|
||||
}
|
||||
|
||||
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文件变更 实时刷新数据
|
||||
WatchJsonFile(usJsonPath);
|
||||
WatchJsonFile(cnJsonPath);
|
||||
|
||||
}
|
||||
|
||||
public void WatchJsonFile(string filePath)
|
||||
{
|
||||
if (!File.Exists(filePath))
|
||||
{
|
||||
throw new BusinessValidationFailedException(StaticData.International("IRaCISCHangfireJob_FileNotFound"));
|
||||
}
|
||||
|
||||
FileSystemWatcher watcher = new FileSystemWatcher(Path.GetDirectoryName(filePath), Path.GetFileName(filePath));
|
||||
watcher.Changed += (sender, e) => LoadJsonFile(filePath);
|
||||
watcher.EnableRaisingEvents = true;
|
||||
|
||||
}
|
||||
|
||||
|
||||
private void LoadJsonFile(string filePath)
|
||||
{
|
||||
|
||||
IConfigurationBuilder builder = new ConfigurationBuilder().AddJsonFile(filePath);
|
||||
|
||||
IConfigurationRoot enConfiguration = builder.Build();
|
||||
|
||||
foreach (IConfigurationSection section in enConfiguration.GetChildren())
|
||||
{
|
||||
if (filePath.Contains(StaticData.En_US_Json))
|
||||
{
|
||||
StaticData.En_US_Dic[section.Key] = section.Value;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
StaticData.Zh_CN_Dic[section.Key] = section.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public async Task InitSysAndTrialCronJobAsync()
|
||||
{
|
||||
@@ -206,6 +105,36 @@ namespace IRaCIS.Application.Services.BackGroundJob
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#region 废弃 前端上传的时候获取
|
||||
|
||||
|
||||
/// <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));
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -8,6 +8,8 @@ using Panda.DynamicWebApi;
|
||||
using Panda.DynamicWebApi.Attributes;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using ZiggyCreatures.Caching.Fusion;
|
||||
|
||||
namespace IRaCIS.Core.Application
|
||||
{
|
||||
@@ -17,7 +19,8 @@ namespace IRaCIS.Core.Application
|
||||
|
||||
#region 非泛型版本
|
||||
|
||||
[Authorize, DynamicWebApi, UnifiedApiResultFilter]
|
||||
[TypeFilter(typeof(UnifiedApiResultFilter))]
|
||||
[Authorize, DynamicWebApi]
|
||||
public class BaseService : IBaseService, IDynamicWebApi
|
||||
{
|
||||
public IMapper _mapper { get; set; }
|
||||
@@ -30,7 +33,7 @@ namespace IRaCIS.Core.Application
|
||||
|
||||
public IWebHostEnvironment _hostEnvironment { get; set; }
|
||||
|
||||
|
||||
public IFusionCache _fusionCache { get; set; }
|
||||
|
||||
|
||||
public static IResponseOutput Null404NotFound<TEntity>(TEntity? businessObject) where TEntity : class
|
||||
@@ -58,6 +61,9 @@ namespace IRaCIS.Core.Application
|
||||
[MemberNotNull(nameof(_hostEnvironment))]
|
||||
public IWebHostEnvironment _hostEnvironment { get; set; }
|
||||
|
||||
[MemberNotNull(nameof(_fusionCache))]
|
||||
public IFusionCache _fusionCache { get; set; }
|
||||
|
||||
}
|
||||
#endregion
|
||||
|
||||
@@ -79,12 +85,13 @@ namespace IRaCIS.Core.Application
|
||||
[MemberNotNull(nameof(_localizer))]
|
||||
public IStringLocalizer _localizer { get; set; }
|
||||
|
||||
[MemberNotNull(nameof(_fusionCache))]
|
||||
public IFusionCache _fusionCache { get; set; }
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
[Authorize, DynamicWebApi, UnifiedApiResultFilter]
|
||||
[TypeFilter(typeof(UnifiedApiResultFilter))]
|
||||
[Authorize, DynamicWebApi]
|
||||
public class BaseServiceTest<T> : IBaseServiceTest<T>, IDynamicWebApi where T : Entity
|
||||
{
|
||||
public IMapper _mapper { get; set; }
|
||||
@@ -95,6 +102,8 @@ namespace IRaCIS.Core.Application
|
||||
|
||||
public IStringLocalizer _localizer { get; set; }
|
||||
|
||||
public IFusionCache _fusionCache { get; set; }
|
||||
|
||||
public static IResponseOutput Null404NotFound<TEntity>(TEntity? businessObject) where TEntity : class
|
||||
{
|
||||
return new ResponseOutput<string>()
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace IRaCIS.Core.Application.BusinessFilter
|
||||
{
|
||||
public class EncreptApiResultFilter : IAsyncResultFilter
|
||||
{
|
||||
|
||||
private readonly IOptionsMonitor<EncreptResponseOption> _encreptResponseMonitor;
|
||||
|
||||
public EncreptApiResultFilter(IOptionsMonitor<EncreptResponseOption> encreptResponseMonitor)
|
||||
{
|
||||
_encreptResponseMonitor = encreptResponseMonitor;
|
||||
}
|
||||
|
||||
public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next)
|
||||
{
|
||||
|
||||
if(_encreptResponseMonitor.CurrentValue.IsEnable)
|
||||
{
|
||||
|
||||
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,50 @@
|
||||
using Microsoft.AspNetCore.Diagnostics;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -35,6 +35,8 @@ public class LimitUserRequestAuthorization : IAsyncAuthorizationFilter
|
||||
|
||||
public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
|
||||
{
|
||||
var minutes = _verifyConfig.CurrentValue.AutoLoginOutMinutes;
|
||||
|
||||
if (_verifyConfig.CurrentValue.OpenLoginLimit)
|
||||
{
|
||||
if (context.ActionDescriptor.EndpointMetadata.Any(item => item is IAllowAnonymous))
|
||||
@@ -58,22 +60,45 @@ public class LimitUserRequestAuthorization : IAsyncAuthorizationFilter
|
||||
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["LimitUser_AuthTokenMissing"]));
|
||||
}
|
||||
|
||||
|
||||
|
||||
//2、在这里取缓存 进行比较 看是否有其他人进行了登陆,如果其他人登陆了,就把之前用户挤掉
|
||||
var cacheUserToken = (await _provider.GetAsync<string>(_userInfo.Id.ToString())).Value;
|
||||
|
||||
|
||||
|
||||
//缓存中没有取到Token
|
||||
if (string.IsNullOrWhiteSpace(cacheUserToken))
|
||||
{
|
||||
cacheUserToken = _userInfo.UserToken;
|
||||
|
||||
//设置当前用户最新Token
|
||||
await _provider.SetAsync(_userInfo.Id.ToString(), _userInfo.UserToken, TimeSpan.FromDays(7));
|
||||
|
||||
cacheUserToken = _userInfo.UserToken;
|
||||
//重启应用程序,所有人续期,不一下子踢出所有人
|
||||
await _provider.SetAsync($"{_userInfo.Id.ToString()}_Online", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), TimeSpan.FromMinutes(minutes));
|
||||
|
||||
}
|
||||
//是同一个人
|
||||
else if (cacheUserToken == _userInfo.UserToken)
|
||||
{
|
||||
|
||||
var cacheTime = (await _provider.GetAsync<string>($"{_userInfo.Id.ToString()}_Online")).Value;
|
||||
|
||||
//过期了 需要自动退出
|
||||
if (string.IsNullOrEmpty(cacheTime))
|
||||
{
|
||||
context.HttpContext.Response.ContentType = "application/json";
|
||||
context.HttpContext.Response.StatusCode = StatusCodes.Status403Forbidden;
|
||||
|
||||
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["LimitUser_AccountAuto_LoginOut"], ApiResponseCodeEnum.AutoLoginOut));
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
await _provider.SetAsync($"{_userInfo.Id.ToString()}_Online", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), TimeSpan.FromMinutes(minutes));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
else
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using IRaCIS.Core.Infrastructure;
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using IRaCIS.Core.Infrastructure;
|
||||
using IRaCIS.Core.Infrastructure.Extention;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
@@ -11,13 +12,13 @@ namespace IRaCIS.Core.Application.Filter
|
||||
{
|
||||
private readonly ILogger<ProjectExceptionFilter> _logger;
|
||||
|
||||
public IStringLocalizer _localizer;
|
||||
public IStringLocalizer _localizer;
|
||||
|
||||
public ProjectExceptionFilter(IStringLocalizer localizer, ILogger<ProjectExceptionFilter> logger)
|
||||
public ProjectExceptionFilter(IStringLocalizer localizer, ILogger<ProjectExceptionFilter> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
_localizer = localizer;
|
||||
}
|
||||
_localizer = localizer;
|
||||
}
|
||||
public void OnException(ExceptionContext context)
|
||||
{
|
||||
//context.ExceptionHandled;//记录当前这个异常是否已经被处理过了
|
||||
@@ -26,34 +27,51 @@ namespace IRaCIS.Core.Application.Filter
|
||||
{
|
||||
if (context.Exception.GetType().Name == "DbUpdateConcurrencyException")
|
||||
{
|
||||
//---并发更新,当前不允许该操作
|
||||
//---并发更新,当前不允许该操作
|
||||
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["ProjectException_ConcurrentUpdateNotAllowed"] + context.Exception.Message));
|
||||
}
|
||||
|
||||
if (context.Exception.GetType() == typeof(BusinessValidationFailedException))
|
||||
{
|
||||
context.Result = new JsonResult(ResponseOutput.NotOk(context.Exception.Message,ApiResponseCodeEnum.BusinessValidationFailed));
|
||||
var error = context.Exception as BusinessValidationFailedException;
|
||||
|
||||
var info = string.Empty;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(error!.LocalizedKey) && StaticData.Log_Locoalize_Dic.ContainsKey(error!.LocalizedKey))
|
||||
{
|
||||
info = $"[{error!.LocalizedKey}]:{StaticData.Log_Locoalize_Dic[error!.LocalizedKey]}";
|
||||
}
|
||||
|
||||
|
||||
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))
|
||||
else if (context.Exception.GetType() == typeof(QueryBusinessObjectNotExistException))
|
||||
{
|
||||
context.Result = new JsonResult(ResponseOutput.NotOk( context.Exception.Message, ApiResponseCodeEnum.DataNotExist));
|
||||
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.ExceptionHandled = true;//标记当前异常已经被处理过了
|
||||
|
||||
_logger.LogError(context.Exception.InnerException is null ? (context.Exception.Message + context.Exception.StackTrace) : (context.Exception.InnerException?.Message + context.Exception.InnerException?.StackTrace));
|
||||
|
||||
//throw new Exception("test-result-exceptioin");
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
//继续
|
||||
}
|
||||
context.ExceptionHandled = true;//标记当前异常已经被处理过了
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
using EasyCaching.Core;
|
||||
using IRaCIS.Core.Application.Helper;
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using System.Text.RegularExpressions;
|
||||
using ZiggyCreatures.Caching.Fusion;
|
||||
using static IRaCIS.Core.Domain.Share.StaticData;
|
||||
|
||||
namespace IRaCIS.Core.Application.Filter
|
||||
@@ -16,47 +18,36 @@ namespace IRaCIS.Core.Application.Filter
|
||||
{
|
||||
private readonly IEasyCachingProvider _provider;
|
||||
private readonly IUserInfo _userInfo;
|
||||
|
||||
private readonly IFusionCache _fusionCache;
|
||||
public IStringLocalizer _localizer;
|
||||
private readonly List<string> _trialOptList=new List<string>();
|
||||
private readonly IRepository<Trial> _trialRepository;
|
||||
private readonly List<string> _trialOptList = new List<string>();
|
||||
|
||||
|
||||
public TrialResourceFilter(IEasyCachingProvider provider, IStringLocalizer localizer , IUserInfo userInfo, string trialOpt = null, string trialOpt2 = null, string trialOpt3 = null)
|
||||
public TrialResourceFilter(IFusionCache fusionCache, IRepository<Trial> trialRepository, IEasyCachingProvider provider, IStringLocalizer localizer, IUserInfo userInfo, string trialOpt = null, string trialOpt2 = null, string trialOpt3 = null)
|
||||
{
|
||||
_fusionCache = fusionCache;
|
||||
_provider = provider;
|
||||
_userInfo = userInfo;
|
||||
_localizer = localizer;
|
||||
//_trialOpt = trialOpt;
|
||||
_trialRepository = trialRepository;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(trialOpt)) _trialOptList.Add(trialOpt.Trim());
|
||||
if (!string.IsNullOrWhiteSpace(trialOpt)) _trialOptList.Add(trialOpt.Trim());
|
||||
if (!string.IsNullOrWhiteSpace(trialOpt2)) _trialOptList.Add(trialOpt2.Trim());
|
||||
if (!string.IsNullOrWhiteSpace(trialOpt3)) _trialOptList.Add(trialOpt3.Trim());
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
//public TrialResourceFilter(IEasyCachingProvider provider, IUserInfo userInfo)
|
||||
//{
|
||||
// _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();
|
||||
// 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();
|
||||
|
||||
#region 处理新的用户类型,不能操作项目相关接口
|
||||
|
||||
// 后期列举出具体的类型,其他任何用户类型,都不允许操作
|
||||
if (_userInfo.UserTypeEnumInt == (int)UserTypeEnum.CRA && _userInfo.RequestUrl.ToLower()!= "TrialDocument/userConfirm".ToLower())
|
||||
if (_userInfo.UserTypeEnumInt == (int)UserTypeEnum.CRA && _userInfo.RequestUrl.ToLower() != "TrialDocument/userConfirm".ToLower())
|
||||
{
|
||||
//---对不起,您的账户没有操作权限。
|
||||
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["TrialResource_NoAccessPermission"]));
|
||||
@@ -97,7 +88,7 @@ namespace IRaCIS.Core.Application.Filter
|
||||
}
|
||||
else
|
||||
{
|
||||
//---正则取请求Refer 中trialId 失败,请联系开发人员核查
|
||||
//---正则取请求Refer 中trialId 失败,请联系开发人员核查
|
||||
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["TrialResource_ReferTrialIdFailed"]));
|
||||
}
|
||||
|
||||
@@ -128,20 +119,20 @@ namespace IRaCIS.Core.Application.Filter
|
||||
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;
|
||||
trialIdStr = matchResult.Value;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(trialStatusStr))
|
||||
var trialStatusStr = await _fusionCache.GetOrSetAsync(CacheKeys.Trial(trialIdStr), _ => CacheHelper.GetTrialStatusAsync(Guid.Parse(trialIdStr), _trialRepository), TimeSpan.FromDays(7));
|
||||
|
||||
if (string.IsNullOrWhiteSpace(trialStatusStr))
|
||||
{
|
||||
trialIdStr = matchResult.Value;
|
||||
//数据库 检查该项目Id不对
|
||||
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["TrialResource_ReferTrialIdFailed"]));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
//---正则取请求Refer 中trialId 失败,请联系开发人员核查
|
||||
//---正则取请求Refer 中trialId 失败,请联系开发人员核查
|
||||
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["TrialResource_ReferTrialIdFailed"]));
|
||||
}
|
||||
|
||||
@@ -153,34 +144,12 @@ namespace IRaCIS.Core.Application.Filter
|
||||
}
|
||||
|
||||
//通过path 或者body 找到trialId 了
|
||||
if ( !string.IsNullOrWhiteSpace(trialIdStr))
|
||||
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();
|
||||
}
|
||||
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) )
|
||||
if (trialStatusStr == StaticData.TrialState.TrialOngoing || _trialOptList.Any(t => t == TrialOpt.BeforeOngoingCantOpt))
|
||||
{
|
||||
|
||||
await next.Invoke();
|
||||
@@ -189,14 +158,14 @@ namespace IRaCIS.Core.Application.Filter
|
||||
// 项目停止、或者完成 不允许操作
|
||||
else
|
||||
{
|
||||
//---本次请求被配置规则拦截:项目状态处于进行中时,才允许操作,若此处逻辑有误,请联系开发人员修改
|
||||
//---本次请求被配置规则拦截:项目状态处于进行中时,才允许操作,若此处逻辑有误,请联系开发人员修改
|
||||
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["TrialResource_InterceptedProjectStatusRule"]));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
//添加项目 签名系统文档的时候 不做拦截 但是更新项目 签名项目文档的时候需要拦截
|
||||
else if (_trialOptList.Any(t => t == TrialOpt.AddOrUpdateTrial ||t ==TrialOpt.SignSystemDocNoTrialId))
|
||||
else if (_trialOptList.Any(t => t == TrialOpt.AddOrUpdateTrial || t == TrialOpt.SignSystemDocNoTrialId))
|
||||
{
|
||||
await next.Invoke();
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace IRaCIS.Application.Services.BusinessFilter
|
||||
{
|
||||
@@ -8,8 +9,15 @@ namespace IRaCIS.Application.Services.BusinessFilter
|
||||
/// 统一返回前端数据包装,之前在控制器包装,现在修改为动态Api 在ResultFilter这里包装,减少重复冗余代码
|
||||
/// by zhouhang 2021.09.12 周末
|
||||
/// </summary>
|
||||
public class UnifiedApiResultFilter : Attribute, IAsyncResultFilter
|
||||
{
|
||||
public class UnifiedApiResultFilter : Attribute, IAsyncResultFilter
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public UnifiedApiResultFilter(ILogger<UnifiedApiResultFilter> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步版本
|
||||
/// </summary>
|
||||
@@ -26,25 +34,15 @@ namespace IRaCIS.Application.Services.BusinessFilter
|
||||
//是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<,>)))
|
||||
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);
|
||||
|
||||
@@ -64,18 +62,20 @@ namespace IRaCIS.Application.Services.BusinessFilter
|
||||
objectResult.DeclaredType = apiResponse.GetType();
|
||||
}
|
||||
|
||||
|
||||
//}
|
||||
|
||||
|
||||
}
|
||||
//如果不是200 是IResponseOutput 不处理
|
||||
else if (statusCode != 200 && (objectResult.Value is IResponseOutput))
|
||||
//如果是200 是IResponseOutput 记录下日志
|
||||
else if (statusCode == 200 && (objectResult.Value is IResponseOutput))
|
||||
{
|
||||
var result = objectResult.Value as IResponseOutput;
|
||||
|
||||
//统一在这里记录国际化的日志信息
|
||||
_logger.LogWarning($"{result.LocalizedInfo}");
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace IRaCIS.Core.Application.Helper
|
||||
{
|
||||
|
||||
public static class CacheKeys
|
||||
{
|
||||
public static string Trial(string trialIdStr) => $"TrialId:{trialIdStr}";
|
||||
|
||||
|
||||
// 你可以为其他实体和模块定义更多的键
|
||||
}
|
||||
|
||||
public static class CacheHelper
|
||||
{
|
||||
public static async Task<string?> GetTrialStatusAsync(Guid trialId, IRepository<Trial> _trialRepository)
|
||||
{
|
||||
var statusStr = await _trialRepository.Where(t => t.Id == trialId, ignoreQueryFilters: true).Select(t => t.TrialStatusStr).FirstOrDefaultAsync();
|
||||
|
||||
return statusStr;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
using NPOI.SS.Formula.Functions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace IRaCIS.Core.Application.Helper
|
||||
{
|
||||
public static class EmailMaskHelper
|
||||
{
|
||||
|
||||
//显示位数:3分之2的位数,向上取整
|
||||
//取哪几个个值:最后一位和前面几位
|
||||
//其他:3个***。
|
||||
//比如:hlj23@126.com
|
||||
//为:hlj***3@126.com
|
||||
|
||||
//he@126.com
|
||||
//为:h*** e@126.com
|
||||
|
||||
public static string MaskEmail(string email)
|
||||
{
|
||||
|
||||
// 找到 "@" 符号的位置
|
||||
int atIndex = email.IndexOf('@');
|
||||
|
||||
string visiblePartBefore = email.Substring(0, atIndex);
|
||||
|
||||
string afterAt = email.Substring(atIndex + 1);
|
||||
|
||||
int visibleLength = (int)Math.Ceiling((double)visiblePartBefore.Length * 2 / 3);
|
||||
|
||||
// 替换中间两位字符为星号
|
||||
string hiddenPartBeforeAt = visiblePartBefore.Substring(0, visibleLength - 1) + "***" + visiblePartBefore.Last();
|
||||
|
||||
|
||||
// 组合隐藏和可见部分
|
||||
string hiddenEmail = hiddenPartBeforeAt + "@" + afterAt;
|
||||
|
||||
return hiddenEmail;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,6 @@ using IRaCIS.Application.Contracts;
|
||||
using IRaCIS.Application.Interfaces;
|
||||
using IRaCIS.Core.Application.Helper;
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using Magicodes.ExporterAndImporter.Excel;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using MiniExcelLibs;
|
||||
@@ -14,7 +13,6 @@ using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NPOI.HSSF.UserModel;
|
||||
using NPOI.XSSF.UserModel;
|
||||
using SkiaSharp;
|
||||
using System.Collections;
|
||||
using System.IO;
|
||||
|
||||
@@ -119,7 +117,7 @@ public static class ExcelExportHelper
|
||||
|
||||
|
||||
|
||||
var (physicalPath, fileNmae) = await FileStoreHelper.GetCommonDocPhysicalFilePathAsync(_hostEnvironment, _commonDocumentRepository, code);
|
||||
var (physicalPath, fileName) = await FileStoreHelper.GetCommonDocPhysicalFilePathAsync(_hostEnvironment, _commonDocumentRepository, code);
|
||||
|
||||
|
||||
//模板路径
|
||||
@@ -159,7 +157,7 @@ public static class ExcelExportHelper
|
||||
}
|
||||
|
||||
// 文件名称 从sheet里面取
|
||||
fileNmae = workbook.GetSheetName(0);
|
||||
//fileNmae = workbook.GetSheetName(0);
|
||||
#endregion
|
||||
|
||||
|
||||
@@ -183,7 +181,7 @@ public static class ExcelExportHelper
|
||||
|
||||
return new FileStreamResult(memoryStream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
|
||||
{
|
||||
FileDownloadName = $"{exportFileNamePrefix}_{fileNmae/*.Substring(0, fileNmae.LastIndexOf('.'))*/}_{DateTime.Now.ToString("yyyyMMddHHmmss")}.xlsx"
|
||||
FileDownloadName = $"{(string.IsNullOrEmpty(exportFileNamePrefix) ? "" : exportFileNamePrefix+ "_") }{Path.GetFileNameWithoutExtension(fileName) }_{DateTime.Now.ToString("yyyyMMddHHmmss")}.xlsx"
|
||||
};
|
||||
|
||||
#endregion
|
||||
@@ -330,7 +328,7 @@ public static class ExcelExportHelper
|
||||
}
|
||||
|
||||
// 文件名称 从sheet里面取
|
||||
fileName = workbook.GetSheetName(0);
|
||||
//fileName = workbook.GetSheetName(0);
|
||||
#endregion
|
||||
|
||||
#region MiniExcel
|
||||
@@ -342,7 +340,7 @@ public static class ExcelExportHelper
|
||||
IgnoreTemplateParameterMissing = true,
|
||||
};
|
||||
|
||||
await MiniExcel.SaveAsByTemplateAsync(memoryStream, tplPath, translateData, config);
|
||||
await MiniExcel.SaveAsByTemplateAsync(memoryStream, templateStream.ToArray(), translateData, config);
|
||||
|
||||
memoryStream.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System.Configuration;
|
||||
using System.Globalization;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace IRaCIS.Core.Application.Helper;
|
||||
@@ -128,65 +129,6 @@ public static class FileStoreHelper
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="_hostEnvironment"></param>
|
||||
/// <param name="_clinicalDataTrialSetRepository"></param>
|
||||
/// <param name="id"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="BusinessValidationFailedException"></exception>
|
||||
public static async Task<(string PhysicalPath, string FileName)> GetSystemClinicalPathAsync(IWebHostEnvironment _hostEnvironment, IRepository<ClinicalDataSystemSet> _clinicalDataTrialSetRepository, Guid id)
|
||||
{
|
||||
var systemClinicalData = await _clinicalDataTrialSetRepository.FirstOrDefaultAsync(t => t.Id == id);
|
||||
|
||||
if (systemClinicalData == null || systemClinicalData.Path == string.Empty)
|
||||
{
|
||||
//---数据库没有找到对应的数据模板文件,请联系系统运维人员。
|
||||
throw new BusinessValidationFailedException(StaticData.International("FileStore_TemplateFileNotFound"));
|
||||
}
|
||||
|
||||
var filePath = FileStoreHelper.GetPhysicalFilePath(_hostEnvironment, systemClinicalData.Path);
|
||||
|
||||
if (!System.IO.File.Exists(filePath))
|
||||
{
|
||||
//---数据模板文件存储路径上未找对应文件,请联系系统运维人员。
|
||||
throw new BusinessValidationFailedException(StaticData.International("FileStore_TemplateFileStoragePathInvalid"));
|
||||
}
|
||||
|
||||
return (filePath, systemClinicalData.FileName);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="_hostEnvironment"></param>
|
||||
/// <param name="_clinicalDataTrialSetRepository"></param>
|
||||
/// <param name="id"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="BusinessValidationFailedException"></exception>
|
||||
public static async Task<(string PhysicalPath, string FileName)> GetTrialClinicalPathAsync(IWebHostEnvironment _hostEnvironment, IRepository<ClinicalDataTrialSet> _clinicalDataTrialSetRepository, Guid id)
|
||||
{
|
||||
var trialClinicalData = await _clinicalDataTrialSetRepository.FirstOrDefaultAsync(t => t.Id == id);
|
||||
|
||||
if (trialClinicalData == null|| trialClinicalData.Path==string.Empty)
|
||||
{
|
||||
//---数据库没有找到对应的数据模板文件,请联系系统运维人员。
|
||||
throw new BusinessValidationFailedException(StaticData.International("FileStore_TemplateFileNotFound"));
|
||||
}
|
||||
|
||||
var filePath = FileStoreHelper.GetPhysicalFilePath(_hostEnvironment, trialClinicalData.Path);
|
||||
|
||||
if (!System.IO.File.Exists(filePath))
|
||||
{
|
||||
//---数据模板文件存储路径上未找对应文件,请联系系统运维人员。
|
||||
throw new BusinessValidationFailedException(StaticData.International("FileStore_TemplateFileStoragePathInvalid"));
|
||||
}
|
||||
|
||||
return (filePath, trialClinicalData.FileName);
|
||||
}
|
||||
|
||||
|
||||
//通过编码获取通用文档具体物理路径
|
||||
|
||||
@@ -194,6 +136,7 @@ public static class FileStoreHelper
|
||||
{
|
||||
var doc = await _commonDocumentRepository.FirstOrDefaultAsync(t => t.Code == code);
|
||||
|
||||
var isEn_US = CultureInfo.CurrentCulture.Name!= "zh-CN";
|
||||
if (doc == null)
|
||||
{
|
||||
//---数据库没有找到对应的数据模板文件,请联系系统运维人员。
|
||||
@@ -208,7 +151,7 @@ public static class FileStoreHelper
|
||||
throw new BusinessValidationFailedException(StaticData.International("FileStore_TemplateFileStoragePathInvalid"));
|
||||
}
|
||||
|
||||
return (filePath, doc.Name.Trim('/'));
|
||||
return (filePath, isEn_US? doc.Name.Trim('/'): doc.NameCN.Trim('/'));
|
||||
}
|
||||
|
||||
|
||||
@@ -235,44 +178,6 @@ public static class FileStoreHelper
|
||||
return writeCount;
|
||||
}
|
||||
|
||||
// 获取项目签名文档存放路径
|
||||
|
||||
public static (string PhysicalPath, string RelativePath) GetTrialSignDocPath(IWebHostEnvironment _hostEnvironment, Guid trialId, string fileName)
|
||||
{
|
||||
var rootPath = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment);
|
||||
|
||||
//文件类型路径处理
|
||||
var uploadFolderPath = Path.Combine(rootPath, StaticData.Folder.TrialDataFolder, trialId.ToString(), StaticData.Folder.SignDocumentFolder);
|
||||
if (!Directory.Exists(uploadFolderPath)) Directory.CreateDirectory(uploadFolderPath);
|
||||
|
||||
var (trustedFileNameForFileStorage, fileRealName) = FileStoreHelper.GetStoreFileName(fileName);
|
||||
|
||||
var relativePath = $"/{StaticData.Folder.IRaCISDataFolder}/{StaticData.Folder.TrialDataFolder}/{trialId}/{StaticData.Folder.SignDocumentFolder}/{trustedFileNameForFileStorage}";
|
||||
|
||||
var serverFilePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage);
|
||||
|
||||
return (serverFilePath, relativePath);
|
||||
}
|
||||
|
||||
// 获取系统签名文档存放路径
|
||||
public static (string PhysicalPath, string RelativePath) GetSystemSignDocPath(IWebHostEnvironment _hostEnvironment, string fileName)
|
||||
{
|
||||
var rootPath = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment);
|
||||
|
||||
|
||||
//文件类型路径处理
|
||||
var uploadFolderPath = Path.Combine(rootPath, StaticData.Folder.SystemDataFolder, StaticData.Folder.SignDocumentFolder);
|
||||
if (!Directory.Exists(uploadFolderPath)) Directory.CreateDirectory(uploadFolderPath);
|
||||
|
||||
|
||||
var (trustedFileNameForFileStorage, fileRealName) = FileStoreHelper.GetStoreFileName(fileName);
|
||||
|
||||
var relativePath = $"/{StaticData.Folder.IRaCISDataFolder}/{StaticData.Folder.SystemDataFolder}/{ StaticData.Folder.SignDocumentFolder}/{trustedFileNameForFileStorage}";
|
||||
|
||||
var serverFilePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage);
|
||||
|
||||
return (serverFilePath, relativePath);
|
||||
}
|
||||
|
||||
// 获取通用文档存放路径(excel模板 )
|
||||
|
||||
@@ -295,238 +200,20 @@ public static class FileStoreHelper
|
||||
return (serverFilePath, relativePath);
|
||||
}
|
||||
|
||||
//获取系统通知文档存放路径
|
||||
|
||||
public static (string PhysicalPath, string RelativePath) GetSystemNoticePath(IWebHostEnvironment _hostEnvironment, string fileName)
|
||||
{
|
||||
var rootPath = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment);
|
||||
|
||||
//文件类型路径处理
|
||||
var uploadFolderPath = Path.Combine(rootPath, StaticData.Folder.SystemDataFolder, StaticData.Folder.NoticeAttachment);
|
||||
if (!Directory.Exists(uploadFolderPath)) Directory.CreateDirectory(uploadFolderPath);
|
||||
|
||||
|
||||
var (trustedFileNameForFileStorage, fileRealName) = FileStoreHelper.GetStoreFileName(fileName);
|
||||
|
||||
|
||||
|
||||
var relativePath = $"/{StaticData.Folder.IRaCISDataFolder}/{StaticData.Folder.SystemDataFolder}/{StaticData.Folder.NoticeAttachment}/{trustedFileNameForFileStorage}";
|
||||
|
||||
var serverFilePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage);
|
||||
|
||||
return (serverFilePath, relativePath);
|
||||
}
|
||||
|
||||
// 获取一致性核查路径
|
||||
public static (string PhysicalPath, string RelativePath) GetTrialCheckFilePath(IWebHostEnvironment _hostEnvironment, string fileName,Guid trialId)
|
||||
{
|
||||
var rootPath = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment);
|
||||
|
||||
//上传根路径
|
||||
string uploadFolderPath = Path.Combine(rootPath, StaticData.Folder.TrialDataFolder, trialId.ToString(), StaticData.Folder.UploadEDCData);
|
||||
|
||||
if (!Directory.Exists(uploadFolderPath)) Directory.CreateDirectory(uploadFolderPath);
|
||||
|
||||
|
||||
//存放核对表
|
||||
var (trustedFileNameForFileStorage, realFileName) = FileStoreHelper.GetStoreFileName(fileName);
|
||||
|
||||
|
||||
var relativePath = $"/{StaticData.Folder.IRaCISDataFolder}/{StaticData.Folder.TrialDataFolder}/{trialId}/{StaticData.Folder.UploadEDCData}/{trustedFileNameForFileStorage}";
|
||||
|
||||
var serverFilePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage);
|
||||
|
||||
return (serverFilePath, relativePath);
|
||||
}
|
||||
|
||||
|
||||
public static (string PhysicalPath, string RelativePath, string FileRealName) GetClinicalTemplatePath(IWebHostEnvironment _hostEnvironment, string fileName,Guid trialId)
|
||||
{
|
||||
|
||||
var rootPath = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment);
|
||||
|
||||
string uploadFolderPath = Path.Combine(rootPath, StaticData.Folder.TrialDataFolder, "ClinicalTemplate", trialId.ToString(), StaticData.Folder.TreatmenthistoryFolder);
|
||||
|
||||
if (!Directory.Exists(uploadFolderPath)) Directory.CreateDirectory(uploadFolderPath);
|
||||
|
||||
var (trustedFileNameForFileStorage, fileRealName) = FileStoreHelper.GetStoreFileName(fileName);
|
||||
|
||||
|
||||
var relativePath = $"/{StaticData.Folder.IRaCISDataFolder}/{StaticData.Folder.TrialDataFolder}/ClinicalTemplate/{trialId}/{StaticData.Folder.TreatmenthistoryFolder}/{trustedFileNameForFileStorage}";
|
||||
|
||||
var serverFilePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage);
|
||||
|
||||
return (serverFilePath, relativePath, fileRealName);
|
||||
|
||||
}
|
||||
|
||||
|
||||
public static (string PhysicalPath, string RelativePath, string FileRealName) GetReadClinicalDataPath(IWebHostEnvironment _hostEnvironment, string fileName, Guid trialId, Guid siteId, Guid subjectId, Guid readingId)
|
||||
{
|
||||
var rootPath = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment);
|
||||
|
||||
string uploadFolderPath = Path.Combine(rootPath, StaticData.Folder.TrialDataFolder, trialId.ToString(), siteId.ToString(), subjectId.ToString(), StaticData.Folder.Reading, readingId.ToString());
|
||||
|
||||
if (!Directory.Exists(uploadFolderPath)) Directory.CreateDirectory(uploadFolderPath);
|
||||
|
||||
var (trustedFileNameForFileStorage, fileRealName) = FileStoreHelper.GetStoreFileName(fileName);
|
||||
|
||||
|
||||
var relativePath = $"/{StaticData.Folder.IRaCISDataFolder}/{StaticData.Folder.TrialDataFolder}/{trialId}/{siteId}/{subjectId}/{StaticData.Folder.Reading}/{readingId}/{trustedFileNameForFileStorage}";
|
||||
|
||||
var serverFilePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage);
|
||||
|
||||
return (serverFilePath, relativePath, fileRealName);
|
||||
|
||||
}
|
||||
|
||||
//获取临床数据存放路径
|
||||
public static (string PhysicalPath, string RelativePath,string FileRealName) GetClinicalDataPath(IWebHostEnvironment _hostEnvironment, string fileName,Guid trialId,Guid siteId,Guid subjectId,Guid subjectVisitId)
|
||||
{
|
||||
var rootPath = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment);
|
||||
|
||||
string uploadFolderPath = Path.Combine(rootPath, StaticData.Folder.TrialDataFolder, trialId.ToString(),siteId.ToString(), subjectId.ToString(), subjectVisitId.ToString(), StaticData.Folder.TreatmenthistoryFolder);
|
||||
|
||||
if (!Directory.Exists(uploadFolderPath)) Directory.CreateDirectory(uploadFolderPath);
|
||||
|
||||
var (trustedFileNameForFileStorage, fileRealName) = FileStoreHelper.GetStoreFileName(fileName);
|
||||
|
||||
|
||||
var relativePath = $"/{StaticData.Folder.IRaCISDataFolder}/{StaticData.Folder.TrialDataFolder}/{trialId}/{siteId}/{subjectId}/{subjectVisitId}/{StaticData.Folder.TreatmenthistoryFolder}/{trustedFileNameForFileStorage}";
|
||||
|
||||
var serverFilePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage);
|
||||
|
||||
return (serverFilePath, relativePath, fileRealName);
|
||||
|
||||
}
|
||||
|
||||
|
||||
public static (string PhysicalPath, string RelativePath, string FileRealName) GetClinicalDataPath(IWebHostEnvironment _hostEnvironment, string fileName, Guid trialId , Guid subjectId)
|
||||
{
|
||||
var rootPath = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment);
|
||||
|
||||
string uploadFolderPath = Path.Combine(rootPath, StaticData.Folder.TrialDataFolder, trialId.ToString(), subjectId.ToString(), StaticData.Folder.TreatmenthistoryFolder);
|
||||
|
||||
if (!Directory.Exists(uploadFolderPath)) Directory.CreateDirectory(uploadFolderPath);
|
||||
|
||||
var (trustedFileNameForFileStorage, fileRealName) = FileStoreHelper.GetStoreFileName(fileName);
|
||||
|
||||
|
||||
var relativePath = $"/{StaticData.Folder.IRaCISDataFolder}/{StaticData.Folder.TrialDataFolder}/{trialId}/{subjectId}/{StaticData.Folder.TreatmenthistoryFolder}/{trustedFileNameForFileStorage}";
|
||||
|
||||
var serverFilePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage);
|
||||
|
||||
return (serverFilePath, relativePath, fileRealName);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 上传截图
|
||||
/// </summary>
|
||||
/// <param name="_hostEnvironment"></param>
|
||||
/// <param name="fileName"></param>
|
||||
/// <param name="trialId"></param>
|
||||
/// <param name="siteid"></param>
|
||||
/// <param name="subjectId"></param>
|
||||
/// <returns></returns>
|
||||
public static (string PhysicalPath, string RelativePath, string FileRealName) GetUploadPrintscreenFilePath(IWebHostEnvironment _hostEnvironment, string fileName, Guid trialId, Guid siteid, Guid subjectId)
|
||||
{
|
||||
var rootPath = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment);
|
||||
|
||||
string uploadFolderPath = Path.Combine(rootPath, StaticData.Folder.TrialDataFolder, trialId.ToString(), siteid.ToString(), subjectId.ToString());
|
||||
|
||||
if (!Directory.Exists(uploadFolderPath)) Directory.CreateDirectory(uploadFolderPath);
|
||||
|
||||
var (trustedFileNameForFileStorage, fileRealName) = FileStoreHelper.GetStoreFileName(fileName);
|
||||
|
||||
|
||||
var relativePath = $"/{StaticData.Folder.IRaCISDataFolder}/{StaticData.Folder.TrialDataFolder}/{trialId}/{siteid}/{subjectId}/{trustedFileNameForFileStorage}";
|
||||
|
||||
var serverFilePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage);
|
||||
|
||||
return (serverFilePath, relativePath, fileRealName);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 通用获取文件路径
|
||||
/// </summary>
|
||||
/// <param name="_hostEnvironment"></param>
|
||||
/// <param name="fileName"></param>
|
||||
/// <param name="trialId"></param>
|
||||
/// <param name="id"></param>
|
||||
/// <param name="type"></param>
|
||||
/// <returns></returns>
|
||||
public static (string PhysicalPath, string RelativePath, string FileRealName) GetFilePath(IWebHostEnvironment _hostEnvironment, string fileName, Guid trialId, Guid id,string type)
|
||||
{
|
||||
var rootPath = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment);
|
||||
|
||||
string uploadFolderPath = Path.Combine(rootPath, StaticData.Folder.TrialDataFolder, trialId.ToString(), id.ToString(), type);
|
||||
|
||||
if (!Directory.Exists(uploadFolderPath)) Directory.CreateDirectory(uploadFolderPath);
|
||||
|
||||
var (trustedFileNameForFileStorage, fileRealName) = FileStoreHelper.GetStoreFileName(fileName);
|
||||
|
||||
|
||||
var relativePath = $"/{StaticData.Folder.IRaCISDataFolder}/{StaticData.Folder.TrialDataFolder}/{trialId}/{id}/{type}/{trustedFileNameForFileStorage}";
|
||||
|
||||
var serverFilePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage);
|
||||
|
||||
return (serverFilePath, relativePath, fileRealName);
|
||||
}
|
||||
|
||||
public static (string PhysicalPath, string RelativePath, string FileRealName) GetMedicalReviewImage(IWebHostEnvironment _hostEnvironment, string fileName, Guid trialId, Guid taskMedicalReviewId)
|
||||
{
|
||||
var rootPath = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment);
|
||||
|
||||
string uploadFolderPath = Path.Combine(rootPath, StaticData.Folder.TrialDataFolder, trialId.ToString(), taskMedicalReviewId.ToString(), StaticData.Folder.MedicalReview);
|
||||
|
||||
if (!Directory.Exists(uploadFolderPath)) Directory.CreateDirectory(uploadFolderPath);
|
||||
|
||||
var (trustedFileNameForFileStorage, fileRealName) = FileStoreHelper.GetStoreFileName(fileName);
|
||||
|
||||
|
||||
var relativePath = $"/{StaticData.Folder.IRaCISDataFolder}/{StaticData.Folder.TrialDataFolder}/{trialId}/{taskMedicalReviewId}/{StaticData.Folder.MedicalReview}/{trustedFileNameForFileStorage}";
|
||||
|
||||
var serverFilePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage);
|
||||
|
||||
return (serverFilePath, relativePath, fileRealName);
|
||||
}
|
||||
|
||||
//获取非dicom文件存放路径
|
||||
public static (string PhysicalPath, string RelativePath, string FileRealName) GetNoneDicomFilePath(IWebHostEnvironment _hostEnvironment, string fileName, Guid trialId, Guid siteId, Guid subjectId, Guid subjectVisitId)
|
||||
{
|
||||
var rootPath = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment);
|
||||
|
||||
string uploadFolderPath = Path.Combine(rootPath, StaticData.Folder.TrialDataFolder, trialId.ToString(), siteId.ToString(), subjectId.ToString(), subjectVisitId.ToString(), StaticData.Folder.NoneDicomFolder);
|
||||
|
||||
if (!Directory.Exists(uploadFolderPath)) Directory.CreateDirectory(uploadFolderPath);
|
||||
|
||||
var (trustedFileNameForFileStorage, fileRealName) = FileStoreHelper.GetStoreFileName(fileName);
|
||||
|
||||
|
||||
var relativePath = $"/{StaticData.Folder.IRaCISDataFolder}/{StaticData.Folder.TrialDataFolder}/{trialId}/{siteId}/{subjectId}/{subjectVisitId}/{StaticData.Folder.NoneDicomFolder}/{trustedFileNameForFileStorage}";
|
||||
|
||||
var serverFilePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage);
|
||||
|
||||
return (serverFilePath, relativePath, fileRealName);
|
||||
}
|
||||
|
||||
// 获取 入组确认 PD 进展发送邮件Word|PDF 存放路径
|
||||
|
||||
public static (string PhysicalPath, string RelativePath, string FileRealName) GetSubjectEnrollConfirmOrPDEmailPath(IWebHostEnvironment _hostEnvironment, string fileName, Guid trialId, Guid siteId, Guid subjectId,bool isChangeToPdfFormat=false)
|
||||
public static (string PhysicalPath, string RelativePath, string FileRealName) GetSubjectEnrollConfirmOrPDEmailPath(IWebHostEnvironment _hostEnvironment, string fileName, Guid trialId, Guid trialSiteId, Guid subjectId,bool isChangeToPdfFormat=false)
|
||||
{
|
||||
var rootPath = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment);
|
||||
|
||||
string uploadFolderPath = Path.Combine(rootPath, StaticData.Folder.TrialDataFolder, trialId.ToString(), siteId.ToString(), subjectId.ToString());
|
||||
string uploadFolderPath = Path.Combine(rootPath, StaticData.Folder.TrialDataFolder, trialId.ToString(), trialSiteId.ToString(), subjectId.ToString());
|
||||
|
||||
if (!Directory.Exists(uploadFolderPath)) Directory.CreateDirectory(uploadFolderPath);
|
||||
|
||||
var (trustedFileNameForFileStorage, fileRealName) = FileStoreHelper.GetStoreFileName(fileName, isChangeToPdfFormat);
|
||||
|
||||
|
||||
var relativePath = $"/{StaticData.Folder.IRaCISDataFolder}/{StaticData.Folder.TrialDataFolder}/{trialId}/{siteId}/{subjectId}/{trustedFileNameForFileStorage}";
|
||||
var relativePath = $"/{StaticData.Folder.IRaCISDataFolder}/{StaticData.Folder.TrialDataFolder}/{trialId}/{trialSiteId}/{subjectId}/{trustedFileNameForFileStorage}";
|
||||
|
||||
var serverFilePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage);
|
||||
|
||||
@@ -541,7 +228,7 @@ public static class FileStoreHelper
|
||||
return Path.Combine(rootPath, StaticData.Folder.TrialDataFolder, trialId.ToString(), siteId.ToString(), subjectId.ToString(), subjectVisitId.ToString(), StaticData.Folder.DicomFolder);
|
||||
}
|
||||
|
||||
public static (string PhysicalPath, string RelativePath) GetDicomInstanceFilePath(IWebHostEnvironment _hostEnvironment, Guid trialId, Guid siteId, Guid subjectId, Guid subjectVisitId, Guid studyId, Guid instanceId)
|
||||
public static (string PhysicalPath, string RelativePath) GetDicomInstanceFilePath(IWebHostEnvironment _hostEnvironment, Guid trialId, Guid subjectId, Guid subjectVisitId, Guid studyId, Guid instanceId)
|
||||
{
|
||||
#region 切换存储前
|
||||
//var rootPath = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment);
|
||||
@@ -552,7 +239,7 @@ public static class FileStoreHelper
|
||||
|
||||
//加入访视层级 和Data
|
||||
var path = Path.Combine(rootPath, StaticData.Folder.TrialDataFolder, trialId.ToString(),
|
||||
siteId.ToString(), subjectId.ToString(), subjectVisitId.ToString(), StaticData.Folder.DicomFolder, studyId.ToString());
|
||||
subjectId.ToString(), subjectVisitId.ToString(), StaticData.Folder.DicomFolder, studyId.ToString());
|
||||
|
||||
if (!Directory.Exists(path))
|
||||
{
|
||||
@@ -561,7 +248,7 @@ public static class FileStoreHelper
|
||||
|
||||
var physicalPath = Path.Combine(path, instanceId.ToString() );
|
||||
|
||||
var relativePath = $"/{StaticData.Folder.IRaCISDataFolder}/{StaticData.Folder.TrialDataFolder}/{trialId}/{siteId}/{subjectId}/{subjectVisitId}/{StaticData.Folder.DicomFolder}/{studyId}/{instanceId}";
|
||||
var relativePath = $"/{StaticData.Folder.IRaCISDataFolder}/{StaticData.Folder.TrialDataFolder}/{trialId}/{subjectId}/{subjectVisitId}/{StaticData.Folder.DicomFolder}/{studyId}/{instanceId}";
|
||||
|
||||
//var physicalPath = Path.Combine(path, instanceId.ToString() + ".dcm");
|
||||
|
||||
@@ -629,49 +316,7 @@ public static class FileStoreHelper
|
||||
|
||||
|
||||
|
||||
// 获取医生通用文件存放路径
|
||||
|
||||
public static (string PhysicalPath, string RelativePath) GetDoctorOrdinaryFilePath(IWebHostEnvironment _hostEnvironment, string fileName,Guid doctorId,string attachmentType)
|
||||
{
|
||||
|
||||
var rootPath = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment);
|
||||
|
||||
//文件类型路径处理
|
||||
var uploadFolderPath = Path.Combine(rootPath, "UploadFile", doctorId.ToString(), attachmentType);
|
||||
if (!Directory.Exists(uploadFolderPath)) Directory.CreateDirectory(uploadFolderPath);
|
||||
|
||||
|
||||
var (trustedFileNameForFileStorage, fileRealName) = FileStoreHelper.GetStoreFileName(fileName);
|
||||
|
||||
|
||||
|
||||
var relativePath = $"/{StaticData.Folder.IRaCISDataFolder}/{StaticData.Folder.UploadFileFolder}/{doctorId}/{attachmentType}/{trustedFileNameForFileStorage}";
|
||||
|
||||
var serverFilePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage);
|
||||
|
||||
return (serverFilePath, relativePath);
|
||||
}
|
||||
|
||||
public static (string PhysicalPath, string RelativePath) GetNonDoctorFilePath(IWebHostEnvironment _hostEnvironment, string fileName, string attachmentType)
|
||||
{
|
||||
|
||||
var rootPath = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment);
|
||||
|
||||
//文件类型路径处理
|
||||
var uploadFolderPath = Path.Combine(rootPath, StaticData.Folder.UploadFileFolder, attachmentType);
|
||||
if (!Directory.Exists(uploadFolderPath)) Directory.CreateDirectory(uploadFolderPath);
|
||||
|
||||
|
||||
var (trustedFileNameForFileStorage, fileRealName) = FileStoreHelper.GetStoreFileName(fileName);
|
||||
|
||||
|
||||
|
||||
var relativePath = $"/{StaticData.Folder.IRaCISDataFolder}/{StaticData.Folder.UploadFileFolder}/{attachmentType}/{trustedFileNameForFileStorage}";
|
||||
|
||||
var serverFilePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage);
|
||||
|
||||
return (serverFilePath, relativePath);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using static BeetleX.Redis.Commands.HSCAN;
|
||||
using static IRaCIS.Core.Application.Service.Common.SystemMonitor;
|
||||
|
||||
namespace IRaCIS.Core.Application.Helper
|
||||
@@ -18,6 +17,9 @@ namespace IRaCIS.Core.Application.Helper
|
||||
{
|
||||
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 static void VerifyFolder()
|
||||
{
|
||||
if (!Directory.Exists(JsonFileFolder) ||
|
||||
@@ -51,6 +53,9 @@ namespace IRaCIS.Core.Application.Helper
|
||||
jsonObject[item.Code] = item.Value;
|
||||
|
||||
StaticData.En_US_Dic[item.Code] = item.Value;
|
||||
|
||||
//日志记录该信息方便自己人看, 返回给客户的是配置的
|
||||
StaticData.Log_Locoalize_Dic[item.Code] = item.Description;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -69,7 +74,7 @@ namespace IRaCIS.Core.Application.Helper
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task AddOrUpdateJsonKeyValueAsync(string key, string value, string valueCN)
|
||||
public static async Task AddOrUpdateJsonKeyValueAsync(string key, string value, string valueCN,string description)
|
||||
{
|
||||
|
||||
VerifyFolder();
|
||||
@@ -92,6 +97,9 @@ namespace IRaCIS.Core.Application.Helper
|
||||
jsonObject[key] = value;
|
||||
|
||||
StaticData.En_US_Dic[key] = value;
|
||||
|
||||
//日志记录该信息方便自己人看, 返回给客户的是配置的
|
||||
StaticData.Log_Locoalize_Dic[key] = description;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -107,14 +115,16 @@ namespace IRaCIS.Core.Application.Helper
|
||||
|
||||
}
|
||||
|
||||
public static async Task InitInternationlizationDataAndWatchJsonFileAsync(IRepository _repository)
|
||||
|
||||
public static async Task InitInternationlizationDataAndWatchJsonFileAsync(IRepository<Internationalization> _internationalizationRepository)
|
||||
{
|
||||
//查询数据库的数据
|
||||
var toJsonList = await _repository.Where<Internationalization>(t => t.InternationalizationType == 1).Select(t => new
|
||||
var toJsonList = await _internationalizationRepository.Where(t => t.InternationalizationType == 1).Select(t => new
|
||||
{
|
||||
t.Code,
|
||||
t.Value,
|
||||
t.ValueCN
|
||||
t.ValueCN,
|
||||
t.Description
|
||||
}).ToListAsync();
|
||||
|
||||
//组织成json 文件
|
||||
@@ -128,6 +138,9 @@ namespace IRaCIS.Core.Application.Helper
|
||||
{
|
||||
StaticData.En_US_Dic[tojsonItem.Code] = tojsonItem.Value;
|
||||
StaticData.Zh_CN_Dic[tojsonItem.Code] = tojsonItem.ValueCN;
|
||||
|
||||
//日志记录该信息方便自己人看, 返回给客户的是配置的
|
||||
StaticData.Log_Locoalize_Dic[tojsonItem.Code] = tojsonItem.Description;
|
||||
}
|
||||
|
||||
File.WriteAllText(usJsonPath, JsonConvert.SerializeObject(StaticData.En_US_Dic));
|
||||
@@ -136,29 +149,45 @@ namespace IRaCIS.Core.Application.Helper
|
||||
|
||||
//监测Json文件变更 实时刷新数据
|
||||
|
||||
WatchJsonFile(usJsonPath);
|
||||
WatchJsonFile(cnJsonPath);
|
||||
|
||||
}
|
||||
|
||||
public static void WatchJsonFile(string filePath)
|
||||
{
|
||||
if (!File.Exists(filePath))
|
||||
if (!File.Exists(usJsonPath) || !File.Exists(cnJsonPath))
|
||||
{
|
||||
throw new BusinessValidationFailedException("国际化Json文件不存在");
|
||||
throw new BusinessValidationFailedException(StaticData.International("IRaCISCHangfireJob_FileNotFound"));
|
||||
}
|
||||
|
||||
FileSystemWatcher watcher = new FileSystemWatcher(Path.GetDirectoryName(filePath), Path.GetFileName(filePath));
|
||||
watcher.Changed += (sender, e) => LoadJsonFile(filePath);
|
||||
watcher.EnableRaisingEvents = true;
|
||||
|
||||
// //监测Json文件变更 实时刷新数据
|
||||
|
||||
|
||||
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(StaticData.Folder.Resources + "\\" + StaticData.En_US_Json);
|
||||
|
||||
|
||||
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(StaticData.Folder.Resources + "\\" + StaticData.Zh_CN_Json);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static void LoadJsonFile(string filePath)
|
||||
{
|
||||
|
||||
IConfigurationBuilder builder = new ConfigurationBuilder().AddJsonFile(filePath);
|
||||
Console.WriteLine("刷新json内存数据");
|
||||
IConfigurationBuilder builder = new ConfigurationBuilder().AddJsonFile(filePath, false, false);
|
||||
|
||||
IConfigurationRoot enConfiguration = builder.Build();
|
||||
|
||||
@@ -175,5 +204,11 @@ namespace IRaCIS.Core.Application.Helper
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
using Aliyun.OSS;
|
||||
using IRaCIS.Core.Application.Contracts;
|
||||
using IRaCIS.Core.Infrastructure;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Minio.DataModel.Args;
|
||||
using Minio;
|
||||
using NPOI.HPSF;
|
||||
using SharpCompress.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -15,7 +13,7 @@ using System.Linq;
|
||||
using System.Security.AccessControl;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using SkiaSharp;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace IRaCIS.Core.Application.Helper
|
||||
{
|
||||
@@ -40,6 +38,9 @@ namespace IRaCIS.Core.Application.Helper
|
||||
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; }
|
||||
|
||||
@@ -96,13 +97,15 @@ namespace IRaCIS.Core.Application.Helper
|
||||
|
||||
public interface IOSSService
|
||||
{
|
||||
public Task<string> UploadToOSSAsync(Stream fileStream, string oosFolderPath, string fileRealName);
|
||||
public Task<string> UploadToOSSAsync(string localFilePath, string oosFolderPath);
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -122,74 +125,84 @@ namespace IRaCIS.Core.Application.Helper
|
||||
/// <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)
|
||||
public async Task<string> UploadToOSSAsync(Stream fileStream, string oosFolderPath, string fileRealName, bool isFileNameAddGuid = true)
|
||||
{
|
||||
|
||||
|
||||
var ossRelativePath = $"{oosFolderPath}/{Guid.NewGuid()}_{fileRealName}";
|
||||
var ossRelativePath = isFileNameAddGuid ? $"{oosFolderPath}/{Guid.NewGuid()}_{fileRealName}" : $"{oosFolderPath}/{fileRealName}";
|
||||
//var ossRelativePath = $"{oosFolderPath}/{Guid.NewGuid()}_{fileRealName}";
|
||||
//var ossRelativePath = oosFolderPath + "/" + fileRealName;
|
||||
|
||||
using (var memoryStream = new MemoryStream())
|
||||
try
|
||||
{
|
||||
fileStream.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
fileStream.CopyTo(memoryStream);
|
||||
|
||||
memoryStream.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
|
||||
if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS")
|
||||
using (var memoryStream = new MemoryStream())
|
||||
{
|
||||
var aliConfig = ObjectStoreServiceOptions.AliyunOSS;
|
||||
fileStream.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
var _ossClient = new OssClient(aliConfig.endPoint, aliConfig.accessKeyId, aliConfig.accessKeySecret);
|
||||
fileStream.CopyTo(memoryStream);
|
||||
|
||||
memoryStream.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
|
||||
if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS")
|
||||
{
|
||||
var aliConfig = ObjectStoreServiceOptions.AliyunOSS;
|
||||
|
||||
var _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.endPoint : aliConfig.internalEndpoint, aliConfig.accessKeyId, aliConfig.accessKeySecret);
|
||||
|
||||
|
||||
|
||||
// 上传文件
|
||||
var result = _ossClient.PutObject(aliConfig.bucketName, ossRelativePath, memoryStream);
|
||||
// 上传文件
|
||||
var result = _ossClient.PutObject(aliConfig.bucketName, ossRelativePath, memoryStream);
|
||||
|
||||
}
|
||||
else if (ObjectStoreServiceOptions.ObjectStoreUse == "MinIO")
|
||||
{
|
||||
var minIOConfig = ObjectStoreServiceOptions.MinIO;
|
||||
}
|
||||
else if (ObjectStoreServiceOptions.ObjectStoreUse == "MinIO")
|
||||
{
|
||||
var minIOConfig = ObjectStoreServiceOptions.MinIO;
|
||||
|
||||
|
||||
var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.endPoint}:{minIOConfig.port}")
|
||||
.WithCredentials(minIOConfig.accessKey, minIOConfig.secretKey).WithSSL(minIOConfig.useSSL)
|
||||
.Build();
|
||||
var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.endPoint}:{minIOConfig.port}")
|
||||
.WithCredentials(minIOConfig.accessKey, minIOConfig.secretKey).WithSSL(minIOConfig.useSSL)
|
||||
.Build();
|
||||
|
||||
var putObjectArgs = new PutObjectArgs()
|
||||
.WithBucket(minIOConfig.bucketName)
|
||||
.WithObject(ossRelativePath)
|
||||
.WithStreamData(memoryStream)
|
||||
.WithObjectSize(memoryStream.Length);
|
||||
var putObjectArgs = new PutObjectArgs()
|
||||
.WithBucket(minIOConfig.bucketName)
|
||||
.WithObject(ossRelativePath)
|
||||
.WithStreamData(memoryStream)
|
||||
.WithObjectSize(memoryStream.Length);
|
||||
|
||||
await minioClient.PutObjectAsync(putObjectArgs);
|
||||
}
|
||||
else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS")
|
||||
{
|
||||
var minIOConfig = ObjectStoreServiceOptions.AWS;
|
||||
await minioClient.PutObjectAsync(putObjectArgs);
|
||||
}
|
||||
else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS")
|
||||
{
|
||||
var minIOConfig = ObjectStoreServiceOptions.AWS;
|
||||
|
||||
|
||||
var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.endPoint}")
|
||||
.WithCredentials(minIOConfig.accessKey, minIOConfig.secretKey).WithSSL(minIOConfig.useSSL)
|
||||
.Build();
|
||||
var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.endPoint}")
|
||||
.WithCredentials(minIOConfig.accessKey, minIOConfig.secretKey).WithSSL(minIOConfig.useSSL)
|
||||
.Build();
|
||||
|
||||
var putObjectArgs = new PutObjectArgs()
|
||||
.WithBucket(minIOConfig.bucketName)
|
||||
.WithObject(ossRelativePath)
|
||||
.WithStreamData(memoryStream)
|
||||
.WithObjectSize(memoryStream.Length);
|
||||
var putObjectArgs = new PutObjectArgs()
|
||||
.WithBucket(minIOConfig.bucketName)
|
||||
.WithObject(ossRelativePath)
|
||||
.WithStreamData(memoryStream)
|
||||
.WithObjectSize(memoryStream.Length);
|
||||
|
||||
await minioClient.PutObjectAsync(putObjectArgs);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new BusinessValidationFailedException("未定义的存储介质类型");
|
||||
await minioClient.PutObjectAsync(putObjectArgs);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new BusinessValidationFailedException("未定义的存储介质类型");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
throw new BusinessValidationFailedException($"上传发生异常:{ex.Message}");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
return "/" + ossRelativePath;
|
||||
@@ -205,11 +218,12 @@ namespace IRaCIS.Core.Application.Helper
|
||||
/// <param name="oosFolderPath"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="BusinessValidationFailedException"></exception>
|
||||
public async Task<string> UploadToOSSAsync(string localFilePath, string oosFolderPath)
|
||||
public async Task<string> UploadToOSSAsync(string localFilePath, string oosFolderPath, bool isFileNameAddGuid = true)
|
||||
{
|
||||
var localFileName = Path.GetFileName(localFilePath);
|
||||
|
||||
var ossRelativePath = $"{oosFolderPath}/{Guid.NewGuid()}_{localFileName}";
|
||||
var ossRelativePath = isFileNameAddGuid ? $"{oosFolderPath}/{Guid.NewGuid()}_{localFileName}" : $"{oosFolderPath}/{localFileName}";
|
||||
|
||||
|
||||
//var ossRelativePath = oosFolderPath + "/" + localFileName;
|
||||
|
||||
@@ -218,7 +232,7 @@ namespace IRaCIS.Core.Application.Helper
|
||||
{
|
||||
var aliConfig = ObjectStoreServiceOptions.AliyunOSS;
|
||||
|
||||
var _ossClient = new OssClient(aliConfig.endPoint, aliConfig.accessKeyId, aliConfig.accessKeySecret);
|
||||
var _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows)? aliConfig.endPoint: aliConfig.internalEndpoint, aliConfig.accessKeyId, aliConfig.accessKeySecret);
|
||||
|
||||
// 上传文件
|
||||
var result = _ossClient.PutObject(aliConfig.bucketName, ossRelativePath, localFilePath);
|
||||
@@ -264,6 +278,7 @@ namespace IRaCIS.Core.Application.Helper
|
||||
|
||||
}
|
||||
|
||||
|
||||
public async Task DownLoadFromOSSAsync(string ossRelativePath, string localFilePath)
|
||||
{
|
||||
|
||||
@@ -276,7 +291,7 @@ namespace IRaCIS.Core.Application.Helper
|
||||
{
|
||||
var aliConfig = ObjectStoreServiceOptions.AliyunOSS;
|
||||
|
||||
var _ossClient = new OssClient(aliConfig.endPoint, aliConfig.accessKeyId, aliConfig.accessKeySecret);
|
||||
var _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.endPoint : aliConfig.internalEndpoint, aliConfig.accessKeyId, aliConfig.accessKeySecret);
|
||||
|
||||
// 上传文件
|
||||
var result = _ossClient.GetObject(aliConfig.bucketName, ossRelativePath);
|
||||
@@ -337,9 +352,89 @@ namespace IRaCIS.Core.Application.Helper
|
||||
|
||||
}
|
||||
|
||||
public async Task<string> GetSignedUrl(string ossRelativePath)
|
||||
{
|
||||
ossRelativePath = ossRelativePath.TrimStart('/');
|
||||
try
|
||||
{
|
||||
|
||||
|
||||
if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS")
|
||||
{
|
||||
var aliConfig = ObjectStoreServiceOptions.AliyunOSS;
|
||||
|
||||
var _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.endPoint : aliConfig.internalEndpoint, aliConfig.accessKeyId, aliConfig.accessKeySecret);
|
||||
|
||||
// 生成签名URL。
|
||||
var req = new GeneratePresignedUriRequest(aliConfig.bucketName, ossRelativePath, SignHttpMethod.Get)
|
||||
{
|
||||
// 设置签名URL过期时间,默认值为3600秒。
|
||||
Expiration = DateTime.Now.AddHours(1),
|
||||
};
|
||||
var uri = _ossClient.GeneratePresignedUri(req);
|
||||
|
||||
return uri.PathAndQuery;
|
||||
|
||||
}
|
||||
else if (ObjectStoreServiceOptions.ObjectStoreUse == "MinIO")
|
||||
{
|
||||
var minIOConfig = ObjectStoreServiceOptions.MinIO;
|
||||
|
||||
var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.endPoint}:{minIOConfig.port}")
|
||||
.WithCredentials(minIOConfig.accessKey, minIOConfig.secretKey).WithSSL(minIOConfig.useSSL)
|
||||
.Build();
|
||||
|
||||
|
||||
var args = new PresignedGetObjectArgs()
|
||||
.WithBucket(minIOConfig.bucketName)
|
||||
.WithObject(ossRelativePath)
|
||||
.WithExpiry(3600)
|
||||
/*.WithHeaders(reqParams)*/;
|
||||
|
||||
var presignedUrl = await minioClient.PresignedGetObjectAsync(args);
|
||||
|
||||
Uri uri = new Uri(presignedUrl);
|
||||
|
||||
string relativePath = uri.PathAndQuery;
|
||||
|
||||
|
||||
return relativePath;
|
||||
|
||||
}
|
||||
else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS")
|
||||
{
|
||||
var minIOConfig = ObjectStoreServiceOptions.AWS;
|
||||
|
||||
var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.endPoint}")
|
||||
.WithCredentials(minIOConfig.accessKey, minIOConfig.secretKey).WithSSL(minIOConfig.useSSL)
|
||||
.Build();
|
||||
|
||||
var args = new PresignedGetObjectArgs()
|
||||
.WithBucket(minIOConfig.bucketName)
|
||||
.WithObject(ossRelativePath)
|
||||
.WithExpiry(3600);
|
||||
|
||||
var presignedUrl = await minioClient.PresignedGetObjectAsync(args);
|
||||
|
||||
Uri uri = new Uri(presignedUrl);
|
||||
|
||||
string relativePath = uri.PathAndQuery;
|
||||
|
||||
|
||||
return relativePath;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new BusinessValidationFailedException("未定义的存储介质类型");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
throw new BusinessValidationFailedException("oss授权url失败!" + ex.Message);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Org.BouncyCastle.Crypto;
|
||||
using Org.BouncyCastle.Crypto.Encodings;
|
||||
using Org.BouncyCastle.Crypto.Engines;
|
||||
using Org.BouncyCastle.Crypto.Generators;
|
||||
using Org.BouncyCastle.OpenSsl;
|
||||
using Org.BouncyCastle.Security;
|
||||
|
||||
namespace IRaCIS.Core.Application.Helper
|
||||
{
|
||||
/// <summary>
|
||||
/// https://www.cnblogs.com/NBDWDYS2214143926/p/13329231.html
|
||||
/// </summary>
|
||||
public class RSAHelper
|
||||
{
|
||||
|
||||
public static AsymmetricCipherKeyPair GenerateRSAKeyPair(int keySize)
|
||||
{
|
||||
var keyGenerationParameters = new KeyGenerationParameters(new SecureRandom(), keySize);
|
||||
var keyPairGenerator = new RsaKeyPairGenerator();
|
||||
keyPairGenerator.Init(keyGenerationParameters);
|
||||
return keyPairGenerator.GenerateKeyPair();
|
||||
}
|
||||
|
||||
public static string ExportPublicKey(AsymmetricKeyParameter publicKey)
|
||||
{
|
||||
using (StringWriter sw = new StringWriter())
|
||||
{
|
||||
PemWriter pw = new PemWriter(sw);
|
||||
pw.WriteObject(publicKey);
|
||||
pw.Writer.Flush();
|
||||
return sw.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
public static string ExportPrivateKey(AsymmetricKeyParameter privateKey)
|
||||
{
|
||||
using (StringWriter sw = new StringWriter())
|
||||
{
|
||||
PemWriter pw = new PemWriter(sw);
|
||||
pw.WriteObject(privateKey);
|
||||
pw.Writer.Flush();
|
||||
return sw.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// RSA解密
|
||||
/// </summary>
|
||||
/// <param name="privateKey">私钥</param>
|
||||
/// <param name="decryptstring">待解密的字符串(Base64)</param>
|
||||
/// <returns>解密后的字符串</returns>
|
||||
public static string Decrypt(string privateKey, string decryptstring)
|
||||
{
|
||||
using (TextReader reader = new StringReader(privateKey))
|
||||
{
|
||||
dynamic key = new PemReader(reader).ReadObject();
|
||||
var rsaDecrypt = new Pkcs1Encoding(new RsaEngine());
|
||||
if (key is AsymmetricKeyParameter)
|
||||
{
|
||||
key = (AsymmetricKeyParameter)key;
|
||||
}
|
||||
else if (key is AsymmetricCipherKeyPair)
|
||||
{
|
||||
key = ((AsymmetricCipherKeyPair)key).Private;
|
||||
}
|
||||
rsaDecrypt.Init(false, key); //这里加密是true;解密是false
|
||||
|
||||
byte[] entData = Convert.FromBase64String(decryptstring);
|
||||
entData = rsaDecrypt.ProcessBlock(entData, 0, entData.Length);
|
||||
return Encoding.UTF8.GetString(entData);
|
||||
}
|
||||
}/// <summary>
|
||||
|
||||
/// 加密
|
||||
/// </summary>
|
||||
/// <param name="publicKey">公钥</param>
|
||||
/// <param name="encryptstring">待加密的字符串</param>
|
||||
/// <returns>加密后的Base64</returns>
|
||||
public static string Encrypt(string publicKey, string encryptstring)
|
||||
{
|
||||
using (TextReader reader = new StringReader(publicKey))
|
||||
{
|
||||
AsymmetricKeyParameter key = new PemReader(reader).ReadObject() as AsymmetricKeyParameter;
|
||||
Pkcs1Encoding pkcs1 = new Pkcs1Encoding(new RsaEngine());
|
||||
pkcs1.Init(true, key);//加密是true;解密是false;
|
||||
byte[] entData = Encoding.UTF8.GetBytes(encryptstring);
|
||||
entData = pkcs1.ProcessBlock(entData, 0, entData.Length);
|
||||
return Convert.ToBase64String(entData);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,7 @@ public static class SendEmailHelper
|
||||
|
||||
//await smtp.AuthenticateAsync("zhou941003@qq.com", "sqfhlpfdvnexbcab");
|
||||
|
||||
await smtp.ConnectAsync(_systemEmailConfig.Host, _systemEmailConfig.Port, SecureSocketOptions.SslOnConnect);
|
||||
await smtp.ConnectAsync(_systemEmailConfig.Host, _systemEmailConfig.Port, SecureSocketOptions.Auto);
|
||||
|
||||
await smtp.AuthenticateAsync(_systemEmailConfig.FromEmail, _systemEmailConfig.AuthorizationCode);
|
||||
|
||||
@@ -40,11 +40,11 @@ public static class SendEmailHelper
|
||||
|
||||
}
|
||||
}
|
||||
catch (Exception )
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
//---邮件发送失败,您进行的操作未能成功,请检查邮箱或联系维护人员
|
||||
throw new Exception(StaticData.International("SendEmail_SendFail"));
|
||||
throw new Exception(StaticData.International("SendEmail_SendFail"),new Exception(ex.Message));
|
||||
}
|
||||
|
||||
|
||||
@@ -52,15 +52,19 @@ public static class SendEmailHelper
|
||||
|
||||
public static async Task<bool> TestEmailConfigAsync(SystemEmailSendConfig _systemEmailConfig)
|
||||
{
|
||||
using (var client = new MailKit.Net.Smtp.SmtpClient())
|
||||
using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5)))
|
||||
{
|
||||
using (var client = new MailKit.Net.Smtp.SmtpClient())
|
||||
{
|
||||
|
||||
await client.ConnectAsync(_systemEmailConfig.Host, _systemEmailConfig.Port, SecureSocketOptions.SslOnConnect);
|
||||
await client.ConnectAsync(_systemEmailConfig.Host, _systemEmailConfig.Port, SecureSocketOptions.Auto, cts.Token);
|
||||
|
||||
await client.AuthenticateAsync(_systemEmailConfig.FromEmail, _systemEmailConfig.AuthorizationCode);
|
||||
await client.AuthenticateAsync(_systemEmailConfig.FromEmail, _systemEmailConfig.AuthorizationCode, cts.Token);
|
||||
|
||||
await client.DisconnectAsync(true);
|
||||
await client.DisconnectAsync(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -133,7 +137,7 @@ public static class SendEmailHelper
|
||||
|
||||
smtp.ServerCertificateValidationCallback = (s, c, h, e) => true;
|
||||
|
||||
await smtp.ConnectAsync(sMTPEmailConfig.Host, sMTPEmailConfig.Port, SecureSocketOptions.SslOnConnect);
|
||||
await smtp.ConnectAsync(sMTPEmailConfig.Host, sMTPEmailConfig.Port, SecureSocketOptions.Auto);
|
||||
|
||||
await smtp.AuthenticateAsync(sMTPEmailConfig.UserName, sMTPEmailConfig.AuthorizationCode);
|
||||
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using NPOI.XWPF.UserModel;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Xceed.Document.NET;
|
||||
using Xceed.Words.NET;
|
||||
|
||||
namespace IRaCIS.Core.Application.Helper
|
||||
{
|
||||
/// <summary>
|
||||
/// 利用DocX 库 处理word国际化模板
|
||||
/// </summary>
|
||||
public static class WordTempleteHelper
|
||||
{
|
||||
public static void DocX_GetInternationalTempleteStream(string filePath, Stream memoryStream)
|
||||
{
|
||||
|
||||
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
|
||||
|
||||
using (DocX document = DocX.Load(filePath))
|
||||
{
|
||||
// 查找书签
|
||||
var bookmarkEn_Start = document.Bookmarks.FirstOrDefault(b => b.Name == StaticData.CultureInfo.en_US_bookMark);
|
||||
|
||||
if (bookmarkEn_Start != null)
|
||||
{
|
||||
// 获取书签的起始位置
|
||||
//int bookmarkCNStartPos = bookmarkCn_Start.Paragraph.StartIndex;
|
||||
|
||||
var bookmarkENStartPos = bookmarkEn_Start.Paragraph.StartIndex;
|
||||
|
||||
// 创建一个要删除段落的列表
|
||||
List<Paragraph> paragraphsToRemove = new List<Paragraph>();
|
||||
|
||||
foreach (var item in document.Paragraphs)
|
||||
{
|
||||
//中文模板在前,英文在后,英文模板,就删除英文之前的,中文模板就删除英文之后的
|
||||
|
||||
if (isEn_US ? item.EndIndex < bookmarkENStartPos : item.StartIndex >= bookmarkENStartPos)
|
||||
{
|
||||
paragraphsToRemove.Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var paragraph in paragraphsToRemove)
|
||||
{
|
||||
document.RemoveParagraph(paragraph);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 保存修改
|
||||
document.SaveAs(memoryStream);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void Npoi_GetInternationalTempleteStream(string filePath, Stream memoryStream)
|
||||
{
|
||||
|
||||
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
|
||||
|
||||
using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
|
||||
{
|
||||
XWPFDocument doc = new XWPFDocument(fs);
|
||||
|
||||
// 查找包含指定书签的段落及其索引
|
||||
var bookmarkParagraph = doc.Paragraphs
|
||||
.FirstOrDefault(p => p.GetCTP().GetBookmarkStartList().Any(b => b.name == StaticData.CultureInfo.en_US_bookMark));
|
||||
|
||||
if (bookmarkParagraph != null)
|
||||
{
|
||||
int bookmarkIndex = doc.Paragraphs.IndexOf(bookmarkParagraph);
|
||||
|
||||
if (isEn_US)
|
||||
{
|
||||
// 从书签所在段落开始,删除之前的所有段落
|
||||
for (int i = bookmarkIndex - 1; i >= 0; i--)
|
||||
{
|
||||
doc.RemoveBodyElement(i);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 删除书签之后的所有段落
|
||||
for (int i = doc.Paragraphs.Count - 1; i >= bookmarkIndex; i--)
|
||||
{
|
||||
doc.RemoveBodyElement(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
doc.Write(memoryStream);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -42,7 +42,7 @@
|
||||
<ItemGroup>
|
||||
<None Remove="IRaCIS.Core.Application.xml" />
|
||||
<None Remove="Resources\en-US.json" />
|
||||
<None Remove="Resources\zh-CN.json" />
|
||||
<None Remove="Resources\zh_ch.json" />
|
||||
<None Remove="Service\Allocation\TaskConsistentRuleService.cs~RF1603d47.TMP" />
|
||||
<None Remove="Service\Reading\ReadingImageTask\ReadingImageTaskService.cs~RF2f9323.TMP" />
|
||||
<None Remove="Service\Reading\ReadingImageTask\ReadingJudgeTaskService.cs~RF198afd23.TMP" />
|
||||
@@ -50,11 +50,6 @@
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="Resources\zh-CN.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
|
||||
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||
</Content>
|
||||
<Content Include="Resources\en-US.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
|
||||
@@ -63,46 +58,45 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Aliyun.OSS.SDK.NetCore" Version="2.13.0" />
|
||||
<PackageReference Include="FreeSpire.Doc" Version="11.6.0" />
|
||||
<PackageReference Include="Hangfire.Core" Version="1.8.7" />
|
||||
<PackageReference Include="BeetleX.BNR" Version="1.0.1" />
|
||||
<PackageReference Include="Aliyun.OSS.SDK.NetCore" Version="2.13.0" />
|
||||
<PackageReference Include="DocX" Version="3.0.1" />
|
||||
<PackageReference Include="FreeSpire.Doc" Version="12.2.0" />
|
||||
<PackageReference Include="Hangfire.Core" Version="1.8.14" />
|
||||
<PackageReference Include="Castle.Core.AsyncInterceptor" Version="2.1.0" />
|
||||
<PackageReference Include="EasyCaching.Redis" Version="1.9.2" />
|
||||
<PackageReference Include="EasyCaching.InMemory" Version="1.9.2" />
|
||||
<PackageReference Include="ExcelDataReader" Version="3.6.0" />
|
||||
<PackageReference Include="ExcelDataReader.DataSet" Version="3.6.0" />
|
||||
<PackageReference Include="DistributedLock.Redis" Version="1.0.2" />
|
||||
<PackageReference Include="DistributedLock.SqlServer" Version="1.0.3" />
|
||||
<PackageReference Include="fo-dicom" Version="5.1.2" />
|
||||
<PackageReference Include="fo-dicom.Imaging.ImageSharp" Version="5.1.2" />
|
||||
<PackageReference Include="fo-dicom.Codecs" Version="5.11.0" />
|
||||
<PackageReference Include="Magicodes.IE.Core" Version="2.7.4.5" />
|
||||
<PackageReference Include="Magicodes.IE.Csv" Version="2.7.4.5">
|
||||
<PackageReference Include="DistributedLock.Redis" Version="1.0.3" />
|
||||
<PackageReference Include="DistributedLock.SqlServer" Version="1.0.4" />
|
||||
<PackageReference Include="fo-dicom" Version="5.1.3" />
|
||||
<PackageReference Include="fo-dicom.Imaging.ImageSharp" Version="5.1.3" />
|
||||
<PackageReference Include="fo-dicom.Codecs" Version="5.14.4" />
|
||||
<PackageReference Include="IP2Region.Net" Version="2.0.2" />
|
||||
<PackageReference Include="MailKit" Version="4.2.0" />
|
||||
<PackageReference Include="MimeKit" Version="4.2.0" />
|
||||
<PackageReference Include="MiniExcel" Version="1.32.0" />
|
||||
<PackageReference Include="Minio" Version="6.0.3" />
|
||||
<PackageReference Include="MiniWord" Version="0.8.0" />
|
||||
<PackageReference Include="My.Extensions.Localization.Json" Version="3.3.0">
|
||||
<TreatAsUsed>true</TreatAsUsed>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Magicodes.IE.Excel" Version="2.7.4.5">
|
||||
<TreatAsUsed>true</TreatAsUsed>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Magicodes.IE.Excel.AspNetCore" Version="2.7.4.5" />
|
||||
<PackageReference Include="MailKit" Version="4.3.0" />
|
||||
<PackageReference Include="MediatR" Version="12.2.0" />
|
||||
<PackageReference Include="MimeKit" Version="4.3.0" />
|
||||
<PackageReference Include="MiniExcel" Version="1.31.3" />
|
||||
<PackageReference Include="Minio" Version="6.0.1" />
|
||||
<PackageReference Include="MiniWord" Version="0.7.0" />
|
||||
<PackageReference Include="My.Extensions.Localization.Json" Version="3.2.0">
|
||||
<TreatAsUsed>true</TreatAsUsed>
|
||||
</PackageReference>
|
||||
<PackageReference Include="NPOI" Version="2.6.2" />
|
||||
<PackageReference Include="Panda.DynamicWebApi" Version="1.2.1" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.2" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.Filters" Version="7.0.12" />
|
||||
<PackageReference Include="WinSCP" Version="6.1.2" />
|
||||
<PackageReference Include="NPOI" Version="2.7.1" />
|
||||
<PackageReference Include="Panda.DynamicWebApi" Version="1.2.2" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.5" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.Filters" Version="8.0.2" />
|
||||
<PackageReference Include="MassTransit.AspNetCore" Version="7.3.1" />
|
||||
<PackageReference Include="ZiggyCreatures.FusionCache" Version="1.3.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\IRaCIS.Core.Infra.EFCore\IRaCIS.Core.Infra.EFCore.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="Resources\ip2region.xdb">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,96 @@
|
||||
using MiniExcelLibs.Attributes;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace IRaCIS.Core.Application.MassTransit.Command
|
||||
{
|
||||
public record ConsistenCheckCommand
|
||||
{
|
||||
public List<CheckViewModel> ETCList { get; set; } = new List<CheckViewModel>();
|
||||
|
||||
public Guid TrialId { get; set; }
|
||||
}
|
||||
|
||||
public record ConsistenCheckResult
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class CheckDBModel : CheckViewModel
|
||||
{
|
||||
|
||||
public Guid SubjectVisitId { get; set; }
|
||||
|
||||
public Guid StudyId { get; set; }
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//[ExcelImporter(/*ImportResultFilter = typeof(ImportResultFilteTest),*/ IsLabelingError = true)]
|
||||
|
||||
public class CheckViewModel
|
||||
{
|
||||
//[Required(ErrorMessage = "中心编号不能为空")]
|
||||
//[ImporterHeader(Name = "Site ID", AutoTrim = true)]
|
||||
[ExcelColumnName("Site ID")]
|
||||
public string SiteCode { get; set; } = string.Empty;
|
||||
|
||||
|
||||
//[Required(ErrorMessage = "受试者筛选号不能为空")]
|
||||
//[ImporterHeader(Name = "Subject ID", AutoTrim = true)]
|
||||
[ExcelColumnName("Subject ID")]
|
||||
public string SubjectCode { get; set; } = string.Empty;
|
||||
|
||||
//[Required(ErrorMessage = "访视名称不能为空")]
|
||||
//[ImporterHeader(Name = "Visit Name", AutoTrim = true)]
|
||||
[ExcelColumnName("Visit Name")]
|
||||
public string VisitName { get; set; } = string.Empty;
|
||||
|
||||
|
||||
|
||||
//[Required(ErrorMessage = "检查日期不能为空")]
|
||||
[CanConvertToTime(ErrorMessage = "Does not conform to Study Date format")]
|
||||
|
||||
//[ImporterHeader(Name = "Study Date", AutoTrim = true)]
|
||||
[ExcelColumnName("Study Date")]
|
||||
public string StudyDate { get; set; } = string.Empty;
|
||||
|
||||
|
||||
|
||||
//[Required(ErrorMessage = "Modality不能为空")]
|
||||
//[ImporterHeader(Name = "Modality", AutoTrim = true)]
|
||||
[ExcelColumnName("Modality")]
|
||||
public string Modality { get; set; } = string.Empty;
|
||||
|
||||
|
||||
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
if (obj == null) return false;
|
||||
|
||||
var checkModel = obj as CheckViewModel;
|
||||
|
||||
if (checkModel is not null)
|
||||
{
|
||||
return SiteCode == checkModel.SiteCode && SubjectCode == checkModel.SubjectCode && VisitName == checkModel.VisitName && StudyDate == checkModel.StudyDate && Modality == checkModel.Modality;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return (SiteCode + SubjectCode + VisitName + StudyDate + Modality).GetHashCode();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
+30
-19
@@ -1,16 +1,22 @@
|
||||
using AutoMapper;
|
||||
using IRaCIS.Core.Application.MediatR.CommandAndQueries;
|
||||
using IRaCIS.Core.Application.Contracts.DTO;
|
||||
using IRaCIS.Core.Application.MassTransit.Command;
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using MassTransit;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Newtonsoft.Json;
|
||||
using MediatR;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Text;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace IRaCIS.Core.Application.MediatR.Handlers
|
||||
namespace IRaCIS.Core.Application.MassTransit.Consumer
|
||||
{
|
||||
public class ConsistencyVerificationHandler : IRequestHandler<ConsistencyVerificationRequest, string>
|
||||
public class ConsistencyCheckConsumer : IConsumer<ConsistenCheckCommand>
|
||||
{
|
||||
|
||||
private readonly IRepository<DicomStudy> _studyRepository;
|
||||
private readonly IUserInfo _userInfo;
|
||||
private readonly IRepository<Subject> _subjectRepository;
|
||||
@@ -24,7 +30,7 @@ namespace IRaCIS.Core.Application.MediatR.Handlers
|
||||
/// 构造函数注入
|
||||
/// </summary>
|
||||
|
||||
public ConsistencyVerificationHandler(IRepository<DicomStudy> studyRepository, IUserInfo userInfo,
|
||||
public ConsistencyCheckConsumer(IRepository<DicomStudy> studyRepository, IUserInfo userInfo,
|
||||
IRepository<Subject> subjectRepository, IRepository<SubjectVisit> subjectVisitRepository,
|
||||
IRepository<TrialSite> trialSiteRepository, IRepository<NoneDicomStudy> noneDicomStudyRepository,
|
||||
IMapper mapper, IStringLocalizer localizer)
|
||||
@@ -39,29 +45,31 @@ namespace IRaCIS.Core.Application.MediatR.Handlers
|
||||
_localizer = localizer;
|
||||
}
|
||||
|
||||
async Task<string> IRequestHandler<ConsistencyVerificationRequest, string>.Handle(ConsistencyVerificationRequest request, CancellationToken cancellationToken)
|
||||
|
||||
|
||||
public async Task Consume(ConsumeContext<ConsistenCheckCommand> context)
|
||||
{
|
||||
var trialId = request.TrialId;
|
||||
|
||||
var trialId = context.Message.TrialId;
|
||||
|
||||
//处理Excel大小写
|
||||
request.ETCList.ForEach(t => { t.Modality = t.Modality.ToUpper().Trim(); t.StudyDate = Convert.ToDateTime(t.StudyDate).ToString("yyyy-MM-dd"); t.SiteCode = t.SiteCode.ToUpper().Trim(); t.VisitName = t.VisitName.ToUpper().Trim(); t.SubjectCode = t.SubjectCode.ToUpper().Trim(); });
|
||||
var etcList = request.ETCList;
|
||||
context.Message.ETCList.ForEach(t => { t.Modality = t.Modality.ToUpper().Trim(); t.StudyDate = Convert.ToDateTime(t.StudyDate).ToString("yyyy-MM-dd"); t.SiteCode = t.SiteCode.ToUpper().Trim(); t.VisitName = t.VisitName.ToUpper().Trim(); t.SubjectCode = t.SubjectCode.ToUpper().Trim(); });
|
||||
var etcList = context.Message.ETCList;
|
||||
|
||||
//Expression<Func<SubjectVisit, bool>> subjectVisitLambda2 = x => x.TrialId == request.TrialId;
|
||||
|
||||
//subjectVisitLambda2= subjectVisitLambda2.And(x => x.CheckState == CheckStateEnum.ToCheck && x.AuditState == AuditStateEnum.QCPassed || (x.CheckState == CheckStateEnum.CVIng && x.AuditState == AuditStateEnum.QCPassed));
|
||||
|
||||
Expression<Func<SubjectVisit, bool>> subjectVisitLambda = x => x.TrialId == request.TrialId &&
|
||||
Expression<Func<SubjectVisit, bool>> subjectVisitLambda = x => x.TrialId == trialId &&
|
||||
(x.CheckState == CheckStateEnum.ToCheck && x.AuditState == AuditStateEnum.QCPassed || (x.CheckState == CheckStateEnum.CVIng && x.AuditState == AuditStateEnum.QCPassed));
|
||||
|
||||
var dicomQuery = from sv in _subjectVisitRepository.Where(subjectVisitLambda)
|
||||
join trialSite in _trialSiteRepository.Where(t => t.TrialId == trialId) on sv.SiteId equals trialSite.SiteId
|
||||
join subject in _subjectRepository.AsQueryable() on sv.SubjectId equals subject.Id
|
||||
join study in _studyRepository.AsQueryable() on sv.Id equals study.SubjectVisitId
|
||||
select new CheckDBModel()
|
||||
{
|
||||
SubjectVisitId = sv.Id,
|
||||
SiteCode = trialSite.TrialSiteCode,
|
||||
SiteCode = sv.TrialSite.TrialSiteCode,
|
||||
StudyDate = study.StudyTime == null ? string.Empty : ((DateTime)study.StudyTime).ToString("yyyy-MM-dd"),
|
||||
StudyId = study.Id,
|
||||
Modality = study.ModalityForEdit,
|
||||
@@ -70,13 +78,12 @@ namespace IRaCIS.Core.Application.MediatR.Handlers
|
||||
};
|
||||
|
||||
var noneDicomQuey = from sv in _subjectVisitRepository.Where(subjectVisitLambda)
|
||||
join trialSite in _trialSiteRepository.Where(t => t.TrialId == trialId) on sv.SiteId equals trialSite.SiteId
|
||||
join subject in _subjectRepository.AsQueryable() on sv.SubjectId equals subject.Id
|
||||
join noneDicomStudy in _noneDicomStudyRepository.AsQueryable() on sv.Id equals noneDicomStudy.SubjectVisitId
|
||||
select new CheckDBModel()
|
||||
{
|
||||
SubjectVisitId = sv.Id,
|
||||
SiteCode = trialSite.TrialSiteCode,
|
||||
SiteCode = sv.TrialSite.TrialSiteCode,
|
||||
StudyDate = noneDicomStudy.ImageDate.ToString("yyyy-MM-dd"),
|
||||
StudyId = noneDicomStudy.Id,
|
||||
Modality = noneDicomStudy.Modality,
|
||||
@@ -191,6 +198,8 @@ namespace IRaCIS.Core.Application.MediatR.Handlers
|
||||
dbSV.CheckState = CheckStateEnum.CVPassed;
|
||||
dbSV.CheckUserId = _userInfo.Id;
|
||||
dbSV.CheckPassedTime = DateTime.Now;
|
||||
dbSV.CheckChallengeState = CheckChanllengeTypeEnum.Closed;
|
||||
|
||||
//---核对EDC数据,完全一致
|
||||
dbSV.CheckResult = _localizer["ConsistencyVerification_EDCB"];
|
||||
//---自动核查通过
|
||||
@@ -213,7 +222,7 @@ namespace IRaCIS.Core.Application.MediatR.Handlers
|
||||
else
|
||||
{
|
||||
//"Problems are as follows:
|
||||
dialogMsg.AppendLine($"<br/><div style='color: #f00'>{_localizer["ConsistencyVerification_Prob"]}</div>");
|
||||
dialogMsg.AppendLine($"<br/><div style='color: red'>{_localizer["ConsistencyVerification_Prob"]}</div>");
|
||||
|
||||
num = 0;
|
||||
foreach (var item in dbExceptExcel)
|
||||
@@ -274,11 +283,13 @@ namespace IRaCIS.Core.Application.MediatR.Handlers
|
||||
|
||||
}
|
||||
await _subjectVisitRepository.SaveChangesAsync();
|
||||
return "OK";
|
||||
|
||||
//await context.RespondAsync<ConsistenCheckResult>(new
|
||||
//{
|
||||
|
||||
//});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Binary file not shown.
@@ -1,37 +0,0 @@
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace IRaCIS.Core.Application.Service.ViewModel
|
||||
{
|
||||
public class AllocationVisitViewModel
|
||||
{
|
||||
|
||||
public Guid SubjectVisitId { get; set; }
|
||||
public Guid SubjectId { get; set; }
|
||||
|
||||
public string BlindName { get; set; } = string.Empty;
|
||||
|
||||
public decimal VisitNum { get; set; }
|
||||
|
||||
|
||||
public Guid SiteId { get; set; }
|
||||
public String TrialSiteCode { get; set; } = String.Empty;
|
||||
public string SubjectCode { get; set; } = String.Empty;
|
||||
|
||||
public string VisitName { get; set; } = string.Empty;
|
||||
|
||||
public DateTime? CheckPassedTime { get; set; }
|
||||
|
||||
public bool IsUrgent { get; set; }
|
||||
|
||||
|
||||
public ReadingCategory ReadingCategoryEnum { get; set; }
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,9 @@ namespace IRaCIS.Core.Application.ViewModel
|
||||
|
||||
public int? GeneratedSubjectCount { get; set; }
|
||||
|
||||
public int? MatchSubejctCount { get; set; }
|
||||
|
||||
public int? CanGeneratedSubejctCount => MatchSubejctCount - GeneratedSubjectCount;
|
||||
|
||||
}
|
||||
|
||||
@@ -34,6 +36,13 @@ namespace IRaCIS.Core.Application.ViewModel
|
||||
|
||||
}
|
||||
|
||||
public class SubjectGeneratedTask
|
||||
{
|
||||
public Guid SubjectId { get; set; }
|
||||
|
||||
public bool IsHaveGeneratedTask { get; set; }
|
||||
}
|
||||
|
||||
public class ConsistentQuery : PageInput
|
||||
{
|
||||
[NotDefault]
|
||||
@@ -46,6 +55,15 @@ namespace IRaCIS.Core.Application.ViewModel
|
||||
|
||||
}
|
||||
|
||||
public class GroupConsistentSimpleQuery
|
||||
{
|
||||
[NotDefault]
|
||||
public Guid TrialId { get; set; }
|
||||
|
||||
[NotDefault]
|
||||
|
||||
public Guid TrialReadingCriterionId { get; set; }
|
||||
}
|
||||
public class GroupConsistentQuery : PageInput
|
||||
{
|
||||
[NotDefault]
|
||||
@@ -56,13 +74,21 @@ namespace IRaCIS.Core.Application.ViewModel
|
||||
public Guid TrialReadingCriterionId { get; set; }
|
||||
}
|
||||
|
||||
public class ConsistentConfirmGenerateCommand
|
||||
public class SelfConsistentSimpleQuery
|
||||
{
|
||||
[NotDefault]
|
||||
public Guid TaskConsistentRuleId { get; set; }
|
||||
[NotDefault]
|
||||
public Guid DoctorUserId { get; set; }
|
||||
}
|
||||
|
||||
public class ConsistentConfirmGenerateCommand: SelfConsistentSimpleQuery
|
||||
{
|
||||
|
||||
|
||||
public List<Guid> SubejctIdList { get; set; }
|
||||
|
||||
public bool IsAutoAllocateGenerateTask { get; set; }
|
||||
}
|
||||
|
||||
|
||||
@@ -70,12 +96,18 @@ namespace IRaCIS.Core.Application.ViewModel
|
||||
{
|
||||
[NotDefault]
|
||||
public Guid TrialId { get; set; }
|
||||
|
||||
[NotDefault]
|
||||
|
||||
public Guid TrialReadingCriterionId { get; set; }
|
||||
public List<Guid> SubejctIdList { get; set; }
|
||||
|
||||
public bool IsAutoAllocateGenerateTask { get; set; }
|
||||
}
|
||||
|
||||
public class DoctorSelfConsistentSubjectView: ConsistentCommonView
|
||||
{
|
||||
|
||||
public string FirstGlobalVisitName { get; set; } = string.Empty;
|
||||
public string? BlindSubjectCode { get; set; }
|
||||
|
||||
public List<VisitTaskSimpleDTO> VisitTaskList { get; set; }
|
||||
@@ -86,7 +118,7 @@ namespace IRaCIS.Core.Application.ViewModel
|
||||
public class ConsistentCommonView
|
||||
{
|
||||
public Guid TrialId { get; set; }
|
||||
public Guid SiteId { get; set; }
|
||||
public Guid TrialSiteId { get; set; }
|
||||
|
||||
public String TrialSiteCode { get; set; }
|
||||
public string SubjectCode { get; set; }
|
||||
@@ -105,6 +137,8 @@ namespace IRaCIS.Core.Application.ViewModel
|
||||
public List<VisitTaskGroupSimpleDTO> SubjectTaskVisitList => VisitTaskList.GroupBy(t => new { t.SubjectId, t.VisitTaskNum }).Where(g => g.Count() == 2).Select(g => g.First()).OrderBy(t=>t.VisitTaskNum).ToList();
|
||||
|
||||
public List<VisitTaskGroupSimpleDTO> VisitTaskList { get; set; } = new List<VisitTaskGroupSimpleDTO>();
|
||||
|
||||
public List<UserSimpleInfo> DoctorUserList { get; set; }=new List<UserSimpleInfo>();
|
||||
}
|
||||
|
||||
public class VisitTaskGroupSimpleDTO
|
||||
@@ -149,7 +183,8 @@ namespace IRaCIS.Core.Application.ViewModel
|
||||
|
||||
public string TaskCode { get; set; }
|
||||
|
||||
|
||||
|
||||
[JsonIgnore]
|
||||
//可以不查询
|
||||
public List<VisitTaskSimpleDTO> GlobalVisitTaskList { get; set; }
|
||||
|
||||
@@ -166,31 +201,12 @@ namespace IRaCIS.Core.Application.ViewModel
|
||||
}
|
||||
|
||||
|
||||
//public class DoctorConsistentRuleSubjectView
|
||||
//{
|
||||
// public Guid TrialId => VisitTaskList.Select(t => t.TrialId).First();
|
||||
// public Guid SiteId => VisitTaskList.Select(t => t.SiteId).First();
|
||||
|
||||
// public String TrialSiteCode => VisitTaskList.Select(t => t.TrialSiteCode).First();
|
||||
// public string SubjectCode => VisitTaskList.Select(t => t.SubjectCode).First();
|
||||
|
||||
// public Guid SubjectId { get; set; }
|
||||
|
||||
// public bool IsHaveGeneratedTask { get; set; }
|
||||
// public int? ValidTaskCount { get; set; }
|
||||
|
||||
// public int? ValidVisitCount => VisitTaskList.Select(t => t.TaskName).Distinct().Count();
|
||||
|
||||
// public List<VisitTaskSimpleView> VisitTaskList { get; set; }
|
||||
|
||||
// //public List<UserSimpleInfo> HistoryDoctorUserList => VisitTaskList.SelectMany(t => t.RelationDoctorUserList).DistinctBy(t=>t.UserId).ToList();
|
||||
//}
|
||||
|
||||
public class InfluenceTaskInfo
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public Guid TrialId { get; set; }
|
||||
public Guid SiteId { get; set; }
|
||||
public Guid TrialSiteId { get; set; }
|
||||
|
||||
public Guid SubjectId { get; set; }
|
||||
|
||||
@@ -233,7 +249,7 @@ namespace IRaCIS.Core.Application.ViewModel
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public Guid TrialId { get; set; }
|
||||
public Guid SiteId { get; set; }
|
||||
public Guid TrialSiteId { get; set; }
|
||||
|
||||
public Guid SubjectId { get; set; }
|
||||
|
||||
@@ -287,6 +303,17 @@ namespace IRaCIS.Core.Application.ViewModel
|
||||
|
||||
|
||||
}
|
||||
public class UpdateTrialSiteCodeCommand
|
||||
{
|
||||
public Guid TrialId { get; set; }
|
||||
|
||||
public string VirtualSiteCode { get; set; }
|
||||
}
|
||||
|
||||
public class UpdateTrialSiteCodeCommandView: UpdateTrialSiteCodeCommand
|
||||
{
|
||||
public DateTime Creatime { get; set; }
|
||||
}
|
||||
|
||||
///<summary> TaskConsistentRuleAddOrEdit 列表查询参数模型</summary>
|
||||
public class TaskConsistentRuleAddOrEdit
|
||||
|
||||
@@ -15,7 +15,6 @@ namespace IRaCIS.Core.Application.ViewModel
|
||||
{
|
||||
|
||||
public Guid VisitTaskId { get; set; }
|
||||
public string MedicalNo { get; set; } = string.Empty;
|
||||
|
||||
public int AuditState { get; set; }
|
||||
public DateTime? AuditSignTime { get; set; }
|
||||
@@ -54,7 +53,6 @@ namespace IRaCIS.Core.Application.ViewModel
|
||||
//开始读片时间
|
||||
public DateTime? FirstReadingTime { get; set; }
|
||||
|
||||
public string MedicalNo { get; set; } = string.Empty;
|
||||
|
||||
//读片时间差
|
||||
public TimeSpan? ReadingDurationTimeSpan { get; set; }
|
||||
@@ -71,7 +69,7 @@ namespace IRaCIS.Core.Application.ViewModel
|
||||
{
|
||||
if (!ReadingDurationTimeSpan.HasValue)
|
||||
return "";
|
||||
else return string.Format("{0}d {1}h {2}min", (SignTime - FirstReadingTime)?.Days, (SignTime - FirstReadingTime)?.Hours, (SignTime - FirstReadingTime)?.Minutes)
|
||||
else return string.Format("{0}h:{1}m:{2}s", (SignTime - FirstReadingTime)?.Hours, (SignTime - FirstReadingTime)?.Minutes, (SignTime - FirstReadingTime)?.Seconds)
|
||||
/*string.Format("{0}分钟", (ReadingDurationTimeSpan)?.TotalMinutes)*/;
|
||||
}
|
||||
}
|
||||
@@ -95,10 +93,10 @@ namespace IRaCIS.Core.Application.ViewModel
|
||||
public class TaskMedicalReviewQuery : PageInput
|
||||
{
|
||||
public Guid TrialId { get; set; }
|
||||
public Guid? SiteId { get; set; }
|
||||
public Guid? TrialSiteId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 传了Id 就不查询这条数据
|
||||
/// 传了Id
|
||||
/// </summary>
|
||||
public Guid? Id { get; set; }
|
||||
|
||||
@@ -113,7 +111,9 @@ namespace IRaCIS.Core.Application.ViewModel
|
||||
|
||||
public bool IsGetBeRead { get; set; } = false;
|
||||
|
||||
public string SubjectCode { get; set; } = String.Empty;
|
||||
public bool IsGetNextMedicalReviewTask { get; set; } = false;
|
||||
|
||||
public string SubjectCode { get; set; } = String.Empty;
|
||||
|
||||
public string TrialSiteCode { get; set; } = String.Empty;
|
||||
public string TaskName { get; set; } = String.Empty;
|
||||
@@ -134,7 +134,19 @@ namespace IRaCIS.Core.Application.ViewModel
|
||||
public Guid? TrialReadingCriterionId { get; set; }
|
||||
|
||||
public MedicalReviewDoctorUserIdea? DoctorUserIdeaEnum { get; set; }
|
||||
}
|
||||
|
||||
public Arm? ArmEnum { get; set; }
|
||||
|
||||
public DateTime? BeginAuditSignTime { get; set; }
|
||||
public DateTime? EndAuditSignTime { get; set; }
|
||||
|
||||
public bool? IsEffect { get; set; }
|
||||
public bool? IsHaveQuestion { get; set; }
|
||||
|
||||
|
||||
public DateTime? BeginAllocateDate { get; set; }
|
||||
public DateTime? EndAllocateDate { get; set; }
|
||||
}
|
||||
|
||||
|
||||
public class SetMedicalReviewInvalidCommand
|
||||
|
||||
@@ -54,10 +54,16 @@ namespace IRaCIS.Core.Application.ViewModel
|
||||
public Guid UpdateUserId { get; set; }
|
||||
|
||||
|
||||
public Guid SiteId { get; set; }
|
||||
public Guid TrialSiteId { get; set; }
|
||||
|
||||
public string BlindTrialSiteCode { get; set; }
|
||||
public String TrialSiteCode { get; set; } = String.Empty;
|
||||
public string SubjectCode { get; set; } = String.Empty;
|
||||
|
||||
public string MedicalNo { get; set; } = string.Empty;
|
||||
|
||||
public string BlindSubjectCode { get; set; } = string.Empty;
|
||||
|
||||
public bool IsPMSetBack { get; set; }
|
||||
|
||||
#region 标准配置
|
||||
@@ -86,7 +92,7 @@ namespace IRaCIS.Core.Application.ViewModel
|
||||
/// <summary>
|
||||
/// 任务展示访视 读片任务显示是否顺序
|
||||
/// </summary>
|
||||
public bool IsReadingTaskViewInOrder { get; set; } = true;
|
||||
public ReadingOrder IsReadingTaskViewInOrder { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
@@ -150,7 +156,18 @@ namespace IRaCIS.Core.Application.ViewModel
|
||||
|
||||
public bool IsCanEditUrgentState { get; set; }
|
||||
|
||||
public DateTime? FirstReadingTime { get;set; }
|
||||
|
||||
public string ReadingDuration
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!SignTime.HasValue)
|
||||
return "";
|
||||
else return string.Format("{0}h:{1}m:{2}s", (SignTime - FirstReadingTime)?.Hours, (SignTime - FirstReadingTime)?.Minutes, (SignTime - FirstReadingTime)?.Seconds)
|
||||
/*string.Format("{0}分钟", (ReadingDurationTimeSpan)?.TotalMinutes)*/;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class UserSimpleInfo
|
||||
@@ -184,6 +201,8 @@ namespace IRaCIS.Core.Application.ViewModel
|
||||
public Guid? SourceSubjectVisitId { get; set; }
|
||||
public Guid? SouceReadModuleId { get; set; }
|
||||
|
||||
|
||||
public bool IsHaveFeedBack { get; set; }
|
||||
//public bool IsAfterConvertedTask { get; set; }
|
||||
}
|
||||
|
||||
@@ -192,7 +211,9 @@ namespace IRaCIS.Core.Application.ViewModel
|
||||
{
|
||||
public bool? IsSelfAnalysis { get; set; }
|
||||
|
||||
public bool IsReReadingOrBackInfluenceAnalysis { get; set; }
|
||||
public int ConsistentClinicalDataCount { get; set; } = 0;
|
||||
|
||||
public bool IsReReadingOrBackInfluenceAnalysis { get; set; }
|
||||
}
|
||||
|
||||
|
||||
@@ -353,16 +374,16 @@ namespace IRaCIS.Core.Application.ViewModel
|
||||
}
|
||||
|
||||
|
||||
public class GetOrderReadingIQueryableInDto
|
||||
public class GetReadingIQueryableInDto:PageInput
|
||||
{
|
||||
public Guid TrialId { get; set; }
|
||||
|
||||
public Guid TrialReadingCriterionId { get; set; }
|
||||
|
||||
public Guid? SubjectId { get; set; }
|
||||
|
||||
public Guid TrialReadingCriterionId { get; set; }
|
||||
|
||||
public string? SubjectCode { get; set; } = null;
|
||||
|
||||
public PageInput? Page { get; set; } = null;
|
||||
|
||||
}
|
||||
|
||||
public class VisitTaskQuery : PageInput
|
||||
@@ -371,7 +392,7 @@ namespace IRaCIS.Core.Application.ViewModel
|
||||
public Guid TrialId { get; set; }
|
||||
|
||||
|
||||
public Guid? SiteId { get; set; }
|
||||
public Guid? TrialSiteId { get; set; }
|
||||
|
||||
public Guid? SubjectId { get; set; }
|
||||
|
||||
@@ -393,11 +414,16 @@ namespace IRaCIS.Core.Application.ViewModel
|
||||
|
||||
public DateTime? EndAllocateDate { get; set; }
|
||||
|
||||
public DateTime? BeginSignTime { get; set; }
|
||||
|
||||
|
||||
public DateTime? BeginSignTime { get; set; }
|
||||
|
||||
public DateTime? EndSignTime { get; set; }
|
||||
|
||||
public Guid? RootReReadingTaskId { get; set; }
|
||||
public DateTime? BeginRequestReReadingTime { get; set; }
|
||||
public DateTime? EndRequestReReadingTime { get; set; }
|
||||
|
||||
public Guid? RootReReadingTaskId { get; set; }
|
||||
|
||||
public string? TaskCode { get; set; }
|
||||
|
||||
@@ -418,6 +444,10 @@ namespace IRaCIS.Core.Application.ViewModel
|
||||
|
||||
public RequestReReadingResult? RequestReReadingResultEnum { get; set; }
|
||||
|
||||
|
||||
public bool? IsEffect { get; set; }
|
||||
|
||||
public string? RequestReReadingReason { get; set; }
|
||||
}
|
||||
|
||||
|
||||
@@ -473,7 +503,7 @@ namespace IRaCIS.Core.Application.ViewModel
|
||||
[NotDefault]
|
||||
public Guid TrialId { get; set; }
|
||||
|
||||
public Guid? SiteId { get; set; }
|
||||
public Guid? TrialSiteId { get; set; }
|
||||
|
||||
public Guid? SubjectId { get; set; }
|
||||
|
||||
@@ -495,7 +525,7 @@ namespace IRaCIS.Core.Application.ViewModel
|
||||
[NotDefault]
|
||||
public Guid TrialId { get; set; }
|
||||
|
||||
public Guid? SiteId { get; set; }
|
||||
public Guid? TrialSiteId { get; set; }
|
||||
|
||||
public Guid? SubjectId { get; set; }
|
||||
|
||||
@@ -504,6 +534,9 @@ namespace IRaCIS.Core.Application.ViewModel
|
||||
|
||||
public string SubjectCode { get; set; } = String.Empty;
|
||||
|
||||
public int? SubjectAllocateState { get; set; }
|
||||
|
||||
public List<Arm> ArmList { get; set; }=new List<Arm>() { };
|
||||
|
||||
[NotDefault]
|
||||
public Guid TrialReadingCriterionId { get; set; }
|
||||
@@ -535,7 +568,7 @@ namespace IRaCIS.Core.Application.ViewModel
|
||||
public class SubjectAssignStat
|
||||
{
|
||||
public Guid TrialId { get; set; }
|
||||
public Guid SiteId { get; set; }
|
||||
public Guid TrialSiteId { get; set; }
|
||||
|
||||
public Guid SubjectId { get; set; }
|
||||
|
||||
@@ -615,7 +648,7 @@ namespace IRaCIS.Core.Application.ViewModel
|
||||
public class SubjectAssignView
|
||||
{
|
||||
public Guid TrialId { get; set; }
|
||||
public Guid SiteId { get; set; }
|
||||
public Guid TrialSiteId { get; set; }
|
||||
|
||||
public Guid SubjectId { get; set; }
|
||||
|
||||
|
||||
@@ -16,6 +16,6 @@ namespace IRaCIS.Core.Application.Service
|
||||
|
||||
Task<IResponseOutput> ConfirmReReading(ConfirmReReadingCommand agreeReReadingCommand, [FromServices] IVisitTaskHelpeService _visitTaskCommonService);
|
||||
|
||||
Task<(int, List<IRUnReadSubjectView>)> GetOrderReadingIQueryable(GetOrderReadingIQueryableInDto inDto);
|
||||
Task<PageOutput<IRUnReadSubjectView>> GetSubjectReadingIQueryable(GetReadingIQueryableInDto inDto);
|
||||
}
|
||||
}
|
||||
@@ -116,9 +116,9 @@ namespace IRaCIS.Core.Application.Service
|
||||
// return ResponseOutput.Ok();
|
||||
//}
|
||||
|
||||
public async Task<List<SubjectCancelDoctorView>> GetSubjectCancelDoctorHistoryList(Guid subjectId)
|
||||
public async Task<List<SubjectCancelDoctorView>> GetSubjectCancelDoctorHistoryList(Guid subjectId,Guid trialReadingCriterionId)
|
||||
{
|
||||
var list = await _subjectCanceDoctorRepository.Where(t => t.SubjectId == subjectId).ProjectTo<SubjectCancelDoctorView>(_mapper.ConfigurationProvider).ToListAsync();
|
||||
var list = await _subjectCanceDoctorRepository.Where(t => t.SubjectId == subjectId && t.TrialReadingCriterionId==trialReadingCriterionId).ProjectTo<SubjectCancelDoctorView>(_mapper.ConfigurationProvider).ToListAsync();
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
@@ -16,6 +16,11 @@ using System.Linq;
|
||||
using IRaCIS.Core.Application.Contracts;
|
||||
using IRaCIS.Core.Application.Filter;
|
||||
using Medallion.Threading;
|
||||
using IRaCIS.Core.Infrastructure.Extention;
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using NPOI.SS.Formula.Functions;
|
||||
using IRaCIS.Core.Application.Service.Reading.Dto;
|
||||
|
||||
namespace IRaCIS.Core.Application.Service
|
||||
{
|
||||
@@ -28,20 +33,42 @@ namespace IRaCIS.Core.Application.Service
|
||||
|
||||
private readonly IRepository<TaskConsistentRule> _taskConsistentRuleRepository;
|
||||
private readonly IRepository<VisitTask> _visitTaskRepository;
|
||||
private readonly IRepository<ReadingConsistentClinicalData> _readingConsistentClinicalDataRepository;
|
||||
private readonly IReadingClinicalDataService _readingClinicalDataService;
|
||||
private readonly IRepository<SubjectUser> _subjectUserRepository;
|
||||
private readonly IRepository<Subject> _subjectRepository;
|
||||
private readonly IRepository<Enroll> _enrollRepository;
|
||||
private readonly IDistributedLockProvider _distributedLockProvider;
|
||||
private readonly IRepository<Trial> _trialRepository;
|
||||
private readonly IRepository<TrialSite> _trialSiteRepository;
|
||||
|
||||
private readonly IRepository<TrialVirtualSiteCodeUpdate> _trialVirtualSiteCodeUpdateRepository;
|
||||
|
||||
private readonly IVisitTaskHelpeService _visitTaskCommonService;
|
||||
|
||||
|
||||
public TaskConsistentRuleService(IRepository<VisitTask> visitTaskRepository, IRepository<Enroll> enrollRepository, IRepository<TaskConsistentRule> taskConsistentRuleRepository, IRepository<SubjectUser> subjectUserRepository, IRepository<Subject> subjectRepository, IDistributedLockProvider distributedLockProvider)
|
||||
public TaskConsistentRuleService(IRepository<VisitTask> visitTaskRepository,
|
||||
IRepository<ReadingConsistentClinicalData> readingConsistentClinicalDataRepository,
|
||||
IReadingClinicalDataService readingClinicalDataService,
|
||||
IRepository<Enroll> enrollRepository, IRepository<TaskConsistentRule> taskConsistentRuleRepository, IRepository<SubjectUser> subjectUserRepository, IRepository<Subject> subjectRepository, IDistributedLockProvider distributedLockProvider,
|
||||
IRepository<Trial> trialRepository, IRepository<TrialSite> trialSiteRepository, IRepository<TrialVirtualSiteCodeUpdate> trialVirtualSiteCodeUpdateRepository, IVisitTaskHelpeService visitTaskCommonService)
|
||||
{
|
||||
_taskConsistentRuleRepository = taskConsistentRuleRepository;
|
||||
_visitTaskRepository = visitTaskRepository;
|
||||
this._readingConsistentClinicalDataRepository = readingConsistentClinicalDataRepository;
|
||||
this._readingClinicalDataService = readingClinicalDataService;
|
||||
_subjectUserRepository = subjectUserRepository;
|
||||
_subjectRepository = subjectRepository;
|
||||
_enrollRepository = enrollRepository;
|
||||
_distributedLockProvider = distributedLockProvider;
|
||||
|
||||
_trialRepository = trialRepository;
|
||||
|
||||
_trialSiteRepository = trialSiteRepository;
|
||||
|
||||
_trialVirtualSiteCodeUpdateRepository = trialVirtualSiteCodeUpdateRepository;
|
||||
|
||||
_visitTaskCommonService = visitTaskCommonService;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -54,7 +81,7 @@ namespace IRaCIS.Core.Application.Service
|
||||
{
|
||||
|
||||
|
||||
await _visitTaskRepository.UpdatePartialFromQueryAsync(t => taskIdList.Contains(t.Id), u => new VisitTask() { TaskState = TaskState.NotEffect },true);
|
||||
await _visitTaskRepository.UpdatePartialFromQueryAsync(t => taskIdList.Contains(t.Id), u => new VisitTask() { TaskState = TaskState.Adbandon }, true);
|
||||
|
||||
await _visitTaskRepository.SaveChangesAsync();
|
||||
|
||||
@@ -69,14 +96,14 @@ namespace IRaCIS.Core.Application.Service
|
||||
/// <param name="queryVisitTask"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost]
|
||||
public async Task<PageOutput<AnalysisTaskView>> GetAnalysisTaskList(VisitTaskQuery queryVisitTask)
|
||||
public async Task<IResponseOutput<PageOutput<AnalysisTaskView>>> GetAnalysisTaskList(VisitTaskQuery queryVisitTask)
|
||||
{
|
||||
var visitTaskQueryable = _visitTaskRepository.Where(t => t.TrialId == queryVisitTask.TrialId)
|
||||
.Where(t => t.IsAnalysisCreate)
|
||||
|
||||
.WhereIf(queryVisitTask.TrialReadingCriterionId != null, t => t.TrialReadingCriterionId == queryVisitTask.TrialReadingCriterionId)
|
||||
|
||||
.WhereIf(queryVisitTask.SiteId != null, t => t.Subject.SiteId == queryVisitTask.SiteId)
|
||||
.WhereIf(queryVisitTask.TrialSiteId != null, t => t.Subject.TrialSiteId == queryVisitTask.TrialSiteId)
|
||||
.WhereIf(queryVisitTask.SubjectId != null, t => t.SubjectId == queryVisitTask.SubjectId)
|
||||
.WhereIf(queryVisitTask.TaskState != null, t => t.TaskState == queryVisitTask.TaskState)
|
||||
.WhereIf(queryVisitTask.IsUrgent != null, t => t.IsUrgent == queryVisitTask.IsUrgent)
|
||||
@@ -86,19 +113,36 @@ namespace IRaCIS.Core.Application.Service
|
||||
.WhereIf(queryVisitTask.TaskAllocationState != null, t => t.TaskAllocationState == queryVisitTask.TaskAllocationState)
|
||||
.WhereIf(queryVisitTask.IsSelfAnalysis != null, t => t.IsSelfAnalysis == queryVisitTask.IsSelfAnalysis)
|
||||
.WhereIf(queryVisitTask.ArmEnum != null, t => t.ArmEnum == queryVisitTask.ArmEnum)
|
||||
.WhereIf(!string.IsNullOrEmpty(queryVisitTask.TrialSiteCode), t => (t.BlindTrialSiteCode.Contains(queryVisitTask.TrialSiteCode!) && t.IsAnalysisCreate) || (t.Subject.TrialSite.TrialSiteCode.Contains(queryVisitTask.TrialSiteCode!) && t.IsAnalysisCreate == false))
|
||||
.WhereIf(!string.IsNullOrEmpty(queryVisitTask.TrialSiteCode), t => /*(t.BlindTrialSiteCode.Contains(queryVisitTask.TrialSiteCode!) && t.IsAnalysisCreate) ||*/ (t.Subject.TrialSite.TrialSiteCode.Contains(queryVisitTask.TrialSiteCode!) /*&& t.IsAnalysisCreate == false*/))
|
||||
.WhereIf(!string.IsNullOrEmpty(queryVisitTask.TaskName), t => t.TaskName.Contains(queryVisitTask.TaskName) || t.TaskBlindName.Contains(queryVisitTask.TaskName))
|
||||
.WhereIf(!string.IsNullOrEmpty(queryVisitTask.SubjectCode), t => (t.Subject.Code.Contains(queryVisitTask.SubjectCode) && t.IsAnalysisCreate == false) || (t.BlindSubjectCode.Contains(queryVisitTask.SubjectCode) && t.IsAnalysisCreate))
|
||||
.WhereIf(queryVisitTask.BeginAllocateDate != null, t => t.AllocateTime > queryVisitTask.BeginAllocateDate)
|
||||
.WhereIf(queryVisitTask.EndAllocateDate != null, t => t.AllocateTime < queryVisitTask.EndAllocateDate!.Value.AddDays(1))
|
||||
.WhereIf(!string.IsNullOrEmpty(queryVisitTask.SubjectCode), t => ((t.Subject.Code.Contains(queryVisitTask.SubjectCode) || t.Subject.MedicalNo.Contains(queryVisitTask.SubjectCode)) && t.IsAnalysisCreate == false) || (t.BlindSubjectCode.Contains(queryVisitTask.SubjectCode) && t.IsAnalysisCreate))
|
||||
.WhereIf(queryVisitTask.BeginAllocateDate != null, t => t.AllocateTime >= queryVisitTask.BeginAllocateDate)
|
||||
.WhereIf(queryVisitTask.EndAllocateDate != null, t => t.AllocateTime <= queryVisitTask.EndAllocateDate)
|
||||
.WhereIf(queryVisitTask.BeginSignTime != null, t => t.SignTime >= queryVisitTask.BeginSignTime)
|
||||
.WhereIf(queryVisitTask.EndSignTime != null, t => t.SignTime <= queryVisitTask.EndSignTime)
|
||||
.ProjectTo<AnalysisTaskView>(_mapper.ConfigurationProvider);
|
||||
|
||||
var defalutSortArray = new string[] { nameof(VisitTask.IsUrgent) + " desc", nameof(VisitTask.SubjectId), nameof(VisitTask.VisitTaskNum) };
|
||||
|
||||
var pageList = await visitTaskQueryable.ToPagedListAsync(queryVisitTask.PageIndex, queryVisitTask.PageSize, queryVisitTask.SortField, queryVisitTask.Asc, string.IsNullOrWhiteSpace(queryVisitTask.SortField), defalutSortArray);
|
||||
#region 统计一致性分析临床数据数量
|
||||
|
||||
//var trialTaskConfig = _repository.Where<Trial>(t => t.Id == queryVisitTask.TrialId).ProjectTo<TrialTaskConfigView>(_mapper.ConfigurationProvider).FirstOrDefault();
|
||||
return pageList;
|
||||
foreach (var item in pageList.CurrentPageData)
|
||||
{
|
||||
item.ConsistentClinicalDataCount = (await _readingClinicalDataService.GetClinicalDataList(new GetReadingOrTaskClinicalDataListInDto()
|
||||
{
|
||||
SubjectId = item.SubjectId,
|
||||
TrialId = item.TrialId,
|
||||
SelectIsSign = false,
|
||||
IsGetAllConsistencyAnalysis = false,
|
||||
VisitTaskId = item.Id,
|
||||
})).Count();
|
||||
}
|
||||
#endregion
|
||||
|
||||
var trialTaskConfig = _repository.Where<Trial>(t => t.Id == queryVisitTask.TrialId).Select(t => new { IsHaveDoubleReadCriterion = t.TrialReadingCriterionList.Any(t => t.IsSigned && t.IsConfirm && t.ReadingType == ReadingMethod.Double), t.VitrualSiteCode }).FirstOrDefault();
|
||||
|
||||
return ResponseOutput.Ok(pageList, trialTaskConfig);
|
||||
}
|
||||
|
||||
|
||||
@@ -123,25 +167,41 @@ namespace IRaCIS.Core.Application.Service
|
||||
/// 确认生成自身一致性分析任务
|
||||
/// </summary>
|
||||
/// <param name="inCommand"></param>
|
||||
/// <param name="_visitTaskCommonService"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost]
|
||||
[UnitOfWork]
|
||||
//[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
||||
|
||||
public async Task<IResponseOutput> ConfirmGenerateSelfConsistentTask(ConsistentConfirmGenerateCommand inCommand, [FromServices] IVisitTaskHelpeService _visitTaskCommonService)
|
||||
public async Task<IResponseOutput> ConfirmGenerateSelfConsistentTask(ConsistentConfirmGenerateCommand inCommand)
|
||||
{
|
||||
|
||||
|
||||
var filterObj = await _taskConsistentRuleRepository.FirstOrDefaultAsync(t => t.Id == inCommand.TaskConsistentRuleId);
|
||||
var filterObj = await _taskConsistentRuleRepository.Where(t => t.Id == inCommand.TaskConsistentRuleId).Include(t => t.TrialReadingCriterion).FirstOrDefaultAsync();
|
||||
var doctorUserId = inCommand.DoctorUserId;
|
||||
var trialReadingCriterionId = filterObj.TrialReadingCriterionId;
|
||||
|
||||
//随机分配
|
||||
if (inCommand.IsAutoAllocateGenerateTask)
|
||||
{
|
||||
var subjectList = (await GetSelfConsistentRuleMatchSubjectIdListAsync(new SelfConsistentSimpleQuery() { TaskConsistentRuleId = inCommand.TaskConsistentRuleId, DoctorUserId = inCommand.DoctorUserId })).ToList();
|
||||
|
||||
if (subjectList.Any(t => t.IsHaveGeneratedTask))
|
||||
{
|
||||
//已手动分配,不允许自动分配
|
||||
throw new BusinessValidationFailedException(_localizer["TaskConsistent_NotAllowedGenerate"]);
|
||||
}
|
||||
|
||||
if (subjectList.Count < 2 * filterObj.PlanSubjectCount)
|
||||
{
|
||||
throw new BusinessValidationFailedException(_localizer["TaskConsistent_NotAllowedGenerate1"]);
|
||||
}
|
||||
|
||||
inCommand.SubejctIdList = subjectList.Select(t => t.SubjectId).ToList().GetRandomCountList(filterObj.PlanSubjectCount);
|
||||
}
|
||||
|
||||
var list = await GetIQueryableDoctorSelfConsistentSubjectView(filterObj, doctorUserId, inCommand.SubejctIdList).ToListAsync();
|
||||
|
||||
//var (group, query) = GetIQueryableDoctorSelfConsistentRuleSubjectView(filterObj, inCommand.SubejctIdList);
|
||||
|
||||
//var list = query.OrderByDescending(t => t.IsHaveGeneratedTask).ToList();
|
||||
|
||||
var @lock = _distributedLockProvider.CreateLock($"VisitTaskCode");
|
||||
|
||||
@@ -155,9 +215,9 @@ namespace IRaCIS.Core.Application.Service
|
||||
|
||||
var blindSubjectCode = string.Empty;
|
||||
|
||||
var subjectTask = _visitTaskRepository.Where(t => t.SubjectId == subject.SubjectId && t.TrialReadingCriterionId==trialReadingCriterionId && t.IsSelfAnalysis == true).OrderByDescending(t => t.BlindSubjectCode).FirstOrDefault();
|
||||
var subjectTask = _visitTaskRepository.Where(t => t.SubjectId == subject.SubjectId && t.TrialReadingCriterionId == trialReadingCriterionId && t.IsSelfAnalysis == true).OrderByDescending(t => t.BlindSubjectCode).FirstOrDefault();
|
||||
|
||||
if (subjectTask!=null && subjectTask.BlindSubjectCode != String.Empty)
|
||||
if (subjectTask != null && subjectTask.BlindSubjectCode != String.Empty)
|
||||
{
|
||||
blindSubjectCode = subjectTask.BlindSubjectCode;
|
||||
}
|
||||
@@ -165,16 +225,27 @@ namespace IRaCIS.Core.Application.Service
|
||||
{
|
||||
var maxCodeStr = _visitTaskRepository.Where(t => t.TrialId == subject.TrialId && t.TrialReadingCriterionId == trialReadingCriterionId && t.IsSelfAnalysis == true).OrderByDescending(t => t.BlindSubjectCode).Select(t => t.BlindSubjectCode).FirstOrDefault();
|
||||
|
||||
if ( !string.IsNullOrEmpty(maxCodeStr))
|
||||
if (!string.IsNullOrEmpty(maxCodeStr))
|
||||
{
|
||||
int.TryParse(maxCodeStr.Substring(maxCodeStr.Length - filterObj.BlindSubjectNumberOfPlaces), out maxCodeInt);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
blindSubjectCode = filterObj.BlindTrialSiteCode + (maxCodeInt + 1).ToString($"D{filterObj.BlindSubjectNumberOfPlaces}");
|
||||
}
|
||||
|
||||
subject.VisitTaskList = subject.VisitTaskList.Take(filterObj.PlanVisitCount).ToList();
|
||||
//有序阅片
|
||||
if (filterObj.TrialReadingCriterion.IsReadingTaskViewInOrder == ReadingOrder.InOrder)
|
||||
{
|
||||
subject.VisitTaskList = subject.VisitTaskList.Take(filterObj.PlanVisitCount).ToList();
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
//完全随机 和受试者随机,都是随机挑选访视
|
||||
subject.VisitTaskList = subject.VisitTaskList.GetRandomCountList(filterObj.PlanVisitCount);
|
||||
}
|
||||
|
||||
|
||||
subject.VisitTaskList.ForEach(t =>
|
||||
{
|
||||
@@ -190,7 +261,7 @@ namespace IRaCIS.Core.Application.Service
|
||||
{
|
||||
var lastTask = (subject.VisitTaskList.Take(filterObj.PlanVisitCount).Last()).Clone();
|
||||
|
||||
var existGlobal = _visitTaskRepository.Where(t => t.SubjectId == lastTask.SubjectId &&t.TrialReadingCriterionId==trialReadingCriterionId && t.TaskState == TaskState.Effect && t.ReadingCategory == ReadingCategory.Global && t.VisitTaskNum == lastTask.VisitTaskNum + ReadingCommon.TaskNumDic[ReadingCategory.Global]).ProjectTo<VisitTaskSimpleDTO>(_mapper.ConfigurationProvider).FirstOrDefault();
|
||||
var existGlobal = _visitTaskRepository.Where(t => t.SubjectId == lastTask.SubjectId && t.TrialReadingCriterionId == trialReadingCriterionId && t.TaskState == TaskState.Effect && t.ReadingCategory == ReadingCategory.Global && t.VisitTaskNum == lastTask.VisitTaskNum + ReadingCommon.TaskNumDic[ReadingCategory.Global]).ProjectTo<VisitTaskSimpleDTO>(_mapper.ConfigurationProvider).FirstOrDefault();
|
||||
|
||||
|
||||
if (existGlobal == null)
|
||||
@@ -203,8 +274,20 @@ namespace IRaCIS.Core.Application.Service
|
||||
ReadingCategory = ReadingCategory.Global,
|
||||
TaskName = lastTask.TaskName + "_Global",
|
||||
TaskBlindName = lastTask.TaskBlindName + "_Global",
|
||||
TrialReadingCriterionId=trialReadingCriterionId,
|
||||
TrialReadingCriterionId = trialReadingCriterionId,
|
||||
VisitTaskNum= lastTask.VisitTaskNum + ReadingCommon.TaskNumDic[ReadingCategory.Global]
|
||||
};
|
||||
|
||||
var afterGlobal = _visitTaskRepository.Where(t => t.SubjectId == lastTask.SubjectId && t.TrialReadingCriterionId == trialReadingCriterionId && t.TaskState == TaskState.Effect && t.ReadingCategory == ReadingCategory.Global && t.VisitTaskNum > lastTask.VisitTaskNum).ProjectTo<VisitTaskSimpleDTO>(_mapper.ConfigurationProvider).FirstOrDefault();
|
||||
|
||||
if (afterGlobal == null)
|
||||
{
|
||||
throw new BusinessValidationFailedException("联系后台排查数据,没找到后续全局");
|
||||
}
|
||||
else
|
||||
{
|
||||
existGlobal.SouceReadModuleId = afterGlobal.SouceReadModuleId;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -245,16 +328,17 @@ namespace IRaCIS.Core.Application.Service
|
||||
/// <param name="inQuery"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost]
|
||||
public async Task<PageOutput<DoctorGroupConsistentSubjectView>> GetGroupConsistentRuleSubjectList(GroupConsistentQuery inQuery)
|
||||
public async Task<IResponseOutput<PageOutput<DoctorGroupConsistentSubjectView>>> GetGroupConsistentRuleSubjectList(GroupConsistentQuery inQuery)
|
||||
{
|
||||
var trialId = inQuery.TrialId;
|
||||
|
||||
var filterObj = await _taskConsistentRuleRepository.FirstOrDefaultAsync(t => t.TrialId == trialId && t.TrialReadingCriterionId==inQuery.TrialReadingCriterionId && t.IsSelfAnalysis == false);
|
||||
var filterObj = await _taskConsistentRuleRepository.FirstOrDefaultAsync(t => t.TrialId == trialId && t.TrialReadingCriterionId == inQuery.TrialReadingCriterionId && t.IsSelfAnalysis == false);
|
||||
|
||||
|
||||
if (filterObj == null)
|
||||
{
|
||||
return new PageOutput<DoctorGroupConsistentSubjectView>();
|
||||
object tt = null;
|
||||
return ResponseOutput.Ok(new PageOutput<DoctorGroupConsistentSubjectView>(), new { Rule = tt, IsAllowAutoAllocate = false });
|
||||
}
|
||||
|
||||
var query = await GetGroupConsistentQueryAsync(filterObj);
|
||||
@@ -262,7 +346,13 @@ namespace IRaCIS.Core.Application.Service
|
||||
|
||||
var pagedList = await query.ToPagedListAsync(inQuery.PageIndex, inQuery.PageSize, string.IsNullOrWhiteSpace(inQuery.SortField) ? nameof(DoctorSelfConsistentSubjectView.SubjectCode) : inQuery.SortField, inQuery.Asc);
|
||||
|
||||
return pagedList;
|
||||
var rule = await _taskConsistentRuleRepository.Where(t => t.TrialId == inQuery.TrialId && t.IsSelfAnalysis == false && t.TrialReadingCriterionId == inQuery.TrialReadingCriterionId).ProjectTo<TaskConsistentRuleBasic>(_mapper.ConfigurationProvider).FirstOrDefaultAsync();
|
||||
|
||||
var list = await GetGroupConsistentRuleMatchSubjectIdListAsync(new GroupConsistentSimpleQuery() { TrialId = inQuery.TrialId, TrialReadingCriterionId = inQuery.TrialReadingCriterionId });
|
||||
|
||||
var isAllowAutoAllocate = !list.Any(t => t.IsHaveGeneratedTask) && list.Count() > 2 * (rule?.PlanSubjectCount ?? 0);
|
||||
|
||||
return ResponseOutput.Ok(pagedList, new { Rule = rule, IsAllowAutoAllocate = isAllowAutoAllocate });
|
||||
}
|
||||
|
||||
|
||||
@@ -271,26 +361,48 @@ namespace IRaCIS.Core.Application.Service
|
||||
/// 确认生成组间一致性分析任务
|
||||
/// </summary>
|
||||
/// <param name="inCommand"></param>
|
||||
/// <param name="_visitTaskCommonService"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost]
|
||||
[UnitOfWork]
|
||||
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
||||
|
||||
public async Task<IResponseOutput> ConfirmGenerateGroupConsistentTask(GroupConsistentConfirmGenrateCommand inCommand, [FromServices] IVisitTaskHelpeService _visitTaskCommonService)
|
||||
public async Task<IResponseOutput> ConfirmGenerateGroupConsistentTask(GroupConsistentConfirmGenrateCommand inCommand)
|
||||
{
|
||||
var trialId = inCommand.TrialId;
|
||||
|
||||
var filterObj = await _taskConsistentRuleRepository.FirstOrDefaultAsync(t => t.TrialId == trialId && t.IsSelfAnalysis == false);
|
||||
var filterObj = await _taskConsistentRuleRepository.Where(t => t.TrialId == trialId && t.TrialReadingCriterionId == inCommand.TrialReadingCriterionId && t.IsSelfAnalysis == false).Include(t => t.TrialReadingCriterion).FirstNotNullAsync();
|
||||
|
||||
var trialReadingCriterionId = filterObj.TrialReadingCriterionId;
|
||||
|
||||
|
||||
|
||||
//随机分配
|
||||
if (inCommand.IsAutoAllocateGenerateTask)
|
||||
{
|
||||
var subjectSelectList = (await GetGroupConsistentRuleMatchSubjectIdListAsync(new GroupConsistentSimpleQuery() { TrialId = inCommand.TrialId, TrialReadingCriterionId = inCommand.TrialReadingCriterionId })).ToList();
|
||||
|
||||
if (subjectSelectList.Any(t => t.IsHaveGeneratedTask))
|
||||
{
|
||||
//已手动分配过,不允许自动分配
|
||||
throw new BusinessValidationFailedException(_localizer["TaskConsistent_NotAllowedGenerate"]);
|
||||
}
|
||||
|
||||
if (subjectSelectList.Count < 2 * filterObj.PlanSubjectCount)
|
||||
{
|
||||
throw new BusinessValidationFailedException(_localizer["TaskConsistent_NotAllowedGenerate1"]);
|
||||
}
|
||||
|
||||
inCommand.SubejctIdList = subjectSelectList.Select(t => t.SubjectId).ToList().GetRandomCountList(filterObj.PlanSubjectCount);
|
||||
}
|
||||
|
||||
|
||||
|
||||
var query = await GetGroupConsistentQueryAsync(filterObj, inCommand.SubejctIdList);
|
||||
|
||||
var subjectList = query.ToList();
|
||||
|
||||
var doctorUserIdQuery = from enroll in _repository.Where<Enroll>(t => t.TrialId == trialId).Where(t => t.EnrollReadingCategoryList.Where(t=>t.TrialReadingCriterionId==trialReadingCriterionId).Any(c => c.ReadingCategory == ReadingCategory.Global || c.ReadingCategory == ReadingCategory.Visit))
|
||||
var doctorUserIdQuery = from enroll in _repository.Where<Enroll>(t => t.TrialId == trialId).Where(t => t.EnrollReadingCategoryList.Where(t => t.TrialReadingCriterionId == trialReadingCriterionId).Any(c => c.ReadingCategory == ReadingCategory.Global || c.ReadingCategory == ReadingCategory.Visit))
|
||||
.Where(t => t.EnrollReadingCriteriaList.Any(t => t.TrialReadingCriterionId == trialReadingCriterionId && t.IsJoinAnalysis == true))
|
||||
join user in _repository.Where<User>() on enroll.DoctorId equals user.DoctorId
|
||||
select user.Id;
|
||||
|
||||
@@ -310,7 +422,7 @@ namespace IRaCIS.Core.Application.Service
|
||||
|
||||
var blindSubjectCode = string.Empty;
|
||||
|
||||
var subjectTask = _visitTaskRepository.Where(t => t.SubjectId == subject.SubjectId && t.TrialReadingCriterionId == trialReadingCriterionId && t.IsSelfAnalysis==false).OrderByDescending(t => t.BlindSubjectCode).FirstOrDefault();
|
||||
var subjectTask = _visitTaskRepository.Where(t => t.SubjectId == subject.SubjectId && t.TrialReadingCriterionId == trialReadingCriterionId && t.IsSelfAnalysis == false).OrderByDescending(t => t.BlindSubjectCode).FirstOrDefault();
|
||||
if (subjectTask != null && subjectTask.BlindSubjectCode != String.Empty)
|
||||
{
|
||||
blindSubjectCode = subjectTask.BlindSubjectCode;
|
||||
@@ -350,6 +462,11 @@ namespace IRaCIS.Core.Application.Service
|
||||
foreach (var needAddDoctorUserId in needAddDoctorUserIdList)
|
||||
{
|
||||
|
||||
//if(filterObj.TrialReadingCriterion.IsReadingTaskViewInOrder== ReadingOrder.InOrder)
|
||||
//{
|
||||
|
||||
//}
|
||||
|
||||
//每个医生 都生成处理的任务
|
||||
foreach (var task in subject.SubjectTaskVisitList.Take(filterObj.PlanVisitCount))
|
||||
{
|
||||
@@ -371,10 +488,10 @@ namespace IRaCIS.Core.Application.Service
|
||||
|
||||
TrialReadingCriterionId = task.TrialReadingCriterionId,
|
||||
|
||||
BlindSubjectCode=blindSubjectCode,
|
||||
BlindTrialSiteCode=filterObj.BlindTrialSiteCode
|
||||
BlindSubjectCode = blindSubjectCode,
|
||||
BlindTrialSiteCode = filterObj.BlindTrialSiteCode
|
||||
|
||||
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
@@ -402,7 +519,9 @@ namespace IRaCIS.Core.Application.Service
|
||||
TrialReadingCriterionId = trialReadingCriterionId,
|
||||
|
||||
BlindSubjectCode = blindSubjectCode,
|
||||
BlindTrialSiteCode = filterObj.BlindTrialSiteCode
|
||||
BlindTrialSiteCode = filterObj.BlindTrialSiteCode,
|
||||
|
||||
VisitTaskNum = lastTask.VisitTaskNum + ReadingCommon.TaskNumDic[ReadingCategory.Global]
|
||||
|
||||
};
|
||||
}
|
||||
@@ -458,10 +577,13 @@ namespace IRaCIS.Core.Application.Service
|
||||
|
||||
var trialReadingCriterionId = filterObj.TrialReadingCriterionId;
|
||||
|
||||
//防止生成sql生成GETDATE() 时区导致的问题
|
||||
var appDateTimeNow = DateTime.Now;
|
||||
|
||||
#region Subejct 维度
|
||||
|
||||
Expression<Func<VisitTask, bool>> comonTaskFilter = u => u.TrialId == trialId && u.IsAnalysisCreate == false && u.TaskState == TaskState.Effect && u.ReadingTaskState == ReadingTaskState.HaveSigned && u.TrialReadingCriterionId== trialReadingCriterionId &&
|
||||
u.SignTime!.Value.AddDays(filterObj.IntervalWeeks * 7) < DateTime.Now && (u.ReReadingApplyState == ReReadingApplyState.Default || u.ReReadingApplyState == ReReadingApplyState.Reject) && u.DoctorUserId == doctorUserId;
|
||||
Expression<Func<VisitTask, bool>> comonTaskFilter = u => u.TrialId == trialId && u.IsAnalysisCreate == false && u.TaskState == TaskState.Effect && u.ReadingTaskState == ReadingTaskState.HaveSigned && u.TrialReadingCriterionId == trialReadingCriterionId &&
|
||||
u.SignTime!.Value.AddDays(filterObj.IntervalWeeks * 7) < appDateTimeNow && (u.ReReadingApplyState == ReReadingApplyState.Default || u.ReReadingApplyState == ReReadingApplyState.Reject) && u.DoctorUserId == doctorUserId;
|
||||
|
||||
|
||||
|
||||
@@ -479,14 +601,15 @@ namespace IRaCIS.Core.Application.Service
|
||||
//{
|
||||
// //这里的过滤条件 不能用 where(comonTaskFilter) 会报错,奇怪的问题 只能重新写一遍
|
||||
// visitTaskFilter = visitTaskFilter.And(c => c.Subject.SubjectVisitTaskList.Any(t => t.VisitTaskNum == c.VisitTaskNum + ReadingCommon.TaskNumDic[ReadingCategory.Global] && t.ReadingCategory == ReadingCategory.Global && t.IsAnalysisCreate == false && t.TaskState == TaskState.Effect && t.ReadingTaskState == ReadingTaskState.HaveSigned &&
|
||||
// t.SignTime!.Value.AddDays(filterObj.IntervalWeeks * 7) < DateTime.Now && (t.ReReadingApplyState == ReReadingApplyState.Default || t.ReReadingApplyState == ReReadingApplyState.Reject)));
|
||||
// t.SignTime!.Value.AddDays(filterObj.IntervalWeeks * 7) < appDateTimeNow && (t.ReReadingApplyState == ReReadingApplyState.Default || t.ReReadingApplyState == ReReadingApplyState.Reject)));
|
||||
|
||||
//}
|
||||
|
||||
|
||||
var subjectQuery = _subjectRepository.Where(t => t.TrialId == trialId &&
|
||||
t.SubjectVisitTaskList.AsQueryable().Where(visitTaskFilter).Count() >= filterObj.PlanVisitCount)
|
||||
.WhereIf(filterObj.IsHaveReadingPeriod == true, u => u.SubjectVisitTaskList.AsQueryable().Where(comonTaskFilter).Where(t => t.ReadingCategory == ReadingCategory.Visit || t.ReadingCategory == ReadingCategory.Global).OrderBy(t => t.VisitTaskNum).Take(filterObj.PlanVisitCount + 1).Any(t => t.ReadingCategory == ReadingCategory.Global))
|
||||
.WhereIf(filterObj.IsHaveReadingPeriod == true, u => u.SubjectVisitTaskList.AsQueryable().Where(comonTaskFilter)
|
||||
.Where(t => t.ReadingCategory == ReadingCategory.Visit || t.ReadingCategory == ReadingCategory.Global).Any(t => t.ReadingCategory == ReadingCategory.Global && t.VisitTaskNum > filterObj.PlanVisitCount - 1))
|
||||
;
|
||||
|
||||
|
||||
@@ -494,16 +617,18 @@ namespace IRaCIS.Core.Application.Service
|
||||
var query = subjectQuery.Select(t => new DoctorSelfConsistentSubjectView()
|
||||
{
|
||||
TrialId = t.TrialId,
|
||||
SiteId = t.SiteId,
|
||||
TrialSiteId = t.TrialSiteId,
|
||||
SubjectCode = t.Code,
|
||||
TrialSiteCode = t.TrialSite.TrialSiteCode,
|
||||
SubjectId = t.Id,
|
||||
|
||||
IsReReadingOrBackInfluenceAnalysis=t.IsReReadingOrBackInfluenceAnalysis,
|
||||
IsReReadingOrBackInfluenceAnalysis = t.IsReReadingOrBackInfluenceAnalysis,
|
||||
|
||||
FirstGlobalVisitName= t.ReadModuleList.Where(c=>c.TrialReadingCriterionId== trialReadingCriterionId && c.ModuleType== ModuleTypeEnum.Global).OrderBy(k=>k.SubjectVisit.VisitNum).Select(u=>u.SubjectVisit.VisitName).FirstOrDefault(),
|
||||
|
||||
BlindSubjectCode = t.SubjectVisitTaskList.Where(t => t.IsAnalysisCreate && t.TrialReadingCriterionId == trialReadingCriterionId).OrderByDescending(t => t.BlindSubjectCode).Select(t => t.BlindSubjectCode).FirstOrDefault(),
|
||||
|
||||
IsHaveGeneratedTask = t.SubjectVisitTaskList.Any(c => c.DoctorUserId == doctorUserId && c.IsSelfAnalysis == true && c.TrialReadingCriterionId==trialReadingCriterionId),
|
||||
IsHaveGeneratedTask = t.SubjectVisitTaskList.Any(c => c.DoctorUserId == doctorUserId && c.IsSelfAnalysis == true && c.TrialReadingCriterionId == trialReadingCriterionId),
|
||||
|
||||
|
||||
ValidVisitCount = t.SubjectVisitTaskList.AsQueryable().Where(visitTaskFilter).Count(),
|
||||
@@ -526,33 +651,31 @@ namespace IRaCIS.Core.Application.Service
|
||||
|
||||
|
||||
|
||||
TrialReadingCriterionId=c.TrialReadingCriterionId,
|
||||
TrialReadingCriterionId = c.TrialReadingCriterionId,
|
||||
IsClinicalDataSign = c.IsClinicalDataSign,
|
||||
IsNeedClinicalDataSign = c.IsNeedClinicalDataSign,
|
||||
|
||||
//自身一致性才有意义
|
||||
//IsHaveGeneratedTask = c.Subject.SubjectVisitTaskList.Any(t => t.ConsistentAnalysisOriginalTaskId == c.Id),
|
||||
|
||||
GlobalVisitTaskList = c.Subject.SubjectVisitTaskList.AsQueryable().Where(comonTaskFilter).Where(t => t.VisitTaskNum == c.VisitTaskNum + ReadingCommon.TaskNumDic[ReadingCategory.Global]).Select(c => new VisitTaskSimpleDTO()
|
||||
{
|
||||
Id = c.Id,
|
||||
ReadingCategory = c.ReadingCategory,
|
||||
ReadingTaskState = c.ReadingTaskState,
|
||||
TaskBlindName = c.TaskBlindName,
|
||||
TaskCode = c.TaskCode,
|
||||
TaskName = c.TaskName,
|
||||
TaskState = c.TaskState,
|
||||
ArmEnum = c.ArmEnum,
|
||||
SubjectId = c.SubjectId,
|
||||
VisitTaskNum = c.VisitTaskNum,
|
||||
TrialId = c.TrialId,
|
||||
SourceSubjectVisitId = c.SourceSubjectVisitId,
|
||||
SouceReadModuleId = c.SouceReadModuleId,
|
||||
//GlobalVisitTaskList = c.Subject.SubjectVisitTaskList.AsQueryable().Where(comonTaskFilter).Where(t => t.VisitTaskNum == c.VisitTaskNum + ReadingCommon.TaskNumDic[ReadingCategory.Global]).Select(c => new VisitTaskSimpleDTO()
|
||||
//{
|
||||
// Id = c.Id,
|
||||
// ReadingCategory = c.ReadingCategory,
|
||||
// ReadingTaskState = c.ReadingTaskState,
|
||||
// TaskBlindName = c.TaskBlindName,
|
||||
// TaskCode = c.TaskCode,
|
||||
// TaskName = c.TaskName,
|
||||
// TaskState = c.TaskState,
|
||||
// ArmEnum = c.ArmEnum,
|
||||
// SubjectId = c.SubjectId,
|
||||
// VisitTaskNum = c.VisitTaskNum,
|
||||
// TrialId = c.TrialId,
|
||||
// SourceSubjectVisitId = c.SourceSubjectVisitId,
|
||||
// SouceReadModuleId = c.SouceReadModuleId,
|
||||
|
||||
TrialReadingCriterionId = c.TrialReadingCriterionId,
|
||||
IsClinicalDataSign = c.IsClinicalDataSign,
|
||||
IsNeedClinicalDataSign = c.IsNeedClinicalDataSign,
|
||||
}).ToList(),
|
||||
// TrialReadingCriterionId = c.TrialReadingCriterionId,
|
||||
// IsClinicalDataSign = c.IsClinicalDataSign,
|
||||
// IsNeedClinicalDataSign = c.IsNeedClinicalDataSign,
|
||||
//}).ToList(),
|
||||
|
||||
}).ToList()
|
||||
});
|
||||
@@ -564,18 +687,21 @@ namespace IRaCIS.Core.Application.Service
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 组间一致性分析 满足条件的subject 筛选
|
||||
/// </summary>
|
||||
/// <param name="filterObj"></param>
|
||||
/// <param name="subejctIdList"></param>
|
||||
/// <returns></returns>
|
||||
private async Task<IQueryable<DoctorGroupConsistentSubjectView>> GetGroupConsistentQueryAsync(TaskConsistentRule filterObj, List<Guid>? subejctIdList = null)
|
||||
{
|
||||
//单重阅片没有组件一致性
|
||||
|
||||
var trialId = filterObj.TrialId;
|
||||
var trialReadingCriterionId = filterObj.TrialReadingCriterionId;
|
||||
|
||||
//var trialConfig = (await _repository.Where<Trial>(t => t.Id == trialId).Select(t => new { TrialId = t.Id, t.ReadingType, t.IsReadingTaskViewInOrder }).FirstOrDefaultAsync()).IfNullThrowException();
|
||||
|
||||
|
||||
|
||||
Expression<Func<VisitTask, bool>> comonTaskFilter = u => u.TrialId == trialId && u.IsAnalysisCreate == false && u.TaskState == TaskState.Effect && u.ReadingTaskState == ReadingTaskState.HaveSigned && u.TrialReadingCriterionId == trialReadingCriterionId
|
||||
Expression<Func<VisitTask, bool>> comonTaskFilter = u => u.TrialId == trialId && u.IsAnalysisCreate == false && u.TaskState == TaskState.Effect && u.ReadingTaskState == ReadingTaskState.HaveSigned && u.TrialReadingCriterionId == trialReadingCriterionId
|
||||
&& (u.ReReadingApplyState == ReReadingApplyState.Default || u.ReReadingApplyState == ReReadingApplyState.Reject);
|
||||
|
||||
|
||||
@@ -587,28 +713,16 @@ namespace IRaCIS.Core.Application.Service
|
||||
Expression<Func<VisitTask, bool>> visitTaskFilter = comonTaskFilter.And(t => t.ReadingCategory == ReadingCategory.Visit);
|
||||
|
||||
|
||||
////所选访视数量 的访视 其中必有一个访视后有全局任务
|
||||
//if (filterObj.IsHaveReadingPeriod == true)
|
||||
//{
|
||||
// //visitTaskFilter = visitTaskFilter.And(t => t.Subject.SubjectVisitTaskList.AsQueryable().Where(comonTaskFilter).Any(u => u.VisitTaskNum == t.VisitTaskNum + ReadingCommon.TaskNumDic[ReadingCategory.Global] && u.ReadingCategory == ReadingCategory.Global));
|
||||
|
||||
// //这里的过滤条件 不能用 where(comonTaskFilter) 会报错,奇怪的问题 只能重新写一遍
|
||||
// visitTaskFilter = visitTaskFilter.And(c => c.Subject.SubjectVisitTaskList.Any(t => t.VisitTaskNum == c.VisitTaskNum + ReadingCommon.TaskNumDic[ReadingCategory.Global] && t.ReadingCategory == ReadingCategory.Global && t.IsAnalysisCreate == false && t.TaskState == TaskState.Effect && t.ReadingTaskState == ReadingTaskState.HaveSigned &&
|
||||
// t.SignTime!.Value.AddDays(filterObj.IntervalWeeks * 7) < DateTime.Now && (t.ReReadingApplyState == ReReadingApplyState.Default || t.ReReadingApplyState == ReReadingApplyState.Reject)));
|
||||
|
||||
//}
|
||||
|
||||
|
||||
IQueryable<Subject> subjectQuery = default;
|
||||
|
||||
//单重阅片没有组件一致性
|
||||
|
||||
//双重阅片,并且都阅片完成
|
||||
subjectQuery = _subjectRepository.Where(t => t.TrialId == trialId &&
|
||||
t.SubjectVisitTaskList.AsQueryable().Where(comonTaskFilter).Where(t => t.ReadingCategory == ReadingCategory.Visit || t.ReadingCategory == ReadingCategory.Global).Select(t => t.DoctorUserId).Distinct().Count() == 2 &&
|
||||
t.SubjectVisitTaskList.AsQueryable().Where(visitTaskFilter).GroupBy(t => new { t.SubjectId, t.VisitTaskNum }).Where(g => g.Count() == 2).Count() >= filterObj.PlanVisitCount
|
||||
)
|
||||
.WhereIf(filterObj.IsHaveReadingPeriod == true, u => u.SubjectVisitTaskList.AsQueryable().Where(comonTaskFilter).Where(t => t.ReadingCategory == ReadingCategory.Visit || t.ReadingCategory == ReadingCategory.Global).OrderBy(t => t.VisitTaskNum).Take(filterObj.PlanVisitCount * 2 + 2).Any(t => t.ReadingCategory == ReadingCategory.Global))
|
||||
|
||||
//.WhereIf(filterObj.IsHaveReadingPeriod == true, u => u.SubjectVisitTaskList.AsQueryable().Where(comonTaskFilter).Where(t => t.ReadingCategory == ReadingCategory.Visit || t.ReadingCategory == ReadingCategory.Global).OrderBy(t => t.VisitTaskNum).Take(filterObj.PlanVisitCount * 2 + 2).Any(t => t.ReadingCategory == ReadingCategory.Global))
|
||||
.WhereIf(filterObj.IsHaveReadingPeriod == true, u => u.SubjectVisitTaskList.AsQueryable().Where(comonTaskFilter).Where(t => t.ReadingCategory == ReadingCategory.Global && t.VisitTaskNum > filterObj.PlanVisitCount - 1).Select(t => t.DoctorUserId).Distinct().Count() == 2)
|
||||
;
|
||||
|
||||
|
||||
@@ -616,14 +730,21 @@ namespace IRaCIS.Core.Application.Service
|
||||
var query = subjectQuery.Select(t => new DoctorGroupConsistentSubjectView()
|
||||
{
|
||||
TrialId = t.TrialId,
|
||||
SiteId = t.SiteId,
|
||||
TrialSiteId = t.TrialSiteId,
|
||||
SubjectCode = t.Code,
|
||||
TrialSiteCode = t.TrialSite.TrialSiteCode,
|
||||
SubjectId = t.Id,
|
||||
IsReReadingOrBackInfluenceAnalysis = t.IsReReadingOrBackInfluenceAnalysis,
|
||||
|
||||
IsHaveGeneratedTask = t.SubjectVisitTaskList.Any(c => c.IsSelfAnalysis == false && c.TrialReadingCriterionId==trialReadingCriterionId),
|
||||
IsHaveGeneratedTask = t.SubjectVisitTaskList.Any(c => c.IsSelfAnalysis == false && c.TrialReadingCriterionId == trialReadingCriterionId),
|
||||
|
||||
DoctorUserList = t.SubjectDoctorList.Where(t => t.TrialReadingCriterionId == trialReadingCriterionId && t.IsConfirmed && t.ArmEnum <= Arm.DoubleReadingArm2).Select(t => new UserSimpleInfo()
|
||||
{
|
||||
UserId = t.Id,
|
||||
FullName = t.DoctorUser.FullName,
|
||||
UserCode = t.DoctorUser.UserCode,
|
||||
UserName = t.DoctorUser.UserName
|
||||
}).ToList(),
|
||||
|
||||
ValidVisitCount = t.SubjectVisitTaskList.AsQueryable().Where(visitTaskFilter).GroupBy(t => new { t.SubjectId, t.VisitTaskNum }).Where(g => g.Count() == 2).Count(),
|
||||
|
||||
@@ -663,7 +784,7 @@ namespace IRaCIS.Core.Application.Service
|
||||
[HttpPost]
|
||||
public async Task<TaskConsistentRuleBasic?> GetConsistentRule(TaskConsistentRuleQuery inQuery)
|
||||
{
|
||||
return await _taskConsistentRuleRepository.Where(t => t.TrialId == inQuery.TrialId && t.IsSelfAnalysis == inQuery.IsSelfAnalysis && t.TrialReadingCriterionId==inQuery.TrialReadingCriterionId).ProjectTo<TaskConsistentRuleBasic>(_mapper.ConfigurationProvider).FirstOrDefaultAsync();
|
||||
return await _taskConsistentRuleRepository.Where(t => t.TrialId == inQuery.TrialId && t.IsSelfAnalysis == inQuery.IsSelfAnalysis && t.TrialReadingCriterionId == inQuery.TrialReadingCriterionId).ProjectTo<TaskConsistentRuleBasic>(_mapper.ConfigurationProvider).FirstOrDefaultAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -672,13 +793,26 @@ namespace IRaCIS.Core.Application.Service
|
||||
/// <param name="inQuery"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost]
|
||||
public async Task<List<TaskConsistentRuleView>> GetSelfConsistentDoctorStatList(TaskConsistentRuleQuery inQuery)
|
||||
public async Task<IResponseOutput<List<TaskConsistentRuleView>>> GetSelfConsistentDoctorStatList(TaskConsistentRuleQuery inQuery)
|
||||
{
|
||||
|
||||
|
||||
var trialId = inQuery.TrialId;
|
||||
|
||||
var taskConsistentRuleQueryable = from enroll in _repository.Where<Enroll>(t => t.TrialId == trialId && t.EnrollStatus==EnrollStatus.ConfirmIntoGroup)
|
||||
join user in _repository.Where<User>() on enroll.DoctorId equals user.DoctorId
|
||||
join taskConsistentRule in _repository.Where<TaskConsistentRule>(t => t.TrialId == trialId &&t.TrialReadingCriterionId==inQuery.TrialReadingCriterionId && t.IsSelfAnalysis) on enroll.TrialId equals taskConsistentRule.TrialId
|
||||
//防止生成sql生成GETDATE() 时区导致的问题
|
||||
var appDateTimeNow = DateTime.Now;
|
||||
|
||||
Expression<Func<VisitTask, bool>> comonTaskFilter = u => u.TrialId == trialId && u.IsAnalysisCreate == false && u.TaskState == TaskState.Effect && u.ReadingTaskState == ReadingTaskState.HaveSigned && (u.ReReadingApplyState == ReReadingApplyState.Default || u.ReReadingApplyState == ReReadingApplyState.Reject);
|
||||
|
||||
//&& u.TrialReadingCriterionId == trialReadingCriterionId &&u.SignTime!.Value.AddDays(filterObj.IntervalWeeks * 7) < appDateTimeNow && u.DoctorUserId == doctorUserId;
|
||||
|
||||
Expression<Func<VisitTask, bool>> visitTaskFilter = comonTaskFilter.And(t => t.ReadingCategory == ReadingCategory.Visit);
|
||||
|
||||
|
||||
var taskConsistentRuleQueryable = from enroll in _repository.Where<Enroll>(t => t.TrialId == trialId && t.EnrollStatus == EnrollStatus.ConfirmIntoGroup
|
||||
&& t.EnrollReadingCriteriaList.Any(c => c.TrialReadingCriterionId == inQuery.TrialReadingCriterionId && c.IsJoinAnalysis))
|
||||
join user in _repository.Where<User>() on enroll.DoctorUserId equals user.Id
|
||||
join taskConsistentRule in _repository.Where<TaskConsistentRule>(t => t.TrialId == trialId && t.TrialReadingCriterionId == inQuery.TrialReadingCriterionId && t.IsSelfAnalysis) on enroll.TrialId equals taskConsistentRule.TrialId
|
||||
select new TaskConsistentRuleView()
|
||||
{
|
||||
Id = taskConsistentRule.Id,
|
||||
@@ -697,7 +831,28 @@ namespace IRaCIS.Core.Application.Service
|
||||
IsHaveReadingPeriod = taskConsistentRule.IsHaveReadingPeriod,
|
||||
PlanVisitCount = taskConsistentRule.PlanVisitCount,
|
||||
|
||||
GeneratedSubjectCount = taskConsistentRule.Trial.VisitTaskList.Where(t => t.IsAnalysisCreate && t.TrialReadingCriterionId == inQuery.TrialReadingCriterionId && t.IsSelfAnalysis == true && t.DoctorUserId == user.Id).Select(t => t.SubjectId).Distinct().Count(),
|
||||
GeneratedSubjectCount = taskConsistentRule.TrialReadingCriterion.VisitTaskList.Where(t => t.IsAnalysisCreate && t.IsSelfAnalysis == true && t.DoctorUserId == user.Id).Select(t => t.SubjectId).Distinct().Count(),
|
||||
|
||||
|
||||
MatchSubejctCount = taskConsistentRule.Trial.SubjectList.AsQueryable()
|
||||
.Where(t => taskConsistentRule.IsHaveReadingPeriod == false ? t.SubjectVisitTaskList.AsQueryable()
|
||||
.Where(visitTaskFilter).Where(t => t.TrialReadingCriterionId == inQuery.TrialReadingCriterionId && t.SignTime!.Value.AddDays(taskConsistentRule.IntervalWeeks * 7) < appDateTimeNow && t.DoctorUserId == user.Id)
|
||||
.Count() >= taskConsistentRule.PlanVisitCount :
|
||||
|
||||
//全局要>计划访视数量后面
|
||||
t.SubjectVisitTaskList.AsQueryable().Where(comonTaskFilter)
|
||||
.Where(t => t.TrialReadingCriterionId == inQuery.TrialReadingCriterionId && t.SignTime!.Value.AddDays(taskConsistentRule.IntervalWeeks * 7) < appDateTimeNow && t.DoctorUserId == user.Id)
|
||||
.Where(t => t.ReadingCategory == ReadingCategory.Visit || t.ReadingCategory == ReadingCategory.Global).Any(t => t.ReadingCategory == ReadingCategory.Global && t.VisitTaskNum > taskConsistentRule.PlanVisitCount - 1)
|
||||
&&
|
||||
t.SubjectVisitTaskList.AsQueryable().Where(visitTaskFilter)
|
||||
.Where(t => t.TrialReadingCriterionId == inQuery.TrialReadingCriterionId && t.SignTime!.Value.AddDays(taskConsistentRule.IntervalWeeks * 7) < appDateTimeNow && t.DoctorUserId == user.Id)
|
||||
.Count() >= taskConsistentRule.PlanVisitCount
|
||||
|
||||
)
|
||||
|
||||
.Count(),
|
||||
|
||||
|
||||
|
||||
AnalysisDoctorUser = new UserSimpleInfo()
|
||||
{
|
||||
@@ -724,10 +879,48 @@ namespace IRaCIS.Core.Application.Service
|
||||
//var taskConsistentRuleQueryable = _taskConsistentRuleRepository.Where(t => t.TrialId == inQuery.TrialId)
|
||||
// .ProjectTo<TaskConsistentRuleView>(_mapper.ConfigurationProvider);
|
||||
|
||||
return await taskConsistentRuleQueryable.ToListAsync();
|
||||
var list = await taskConsistentRuleQueryable.ToListAsync();
|
||||
|
||||
var rule = await _taskConsistentRuleRepository.Where(t => t.TrialId == inQuery.TrialId && t.IsSelfAnalysis == true && t.TrialReadingCriterionId == inQuery.TrialReadingCriterionId).ProjectTo<TaskConsistentRuleBasic>(_mapper.ConfigurationProvider).FirstOrDefaultAsync();
|
||||
return ResponseOutput.Ok(list, rule);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[UnitOfWork]
|
||||
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
||||
public async Task<IResponseOutput> UpdateTrialVirtualSiteCode(UpdateTrialSiteCodeCommand inCommand)
|
||||
{
|
||||
|
||||
if (_trialSiteRepository.Where(t => t.TrialId == inCommand.TrialId, false, true).Any(t => t.TrialSiteCode == inCommand.VirtualSiteCode))
|
||||
{
|
||||
return ResponseOutput.NotOk(_localizer["TaskConsistent_SiteCodeExists"]);
|
||||
}
|
||||
|
||||
var trial = await _trialRepository.FirstOrDefaultAsync(t => t.Id == inCommand.TrialId);
|
||||
|
||||
var oldCode = trial.VitrualSiteCode;
|
||||
|
||||
trial.VitrualSiteCode = inCommand.VirtualSiteCode;
|
||||
|
||||
await _visitTaskRepository.BatchUpdateNoTrackingAsync(t => t.TrialId == trial.Id, u => new VisitTask() { BlindTrialSiteCode = inCommand.VirtualSiteCode });
|
||||
|
||||
await _taskConsistentRuleRepository.BatchUpdateNoTrackingAsync(t => t.TrialId == trial.Id, u => new TaskConsistentRule() { BlindTrialSiteCode = inCommand.VirtualSiteCode });
|
||||
|
||||
|
||||
await _visitTaskRepository.ExecuteUpdateAsync(t => t.TrialId == trial.Id, s => s.SetProperty(e => e.BlindSubjectCode, u => u.BlindSubjectCode.Replace(oldCode, inCommand.VirtualSiteCode)));
|
||||
|
||||
await _trialVirtualSiteCodeUpdateRepository.AddAsync(new TrialVirtualSiteCodeUpdate() { VirturalSiteCode = inCommand.VirtualSiteCode, TrialId = inCommand.TrialId });
|
||||
|
||||
await _trialRepository.SaveChangesAsync();
|
||||
|
||||
return ResponseOutput.Ok();
|
||||
}
|
||||
|
||||
public async Task<List<UpdateTrialSiteCodeCommandView>> GetUpdateVirtualSiteCodeList(Guid trialId)
|
||||
{
|
||||
var list = await _trialVirtualSiteCodeUpdateRepository.Where(t => t.TrialId == trialId).Select(t => new UpdateTrialSiteCodeCommandView() { TrialId = trialId, VirtualSiteCode = t.VirturalSiteCode, Creatime = t.CreateTime }).OrderByDescending(t => t.Creatime).ToListAsync();
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
||||
@@ -737,7 +930,7 @@ namespace IRaCIS.Core.Application.Service
|
||||
|
||||
var verifyExp1 = new EntityVerifyExp<TaskConsistentRule>()
|
||||
{
|
||||
VerifyExp = t => t.TrialId == addOrEditTaskConsistentRule.TrialId && t.IsSelfAnalysis == addOrEditTaskConsistentRule.IsSelfAnalysis && t.TrialReadingCriterionId==addOrEditTaskConsistentRule.TrialReadingCriterionId,
|
||||
VerifyExp = t => t.TrialId == addOrEditTaskConsistentRule.TrialId && t.IsSelfAnalysis == addOrEditTaskConsistentRule.IsSelfAnalysis && t.TrialReadingCriterionId == addOrEditTaskConsistentRule.TrialReadingCriterionId,
|
||||
//"已有该项目配置,不允许继续增加"
|
||||
VerifyMsg = _localizer["TaskConsistent_TaskGenerated"]
|
||||
};
|
||||
@@ -748,6 +941,22 @@ namespace IRaCIS.Core.Application.Service
|
||||
return ResponseOutput.NotOk(_localizer["TaskConsistent_MedicalAuditTaskExists"]);
|
||||
}
|
||||
|
||||
|
||||
if (_trialSiteRepository.Where(t => t.TrialId == addOrEditTaskConsistentRule.TrialId, false, true).Any(t => t.TrialSiteCode == addOrEditTaskConsistentRule.BlindTrialSiteCode))
|
||||
{
|
||||
return ResponseOutput.NotOk(_localizer["TaskConsistent_SiteCodeExists"]);
|
||||
}
|
||||
|
||||
var trial = await _trialRepository.FirstOrDefaultAsync(t => t.Id == addOrEditTaskConsistentRule.TrialId);
|
||||
|
||||
if (string.IsNullOrEmpty(trial.VitrualSiteCode))
|
||||
{
|
||||
await _trialVirtualSiteCodeUpdateRepository.AddAsync(new TrialVirtualSiteCodeUpdate() { VirturalSiteCode = addOrEditTaskConsistentRule.BlindTrialSiteCode, TrialId = addOrEditTaskConsistentRule.TrialId });
|
||||
}
|
||||
|
||||
trial.VitrualSiteCode = addOrEditTaskConsistentRule.BlindTrialSiteCode;
|
||||
|
||||
|
||||
var entity = await _taskConsistentRuleRepository.InsertOrUpdateAsync(addOrEditTaskConsistentRule, true, verifyExp1);
|
||||
|
||||
return ResponseOutput.Ok(entity.Id.ToString());
|
||||
@@ -762,7 +971,7 @@ namespace IRaCIS.Core.Application.Service
|
||||
var config = await _taskConsistentRuleRepository.FirstOrDefaultAsync(t => t.Id == taskConsistentRuleId);
|
||||
|
||||
|
||||
if (await _visitTaskRepository.AnyAsync(t => t.IsAnalysisCreate && t.TrialId == config.TrialId && t.IsSelfAnalysis == config.IsSelfAnalysis && t.TrialReadingCriterionId==config.TrialReadingCriterionId))
|
||||
if (await _visitTaskRepository.AnyAsync(t => t.IsAnalysisCreate && t.TrialId == config.TrialId && t.IsSelfAnalysis == config.IsSelfAnalysis && t.TrialReadingCriterionId == config.TrialReadingCriterionId))
|
||||
{
|
||||
//"该标准已产生一致性分析任务,不允许删除"
|
||||
throw new BusinessValidationFailedException(_localizer["TaskConsistent_SignedTaskCannotBeInvalidated"]);
|
||||
@@ -775,6 +984,38 @@ namespace IRaCIS.Core.Application.Service
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 获取自身一致性分析 符合条件的subject 数组
|
||||
/// </summary>
|
||||
/// <param name="inQuery"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<List<SubjectGeneratedTask>> GetSelfConsistentRuleMatchSubjectIdListAsync(SelfConsistentSimpleQuery inQuery)
|
||||
{
|
||||
var filterObj = await _taskConsistentRuleRepository.FirstOrDefaultAsync(t => t.Id == inQuery.TaskConsistentRuleId);
|
||||
|
||||
var subjectList = await GetIQueryableDoctorSelfConsistentSubjectView(filterObj, inQuery.DoctorUserId).Select(t => new SubjectGeneratedTask() { SubjectId = t.SubjectId, IsHaveGeneratedTask = t.IsHaveGeneratedTask }).ToListAsync();
|
||||
|
||||
return subjectList;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取组件一致性分析符合条件的subject 数组
|
||||
/// </summary>
|
||||
/// <param name="inQuery"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<List<SubjectGeneratedTask>> GetGroupConsistentRuleMatchSubjectIdListAsync(GroupConsistentSimpleQuery inQuery)
|
||||
{
|
||||
var filterObj = await _taskConsistentRuleRepository.FirstOrDefaultAsync(t => t.TrialId == inQuery.TrialId && t.TrialReadingCriterionId == inQuery.TrialReadingCriterionId && t.IsSelfAnalysis == false);
|
||||
|
||||
var subjectList = await (await GetGroupConsistentQueryAsync(filterObj)).Select(t => new SubjectGeneratedTask() { SubjectId = t.SubjectId, IsHaveGeneratedTask = t.IsHaveGeneratedTask }).ToListAsync();
|
||||
|
||||
return subjectList;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user