Compare commits
1167 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1e9efd6d40 | |||
| 80d78cb989 | |||
| 681ba17e37 | |||
| 3385b65967 | |||
| d6045ef7e7 | |||
| f66829ca62 | |||
| 471ea524b7 | |||
| 34e9269916 | |||
| 29d50fdd2d | |||
| 9f19dfceac | |||
| 799c579121 | |||
| 4841a9ac1e | |||
| 287dd5d0bf | |||
| 32e23ca0a2 | |||
| b7d7b8c815 | |||
| 96fd034c5a | |||
| 6118e0a4f8 | |||
| c00d1fb89c | |||
| 900f4dfb64 | |||
| 11f8c9ca56 | |||
| 026f63c730 | |||
| e322952ff4 | |||
| e762da2bf5 | |||
| 822a4331aa | |||
| 2b2ba2a551 | |||
| 317fbc2ea7 | |||
| 0cd9d666c1 | |||
| 187ea3b92f | |||
| d2beb399dd | |||
| 15623ab515 | |||
| f771a23c2a | |||
| f20b8708a3 | |||
| 536ed731ed | |||
| 5b10619460 | |||
| 311ac85419 | |||
| dba3351195 | |||
| 506d6504c7 | |||
| 1eede7832b | |||
| 5ec79bb61b | |||
| 548863845b | |||
| 2f73376ff6 | |||
| 9a8ee3a6aa | |||
| 28e367eb88 | |||
| 7d642b46c9 | |||
| 2be6757e23 | |||
| 4ca67212aa | |||
| 503f99ccbe | |||
| 93410b4075 | |||
| a96945201f | |||
| 35c495f8a8 | |||
| 9abd8e2c54 | |||
| b9a79f0658 | |||
| 83111d3338 | |||
| 92072d3640 | |||
| fd0ea313c2 | |||
| 79a15a08cc | |||
| 1af798fb04 | |||
| c19eb5bab7 | |||
| db693de576 | |||
| 651fa35018 | |||
| 36f1e9e8dd | |||
| 7e454467d1 | |||
| 978caf8e9c | |||
| 2bf4e5d8fd | |||
| dfb0c5a61c | |||
| 9d54fac8c5 | |||
| 5842590f6d | |||
| 06a60cac38 | |||
| dac5eb0d7d | |||
| 923edf166b | |||
| a0776e5462 | |||
| 9ac6d3df70 | |||
| a8a3104019 | |||
| 7cd476028a | |||
| fe96ff5924 | |||
| 890d349ee9 | |||
| 895eaa42c8 | |||
| 2e4457b56d | |||
| b30638fa82 | |||
| a3fac7bcb1 | |||
| 7228bdd9e9 | |||
| fa84171da4 | |||
| f4c3f685eb | |||
| db17d23360 | |||
| 76fe1e12d3 | |||
| d7f455c740 | |||
| d62d7172d0 | |||
| 9bfdb2f06a | |||
| d6436b01d4 | |||
| e848ee1972 | |||
| 2716b462b1 | |||
| 10f3db1ec2 | |||
| 0848a441b1 | |||
| 00c59958e6 | |||
| 9cc438a647 | |||
| 1053fdc8a8 | |||
| e1ee129f41 | |||
| dae0cab261 | |||
| 5871d054d3 | |||
| a69e8969a4 | |||
| dd05e26feb | |||
| 8cd8b7cf43 | |||
| 1d90aba7cd | |||
| 826aff3d36 | |||
| 718294f776 | |||
| 7a003eb490 | |||
| bcceb5d42d | |||
| b1802d7d79 | |||
| e14eeb4da3 | |||
| f726088cc9 | |||
| 4082850b7a | |||
| 641d61d242 | |||
| e2b67bb3f5 | |||
| 1f3e1ceb37 | |||
| 254e9508b6 | |||
| 631be6d7c7 | |||
| ca31e9a062 | |||
| 9fd7d9928a | |||
| 9362b2fb23 | |||
| 1735a4621e | |||
| 6ff332367d | |||
| e045302d38 | |||
| 88793cf011 | |||
| a4181f819b | |||
| 79aeb663c6 | |||
| 169301836a | |||
| 71bd67ba01 | |||
| 3e94dc6518 | |||
| 503322d57e | |||
| 0c9ffbf5e4 | |||
| fae7889995 | |||
| 532e514060 | |||
| b3a5047ba8 | |||
| 810e6ccb19 | |||
| 352e719fae | |||
| d8e0c73f56 | |||
| 46374be4c0 | |||
| 020c5280b3 | |||
| 4beba23f99 | |||
| 59f36cceb4 | |||
| d08b409f0e | |||
| 1612201101 | |||
| 924347361a | |||
| 061d212623 | |||
| af937b3f53 | |||
| 1faa72c5ea | |||
| db53e5a60b | |||
| 802e768bad | |||
| 67f17cffe0 | |||
| 70160cb937 | |||
| 6e22246555 | |||
| 7ed0258cfd | |||
| ddd06c8313 | |||
| d33b84c245 | |||
| 14cd5dd91c | |||
| 2b29ebc29b | |||
| 84b65ee122 | |||
| c3e9024a57 | |||
| d49b5663ba | |||
| e1386da5ae | |||
| b9f9af06f4 | |||
| 01a79e0612 | |||
| 3e69d72597 | |||
| f69c7af9d6 | |||
| ae03503d76 | |||
| 39eade67f0 | |||
| e6d7a0c0e0 | |||
| 98103451e9 | |||
| b64eed88cf | |||
| bafe61ae64 | |||
| 0579c4aeda | |||
| 7599e06b9d | |||
| 8e77d2e3ed | |||
| c2a129c56a | |||
| 111d5509bc | |||
| 7c9987f3de | |||
| f1471b6424 | |||
| 935369c05c | |||
| 2b03f382bd | |||
| 152a142d82 | |||
| f55ce703b0 | |||
| a08c8a0d64 | |||
| 76dd0c46ae | |||
| 1e8056f689 | |||
| d719015fa2 | |||
| 73fa62e6a2 | |||
| bb3f612772 | |||
| 99b6c63de7 | |||
| 8b046e61ab | |||
| ff2c619df7 | |||
| c9ffa64560 | |||
| 2969306d1e | |||
| 2fb558d388 | |||
| 9a0859f915 | |||
| 19bf8210f6 | |||
| 7a30fd0f28 | |||
| 432bf1ae12 | |||
| 967c0114d8 | |||
| 0f55c6da9d | |||
| 5e345418fb | |||
| de19c5bc32 | |||
| 1998842200 | |||
| 25a18e8366 | |||
| a2966e761c | |||
| 0508b8ffb3 | |||
| 9d4c69b39a | |||
| e11790ea92 | |||
| e559c2503b | |||
| c0a383ef58 | |||
| 9b6ddb0f16 | |||
| 45db942f66 | |||
| 41efcbfdf1 | |||
| 76c69a8464 | |||
| abe42ef3cf | |||
| 40f744ea4a | |||
| 7bc0d7f3dc | |||
| 9bbac291b6 | |||
| d7da7b69d4 | |||
| f6530cfb32 | |||
| 376dcc5bd5 | |||
| 89546b9874 | |||
| 385c40c895 | |||
| 8b9580abc8 | |||
| 3b9c8efeba | |||
| 5ba831c044 | |||
| 1141cc20e9 | |||
| 11a439a48f | |||
| 9ef3e53d77 | |||
| 104f46c9db | |||
| 0f0c670ec8 | |||
| c901f0e3d1 | |||
| 63e9428d6d | |||
| cbe7270f4b | |||
| fd67dad4b2 | |||
| 38b8f0085b | |||
| ab0e7afdb8 | |||
| 8045f96106 | |||
| 6481aa37e7 | |||
| 0b9ee87039 | |||
| 9b0d68d26d | |||
| 8ee7d7018f | |||
| 7c1c45417f | |||
| 1fbda0327c | |||
| 12f91c3b99 | |||
| 7d7c350df6 | |||
| caffff259e | |||
| 80b3defd37 | |||
| bbe1642786 | |||
| 4a2b9fdb41 | |||
| f7bc32ed90 | |||
| 592de4deb4 | |||
| ad5c36930e | |||
| c4570ebe62 | |||
| fdaf7180c0 | |||
| ee9152bbc4 | |||
| 413503e0f4 | |||
| 27c41af918 | |||
| fd21cf2539 | |||
| da485e1dde | |||
| 24dfa32ad4 | |||
| 1f9d9b2831 | |||
| 6211506aee | |||
| e1bd659dc5 | |||
| b56d45f411 | |||
| 35a2a0cc61 | |||
| ff43a5e71b | |||
| 8bdbc67535 | |||
| 85261bc3cf | |||
| 311359f6b8 | |||
| 0cb3646554 | |||
| cd02042d63 | |||
| db73a841b2 | |||
| a3e773cb4f | |||
| 09ed4adc93 | |||
| 530e4d412c | |||
| bff593394f | |||
| 32c38e9f95 | |||
| 005b0b9e0d | |||
| fdb124b12e | |||
| 652a446737 | |||
| 098196b599 | |||
| 01c5b96bc7 | |||
| 334743f3f0 | |||
| bd60f48cd1 | |||
| 4fe915cfd8 | |||
| 14cc78cffc | |||
| a04ae45402 | |||
| 0f68f170ad | |||
| 4033c8d004 | |||
| 68d320d9cf | |||
| 603a522c4b | |||
| 030aa36f94 | |||
| 577f35a3fe | |||
| 790ac40f4e | |||
| 44d9c23c0c | |||
| 547bd54085 | |||
| f38bcd2a09 | |||
| c2228d6d23 | |||
| 9ee85bfa3c | |||
| 434cdf8529 | |||
| f42ebcac61 | |||
| d594c6e52a | |||
| 9bb32c8779 | |||
| 13f983bc21 | |||
| 1fa81d1e4f | |||
| 9bd090da66 | |||
| e40a9fe9cb | |||
| 3200c71996 | |||
| 88dca637c2 | |||
| c65860796b | |||
| 8ea8572947 | |||
| 82ec00e9e9 | |||
| 0af8e2f017 | |||
| f56dcb0cce | |||
| 7957a33fc4 | |||
| e150fbb807 | |||
| 32a06be96d | |||
| f6c9490124 | |||
| e84c96f4ee | |||
| c330fdc145 | |||
| d38ce06f90 | |||
| fe34788cff | |||
| 8983cb59b4 | |||
| 3179fca112 | |||
| 9ad1bbdd7d | |||
| 74844bf9ce | |||
| 6296c54e2a | |||
| 4b8d9bf7bf | |||
| 7bfb6d2c5f | |||
| 9e2cede844 | |||
| d60229fa99 | |||
| 5c29eb5860 | |||
| 9a6220bda1 | |||
| 55b75d7c32 | |||
| 0b9115f6a6 | |||
| ce49d9ed45 | |||
| 13397b0bc8 | |||
| 388dac5f8d | |||
| ec0a4aeef9 | |||
| 8bf0be9699 | |||
| 7707b339dc | |||
| 303bf195d2 | |||
| 8eaa6606c4 | |||
| af1d4b3cd1 | |||
| 0fa16e2962 | |||
| 855f66566f | |||
| 913663068c | |||
| 6f05a9e456 | |||
| 5c86778902 | |||
| cfe9023ddd | |||
| 04815b9217 | |||
| 3a918f1aa5 | |||
| e3ee2cbe2f | |||
| 962f88f94d | |||
| 34f5978aea | |||
| 50576c4ba9 | |||
| b56bfcbfcf | |||
| 80cc578be9 | |||
| bb9c7066c7 | |||
| f3d70c0d12 | |||
| 372c6d5e0d | |||
| eca91b139b | |||
| 0d8626509c | |||
| e7548c08a6 | |||
| cfadc07b52 | |||
| 2f2f49c6c7 | |||
| 9a23c31313 | |||
| 27bdc95f6e | |||
| d74e3a9f60 | |||
| 10f8039b1b | |||
| f2adb16189 | |||
| d807fe0126 | |||
| fa464e6ed8 | |||
| 3c62d16ff8 | |||
| 2008a1f104 | |||
| 3716ad2f62 | |||
| 6c929043c1 | |||
| 3888b6f5d2 | |||
| b59415e461 | |||
| af160e3c96 | |||
| b5e6a81a52 | |||
| 58eb59bd61 | |||
| 104d44d757 | |||
| b29ed4d6a2 | |||
| 34008ef6db | |||
| a1dc17c582 | |||
| 01f339cb41 | |||
| 6315f1dc55 | |||
| 5df7c5c37d | |||
| dee2cae126 | |||
| 4d7ad9255a | |||
| ae5e9a75d6 | |||
| 53134ee4d2 | |||
| ea781dcd38 | |||
| 633a0bb777 | |||
| 3d83f18c71 | |||
| 89f6cd4244 | |||
| 1c154de18b | |||
| 6bb3be17ee | |||
| 8259e2fea3 | |||
| 152c5200cb | |||
| 99a1690481 | |||
| fa4564d491 | |||
| be5c25ba5f | |||
| 65e57aab7f | |||
| 5e04634837 | |||
| c9a8d7ad4c | |||
| 7c2ca13951 | |||
| ebf6c225f3 | |||
| 7083cc4493 | |||
| 711e2a53a5 | |||
| 5fd2f782c2 | |||
| 6147415cea | |||
| 6c34489aee | |||
| 1618f0454c | |||
| 1771e9731c | |||
| 1e509c0ec7 | |||
| 0d412c245f | |||
| 736266d8e8 | |||
| 05b2ab75a3 | |||
| e197a889d4 | |||
| 334e8f6267 | |||
| e430c40b99 | |||
| 1d6cb765cc | |||
| d7926a309a | |||
| cca587fb17 | |||
| 742b36ed4a | |||
| 50da5376b5 | |||
| d262057a73 | |||
| 797b076705 | |||
| 6633e26175 | |||
| 6c1c46601c | |||
| 411168be9c | |||
| efda03a0c9 | |||
| d4d632adce | |||
| 040ca6eb77 | |||
| 9bdfb355f1 | |||
| e392c436de | |||
| 294f59c4f3 | |||
| e6bdb51b5b | |||
| 5a7796939f | |||
| cad9eaf966 | |||
| 651af672f6 | |||
| 49375ec5a9 | |||
| 50f2a6a8b0 | |||
| 81280a3fed | |||
| 626de2e44a | |||
| 195d0d406c | |||
| 30ec92c805 | |||
| 9a276e3d3c | |||
| 75cceee734 | |||
| 00dcf84abe | |||
| 529033c5e4 | |||
| fecbc276c8 | |||
| f778451356 | |||
| 6b9f108fe1 | |||
| b848fbdd20 | |||
| beca7bbfc6 | |||
| a8af33a30a | |||
| 9a40cf66cc | |||
| 25f62a35ee | |||
| 24261a3dcb | |||
| 42e34b5e5c | |||
| 52f49994ab | |||
| 3f9e506d09 | |||
| 4c27e66870 | |||
| 0e909906be | |||
| 94bf9895af | |||
| b7f53f4036 | |||
| 49ffbad759 | |||
| a8f405ddc0 | |||
| 3396d73077 | |||
| 5ed24c209f | |||
| 14499009b6 | |||
| 77b2d7bed4 | |||
| 2d52102014 | |||
| 4e20a86155 | |||
| de0cb6788e | |||
| 3d8fc0776c | |||
| 38d1c2f92a | |||
| ada55aefd4 | |||
| 3bb1a9b46d | |||
| 751121e878 | |||
| 61c4d79fa1 | |||
| 22730f063c | |||
| 396244f044 | |||
| dccbe6be77 | |||
| f4fc51b66d | |||
| 7a8ec6221f | |||
| c84fed8af1 | |||
| 67216d1c92 | |||
| 24b30b1c4b | |||
| 62d065fccb | |||
| a5d1ad92c5 | |||
| bfe2478628 | |||
| dedab4e6ba | |||
| 52d1575890 | |||
| 3beac6177f | |||
| 3813b93dc5 | |||
| e0bf859cb2 | |||
| 8ac515acbd | |||
| aafc679e45 | |||
| f172a15e6e | |||
| c279733413 | |||
| 1b808595b8 | |||
| c833c88fe3 | |||
| 89fb8eac00 | |||
| 5c15967985 | |||
| 5ad84eebbf | |||
| e149a45981 | |||
| 4969e0f812 | |||
| bb7f6f3a1c | |||
| 991f1554ec | |||
| 02a1ffe8b7 | |||
| 56cbe08f39 | |||
| b048437b37 | |||
| 1353fed0ee | |||
| 8ec535be53 | |||
| 121ca01be3 | |||
| 229a83720f | |||
| 3bca306702 | |||
| 3c2701fe9d | |||
| 01058d42d3 | |||
| 59be897fd8 | |||
| 1a39d03c7e | |||
| 49eeee35bb | |||
| c1743c2934 | |||
| c906284f07 | |||
| 6427d1f5fb | |||
| 4b569e8fdb | |||
| 6b453648ee | |||
| 9cd09470d4 | |||
| 216d194d77 | |||
| 29600e9ac2 | |||
| 67fd739b0e | |||
| 083c68b528 | |||
| 121d3a3e64 | |||
| cfa38f2318 | |||
| 20f87c953c | |||
| 4f62f2f2d0 | |||
| ace5afca66 | |||
| 3dfffa557a | |||
| 2c878a90f8 | |||
| 03b5a01400 | |||
| d7e4a810f6 | |||
| 338ef9d988 | |||
| 65c67835f2 | |||
| d8d9e25191 | |||
| 3dd7859752 | |||
| 358699dbd3 | |||
| 2754973c8d | |||
| 8865db72d1 | |||
| 31158fed0e | |||
| ae624730aa | |||
| 3fd299bb49 | |||
| 9acfc4c544 | |||
| bbcab237d4 | |||
| 010367d018 | |||
| ea43698ee9 | |||
| 7155f6c985 | |||
| 3bdba35501 | |||
| ebcf3c95e2 | |||
| 75c3c9ee06 | |||
| 048193e4f3 | |||
| 7879504b71 | |||
| 93558164de | |||
| 83d123a31e | |||
| 733de4c942 | |||
| 0e7bd5f5dd | |||
| 0ef7ef675c | |||
| ee76366a1c | |||
| 369a0803f9 | |||
| 1f7b96d9f0 | |||
| 72ff2bbf15 | |||
| 0862182f8e | |||
| 746a8de799 | |||
| 37e8545eba | |||
| 570318cf3b | |||
| 7e13edc440 | |||
| 5579181f54 | |||
| 39b840f8fc | |||
| 02fb4c9753 | |||
| cf15883d52 | |||
| 84380a0d21 | |||
| 76993b4c8b | |||
| 0a4f00e63d | |||
| 9a52e30172 | |||
| 120d583a5a | |||
| dd8e244e64 | |||
| 5d7a083f4a | |||
| b7f22245e1 | |||
| 876deb102c | |||
| 49741e5c44 | |||
| 29ee2ba800 | |||
| 65e2bee680 | |||
| d45871a0db | |||
| ab3b9bddc8 | |||
| 7a5ad83cbb | |||
| 2d2e0f6f2f | |||
| 3b5a735abf | |||
| e00f8b3549 | |||
| dee8c90319 | |||
| 47fd242200 | |||
| 1181540302 | |||
| e6b3c662e2 | |||
| 3a06ecd66d | |||
| 7121799900 | |||
| c814736427 | |||
| 4ac9e83903 | |||
| 782270d462 | |||
| ad77b66bdd | |||
| 77538ab7c2 | |||
| 2e1e042658 | |||
| 6736fa96fd | |||
| a965ab9336 | |||
| 2ce112720a | |||
| f8b7e7d764 | |||
| 1aa1189ae8 | |||
| 9b90ded79f | |||
| 61ab33b3f3 | |||
| e6458e66df | |||
| 0ef7fd85d4 | |||
| 2bf47515e7 | |||
| 296ecfa9d2 | |||
| 473d10533a | |||
| 59ba1a6193 | |||
| cf2b3f5954 | |||
| 7fed9cdceb | |||
| 0585966215 | |||
| 7f9d6afc62 | |||
| 6dce301ff7 | |||
| 7a2d95a77c | |||
| 74a8ac7575 | |||
| e9c6d123d3 | |||
| 310c4a0f70 | |||
| 571f81f271 | |||
| c3a9ca3ca2 | |||
| 6874459046 | |||
| f4af6d3218 | |||
| ec87ed1caf | |||
| 0bb5d09a23 | |||
| f43ac9e649 | |||
| 9e3180b326 | |||
| 1011e704b9 | |||
| 0afcaa7d70 | |||
| 5fb889bac9 | |||
| febf8868f9 | |||
| 665d9a27fb | |||
| ccf16423d2 | |||
| a5155457b1 | |||
| e358601232 | |||
| 1514694e37 | |||
| fbd128138d | |||
| eca7871684 | |||
| 8eb31d0c81 | |||
| 051094a742 | |||
| 9012b9c35c | |||
| c49cd2e75e | |||
| f8b36df86d | |||
| 8e5b7fb57c | |||
| b5a76804ad | |||
| d4e79e0905 | |||
| 11e4c39dba | |||
| 02dd26188d | |||
| 4f439c6ee7 | |||
| bb36240c3c | |||
| e17b26b41d | |||
| 3fce7b772e | |||
| 8ca740bae3 | |||
| ead07cb1ca | |||
| 951081edb7 | |||
| 7ceca5d802 | |||
| 86a3f1d2a5 | |||
| b0a349b5ff | |||
| 7523f646fe | |||
| fcd0dd9b08 | |||
| 208cb40525 | |||
| aa2861c399 | |||
| 4ee064dd14 | |||
| 8803a5f5b2 | |||
| 834f6be548 | |||
| df8292b2a5 | |||
| 312c96ef8f | |||
| de29bd7d15 | |||
| 3a2c78fe37 | |||
| 4465e19987 | |||
| afe84fdb5b | |||
| 728727af54 | |||
| 987667f59f | |||
| af1ca2ffc1 | |||
| e71682f8fa | |||
| 339414694d | |||
| 3ad6fad018 | |||
| adf82e862d | |||
| c9e4f63e03 | |||
| ade2ffd9c0 | |||
| 3919d6a8a3 | |||
| 0fab295995 | |||
| aa7eac50a5 | |||
| 23e0fae6ac | |||
| 27233c1e01 | |||
| c16905101f | |||
| 6ef9708bb7 | |||
| cd9769b39c | |||
| 109bdd263e | |||
| c8e5a5173c | |||
| e94e875a8a | |||
| 9d7bd3c35b | |||
| 5725e684c0 | |||
| 7927ad4161 | |||
| e070a78872 | |||
| 1a6ec6b315 | |||
| 64ea537dae | |||
| 990cf0ec41 | |||
| 9cdd70f491 | |||
| a8623ade44 | |||
| c0e19eaab4 | |||
| 43241709af | |||
| 292263a60c | |||
| 760f75061e | |||
| e11ca4a244 | |||
| 0266072c6a | |||
| 99b76704d3 | |||
| b39edee3eb | |||
| 9e426308d0 | |||
| 416d76e03a | |||
| ae867ae230 | |||
| e83dc93dca | |||
| da220a3f5a | |||
| 8141c64f87 | |||
| 5aaedffe20 | |||
| 9ec53e39ec | |||
| c93807d67f | |||
| 6430f11188 | |||
| bc4c523d88 | |||
| b7aaeee13e | |||
| 504304c341 | |||
| 941bddb771 | |||
| 1990cebd58 | |||
| 041d6724e2 | |||
| 0086ed4723 | |||
| b83dd29b20 | |||
| aa01c68887 | |||
| 00f5fa993a | |||
| 6b98e23e50 | |||
| 3810b33c9e | |||
| e58b0e5315 | |||
| a2548c4f95 | |||
| 0151305ca3 | |||
| fbdac497fa | |||
| 052ac1ff8d | |||
| e113bc9fe1 | |||
| 03b7f00bcd | |||
| 3bedea2aba | |||
| 90a7e20be4 | |||
| 4a02ae2e98 | |||
| 9852cacb01 | |||
| eda79fd455 | |||
| 45af1d7ba6 | |||
| 7a4520086f | |||
| a252142cef | |||
| 12a0f7ad82 | |||
| 3ef3586bf7 | |||
| 4aecfb3bed | |||
| 9a31e04fad | |||
| af333e72f5 | |||
| 26f1a2b8ee | |||
| 497d91f4a4 | |||
| bd2d698d9f | |||
| b4c884a367 | |||
| 337dec37b8 | |||
| c12ecfdcf0 | |||
| 919e2bcdd7 | |||
| 943dbb28a6 | |||
| 0926da24f7 | |||
| 6ad80f540d | |||
| 77b9b14cb6 | |||
| b9c55871b8 | |||
| 848581452a | |||
| 2e53cdf29b | |||
| d9bc12c8d4 | |||
| 234f8b074b | |||
| 079c11a7e3 | |||
| 9d9d65e381 | |||
| 2517c57778 | |||
| 5d15ae6fe3 | |||
| f9fbb59c53 | |||
| c55f7a427c | |||
| 70b8c23e41 | |||
| 2591a794bf | |||
| 1c4be1012a | |||
| 030eeb663b | |||
| 4b3180bdfe | |||
| 28b7cdb187 | |||
| f57f6bb389 | |||
| 673abdfec2 | |||
| 1d4d068e08 | |||
| 72d83e9cfd | |||
| 883783250f | |||
| cc1ae55480 | |||
| 1bc3e77c1a | |||
| e24d865ce3 | |||
| 2762c83d84 | |||
| c58ad6142f | |||
| c45f5ca083 | |||
| fc5c792328 | |||
| 1b00209c55 | |||
| f199d18c4b | |||
| 3f1d07727b | |||
| 64d70269f9 | |||
| f0a71f8e70 | |||
| 8901479dd5 | |||
| 389456c46b | |||
| 40483dd4ee | |||
| fe1d7724bc | |||
| 9204183add | |||
| db9895077c | |||
| 36c086136f | |||
| f3c78c1892 | |||
| 6d538bf250 | |||
| 7a84eebf98 | |||
| 064101b155 | |||
| 91e7d04e4d | |||
| 711fa5a2b8 | |||
| 9f7bc4be75 | |||
| df7022b2e3 | |||
| 6ee51a7f43 | |||
| aed8a77e13 | |||
| bcfa22a818 | |||
| 0840d57689 | |||
| deb7e77263 | |||
| aba477a49c | |||
| a9e5a7754e | |||
| a2d4d100d3 | |||
| 7f4f5e9d08 | |||
| 55a2301bb5 | |||
| 14ac852eee | |||
| d2e53c7b8c | |||
| 4a335d2a28 | |||
| 999fde1f5c | |||
| 024e62103a | |||
| 73889c51ae | |||
| 7a0b29bda6 | |||
| a592a39fae | |||
| aa96ef66c6 | |||
| 2b24ce723e | |||
| 867ee32f91 | |||
| 662001accd | |||
| 334b2fe440 | |||
| 2f728629f3 | |||
| eb8628da5b | |||
| 1427940224 | |||
| 06f10e38f5 | |||
| 5092f5204f | |||
| 13f4420e42 | |||
| e0e6e60ae0 | |||
| 9cac49d539 | |||
| 97d9aefbeb | |||
| 340119f697 | |||
| a075eb34cd | |||
| 53913d3777 | |||
| c1d79a7e9f | |||
| 3ee6f0ceaa | |||
| 1821d82ded | |||
| 476ca95aa3 | |||
| d8b799f057 | |||
| 4c4807c22e | |||
| 8a9afc2d9a | |||
| 6fcb670a89 | |||
| d224b519f7 | |||
| b382595767 | |||
| 64ff6f73d4 | |||
| e1632b58ac | |||
| 7e39692cc4 | |||
| 8de44fb7b3 | |||
| 647aee010a | |||
| 296459d38a | |||
| 7941328a29 | |||
| 38d9a21a5d | |||
| 7d88f49e68 | |||
| 3c46215a3c | |||
| 137ed07b2b | |||
| 6f05850fbc | |||
| 80447f5c7a | |||
| 971d94495a | |||
| 77790a1cb2 | |||
| ecfadcb5a3 | |||
| d9e3b642a5 | |||
| e56e269f2e | |||
| 98f7ab8c02 | |||
| 5d33bd03a7 | |||
| 55882aa0ba | |||
| 43d1a94f22 | |||
| 53b2b9156f | |||
| fafc075735 | |||
| 09ee238e50 | |||
| 7be3813527 | |||
| e6491a6203 | |||
| 4955a24d6f | |||
| 425f53b74c | |||
| 2ed9595186 | |||
| 4d1e3beda6 | |||
| 8b0e1b8988 | |||
| c77682a9ad | |||
| 8e0ff1f2b2 | |||
| b5e027a553 | |||
| ef712949ad | |||
| b429b2cf8c | |||
| 9fb95c3030 | |||
| 6883d47508 | |||
| dae44745ca | |||
| 24b7d2e1b9 | |||
| 21ac6850d2 | |||
| 8b69a07b93 | |||
| f92bf347e3 | |||
| 78669051de | |||
| ea90b2b38b | |||
| bd61a0f2e9 | |||
| 888ad8897e | |||
| eb18021988 | |||
| 7b243a9615 | |||
| 5716c4169a | |||
| abb9f47646 | |||
| 9a5221ef7d | |||
| d9eaa06de3 | |||
| 4f91874b82 | |||
| 139438133d | |||
| 6705cf0fc0 | |||
| 00255e902c | |||
| 71e867156f | |||
| 658f437f73 | |||
| 95bf613263 | |||
| 03d61347ca | |||
| 050a0875fe | |||
| d9c46acb03 | |||
| f19fc0e92f | |||
| 3bc4b86749 | |||
| 43455da7bc | |||
| 0030eac22e | |||
| 671c071a45 | |||
| 3419015542 | |||
| 3eea6cf8b7 | |||
| 2c407c6e4e | |||
| ab90591e7c | |||
| 33a2b7f373 | |||
| 9158f28998 | |||
| 680a6cb437 | |||
| 21c19f4b6d | |||
| 8c03167f7e | |||
| b629b2eec4 | |||
| 01af8f72d3 | |||
| 4d10db054c | |||
| a019a542b0 | |||
| b42325337e | |||
| a3152bb6e1 | |||
| 4ec0441d28 | |||
| 9adc8a93b1 | |||
| 3f3d1a562b | |||
| 1b6ba91240 | |||
| c406976a6d | |||
| d213fa7a99 | |||
| 60707150a6 | |||
| 008906d245 | |||
| 762b8b7425 | |||
| 24228428fa | |||
| 9148651556 | |||
| 1c9c3eb893 | |||
| 17e0517e5b | |||
| 7662880352 | |||
| c3c433244e | |||
| df65d592ad | |||
| 7873523c1b | |||
| 8b5800b32e | |||
| 2e46791466 | |||
| fc0ef2e89f | |||
| 1de687cbf3 | |||
| 3a017dbfa3 | |||
| 1f828b403b | |||
| f75fafaa42 | |||
| 7c561a4656 | |||
| 033b849bdc | |||
| 8ae0b0d621 | |||
| b6bad061b7 | |||
| 1dd8515ac2 | |||
| 9f3bb29004 | |||
| 52dd676cf9 | |||
| 342b567e2c | |||
| 35ee76be0b | |||
| b4d40a4770 | |||
| 84bfcb4d3e | |||
| f030ed9b66 | |||
| c6920a1995 | |||
| 2d681ce6bc | |||
| 3981f2cd50 | |||
| 2b961c7985 | |||
| fb2fdd2c27 | |||
| f6bcefd1e8 | |||
| 1d7b28e7f9 | |||
| 3ea2bdfc92 | |||
| 4aa0161c41 | |||
| ff4ab39025 | |||
| bb35ae1b9a | |||
| 0e0ce59d74 | |||
| 82d3157e2a | |||
| 12cb53b167 | |||
| 5fc53a1999 | |||
| b712abd1d5 | |||
| a1c13b5850 | |||
| a0fe7be308 | |||
| bbfa966b57 | |||
| 6d11fc68b4 | |||
| eef764dd90 | |||
| 8423b00bec | |||
| dbaac006c7 | |||
| 510c73dd24 | |||
| 41486798d2 | |||
| f40faa5519 | |||
| eecfe6dcae | |||
| 8a004ef35f | |||
| 369805e6c6 | |||
| 93b93f6a3a | |||
| 9fbee7e0c9 | |||
| 46704d5299 | |||
| 4225014cdd | |||
| ae36d4680a | |||
| d48f736392 | |||
| b0d8a49929 | |||
| e45791c676 | |||
| ca425c602b | |||
| 44e7ffaeeb | |||
| cf42b2e57f | |||
| 840de08e69 | |||
| 929903aa1b | |||
| 5eafcc9c8c | |||
| a1657f5827 | |||
| dff2e712ad | |||
| a5f3430965 | |||
| 5059d66aa1 | |||
| 668af5ee0b | |||
| a70caefe01 | |||
| 64bd6a5604 | |||
| b68deafa82 | |||
| d29c150081 | |||
| 4d3ecd05dc | |||
| 143d7f0c4b | |||
| 4d2f04b30e | |||
| 955ca4a1a6 | |||
| 6e92895043 | |||
| 6102309828 | |||
| 8f34644d45 | |||
| f347b34f59 | |||
| dc7e71dc74 | |||
| 749bf95761 | |||
| a67472c9b4 | |||
| 32c8d70179 | |||
| 510dfcac30 | |||
| 9b59a55423 | |||
| 0bd269f93e | |||
| e55a13cfb1 | |||
| 1617de2fc1 | |||
| 40c3ea65ff | |||
| 541e000183 | |||
| 0e93894ed8 | |||
| 7f1621915f | |||
| 70e4829f32 | |||
| c77b3d74c8 | |||
| 1f4ea09bc6 | |||
| c45020f24f | |||
| ca679057c4 | |||
| 456793293b | |||
| 75d3908911 | |||
| a7fbd00530 | |||
| 785d5ec869 | |||
| c5feb98ed8 | |||
| 121b1d4eb4 | |||
| 8e2b99333c | |||
| 5bd055c77d | |||
| 5ea9d63cb9 | |||
| 0134d6c723 | |||
| a9c80a670b | |||
| 998b92aa79 | |||
| 60ce8c45b2 | |||
| a66d25f564 | |||
| 91b66660a6 | |||
| 1031222e47 | |||
| 2005eacaa9 | |||
| b581afebe6 | |||
| 1532ac4a7e | |||
| 231b3f71c5 | |||
| 308f5140a1 | |||
| ef673fc44b | |||
| f347b0cace | |||
| 102cf5f6dc | |||
| d9ca71009f | |||
| 114ee50273 | |||
| 54bb66f738 | |||
| 19f52bc133 | |||
| 2439dec9fb | |||
| fb0fd7d91f | |||
| 3765b390a6 | |||
| f6be3186e7 | |||
| 7399568e36 | |||
| 37d798627a | |||
| fe2a375b4e | |||
| 70443b7b8d | |||
| 7a37c01b7f | |||
| 7215c75f5a | |||
| a34c47dc3c | |||
| 6fdad9a482 | |||
| 08e62e4500 | |||
| 3cfb4556f3 | |||
| b8e3e83b80 | |||
| 7e9bf65f9f | |||
| 5197e49c1f | |||
| 7880836f39 | |||
| 487eb9dca8 | |||
| 473cee0e30 | |||
| 817cdcc8f7 | |||
| bf03430958 | |||
| 58321c4c82 | |||
| 8cabc8c96a | |||
| 7f050a1dd6 | |||
| e5e2a0394b | |||
| a56ab0680b | |||
| ee13d0ae2e | |||
| ee2c66c8fb | |||
| f1fb2068de | |||
| 95e0239e42 | |||
| 0b28ef5461 | |||
| 68fe2b75dc | |||
| ecfeee1693 | |||
| 9cda4360ca | |||
| 18b9c08101 | |||
| d4e89aec20 | |||
| 01a3a9057b | |||
| 2107a42e37 | |||
| d96e746db7 | |||
| be034d4d8b | |||
| ca44b4786a | |||
| 74056167d6 | |||
| e6aec6b559 | |||
| 9a488610da | |||
| f82420b7ef | |||
| fbeaec2820 | |||
| b4907bfe9b | |||
| 6e75e45942 | |||
| 3292b9f978 | |||
| 1959045ab2 | |||
| 6a99e3104a | |||
| ab91d360b1 | |||
| 50a4a82819 | |||
| 498c02431b | |||
| fd54d64ae3 | |||
| ac80e3a455 | |||
| c6a55949b3 | |||
| 18547cdec1 | |||
| d29707c8ee | |||
| c7e29116dd | |||
| a85cc1236c | |||
| 7b4c603fa6 | |||
| 67c93e654e | |||
| 9b0c874314 | |||
| d172e48c79 | |||
| 2a2419af90 | |||
| 7e39094b13 | |||
| 8936e9db99 | |||
| bd92d48232 |
@@ -0,0 +1,9 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<!-- 在这里定义所有项目共享的属性 -->
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<!-- 如果需要Nullable等全局设置,也可以加在这里 -->
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
@@ -1,36 +1,35 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.11" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2" />
|
||||
<PackageReference Include="AlibabaCloud.SDK.Sts20150401" Version="1.1.5" />
|
||||
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="10.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.8" />
|
||||
<PackageReference Include="AlibabaCloud.SDK.Sts20150401" Version="1.2.0" />
|
||||
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="11.0.0" />
|
||||
<PackageReference Include="Aliyun.OSS.SDK.NetCore" Version="2.14.1" />
|
||||
<PackageReference Include="AWSSDK.S3" Version="3.7.416.8" />
|
||||
<PackageReference Include="AWSSDK.SecurityToken" Version="3.7.401.81" />
|
||||
<PackageReference Include="DistributedLock.Core" Version="1.0.8" />
|
||||
<PackageReference Include="DistributedLock.SqlServer" Version="1.0.6" />
|
||||
<PackageReference Include="fo-dicom" Version="5.2.1" />
|
||||
<PackageReference Include="fo-dicom.Codecs" Version="5.16.1" />
|
||||
<PackageReference Include="fo-dicom.Imaging.ImageSharp" Version="5.2.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.10" />
|
||||
<PackageReference Include="AutoMapper" Version="13.0.1" />
|
||||
<PackageReference Include="Minio" Version="6.0.4" />
|
||||
<PackageReference Include="My.Extensions.Localization.Json" Version="3.3.0">
|
||||
<PackageReference Include="AWSSDK.S3" Version="4.0.24" />
|
||||
<PackageReference Include="AWSSDK.SecurityToken" Version="4.0.7" />
|
||||
|
||||
<PackageReference Include="DistributedLock.Core" Version="1.0.9" />
|
||||
<PackageReference Include="DistributedLock.SqlServer" Version="1.0.7" />
|
||||
<PackageReference Include="fo-dicom" Version="5.2.6" />
|
||||
<PackageReference Include="fo-dicom.Codecs" Version="5.16.7" />
|
||||
<PackageReference Include="fo-dicom.Imaging.ImageSharp" Version="5.2.6" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="10.0.8" />
|
||||
<PackageReference Include="AutoMapper" Version="14.0.0" />
|
||||
<PackageReference Include="Minio" Version="7.0.0" />
|
||||
<PackageReference Include="My.Extensions.Localization.Json" Version="4.0.0">
|
||||
<TreatAsUsed>true</TreatAsUsed>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Panda.DynamicWebApi" Version="1.2.2" />
|
||||
<PackageReference Include="Serilog.Enrichers.ClientInfo" Version="2.1.2" />
|
||||
<PackageReference Include="Serilog.Extensions.Hosting" Version="9.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="8.1.1" />
|
||||
<PackageReference Include="Serilog.Enrichers.ClientInfo" Version="2.9.0" />
|
||||
<PackageReference Include="Serilog.Extensions.Hosting" Version="10.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.Console" Version="6.1.1" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="7.0.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="10.2.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -56,7 +56,7 @@ var _configuration = builder.Configuration;
|
||||
builder.Services.AddHealthChecks();
|
||||
|
||||
//本地化
|
||||
builder.Services.AddJsonLocalization(options => options.ResourcesPath = "Resources");
|
||||
builder.Services.AddJsonLocalization(options => options.ResourcesPath = new[] { "Resources" });
|
||||
|
||||
|
||||
// 异常、参数统一验证过滤器、Json序列化配置、字符串参数绑型统一Trim()
|
||||
|
||||
@@ -118,7 +118,7 @@ public class AWSTempToken
|
||||
public string SecretAccessKey { get; set; }
|
||||
public string BucketName { get; set; }
|
||||
public string ViewEndpoint { get; set; }
|
||||
public DateTime Expiration { get; set; }
|
||||
public DateTime? Expiration { get; set; }
|
||||
}
|
||||
|
||||
public enum ObjectStoreUse
|
||||
@@ -231,8 +231,8 @@ public class OSSService : IOSSService
|
||||
//提供awsEndPoint(域名)进行访问配置
|
||||
var clientConfig = new AmazonS3Config
|
||||
{
|
||||
RegionEndpoint = RegionEndpoint.USEast1,
|
||||
UseHttp = true,
|
||||
RegionEndpoint = RegionEndpoint.GetBySystemName(awsConfig.Region)
|
||||
//,UseHttp = true,
|
||||
};
|
||||
|
||||
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
|
||||
@@ -320,8 +320,8 @@ public class OSSService : IOSSService
|
||||
//提供awsEndPoint(域名)进行访问配置
|
||||
var clientConfig = new AmazonS3Config
|
||||
{
|
||||
RegionEndpoint = RegionEndpoint.USEast1,
|
||||
UseHttp = true,
|
||||
RegionEndpoint = RegionEndpoint.GetBySystemName(awsConfig.Region)
|
||||
//,UseHttp = true,
|
||||
};
|
||||
|
||||
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
|
||||
@@ -397,8 +397,8 @@ public class OSSService : IOSSService
|
||||
//提供awsEndPoint(域名)进行访问配置
|
||||
var clientConfig = new AmazonS3Config
|
||||
{
|
||||
RegionEndpoint = RegionEndpoint.USEast1,
|
||||
UseHttp = true,
|
||||
RegionEndpoint = RegionEndpoint.GetBySystemName(awsConfig.Region)
|
||||
//,UseHttp = true,
|
||||
};
|
||||
|
||||
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
|
||||
@@ -493,8 +493,8 @@ public class OSSService : IOSSService
|
||||
//提供awsEndPoint(域名)进行访问配置
|
||||
var clientConfig = new AmazonS3Config
|
||||
{
|
||||
RegionEndpoint = RegionEndpoint.USEast1,
|
||||
UseHttp = true,
|
||||
RegionEndpoint = RegionEndpoint.GetBySystemName(awsConfig.Region)
|
||||
//,UseHttp = true,
|
||||
};
|
||||
|
||||
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
|
||||
@@ -625,8 +625,8 @@ public class OSSService : IOSSService
|
||||
//提供awsEndPoint(域名)进行访问配置
|
||||
var clientConfig = new AmazonS3Config
|
||||
{
|
||||
RegionEndpoint = RegionEndpoint.USEast1,
|
||||
UseHttp = true,
|
||||
RegionEndpoint = RegionEndpoint.GetBySystemName(awsConfig.Region)
|
||||
//,UseHttp = true,
|
||||
};
|
||||
|
||||
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
|
||||
|
||||
@@ -0,0 +1,175 @@
|
||||
using IRaCIS.Core.Application.Helper;
|
||||
using IRaCIS.Core.Application.Service;
|
||||
using IRaCIS.Core.Domain.Models;
|
||||
using IRaCIS.Core.Infra.EFCore;
|
||||
using IRaCIS.Core.SCP.Service;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace IRaCIS.Core.SCP;
|
||||
|
||||
|
||||
public class SyncFileRecoveryService(IServiceScopeFactory _scopeFactory, FileSyncQueue _fileSyncQueue) : BackgroundService
|
||||
{
|
||||
|
||||
private readonly int _pageSize = 500;
|
||||
|
||||
/// <summary>
|
||||
/// 多个程序,如果恢复同一份数据,造成重复同步,SCP服务不恢复任务
|
||||
/// </summary>
|
||||
/// <param name="stoppingToken"></param>
|
||||
/// <returns></returns>
|
||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
//在本地调试的时候,不干涉部署同步任务
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
await Task.CompletedTask;
|
||||
|
||||
//using var scope = _scopeFactory.CreateScope();
|
||||
//var fileUploadRecordRepository = scope.ServiceProvider.GetRequiredService<IRepository<FileUploadRecord>>();
|
||||
|
||||
//// 延迟启动,保证主机快速启动
|
||||
//await Task.Delay(5000, stoppingToken);
|
||||
|
||||
//int page = 0;
|
||||
|
||||
|
||||
//while (!stoppingToken.IsCancellationRequested)
|
||||
//{
|
||||
// // 分页获取未入队任务
|
||||
// var pending = await fileUploadRecordRepository
|
||||
// .Where(x => x.IsNeedSync == true && (x.IsSync == false || x.IsSync == null))
|
||||
// .OrderByDescending(x => x.Priority)
|
||||
// .Select(t => new { t.Id, t.Priority })
|
||||
// .Skip(page * _pageSize)
|
||||
// .Take(_pageSize)
|
||||
// .ToListAsync(stoppingToken);
|
||||
|
||||
// if (!pending.Any())
|
||||
// break; // 扫描完毕,退出循环
|
||||
|
||||
// foreach (var file in pending)
|
||||
// {
|
||||
// //file.IsQueued = true; // 避免重复入队
|
||||
// _fileSyncQueue.Enqueue(file.Id, file.Priority ?? 0); // 放入队列
|
||||
// }
|
||||
|
||||
// page++; // 下一页
|
||||
// await Task.Delay(200, stoppingToken); // 缓解数据库压力
|
||||
//}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public class FileSyncWorker(IServiceScopeFactory _scopeFactory, ILogger<FileSyncWorker> _logger, FileSyncQueue _fileSyncQueue) : BackgroundService
|
||||
{
|
||||
|
||||
// ⭐ 自动根据服务器CPU
|
||||
private readonly int _workerCount = Math.Max(1, Environment.ProcessorCount - 1);
|
||||
|
||||
|
||||
|
||||
protected override Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
for (int i = 0; i < _workerCount; i++)
|
||||
Task.Run(() => WorkerLoop(stoppingToken));
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
|
||||
private async Task WorkerLoop(CancellationToken stoppingToken)
|
||||
{
|
||||
while (!stoppingToken.IsCancellationRequested)
|
||||
{
|
||||
var id = await _fileSyncQueue.DequeueAsync(stoppingToken);
|
||||
|
||||
try
|
||||
{
|
||||
using var scope = _scopeFactory.CreateScope();
|
||||
var _fileUploadRecordRepository = scope.ServiceProvider.GetRequiredService<IRepository<FileUploadRecord>>();
|
||||
var _uploadFileSyncRecordRepository = scope.ServiceProvider.GetRequiredService<IRepository<UploadFileSyncRecord>>();
|
||||
|
||||
var syncConfig = (scope.ServiceProvider.GetRequiredService<IOptionsMonitor<ObjectStoreServiceOptions>>()).CurrentValue;
|
||||
|
||||
var oss = scope.ServiceProvider.GetRequiredService<IOSSService>();
|
||||
|
||||
var file = await _fileUploadRecordRepository.FirstOrDefaultAsync(t => t.Id == id);
|
||||
|
||||
// ✅ 不要 return
|
||||
if (file == null || file.IsNeedSync != true || syncConfig.IsOpenStoreSync == false)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (syncConfig.SyncConfigList.Any(t => t.UploadRegion == file.UploadRegion && t.IsOpenSync == false))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
//如果发现系统配置某一边同步进行了关闭,那么就直接返回,不执行任务
|
||||
if (syncConfig.SyncConfigList.Any(t => t.UploadRegion == file.UploadRegion && t.IsOpenSync == false))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var log = new UploadFileSyncRecord
|
||||
{
|
||||
FileUploadRecordId = id,
|
||||
StartTime = DateTime.Now,
|
||||
JobState = jobState.RUNNING
|
||||
};
|
||||
|
||||
await _uploadFileSyncRecordRepository.AddAsync(log);
|
||||
await _uploadFileSyncRecordRepository.SaveChangesAsync(stoppingToken);
|
||||
|
||||
try
|
||||
{
|
||||
await oss.SyncFileAsync(file.Path.TrimStart('/'), file.UploadRegion == "CN" ? ObjectStoreUse.AliyunOSS : ObjectStoreUse.AWS, file.UploadRegion == "CN" ? ObjectStoreUse.AWS : ObjectStoreUse.AliyunOSS);
|
||||
|
||||
file.IsSync = true;
|
||||
file.SyncFinishedTime = DateTime.Now;
|
||||
|
||||
log.JobState = jobState.SUCCESS;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
log.JobState = jobState.FAILED;
|
||||
|
||||
log.Msg = ex.Message.Length > 300 ? ex.Message.Substring(0, 300) : ex.Message;
|
||||
}
|
||||
|
||||
log.EndTime = DateTime.Now;
|
||||
|
||||
await _uploadFileSyncRecordRepository.SaveChangesAsync(stoppingToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Sync failed {Id}", id);
|
||||
}
|
||||
finally
|
||||
{
|
||||
// ⭐⭐⭐ 永远执行
|
||||
_fileSyncQueue.Complete(id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,34 +1,32 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AlibabaCloud.SDK.Sts20150401" Version="1.1.5" />
|
||||
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="10.0.0" />
|
||||
<PackageReference Include="AlibabaCloud.SDK.Sts20150401" Version="1.2.0" />
|
||||
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="11.0.0" />
|
||||
<PackageReference Include="Aliyun.OSS.SDK.NetCore" Version="2.14.1" />
|
||||
<PackageReference Include="AWSSDK.S3" Version="3.7.416.8" />
|
||||
<PackageReference Include="AWSSDK.SecurityToken" Version="3.7.401.81" />
|
||||
<PackageReference Include="DistributedLock.Core" Version="1.0.8" />
|
||||
<PackageReference Include="DistributedLock.SqlServer" Version="1.0.6" />
|
||||
<PackageReference Include="fo-dicom" Version="5.2.1" />
|
||||
<PackageReference Include="fo-dicom.Codecs" Version="5.16.1" />
|
||||
<PackageReference Include="fo-dicom.Imaging.ImageSharp" Version="5.2.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.10" />
|
||||
<PackageReference Include="AutoMapper" Version="13.0.1" />
|
||||
<PackageReference Include="Minio" Version="6.0.3" />
|
||||
<PackageReference Include="My.Extensions.Localization.Json" Version="3.3.0">
|
||||
<PackageReference Include="AWSSDK.S3" Version="4.0.24" />
|
||||
<PackageReference Include="AWSSDK.SecurityToken" Version="4.0.7" />
|
||||
<PackageReference Include="DistributedLock.Core" Version="1.0.9" />
|
||||
<PackageReference Include="DistributedLock.SqlServer" Version="1.0.7" />
|
||||
<PackageReference Include="fo-dicom" Version="5.2.6" />
|
||||
<PackageReference Include="fo-dicom.Codecs" Version="5.16.7" />
|
||||
<PackageReference Include="fo-dicom.Imaging.ImageSharp" Version="5.2.6" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="10.0.8" />
|
||||
<PackageReference Include="AutoMapper" Version="14.0.0" />
|
||||
<PackageReference Include="Minio" Version="7.0.0" />
|
||||
<PackageReference Include="My.Extensions.Localization.Json" Version="4.0.0">
|
||||
<TreatAsUsed>true</TreatAsUsed>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Panda.DynamicWebApi" Version="1.2.2" />
|
||||
<PackageReference Include="Serilog.Enrichers.ClientInfo" Version="2.1.2" />
|
||||
<PackageReference Include="Serilog.Extensions.Hosting" Version="9.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="8.1.1" />
|
||||
<PackageReference Include="Serilog.Enrichers.ClientInfo" Version="2.9.0" />
|
||||
<PackageReference Include="Serilog.Extensions.Hosting" Version="10.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.Console" Version="6.1.1" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="7.0.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="10.2.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -13,7 +13,6 @@ 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;
|
||||
@@ -63,11 +62,15 @@ builder.Host
|
||||
#region 配置服务
|
||||
var _configuration = builder.Configuration;
|
||||
|
||||
builder.Services.AddSingleton<FileSyncQueue>();
|
||||
builder.Services.AddHostedService<SyncFileRecoveryService>();
|
||||
builder.Services.AddHostedService<FileSyncWorker>();
|
||||
|
||||
//健康检查
|
||||
builder.Services.AddHealthChecks();
|
||||
|
||||
//本地化
|
||||
builder.Services.AddJsonLocalization(options => options.ResourcesPath = "Resources");
|
||||
builder.Services.AddJsonLocalization(options => options.ResourcesPath = new[] { "Resources" });
|
||||
|
||||
|
||||
// 异常、参数统一验证过滤器、Json序列化配置、字符串参数绑型统一Trim()
|
||||
@@ -113,6 +116,9 @@ builder.Services.AddAutoMapper(automapper =>
|
||||
//EF ORM QueryWithNoLock
|
||||
builder.Services.AddEFSetup(_configuration);
|
||||
|
||||
// FusionCache
|
||||
builder.Services.AddFusionCache();
|
||||
|
||||
builder.Services.AddMediator(cfg =>
|
||||
{
|
||||
|
||||
@@ -191,6 +197,7 @@ app.MapControllers();
|
||||
Log.Logger = new LoggerConfiguration()
|
||||
//.MinimumLevel.Information()
|
||||
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
|
||||
.MinimumLevel.Override("ZiggyCreatures.Caching.Fusion", LogEventLevel.Warning)
|
||||
.WriteTo.Console()
|
||||
.WriteTo.File($"{AppContext.BaseDirectory}Serilogs/.log", rollingInterval: RollingInterval.Day)
|
||||
.CreateLogger();
|
||||
|
||||
@@ -1,28 +1,30 @@
|
||||
using FellowOakDicom.Network;
|
||||
using FellowOakDicom;
|
||||
using FellowOakDicom;
|
||||
using FellowOakDicom.Imaging;
|
||||
using FellowOakDicom.IO.Buffer;
|
||||
using FellowOakDicom.Network;
|
||||
using IRaCIS.Core.Domain.Models;
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using IRaCIS.Core.Infra.EFCore;
|
||||
using IRaCIS.Core.Infrastructure;
|
||||
using IRaCIS.Core.Infrastructure.Extention;
|
||||
using IRaCIS.Core.SCP.Service;
|
||||
using MassTransit;
|
||||
using Medallion.Threading;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion.Internal;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Serilog;
|
||||
using SharpCompress.Common;
|
||||
using SixLabors.ImageSharp.Formats.Jpeg;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
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;
|
||||
using IRaCIS.Core.Infrastructure.Extention;
|
||||
using FellowOakDicom.IO.Buffer;
|
||||
using ZiggyCreatures.Caching.Fusion;
|
||||
|
||||
namespace IRaCIS.Core.SCP.Service
|
||||
{
|
||||
@@ -51,6 +53,8 @@ namespace IRaCIS.Core.SCP.Service
|
||||
|
||||
private Guid _trialSiteId { get; set; }
|
||||
|
||||
private bool _releasedNormally = false;
|
||||
|
||||
|
||||
|
||||
private static readonly DicomTransferSyntax[] _acceptedTransferSyntaxes = new DicomTransferSyntax[]
|
||||
@@ -92,7 +96,7 @@ namespace IRaCIS.Core.SCP.Service
|
||||
public Task OnReceiveAssociationRequestAsync(DicomAssociation association)
|
||||
{
|
||||
|
||||
_upload = new SCPImageUpload() { StartTime = DateTime.Now, CallingAE = association.CallingAE, CalledAE = association.CalledAE, CallingAEIP = association.RemoteHost };
|
||||
_upload = new SCPImageUpload() { Id = NewId.NextSequentialGuid(), StartTime = DateTime.Now, CallingAE = association.CallingAE, CalledAE = association.CalledAE, CallingAEIP = association.RemoteHost };
|
||||
|
||||
|
||||
Log.Logger.Warning($"接收到来自{association.CallingAE}的连接");
|
||||
@@ -118,7 +122,7 @@ namespace IRaCIS.Core.SCP.Service
|
||||
var _trialSiteDicomAERepository = _serviceProvider.GetService<IRepository<TrialSiteDicomAE>>();
|
||||
|
||||
|
||||
var findTrialSiteAE = _trialSiteDicomAERepository.Where(t => t.CallingAE == association.CallingAE && t.TrialId==_trialId).FirstOrDefault();
|
||||
var findTrialSiteAE = _trialSiteDicomAERepository.Where(t => t.CallingAE == association.CallingAE && t.TrialId == _trialId).FirstOrDefault();
|
||||
|
||||
if (findTrialSiteAE != null)
|
||||
{
|
||||
@@ -127,7 +131,7 @@ namespace IRaCIS.Core.SCP.Service
|
||||
isCanReceiveIamge = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
if (association.CallingAE == "test-callingAE")
|
||||
@@ -180,12 +184,15 @@ namespace IRaCIS.Core.SCP.Service
|
||||
await AddUploadLogAsync();
|
||||
|
||||
|
||||
_releasedNormally = true;
|
||||
|
||||
var _studyRepository = _serviceProvider.GetService<IRepository<SCPStudy>>();
|
||||
//将检查设置为传输结束
|
||||
await _studyRepository.BatchUpdateNoTrackingAsync(t => _SCPStudyIdList.Contains(t.Id), u => new SCPStudy() { IsUploadFinished = true });
|
||||
Log.Logger.Information($"进入释放连接请求 {_releasedNormally}");
|
||||
|
||||
await _studyRepository.SaveChangesAndClearAllTrackingAsync();
|
||||
//var _studyRepository = _serviceProvider.GetService<IRepository<SCPStudy>>();
|
||||
////将检查设置为传输结束
|
||||
//await _studyRepository.BatchUpdateNoTrackingAsync(t => _SCPStudyIdList.Contains(t.Id), u => new SCPStudy() { IsUploadFinished = true });
|
||||
|
||||
//await _studyRepository.SaveChangesAndClearAllTrackingAsync();
|
||||
|
||||
}
|
||||
|
||||
@@ -199,15 +206,25 @@ namespace IRaCIS.Core.SCP.Service
|
||||
|
||||
var _SCPImageUploadRepository = _serviceProvider.GetService<IRepository<SCPImageUpload>>();
|
||||
|
||||
_upload.EndTime = DateTime.Now;
|
||||
_upload.StudyCount = _ImageUploadList.Count;
|
||||
_upload.TrialId = _trialId;
|
||||
_upload.TrialSiteId = _trialSiteId;
|
||||
//有时候没有建立连接会走关闭通过日志发现
|
||||
if (_upload != null)
|
||||
{
|
||||
_upload.EndTime = DateTime.Now;
|
||||
_upload.StudyCount = _ImageUploadList.Count;
|
||||
_upload.TrialId = _trialId;
|
||||
_upload.TrialSiteId = _trialSiteId;
|
||||
|
||||
_upload.UploadJsonStr = (new SCPImageLog() { UploadList = _ImageUploadList }).ToJsonStr();
|
||||
|
||||
//可能是测试echo 导致记录了
|
||||
if (_upload.FileCount > 0)
|
||||
{
|
||||
//可能是测试echo 导致记录了
|
||||
await _SCPImageUploadRepository.AddAsync(_upload, true);
|
||||
}
|
||||
}
|
||||
|
||||
_upload.UploadJsonStr = (new SCPImageLog() { UploadList = _ImageUploadList }).ToJsonStr();
|
||||
|
||||
//可能是测试echo 导致记录了
|
||||
await _SCPImageUploadRepository.AddAsync(_upload, _upload.FileCount > 0 ? true : false);
|
||||
}
|
||||
|
||||
|
||||
@@ -262,22 +279,25 @@ namespace IRaCIS.Core.SCP.Service
|
||||
|
||||
public async void OnConnectionClosed(Exception exception)
|
||||
{
|
||||
/* nothing to do here */
|
||||
var _studyRepository = _serviceProvider.GetService<IRepository<SCPStudy>>();
|
||||
|
||||
//奇怪的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();
|
||||
}
|
||||
else
|
||||
if (exception != null || _releasedNormally == false)
|
||||
{
|
||||
//客户端断网,恢复后,也是没有异常的,估计是超时走了关闭
|
||||
|
||||
await _studyRepository.BatchUpdateNoTrackingAsync(t => _SCPStudyIdList.Contains(t.Id), u => new SCPStudy() { IsUploadFinished = true, IsUploadFaild = true });
|
||||
//记录日志
|
||||
await AddUploadLogAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
//将检查设置为传输结束
|
||||
await _studyRepository.BatchUpdateNoTrackingAsync(t => _SCPStudyIdList.Contains(t.Id), u => new SCPStudy() { IsUploadFinished = true, IsUploadFaild = false });
|
||||
}
|
||||
|
||||
|
||||
await _studyRepository.SaveChangesAndClearAllTrackingAsync();
|
||||
|
||||
Log.Logger.Warning($"连接关闭 {exception?.Message} {exception?.InnerException?.Message}");
|
||||
}
|
||||
@@ -316,6 +336,11 @@ namespace IRaCIS.Core.SCP.Service
|
||||
var _seriesRepository = _serviceProvider.GetService<IRepository<SCPSeries>>();
|
||||
|
||||
var _distributedLockProvider = _serviceProvider.GetService<IDistributedLockProvider>();
|
||||
var _fusionCache = _serviceProvider.GetService<IFusionCache>();
|
||||
var _trialSiteRepository = _serviceProvider.GetService<IRepository<TrialSite>>();
|
||||
var _systemAnonymizationRepository = _serviceProvider.GetService<IRepository<SystemAnonymization>>();
|
||||
|
||||
|
||||
|
||||
var storeRelativePath = string.Empty;
|
||||
var ossFolderPath = $"{_trialId}/Image/PACS/{_trialSiteId}/{studyInstanceUid}";
|
||||
@@ -324,74 +349,383 @@ namespace IRaCIS.Core.SCP.Service
|
||||
long fileSize = 0;
|
||||
try
|
||||
{
|
||||
using (MemoryStream ms = new MemoryStream())
|
||||
// 直接拿 Dataset(已经完整)
|
||||
var dataset = request.Dataset;
|
||||
|
||||
#region 匿名化
|
||||
|
||||
|
||||
var anonymizeList = await _fusionCache.GetOrSetAsync(CacheKeys.SystemAnonymization, _ => CacheHelper.GetSystemAnonymizationListAsync(_systemAnonymizationRepository), TimeSpan.FromDays(7));
|
||||
|
||||
var trialSiteInfo = await _fusionCache.GetOrSetAsync(CacheKeys.TrialSiteInfo(_trialSiteId), _ => CacheHelper.GetTrialSiteInfo(_trialSiteId, _trialSiteRepository), TimeSpan.FromMinutes(2));
|
||||
|
||||
var fixedFiledList = anonymizeList.Where(t => t.IsFixed).ToList();
|
||||
|
||||
var ircFiledList = anonymizeList.Where(t => t.IsFixed == false).ToList();
|
||||
|
||||
foreach (var item in fixedFiledList)
|
||||
{
|
||||
await request.File.SaveAsync(ms);
|
||||
|
||||
#region 1帧拆成多个固定大小的,方便移动端浏览
|
||||
var dicomTag = new DicomTag(Convert.ToUInt16(item.Group, 16), Convert.ToUInt16(item.Element, 16));
|
||||
|
||||
// 回到开头,读取 dicom
|
||||
ms.Position = 0;
|
||||
var dicomFile = DicomFile.Open(ms);
|
||||
dataset.AddOrUpdate(dicomTag, item.ReplaceValue);
|
||||
}
|
||||
|
||||
var pixelData = DicomPixelData.Create(dicomFile.Dataset);
|
||||
var syntax = pixelData.Syntax;
|
||||
foreach (var item in ircFiledList)
|
||||
{
|
||||
|
||||
// 每个 fragment 固定大小 (64KB 示例,可以自己调整)
|
||||
int fragmentSize = 20 * 1024;
|
||||
var dicomTag = new DicomTag(Convert.ToUInt16(item.Group, 16), Convert.ToUInt16(item.Element, 16));
|
||||
|
||||
if (syntax.IsEncapsulated)
|
||||
if (dicomTag == DicomTag.ClinicalTrialProtocolID)
|
||||
{
|
||||
var newFragments = new DicomOtherByteFragment(DicomTag.PixelData);
|
||||
dataset.AddOrUpdate(DicomTag.ClinicalTrialProtocolID, trialSiteInfo.TrialCode);
|
||||
|
||||
for (int n = 0; n < pixelData.NumberOfFrames; n++)
|
||||
}
|
||||
if (dicomTag == DicomTag.ClinicalTrialSiteID)
|
||||
{
|
||||
dataset.AddOrUpdate(DicomTag.ClinicalTrialSiteID, trialSiteInfo.TrialSiteCode);
|
||||
|
||||
}
|
||||
|
||||
if (dicomTag == DicomTag.ClinicalTrialSubjectID)
|
||||
{
|
||||
dataset.AddOrUpdate(DicomTag.ClinicalTrialSubjectID, "");
|
||||
|
||||
}
|
||||
if (dicomTag == DicomTag.ClinicalTrialTimePointID)
|
||||
{
|
||||
dataset.AddOrUpdate(DicomTag.ClinicalTrialTimePointID, "");
|
||||
}
|
||||
|
||||
if (dicomTag == DicomTag.PatientID)
|
||||
{
|
||||
var pid = dataset.GetSingleValueOrDefault(DicomTag.PatientID, string.Empty);
|
||||
|
||||
dataset.AddOrUpdate(DicomTag.PatientID, trialSiteInfo.TrialCode + "-" + pid);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
#endregion
|
||||
|
||||
// 构造 DicomFile(不用 Open)
|
||||
var dicomFile = new DicomFile(dataset);
|
||||
|
||||
|
||||
|
||||
#region 1帧拆成多个固定大小的,方便移动端浏览
|
||||
|
||||
|
||||
|
||||
var numberOfFrames = dicomFile.Dataset.GetSingleValueOrDefault(DicomTag.NumberOfFrames, 1);
|
||||
|
||||
//多帧处理逻辑
|
||||
if (numberOfFrames > 1)
|
||||
{
|
||||
//一定要有像素数据才处理
|
||||
var pixelData = DicomPixelData.Create(dicomFile.Dataset);
|
||||
|
||||
if (pixelData != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var frameData = pixelData.GetFrame(n); // 获取完整一帧
|
||||
var data = frameData.Data;
|
||||
int offset = 0;
|
||||
|
||||
while (offset < data.Length)
|
||||
Log.Logger.Warning($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE} 开始处理多帧instanceId:{instanceId}");
|
||||
|
||||
var syntax = pixelData.Syntax;
|
||||
|
||||
// 每个 fragment 固定大小 (64KB 示例,可以自己调整)
|
||||
int fragmentSize = 20 * 1024;
|
||||
|
||||
|
||||
|
||||
var frag = dicomFile.Dataset.GetDicomItem<DicomOtherByteFragment>(DicomTag.PixelData);
|
||||
|
||||
int fragmentCount = frag?.Fragments?.Count() ?? 0;
|
||||
|
||||
var originOffsetTable = frag?.OffsetTable; //有可能没有表,需要自己重建
|
||||
|
||||
var bot = new List<uint>();
|
||||
|
||||
uint botOffset = 0;
|
||||
|
||||
//需要拆成固定片段的
|
||||
if (syntax.IsEncapsulated && fragmentCount == pixelData.NumberOfFrames && numberOfFrames > 1)
|
||||
{
|
||||
int size = Math.Min(fragmentSize, data.Length - offset);
|
||||
var buffer = new byte[size];
|
||||
Buffer.BlockCopy(data, offset, buffer, 0, size);
|
||||
|
||||
newFragments.Fragments.Add(new MemoryByteBuffer(buffer));
|
||||
offset += size;
|
||||
|
||||
|
||||
var newFragments = new DicomOtherByteFragment(DicomTag.PixelData);
|
||||
|
||||
#region test
|
||||
//var newDicomFile = dicomFile.Clone();
|
||||
|
||||
//var newDataset = newDicomFile.Dataset;
|
||||
|
||||
//var dstPd = DicomPixelData.Create(newDataset, true);
|
||||
|
||||
//for (int i = 0; i < pixelData.NumberOfFrames; i++)
|
||||
//{
|
||||
// var frame = pixelData.GetFrame(i);
|
||||
|
||||
// dstPd.AddFrame(frame);
|
||||
|
||||
// var data = frame.Data;
|
||||
// int offset = 0;
|
||||
|
||||
// while (offset < data.Length)
|
||||
// {
|
||||
// int size = Math.Min(fragmentSize, data.Length - offset);
|
||||
// var buffer = new byte[size];
|
||||
// Buffer.BlockCopy(data, offset, buffer, 0, size);
|
||||
|
||||
// newFragments.Fragments.Add(new MemoryByteBuffer(buffer));
|
||||
|
||||
// offset += size;
|
||||
|
||||
// }
|
||||
|
||||
|
||||
//}
|
||||
//var newOffsetTable = newDataset.GetDicomItem<DicomOtherByteFragment>(DicomTag.PixelData).OffsetTable;
|
||||
|
||||
//newFragments.OffsetTable.AddRange(newOffsetTable.ToArray());
|
||||
#endregion
|
||||
|
||||
#region test fo-dicom auto bot
|
||||
//var newDicomFile = dicomFile.Clone();
|
||||
|
||||
//var newDataset = newDicomFile.Dataset;
|
||||
|
||||
//var dstPd = DicomPixelData.Create(newDataset, true);
|
||||
|
||||
//for (int i = 0; i < pixelData.NumberOfFrames; i++)
|
||||
//{
|
||||
// var frame = pixelData.GetFrame(i);
|
||||
|
||||
// dstPd.AddFrame(frame);
|
||||
//}
|
||||
//var newOffsetTable = newDataset.GetDicomItem<DicomOtherByteFragment>(DicomTag.PixelData).OffsetTable;
|
||||
|
||||
//Console.WriteLine(newOffsetTable.ToJsonStr());
|
||||
#endregion
|
||||
|
||||
#region 最终使用
|
||||
|
||||
for (int n = 0; n < pixelData.NumberOfFrames; n++)
|
||||
{
|
||||
var frameData = pixelData.GetFrame(n); // 获取完整一帧
|
||||
var data = frameData.Data;
|
||||
int offset = 0;
|
||||
|
||||
bot.Add(botOffset);
|
||||
|
||||
botOffset += (uint)data.Length;
|
||||
|
||||
|
||||
while (offset < data.Length)
|
||||
{
|
||||
botOffset += 8;
|
||||
|
||||
|
||||
int size = Math.Min(fragmentSize, data.Length - offset);
|
||||
var buffer = new byte[size];
|
||||
Buffer.BlockCopy(data, offset, buffer, 0, size);
|
||||
|
||||
newFragments.Fragments.Add(new MemoryByteBuffer(buffer));
|
||||
|
||||
offset += size;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//保留原始偏移表
|
||||
|
||||
//if (originOffsetTable.Count == pixelData.NumberOfFrames)
|
||||
//{
|
||||
// newFragments.OffsetTable.AddRange(originOffsetTable.ToArray());
|
||||
|
||||
//}
|
||||
//else
|
||||
{
|
||||
newFragments.OffsetTable.AddRange(bot.ToArray());
|
||||
|
||||
//Console.WriteLine(bot.ToJsonStr());
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
dicomFile.Dataset.AddOrUpdate(newFragments);
|
||||
|
||||
|
||||
}
|
||||
//传递过来的就是拆分的,但是是没有偏移表的,我需要自己创建偏移表,不然生成缩略图失败
|
||||
else if (syntax.IsEncapsulated && fragmentCount > pixelData.NumberOfFrames && originOffsetTable.Count == 0)
|
||||
{
|
||||
|
||||
|
||||
|
||||
uint offset = 0;
|
||||
|
||||
bot.Add(offset);
|
||||
|
||||
var fistSize = frag.Fragments.FirstOrDefault()?.Size ?? 0;
|
||||
|
||||
//和上一个大小不一样
|
||||
var isDiffrentBefore = false;
|
||||
|
||||
uint count = 0;
|
||||
|
||||
// 假设你知道每帧对应的 fragment 数量
|
||||
foreach (var frameFragments in frag.Fragments)
|
||||
{
|
||||
count++;
|
||||
|
||||
if (frameFragments.Size == fistSize)
|
||||
{
|
||||
isDiffrentBefore = false;
|
||||
// 累加这一帧所有 fragment 的大小
|
||||
offset += (uint)frameFragments.Size;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
offset += (uint)frameFragments.Size;
|
||||
isDiffrentBefore = true;
|
||||
|
||||
}
|
||||
|
||||
if (isDiffrentBefore)
|
||||
{
|
||||
//每个Fragment 也占用字节
|
||||
offset += 8 * count;
|
||||
bot.Add(offset);
|
||||
count = 0;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bot.RemoveAt(bot.Count - 1);
|
||||
// 设置到新的 PixelData
|
||||
frag.OffsetTable.AddRange(bot.ToArray());
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception mutiEx)
|
||||
{
|
||||
Log.Logger.Warning($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE} 处理多帧失败,上传原始文件:{mutiEx.ToString()}");
|
||||
}
|
||||
|
||||
// 替换原 PixelData
|
||||
dicomFile.Dataset.AddOrUpdate(newFragments);
|
||||
|
||||
|
||||
// 重新保存 dicom 到流
|
||||
ms.SetLength(0);
|
||||
dicomFile.Save(ms);
|
||||
}
|
||||
|
||||
|
||||
ms.Position = 0;
|
||||
}
|
||||
|
||||
#endregion
|
||||
#endregion
|
||||
|
||||
#region 本地测试
|
||||
//// --- 保存到本地文件测试 ---
|
||||
//var localPath = @"D:\TestDicom.dcm";
|
||||
//using (var fs = new FileStream(localPath, FileMode.Create, FileAccess.Write))
|
||||
//{
|
||||
// ms.CopyTo(fs);
|
||||
//}
|
||||
//return new DicomCStoreResponse(request, DicomStatus.Success);
|
||||
|
||||
#endregion
|
||||
|
||||
// 直接写入内存
|
||||
await using var ms = new MemoryStream();
|
||||
await dicomFile.SaveAsync(ms);
|
||||
ms.Position = 0;
|
||||
|
||||
//irc 从路径最后一截取Guid
|
||||
storeRelativePath = await ossService.UploadToOSSAsync(ms, ossFolderPath, instanceId.ToString(), false, uploadInfo: new FileUploadRecordAddOrEdit() { TrialId = _trialId, BatchDataType = BatchDataType.PACSReceive, UploadBatchId = _upload.Id.ToString() });
|
||||
|
||||
fileSize = ms.Length;
|
||||
|
||||
|
||||
#region 本地测试
|
||||
//// --- 保存到本地文件测试 ---
|
||||
//var localPath = @"D:\TestDicom.dcm";
|
||||
//using (var fs = new FileStream(localPath, FileMode.Create, FileAccess.Write))
|
||||
//{
|
||||
// ms.CopyTo(fs);
|
||||
//}
|
||||
//return new DicomCStoreResponse(request, DicomStatus.Success);
|
||||
var @lock = _distributedLockProvider.CreateLock($"{studyInstanceUid}");
|
||||
|
||||
#endregion
|
||||
using (await @lock.AcquireAsync())
|
||||
{
|
||||
try
|
||||
{
|
||||
var scpStudyId = await dicomArchiveService.ArchiveDicomFileAsync(dicomFile, _trialId, _trialSiteId, storeRelativePath, Association.CallingAE, Association.CalledAE, fileSize);
|
||||
|
||||
|
||||
//irc 从路径最后一截取Guid
|
||||
storeRelativePath = await ossService.UploadToOSSAsync(ms, ossFolderPath, instanceId.ToString(), false);
|
||||
var series = await _seriesRepository.FirstOrDefaultAsync(t => t.Id == seriesId);
|
||||
|
||||
//没有缩略图
|
||||
if (series != null && string.IsNullOrEmpty(series.ImageResizePath))
|
||||
{
|
||||
|
||||
// 生成缩略图
|
||||
using (var memoryStream = new MemoryStream())
|
||||
{
|
||||
DicomImage image = new DicomImage(dicomFile.Dataset);
|
||||
|
||||
var sharpimage = image.RenderImage().AsSharpImage();
|
||||
sharpimage.Save(memoryStream, new JpegEncoder());
|
||||
|
||||
// 上传缩略图到 OSS
|
||||
|
||||
var seriesPath = await ossService.UploadToOSSAsync(memoryStream, ossFolderPath, $"{seriesId.ToString()}_{instanceId.ToString()}.preview.jpg", false, uploadInfo: new FileUploadRecordAddOrEdit() { TrialId = _trialId, BatchDataType = BatchDataType.PACSReceive });
|
||||
|
||||
|
||||
series.ImageResizePath = seriesPath;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
await _seriesRepository.SaveChangesAsync();
|
||||
|
||||
if (_ImageUploadList.Any(t => t.StudyInstanceUid == studyInstanceUid))
|
||||
{
|
||||
var find = _ImageUploadList.FirstOrDefault(t => t.StudyInstanceUid.Equals(studyInstanceUid));
|
||||
|
||||
|
||||
find.SuccessImageCount++;
|
||||
|
||||
if (!find.PatientNameList.Any(t => t == patientIdStr) && patientIdStr.IsNotNullOrEmpty())
|
||||
{
|
||||
find.PatientNameList.Add(patientIdStr);
|
||||
}
|
||||
|
||||
//首次 (默认是Guid 空,数据库归档出了Id)
|
||||
if (find.SCPStudyId != scpStudyId)
|
||||
{
|
||||
find.SCPStudyId = scpStudyId;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//监控信息设置
|
||||
_upload.FileCount++;
|
||||
_upload.FileSize = _upload.FileSize + fileSize;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
Log.Logger.Warning($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE} 传输处理异常:{ex.ToString()}");
|
||||
|
||||
if (_ImageUploadList.Any(t => t.StudyInstanceUid == studyInstanceUid))
|
||||
{
|
||||
var find = _ImageUploadList.FirstOrDefault(t => t.StudyInstanceUid.Equals(studyInstanceUid));
|
||||
|
||||
find.FailedImageCount++;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fileSize = ms.Length;
|
||||
}
|
||||
|
||||
Log.Logger.Information($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE} {request.SOPInstanceUID} 上传完成 ");
|
||||
@@ -402,87 +736,6 @@ namespace IRaCIS.Core.SCP.Service
|
||||
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.File, _trialId, _trialSiteId, storeRelativePath, Association.CallingAE, Association.CalledAE,fileSize);
|
||||
|
||||
|
||||
var series = await _seriesRepository.FirstOrDefaultAsync(t => t.Id == seriesId);
|
||||
|
||||
//没有缩略图
|
||||
if (series != null && string.IsNullOrEmpty(series.ImageResizePath))
|
||||
{
|
||||
|
||||
// 生成缩略图
|
||||
using (var memoryStream = new MemoryStream())
|
||||
{
|
||||
DicomImage image = new DicomImage(request.Dataset);
|
||||
|
||||
var sharpimage = image.RenderImage().AsSharpImage();
|
||||
sharpimage.Save(memoryStream, new JpegEncoder());
|
||||
|
||||
// 上传缩略图到 OSS
|
||||
|
||||
var seriesPath = await ossService.UploadToOSSAsync(memoryStream, ossFolderPath, seriesId.ToString() + ".preview.jpg", false);
|
||||
|
||||
Console.WriteLine(seriesPath + " Id: " + seriesId);
|
||||
|
||||
series.ImageResizePath = seriesPath;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
await _seriesRepository.SaveChangesAsync();
|
||||
|
||||
if (_ImageUploadList.Any(t => t.StudyInstanceUid == studyInstanceUid))
|
||||
{
|
||||
var find = _ImageUploadList.FirstOrDefault(t => t.StudyInstanceUid.Equals(studyInstanceUid));
|
||||
|
||||
|
||||
find.SuccessImageCount++;
|
||||
|
||||
if (!find.PatientNameList.Any(t => t == patientIdStr) && patientIdStr.IsNotNullOrEmpty())
|
||||
{
|
||||
find.PatientNameList.Add(patientIdStr);
|
||||
}
|
||||
|
||||
//首次 (默认是Guid 空,数据库归档出了Id)
|
||||
if (find.SCPStudyId != scpStudyId)
|
||||
{
|
||||
find.SCPStudyId = scpStudyId;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
Log.Logger.Warning($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE} 传输处理异常:{ex.ToString()}");
|
||||
|
||||
if (_ImageUploadList.Any(t => t.StudyInstanceUid == studyInstanceUid))
|
||||
{
|
||||
var find = _ImageUploadList.FirstOrDefault(t => t.StudyInstanceUid.Equals(studyInstanceUid));
|
||||
|
||||
find.FailedImageCount++;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
//监控信息设置
|
||||
_upload.FileCount++;
|
||||
_upload.FileSize = _upload.FileSize + fileSize;
|
||||
return new DicomCStoreResponse(request, DicomStatus.Success);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
using IRaCIS.Core.Domain.Models;
|
||||
using IRaCIS.Core.Infra.EFCore;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace IRaCIS.Core.SCP.Service;
|
||||
|
||||
|
||||
public static class CacheKeys
|
||||
{
|
||||
//项目缓存
|
||||
public static string Trial(string trialIdStr) => $"TrialId:{trialIdStr}";
|
||||
|
||||
//检查编号递增锁
|
||||
public static string TrialStudyMaxCode(Guid trialId) => $"TrialStudyMaxCode:{trialId}";
|
||||
|
||||
public static string TrialStudyUidUploading(Guid trialId, string studyUid) => $"TrialStudyUid:{trialId}_{studyUid}";
|
||||
//CRC上传影像提交锁key
|
||||
public static string TrialStudyUidDBLock(Guid trialId, string studyUid) => $"TrialStudyUidDBLock:{trialId}_{studyUid}";
|
||||
|
||||
public static string TrialTaskStudyUidUploading(Guid trialId, Guid visiTaskId, string studyUid) => $"TrialStudyUid:{trialId}_{visiTaskId}_{studyUid}";
|
||||
//影像后处理上传提交锁key
|
||||
public static string TrialTaskStudyUidDBLock(Guid trialId, Guid visiTaskId, string studyUid) => $"TrialTaskStudyUidDBLock:{trialId}_{visiTaskId}_{studyUid}";
|
||||
//系统匿名化
|
||||
public static string SystemAnonymization => $"SystemAnonymization";
|
||||
//前端国际化
|
||||
public static string FrontInternational => $"FrontInternationalList";
|
||||
|
||||
//登录挤账号
|
||||
public static string UserToken(Guid userId) => $"UserToken:{userId}";
|
||||
|
||||
//超时没请求接口自动退出
|
||||
public static string UserAutoLoginOut(Guid userId) => $"UserAutoLoginOut:{userId}";
|
||||
|
||||
|
||||
public static string UserDisable(Guid userId) => $"UserDisable:{userId}";
|
||||
|
||||
public static string UserRoleDisable(Guid userRoleId) => $"UserRoleDisable:{userRoleId}";
|
||||
|
||||
/// <summary>
|
||||
/// 用户登录错误 限制登录
|
||||
/// </summary>
|
||||
/// <param name="userName"></param>
|
||||
/// <returns></returns>
|
||||
public static string UserLoginError(string userName) => $"login-failures:{userName}";
|
||||
|
||||
/// <summary>
|
||||
/// 跳过阅片
|
||||
/// </summary>
|
||||
/// <param name="userId"></param>
|
||||
/// <returns></returns>
|
||||
public static string SkipReadingCacheKey(Guid userId) => $"{userId}SkipReadingCache";
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 开始阅片时间
|
||||
/// </summary>
|
||||
/// <param name="userId"></param>
|
||||
/// <returns></returns>
|
||||
public static string StartReadingTimeKey(Guid userId) => $"{userId}StartReadingTime";
|
||||
|
||||
/// <summary>
|
||||
/// 开始休息时间
|
||||
/// </summary>
|
||||
/// <param name="userId"></param>
|
||||
/// <returns></returns>
|
||||
public static string StartRestTime(Guid userId) => $"{userId}StartRestTime";
|
||||
|
||||
//每个用户 每个浏览器独立时间
|
||||
public static string UserMFAVerifyPass(Guid userId, string browserFingerprint) => $"UserMFAVerifyPass:{userId}:{browserFingerprint}";
|
||||
|
||||
public static string TrialSiteInfo(Guid trialSiteId) => $"{trialSiteId}TrialSiteInfo";
|
||||
|
||||
public static string TrialDataStoreType(Guid trialId) => $"TrialDataStoreType:{trialId}";
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public class TrialSiteInfoDTO
|
||||
{
|
||||
public string TrialSiteCode { get; set; }
|
||||
public string TrialCode { get; set; }
|
||||
}
|
||||
public static async Task<TrialSiteInfoDTO> GetTrialSiteInfo(Guid trialSiteId, IRepository<TrialSite> _trialSiteRepository)
|
||||
{
|
||||
var obj = await _trialSiteRepository.Where(t => t.Id == trialSiteId, ignoreQueryFilters: true).Select(t => new TrialSiteInfoDTO { TrialCode= t.Trial.TrialCode, TrialSiteCode= t.TrialSiteCode }).FirstOrDefaultAsync();
|
||||
|
||||
return obj??new TrialSiteInfoDTO();
|
||||
}
|
||||
|
||||
public static async Task<List<SystemAnonymization>> GetSystemAnonymizationListAsync(IRepository<SystemAnonymization> _systemAnonymizationRepository)
|
||||
{
|
||||
var list = await _systemAnonymizationRepository.Where(t => t.IsEnable).ToListAsync();
|
||||
|
||||
return list;
|
||||
}
|
||||
}
|
||||
@@ -1,49 +1,32 @@
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using System.Text;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using IRaCIS.Core.Infrastructure;
|
||||
using Medallion.Threading;
|
||||
using FellowOakDicom;
|
||||
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.Domain.Models;
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using IRaCIS.Core.Infra.EFCore;
|
||||
using MassTransit;
|
||||
using System.Runtime.Intrinsics.X86;
|
||||
using Serilog.Sinks.File;
|
||||
using IRaCIS.Core.Infrastructure;
|
||||
using IRaCIS.Core.Infrastructure.Extention;
|
||||
using IRaCIS.Core.SCP.Service;
|
||||
using MassTransit;
|
||||
using Medallion.Threading;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Serilog.Sinks.File;
|
||||
using System.Data;
|
||||
using System.Runtime.Intrinsics.X86;
|
||||
using System.Text;
|
||||
using ZiggyCreatures.Caching.Fusion;
|
||||
using static IRaCIS.Core.SCP.Service.CacheHelper;
|
||||
|
||||
namespace IRaCIS.Core.SCP.Service
|
||||
{
|
||||
public class DicomArchiveService : BaseService, IDicomArchiveService
|
||||
public class DicomArchiveService(IRepository<SCPPatient> _patientRepository,
|
||||
IRepository<SCPStudy> _studyRepository,
|
||||
IRepository<SCPSeries> _seriesRepository,
|
||||
IRepository<SCPInstance> _instanceRepository
|
||||
) : 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;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -53,18 +36,19 @@ namespace IRaCIS.Core.SCP.Service
|
||||
/// <param name="dataset"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="NotImplementedException"></exception>
|
||||
public async Task<Guid> ArchiveDicomFileAsync(DicomFile dicomFile, Guid trialId, Guid trialSiteId, string fileRelativePath, string callingAE, string calledAE,long fileSize)
|
||||
public async Task<Guid> ArchiveDicomFileAsync(DicomFile dicomFile, Guid trialId, Guid trialSiteId, string fileRelativePath, string callingAE, string calledAE, long fileSize)
|
||||
{
|
||||
|
||||
var dataset = dicomFile.Dataset;
|
||||
|
||||
string studyInstanceUid = dataset.GetString(DicomTag.StudyInstanceUID);
|
||||
string seriesInstanceUid = dataset.GetString(DicomTag.SeriesInstanceUID);
|
||||
string sopInstanceUid = dataset.GetString(DicomTag.SOPInstanceUID);
|
||||
|
||||
string patientIdStr = dataset.GetSingleValueOrDefault(DicomTag.PatientID,string.Empty);
|
||||
string patientIdStr = dataset.GetSingleValueOrDefault(DicomTag.PatientID, string.Empty);
|
||||
|
||||
//Guid patientId= IdentifierHelper.CreateGuid(patientIdStr);
|
||||
Guid studyId = IdentifierHelper.CreateGuid(studyInstanceUid,trialId.ToString());
|
||||
Guid studyId = IdentifierHelper.CreateGuid(studyInstanceUid, trialId.ToString());
|
||||
Guid seriesId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid, trialId.ToString());
|
||||
Guid instanceId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid, sopInstanceUid, trialId.ToString());
|
||||
|
||||
@@ -77,9 +61,9 @@ namespace IRaCIS.Core.SCP.Service
|
||||
|
||||
//using (@lock.Acquire())
|
||||
{
|
||||
var findPatient = await _patientRepository.FirstOrDefaultAsync(t => t.PatientIdStr == patientIdStr && t.TrialSiteId==trialSiteId );
|
||||
var findStudy = await _studyRepository.FirstOrDefaultAsync(t=>t.Id== studyId);
|
||||
var findSerice = await _seriesRepository.FirstOrDefaultAsync(t => t.Id == seriesId);
|
||||
var findPatient = await _patientRepository.FirstOrDefaultAsync(t => t.PatientIdStr == patientIdStr && t.TrialSiteId == trialSiteId);
|
||||
var findStudy = await _studyRepository.FirstOrDefaultAsync(t => t.Id == studyId);
|
||||
var findSerice = await _seriesRepository.FirstOrDefaultAsync(t => t.Id == seriesId);
|
||||
var findInstance = await _instanceRepository.FirstOrDefaultAsync(t => t.Id == instanceId);
|
||||
|
||||
DateTime? studyTime = dataset.GetSingleValueOrDefault(DicomTag.StudyDate, string.Empty) == string.Empty ? null : dataset.GetSingleValue<DateTime>(DicomTag.StudyDate).Add(dataset.GetSingleValueOrDefault(DicomTag.StudyTime, string.Empty) == string.Empty ? TimeSpan.Zero : dataset.GetSingleValue<DateTime>(DicomTag.StudyTime).TimeOfDay);
|
||||
@@ -93,8 +77,8 @@ namespace IRaCIS.Core.SCP.Service
|
||||
findPatient = new SCPPatient()
|
||||
{
|
||||
Id = NewId.NextSequentialGuid(),
|
||||
TrialId=trialId,
|
||||
TrialSiteId=trialSiteId,
|
||||
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),
|
||||
@@ -122,7 +106,7 @@ namespace IRaCIS.Core.SCP.Service
|
||||
|
||||
DateTime birthDate;
|
||||
|
||||
if (findPatient.PatientAge == string.Empty && studyTime.HasValue && DateTime.TryParse(findPatient.PatientBirthDate,out birthDate))
|
||||
if (findPatient.PatientAge == string.Empty && studyTime.HasValue && DateTime.TryParse(findPatient.PatientBirthDate, out birthDate))
|
||||
{
|
||||
var patientAge = studyTime.Value.Year - birthDate.Year;
|
||||
// 如果生日还未到,年龄减去一岁
|
||||
@@ -220,6 +204,18 @@ namespace IRaCIS.Core.SCP.Service
|
||||
AcquisitionNumber = dataset.GetSingleValueOrDefault(DicomTag.AcquisitionNumber, string.Empty),
|
||||
TriggerTime = dataset.GetSingleValueOrDefault(DicomTag.TriggerTime, string.Empty),
|
||||
|
||||
RadionuclideTotalDose= dataset.GetSingleValueOrDefault(DicomTag.RadionuclideTotalDose, string.Empty),
|
||||
|
||||
RadionuclideHalfLife= dataset.GetSingleValueOrDefault(DicomTag.RadionuclideHalfLife, string.Empty),
|
||||
RadiopharmaceuticalStartTime = dataset.GetSingleValueOrDefault(DicomTag.RadiopharmaceuticalStartTime, string.Empty),
|
||||
|
||||
|
||||
Manufacturer = dataset.GetSingleValueOrDefault(DicomTag.Manufacturer, string.Empty),
|
||||
ManufacturerModelName = dataset.GetSingleValueOrDefault(DicomTag.ManufacturerModelName, string.Empty),
|
||||
DeviceSerialNumber = dataset.GetSingleValueOrDefault(DicomTag.DeviceSerialNumber, string.Empty),
|
||||
DeviceUID = dataset.GetSingleValueOrDefault(DicomTag.DeviceUID, string.Empty),
|
||||
SoftwareVersions = dataset.GetSingleValueOrDefault(DicomTag.SoftwareVersions, string.Empty),
|
||||
PatientWeight = dataset.GetSingleValueOrDefault(DicomTag.PatientWeight, string.Empty),
|
||||
|
||||
|
||||
//IsDoubleReview = addtionalInfo.IsDoubleReview,
|
||||
@@ -239,6 +235,7 @@ namespace IRaCIS.Core.SCP.Service
|
||||
findStudy.DicomStudyTime = dataset.GetSingleValueOrDefault(DicomTag.StudyTime, string.Empty);
|
||||
findStudy.CalledAE = calledAE;
|
||||
findStudy.CallingAE = callingAE;
|
||||
findStudy.PatientIdStr = patientIdStr;
|
||||
findStudy.PatientName = dataset.GetSingleValueOrDefault(DicomTag.PatientName, string.Empty);
|
||||
findStudy.PatientSex = dataset.GetSingleValueOrDefault(DicomTag.PatientSex, string.Empty);
|
||||
findStudy.PatientAge = dataset.GetSingleValueOrDefault(DicomTag.PatientAge, string.Empty);
|
||||
@@ -281,6 +278,9 @@ namespace IRaCIS.Core.SCP.Service
|
||||
AcquisitionNumber = dataset.GetSingleValueOrDefault(DicomTag.AcquisitionNumber, string.Empty),
|
||||
TriggerTime = dataset.GetSingleValueOrDefault(DicomTag.TriggerTime, string.Empty),
|
||||
|
||||
RadiopharmaceuticalInformationSequence = dataset.GetSingleValueOrDefault(DicomTag.RadiopharmaceuticalInformationSequence, string.Empty),
|
||||
AcquisitionDate = dataset.GetSingleValueOrDefault(DicomTag.AcquisitionDate, string.Empty),
|
||||
|
||||
|
||||
InstanceCount = 0
|
||||
};
|
||||
@@ -325,16 +325,31 @@ namespace IRaCIS.Core.SCP.Service
|
||||
SliceLocation = dataset.GetSingleValueOrDefault(DicomTag.SliceLocation, 0),
|
||||
|
||||
SliceThickness = dataset.GetSingleValueOrDefault(DicomTag.SliceThickness, string.Empty),
|
||||
NumberOfFrames = dataset.GetSingleValueOrDefault(DicomTag.NumberOfFrames, 0),
|
||||
NumberOfFrames = dataset.GetSingleValueOrDefault(DicomTag.NumberOfFrames, 1),
|
||||
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),
|
||||
|
||||
PhotometricInterpretation = dataset.GetSingleValueOrDefault(DicomTag.PhotometricInterpretation, string.Empty),
|
||||
BitsAllocated = dataset.GetSingleValueOrDefault(DicomTag.BitsAllocated, 0),
|
||||
PixelRepresentation = dataset.GetSingleValueOrDefault(DicomTag.PixelRepresentation, string.Empty),
|
||||
RescaleIntercept = dataset.GetSingleValueOrDefault(DicomTag.RescaleIntercept, string.Empty),
|
||||
RescaleSlope = dataset.GetSingleValueOrDefault(DicomTag.RescaleSlope, string.Empty),
|
||||
ImagePositionPatient = dataset.GetSingleValueOrDefault(DicomTag.ImagePositionPatient, string.Empty),
|
||||
ImageOrientationPatient = dataset.GetSingleValueOrDefault(DicomTag.ImageOrientationPatient, string.Empty),
|
||||
SequenceOfUltrasoundRegions = dataset.GetSingleValueOrDefault(DicomTag.SequenceOfUltrasoundRegions, string.Empty),
|
||||
FrameTime = dataset.GetSingleValueOrDefault(DicomTag.FrameTime, string.Empty),
|
||||
CorrectedImage = dataset.GetSingleValueOrDefault(DicomTag.CorrectedImage, string.Empty),
|
||||
Units = dataset.GetSingleValueOrDefault(DicomTag.Units, string.Empty),
|
||||
DecayCorrection = dataset.GetSingleValueOrDefault(DicomTag.DecayCorrection, string.Empty),
|
||||
EncapsulatedDocument = dataset.GetSingleValueOrDefault(DicomTag.EncapsulatedDocument, string.Empty),
|
||||
|
||||
|
||||
Path = fileRelativePath,
|
||||
|
||||
FileSize= fileSize,
|
||||
FileSize = fileSize,
|
||||
|
||||
};
|
||||
|
||||
@@ -374,7 +389,7 @@ namespace IRaCIS.Core.SCP.Service
|
||||
}
|
||||
else
|
||||
{
|
||||
await _instanceRepository.BatchUpdateNoTrackingAsync(t => t.Id == instanceId, u => new SCPInstance() { Path = fileRelativePath,FileSize=fileSize });
|
||||
await _instanceRepository.BatchUpdateNoTrackingAsync(t => t.Id == instanceId, u => new SCPInstance() { Path = fileRelativePath, FileSize = fileSize });
|
||||
}
|
||||
|
||||
await _studyRepository.SaveChangesAsync();
|
||||
|
||||
@@ -0,0 +1,335 @@
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// 此代码由liquid模板自动生成 byzhouhang 20240909
|
||||
// 生成时间 2026-03-10 06:15:17Z
|
||||
// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。
|
||||
//--------------------------------------------------------------------
|
||||
using AutoMapper;
|
||||
using IRaCIS.Core.Application.Helper;
|
||||
|
||||
using IRaCIS.Core.Domain.Models;
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using IRaCIS.Core.Infra.EFCore;
|
||||
using IRaCIS.Core.Infra.EFCore.Common;
|
||||
using IRaCIS.Core.Infrastructure.Extention;
|
||||
using IRaCIS.Core.SCP;
|
||||
using IRaCIS.Core.SCP.Service;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Microsoft.Extensions.Options;
|
||||
using System.Drawing;
|
||||
using System.Threading.Channels;
|
||||
using System.Threading.Tasks;
|
||||
using ZiggyCreatures.Caching.Fusion;
|
||||
|
||||
namespace IRaCIS.Core.SCP.Service;
|
||||
|
||||
public class FileUploadRecordAddOrEdit
|
||||
{
|
||||
public Guid? Id { get; set; }
|
||||
|
||||
|
||||
public string FileName { get; set; }
|
||||
|
||||
public long FileSize { get; set; }
|
||||
|
||||
public string FileType { get; set; }
|
||||
|
||||
public string Path { get; set; }
|
||||
|
||||
|
||||
public string UploadBatchId { get; set; }
|
||||
public BatchDataType BatchDataType { get; set; }
|
||||
|
||||
public string StudyCode { get; set; }
|
||||
|
||||
public Guid? TrialId { get; set; }
|
||||
|
||||
public Guid? SubjectId { get; set; }
|
||||
|
||||
public Guid? SubjectVisitId { get; set; }
|
||||
|
||||
public Guid? DicomStudyId { get; set; }
|
||||
|
||||
public Guid? NoneDicomStudyId { get; set; }
|
||||
|
||||
|
||||
|
||||
public string FileMarkId { get; set; }
|
||||
|
||||
public int? Priority { get; set; }
|
||||
public string IP { get; set; }
|
||||
public bool? IsNeedSync { get; set; }
|
||||
public string UploadRegion { get; set; }
|
||||
public string TargetRegion { get; set; }
|
||||
|
||||
public bool? IsSync { get; set; }
|
||||
}
|
||||
public interface IFileUploadRecordService
|
||||
{
|
||||
|
||||
|
||||
Task<IResponseOutput> AddOrUpdateFileUploadRecord(FileUploadRecordAddOrEdit addOrEditFileUploadRecord);
|
||||
|
||||
}
|
||||
|
||||
[ApiExplorerSettings(GroupName = "Common")]
|
||||
public class FileUploadRecordService(IRepository<FileUploadRecord> _fileUploadRecordRepository,
|
||||
IMapper _mapper, IUserInfo _userInfo, IStringLocalizer _localizer, IOptionsMonitor<ObjectStoreServiceOptions> options,
|
||||
IFusionCache _fusionCache, IRepository<Trial> _trialRepository, FileSyncQueue _fileSyncQueue) : BaseService, IFileUploadRecordService
|
||||
{
|
||||
|
||||
ObjectStoreServiceOptions ObjectStoreServiceConfig = options.CurrentValue;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public async Task<IResponseOutput> AddOrUpdateFileUploadRecord(FileUploadRecordAddOrEdit addOrEditFileUploadRecord)
|
||||
{
|
||||
return ResponseOutput.Ok();
|
||||
|
||||
addOrEditFileUploadRecord.IP = _userInfo.IP;
|
||||
|
||||
if (ObjectStoreServiceConfig.IsOpenStoreSync && _userInfo.Domain.IsNotNullOrEmpty())
|
||||
{
|
||||
var find = ObjectStoreServiceConfig.SyncConfigList.FirstOrDefault(t => t.Domain == _userInfo.Domain);
|
||||
if (find != null)
|
||||
{
|
||||
addOrEditFileUploadRecord.UploadRegion = find.UploadRegion;
|
||||
addOrEditFileUploadRecord.TargetRegion = find.TargetRegion;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
//前后端调试的时候,上传的时候域名不对应,自动按照后端配置设置上传区域和同步区域
|
||||
var apiDefalut = ObjectStoreServiceConfig.SyncConfigList.FirstOrDefault(t => t.UploadRegion == ObjectStoreServiceConfig.ApiDeployRegion);
|
||||
|
||||
if (apiDefalut != null)
|
||||
{
|
||||
addOrEditFileUploadRecord.UploadRegion = apiDefalut.UploadRegion;
|
||||
addOrEditFileUploadRecord.TargetRegion = apiDefalut.TargetRegion;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//SCP推送过来IP为空,后端归档的,设置区域
|
||||
if (_userInfo.IP.IsNullOrEmpty() && _userInfo.Domain.IsNullOrEmpty())
|
||||
{
|
||||
var apiDefalut = ObjectStoreServiceConfig.SyncConfigList.FirstOrDefault(t => t.UploadRegion == ObjectStoreServiceConfig.ApiDeployRegion);
|
||||
|
||||
if (apiDefalut != null)
|
||||
{
|
||||
addOrEditFileUploadRecord.UploadRegion = apiDefalut.UploadRegion;
|
||||
addOrEditFileUploadRecord.TargetRegion = apiDefalut.TargetRegion;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (addOrEditFileUploadRecord.TrialId != null)
|
||||
{
|
||||
|
||||
var trialDataStore = await _fusionCache.GetOrSetAsync(CacheKeys.TrialDataStoreType(addOrEditFileUploadRecord.TrialId.Value), async _ =>
|
||||
{
|
||||
return await _trialRepository.Where(t => t.Id == addOrEditFileUploadRecord.TrialId).Select(t => t.TrialDataStoreType)
|
||||
.FirstOrDefaultAsync();
|
||||
},
|
||||
TimeSpan.FromDays(7)
|
||||
);
|
||||
|
||||
//项目配置了,那么就设置需要同步
|
||||
if (trialDataStore == TrialDataStore.MUtiCenter)
|
||||
{
|
||||
addOrEditFileUploadRecord.IsNeedSync = true;
|
||||
|
||||
addOrEditFileUploadRecord.Priority = addOrEditFileUploadRecord.Priority ?? 0;
|
||||
|
||||
addOrEditFileUploadRecord.IsSync = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
addOrEditFileUploadRecord.IsNeedSync = false;
|
||||
|
||||
//addOrEditFileUploadRecord.TargetRegion = "";
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
//系统文件,默认同步
|
||||
addOrEditFileUploadRecord.IsNeedSync = true;
|
||||
|
||||
addOrEditFileUploadRecord.IsSync = false;
|
||||
|
||||
addOrEditFileUploadRecord.Priority = addOrEditFileUploadRecord.Priority ?? 0;
|
||||
}
|
||||
|
||||
var entity = await _fileUploadRecordRepository.InsertOrUpdateAsync(addOrEditFileUploadRecord, true);
|
||||
|
||||
if (addOrEditFileUploadRecord.IsNeedSync == true)
|
||||
{
|
||||
_fileSyncQueue.Enqueue(entity.Id, addOrEditFileUploadRecord.Priority ?? 0);
|
||||
}
|
||||
|
||||
return ResponseOutput.Ok(entity.Id.ToString());
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public sealed class FileSyncQueue
|
||||
{
|
||||
/// <summary>
|
||||
/// 优先级队列(仅负责排序)
|
||||
/// </summary>
|
||||
private readonly PriorityQueue<Guid, int> _queue = new();
|
||||
|
||||
/// <summary>
|
||||
/// 当前等待中的任务(唯一真实数据)
|
||||
/// key = Guid
|
||||
/// value = 最新 priority
|
||||
/// </summary>
|
||||
private readonly Dictionary<Guid, int> _waiting = new();
|
||||
|
||||
/// <summary>
|
||||
/// 正在执行的任务(防止重复执行)
|
||||
/// </summary>
|
||||
private readonly HashSet<Guid> _running = new();
|
||||
|
||||
/// <summary>
|
||||
/// worker 等待信号
|
||||
/// </summary>
|
||||
private readonly SemaphoreSlim _signal = new(0);
|
||||
|
||||
|
||||
private readonly object _lock = new();
|
||||
|
||||
// ============================================================
|
||||
// Enqueue
|
||||
// ============================================================
|
||||
|
||||
/// <summary>
|
||||
/// 入队(同 Guid 会覆盖优先级)
|
||||
/// </summary>
|
||||
public void Enqueue(Guid id, int priority)
|
||||
{
|
||||
bool needSignal = false;
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
// 如果正在执行,忽略(防止重复)
|
||||
if (_running.Contains(id))
|
||||
return;
|
||||
|
||||
// 是否新任务(用于减少 signal 风暴)
|
||||
if (!_waiting.ContainsKey(id))
|
||||
needSignal = true;
|
||||
|
||||
// 更新为最新优先级(最后一次为准)
|
||||
_waiting[id] = priority; //等价于添加或者更新
|
||||
|
||||
// PriorityQueue 无法更新节点
|
||||
// 允许旧节点存在,Dequeue 时过滤
|
||||
_queue.Enqueue(id, -priority);
|
||||
}
|
||||
|
||||
// 只有新增任务才唤醒 worker
|
||||
if (needSignal)
|
||||
_signal.Release();
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Dequeue
|
||||
// ============================================================
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个待执行任务(无任务时自动等待)
|
||||
/// </summary>
|
||||
public async Task<Guid> DequeueAsync(CancellationToken ct)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
await _signal.WaitAsync(ct);
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
while (_queue.Count > 0)
|
||||
{
|
||||
var id = _queue.Dequeue();
|
||||
|
||||
// 已被覆盖或取消
|
||||
if (!_waiting.TryGetValue(id, out _))
|
||||
continue;
|
||||
|
||||
// 标记为运行中
|
||||
_waiting.Remove(id);
|
||||
_running.Add(id);
|
||||
|
||||
return id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Complete
|
||||
// ============================================================
|
||||
|
||||
/// <summary>
|
||||
/// 任务执行完成(必须调用)
|
||||
/// </summary>
|
||||
public void Complete(Guid id)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
_running.Remove(id);
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Snapshot
|
||||
// ============================================================
|
||||
|
||||
/// <summary>
|
||||
/// 当前等待中的任务快照
|
||||
/// </summary>
|
||||
public Guid[] Snapshot()
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
return _waiting.Keys.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 状态信息(调试用)
|
||||
// ============================================================
|
||||
|
||||
public int WaitingCount
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_lock)
|
||||
return _waiting.Count;
|
||||
}
|
||||
}
|
||||
|
||||
public int RunningCount
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_lock)
|
||||
return _running.Count;
|
||||
}
|
||||
}
|
||||
}
|
||||
+1434
-107
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,16 @@
|
||||
using AutoMapper;
|
||||
using IRaCIS.Core.Domain.Models;
|
||||
using IRaCIS.Core.SCP.Service;
|
||||
|
||||
|
||||
namespace IRaCIS.Core.Application.Service
|
||||
{
|
||||
public class CommonConfig : Profile
|
||||
{
|
||||
public CommonConfig()
|
||||
{
|
||||
CreateMap<FileUploadRecordAddOrEdit, FileUploadRecord>().ReverseMap();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
"ObjectStoreService": {
|
||||
"ObjectStoreUse": "AliyunOSS",
|
||||
"AliyunOSS": {
|
||||
"RegionId": "cn-shanghai",
|
||||
"InternalEndpoint": "https://oss-cn-shanghai-internal.aliyuncs.com",
|
||||
"EndPoint": "https://oss-cn-shanghai.aliyuncs.com",
|
||||
"AccessKeyId": "LTAI5tFUCCmz5TwghZHsj45Y",
|
||||
"AccessKeySecret": "8evrBy1fVfzJG25i67Jm0xqn9Xcw2T",
|
||||
"RoleArn": "acs:ram::1078130221702011:role/uat-oss-access",
|
||||
"BucketName": "tl-med-irc-event-store",
|
||||
"ViewEndpoint": "https://tl-med-irc-event-store.oss-cn-shanghai.aliyuncs.com",
|
||||
"Region": "oss-cn-shanghai",
|
||||
"DurationSeconds": 7200
|
||||
}
|
||||
},
|
||||
|
||||
"ConnectionStrings": {
|
||||
"RemoteNew": "Server=101.132.253.119,1435;Database=irc_Prpd_bak;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
||||
"Hangfire": "Server=101.132.253.119,1435;Database=irc_Hangfire_bak;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
|
||||
},
|
||||
|
||||
"DicomSCPServiceConfig": {
|
||||
"CalledAEList": [
|
||||
"STORESCP"
|
||||
],
|
||||
"ServerPort": 11112
|
||||
}
|
||||
|
||||
}
|
||||
@@ -8,6 +8,26 @@
|
||||
},
|
||||
"ObjectStoreService": {
|
||||
"ObjectStoreUse": "AliyunOSS",
|
||||
"IsOpenStoreSync": true,
|
||||
"ApiDeployRegion": "CN",
|
||||
"SyncConfigList": [
|
||||
{
|
||||
"Domain": "irc.extimaging.com",
|
||||
"Primary": "AliyunOSS",
|
||||
"Target": "AWS",
|
||||
"UploadRegion": "CN",
|
||||
"TargetRegion": "US",
|
||||
"IsOpenSync": true
|
||||
},
|
||||
{
|
||||
"Domain": "eiirc.extimaging.com",
|
||||
"Primary": "AWS",
|
||||
"Target": "AliyunOSS",
|
||||
"UploadRegion": "US",
|
||||
"TargetRegion": "CN",
|
||||
"IsOpenSync": true
|
||||
}
|
||||
],
|
||||
"AliyunOSS": {
|
||||
"RegionId": "cn-shanghai",
|
||||
"InternalEndpoint": "https://oss-cn-shanghai-internal.aliyuncs.com",
|
||||
@@ -19,6 +39,18 @@
|
||||
"ViewEndpoint": "https://zy-irc-cache.oss-cn-shanghai.aliyuncs.com",
|
||||
"Region": "oss-cn-shanghai",
|
||||
"DurationSeconds": 7200
|
||||
},
|
||||
|
||||
"AWS": {
|
||||
"Region": "us-east-1",
|
||||
"EndPoint": "s3.us-east-1.amazonaws.com",
|
||||
"UseSSL": true,
|
||||
"RoleArn": "arn:aws:iam::471112624751:role/lili_s3_access",
|
||||
"AccessKeyId": "AKIAW3MEAFJXZ2TZK7GM",
|
||||
"SecretAccessKey": "9MLQCQ1HifEVW1gf068zBRAOb4wNnfrOkvBVByth",
|
||||
"BucketName": "ei-med-s3-irc-store",
|
||||
"ViewEndpoint": "https://ei-med-s3-irc-store.s3.amazonaws.com",
|
||||
"DurationSeconds": 7200
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
"ObjectStoreService": {
|
||||
"ObjectStoreUse": "AliyunOSS",
|
||||
"IsOpenStoreSync": true,
|
||||
"ApiDeployRegion": "CN",
|
||||
"SyncConfigList": [
|
||||
{
|
||||
"Domain": "irc.test.rayplus.net",
|
||||
"Primary": "AliyunOSS",
|
||||
"Target": "AWS",
|
||||
"UploadRegion": "CN",
|
||||
"TargetRegion": "US",
|
||||
"IsOpenSync": false
|
||||
},
|
||||
{
|
||||
"Domain": "lili.uat.extimaging.com",
|
||||
"Primary": "AWS",
|
||||
"Target": "AliyunOSS",
|
||||
"UploadRegion": "US",
|
||||
"TargetRegion": "CN",
|
||||
"IsOpenSync": false
|
||||
}
|
||||
],
|
||||
"AliyunOSS": {
|
||||
"RegionId": "cn-shanghai",
|
||||
"InternalEndpoint": "https://oss-cn-shanghai-internal.aliyuncs.com",
|
||||
"EndPoint": "https://oss-cn-shanghai.aliyuncs.com",
|
||||
"AccessKeyId": "LTAI5tRRZehUp2V9pyTPtAJm",
|
||||
"AccessKeySecret": "FLizxkHsMm4CGYHtkV8E3PNJJZU7oV",
|
||||
"RoleArn": "acs:ram::1899121822495495:role/dev-oss-access",
|
||||
"BucketName": "rayplus-irc-test-store",
|
||||
"ViewEndpoint": "https://rayplus-irc-test-store.oss-cn-shanghai.aliyuncs.com",
|
||||
"Region": "oss-cn-shanghai",
|
||||
"DurationSeconds": 7200
|
||||
},
|
||||
// AWS S3 对象存储服务的配置
|
||||
"AWS": {
|
||||
// AWS S3 的Region
|
||||
"Region": "us-east-1",
|
||||
// AWS S3 的内部访问端点
|
||||
"EndPoint": "s3.us-east-1.amazonaws.com",
|
||||
// 是否使用 SSL
|
||||
"UseSSL": true,
|
||||
// AWS S3 的角色 ARN
|
||||
"RoleArn": "arn:aws:iam::471112624751:role/uat_s3_access",
|
||||
// AWS S3 的访问密钥 ID
|
||||
"AccessKeyId": "AKIAW3MEAFJX7IPXISP4",
|
||||
// AWS S3 的访问密钥 Secret
|
||||
"SecretAccessKey": "Pgrg3le5jPxZQ7MR1yYNS30J0XRyJeKVyIIjElXc",
|
||||
// AWS S3 的Bucket名称
|
||||
"BucketName": "ei-med-s3-lili-uat-store",
|
||||
// AWS S3 的访问端点
|
||||
"ViewEndpoint": "https://ei-med-s3-lili-uat-store.s3.amazonaws.com",
|
||||
// AWS S3 的持续数秒
|
||||
"DurationSeconds": 7200
|
||||
}
|
||||
},
|
||||
"ConnectionStrings": {
|
||||
"RemoteNew": "Server=prod_mssql_standard,1433;Database=Prod_Rayplus;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true",
|
||||
"Hangfire": "Server=prod_mssql_standard,1433;Database=Prod_Rayplus_Hangfire;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true"
|
||||
},
|
||||
"DicomSCPServiceConfig": {
|
||||
"CalledAEList": [
|
||||
"STORESCP"
|
||||
],
|
||||
"ServerPort": 11112
|
||||
}
|
||||
|
||||
}
|
||||
@@ -7,7 +7,28 @@
|
||||
}
|
||||
},
|
||||
"ObjectStoreService": {
|
||||
// 使用的对象存储服务类型
|
||||
"ObjectStoreUse": "AliyunOSS",
|
||||
"IsOpenStoreSync": true,
|
||||
"ApiDeployRegion": "CN",
|
||||
"SyncConfigList": [
|
||||
{
|
||||
"Domain": "irc.test.extimaging.com",
|
||||
"Primary": "AliyunOSS",
|
||||
"Target": "AWS",
|
||||
"UploadRegion": "CN",
|
||||
"TargetRegion": "US",
|
||||
"IsOpenSync": true
|
||||
},
|
||||
{
|
||||
"Domain": "lili.test.extimaging.com",
|
||||
"Primary": "AWS",
|
||||
"Target": "AliyunOSS",
|
||||
"UploadRegion": "US",
|
||||
"TargetRegion": "CN",
|
||||
"IsOpenSync": true
|
||||
}
|
||||
],
|
||||
"AliyunOSS": {
|
||||
"RegionId": "cn-shanghai",
|
||||
"InternalEndpoint": "https://oss-cn-shanghai-internal.aliyuncs.com",
|
||||
@@ -29,6 +50,27 @@
|
||||
"secretKey": "TzgvyA3zGXMUnpilJNUlyMYHfosl1hBMl6lxPmjy",
|
||||
"bucketName": "hir-test",
|
||||
"viewEndpoint": "http://106.14.89.110:9001/hir-test/"
|
||||
},
|
||||
// AWS S3 对象存储服务的配置
|
||||
"AWS": {
|
||||
// AWS S3 的Region
|
||||
"Region": "us-east-1",
|
||||
// AWS S3 的内部访问端点
|
||||
"EndPoint": "s3.us-east-1.amazonaws.com",
|
||||
// 是否使用 SSL
|
||||
"UseSSL": true,
|
||||
// AWS S3 的角色 ARN
|
||||
"RoleArn": "arn:aws:iam::471112624751:role/uat_s3_access",
|
||||
// AWS S3 的访问密钥 ID
|
||||
"AccessKeyId": "AKIAW3MEAFJX7IPXISP4",
|
||||
// AWS S3 的访问密钥 Secret
|
||||
"SecretAccessKey": "Pgrg3le5jPxZQ7MR1yYNS30J0XRyJeKVyIIjElXc",
|
||||
// AWS S3 的Bucket名称
|
||||
"BucketName": "ei-med-s3-lili-uat-store",
|
||||
// AWS S3 的访问端点
|
||||
"ViewEndpoint": "https://ei-med-s3-lili-uat-store.s3.amazonaws.com",
|
||||
// AWS S3 的持续数秒
|
||||
"DurationSeconds": 7200
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
"ObjectStoreService": {
|
||||
"ObjectStoreUse": "AliyunOSS",
|
||||
"IsOpenStoreSync": true,
|
||||
"ApiDeployRegion": "CN",
|
||||
"SyncConfigList": [
|
||||
{
|
||||
"Domain": "irc.test.rayplus.net",
|
||||
"Primary": "AliyunOSS",
|
||||
"Target": "AWS",
|
||||
"UploadRegion": "CN",
|
||||
"TargetRegion": "US",
|
||||
"IsOpenSync": false
|
||||
},
|
||||
{
|
||||
"Domain": "lili.uat.extimaging.com",
|
||||
"Primary": "AWS",
|
||||
"Target": "AliyunOSS",
|
||||
"UploadRegion": "US",
|
||||
"TargetRegion": "CN",
|
||||
"IsOpenSync": false
|
||||
}
|
||||
],
|
||||
"AliyunOSS": {
|
||||
"RegionId": "cn-shanghai",
|
||||
"InternalEndpoint": "https://oss-cn-shanghai-internal.aliyuncs.com",
|
||||
"EndPoint": "https://oss-cn-shanghai.aliyuncs.com",
|
||||
"AccessKeyId": "LTAI5tRRZehUp2V9pyTPtAJm",
|
||||
"AccessKeySecret": "FLizxkHsMm4CGYHtkV8E3PNJJZU7oV",
|
||||
"RoleArn": "acs:ram::1899121822495495:role/dev-oss-access",
|
||||
"BucketName": "rayplus-irc-test-store",
|
||||
"ViewEndpoint": "https://rayplus-irc-test-store.oss-cn-shanghai.aliyuncs.com",
|
||||
"Region": "oss-cn-shanghai",
|
||||
"DurationSeconds": 7200
|
||||
},
|
||||
// AWS S3 对象存储服务的配置
|
||||
"AWS": {
|
||||
// AWS S3 的Region
|
||||
"Region": "us-east-1",
|
||||
// AWS S3 的内部访问端点
|
||||
"EndPoint": "s3.us-east-1.amazonaws.com",
|
||||
// 是否使用 SSL
|
||||
"UseSSL": true,
|
||||
// AWS S3 的角色 ARN
|
||||
"RoleArn": "arn:aws:iam::471112624751:role/uat_s3_access",
|
||||
// AWS S3 的访问密钥 ID
|
||||
"AccessKeyId": "AKIAW3MEAFJX7IPXISP4",
|
||||
// AWS S3 的访问密钥 Secret
|
||||
"SecretAccessKey": "Pgrg3le5jPxZQ7MR1yYNS30J0XRyJeKVyIIjElXc",
|
||||
// AWS S3 的Bucket名称
|
||||
"BucketName": "ei-med-s3-lili-uat-store",
|
||||
// AWS S3 的访问端点
|
||||
"ViewEndpoint": "https://ei-med-s3-lili-uat-store.s3.amazonaws.com",
|
||||
// AWS S3 的持续数秒
|
||||
"DurationSeconds": 7200
|
||||
}
|
||||
},
|
||||
"ConnectionStrings": {
|
||||
"RemoteNew": "Server=106.14.89.110,1435;Database=Test_Rayplus;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
||||
"Hangfire": "Server=106.14.89.110,1435;Database=Test_Rayplus_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
|
||||
},
|
||||
"DicomSCPServiceConfig": {
|
||||
"CalledAEList": [
|
||||
"STORESCP"
|
||||
],
|
||||
"ServerPort": 11112
|
||||
}
|
||||
|
||||
}
|
||||
@@ -8,6 +8,38 @@
|
||||
},
|
||||
"ObjectStoreService": {
|
||||
"ObjectStoreUse": "AWS",
|
||||
"IsOpenStoreSync": true,
|
||||
"ApiDeployRegion": "US",
|
||||
"SyncConfigList": [
|
||||
{
|
||||
"Domain": "eilili.elevateimaging.ai",
|
||||
"Primary": "AliyunOSS",
|
||||
"Target": "AWS",
|
||||
"UploadRegion": "CN",
|
||||
"TargetRegion": "US",
|
||||
"IsOpenSync": true
|
||||
},
|
||||
{
|
||||
"Domain": "lili.elevateimaging.ai",
|
||||
"Primary": "AWS",
|
||||
"Target": "AliyunOSS",
|
||||
"UploadRegion": "US",
|
||||
"TargetRegion": "CN",
|
||||
"IsOpenSync": true
|
||||
}
|
||||
],
|
||||
"AliyunOSS": {
|
||||
"RegionId": "cn-shanghai",
|
||||
"InternalEndpoint": "https://oss-cn-shanghai-internal.aliyuncs.com",
|
||||
"EndPoint": "https://oss-cn-shanghai.aliyuncs.com",
|
||||
"AccessKeyId": "LTAI5tNRTsqL6aWmHkDmTwoH",
|
||||
"AccessKeySecret": "7mtGz3qrYWI6JMMBZiLeC119VWicZH",
|
||||
"RoleArn": "acs:ram::1899121822495495:role/irc-oss-access",
|
||||
"BucketName": "zy-lili-store",
|
||||
"ViewEndpoint": "https://zy-lili-cache.oss-cn-shanghai.aliyuncs.com",
|
||||
"Region": "oss-cn-shanghai",
|
||||
"DurationSeconds": 7200
|
||||
},
|
||||
"AWS": {
|
||||
"Region": "us-east-1",
|
||||
"EndPoint": "s3.us-east-1.amazonaws.com",
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
"ObjectStoreService": {
|
||||
"ObjectStoreUse": "AliyunOSS",
|
||||
"IsOpenStoreSync": true,
|
||||
"ApiDeployRegion": "CN",
|
||||
"SyncConfigList": [
|
||||
{
|
||||
"Domain": "irc.uat.extimaging.com",
|
||||
"Primary": "AliyunOSS",
|
||||
"Target": "AWS",
|
||||
"UploadRegion": "CN",
|
||||
"TargetRegion": "US",
|
||||
"IsOpenSync": true
|
||||
},
|
||||
{
|
||||
"Domain": "lili.uat.extimaging.com",
|
||||
"Primary": "AWS",
|
||||
"Target": "AliyunOSS",
|
||||
"UploadRegion": "US",
|
||||
"TargetRegion": "CN",
|
||||
"IsOpenSync": true
|
||||
}
|
||||
],
|
||||
"AliyunOSS": {
|
||||
"RegionId": "cn-shanghai",
|
||||
"InternalEndpoint": "https://oss-cn-shanghai-internal.aliyuncs.com",
|
||||
"EndPoint": "https://oss-cn-shanghai.aliyuncs.com",
|
||||
"AccessKeyId": "LTAI5tRRZehUp2V9pyTPtAJm",
|
||||
"AccessKeySecret": "FLizxkHsMm4CGYHtkV8E3PNJJZU7oV",
|
||||
"RoleArn": "acs:ram::1899121822495495:role/dev-oss-access",
|
||||
"BucketName": "zy-irc-test-store",
|
||||
"ViewEndpoint": "https://zy-irc-test-store.oss-cn-shanghai.aliyuncs.com",
|
||||
"Region": "oss-cn-shanghai",
|
||||
"DurationSeconds": 7200
|
||||
},
|
||||
// AWS S3 对象存储服务的配置
|
||||
"AWS": {
|
||||
// AWS S3 的Region
|
||||
"Region": "us-east-1",
|
||||
// AWS S3 的内部访问端点
|
||||
"EndPoint": "s3.us-east-1.amazonaws.com",
|
||||
// 是否使用 SSL
|
||||
"UseSSL": true,
|
||||
// AWS S3 的角色 ARN
|
||||
"RoleArn": "arn:aws:iam::471112624751:role/uat_s3_access",
|
||||
// AWS S3 的访问密钥 ID
|
||||
"AccessKeyId": "AKIAW3MEAFJX7IPXISP4",
|
||||
// AWS S3 的访问密钥 Secret
|
||||
"SecretAccessKey": "Pgrg3le5jPxZQ7MR1yYNS30J0XRyJeKVyIIjElXc",
|
||||
// AWS S3 的Bucket名称
|
||||
"BucketName": "ei-med-s3-lili-uat-store",
|
||||
// AWS S3 的访问端点
|
||||
"ViewEndpoint": "https://ei-med-s3-lili-uat-store.s3.amazonaws.com",
|
||||
// AWS S3 的持续数秒
|
||||
"DurationSeconds": 7200
|
||||
}
|
||||
},
|
||||
"ConnectionStrings": {
|
||||
"RemoteNew": "Server=101.132.253.119,1435;Database=Uat_HeAnShu;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
||||
"Hangfire": "Server101.132.253.119,1435;Database=Uat_HeAnShu_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
|
||||
},
|
||||
"DicomSCPServiceConfig": {
|
||||
"CalledAEList": [
|
||||
"STORESCP"
|
||||
],
|
||||
"ServerPort": 11112
|
||||
}
|
||||
|
||||
}
|
||||
@@ -8,17 +8,58 @@
|
||||
},
|
||||
"ObjectStoreService": {
|
||||
"ObjectStoreUse": "AliyunOSS",
|
||||
"IsOpenStoreSync": true,
|
||||
"ApiDeployRegion": "CN",
|
||||
"SyncConfigList": [
|
||||
{
|
||||
"Domain": "irc.uat.extimaging.com",
|
||||
"Primary": "AliyunOSS",
|
||||
"Target": "AWS",
|
||||
"UploadRegion": "CN",
|
||||
"TargetRegion": "US",
|
||||
"IsOpenSync": true
|
||||
},
|
||||
{
|
||||
"Domain": "lili.uat.extimaging.com",
|
||||
"Primary": "AWS",
|
||||
"Target": "AliyunOSS",
|
||||
"UploadRegion": "US",
|
||||
"TargetRegion": "CN",
|
||||
"IsOpenSync": true
|
||||
}
|
||||
],
|
||||
"AliyunOSS": {
|
||||
"RegionId": "cn-shanghai",
|
||||
"InternalEndpoint": "https://oss-cn-shanghai-internal.aliyuncs.com",
|
||||
"EndPoint": "https://oss-cn-shanghai.aliyuncs.com",
|
||||
"AccessKeyId": "LTAI5tFUCCmz5TwghZHsj45Y",
|
||||
"AccessKeySecret": "8evrBy1fVfzJG25i67Jm0xqn9Xcw2T",
|
||||
"RoleArn": "acs:ram::1078130221702011:role/uat-oss-access",
|
||||
"BucketName": "tl-med-irc-uat-store",
|
||||
"ViewEndpoint": "https://tl-med-irc-uat-store.oss-cn-shanghai.aliyuncs.com",
|
||||
"AccessKeyId": "LTAI5tRRZehUp2V9pyTPtAJm",
|
||||
"AccessKeySecret": "FLizxkHsMm4CGYHtkV8E3PNJJZU7oV",
|
||||
"RoleArn": "acs:ram::1899121822495495:role/dev-oss-access",
|
||||
"BucketName": "zy-irc-test-store",
|
||||
"ViewEndpoint": "https://zy-irc-test-store.oss-cn-shanghai.aliyuncs.com",
|
||||
"Region": "oss-cn-shanghai",
|
||||
"DurationSeconds": 7200
|
||||
},
|
||||
// AWS S3 对象存储服务的配置
|
||||
"AWS": {
|
||||
// AWS S3 的Region
|
||||
"Region": "us-east-1",
|
||||
// AWS S3 的内部访问端点
|
||||
"EndPoint": "s3.us-east-1.amazonaws.com",
|
||||
// 是否使用 SSL
|
||||
"UseSSL": true,
|
||||
// AWS S3 的角色 ARN
|
||||
"RoleArn": "arn:aws:iam::471112624751:role/uat_s3_access",
|
||||
// AWS S3 的访问密钥 ID
|
||||
"AccessKeyId": "AKIAW3MEAFJX7IPXISP4",
|
||||
// AWS S3 的访问密钥 Secret
|
||||
"SecretAccessKey": "Pgrg3le5jPxZQ7MR1yYNS30J0XRyJeKVyIIjElXc",
|
||||
// AWS S3 的Bucket名称
|
||||
"BucketName": "ei-med-s3-lili-uat-store",
|
||||
// AWS S3 的访问端点
|
||||
"ViewEndpoint": "https://ei-med-s3-lili-uat-store.s3.amazonaws.com",
|
||||
// AWS S3 的持续数秒
|
||||
"DurationSeconds": 7200
|
||||
}
|
||||
},
|
||||
"ConnectionStrings": {
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
"ObjectStoreService": {
|
||||
"ObjectStoreUse": "AliyunOSS",
|
||||
"IsOpenStoreSync": true,
|
||||
"ApiDeployRegion": "CN",
|
||||
"SyncConfigList": [
|
||||
{
|
||||
"Domain": "irc.uat.rayplus.net",
|
||||
"Primary": "AliyunOSS",
|
||||
"Target": "AWS",
|
||||
"UploadRegion": "CN",
|
||||
"TargetRegion": "US",
|
||||
"IsOpenSync": false
|
||||
},
|
||||
{
|
||||
"Domain": "lili.uat.extimaging.com",
|
||||
"Primary": "AWS",
|
||||
"Target": "AliyunOSS",
|
||||
"UploadRegion": "US",
|
||||
"TargetRegion": "CN",
|
||||
"IsOpenSync": false
|
||||
}
|
||||
],
|
||||
"AliyunOSS": {
|
||||
"RegionId": "cn-shanghai",
|
||||
"InternalEndpoint": "https://oss-cn-shanghai-internal.aliyuncs.com",
|
||||
"EndPoint": "https://oss-cn-shanghai.aliyuncs.com",
|
||||
"AccessKeyId": "LTAI5tFUCCmz5TwghZHsj45Y",
|
||||
"AccessKeySecret": "8evrBy1fVfzJG25i67Jm0xqn9Xcw2T",
|
||||
"RoleArn": "acs:ram::1078130221702011:role/uat-oss-access",
|
||||
"BucketName": "rayplus-irc-uat-store",
|
||||
"ViewEndpoint": "https://rayplus-irc-uat-store.oss-cn-shanghai.aliyuncs.com",
|
||||
"Region": "oss-cn-shanghai",
|
||||
"DurationSeconds": 7200
|
||||
},
|
||||
// AWS S3 对象存储服务的配置
|
||||
"AWS": {
|
||||
// AWS S3 的Region
|
||||
"Region": "us-east-1",
|
||||
// AWS S3 的内部访问端点
|
||||
"EndPoint": "s3.us-east-1.amazonaws.com",
|
||||
// 是否使用 SSL
|
||||
"UseSSL": true,
|
||||
// AWS S3 的角色 ARN
|
||||
"RoleArn": "arn:aws:iam::471112624751:role/uat_s3_access",
|
||||
// AWS S3 的访问密钥 ID
|
||||
"AccessKeyId": "AKIAW3MEAFJX7IPXISP4",
|
||||
// AWS S3 的访问密钥 Secret
|
||||
"SecretAccessKey": "Pgrg3le5jPxZQ7MR1yYNS30J0XRyJeKVyIIjElXc",
|
||||
// AWS S3 的Bucket名称
|
||||
"BucketName": "ei-med-s3-lili-uat-store",
|
||||
// AWS S3 的访问端点
|
||||
"ViewEndpoint": "https://ei-med-s3-lili-uat-store.s3.amazonaws.com",
|
||||
// AWS S3 的持续数秒
|
||||
"DurationSeconds": 7200
|
||||
}
|
||||
},
|
||||
"ConnectionStrings": {
|
||||
"RemoteNew": "Server=101.132.253.119,1435;Database=Uat_Rayplus;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
||||
"Hangfire": "Server101.132.253.119,1435;Database=Uat_Rayplus_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
|
||||
},
|
||||
"DicomSCPServiceConfig": {
|
||||
"CalledAEList": [
|
||||
"STORESCP"
|
||||
],
|
||||
"ServerPort": 11112
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,42 +1,21 @@
|
||||
using AlibabaCloud.SDK.Sts20150401;
|
||||
using Amazon.Auth.AccessControlPolicy;
|
||||
using Amazon.SecurityToken;
|
||||
using AutoMapper;
|
||||
using Azure.Core;
|
||||
using IdentityModel.Client;
|
||||
using IdentityModel.OidcClient;
|
||||
using IRaCIS.Application.Contracts;
|
||||
using IRaCIS.Application.Interfaces;
|
||||
using IRaCIS.Core.Application.Auth;
|
||||
using IRaCIS.Core.Application.Contracts;
|
||||
using IRaCIS.Core.Application.Helper;
|
||||
using IRaCIS.Core.Application.Service;
|
||||
using IRaCIS.Core.Domain.Models;
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using IRaCIS.Core.Infra.EFCore;
|
||||
using IRaCIS.Core.Infrastructure.Extention;
|
||||
using MassTransit;
|
||||
using MassTransit.Futures.Contracts;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Org.BouncyCastle.Tls;
|
||||
using RestSharp;
|
||||
using RestSharp.Authenticators;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Serilog;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using ZiggyCreatures.Caching.Fusion;
|
||||
using AssumeRoleRequest = Amazon.SecurityToken.Model.AssumeRoleRequest;
|
||||
using LoginReturnDTO = IRaCIS.Application.Contracts.LoginReturnDTO;
|
||||
|
||||
namespace IRaCIS.Api.Controllers
|
||||
{
|
||||
@@ -126,7 +105,7 @@ namespace IRaCIS.Api.Controllers
|
||||
var token = _tokenService.GetToken(new UserTokenInfo()
|
||||
{
|
||||
IdentityUserId = Guid.NewGuid(),
|
||||
UserName = "Share001",
|
||||
UserName = "ImageShare",
|
||||
UserTypeEnum = UserTypeEnum.ShareImage,
|
||||
|
||||
});
|
||||
@@ -137,9 +116,14 @@ namespace IRaCIS.Api.Controllers
|
||||
public async Task<IResponseOutput> GetObjectStoreTokenAsync([FromServices] IOptionsMonitor<ObjectStoreServiceOptions> options, [FromServices] IOSSService _oSSService)
|
||||
{
|
||||
|
||||
var result = _oSSService.GetObjectStoreTempToken();
|
||||
var domain = HttpContext.Request.Host.Host;
|
||||
|
||||
//result.AWS = await GetAWSTemToken(options.CurrentValue);
|
||||
|
||||
var result = _oSSService.GetObjectStoreTempToken(domain);
|
||||
|
||||
|
||||
|
||||
Log.Logger.Information($"使用域名:{domain}请求token.返回{result.ToJsonStr()}");
|
||||
|
||||
return ResponseOutput.Ok(result);
|
||||
|
||||
@@ -279,7 +263,7 @@ namespace IRaCIS.Api.Controllers
|
||||
|
||||
var isExpire = _tokenService.IsTokenExpired(token);
|
||||
|
||||
if (!await _useRepository.AnyAsync(t => t.Id == Guid.Parse(userId) && t.EmailToken == token && t.IsFirstAdd) || isExpire)
|
||||
if ( Guid.TryParse(userId,out _) == false || isExpire || !await _useRepository.AnyAsync(t => t.Id == Guid.Parse(userId) && t.EmailToken == token && t.IsFirstAdd) )
|
||||
{
|
||||
decodeUrl = errorUrl + $"?lang={lang}&ErrorMessage={System.Web.HttpUtility.UrlEncode(I18n.T("UserRedirect_InitializationLinkExpire"))} ";
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ namespace IRaCIS.Core.API.Controllers
|
||||
ITrialDocumentService _trialDocumentService,
|
||||
IReadingImageTaskService _iReadingImageTaskService,
|
||||
ITrialConfigService _trialConfigService,
|
||||
IStudyService _studyService,
|
||||
IClinicalAnswerService _clinicalAnswerService,
|
||||
IReadingClinicalDataService _readingClinicalDataService,
|
||||
IQCOperationService _qCOperationService,
|
||||
@@ -146,6 +147,26 @@ namespace IRaCIS.Core.API.Controllers
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 修正患者基本信息
|
||||
/// </summary>
|
||||
/// <param name="opt"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost, Route("Inspection/Study/AmendmentPatientInfo")]
|
||||
[TrialGlobalLimit("AfterStopCannNotOpt")]
|
||||
|
||||
[UnitOfWork]
|
||||
public async Task<IResponseOutput> AmendmentPatientInfo(DataInspectionDto<EditPatientInfoCommand> opt)
|
||||
{
|
||||
|
||||
var singid = await _inspectionService.RecordSing(opt.SignInfo);
|
||||
var result = await _studyService.AmendmentPatientInfo(opt.Data);
|
||||
await _inspectionService.CompletedSign(singid, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 医学审核完成
|
||||
/// </summary>
|
||||
@@ -155,10 +176,19 @@ namespace IRaCIS.Core.API.Controllers
|
||||
[TrialGlobalLimit("AfterStopCannNotOpt")]
|
||||
|
||||
[UnitOfWork]
|
||||
public async Task<IResponseOutput> FinishMedicalReview(DataInspectionDto<FinishMedicalReviewInDto> opt)
|
||||
public async Task<IResponseOutput> FinishMedicalReview(DataInspectionDto<CloseAndFinishMedicalReview> opt)
|
||||
{
|
||||
var singid = await _inspectionService.RecordSing(opt.SignInfo);
|
||||
var result = await _readingMedicalReviewService.FinishMedicalReview(opt.Data);
|
||||
|
||||
await _readingMedicalReviewService.ClosedMedicalReviewDialog(new ClosedMedicalReviewDialogInDto() {
|
||||
DialogCloseReason=opt.Data.DialogCloseReason,
|
||||
IsClosedDialog=opt.Data.IsClosedDialog,
|
||||
MedicalDialogCloseEnum=opt.Data.MedicalDialogCloseEnum,
|
||||
TaskMedicalReviewId=opt.Data.TaskMedicalReviewId
|
||||
});
|
||||
var result = await _readingMedicalReviewService.FinishMedicalReview(new FinishMedicalReviewInDto() {
|
||||
TaskMedicalReviewId=opt.Data.TaskMedicalReviewId
|
||||
});
|
||||
await _inspectionService.CompletedSign(singid, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
using AutoMapper;
|
||||
using ExcelDataReader;
|
||||
using IRaCIS.Application.Interfaces;
|
||||
using IRaCIS.Core.Application.BusinessFilter;
|
||||
using IRaCIS.Core.Application.Contracts;
|
||||
using IRaCIS.Core.Application.Contracts.Dicom;
|
||||
using IRaCIS.Core.Application.Contracts.Dicom.DTO;
|
||||
using IRaCIS.Core.Application.Filter;
|
||||
using IRaCIS.Core.Application.Helper;
|
||||
using IRaCIS.Core.Application.MassTransit.Command;
|
||||
using IRaCIS.Core.Application.Service;
|
||||
using IRaCIS.Core.Application.ViewModel;
|
||||
using IRaCIS.Core.Domain.Models;
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using IRaCIS.Core.Infra.EFCore;
|
||||
@@ -16,8 +15,6 @@ using IRaCIS.Core.Infrastructure;
|
||||
using IRaCIS.Core.Infrastructure.Extention;
|
||||
using MassTransit.Mediator;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
@@ -26,21 +23,15 @@ using Microsoft.AspNetCore.StaticFiles;
|
||||
using Microsoft.AspNetCore.WebUtilities;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
using MiniExcelLibs;
|
||||
using Newtonsoft.Json;
|
||||
using SharpCompress.Archives;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Data;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using Path = System.IO.Path;
|
||||
|
||||
namespace IRaCIS.Core.API.Controllers
|
||||
@@ -131,7 +122,7 @@ namespace IRaCIS.Core.API.Controllers
|
||||
//处理压缩文件
|
||||
if (fileName.Contains(".Zip", StringComparison.OrdinalIgnoreCase) || fileName.Contains(".rar", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var archive = ArchiveFactory.Open(section.Body);
|
||||
var archive = ArchiveFactory.OpenArchive(section.Body);
|
||||
|
||||
foreach (var entry in archive.Entries)
|
||||
{
|
||||
@@ -212,7 +203,7 @@ namespace IRaCIS.Core.API.Controllers
|
||||
//处理压缩文件
|
||||
if (fileName.Contains(".Zip", StringComparison.OrdinalIgnoreCase) || fileName.Contains(".rar", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var archive = ArchiveFactory.Open(section.Body);
|
||||
var archive = ArchiveFactory.OpenArchive(section.Body);
|
||||
|
||||
foreach (var entry in archive.Entries)
|
||||
{
|
||||
@@ -568,6 +559,7 @@ namespace IRaCIS.Core.API.Controllers
|
||||
/// 一致性核查 excel上传 支持三种格式
|
||||
/// </summary>
|
||||
/// <param name="trialId"></param>
|
||||
/// <param name="isFullCheck"></param>
|
||||
/// <param name="oSSService"></param>
|
||||
/// <param name="_inspectionFileRepository"></param>
|
||||
/// <returns></returns>
|
||||
@@ -575,12 +567,14 @@ namespace IRaCIS.Core.API.Controllers
|
||||
[HttpPost("QCOperation/UploadVisitCheckExcel/{trialId:guid}")]
|
||||
[TrialGlobalLimit("AfterStopCannNotOpt")]
|
||||
|
||||
public async Task<IResponseOutput> UploadVisitCheckExcel(Guid trialId, [FromServices] IOSSService oSSService, [FromServices] IRepository<InspectionFile> _inspectionFileRepository)
|
||||
public async Task<IResponseOutput> UploadVisitCheckExcel(Guid trialId, bool isFullCheck, [FromServices] IOSSService oSSService, [FromServices] IRepository<InspectionFile> _inspectionFileRepository)
|
||||
{
|
||||
|
||||
var fileName = string.Empty;
|
||||
var templateFileStream = new MemoryStream();
|
||||
|
||||
var inspectionFileId = Guid.Empty;
|
||||
|
||||
await FileUploadToOSSAsync(async (realFileName, fileStream) =>
|
||||
{
|
||||
fileName = realFileName;
|
||||
@@ -595,9 +589,11 @@ namespace IRaCIS.Core.API.Controllers
|
||||
templateFileStream.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
|
||||
var ossRelativePath = await oSSService.UploadToOSSAsync(fileStream, $"{trialId.ToString()}/InspectionUpload/DataReconciliation", realFileName);
|
||||
var ossRelativePath = await oSSService.UploadToOSSAsync(fileStream, $"{trialId.ToString()}/InspectionUpload/DataReconciliation", realFileName, uploadInfo: new FileUploadRecordAddOrEdit() { TrialId = trialId, BatchDataType = BatchDataType.DataReconciliation });
|
||||
|
||||
await _inspectionFileRepository.AddAsync(new InspectionFile() { FileName = realFileName, RelativePath = ossRelativePath, TrialId = trialId }, true);
|
||||
var addEntity = await _inspectionFileRepository.AddAsync(new InspectionFile() { FileName = realFileName, RelativePath = ossRelativePath, TrialId = trialId }, true);
|
||||
|
||||
inspectionFileId = addEntity.Id;
|
||||
|
||||
return ossRelativePath;
|
||||
|
||||
@@ -743,8 +739,11 @@ namespace IRaCIS.Core.API.Controllers
|
||||
|
||||
if (etcCheckList == null || etcCheckList.Count == 0)
|
||||
{
|
||||
await _inspectionFileRepository.BatchUpdateNoTrackingAsync(t => t.Id == inspectionFileId, u => new InspectionFile() { CheckState = EDCCheckState.Failed });
|
||||
|
||||
//---请保证上传数据符合模板文件中的样式,且存在有效数据。
|
||||
return ResponseOutput.NotOk(_localizer["UploadDownLoad_InvalidData"]);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -764,6 +763,8 @@ namespace IRaCIS.Core.API.Controllers
|
||||
|
||||
if (etcCheckList.Count == 0)
|
||||
{
|
||||
await _inspectionFileRepository.BatchUpdateNoTrackingAsync(t => t.Id == inspectionFileId, u => new InspectionFile() { CheckState = EDCCheckState.Failed });
|
||||
|
||||
//---请保证上传数据符合模板文件中的样式,且存在有效数据。
|
||||
return ResponseOutput.NotOk(_localizer["UploadDownLoad_InvalidData"]);
|
||||
}
|
||||
@@ -775,8 +776,19 @@ namespace IRaCIS.Core.API.Controllers
|
||||
//var client = _mediator.CreateRequestClient<ConsistenCheckCommand>();
|
||||
//await client.GetResponse<ConsistenCheckResult>(new ConsistenCheckCommand() { ETCList = etcCheckList, TrialId = trialId });
|
||||
|
||||
//不获取结果,不用定义返回类型
|
||||
await _mediator.Send(new ConsistenCheckCommand() { ETCList = etcCheckList, TrialId = trialId });
|
||||
if (isFullCheck)
|
||||
{
|
||||
await _mediator.Send(new ConsistenFullCheckCommand() { ETCList = etcCheckList, TrialId = trialId, InspectionFileId = inspectionFileId });
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
//不获取结果,不用定义返回类型
|
||||
await _mediator.Send(new ConsistenCheckCommand() { ETCList = etcCheckList, TrialId = trialId });
|
||||
}
|
||||
|
||||
|
||||
|
||||
return ResponseOutput.Ok();
|
||||
|
||||
@@ -835,7 +847,7 @@ namespace IRaCIS.Core.API.Controllers
|
||||
throw new BusinessValidationFailedException(_localizer["UploadDownLoad_TemplateUploadData"]);
|
||||
}
|
||||
|
||||
var ossRelativePath = await oSSService.UploadToOSSAsync(fileStream, $"{trialId.ToString()}/InspectionUpload/SiteSurvey", realFileName);
|
||||
var ossRelativePath = await oSSService.UploadToOSSAsync(fileStream, $"{trialId.ToString()}/InspectionUpload/SiteSurvey", realFileName, uploadInfo: new FileUploadRecordAddOrEdit() { TrialId = trialId, BatchDataType = BatchDataType.SiteUserSurvey });
|
||||
|
||||
await _inspectionFileRepository.AddAsync(new InspectionFile() { FileName = realFileName, RelativePath = ossRelativePath, TrialId = trialId }, true);
|
||||
|
||||
@@ -911,7 +923,7 @@ namespace IRaCIS.Core.API.Controllers
|
||||
}
|
||||
//处理好 用户类型 和用户类型枚举
|
||||
var sysUserTypeList = _usertypeRepository.Where(t => t.UserTypeEnum == UserTypeEnum.CRA || t.UserTypeEnum == UserTypeEnum.ClinicalResearchCoordinator).Select(t => new { UserTypeId = t.Id, t.UserTypeEnum }).ToList();
|
||||
var siteList = _trialSiteRepository.Where(t => t.TrialId == trialId && siteCodeList.Contains(t.TrialSiteCode)).Select(t => new { t.TrialSiteCode, TrialSiteId = t.Id }).ToList();
|
||||
var siteList = _trialSiteRepository.Where(t => t.TrialId == trialId && siteCodeList.Contains(t.TrialSiteCode)).Select(t => new { t.TrialSiteCode, TrialSiteId = t.Id, t.Country }).ToList();
|
||||
|
||||
foreach (var item in excelList)
|
||||
{
|
||||
@@ -933,6 +945,8 @@ namespace IRaCIS.Core.API.Controllers
|
||||
}
|
||||
|
||||
item.TrialSiteId = siteList.FirstOrDefault(t => t.TrialSiteCode.Trim().ToUpper() == item.TrialSiteCode.Trim().ToUpper()).TrialSiteId;
|
||||
|
||||
item.Country = siteList.FirstOrDefault(t => t.TrialSiteCode.Trim().ToUpper() == item.TrialSiteCode.Trim().ToUpper()).Country;
|
||||
}
|
||||
|
||||
var list = excelList.Where(t => t.UserTypeEnum == UserTypeEnum.ClinicalResearchCoordinator || t.UserTypeEnum == UserTypeEnum.CRA).ToList();
|
||||
@@ -983,7 +997,11 @@ namespace IRaCIS.Core.API.Controllers
|
||||
|
||||
EmailBodyHtml = 4,
|
||||
|
||||
Other = 5
|
||||
ReadKeyFile = 5,
|
||||
|
||||
Other = 6,
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1015,6 +1033,9 @@ namespace IRaCIS.Core.API.Controllers
|
||||
case UploadFileType.EmailBodyHtml:
|
||||
result = await SingleFileUploadAsync((fileName) => FileStoreHelper.GetSystemFileUploadPath(_hostEnvironment, StaticData.Folder.EmailTemplate, fileName));
|
||||
break;
|
||||
case UploadFileType.ReadKeyFile:
|
||||
result = await SingleFileUploadAsync((fileName) => FileStoreHelper.GetSystemFileUploadPath(_hostEnvironment, StaticData.Folder.ReadKetFile, fileName));
|
||||
break;
|
||||
|
||||
default:
|
||||
result = await SingleFileUploadAsync((fileName) => FileStoreHelper.GetOtherFileUploadPath(_hostEnvironment, StaticData.Folder.TempFile, fileName));
|
||||
|
||||
@@ -15,12 +15,13 @@ using Hangfire.Storage;
|
||||
using System.Linq;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using MassTransit.Mediator;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace IRaCIS.Core.API.HostService;
|
||||
|
||||
public class HangfireHostService(IRecurringMessageScheduler _recurringMessageScheduler,
|
||||
IRepository<TrialEmailNoticeConfig> _trialEmailNoticeConfigRepository,
|
||||
|
||||
IConfiguration _config,
|
||||
IRepository<EmailNoticeConfig> _emailNoticeConfigrepository,
|
||||
IMediator _mediator,
|
||||
ILogger<HangfireHostService> _logger) : IHostedService
|
||||
@@ -56,7 +57,9 @@ public class HangfireHostService(IRecurringMessageScheduler _recurringMessageSch
|
||||
}
|
||||
}
|
||||
|
||||
string defaultCulture = _config.GetValue<string>("SystemEmailSendConfig:CronEmailDefaultCulture");
|
||||
|
||||
// 项目手动选择,周期性邮件
|
||||
var taskInfoList = await _trialEmailNoticeConfigRepository.Where(t => t.Trial.TrialStatusStr == StaticData.TrialState.TrialOngoing && t.EmailCron != string.Empty && t.IsAutoSend)
|
||||
.Select(t => new { t.Id, t.Code, TrialCode = t.Trial.TrialCode, t.EmailCron, t.BusinessScenarioEnum, t.TrialId })
|
||||
.ToListAsync();
|
||||
@@ -68,11 +71,11 @@ public class HangfireHostService(IRecurringMessageScheduler _recurringMessageSch
|
||||
|
||||
var trialId = task.TrialId;
|
||||
|
||||
HangfireJobHelper.AddOrUpdateTrialCronJob(jobId, trialId, task.BusinessScenarioEnum, task.EmailCron);
|
||||
HangfireJobHelper.AddOrUpdateTrialCronJob(jobId, trialId, task.BusinessScenarioEnum, task.EmailCron, defaultCulture);
|
||||
}
|
||||
|
||||
|
||||
// 系统邮件定时任务
|
||||
// 系统邮件定时任务 要设置默认语言
|
||||
var systemTaskInfoList = await _emailNoticeConfigrepository.Where(t => t.EmailCron != string.Empty && t.IsAutoSend)
|
||||
.Select(t => new { t.Id, t.Code, t.EmailCron, t.BusinessScenarioEnum, })
|
||||
.ToListAsync();
|
||||
@@ -82,7 +85,7 @@ public class HangfireHostService(IRecurringMessageScheduler _recurringMessageSch
|
||||
//利用主键作为任务Id
|
||||
var jobId = $"{task.Id}_({task.BusinessScenarioEnum})";
|
||||
|
||||
HangfireJobHelper.AddOrUpdateTimingCronJob(jobId, task.BusinessScenarioEnum, task.EmailCron);
|
||||
HangfireJobHelper.AddOrUpdateTimingCronJob(jobId, task.BusinessScenarioEnum, task.EmailCron, defaultCulture);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
using IRaCIS.Core.Application.Helper;
|
||||
using IRaCIS.Core.Application.Service;
|
||||
using IRaCIS.Core.Domain.Models;
|
||||
using IRaCIS.Core.Infra.EFCore;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace IRaCIS.Core.API.HostService;
|
||||
|
||||
|
||||
public class SyncFileRecoveryService(IServiceScopeFactory _scopeFactory, FileSyncQueue _fileSyncQueue) : BackgroundService
|
||||
{
|
||||
|
||||
private readonly int _pageSize = 500;
|
||||
|
||||
/// <summary>
|
||||
/// 多个程序,如果恢复同一份数据,造成重复同步,SCP服务不恢复任务
|
||||
/// </summary>
|
||||
/// <param name="stoppingToken"></param>
|
||||
/// <returns></returns>
|
||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
//在本地调试的时候,不干涉部署同步任务
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
using var scope = _scopeFactory.CreateScope();
|
||||
var fileUploadRecordRepository = scope.ServiceProvider.GetRequiredService<IRepository<FileUploadRecord>>();
|
||||
|
||||
// 延迟启动,保证主机快速启动
|
||||
await Task.Delay(5000, stoppingToken);
|
||||
|
||||
int page = 0;
|
||||
|
||||
|
||||
while (!stoppingToken.IsCancellationRequested)
|
||||
{
|
||||
// 分页获取未入队任务
|
||||
var pending = await fileUploadRecordRepository
|
||||
.Where(x => x.IsNeedSync == true && (x.IsSync == false || x.IsSync == null))
|
||||
.OrderByDescending(x => x.Priority)
|
||||
.Select(t => new { t.Id, t.Priority })
|
||||
.Skip(page * _pageSize)
|
||||
.Take(_pageSize)
|
||||
.ToListAsync(stoppingToken);
|
||||
|
||||
if (!pending.Any())
|
||||
break; // 扫描完毕,退出循环
|
||||
|
||||
foreach (var file in pending)
|
||||
{
|
||||
//file.IsQueued = true; // 避免重复入队
|
||||
_fileSyncQueue.Enqueue(file.Id, file.Priority ?? 0); // 放入队列
|
||||
}
|
||||
|
||||
page++; // 下一页
|
||||
await Task.Delay(200, stoppingToken); // 缓解数据库压力
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class FileSyncWorker(IServiceScopeFactory _scopeFactory, ILogger<FileSyncWorker> _logger, FileSyncQueue _fileSyncQueue) : BackgroundService
|
||||
{
|
||||
|
||||
// ⭐ 自动根据服务器CPU
|
||||
private readonly int _workerCount = Math.Max(1, Environment.ProcessorCount - 1);
|
||||
|
||||
|
||||
|
||||
protected override Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
for (int i = 0; i < _workerCount; i++)
|
||||
Task.Run(() => WorkerLoop(stoppingToken));
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private async Task WorkerLoop(CancellationToken stoppingToken)
|
||||
{
|
||||
while (!stoppingToken.IsCancellationRequested)
|
||||
{
|
||||
var id = await _fileSyncQueue.DequeueAsync(stoppingToken);
|
||||
|
||||
try
|
||||
{
|
||||
using var scope = _scopeFactory.CreateScope();
|
||||
var _fileUploadRecordRepository = scope.ServiceProvider.GetRequiredService<IRepository<FileUploadRecord>>();
|
||||
var _uploadFileSyncRecordRepository = scope.ServiceProvider.GetRequiredService<IRepository<UploadFileSyncRecord>>();
|
||||
|
||||
var syncConfig = (scope.ServiceProvider.GetRequiredService<IOptionsMonitor<ObjectStoreServiceOptions>>()).CurrentValue;
|
||||
|
||||
var oss = scope.ServiceProvider.GetRequiredService<IOSSService>();
|
||||
|
||||
var file = await _fileUploadRecordRepository.FirstOrDefaultAsync(t => t.Id == id);
|
||||
|
||||
// ✅ 不要 return
|
||||
if (file == null || file.IsNeedSync != true || syncConfig.IsOpenStoreSync == false)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (syncConfig.SyncConfigList.Any(t => t.UploadRegion == file.UploadRegion && t.IsOpenSync == false))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
//如果发现系统配置某一边同步进行了关闭,那么就直接返回,不执行任务
|
||||
if (syncConfig.SyncConfigList.Any(t => t.UploadRegion == file.UploadRegion && t.IsOpenSync == false))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var log = new UploadFileSyncRecord
|
||||
{
|
||||
FileUploadRecordId = id,
|
||||
StartTime = DateTime.Now,
|
||||
JobState = jobState.RUNNING
|
||||
};
|
||||
|
||||
await _uploadFileSyncRecordRepository.AddAsync(log);
|
||||
await _uploadFileSyncRecordRepository.SaveChangesAsync(stoppingToken);
|
||||
|
||||
try
|
||||
{
|
||||
await oss.SyncFileAsync(file.Path.TrimStart('/'), file.UploadRegion == "CN" ? ObjectStoreUse.AliyunOSS : ObjectStoreUse.AWS, file.UploadRegion == "CN" ? ObjectStoreUse.AWS : ObjectStoreUse.AliyunOSS);
|
||||
|
||||
file.IsSync = true;
|
||||
file.SyncFinishedTime = DateTime.Now;
|
||||
|
||||
log.JobState = jobState.SUCCESS;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
log.JobState = jobState.FAILED;
|
||||
|
||||
log.Msg = ex.Message.Length > 300 ? ex.Message.Substring(0, 300) : ex.Message;
|
||||
}
|
||||
|
||||
log.EndTime = DateTime.Now;
|
||||
|
||||
await _uploadFileSyncRecordRepository.SaveChangesAsync(stoppingToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Sync failed {Id}", id);
|
||||
}
|
||||
finally
|
||||
{
|
||||
// ⭐⭐⭐ 永远执行
|
||||
_fileSyncQueue.Complete(id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<SignAssembly>false</SignAssembly>
|
||||
<UserSecretsId>354572d4-9e15-4099-807c-63a2d29ff9f2</UserSecretsId>
|
||||
<LangVersion>default</LangVersion>
|
||||
@@ -10,6 +9,7 @@
|
||||
<Company>上海展影医疗科技有限公司</Company>
|
||||
<Product>IRC影像系统 (EICS)</Product>
|
||||
<Copyright>上海展影医疗科技有限公司版权所有</Copyright>
|
||||
<NoWarn>$(NoWarn);NU1903</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
@@ -69,22 +69,21 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AspNetCoreRateLimit" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.14" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="10.0.8" />
|
||||
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.15">
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.8">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="ConfigMapFileProvider" Version="2.0.1" />
|
||||
<PackageReference Include="Hangfire.AspNetCore" Version="1.8.20" />
|
||||
<PackageReference Include="Hangfire.AspNetCore" Version="1.8.23" />
|
||||
<PackageReference Include="Hangfire.Dashboard.BasicAuthorization" Version="1.0.2" />
|
||||
<PackageReference Include="Hangfire.InMemory" Version="1.0.0" />
|
||||
<PackageReference Include="Hangfire.SqlServer" Version="1.8.20" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.10" />
|
||||
<PackageReference Include="Hangfire.SqlServer" Version="1.8.23" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="10.0.8" />
|
||||
<PackageReference Include="Serilog.Formatting.Compact" Version="3.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.Email" Version="4.1.0" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="7.0.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="9.0.1" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="10.2.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -136,7 +136,14 @@
|
||||
<param name="opt"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Core.API.Controllers.InspectionController.FinishMedicalReview(IRaCIS.Core.Application.Service.Inspection.DTO.DataInspectionDto{IRaCIS.Core.Application.Service.Reading.Dto.FinishMedicalReviewInDto})">
|
||||
<member name="M:IRaCIS.Core.API.Controllers.InspectionController.AmendmentPatientInfo(IRaCIS.Core.Application.Service.Inspection.DTO.DataInspectionDto{IRaCIS.Core.Application.Contracts.EditPatientInfoCommand})">
|
||||
<summary>
|
||||
修正患者基本信息
|
||||
</summary>
|
||||
<param name="opt"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Core.API.Controllers.InspectionController.FinishMedicalReview(IRaCIS.Core.Application.Service.Inspection.DTO.DataInspectionDto{IRaCIS.Core.Application.Service.Reading.Dto.CloseAndFinishMedicalReview})">
|
||||
<summary>
|
||||
医学审核完成
|
||||
</summary>
|
||||
@@ -318,11 +325,12 @@
|
||||
<param name="_noneDicomStudyFileRepository"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Core.API.Controllers.StudyController.UploadVisitCheckExcel(System.Guid,IRaCIS.Core.Application.Helper.IOSSService,IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.InspectionFile})">
|
||||
<member name="M:IRaCIS.Core.API.Controllers.StudyController.UploadVisitCheckExcel(System.Guid,System.Boolean,IRaCIS.Core.Application.Helper.IOSSService,IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.InspectionFile})">
|
||||
<summary>
|
||||
一致性核查 excel上传 支持三种格式
|
||||
</summary>
|
||||
<param name="trialId"></param>
|
||||
<param name="isFullCheck"></param>
|
||||
<param name="oSSService"></param>
|
||||
<param name="_inspectionFileRepository"></param>
|
||||
<returns></returns>
|
||||
@@ -343,6 +351,13 @@
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Core.API.HostService.SyncFileRecoveryService.ExecuteAsync(System.Threading.CancellationToken)">
|
||||
<summary>
|
||||
多个程序,如果恢复同一份数据,造成重复同步,SCP服务不恢复任务
|
||||
</summary>
|
||||
<param name="stoppingToken"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="T:IRaCIS.Core.API.IpPolicyRateLimitSetup">
|
||||
<summary>
|
||||
IPLimit限流 启动服务
|
||||
@@ -363,6 +378,11 @@
|
||||
LowerCamelCaseJsonAttribute 可以设置类小写返回给前端
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:Program">
|
||||
<summary>
|
||||
Auto-generated public partial Program class for top-level statement apps.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:ZhaoXi._001.NET5Demo.Practice.WebApi.Utility.Jwt.CustomHSJWTService">
|
||||
<summary>
|
||||
对称可逆加密
|
||||
@@ -398,5 +418,49 @@
|
||||
<param name="withPrivate"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:Microsoft.AspNetCore.OpenApi.Generated.DocumentationCommentIdHelper.CreateDocumentationId(System.Type)">
|
||||
<summary>
|
||||
Generates a documentation comment ID for a type.
|
||||
Example: T:Namespace.Outer+Inner`1 becomes T:Namespace.Outer.Inner`1
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Microsoft.AspNetCore.OpenApi.Generated.DocumentationCommentIdHelper.CreateDocumentationId(System.Reflection.PropertyInfo)">
|
||||
<summary>
|
||||
Generates a documentation comment ID for a property.
|
||||
Example: P:Namespace.ContainingType.PropertyName or for an indexer P:Namespace.ContainingType.Item(System.Int32)
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Microsoft.AspNetCore.OpenApi.Generated.DocumentationCommentIdHelper.CreateDocumentationId(System.Type,System.String)">
|
||||
<summary>
|
||||
Generates a documentation comment ID for a property given its container type and property name.
|
||||
Example: P:Namespace.ContainingType.PropertyName
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Microsoft.AspNetCore.OpenApi.Generated.DocumentationCommentIdHelper.CreateDocumentationId(System.Reflection.MethodInfo)">
|
||||
<summary>
|
||||
Generates a documentation comment ID for a method (or constructor).
|
||||
For example:
|
||||
M:Namespace.ContainingType.MethodName(ParamType1,ParamType2)~ReturnType
|
||||
M:Namespace.ContainingType.#ctor(ParamType)
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Microsoft.AspNetCore.OpenApi.Generated.DocumentationCommentIdHelper.GetTypeDocId(System.Type,System.Boolean,System.Boolean)">
|
||||
<summary>
|
||||
Generates a documentation ID string for a type.
|
||||
This method handles nested types (replacing '+' with '.'),
|
||||
generic types, arrays, pointers, by-ref types, and generic parameters.
|
||||
The <paramref name="includeGenericArguments"/> flag controls whether
|
||||
constructed generic type arguments are emitted, while <paramref name="omitGenericArity"/>
|
||||
controls whether the generic arity marker (e.g. "`1") is appended.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Microsoft.AspNetCore.OpenApi.Generated.DocumentationCommentIdHelper.NormalizeDocId(System.String)">
|
||||
<summary>
|
||||
Normalizes a documentation comment ID to match the compiler-style format.
|
||||
Strips the return type suffix for ordinary methods but retains it for conversion operators.
|
||||
</summary>
|
||||
<param name="docId">The documentation comment ID to normalize.</param>
|
||||
<returns>The normalized documentation comment ID.</returns>
|
||||
</member>
|
||||
</members>
|
||||
</doc>
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using IRaCIS.Core.API;
|
||||
using FellowOakDicom;
|
||||
using IRaCIS.Core.API;
|
||||
using IRaCIS.Core.API.HostService;
|
||||
using IRaCIS.Core.Application.BusinessFilter;
|
||||
using IRaCIS.Core.Application.BusinessFilter.LegacyController.Database.Api;
|
||||
using IRaCIS.Core.Application.Filter;
|
||||
using IRaCIS.Core.Application.MassTransit.Consumer;
|
||||
using IRaCIS.Core.Application.Service;
|
||||
@@ -13,6 +15,7 @@ using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
@@ -21,6 +24,7 @@ using Newtonsoft.Json;
|
||||
using Serilog;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
@@ -28,6 +32,10 @@ using System.Runtime.InteropServices;
|
||||
AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);
|
||||
AppContext.SetSwitch("Npgsql.DisableDateTimeInfinityConversions", true);
|
||||
|
||||
|
||||
Console.WriteLine("Startup Culture: " + CultureInfo.CurrentCulture.Name);
|
||||
Console.WriteLine("Startup UI Culture: " + CultureInfo.CurrentUICulture.Name);
|
||||
|
||||
#region 获取环境变量
|
||||
//以配置文件为准,否则 从url中取环境值(服务以命令行传递参数启动,配置文件配置了就不需要传递环境参数)
|
||||
var config = new ConfigurationBuilder()
|
||||
@@ -57,7 +65,8 @@ SerilogExtension.AddSerilogSetup(enviromentName);
|
||||
var builder = WebApplication.CreateBuilder(new WebApplicationOptions
|
||||
{
|
||||
|
||||
EnvironmentName = enviromentName
|
||||
EnvironmentName = enviromentName,
|
||||
Args = args
|
||||
});
|
||||
|
||||
#region 主机配置
|
||||
@@ -82,6 +91,10 @@ builder.Services.ConfigureServices(_configuration);
|
||||
|
||||
builder.Services.AddHostedService<HangfireHostService>();
|
||||
|
||||
builder.Services.AddSingleton<FileSyncQueue>();
|
||||
builder.Services.AddHostedService<SyncFileRecoveryService>();
|
||||
builder.Services.AddHostedService<FileSyncWorker>();
|
||||
|
||||
//minimal api 异常处理
|
||||
builder.Services.AddExceptionHandler<GlobalExceptionHandler>();
|
||||
//builder.Services.AddProblemDetails();
|
||||
@@ -90,16 +103,23 @@ builder.Services.AddExceptionHandler<GlobalExceptionHandler>();
|
||||
builder.Services.AddHealthChecks();
|
||||
builder.Services.AddSerilog();
|
||||
//本地化
|
||||
builder.Services.AddJsonLocalization(options => options.ResourcesPath = "Resources");
|
||||
builder.Services.AddJsonLocalization(options => options.ResourcesPath = new[] { "Resources" });
|
||||
|
||||
// 异常、参数统一验证过滤器、Json序列化配置、字符串参数绑型统一Trim()
|
||||
builder.Services.AddControllers(options =>
|
||||
{
|
||||
// 关键配置:禁用不可空引用类型的自动 Required 验证
|
||||
options.SuppressImplicitRequiredAttributeForNonNullableReferenceTypes = true;
|
||||
|
||||
// 插到最前,抢在默认绑定器之前
|
||||
//options.ModelBinderProviders.Insert(0, new SpecificNullableBinderProvider());
|
||||
|
||||
options.Filters.Add<ModelActionFilter>();
|
||||
options.Filters.Add<ProjectExceptionFilter>();
|
||||
options.Filters.Add<UnitOfWorkFilter>();
|
||||
options.Filters.Add<LimitUserRequestAuthorization>();
|
||||
options.Filters.Add<TrialGlobalLimitActionFilter>();
|
||||
options.Filters.Add<RequestDuplicationFilter>();
|
||||
|
||||
})
|
||||
.AddNewtonsoftJsonSetup(builder.Services); // NewtonsoftJson 序列化 处理
|
||||
@@ -160,6 +180,7 @@ var env = app.Environment;
|
||||
|
||||
#region 配置中间件
|
||||
|
||||
DicomSetupBuilder.UseServiceProvider(app.Services);
|
||||
|
||||
app.UseMiddleware<EncryptionRequestMiddleware>();
|
||||
|
||||
@@ -265,6 +286,7 @@ try
|
||||
#region 运行环境 部署平台
|
||||
|
||||
Log.Logger.Warning($"当前环境:{enviromentName}");
|
||||
Log.Logger.Warning($".NET 运行时版本:{Environment.Version}");
|
||||
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:3305",
|
||||
"applicationUrl": "http://0.0.0.0:3305",
|
||||
"sslPort": 0
|
||||
}
|
||||
},
|
||||
@@ -23,7 +23,25 @@
|
||||
"ASPNETCORE_ENVIRONMENT": "Test_IRC",
|
||||
"ASPNETCORE_OpenSwagger": "true"
|
||||
},
|
||||
"applicationUrl": "http://localhost:6100"
|
||||
"applicationUrl": "http://0.0.0.0:6100"
|
||||
},
|
||||
"IRaCIS.Test_Rayplus": {
|
||||
"commandName": "Project",
|
||||
"launchBrowser": true,
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Test_Rayplus",
|
||||
"ASPNETCORE_OpenSwagger": "true"
|
||||
},
|
||||
"applicationUrl": "http://0.0.0.0:6100"
|
||||
},
|
||||
"IRaCIS.Uat_Rayplus": {
|
||||
"commandName": "Project",
|
||||
"launchBrowser": true,
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Uat_Rayplus",
|
||||
"ASPNETCORE_OpenSwagger": "true"
|
||||
},
|
||||
"applicationUrl": "http://0.0.0.0:6100"
|
||||
},
|
||||
"IRaCIS.Test_IRC_PGSQL": {
|
||||
"commandName": "Project",
|
||||
@@ -31,15 +49,15 @@
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Test_IRC_PGSQL"
|
||||
},
|
||||
"applicationUrl": "http://localhost:6100"
|
||||
"applicationUrl": "http://0.0.0.0:6100"
|
||||
},
|
||||
"IRaCIS.Event_IRC": {
|
||||
"IRaCIS.Prod_Event_IRC": {
|
||||
"commandName": "Project",
|
||||
"launchBrowser": true,
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Event_IRC"
|
||||
"ASPNETCORE_ENVIRONMENT": "Prod_Event_IRC"
|
||||
},
|
||||
"applicationUrl": "http://localhost:6100"
|
||||
"applicationUrl": "http://0.0.0.0:6100"
|
||||
},
|
||||
"Docker": {
|
||||
"commandName": "Docker",
|
||||
@@ -53,7 +71,7 @@
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Uat_IRC"
|
||||
},
|
||||
"applicationUrl": "http://localhost:6100"
|
||||
"applicationUrl": "http://0.0.0.0:6100"
|
||||
},
|
||||
"IRaCIS.Prod_IRC": {
|
||||
"commandName": "Project",
|
||||
@@ -61,7 +79,7 @@
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Prod_IRC"
|
||||
},
|
||||
"applicationUrl": "http://localhost:6100"
|
||||
"applicationUrl": "http://0.0.0.0:6100"
|
||||
},
|
||||
"IRaCIS.US_Uat_IRC": {
|
||||
"commandName": "Project",
|
||||
@@ -69,7 +87,7 @@
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "US_Uat_IRC"
|
||||
},
|
||||
"applicationUrl": "http://localhost:6100"
|
||||
"applicationUrl": "http://0.0.0.0:6100"
|
||||
},
|
||||
"IRaCIS.US_Prod_IRC": {
|
||||
"commandName": "Project",
|
||||
@@ -77,7 +95,7 @@
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "US_Prod_IRC"
|
||||
},
|
||||
"applicationUrl": "http://localhost:6100"
|
||||
"applicationUrl": "http://0.0.0.0:6100"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@@ -39,6 +39,8 @@ namespace IRaCIS.Core.API._PipelineExtensions.Serilog
|
||||
|
||||
var requestBodyPayload = await ReadRequestBody(context.Request);
|
||||
|
||||
context.Items["RequestBody"] = requestBodyPayload;
|
||||
|
||||
using (LogContext.PushProperty("RequestBody", requestBodyPayload))
|
||||
{
|
||||
//await _next.Invoke()
|
||||
|
||||
@@ -8,13 +8,21 @@ namespace IRaCIS.Core.API
|
||||
{
|
||||
public static void AddDicomSetup(this IServiceCollection services)
|
||||
{
|
||||
|
||||
// ⭐ 先做全局 DICOM 配置
|
||||
new DicomSetupBuilder()
|
||||
.RegisterServices(s => s.AddFellowOakDicom()
|
||||
.AddTranscoderManager<FellowOakDicom.Imaging.NativeCodec.NativeTranscoderManager>()
|
||||
.AddImageManager<ImageSharpImageManager>()
|
||||
)
|
||||
.SkipValidation()
|
||||
.Build();
|
||||
.SkipValidation() // 👈 在这里设置
|
||||
.Build();
|
||||
|
||||
services.AddFellowOakDicom().AddTranscoderManager<FellowOakDicom.Imaging.NativeCodec.NativeTranscoderManager>().AddImageManager<ImageSharpImageManager>();
|
||||
|
||||
// new DicomSetupBuilder()
|
||||
// .RegisterServices(s => s.AddFellowOakDicom()
|
||||
//.AddTranscoderManager<FellowOakDicom.Imaging.NativeCodec.NativeTranscoderManager>()
|
||||
// .AddImageManager<ImageSharpImageManager>()
|
||||
// )
|
||||
// .SkipValidation()
|
||||
// .Build();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,12 +40,12 @@ namespace IRaCIS.Core.API
|
||||
var dbType = configuration.GetSection("ConnectionStrings:Db_Type").Value;
|
||||
if (!string.IsNullOrWhiteSpace(dbType) && dbType == "pgsql")
|
||||
{
|
||||
options.UseNpgsql(configuration.GetSection("ConnectionStrings:RemoteNew").Value, contextOptionsBuilder => contextOptionsBuilder.EnableRetryOnFailure());
|
||||
options.UseNpgsql(configuration.GetSection("ConnectionStrings:RemoteNew").Value, contextOptionsBuilder => contextOptionsBuilder.EnableRetryOnFailure().CommandTimeout(90));
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
options.UseSqlServer(configuration.GetSection("ConnectionStrings:RemoteNew").Value, contextOptionsBuilder => contextOptionsBuilder.EnableRetryOnFailure());
|
||||
options.UseSqlServer(configuration.GetSection("ConnectionStrings:RemoteNew").Value, contextOptionsBuilder => contextOptionsBuilder.EnableRetryOnFailure().CommandTimeout(90));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -71,29 +71,27 @@ namespace IRaCIS.Core.API
|
||||
|
||||
DateTime dateTime;
|
||||
|
||||
if (reader.ValueType == typeof(DateTime) || reader.ValueType == typeof(DateTime?))
|
||||
// 2. 检查目标类型是否可空
|
||||
bool isNullable = objectType == typeof(DateTime?);
|
||||
|
||||
var canConvert = DateTime.TryParse(reader.Value?.ToString(), out dateTime);
|
||||
|
||||
|
||||
if (isNullable == false && canConvert == false)
|
||||
{
|
||||
DateTime? nullableDateTime = reader.Value as DateTime?;
|
||||
|
||||
|
||||
if (nullableDateTime != null && nullableDateTime.HasValue)
|
||||
{
|
||||
dateTime = nullableDateTime.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
throw new JsonSerializationException($"Could not convert string to DateTime: {reader.Value} Path {reader.Path}");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (DateTime.TryParse((string)reader.Value, out dateTime) == false)
|
||||
if (canConvert == false)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////如果前端传递带时区的,那么转换会报错,需要DateTimeKind.Unspecified
|
||||
//dateTime = DateTime.SpecifyKind(dateTime, DateTimeKind.Unspecified);
|
||||
|
||||
// 将客户端时间转换为服务器时区的时间
|
||||
var serverZoneTime = TimeZoneInfo.ConvertTime(dateTime, _clientTimeZone, TimeZoneInfo.Local);
|
||||
@@ -113,7 +111,10 @@ namespace IRaCIS.Core.API
|
||||
//第一个参数默认使用系统本地时区 也就是应用服务器的时区
|
||||
DateTime clientZoneTime = TimeZoneInfo.ConvertTime(nullableDateTime.Value, _clientTimeZone);
|
||||
|
||||
//writer.WriteValue(clientZoneTime);
|
||||
|
||||
//// 最简单的方式:创建 DateTimeOffset
|
||||
//DateTimeOffset dateTimeOffset = new DateTimeOffset(clientZoneTime, _clientTimeZone.GetUtcOffset(clientZoneTime));
|
||||
//writer.WriteValue(dateTimeOffset.ToString("yyyy-MM-dd HH:mm:sszzz"));
|
||||
|
||||
writer.WriteValue(clientZoneTime.ToString(_dateFormat));
|
||||
}
|
||||
|
||||
@@ -3,8 +3,10 @@ using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Metadata;
|
||||
|
||||
namespace IRaCIS.Core.API
|
||||
{
|
||||
@@ -51,34 +53,90 @@ namespace IRaCIS.Core.API
|
||||
}
|
||||
public class NullToEmptyStringValueProvider : IValueProvider
|
||||
{
|
||||
PropertyInfo _MemberInfo;
|
||||
PropertyInfo _memberInfo;
|
||||
private readonly bool _isNullable;
|
||||
public NullToEmptyStringValueProvider(PropertyInfo memberInfo)
|
||||
{
|
||||
_MemberInfo = memberInfo;
|
||||
_memberInfo = memberInfo;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
// DTO → JSON 返回前端的时候 处理null 变为"" 方便前端判断
|
||||
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;
|
||||
var result = _memberInfo.GetValue(target);
|
||||
// 检查类型是否为string或string?
|
||||
if (_memberInfo.PropertyType == typeof(string) && result == null)
|
||||
{
|
||||
|
||||
#region 返回的时候处理 string string? null 为"" 不区分处理
|
||||
|
||||
|
||||
//// 如果是string?类型,返回null 可以做到,但是得反射,因为 string string? 是编译层区分的
|
||||
|
||||
//var isNullable1 = _memberInfo.CustomAttributes
|
||||
// .Any(a => a.AttributeType.Name == "NullableAttribute");
|
||||
|
||||
////var isNullable2 = _memberInfo.CustomAttributes
|
||||
//// .Any(a => a.AttributeType.FullName == "System.Runtime.CompilerServices.NullableAttribute");
|
||||
|
||||
//if (isNullable1)
|
||||
//{
|
||||
// return result;
|
||||
//}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
// 如果是string类型,返回空字符串
|
||||
return string.Empty;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
//影响 模型绑定时接收前端的值,同时影响 正常 JSON 序列化
|
||||
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()*/);
|
||||
_memberInfo.SetValue(target, value);
|
||||
|
||||
#region 前端针对 string 类型的变量,如果传递null 会报错必传
|
||||
|
||||
//if (_memberInfo.PropertyType == typeof(string))
|
||||
//{
|
||||
// _memberInfo.SetValue(target, value == null ? string.Empty : value);
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
// _memberInfo.SetValue(target, value);
|
||||
//}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 处理模型验证区分 string string?
|
||||
|
||||
//////接收模型的时候 定义的明明是string 但是上面也有该属性,判断不准的 比如阅片跟踪列表查询
|
||||
//var isNullable1 = _memberInfo.CustomAttributes.Any(a => a.AttributeType.Name == "NullableAttribute");
|
||||
|
||||
////不影响 string? 传递null 变为""
|
||||
//if (_memberInfo.PropertyType == typeof(string) && isNullable1 == false)
|
||||
//{
|
||||
// //如果不处理 前段传递null string不会接收,说前段没传递会验证报错
|
||||
// _memberInfo.SetValue(target, value == null ? string.Empty : value);
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
|
||||
// _memberInfo.SetValue(target, value);
|
||||
|
||||
//}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
_MemberInfo.SetValue(target, value);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ namespace IRaCIS.Core.API
|
||||
.Enrich.FromLogContext()
|
||||
.Filter.ByExcluding(logEvent => logEvent.Properties.ContainsKey("RequestPath") && logEvent.Properties["RequestPath"].ToString().Contains("/health"))
|
||||
.WriteTo.Console()
|
||||
.WriteTo.File($"{AppContext.BaseDirectory}Serilogs/.log", rollingInterval: RollingInterval.Day,retainedFileCountLimit:60);
|
||||
.WriteTo.File($"{AppContext.BaseDirectory}Serilogs/.log", rollingInterval: RollingInterval.Day,retainedFileCountLimit:365);
|
||||
|
||||
#region 根据环境配置是否打开错误发送邮件通知
|
||||
|
||||
@@ -46,19 +46,19 @@ namespace IRaCIS.Core.API
|
||||
var emailConfig = new SystemEmailSendConfig();
|
||||
configuration.GetSection("SystemEmailSendConfig").Bind(emailConfig);
|
||||
|
||||
if (emailConfig.IsOpenErrorNoticeEmail)
|
||||
{
|
||||
config.WriteTo.Email(options: new Serilog.Sinks.Email.EmailSinkOptions()
|
||||
{
|
||||
From = emailConfig.FromEmail,
|
||||
To = emailConfig.ErrorNoticeEmailList,
|
||||
Host = emailConfig.Host,
|
||||
Port = emailConfig.Port,
|
||||
Subject = new MessageTemplateTextFormatter("Log Alert - 系统发生了异常,请核查"),
|
||||
Credentials = new NetworkCredential(emailConfig.FromEmail, emailConfig.AuthorizationCode)
|
||||
//if (emailConfig.IsOpenErrorNoticeEmail)
|
||||
//{
|
||||
// config.WriteTo.Email(options: new Serilog.Sinks.Email.EmailSinkOptions()
|
||||
// {
|
||||
// From = emailConfig.FromEmail,
|
||||
// To = emailConfig.ErrorNoticeEmailList,
|
||||
// Host = emailConfig.Host,
|
||||
// Port = emailConfig.Port,
|
||||
// Subject = new MessageTemplateTextFormatter("Log Alert - 系统发生了异常,请核查"),
|
||||
// Credentials = new NetworkCredential(emailConfig.FromEmail, emailConfig.AuthorizationCode)
|
||||
|
||||
}, restrictedToMinimumLevel: Serilog.Events.LogEventLevel.Error);
|
||||
}
|
||||
// }, restrictedToMinimumLevel: Serilog.Events.LogEventLevel.Error);
|
||||
//}
|
||||
#endregion
|
||||
|
||||
Log.Logger = config.CreateLogger();
|
||||
|
||||
@@ -28,12 +28,13 @@ public static class ServiceCollectionSetup
|
||||
services.AddOptions().Configure<AliyunOSSOptions>(_configuration.GetSection("AliyunOSS"));
|
||||
services.AddOptions().Configure<ObjectStoreServiceOptions>(_configuration.GetSection("ObjectStoreService"));
|
||||
services.AddOptions().Configure<IRCEncreptOption>(_configuration.GetSection("EncrypteResponseConfig"));
|
||||
services.AddOptions().Configure<RequestDuplicationOptions>(_configuration.GetSection("RequestDuplicationOptions"));
|
||||
services.AddOptions().Configure<SystemPacsConfig>(_configuration.GetSection("SystemPacsConfig"));
|
||||
services.Configure<IRaCISBasicConfigOption>(_configuration.GetSection("IRaCISBasicConfig"));
|
||||
|
||||
services.Configure<ServiceVerifyConfigOption>(_configuration.GetSection("BasicSystemConfig"));
|
||||
|
||||
//转发头设置 获取真实IP
|
||||
//ת��ͷ���� ��ȡ��ʵIP
|
||||
services.Configure<ForwardedHeadersOptions>(options =>
|
||||
{
|
||||
options.ForwardedHeaders =
|
||||
@@ -51,7 +52,7 @@ public static class ServiceCollectionSetup
|
||||
|
||||
services.AddScoped<IObtainTaskAutoCancelJob, ObtainTaskAutoCancelJob>();
|
||||
|
||||
// 注册以Service 结尾的服务
|
||||
// ע����Service ��β�ķ���
|
||||
services.Scan(scan => scan
|
||||
.FromAssemblies(typeof(BaseService).Assembly)
|
||||
.AddClasses(classes => classes.Where(t => t.Name.Contains("Service")))
|
||||
@@ -70,23 +71,23 @@ public static class ServiceCollectionSetup
|
||||
#endregion
|
||||
|
||||
|
||||
// MediatR 进程内消息 事件解耦 从程序集中 注册命令和handler对应关系
|
||||
// MediatR ��������Ϣ �¼����� �ӳ����� ע�������handler��Ӧ��ϵ
|
||||
//builder.Services.AddMediatR(cfg => cfg.RegisterServicesFromAssemblyContaining<ConsistencyVerificationHandler>());
|
||||
|
||||
|
||||
|
||||
#region 历史废弃配置
|
||||
#region ��ʷ��������
|
||||
//builder.Services.AddMemoryCache();
|
||||
////上传限制 配置
|
||||
////�ϴ����� ����
|
||||
//builder.Services.Configure<FormOptions>(options =>
|
||||
//{
|
||||
// options.MultipartBodyLengthLimit = int.MaxValue;
|
||||
// options.ValueCountLimit = int.MaxValue;
|
||||
// options.ValueLengthLimit = int.MaxValue;
|
||||
//});
|
||||
//IP 限流 可设置白名单 或者黑名单
|
||||
//IP ���� �����ð����� ���ߺ�����
|
||||
//services.AddIpPolicyRateLimitSetup(_configuration);
|
||||
// 用户类型 策略授权
|
||||
// �û����� ������Ȩ
|
||||
//services.AddAuthorizationPolicySetup(_configuration);
|
||||
#endregion
|
||||
}
|
||||
@@ -94,25 +95,25 @@ public static class ServiceCollectionSetup
|
||||
|
||||
}
|
||||
|
||||
#region Autofac 废弃
|
||||
#region Autofac ����
|
||||
//public class AutofacModuleSetup : Autofac.Module
|
||||
//{
|
||||
// protected override void Load(ContainerBuilder containerBuilder)
|
||||
// {
|
||||
|
||||
// #region byzhouhang 20210917 此处注册泛型仓储 可以减少Domain层 和Infra.EFcore 两层 空的仓储接口定义和 仓储文件定义
|
||||
// #region byzhouhang 20210917 �˴�ע�᷺�Ͳִ� ���Լ���Domain�� ��Infra.EFcore ���� �յIJִ��ӿڶ���� �ִ��ļ�����
|
||||
|
||||
// containerBuilder.RegisterGeneric(typeof(Repository<>))
|
||||
// .As(typeof(IRepository<>)).InstancePerLifetimeScope();//注册泛型仓储
|
||||
// .As(typeof(IRepository<>)).InstancePerLifetimeScope();//ע�᷺�Ͳִ�
|
||||
|
||||
// containerBuilder.RegisterType<Repository>().As<IRepository>().InstancePerLifetimeScope();
|
||||
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region 指定控制器也由autofac 来进行实例获取 https://www.cnblogs.com/xwhqwer/p/15320838.html
|
||||
// #region ָ��������Ҳ��autofac ������ʵ����ȡ https://www.cnblogs.com/xwhqwer/p/15320838.html
|
||||
|
||||
// //获取所有控制器类型并使用属性注入
|
||||
// //��ȡ���п��������Ͳ�ʹ������ע��
|
||||
// containerBuilder.RegisterAssemblyTypes(typeof(BaseService).Assembly)
|
||||
// .Where(type => typeof(IDynamicWebApi).IsAssignableFrom(type))
|
||||
// .PropertiesAutowired();
|
||||
@@ -128,7 +129,7 @@ public static class ServiceCollectionSetup
|
||||
// //containerBuilder.RegisterType<UserInfo>().As<IUserInfo>().InstancePerLifetimeScope();
|
||||
|
||||
|
||||
// //注册hangfire任务 依赖注入
|
||||
// //ע��hangfire���� ����ע��
|
||||
// containerBuilder.RegisterType<ObtainTaskAutoCancelJob>().As<IObtainTaskAutoCancelJob>().InstancePerDependency();
|
||||
|
||||
// }
|
||||
|
||||
@@ -1,15 +1,7 @@
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using Microsoft.OpenApi;
|
||||
using Swashbuckle.AspNetCore.Filters;
|
||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||
using Swashbuckle.AspNetCore.SwaggerUI;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace IRaCIS.Core.API;
|
||||
@@ -52,18 +44,21 @@ public static class SwaggerSetup
|
||||
services.AddSwaggerGen(options =>
|
||||
{
|
||||
|
||||
typeof(SwaggerVersion).GetFields(BindingFlags.Public | BindingFlags.Static).ToList()
|
||||
.ForEach(field =>
|
||||
{
|
||||
var description = field.GetCustomAttribute<DescriptionAttribute>()?.Description ?? field.Name;
|
||||
options.SwaggerDoc(field.Name, new Microsoft.OpenApi.Models.OpenApiInfo
|
||||
{
|
||||
Version = field.Name,
|
||||
Description = $"{field.Name} API",
|
||||
Title = description // 使用Description作为Title
|
||||
});
|
||||
});
|
||||
// 使用反射获取字段并动态创建 Swagger 文档
|
||||
typeof(SwaggerVersion).GetFields(BindingFlags.Public | BindingFlags.Static)
|
||||
.ToList()
|
||||
.ForEach(field =>
|
||||
{
|
||||
var description = field.GetCustomAttribute<DescriptionAttribute>()?.Description ?? field.Name;
|
||||
options.SwaggerDoc(field.Name, new OpenApiInfo
|
||||
{
|
||||
Version = field.Name,
|
||||
Description = $"{field.Name} API",
|
||||
Title = description // 使用 Description 作为 Title
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
// 接口排序
|
||||
options.OrderActionsBy(o => o.GroupName);
|
||||
|
||||
@@ -116,6 +111,9 @@ public static class SwaggerSetup
|
||||
//DocExpansion设置为none可折叠所有方法
|
||||
options.DocExpansion(DocExpansion.None);
|
||||
|
||||
// 开启Swagger UI的搜索/过滤功能
|
||||
options.EnableFilter();
|
||||
|
||||
//DefaultModelsExpandDepth设置为 - 1 可不显示models
|
||||
options.DefaultModelsExpandDepth(-1);
|
||||
});
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
"ConnectionStrings": {
|
||||
"RemoteNew": "Server=101.132.253.119,1435;Database=irc_Prpd_bak;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
||||
"Hangfire": "Server=101.132.253.119,1435;Database=irc_Hangfire_bak;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
|
||||
},
|
||||
"ObjectStoreService": {
|
||||
"ObjectStoreUse": "AliyunOSS",
|
||||
"AliyunOSS": {
|
||||
"RegionId": "cn-shanghai",
|
||||
"InternalEndpoint": "https://oss-cn-shanghai-internal.aliyuncs.com",
|
||||
"EndPoint": "https://oss-cn-shanghai.aliyuncs.com",
|
||||
"AccessKeyId": "LTAI5tFUCCmz5TwghZHsj45Y",
|
||||
"AccessKeySecret": "8evrBy1fVfzJG25i67Jm0xqn9Xcw2T",
|
||||
"RoleArn": "acs:ram::1078130221702011:role/uat-oss-access",
|
||||
"BucketName": "tl-med-irc-event-store",
|
||||
"ViewEndpoint": "https://tl-med-irc-event-store.oss-cn-shanghai.aliyuncs.com",
|
||||
"Region": "oss-cn-shanghai",
|
||||
"DurationSeconds": 7200
|
||||
},
|
||||
"MinIO": {
|
||||
"endpoint": "http://192.168.3.68",
|
||||
"port": "8001",
|
||||
"useSSL": false,
|
||||
"accessKey": "IDFkwEpWej0b4DtiuThL",
|
||||
"secretKey": "Lhuu83yMhVwu7c1SnjvGY6lq74jzpYqifK6Qtj4h",
|
||||
"bucketName": "test"
|
||||
}
|
||||
},
|
||||
|
||||
"BasicSystemConfig": {
|
||||
|
||||
"OpenUserComplexPassword": true,
|
||||
|
||||
"OpenSignDocumentBeforeWork": true,
|
||||
|
||||
"OpenLoginLimit": true,
|
||||
"LoginMaxFailCount": 5,
|
||||
|
||||
"LoginFailLockMinutes": 30,
|
||||
"AutoLoginOutMinutes": 60,
|
||||
|
||||
"ContinuousReadingTimeMin": 120,
|
||||
|
||||
"ReadingRestTimeMin": 10,
|
||||
|
||||
"IsNeedChangePassWord": true,
|
||||
|
||||
"ChangePassWordDays": 90,
|
||||
|
||||
// 模板类型 1 Elevate 2 Extensive
|
||||
"TemplateType": 2
|
||||
},
|
||||
"SystemEmailSendConfig": {
|
||||
"Port": 465,
|
||||
"Host": "smtp.qiye.aliyun.com",
|
||||
"FromEmail": "uat@extimaging.com",
|
||||
"FromName": "UAT_IRC",
|
||||
"AuthorizationCode": "SHzyyl2021",
|
||||
"SiteUrl": "http://irc.event.extimaging.com/login",
|
||||
"CompanyName": "Extensive Imaging",
|
||||
"CompanyNameCN": "上海展影医疗科技有限公司",
|
||||
"CompanyShortName": "Extensive Imaging",
|
||||
"CompanyShortNameCN": "展影医疗",
|
||||
"IsEnv_US": false
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
"ConnectionStrings": {
|
||||
"RemoteNew": "Server=101.132.193.237,1433;Database=Prod_IRC_pidr_restore;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
||||
"Hangfire": "Server=101.132.193.237,1433;Database=irc_Hangfire_bak;User ID=sa;Password=xc@123456;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"
|
||||
},
|
||||
"WeComNoticeConfig": {
|
||||
"IsOpenWeComNotice": true,
|
||||
"WebhookUrl": "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=4355b98e-1e72-4678-8dfb-2fc6ad0bf449", //4355b98e-1e72-4678-8dfb-2fc6ad0bf449 //cdd97aab-d256-4f07-9145-a0a2b1555322
|
||||
"APINoticeUserList": [ "u", "wait..." ],
|
||||
"VueNoticeUserList": [ "wangxiaoshuang", "6b7717a31647293621b97b96f74e6f3d" ]
|
||||
},
|
||||
"ObjectStoreService": {
|
||||
"ObjectStoreUse": "AliyunOSS",
|
||||
"IsOpenStoreSync": true,
|
||||
"ApiDeployRegion": "CN",
|
||||
"SyncConfigList": [
|
||||
{
|
||||
"Domain": "irc.extimaging.com",
|
||||
"Primary": "AliyunOSS",
|
||||
"Target": "AWS",
|
||||
"UploadRegion": "CN",
|
||||
"TargetRegion": "US",
|
||||
"IsOpenSync": true
|
||||
},
|
||||
{
|
||||
"Domain": "eiirc.extimaging.com",
|
||||
"Primary": "AWS",
|
||||
"Target": "AliyunOSS",
|
||||
"UploadRegion": "US",
|
||||
"TargetRegion": "CN",
|
||||
"IsOpenSync": true
|
||||
}
|
||||
],
|
||||
"AliyunOSS": {
|
||||
"RegionId": "cn-shanghai",
|
||||
"InternalEndpoint": "https://oss-cn-shanghai-internal.aliyuncs.com",
|
||||
"EndPoint": "https://oss-cn-shanghai.aliyuncs.com",
|
||||
"AccessKeyId": "LTAI5tFUCCmz5TwghZHsj45Y",
|
||||
"AccessKeySecret": "8evrBy1fVfzJG25i67Jm0xqn9Xcw2T",
|
||||
"RoleArn": "acs:ram::1078130221702011:role/uat-oss-access",
|
||||
"BucketName": "tl-med-irc-event-store",
|
||||
"ViewEndpoint": "https://tl-med-rc-event-cache.oss-cn-shanghai.aliyuncs.com",
|
||||
"Region": "oss-cn-shanghai",
|
||||
"DurationSeconds": 7200
|
||||
},
|
||||
"AWS": {
|
||||
"Region": "us-east-1",
|
||||
"EndPoint": "s3.us-east-1.amazonaws.com",
|
||||
"UseSSL": true,
|
||||
"RoleArn": "arn:aws:iam::471112624751:role/lili_s3_access",
|
||||
"AccessKeyId": "AKIAW3MEAFJXZ2TZK7GM",
|
||||
"SecretAccessKey": "9MLQCQ1HifEVW1gf068zBRAOb4wNnfrOkvBVByth",
|
||||
"BucketName": "ei-med-s3-irc-store",
|
||||
"ViewEndpoint": "https://ei-med-s3-irc-store.s3.amazonaws.com",
|
||||
"DurationSeconds": 7200
|
||||
}
|
||||
},
|
||||
"BasicSystemConfig": {
|
||||
// 启用质控风险控制功能
|
||||
"QCRiskControl": true,
|
||||
"OpenUserComplexPassword": true,
|
||||
"OpenSignDocumentBeforeWork": true,
|
||||
|
||||
"OpenLoginLimit": false,
|
||||
"LoginMaxFailCount": 5,
|
||||
"LoginFailLockMinutes": 30,
|
||||
"AutoLoginOutMinutes": 120,
|
||||
|
||||
"OpenLoginMFA": false,
|
||||
|
||||
"ContinuousReadingTimeMin": 120,
|
||||
"ReadingRestTimeMin": 10,
|
||||
|
||||
"IsNeedChangePassWord": true,
|
||||
"ChangePassWordDays": 90,
|
||||
// 模板类型 1 Elevate 2 Extensive
|
||||
"TemplateType": 2,
|
||||
//MFA免验证发送天数
|
||||
"UserMFAVerifyMinutes": 1440
|
||||
},
|
||||
|
||||
"SystemEmailSendConfig": {
|
||||
"Port": 465,
|
||||
"Host": "smtp.qiye.aliyun.com",
|
||||
"Imap": "imap.qiye.aliyun.com",
|
||||
"ImapPort": 993,
|
||||
"FromEmail": "irc@extimaging.com",
|
||||
"FromName": "EIC Imaging System",
|
||||
"AuthorizationCode": "ExtImg@2022",
|
||||
"SiteUrl": "http://irc.extimaging.com/login",
|
||||
|
||||
"PlatformName": "EICS",
|
||||
"PlatformNameCN": "展影云平台",
|
||||
"SystemShortName": "IRC",
|
||||
"OrganizationName": "ExtImaging",
|
||||
"OrganizationNameCN": "ExtImaging",
|
||||
"CompanyName": "Extensive Imaging",
|
||||
"CompanyNameCN": "上海展影医疗科技有限公司",
|
||||
"CompanyShortName": "Extensive Imaging",
|
||||
"CompanyShortNameCN": "展影医疗",
|
||||
"IsEnv_US": false,
|
||||
"EmailRegexStr": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$",
|
||||
"CronEmailDefaultCulture": "zh-CN"
|
||||
},
|
||||
"SystemPacsConfig": {
|
||||
"Port": "11113",
|
||||
"IP": "101.132.193.237"
|
||||
},
|
||||
"RequestDuplicationOptions": {
|
||||
"IsEnabled": true,
|
||||
"DuplicationWindowMs": 500,
|
||||
"CacheTimeSeconds": 5,
|
||||
"ExcludedPaths": [
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -7,13 +7,39 @@
|
||||
}
|
||||
},
|
||||
"ConnectionStrings": {
|
||||
"RemoteNew": "Server=101.132.193.237,1434;Database=Prod_IRC;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true",
|
||||
"Hangfire": "Server=101.132.193.237,1434;Database=Prod_IRC_Hangfire;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true"
|
||||
//"RemoteNew": "Server=prod_mssql_standard,1433;Database=Prod_IRC;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true",
|
||||
//"Hangfire": "Server=prod_mssql_standard,1433;Database=Prod_IRC_Hangfire;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true"
|
||||
//"RemoteNew": "Server=10.10.10.49,1434;Database=Prod_IRC;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true",
|
||||
//"Hangfire": "Server=10.10.10.49,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"
|
||||
},
|
||||
"WeComNoticeConfig": {
|
||||
"IsOpenWeComNotice": true,
|
||||
"WebhookUrl": "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=4355b98e-1e72-4678-8dfb-2fc6ad0bf449", //4355b98e-1e72-4678-8dfb-2fc6ad0bf449 //cdd97aab-d256-4f07-9145-a0a2b1555322
|
||||
"APINoticeUserList": [ "u", "wait..." ],
|
||||
"VueNoticeUserList": [ "wangxiaoshuang", "6b7717a31647293621b97b96f74e6f3d" ]
|
||||
},
|
||||
"ObjectStoreService": {
|
||||
"ObjectStoreUse": "AliyunOSS",
|
||||
"IsOpenStoreSync": true,
|
||||
"ApiDeployRegion": "CN",
|
||||
"SyncConfigList": [
|
||||
{
|
||||
"Domain": "irc.extimaging.com",
|
||||
"Primary": "AliyunOSS",
|
||||
"Target": "AWS",
|
||||
"UploadRegion": "CN",
|
||||
"TargetRegion": "US",
|
||||
"IsOpenSync": true
|
||||
},
|
||||
{
|
||||
"Domain": "eiirc.extimaging.com",
|
||||
"Primary": "AWS",
|
||||
"Target": "AliyunOSS",
|
||||
"UploadRegion": "US",
|
||||
"TargetRegion": "CN",
|
||||
"IsOpenSync": true
|
||||
}
|
||||
],
|
||||
"AliyunOSS": {
|
||||
"RegionId": "cn-shanghai",
|
||||
"InternalEndpoint": "https://oss-cn-shanghai-internal.aliyuncs.com",
|
||||
@@ -26,17 +52,21 @@
|
||||
"Region": "oss-cn-shanghai",
|
||||
"DurationSeconds": 7200
|
||||
},
|
||||
"MinIO": {
|
||||
"endpoint": "http://192.168.3.68",
|
||||
"port": "8001",
|
||||
"useSSL": false,
|
||||
"accessKey": "IDFkwEpWej0b4DtiuThL",
|
||||
"secretKey": "Lhuu83yMhVwu7c1SnjvGY6lq74jzpYqifK6Qtj4h",
|
||||
"bucketName": "test"
|
||||
"AWS": {
|
||||
"Region": "us-east-1",
|
||||
"EndPoint": "s3.us-east-1.amazonaws.com",
|
||||
"UseSSL": true,
|
||||
"RoleArn": "arn:aws:iam::471112624751:role/lili_s3_access",
|
||||
"AccessKeyId": "AKIAW3MEAFJXZ2TZK7GM",
|
||||
"SecretAccessKey": "9MLQCQ1HifEVW1gf068zBRAOb4wNnfrOkvBVByth",
|
||||
"BucketName": "ei-med-s3-irc-store",
|
||||
"ViewEndpoint": "https://ei-med-s3-irc-store.s3.amazonaws.com",
|
||||
"DurationSeconds": 7200
|
||||
}
|
||||
},
|
||||
"BasicSystemConfig": {
|
||||
|
||||
// 启用质控风险控制功能
|
||||
"QCRiskControl": true,
|
||||
"OpenUserComplexPassword": true,
|
||||
"OpenSignDocumentBeforeWork": true,
|
||||
|
||||
@@ -53,31 +83,43 @@
|
||||
"IsNeedChangePassWord": true,
|
||||
"ChangePassWordDays": 90,
|
||||
// 模板类型 1 Elevate 2 Extensive
|
||||
"TemplateType": 2
|
||||
"TemplateType": 2,
|
||||
//MFA免验证发送天数
|
||||
"UserMFAVerifyMinutes": 1440
|
||||
},
|
||||
|
||||
"SystemEmailSendConfig": {
|
||||
"Port": 465,
|
||||
"Host": "smtp.qiye.aliyun.com",
|
||||
"Imap": "imap.qiye.aliyun.com",
|
||||
"ImapPort": 993,
|
||||
"FromEmail": "irc@extimaging.com",
|
||||
"FromName": "IRC Imaging System",
|
||||
"FromName": "EIC Imaging System",
|
||||
"AuthorizationCode": "ExtImg@2022",
|
||||
"SiteUrl": "http://irc.extimaging.com/login",
|
||||
|
||||
"PlatformName": "EICS",
|
||||
"PlatformNameCN": "展影云平台",
|
||||
"SystemShortName": "IRC",
|
||||
"OrganizationName": "Extlmaging",
|
||||
"OrganizationNameCN": "Extlmaging",
|
||||
"OrganizationName": "ExtImaging",
|
||||
"OrganizationNameCN": "ExtImaging",
|
||||
"CompanyName": "Extensive Imaging",
|
||||
"CompanyNameCN": "上海展影医疗科技有限公司",
|
||||
"CompanyShortName": "Extensive Imaging",
|
||||
"CompanyShortNameCN": "展影医疗",
|
||||
"IsEnv_US": false,
|
||||
"IsOpenErrorNoticeEmail": false,
|
||||
"EmailRegexStr": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$",
|
||||
"ErrorNoticeEmailList": [ "872297557@qq.com" ]
|
||||
"CronEmailDefaultCulture": "zh-CN"
|
||||
},
|
||||
"SystemPacsConfig": {
|
||||
"Port": "11113",
|
||||
"IP": "101.132.193.237"
|
||||
},
|
||||
"RequestDuplicationOptions": {
|
||||
"IsEnabled": true,
|
||||
"DuplicationWindowMs": 500,
|
||||
"CacheTimeSeconds": 5,
|
||||
"ExcludedPaths": [
|
||||
]
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
"ConnectionStrings": {
|
||||
"RemoteNew": "Server=prod_mssql_standard,1433;Database=Prod_Rayplus;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true",
|
||||
"Hangfire": "Server=prod_mssql_standard,1433;Database=Prod_Rayplus_Hangfire;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true"
|
||||
},
|
||||
"WeComNoticeConfig": {
|
||||
"IsOpenWeComNotice": true,
|
||||
"WebhookUrl": "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=4355b98e-1e72-4678-8dfb-2fc6ad0bf449", //4355b98e-1e72-4678-8dfb-2fc6ad0bf449 //cdd97aab-d256-4f07-9145-a0a2b1555322
|
||||
"APINoticeUserList": [ "u", "wait..." ],
|
||||
"VueNoticeUserList": [ "wangxiaoshuang", "6b7717a31647293621b97b96f74e6f3d" ]
|
||||
},
|
||||
"ObjectStoreService": {
|
||||
|
||||
"ObjectStoreUse": "AliyunOSS",
|
||||
"IsOpenStoreSync": true,
|
||||
"ApiDeployRegion": "CN",
|
||||
"SyncConfigList": [
|
||||
{
|
||||
"Domain": "irc.uat.rayplus.net",
|
||||
"Primary": "AliyunOSS",
|
||||
"Target": "AWS",
|
||||
"UploadRegion": "CN",
|
||||
"TargetRegion": "US",
|
||||
"IsOpenSync": false
|
||||
},
|
||||
{
|
||||
"Domain": "lili.uat.extimaging.com",
|
||||
"Primary": "AWS",
|
||||
"Target": "AliyunOSS",
|
||||
"UploadRegion": "US",
|
||||
"TargetRegion": "CN",
|
||||
"IsOpenSync": false
|
||||
}
|
||||
],
|
||||
|
||||
"AliyunOSS": {
|
||||
"RegionId": "cn-shanghai",
|
||||
"InternalEndpoint": "https://oss-cn-shanghai-internal.aliyuncs.com",
|
||||
"EndPoint": "https://oss-cn-shanghai.aliyuncs.com",
|
||||
"AccessKeyId": "LTAI5tNRTsqL6aWmHkDmTwoH",
|
||||
"AccessKeySecret": "7mtGz3qrYWI6JMMBZiLeC119VWicZH",
|
||||
"RoleArn": "acs:ram::1899121822495495:role/irc-oss-access",
|
||||
"BucketName": "rayplus-irc-store",
|
||||
"ViewEndpoint": "https://rayplus-irc-store.oss-cn-shanghai.aliyuncs.com",
|
||||
"Region": "oss-cn-shanghai",
|
||||
"DurationSeconds": 7200
|
||||
},
|
||||
|
||||
"MinIO": {
|
||||
"endPoint": "hir-oss.uat.extimaging.com",
|
||||
"port": "80",
|
||||
"useSSL": false,
|
||||
"viewEndpoint": "http://hir-oss.uat.extimaging.com/irc-uat",
|
||||
//"port": "443",
|
||||
//"useSSL": true,
|
||||
//"viewEndpoint": "https://hir-oss.uat.extimaging.com/irc-uat",
|
||||
"accessKey": "b9Ul0e98xPzt6PwRXA1Q",
|
||||
"secretKey": "DzMaU2L4OXl90uytwOmDXF2encN0Jf4Nxu2XkYqQ",
|
||||
"bucketName": "irc-uat"
|
||||
|
||||
},
|
||||
"AWS": {
|
||||
"Region": "us-east-1",
|
||||
"EndPoint": "s3.us-east-1.amazonaws.com",
|
||||
"UseSSL": true,
|
||||
"RoleArn": "arn:aws:iam::471112624751:role/uat_s3_access",
|
||||
"AccessKeyId": "AKIAW3MEAFJX7IPXISP4",
|
||||
"SecretAccessKey": "Pgrg3le5jPxZQ7MR1yYNS30J0XRyJeKVyIIjElXc",
|
||||
"BucketName": "ei-med-s3-lili-uat-store",
|
||||
"ViewEndpoint": "https://ei-med-s3-lili-uat-store.s3.amazonaws.com/",
|
||||
"DurationSeconds": 7200
|
||||
}
|
||||
},
|
||||
|
||||
"BasicSystemConfig": {
|
||||
// 启用质控风险控制功能
|
||||
"QCRiskControl": true,
|
||||
"OpenUserComplexPassword": true,
|
||||
"OpenSignDocumentBeforeWork": true,
|
||||
|
||||
"OpenLoginLimit": true,
|
||||
"LoginMaxFailCount": 5,
|
||||
|
||||
"LoginFailLockMinutes": 30,
|
||||
"AutoLoginOutMinutes": 120,
|
||||
|
||||
"OpenLoginMFA": false,
|
||||
|
||||
"ContinuousReadingTimeMin": 120,
|
||||
"ReadingRestTimeMin": 10,
|
||||
|
||||
"IsNeedChangePassWord": true,
|
||||
"ChangePassWordDays": 90,
|
||||
// 模板类型 1 Elevate 2 Extensive
|
||||
"TemplateType": 2,
|
||||
//MFA免验证发送天数
|
||||
"UserMFAVerifyMinutes": 1440
|
||||
|
||||
},
|
||||
"SystemEmailSendConfig": {
|
||||
"Port": 465,
|
||||
"Host": "smtp.qiye.aliyun.com",
|
||||
"Imap": "imap.qiye.aliyun.com",
|
||||
"ImapPort": 993,
|
||||
"FromEmail": "irc@mail.rayplus.net",
|
||||
"FromName": "RayPlus Imaging System",
|
||||
"AuthorizationCode": "QWE1zxcv7632",
|
||||
"SiteUrl": "https://irc.rayplus.net/login",
|
||||
|
||||
"PlatformName": "RayPlus",
|
||||
"PlatformNameCN": "睿佳影像云平台",
|
||||
"SystemShortName": "RayPlus",
|
||||
"OrganizationName": "RayPlus",
|
||||
"OrganizationNameCN": "RayPlus",
|
||||
"CompanyName": "RayPlus",
|
||||
"CompanyNameCN": "睿佳(武汉)软件科技有限公司",
|
||||
"CompanyShortName": "RayPlus",
|
||||
"CompanyShortNameCN": "睿佳",
|
||||
"IsEnv_US": false,
|
||||
"EmailRegexStr": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$",
|
||||
"CronEmailDefaultCulture": "zh-CN"
|
||||
},
|
||||
|
||||
"SystemPacsConfig": {
|
||||
"Port": "11113",
|
||||
"IP": "47.117.165.18"
|
||||
},
|
||||
"RequestDuplicationOptions": {
|
||||
"IsEnabled": true,
|
||||
"DuplicationWindowMs": 200,
|
||||
"CacheTimeSeconds": 5,
|
||||
"ExcludedPaths": [
|
||||
]
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,164 +1,131 @@
|
||||
{
|
||||
// 日志信息
|
||||
"Logging": {
|
||||
// 日志等级
|
||||
"LogLevel": {
|
||||
// 默认日志等级
|
||||
"Default": "Information",
|
||||
// 调试日志等级
|
||||
"Microsoft": "Warning",
|
||||
// ASP.NET Core 日志等级
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
// 数据库链接字符串
|
||||
"ConnectionStrings": {
|
||||
// 本地数据库链接字符串
|
||||
"RemoteNew": "Server=106.14.89.110,1435;Database=Test_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
||||
// Hangfire 定时任务数据库链接字符串
|
||||
"Hangfire": "Server=106.14.89.110,1435;Database=Test_IRC_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
|
||||
},
|
||||
// 对象存储服务配置
|
||||
"WeComNoticeConfig": {
|
||||
"IsOpenWeComNotice": true,
|
||||
"WebhookUrl": "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=4355b98e-1e72-4678-8dfb-2fc6ad0bf449",
|
||||
"APINoticeUserList": [
|
||||
"u",
|
||||
"wait..."
|
||||
],
|
||||
"VueNoticeUserList": [
|
||||
"wangxiaoshuang",
|
||||
"6b7717a31647293621b97b96f74e6f3d"
|
||||
]
|
||||
},
|
||||
"ObjectStoreService": {
|
||||
// 使用的对象存储服务类型
|
||||
"ObjectStoreUse": "AliyunOSS",
|
||||
// 阿里云对象存储服务的配置
|
||||
"IsOpenStoreSync": false,
|
||||
"ApiDeployRegion": "CN",
|
||||
"SyncConfigList": [
|
||||
{
|
||||
"Domain": "irc.test.extimaging.com",
|
||||
"Primary": "AliyunOSS",
|
||||
"Target": "AWS",
|
||||
"UploadRegion": "CN",
|
||||
"TargetRegion": "US",
|
||||
"IsOpenSync": true
|
||||
},
|
||||
{
|
||||
"Domain": "lili.test.extimaging.com",
|
||||
"Primary": "AWS",
|
||||
"Target": "AliyunOSS",
|
||||
"UploadRegion": "US",
|
||||
"TargetRegion": "CN",
|
||||
"IsOpenSync": false
|
||||
}
|
||||
],
|
||||
"AliyunOSS": {
|
||||
// 阿里云 OSS 的 Region ID
|
||||
"RegionId": "cn-shanghai",
|
||||
// 阿里云 OSS 的内部访问端点
|
||||
"InternalEndpoint": "https://oss-cn-shanghai-internal.aliyuncs.com",
|
||||
// 阿里云 OSS 的外部访问端点
|
||||
"EndPoint": "https://oss-cn-shanghai.aliyuncs.com",
|
||||
// 阿里云 OSS 的访问密钥 ID
|
||||
"AccessKeyId": "LTAI5tRRZehUp2V9pyTPtAJm",
|
||||
// 阿里云 OSS 的访问密钥 Secret
|
||||
"AccessKeySecret": "FLizxkHsMm4CGYHtkV8E3PNJJZU7oV",
|
||||
// 阿里云 OSS 的角色 ARN
|
||||
"RoleArn": "acs:ram::1899121822495495:role/dev-oss-access",
|
||||
// 阿里云 OSS 的Bucket名称
|
||||
"BucketName": "zy-irc-test-store",
|
||||
// 阿里云 OSS 的访问端点
|
||||
"ViewEndpoint": "https://zy-irc-test-dev-cache.oss-cn-shanghai.aliyuncs.com",
|
||||
// 阿里云 OSS 的预览端点
|
||||
"Region": "oss-cn-shanghai",
|
||||
// 阿里云 OSS 的临时访问凭证有效时间(秒)
|
||||
"DurationSeconds": 7200,
|
||||
// 阿里云 OSS 的预览端点
|
||||
"PreviewEndpoint": "https://test-oss.test.extimaging.com"
|
||||
},
|
||||
// MinIO 对象存储服务的配置
|
||||
"MinIO": {
|
||||
// MinIO 的访问端点
|
||||
"EndPoint": "hir-oss.test.extimaging.com",
|
||||
// MinIO 的端口
|
||||
"Port": "443",
|
||||
// 是否使用 SSL
|
||||
"UseSSL": true,
|
||||
// MinIO 的角色 ARN
|
||||
"AccessKey": "fbStsVYCIPKHQneeqMwD",
|
||||
// MinIO 的访问密钥
|
||||
"SecretKey": "TzgvyA3zGXMUnpilJNUlyMYHfosl1hBMl6lxPmjy",
|
||||
// MinIO 的BucketName
|
||||
"BucketName": "irc-test",
|
||||
// MinIO 的访问端点
|
||||
"ViewEndpoint": "https://hir-oss.test.extimaging.com/irc-test"
|
||||
},
|
||||
// AWS S3 对象存储服务的配置
|
||||
"AWS": {
|
||||
// AWS S3 的Region
|
||||
"Region": "us-east-1",
|
||||
// AWS S3 的内部访问端点
|
||||
"EndPoint": "s3.us-east-1.amazonaws.com",
|
||||
// 是否使用 SSL
|
||||
"UseSSL": true,
|
||||
// AWS S3 的角色 ARN
|
||||
"RoleArn": "arn:aws:iam::471112624751:role/uat_s3_access",
|
||||
// AWS S3 的访问密钥 ID
|
||||
"AccessKeyId": "AKIAW3MEAFJX7IPXISP4",
|
||||
// AWS S3 的访问密钥 Secret
|
||||
"SecretAccessKey": "Pgrg3le5jPxZQ7MR1yYNS30J0XRyJeKVyIIjElXc",
|
||||
// AWS S3 的Bucket名称
|
||||
"BucketName": "ei-med-s3-lili-uat-store",
|
||||
// AWS S3 的访问端点
|
||||
"ViewEndpoint": "https://ei-med-s3-lili-uat-store.s3.amazonaws.com",
|
||||
// AWS S3 的持续数秒
|
||||
"DurationSeconds": 7200
|
||||
}
|
||||
},
|
||||
// 系统配置
|
||||
"BasicSystemConfig": {
|
||||
// 打开用户复杂密码
|
||||
"QCRiskControl": true,
|
||||
"OpenUserComplexPassword": false,
|
||||
// 是否在开始工作前强制签署电子知情同意书
|
||||
"OpenSignDocumentBeforeWork": false,
|
||||
// 是否启用登录失败次数限制(防暴力破解)
|
||||
"OpenLoginLimit": false,
|
||||
// 连续登录失败多少次后触发锁定
|
||||
"LoginMaxFailCount": 5,
|
||||
// 触发锁定后账号锁定时长(分钟)
|
||||
"LoginFailLockMinutes": 1,
|
||||
// 无操作自动登出的超时时间(分钟)
|
||||
"AutoLoginOutMinutes": 10,
|
||||
// 是否启用多因子登录认证(MFA)
|
||||
"OpenLoginMFA": false,
|
||||
// 连续阅片的最长工作时间(分钟),超时后强制休息
|
||||
"ContinuousReadingTimeMin": 120,
|
||||
// 强制休息时长(分钟)
|
||||
"ReadingRestTimeMin": 10,
|
||||
// 是否强制用户定期修改密码
|
||||
"IsNeedChangePassWord": true,
|
||||
// 密码有效期(天),到期后必须修改
|
||||
"ChangePassWordDays": 90,
|
||||
// 模板类型 1 Elevate 2 Extensive
|
||||
"TemplateType": 2,
|
||||
// 是否打开项目关联删除
|
||||
"OpenTrialRelationDelete": true,
|
||||
// 转换PDF服务配置
|
||||
"ThirdPdfUrl": "http://106.14.89.110:30088/api/v1/convert/file/pdf"
|
||||
"OpenTrialRelationDelete": false,
|
||||
"ThirdPdfUrl": "http://106.14.89.110:30088/api/v1/convert/file/pdf",
|
||||
"UserMFAVerifyMinutes": 1440
|
||||
},
|
||||
// 邮件服务配置(用于系统通知、找回密码、错误报警等)
|
||||
"SystemEmailSendConfig": {
|
||||
// SMTP端口
|
||||
"Port": 465,
|
||||
// 企业邮箱SMTP服务器地址
|
||||
"Host": "smtp.qiye.aliyun.com",
|
||||
// 发件人邮箱地址
|
||||
"Port": 465,
|
||||
"Imap": "imap.qiye.aliyun.com",
|
||||
"ImapPort": 993,
|
||||
"FromEmail": "test@extimaging.com",
|
||||
// 发件人显示名称
|
||||
"FromName": "Test IRC Imaging System",
|
||||
// SMTP授权码
|
||||
"FromName": "Test EIC Imaging System",
|
||||
"AuthorizationCode": "SHzyyl2021",
|
||||
// 系统对外访问地址
|
||||
"SiteUrl": "http://irc.test.extimaging.com/login",
|
||||
// 系统简称
|
||||
"SystemShortName": "IRC",
|
||||
// 组织英文名称
|
||||
"OrganizationName": "Extlmaging",
|
||||
// 组织中文名称
|
||||
"OrganizationNameCN": "Extlmaging",
|
||||
// 公司英文全称
|
||||
"OrganizationName": "ExtImaging",
|
||||
"OrganizationNameCN": "ExtImaging",
|
||||
"PlatformName": "EICS",
|
||||
"PlatformNameCN": "展影云平台",
|
||||
"CompanyName": "Extensive Imaging",
|
||||
// 公司中文全称
|
||||
"CompanyNameCN": "上海展影医疗科技有限公司",
|
||||
// 公司英文简称
|
||||
"CompanyShortName": "Extensive Imaging",
|
||||
// 公司中文简称
|
||||
"CompanyShortNameCN": "展影医疗",
|
||||
// 是否为国际版环境
|
||||
"IsEnv_US": false,
|
||||
// 是否开启系统异常邮件报警
|
||||
"IsOpenErrorNoticeEmail": false,
|
||||
// 邮箱格式校验正则表达式
|
||||
"EmailRegexStr": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$",
|
||||
// 接收系统异常报警的邮箱列表
|
||||
"ErrorNoticeEmailList": [ "872297557@qq.com" ]
|
||||
"CronEmailDefaultCulture": "zh-CN"
|
||||
},
|
||||
// PACS 连接配置
|
||||
"SystemPacsConfig": {
|
||||
// PACS服务器端口
|
||||
"Port": "11113",
|
||||
// PACS服务器IP地址
|
||||
"IP": "106.14.89.110"
|
||||
},
|
||||
"RequestDuplicationOptions": {
|
||||
"IsEnabled": true,
|
||||
"DuplicationWindowMs": 500,
|
||||
"CacheTimeSeconds": 5,
|
||||
"ExcludedPaths": []
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -47,7 +47,8 @@
|
||||
},
|
||||
|
||||
"BasicSystemConfig": {
|
||||
|
||||
// 启用质控风险控制功能
|
||||
"QCRiskControl": true,
|
||||
"OpenUserComplexPassword": false,
|
||||
|
||||
"OpenSignDocumentBeforeWork": false,
|
||||
@@ -75,13 +76,15 @@
|
||||
"SystemEmailSendConfig": {
|
||||
"Port": 465,
|
||||
"Host": "smtp.qiye.aliyun.com",
|
||||
"Imap": "imap.qiye.aliyun.com",
|
||||
"ImapPort": 993,
|
||||
"FromEmail": "test@extimaging.com",
|
||||
"FromName": "Test_IRC",
|
||||
"AuthorizationCode": "SHzyyl2021",
|
||||
"SiteUrl": "http://irc.test.extimaging.com/login",
|
||||
|
||||
"OrganizationName": "Extlmaging",
|
||||
"OrganizationNameCN": "Extlmaging",
|
||||
"OrganizationName": "ExtImaging",
|
||||
"OrganizationNameCN": "ExtImaging",
|
||||
"CompanyName": "Extensive Imaging",
|
||||
"CompanyNameCN": "上海展影医疗科技有限公司",
|
||||
"CompanyShortName": "Extensive Imaging",
|
||||
@@ -91,5 +94,12 @@
|
||||
"SystemPacsConfig": {
|
||||
"Port": "11113",
|
||||
"IP": "106.14.89.110"
|
||||
},
|
||||
"RequestDuplicationOptions": {
|
||||
"IsEnabled": true,
|
||||
"DuplicationWindowMs": 500,
|
||||
"CacheTimeSeconds": 5,
|
||||
"ExcludedPaths": [
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
"ConnectionStrings": {
|
||||
"RemoteNew": "Server=106.14.89.110,1435;Database=Test_Rayplus;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
||||
"Hangfire": "Server=106.14.89.110,1435;Database=Test_Rayplus_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
|
||||
},
|
||||
"WeComNoticeConfig": {
|
||||
"IsOpenWeComNotice": true,
|
||||
"WebhookUrl": "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=4355b98e-1e72-4678-8dfb-2fc6ad0bf449", //4355b98e-1e72-4678-8dfb-2fc6ad0bf449 //cdd97aab-d256-4f07-9145-a0a2b1555322
|
||||
"APINoticeUserList": [ "u", "wait..." ],
|
||||
"VueNoticeUserList": [ "wangxiaoshuang", "6b7717a31647293621b97b96f74e6f3d" ]
|
||||
},
|
||||
"ObjectStoreService": {
|
||||
|
||||
"ObjectStoreUse": "AliyunOSS",
|
||||
"IsOpenStoreSync": true,
|
||||
"ApiDeployRegion": "CN",
|
||||
"SyncConfigList": [
|
||||
{
|
||||
"Domain": "irc.test.rayplus.net",
|
||||
"Primary": "AliyunOSS",
|
||||
"Target": "AWS",
|
||||
"UploadRegion": "CN",
|
||||
"TargetRegion": "US",
|
||||
"IsOpenSync": false
|
||||
},
|
||||
{
|
||||
"Domain": "lili.uat.extimaging.com",
|
||||
"Primary": "AWS",
|
||||
"Target": "AliyunOSS",
|
||||
"UploadRegion": "US",
|
||||
"TargetRegion": "CN",
|
||||
"IsOpenSync": false
|
||||
}
|
||||
],
|
||||
|
||||
"AliyunOSS": {
|
||||
"RegionId": "cn-shanghai",
|
||||
"InternalEndpoint": "https://oss-cn-shanghai-internal.aliyuncs.com",
|
||||
"EndPoint": "https://oss-cn-shanghai.aliyuncs.com",
|
||||
"AccessKeyId": "LTAI5tRRZehUp2V9pyTPtAJm",
|
||||
"AccessKeySecret": "FLizxkHsMm4CGYHtkV8E3PNJJZU7oV",
|
||||
"RoleArn": "acs:ram::1899121822495495:role/dev-oss-access",
|
||||
"BucketName": "rayplus-irc-test-store",
|
||||
"ViewEndpoint": "https://rayplus-irc-test-store.oss-cn-shanghai.aliyuncs.com",
|
||||
"Region": "oss-cn-shanghai",
|
||||
"DurationSeconds": 7200
|
||||
},
|
||||
|
||||
"MinIO": {
|
||||
"endPoint": "hir-oss.uat.extimaging.com",
|
||||
"port": "80",
|
||||
"useSSL": false,
|
||||
"viewEndpoint": "http://hir-oss.uat.extimaging.com/irc-uat",
|
||||
//"port": "443",
|
||||
//"useSSL": true,
|
||||
//"viewEndpoint": "https://hir-oss.uat.extimaging.com/irc-uat",
|
||||
"accessKey": "b9Ul0e98xPzt6PwRXA1Q",
|
||||
"secretKey": "DzMaU2L4OXl90uytwOmDXF2encN0Jf4Nxu2XkYqQ",
|
||||
"bucketName": "irc-uat"
|
||||
|
||||
},
|
||||
"AWS": {
|
||||
"Region": "us-east-1",
|
||||
"EndPoint": "s3.us-east-1.amazonaws.com",
|
||||
"UseSSL": true,
|
||||
"RoleArn": "arn:aws:iam::471112624751:role/uat_s3_access",
|
||||
"AccessKeyId": "AKIAW3MEAFJX7IPXISP4",
|
||||
"SecretAccessKey": "Pgrg3le5jPxZQ7MR1yYNS30J0XRyJeKVyIIjElXc",
|
||||
"BucketName": "ei-med-s3-lili-uat-store",
|
||||
"ViewEndpoint": "https://ei-med-s3-lili-uat-store.s3.amazonaws.com/",
|
||||
"DurationSeconds": 7200
|
||||
}
|
||||
},
|
||||
|
||||
"BasicSystemConfig": {
|
||||
// 启用质控风险控制功能
|
||||
"QCRiskControl": true,
|
||||
"OpenUserComplexPassword": true,
|
||||
"OpenSignDocumentBeforeWork": true,
|
||||
|
||||
"OpenLoginLimit": true,
|
||||
"LoginMaxFailCount": 5,
|
||||
|
||||
"LoginFailLockMinutes": 30,
|
||||
"AutoLoginOutMinutes": 120,
|
||||
|
||||
"OpenLoginMFA": false,
|
||||
|
||||
"ContinuousReadingTimeMin": 120,
|
||||
"ReadingRestTimeMin": 10,
|
||||
|
||||
"IsNeedChangePassWord": true,
|
||||
"ChangePassWordDays": 90,
|
||||
// 模板类型 1 Elevate 2 Extensive
|
||||
"TemplateType": 2,
|
||||
//MFA免验证发送天数
|
||||
"UserMFAVerifyMinutes": 1440
|
||||
|
||||
},
|
||||
"SystemEmailSendConfig": {
|
||||
"Port": 465,
|
||||
"Host": "smtp.qiye.aliyun.com",
|
||||
"Imap": "imap.qiye.aliyun.com",
|
||||
"ImapPort": 993,
|
||||
"FromEmail": "test_irc@mail.rayplus.net",
|
||||
"FromName": "Test RayPlus Imaging System",
|
||||
"AuthorizationCode": "QWE1zxcv7632",
|
||||
"SiteUrl": "https://irc.test.rayplus.net/login",
|
||||
|
||||
"PlatformName": "RayPlus",
|
||||
"PlatformNameCN": "睿佳影像云平台",
|
||||
"SystemShortName": "RayPlus",
|
||||
"OrganizationName": "RayPlus",
|
||||
"OrganizationNameCN": "RayPlus",
|
||||
"CompanyName": "RayPlus",
|
||||
"CompanyNameCN": "睿佳(武汉)软件科技有限公司",
|
||||
"CompanyShortName": "RayPlus",
|
||||
"CompanyShortNameCN": "睿佳",
|
||||
"IsEnv_US": false,
|
||||
"EmailRegexStr": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$",
|
||||
"CronEmailDefaultCulture": "zh-CN"
|
||||
},
|
||||
|
||||
"SystemPacsConfig": {
|
||||
"Port": "11113",
|
||||
"IP": "101.132.253.119"
|
||||
},
|
||||
"RequestDuplicationOptions": {
|
||||
"IsEnabled": true,
|
||||
"DuplicationWindowMs": 200,
|
||||
"CacheTimeSeconds": 5,
|
||||
"ExcludedPaths": [
|
||||
]
|
||||
}
|
||||
|
||||
}
|
||||
@@ -12,19 +12,46 @@
|
||||
//"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",
|
||||
},
|
||||
|
||||
"WeComNoticeConfig": {
|
||||
"IsOpenWeComNotice": true,
|
||||
"WebhookUrl": "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=4355b98e-1e72-4678-8dfb-2fc6ad0bf449", //4355b98e-1e72-4678-8dfb-2fc6ad0bf449 //cdd97aab-d256-4f07-9145-a0a2b1555322
|
||||
"APINoticeUserList": [ "u", "wait..." ],
|
||||
"VueNoticeUserList": [ "wangxiaoshuang", "6b7717a31647293621b97b96f74e6f3d" ]
|
||||
},
|
||||
"ObjectStoreService": {
|
||||
"ObjectStoreUse": "AWS",
|
||||
"MinIO": {
|
||||
"endPoint": "44.210.231.169",
|
||||
"port": "9001",
|
||||
"useSSL": false,
|
||||
"accessKey": "e9bT1isTOqSAUxb6wd4n",
|
||||
"secretKey": "b5TaDzNdQCBtCvfm8eZ3dR6yY7tfZu2JYze2Po1i",
|
||||
"bucketName": "prod-irc-us",
|
||||
"viewEndpoint": "http://44.210.231.169:9001/prod-irc-us/"
|
||||
"IsOpenStoreSync": true,
|
||||
"ApiDeployRegion": "US",
|
||||
"SyncConfigList": [
|
||||
{
|
||||
"Domain": "eilili.elevateimaging.ai",
|
||||
"Primary": "AliyunOSS",
|
||||
"Target": "AWS",
|
||||
"UploadRegion": "CN",
|
||||
"TargetRegion": "US",
|
||||
"IsOpenSync": true
|
||||
},
|
||||
{
|
||||
"Domain": "lili.elevateimaging.ai",
|
||||
"Primary": "AWS",
|
||||
"Target": "AliyunOSS",
|
||||
"UploadRegion": "US",
|
||||
"TargetRegion": "CN",
|
||||
"IsOpenSync": true
|
||||
}
|
||||
],
|
||||
"AliyunOSS": {
|
||||
"RegionId": "cn-shanghai",
|
||||
"InternalEndpoint": "https://oss-cn-shanghai-internal.aliyuncs.com",
|
||||
"EndPoint": "https://oss-cn-shanghai.aliyuncs.com",
|
||||
"AccessKeyId": "LTAI5tNRTsqL6aWmHkDmTwoH",
|
||||
"AccessKeySecret": "7mtGz3qrYWI6JMMBZiLeC119VWicZH",
|
||||
"RoleArn": "acs:ram::1899121822495495:role/irc-oss-access",
|
||||
"BucketName": "zy-lili-store",
|
||||
"ViewEndpoint": "https://zy-lili-cache.oss-cn-shanghai.aliyuncs.com",
|
||||
"Region": "oss-cn-shanghai",
|
||||
"DurationSeconds": 7200
|
||||
},
|
||||
|
||||
"AWS": {
|
||||
"Region": "us-east-1",
|
||||
"EndPoint": "s3.us-east-1.amazonaws.com",
|
||||
@@ -38,7 +65,8 @@
|
||||
}
|
||||
},
|
||||
"BasicSystemConfig": {
|
||||
|
||||
// 启用质控风险控制功能
|
||||
"QCRiskControl": true,
|
||||
"OpenUserComplexPassword": true,
|
||||
"OpenSignDocumentBeforeWork": true,
|
||||
|
||||
@@ -57,17 +85,24 @@
|
||||
// 模板类型 1 Elevate 2 Extensive
|
||||
"TemplateType": 1,
|
||||
|
||||
"OpenTrialRelationDelete": false
|
||||
"OpenTrialRelationDelete": false,
|
||||
//MFA免验证
|
||||
"UserMFAVerifyMinutes": 1440
|
||||
|
||||
},
|
||||
|
||||
"SystemEmailSendConfig": {
|
||||
"Port": 587,
|
||||
"Host": "smtp-mail.outlook.com",
|
||||
"Imap": "imap-mail.outlook.com",
|
||||
"ImapPort": 993,
|
||||
"FromEmail": "donotreply@elevateimaging.ai",
|
||||
"FromName": "LiLi System",
|
||||
"AuthorizationCode": "Q#669869497420ul",
|
||||
|
||||
|
||||
"PlatformName": "LiLi",
|
||||
"PlatformNameCN": "LiLi",
|
||||
"SystemShortName": "LiLi",
|
||||
"OrganizationName": "Elevate Imaging",
|
||||
"OrganizationNameCN": "Elevate Imaging",
|
||||
@@ -77,14 +112,20 @@
|
||||
"CompanyShortNameCN": "展影医疗",
|
||||
"SiteUrl": "https://lili.elevateimaging.ai/login",
|
||||
"IsEnv_US": true,
|
||||
"IsOpenErrorNoticeEmail": false,
|
||||
"EmailRegexStr": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$",
|
||||
"ErrorNoticeEmailList": [ "872297557@qq.com" ]
|
||||
"CronEmailDefaultCulture": "en-US"
|
||||
},
|
||||
|
||||
"SystemPacsConfig": {
|
||||
"Port": "104",
|
||||
"IP": "44.210.231.169"
|
||||
},
|
||||
"RequestDuplicationOptions": {
|
||||
"IsEnabled": true,
|
||||
"DuplicationWindowMs": 500,
|
||||
"CacheTimeSeconds": 5,
|
||||
"ExcludedPaths": [
|
||||
]
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -44,7 +44,8 @@
|
||||
}
|
||||
},
|
||||
"BasicSystemConfig": {
|
||||
|
||||
// 启用质控风险控制功能
|
||||
"QCRiskControl": true,
|
||||
"OpenUserComplexPassword": true,
|
||||
|
||||
"OpenSignDocumentBeforeWork": true,
|
||||
@@ -66,16 +67,24 @@
|
||||
"ChangePassWordDays": 90,
|
||||
// 模板类型 1 Elevate 2 Extensive
|
||||
"TemplateType": 1,
|
||||
"OpenLoginMFA": true
|
||||
"OpenLoginMFA": true,
|
||||
//MFA免验证发送天数
|
||||
"UserMFAVerifyMinutes": 1440
|
||||
|
||||
},
|
||||
|
||||
"SystemEmailSendConfig": {
|
||||
"Port": 587,
|
||||
"Host": "smtp-mail.outlook.com",
|
||||
"Imap": "imap-mail.outlook.com",
|
||||
"ImapPort": 993,
|
||||
"FromEmail": "donotreply@elevateimaging.ai",
|
||||
"FromName": "LiLi System",
|
||||
"AuthorizationCode": "Q#669869497420ul",
|
||||
|
||||
|
||||
"PlatformName": "LiLi",
|
||||
"PlatformNameCN": "LiLi",
|
||||
"SystemShortName": "LiLi",
|
||||
"OrganizationName": "Elevate Imaging",
|
||||
"OrganizationNameCN": "Elevate Imaging",
|
||||
@@ -84,14 +93,19 @@
|
||||
"CompanyShortName": "Elevate Imaging",
|
||||
"CompanyShortNameCN": "展影医疗",
|
||||
"SiteUrl": "https://lili.test.elevateimaging.ai/login",
|
||||
"IsEnv_US": true,
|
||||
"IsOpenErrorNoticeEmail": false,
|
||||
"ErrorNoticeEmailList": [ "872297557@qq.com" ]
|
||||
"IsEnv_US": true
|
||||
},
|
||||
|
||||
"SystemPacsConfig": {
|
||||
"Port": "104",
|
||||
"IP": "3.226.182.187"
|
||||
},
|
||||
"RequestDuplicationOptions": {
|
||||
"IsEnabled": true,
|
||||
"DuplicationWindowMs": 500,
|
||||
"CacheTimeSeconds": 5,
|
||||
"ExcludedPaths": [
|
||||
]
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -13,6 +13,12 @@
|
||||
"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"
|
||||
},
|
||||
"WeComNoticeConfig": {
|
||||
"IsOpenWeComNotice": true,
|
||||
"WebhookUrl": "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=4355b98e-1e72-4678-8dfb-2fc6ad0bf449", //4355b98e-1e72-4678-8dfb-2fc6ad0bf449 //cdd97aab-d256-4f07-9145-a0a2b1555322
|
||||
"APINoticeUserList": [ "u", "wait..." ],
|
||||
"VueNoticeUserList": [ "wangxiaoshuang", "6b7717a31647293621b97b96f74e6f3d" ]
|
||||
},
|
||||
|
||||
"ObjectStoreService": {
|
||||
|
||||
@@ -48,7 +54,8 @@
|
||||
}
|
||||
},
|
||||
"BasicSystemConfig": {
|
||||
|
||||
// 启用质控风险控制功能
|
||||
"QCRiskControl": true,
|
||||
"OpenUserComplexPassword": true,
|
||||
"OpenSignDocumentBeforeWork": true,
|
||||
|
||||
@@ -65,16 +72,22 @@
|
||||
"IsNeedChangePassWord": true,
|
||||
"ChangePassWordDays": 90,
|
||||
// 模板类型 1 Elevate 2 Extensive
|
||||
"TemplateType": 1
|
||||
"TemplateType": 1,
|
||||
//MFA免验证发送天数
|
||||
"UserMFAVerifyMinutes": 1440
|
||||
},
|
||||
|
||||
"SystemEmailSendConfig": {
|
||||
"Port": 587,
|
||||
"Host": "smtp-mail.outlook.com",
|
||||
"Imap": "imap-mail.outlook.com",
|
||||
"ImapPort": 993,
|
||||
"FromEmail": "donotreply@elevateimaging.ai",
|
||||
"FromName": "LiLi System",
|
||||
"AuthorizationCode": "Q#669869497420ul",
|
||||
|
||||
"PlatformName": "LiLi",
|
||||
"PlatformNameCN": "LiLi",
|
||||
"SystemShortName": "LiLi",
|
||||
"OrganizationName": "Elevate Imaging",
|
||||
"OrganizationNameCN": "Elevate Imaging",
|
||||
@@ -84,14 +97,19 @@
|
||||
"CompanyShortNameCN": "展影医疗",
|
||||
"SiteUrl": "https://lili.uat.elevateimaging.ai/login",
|
||||
"IsEnv_US": true,
|
||||
"IsOpenErrorNoticeEmail": false,
|
||||
"EmailRegexStr": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$",
|
||||
"ErrorNoticeEmailList": [ "872297557@qq.com" ]
|
||||
"CronEmailDefaultCulture": "en-US"
|
||||
},
|
||||
|
||||
"SystemPacsConfig": {
|
||||
"Port": "104",
|
||||
"IP": "3.226.182.187"
|
||||
},
|
||||
"RequestDuplicationOptions": {
|
||||
"IsEnabled": true,
|
||||
"DuplicationWindowMs": 500,
|
||||
"CacheTimeSeconds": 5,
|
||||
"ExcludedPaths": [
|
||||
]
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
"ConnectionStrings": {
|
||||
"RemoteNew": "Server=101.132.253.119,1435;Database=Uat_HeAnShu;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
||||
"Hangfire": "Server=101.132.253.119,1435;Database=Uat_HeAnShu_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
|
||||
},
|
||||
"WeComNoticeConfig": {
|
||||
"IsOpenWeComNotice": true,
|
||||
"WebhookUrl": "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=4355b98e-1e72-4678-8dfb-2fc6ad0bf449", //4355b98e-1e72-4678-8dfb-2fc6ad0bf449 //cdd97aab-d256-4f07-9145-a0a2b1555322
|
||||
"APINoticeUserList": [ "u", "wait..." ],
|
||||
"VueNoticeUserList": [ "wangxiaoshuang", "6b7717a31647293621b97b96f74e6f3d" ]
|
||||
},
|
||||
"ObjectStoreService": {
|
||||
|
||||
"ObjectStoreUse": "AliyunOSS",
|
||||
"IsOpenStoreSync": true,
|
||||
"ApiDeployRegion": "CN",
|
||||
"SyncConfigList": [
|
||||
{
|
||||
"Domain": "irc.uat.rayplus.net",
|
||||
"Primary": "AliyunOSS",
|
||||
"Target": "AWS",
|
||||
"UploadRegion": "CN",
|
||||
"TargetRegion": "US",
|
||||
"IsOpenSync": false
|
||||
},
|
||||
{
|
||||
"Domain": "lili.uat.extimaging.com",
|
||||
"Primary": "AWS",
|
||||
"Target": "AliyunOSS",
|
||||
"UploadRegion": "US",
|
||||
"TargetRegion": "CN",
|
||||
"IsOpenSync": false
|
||||
}
|
||||
],
|
||||
|
||||
"AliyunOSS": {
|
||||
"RegionId": "cn-shanghai",
|
||||
"InternalEndpoint": "https://oss-cn-shanghai-internal.aliyuncs.com",
|
||||
"EndPoint": "https://oss-cn-shanghai.aliyuncs.com",
|
||||
"AccessKeyId": "LTAI5tFUCCmz5TwghZHsj45Y",
|
||||
"AccessKeySecret": "8evrBy1fVfzJG25i67Jm0xqn9Xcw2T",
|
||||
"RoleArn": "acs:ram::1078130221702011:role/uat-oss-access",
|
||||
"BucketName": "heanshu-irc-uat-store",
|
||||
"ViewEndpoint": "https://heanshu-irc-uat-store.oss-cn-shanghai.aliyuncs.com",
|
||||
"Region": "oss-cn-shanghai",
|
||||
"DurationSeconds": 7200
|
||||
},
|
||||
|
||||
"MinIO": {
|
||||
"endPoint": "hir-oss.uat.extimaging.com",
|
||||
"port": "80",
|
||||
"useSSL": false,
|
||||
"viewEndpoint": "http://hir-oss.uat.extimaging.com/irc-uat",
|
||||
//"port": "443",
|
||||
//"useSSL": true,
|
||||
//"viewEndpoint": "https://hir-oss.uat.extimaging.com/irc-uat",
|
||||
"accessKey": "b9Ul0e98xPzt6PwRXA1Q",
|
||||
"secretKey": "DzMaU2L4OXl90uytwOmDXF2encN0Jf4Nxu2XkYqQ",
|
||||
"bucketName": "irc-uat"
|
||||
|
||||
},
|
||||
"AWS": {
|
||||
"Region": "us-east-1",
|
||||
"EndPoint": "s3.us-east-1.amazonaws.com",
|
||||
"UseSSL": true,
|
||||
"RoleArn": "arn:aws:iam::471112624751:role/uat_s3_access",
|
||||
"AccessKeyId": "AKIAW3MEAFJX7IPXISP4",
|
||||
"SecretAccessKey": "Pgrg3le5jPxZQ7MR1yYNS30J0XRyJeKVyIIjElXc",
|
||||
"BucketName": "ei-med-s3-lili-uat-store",
|
||||
"ViewEndpoint": "https://ei-med-s3-lili-uat-store.s3.amazonaws.com/",
|
||||
"DurationSeconds": 7200
|
||||
}
|
||||
},
|
||||
|
||||
"BasicSystemConfig": {
|
||||
// 启用质控风险控制功能
|
||||
"QCRiskControl": true,
|
||||
"OpenUserComplexPassword": true,
|
||||
"OpenSignDocumentBeforeWork": true,
|
||||
|
||||
"OpenLoginLimit": true,
|
||||
"LoginMaxFailCount": 5,
|
||||
|
||||
"LoginFailLockMinutes": 30,
|
||||
"AutoLoginOutMinutes": 120,
|
||||
|
||||
"OpenLoginMFA": false,
|
||||
|
||||
"ContinuousReadingTimeMin": 120,
|
||||
"ReadingRestTimeMin": 10,
|
||||
|
||||
"IsNeedChangePassWord": true,
|
||||
"ChangePassWordDays": 90,
|
||||
// 模板类型 1 Elevate 2 Extensive
|
||||
"TemplateType": 2,
|
||||
//MFA免验证发送天数
|
||||
"UserMFAVerifyMinutes": 1440
|
||||
|
||||
},
|
||||
"SystemEmailSendConfig": {
|
||||
//"Port": 465,
|
||||
//"Host": "smtp.qiye.163.com",
|
||||
//"Imap": "imap.qiye.163.com",
|
||||
//"ImapPort": 993,
|
||||
//"FromEmail": "service@heanshu.com",
|
||||
//"FromName": "Uat HeAnShu Imaging System",
|
||||
//"AuthorizationCode": "j#cAPU%XgvcHWn3N",
|
||||
//"SiteUrl": "https://irc.uat.heanshu.com",
|
||||
|
||||
"Port": 465,
|
||||
"Host": "smtp.qiye.aliyun.com",
|
||||
"Imap": "imap.qiye.aliyun.com",
|
||||
"ImapPort": 993,
|
||||
"FromEmail": "service@mail.rayplus.net",
|
||||
"FromName": "Uat HeAnShu Imaging System",
|
||||
"AuthorizationCode": "crefHpx3WtenFr6X",
|
||||
"SiteUrl": "https://irc.uat.heanshu.com",
|
||||
|
||||
"PlatformName": "HeAnShu",
|
||||
"PlatformNameCN": "禾安枢影像云平台",
|
||||
"SystemShortName": "HeAnShu",
|
||||
"OrganizationName": "HeAnShu",
|
||||
"OrganizationNameCN": "HeAnShu",
|
||||
"CompanyName": "HeAnShu",
|
||||
"CompanyNameCN": "禾安枢软件科技有限公司",
|
||||
"CompanyShortName": "HeAnShu",
|
||||
"CompanyShortNameCN": "禾安枢",
|
||||
"IsEnv_US": false,
|
||||
"EmailRegexStr": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$",
|
||||
"CronEmailDefaultCulture": "zh-CN"
|
||||
},
|
||||
|
||||
"SystemPacsConfig": {
|
||||
"Port": "11113",
|
||||
"IP": "101.132.253.119"
|
||||
},
|
||||
"RequestDuplicationOptions": {
|
||||
"IsEnabled": true,
|
||||
"DuplicationWindowMs": 200,
|
||||
"CacheTimeSeconds": 5,
|
||||
"ExcludedPaths": [
|
||||
]
|
||||
}
|
||||
|
||||
}
|
||||
@@ -10,9 +10,35 @@
|
||||
"RemoteNew": "Server=101.132.253.119,1435;Database=Uat_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
||||
"Hangfire": "Server=101.132.253.119,1435;Database=Uat_IRC_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
|
||||
},
|
||||
"WeComNoticeConfig": {
|
||||
"IsOpenWeComNotice": true,
|
||||
"WebhookUrl": "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=4355b98e-1e72-4678-8dfb-2fc6ad0bf449", //4355b98e-1e72-4678-8dfb-2fc6ad0bf449 //cdd97aab-d256-4f07-9145-a0a2b1555322
|
||||
"APINoticeUserList": [ "u", "wait..." ],
|
||||
"VueNoticeUserList": [ "wangxiaoshuang", "6b7717a31647293621b97b96f74e6f3d" ]
|
||||
},
|
||||
"ObjectStoreService": {
|
||||
|
||||
"ObjectStoreUse": "AliyunOSS",
|
||||
"IsOpenStoreSync": true,
|
||||
"ApiDeployRegion": "CN",
|
||||
"SyncConfigList": [
|
||||
{
|
||||
"Domain": "irc.uat.extimaging.com",
|
||||
"Primary": "AliyunOSS",
|
||||
"Target": "AWS",
|
||||
"UploadRegion": "CN",
|
||||
"TargetRegion": "US",
|
||||
"IsOpenSync": true
|
||||
},
|
||||
{
|
||||
"Domain": "lili.uat.extimaging.com",
|
||||
"Primary": "AWS",
|
||||
"Target": "AliyunOSS",
|
||||
"UploadRegion": "US",
|
||||
"TargetRegion": "CN",
|
||||
"IsOpenSync": true
|
||||
}
|
||||
],
|
||||
|
||||
"AliyunOSS": {
|
||||
"RegionId": "cn-shanghai",
|
||||
@@ -44,9 +70,9 @@
|
||||
"Region": "us-east-1",
|
||||
"EndPoint": "s3.us-east-1.amazonaws.com",
|
||||
"UseSSL": true,
|
||||
"RoleArn": "arn:aws:iam::471112624751:role/sts_s3_upload",
|
||||
"AccessKeyId": "AKIAW3MEAFJXWRCGSX5Z",
|
||||
"SecretAccessKey": "miais4jQGSd37A+TfBEP11AQM5u/CvotSmznJd8k",
|
||||
"RoleArn": "arn:aws:iam::471112624751:role/uat_s3_access",
|
||||
"AccessKeyId": "AKIAW3MEAFJX7IPXISP4",
|
||||
"SecretAccessKey": "Pgrg3le5jPxZQ7MR1yYNS30J0XRyJeKVyIIjElXc",
|
||||
"BucketName": "ei-med-s3-lili-uat-store",
|
||||
"ViewEndpoint": "https://ei-med-s3-lili-uat-store.s3.amazonaws.com/",
|
||||
"DurationSeconds": 7200
|
||||
@@ -54,7 +80,8 @@
|
||||
},
|
||||
|
||||
"BasicSystemConfig": {
|
||||
|
||||
// 启用质控风险控制功能
|
||||
"QCRiskControl": true,
|
||||
"OpenUserComplexPassword": true,
|
||||
"OpenSignDocumentBeforeWork": true,
|
||||
|
||||
@@ -72,33 +99,45 @@
|
||||
"IsNeedChangePassWord": true,
|
||||
"ChangePassWordDays": 90,
|
||||
// 模板类型 1 Elevate 2 Extensive
|
||||
"TemplateType": 2
|
||||
"TemplateType": 2,
|
||||
//MFA免验证发送天数
|
||||
"UserMFAVerifyMinutes": 1440
|
||||
|
||||
},
|
||||
"SystemEmailSendConfig": {
|
||||
"Port": 465,
|
||||
"Host": "smtp.qiye.aliyun.com",
|
||||
"Imap": "imap.qiye.aliyun.com",
|
||||
"ImapPort": 993,
|
||||
"FromEmail": "uat@extimaging.com",
|
||||
"FromName": "Uat IRC Imaging System",
|
||||
"FromName": "Uat EIC Imaging System",
|
||||
"AuthorizationCode": "SHzyyl2021",
|
||||
"SiteUrl": "http://irc.uat.extimaging.com/login",
|
||||
|
||||
"PlatformName": "EICS",
|
||||
"PlatformNameCN": "展影云平台",
|
||||
"SystemShortName": "IRC",
|
||||
"OrganizationName": "Extlmaging",
|
||||
"OrganizationNameCN": "Extlmaging",
|
||||
"OrganizationName": "ExtImaging",
|
||||
"OrganizationNameCN": "ExtImaging",
|
||||
"CompanyName": "Extensive Imaging",
|
||||
"CompanyNameCN": "上海展影医疗科技有限公司",
|
||||
"CompanyShortName": "Extensive Imaging",
|
||||
"CompanyShortNameCN": "展影医疗",
|
||||
"IsEnv_US": false,
|
||||
"IsOpenErrorNoticeEmail": false,
|
||||
"EmailRegexStr": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$",
|
||||
"ErrorNoticeEmailList": [ "872297557@qq.com" ]
|
||||
"CronEmailDefaultCulture": "zh-CN"
|
||||
},
|
||||
|
||||
"SystemPacsConfig": {
|
||||
"Port": "11113",
|
||||
"IP": "101.132.253.119"
|
||||
},
|
||||
"RequestDuplicationOptions": {
|
||||
"IsEnabled": true,
|
||||
"DuplicationWindowMs": 500,
|
||||
"CacheTimeSeconds": 5,
|
||||
"ExcludedPaths": [
|
||||
]
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
"ConnectionStrings": {
|
||||
"RemoteNew": "Server=101.132.253.119,1435;Database=Uat_Rayplus;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
||||
"Hangfire": "Server=101.132.253.119,1435;Database=Uat_Rayplus_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
|
||||
},
|
||||
"WeComNoticeConfig": {
|
||||
"IsOpenWeComNotice": true,
|
||||
"WebhookUrl": "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=4355b98e-1e72-4678-8dfb-2fc6ad0bf449", //4355b98e-1e72-4678-8dfb-2fc6ad0bf449 //cdd97aab-d256-4f07-9145-a0a2b1555322
|
||||
"APINoticeUserList": [ "u", "wait..." ],
|
||||
"VueNoticeUserList": [ "wangxiaoshuang", "6b7717a31647293621b97b96f74e6f3d" ]
|
||||
},
|
||||
"ObjectStoreService": {
|
||||
|
||||
"ObjectStoreUse": "AliyunOSS",
|
||||
"IsOpenStoreSync": true,
|
||||
"ApiDeployRegion": "CN",
|
||||
"SyncConfigList": [
|
||||
{
|
||||
"Domain": "irc.uat.rayplus.net",
|
||||
"Primary": "AliyunOSS",
|
||||
"Target": "AWS",
|
||||
"UploadRegion": "CN",
|
||||
"TargetRegion": "US",
|
||||
"IsOpenSync": false
|
||||
},
|
||||
{
|
||||
"Domain": "lili.uat.extimaging.com",
|
||||
"Primary": "AWS",
|
||||
"Target": "AliyunOSS",
|
||||
"UploadRegion": "US",
|
||||
"TargetRegion": "CN",
|
||||
"IsOpenSync": false
|
||||
}
|
||||
],
|
||||
|
||||
"AliyunOSS": {
|
||||
"RegionId": "cn-shanghai",
|
||||
"InternalEndpoint": "https://oss-cn-shanghai-internal.aliyuncs.com",
|
||||
"EndPoint": "https://oss-cn-shanghai.aliyuncs.com",
|
||||
"AccessKeyId": "LTAI5tFUCCmz5TwghZHsj45Y",
|
||||
"AccessKeySecret": "8evrBy1fVfzJG25i67Jm0xqn9Xcw2T",
|
||||
"RoleArn": "acs:ram::1078130221702011:role/uat-oss-access",
|
||||
"BucketName": "rayplus-irc-uat-store",
|
||||
"ViewEndpoint": "https://rayplus-irc-uat-store.oss-cn-shanghai.aliyuncs.com",
|
||||
"Region": "oss-cn-shanghai",
|
||||
"DurationSeconds": 7200
|
||||
},
|
||||
|
||||
"MinIO": {
|
||||
"endPoint": "hir-oss.uat.extimaging.com",
|
||||
"port": "80",
|
||||
"useSSL": false,
|
||||
"viewEndpoint": "http://hir-oss.uat.extimaging.com/irc-uat",
|
||||
//"port": "443",
|
||||
//"useSSL": true,
|
||||
//"viewEndpoint": "https://hir-oss.uat.extimaging.com/irc-uat",
|
||||
"accessKey": "b9Ul0e98xPzt6PwRXA1Q",
|
||||
"secretKey": "DzMaU2L4OXl90uytwOmDXF2encN0Jf4Nxu2XkYqQ",
|
||||
"bucketName": "irc-uat"
|
||||
|
||||
},
|
||||
"AWS": {
|
||||
"Region": "us-east-1",
|
||||
"EndPoint": "s3.us-east-1.amazonaws.com",
|
||||
"UseSSL": true,
|
||||
"RoleArn": "arn:aws:iam::471112624751:role/uat_s3_access",
|
||||
"AccessKeyId": "AKIAW3MEAFJX7IPXISP4",
|
||||
"SecretAccessKey": "Pgrg3le5jPxZQ7MR1yYNS30J0XRyJeKVyIIjElXc",
|
||||
"BucketName": "ei-med-s3-lili-uat-store",
|
||||
"ViewEndpoint": "https://ei-med-s3-lili-uat-store.s3.amazonaws.com/",
|
||||
"DurationSeconds": 7200
|
||||
}
|
||||
},
|
||||
|
||||
"BasicSystemConfig": {
|
||||
// 启用质控风险控制功能
|
||||
"QCRiskControl": true,
|
||||
"OpenUserComplexPassword": true,
|
||||
"OpenSignDocumentBeforeWork": true,
|
||||
|
||||
"OpenLoginLimit": true,
|
||||
"LoginMaxFailCount": 5,
|
||||
|
||||
"LoginFailLockMinutes": 30,
|
||||
"AutoLoginOutMinutes": 120,
|
||||
|
||||
"OpenLoginMFA": false,
|
||||
|
||||
"ContinuousReadingTimeMin": 120,
|
||||
"ReadingRestTimeMin": 10,
|
||||
|
||||
"IsNeedChangePassWord": true,
|
||||
"ChangePassWordDays": 90,
|
||||
// 模板类型 1 Elevate 2 Extensive
|
||||
"TemplateType": 2,
|
||||
//MFA免验证发送天数
|
||||
"UserMFAVerifyMinutes": 1440
|
||||
|
||||
},
|
||||
"SystemEmailSendConfig": {
|
||||
"Port": 465,
|
||||
"Host": "smtp.qiye.aliyun.com",
|
||||
"Imap": "imap.qiye.aliyun.com",
|
||||
"ImapPort": 993,
|
||||
"FromEmail": "uat@mail.rayplus.net",
|
||||
"FromName": "Uat RayPlus Imaging System",
|
||||
"AuthorizationCode": "QWE1zxcv7632",
|
||||
"SiteUrl": "https://irc.uat.rayplus.net/login",
|
||||
|
||||
"PlatformName": "RayPlus",
|
||||
"PlatformNameCN": "睿佳影像云平台",
|
||||
"SystemShortName": "RayPlus",
|
||||
"OrganizationName": "RayPlus",
|
||||
"OrganizationNameCN": "RayPlus",
|
||||
"CompanyName": "RayPlus",
|
||||
"CompanyNameCN": "睿佳(武汉)软件科技有限公司",
|
||||
"CompanyShortName": "RayPlus",
|
||||
"CompanyShortNameCN": "睿佳",
|
||||
"IsEnv_US": false,
|
||||
"EmailRegexStr": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$",
|
||||
"CronEmailDefaultCulture": "zh-CN"
|
||||
},
|
||||
|
||||
"SystemPacsConfig": {
|
||||
"Port": "11113",
|
||||
"IP": "101.132.253.119"
|
||||
},
|
||||
"RequestDuplicationOptions": {
|
||||
"IsEnabled": true,
|
||||
"DuplicationWindowMs": 200,
|
||||
"CacheTimeSeconds": 5,
|
||||
"ExcludedPaths": [
|
||||
]
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
"SecurityKey": "ShangHaiZhanYing_SecurityKey_SHzyyl@2021",
|
||||
"Issuer": "Extimaging",
|
||||
"Audience": "EICS",
|
||||
"TokenExpireMinute": "10080"//7天
|
||||
"TokenExpireMinute": "10080" //7天
|
||||
},
|
||||
"IpRateLimiting": {
|
||||
"EnableEndpointRateLimiting": true,
|
||||
@@ -74,5 +74,12 @@
|
||||
"redirect_uri": "https://oauthlogin.net/oauth/githubcallback",
|
||||
"scope": "repo"
|
||||
}
|
||||
},
|
||||
"RequestDuplicationOptions": {
|
||||
"IsEnabled": true,
|
||||
"DuplicationWindowMs": 500,
|
||||
"CacheTimeSeconds": 5,
|
||||
"ExcludedPaths": [
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,9 @@
|
||||
<!-- HTML for static distribution bundle build -->
|
||||
<!-- HTML for static distribution bundle build -->
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<title>%(DocumentTitle)</title>
|
||||
<link href="https://fonts.googleapis.com/css?family=Open+Sans:400,700|Source+Code+Pro:300,600|Titillium+Web:400,600,700" rel="stylesheet">
|
||||
<link rel="stylesheet" type="text/css" href="./swagger-ui.css">
|
||||
<link rel="icon" type="image/png" href="./favicon-32x32.png" sizes="32x32" />
|
||||
<link rel="icon" type="image/png" href="./favicon-16x16.png" sizes="16x16" />
|
||||
@@ -113,8 +112,23 @@
|
||||
}, info: function () {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
fn: {
|
||||
opsFilter: function (taggedOps, phrase) {
|
||||
var normalPhrase = phrase.toLowerCase();
|
||||
return taggedOps.map(function (tagObj, tag) {
|
||||
var operations = tagObj.get("operations").filter(function (op) {
|
||||
var summary = op.getIn(["operation", "summary"]) || "";
|
||||
var path = op.get("path") || "";
|
||||
var tagMatch = tag.toLowerCase().indexOf(normalPhrase) !== -1;
|
||||
return tagMatch || summary.toLowerCase().indexOf(normalPhrase) !== -1 || path.toLowerCase().indexOf(normalPhrase) !== -1;
|
||||
});
|
||||
return tagObj.set("operations", operations);
|
||||
}).filter(function (tagObj) {
|
||||
return tagObj.get("operations").size > 0;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
@@ -505,8 +505,8 @@ var abp = abp || {};
|
||||
|
||||
//Inputs
|
||||
createInput(modalUxContent, 'tenancyName', 'Tenancy Name (Leave empty for Host)');
|
||||
createInput(modalUxContent, 'userName', 'Username or email address', 'text', 'cyldev');
|
||||
createInput(modalUxContent, 'password', 'Password', 'password', '123456');
|
||||
createInput(modalUxContent, 'userName', 'Username or email address', 'text', 'user1wj');
|
||||
createInput(modalUxContent, 'password', 'Password', 'password', '1');
|
||||
createInput(modalUxContent, 'pwdMd5', 'PwdMd5', 'text', '');
|
||||
createSelect(modalUxContent, 'roleSelect', 'role', [])
|
||||
|
||||
|
||||
@@ -39,7 +39,8 @@ namespace IRaCIS.Core.Application.Auth
|
||||
new Claim(JwtIRaCISClaimType.UserTypeShortName,user.UserTypeShortName),
|
||||
new Claim(JwtIRaCISClaimType.PermissionStr,user.PermissionStr),
|
||||
new Claim(JwtIRaCISClaimType.IsZhiZhun,user.IsZhiZhun.ToString()),
|
||||
new Claim(JwtIRaCISClaimType.IsTestUser,user.IsTestUser.ToString())
|
||||
new Claim(JwtIRaCISClaimType.IsTestUser,user.IsTestUser.ToString()),
|
||||
new Claim(JwtIRaCISClaimType.UserWorkLanguage,user.UserWorkLanguage.ToString())
|
||||
};
|
||||
|
||||
////创建令牌
|
||||
|
||||
@@ -23,5 +23,7 @@ namespace IRaCIS.Core.Application.Auth
|
||||
public bool IsZhiZhun { get; set; }
|
||||
|
||||
public string UserTypeShortName { get; set; } = string.Empty;
|
||||
|
||||
public UserWorkLanguage UserWorkLanguage { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,22 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using IRaCIS.Core.Application.Helper.OtherTool;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Binders;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using static System.Runtime.InteropServices.JavaScript.JSType;
|
||||
|
||||
|
||||
namespace IRaCIS.Core.Application.Filter;
|
||||
|
||||
|
||||
|
||||
public class ModelActionFilter(IStringLocalizer _localizer) : ActionFilterAttribute, IActionFilter
|
||||
public class ModelActionFilter(IStringLocalizer _localizer, IConfiguration _config, IUserInfo _userInfo) : ActionFilterAttribute, IActionFilter
|
||||
{
|
||||
|
||||
|
||||
@@ -23,8 +31,55 @@ public class ModelActionFilter(IStringLocalizer _localizer) : ActionFilterAttrib
|
||||
.Select(e => e.ErrorMessage)
|
||||
.ToArray();
|
||||
|
||||
var request = context.HttpContext.Request;
|
||||
|
||||
try
|
||||
{
|
||||
bool isOpenWeComNotice = _config.GetValue<bool>("WeComNoticeConfig:IsOpenWeComNotice");
|
||||
|
||||
if (isOpenWeComNotice)
|
||||
{
|
||||
string webhook = _config["WeComNoticeConfig:WebhookUrl"] ?? string.Empty;
|
||||
|
||||
var uri = new Uri(_config["SystemEmailSendConfig:SiteUrl"]);
|
||||
var baseUrl = uri.GetLeftPart(UriPartial.Authority);
|
||||
|
||||
var userList = _config.GetSection("WeComNoticeConfig:VueNoticeUserList").Get<string[]>();
|
||||
|
||||
var requestBody = context.HttpContext.Items["RequestBody"] as string;
|
||||
|
||||
|
||||
// 🔔 异步告警(不要阻塞请求)
|
||||
_ = WeComNotifier.SendAlertAsync(
|
||||
webhook: webhook,
|
||||
alert: new WeComAlert
|
||||
{
|
||||
Env = baseUrl,
|
||||
UserName = _userInfo.UserName.IsNotNullOrEmpty()? $"{_userInfo.UserName}({_userInfo.UserTypeShortName})": "匿名",
|
||||
Api = $"{request.Method} {request.Path}",
|
||||
Message = $"前端传递参数,后端模型验证失败\n 具体信息:{JsonConvert.SerializeObject(validationErrors)}",
|
||||
JsonData = requestBody,
|
||||
AtUsers = userList ?? []
|
||||
}
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Logger.Error($"模型验证过滤器里发送企业微信出现错误:{ex.Message}");
|
||||
}
|
||||
|
||||
|
||||
|
||||
//---提供给接口的参数无效。
|
||||
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["ModelAction_InvalidAPIParameter"] + JsonConvert.SerializeObject(validationErrors)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using IRaCIS.Core.Application.Helper.OtherTool;
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using IRaCIS.Core.Infrastructure;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace IRaCIS.Core.Application.Filter;
|
||||
|
||||
public class ProjectExceptionFilter(ILogger<ProjectExceptionFilter> _logger, IStringLocalizer _localizer) : Attribute, IExceptionFilter
|
||||
public class ProjectExceptionFilter(ILogger<ProjectExceptionFilter> _logger, IStringLocalizer _localizer, IConfiguration _config, IUserInfo _userInfo) : Attribute, IExceptionFilter
|
||||
{
|
||||
|
||||
public void OnException(ExceptionContext context)
|
||||
@@ -46,7 +49,44 @@ public class ProjectExceptionFilter(ILogger<ProjectExceptionFilter> _logger, ISt
|
||||
else
|
||||
{
|
||||
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["Project_ExceptionContactDeveloper"] + (exception.InnerException is null ? (exception.Message)
|
||||
: (exception.InnerException?.Message )), ApiResponseCodeEnum.ProgramException));
|
||||
: (exception.Message + "Inner ExceptionMsg:" + exception.InnerException?.Message)), ApiResponseCodeEnum.ProgramException));
|
||||
|
||||
try
|
||||
{
|
||||
bool isOpenWeComNotice = _config.GetValue<bool>("WeComNoticeConfig:IsOpenWeComNotice");
|
||||
|
||||
if (isOpenWeComNotice)
|
||||
{
|
||||
string webhook = _config["WeComNoticeConfig:WebhookUrl"] ?? string.Empty;
|
||||
|
||||
var uri = new Uri(_config["SystemEmailSendConfig:SiteUrl"]);
|
||||
var baseUrl = uri.GetLeftPart(UriPartial.Authority);
|
||||
|
||||
var userList = _config.GetSection("WeComNoticeConfig:APINoticeUserList").Get<string[]>();
|
||||
|
||||
var requestBody = context.HttpContext.Items["RequestBody"] as string;
|
||||
|
||||
var alert = new WeComAlert
|
||||
{
|
||||
Env = baseUrl,
|
||||
UserName = $"{_userInfo.UserName}({_userInfo.UserTypeShortName})",
|
||||
Api = context.HttpContext.Request.Path,
|
||||
Message = exception.Message,
|
||||
JsonData = requestBody,
|
||||
Stack = exception.StackTrace,
|
||||
AtUsers = userList ?? []
|
||||
};
|
||||
|
||||
_ = WeComNotifier.SendAlertAsync(webhook, alert);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
_logger.LogError($"异常过滤器里发送企业微信出现错误:{ex.Message}");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -58,6 +98,7 @@ public class ProjectExceptionFilter(ILogger<ProjectExceptionFilter> _logger, ISt
|
||||
|
||||
_logger.LogError(errorInfo);
|
||||
|
||||
|
||||
//_logger.LogError(exception, exception.Message);
|
||||
}
|
||||
else
|
||||
|
||||
@@ -0,0 +1,185 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace IRaCIS.Core.Application.BusinessFilter.LegacyController
|
||||
{
|
||||
using Database.Api;
|
||||
using DocumentFormat.OpenXml.InkML;
|
||||
using global::MassTransit;
|
||||
using IRaCIS.Core.Application.Helper.OtherTool;
|
||||
using IRaCIS.Core.Application.Service.Common;
|
||||
using IRaCIS.Core.Infrastructure;
|
||||
using IRaCIS.Core.Infrastructure.Extention;
|
||||
using Microsoft.AspNetCore.Components.Endpoints;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Minio.Helper;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Database.Api
|
||||
{
|
||||
/// <summary>
|
||||
/// 请求拦截 请求前后的操作
|
||||
/// </summary>
|
||||
/// <param name="logger">logger</param>
|
||||
/// <param name="accessor">loggerHelper</param>
|
||||
public class RequestDuplicationFilter(ILogger<RequestDuplicationFilter> logger, IHttpContextAccessor accessor, IUserInfo _userInfo, IStringLocalizer _localizer, IOptionsMonitor<RequestDuplicationOptions> RequestDuplicationOptionsMonitor, IConfiguration _config) : ExceptionFilterAttribute, IAsyncActionFilter
|
||||
{
|
||||
/// <summary>
|
||||
/// 传入的参数
|
||||
/// </summary>
|
||||
public string Intoparam { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 这个是正常记录(请求刚进入的时候)
|
||||
/// </summary>
|
||||
/// <param name="context">context</param>
|
||||
/// <param name="next">next</param>
|
||||
/// <returns>返回的对象</returns>
|
||||
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
|
||||
{
|
||||
if (RequestDuplicationOptionsMonitor.CurrentValue.IsEnabled)
|
||||
{
|
||||
Dictionary<string, object?> dic = new Dictionary<string, object?>();
|
||||
var desc = context.ActionDescriptor as ControllerActionDescriptor;
|
||||
foreach (var p in desc.Parameters)
|
||||
{
|
||||
// 关键判断:绑定源是否是 Services
|
||||
if (p.BindingInfo?.BindingSource == BindingSource.Services) continue;
|
||||
// 普通参数,取值
|
||||
if (context.ActionArguments.TryGetValue(p.Name, out var value) && value != null)
|
||||
{
|
||||
dic.Add(p.Name, value);
|
||||
}
|
||||
}
|
||||
|
||||
this.Intoparam = JsonConvert.SerializeObject(dic);
|
||||
try
|
||||
{
|
||||
this.RequestDuplication();
|
||||
}
|
||||
catch (BusinessValidationFailedException)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var resultContext = await next();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
private void RequestDuplication()
|
||||
{
|
||||
var requestPath = accessor?.HttpContext?.Request?.Path.ToString() ?? string.Empty;
|
||||
|
||||
var httpMethod = accessor?.HttpContext?.Request?.Method?.ToUpper() ?? string.Empty;
|
||||
|
||||
// 验证请求频繁情况
|
||||
if (
|
||||
!httpMethod.Equals("GET", StringComparison.OrdinalIgnoreCase) &&
|
||||
|
||||
!requestPath
|
||||
.Split("/", StringSplitOptions.RemoveEmptyEntries)
|
||||
.Any(segment => segment.StartsWith("get", StringComparison.OrdinalIgnoreCase)) &&
|
||||
_userInfo.UserRoleId != default(Guid)&&
|
||||
RequestDuplicationOptionsMonitor.CurrentValue.IsEnabled &&
|
||||
!RequestDuplicationOptionsMonitor.CurrentValue.ExcludePaths.Contains(requestPath))
|
||||
{
|
||||
RequestInfo requestInfo = new RequestInfo
|
||||
{
|
||||
UserRoleId = _userInfo.UserRoleId,
|
||||
RequestPath = requestPath,
|
||||
ParameterHash = GenerateParameterHash(this.Intoparam),
|
||||
RequestTime = DateTime.Now
|
||||
};
|
||||
|
||||
var isAdded = IRCSystemInfo.TryAddRequestRecord(
|
||||
requestInfo,
|
||||
RequestDuplicationOptionsMonitor.CurrentValue.CacheTimeSeconds,
|
||||
RequestDuplicationOptionsMonitor.CurrentValue.DuplicationWindowMs);
|
||||
if (!isAdded)
|
||||
{
|
||||
SendRequestDuplicationWeComNotice(requestPath);
|
||||
logger.LogError($"重复请求出现错误:{this.Intoparam}");
|
||||
throw new BusinessValidationFailedException(_localizer["RequestDuplicationFilter_RequestDuplication"], ApiResponseCodeEnum.NotRemind);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SendRequestDuplicationWeComNotice(string requestPath)
|
||||
{
|
||||
try
|
||||
{
|
||||
bool isOpenWeComNotice = _config.GetValue<bool>("WeComNoticeConfig:IsOpenWeComNotice");
|
||||
|
||||
if (isOpenWeComNotice)
|
||||
{
|
||||
string webhook = _config["WeComNoticeConfig:WebhookUrl"] ?? string.Empty;
|
||||
|
||||
var uri = new Uri(_config["SystemEmailSendConfig:SiteUrl"]);
|
||||
var baseUrl = uri.GetLeftPart(UriPartial.Authority);
|
||||
|
||||
var userList = _config.GetSection("WeComNoticeConfig:VueNoticeUserList").Get<string[]>();
|
||||
|
||||
var request = accessor?.HttpContext?.Request;
|
||||
|
||||
_ = WeComNotifier.SendAlertAsync(
|
||||
webhook: webhook,
|
||||
alert: new WeComAlert
|
||||
{
|
||||
Env = baseUrl,
|
||||
UserName = _userInfo.UserName.IsNotNullOrEmpty() ? $"{_userInfo.UserName}({_userInfo.UserTypeShortName})" : "匿名",
|
||||
Api = $"{request?.Method} {requestPath}",
|
||||
Message = "重复请求拦截",
|
||||
JsonData = this.Intoparam,
|
||||
Stack = string.Empty,
|
||||
AtUsers = userList ?? []
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError($"重复请求过滤器里发送企业微信出现错误:{ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
public string GenerateParameterHash(string parameters)
|
||||
{
|
||||
if (string.IsNullOrEmpty(parameters))
|
||||
return string.Empty;
|
||||
|
||||
using var sha256 = SHA256.Create();
|
||||
var hashBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(parameters));
|
||||
return Convert.ToBase64String(hashBytes);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
-11
@@ -29,21 +29,21 @@ public class TrialGlobalLimitActionFilter(IFusionCache _fusionCache, IUserInfo _
|
||||
var requestHost = context.HttpContext.Request.Host;
|
||||
|
||||
// 检查请求是否来自 localhost:6100
|
||||
//if (requestHost.Host == "localhost" && (requestHost.Port == 6100 || requestHost.Port == 3305))
|
||||
//{
|
||||
// await next();
|
||||
// return;
|
||||
//}
|
||||
if (requestHost.Host == "localhost" && (requestHost.Port == 6100 || requestHost.Port == 3305))
|
||||
{
|
||||
await next();
|
||||
return;
|
||||
}
|
||||
|
||||
#region 特殊用户类型拦截
|
||||
// 用户类型检查
|
||||
if (_userInfo.UserTypeEnumInt == (int)UserTypeEnum.CRA && _userInfo.RequestUrl.ToLower() != "TrialDocument/userConfirm".ToLower())
|
||||
{
|
||||
//对不起,您的账户没有操作权限
|
||||
context.Result = new JsonResult(ResponseOutput.NotOk(I18n.T("TrialResource_NoAccessPermission")));
|
||||
//if (_userInfo.UserTypeEnumInt == (int)UserTypeEnum.CRA && _userInfo.RequestUrl.ToLower() != "TrialDocument/userConfirm".ToLower())
|
||||
//{
|
||||
// //对不起,您的账户没有操作权限
|
||||
// context.Result = new JsonResult(ResponseOutput.NotOk(I18n.T("TrialResource_NoAccessPermission")));
|
||||
|
||||
return;
|
||||
}
|
||||
// return;
|
||||
//}
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
@@ -47,12 +47,12 @@ public class TrialGlobalLimitEndpointFilter(IFusionCache _fusionCache, IUserInfo
|
||||
}
|
||||
|
||||
#region 特殊用户类型拦截
|
||||
// 用户类型检查
|
||||
if (_userInfo.UserTypeEnumInt == (int)UserTypeEnum.CRA && _userInfo.RequestUrl.ToLower() != "TrialDocument/userConfirm".ToLower())
|
||||
{
|
||||
//对不起,您的账户没有操作权限
|
||||
return Results.Json(ResponseOutput.NotOk(I18n.T("TrialResource_NoAccessPermission")));
|
||||
}
|
||||
//// 用户类型检查
|
||||
//if (_userInfo.UserTypeEnumInt == (int)UserTypeEnum.CRA && _userInfo.RequestUrl.ToLower() != "TrialDocument/userConfirm".ToLower())
|
||||
//{
|
||||
// //对不起,您的账户没有操作权限
|
||||
// return Results.Json(ResponseOutput.NotOk(I18n.T("TrialResource_NoAccessPermission")));
|
||||
//}
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ namespace IRaCIS.Core.Domain.Share;
|
||||
[Description("多环境 配置环境实体")]
|
||||
public class ServiceVerifyConfigOption
|
||||
{
|
||||
public bool QCRiskControl { get; set; }
|
||||
public bool OpenUserComplexPassword { get; set; }
|
||||
|
||||
public bool OpenSignDocumentBeforeWork { get; set; }
|
||||
@@ -38,6 +39,8 @@ public class ServiceVerifyConfigOption
|
||||
|
||||
public string ThirdPdfUrl { get; set; }
|
||||
|
||||
public int UserMFAVerifyMinutes { get; set; } = 1440;
|
||||
|
||||
}
|
||||
|
||||
public class SystemEmailSendConfig
|
||||
@@ -45,6 +48,10 @@ public class SystemEmailSendConfig
|
||||
public int Port { get; set; }
|
||||
|
||||
public string Host { get; set; } = string.Empty;
|
||||
|
||||
public string Imap { get; set; } = string.Empty;
|
||||
|
||||
public int ImapPort { get; set; }
|
||||
public string FromEmail { get; set; } = string.Empty;
|
||||
|
||||
public string FromName { get; set; } = string.Empty;
|
||||
@@ -53,7 +60,11 @@ public class SystemEmailSendConfig
|
||||
|
||||
public string SiteUrl { get; set; } = string.Empty;
|
||||
|
||||
public string SystemShortName { get; set; } = string.Empty;
|
||||
|
||||
public string PlatformName { get; set; } = string.Empty;
|
||||
public string PlatformNameCN { get; set; } = string.Empty;
|
||||
|
||||
public string SystemShortName { get; set; } = string.Empty;
|
||||
|
||||
public string OrganizationName { get; set; } = string.Empty;
|
||||
public string OrganizationNameCN { get; set; } = string.Empty;
|
||||
@@ -68,16 +79,17 @@ public class SystemEmailSendConfig
|
||||
|
||||
public bool IsEnv_US { get; set; }
|
||||
|
||||
public bool IsOpenErrorNoticeEmail { get; set; }
|
||||
|
||||
public string EmailRegexStr { get; set; }
|
||||
|
||||
public List<string> ErrorNoticeEmailList { get; set; } =new List<string>();
|
||||
//public bool IsOpenErrorNoticeEmail { get; set; }
|
||||
|
||||
|
||||
//public List<string> ErrorNoticeEmailList { get; set; } = new List<string>();
|
||||
}
|
||||
|
||||
public class SystemEmailSendConfigView
|
||||
{
|
||||
public string SystemShortName { get; set; } = string.Empty;
|
||||
public string SystemShortName { get; set; } = string.Empty;
|
||||
public string CompanyName { get; set; } = string.Empty;
|
||||
|
||||
public string CompanyNameCN { get; set; } = string.Empty;
|
||||
@@ -105,6 +117,79 @@ public class IRCEncreptOption
|
||||
public List<string> ApiPathList { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 请求缓存配置
|
||||
/// </summary>
|
||||
public class RequestDuplicationOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// 缓存时间(秒),默认5秒
|
||||
/// </summary>
|
||||
public int CacheTimeSeconds { get; set; } = 5;
|
||||
|
||||
/// <summary>
|
||||
/// 重复请求检测时间窗口(毫秒),默认500毫秒
|
||||
/// </summary>
|
||||
public int DuplicationWindowMs { get; set; } = 500;
|
||||
|
||||
/// <summary>
|
||||
/// 是否启用防重复请求
|
||||
/// </summary>
|
||||
public bool IsEnabled { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// 需要排除的路径(不进行重复检测)
|
||||
/// </summary>
|
||||
public List<string> ExcludePaths { get; set; } = new List<string>();
|
||||
}
|
||||
|
||||
public static class IRCSystemInfo
|
||||
{
|
||||
public static List<RequestInfo> RequestRecordList { get; set; } = new List<RequestInfo>();
|
||||
|
||||
private static readonly object requestRecordLock = new object();
|
||||
|
||||
public static bool TryAddRequestRecord(RequestInfo requestInfo, int cacheTimeSeconds, int duplicationWindowMs)
|
||||
{
|
||||
lock (requestRecordLock)
|
||||
{
|
||||
var now = DateTime.Now;
|
||||
RequestRecordList = RequestRecordList
|
||||
.Where(x => x.RequestTime >= now.AddSeconds(-cacheTimeSeconds))
|
||||
.ToList();
|
||||
|
||||
var isDuplicate = RequestRecordList.Any(x =>
|
||||
x.RequestTime >= requestInfo.RequestTime.AddMilliseconds(-duplicationWindowMs) &&
|
||||
x.RequestKey == requestInfo.RequestKey);
|
||||
|
||||
if (isDuplicate)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
RequestRecordList.Add(requestInfo);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class RequestInfo
|
||||
{
|
||||
public Guid UserRoleId { get; set; }
|
||||
|
||||
public string RequestPath { get; set; } = string.Empty;
|
||||
|
||||
public string ParameterHash { get; set; } = string.Empty;
|
||||
|
||||
public DateTime RequestTime { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 请求的唯一标识
|
||||
/// </summary>
|
||||
public string RequestKey => $"{UserRoleId}_{RequestPath}_{ParameterHash}";
|
||||
}
|
||||
|
||||
public class IRaCISBasicConfigOption
|
||||
{
|
||||
public string DoctorCodePrefix { get; set; }
|
||||
@@ -176,4 +261,4 @@ public static class AppSettings
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
namespace IRaCIS.Core.Application.Helper;
|
||||
using DocumentFormat.OpenXml.Spreadsheet;
|
||||
|
||||
namespace IRaCIS.Core.Application.Helper;
|
||||
|
||||
|
||||
public static class CacheKeys
|
||||
@@ -6,6 +8,8 @@ public static class CacheKeys
|
||||
//项目缓存
|
||||
public static string Trial(string trialIdStr) => $"TrialId:{trialIdStr}";
|
||||
|
||||
public static string TrialVisitTaskMaxCode(Guid trialId) => $"TrialVisitTaskMaxCode:{trialId}";
|
||||
|
||||
//检查编号递增锁
|
||||
public static string TrialStudyMaxCode(Guid trialId) => $"TrialStudyMaxCode:{trialId}";
|
||||
|
||||
@@ -61,6 +65,15 @@ public static class CacheKeys
|
||||
/// <returns></returns>
|
||||
public static string StartRestTime(Guid userId) => $"{userId}StartRestTime";
|
||||
|
||||
//每个用户 每个浏览器独立时间
|
||||
public static string UserMFAVerifyPass(Guid userId,string browserFingerprint) => $"UserMFAVerifyPass:{userId}:{browserFingerprint}";
|
||||
|
||||
public static string UserMFATag(Guid userId) => $"UserMFAVerifyPass:{userId}";
|
||||
|
||||
public static string TrialDataStoreType(Guid trialId) => $"TrialDataStoreType:{trialId}";
|
||||
|
||||
|
||||
public static string UserQCSkipTask(Guid userId) => $"UserSKipQCTask:{userId}";
|
||||
}
|
||||
|
||||
public static class CacheHelper
|
||||
@@ -72,6 +85,9 @@ public static class CacheHelper
|
||||
return statusStr;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public static async Task<List<SystemAnonymization>> GetSystemAnonymizationListAsync(IRepository<SystemAnonymization> _systemAnonymizationRepository)
|
||||
{
|
||||
var list = await _systemAnonymizationRepository.Where(t => t.IsEnable).ToListAsync();
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
using FellowOakDicom;
|
||||
using FellowOakDicom.Media;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using IRaCIS.Core.Application.ViewModel;
|
||||
using System.Data;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace IRaCIS.Core.Application.Helper
|
||||
{
|
||||
@@ -57,6 +53,11 @@ namespace IRaCIS.Core.Application.Helper
|
||||
var mappings = new List<string>();
|
||||
int index = 1;
|
||||
|
||||
var trialId = Guid.Empty;
|
||||
|
||||
Guid.TryParse(ossFolder.Split('/', StringSplitOptions.RemoveEmptyEntries)[0], out trialId);
|
||||
|
||||
var studyUid = list.FirstOrDefault()?.StudyInstanceUid;
|
||||
|
||||
var dicomDir = new DicomDirectory();
|
||||
|
||||
@@ -100,11 +101,11 @@ namespace IRaCIS.Core.Application.Helper
|
||||
var dicomFile = new DicomFile(dataset);
|
||||
|
||||
// 文件名递增格式:IM_00001, IM_00002, ...
|
||||
string filename = $@"IMAGE/IM_{index:D5}"; // :D5 表示补足5位
|
||||
string filename = $@"IMAGE\IM_{index:D5}"; // :D5 表示补足5位
|
||||
|
||||
mappings.Add($"{filename} => {item.InstanceId}");
|
||||
|
||||
dic.Add(item.InstanceId.ToString(), Path.GetFileName(filename));
|
||||
dic.Add(item.InstanceId.ToString(), filename.TrimEnd('/', '\\').Split('/', '\\').Last());
|
||||
|
||||
dicomDir.AddFile(dicomFile, filename);
|
||||
|
||||
@@ -128,7 +129,9 @@ namespace IRaCIS.Core.Application.Helper
|
||||
// 重置流位置
|
||||
memoryStream.Position = 0;
|
||||
|
||||
await _oSSService.UploadToOSSAsync(memoryStream, ossFolder, "DICOMDIR", false);
|
||||
var relativePath = await _oSSService.UploadToOSSAsync(memoryStream, ossFolder, "DICOMDIR", true, uploadInfo: new FileUploadRecordAddOrEdit() { TrialId = trialId, BatchDataType = BatchDataType.DICOMDIR });
|
||||
|
||||
dic.Add($"{studyUid}_DICOMDIR", relativePath.Split('/').Last());
|
||||
}
|
||||
|
||||
//清理临时文件
|
||||
@@ -142,7 +145,7 @@ namespace IRaCIS.Core.Application.Helper
|
||||
var mappingText = string.Join(Environment.NewLine, mappings);
|
||||
await using var mappingStream = new MemoryStream(Encoding.UTF8.GetBytes(mappingText));
|
||||
|
||||
await _oSSService.UploadToOSSAsync(mappingStream, ossFolder, $"Download_{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}", false);
|
||||
await _oSSService.UploadToOSSAsync(mappingStream, ossFolder, $"Download_{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}", false, uploadInfo: new FileUploadRecordAddOrEdit() { TrialId = trialId, BatchDataType = BatchDataType.DICOMDIR });
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -150,6 +153,91 @@ namespace IRaCIS.Core.Application.Helper
|
||||
}
|
||||
|
||||
|
||||
public static async Task GenerateStudyDIR(List<StudyDIRInfo> list, Dictionary<string, string> dic, string? dirSavePath = null, Stream? outputStream = null)
|
||||
{
|
||||
var mappings = new List<string>();
|
||||
int index = 1;
|
||||
|
||||
var trialId = Guid.Empty;
|
||||
|
||||
|
||||
var studyUid = list.FirstOrDefault()?.StudyInstanceUid;
|
||||
|
||||
var dicomDir = new DicomDirectory();
|
||||
|
||||
foreach (var item in list.OrderBy(t => t.SeriesNumber).ThenBy(t => t.InstanceNumber))
|
||||
{
|
||||
var dicomUid = DicomUID.Enumerate().FirstOrDefault(uid => uid.UID == item.TransferSytaxUID);
|
||||
|
||||
if (dicomUid != null)
|
||||
{
|
||||
var ts = DicomTransferSyntax.Query(dicomUid);
|
||||
|
||||
var dataset = new DicomDataset(ts)
|
||||
{
|
||||
{ DicomTag.PatientID, item.PatientId ?? string.Empty },
|
||||
{ DicomTag.PatientName, item.PatientName ?? string.Empty },
|
||||
{ DicomTag.PatientBirthDate, item.PatientBirthDate ?? string.Empty },
|
||||
{ DicomTag.PatientSex, item.PatientSex ?? string.Empty },
|
||||
|
||||
{ DicomTag.StudyInstanceUID, item.StudyInstanceUid ?? string.Empty },
|
||||
{ DicomTag.StudyID, item.StudyId ?? string.Empty },
|
||||
{ DicomTag.StudyDate, item.DicomStudyDate ?? string.Empty },
|
||||
{ DicomTag.StudyTime, item.DicomStudyTime ?? string.Empty },
|
||||
{ DicomTag.AccessionNumber, item.AccessionNumber ?? string.Empty },
|
||||
{ DicomTag.StudyDescription, item.StudyDescription ?? string.Empty },
|
||||
|
||||
{ DicomTag.SeriesInstanceUID, item.SeriesInstanceUid ?? string.Empty },
|
||||
{ DicomTag.Modality, item.Modality ?? string.Empty },
|
||||
{ DicomTag.SeriesDate, item.DicomSeriesDate ?? string.Empty },
|
||||
{ DicomTag.SeriesTime, item.DicomSeriesTime ?? string.Empty },
|
||||
{ DicomTag.SeriesNumber, item.SeriesNumber.ToString() ?? string.Empty },
|
||||
{ DicomTag.SeriesDescription, item.SeriesDescription ?? string.Empty },
|
||||
|
||||
{ DicomTag.SOPInstanceUID, item.SopInstanceUid ?? string.Empty },
|
||||
{ DicomTag.SOPClassUID, item.SOPClassUID ?? string.Empty },
|
||||
{ DicomTag.InstanceNumber, item.InstanceNumber.ToString() ?? string.Empty },
|
||||
{ DicomTag.MediaStorageSOPClassUID, item.MediaStorageSOPClassUID ?? string.Empty },
|
||||
{ DicomTag.MediaStorageSOPInstanceUID, item.MediaStorageSOPInstanceUID ?? string.Empty },
|
||||
{ DicomTag.TransferSyntaxUID, item.TransferSytaxUID ?? string.Empty },
|
||||
};
|
||||
|
||||
var dicomFile = new DicomFile(dataset);
|
||||
|
||||
// 文件名递增格式:IM_00001, IM_00002, ...
|
||||
string filename = $@"IMAGE\IM_{index:D5}"; // :D5 表示补足5位
|
||||
|
||||
mappings.Add($"{filename} => {item.InstanceId}");
|
||||
|
||||
dic.Add(item.InstanceId.ToString(), filename.TrimEnd('/', '\\').Split('/', '\\').Last());
|
||||
|
||||
dicomDir.AddFile(dicomFile, filename);
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
//有实际的文件
|
||||
if (mappings.Count > 0)
|
||||
{
|
||||
// 保存 DICOMDIR 到临时文件 不能直接写入到流种
|
||||
|
||||
if (dirSavePath.IsNotNullOrEmpty())
|
||||
{
|
||||
await dicomDir.SaveAsync(dirSavePath);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
await dicomDir.SaveAsync(outputStream);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static StudyDIRInfo ReadDicomDIRInfo(DicomFile dicomFile)
|
||||
{
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using MailKit;
|
||||
using MailKit.Security;
|
||||
using MimeKit;
|
||||
using Org.BouncyCastle.Tls;
|
||||
|
||||
|
||||
namespace IRaCIS.Core.Application.Helper;
|
||||
@@ -9,12 +10,15 @@ namespace IRaCIS.Core.Application.Helper;
|
||||
public static class SendEmailHelper
|
||||
{
|
||||
|
||||
public static async Task SendEmailAsync(MimeMessage messageToSend, SystemEmailSendConfig _systemEmailConfig, EventHandler<MessageSentEventArgs>? messageSentSuccess = null)
|
||||
public static async Task<string> SendEmailAsync(MimeMessage messageToSend, SystemEmailSendConfig _systemEmailConfig, EventHandler<MessageSentEventArgs>? messageSentSuccess = null)
|
||||
{
|
||||
string result = string.Empty;
|
||||
result = messageToSend.MessageId;
|
||||
|
||||
//没有收件人 那么不发送
|
||||
if (messageToSend.To.Count == 0)
|
||||
{
|
||||
return;
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
try
|
||||
@@ -42,6 +46,7 @@ public static class SendEmailHelper
|
||||
|
||||
await smtp.DisconnectAsync(true);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -51,9 +56,133 @@ public static class SendEmailHelper
|
||||
throw new Exception(I18n.T("SendEmail_SendFail"), new Exception(ex.Message));
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 发送项目邮件
|
||||
/// </summary>
|
||||
/// <param name="messageToSend"></param>
|
||||
/// <param name="trial"></param>
|
||||
/// <param name="messageSentSuccess"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="Exception"></exception>
|
||||
public static async Task<string> SendTrialEmailAsync(MimeMessage messageToSend, Trial trial, EventHandler<MessageSentEventArgs>? messageSentSuccess = null)
|
||||
{
|
||||
|
||||
// 项目的需要重设 发件地址与邮件地址
|
||||
var fromAddress = messageToSend.From.Mailboxes.FirstOrDefault();
|
||||
if (fromAddress != null)
|
||||
{
|
||||
messageToSend.From.Clear();
|
||||
messageToSend.From.Add(new MailboxAddress(trial.EmailFromName, trial.EmailFromEmail));
|
||||
}
|
||||
|
||||
|
||||
string result = string.Empty;
|
||||
result = messageToSend.MessageId;
|
||||
|
||||
|
||||
//没有收件人 那么不发送
|
||||
if (messageToSend.To.Count == 0)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
// 替换邮件标题
|
||||
if (!string.IsNullOrEmpty(messageToSend.Subject))
|
||||
{
|
||||
foreach (var item in trial.TrialObjectNameList)
|
||||
{
|
||||
// 把标题里的占位符替换成真实名称
|
||||
messageToSend.Subject = messageToSend.Subject.Replace(item.Name, item.TrialName);
|
||||
}
|
||||
}
|
||||
|
||||
// 构建替换字典
|
||||
var replacements = new Dictionary<string, string>();
|
||||
foreach (var item in trial.TrialObjectNameList)
|
||||
{
|
||||
replacements[item.Name] = item.TrialName;
|
||||
}
|
||||
|
||||
// 安全替换 HTML
|
||||
ReplaceHtmlContent(messageToSend.Body, replacements);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
using (var smtp = new MailKit.Net.Smtp.SmtpClient())
|
||||
{
|
||||
if (messageSentSuccess != null)
|
||||
{
|
||||
smtp.MessageSent += messageSentSuccess;
|
||||
}
|
||||
|
||||
|
||||
smtp.ServerCertificateValidationCallback = (s, c, h, e) => true;
|
||||
|
||||
//await smtp.ConnectAsync("smtp.qq.com", 465, SecureSocketOptions.SslOnConnect);
|
||||
|
||||
//await smtp.AuthenticateAsync("zhou941003@qq.com", "sqfhlpfdvnexbcab");
|
||||
|
||||
|
||||
//await smtp.ConnectAsync(_systemEmailConfig.Host, _systemEmailConfig.Port, SecureSocketOptions.Auto);
|
||||
|
||||
//await smtp.AuthenticateAsync(_systemEmailConfig.FromEmail, _systemEmailConfig.AuthorizationCode);
|
||||
|
||||
|
||||
await smtp.ConnectAsync(trial.EmailSMTPServerAddress, trial.EmailSMTPServerPort, SecureSocketOptions.Auto);
|
||||
|
||||
await smtp.AuthenticateAsync(trial.EmailFromEmail, trial.EmailAuthorizationCode);
|
||||
|
||||
|
||||
await smtp.SendAsync(messageToSend);
|
||||
|
||||
await smtp.DisconnectAsync(true);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
//---邮件发送失败,您进行的操作未能成功,请检查邮箱或联系维护人员
|
||||
throw new Exception(I18n.T("SendEmail_SendFail"), new Exception(ex.Message));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 遍历邮件体,找到 HTML 部分并替换
|
||||
public static void ReplaceHtmlContent(MimeEntity entity, Dictionary<string, string> replacements)
|
||||
{
|
||||
if (entity is Multipart multipart)
|
||||
{
|
||||
foreach (var part in multipart)
|
||||
{
|
||||
ReplaceHtmlContent(part, replacements);
|
||||
}
|
||||
}
|
||||
else if (entity is TextPart textPart && textPart.IsHtml)
|
||||
{
|
||||
// 只有这里才是真正的 HTML
|
||||
foreach (var kv in replacements)
|
||||
{
|
||||
textPart.Text = textPart.Text.Replace(kv.Key, kv.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
public static async Task<bool> TestEmailConfigAsync(SystemEmailSendConfig _systemEmailConfig)
|
||||
{
|
||||
using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5)))
|
||||
@@ -73,7 +202,7 @@ public static class SendEmailHelper
|
||||
return true;
|
||||
}
|
||||
|
||||
public static async Task SendEmailAsync(SMTPEmailConfig sMTPEmailConfig, EventHandler<MessageSentEventArgs>? messageSentSuccess = null)
|
||||
public static async Task SendEmailAsync(SMTPEmailConfig sMTPEmailConfig, Trial? trial, EventHandler<MessageSentEventArgs>? messageSentSuccess = null)
|
||||
{
|
||||
var messageToSend = new MimeMessage();
|
||||
|
||||
@@ -87,8 +216,9 @@ public static class SendEmailHelper
|
||||
|
||||
if (sMTPEmailConfig.ToMailAddressList.Count == 0)
|
||||
{
|
||||
return;
|
||||
//---没有收件人
|
||||
throw new ArgumentException(I18n.T("SendEmail_NoRecipient"));
|
||||
//throw new ArgumentException(I18n.T("SendEmail_NoRecipient"));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -143,7 +273,7 @@ public static class SendEmailHelper
|
||||
|
||||
await smtp.ConnectAsync(sMTPEmailConfig.Host, sMTPEmailConfig.Port, SecureSocketOptions.Auto);
|
||||
|
||||
await smtp.AuthenticateAsync(sMTPEmailConfig.UserName, sMTPEmailConfig.AuthorizationCode);
|
||||
await smtp.AuthenticateAsync(trial.EmailFromEmail, trial.EmailAuthorizationCode);
|
||||
|
||||
await smtp.SendAsync(messageToSend);
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,4 @@
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using NPOI.XWPF.UserModel;
|
||||
using System.Globalization;
|
||||
using System.Globalization;
|
||||
using Xceed.Document.NET;
|
||||
using Xceed.Words.NET;
|
||||
|
||||
@@ -54,42 +52,42 @@ public static class WordTempleteHelper
|
||||
}
|
||||
|
||||
|
||||
public static void Npoi_GetInternationalTempleteStream(string filePath, Stream memoryStream)
|
||||
{
|
||||
//public static void Npoi_GetInternationalTempleteStream(string filePath, Stream memoryStream)
|
||||
//{
|
||||
|
||||
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
|
||||
// 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);
|
||||
// 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));
|
||||
// // 查找包含指定书签的段落及其索引
|
||||
// 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 (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);
|
||||
}
|
||||
}
|
||||
// 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);
|
||||
// }
|
||||
//}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using IRaCIS.Core.Application.MassTransit.Consumer;
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using MassTransit.Mediator;
|
||||
using System.Globalization;
|
||||
|
||||
namespace IRaCIS.Core.Application.Helper
|
||||
{
|
||||
@@ -57,25 +58,29 @@ namespace IRaCIS.Core.Application.Helper
|
||||
}
|
||||
|
||||
|
||||
public static void AddOrUpdateTrialCronJob(string jobId, Guid trialId, EmailBusinessScenario businessScenario, string emailCron)
|
||||
public static void AddOrUpdateTrialCronJob(string jobId, Guid trialId, EmailBusinessScenario businessScenario, string emailCron,string defaultCulture="")
|
||||
{
|
||||
if (defaultCulture == "")
|
||||
{
|
||||
defaultCulture = CultureInfo.CurrentCulture.Name;
|
||||
}
|
||||
|
||||
switch (businessScenario)
|
||||
{
|
||||
|
||||
case EmailBusinessScenario.QCTask:
|
||||
|
||||
HangfireJobHelper.AddOrUpdateCronJob<IMediator>(jobId, t => t.Send(new ImageQCRecurringEvent() { TrialId = trialId }, default), emailCron);
|
||||
HangfireJobHelper.AddOrUpdateCronJob<IMediator>(jobId, t => t.Send(new ImageQCRecurringEvent() { TrialId = trialId,CultureInfoName= defaultCulture }, default), emailCron);
|
||||
break;
|
||||
case EmailBusinessScenario.CRCToQCQuestion:
|
||||
HangfireJobHelper.AddOrUpdateCronJob<IMediator>(jobId, t => t.Send(new CRCImageQuestionRecurringEvent() { TrialId = trialId }, default), emailCron);
|
||||
HangfireJobHelper.AddOrUpdateCronJob<IMediator>(jobId, t => t.Send(new CRCImageQuestionRecurringEvent() { TrialId = trialId, CultureInfoName = defaultCulture }, default), emailCron);
|
||||
break;
|
||||
case EmailBusinessScenario.QCToCRCImageQuestion:
|
||||
HangfireJobHelper.AddOrUpdateCronJob<IMediator>(jobId, t => t.Send(new QCImageQuestionRecurringEvent() { TrialId = trialId }, default), emailCron);
|
||||
HangfireJobHelper.AddOrUpdateCronJob<IMediator>(jobId, t => t.Send(new QCImageQuestionRecurringEvent() { TrialId = trialId, CultureInfoName = defaultCulture }, default), emailCron);
|
||||
break;
|
||||
//加急阅片 10分钟
|
||||
case EmailBusinessScenario.ExpeditedReading:
|
||||
HangfireJobHelper.AddOrUpdateCronJob<IMediator>(jobId, t => t.Send(new UrgentIRUnReadTaskRecurringEvent() { TrialId = trialId }, default), emailCron);
|
||||
HangfireJobHelper.AddOrUpdateCronJob<IMediator>(jobId, t => t.Send(new UrgentIRUnReadTaskRecurringEvent() { TrialId = trialId, CultureInfoName = defaultCulture }, default), emailCron);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -83,20 +88,25 @@ namespace IRaCIS.Core.Application.Helper
|
||||
|
||||
}
|
||||
|
||||
public static void AddOrUpdateTimingCronJob(string jobId, EmailBusinessScenario businessScenario, string emailCron)
|
||||
public static void AddOrUpdateTimingCronJob(string jobId, EmailBusinessScenario businessScenario, string emailCron, string defaultCulture="")
|
||||
{
|
||||
if (defaultCulture == "")
|
||||
{
|
||||
defaultCulture = CultureInfo.CurrentCulture.Name;
|
||||
}
|
||||
|
||||
switch (businessScenario)
|
||||
{
|
||||
|
||||
case EmailBusinessScenario.GeneralTraining_ExpirationNotification:
|
||||
|
||||
|
||||
HangfireJobHelper.AddOrUpdateCronJob<IMediator>(jobId, t => t.Send(new SystemDocumentErverDayEvent() { }, default), emailCron);
|
||||
HangfireJobHelper.AddOrUpdateCronJob<IMediator>(jobId, t => t.Send(new SystemDocumentErverDayEvent() { CultureInfoName = defaultCulture }, default), emailCron);
|
||||
break;
|
||||
case EmailBusinessScenario.TrialTraining_ExpirationNotification:
|
||||
|
||||
Console.WriteLine("更新项目到期job");
|
||||
HangfireJobHelper.AddOrUpdateCronJob<IMediator>(jobId, t => t.Send(new TrialDocumentErverDayEvent() { }, default), emailCron);
|
||||
HangfireJobHelper.AddOrUpdateCronJob<IMediator>(jobId, t => t.Send(new TrialDocumentErverDayEvent() { CultureInfoName = defaultCulture }, default), emailCron);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -3,8 +3,11 @@ using FellowOakDicom.Imaging;
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.Formats.Jpeg;
|
||||
using SixLabors.ImageSharp.Processing;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
using IRaCIS.Core.Application.Helper;
|
||||
using SixLabors.ImageSharp.Drawing.Processing;
|
||||
|
||||
|
||||
namespace IRaCIS.Core.Application.Helper;
|
||||
|
||||
public static class ImageHelper
|
||||
{
|
||||
@@ -28,7 +31,7 @@ public static class ImageHelper
|
||||
image.Save(fileStorePath, new JpegEncoder());
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -60,6 +63,36 @@ public static class ImageHelper
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public static class ImageMaskHelper
|
||||
{
|
||||
public static async Task MaskAsync(Stream input, Stream output, IEnumerable<MaskRegion> regions)
|
||||
{
|
||||
// 确保输入流从起始位置读取
|
||||
if (input.CanSeek && input.Position != 0)
|
||||
{
|
||||
input.Position = 0;
|
||||
}
|
||||
|
||||
using var image = await Image.LoadAsync(input);
|
||||
|
||||
// 保存原始图片的编码格式
|
||||
var originalFormat = image.Metadata.DecodedImageFormat;
|
||||
|
||||
image.Mutate(ctx =>
|
||||
{
|
||||
foreach (var r in regions)
|
||||
{
|
||||
ctx.Fill(Color.Black, new Rectangle(r.X, r.Y, r.Width, r.Height));
|
||||
}
|
||||
});
|
||||
|
||||
// 使用原始格式保存,保持格式不变
|
||||
await image.SaveAsync(output, originalFormat);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,15 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Security;
|
||||
using System.Net;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
using RestSharp;
|
||||
using Newtonsoft.Json;
|
||||
using IRaCIS.Core.Infrastructure.Extention;
|
||||
using NPOI.SS.Formula.Functions;
|
||||
|
||||
namespace IRaCIS.Core.Application.Helper.OtherTool
|
||||
{
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
using IdentityModel;
|
||||
using Newtonsoft.Json;
|
||||
using RestSharp;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace IRaCIS.Core.Application.Helper.OtherTool;
|
||||
|
||||
|
||||
public class WeComAlert
|
||||
{
|
||||
public string Env { get; set; } = "";
|
||||
public string UserName { get; set; } = "";
|
||||
public string Api { get; set; } = "";
|
||||
public string Message { get; set; } = "";
|
||||
public string? JsonData { get; set; }
|
||||
public string? Stack { get; set; }
|
||||
public bool HasStack => !string.IsNullOrWhiteSpace(Stack);
|
||||
public string[] AtUsers { get; set; } = [];
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static class WeComNotifier
|
||||
{
|
||||
|
||||
public static async Task SendAlertAsync(string webhook, WeComAlert alert)
|
||||
{
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
await Task.CompletedTask;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var client = new RestClient();
|
||||
var request = new RestRequest(webhook, Method.Post);
|
||||
|
||||
var time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
// 处理 @
|
||||
var atText = alert.AtUsers != null && alert.AtUsers.Any()
|
||||
? string.Join("", alert.AtUsers.Select(u => $" <@{u}>"))
|
||||
: "";
|
||||
|
||||
// 处理堆栈
|
||||
var stack = alert.Stack;
|
||||
if (!string.IsNullOrWhiteSpace(stack))
|
||||
{
|
||||
stack = stack.Replace("\n", "\n> ");
|
||||
if (stack.Length > 600)
|
||||
stack = stack[..600] + "...(已截断)";
|
||||
}
|
||||
|
||||
var markdown = $@"## 🚨 系统告警
|
||||
> {atText}
|
||||
> **部署环境:** [{alert.Env}]({alert.Env})
|
||||
> **发生时间:** {time}
|
||||
> **操作人:** {alert.UserName}
|
||||
> **接口地址:** {alert.Api}
|
||||
### ❗ 告警信息
|
||||
<font color=""warning"">{alert.Message}</font>";
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(alert.JsonData))
|
||||
{
|
||||
markdown += $@"
|
||||
> **📦 请求数据(JSON 格式):**
|
||||
```json
|
||||
{alert.JsonData}
|
||||
```";
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(stack))
|
||||
{
|
||||
markdown += $@"
|
||||
### 堆栈信息(部分)
|
||||
<font color=""comment"">{stack}</font>";
|
||||
}
|
||||
|
||||
var payload = new
|
||||
{
|
||||
msgtype = "markdown",
|
||||
markdown = new { content = markdown }
|
||||
};
|
||||
|
||||
request.AddHeader("Content-Type", "application/json");
|
||||
request.AddStringBody(JsonConvert.SerializeObject(payload), DataFormat.Json);
|
||||
|
||||
await client.ExecuteAsync(request);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Logger.Error("企业微信告警发送失败: " + ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<LangVersion>default</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<GenerateDocumentationFile>True</GenerateDocumentationFile>
|
||||
</PropertyGroup>
|
||||
@@ -12,6 +10,7 @@
|
||||
<OutputPath>..\bin\</OutputPath>
|
||||
<DocumentationFile>.\IRaCIS.Core.Application.xml</DocumentationFile>
|
||||
<NoWarn>1701;1702;1591</NoWarn>
|
||||
<NoWarn>$(NoWarn);NU1903</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
@@ -32,39 +31,39 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ClosedXML" Version="0.105.0" />
|
||||
<PackageReference Include="IdentityModel.OidcClient" Version="6.0.0" />
|
||||
<PackageReference Include="AlibabaCloud.SDK.Sts20150401" Version="1.1.6" />
|
||||
<PackageReference Include="AWSSDK.SecurityToken" Version="4.0.1.3" />
|
||||
<PackageReference Include="AlibabaCloud.SDK.Sts20150401" Version="1.2.0" />
|
||||
<PackageReference Include="Aliyun.OSS.SDK.NetCore" Version="2.14.1" />
|
||||
<PackageReference Include="AWSSDK.S3" Version="4.0.4.1" />
|
||||
<PackageReference Include="DocX" Version="4.0.25105.5786" />
|
||||
<PackageReference Include="FreeSpire.Doc" Version="12.2.0" />
|
||||
<PackageReference Include="ExcelDataReader" Version="3.7.0" />
|
||||
<PackageReference Include="ExcelDataReader.DataSet" Version="3.7.0" />
|
||||
<PackageReference Include="DistributedLock.Redis" Version="1.1.0" />
|
||||
<PackageReference Include="DistributedLock.SqlServer" Version="1.0.6" />
|
||||
<PackageReference Include="fo-dicom" Version="5.2.2" />
|
||||
<PackageReference Include="fo-dicom.Imaging.ImageSharp" Version="5.2.2" />
|
||||
<PackageReference Include="fo-dicom.Codecs" Version="5.16.4" />
|
||||
<PackageReference Include="IP2Region.Net" Version="2.0.2" />
|
||||
<PackageReference Include="MailKit" Version="4.11.0" />
|
||||
<PackageReference Include="Masa.Contrib.Service.MinimalAPIs" Version="1.0.0" />
|
||||
<PackageReference Include="MaxMind.GeoIP2" Version="5.3.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.10" />
|
||||
<PackageReference Include="MimeKit" Version="4.11.0" />
|
||||
<PackageReference Include="MiniExcel" Version="1.41.2" />
|
||||
<PackageReference Include="Minio" Version="6.0.3" />
|
||||
<PackageReference Include="AWSSDK.S3" Version="4.0.24" />
|
||||
<PackageReference Include="AWSSDK.SecurityToken" Version="4.0.7" />
|
||||
<PackageReference Include="DocX" Version="5.0.0" />
|
||||
<PackageReference Include="FreeSpire.Doc" Version="14.4.0" />
|
||||
<PackageReference Include="ExcelDataReader" Version="3.8.0" />
|
||||
<PackageReference Include="ExcelDataReader.DataSet" Version="3.8.0" />
|
||||
<PackageReference Include="DistributedLock.Redis" Version="1.1.1" />
|
||||
<PackageReference Include="DistributedLock.SqlServer" Version="1.0.7" />
|
||||
<PackageReference Include="fo-dicom" Version="5.2.6" />
|
||||
<PackageReference Include="fo-dicom.Imaging.ImageSharp" Version="5.2.6" />
|
||||
<PackageReference Include="fo-dicom.Codecs" Version="5.16.7" />
|
||||
<PackageReference Include="IP2Region.Net" Version="3.0.2" />
|
||||
<PackageReference Include="MailKit" Version="4.17.0" />
|
||||
<PackageReference Include="Masa.Contrib.Service.MinimalAPIs" Version="1.1.0" />
|
||||
<PackageReference Include="MaxMind.GeoIP2" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.8" />
|
||||
<PackageReference Include="MimeKit" Version="4.17.0" />
|
||||
<PackageReference Include="MiniExcel" Version="1.44.1" />
|
||||
<PackageReference Include="Minio" Version="7.0.0" />
|
||||
<PackageReference Include="MiniWord" Version="0.9.2" />
|
||||
<PackageReference Include="Serilog.AspNetCore" Version="9.0.0" />
|
||||
<PackageReference Include="My.Extensions.Localization.Json" Version="3.3.0">
|
||||
<PackageReference Include="Serilog.AspNetCore" Version="10.0.0" />
|
||||
<PackageReference Include="My.Extensions.Localization.Json" Version="4.0.0">
|
||||
<TreatAsUsed>true</TreatAsUsed>
|
||||
</PackageReference>
|
||||
<PackageReference Include="NPOI" Version="2.7.4" />
|
||||
<PackageReference Include="Panda.DynamicWebApi" Version="1.2.2" />
|
||||
<PackageReference Include="RestSharp" Version="112.1.0" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.11" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.Filters" Version="8.0.2" />
|
||||
<PackageReference Include="ZiggyCreatures.FusionCache" Version="2.4.0" />
|
||||
<PackageReference Include="RestSharp" Version="114.0.0" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.12" />
|
||||
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="2.1.7" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.Filters" Version="10.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -75,4 +74,8 @@
|
||||
<Folder Include="Service\MinimalApiService\CodeTemplate\FrontTemplate\" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="Service\TrialSiteUser\TrialStatService.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,21 @@
|
||||
using MiniExcelLibs.Attributes;
|
||||
using IRaCIS.Core.Application.Helper;
|
||||
using MiniExcelLibs.Attributes;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace IRaCIS.Core.Application.MassTransit.Command
|
||||
{
|
||||
/// <summary>
|
||||
/// 全量一致性核查
|
||||
/// </summary>
|
||||
public record ConsistenFullCheckCommand
|
||||
{
|
||||
public List<CheckViewModel> ETCList { get; set; } = new List<CheckViewModel>();
|
||||
|
||||
public Guid TrialId { get; set; }
|
||||
|
||||
public Guid InspectionFileId { get; set; }
|
||||
}
|
||||
|
||||
public record ConsistenCheckCommand
|
||||
{
|
||||
public List<CheckViewModel> ETCList { get; set; } = new List<CheckViewModel>();
|
||||
@@ -17,13 +30,26 @@ namespace IRaCIS.Core.Application.MassTransit.Command
|
||||
|
||||
public class CheckDBModel : CheckViewModel
|
||||
{
|
||||
|
||||
|
||||
public Guid SubjectVisitId { get; set; }
|
||||
|
||||
public Guid StudyId { get; set; }
|
||||
|
||||
public string SiteCounty { get; set; }
|
||||
}
|
||||
|
||||
public class FullCheckResult: CheckViewModel
|
||||
{
|
||||
public string LatestScanDateStr { get; set; } = string.Empty;
|
||||
public string Modalitys { get; set; } = string.Empty;
|
||||
public DateTime CheckTime { get; set; }
|
||||
|
||||
public string CheckResult { get; set; }
|
||||
|
||||
|
||||
[DictionaryTranslateAttribute("CheckState")]
|
||||
public CheckStateEnum CheckState { get; set; }
|
||||
}
|
||||
|
||||
|
||||
//[ExcelImporter(/*ImportResultFilter = typeof(ImportResultFilteTest),*/ IsLabelingError = true)]
|
||||
@@ -38,7 +64,7 @@ namespace IRaCIS.Core.Application.MassTransit.Command
|
||||
|
||||
//[Required(ErrorMessage = "受试者筛选号不能为空")]
|
||||
//[ImporterHeader(Name = "Subject ID", AutoTrim = true)]
|
||||
[ExcelColumnName("Subject ID")]
|
||||
[ExcelColumnName("Participant ID")]
|
||||
public string SubjectCode { get; set; } = string.Empty;
|
||||
|
||||
//[Required(ErrorMessage = "访视名称不能为空")]
|
||||
@@ -62,8 +88,13 @@ namespace IRaCIS.Core.Application.MassTransit.Command
|
||||
[ExcelColumnName("Modality")]
|
||||
public string Modality { get; set; } = string.Empty;
|
||||
|
||||
#region 全量一致性核查加入
|
||||
[DictionaryTranslateAttribute("Subject_Visit_Status")]
|
||||
public SubjectStatus SubjectStatus { get; set; }
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
|
||||
@@ -21,12 +21,14 @@ public static class CommonEmailHelper
|
||||
/// <param name="configInfo"></param>
|
||||
/// <param name="messageToSend"></param>
|
||||
/// <param name="emailFunc"></param>
|
||||
/// <param name="userWorkLanguage"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="BusinessValidationFailedException"></exception>
|
||||
public static async Task<EmailNoticeConfig> GetEmailSubejctAndHtmlInfoAndBuildAsync(EmailNoticeConfig configInfo, MimeMessage messageToSend,
|
||||
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailFunc)
|
||||
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailFunc, UserWorkLanguage? userWorkLanguage = null)
|
||||
{
|
||||
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
|
||||
|
||||
bool isEn_US = userWorkLanguage.HasValue ? userWorkLanguage.Value == UserWorkLanguage.US : CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
|
||||
|
||||
var (topicStr, htmlBodyStr) = isEn_US ? (configInfo.EmailTopic, configInfo.EmailHtmlContent) : (configInfo.EmailTopicCN, configInfo.EmailHtmlContentCN);
|
||||
|
||||
@@ -60,13 +62,14 @@ Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr
|
||||
/// <param name="configInfo"></param>
|
||||
/// <param name="messageToSend"></param>
|
||||
/// <param name="emailFunc"></param>
|
||||
/// <param name="userWorkLanguage"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="BusinessValidationFailedException"></exception>
|
||||
public static async Task<TrialEmailNoticeConfig> GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(TrialEmailNoticeConfig configInfo, MimeMessage messageToSend,
|
||||
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailFunc)
|
||||
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailFunc, UserWorkLanguage? userWorkLanguage = null)
|
||||
{
|
||||
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
|
||||
|
||||
bool isEn_US = userWorkLanguage.HasValue ? userWorkLanguage.Value == UserWorkLanguage.US : CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
|
||||
|
||||
var (topicStr, htmlBodyStr) = isEn_US ? (configInfo.EmailTopic, configInfo.EmailHtmlContent) : (configInfo.EmailTopicCN, configInfo.EmailHtmlContentCN);
|
||||
|
||||
@@ -94,12 +97,20 @@ Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr
|
||||
return configInfo;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public static string ReplaceCompanyName(SystemEmailSendConfig _systemEmailConfig, string needDealtxt)
|
||||
public static string ReplacePlatformName(SystemEmailSendConfig _systemEmailConfig, string needDealtxt, UserWorkLanguage? userWorkLanguage = null)
|
||||
{
|
||||
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
|
||||
var isEn_US = userWorkLanguage.HasValue ? userWorkLanguage.Value == UserWorkLanguage.US : CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
|
||||
|
||||
var platformName = isEn_US ? _systemEmailConfig.PlatformName : _systemEmailConfig.PlatformNameCN;
|
||||
|
||||
var str = needDealtxt.Replace("{platformName}", platformName);
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
public static string ReplaceCompanyName(SystemEmailSendConfig _systemEmailConfig, string needDealtxt, UserWorkLanguage? userWorkLanguage = null)
|
||||
{
|
||||
var isEn_US = userWorkLanguage.HasValue ? userWorkLanguage.Value == UserWorkLanguage.US : CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
|
||||
|
||||
var str = needDealtxt.Replace("{company}", isEn_US ? _systemEmailConfig.CompanyName : _systemEmailConfig.CompanyNameCN)
|
||||
.Replace("{company abbreviation}", isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN);
|
||||
|
||||
@@ -1,53 +1,52 @@
|
||||
using AutoMapper;
|
||||
using IRaCIS.Application.Contracts;
|
||||
using IRaCIS.Application.Interfaces;
|
||||
using IRaCIS.Core.API._ServiceExtensions.NewtonsoftJson;
|
||||
using IRaCIS.Core.Application.Contracts;
|
||||
using IRaCIS.Core.Application.Contracts.DTO;
|
||||
using IRaCIS.Core.Application.Helper;
|
||||
using IRaCIS.Core.Application.MassTransit.Command;
|
||||
using IRaCIS.Core.Application.ViewModel;
|
||||
using IRaCIS.Core.Domain.Models;
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using MassTransit;
|
||||
using MaxMind.GeoIP2.Model;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Newtonsoft.Json;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace IRaCIS.Core.Application.MassTransit.Consumer
|
||||
{
|
||||
public class ConsistencyCheckConsumer : IConsumer<ConsistenCheckCommand>
|
||||
public class ConsistencyCheckConsumer(
|
||||
IRepository<DicomStudy> _studyRepository,
|
||||
IUserInfo _userInfo,
|
||||
IRepository<Subject> _subjectRepository,
|
||||
IRepository<NoneDicomStudy> _noneDicomStudyRepository,
|
||||
IRepository<SubjectVisit> _subjectVisitRepository,
|
||||
IRepository<Dictionary> _dictionaryRepository,
|
||||
IOSSService _oSSService,
|
||||
IMapper _mapper,
|
||||
IDictionaryService _dictionaryService,
|
||||
IRepository<CommonDocument> _commonDocumentRepository,
|
||||
IStringLocalizer _localizer,
|
||||
IWebHostEnvironment _hostEnvironment,
|
||||
IRepository<InspectionFile> _inspectionFileRepository,
|
||||
IOptionsMonitor<SystemEmailSendConfig> _systemEmailSendConfig
|
||||
) : IConsumer<ConsistenCheckCommand>, IConsumer<ConsistenFullCheckCommand>
|
||||
{
|
||||
|
||||
private readonly IRepository<DicomStudy> _studyRepository;
|
||||
private readonly IUserInfo _userInfo;
|
||||
private readonly IRepository<Subject> _subjectRepository;
|
||||
private readonly IRepository<SubjectVisit> _subjectVisitRepository;
|
||||
private readonly IRepository<TrialSite> _trialSiteRepository;
|
||||
private readonly IMapper _mapper;
|
||||
private readonly IRepository<NoneDicomStudy> _noneDicomStudyRepository;
|
||||
public IStringLocalizer _localizer { get; set; }
|
||||
|
||||
private readonly SystemEmailSendConfig _systemEmailConfig;
|
||||
|
||||
private readonly IRepository<Dictionary> _dictionaryRepository;
|
||||
/// <summary>
|
||||
/// 构造函数注入
|
||||
/// </summary>
|
||||
private readonly SystemEmailSendConfig _systemEmailConfig = _systemEmailSendConfig.CurrentValue;
|
||||
|
||||
|
||||
public ConsistencyCheckConsumer(IRepository<DicomStudy> studyRepository, IUserInfo userInfo,
|
||||
IRepository<Subject> subjectRepository, IRepository<SubjectVisit> subjectVisitRepository,
|
||||
IRepository<TrialSite> trialSiteRepository, IRepository<NoneDicomStudy> noneDicomStudyRepository,
|
||||
IMapper mapper, IStringLocalizer localizer, IOptionsMonitor<SystemEmailSendConfig> systemEmailConfig, IRepository<Dictionary> dictionaryRepository)
|
||||
{
|
||||
_noneDicomStudyRepository = noneDicomStudyRepository;
|
||||
_studyRepository = studyRepository;
|
||||
_userInfo = userInfo;
|
||||
_subjectRepository = subjectRepository;
|
||||
_subjectVisitRepository = subjectVisitRepository;
|
||||
_trialSiteRepository = trialSiteRepository;
|
||||
_mapper = mapper;
|
||||
_localizer = localizer;
|
||||
|
||||
_systemEmailConfig = systemEmailConfig.CurrentValue;
|
||||
_dictionaryRepository = dictionaryRepository;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -67,8 +66,8 @@ namespace IRaCIS.Core.Application.MassTransit.Consumer
|
||||
|
||||
//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 == trialId && x.Subject.IsSubjectQuit == false &&
|
||||
(x.CheckState == CheckStateEnum.ToCheck && x.AuditState == AuditStateEnum.QCPassed || (x.CheckState == CheckStateEnum.CVIng && x.AuditState == AuditStateEnum.QCPassed));
|
||||
Expression<Func<SubjectVisit, bool>> subjectVisitLambda = x => x.TrialId == trialId && x.Subject.IsSubjectQuit == false && x.AuditState == AuditStateEnum.QCPassed &&
|
||||
(x.CheckState == CheckStateEnum.ToCheck || x.CheckState == CheckStateEnum.CVIng);
|
||||
|
||||
var dicomQuery = from sv in _subjectVisitRepository.Where(subjectVisitLambda)
|
||||
join subject in _subjectRepository.AsQueryable() on sv.SubjectId equals subject.Id
|
||||
@@ -82,6 +81,8 @@ namespace IRaCIS.Core.Application.MassTransit.Consumer
|
||||
Modality = study.ModalityForEdit,
|
||||
SubjectCode = subject.Code,
|
||||
VisitName = sv.VisitName,
|
||||
|
||||
SiteCounty = sv.TrialSite.Country,
|
||||
};
|
||||
|
||||
var noneDicomQuey = from sv in _subjectVisitRepository.Where(subjectVisitLambda)
|
||||
@@ -96,6 +97,8 @@ namespace IRaCIS.Core.Application.MassTransit.Consumer
|
||||
Modality = noneDicomStudy.Modality,
|
||||
SubjectCode = subject.Code,
|
||||
VisitName = sv.VisitName,
|
||||
|
||||
SiteCounty = sv.TrialSite.Country,
|
||||
};
|
||||
|
||||
var dbList = (await dicomQuery.ToListAsync()).Union(await noneDicomQuey.ToListAsync()).ToList();
|
||||
@@ -117,12 +120,27 @@ namespace IRaCIS.Core.Application.MassTransit.Consumer
|
||||
foreach (var sv in svExcelGroup)
|
||||
{
|
||||
|
||||
//Excel 的数据 在IRC 中可以找到该访视
|
||||
//Excel 的数据 在IRC 中可以找到该访视(一致性核查中,或者待核查)
|
||||
if (dbCheckList.Any(t => t.SubjectCode == sv.SubjectCode && t.SiteCode == sv.SiteCode && t.VisitName == sv.VisitName))
|
||||
{
|
||||
|
||||
var dbVisitStudyList = dbList.Where(t => t.SubjectCode == sv.SubjectCode && t.SiteCode == sv.SiteCode && t.VisitName == sv.VisitName).ToList();
|
||||
|
||||
//设置当前中心语言
|
||||
|
||||
var isEn_us = dbVisitStudyList.First().SiteCounty == StaticData.SiteCountry.US;
|
||||
|
||||
var cultureInfoName = StaticData.CultureInfo.en_US;
|
||||
|
||||
if (isEn_us == false)
|
||||
{
|
||||
cultureInfoName = StaticData.CultureInfo.zh_CN;
|
||||
}
|
||||
|
||||
CultureInfo.CurrentCulture = new CultureInfo(cultureInfoName);
|
||||
CultureInfo.CurrentUICulture = new CultureInfo(cultureInfoName);
|
||||
|
||||
|
||||
//找到etc 当前visit site 和subject 一致的检查列表
|
||||
var etcVisitStudyList = etcList.Where(t => t.SubjectCode == sv.SubjectCode && t.SiteCode == sv.SiteCode && t.VisitName == sv.VisitName).ToList();
|
||||
|
||||
@@ -201,7 +219,7 @@ namespace IRaCIS.Core.Application.MassTransit.Consumer
|
||||
var dbExceptExcel = dbVisitStudyList.Except(etcVisitStudyList);
|
||||
|
||||
// excel 存在
|
||||
var excelExceptDB = etcVisitStudyList.Except(dbCheckList);
|
||||
var excelExceptDB = etcVisitStudyList.Except(dbVisitStudyList);
|
||||
|
||||
//ETC 和系统的完全一致 两者没有差别
|
||||
if (dbExceptExcel.Count() == 0 && excelExceptDB.Count() == 0)
|
||||
@@ -301,12 +319,169 @@ namespace IRaCIS.Core.Application.MassTransit.Consumer
|
||||
}
|
||||
await _subjectVisitRepository.SaveChangesAsync();
|
||||
|
||||
//await context.RespondAsync<ConsistenCheckResult>(new
|
||||
//{
|
||||
|
||||
//});
|
||||
}
|
||||
|
||||
public async Task Consume(ConsumeContext<ConsistenFullCheckCommand> context)
|
||||
{
|
||||
var inspectionFileId = context.Message.InspectionFileId;
|
||||
var trialId = context.Message.TrialId;
|
||||
|
||||
//系统定义MRI 到底是MR 还是MRI
|
||||
var mriModality = await _dictionaryRepository.Where(t => t.Parent.Code == "Modality").Where(t => t.Code == "MRI").Select(t => t.Value).FirstNotNullAsync();
|
||||
|
||||
//处理Excel大小写
|
||||
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>> subjectVisitLambda = x => x.TrialId == trialId /*&& x.Subject.IsSubjectQuit == false*/ && x.AuditState == AuditStateEnum.QCPassed;
|
||||
|
||||
var dicomQuery = from sv in _subjectVisitRepository.Where(subjectVisitLambda)
|
||||
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()
|
||||
{
|
||||
SubjectStatus = sv.Subject.Status,
|
||||
SubjectVisitId = sv.Id,
|
||||
SiteCode = sv.TrialSite.TrialSiteCode,
|
||||
StudyDate = study.StudyTime == null ? string.Empty : ((DateTime)study.StudyTime).ToString("yyyy-MM-dd"),
|
||||
StudyId = study.Id,
|
||||
Modality = study.ModalityForEdit,
|
||||
SubjectCode = subject.Code,
|
||||
VisitName = sv.VisitName,
|
||||
};
|
||||
|
||||
var noneDicomQuey = from sv in _subjectVisitRepository.Where(subjectVisitLambda)
|
||||
join subject in _subjectRepository.AsQueryable() on sv.SubjectId equals subject.Id
|
||||
join noneDicomStudy in _noneDicomStudyRepository.Where(t => t.FileCount > 0) on sv.Id equals noneDicomStudy.SubjectVisitId
|
||||
select new CheckDBModel()
|
||||
{
|
||||
SubjectStatus = sv.Subject.Status,
|
||||
SubjectVisitId = sv.Id,
|
||||
SiteCode = sv.TrialSite.TrialSiteCode,
|
||||
StudyDate = noneDicomStudy.ImageDate.ToString("yyyy-MM-dd"),
|
||||
StudyId = noneDicomStudy.Id,
|
||||
Modality = noneDicomStudy.Modality,
|
||||
SubjectCode = subject.Code,
|
||||
VisitName = sv.VisitName,
|
||||
};
|
||||
|
||||
var dbList = (await dicomQuery.ToListAsync()).Union(await noneDicomQuey.ToListAsync()).ToList();
|
||||
|
||||
//处理数据库 大小写
|
||||
dbList.ForEach(t => { t.Modality = t.Modality.ToUpper().Trim(); t.SiteCode = t.SiteCode.ToUpper().Trim(); t.VisitName = t.VisitName.ToUpper().Trim(); t.SubjectCode = t.SubjectCode.ToUpper().Trim(); });
|
||||
|
||||
var dbCheckList = _mapper.Map<List<CheckViewModel>>(dbList);
|
||||
|
||||
|
||||
//按照Excel数据访视分组 按照数据库的数据 一个个的访视对比
|
||||
var svExcelGroup = etcList.GroupBy(t => new { t.SiteCode, t.SubjectCode, t.VisitName })
|
||||
.Select(g => new { g.Key.SubjectCode, g.Key.VisitName, g.Key.SiteCode, ExcelStudyList = g.ToList() }).ToList();
|
||||
|
||||
var fullCheckResultList = new List<FullCheckResult>();
|
||||
|
||||
|
||||
foreach (var sv in svExcelGroup)
|
||||
{
|
||||
|
||||
//Excel 的数据 在IRC 中可以找到该访视(一致性核查中,或者待核查)
|
||||
if (dbCheckList.Any(t => t.SubjectCode == sv.SubjectCode && t.SiteCode == sv.SiteCode && t.VisitName == sv.VisitName))
|
||||
{
|
||||
|
||||
var dbVisitStudyList = dbList.Where(t => t.SubjectCode == sv.SubjectCode && t.SiteCode == sv.SiteCode && t.VisitName == sv.VisitName).ToList();
|
||||
|
||||
//找到etc 当前visit site 和subject 一致的检查列表
|
||||
var etcVisitStudyList = etcList.Where(t => t.SubjectCode == sv.SubjectCode && t.SiteCode == sv.SiteCode && t.VisitName == sv.VisitName).ToList();
|
||||
|
||||
|
||||
//以我们系统数据库为准 ,判断是MR 还是MRI, 把Excel 里的MR 或者MRI 处理成一致,MR MRI 是一致的
|
||||
foreach (var item in etcVisitStudyList)
|
||||
{
|
||||
if (item.Modality == "MR" || item.Modality == "MRI")
|
||||
{
|
||||
item.Modality = mriModality;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//etc 和数据库 并集
|
||||
var unionList = dbVisitStudyList.Union(etcVisitStudyList);
|
||||
// 数据库存在
|
||||
var dbExceptExcel = dbVisitStudyList.Except(etcVisitStudyList);
|
||||
|
||||
// excel 存在
|
||||
var excelExceptDB = etcVisitStudyList.Except(dbVisitStudyList);
|
||||
|
||||
var dbCurrentVisitFirst = dbVisitStudyList.First();
|
||||
|
||||
//ETC 和系统的完全一致 两者没有差别
|
||||
if (dbExceptExcel.Count() == 0 && excelExceptDB.Count() == 0)
|
||||
{
|
||||
|
||||
|
||||
//每个受试者每个访视加一条记录
|
||||
fullCheckResultList.Add(new FullCheckResult()
|
||||
{
|
||||
SubjectStatus = dbCurrentVisitFirst.SubjectStatus,
|
||||
CheckTime = DateTime.Now,
|
||||
CheckState = CheckStateEnum.CVPassed,
|
||||
SiteCode = dbCurrentVisitFirst.SiteCode,
|
||||
SubjectCode = dbCurrentVisitFirst.SubjectCode,
|
||||
VisitName = dbCurrentVisitFirst.VisitName,
|
||||
Modalitys = string.Join('、', dbVisitStudyList.Select(t => t.Modality)),
|
||||
LatestScanDateStr = dbVisitStudyList.Select(t => t.StudyDate).MaxBy(d => DateTime.Parse(d)) ?? ""
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
var checkResult =
|
||||
String.Join(" | ", dbExceptExcel.Select(t => $"{_localizer["ConsistencyVerification_EdcL", t.StudyDate, t.Modality/*, _systemEmailConfig.SystemShortName*/]}")) + " | "
|
||||
+ String.Join(" | ", excelExceptDB.Select(t => $"{_localizer["ConsistencyVerification_IrcLi", t.StudyDate, t.Modality, _systemEmailConfig.SystemShortName]}"));
|
||||
|
||||
//每个受试者每个访视加一条记录
|
||||
fullCheckResultList.Add(new FullCheckResult()
|
||||
{
|
||||
SubjectStatus = dbCurrentVisitFirst.SubjectStatus,
|
||||
CheckState = CheckStateEnum.None,
|
||||
CheckTime = DateTime.Now,
|
||||
SiteCode = dbCurrentVisitFirst.SiteCode,
|
||||
SubjectCode = dbCurrentVisitFirst.SubjectCode,
|
||||
VisitName = dbCurrentVisitFirst.VisitName,
|
||||
Modalitys = string.Join('、', dbVisitStudyList.Select(t => t.Modality)),
|
||||
LatestScanDateStr = dbVisitStudyList.Select(t => t.StudyDate).MaxBy(d => DateTime.Parse(d)) ?? "",
|
||||
CheckResult = checkResult
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//导到Excel 上传oss 回更记录状态
|
||||
|
||||
var list = fullCheckResultList;
|
||||
|
||||
var exportInfo = new ExcelExportInfo();
|
||||
|
||||
exportInfo.List = ExportExcelConverterDate.ConvertToClientTimeInObject(list, _userInfo.TimeZoneId);
|
||||
exportInfo.CurrentTime = ExportExcelConverterDate.DateTimeInternationalToString(DateTime.Now, _userInfo.TimeZoneId);
|
||||
|
||||
var fileStreamResult = (FileStreamResult)await ExcelExportHelper.DataExportAsync(StaticData.Export.TrialConsistentFUllCheckList_Export, exportInfo, exportInfo.TrialCode, _commonDocumentRepository, _hostEnvironment, _dictionaryService, typeof(FullCheckResult));
|
||||
|
||||
var ossRelativePath = await _oSSService.UploadToOSSAsync(fileStreamResult.FileStream, $"{trialId.ToString()}/InspectionUpload/DataReconciliation", "DataReconciliation", uploadInfo: new FileUploadRecordAddOrEdit() { TrialId = trialId, BatchDataType = BatchDataType.DataReconciliation });
|
||||
|
||||
|
||||
//var add = await _inspectionFileRepository.FindAsync(inspectionFileId);
|
||||
//add.CheckState = EDCCheckState.Success;
|
||||
//add.ResultPath = ossRelativePath;
|
||||
|
||||
//更新
|
||||
await _inspectionFileRepository.BatchUpdateNoTrackingAsync(t => t.Id == inspectionFileId, u => new InspectionFile() { CheckState = EDCCheckState.Success, ResultPath = ossRelativePath });
|
||||
|
||||
await _subjectVisitRepository.SaveChangesAsync();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,5 +27,16 @@ namespace IRaCIS.Core.Application.MassTransit.Consumer.Dto
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
public class SendEmailUserDto
|
||||
{
|
||||
public UserTypeEnum UserTypeEnum { get; set; }
|
||||
|
||||
public string FullName { get; set; }
|
||||
|
||||
public UserWorkLanguage UserWorkLanguage { get; set; }
|
||||
|
||||
public string EMail { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -256,7 +256,7 @@ public class ImageConsumer(
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送影像重传相关邮件的通用方法
|
||||
/// 发送影像重传相关邮件的通用方法 已经处理项目
|
||||
/// </summary>
|
||||
private async Task SendImageReuploadEmail(SendImageReuploadEmailInDto inDto)
|
||||
{
|
||||
@@ -273,15 +273,30 @@ public class ImageConsumer(
|
||||
};
|
||||
|
||||
// 根据不同场景获取不同角色的用户 先排除CRC和CRA
|
||||
var trialUserList = await _trialUseRoleRepository.Where(x => x.TrialId == inDto.TrialId && !x.TrialUser.IsDeleted && !filterUserTypeList.Contains(x.UserRole.UserTypeEnum) ).Include(x => x.UserRole).Select(x => x.UserRole).ToListAsync();
|
||||
var trialUserList = await _trialUseRoleRepository.Where(x => x.TrialId == inDto.TrialId && !x.TrialUser.IsDeleted && !filterUserTypeList.Contains(x.UserRole.UserTypeEnum) ).Include(x => x.UserRole).Select(x => x.UserRole)
|
||||
.Select(x => new SendEmailUserDto()
|
||||
{
|
||||
UserTypeEnum = x.UserTypeEnum,
|
||||
FullName = x.FullName,
|
||||
UserWorkLanguage = x.IdentityUser.UserWorkLanguage,
|
||||
EMail=x.EMail,
|
||||
})
|
||||
.ToListAsync();
|
||||
|
||||
// CRC和CRA单独取
|
||||
var crcAndcraUserList = await _trialSiteRepository.Where(x => x.Id == inDto.SubjectVisit.TrialSiteId).SelectMany(x => x.CRCUserList.Select(y => y.UserRole)).ToListAsync();
|
||||
var crcAndcraUserList = await _trialSiteRepository.Where(x => x.Id == inDto.SubjectVisit.TrialSiteId).SelectMany(x => x.CRCUserList.Select(y => y.UserRole))
|
||||
.Select(x => new SendEmailUserDto()
|
||||
{
|
||||
UserTypeEnum = x.UserTypeEnum,
|
||||
FullName = x.FullName,
|
||||
UserWorkLanguage = x.IdentityUser.UserWorkLanguage,
|
||||
EMail = x.EMail,
|
||||
}).ToListAsync();
|
||||
trialUserList.AddRange(crcAndcraUserList);
|
||||
|
||||
// 根据场景确定收件人
|
||||
List<UserRole> toUserList = new List<UserRole>();
|
||||
List<UserRole> ccUserList = new List<UserRole>();
|
||||
List<SendEmailUserDto> toUserList = new List<SendEmailUserDto>();
|
||||
List<SendEmailUserDto> ccUserList = new List<SendEmailUserDto>();
|
||||
|
||||
var emailNoticeUserList = await _emailNoticeUserTypeRepository.Where(x => x.EmailNoticeConfigId == inDto.EmailNoticeConfig.Id).ToListAsync();
|
||||
var userTypeEnumList = emailNoticeUserList.Select(x => x.UserType).ToList();
|
||||
@@ -334,40 +349,55 @@ public class ImageConsumer(
|
||||
DictionaryList = dictionaryDtos
|
||||
});
|
||||
|
||||
foreach (var userinfo in toUserList)
|
||||
|
||||
var userWorkLanguageList = toUserList.Select(x => x.UserWorkLanguage).Distinct().ToList();
|
||||
|
||||
foreach (var workLanguage in userWorkLanguageList)
|
||||
{
|
||||
|
||||
var userinfoList = toUserList.Where(x => x.UserWorkLanguage == workLanguage).ToList();
|
||||
var messageToSend = new MimeMessage();
|
||||
// 发件地址
|
||||
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
|
||||
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
|
||||
|
||||
foreach (var userinfo in userinfoList)
|
||||
{
|
||||
messageToSend.To.Add(new MailboxAddress(userinfo.FullName, userinfo.EMail));
|
||||
}
|
||||
|
||||
|
||||
// 添加抄送
|
||||
foreach (var ccUser in ccUserList)
|
||||
{
|
||||
messageToSend.Cc.Add(new MailboxAddress(String.Empty, ccUser.EMail));
|
||||
messageToSend.Cc.Add(new MailboxAddress(ccUser.FullName, ccUser.EMail));
|
||||
}
|
||||
|
||||
var userNames = userinfoList.Select(x => x.FullName).ToList();
|
||||
|
||||
// 格式化邮件内容
|
||||
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
|
||||
{
|
||||
var subjectCode = inDto.SubjectVisit.Subject.Code;
|
||||
var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo, subjectCode, inDto.SubjectVisit.VisitName);
|
||||
var htmlBodyStr = string.Format(
|
||||
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
|
||||
userinfo.FullName, // 用户名 {0}
|
||||
trialInfo.ExperimentName, // 项目 {1}
|
||||
subjectCode, // 受试者 {2}
|
||||
inDto.SubjectVisit.VisitName, // 访视 {3}
|
||||
dictionValue[0], // 是否加急 {4}
|
||||
dictionValue[1], // 审批结果 {5}
|
||||
_systemEmailConfig.SiteUrl // 链接 {6}
|
||||
);
|
||||
{
|
||||
var subjectCode = inDto.SubjectVisit.Subject.Code;
|
||||
var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo, subjectCode, inDto.SubjectVisit.VisitName);
|
||||
var htmlBodyStr = string.Format(
|
||||
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr, workLanguage),
|
||||
string.Join(',', userNames), // 用户名 {0}
|
||||
trialInfo.ExperimentName, // 项目 {1}
|
||||
subjectCode, // 受试者 {2}
|
||||
inDto.SubjectVisit.VisitName, // 访视 {3}
|
||||
dictionValue[0], // 是否加急 {4}
|
||||
dictionValue[1], // 审批结果 {5}
|
||||
_systemEmailConfig.SiteUrl // 链接 {6}
|
||||
);
|
||||
|
||||
return (topicStr, htmlBodyStr);
|
||||
};
|
||||
return (topicStr, htmlBodyStr);
|
||||
};
|
||||
|
||||
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(inDto.EmailNoticeConfig, messageToSend, emailConfigFunc);
|
||||
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
|
||||
|
||||
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(inDto.EmailNoticeConfig, messageToSend, emailConfigFunc, workLanguage);
|
||||
|
||||
await SendEmailHelper.SendTrialEmailAsync(messageToSend, trialInfo);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -434,7 +464,7 @@ public class ImageConsumer(
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送阅片人筛选相关邮件的通用方法
|
||||
/// 发送阅片人筛选相关邮件的通用方法 已经处理项目
|
||||
/// </summary>
|
||||
private async Task SendReviewerSelectionEmail(EmailNoticeConfig emailNoticeConfig, Guid trialId, List<Guid> enrollIdList)
|
||||
{
|
||||
@@ -444,11 +474,19 @@ public class ImageConsumer(
|
||||
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == trialId);
|
||||
|
||||
// 根据不同场景获取不同角色的用户
|
||||
var trialUser = await _trialUseRoleRepository.Where(x => x.TrialId == trialId && !x.TrialUser.IsDeleted).Include(x => x.UserRole).Select(x => x.UserRole).ToListAsync();
|
||||
var trialUser = await _trialUseRoleRepository.Where(x => x.TrialId == trialId && !x.TrialUser.IsDeleted).Include(x => x.UserRole).Select(x => x.UserRole)
|
||||
.Select(x => new SendEmailUserDto()
|
||||
{
|
||||
UserTypeEnum = x.UserTypeEnum,
|
||||
FullName = x.FullName,
|
||||
UserWorkLanguage = x.IdentityUser.UserWorkLanguage,
|
||||
EMail = x.EMail,
|
||||
})
|
||||
.ToListAsync();
|
||||
|
||||
// 根据场景确定收件人
|
||||
List<UserRole> toUserList = new List<UserRole>();
|
||||
List<UserRole> ccUserList = new List<UserRole>();
|
||||
List<SendEmailUserDto> toUserList = new List<SendEmailUserDto>();
|
||||
List<SendEmailUserDto> ccUserList = new List<SendEmailUserDto>();
|
||||
|
||||
var emailNoticeUserList = await _emailNoticeUserTypeRepository.Where(x => x.EmailNoticeConfigId == emailNoticeConfig.Id).ToListAsync();
|
||||
|
||||
@@ -467,26 +505,35 @@ public class ImageConsumer(
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var userinfo in toUserList)
|
||||
var userWorkLanguageList = toUserList.Select(x => x.UserWorkLanguage).Distinct().ToList();
|
||||
|
||||
foreach (var workLanguage in userWorkLanguageList)
|
||||
{
|
||||
var userinfoList = toUserList.Where(x => x.UserWorkLanguage == workLanguage).ToList();
|
||||
var messageToSend = new MimeMessage();
|
||||
// 发件地址
|
||||
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
|
||||
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
|
||||
|
||||
foreach (var userinfo in userinfoList)
|
||||
{
|
||||
messageToSend.To.Add(new MailboxAddress(userinfo.FullName, userinfo.EMail));
|
||||
}
|
||||
|
||||
// 添加抄送
|
||||
foreach (var ccUser in ccUserList)
|
||||
{
|
||||
messageToSend.Cc.Add(new MailboxAddress(String.Empty, ccUser.EMail));
|
||||
messageToSend.Cc.Add(new MailboxAddress(ccUser.FullName, ccUser.EMail));
|
||||
}
|
||||
|
||||
var userNames = userinfoList.Select(x => x.FullName).ToList();
|
||||
|
||||
// 格式化邮件内容
|
||||
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
|
||||
{
|
||||
var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo);
|
||||
var htmlBodyStr = string.Format(
|
||||
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
|
||||
userinfo.FullName, // 用户名 {0}
|
||||
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr, workLanguage),
|
||||
string.Join(',', userNames), // 用户名 {0}
|
||||
trialInfo.ExperimentName, // 项目 {1}
|
||||
"", // 受试者 {2} - 阅片人筛选不涉及受试者
|
||||
"", // 访视 {3} - 阅片人筛选不涉及访视
|
||||
@@ -498,9 +545,9 @@ public class ImageConsumer(
|
||||
return (topicStr, htmlBodyStr);
|
||||
};
|
||||
|
||||
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(emailNoticeConfig, messageToSend, emailConfigFunc);
|
||||
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(emailNoticeConfig, messageToSend, emailConfigFunc, workLanguage);
|
||||
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
|
||||
await SendEmailHelper.SendTrialEmailAsync(messageToSend, trialInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using IRaCIS.Core.Application.Helper;
|
||||
using IRaCIS.Core.Application.Helper;
|
||||
using IRaCIS.Core.Application.MassTransit.Command;
|
||||
using IRaCIS.Core.Application.MassTransit.Consumer.Dto;
|
||||
using IRaCIS.Core.Domain;
|
||||
using IRaCIS.Core.Domain.BaseModel;
|
||||
using IRaCIS.Core.Domain.Models;
|
||||
@@ -68,7 +69,7 @@ public class UrgentMedicalReviewAddedEventConsumer(
|
||||
|
||||
|
||||
var criterion = await _readingQuestionCriterionTrialRepository.FirstOrDefaultAsync(x => x.Id == medicalReview.VisitTask.TrialReadingCriterionId);
|
||||
var userinfo = await _userRoleRepository.Where(x => x.Id == medicalReview.MedicalManagerUserId).FirstOrDefaultAsync();
|
||||
var userinfo = await _userRoleRepository.Where(x => x.Id == medicalReview.MedicalManagerUserId).Include(x => x.IdentityUser).FirstOrDefaultAsync();
|
||||
|
||||
var taskInfo = await _visitTaskRepository.Where(x => x.Id == medicalReview.VisitTaskId).Include(x => x.SourceSubjectVisit).Include(x => x.ReadModule).Include(x => x.Subject).FirstNotNullAsync();
|
||||
|
||||
@@ -78,22 +79,23 @@ public class UrgentMedicalReviewAddedEventConsumer(
|
||||
|
||||
var subjectVisit = await _subjectVisitRepository.Where(x => x.Id == visitid).FirstOrDefaultAsync();
|
||||
|
||||
|
||||
var workLanguage = userinfo.IdentityUser?.UserWorkLanguage;
|
||||
var userIsEn_US = workLanguage.HasValue ? workLanguage.Value == UserWorkLanguage.US : isEn_US;
|
||||
|
||||
var messageToSend = new MimeMessage();
|
||||
//发件地址
|
||||
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
|
||||
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
|
||||
messageToSend.To.Add(new MailboxAddress(userinfo.FullName, userinfo.EMail));
|
||||
|
||||
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == medicalReview.TrialId);
|
||||
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
|
||||
var companyName = userIsEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
|
||||
|
||||
|
||||
var dictionValue = await CommonEmailHelper.TranslationDictionary(new TranslationDictionaryDto()
|
||||
{
|
||||
|
||||
DictionaryRepository = _dictionaryRepository,
|
||||
IsEn_US = isEn_US,
|
||||
IsEn_US = userIsEn_US,
|
||||
|
||||
|
||||
DictionaryList = new List<DictionaryDto>()
|
||||
@@ -110,7 +112,7 @@ public class UrgentMedicalReviewAddedEventConsumer(
|
||||
var subjectName = taskInfo.BlindSubjectCode.IsNullOrEmpty() ? taskInfo.Subject.Code : taskInfo.BlindSubjectCode;
|
||||
var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo, subjectName, taskInfo.TaskBlindName);
|
||||
var htmlBodyStr = string.Format(
|
||||
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
|
||||
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr, workLanguage),
|
||||
userinfo.FullName, // 用户名 {0}
|
||||
trialInfo.ExperimentName, // 项目 {1}
|
||||
criterion.CriterionName, // 阅片标准 {2}
|
||||
@@ -126,9 +128,9 @@ public class UrgentMedicalReviewAddedEventConsumer(
|
||||
return (topicStr, htmlBodyStr);
|
||||
};
|
||||
|
||||
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc);
|
||||
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc, workLanguage);
|
||||
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
|
||||
await SendEmailHelper.SendTrialEmailAsync(messageToSend, trialInfo);
|
||||
|
||||
}
|
||||
|
||||
@@ -177,7 +179,7 @@ public class UrgentIRRepliedMedicalReviewConsumer(
|
||||
|
||||
|
||||
var criterion = await _readingQuestionCriterionTrialRepository.FirstOrDefaultAsync(x => x.Id == medicalReview.VisitTask.TrialReadingCriterionId);
|
||||
var userinfo = await _userRoleRepository.Where(x => x.Id == medicalReview.MedicalManagerUserId).FirstOrDefaultAsync();
|
||||
var userinfo = await _userRoleRepository.Where(x => x.Id == medicalReview.MedicalManagerUserId).Include(x => x.IdentityUser).FirstOrDefaultAsync();
|
||||
|
||||
var taskInfo = await _visitTaskRepository.Where(x => x.Id == medicalReview.VisitTaskId).Include(x => x.SourceSubjectVisit).Include(x => x.ReadModule).Include(x => x.Subject).FirstNotNullAsync();
|
||||
|
||||
@@ -187,22 +189,24 @@ public class UrgentIRRepliedMedicalReviewConsumer(
|
||||
|
||||
var subjectVisit = await _subjectVisitRepository.Where(x => x.Id == visitid).FirstOrDefaultAsync();
|
||||
|
||||
var workLanguage = userinfo.IdentityUser?.UserWorkLanguage;
|
||||
var userIsEn_US = workLanguage.HasValue ? workLanguage.Value == UserWorkLanguage.US : isEn_US;
|
||||
|
||||
|
||||
var messageToSend = new MimeMessage();
|
||||
//发件地址
|
||||
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
|
||||
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
|
||||
messageToSend.To.Add(new MailboxAddress(userinfo.FullName, userinfo.EMail));
|
||||
|
||||
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == medicalReview.TrialId);
|
||||
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
|
||||
var companyName = userIsEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
|
||||
|
||||
|
||||
var dictionValue = await CommonEmailHelper.TranslationDictionary(new TranslationDictionaryDto()
|
||||
{
|
||||
|
||||
DictionaryRepository = _dictionaryRepository,
|
||||
IsEn_US = isEn_US,
|
||||
IsEn_US = userIsEn_US,
|
||||
|
||||
|
||||
DictionaryList = new List<DictionaryDto>()
|
||||
@@ -219,7 +223,7 @@ public class UrgentIRRepliedMedicalReviewConsumer(
|
||||
var subjectCode = taskInfo.BlindSubjectCode.IsNullOrEmpty() ? taskInfo.Subject.Code : taskInfo.BlindSubjectCode;
|
||||
var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo, subjectCode, taskInfo.TaskBlindName);
|
||||
var htmlBodyStr = string.Format(
|
||||
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
|
||||
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr, workLanguage),
|
||||
userinfo.FullName, // 用户名 {0}
|
||||
trialInfo.ExperimentName, // 项目 {1}
|
||||
criterion.CriterionName, // 阅片标准 {2}
|
||||
@@ -234,9 +238,9 @@ public class UrgentIRRepliedMedicalReviewConsumer(
|
||||
|
||||
return (topicStr, htmlBodyStr);
|
||||
};
|
||||
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc);
|
||||
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc, workLanguage);
|
||||
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
|
||||
await SendEmailHelper.SendTrialEmailAsync(messageToSend, trialInfo);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -293,7 +297,7 @@ public class UrgentMIMRepliedMedicalReviewConsumer(
|
||||
var criterion = await _readingQuestionCriterionTrialRepository.FirstOrDefaultAsync(x => x.Id == medicalReview.VisitTask.TrialReadingCriterionId);
|
||||
var taskInfo = await _visitTaskRepository.Where(x => x.Id == medicalReview.VisitTaskId).Include(x => x.SourceSubjectVisit).Include(x => x.ReadModule).Include(x => x.Subject).FirstNotNullAsync();
|
||||
|
||||
var userinfo = await _userRoleRepository.Where(x => x.Id == taskInfo.DoctorUserId).FirstOrDefaultAsync();
|
||||
var userinfo = await _userRoleRepository.Where(x => x.Id == taskInfo.DoctorUserId).Include(x => x.IdentityUser).FirstOrDefaultAsync();
|
||||
|
||||
|
||||
|
||||
@@ -303,22 +307,25 @@ public class UrgentMIMRepliedMedicalReviewConsumer(
|
||||
|
||||
var subjectVisit = await _subjectVisitRepository.Where(x => x.Id == visitid).FirstOrDefaultAsync();
|
||||
|
||||
var workLanguage = userinfo.IdentityUser?.UserWorkLanguage;
|
||||
var userIsEn_US = workLanguage.HasValue ? workLanguage.Value == UserWorkLanguage.US : isEn_US;
|
||||
|
||||
|
||||
|
||||
var messageToSend = new MimeMessage();
|
||||
//发件地址
|
||||
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
|
||||
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
|
||||
messageToSend.To.Add(new MailboxAddress(userinfo.FullName, userinfo.EMail));
|
||||
|
||||
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == medicalReview.TrialId);
|
||||
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
|
||||
var companyName = userIsEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
|
||||
|
||||
|
||||
var dictionValue = await CommonEmailHelper.TranslationDictionary(new TranslationDictionaryDto()
|
||||
{
|
||||
|
||||
DictionaryRepository = _dictionaryRepository,
|
||||
IsEn_US = isEn_US,
|
||||
IsEn_US = userIsEn_US,
|
||||
|
||||
|
||||
DictionaryList = new List<DictionaryDto>()
|
||||
@@ -335,7 +342,7 @@ public class UrgentMIMRepliedMedicalReviewConsumer(
|
||||
var subjectCode = taskInfo.BlindSubjectCode.IsNullOrEmpty() ? taskInfo.Subject.Code : taskInfo.BlindSubjectCode;
|
||||
var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo, subjectCode, taskInfo.TaskBlindName);
|
||||
var htmlBodyStr = string.Format(
|
||||
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
|
||||
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr, workLanguage),
|
||||
userinfo.FullName, // 用户名 {0}
|
||||
trialInfo.ExperimentName, // 项目 {1}
|
||||
subjectCode, // 受试者 {2}
|
||||
@@ -352,9 +359,8 @@ public class UrgentMIMRepliedMedicalReviewConsumer(
|
||||
return (topicStr, htmlBodyStr);
|
||||
};
|
||||
|
||||
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc);
|
||||
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
|
||||
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc, workLanguage);
|
||||
await SendEmailHelper.SendTrialEmailAsync(messageToSend, trialInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -400,7 +406,15 @@ public class UrgentIRApplyedReReadingConsumer(
|
||||
|
||||
var doctorInfo = await _userRoleRepository.Where(x => x.Id == taskInfo.DoctorUserId).FirstOrDefaultAsync();
|
||||
|
||||
var trialUserList = await _trialUseRoleRepository.Where(x => x.TrialId == taskInfo.TrialId && x.TrialUser.IsDeleted==false).Include(x => x.UserRole).Select(x => x.UserRole).ToListAsync();
|
||||
var trialUserList = await _trialUseRoleRepository.Where(x => x.TrialId == taskInfo.TrialId && x.TrialUser.IsDeleted==false).Include(x => x.UserRole).Select(x => x.UserRole)
|
||||
.Select(x => new SendEmailUserDto()
|
||||
{
|
||||
UserTypeEnum = x.UserTypeEnum,
|
||||
FullName = x.FullName,
|
||||
UserWorkLanguage = x.IdentityUser.UserWorkLanguage,
|
||||
EMail = x.EMail,
|
||||
})
|
||||
.ToListAsync();
|
||||
|
||||
var userinfoList = trialUserList.Where(x => x.UserTypeEnum == UserTypeEnum.ProjectManager || x.UserTypeEnum == UserTypeEnum.APM).ToList();
|
||||
if (context.Message.ReReadingApplyState == ReReadingApplyState.TrialGroupHaveApplyed)
|
||||
@@ -413,37 +427,48 @@ public class UrgentIRApplyedReReadingConsumer(
|
||||
|
||||
var subjectVisit = await _subjectVisitRepository.Where(x => x.Id == visitid).FirstOrDefaultAsync();
|
||||
|
||||
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == taskInfo.TrialId);
|
||||
|
||||
foreach (var userinfo in userinfoList)
|
||||
var userWorkLanguageList = userinfoList.Select(x => x.UserWorkLanguage).Distinct().ToList();
|
||||
|
||||
foreach (var workLanguage in userWorkLanguageList)
|
||||
{
|
||||
var messageToSend = new MimeMessage();
|
||||
//发件地址
|
||||
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
|
||||
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
|
||||
|
||||
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == taskInfo.TrialId);
|
||||
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
|
||||
var langUserinfoList = userinfoList.Where(x => x.UserWorkLanguage == workLanguage).ToList();
|
||||
if (!langUserinfoList.Any()) continue;
|
||||
|
||||
var userIsEn_US = workLanguage == UserWorkLanguage.US;
|
||||
|
||||
var dictionValue = await CommonEmailHelper.TranslationDictionary(new TranslationDictionaryDto()
|
||||
{
|
||||
|
||||
DictionaryRepository = _dictionaryRepository,
|
||||
IsEn_US = isEn_US,
|
||||
IsEn_US = userIsEn_US,
|
||||
DictionaryList = new List<DictionaryDto>()
|
||||
{
|
||||
new DictionaryDto (){DictionaryCode= "ReadingCategory",EnumValue=taskInfo.ReadingCategory.GetEnumInt(), }, //任务类型
|
||||
new DictionaryDto (){DictionaryCode= "RequestReReadingResult",EnumValue="0", }, //审批结果 都是待审批
|
||||
}
|
||||
{
|
||||
new DictionaryDto (){DictionaryCode= "ReadingCategory",EnumValue=taskInfo.ReadingCategory.GetEnumInt(), }, //任务类型
|
||||
new DictionaryDto (){DictionaryCode= "RequestReReadingResult",EnumValue="0", }, //审批结果 都是待审批
|
||||
}
|
||||
});
|
||||
|
||||
var messageToSend = new MimeMessage();
|
||||
//发件地址
|
||||
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
|
||||
|
||||
foreach (var userinfo in langUserinfoList)
|
||||
{
|
||||
messageToSend.To.Add(new MailboxAddress(userinfo.FullName, userinfo.EMail));
|
||||
}
|
||||
|
||||
var companyName = userIsEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
|
||||
|
||||
var userNames = langUserinfoList.Select(x => x.FullName).ToList();
|
||||
|
||||
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
|
||||
{
|
||||
var subjectCode = taskInfo.BlindSubjectCode.IsNullOrEmpty() ? taskInfo.Subject.Code : taskInfo.BlindSubjectCode;
|
||||
var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo, subjectCode, taskInfo.TaskBlindName);
|
||||
var htmlBodyStr = string.Format(
|
||||
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
|
||||
userinfo.FullName, // 用户名 {0}
|
||||
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr, workLanguage),
|
||||
string.Join(',', userNames), // 用户名 {0}
|
||||
trialInfo.ExperimentName, // 项目 {1}
|
||||
subjectCode, // 受试者 {2}
|
||||
taskInfo.TaskBlindName, // 访视 {3}
|
||||
@@ -452,18 +477,14 @@ public class UrgentIRApplyedReReadingConsumer(
|
||||
criterion.CriterionName, // 阅片标准 {6}
|
||||
dictionValue[1], // 审批结果 {7}
|
||||
_systemEmailConfig.SiteUrl // 链接 {8}
|
||||
|
||||
|
||||
|
||||
|
||||
);
|
||||
|
||||
return (topicStr, htmlBodyStr);
|
||||
};
|
||||
|
||||
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc);
|
||||
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc, workLanguage);
|
||||
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
|
||||
await SendEmailHelper.SendTrialEmailAsync(messageToSend, trialInfo);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,26 +1,9 @@
|
||||
using DocumentFormat.OpenXml.Office2013.Excel;
|
||||
using DocumentFormat.OpenXml.Spreadsheet;
|
||||
using DocumentFormat.OpenXml.Vml;
|
||||
using DocumentFormat.OpenXml.Wordprocessing;
|
||||
using IRaCIS.Core.Application.Contracts;
|
||||
using IRaCIS.Core.Application.Helper;
|
||||
using IRaCIS.Core.Application.MassTransit.Command;
|
||||
using IRaCIS.Core.Application.Helper;
|
||||
using IRaCIS.Core.Domain;
|
||||
using IRaCIS.Core.Domain.BaseModel;
|
||||
using IRaCIS.Core.Domain.Models;
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using MassTransit;
|
||||
using Microsoft.AspNetCore.Components.Routing;
|
||||
using Microsoft.Extensions.Options;
|
||||
using MimeKit;
|
||||
using NPOI.SS.Formula.Functions;
|
||||
using NPOI.Util;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace IRaCIS.Core.Application.MassTransit.Consumer;
|
||||
|
||||
@@ -55,74 +38,86 @@ public class UserSiteSurveySubmitedEventConsumer(
|
||||
|
||||
if (emailConfig != null)
|
||||
{
|
||||
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == trialId);
|
||||
var siteInfo = await _trialSiteRepository.FirstOrDefaultAsync(t => t.TrialId == trialId && t.Id == siteSurveyInfo.TrialSiteId, true);
|
||||
|
||||
|
||||
var trialUserList = await _trialUserRoleRepository.Where(t => t.TrialId == siteSurveyInfo.TrialId && t.TrialUser.IsDeleted == false)
|
||||
.Where(t => t.UserRole.UserTypeEnum == UserTypeEnum.SPM || t.UserRole.UserTypeEnum == UserTypeEnum.CPM || t.UserRole.UserTypeEnum == UserTypeEnum.ProjectManager || t.UserRole.UserTypeEnum == UserTypeEnum.APM)
|
||||
.Select(t => new { t.UserRole.FullName, t.UserRole.IdentityUser.EMail, t.UserRole.UserTypeEnum }).ToListAsync();
|
||||
.Select(t => new { t.UserRole.FullName, t.UserRole.IdentityUser.EMail, t.UserRole.UserTypeEnum, t.UserRole.IdentityUser.UserWorkLanguage }).ToListAsync();
|
||||
|
||||
var sPMOrCPMList = trialUserList.Where(t => t.UserTypeEnum == UserTypeEnum.SPM || t.UserTypeEnum == UserTypeEnum.CPM).ToList();
|
||||
var pmAndAPMList = trialUserList.Where(t => t.UserTypeEnum == UserTypeEnum.ProjectManager || t.UserTypeEnum == UserTypeEnum.APM).ToList();
|
||||
|
||||
var messageToSend = new MimeMessage();
|
||||
|
||||
var toUserName = string.Empty;
|
||||
|
||||
//有SPM
|
||||
if (sPMOrCPMList.Count > 0)
|
||||
// 遍历语言枚举 在底层判断收件人是否为空,决定是否跳过邮件发送,避免每个邮件场景的地方都重复判断
|
||||
foreach (UserWorkLanguage workLanguage in Enum.GetValues<UserWorkLanguage>())
|
||||
{
|
||||
foreach (var user in sPMOrCPMList)
|
||||
//重新设置当前发送邮件的语言
|
||||
isEn_US = workLanguage == UserWorkLanguage.US;
|
||||
|
||||
var messageToSend = new MimeMessage();
|
||||
|
||||
var toUserName = string.Empty;
|
||||
|
||||
//有SPM 并且参与
|
||||
if (trialInfo.IsSPMJoinSiteSurvey && sPMOrCPMList.Count > 0)
|
||||
{
|
||||
messageToSend.To.Add(new MailboxAddress(user.FullName, user.EMail));
|
||||
var lan_SPMOrCPMList = sPMOrCPMList.Where(t => t.UserWorkLanguage == workLanguage);
|
||||
|
||||
foreach (var user in lan_SPMOrCPMList)
|
||||
{
|
||||
messageToSend.To.Add(new MailboxAddress(user.FullName, user.EMail));
|
||||
}
|
||||
|
||||
toUserName = string.Join('、', lan_SPMOrCPMList.Select(t => t.FullName));
|
||||
|
||||
foreach (var user in pmAndAPMList)
|
||||
{
|
||||
messageToSend.Cc.Add(new MailboxAddress(user.FullName, user.EMail));
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
var lan_pmAndAPMList = pmAndAPMList.Where(t => t.UserWorkLanguage == workLanguage);
|
||||
|
||||
foreach (var user in lan_pmAndAPMList)
|
||||
{
|
||||
messageToSend.To.Add(new MailboxAddress(user.FullName, user.EMail));
|
||||
}
|
||||
|
||||
toUserName = string.Join('、', lan_pmAndAPMList.Select(t => t.FullName));
|
||||
}
|
||||
|
||||
toUserName = string.Join('、', sPMOrCPMList.Select(t => t.FullName));
|
||||
//发件地址
|
||||
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
|
||||
|
||||
foreach (var user in pmAndAPMList)
|
||||
|
||||
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
|
||||
|
||||
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
|
||||
{
|
||||
messageToSend.Cc.Add(new MailboxAddress(user.FullName, user.EMail));
|
||||
}
|
||||
var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo);
|
||||
var htmlBodyStr = string.Format(CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr, workLanguage),
|
||||
toUserName,
|
||||
siteInfo.TrialSiteCode,
|
||||
siteInfo.TrialSiteAliasName,
|
||||
|
||||
siteSurveyInfo.UserName,
|
||||
siteSurveyInfo.Email,
|
||||
siteSurveyInfo.Phone,
|
||||
_systemEmailConfig.SiteUrl
|
||||
);
|
||||
|
||||
return (topicStr, htmlBodyStr);
|
||||
};
|
||||
|
||||
|
||||
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(emailConfig, messageToSend, emailConfigFunc, workLanguage);
|
||||
|
||||
await SendEmailHelper.SendTrialEmailAsync(messageToSend, trialInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var user in pmAndAPMList)
|
||||
{
|
||||
messageToSend.To.Add(new MailboxAddress(user.FullName, user.EMail));
|
||||
}
|
||||
|
||||
toUserName = string.Join('、', pmAndAPMList.Select(t => t.FullName));
|
||||
}
|
||||
|
||||
//发件地址
|
||||
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
|
||||
|
||||
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == trialId);
|
||||
|
||||
var siteInfo = await _trialSiteRepository.FirstOrDefaultAsync(t => t.TrialId == trialId && t.Id == siteSurveyInfo.TrialSiteId, true);
|
||||
|
||||
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
|
||||
|
||||
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
|
||||
{
|
||||
var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo);
|
||||
var htmlBodyStr = string.Format(CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
|
||||
toUserName,
|
||||
siteInfo.TrialSiteCode,
|
||||
siteInfo.TrialSiteAliasName,
|
||||
|
||||
siteSurveyInfo.UserName,
|
||||
siteSurveyInfo.Email,
|
||||
siteSurveyInfo.Phone,
|
||||
_systemEmailConfig.SiteUrl
|
||||
);
|
||||
|
||||
return (topicStr, htmlBodyStr);
|
||||
};
|
||||
|
||||
|
||||
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(emailConfig, messageToSend, emailConfigFunc);
|
||||
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -151,61 +146,74 @@ public class SiteSurveySPMSubmitedEventConsumer(
|
||||
|
||||
var trialId = siteSurveyInfo.TrialId;
|
||||
|
||||
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == trialId);
|
||||
|
||||
var siteInfo = await _trialSiteRepository.FirstOrDefaultAsync(t => t.TrialId == trialId && t.Id == siteSurveyInfo.TrialSiteId, true);
|
||||
|
||||
var trialUserList = _trialUserRoleRepository.Where(t => t.TrialId == trialId && t.TrialUser.IsDeleted == false)
|
||||
.Where(t => t.UserRole.UserTypeEnum == UserTypeEnum.SPM || t.UserRole.UserTypeEnum == UserTypeEnum.CPM || t.UserRole.UserTypeEnum == UserTypeEnum.ProjectManager || t.UserRole.UserTypeEnum == UserTypeEnum.APM)
|
||||
.Select(t => new { t.UserRole.IdentityUser.EMail, t.UserRole.FullName, t.UserRole.UserTypeEnum, t.UserRole.IdentityUser.UserWorkLanguage }).ToList();
|
||||
|
||||
var scenario = EmailBusinessScenario.Approval_SubmitSiteSurvey;
|
||||
|
||||
var emailConfig = _emailNoticeConfigrepository.Where(t => t.BusinessScenarioEnum == scenario && t.IsAutoSend && t.IsEnable).FirstOrDefault();
|
||||
|
||||
if (emailConfig != null)
|
||||
{
|
||||
var messageToSend = new MimeMessage();
|
||||
|
||||
var trialUserList = _trialUserRoleRepository.Where(t => t.TrialId == trialId && t.TrialUser.IsDeleted == false)
|
||||
.Where(t => t.UserRole.UserTypeEnum == UserTypeEnum.SPM || t.UserRole.UserTypeEnum == UserTypeEnum.CPM || t.UserRole.UserTypeEnum == UserTypeEnum.ProjectManager || t.UserRole.UserTypeEnum == UserTypeEnum.APM)
|
||||
.Select(t => new { t.UserRole.IdentityUser.EMail, t.UserRole.FullName, t.UserRole.UserTypeEnum }).ToList();
|
||||
|
||||
var sPMOrCPMList = trialUserList.Where(t => t.UserTypeEnum == UserTypeEnum.SPM || t.UserTypeEnum == UserTypeEnum.CPM).ToList();
|
||||
var pmAndAPMList = trialUserList.Where(t => t.UserTypeEnum == UserTypeEnum.ProjectManager || t.UserTypeEnum == UserTypeEnum.APM).ToList();
|
||||
|
||||
var toUserName = string.Empty;
|
||||
|
||||
foreach (var item in pmAndAPMList)
|
||||
// 遍历语言枚举 在底层判断收件人是否为空,决定是否跳过邮件发送,避免每个邮件场景的地方都重复判断
|
||||
foreach (UserWorkLanguage workLanguage in Enum.GetValues<UserWorkLanguage>())
|
||||
{
|
||||
messageToSend.To.Add(new MailboxAddress(item.FullName, item.EMail));
|
||||
//重新设置当前发送邮件的语言
|
||||
isEn_US = workLanguage == UserWorkLanguage.US;
|
||||
|
||||
|
||||
var sPMOrCPMList = trialUserList.Where(t => t.UserTypeEnum == UserTypeEnum.SPM || t.UserTypeEnum == UserTypeEnum.CPM).ToList();
|
||||
var pmAndAPMList = trialUserList.Where(t => t.UserTypeEnum == UserTypeEnum.ProjectManager || t.UserTypeEnum == UserTypeEnum.APM).ToList();
|
||||
|
||||
var messageToSend = new MimeMessage();
|
||||
|
||||
var toUserName = string.Empty;
|
||||
|
||||
var lan_pmAndAPMList = pmAndAPMList.Where(t => t.UserWorkLanguage == workLanguage);
|
||||
|
||||
foreach (var item in lan_pmAndAPMList)
|
||||
{
|
||||
messageToSend.To.Add(new MailboxAddress(item.FullName, item.EMail));
|
||||
|
||||
toUserName = string.Join('、', lan_pmAndAPMList.Select(t => t.FullName));
|
||||
}
|
||||
|
||||
//发件地址
|
||||
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
|
||||
|
||||
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
|
||||
|
||||
|
||||
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
|
||||
{
|
||||
var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo);
|
||||
var htmlBodyStr = string.Format(CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr, workLanguage),
|
||||
toUserName,
|
||||
siteInfo.TrialSiteCode, //中心编号
|
||||
siteInfo.TrialSiteAliasName,//中心名称
|
||||
|
||||
siteSurveyInfo.UserName, //联系人
|
||||
siteSurveyInfo.Email, //联系邮箱
|
||||
siteSurveyInfo.Phone, //联系电话
|
||||
_systemEmailConfig.SiteUrl
|
||||
);
|
||||
|
||||
return (topicStr, htmlBodyStr);
|
||||
};
|
||||
|
||||
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(emailConfig, messageToSend, emailConfigFunc, workLanguage);
|
||||
|
||||
await SendEmailHelper.SendTrialEmailAsync(messageToSend, trialInfo);
|
||||
|
||||
toUserName = string.Join('、', pmAndAPMList.Select(t => t.FullName));
|
||||
}
|
||||
|
||||
//发件地址
|
||||
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
|
||||
|
||||
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == trialId);
|
||||
|
||||
var siteInfo = await _trialSiteRepository.FirstOrDefaultAsync(t => t.TrialId == trialId && t.Id == siteSurveyInfo.TrialSiteId, true);
|
||||
|
||||
|
||||
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
|
||||
|
||||
|
||||
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
|
||||
{
|
||||
var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo);
|
||||
var htmlBodyStr = string.Format(CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
|
||||
toUserName,
|
||||
siteInfo.TrialSiteCode, //中心编号
|
||||
siteInfo.TrialSiteAliasName,//中心名称
|
||||
|
||||
siteSurveyInfo.UserName, //联系人
|
||||
siteSurveyInfo.Email, //联系邮箱
|
||||
siteSurveyInfo.Phone, //联系电话
|
||||
_systemEmailConfig.SiteUrl
|
||||
);
|
||||
|
||||
return (topicStr, htmlBodyStr);
|
||||
};
|
||||
|
||||
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(emailConfig, messageToSend, emailConfigFunc);
|
||||
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
|
||||
}
|
||||
|
||||
|
||||
@@ -234,92 +242,108 @@ public class SiteSurverRejectedEventConsumer(
|
||||
|
||||
var trialSiteSurveyId = context.Message.TrialSiteSurveyId;
|
||||
|
||||
var siteSurveyInfo = _trialSiteSurveyRepository.Where(t => t.Id == trialSiteSurveyId ,ignoreQueryFilters:true).FirstOrDefault().IfNullThrowException();
|
||||
var siteSurveyInfo = _trialSiteSurveyRepository.Where(t => t.Id == trialSiteSurveyId, ignoreQueryFilters: true).FirstOrDefault().IfNullThrowException();
|
||||
|
||||
var trialId = siteSurveyInfo.TrialId;
|
||||
|
||||
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == trialId);
|
||||
|
||||
var siteInfo = await _trialSiteRepository.FirstOrDefaultAsync(t => t.TrialId == trialId && t.Id == siteSurveyInfo.TrialSiteId, true);
|
||||
|
||||
var scenario = EmailBusinessScenario.SiteSurveyReject;
|
||||
|
||||
var emailConfig = _emailNoticeConfigrepository.Where(t => t.BusinessScenarioEnum == scenario && t.IsAutoSend && t.IsEnable).FirstOrDefault();
|
||||
|
||||
if (emailConfig != null)
|
||||
{
|
||||
|
||||
|
||||
var messageToSend = new MimeMessage();
|
||||
|
||||
var toUserName = siteSurveyInfo.UserName;
|
||||
|
||||
if (context.Message.IsHaveSPMOrCPM)
|
||||
// 遍历语言枚举 在底层判断收件人是否为空,决定是否跳过邮件发送,避免每个邮件场景的地方都重复判断
|
||||
foreach (UserWorkLanguage workLanguage in Enum.GetValues<UserWorkLanguage>())
|
||||
{
|
||||
//PM 驳回到SPM
|
||||
if (siteSurveyInfo.State == TrialSiteSurveyEnum.CRCSubmitted)
|
||||
//重新设置当前发送邮件的语言
|
||||
isEn_US = workLanguage == UserWorkLanguage.US;
|
||||
|
||||
|
||||
var messageToSend = new MimeMessage();
|
||||
|
||||
var toUserName = siteSurveyInfo.UserName;
|
||||
|
||||
if (context.Message.IsHaveSPMOrCPM)
|
||||
{
|
||||
//var user = await _userRoleRepository.FirstOrDefaultAsync(t => t.Id == siteSurveyInfo.PreliminaryUserId);
|
||||
|
||||
//name = user.FullName;
|
||||
|
||||
var sPMOrCPMList = _trialUserRoleRepository.Where(t => t.TrialId == trialId && t.TrialUser.IsDeleted == false)
|
||||
.Where(t => t.UserRole.UserTypeEnum == UserTypeEnum.SPM || t.UserRole.UserTypeEnum == UserTypeEnum.CPM)
|
||||
.Select(t => new { t.UserRole.IdentityUser.EMail, t.UserRole.FullName, t.UserRole.UserTypeEnum }).ToList();
|
||||
|
||||
|
||||
foreach (var user in sPMOrCPMList)
|
||||
//PM 驳回到SPM
|
||||
if (siteSurveyInfo.State == TrialSiteSurveyEnum.CRCSubmitted)
|
||||
{
|
||||
messageToSend.To.Add(new MailboxAddress(user.FullName, user.EMail));
|
||||
|
||||
var sPMOrCPMList = _trialUserRoleRepository.Where(t => t.TrialId == trialId && t.TrialUser.IsDeleted == false)
|
||||
.Where(t => t.UserRole.UserTypeEnum == UserTypeEnum.SPM || t.UserRole.UserTypeEnum == UserTypeEnum.CPM)
|
||||
.Select(t => new { t.UserRole.IdentityUser.EMail, t.UserRole.FullName, t.UserRole.UserTypeEnum, t.UserRole.IdentityUser.UserWorkLanguage }).ToList();
|
||||
|
||||
var lan_SPMOrCPMList = sPMOrCPMList.Where(t => t.UserWorkLanguage == workLanguage);
|
||||
|
||||
foreach (var user in lan_SPMOrCPMList)
|
||||
{
|
||||
messageToSend.To.Add(new MailboxAddress(user.FullName, user.EMail));
|
||||
}
|
||||
|
||||
toUserName = string.Join('、', lan_SPMOrCPMList.Select(t => t.FullName));
|
||||
|
||||
}
|
||||
//SPM 驳回到CRC
|
||||
else if (siteSurveyInfo.State == TrialSiteSurveyEnum.ToSubmit)
|
||||
{
|
||||
//使用中心国家语言 一致才发送
|
||||
if ((siteInfo.Country == StaticData.SiteCountry.US && isEn_US) || (siteInfo.Country == StaticData.SiteCountry.CN && isEn_US == false))
|
||||
{
|
||||
messageToSend.To.Add(new MailboxAddress(toUserName, siteSurveyInfo.Email));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
toUserName = string.Join('、', sPMOrCPMList.Select(t => t.FullName));
|
||||
|
||||
}
|
||||
//SPM 驳回到CRC
|
||||
else if (siteSurveyInfo.State == TrialSiteSurveyEnum.ToSubmit)
|
||||
else
|
||||
{
|
||||
messageToSend.To.Add(new MailboxAddress(toUserName, siteSurveyInfo.Email));
|
||||
//没有SPM PM驳回到CRC
|
||||
|
||||
//使用中心国家语言 一致才发送
|
||||
if ((siteInfo.Country == StaticData.SiteCountry.US && isEn_US) || (siteInfo.Country == StaticData.SiteCountry.CN && isEn_US == false))
|
||||
{
|
||||
messageToSend.To.Add(new MailboxAddress(toUserName, siteSurveyInfo.Email));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
//没有SPM PM驳回到CRC
|
||||
//发件地址
|
||||
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
|
||||
|
||||
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
|
||||
|
||||
|
||||
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
|
||||
{
|
||||
var topicStr = string.Format(input.topicStr, companyName, trialInfo.ResearchProgramNo);
|
||||
var htmlBodyStr = string.Format(CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr, workLanguage),
|
||||
toUserName,
|
||||
trialInfo.TrialCode,
|
||||
trialInfo.ResearchProgramNo,
|
||||
trialInfo.ExperimentName,
|
||||
|
||||
siteInfo.TrialSiteCode, //中心编号
|
||||
siteInfo.TrialSiteAliasName,//中心名称
|
||||
siteSurveyInfo.LatestBackReason, //驳回原因
|
||||
_systemEmailConfig.SiteUrl, //链接
|
||||
(siteSurveyInfo.State == TrialSiteSurveyEnum.ToSubmit ? "inline - block" : "none")
|
||||
);
|
||||
|
||||
return (topicStr, htmlBodyStr);
|
||||
};
|
||||
|
||||
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(emailConfig, messageToSend, emailConfigFunc);
|
||||
|
||||
await SendEmailHelper.SendTrialEmailAsync(messageToSend, trialInfo);
|
||||
|
||||
messageToSend.To.Add(new MailboxAddress(String.Empty, siteSurveyInfo.Email));
|
||||
}
|
||||
|
||||
//发件地址
|
||||
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
|
||||
|
||||
|
||||
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == trialId);
|
||||
|
||||
var siteInfo = await _trialSiteRepository.FirstOrDefaultAsync(t => t.TrialId == trialId && t.Id == siteSurveyInfo.TrialSiteId, true);
|
||||
|
||||
|
||||
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
|
||||
|
||||
|
||||
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
|
||||
{
|
||||
var topicStr = string.Format(input.topicStr, companyName,trialInfo.ResearchProgramNo);
|
||||
var htmlBodyStr = string.Format(CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
|
||||
toUserName,
|
||||
trialInfo.TrialCode,
|
||||
trialInfo.ResearchProgramNo,
|
||||
trialInfo.ExperimentName,
|
||||
|
||||
siteInfo.TrialSiteCode, //中心编号
|
||||
siteInfo.TrialSiteAliasName,//中心名称
|
||||
siteSurveyInfo.LatestBackReason, //驳回原因
|
||||
_systemEmailConfig.SiteUrl, //链接
|
||||
(siteSurveyInfo.State == TrialSiteSurveyEnum.ToSubmit ? "inline - block" : "none")
|
||||
);
|
||||
|
||||
return (topicStr, htmlBodyStr);
|
||||
};
|
||||
|
||||
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(emailConfig, messageToSend, emailConfigFunc);
|
||||
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ public class CRCSubmitedAndQCToAuditEventConsumer(
|
||||
private readonly SystemEmailSendConfig _systemEmailConfig = systemEmailConfig.CurrentValue;
|
||||
public async Task Consume(ConsumeContext<CRCSubmitedAndQCToAuditEvent> context)
|
||||
{
|
||||
Console.WriteLine("发送(005,006) 【加急项目所有IQC待领取质控任务】邮件!!!");
|
||||
Log.Logger.Warning("发送(005,006) 【加急项目所有IQC待领取质控任务】邮件!!!");
|
||||
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
|
||||
|
||||
var subjectVisitId = context.Message.SubjectVisitId;
|
||||
@@ -49,80 +49,91 @@ public class CRCSubmitedAndQCToAuditEventConsumer(
|
||||
|
||||
var trialEmailConfig = _trialEmailNoticeConfigrepository.Where(t => t.TrialId == trialId && t.BusinessScenarioEnum == scenario && t.IsAutoSend && t.IsEnable).FirstOrDefault();
|
||||
|
||||
var trialUserList = await _trialUseRoleRepository.Where(x => x.TrialId == subjectVisit.TrialId && x.TrialUser.IsDeleted == false)
|
||||
.Select(t => new { t.UserRole.IdentityUser.EMail, t.UserRole.FullName, t.UserRole.UserTypeEnum, t.UserRole.IdentityUser.UserWorkLanguage }).ToListAsync();
|
||||
|
||||
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == subjectVisit.TrialId);
|
||||
|
||||
if (trialEmailConfig != null)
|
||||
{
|
||||
|
||||
var trialUserList = await _trialUseRoleRepository.Where(x => x.TrialId == subjectVisit.TrialId && x.TrialUser.IsDeleted == false).Include(x => x.UserRole).Select(x => x.UserRole).ToListAsync();
|
||||
|
||||
var userinfoList = trialUserList.Where(x => x.UserTypeEnum == UserTypeEnum.IQC).ToList();
|
||||
|
||||
|
||||
var pmandAPm = trialUserList.Where(x => x.UserTypeEnum == UserTypeEnum.APM || x.UserTypeEnum == UserTypeEnum.ProjectManager).ToList();
|
||||
|
||||
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == subjectVisit.TrialId);
|
||||
|
||||
var auditStateCode = "AuditStatePE";
|
||||
|
||||
if (trialInfo.QCProcessEnum == TrialQCProcess.DoubleAudit)
|
||||
// 遍历语言枚举 在底层判断收件人是否为空,决定是否跳过邮件发送,避免每个邮件场景的地方都重复判断
|
||||
foreach (UserWorkLanguage workLanguage in Enum.GetValues<UserWorkLanguage>())
|
||||
{
|
||||
auditStateCode = "AuditStateRC";
|
||||
}
|
||||
//重新设置当前发送邮件的语言
|
||||
isEn_US = workLanguage == UserWorkLanguage.US;
|
||||
|
||||
|
||||
var iqcList = trialUserList.Where(x => x.UserTypeEnum == UserTypeEnum.IQC).Where(t => t.UserWorkLanguage == workLanguage).ToList();
|
||||
|
||||
var dictionValue = await CommonEmailHelper.TranslationDictionary(new TranslationDictionaryDto()
|
||||
{
|
||||
|
||||
DictionaryRepository = _dictionaryRepository,
|
||||
IsEn_US = isEn_US,
|
||||
DictionaryList = new List<DictionaryDto>()
|
||||
var pmandAPm = trialUserList.Where(x => x.UserTypeEnum == UserTypeEnum.APM || x.UserTypeEnum == UserTypeEnum.ProjectManager).ToList();
|
||||
|
||||
|
||||
var auditStateCode = "AuditStatePE";
|
||||
|
||||
if (trialInfo.QCProcessEnum == TrialQCProcess.DoubleAudit)
|
||||
{
|
||||
auditStateCode = "AuditStateRC";
|
||||
}
|
||||
|
||||
var dictionValue = await CommonEmailHelper.TranslationDictionary(new TranslationDictionaryDto()
|
||||
{
|
||||
|
||||
DictionaryRepository = _dictionaryRepository,
|
||||
IsEn_US = isEn_US,
|
||||
DictionaryList = new List<DictionaryDto>()
|
||||
{
|
||||
new DictionaryDto (){DictionaryCode= auditStateCode,EnumValue=subjectVisit.AuditState.GetEnumInt(), }, //审核状态
|
||||
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
var messageToSend = new MimeMessage();
|
||||
//发件地址
|
||||
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
|
||||
var messageToSend = new MimeMessage();
|
||||
//发件地址
|
||||
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
|
||||
|
||||
|
||||
foreach (var userinfo in iqcList)
|
||||
{
|
||||
messageToSend.To.Add(new MailboxAddress(userinfo.FullName, userinfo.EMail));
|
||||
}
|
||||
|
||||
var userNames = iqcList.Select(x => x.FullName).ToList();
|
||||
|
||||
foreach (var pm in pmandAPm)
|
||||
{
|
||||
messageToSend.Cc.Add(new MailboxAddress(pm.FullName, pm.EMail));
|
||||
}
|
||||
|
||||
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
|
||||
|
||||
|
||||
|
||||
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
|
||||
{
|
||||
var subjectCode = subjectVisit.Subject.Code;
|
||||
var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo, subjectCode, subjectVisit.VisitName);
|
||||
var htmlBodyStr = string.Format(
|
||||
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr, workLanguage),
|
||||
string.Join(',', userNames), // 用户名 {0}
|
||||
trialInfo.ExperimentName, // 项目 {1}
|
||||
subjectCode, // 受试者 {2}
|
||||
subjectVisit.VisitName, // 访视 {3}
|
||||
dictionValue[0], // 审核状态 {4}
|
||||
_systemEmailConfig.SiteUrl // 链接 {5}
|
||||
);
|
||||
|
||||
return (topicStr, htmlBodyStr);
|
||||
};
|
||||
|
||||
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc, workLanguage);
|
||||
|
||||
await SendEmailHelper.SendTrialEmailAsync(messageToSend, trialInfo);
|
||||
|
||||
|
||||
foreach (var userinfo in userinfoList)
|
||||
{
|
||||
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
|
||||
}
|
||||
|
||||
var userNames = userinfoList.Select(x => x.FullName).ToList();
|
||||
|
||||
foreach (var pm in pmandAPm)
|
||||
{
|
||||
messageToSend.Cc.Add(new MailboxAddress(String.Empty, pm.EMail));
|
||||
}
|
||||
|
||||
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
|
||||
|
||||
|
||||
|
||||
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
|
||||
{
|
||||
var subjectCode = subjectVisit.Subject.Code;
|
||||
var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo, subjectCode, subjectVisit.VisitName);
|
||||
var htmlBodyStr = string.Format(
|
||||
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
|
||||
string.Join(',', userNames), // 用户名 {0}
|
||||
trialInfo.ExperimentName, // 项目 {1}
|
||||
subjectCode, // 受试者 {2}
|
||||
subjectVisit.VisitName, // 访视 {3}
|
||||
dictionValue[0], // 审核状态 {4}
|
||||
_systemEmailConfig.SiteUrl // 链接 {5}
|
||||
);
|
||||
|
||||
return (topicStr, htmlBodyStr);
|
||||
};
|
||||
|
||||
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc);
|
||||
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -147,7 +158,7 @@ public class CRCRepliedQCChallengeEventConsumer(
|
||||
private readonly SystemEmailSendConfig _systemEmailConfig = systemEmailConfig.CurrentValue;
|
||||
public async Task Consume(ConsumeContext<CRCRepliedQCChallengeEvent> context)
|
||||
{
|
||||
Console.WriteLine("发送(Code012,013) 【CRC 回复质控质疑 通知QC】邮件!!!");
|
||||
Log.Logger.Warning("发送(Code012,013) 【CRC 回复质控质疑 通知QC】邮件!!!");
|
||||
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
|
||||
|
||||
var subjectVisitId = context.Message.SubjectVisitId;
|
||||
@@ -158,34 +169,37 @@ public class CRCRepliedQCChallengeEventConsumer(
|
||||
|
||||
var scenario = context.Message.IsPd ? EmailBusinessScenario.PDVerification_UnderQCQuery : EmailBusinessScenario.EligibilityVerification_Pending;
|
||||
|
||||
|
||||
|
||||
var trialEmailConfig = _trialEmailNoticeConfigrepository.Where(t => t.TrialId == trialId && t.BusinessScenarioEnum == scenario && t.IsAutoSend && t.IsEnable).FirstOrDefault();
|
||||
|
||||
if (trialEmailConfig != null)
|
||||
{
|
||||
|
||||
|
||||
var qCChallengeDialog = await _qCChallengeDialogRepository.Where(x => x.Id == context.Message.QCChallengeDialogId).Include(x => x.QCChallenge).FirstNotNullAsync();
|
||||
|
||||
var trialUserList = await _trialUseRoleRepository.Where(x => x.TrialId == subjectVisit.TrialId && x.TrialUser.IsDeleted == false)
|
||||
.Select(t => new { t.UserRole.IdentityUser.EMail, t.UserRole.FullName, t.UserRole.UserTypeEnum, t.UserRole.IdentityUser.UserWorkLanguage }).ToListAsync();
|
||||
|
||||
var trialUserList = await _trialUseRoleRepository.Where(x => x.TrialId == subjectVisit.TrialId && x.TrialUser.IsDeleted == false).Include(x => x.UserRole).Select(x => x.UserRole).ToListAsync();
|
||||
|
||||
var userinfoList = trialUserList.Where(x => x.UserTypeEnum == UserTypeEnum.IQC).ToList();
|
||||
var pmandAPm = trialUserList.Where(x => x.UserTypeEnum == UserTypeEnum.APM || x.UserTypeEnum == UserTypeEnum.ProjectManager).ToList();
|
||||
|
||||
|
||||
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == subjectVisit.TrialId);
|
||||
|
||||
|
||||
var userinfo = subjectVisit.CurrentActionUser;
|
||||
|
||||
//重新设置当前发送邮件的语言
|
||||
var workLanguage = _userRoleRepository.Where(t => t.Id == userinfo.Id).Select(t => t.IdentityUser.UserWorkLanguage).First();
|
||||
isEn_US = workLanguage == UserWorkLanguage.US;
|
||||
|
||||
var messageToSend = new MimeMessage();
|
||||
//发件地址
|
||||
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
|
||||
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
|
||||
messageToSend.To.Add(new MailboxAddress(userinfo.FullName, userinfo.EMail));
|
||||
|
||||
foreach (var pm in pmandAPm)
|
||||
{
|
||||
messageToSend.Cc.Add(new MailboxAddress(String.Empty, pm.EMail));
|
||||
messageToSend.Cc.Add(new MailboxAddress(pm.FullName, pm.EMail));
|
||||
}
|
||||
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
|
||||
|
||||
@@ -207,7 +221,7 @@ public class CRCRepliedQCChallengeEventConsumer(
|
||||
var subjectCode = subjectVisit.Subject.Code;
|
||||
var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo, subjectCode, subjectVisit.VisitName);
|
||||
var htmlBodyStr = string.Format(
|
||||
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
|
||||
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr, workLanguage),
|
||||
userinfo.FullName, // 用户名 {0}
|
||||
trialInfo.ExperimentName, // 项目 {1}
|
||||
subjectCode, // 受试者 {2}
|
||||
@@ -219,9 +233,9 @@ public class CRCRepliedQCChallengeEventConsumer(
|
||||
|
||||
return (topicStr, htmlBodyStr);
|
||||
};
|
||||
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc);
|
||||
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc, workLanguage);
|
||||
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
|
||||
await SendEmailHelper.SendTrialEmailAsync(messageToSend, trialInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -244,7 +258,7 @@ public class QCRepliedQCChallengeEventConsumer(
|
||||
private readonly SystemEmailSendConfig _systemEmailConfig = systemEmailConfig.CurrentValue;
|
||||
public async Task Consume(ConsumeContext<QCRepliedQCChallengeEvent> context)
|
||||
{
|
||||
Console.WriteLine("发送(014,015) 【 QC回复 质控质疑,通知CRC】邮件!!!");
|
||||
Log.Logger.Warning("发送(014,015) 【 QC回复 质控质疑,通知CRC】邮件!!!");
|
||||
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
|
||||
|
||||
var subjectVisitId = context.Message.SubjectVisitId;
|
||||
@@ -277,54 +291,61 @@ public class QCRepliedQCChallengeEventConsumer(
|
||||
}
|
||||
|
||||
|
||||
|
||||
var trialUserList = await _trialUseRoleRepository.Where(x => x.TrialId == subjectVisit.TrialId && x.TrialUser.IsDeleted == false).Include(x => x.UserRole).Select(x => x.UserRole).ToListAsync();
|
||||
|
||||
var userinfoList = trialUserList.Where(x => x.UserTypeEnum == UserTypeEnum.ClinicalResearchCoordinator).ToList();
|
||||
|
||||
var craInfo = trialUserList.Where(x => x.UserTypeEnum == UserTypeEnum.CRA).FirstOrDefault();
|
||||
var trialUserList = await _trialUseRoleRepository.Where(x => x.TrialId == subjectVisit.TrialId && x.TrialUser.IsDeleted == false)
|
||||
.Select(t => new { t.UserRole.IdentityUser.EMail, t.UserRole.FullName, t.UserRole.UserTypeEnum, t.UserRole.IdentityUser.UserWorkLanguage }).ToListAsync();
|
||||
|
||||
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == subjectVisit.TrialId);
|
||||
|
||||
|
||||
|
||||
|
||||
var dictionValue = await CommonEmailHelper.TranslationDictionary(new TranslationDictionaryDto()
|
||||
// 遍历语言枚举 在底层判断收件人是否为空,决定是否跳过邮件发送,避免每个邮件场景的地方都重复判断
|
||||
foreach (UserWorkLanguage workLanguage in Enum.GetValues<UserWorkLanguage>())
|
||||
{
|
||||
|
||||
DictionaryRepository = _dictionaryRepository,
|
||||
IsEn_US = isEn_US,
|
||||
DictionaryList = new List<DictionaryDto>()
|
||||
{
|
||||
new DictionaryDto (){DictionaryCode= "YesOrNo",EnumValue=isclose.ToString().ToLower(), }, //是否关闭
|
||||
|
||||
}
|
||||
});
|
||||
//重新设置当前发送邮件的语言
|
||||
isEn_US = workLanguage == UserWorkLanguage.US;
|
||||
|
||||
|
||||
var userinfoList = trialUserList.Where(x => x.UserTypeEnum == UserTypeEnum.ClinicalResearchCoordinator).Where(t => t.UserWorkLanguage == workLanguage).ToList();
|
||||
|
||||
var craInfoList = trialUserList.Where(x => x.UserTypeEnum == UserTypeEnum.CRA).ToList();
|
||||
|
||||
var dictionValue = await CommonEmailHelper.TranslationDictionary(new TranslationDictionaryDto()
|
||||
{
|
||||
|
||||
DictionaryRepository = _dictionaryRepository,
|
||||
IsEn_US = isEn_US,
|
||||
DictionaryList = new List<DictionaryDto>()
|
||||
{
|
||||
new DictionaryDto (){DictionaryCode= "YesOrNo",EnumValue=isclose.ToString().ToLower(), }, //是否关闭
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
foreach (var userinfo in userinfoList)
|
||||
{
|
||||
var messageToSend = new MimeMessage();
|
||||
//发件地址
|
||||
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
|
||||
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
|
||||
if (craInfo != null)
|
||||
|
||||
var toUserName = string.Join('、', userinfoList.Select(t => t.FullName));
|
||||
|
||||
foreach (var userinfo in userinfoList)
|
||||
{
|
||||
messageToSend.Cc.Add(new MailboxAddress(String.Empty, craInfo.EMail));
|
||||
messageToSend.To.Add(new MailboxAddress(userinfo.FullName, userinfo.EMail));
|
||||
}
|
||||
|
||||
foreach (var user in craInfoList)
|
||||
{
|
||||
messageToSend.Cc.Add(new MailboxAddress(user.FullName, user.EMail));
|
||||
}
|
||||
|
||||
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
|
||||
|
||||
|
||||
|
||||
|
||||
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
|
||||
{
|
||||
var subjectCode = subjectVisit.Subject.Code;
|
||||
var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo, subjectCode, subjectVisit.VisitName);
|
||||
var htmlBodyStr = string.Format(
|
||||
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
|
||||
userinfo.FullName, // 用户名 {0}
|
||||
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr, workLanguage),
|
||||
toUserName, // 用户名 {0}
|
||||
trialInfo.ExperimentName, // 项目 {1}
|
||||
subjectCode, // 受试者 {2}
|
||||
subjectVisit.VisitName, // 访视 {3}
|
||||
@@ -337,10 +358,13 @@ public class QCRepliedQCChallengeEventConsumer(
|
||||
};
|
||||
|
||||
|
||||
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc);
|
||||
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc, workLanguage);
|
||||
|
||||
await SendEmailHelper.SendTrialEmailAsync(messageToSend, trialInfo);
|
||||
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -364,7 +388,7 @@ public class CRCRepliedCheckChallengeEventConsumer(
|
||||
private readonly SystemEmailSendConfig _systemEmailConfig = systemEmailConfig.CurrentValue;
|
||||
public async Task Consume(ConsumeContext<CRCRepliedCheckChallengeEvent> context)
|
||||
{
|
||||
Console.WriteLine("发送(019,020) 【 CRC 回复一致性核查质疑 通知PM】邮件!!!");
|
||||
Log.Logger.Warning("发送(019,020) 【 CRC 回复一致性核查质疑 通知PM】邮件!!!");
|
||||
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
|
||||
|
||||
|
||||
@@ -383,49 +407,58 @@ public class CRCRepliedCheckChallengeEventConsumer(
|
||||
{
|
||||
|
||||
|
||||
var trialUserList = await _trialUseRoleRepository.Where(x => x.TrialId == subjectVisit.TrialId && x.TrialUser.IsDeleted == false).Include(x => x.UserRole).Select(x => x.UserRole).ToListAsync();
|
||||
var trialUserList = await _trialUseRoleRepository.Where(x => x.TrialId == subjectVisit.TrialId && x.TrialUser.IsDeleted == false)
|
||||
.Select(t => new { t.UserRole.IdentityUser.EMail, t.UserRole.FullName, t.UserRole.UserTypeEnum, t.UserRole.IdentityUser.UserWorkLanguage }).ToListAsync();
|
||||
|
||||
var userinfoList = trialUserList.Where(x => x.UserTypeEnum == UserTypeEnum.APM || x.UserTypeEnum == UserTypeEnum.ProjectManager).ToList();
|
||||
|
||||
|
||||
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == subjectVisit.TrialId);
|
||||
|
||||
|
||||
|
||||
|
||||
var dictionValue = await CommonEmailHelper.TranslationDictionary(new TranslationDictionaryDto()
|
||||
// 遍历语言枚举 在底层判断收件人是否为空,决定是否跳过邮件发送,避免每个邮件场景的地方都重复判断
|
||||
foreach (UserWorkLanguage workLanguage in Enum.GetValues<UserWorkLanguage>())
|
||||
{
|
||||
|
||||
DictionaryRepository = _dictionaryRepository,
|
||||
IsEn_US = isEn_US,
|
||||
DictionaryList = new List<DictionaryDto>()
|
||||
{
|
||||
new DictionaryDto (){DictionaryCode= "CheckState",EnumValue=subjectVisit.CheckState.GetEnumInt(), }, //审核状态
|
||||
|
||||
}
|
||||
});
|
||||
//重新设置当前发送邮件的语言
|
||||
isEn_US = workLanguage == UserWorkLanguage.US;
|
||||
|
||||
|
||||
|
||||
var dictionValue = await CommonEmailHelper.TranslationDictionary(new TranslationDictionaryDto()
|
||||
{
|
||||
|
||||
DictionaryRepository = _dictionaryRepository,
|
||||
IsEn_US = isEn_US,
|
||||
DictionaryList = new List<DictionaryDto>()
|
||||
{
|
||||
new DictionaryDto (){DictionaryCode= "CheckState",EnumValue=subjectVisit.CheckState.GetEnumInt(), }, //审核状态
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
var userinfoList = trialUserList.Where(x => x.UserTypeEnum == UserTypeEnum.APM || x.UserTypeEnum == UserTypeEnum.ProjectManager)
|
||||
.Where(t => t.UserWorkLanguage == workLanguage).ToList();
|
||||
|
||||
|
||||
foreach (var userinfo in userinfoList)
|
||||
{
|
||||
var messageToSend = new MimeMessage();
|
||||
|
||||
//发件地址
|
||||
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
|
||||
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
|
||||
|
||||
var toUserName = string.Join('、', userinfoList.Select(t => t.FullName));
|
||||
foreach (var userinfo in userinfoList)
|
||||
{
|
||||
messageToSend.To.Add(new MailboxAddress(userinfo.FullName, userinfo.EMail));
|
||||
}
|
||||
|
||||
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
|
||||
|
||||
|
||||
|
||||
|
||||
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
|
||||
{
|
||||
var subjectCode = subjectVisit.Subject.Code;
|
||||
var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo, subjectCode, subjectVisit.VisitName);
|
||||
var htmlBodyStr = string.Format(
|
||||
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
|
||||
userinfo.FullName, // 用户名 {0}
|
||||
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr, workLanguage),
|
||||
toUserName, // 用户名 {0}
|
||||
trialInfo.ExperimentName, // 项目 {1}
|
||||
subjectCode, // 受试者 {2}
|
||||
subjectVisit.VisitName, // 访视 {3}
|
||||
@@ -437,10 +470,17 @@ public class CRCRepliedCheckChallengeEventConsumer(
|
||||
};
|
||||
|
||||
|
||||
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc);
|
||||
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc, workLanguage);
|
||||
|
||||
await SendEmailHelper.SendTrialEmailAsync(messageToSend, trialInfo);
|
||||
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -466,12 +506,12 @@ public class PMRepliedCheckChallengeEventConsumer(
|
||||
private readonly SystemEmailSendConfig _systemEmailConfig = systemEmailConfig.CurrentValue;
|
||||
public async Task Consume(ConsumeContext<PMRepliedCheckChallengeEvent> context)
|
||||
{
|
||||
Console.WriteLine("发送(016,017) 【 PM 一致性核查 通知CRC】邮件!!!");
|
||||
Log.Logger.Warning("发送(016,017) 【 PM 一致性核查 通知CRC】邮件!!!");
|
||||
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
|
||||
|
||||
var subjectVisitId = context.Message.SubjectVisitId;
|
||||
|
||||
var subjectVisit = await _subjectVisitRepository.Where(x => x.Id == subjectVisitId).Include(x => x.NoneDicomStudyList).Include(x => x.StudyList).Include(x => x.Subject).FirstNotNullAsync();
|
||||
var subjectVisit = await _subjectVisitRepository.Where(x => x.Id == subjectVisitId).Include(x => x.NoneDicomStudyList).Include(x => x.StudyList).Include(x => x.Subject).Include(t => t.TrialSite).FirstNotNullAsync();
|
||||
|
||||
var trialId = subjectVisit.TrialId;
|
||||
|
||||
@@ -486,11 +526,11 @@ public class PMRepliedCheckChallengeEventConsumer(
|
||||
var checkChallengeDialog = await _checkChallengeDialogRepository.Where(x => x.Id == context.Message.CheckChallengeDialogId).FirstNotNullAsync();
|
||||
|
||||
|
||||
var trialUserList = await _trialUseRoleRepository.Where(x => x.TrialId == subjectVisit.TrialId && x.TrialUser.IsDeleted == false).Include(x => x.UserRole).Select(x => x.UserRole).ToListAsync();
|
||||
var trialUserList = await _trialUseRoleRepository.Where(x => x.TrialId == subjectVisit.TrialId && x.TrialUser.IsDeleted == false)
|
||||
.Select(t => new { t.UserRole.IdentityUser.EMail, t.UserRole.FullName, t.UserRole.UserTypeEnum, t.UserRole.IdentityUser.UserWorkLanguage }).ToListAsync();
|
||||
|
||||
var userinfoList = trialUserList.Where(x => x.UserTypeEnum == UserTypeEnum.ClinicalResearchCoordinator).ToList();
|
||||
|
||||
var craInfo = trialUserList.Where(x => x.UserTypeEnum == UserTypeEnum.CRA).FirstOrDefault();
|
||||
var craInfoList = trialUserList.Where(x => x.UserTypeEnum == UserTypeEnum.CRA).ToList();
|
||||
|
||||
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == subjectVisit.TrialId);
|
||||
|
||||
@@ -499,28 +539,46 @@ public class PMRepliedCheckChallengeEventConsumer(
|
||||
modalities = subjectVisit.NoneDicomStudyList.Select(t => t.Modality)
|
||||
.Union(subjectVisit.StudyList.Select(k => k.ModalityForEdit)).ToList();
|
||||
|
||||
var dictionValue = await CommonEmailHelper.TranslationDictionary(new TranslationDictionaryDto()
|
||||
|
||||
|
||||
// 遍历语言枚举 在底层判断收件人是否为空,决定是否跳过邮件发送,避免每个邮件场景的地方都重复判断
|
||||
//foreach (UserWorkLanguage workLanguage in Enum.GetValues<UserWorkLanguage>())
|
||||
{
|
||||
//重新设置当前发送邮件的语言
|
||||
|
||||
DictionaryRepository = _dictionaryRepository,
|
||||
IsEn_US = isEn_US,
|
||||
DictionaryList = new List<DictionaryDto>()
|
||||
{
|
||||
new DictionaryDto (){DictionaryCode= "CheckState",EnumValue=subjectVisit.CheckState.GetEnumInt(), }, //核查状态
|
||||
isEn_US = subjectVisit.TrialSite.Country == StaticData.SiteCountry.US;
|
||||
|
||||
}
|
||||
});
|
||||
var workLanguage = isEn_US ? UserWorkLanguage.US : UserWorkLanguage.CN;
|
||||
|
||||
var userinfoList = trialUserList.Where(x => x.UserTypeEnum == UserTypeEnum.ClinicalResearchCoordinator).Where(t => t.UserWorkLanguage == workLanguage).ToList();
|
||||
|
||||
|
||||
foreach (var userinfo in userinfoList)
|
||||
{
|
||||
var dictionValue = await CommonEmailHelper.TranslationDictionary(new TranslationDictionaryDto()
|
||||
{
|
||||
|
||||
DictionaryRepository = _dictionaryRepository,
|
||||
IsEn_US = isEn_US,
|
||||
DictionaryList = new List<DictionaryDto>()
|
||||
{
|
||||
new DictionaryDto (){DictionaryCode= "CheckState",EnumValue=subjectVisit.CheckState.GetEnumInt(), }, //核查状态
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
var messageToSend = new MimeMessage();
|
||||
|
||||
//发件地址
|
||||
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
|
||||
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
|
||||
if (craInfo != null)
|
||||
|
||||
var toUserName = string.Join('、', userinfoList.Select(t => t.FullName));
|
||||
foreach (var userinfo in userinfoList)
|
||||
{
|
||||
messageToSend.Cc.Add(new MailboxAddress(String.Empty, craInfo.EMail));
|
||||
messageToSend.To.Add(new MailboxAddress(userinfo.FullName, userinfo.EMail));
|
||||
}
|
||||
|
||||
foreach (var user in craInfoList)
|
||||
{
|
||||
messageToSend.Cc.Add(new MailboxAddress(user.FullName, user.EMail));
|
||||
}
|
||||
|
||||
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
|
||||
@@ -531,8 +589,8 @@ public class PMRepliedCheckChallengeEventConsumer(
|
||||
var subjectCode = subjectVisit.Subject.Code;
|
||||
var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo, subjectCode, subjectVisit.VisitName);
|
||||
var htmlBodyStr = string.Format(
|
||||
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
|
||||
userinfo.FullName, // 用户名 {0}
|
||||
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr, workLanguage),
|
||||
toUserName, // 用户名 {0}
|
||||
trialInfo.ExperimentName, // 项目 {1}
|
||||
|
||||
subjectCode, // 受试者 {2}
|
||||
@@ -547,10 +605,11 @@ public class PMRepliedCheckChallengeEventConsumer(
|
||||
};
|
||||
|
||||
|
||||
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc);
|
||||
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc, workLanguage);
|
||||
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
|
||||
await SendEmailHelper.SendTrialEmailAsync(messageToSend, trialInfo);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -592,49 +651,57 @@ public class CheckStateChangedToAuditEventConsumer(
|
||||
|
||||
|
||||
|
||||
var trialUserList = await _trialUseRoleRepository.Where(x => x.TrialId == subjectVisit.TrialId && x.TrialUser.IsDeleted == false).Include(x => x.UserRole).Select(x => x.UserRole).ToListAsync();
|
||||
var trialUserList = await _trialUseRoleRepository.Where(x => x.TrialId == subjectVisit.TrialId && x.TrialUser.IsDeleted == false)
|
||||
.Select(t => new { t.UserRole.IdentityUser.EMail, t.UserRole.FullName, t.UserRole.UserTypeEnum, t.UserRole.IdentityUser.UserWorkLanguage }).ToListAsync();
|
||||
|
||||
var userinfoList = trialUserList.Where(x => x.UserTypeEnum == UserTypeEnum.APM || x.UserTypeEnum == UserTypeEnum.ProjectManager).ToList();
|
||||
|
||||
|
||||
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == subjectVisit.TrialId);
|
||||
|
||||
|
||||
|
||||
|
||||
var dictionValue = await CommonEmailHelper.TranslationDictionary(new TranslationDictionaryDto()
|
||||
// 遍历语言枚举 在底层判断收件人是否为空,决定是否跳过邮件发送,避免每个邮件场景的地方都重复判断
|
||||
foreach (UserWorkLanguage workLanguage in Enum.GetValues<UserWorkLanguage>())
|
||||
{
|
||||
//重新设置当前发送邮件的语言
|
||||
isEn_US = workLanguage == UserWorkLanguage.US;
|
||||
|
||||
DictionaryRepository = _dictionaryRepository,
|
||||
IsEn_US = isEn_US,
|
||||
DictionaryList = new List<DictionaryDto>()
|
||||
{
|
||||
new DictionaryDto (){DictionaryCode= "CheckState",EnumValue=subjectVisit.CheckState.GetEnumInt(), }, //审核状态
|
||||
var userinfoList = trialUserList.Where(x => x.UserTypeEnum == UserTypeEnum.APM || x.UserTypeEnum == UserTypeEnum.ProjectManager)
|
||||
.Where(t => t.UserWorkLanguage == workLanguage).ToList();
|
||||
|
||||
}
|
||||
});
|
||||
var dictionValue = await CommonEmailHelper.TranslationDictionary(new TranslationDictionaryDto()
|
||||
{
|
||||
|
||||
DictionaryRepository = _dictionaryRepository,
|
||||
IsEn_US = isEn_US,
|
||||
DictionaryList = new List<DictionaryDto>()
|
||||
{
|
||||
new DictionaryDto (){DictionaryCode= "CheckState",EnumValue=subjectVisit.CheckState.GetEnumInt(), }, //审核状态
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
foreach (var userinfo in userinfoList)
|
||||
{
|
||||
var messageToSend = new MimeMessage();
|
||||
//发件地址
|
||||
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
|
||||
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
|
||||
|
||||
var toUserName = string.Join('、', userinfoList.Select(t => t.FullName));
|
||||
|
||||
foreach (var userinfo in userinfoList)
|
||||
{
|
||||
messageToSend.To.Add(new MailboxAddress(userinfo.FullName, userinfo.EMail));
|
||||
}
|
||||
|
||||
|
||||
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
|
||||
|
||||
|
||||
|
||||
|
||||
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
|
||||
{
|
||||
var subjectCode = subjectVisit.Subject.Code;
|
||||
var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo, subjectCode, subjectVisit.VisitName);
|
||||
var htmlBodyStr = string.Format(
|
||||
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
|
||||
userinfo.FullName, // 姓名 {0}
|
||||
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr, workLanguage),
|
||||
toUserName, // 姓名 {0}
|
||||
trialInfo.ExperimentName, // 项目 {1}
|
||||
subjectCode, // 受试者 {2}
|
||||
subjectVisit.VisitName, // 访视 {3}
|
||||
@@ -646,10 +713,11 @@ public class CheckStateChangedToAuditEventConsumer(
|
||||
};
|
||||
|
||||
|
||||
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc);
|
||||
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc, workLanguage);
|
||||
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
|
||||
await SendEmailHelper.SendTrialEmailAsync(messageToSend, trialInfo);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -671,7 +739,7 @@ public class QCClaimTaskEventConsumer(
|
||||
private readonly SystemEmailSendConfig _systemEmailConfig = systemEmailConfig.CurrentValue;
|
||||
public async Task Consume(ConsumeContext<QCClaimTaskEvent> context)
|
||||
{
|
||||
Console.WriteLine("发送(Code007,008) 【QC 领取了质控任务】邮件!!!");
|
||||
Log.Logger.Warning("发送(Code007,008) 【QC 领取了质控任务】邮件!!!");
|
||||
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
|
||||
|
||||
|
||||
@@ -703,15 +771,23 @@ public class QCClaimTaskEventConsumer(
|
||||
|
||||
|
||||
var userinfo = subjectVisit.CurrentActionUser;
|
||||
if (userinfo == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//重新设置当前发送邮件的语言
|
||||
var workLanguage = _userRoleRepository.Where(t => t.Id == userinfo.Id).Select(t => t.IdentityUser.UserWorkLanguage).First();
|
||||
isEn_US = workLanguage == UserWorkLanguage.US;
|
||||
|
||||
var messageToSend = new MimeMessage();
|
||||
//发件地址
|
||||
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
|
||||
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
|
||||
messageToSend.To.Add(new MailboxAddress(userinfo.FullName, userinfo.EMail));
|
||||
|
||||
foreach (var pm in pmandAPm)
|
||||
{
|
||||
messageToSend.Cc.Add(new MailboxAddress(String.Empty, pm.EMail));
|
||||
messageToSend.Cc.Add(new MailboxAddress(pm.FullName, pm.EMail));
|
||||
}
|
||||
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
|
||||
|
||||
@@ -741,7 +817,7 @@ public class QCClaimTaskEventConsumer(
|
||||
var subjectCode = subjectVisit.Subject.Code;
|
||||
var topicStr = string.Format(input.topicStr, trialInfo.ResearchProgramNo, subjectCode, subjectVisit.VisitName);
|
||||
var htmlBodyStr = string.Format(
|
||||
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
|
||||
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr, workLanguage),
|
||||
userinfo.FullName, // 用户名 {0}
|
||||
trialInfo.ExperimentName, // 项目 {1}
|
||||
string.Join(',', subjectcodes), // 受试者 {2}
|
||||
@@ -755,9 +831,9 @@ public class QCClaimTaskEventConsumer(
|
||||
};
|
||||
|
||||
|
||||
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc);
|
||||
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc, workLanguage);
|
||||
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
|
||||
await SendEmailHelper.SendTrialEmailAsync(messageToSend, trialInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using IRaCIS.Core.Application.Helper.OtherTool;
|
||||
using IRaCIS.Core.Domain.BaseModel;
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using MassTransit;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using NPOI.SS.Formula.Functions;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
public class ConsumeExceptionFilter<T>(ILogger<ConsumeExceptionFilter<T>> _logger) : IFilter<ConsumeContext<T>> where T : DomainEvent
|
||||
public class ConsumeExceptionFilter<T>(ILogger<ConsumeExceptionFilter<T>> _logger, IConfiguration _config) : IFilter<ConsumeContext<T>> where T : DomainEvent
|
||||
{
|
||||
|
||||
public async Task Send(ConsumeContext<T> context, IPipe<ConsumeContext<T>> next)
|
||||
@@ -23,6 +25,44 @@ public class ConsumeExceptionFilter<T>(ILogger<ConsumeExceptionFilter<T>> _logge
|
||||
{
|
||||
var errorInfo = $"Exception: {exception.Message}[{exception.StackTrace}]" + (exception.InnerException != null ? $" InnerException: {exception.InnerException.Message}[{exception.InnerException.StackTrace}]" : "");
|
||||
_logger.LogError(errorInfo);
|
||||
|
||||
try
|
||||
{
|
||||
bool isOpenWeComNotice = _config.GetValue<bool>("WeComNoticeConfig:IsOpenWeComNotice");
|
||||
|
||||
if (isOpenWeComNotice)
|
||||
{
|
||||
string webhook = _config["WeComNoticeConfig:WebhookUrl"] ?? string.Empty;
|
||||
|
||||
var uri = new Uri(_config["SystemEmailSendConfig:SiteUrl"]);
|
||||
var baseUrl = uri.GetLeftPart(UriPartial.Authority);
|
||||
|
||||
var userList = _config.GetSection("WeComNoticeConfig:APINoticeUserList").Get<string[]>();
|
||||
|
||||
|
||||
|
||||
// 🔔 异步告警(不要阻塞请求)
|
||||
_ = WeComNotifier.SendAlertAsync(
|
||||
webhook: webhook,
|
||||
alert: new WeComAlert
|
||||
{
|
||||
Env = baseUrl,
|
||||
UserName = "MassTransit 自动触发邮件",
|
||||
Api = "",
|
||||
Message = $"异常信息:{exception.Message} ",
|
||||
Stack = exception.StackTrace,
|
||||
AtUsers = userList ?? []
|
||||
}
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Logger.Error($"MassTransit里发送企业微信出现错误:{ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
using System.Globalization;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using IRaCIS.Core.Domain.BaseModel;
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using IRaCIS.Core.Domain.BaseModel;
|
||||
using MassTransit;
|
||||
using NPOI.SS.Formula.Functions;
|
||||
using System.Globalization;
|
||||
|
||||
public class CultureInfoFilter<T> (IRepository<EventStoreRecord> _eventStoreRecordRepository) : IFilter<ConsumeContext<T>> where T : DomainEvent
|
||||
{
|
||||
|
||||
@@ -5,6 +5,7 @@ using IRaCIS.Core.Application.Contracts;
|
||||
using IRaCIS.Core.Application.Helper;
|
||||
using IRaCIS.Core.Application.MassTransit.Consumer;
|
||||
using IRaCIS.Core.Application.Service.Reading.Dto;
|
||||
using IRaCIS.Core.Domain.Models;
|
||||
using MassTransit;
|
||||
using Microsoft.Extensions.Options;
|
||||
using MimeKit;
|
||||
@@ -26,6 +27,7 @@ namespace IRaCIS.Core.Application.MassTransit.Recurring
|
||||
public class UrgentIRUnReadTaskRecurringEventConsumer(
|
||||
IRepository<ReadingQuestionCriterionTrial> _trialReadingCriterionRepository,
|
||||
IRepository<VisitTask> _visitTaskRepository,
|
||||
IRepository<Trial> _trialRepository,
|
||||
IRepository<Dictionary> _dictionaryRepository,
|
||||
IRepository<TrialUserRole> _trialUserRoleRepository,
|
||||
IRepository<TrialEmailNoticeConfig> _trialEmailNoticeConfigrepository,
|
||||
@@ -35,9 +37,15 @@ namespace IRaCIS.Core.Application.MassTransit.Recurring
|
||||
|
||||
public async Task Consume(ConsumeContext<UrgentIRUnReadTaskRecurringEvent> context)
|
||||
{
|
||||
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
|
||||
var isEn_US = context.Message.CultureInfoName == StaticData.CultureInfo.en_US;
|
||||
|
||||
//设置当前事件传递过来的语言
|
||||
var culture = context.Message.CultureInfoName;
|
||||
CultureInfo.CurrentCulture = new CultureInfo(culture);
|
||||
|
||||
var trialId = context.Message.TrialId;
|
||||
|
||||
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == trialId);
|
||||
var scenario = EmailBusinessScenario.ExpeditedReading;
|
||||
var trialEmailConfig = _trialEmailNoticeConfigrepository.Where(t => t.TrialId == trialId && t.BusinessScenarioEnum == scenario && t.IsAutoSend && t.IsEnable).FirstOrDefault();
|
||||
|
||||
@@ -66,13 +74,17 @@ namespace IRaCIS.Core.Application.MassTransit.Recurring
|
||||
t.UserRole.IdentityUser.EMail,
|
||||
t.UserRole.IdentityUser.UserName,
|
||||
t.Trial.TrialCode,
|
||||
t.Trial.ResearchProgramNo
|
||||
t.Trial.ResearchProgramNo,
|
||||
t.UserRole.IdentityUser.UserWorkLanguage
|
||||
//TrialReadingCriterionList = t.Trial.TrialReadingCriterionList.Select(t => new { t.CriterionName, TrialReadingCriterionId = t.Id }).ToList()
|
||||
});
|
||||
|
||||
|
||||
foreach (var trialUser in trialUserList)
|
||||
{
|
||||
//重新设置当前发送邮件的语言
|
||||
var workLanguage = trialUser.UserWorkLanguage;
|
||||
isEn_US = workLanguage == UserWorkLanguage.US;
|
||||
|
||||
var userId = trialUser.UserId;
|
||||
|
||||
@@ -145,7 +157,7 @@ namespace IRaCIS.Core.Application.MassTransit.Recurring
|
||||
|
||||
|
||||
|
||||
var htmlBodyStr = string.Format(CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, emailContent),
|
||||
var htmlBodyStr = string.Format(CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, emailContent, workLanguage),
|
||||
trialUser.FullName,
|
||||
|
||||
DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
|
||||
@@ -159,9 +171,9 @@ namespace IRaCIS.Core.Application.MassTransit.Recurring
|
||||
return (topicStr, htmlBodyStr);
|
||||
};
|
||||
|
||||
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc);
|
||||
await CommonEmailHelper.GetTrialEmailSubejctAndHtmlInfoAndBuildAsync(trialEmailConfig, messageToSend, emailConfigFunc, workLanguage);
|
||||
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
|
||||
await SendEmailHelper.SendTrialEmailAsync(messageToSend, trialInfo);
|
||||
|
||||
//处理标记已通知的任务
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ public static class OldRecurringEmailHelper
|
||||
public static string EmailNamePlaceholder => StaticData.EmailSend.EmailNamePlaceholder;
|
||||
|
||||
|
||||
|
||||
//发送项目邮件的通用方法 已经处理
|
||||
public static async Task SendTrialEmailAsync(
|
||||
IRepository<TrialEmailNoticeConfig> _trialEmailNoticeConfigRepository,
|
||||
IRepository<Trial> _trialRepository,
|
||||
@@ -34,6 +34,7 @@ public static class OldRecurringEmailHelper
|
||||
var trialEmailConfig = await _trialEmailNoticeConfigRepository.Where(t => t.TrialId == trialId && t.BusinessScenarioEnum == businessScenario, ignoreQueryFilters: true)
|
||||
.Include(t => t.TrialEmailNoticeUserList).Include(t => t.TrialEmailBlackUserList).FirstOrDefaultAsync();
|
||||
|
||||
var trialInfo=await _trialRepository.Where(t=>t.Id== trialId).FirstOrDefaultAsync();
|
||||
|
||||
if (trialEmailConfig == null || trialEmailConfig.IsAutoSend == false || trialEmailConfig.IsEnable == false)
|
||||
{
|
||||
@@ -162,7 +163,7 @@ public static class OldRecurringEmailHelper
|
||||
if (sendEmailConfig != null)
|
||||
{
|
||||
|
||||
await SendEmailHelper.SendEmailAsync(sendEmailConfig);
|
||||
await SendEmailHelper.SendEmailAsync(sendEmailConfig, trialInfo);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ using MassTransit.Scheduling;
|
||||
using Microsoft.Extensions.Options;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
@@ -14,7 +15,7 @@ using System.Threading.Tasks;
|
||||
namespace IRaCIS.Core.Application.MassTransit.Consumer;
|
||||
|
||||
|
||||
|
||||
//项目手动选择 周期性邮件
|
||||
|
||||
|
||||
/// <summary>
|
||||
@@ -34,17 +35,26 @@ public class QCImageQuestionRecurringEventConsumer(IRepository<Trial> _trialRepo
|
||||
{
|
||||
var trialId = context.Message.TrialId;
|
||||
|
||||
var isEn_us = false;
|
||||
var isEn_us = context.Message.CultureInfoName == StaticData.CultureInfo.en_US;
|
||||
|
||||
//设置当前事件传递过来的语言
|
||||
var culture = context.Message.CultureInfoName;
|
||||
CultureInfo.CurrentCulture = new CultureInfo(culture);
|
||||
|
||||
var trialInfo = await _trialRepository.Where(t => t.Id == trialId).Select(t => new { t.ResearchProgramNo, t.ExperimentName, t.TrialCode, t.TrialStatusStr }).FirstNotNullAsync();
|
||||
|
||||
//找到 该项目的CRC 用户Id
|
||||
var userList = await _trialUserRoleRepository.Where(t => t.TrialId == trialId).Where(t => t.UserRole.UserTypeEnum == UserTypeEnum.ClinicalResearchCoordinator).Select(t => new { t.UserId, t.UserRole.FullName }).ToListAsync();
|
||||
var userList = await _trialUserRoleRepository.Where(t => t.TrialId == trialId).Where(t => t.UserRole.UserTypeEnum == UserTypeEnum.ClinicalResearchCoordinator).Select(t => new { t.UserId, t.UserRole.FullName, t.UserRole.IdentityUser.UserWorkLanguage }).ToListAsync();
|
||||
|
||||
//判断是否任务可以领取 ,可以的话 发送邮件
|
||||
|
||||
foreach (var user in userList)
|
||||
{
|
||||
|
||||
//重新设置当前发送邮件的语言
|
||||
var workLanguage = user.UserWorkLanguage;
|
||||
isEn_us = workLanguage == UserWorkLanguage.US;
|
||||
|
||||
var userId = user.UserId;
|
||||
//过滤项目 并且 将 _userInfo.Id 换位 当前发送邮件的Id
|
||||
var query = _trialRepository.Where(t => t.Id == trialId)
|
||||
@@ -67,7 +77,7 @@ public class QCImageQuestionRecurringEventConsumer(IRepository<Trial> _trialRepo
|
||||
{
|
||||
var topicStr = string.Format(isEn_us ? trialEmailConfig.EmailTopic : trialEmailConfig.EmailTopicCN, trialInfo.ResearchProgramNo);
|
||||
var htmlContent = isEn_us ? trialEmailConfig.EmailHtmlContent : trialEmailConfig.EmailHtmlContentCN;
|
||||
var htmlBodyStr = string.Format(CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, htmlContent),
|
||||
var htmlBodyStr = string.Format(CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, htmlContent, workLanguage),
|
||||
user.FullName, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), sendStat.ToBeDealedCount, _SystemEmailSendConfig.CurrentValue.SiteUrl);
|
||||
|
||||
return (topicStr, htmlBodyStr, isEn_us, userId);
|
||||
@@ -102,16 +112,26 @@ public class CRCImageQuestionRecurringEventConsumer(IRepository<Trial> _trialRep
|
||||
{
|
||||
var trialId = context.Message.TrialId;
|
||||
|
||||
var isEn_us = false;
|
||||
var isEn_us = context.Message.CultureInfoName == StaticData.CultureInfo.en_US;
|
||||
|
||||
//设置当前事件传递过来的语言
|
||||
var culture = context.Message.CultureInfoName;
|
||||
CultureInfo.CurrentCulture = new CultureInfo(culture);
|
||||
|
||||
var trialInfo = await _trialRepository.Where(t => t.Id == trialId).Select(t => new { t.ResearchProgramNo, t.ExperimentName, t.TrialCode, t.TrialStatusStr, t.DeclarationTypeEnumList }).FirstNotNullAsync();
|
||||
|
||||
//找到 该项目的IQC 用户Id
|
||||
var userList = await _trialUserRoleRepository.Where(t => t.TrialId == trialId).Where(t => t.UserRole.UserTypeEnum == UserTypeEnum.IQC).Select(t => new { t.UserId, t.UserRole.FullName }).ToListAsync();
|
||||
var userList = await _trialUserRoleRepository.Where(t => t.TrialId == trialId).Where(t => t.UserRole.UserTypeEnum == UserTypeEnum.IQC).Select(t => new { t.UserId, t.UserRole.FullName, t.UserRole.IdentityUser.UserWorkLanguage }).ToListAsync();
|
||||
|
||||
//判断是否任务可以领取 ,可以的话 发送邮件
|
||||
|
||||
foreach (var user in userList)
|
||||
{
|
||||
|
||||
//重新设置当前发送邮件的语言
|
||||
var workLanguage = user.UserWorkLanguage;
|
||||
isEn_us = workLanguage == UserWorkLanguage.US;
|
||||
|
||||
var userId = user.UserId;
|
||||
|
||||
//过滤项目 并且 将 _userInfo.Id 换位 当前发送邮件的Id
|
||||
@@ -137,7 +157,7 @@ public class CRCImageQuestionRecurringEventConsumer(IRepository<Trial> _trialRep
|
||||
{
|
||||
var topicStr = string.Format(isEn_us ? trialEmailConfig.EmailTopic : trialEmailConfig.EmailTopicCN, trialInfo.ResearchProgramNo);
|
||||
var htmlContent = isEn_us ? trialEmailConfig.EmailHtmlContent : trialEmailConfig.EmailHtmlContentCN;
|
||||
var htmlBodyStr = string.Format(CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, htmlContent),
|
||||
var htmlBodyStr = string.Format(CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, htmlContent, workLanguage),
|
||||
|
||||
user.FullName, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), sendStat.ToBeDealedCount - sendStat.ReUploadTobeDealedCount, sendStat.ReUploadTobeDealedCount, _SystemEmailSendConfig.CurrentValue.SiteUrl);
|
||||
|
||||
@@ -171,20 +191,28 @@ public class ImageQCRecurringEventConsumer(IRepository<Trial> _trialRepository,
|
||||
private readonly SystemEmailSendConfig _systemEmailConfig = systemEmailConfig.CurrentValue;
|
||||
public async Task Consume(ConsumeContext<ImageQCRecurringEvent> context)
|
||||
{
|
||||
var trialId=context.Message.TrialId;
|
||||
var trialId = context.Message.TrialId;
|
||||
|
||||
var isEn_us = false;
|
||||
var isEn_us = context.Message.CultureInfoName == StaticData.CultureInfo.en_US;
|
||||
|
||||
//设置当前事件传递过来的语言
|
||||
var culture = context.Message.CultureInfoName;
|
||||
CultureInfo.CurrentCulture = new CultureInfo(culture);
|
||||
|
||||
var trialInfo = await _trialRepository.Where(t => t.Id == trialId).Select(t => new { t.ResearchProgramNo, t.ExperimentName, t.TrialCode, t.TrialStatusStr }).FirstNotNullAsync();
|
||||
|
||||
//找到 该项目的IQC 用户Id
|
||||
var userList = await _trialUserRoleRepository.Where(t => t.TrialId == trialId).Where(t => t.UserRole.UserTypeEnum == UserTypeEnum.IQC).Select(t => new { t.UserId, t.UserRole.FullName }).ToListAsync();
|
||||
var userList = await _trialUserRoleRepository.Where(t => t.TrialId == trialId).Where(t => t.UserRole.UserTypeEnum == UserTypeEnum.IQC).Select(t => new { t.UserId, t.UserRole.FullName, t.UserRole.IdentityUser.UserWorkLanguage }).ToListAsync();
|
||||
|
||||
//判断是否任务可以领取 ,可以的话 发送邮件
|
||||
var userIdList = userList.Select(t => t.UserId).ToList();
|
||||
|
||||
foreach (var user in userList)
|
||||
{
|
||||
//重新设置当前发送邮件的语言
|
||||
var workLanguage = user.UserWorkLanguage;
|
||||
isEn_us = workLanguage == UserWorkLanguage.US;
|
||||
|
||||
var userId = user.UserId;
|
||||
|
||||
//过滤项目 并且 将 _userInfo.Id 换位 当前发送邮件的Id
|
||||
@@ -208,11 +236,11 @@ public class ImageQCRecurringEventConsumer(IRepository<Trial> _trialRepository,
|
||||
|
||||
Func<TrialEmailNoticeConfig, (string topicStr, string htmlBodyStr, bool isEn_us, Guid? onlyToUserId)> topicAndHtmlFunc = trialEmailConfig =>
|
||||
{
|
||||
|
||||
|
||||
var topicStr = string.Format(isEn_us ? trialEmailConfig.EmailTopic : trialEmailConfig.EmailTopicCN, trialInfo.ResearchProgramNo);
|
||||
|
||||
var htmlContent = isEn_us ? trialEmailConfig.EmailHtmlContent : trialEmailConfig.EmailHtmlContentCN;
|
||||
var htmlBodyStr = string.Format(CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, htmlContent),
|
||||
var htmlBodyStr = string.Format(CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, htmlContent, workLanguage),
|
||||
user.FullName, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), sendStat.ToBeClaimedCount, sendStat.ToBeReviewedCount, _SystemEmailSendConfig.CurrentValue.SiteUrl);
|
||||
|
||||
return (topicStr, htmlBodyStr, false, userId);
|
||||
|
||||
@@ -5,6 +5,7 @@ using IRaCIS.Core.Application.Helper;
|
||||
using IRaCIS.Core.Application.MassTransit.Consumer;
|
||||
using IRaCIS.Core.Application.Service.Reading.Dto;
|
||||
using IRaCIS.Core.Domain.Models;
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using MassTransit;
|
||||
using Microsoft.Extensions.Options;
|
||||
using MimeKit;
|
||||
@@ -42,14 +43,21 @@ namespace IRaCIS.Core.Application.MassTransit.Recurring
|
||||
|
||||
DateTime now = DateTime.Now;
|
||||
Console.WriteLine("发送定时过期提醒");
|
||||
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
|
||||
var isEn_US = context.Message.CultureInfoName == StaticData.CultureInfo.en_US;
|
||||
|
||||
//设置当前事件传递过来的语言
|
||||
var culture = context.Message.CultureInfoName;
|
||||
CultureInfo.CurrentCulture = new CultureInfo(culture);
|
||||
|
||||
var systemDocQuery =
|
||||
from sysDoc in _systemDocumentRepository.AsQueryable(false)
|
||||
from identityUser in _identityUserRepository.AsQueryable(false).Where(t => t.UserRoleList.Where(t => t.IsUserRoleDisabled == false).Any(t => sysDoc.NeedConfirmedUserTypeList.AsQueryable().Any(c => c.NeedConfirmUserTypeId == t.UserTypeId)))
|
||||
from identityUser in _identityUserRepository.AsQueryable(false).Where(t => t.Status == UserStateEnum.Enable).Where(t => t.UserRoleList.Where(t => t.IsUserRoleDisabled == false).Any(t => sysDoc.NeedConfirmedUserTypeList.AsQueryable().Any(c => c.NeedConfirmUserTypeId == t.UserTypeId)))
|
||||
join confirmc in _systemDocConfirmedUserRepository.Where() on new { ConfirmUserId = identityUser.Id, SystemDocumentId = sysDoc.Id } equals new { confirmc.ConfirmUserId, confirmc.SystemDocumentId } into cc
|
||||
from confirm in cc.DefaultIfEmpty()
|
||||
select new UnionDocumentWithConfirmInfoView()
|
||||
select new TriaAndSystemlSignDocView()
|
||||
{
|
||||
DocLanguageType=sysDoc.DocLanguageType,
|
||||
UserWorkLanguage=identityUser.UserWorkLanguage,
|
||||
IsSystemDoc = true,
|
||||
Id = sysDoc.Id,
|
||||
CreateTime = sysDoc.CreateTime,
|
||||
@@ -73,205 +81,211 @@ namespace IRaCIS.Core.Application.MassTransit.Recurring
|
||||
};
|
||||
var datalist = await systemDocQuery.IgnoreQueryFilters().Where(t => t.IsDeleted == false && t.ConfirmTime == null)
|
||||
.ToListAsync();
|
||||
datalist = datalist.Where(x => x.SuggestFinishTime != null && x.SuggestFinishTime.Value.Date == DateTime.Now.Date)
|
||||
datalist = datalist
|
||||
.Where(x=>x.DocLanguageType == DocLanguageType.CN_US || (int)x.DocLanguageType == (int)x.UserWorkLanguage)
|
||||
.Where(x => x.SuggestFinishTime != null && x.SuggestFinishTime.Value.Date == DateTime.Now.Date)
|
||||
.Where(x => x.IsNeedSendEmial).ToList();
|
||||
var confirmUserIdList = datalist.Select(t => t.ConfirmUserId).Distinct().ToList();
|
||||
var userinfoList = await _identityUserRepository.Where(x => confirmUserIdList.Contains(x.Id)).ToListAsync();
|
||||
|
||||
Console.WriteLine("发送定时过期提醒:人员数量" + userinfoList.Count);
|
||||
int index = 1;
|
||||
foreach (var userinfo in userinfoList)
|
||||
|
||||
var scenario = EmailBusinessScenario.GeneralTraining_ExpirationNotification;
|
||||
var emailConfig = _emailNoticeConfigrepository.Where(t => t.BusinessScenarioEnum == scenario && t.IsAutoSend && t.IsEnable).FirstOrDefault();
|
||||
if (emailConfig == null)
|
||||
{
|
||||
try
|
||||
{
|
||||
Console.WriteLine($"{index}发送定时过期提醒,邮箱:{userinfo.EMail},姓名{userinfo.UserName}");
|
||||
index++;
|
||||
var messageToSend = new MimeMessage();
|
||||
//发件地址
|
||||
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
|
||||
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
|
||||
|
||||
|
||||
|
||||
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
|
||||
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
|
||||
{
|
||||
var topicStr = string.Format(input.topicStr, companyName);
|
||||
|
||||
var htmlBodyStr = string.Format(
|
||||
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
|
||||
userinfo.UserName, // 用户名 {0}
|
||||
_systemEmailConfig.SiteUrl
|
||||
);
|
||||
|
||||
return (topicStr, htmlBodyStr);
|
||||
};
|
||||
|
||||
var scenario = EmailBusinessScenario.GeneralTraining_ExpirationNotification;
|
||||
|
||||
var emailConfig = _emailNoticeConfigrepository.Where(t => t.BusinessScenarioEnum == scenario && t.IsAutoSend && t.IsEnable).FirstOrDefault();
|
||||
|
||||
if (emailConfig != null)
|
||||
{
|
||||
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(emailConfig, messageToSend, emailConfigFunc);
|
||||
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var userWorkLanguageList = userinfoList.Select(x => x.UserWorkLanguage).Distinct().ToList();
|
||||
foreach (var workLanguage in userWorkLanguageList)
|
||||
{
|
||||
var langUserInfoList = userinfoList.Where(x => x.UserWorkLanguage == workLanguage).ToList();
|
||||
if (!langUserInfoList.Any())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var userIsEn_US = workLanguage == UserWorkLanguage.US;
|
||||
var companyName = userIsEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
|
||||
|
||||
foreach (var userinfo in langUserInfoList)
|
||||
{
|
||||
try
|
||||
{
|
||||
var messageToSend = new MimeMessage();
|
||||
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
|
||||
messageToSend.To.Add(new MailboxAddress(userinfo.FullName, userinfo.EMail));
|
||||
|
||||
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
|
||||
{
|
||||
var topicStr = string.Format(input.topicStr, companyName);
|
||||
|
||||
var htmlBodyStr = string.Format(
|
||||
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr, workLanguage),
|
||||
userinfo.FullName,
|
||||
_systemEmailConfig.SiteUrl
|
||||
);
|
||||
|
||||
return (topicStr, htmlBodyStr);
|
||||
};
|
||||
|
||||
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(emailConfig, messageToSend, emailConfigFunc, workLanguage);
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生效通知
|
||||
/// </summary>
|
||||
public class SystemDocumentPublishEventConsumer(
|
||||
IRepository<ReadingQuestionCriterionTrial> _trialReadingCriterionRepository,
|
||||
IRepository<VisitTask> _visitTaskRepository,
|
||||
IRepository<SystemDocument> _systemDocumentRepository,
|
||||
IRepository<IdentityUser> _identityUserRepository,
|
||||
IRepository<SystemDocConfirmedIdentityUser> _systemDocConfirmedUserRepository,
|
||||
IRepository<Dictionary> _dictionaryRepository,
|
||||
IRepository<TrialUserRole> _trialUserRoleRepository,
|
||||
IRepository<EmailNoticeConfig> _emailNoticeConfigrepository,
|
||||
/// <summary>
|
||||
/// 生效通知
|
||||
/// </summary>
|
||||
public class SystemDocumentPublishEventConsumer(
|
||||
IRepository<ReadingQuestionCriterionTrial> _trialReadingCriterionRepository,
|
||||
IRepository<VisitTask> _visitTaskRepository,
|
||||
IRepository<SystemDocument> _systemDocumentRepository,
|
||||
IRepository<IdentityUser> _identityUserRepository,
|
||||
IRepository<SystemDocConfirmedIdentityUser> _systemDocConfirmedUserRepository,
|
||||
IRepository<Dictionary> _dictionaryRepository,
|
||||
IRepository<TrialUserRole> _trialUserRoleRepository,
|
||||
IRepository<EmailNoticeConfig> _emailNoticeConfigrepository,
|
||||
|
||||
IOptionsMonitor<SystemEmailSendConfig> systemEmailConfig) : IConsumer<SystemDocumentPublishEvent>
|
||||
{
|
||||
private readonly SystemEmailSendConfig _systemEmailConfig = systemEmailConfig.CurrentValue;
|
||||
|
||||
public async Task Consume(ConsumeContext<SystemDocumentPublishEvent> context)
|
||||
IOptionsMonitor<SystemEmailSendConfig> systemEmailConfig) : IConsumer<SystemDocumentPublishEvent>
|
||||
{
|
||||
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
|
||||
private readonly SystemEmailSendConfig _systemEmailConfig = systemEmailConfig.CurrentValue;
|
||||
|
||||
// 记录是否只发送给新增角色的日志
|
||||
if (context.Message.NewUserTypeIds != null && context.Message.NewUserTypeIds.Any())
|
||||
public async Task Consume(ConsumeContext<SystemDocumentPublishEvent> context)
|
||||
{
|
||||
Console.WriteLine($"只发送给新增的角色,角色数量: {context.Message.NewUserTypeIds.Count}");
|
||||
}
|
||||
// 构建查询
|
||||
IQueryable<UnionDocumentWithConfirmInfoView> systemDocQuery;
|
||||
var isEn_US = context.Message.CultureInfoName == StaticData.CultureInfo.en_US;
|
||||
|
||||
if (context.Message.NewUserTypeIds != null && context.Message.NewUserTypeIds.Any())
|
||||
{
|
||||
// 只查询新增角色的用户
|
||||
systemDocQuery =
|
||||
from sysDoc in _systemDocumentRepository.AsQueryable(false).Where(x => context.Message.Ids.Contains(x.Id))
|
||||
from identityUser in _identityUserRepository.AsQueryable(false)
|
||||
.Where(t => t.Status == UserStateEnum.Enable &&
|
||||
t.UserRoleList.Where(t => t.IsUserRoleDisabled == false)
|
||||
.Any(t => context.Message.NewUserTypeIds.Contains(t.UserTypeId) &&
|
||||
sysDoc.NeedConfirmedUserTypeList.AsQueryable().Any(c => c.NeedConfirmUserTypeId == t.UserTypeId)))
|
||||
select new UnionDocumentWithConfirmInfoView()
|
||||
//设置当前事件传递过来的语言
|
||||
var culture = context.Message.CultureInfoName;
|
||||
CultureInfo.CurrentCulture = new CultureInfo(culture);
|
||||
|
||||
// 记录是否只发送给新增角色的日志
|
||||
if (context.Message.NewUserTypeIds != null && context.Message.NewUserTypeIds.Any())
|
||||
{
|
||||
IsSystemDoc = true,
|
||||
Id = sysDoc.Id,
|
||||
CreateTime = sysDoc.CreateTime,
|
||||
IsDeleted = sysDoc.IsDeleted,
|
||||
SignViewMinimumMinutes = sysDoc.SignViewMinimumMinutes,
|
||||
Name = sysDoc.Name,
|
||||
Path = sysDoc.Path,
|
||||
FileTypeId = sysDoc.FileTypeId,
|
||||
UpdateTime = sysDoc.UpdateTime,
|
||||
ConfirmUserId = identityUser.Id,
|
||||
RealName = identityUser.FullName,
|
||||
UserName = identityUser.UserName,
|
||||
IsNeedSendEmial = identityUser.IsZhiZhun || (!identityUser.IsZhiZhun && sysDoc.DocUserSignType == DocUserSignType.InnerAndOuter),
|
||||
FullFilePath = sysDoc.Path
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
// 查询所有相关角色的用户
|
||||
systemDocQuery =
|
||||
from sysDoc in _systemDocumentRepository.AsQueryable(false).Where(x => context.Message.Ids.Contains(x.Id))
|
||||
from identityUser in _identityUserRepository.AsQueryable(false)
|
||||
.Where(t => t.Status == UserStateEnum.Enable &&
|
||||
t.UserRoleList.Where(t => t.IsUserRoleDisabled == false)
|
||||
.Any(t => sysDoc.NeedConfirmedUserTypeList.AsQueryable().Any(c => c.NeedConfirmUserTypeId == t.UserTypeId)))
|
||||
select new UnionDocumentWithConfirmInfoView()
|
||||
Console.WriteLine($"只发送给新增的角色,角色数量: {context.Message.NewUserTypeIds.Count}");
|
||||
}
|
||||
// 构建查询
|
||||
IQueryable<TriaAndSystemlSignDocView> systemDocQuery;
|
||||
|
||||
if (context.Message.NewUserTypeIds != null && context.Message.NewUserTypeIds.Any())
|
||||
{
|
||||
IsSystemDoc = true,
|
||||
Id = sysDoc.Id,
|
||||
CreateTime = sysDoc.CreateTime,
|
||||
IsDeleted = sysDoc.IsDeleted,
|
||||
SignViewMinimumMinutes = sysDoc.SignViewMinimumMinutes,
|
||||
Name = sysDoc.Name,
|
||||
Path = sysDoc.Path,
|
||||
FileTypeId = sysDoc.FileTypeId,
|
||||
UpdateTime = sysDoc.UpdateTime,
|
||||
ConfirmUserId = identityUser.Id,
|
||||
RealName = identityUser.FullName,
|
||||
UserName = identityUser.UserName,
|
||||
IsNeedSendEmial = identityUser.IsZhiZhun || (!identityUser.IsZhiZhun && sysDoc.DocUserSignType == DocUserSignType.InnerAndOuter),
|
||||
FullFilePath = sysDoc.Path
|
||||
};
|
||||
}
|
||||
var datalist = await systemDocQuery.IgnoreQueryFilters().Where(x => x.IsNeedSendEmial).ToListAsync();
|
||||
|
||||
var confirmUserIdList = datalist.Select(t => t.ConfirmUserId).Distinct().ToList();
|
||||
var userinfoList = await _identityUserRepository.Where(x => confirmUserIdList.Contains(x.Id)).ToListAsync();
|
||||
int index = 1;
|
||||
foreach (var userinfo in userinfoList)
|
||||
{
|
||||
string msg = $"{index}生效通知,邮箱:{userinfo.EMail},姓名{userinfo.UserName},";
|
||||
index++;
|
||||
try
|
||||
{
|
||||
|
||||
var messageToSend = new MimeMessage();
|
||||
//发件地址
|
||||
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
|
||||
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
|
||||
|
||||
|
||||
|
||||
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
|
||||
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
|
||||
// 只查询新增角色的用户
|
||||
systemDocQuery =
|
||||
from sysDoc in _systemDocumentRepository.AsQueryable(false).Where(x => context.Message.Ids.Contains(x.Id))
|
||||
from identityUser in _identityUserRepository.AsQueryable(false)
|
||||
.Where(t => t.Status == UserStateEnum.Enable &&
|
||||
t.UserRoleList.Where(t => t.IsUserRoleDisabled == false)
|
||||
.Any(t => context.Message.NewUserTypeIds.Contains(t.UserTypeId) &&
|
||||
sysDoc.NeedConfirmedUserTypeList.AsQueryable().Any(c => c.NeedConfirmUserTypeId == t.UserTypeId)))
|
||||
select new TriaAndSystemlSignDocView()
|
||||
{
|
||||
var topicStr = string.Format(input.topicStr, companyName);
|
||||
|
||||
var htmlBodyStr = string.Format(
|
||||
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
|
||||
userinfo.UserName, // 用户名 {0}
|
||||
_systemEmailConfig.SiteUrl
|
||||
);
|
||||
|
||||
return (topicStr, htmlBodyStr);
|
||||
DocLanguageType = sysDoc.DocLanguageType,
|
||||
UserWorkLanguage = identityUser.UserWorkLanguage,
|
||||
IsSystemDoc = true,
|
||||
Id = sysDoc.Id,
|
||||
CreateTime = sysDoc.CreateTime,
|
||||
IsDeleted = sysDoc.IsDeleted,
|
||||
SignViewMinimumMinutes = sysDoc.SignViewMinimumMinutes,
|
||||
Name = sysDoc.Name,
|
||||
Path = sysDoc.Path,
|
||||
FileTypeId = sysDoc.FileTypeId,
|
||||
UpdateTime = sysDoc.UpdateTime,
|
||||
ConfirmUserId = identityUser.Id,
|
||||
RealName = identityUser.FullName,
|
||||
UserName = identityUser.UserName,
|
||||
IsNeedSendEmial = identityUser.IsZhiZhun || (!identityUser.IsZhiZhun && sysDoc.DocUserSignType == DocUserSignType.InnerAndOuter),
|
||||
FullFilePath = sysDoc.Path
|
||||
};
|
||||
|
||||
var scenario = EmailBusinessScenario.GeneralTraining_EffectiveNotification;
|
||||
|
||||
var emailConfig = _emailNoticeConfigrepository.Where(t => t.BusinessScenarioEnum == scenario && t.IsAutoSend && t.IsEnable).FirstOrDefault();
|
||||
|
||||
if (emailConfig != null)
|
||||
}
|
||||
else
|
||||
{
|
||||
// 查询所有相关角色的用户
|
||||
systemDocQuery =
|
||||
from sysDoc in _systemDocumentRepository.AsQueryable(false).Where(x => context.Message.Ids.Contains(x.Id))
|
||||
from identityUser in _identityUserRepository.AsQueryable(false)
|
||||
.Where(t => t.Status == UserStateEnum.Enable &&
|
||||
t.UserRoleList.Where(t => t.IsUserRoleDisabled == false)
|
||||
.Any(t => sysDoc.NeedConfirmedUserTypeList.AsQueryable().Any(c => c.NeedConfirmUserTypeId == t.UserTypeId)))
|
||||
select new TriaAndSystemlSignDocView()
|
||||
{
|
||||
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(emailConfig, messageToSend, emailConfigFunc);
|
||||
DocLanguageType = sysDoc.DocLanguageType,
|
||||
UserWorkLanguage = identityUser.UserWorkLanguage,
|
||||
IsSystemDoc = true,
|
||||
Id = sysDoc.Id,
|
||||
CreateTime = sysDoc.CreateTime,
|
||||
IsDeleted = sysDoc.IsDeleted,
|
||||
SignViewMinimumMinutes = sysDoc.SignViewMinimumMinutes,
|
||||
Name = sysDoc.Name,
|
||||
Path = sysDoc.Path,
|
||||
FileTypeId = sysDoc.FileTypeId,
|
||||
UpdateTime = sysDoc.UpdateTime,
|
||||
ConfirmUserId = identityUser.Id,
|
||||
RealName = identityUser.FullName,
|
||||
UserName = identityUser.UserName,
|
||||
IsNeedSendEmial = identityUser.IsZhiZhun || (!identityUser.IsZhiZhun && sysDoc.DocUserSignType == DocUserSignType.InnerAndOuter),
|
||||
FullFilePath = sysDoc.Path
|
||||
};
|
||||
}
|
||||
var datalist = await systemDocQuery.IgnoreQueryFilters().Where(x => x.IsNeedSendEmial).ToListAsync();
|
||||
datalist = datalist.Where(x => x.DocLanguageType == DocLanguageType.CN_US || (int)x.DocLanguageType == (int)x.UserWorkLanguage).ToList();
|
||||
var confirmUserIdList = datalist.Select(t => t.ConfirmUserId).Distinct().ToList();
|
||||
var userinfoList = await _identityUserRepository.Where(x => confirmUserIdList.Contains(x.Id)).ToListAsync();
|
||||
|
||||
var scenario = EmailBusinessScenario.GeneralTraining_EffectiveNotification;
|
||||
var emailConfig = _emailNoticeConfigrepository.Where(t => t.BusinessScenarioEnum == scenario && t.IsAutoSend && t.IsEnable).FirstOrDefault();
|
||||
if (emailConfig == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var userinfo in userinfoList)
|
||||
{
|
||||
string msg = $"{userinfo.UserName}生效通知,";
|
||||
try
|
||||
{
|
||||
var messageToSend = new MimeMessage();
|
||||
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
|
||||
messageToSend.To.Add(new MailboxAddress(userinfo.FullName, userinfo.EMail));
|
||||
|
||||
var workLanguage = userinfo.UserWorkLanguage;
|
||||
var userIsEn_US = workLanguage == UserWorkLanguage.US;
|
||||
var companyName = userIsEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
|
||||
|
||||
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
|
||||
{
|
||||
var topicStr = string.Format(input.topicStr, companyName);
|
||||
|
||||
var htmlBodyStr = string.Format(
|
||||
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr, workLanguage),
|
||||
userinfo.FullName,
|
||||
_systemEmailConfig.SiteUrl
|
||||
);
|
||||
|
||||
return (topicStr, htmlBodyStr);
|
||||
};
|
||||
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(emailConfig, messageToSend, emailConfigFunc, workLanguage);
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
|
||||
msg += "发送成功";
|
||||
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
msg += "发送失败";
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
msg += "发送失败";
|
||||
|
||||
Console.WriteLine(msg);
|
||||
}
|
||||
|
||||
|
||||
Console.WriteLine(msg);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,16 @@
|
||||
using DocumentFormat.OpenXml;
|
||||
using IRaCIS.Application.Contracts;
|
||||
using IRaCIS.Core.Application.Contracts;
|
||||
using IRaCIS.Core.Application.Helper;
|
||||
using IRaCIS.Core.Application.MassTransit.Consumer;
|
||||
using IRaCIS.Core.Application.Service.Reading.Dto;
|
||||
using IRaCIS.Core.Domain.Models;
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using MassTransit;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using MimeKit;
|
||||
using NPOI.SS.Formula.Functions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Reactive.Joins;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using System.Linq.Dynamic.Core;
|
||||
|
||||
namespace IRaCIS.Core.Application.MassTransit.Recurring
|
||||
{
|
||||
@@ -52,10 +45,14 @@ namespace IRaCIS.Core.Application.MassTransit.Recurring
|
||||
|
||||
DateTime now = DateTime.Now;
|
||||
Console.WriteLine("发送定时项目过期提醒");
|
||||
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
|
||||
var isEn_US = context.Message.CultureInfoName == StaticData.CultureInfo.en_US;
|
||||
//设置当前事件传递过来的语言
|
||||
var culture = context.Message.CultureInfoName;
|
||||
CultureInfo.CurrentCulture = new CultureInfo(culture);
|
||||
|
||||
var trialDocQuery =
|
||||
from trialDoc in _trialDocumentRepository.AsQueryable(true)
|
||||
|
||||
join trial in _trialRepository.Where(x => x.IsDeleted == false) on trialDoc.TrialId equals trial.Id
|
||||
join trialIdentityUser in _trialIdentityUserRepository.Where(x => x.IsDeleted == false) on trialDoc.TrialId equals trialIdentityUser.TrialId
|
||||
join trialUserRole in _trialUserRoleRepository.Where(x => x.IsDeleted == false) on trialIdentityUser.Id equals trialUserRole.TrialUserId
|
||||
join identityUser in _identityUserRepository.AsQueryable(false).Where(u => u.Status == UserStateEnum.Enable)
|
||||
@@ -67,14 +64,18 @@ namespace IRaCIS.Core.Application.MassTransit.Recurring
|
||||
|
||||
from confirm in cc.DefaultIfEmpty()
|
||||
where trialIdentityUser.TrialUserRoleList.Any(ur => !ur.IsDeleted && trialDoc.NeedConfirmedUserTypeList.Any(c => c.NeedConfirmUserTypeId == ur.UserRole.UserTypeId))
|
||||
select new TrialSignDocView()
|
||||
select new TriaAndSystemlSignDocView()
|
||||
{
|
||||
|
||||
TrialCode = trialDoc.Trial.TrialCode,
|
||||
ResearchProgramNo = trialDoc.Trial.ResearchProgramNo,
|
||||
ExperimentName = trialDoc.Trial.ExperimentName,
|
||||
CurrentStaffTrainDays = trialDoc.CurrentStaffTrainDays,
|
||||
NewStaffTrainDays = trialDoc.NewStaffTrainDays,
|
||||
Id = trialDoc.Id,
|
||||
TrialType= trial.TrialType,
|
||||
IsTestUser= identityUser.IsTestUser,
|
||||
TrialId = trialDoc.TrialId,
|
||||
IsSystemDoc = false,
|
||||
CreateTime = trialDoc.CreateTime,
|
||||
FullFilePath = trialDoc.Path,
|
||||
@@ -96,56 +97,79 @@ namespace IRaCIS.Core.Application.MassTransit.Recurring
|
||||
};
|
||||
var datalist = await trialDocQuery.IgnoreQueryFilters().Where(t => t.IsDeleted == false && t.ConfirmTime == null&&t.ConfirmTime==null)
|
||||
.ToListAsync();
|
||||
datalist = datalist.Where(x => x.SuggestFinishTime != null && x.SuggestFinishTime.Value.Date == DateTime.Now.Date)
|
||||
|
||||
|
||||
datalist = datalist
|
||||
.Where(x=>((x.TrialType== TrialType.OfficialTrial|| x.TrialType== TrialType.Training) && !x.IsTestUser) || (x.TrialType==TrialType.NoneOfficial &&x.IsTestUser))
|
||||
.Where(x => x.SuggestFinishTime != null && x.SuggestFinishTime.Value.Date == DateTime.Now.Date)
|
||||
.ToList();
|
||||
var confirmUserIdList = datalist.Select(t => t.ConfirmUserId).Distinct().ToList();
|
||||
var userinfoList = await _identityUserRepository.Where(x => confirmUserIdList.Contains(x.Id)).ToListAsync();
|
||||
|
||||
Console.WriteLine("发送定时项目过期提醒:人员数量" + userinfoList.Count);
|
||||
int index = 1;
|
||||
foreach (var userinfo in userinfoList)
|
||||
|
||||
var scenario = EmailBusinessScenario.TrialTraining_ExpirationNotification;
|
||||
var emailConfig = _emailNoticeConfigrepository.Where(t => t.BusinessScenarioEnum == scenario && t.IsAutoSend && t.IsEnable).FirstOrDefault();
|
||||
if (emailConfig == null)
|
||||
{
|
||||
try
|
||||
return;
|
||||
}
|
||||
|
||||
var userTrialIdList = datalist.Select(t => new { t.ConfirmUserId, t.TrialId }).Distinct().ToList();
|
||||
var userWithTrialList = (from ut in userTrialIdList
|
||||
join u in userinfoList on ut.ConfirmUserId equals u.Id
|
||||
select new { ut.TrialId, User = u }).ToList();
|
||||
|
||||
var groupedList = userWithTrialList.GroupBy(x => new { x.TrialId, x.User.UserWorkLanguage }).ToList();
|
||||
foreach (var groupItem in groupedList)
|
||||
{
|
||||
var langUserInfoList = groupItem.Select(x => x.User).Distinct().ToList();
|
||||
if (!langUserInfoList.Any())
|
||||
{
|
||||
Console.WriteLine($"{index}发送定时过期提醒,邮箱:{userinfo.EMail},姓名{userinfo.UserName}");
|
||||
index++;
|
||||
var messageToSend = new MimeMessage();
|
||||
//发件地址
|
||||
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
|
||||
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
|
||||
|
||||
|
||||
|
||||
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
|
||||
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
|
||||
{
|
||||
var topicStr = string.Format(input.topicStr, companyName);
|
||||
|
||||
var htmlBodyStr = string.Format(
|
||||
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
|
||||
userinfo.UserName, // 用户名 {0}
|
||||
_systemEmailConfig.SiteUrl
|
||||
);
|
||||
|
||||
return (topicStr, htmlBodyStr);
|
||||
};
|
||||
|
||||
var scenario = EmailBusinessScenario.TrialTraining_ExpirationNotification;
|
||||
|
||||
var emailConfig = _emailNoticeConfigrepository.Where(t => t.BusinessScenarioEnum == scenario && t.IsAutoSend && t.IsEnable).FirstOrDefault();
|
||||
|
||||
if (emailConfig != null)
|
||||
{
|
||||
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(emailConfig, messageToSend, emailConfigFunc);
|
||||
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
|
||||
}
|
||||
|
||||
|
||||
continue;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
|
||||
Console.WriteLine($"发送定时过期提醒,数量:{langUserInfoList.Count}");
|
||||
|
||||
var trialInfo = await _trialRepository.Where(x => x.Id == groupItem.Key.TrialId).FirstOrDefaultAsync();
|
||||
if (trialInfo == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var workLanguage = groupItem.Key.UserWorkLanguage;
|
||||
var userIsEn_US = workLanguage == UserWorkLanguage.US;
|
||||
var companyName = userIsEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
|
||||
|
||||
foreach (var userinfo in langUserInfoList)
|
||||
{
|
||||
try
|
||||
{
|
||||
var messageToSend = new MimeMessage();
|
||||
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
|
||||
messageToSend.To.Add(new MailboxAddress(userinfo.FullName, userinfo.EMail));
|
||||
|
||||
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
|
||||
{
|
||||
var topicStr = string.Format(input.topicStr, companyName);
|
||||
|
||||
var htmlBodyStr = string.Format(
|
||||
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr, workLanguage),
|
||||
userinfo.FullName,
|
||||
_systemEmailConfig.SiteUrl
|
||||
);
|
||||
|
||||
return (topicStr, htmlBodyStr);
|
||||
};
|
||||
|
||||
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(emailConfig, messageToSend, emailConfigFunc, workLanguage);
|
||||
await SendEmailHelper.SendTrialEmailAsync(messageToSend, trialInfo);
|
||||
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -162,6 +186,7 @@ namespace IRaCIS.Core.Application.MassTransit.Recurring
|
||||
IRepository<VisitTask> _visitTaskRepository,
|
||||
IRepository<TrialDocument> _trialDocumentRepository,
|
||||
IRepository<IdentityUser> _identityUserRepository,
|
||||
IRepository<Trial> _trialRepository,
|
||||
IRepository<TrialIdentityUser> _trialIdentityUserRepository,
|
||||
IRepository<Dictionary> _dictionaryRepository,
|
||||
IRepository<TrialUserRole> _trialUserRoleRepository,
|
||||
@@ -173,7 +198,11 @@ namespace IRaCIS.Core.Application.MassTransit.Recurring
|
||||
|
||||
public async Task Consume(ConsumeContext<TrialDocumentPublishEvent> context)
|
||||
{
|
||||
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
|
||||
var isEn_US = context.Message.CultureInfoName == StaticData.CultureInfo.en_US;
|
||||
|
||||
//设置当前事件传递过来的语言
|
||||
var culture = context.Message.CultureInfoName;
|
||||
CultureInfo.CurrentCulture = new CultureInfo(culture);
|
||||
|
||||
// 记录是否只发送给新增角色的日志
|
||||
if (context.Message.NewUserTypeIds != null && context.Message.NewUserTypeIds.Any())
|
||||
@@ -181,26 +210,30 @@ namespace IRaCIS.Core.Application.MassTransit.Recurring
|
||||
Console.WriteLine($"只发送给新增项目的角色,角色数量: {context.Message.NewUserTypeIds.Count}");
|
||||
}
|
||||
// 构建查询
|
||||
IQueryable<UnionDocumentWithConfirmInfoView> systemDocQuery;
|
||||
IQueryable<TriaAndSystemlSignDocView> trialDocQuery;
|
||||
|
||||
if (context.Message.NewUserTypeIds != null && context.Message.NewUserTypeIds.Any())
|
||||
{
|
||||
// 只查询新增角色的用户
|
||||
systemDocQuery =
|
||||
trialDocQuery =
|
||||
from trialDoc in _trialDocumentRepository.AsQueryable(false).Where(x => context.Message.Ids.Contains(x.Id))
|
||||
join trial in _trialRepository.Where(x => x.IsDeleted == false) on trialDoc.TrialId equals trial.Id
|
||||
join trialIdentityUser in _trialIdentityUserRepository.Where(x=>x.IsDeleted==false) on trialDoc.TrialId equals trialIdentityUser.TrialId
|
||||
join trialUserRole in _trialUserRoleRepository.Where(x => x.IsDeleted == false) on trialIdentityUser.Id equals trialUserRole.TrialUserId
|
||||
join identityUser in _identityUserRepository.AsQueryable(false).Where(u => u.Status == UserStateEnum.Enable)
|
||||
on trialIdentityUser.IdentityUserId equals identityUser.Id
|
||||
where trialIdentityUser.TrialUserRoleList.Any(ur => !ur.IsDeleted && context.Message.NewUserTypeIds.Contains(ur.UserRole.UserTypeId) && trialDoc.NeedConfirmedUserTypeList.Any(c => c.NeedConfirmUserTypeId == ur.UserRole.UserTypeId))
|
||||
|
||||
select new UnionDocumentWithConfirmInfoView()
|
||||
select new TriaAndSystemlSignDocView()
|
||||
{
|
||||
IsSystemDoc = true,
|
||||
Id = trialDoc.Id,
|
||||
TrialId= trialDoc.TrialId,
|
||||
CreateTime = trialDoc.CreateTime,
|
||||
IsDeleted = trialDoc.IsDeleted,
|
||||
SignViewMinimumMinutes = trialDoc.SignViewMinimumMinutes,
|
||||
TrialType = trial.TrialType,
|
||||
IsTestUser = identityUser.IsTestUser,
|
||||
Name = trialDoc.Name,
|
||||
Path = trialDoc.Path,
|
||||
FileTypeId = trialDoc.FileTypeId,
|
||||
@@ -215,22 +248,29 @@ namespace IRaCIS.Core.Application.MassTransit.Recurring
|
||||
else
|
||||
{
|
||||
// 查询所有相关角色的用户
|
||||
systemDocQuery =
|
||||
trialDocQuery =
|
||||
from trialDoc in _trialDocumentRepository.AsQueryable(false).Where(x => context.Message.Ids.Contains(x.Id))
|
||||
|
||||
join trialIdentityUser in _trialIdentityUserRepository.Where(x => x.IsDeleted == false) on trialDoc.TrialId equals trialIdentityUser.TrialId
|
||||
join trialUserRole in _trialUserRoleRepository.Where(x=>x.IsDeleted==false) on trialIdentityUser.Id equals trialUserRole.TrialUserId
|
||||
join identityUser in _identityUserRepository.AsQueryable(false).Where(u => u.Status == UserStateEnum.Enable)
|
||||
|
||||
join trial in _trialRepository.AsQueryable(false) on trialDoc.TrialId equals trial.Id
|
||||
join identityUser in _identityUserRepository.AsQueryable(false).Where(u => u.Status == UserStateEnum.Enable)
|
||||
on trialIdentityUser.IdentityUserId equals identityUser.Id
|
||||
where trialIdentityUser.TrialUserRoleList.Any(ur => !ur.IsDeleted &&trialDoc.NeedConfirmedUserTypeList.Any(c => c.NeedConfirmUserTypeId == ur.UserRole.UserTypeId))
|
||||
|
||||
|
||||
select new UnionDocumentWithConfirmInfoView()
|
||||
select new TriaAndSystemlSignDocView()
|
||||
{
|
||||
IsSystemDoc = false,
|
||||
Id = trialDoc.Id,
|
||||
TrialId = trialDoc.TrialId,
|
||||
EmailFromName =trial.EmailFromName,
|
||||
CreateTime = trialDoc.CreateTime,
|
||||
IsDeleted = trialDoc.IsDeleted,
|
||||
SignViewMinimumMinutes = trialDoc.SignViewMinimumMinutes,
|
||||
TrialType = trial.TrialType,
|
||||
IsTestUser = identityUser.IsTestUser,
|
||||
Name = trialDoc.Name,
|
||||
Path = trialDoc.Path,
|
||||
FileTypeId = trialDoc.FileTypeId,
|
||||
@@ -242,51 +282,61 @@ namespace IRaCIS.Core.Application.MassTransit.Recurring
|
||||
FullFilePath = trialDoc.Path
|
||||
};
|
||||
}
|
||||
var datalist = await systemDocQuery.IgnoreQueryFilters().ToListAsync();
|
||||
|
||||
var datalist = await trialDocQuery.IgnoreQueryFilters().ToListAsync();
|
||||
datalist = datalist.Where(x => ((x.TrialType == TrialType.OfficialTrial || x.TrialType == TrialType.Training) && !x.IsTestUser) || (x.TrialType == TrialType.NoneOfficial && x.IsTestUser)).ToList();
|
||||
|
||||
|
||||
var confirmUserIdList = datalist.Select(t => t.ConfirmUserId).Distinct().ToList();
|
||||
var userinfoList = await _identityUserRepository.Where(x => confirmUserIdList.Contains(x.Id)).ToListAsync();
|
||||
int index = 1;
|
||||
foreach (var userinfo in userinfoList)
|
||||
|
||||
var scenario = EmailBusinessScenario.TrialTraining_EffectiveNotification;
|
||||
var emailConfig = _emailNoticeConfigrepository.Where(t => t.BusinessScenarioEnum == scenario && t.IsEnable).FirstOrDefault();
|
||||
if (emailConfig == null)
|
||||
{
|
||||
string msg = $"{index}项目生效通知,邮箱:{userinfo.EMail},姓名{userinfo.UserName},";
|
||||
index++;
|
||||
return;
|
||||
}
|
||||
|
||||
var userTrialIdList = datalist.Select(t => new { t.ConfirmUserId, t.TrialId }).Distinct().ToList();
|
||||
var userWithTrialList = (from ut in userTrialIdList
|
||||
join u in userinfoList on ut.ConfirmUserId equals u.Id
|
||||
select new { ut.TrialId, User = u }).ToList();
|
||||
|
||||
foreach (var userWithTrial in userWithTrialList)
|
||||
{
|
||||
var userinfo = userWithTrial.User;
|
||||
string msg = $"{userinfo.UserName}项目生效通知,";
|
||||
try
|
||||
{
|
||||
|
||||
var messageToSend = new MimeMessage();
|
||||
//发件地址
|
||||
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
|
||||
messageToSend.To.Add(new MailboxAddress(String.Empty, userinfo.EMail));
|
||||
messageToSend.To.Add(new MailboxAddress(userinfo.FullName, userinfo.EMail));
|
||||
|
||||
|
||||
|
||||
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
|
||||
var workLanguage = userinfo.UserWorkLanguage;
|
||||
var userIsEn_US = workLanguage == UserWorkLanguage.US;
|
||||
var companyName = userIsEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
|
||||
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
|
||||
{
|
||||
var topicStr = string.Format(input.topicStr, companyName);
|
||||
|
||||
var htmlBodyStr = string.Format(
|
||||
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
|
||||
userinfo.UserName, // 用户名 {0}
|
||||
CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr, workLanguage),
|
||||
userinfo.FullName,
|
||||
_systemEmailConfig.SiteUrl
|
||||
);
|
||||
|
||||
return (topicStr, htmlBodyStr);
|
||||
};
|
||||
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(emailConfig, messageToSend, emailConfigFunc, workLanguage);
|
||||
|
||||
var scenario = EmailBusinessScenario.TrialTraining_EffectiveNotification;
|
||||
|
||||
var emailConfig = _emailNoticeConfigrepository.Where(t => t.BusinessScenarioEnum == scenario && t.IsEnable).FirstOrDefault();
|
||||
|
||||
if (emailConfig != null)
|
||||
var trialInfo = await _trialRepository.Where(x => x.Id == userWithTrial.TrialId).FirstOrDefaultAsync();
|
||||
if (trialInfo == null)
|
||||
{
|
||||
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(emailConfig, messageToSend, emailConfigFunc);
|
||||
|
||||
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
|
||||
msg += "发送成功";
|
||||
continue;
|
||||
}
|
||||
|
||||
await SendEmailHelper.SendTrialEmailAsync(messageToSend, trialInfo);
|
||||
msg += "发送成功";
|
||||
|
||||
}
|
||||
catch (Exception)
|
||||
|
||||
@@ -439,6 +439,8 @@ namespace IRaCIS.Core.Application.ViewModel
|
||||
|
||||
public string TaskName { get; set; } = String.Empty;
|
||||
|
||||
public string TaskBlindName { get; set; } = String.Empty;
|
||||
|
||||
public Guid? DoctorUserId { get; set; }
|
||||
|
||||
public ReadingCategory? ReadingCategory { get; set; }
|
||||
|
||||
@@ -54,8 +54,8 @@ namespace IRaCIS.Core.Application.Service
|
||||
|
||||
.WhereIf(inQuery.DoctorUserIdeaEnum != null, t => t.DoctorUserIdeaEnum == inQuery.DoctorUserIdeaEnum)
|
||||
.WhereIf(inQuery.TrialReadingCriterionId != null, t => t.VisitTask.TrialReadingCriterionId == inQuery.TrialReadingCriterionId)
|
||||
.WhereIf(inQuery.IsInvalid != null, t => t.IsInvalid == inQuery.IsInvalid)
|
||||
.WhereIf(inQuery.IsHaveQuestion != null, t => t.IsHaveQuestion == inQuery.IsHaveQuestion)
|
||||
.WhereIf(inQuery.IsInvalid != null, t => t.IsInvalid == inQuery.IsInvalid)
|
||||
.WhereIf(inQuery.IsHaveQuestion != null, t => t.IsHaveQuestion == inQuery.IsHaveQuestion)
|
||||
|
||||
.WhereIf(inQuery.BeginAllocateDate != null, t => t.AllocateTime >= inQuery.BeginAllocateDate)
|
||||
.WhereIf(inQuery.EndAllocateDate != null, t => t.AllocateTime <= inQuery.EndAllocateDate)
|
||||
@@ -211,23 +211,23 @@ namespace IRaCIS.Core.Application.Service
|
||||
.WhereIf(inQuery.SubjectId != null, t => t.VisitTask.SubjectId == inQuery.SubjectId)
|
||||
.WhereIf(inQuery.TrialSiteId != null, t => t.VisitTask.Subject.TrialSiteId == inQuery.TrialSiteId)
|
||||
.WhereIf(inQuery.IsUrgent != null, t => t.VisitTask.IsUrgent == inQuery.IsUrgent)
|
||||
.WhereIf(inQuery.AuditState != null, t => t.AuditState == inQuery.AuditState)
|
||||
.WhereIf(inQuery.DoctorUserIdeaEnum != null, t => t.DoctorUserIdeaEnum == inQuery.DoctorUserIdeaEnum)
|
||||
.WhereIf(inQuery.TaskState != null, t => t.VisitTask.TaskState == inQuery.TaskState)
|
||||
.WhereIf(!string.IsNullOrEmpty(inQuery.SubjectCode), t => t.VisitTask.Subject.Code.Contains(inQuery.SubjectCode) || t.VisitTask.Subject.MedicalNo.Contains(inQuery.SubjectCode))
|
||||
.WhereIf(!string.IsNullOrEmpty(inQuery.TaskName), t => t.VisitTask.TaskName.Contains(inQuery.TaskName) || t.VisitTask.TaskBlindName.Contains(inQuery.TaskName))
|
||||
.WhereIf(inQuery.AuditState != null, t => t.AuditState == inQuery.AuditState)
|
||||
.WhereIf(inQuery.DoctorUserIdeaEnum != null, t => t.DoctorUserIdeaEnum == inQuery.DoctorUserIdeaEnum)
|
||||
.WhereIf(inQuery.TaskState != null, t => t.VisitTask.TaskState == inQuery.TaskState)
|
||||
.WhereIf(!string.IsNullOrEmpty(inQuery.SubjectCode), t => t.VisitTask.Subject.Code.Contains(inQuery.SubjectCode) || t.VisitTask.Subject.MedicalNo.Contains(inQuery.SubjectCode))
|
||||
.WhereIf(!string.IsNullOrEmpty(inQuery.TaskName), t => t.VisitTask.TaskName.Contains(inQuery.TaskName) || t.VisitTask.TaskBlindName.Contains(inQuery.TaskName))
|
||||
.WhereIf(inQuery.DoctorUserId != null, t => t.VisitTask.DoctorUserId == inQuery.DoctorUserId)
|
||||
.WhereIf(inQuery.ReadingCategory != null, t => t.VisitTask.ReadingCategory == inQuery.ReadingCategory)
|
||||
.WhereIf(inQuery.ReadingTaskState != null, t => t.VisitTask.ReadingTaskState == inQuery.ReadingTaskState)
|
||||
.WhereIf(inQuery.IsInvalid != null, t => t.IsInvalid == inQuery.IsInvalid)
|
||||
.WhereIf(inQuery.IsInvalid != null, t => t.IsInvalid == inQuery.IsInvalid)
|
||||
.WhereIf(inQuery.IsGetBeRead, x => !x.IsInvalid && x.AuditState != MedicalReviewAuditState.HaveSigned)
|
||||
.WhereIf(inQuery.TrialReadingCriterionId != null, t => t.VisitTask.TrialReadingCriterionId == inQuery.TrialReadingCriterionId)
|
||||
.WhereIf(inQuery.BeginAuditSignTime != null, t => t.AuditSignTime >= inQuery.BeginAuditSignTime)
|
||||
.WhereIf(inQuery.EndAuditSignTime != null, t => t.AuditSignTime <= inQuery.EndAuditSignTime)
|
||||
.WhereIf(inQuery.BeginAllocateDate != null, t => t.AllocateTime >= inQuery.BeginAllocateDate)
|
||||
.WhereIf(inQuery.TrialReadingCriterionId != null, t => t.VisitTask.TrialReadingCriterionId == inQuery.TrialReadingCriterionId)
|
||||
.WhereIf(inQuery.BeginAuditSignTime != null, t => t.AuditSignTime >= inQuery.BeginAuditSignTime)
|
||||
.WhereIf(inQuery.EndAuditSignTime != null, t => t.AuditSignTime <= inQuery.EndAuditSignTime)
|
||||
.WhereIf(inQuery.BeginAllocateDate != null, t => t.AllocateTime >= inQuery.BeginAllocateDate)
|
||||
.WhereIf(inQuery.EndAllocateDate != null, t => t.AllocateTime <= inQuery.EndAllocateDate)
|
||||
|
||||
.WhereIf(inQuery.ArmEnum != null, t => t.VisitTask.ArmEnum == inQuery.ArmEnum)
|
||||
.WhereIf(inQuery.ArmEnum != null, t => t.VisitTask.ArmEnum == inQuery.ArmEnum)
|
||||
.WhereIf(inQuery.MedicalManagerUserId != null, t => t.MedicalManagerUserId == inQuery.MedicalManagerUserId)
|
||||
.WhereIf(inQuery.BeginSignTime != null, t => t.VisitTask.SignTime > inQuery.BeginSignTime)
|
||||
.WhereIf(inQuery.EndSignTime != null, t => t.VisitTask.SignTime < inQuery.EndSignTime)
|
||||
|
||||
@@ -124,6 +124,45 @@ namespace IRaCIS.Core.Application.Service
|
||||
}
|
||||
|
||||
|
||||
#region 获取随机编号
|
||||
/// <summary>
|
||||
/// 从 NewId 的后 3 个字节(24 位)生成一个 6 位十六进制字符串
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static string GenerateTaskNo()
|
||||
{
|
||||
var bytes = NewId.Next().ToByteArray();
|
||||
|
||||
uint value =
|
||||
BitConverter.ToUInt32(bytes, 0)
|
||||
^ BitConverter.ToUInt32(bytes, 4)
|
||||
^ BitConverter.ToUInt32(bytes, 8)
|
||||
^ BitConverter.ToUInt32(bytes, 12);
|
||||
|
||||
return (value & 0xFFFFFF).ToString("X6");
|
||||
}
|
||||
|
||||
private async Task<string> GetRandomTaskName(Guid trialId, List<string> existList)
|
||||
{
|
||||
var taskName = GenerateTaskNo();
|
||||
|
||||
while (existList.Any(t => t == taskName))
|
||||
{
|
||||
taskName = GenerateTaskNo();
|
||||
}
|
||||
|
||||
while (await _visitTaskRepository.AnyAsync(t => t.TaskBlindName == taskName && t.TrialId == trialId))
|
||||
{
|
||||
taskName = GenerateTaskNo();
|
||||
}
|
||||
|
||||
existList.Add(taskName);
|
||||
|
||||
return taskName;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
//基于标准签名 产生任务 (或者手动选择某些访视生成该标准的任务)
|
||||
public async Task BaseCritrionGenerateVisitTask(Guid trialId, Guid confirmedTrialReadingCriterionId, bool? isManualSelectVisit = null, List<Guid>? subjectVisitIdList = null)
|
||||
@@ -151,11 +190,11 @@ namespace IRaCIS.Core.Application.Service
|
||||
|
||||
var dbMaxCode = _visitTaskRepository.Where(t => t.TrialId == trialId).Select(t => t.Code).DefaultIfEmpty().Max();
|
||||
|
||||
var cacheMaxCodeInt = _fusionCache.GetOrDefault<int>(CacheKeys.TrialStudyMaxCode(trialId));
|
||||
var cacheMaxCodeInt = _fusionCache.GetOrDefault<int>(CacheKeys.TrialVisitTaskMaxCode(trialId));
|
||||
|
||||
int currentMaxCodeInt = cacheMaxCodeInt > dbMaxCode ? cacheMaxCodeInt : dbMaxCode;
|
||||
|
||||
|
||||
var randomTaskBlindNameList = new List<string>();
|
||||
|
||||
foreach (var subjectGroup in needGenerateVisit.GroupBy(t => t.SubjectId).Select(g => new { SubjectId = g.Key, SubjectVisitList = g.ToList() }))
|
||||
{
|
||||
@@ -173,6 +212,8 @@ namespace IRaCIS.Core.Application.Service
|
||||
|
||||
var blindTaskName = string.Empty;
|
||||
|
||||
var isRandom = false;
|
||||
|
||||
if (trialReadingCriterionConfig.IsReadingTaskViewInOrder == ReadingOrder.InOrder)
|
||||
{
|
||||
if (visitNumList.IndexOf(subjectVisit.VisitNum) == 0 && subjectVisit.VisitNum == 0)
|
||||
@@ -185,6 +226,12 @@ namespace IRaCIS.Core.Application.Service
|
||||
|
||||
}
|
||||
}
|
||||
else if (trialReadingCriterionConfig.IsReadingTaskViewInOrder == ReadingOrder.Random)
|
||||
{
|
||||
blindTaskName = await GetRandomTaskName(trialId, randomTaskBlindNameList);
|
||||
|
||||
isRandom = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
blindTaskName = "Timepoint";
|
||||
@@ -258,12 +305,14 @@ namespace IRaCIS.Core.Application.Service
|
||||
currentMaxCodeInt = currentMaxCodeInt + 1;
|
||||
|
||||
|
||||
var blindTaskName2 = isRandom ? await GetRandomTaskName(trialId, randomTaskBlindNameList) : blindTaskName;
|
||||
|
||||
task2 = await _visitTaskRepository.AddAsync(new VisitTask()
|
||||
{
|
||||
TrialId = trialId,
|
||||
SubjectId = subjectVisit.SubjectId,
|
||||
IsUrgent = subjectVisit.IsUrgent,
|
||||
TaskBlindName = blindTaskName,
|
||||
TaskBlindName = blindTaskName2,
|
||||
TaskName = subjectVisit.VisitName,
|
||||
TaskUrgentType = taskUrgentType,
|
||||
IsCanEditUrgentState = isCanEditUrgentState,
|
||||
@@ -283,7 +332,7 @@ namespace IRaCIS.Core.Application.Service
|
||||
|
||||
}
|
||||
|
||||
_fusionCache.Set<int>(CacheKeys.TrialStudyMaxCode(trialId), currentMaxCodeInt, TimeSpan.FromMinutes(30));
|
||||
_fusionCache.Set<int>(CacheKeys.TrialVisitTaskMaxCode(trialId), currentMaxCodeInt, TimeSpan.FromMinutes(30));
|
||||
|
||||
|
||||
|
||||
@@ -344,7 +393,7 @@ namespace IRaCIS.Core.Application.Service
|
||||
|
||||
|
||||
|
||||
_fusionCache.Set<int>(CacheKeys.TrialStudyMaxCode(trialId), currentMaxCodeInt, TimeSpan.FromMinutes(30));
|
||||
_fusionCache.Set<int>(CacheKeys.TrialVisitTaskMaxCode(trialId), currentMaxCodeInt, TimeSpan.FromMinutes(30));
|
||||
|
||||
|
||||
|
||||
@@ -590,11 +639,11 @@ namespace IRaCIS.Core.Application.Service
|
||||
|
||||
var dbMaxCode = _visitTaskRepository.Where(t => t.TrialId == trialId).Select(t => t.Code).DefaultIfEmpty().Max();
|
||||
|
||||
var cacheMaxCodeInt = _fusionCache.GetOrDefault<int>(CacheKeys.TrialStudyMaxCode(trialId));
|
||||
var cacheMaxCodeInt = _fusionCache.GetOrDefault<int>(CacheKeys.TrialVisitTaskMaxCode(trialId));
|
||||
|
||||
int currentMaxCodeInt = cacheMaxCodeInt > dbMaxCode ? cacheMaxCodeInt : dbMaxCode;
|
||||
|
||||
|
||||
var randomTaskBlindNameList = new List<string>();
|
||||
|
||||
|
||||
switch (generateTaskCommand.ReadingCategory)
|
||||
@@ -654,7 +703,9 @@ namespace IRaCIS.Core.Application.Service
|
||||
|
||||
var isFrontTaskNeedSignButNotSign = await _visitTaskRepository.AnyAsync(t => t.TrialReadingCriterionId == trialReadingCriterionId && t.SubjectId == subjectVisit.SubjectId && t.TaskState == TaskState.Effect && t.VisitTaskNum < subjectVisit.VisitNum && t.IsNeedClinicalDataSign == true && t.IsClinicalDataSign == false);
|
||||
|
||||
|
||||
var blindTaskName = string.Empty;
|
||||
var isFullRandom = false;
|
||||
|
||||
if (trialReadingCriterionConfig.IsReadingTaskViewInOrder == ReadingOrder.InOrder)
|
||||
{
|
||||
@@ -668,6 +719,12 @@ namespace IRaCIS.Core.Application.Service
|
||||
|
||||
}
|
||||
}
|
||||
else if (trialReadingCriterionConfig.IsReadingTaskViewInOrder == ReadingOrder.Random)
|
||||
{
|
||||
blindTaskName = await GetRandomTaskName(trialId, randomTaskBlindNameList);
|
||||
isFullRandom = true;
|
||||
}
|
||||
//受试者随机
|
||||
else
|
||||
{
|
||||
blindTaskName = "Timepoint";
|
||||
@@ -721,11 +778,15 @@ namespace IRaCIS.Core.Application.Service
|
||||
if (!existCurrentVisitTaskList.Any(t => t.ArmEnum == Arm.DoubleReadingArm2))
|
||||
{
|
||||
currentMaxCodeInt = currentMaxCodeInt + 1;
|
||||
|
||||
var blindTaskName2 = isFullRandom ? await GetRandomTaskName(trialId, randomTaskBlindNameList) : blindTaskName;
|
||||
|
||||
|
||||
task2 = await _visitTaskRepository.AddAsync(new VisitTask()
|
||||
{
|
||||
TrialId = trialId,
|
||||
SubjectId = subjectVisit.SubjectId,
|
||||
TaskBlindName = blindTaskName,
|
||||
TaskBlindName = blindTaskName2,
|
||||
TaskName = subjectVisit.VisitName,
|
||||
IsUrgent = subjectVisit.IsUrgent,
|
||||
|
||||
@@ -747,7 +808,7 @@ namespace IRaCIS.Core.Application.Service
|
||||
}
|
||||
|
||||
|
||||
_fusionCache.Set<int>(CacheKeys.TrialStudyMaxCode(trialId), currentMaxCodeInt, TimeSpan.FromMinutes(30));
|
||||
_fusionCache.Set<int>(CacheKeys.TrialVisitTaskMaxCode(trialId), currentMaxCodeInt, TimeSpan.FromMinutes(30));
|
||||
|
||||
|
||||
#region 分配
|
||||
@@ -873,7 +934,7 @@ namespace IRaCIS.Core.Application.Service
|
||||
{
|
||||
arm1.TaskAllocationState = TaskAllocationState.Allocated;
|
||||
arm1.AllocateTime = DateTime.Now;
|
||||
arm1.DoctorUserId = task1.DoctorUserId;
|
||||
arm1.DoctorUserId = assignConfigList.FirstOrDefault(t => t.ArmEnum == Arm.DoubleReadingArm1)!.DoctorUserId;
|
||||
arm1.SuggesteFinishedTime = GetSuggessFinishTime(true, UrgentType.NotUrget);
|
||||
}
|
||||
|
||||
@@ -913,14 +974,14 @@ namespace IRaCIS.Core.Application.Service
|
||||
{
|
||||
taskOne.TaskAllocationState = TaskAllocationState.Allocated;
|
||||
taskOne.AllocateTime = DateTime.Now;
|
||||
taskOne.DoctorUserId = task1!.DoctorUserId;
|
||||
taskOne.DoctorUserId = assignConfigList.FirstOrDefault(t => t.ArmEnum == Arm.DoubleReadingArm1)!.DoctorUserId;
|
||||
taskOne.SuggesteFinishedTime = GetSuggessFinishTime(true, UrgentType.NotUrget);
|
||||
}
|
||||
|
||||
|
||||
currentMaxCodeInt = currentMaxCodeInt + 1;
|
||||
|
||||
_fusionCache.Set<int>(CacheKeys.TrialStudyMaxCode(trialId), currentMaxCodeInt, TimeSpan.FromMinutes(30));
|
||||
_fusionCache.Set<int>(CacheKeys.TrialVisitTaskMaxCode(trialId), currentMaxCodeInt, TimeSpan.FromMinutes(30));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -944,7 +1005,7 @@ namespace IRaCIS.Core.Application.Service
|
||||
{
|
||||
arm2.TaskAllocationState = TaskAllocationState.Allocated;
|
||||
arm2.AllocateTime = DateTime.Now;
|
||||
arm2.DoctorUserId = task2.DoctorUserId;
|
||||
arm2.DoctorUserId = assignConfigList.FirstOrDefault(t => t.ArmEnum == Arm.DoubleReadingArm2)!.DoctorUserId;
|
||||
arm2.SuggesteFinishedTime = GetSuggessFinishTime(true, UrgentType.NotUrget);
|
||||
}
|
||||
|
||||
@@ -986,13 +1047,13 @@ namespace IRaCIS.Core.Application.Service
|
||||
{
|
||||
taskTwo.TaskAllocationState = TaskAllocationState.Allocated;
|
||||
taskTwo.AllocateTime = DateTime.Now;
|
||||
taskTwo.DoctorUserId = task2!.DoctorUserId;
|
||||
taskTwo.DoctorUserId = assignConfigList.FirstOrDefault(t => t.ArmEnum == Arm.DoubleReadingArm2)!.DoctorUserId;
|
||||
taskTwo.SuggesteFinishedTime = GetSuggessFinishTime(true, UrgentType.NotUrget);
|
||||
}
|
||||
|
||||
currentMaxCodeInt = currentMaxCodeInt + 1;
|
||||
|
||||
_fusionCache.Set<int>(CacheKeys.TrialStudyMaxCode(trialId), currentMaxCodeInt, TimeSpan.FromMinutes(30));
|
||||
_fusionCache.Set<int>(CacheKeys.TrialVisitTaskMaxCode(trialId), currentMaxCodeInt, TimeSpan.FromMinutes(30));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1069,7 +1130,7 @@ namespace IRaCIS.Core.Application.Service
|
||||
|
||||
currentMaxCodeInt = currentMaxCodeInt + 1;
|
||||
|
||||
_fusionCache.Set<int>(CacheKeys.TrialStudyMaxCode(trialId), currentMaxCodeInt, TimeSpan.FromMinutes(30));
|
||||
_fusionCache.Set<int>(CacheKeys.TrialVisitTaskMaxCode(trialId), currentMaxCodeInt, TimeSpan.FromMinutes(30));
|
||||
|
||||
#region 分配
|
||||
|
||||
@@ -1197,7 +1258,7 @@ namespace IRaCIS.Core.Application.Service
|
||||
|
||||
currentMaxCodeInt = currentMaxCodeInt + 1;
|
||||
|
||||
_fusionCache.Set<int>(CacheKeys.TrialStudyMaxCode(trialId), currentMaxCodeInt, TimeSpan.FromMinutes(30));
|
||||
_fusionCache.Set<int>(CacheKeys.TrialVisitTaskMaxCode(trialId), currentMaxCodeInt, TimeSpan.FromMinutes(30));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1277,6 +1338,7 @@ namespace IRaCIS.Core.Application.Service
|
||||
|
||||
var reReadingVisitTask = generateTaskCommand.ReReadingTask;
|
||||
|
||||
|
||||
Guid? subjectVisitId = Guid.Empty;
|
||||
|
||||
if (reReadingVisitTask.SourceSubjectVisitId == null)
|
||||
@@ -1317,6 +1379,10 @@ namespace IRaCIS.Core.Application.Service
|
||||
//&& t.TrialReadingCriterionId == reReadingVisitTask.TrialReadingCriterionId && t.TaskState == TaskState.Effect
|
||||
// && t.SourceSubjectVisitId == reReadingVisitTask.SourceSubjectVisitId && t.ArmEnum == reReadingVisitTask.ArmEnum && t.DoctorUserId == reReadingVisitTask.DoctorUserId).FirstOrDefault();
|
||||
|
||||
var isRandomReading = trialReadingCriterionConfigList.FirstOrDefault(t => t.TrialReadingCriterionId == reReadingVisitTask.TrialReadingCriterionId)?.IsReadingTaskViewInOrder == ReadingOrder.Random;
|
||||
|
||||
var taskBlindName = isRandomReading ? await GetRandomTaskName(trialId, randomTaskBlindNameList) : reReadingVisitTask.TaskBlindName;
|
||||
|
||||
|
||||
var newTask = await _visitTaskRepository.AddAsync(new VisitTask()
|
||||
{
|
||||
@@ -1325,7 +1391,7 @@ namespace IRaCIS.Core.Application.Service
|
||||
SubjectId = reReadingVisitTask.SubjectId,
|
||||
ArmEnum = reReadingVisitTask.ArmEnum,
|
||||
TaskName = reReadingVisitTask.TaskName,
|
||||
TaskBlindName = reReadingVisitTask.TaskBlindName,
|
||||
TaskBlindName = taskBlindName,
|
||||
|
||||
IsUrgent = reReadingVisitTask.IsAnalysisCreate ? false : subjectVisitUrgentInfo.IsUrgent,
|
||||
TaskUrgentType = reReadingVisitTask.IsAnalysisCreate ? null : taskUrgent,
|
||||
@@ -1386,7 +1452,7 @@ namespace IRaCIS.Core.Application.Service
|
||||
|
||||
currentMaxCodeInt = currentMaxCodeInt + 1;
|
||||
|
||||
_fusionCache.Set<int>(CacheKeys.TrialStudyMaxCode(trialId), currentMaxCodeInt, TimeSpan.FromMinutes(30));
|
||||
_fusionCache.Set<int>(CacheKeys.TrialVisitTaskMaxCode(trialId), currentMaxCodeInt, TimeSpan.FromMinutes(30));
|
||||
|
||||
//}
|
||||
|
||||
@@ -1491,7 +1557,7 @@ namespace IRaCIS.Core.Application.Service
|
||||
|
||||
currentMaxCodeInt = currentMaxCodeInt + 1;
|
||||
|
||||
_fusionCache.Set<int>(CacheKeys.TrialStudyMaxCode(trialId), currentMaxCodeInt, TimeSpan.FromMinutes(30));
|
||||
_fusionCache.Set<int>(CacheKeys.TrialVisitTaskMaxCode(trialId), currentMaxCodeInt, TimeSpan.FromMinutes(30));
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -1538,7 +1604,7 @@ namespace IRaCIS.Core.Application.Service
|
||||
|
||||
currentMaxCodeInt = currentMaxCodeInt + 1;
|
||||
|
||||
_fusionCache.Set<int>(CacheKeys.TrialStudyMaxCode(trialId), currentMaxCodeInt, TimeSpan.FromMinutes(30));
|
||||
_fusionCache.Set<int>(CacheKeys.TrialVisitTaskMaxCode(trialId), currentMaxCodeInt, TimeSpan.FromMinutes(30));
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -1548,6 +1614,11 @@ namespace IRaCIS.Core.Application.Service
|
||||
|
||||
var subjectUser = await _subjectUserRepository.Where(x => x.SubjectId == firstTask.SubjectId && x.ArmEnum == Arm.JudgeArm && x.IsConfirmed && x.TrialReadingCriterionId == firstTask.TrialReadingCriterionId).FirstOrDefaultAsync();
|
||||
|
||||
|
||||
var isRandom = trialReadingCriterionConfigList.FirstOrDefault(t => t.TrialReadingCriterionId == firstTask.TrialReadingCriterionId)?.IsReadingTaskViewInOrder == ReadingOrder.Random;
|
||||
|
||||
var blindName = isRandom ? await GetRandomTaskName(trialId, randomTaskBlindNameList) : firstTask.TaskBlindName;
|
||||
|
||||
VisitTask visitTask = new VisitTask()
|
||||
{
|
||||
ArmEnum = Arm.JudgeArm,
|
||||
@@ -1564,7 +1635,7 @@ namespace IRaCIS.Core.Application.Service
|
||||
SouceReadModuleId = firstTask.SouceReadModuleId,
|
||||
TaskCode = AppSettings.GetCodeStr(currentMaxCodeInt + 1, nameof(VisitTask)),
|
||||
TaskState = TaskState.Effect,
|
||||
TaskBlindName = firstTask.TaskBlindName,
|
||||
TaskBlindName = blindName,
|
||||
DoctorUserId = subjectUser == null ? null : subjectUser.DoctorUserId,
|
||||
TaskAllocationState = subjectUser == null ? TaskAllocationState.NotAllocate : TaskAllocationState.Allocated,
|
||||
AllocateTime = subjectUser == null ? null : DateTime.Now,
|
||||
@@ -1576,7 +1647,7 @@ namespace IRaCIS.Core.Application.Service
|
||||
await _visitTaskRepository.AddAsync(visitTask);
|
||||
currentMaxCodeInt = currentMaxCodeInt + 1;
|
||||
|
||||
_fusionCache.Set<int>(CacheKeys.TrialStudyMaxCode(trialId), currentMaxCodeInt, TimeSpan.FromMinutes(30));
|
||||
_fusionCache.Set<int>(CacheKeys.TrialVisitTaskMaxCode(trialId), currentMaxCodeInt, TimeSpan.FromMinutes(30));
|
||||
|
||||
await _visitTaskRepository.BatchUpdateNoTrackingAsync(x => generateTaskCommand.JudgeVisitTaskIdList.Contains(x.Id), x => new VisitTask()
|
||||
{
|
||||
@@ -1637,7 +1708,7 @@ namespace IRaCIS.Core.Application.Service
|
||||
});
|
||||
|
||||
currentMaxCodeInt = currentMaxCodeInt + 1;
|
||||
_fusionCache.Set<int>(CacheKeys.TrialStudyMaxCode(trialId), currentMaxCodeInt, TimeSpan.FromMinutes(30));
|
||||
_fusionCache.Set<int>(CacheKeys.TrialVisitTaskMaxCode(trialId), currentMaxCodeInt, TimeSpan.FromMinutes(30));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1707,7 +1778,7 @@ namespace IRaCIS.Core.Application.Service
|
||||
TrialId = originalTaskInfo.TrialId
|
||||
});
|
||||
|
||||
_fusionCache.Set<int>(CacheKeys.TrialStudyMaxCode(trialId), currentMaxCodeInt, TimeSpan.FromMinutes(30));
|
||||
_fusionCache.Set<int>(CacheKeys.TrialVisitTaskMaxCode(trialId), currentMaxCodeInt, TimeSpan.FromMinutes(30));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -17,7 +17,9 @@ using MassTransit;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json;
|
||||
using System.Linq;
|
||||
using System.Linq.Dynamic.Core;
|
||||
using System.Threading.Tasks;
|
||||
using Subject = IRaCIS.Core.Domain.Models.Subject;
|
||||
|
||||
namespace IRaCIS.Core.Application.Service.Allocation;
|
||||
@@ -41,12 +43,21 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
|
||||
IRepository<ReadingQuestionCriterionTrial> _trialReadingCriterionRepository,
|
||||
IRepository<ReadingQuestionCriterionTrial> _readingQuestionCriterionTrialRepository,
|
||||
IRepository<ReadingTaskQuestionAnswer> _readingTaskQuestionAnswerRepository,
|
||||
IRepository<Segment> _segmentRepository,
|
||||
IRepository<Segmentation> _segmentationRepository,
|
||||
IRepository<SegmentBinding> _segmentBindingRepository,
|
||||
IRepository<DicomInstance> _dicomInstanceRepository,
|
||||
IRepository<DicomSeries> _dicomSeriesRepository,
|
||||
IRepository<SubjectCanceDoctor> _subjectCanceDoctorRepository,
|
||||
IRepository<ReadingTaskQuestionMark> _readingTaskQuestionMarkRepository,
|
||||
IRepository<ReadingNoneDicomMark> _readingNoneDicomMarkRepository,
|
||||
IRepository<ReadingNoneDicomMarkBinding> _readingNoneDicomMarkBindingRepository,
|
||||
IRepository<ReadingTableAnswerRowInfo> _readingTableAnswerRowInfoRepository,
|
||||
//IRepository<ReadingCustomTag> _readingCustomTagRepository,
|
||||
//IRepository<ReadingCustomTag> _readingCustomTagRepository,
|
||||
IRepository<TaskStudy> _taskStudyRepository,
|
||||
IRepository<TaskSeries> _taskSeriesRepository,
|
||||
IRepository<TaskInstance> _taskInstanceRepository,
|
||||
IRepository<NoneDicomStudyFile> _noneDicomStudyFileRepository,
|
||||
IRepository<TaskInfluence> _taskInfluenceRepository,
|
||||
IRepository<TrialQCQuestionAnswer> _trialQCQuestionAnswerRepository,
|
||||
ILogger<VisitTaskService> _logger,
|
||||
@@ -84,6 +95,7 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
|
||||
{
|
||||
TrialReadingCriterionId = t.Id,
|
||||
IsAutoCreate = t.IsAutoCreate,
|
||||
ReadingTool = t.ReadingTool,
|
||||
IsAdditionalAssessment = t.IsAdditionalAssessment,
|
||||
TrialReadingCriterionName = t.CriterionName,
|
||||
CriterionType = t.CriterionType,
|
||||
@@ -842,7 +854,7 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
|
||||
/// <param name="inQuery"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost]
|
||||
public async Task<PageOutput<ReReadingTaskView>> GetIRReReadingTaskList(VisitTaskQuery inQuery)
|
||||
public async Task<IResponseOutput<PageOutput<ReReadingTaskView>>> GetIRReReadingTaskList(VisitTaskQuery inQuery)
|
||||
{
|
||||
|
||||
var visitTaskQueryable = _visitTaskReReadingRepository
|
||||
@@ -866,6 +878,7 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
|
||||
|
||||
.WhereIf(!string.IsNullOrEmpty(inQuery.TrialSiteCode), t => (t.OriginalReReadingTask.BlindTrialSiteCode.Contains(inQuery.TrialSiteCode!) && t.OriginalReReadingTask.IsAnalysisCreate) || (t.OriginalReReadingTask.Subject.TrialSite.TrialSiteCode.Contains(inQuery.TrialSiteCode!) && t.OriginalReReadingTask.IsAnalysisCreate == false))
|
||||
.WhereIf(!string.IsNullOrEmpty(inQuery.TaskName), t => t.OriginalReReadingTask.TaskName.Contains(inQuery.TaskName) || t.NewReReadingTask.TaskBlindName.Contains(inQuery.TaskName))
|
||||
.WhereIf(!string.IsNullOrEmpty(inQuery.TaskBlindName), t => t.NewReReadingTask.TaskBlindName.Contains(inQuery.TaskBlindName))
|
||||
.WhereIf(!string.IsNullOrEmpty(inQuery.SubjectCode), t => (t.OriginalReReadingTask.Subject.Code.Contains(inQuery.SubjectCode) && t.OriginalReReadingTask.IsAnalysisCreate == false) || (t.OriginalReReadingTask.BlindSubjectCode.Contains(inQuery.SubjectCode) && t.OriginalReReadingTask.IsAnalysisCreate))
|
||||
.WhereIf(inQuery.BeginAllocateDate != null, t => t.OriginalReReadingTask.AllocateTime > inQuery.BeginAllocateDate)
|
||||
.WhereIf(inQuery.EndAllocateDate != null, t => t.OriginalReReadingTask.AllocateTime < inQuery.EndAllocateDate!.Value.AddDays(1))
|
||||
@@ -877,7 +890,11 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
|
||||
|
||||
var pageList = await visitTaskQueryable.ToPagedListAsync(inQuery, defalutSortArray);
|
||||
|
||||
return pageList;
|
||||
var criterionInfo = await _readingQuestionCriterionTrialRepository.Where(t => t.Id == inQuery.TrialReadingCriterionId)
|
||||
.Select(t => new { t.IsReadingTaskViewInOrder }).FirstNotNullAsync();
|
||||
|
||||
return ResponseOutput.Ok(pageList, criterionInfo);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1009,9 +1026,9 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
|
||||
|
||||
if (critrion.IsReadingTaskViewInOrder == ReadingOrder.InOrder)
|
||||
{
|
||||
var extralObj = _trialRepository.Where(t => t.Id == trialId).Select(t => new { t.TrialExtraConfigJsonStr }).FirstOrDefault();
|
||||
var extralObj = _trialRepository.Where(t => t.Id == trialId).Select(t => new { t.IsOpenLostVistRead }).FirstOrDefault();
|
||||
|
||||
var extralConfig = JsonConvert.DeserializeObject<TrialExtraConfig>(extralObj?.TrialExtraConfigJsonStr) ?? new TrialExtraConfig();
|
||||
//var extralConfig = JsonConvert.DeserializeObject<TrialExtraConfig>(extralObj?.TrialExtraConfigJsonStr) ?? new TrialExtraConfig();
|
||||
|
||||
var visitQuery = _visitTaskRepository.Where(x => x.TrialId == inQuery.TrialId && x.DoctorUserId == _userInfo.UserRoleId && x.TaskState == TaskState.Effect && x.Subject.IsSubjectQuit == false)
|
||||
|
||||
@@ -1020,7 +1037,7 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
|
||||
.WhereIf(critrion.IsAutoCreate == false, t => !t.Subject.SubjectCriteriaEvaluationVisitFilterList.Where(t => t.TrialReadingCriterionId == trialReadingCriterionId).Any(f => f.IsGeneratedTask == false && t.VisitTaskNum > f.SubjectVisit.VisitNum))
|
||||
|
||||
// 前序 不存在 未一致性核查未通过的
|
||||
.Where(t => !t.Subject.SubjectVisitList.Where(t => extralConfig.IsOpenLostVistRead ? t.IsLostVisit == false : true).Any(sv => sv.CheckState != CheckStateEnum.CVPassed && t.VisitTaskNum >= sv.VisitNum))
|
||||
.Where(t => !t.Subject.SubjectVisitList.Where(t => extralObj.IsOpenLostVistRead ? t.IsLostVisit == false : true).Any(sv => sv.CheckState != CheckStateEnum.CVPassed && t.VisitTaskNum >= sv.VisitNum))
|
||||
//.WhereIf(critrion.IsAutoCreate == false, t => t.Subject.SubjectCriteriaEvaluationVisitFilterList.Where(t => t.TrialReadingCriterionId == trialReadingCriterionId).Any(t => t.IsGeneratedTask == false) ?
|
||||
//t.VisitTaskNum <= t.Subject.SubjectCriteriaEvaluationVisitFilterList.Where(t => t.TrialReadingCriterionId == trialReadingCriterionId && t.IsGeneratedTask == false).Min(t => t.SubjectVisit.VisitNum) : true)
|
||||
//.Where(t => t.Subject.SubjectVisitList.Any(t => t.CheckState != CheckStateEnum.CVPassed) ? t.VisitTaskNum <= t.Subject.SubjectVisitList.Where(t => t.CheckState != CheckStateEnum.CVPassed).Min(t => t.VisitNum) : true)
|
||||
@@ -1043,14 +1060,14 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
|
||||
UnReadTaskCount = x.Where(y => y.TrialReadingCriterionId == trialReadingCriterionId && y.ReadingTaskState != ReadingTaskState.HaveSigned).Count(),
|
||||
|
||||
//未读 里可读任务量
|
||||
UnReadCanReadTaskCount = x.Where(t => extralConfig.IsOpenLostVistRead ? true : t.IsFrontTaskNeedSignButNotSign == false).Where(y => y.TrialReadingCriterionId == trialReadingCriterionId && y.ReadingTaskState != ReadingTaskState.HaveSigned && (y.IsNeedClinicalDataSign == false || y.IsClinicalDataSign == true)
|
||||
UnReadCanReadTaskCount = x.Where(t => extralObj.IsOpenLostVistRead ? true : t.IsFrontTaskNeedSignButNotSign == false).Where(y => y.TrialReadingCriterionId == trialReadingCriterionId && y.ReadingTaskState != ReadingTaskState.HaveSigned && (y.IsNeedClinicalDataSign == false || y.IsClinicalDataSign == true)
|
||||
//不能对包含聚合或子查询的表达式执行聚合函数
|
||||
//&& !x.Any(t => t.ReadingTaskState != ReadingTaskState.HaveSigned && t.IsNeedClinicalDataSign == true && t.IsClinicalDataSign == false && t.VisitTaskNum<y.VisitTaskNum )
|
||||
).Count(),
|
||||
|
||||
|
||||
UnReadCanReadTaskList = x.Where(y => y.TrialReadingCriterionId == trialReadingCriterionId && y.ReadingTaskState != ReadingTaskState.HaveSigned)
|
||||
.Where(t => extralConfig.IsOpenLostVistRead ? true : t.IsFrontTaskNeedSignButNotSign == false)
|
||||
.Where(t => extralObj.IsOpenLostVistRead ? true : t.IsFrontTaskNeedSignButNotSign == false)
|
||||
.Where(y => /*y.IsFrontTaskNeedSignButNotSign == false &&*/ (y.IsNeedClinicalDataSign == false || y.IsClinicalDataSign == true))
|
||||
.OrderBy(x => x.VisitTaskNum)
|
||||
.Select(u => new IRUnreadTaskView()
|
||||
@@ -1172,8 +1189,8 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
|
||||
var visitQuery = _visitTaskRepository.Where(x => x.TrialId == trialId && x.DoctorUserId == _userInfo.UserRoleId && x.TaskState == TaskState.Effect)
|
||||
.WhereIf(inQuery.SubjectId != null, x => x.SubjectId == inQuery.SubjectId)
|
||||
.WhereIf(!string.IsNullOrEmpty(subjectCode), t => (t.Subject.Code.Contains(subjectCode!) && t.IsAnalysisCreate == false) || (t.BlindSubjectCode.Contains(subjectCode!) && t.IsAnalysisCreate))
|
||||
.WhereIf(critrion.CriterionType == CriterionType.OCT, t => t.ReadingCategory == ReadingCategory.Visit ? t.SourceSubjectVisit.NoneDicomStudyList.SelectMany(t => t.ImageLabelNoneDicomFileList).Any() : true)
|
||||
.WhereIf(critrion.CriterionType == CriterionType.IVUS, t => t.ReadingCategory == ReadingCategory.Visit ? t.SourceSubjectVisit.TaskStudyList.Any() : true);
|
||||
.WhereIf(critrion.CriterionType == CriterionType.OCT, t => t.ReadingCategory == ReadingCategory.Visit ? t.SourceSubjectVisit.NoneDicomStudyList.Where(t => t.Modality == "OCT").SelectMany(t => t.ImageLabelNoneDicomFileList).Any() : true)
|
||||
.WhereIf(critrion.CriterionType == CriterionType.IVUS, t => t.ReadingCategory == ReadingCategory.Visit ? t.SourceSubjectVisit.NoneDicomStudyList.Where(t => t.Modality == "IVUS").SelectMany(t => t.ImageLabelNoneDicomFileList).Any() : true);
|
||||
|
||||
var visitGroupQuery = visitQuery.GroupBy(x => new { x.SubjectId, x.Subject.Code, x.BlindSubjectCode });
|
||||
|
||||
@@ -1230,7 +1247,7 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
|
||||
/// <param name="inQuery"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost]
|
||||
public async Task<PageOutput<IRHaveReadView>> GetIRHaveReadTaskList(VisitTaskQuery inQuery)
|
||||
public async Task<IResponseOutput<PageOutput<IRHaveReadView>>> GetIRHaveReadTaskList(VisitTaskQuery inQuery)
|
||||
{
|
||||
|
||||
|
||||
@@ -1249,6 +1266,7 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
|
||||
.WhereIf(inQuery.TaskState != null, t => t.TaskState == inQuery.TaskState)
|
||||
.WhereIf(!string.IsNullOrEmpty(inQuery.TrialSiteCode), t => (t.BlindTrialSiteCode.Contains(inQuery.TrialSiteCode!) && t.IsAnalysisCreate) || (t.Subject.TrialSite.TrialSiteCode.Contains(inQuery.TrialSiteCode!) && t.IsAnalysisCreate == false))
|
||||
.WhereIf(!string.IsNullOrEmpty(inQuery.TaskName), t => t.TaskName.Contains(inQuery.TaskName) || t.TaskBlindName.Contains(inQuery.TaskName))
|
||||
.WhereIf(!string.IsNullOrEmpty(inQuery.TaskBlindName), t => t.TaskBlindName.Contains(inQuery.TaskBlindName))
|
||||
.WhereIf(!string.IsNullOrEmpty(inQuery.SubjectCode), t => (t.Subject.Code.Contains(inQuery.SubjectCode) && t.IsAnalysisCreate == false) || (t.BlindSubjectCode.Contains(inQuery.SubjectCode) && t.IsAnalysisCreate))
|
||||
.WhereIf(inQuery.BeginAllocateDate != null, t => t.AllocateTime > inQuery.BeginAllocateDate)
|
||||
.WhereIf(inQuery.EndAllocateDate != null, t => t.AllocateTime < inQuery.EndAllocateDate!.Value.AddDays(1))
|
||||
@@ -1258,7 +1276,10 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
|
||||
|
||||
var pageList = await visitTaskQueryable.ToPagedListAsync(inQuery, defalutSortArray);
|
||||
|
||||
return pageList;
|
||||
var criterionInfo = await _readingQuestionCriterionTrialRepository.Where(t => t.Id == inQuery.TrialReadingCriterionId)
|
||||
.Select(t => new { t.IsReadingTaskViewInOrder }).FirstNotNullAsync();
|
||||
|
||||
return ResponseOutput.Ok(pageList, criterionInfo);
|
||||
}
|
||||
|
||||
|
||||
@@ -2067,27 +2088,27 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
|
||||
//申请表 设置新任务Id
|
||||
visitTaskReReadingAppply.NewReReadingTaskId = newTask.Id;
|
||||
|
||||
//拷贝原始表单
|
||||
if (visitTaskReReadingAppply.IsCopyOrigenalForms && influenceTask.Id == origenalTask.Id)
|
||||
|
||||
if (origenalTask.ReadingCategory == ReadingCategory.Visit)
|
||||
{
|
||||
if (origenalTask.ReadingCategory == ReadingCategory.Visit)
|
||||
CopyImages(newTask, influenceTask);
|
||||
|
||||
//拷贝原始表单
|
||||
if (visitTaskReReadingAppply.IsCopyOrigenalForms && influenceTask.Id == origenalTask.Id)
|
||||
{
|
||||
CopyForms(newTask, origenalTask);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//拷贝后续表单
|
||||
if (visitTaskReReadingAppply.IsCopyFollowForms && origenalTask.VisitTaskNum != influenceTask.VisitTaskNum)
|
||||
{
|
||||
if (origenalTask.ReadingCategory == ReadingCategory.Visit)
|
||||
//拷贝后续表单
|
||||
if (visitTaskReReadingAppply.IsCopyFollowForms && origenalTask.VisitTaskNum != influenceTask.VisitTaskNum)
|
||||
{
|
||||
CopyForms(newTask, origenalTask);
|
||||
newTask.IsCopyLesionAnswer = true;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2176,25 +2197,26 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
|
||||
//申请表 设置新任务Id
|
||||
visitTaskReReadingAppply.NewReReadingTaskId = newTask.Id;
|
||||
|
||||
|
||||
if (visitTaskReReadingAppply.IsCopyOrigenalForms && influenceTask.Id == origenalTask.Id)
|
||||
{
|
||||
if (origenalTask.ReadingCategory == ReadingCategory.Visit)
|
||||
{
|
||||
CopyForms(newTask, origenalTask);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//拷贝后续表单
|
||||
if (visitTaskReReadingAppply.IsCopyFollowForms && origenalTask.VisitTaskNum != influenceTask.VisitTaskNum)
|
||||
{
|
||||
if (origenalTask.ReadingCategory == ReadingCategory.Visit)
|
||||
{
|
||||
CopyForms(newTask, origenalTask);
|
||||
////拷贝后续表单
|
||||
//if (visitTaskReReadingAppply.IsCopyFollowForms && origenalTask.VisitTaskNum != influenceTask.VisitTaskNum)
|
||||
//{
|
||||
// if (origenalTask.ReadingCategory == ReadingCategory.Visit)
|
||||
// {
|
||||
// newTask.IsCopyLesionAnswer = true;
|
||||
// //CopyForms(newTask, origenalTask);
|
||||
|
||||
}
|
||||
}
|
||||
// }
|
||||
//}
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -2214,9 +2236,86 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void CopyImages(VisitTask newTask, VisitTask origenalTask)
|
||||
{
|
||||
//拷贝后处理绑定的的Dicom影像
|
||||
|
||||
var newTaskId = newTask.Id;
|
||||
|
||||
var taskStudyList = _taskStudyRepository.Where(t => t.VisitTaskId == origenalTask.Id).ToList();
|
||||
|
||||
var taskSeriesList = _taskSeriesRepository.Where(t => t.VisitTaskId == origenalTask.Id).ToList();
|
||||
var taskInstanceList = _taskInstanceRepository.Where(t => t.VisitTaskId == origenalTask.Id).ToList();
|
||||
|
||||
|
||||
foreach (var taskStudy in taskStudyList)
|
||||
{
|
||||
var newStudyId = IdentifierHelper.CreateGuid(taskStudy.StudyInstanceUid, taskStudy.TrialId.ToString(), newTaskId.ToString());
|
||||
|
||||
var newStudy = taskStudy.Clone();
|
||||
newStudy.Id = newStudyId;
|
||||
newStudy.SeqId = NewId.NextSequentialGuid();
|
||||
newStudy.VisitTaskId = newTaskId;
|
||||
|
||||
_ = _taskStudyRepository.AddAsync(newStudy).Result;
|
||||
|
||||
var studySeriesList = taskSeriesList.Where(t => t.StudyId == taskStudy.Id).ToList();
|
||||
|
||||
foreach (var series in studySeriesList)
|
||||
{
|
||||
var newSeriesId = IdentifierHelper.CreateGuid(series.StudyInstanceUid, series.SeriesInstanceUid, series.TrialId.ToString(), newTaskId.ToString());
|
||||
|
||||
var newSeries = series.Clone();
|
||||
newSeries.Id = newSeriesId;
|
||||
newSeries.SeqId = NewId.NextSequentialGuid();
|
||||
newSeries.StudyId = newStudyId;
|
||||
newSeries.VisitTaskId = newTaskId;
|
||||
|
||||
|
||||
_ = _taskSeriesRepository.AddAsync(newSeries).Result;
|
||||
|
||||
|
||||
var studyInstanceList = taskInstanceList.Where(t => t.StudyId == taskStudy.Id && t.SeriesId == series.Id).ToList();
|
||||
|
||||
foreach (var instance in studyInstanceList)
|
||||
{
|
||||
var newInstanceId = IdentifierHelper.CreateGuid(newStudy.StudyInstanceUid, newSeries.SeriesInstanceUid, instance.SopInstanceUid, newStudy.TrialId.ToString(), newTaskId.ToString());
|
||||
|
||||
var newInstance = instance.Clone();
|
||||
|
||||
newInstance.Id = newInstanceId;
|
||||
newInstance.SeqId = NewId.NextSequentialGuid();
|
||||
newInstance.StudyId = newStudyId;
|
||||
newInstance.SeriesId = newSeriesId;
|
||||
newInstance.VisitTaskId = newTaskId;
|
||||
|
||||
Console.WriteLine($"StudyInstanceUid:{newStudy.StudyInstanceUid} SeriesInstanceUid:{newSeries.SeriesInstanceUid} SopInstanceUid: {instance.SopInstanceUid},newInstanceId:{newInstanceId} ");
|
||||
|
||||
_ = _taskInstanceRepository.AddAsync(newInstance).Result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//拷贝后处理的NoneDicom
|
||||
|
||||
var fileList = _noneDicomStudyFileRepository.Where(t => t.VisitTaskId == origenalTask.Id).ToList();
|
||||
|
||||
foreach (var file in fileList)
|
||||
{
|
||||
var newFile = file.Clone();
|
||||
|
||||
newFile.Id = NewId.NextSequentialGuid();
|
||||
newFile.VisitTaskId = newTaskId;
|
||||
|
||||
_ = _noneDicomStudyFileRepository.AddAsync(newFile).Result;
|
||||
}
|
||||
}
|
||||
private void CopyForms(VisitTask newTask, VisitTask origenalTask)
|
||||
{
|
||||
|
||||
newTask.IsCopyLesionAnswer = true;
|
||||
//自定义
|
||||
//var readingCustomTagList = _readingCustomTagRepository.Where(t => t.VisitTaskId == origenalTask.Id).ToList();
|
||||
|
||||
@@ -2232,6 +2331,9 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
var readingTaskQuestionAnswerList = _readingTaskQuestionAnswerRepository.Where(t => t.VisitTaskId == origenalTask.Id).ToList();
|
||||
|
||||
foreach (var item in readingTaskQuestionAnswerList)
|
||||
@@ -2258,7 +2360,7 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
|
||||
|
||||
//ReadingTableAnswerRowInfo ReadingTableQuestionAnswer 一起加
|
||||
var readingTableAnswerRowInfoList = _readingTableAnswerRowInfoRepository.Where(t => t.VisitTaskId == origenalTask.Id).Include(t => t.LesionAnswerList).ToList();
|
||||
|
||||
Dictionary<Guid, Guid> lesionRelationship = new Dictionary<Guid, Guid>() { };
|
||||
foreach (var item in readingTableAnswerRowInfoList)
|
||||
{
|
||||
|
||||
@@ -2266,7 +2368,7 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
|
||||
var originalFristAddTaskId = item.FristAddTaskId;
|
||||
|
||||
var newRowId = NewId.NextSequentialGuid();
|
||||
|
||||
lesionRelationship.Add(item.Id, newRowId);
|
||||
foreach (var mark in readingTaskQuestionMarkList)
|
||||
{
|
||||
mark.RowId = mark.RowId == item.Id ? newRowId : mark.RowId;
|
||||
@@ -2288,13 +2390,112 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#region 分割
|
||||
Dictionary<Guid, Guid> segmentationRelationship = new Dictionary<Guid, Guid>() { };
|
||||
var segmentationList = _segmentationRepository.Where(t => t.VisitTaskId == origenalTask.Id).ToList();
|
||||
|
||||
foreach (var item in segmentationList)
|
||||
{
|
||||
|
||||
var newSegmentationId = NewId.NextSequentialGuid();
|
||||
segmentationRelationship.Add(item.Id, newSegmentationId);
|
||||
item.Id = newSegmentationId;
|
||||
item.VisitTaskId = newTask.Id;
|
||||
}
|
||||
|
||||
|
||||
Dictionary<Guid, Guid> segmentRelationship = new Dictionary<Guid, Guid>() { };
|
||||
var segmentList = _segmentRepository.Where(t => t.VisitTaskId == origenalTask.Id).ToList();
|
||||
foreach (var item in segmentList)
|
||||
{
|
||||
var newSegmentationId = NewId.NextSequentialGuid();
|
||||
segmentRelationship.Add(item.Id, newSegmentationId);
|
||||
if (segmentationRelationship.ContainsKey(item.SegmentationId))
|
||||
{
|
||||
item.SegmentationId = segmentationRelationship[item.SegmentationId];
|
||||
}
|
||||
item.Id = newSegmentationId;
|
||||
item.VisitTaskId = newTask.Id;
|
||||
}
|
||||
|
||||
var segmentBindingList = _segmentBindingRepository.Where(x => x.VisitTaskId == origenalTask.Id).ToList();
|
||||
|
||||
foreach (var item in segmentBindingList)
|
||||
{
|
||||
if (segmentationRelationship.ContainsKey(item.SegmentationId))
|
||||
{
|
||||
item.SegmentationId = segmentationRelationship[item.SegmentationId];
|
||||
}
|
||||
if (segmentRelationship.ContainsKey(item.SegmentId))
|
||||
{
|
||||
item.SegmentId = segmentRelationship[item.SegmentId];
|
||||
}
|
||||
|
||||
if (item.RowId != null && lesionRelationship.ContainsKey(item.RowId.Value))
|
||||
{
|
||||
item.RowId = lesionRelationship[item.RowId.Value];
|
||||
}
|
||||
item.Id = NewId.NextSequentialGuid();
|
||||
item.VisitTaskId = newTask.Id;
|
||||
}
|
||||
|
||||
|
||||
_ = _segmentationRepository.AddRangeAsync(segmentationList).Result;
|
||||
_ = _segmentRepository.AddRangeAsync(segmentList).Result;
|
||||
_ = _segmentBindingRepository.AddRangeAsync(segmentBindingList).Result;
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
foreach (var item in readingTableAnswerRowInfoList)
|
||||
{
|
||||
if (item.SplitRowId != null && lesionRelationship.ContainsKey(item.SplitRowId.Value))
|
||||
{
|
||||
item.SplitRowId = lesionRelationship[item.SplitRowId.Value];
|
||||
}
|
||||
|
||||
if (item.MergeRowId != null && lesionRelationship.ContainsKey(item.MergeRowId.Value))
|
||||
{
|
||||
item.MergeRowId = lesionRelationship[item.MergeRowId.Value];
|
||||
}
|
||||
}
|
||||
|
||||
_ = _readingTaskQuestionMarkRepository.AddRangeAsync(readingTaskQuestionMarkList).Result;
|
||||
_ = _readingTableAnswerRowInfoRepository.AddRangeAsync(readingTableAnswerRowInfoList).Result;
|
||||
|
||||
|
||||
var noneMarkList = _readingNoneDicomMarkRepository.Where(x => x.VisitTaskId == origenalTask.Id).ToList();
|
||||
|
||||
var noneMarkBindingList = _readingNoneDicomMarkBindingRepository.Where(x => x.VisitTaskId == origenalTask.Id).ToList();
|
||||
|
||||
|
||||
foreach (var item in noneMarkList)
|
||||
{
|
||||
var newid = NewId.NextSequentialGuid();
|
||||
|
||||
item.MeasureData = item.MeasureData.Replace(origenalTask.Id.ToString(), newTask.Id.ToString());
|
||||
item.VisitTaskId = newTask.Id;
|
||||
foreach (var item1 in noneMarkBindingList)
|
||||
{
|
||||
if (item1.NoneDicomMarkId == item.Id)
|
||||
{
|
||||
item1.NoneDicomMarkId = newid;
|
||||
}
|
||||
}
|
||||
|
||||
item.Id = newid;
|
||||
}
|
||||
|
||||
|
||||
foreach (var item in noneMarkBindingList)
|
||||
{
|
||||
item.Id = NewId.NextSequentialGuid();
|
||||
item.VisitTaskId = newTask.Id;
|
||||
}
|
||||
|
||||
_ = _readingNoneDicomMarkRepository.AddRangeAsync(noneMarkList).Result;
|
||||
_ = _readingNoneDicomMarkBindingRepository.AddRangeAsync(noneMarkBindingList).Result;
|
||||
}
|
||||
|
||||
|
||||
@@ -2387,6 +2588,10 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
|
||||
//另一个阅片人的任务根据任务进度自动进入PM退回或PM申请重阅
|
||||
filterExpression = filterExpression.And(t => t.VisitTaskNum >= task.VisitTaskNum);
|
||||
|
||||
//退回只影响有序的后续所有的,无序的当前访视
|
||||
filterExpression = filterExpression.And(t => (t.TrialReadingCriterion.IsReadingTaskViewInOrder == ReadingOrder.InOrder) ||
|
||||
(t.TrialReadingCriterion.IsReadingTaskViewInOrder != ReadingOrder.InOrder && t.SourceSubjectVisitId == task.SourceSubjectVisitId));
|
||||
|
||||
|
||||
var influenceTaskList = await _visitTaskRepository.Where(filterExpression, true).ToListAsync();
|
||||
|
||||
@@ -2544,9 +2749,12 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
|
||||
|
||||
//删除序列数据
|
||||
await _subjectCriteriaEvaluationVisitStudyFilterRepository.BatchDeleteNoTrackingAsync(t => t.TrialReadingCriterion.CriterionType == CriterionType.RECIST1Pointt1_MB && t.SubjectVisit.SubjectId == task.SubjectId && t.SubjectVisitId == task.SourceSubjectVisitId);
|
||||
|
||||
otherVisitIdList = otherVisitIdList.Where(t => t != task.SourceSubjectVisitId.Value).ToList();
|
||||
}
|
||||
|
||||
|
||||
|
||||
//BM后续访视 ,筛选状态不变,任务生成状态重置(实际该访视任务状态 可能是重阅重置了或者失效了,需要后续生成,或者取消分配了,需要后续重新分配)
|
||||
await _subjectCriteriaEvaluationVisitFilterRepository.UpdatePartialFromQueryAsync(t => t.TrialReadingCriterion.CriterionType == CriterionType.RECIST1Pointt1_MB && t.SubjectVisit.SubjectId == task.SubjectId && otherVisitIdList.Contains(t.SubjectVisitId),
|
||||
t => new SubjectCriteriaEvaluationVisitFilter()
|
||||
@@ -2600,6 +2808,19 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
|
||||
sv.ReviewAuditUserId = null;
|
||||
sv.SecondReviewState = SecondReviewState.None;
|
||||
|
||||
|
||||
// 处理阅片期的临床数据
|
||||
var readModuleIdList = await _readModuleRepository.Where(x => x.SubjectVisitId == sv.Id).Select(x => x.Id).ToListAsync();
|
||||
|
||||
|
||||
await _readingClinicalDataReposiotry.UpdatePartialFromQueryAsync(t => readModuleIdList.Contains(t.ReadingId), c => new ReadingClinicalData()
|
||||
{
|
||||
IsSign = false,
|
||||
ReadingClinicalDataState = ReadingClinicalDataStatus.HaveUploaded,
|
||||
});
|
||||
|
||||
|
||||
|
||||
if (sv.IsBaseLine)
|
||||
{
|
||||
await _readingClinicalDataReposiotry.UpdatePartialFromQueryAsync(t => t.ReadingId == sv.Id && (t.ClinicalDataTrialSet.ClinicalDataLevel == ClinicalLevel.Subject || t.ClinicalDataTrialSet.ClinicalDataLevel == ClinicalLevel.SubjectVisit), c => new ReadingClinicalData() { IsSign = false, ReadingClinicalDataState = ReadingClinicalDataStatus.HaveUploaded });
|
||||
@@ -2708,6 +2929,13 @@ public class VisitTaskService(IRepository<VisitTask> _visitTaskRepository,
|
||||
//默认影响的都是该标准的任务
|
||||
filterExpression = filterExpression.And(t => t.TrialReadingCriterionId == filterObj.TrialReadingCriterionId);
|
||||
}
|
||||
|
||||
//退回只影响有序的后续所有的,无序的当前访视
|
||||
if (isReReading == false)
|
||||
{
|
||||
filterExpression = filterExpression.And(t => (t.TrialReadingCriterion.IsReadingTaskViewInOrder == ReadingOrder.InOrder) ||
|
||||
(t.TrialReadingCriterion.IsReadingTaskViewInOrder != ReadingOrder.InOrder && t.SourceSubjectVisitId == filterObj.SourceSubjectVisitId));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -96,6 +96,8 @@ namespace IRaCIS.Application.Contracts
|
||||
public Guid? TrialReadingCriterionId { get; set; }
|
||||
|
||||
public Guid? SystemReadingCriterionId { get; set; }
|
||||
|
||||
public Guid? TrialId { get; set; }
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,187 @@
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// 此代码由liquid模板自动生成 byzhouhang 20240909
|
||||
// 生成时间 2025-10-28 06:22:47Z
|
||||
// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。
|
||||
//--------------------------------------------------------------------
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
namespace IRaCIS.Core.Application.ViewModel;
|
||||
|
||||
|
||||
public class GetReSendEmailInDto : PageInput
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
|
||||
|
||||
public DateTime? EmailStartDate { get; set; }
|
||||
|
||||
public DateTime? EmailEndDate { get; set; }
|
||||
|
||||
public EmailState? EmailStateEnum { get; set; }
|
||||
|
||||
public string ToRecipientName { get; set; } = string.Empty;
|
||||
|
||||
public string CcRecipientName { get; set; } = string.Empty;
|
||||
}
|
||||
public class EmailLogView : EmailLogAddOrEdit
|
||||
{
|
||||
|
||||
public DateTime CreateTime { get; set; }
|
||||
|
||||
public DateTime UpdateTime { get; set; }
|
||||
|
||||
public List<EmailRecipientLogView> RecipientList { get; set; }
|
||||
|
||||
public List<EmaliAttachmentInfo> AttachmentList { get; set; }
|
||||
|
||||
}
|
||||
|
||||
|
||||
public class EmaliAttachmentInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// 附件名称
|
||||
/// </summary>
|
||||
public string AttachmentName { get; set; }
|
||||
/// <summary>
|
||||
/// 附件路径
|
||||
/// </summary>
|
||||
public string AttachmentPath { get; set; }
|
||||
}
|
||||
|
||||
public class EmailRecipientLogView
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 邮件Id
|
||||
/// </summary>
|
||||
public Guid EmailLogId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 收件人姓名
|
||||
/// </summary>
|
||||
public string RecipientName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 收件人地址
|
||||
/// </summary>
|
||||
public string RecipientAddress { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 收件人类型
|
||||
/// </summary>
|
||||
public RecipientType RecipientTypeEnum { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 排序
|
||||
/// </summary>
|
||||
public int Sort { get; set; }
|
||||
}
|
||||
|
||||
|
||||
public class EmailLogAddOrEdit
|
||||
{
|
||||
public Guid? Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 邮件Id
|
||||
/// </summary>
|
||||
public string MessageId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 唯一Id
|
||||
/// </summary>
|
||||
public string UniqueId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 邮件主题
|
||||
/// </summary>
|
||||
public string EmailSubject { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 日期
|
||||
/// </summary>
|
||||
public DateTime? EmailDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 错误信息
|
||||
/// </summary>
|
||||
public string ErrorInfo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 发件人地址
|
||||
/// </summary>
|
||||
public string SenderAddress { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 发件人姓名
|
||||
/// </summary>
|
||||
public string SenderName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 邮件内容
|
||||
/// </summary>
|
||||
public string Content { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 邮件状态
|
||||
/// </summary>
|
||||
public EmailState EmailStateEnum { get; set; }
|
||||
}
|
||||
|
||||
public class GetEmailInfoOutDto:EmailLogView
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class GetEmailInfoInDto
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
|
||||
public Guid? TrialId { get; set; }
|
||||
}
|
||||
|
||||
public class SynchronizationEmailInDto
|
||||
{
|
||||
public Guid? TrialId { get; set; }
|
||||
}
|
||||
|
||||
public class ResendEmailInDto
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
|
||||
public Guid? TrialId { get; set; }
|
||||
}
|
||||
|
||||
public class EmailAuthorization
|
||||
{
|
||||
public string FromEmail { get; set; } = string.Empty;
|
||||
|
||||
public string AuthorizationCode { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public class EmailLogQuery:PageInput
|
||||
{
|
||||
|
||||
public Guid? TrialId { get; set; }
|
||||
|
||||
public DateTime? EmailStartDate { get; set; }
|
||||
|
||||
public DateTime? EmailEndDate { get; set; }
|
||||
|
||||
public EmailState? EmailStateEnum { get; set; }
|
||||
|
||||
public string ToRecipientName { get; set; } = string.Empty;
|
||||
|
||||
public string CcRecipientName { get; set; } = string.Empty;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,248 @@
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// 此代码由liquid模板自动生成 byzhouhang 20240909
|
||||
// 生成时间 2026-03-10 06:15:31Z
|
||||
// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。
|
||||
//--------------------------------------------------------------------
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using System.Collections.Generic;
|
||||
namespace IRaCIS.Core.Application.ViewModel;
|
||||
|
||||
public class SubjectFileUploadRecordView
|
||||
{
|
||||
public string? SubjectCode { get; set; }
|
||||
|
||||
public string? VisitName { get; set; }
|
||||
|
||||
public string StudyCode { get; set; }
|
||||
|
||||
public Guid? SubjectId { get; set; }
|
||||
|
||||
public Guid? SubjectVisitId { get; set; }
|
||||
|
||||
public int FileCount { get; set; }
|
||||
|
||||
|
||||
public string? UploadRegion { get; set; }
|
||||
public string? TargetRegion { get; set; }
|
||||
|
||||
|
||||
public DateTime CreateTime { get; set; }
|
||||
public DateTime? SyncFinishedTime { get; set; }
|
||||
|
||||
public bool IsSync { get; set; }
|
||||
}
|
||||
|
||||
public class FileUploadRecordView : FileUploadRecordAddOrEdit
|
||||
{
|
||||
|
||||
public DateTime CreateTime { get; set; }
|
||||
|
||||
public DateTime UpdateTime { get; set; }
|
||||
|
||||
|
||||
|
||||
[Comment("同步结束时间-最后一个任务的时间")]
|
||||
public DateTime? SyncFinishedTime { get; set; }
|
||||
|
||||
|
||||
public string? SubjectCode { get; set; }
|
||||
|
||||
public string? VisitName { get; set; }
|
||||
|
||||
}
|
||||
|
||||
|
||||
public class FileUploadRecordAddOrEdit
|
||||
{
|
||||
public Guid? Id { get; set; }
|
||||
|
||||
|
||||
public string FileName { get; set; }
|
||||
|
||||
public long FileSize { get; set; }
|
||||
|
||||
public string FileType { get; set; }
|
||||
|
||||
public string Path { get; set; }
|
||||
|
||||
|
||||
public string UploadBatchId { get; set; }
|
||||
public BatchDataType BatchDataType { get; set; }
|
||||
|
||||
public string StudyCode { get; set; }
|
||||
|
||||
public Guid? TrialId { get; set; }
|
||||
|
||||
public Guid? SubjectId { get; set; }
|
||||
|
||||
public Guid? SubjectVisitId { get; set; }
|
||||
|
||||
public Guid? DicomStudyId { get; set; }
|
||||
|
||||
public Guid? NoneDicomStudyId { get; set; }
|
||||
|
||||
|
||||
|
||||
public string FileMarkId { get; set; }
|
||||
|
||||
public int? Priority { get; set; }
|
||||
public string IP { get; set; }
|
||||
public bool? IsNeedSync { get; set; }
|
||||
public string UploadRegion { get; set; }
|
||||
public string TargetRegion { get; set; }
|
||||
|
||||
public bool? IsSync { get; set; }
|
||||
}
|
||||
|
||||
public class CommonUploadRecordDto: CommonUploadRecordAddOrEdit
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class CommonUploadRecordAddOrEdit
|
||||
{
|
||||
public Guid? Id { get; set; }
|
||||
public string FileType { get; set; }
|
||||
|
||||
public string FileName { get; set; }
|
||||
|
||||
public long FileSize { get; set; }
|
||||
|
||||
public string Path { get; set; }
|
||||
}
|
||||
public class SubjectFileUploadRecordQuery : PageInput
|
||||
{
|
||||
public Guid TrialId { get; set; }
|
||||
|
||||
public string? SubjectCode { get; set; }
|
||||
|
||||
public string? VisitName { get; set; }
|
||||
|
||||
public string? StudyCode { get; set; }
|
||||
|
||||
public string? UploadRegion { get; set; }
|
||||
public string? TargetRegion { get; set; }
|
||||
|
||||
public bool? IsSync { get; set; }
|
||||
|
||||
|
||||
}
|
||||
|
||||
public class FileUploadRecordQuery : PageInput
|
||||
{
|
||||
|
||||
public BatchDataType? BatchDataType { get; set; }
|
||||
|
||||
public Guid? TrialId { get; set; }
|
||||
|
||||
public int? DataFileType { get; set; }
|
||||
|
||||
public string StudyCode { get; set; }
|
||||
|
||||
public string? SubjectCode { get; set; }
|
||||
|
||||
public string? VisitName { get; set; }
|
||||
|
||||
|
||||
public Guid? SubjectId { get; set; }
|
||||
|
||||
public Guid? SubjectVisitId { get; set; }
|
||||
|
||||
|
||||
|
||||
public string? FileMarkId { get; set; }
|
||||
|
||||
public string? FileName { get; set; }
|
||||
|
||||
|
||||
public string? FileType { get; set; }
|
||||
|
||||
public string? IP { get; set; }
|
||||
|
||||
public bool? IsNeedSync { get; set; }
|
||||
|
||||
public bool? IsSync { get; set; }
|
||||
|
||||
|
||||
public string? Path { get; set; }
|
||||
|
||||
public int? Priority { get; set; }
|
||||
|
||||
public string? TargetRegion { get; set; }
|
||||
|
||||
|
||||
public string? UploadBatchId { get; set; }
|
||||
|
||||
public string? UploadRegion { get; set; }
|
||||
|
||||
|
||||
public DateTime? SyncFinishedStartTime { get; set; }
|
||||
|
||||
public DateTime? SyncFinishedEndTime { get; set; }
|
||||
|
||||
public DateTime? UploadStartTime { get; set; }
|
||||
|
||||
public DateTime? UploadEndTime { get; set; }
|
||||
}
|
||||
|
||||
|
||||
|
||||
public class UploadFileSyncRecordView
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
|
||||
|
||||
public DateTime? StartTime { get; set; }
|
||||
|
||||
public DateTime? EndTime { get; set; }
|
||||
|
||||
public jobState JobState { get; set; }
|
||||
|
||||
|
||||
public string Msg { get; set; }
|
||||
|
||||
public DateTime CreateTime { get; set; }
|
||||
|
||||
public DateTime UpdateTime { get; set; }
|
||||
|
||||
|
||||
public Guid? FileUploadRecordId { get; set; }
|
||||
public string? FileName { get; set; }
|
||||
|
||||
public string? FileType { get; set; }
|
||||
|
||||
public string? Path { get; set; }
|
||||
|
||||
|
||||
public string StudyCode { get; set; }
|
||||
|
||||
public string? SubjectCode { get; set; }
|
||||
|
||||
public string? VisitName { get; set; }
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
public class UploadFileSyncRecordQuery : PageInput
|
||||
{
|
||||
|
||||
public Guid? FileUploadRecordId { get; set; }
|
||||
|
||||
|
||||
public jobState? JobState { get; set; }
|
||||
|
||||
public string? SubjectCode { get; set; }
|
||||
|
||||
public string? VisitName { get; set; }
|
||||
|
||||
public string? StudyCode { get; set; }
|
||||
}
|
||||
|
||||
public class BatchAddSyncFileCommand
|
||||
{
|
||||
public List<Guid> FileUploadRecordIdList { get; set; }
|
||||
|
||||
public int? Priority { get; set; }
|
||||
}
|
||||
@@ -159,6 +159,12 @@ namespace IRaCIS.Core.Application.ViewModel
|
||||
public class FrontAuditConfigAddOrEdit
|
||||
{
|
||||
public Guid? Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 适用的标准
|
||||
/// </summary>
|
||||
public List<CriterionType> ApplyCriterionList { get; set; } = new List<CriterionType>() { };
|
||||
|
||||
public string Value { get; set; } = string.Empty;
|
||||
public string ValueCN { get; set; } = string.Empty;
|
||||
public string Description { get; set; } = string.Empty;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user