Compare commits
1484 Commits
test/stabl
...
716d720782
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
716d720782 | ||
|
|
6b5bacc49b | ||
|
|
409bc7b1fd | ||
|
|
ec6a5df8af | ||
|
|
029b96ae99 | ||
|
|
14002e7331 | ||
| 14dfe2806c | |||
| 798c7b0592 | |||
| 9bd10581f4 | |||
| 1f288fe5e3 | |||
| 72602eb245 | |||
| 983d53268d | |||
| f3aeeb3584 | |||
| 5d3692a204 | |||
| f2a074b2f6 | |||
| 6a7a37dcec | |||
|
|
c4d2780f0e | ||
|
|
1da6b7728c | ||
|
|
0faf77899b | ||
|
|
e4940019bf | ||
|
|
0da66ff210 | ||
|
|
5dd862ff79 | ||
|
|
edaec9884d | ||
|
|
76eeb2be53 | ||
|
|
cb6f94d2d4 | ||
|
|
28656c44c8 | ||
|
|
6757a89d04 | ||
|
|
9be1a1e307 | ||
|
|
2168978f61 | ||
|
|
54466b935d | ||
|
|
c970ebe691 | ||
|
|
1c5a3a12b9 | ||
|
|
6e06000083 | ||
|
|
dea2b3be42 | ||
|
|
bcf51aea23 | ||
|
|
0c9d5404c6 | ||
|
|
93429839c0 | ||
|
|
27859c3e28 | ||
|
|
f02c0930a6 | ||
|
|
d57bb83b25 | ||
| 731e34f133 | |||
| 75eca8d6ba | |||
| 3e53401f76 | |||
|
|
b6a068ebcd | ||
|
|
dc291ea086 | ||
|
|
2e846e671a | ||
|
|
a5093311f9 | ||
|
|
aed338a6d7 | ||
|
|
8bdb49d25c | ||
|
|
5d53a8cd42 | ||
|
|
61b7f3072f | ||
|
|
a1f489f3a1 | ||
|
|
fc3fd877a8 | ||
|
|
fc72d2c430 | ||
|
|
1ac01dd090 | ||
|
|
3bbdf7c672 | ||
|
|
0646484fba | ||
|
|
96b8613741 | ||
|
|
cf30226a51 | ||
|
|
3c15a3ff68 | ||
|
|
0c904be227 | ||
|
|
7759b56123 | ||
|
|
d5bfaa8822 | ||
| 967c0cbc01 | |||
| 417e34b41a | |||
|
|
d51aa84647 | ||
|
|
5895bc6ab6 | ||
|
|
3301869f20 | ||
|
|
1ec42f4ad5 | ||
| cc506ff7e9 | |||
|
|
f2d43f06f4 | ||
|
|
9251df49f8 | ||
| 430156f4e8 | |||
|
|
d1123aedcc | ||
| 8c007077a3 | |||
|
|
d63b4b4e63 | ||
|
|
b826f0bf39 | ||
|
|
1decd8e258 | ||
|
|
1286e84488 | ||
| a252fdf7f9 | |||
| 807d802178 | |||
| 53f1b548be | |||
| 45dd78032a | |||
| c160da5132 | |||
| b23faeeee2 | |||
| 67789abca4 | |||
| 1c78d66aab | |||
| 528bc69923 | |||
|
|
22880d128d | ||
| 9c56a102cc | |||
| 2f59fe074f | |||
| 9c61b1c8fe | |||
| e30fdf7401 | |||
| ba2d10afbc | |||
| 6146112d04 | |||
| 412550df27 | |||
| 497421e7fe | |||
| 891527426c | |||
|
|
3e334d7956 | ||
|
|
8f0d0953b2 | ||
| f5c3621a5d | |||
|
|
9a1a0045e0 | ||
| 6223c8e994 | |||
| 67bbee49fd | |||
| ad62ceb32a | |||
| 082afe9e94 | |||
| 49288c3a31 | |||
| 81624e36db | |||
| a526b122d1 | |||
|
|
d882b2e817 | ||
|
|
ebf6427d42 | ||
| 77fe03d361 | |||
| 7a44d67dbf | |||
| 55ce2c6c7e | |||
| a426caaca3 | |||
| 7cb7ce2836 | |||
| 8e075f1da4 | |||
|
|
0f0fde2a3e | ||
|
|
8c6389a1f6 | ||
| 652f82b6a4 | |||
| 7ca2528dcf | |||
|
|
a7800913d2 | ||
|
|
1eaec64ff4 | ||
| e603952332 | |||
| 2bc8b8ef96 | |||
| 0ce968b919 | |||
|
|
dfc9ae4db2 | ||
|
|
a3505c6d95 | ||
|
|
6db0afd515 | ||
|
|
b1e6183dd1 | ||
| 30d08356c0 | |||
| 64cc29f456 | |||
|
|
2b3e12a11c | ||
|
|
d4a4724f61 | ||
|
|
ba6e2bd24c | ||
|
|
a38895b028 | ||
|
|
69a95e66ca | ||
|
|
40518cab37 | ||
|
|
46d61cb73f | ||
| 08f20fd1fe | |||
| d7edc166b3 | |||
|
|
79ad02f66b | ||
|
|
5e261b55c7 | ||
|
|
bc92fcbaf4 | ||
|
|
c6aec917c2 | ||
| 6bc500e78f | |||
| 4c43b98c02 | |||
|
|
5bae785a9f | ||
|
|
7b619aa4cb | ||
| c93ad6daa9 | |||
| 0047be7a03 | |||
| 4ef209cfd4 | |||
| a19751b4b7 | |||
|
|
bb0e5a4263 | ||
|
|
9e9df5367d | ||
| ba8a2c52de | |||
| 39d8c7efcf | |||
|
|
401910901a | ||
|
|
3f5ce6e0e7 | ||
| 0787025151 | |||
| 08b26872ff | |||
| 5bbf1326bb | |||
|
|
c5e27cd220 | ||
|
|
112e9c3bc9 | ||
|
|
ce95cb5080 | ||
|
|
71211bfbc3 | ||
| 72ad977dcb | |||
|
|
6400e79929 | ||
|
|
dd8c72f7d7 | ||
| 13151b65f5 | |||
| 9f523d5953 | |||
| 4879cfeb60 | |||
| 9e252b16ef | |||
| e64add14af | |||
| 3beb27e491 | |||
| 501032ef17 | |||
|
|
cb25bdd2e0 | ||
|
|
7a9fb0213b | ||
| cd767dce6f | |||
| bf95b85841 | |||
| 9e58bd9e7d | |||
| d0ec5c5c26 | |||
| ab8aa5ea5c | |||
| aefcd2fdb0 | |||
| e74eab1070 | |||
|
|
34da437a26 | ||
|
|
f84935d0bd | ||
| 35edaa0f27 | |||
| f43099e19e | |||
| 8079877734 | |||
| ef686e38ac | |||
| 100019d2a4 | |||
|
|
12af237d76 | ||
|
|
44dbbb2a4b | ||
| 9f42e153a4 | |||
| 4fa70a1c90 | |||
| dfb34916e7 | |||
| 9f7987306c | |||
|
|
d3fc70fbf2 | ||
|
|
17645d17e5 | ||
|
|
258eea5277 | ||
|
|
bb1d3bd359 | ||
|
|
6a8c87ed95 | ||
|
|
eb3826927d | ||
|
|
7720c8c771 | ||
|
|
b459148d58 | ||
|
|
eadda18d1e | ||
|
|
d403df51ec | ||
| 6903b98b60 | |||
| 81bf65515c | |||
| 8e984eb283 | |||
|
|
afc6041570 | ||
| bce368248c | |||
| ca7121dcda | |||
|
|
9a206f9979 | ||
|
|
eb7a46c7e8 | ||
|
|
95ef68a784 | ||
|
|
6429288fd9 | ||
| c5af194876 | |||
| da84e1e4b4 | |||
| 95a9c81a1b | |||
| 81c0d7eeac | |||
|
|
fbc473735c | ||
|
|
9f48a2ce09 | ||
| 459c743ce4 | |||
| ce3516916d | |||
| 0692b29065 | |||
| 94cafbd10c | |||
| 5c2008ec4a | |||
|
|
4e4b8bd4d2 | ||
|
|
9c19d061be | ||
| 7f094265da | |||
| 22bc8750c8 | |||
| a940105fa4 | |||
| 4c0edf09bd | |||
| bd69793a72 | |||
|
|
a643338916 | ||
|
|
9344a946ad | ||
| fab0c7b372 | |||
| fc0867d9b1 | |||
| c36ba6211a | |||
|
|
aa0a787e3f | ||
|
|
964ee1c5cc | ||
| ac98cd01db | |||
| 70d498b90d | |||
| ae1a2eff2a | |||
| b54eaea7e3 | |||
| aa2363b9a5 | |||
| 41ef9132e9 | |||
| 3b5b57ab08 | |||
| 2ec9ef3290 | |||
| fe3fbbe135 | |||
| 01e82b4c23 | |||
|
|
e085275aee | ||
|
|
fa7271567e | ||
| 05049ae7a2 | |||
| b023a68f83 | |||
| 6cd42b799a | |||
| 6e1ed7f9b8 | |||
| b7be16738b | |||
| 6da5e91ec1 | |||
| 00c0a65547 | |||
| 77aab576de | |||
| 5b2c77f81b | |||
| 96040f2349 | |||
| a710fdd432 | |||
| d598f53d3c | |||
| 62bf538d96 | |||
| 456cb1ff19 | |||
| cbca666e7b | |||
| d622195f9d | |||
| 1917fd518d | |||
| 96170a9956 | |||
| 8205fb5290 | |||
| 8f72686809 | |||
| 37ddf6f441 | |||
| dbf9b2d722 | |||
| a91f9ccdd4 | |||
| dac51afe73 | |||
| 3aa744895f | |||
| 9535d8ce59 | |||
| 6c8b316dfd | |||
| e366d5a2c5 | |||
| b6add404b3 | |||
| fcbe4762b3 | |||
| e750adcc94 | |||
|
|
1c782f8fd7 | ||
|
|
b58b8df88f | ||
|
|
c09f734a30 | ||
| 1464689657 | |||
|
|
a2ff345da5 | ||
| 9ae1626309 | |||
| 70a41bdfbc | |||
|
|
8757db4f3d | ||
|
|
a8e3304db2 | ||
|
|
26f2299381 | ||
| c1a5799da7 | |||
| 94c0e81335 | |||
| b9641474ba | |||
| 059d6edc12 | |||
| 97c5be7b22 | |||
| c4da4ea111 | |||
|
|
db1048d875 | ||
| bbc8b025a9 | |||
| 630f6b2737 | |||
| d14fdfee79 | |||
| c14db62c0a | |||
|
|
9166bf1a1a | ||
|
|
84c024eab8 | ||
|
|
b86f3c9f7e | ||
| e2009617db | |||
| 65b17fe109 | |||
| 0917154283 | |||
| 0f525c6c00 | |||
| 4beae1094b | |||
| cefdac133e | |||
| a466f269c2 | |||
| 97f0c8f65f | |||
| ad00ac08ca | |||
| d236057f2a | |||
| 62e3e9c16f | |||
| 604c529b42 | |||
| bc612481dc | |||
| 2f3fa695b2 | |||
| 313fc19dac | |||
| f36e0488ea | |||
| f239c8c02b | |||
|
|
1d4017bafd | ||
|
|
8c98f91445 | ||
| 18f5528d59 | |||
| 88ce191950 | |||
|
|
f3d6d7b000 | ||
| ef84f32ca0 | |||
| 3886aeaa46 | |||
| 8750ea355a | |||
|
|
b107fa82df | ||
|
|
e56a6176a9 | ||
|
|
e35ed2c768 | ||
|
|
52094d58e7 | ||
|
|
e4e6cfbff7 | ||
|
|
15f2b78c94 | ||
|
|
1fd1f7dd47 | ||
|
|
2a6d3c1b58 | ||
| 59e33c22b7 | |||
| e5eecbfe8d | |||
| 9759cf4740 | |||
| e8766b9af3 | |||
| 4fed5f7439 | |||
|
|
c3e0a61ff7 | ||
|
|
58212e92ec | ||
| 5efaaf01b2 | |||
| 18c94ad1e0 | |||
| e825c9bd75 | |||
| 1af705592a | |||
|
|
d426fb34a2 | ||
|
|
8c3e1fdda5 | ||
|
|
a76b9ba365 | ||
| cd74022d04 | |||
|
|
d5b972ab16 | ||
| a3e63c9877 | |||
| 0c227a1efa | |||
|
|
59602540a3 | ||
| 4578cb5778 | |||
|
|
9959a03ccc | ||
|
|
ff43ffaba6 | ||
|
|
1c32aaa46c | ||
| db5f435a4d | |||
|
|
c9f9ef0ce2 | ||
|
|
3b6a9882d2 | ||
|
|
854847a6f4 | ||
|
|
309d95ab3c | ||
|
|
422d547cfd | ||
|
|
e6a0706d61 | ||
| 19c08f4237 | |||
| 6ac9680c64 | |||
| 4ce6fb4190 | |||
|
|
6b7abafb68 | ||
|
|
02f7031a67 | ||
| 3d67ac71f3 | |||
|
|
67f0ce7c6c | ||
|
|
5fbcaf9077 | ||
| ebb19ea15e | |||
|
|
5efa5cad33 | ||
|
|
f9dfd61b89 | ||
| 42aed5804b | |||
|
|
df5ee235f2 | ||
|
|
ad2bb7062e | ||
|
|
4fbacb37c1 | ||
|
|
3150f5ae57 | ||
|
|
20cf415635 | ||
|
|
7bd32914a1 | ||
|
|
24d54ea9ea | ||
| c00c2e738b | |||
|
|
6772610129 | ||
|
|
a681a298f4 | ||
|
|
e6d5d4f614 | ||
|
|
187c38ad61 | ||
|
|
95f6f02290 | ||
| ab7ef19657 | |||
|
|
d837f0dc78 | ||
|
|
ab6cc04483 | ||
| 9110e0dd34 | |||
| 0f084c27ea | |||
| b1b465a05a | |||
| 2e4b1516cc | |||
| 1e389bec86 | |||
| 2d0936f0f3 | |||
| dab253c313 | |||
| ed86cd45ad | |||
| 814324430c | |||
| 1d006342fb | |||
| 24fe265172 | |||
| 79e19a029b | |||
| 227a20f6f2 | |||
| efa26d912d | |||
|
|
ea19e915db | ||
| 41759ca44b | |||
| 29e1d864ae | |||
| 6cfd420e46 | |||
| 51024508b9 | |||
| b7b07b8843 | |||
| c1d26a87ce | |||
| d59a69e6f4 | |||
|
|
042501f9b1 | ||
|
|
9e6fcb4f73 | ||
| 9386a2c6c4 | |||
| c43a716104 | |||
|
|
950707ab44 | ||
|
|
b0e5f39765 | ||
| 5eebb878ca | |||
|
|
d568129843 | ||
| d093aa9d1a | |||
| 3c1d180ed6 | |||
| b9c24386b9 | |||
|
|
8f53902d18 | ||
| c79535ac3f | |||
| 106528ce08 | |||
|
|
23eda80389 | ||
|
|
eccd2a35e9 | ||
| 9c69aeae53 | |||
| 5bac740efd | |||
|
|
fa74236035 | ||
|
|
630dfe98a4 | ||
| c922563919 | |||
| ce68696934 | |||
| 5d8bdf9ecc | |||
| e3b0d3bc0a | |||
| 5f5f2c83d7 | |||
| aff51a2793 | |||
| 4c5d8d3c61 | |||
| f667099f4f | |||
| d5a74c1ff3 | |||
| c187d25d74 | |||
| f20a85a0e2 | |||
| 4f93ec144b | |||
| de16437d08 | |||
| c67d197da0 | |||
| d8f0188596 | |||
| ddc089faa4 | |||
| f5ecd34a9c | |||
| 3c071378ad | |||
| 8b2aab2e5e | |||
| c4a42b03f6 | |||
| 0dad9ff618 | |||
| 136bcdf82b | |||
| a59acb0903 | |||
| feca192f53 | |||
| 179b79812a | |||
| 94cf82340a | |||
| c649907222 | |||
| c93ae85b65 | |||
| 6441bfbbba | |||
| 933522482b | |||
| 56b56981b1 | |||
| 9e2ea2de62 | |||
| 7005b75e11 | |||
| 5b09d2cbf5 | |||
| 126ac209c6 | |||
| 98eb29eeb5 | |||
| ee551a0be2 | |||
| c3cbb61c16 | |||
| 71f0293cd9 | |||
| 35604e5130 | |||
| 18c38b020e | |||
| 6e9213ec5a | |||
| 83ba69c950 | |||
| 3cc329f983 | |||
| c42ee8a1bf | |||
| 9a650afc7d | |||
| 45a336ced3 | |||
| e258166a16 | |||
| f9ec733f07 | |||
| 54b09b4940 | |||
| 6d0c29f5c3 | |||
| 23523f73da | |||
| e45d572078 | |||
| eebb790aeb | |||
| 9c6573f1a6 | |||
| 6a5282e4d5 | |||
| 3c5a5ff225 | |||
| e4ce7e9e63 | |||
| 1089a5a2a5 | |||
| 156fcfae6b | |||
| 7436fa17c0 | |||
| d9f19f1728 | |||
| 81c417ed76 | |||
| 59a3015170 | |||
| b02009bf14 | |||
| caa9985d11 | |||
| 80957bfc00 | |||
| 552ec828ab | |||
| 42273750f4 | |||
| 45d6af92e8 | |||
| c28db81893 | |||
| 1e25ae36f1 | |||
| 9bf40d879f | |||
| 878f556a0b | |||
| 4ad2cd027c | |||
| 132f48eb60 | |||
| 5b33e31504 | |||
| e16268a013 | |||
| 739e637267 | |||
| 7edc959432 | |||
| c444d5a69f | |||
| 830d06a900 | |||
| 0fcd8e5444 | |||
| 020cfe9016 | |||
| 39903f3da6 | |||
| b1d682a909 | |||
| 07d60303db | |||
| 5287d83b82 | |||
| e2085190d1 | |||
| 565b5c3de1 | |||
| fd29452e0c | |||
| 0f93099d04 | |||
| e2d50a6dfa | |||
| c4d20cd522 | |||
| 229e1353f4 | |||
| 07571aaad1 | |||
| 3c2b4ffc48 | |||
| bb0a3bd1cd | |||
| 3bd9ff82d7 | |||
| f0fccaaf51 | |||
| 2041809ea6 | |||
| b90df7468e | |||
| e2e0bd6b47 | |||
| 785ae33e86 | |||
| 20f7dc0ef9 | |||
| 0155154664 | |||
| d264c29557 | |||
| 432149dce2 | |||
| 5b3a52152a | |||
| 301f58bc62 | |||
| 64318de24a | |||
| fb01250142 | |||
| 0beec5392e | |||
| f551ba452f | |||
| f4b22fe874 | |||
| e95444266b | |||
| e361960e51 | |||
| d0a9c05d4b | |||
| f852927116 | |||
| daa88889d0 | |||
| 766e75f2ed | |||
| eb7f2a1419 | |||
| c7c69417ef | |||
| 9f548d299b | |||
| 1dc6c6f8c4 | |||
| 5472e59a84 | |||
| d379f778dc | |||
| ec44c2a6b5 | |||
| 1f77500d61 | |||
| 060db899cf | |||
| 9fc8f52aca | |||
| 9af1bc867a | |||
| e92bb0c7cd | |||
| 1d07d7d88a | |||
| ec7d84b354 | |||
| 0a2c5e028f | |||
| 4379b9cff1 | |||
| 1b7c2041be | |||
| f3632f53e3 | |||
| d76a9e6afd | |||
| e67acc4319 | |||
| 373a360e76 | |||
| f541bee0e7 | |||
| 75d086f680 | |||
| 81ca723486 | |||
| d51bbffb3e | |||
| 07aad4afa0 | |||
| 6495bb2be3 | |||
| 3d8363c99b | |||
| 333c11f81e | |||
| 77fc3e967a | |||
| a447cde05b | |||
| e8ad241653 | |||
| 1f824f3ae6 | |||
| 370d9402f2 | |||
| 4d518d05b9 | |||
| 8c87333fa2 | |||
| 4273d0efc0 | |||
| a37b64be91 | |||
| 905d296bdf | |||
| 9a25e8ce4a | |||
| 68311e1d8b | |||
| de5da5b299 | |||
| dcd63668bd | |||
|
|
1a52399ec9 | ||
|
|
b01f5f59d1 | ||
| c70231d5de | |||
| d3deec72eb | |||
|
|
a7347c9dc0 | ||
|
|
6ad5d373d5 | ||
| e048b75ed7 | |||
| 469681b96c | |||
| 89457b0593 | |||
| 7a6b257714 | |||
| 443b557623 | |||
|
|
84691f45b5 | ||
| de65f78e89 | |||
| 252cd0a69c | |||
| d77aabca92 | |||
| 13514b277b | |||
| eaf90ab9e8 | |||
| f828983969 | |||
| be6eb64b35 | |||
| abc6c088fe | |||
| 6f44489c6e | |||
| a809905c52 | |||
| 7abab7121a | |||
| 8e31968fd3 | |||
| 3d08e86049 | |||
| e0fdf1cc88 | |||
| 4acdd8adb9 | |||
| d6c5d0e95d | |||
|
|
e8e6f8bc17 | ||
|
|
27983fe8ee | ||
|
|
c3d92aacd5 | ||
|
|
b287ec8529 | ||
| 07d3f6e224 | |||
| fc42f361fa | |||
|
|
424f9a126d | ||
| a042f05401 | |||
| f59adbe9bd | |||
| a0ece486d8 | |||
|
|
4caa0a9b0d | ||
|
|
df5bbc3675 | ||
| 92278cf91b | |||
| 6b165fe5bc | |||
| 64b32dc3a3 | |||
| 301a62a563 | |||
| 1f84b4b3e6 | |||
| a0979caba6 | |||
| 18be52e806 | |||
|
|
627dad7aac | ||
|
|
7166db59bd | ||
| 13759d806b | |||
| 0fd1ddcde1 | |||
|
|
a5e743ce84 | ||
| 161bba896c | |||
|
|
da8338be3f | ||
|
|
ea235b0d5c | ||
| 98ea6004b4 | |||
| 62cbc612a7 | |||
| 589fa8501e | |||
|
|
d6285781f2 | ||
|
|
137ef7045b | ||
|
|
5e066995e8 | ||
|
|
c1c50cb28b | ||
| 3f13f64ab9 | |||
| 5abe45ff34 | |||
|
|
0970127f68 | ||
|
|
93bb8d2f31 | ||
|
|
88d8b75298 | ||
| ea395e87ba | |||
| 8d7b4adf48 | |||
| e3ee724e6f | |||
|
|
9d022ae13e | ||
|
|
8b0f9cfd11 | ||
| f36b3818c5 | |||
| 2ccbbf8893 | |||
| 76219aa4fe | |||
| 5ed3d8dfab | |||
| 4dd3ddb1d0 | |||
| fb16840a98 | |||
| 9ad6acf28c | |||
|
|
774a20257d | ||
|
|
3db0a94981 | ||
| 891b26d493 | |||
| 86a991db81 | |||
|
|
fd8e6fd2e8 | ||
| 78239d3ead | |||
| 174d1bf0d0 | |||
| da9f54982c | |||
| b6993b04c2 | |||
|
|
4cca30ec75 | ||
|
|
b6c43e534b | ||
|
|
942b5eb19b | ||
| 22e5a97143 | |||
|
|
31b2bd97b4 | ||
|
|
3eaa0d017c | ||
| be42550d0a | |||
| 6e32289b98 | |||
|
|
a750a91f7b | ||
|
|
2cf3b75e46 | ||
|
|
9586be40ce | ||
|
|
4494ec7dbc | ||
|
|
33af9d0b9d | ||
|
|
7f30b2d05b | ||
|
|
3cd86042ff | ||
| af1fab644a | |||
| ace53e81f2 | |||
|
|
f179368668 | ||
| 765d404845 | |||
|
|
9134933a07 | ||
|
|
290f6a9a35 | ||
| ac2d1e611b | |||
| 1b75e2ab55 | |||
| de9750b24a | |||
| 595effa04c | |||
|
|
09e24ab062 | ||
|
|
391208f8bd | ||
|
|
3cf47eb723 | ||
|
|
c3ce25315c | ||
|
|
ae71564b94 | ||
|
|
d6bd24865e | ||
|
|
c420300c11 | ||
|
|
4c67fce8d1 | ||
| d5cf4e9d3f | |||
| 4b7fd649a3 | |||
|
|
b8d3b0192d | ||
|
|
1afd86d3ce | ||
|
|
daa133b993 | ||
|
|
bf258ee4f9 | ||
| 2bc17ec4d0 | |||
| 090b9ab6ab | |||
|
|
fb5b52bec2 | ||
|
|
df678a12b9 | ||
| 21f85a7322 | |||
| 4e2590fd0f | |||
| 31c5fef5c0 | |||
|
|
94e26ac912 | ||
|
|
711859ce89 | ||
| 8502fffb68 | |||
| 6f0cc3aaf8 | |||
|
|
ce13874431 | ||
|
|
054c9fd604 | ||
| f5501db19e | |||
| 4609af875e | |||
| 1dad0a008a | |||
|
|
b757cdc5dd | ||
|
|
249351bf52 | ||
|
|
38ca810068 | ||
|
|
bab5c9a8e6 | ||
|
|
4e80493a26 | ||
|
|
12dbe90150 | ||
| 6249d53b7b | |||
| b445f1f11e | |||
|
|
f2c268683c | ||
|
|
f69eaa4fe3 | ||
|
|
a9172dcd36 | ||
|
|
442e87267a | ||
|
|
32c8aaec9b | ||
|
|
112294bd7b | ||
|
|
3bfbd8abc1 | ||
|
|
78341850e1 | ||
|
|
becdbf1891 | ||
|
|
f0b1d3f9ce | ||
|
|
73bab2338e | ||
|
|
448e6d2c40 | ||
|
|
131d782b54 | ||
|
|
d6cc8ab9a2 | ||
|
|
d9e25fd0e4 | ||
|
|
276994759e | ||
| a569ee7736 | |||
| d7762cbfed | |||
| 8356326c7d | |||
|
|
8003a799c2 | ||
|
|
6de1ad150c | ||
|
|
b6a7354322 | ||
|
|
6e96f38c90 | ||
|
|
fc24e1fab8 | ||
|
|
718e1c93f6 | ||
|
|
74540610c6 | ||
|
|
1be4701f6d | ||
| 5c66ece467 | |||
|
|
d3b4d15df8 | ||
|
|
8fc79f6699 | ||
| 3b6b0c7e2c | |||
|
|
91ee58db1b | ||
|
|
af1c839495 | ||
|
|
083416951a | ||
|
|
cd20bdd88f | ||
|
|
5a0fb6fd3f | ||
|
|
93fe0781b2 | ||
|
|
94e00459b6 | ||
|
|
9614985690 | ||
|
|
8c597db8a9 | ||
| 9a81fb7ee4 | |||
|
|
a0bd4cfa38 | ||
|
|
82388b0c19 | ||
|
|
588202311c | ||
|
|
46820505ae | ||
|
|
b030e9c09b | ||
|
|
f1adbb4da7 | ||
|
|
4dfee09abb | ||
|
|
16e4f7c5b5 | ||
|
|
e9e40e53bc | ||
| 31bb29f2fd | |||
|
|
a2625fa73f | ||
|
|
ab804d57c1 | ||
|
|
cef2fca099 | ||
|
|
3d783974fc | ||
|
|
7f5a1615b3 | ||
| c7b46229b5 | |||
| 1482bb6ab2 | |||
| eae4087b3e | |||
| faf98607c4 | |||
| 6c625de936 | |||
| aeb372bc17 | |||
|
|
738144ad22 | ||
|
|
a67b487dc0 | ||
|
|
8613d4fc7a | ||
|
|
6aa1a3d167 | ||
|
|
59ffa38ff7 | ||
| c19e9094d1 | |||
| bf92edb267 | |||
| db55c5597f | |||
| 0c79739867 | |||
| e484f22788 | |||
| c72f382804 | |||
| 6a694cee18 | |||
| 1a077edf0c | |||
|
|
68926757dc | ||
|
|
eea57435e5 | ||
| 1f0e8f9cf2 | |||
|
|
6d6768e6cb | ||
|
|
a84fe12a28 | ||
|
|
be019dcdd0 | ||
|
|
82d65c3015 | ||
|
|
815392fffa | ||
|
|
35e95c835e | ||
|
|
1263409d7c | ||
| e5b184ea6d | |||
| aa0778958e | |||
| 50b78e9898 | |||
| 58af5c5570 | |||
| 63cde1f6a0 | |||
| 89e6ee9eff | |||
| 200c0adfba | |||
|
|
306f4f3987 | ||
|
|
1240a57405 | ||
|
|
a9acc83bdf | ||
| a2d259aea1 | |||
|
|
7ff8a9db22 | ||
|
|
7b6904059f | ||
|
|
eec9c6ebf2 | ||
|
|
1e8884a7c3 | ||
| 9d591bb070 | |||
| db4bad22f4 | |||
| 58bd986090 | |||
|
|
9e5bafc75e | ||
|
|
938ad7366f | ||
|
|
56ec903807 | ||
|
|
5913445257 | ||
|
|
2d1d458929 | ||
|
|
5a0c461961 | ||
|
|
74ad20646d | ||
|
|
604c57c208 | ||
|
|
aca21b9d98 | ||
|
|
b83a416dda | ||
|
|
d1858ddf83 | ||
|
|
aa1c48fcfe | ||
|
|
b19bbf91f2 | ||
|
|
d9e4fb3022 | ||
|
|
cc8f2e0778 | ||
|
|
9b1881349f | ||
|
|
eacceb4b76 | ||
|
|
c50c193a46 | ||
|
|
dfe99ef080 | ||
|
|
98e1f37c1d | ||
|
|
05295b1a0d | ||
|
|
6fb04a66df | ||
|
|
ba14b67c24 | ||
|
|
0a4d6ff13b | ||
|
|
fc2795b83e | ||
|
|
4c726ff747 | ||
|
|
4ed93c7e48 | ||
|
|
acb37bc255 | ||
|
|
9b5040d449 | ||
|
|
fe0d1f39b0 | ||
| 79d2720733 | |||
| 218d828e0d | |||
| 2269662290 | |||
| eaec1e4177 | |||
| eaff87882e | |||
| 0ba28588da | |||
| 25f64831f3 | |||
| d6c869727a | |||
| 77a06e0edd | |||
| 21efcf3ea6 | |||
| d77e1d74b0 | |||
| 1ce6c74f2c | |||
| 3c4b645772 | |||
| 909978467c | |||
| 912c449161 | |||
| 7f05bb2f7d | |||
| cbc760ebaf | |||
|
|
48bb0cbff5 | ||
|
|
1403b7851a | ||
|
|
c36081996d | ||
|
|
14dbc35eda | ||
|
|
64c03768ff | ||
|
|
b806bef0bf | ||
|
|
811d22e229 | ||
|
|
1ee612674e | ||
|
|
cf9b621159 | ||
|
|
03ae184541 | ||
|
|
6b62d26544 | ||
|
|
3883864558 | ||
|
|
1089f37022 | ||
| c661767de3 | |||
| 7b75e6ac69 | |||
| bd1f1deb53 | |||
| e142d50d18 | |||
| 6250e763c3 | |||
|
|
4cda43f3b5 | ||
|
|
fd8d71e2a2 | ||
|
|
87b702c24b | ||
|
|
27cd98f0db | ||
|
|
d1603fbc16 | ||
|
|
96ba29a9a2 | ||
|
|
5d9ca09272 | ||
|
|
64676736b5 | ||
|
|
7106482831 | ||
|
|
b1c8606432 | ||
|
|
0eb02c598e | ||
|
|
b6367d72ba | ||
| 2e76bded8c | |||
| 0e7c07b3b5 | |||
|
|
3e3b1eb868 | ||
|
|
ec0309d6f2 | ||
|
|
8ee7a030bd | ||
|
|
ca9778a416 | ||
| 07504d8412 | |||
| 88796f7f28 | |||
|
|
8764e750ff | ||
|
|
9d2cb29255 | ||
| a1b8f9e000 | |||
| 6f2c670fce | |||
| d21cb4adfc | |||
|
|
97b922c1be | ||
|
|
0af5b2121c | ||
| 36be8b5420 | |||
| a34d775f63 | |||
| 1bee672592 | |||
|
|
cf08f9126e | ||
| 8e938e9f06 | |||
|
|
a08744f71a | ||
|
|
913c4c1ece | ||
|
|
63ee41e3b8 | ||
|
|
08072001a7 | ||
| 6b4d82a67d | |||
|
|
03f3162dc1 | ||
|
|
0eb839e7c7 | ||
|
|
807d3e1c46 | ||
|
|
e6f990b766 | ||
|
|
6dcc492127 | ||
|
|
a2bd2fa6c6 | ||
| 5612b5b1d4 | |||
|
|
2edf7d211b | ||
|
|
890265611a | ||
|
|
48c7182f98 | ||
|
|
2cfd342ef8 | ||
|
|
63513df053 | ||
|
|
bb00d9a714 | ||
|
|
3398992bcd | ||
| 2912463562 | |||
| 3df4433381 | |||
| 1d46a3b6d4 | |||
|
|
a8f868e215 | ||
|
|
78a5bebb4f | ||
|
|
33582922be | ||
|
|
e6234213f0 | ||
|
|
1d9a89d7c7 | ||
|
|
b0560b840f | ||
| 20b15c0b78 | |||
| a4365abd4d | |||
| 1cb02a62e8 | |||
| 67225c1a96 | |||
| 50ecc3a177 | |||
| 63c9ff36a1 | |||
|
|
dc62ca956c | ||
|
|
d45c555415 | ||
| a657464d22 | |||
| b5b67e0c75 | |||
| 619d0e817f | |||
|
|
5d7beca57d | ||
|
|
9a933e74ff | ||
| 0b60dd799f | |||
| a38c15005e | |||
| b5a8c635d6 | |||
| 6b288a3b3c | |||
| 9a5718cc35 | |||
| cc804bac7d | |||
| b18a5a0050 | |||
| 078a0c0dfb | |||
|
|
d080fc04bb | ||
|
|
ae484c9e6c | ||
|
|
39cb20618b | ||
|
|
5b95401204 | ||
| 6b62cf7299 | |||
|
|
af352bbfe8 | ||
|
|
4d7673e8df | ||
|
|
55ec6da74b | ||
|
|
83d9a5befb | ||
|
|
ec836a6470 | ||
|
|
d6f9aa155e | ||
|
|
d3b507ed45 | ||
|
|
b027396567 | ||
|
|
7d0a6ae2bf | ||
|
|
d12bf41021 | ||
|
|
ca3a584100 | ||
|
|
47d0856296 | ||
|
|
a828d75248 | ||
| edc769ca79 | |||
| 0a22f01587 | |||
| 45f0d571d6 | |||
|
|
880adf7c69 | ||
|
|
591b4bf119 | ||
| 0500557dca | |||
| 654a3829c6 | |||
|
|
2db65776b1 | ||
|
|
7742bd56f8 | ||
|
|
3159208a0a | ||
| fccafba438 | |||
| 8cd4cfb36d | |||
|
|
971b2034ee | ||
|
|
f17d171231 | ||
|
|
4be008382f | ||
|
|
1ae1703f53 | ||
|
|
340227d9a6 | ||
|
|
fb99e83b2d | ||
|
|
8fa76c6732 | ||
|
|
1637db2fe3 | ||
|
|
7e55b1f85e | ||
|
|
575d1492ad | ||
|
|
a043db2de5 | ||
|
|
d3ee0e5f4f | ||
|
|
484a8d1d93 | ||
|
|
7839ac3322 | ||
|
|
e16f494cf4 | ||
|
|
6fc22784b8 | ||
| 52bafa95e3 | |||
| 377c86e390 | |||
| 79abe93744 | |||
| f580955c97 | |||
| 6a625ed4ea | |||
| 8a47d9f3da | |||
| 148b6492ea | |||
| be3fa7a704 | |||
| 63af2daf28 | |||
| 37be3a1759 | |||
| df2547ff81 | |||
| 7693de93dc | |||
| c061045466 | |||
| abd68f2dc8 | |||
| 285f956caa | |||
| 019cf4adf4 | |||
| cf38e7253f | |||
| d809362ea7 | |||
| c08987a781 | |||
| e60c3d7aa3 | |||
| 614b8d0948 | |||
| 8834b906f4 | |||
| 828a022768 | |||
| 26c8d08d04 | |||
| 68a9b2281a | |||
| 63df493d33 | |||
| b7110c80b5 | |||
| c4dbd10a85 | |||
| 041a407978 | |||
| f73c997c38 | |||
| db2a955dea | |||
| a91643d2ec | |||
| 269cf7ea03 | |||
| e3aa964db8 | |||
| fa2fa66d4a | |||
| 6d2a189fef | |||
| 2033f61c7e | |||
| 4e2222966f | |||
| fbe9dc87e9 | |||
| d6f078ab60 | |||
| a940863799 | |||
| 11ffa66851 | |||
| fc3fa22bee | |||
| 37ff60fcfb | |||
| d2d5eebe12 | |||
| 9cfb5aa344 | |||
| 5e7f898840 | |||
|
|
6d3253aed3 | ||
|
|
52e5f246ce | ||
|
|
6222377bc5 | ||
|
|
2d10f57643 | ||
|
|
5f27a95498 | ||
|
|
7fdf832f44 | ||
| 43d2f83677 | |||
| e882a15aa0 | |||
| 1a3f16119b | |||
| 8e2e515af3 | |||
| f1a2495431 | |||
| c66b794daa | |||
| b9f7ea2722 | |||
| 455e17d4b1 | |||
| a1a6a78dac | |||
| 07f91aef74 | |||
|
|
079ab4d98f | ||
|
|
6e53bc3ca5 | ||
|
|
5e8eb716b1 | ||
|
|
34ee8fa11d | ||
|
|
b7dc95af26 | ||
|
|
297391d57e | ||
|
|
aff870fba7 | ||
| 7b1ac47228 | |||
| 7eb9abbdb5 | |||
| 521bd5071a | |||
| ab7e1705c4 | |||
|
|
a2390a6ab2 | ||
|
|
8dc5dcb1bd | ||
|
|
5d010d2f77 | ||
|
|
cec5544472 | ||
|
|
5f79c3276a | ||
| aadb717c9f | |||
| e2e9201fdd | |||
|
|
9053bdf10c | ||
|
|
b14297d1af | ||
|
|
e90979bbdc | ||
|
|
e02c278e7a | ||
|
|
e60d9483a3 | ||
|
|
ba49312fd5 | ||
| ba3c98771f | |||
| 4c738cc61d | |||
| d648a79ec4 | |||
| c325927418 | |||
| dba5625446 | |||
| 91189968e9 | |||
| 433d8a460e | |||
| ec9857fe73 | |||
| a9b01566e1 | |||
|
|
a384aba4df | ||
| e587ad3ca1 | |||
|
|
6a7c8b3d28 | ||
|
|
6f52c956ec | ||
|
|
9e5d2f47e2 | ||
|
|
627f264ef3 | ||
|
|
464de4ee5a | ||
|
|
cf76235123 | ||
| 8d9f51f50f | |||
| d840185ec7 | |||
| 9f4ab20b9d | |||
| 0877ff8f66 | |||
| 78046e8707 | |||
|
|
9fb8bc989d | ||
|
|
3c89670dae | ||
| 21b73d55f7 | |||
| 9199cdf7e2 | |||
|
|
8589216a77 | ||
|
|
87768c6b53 | ||
|
|
ca320f4334 | ||
|
|
7697ab59fa | ||
| e5e6f360dc | |||
| 68a9462714 | |||
| b185a94fcc | |||
| 708280ca14 | |||
| 0ab1e7eef7 | |||
| c7b1d46b18 | |||
| 9357a62f0c | |||
| c5b25e9a16 | |||
| a35d057146 | |||
| 7fc495eec9 | |||
| 46f37942d2 | |||
| e75196c904 | |||
| bdfa249c2e | |||
| d0e5f7da61 | |||
| d8320ba83d | |||
| 23460800d6 | |||
| 58c8b644bc | |||
| c4c04aecb6 | |||
| bb7c98c094 | |||
| dcdb0d06c4 | |||
| b6a66bed41 | |||
|
|
5f277ed815 | ||
|
|
845a553097 | ||
| 74f89c8b2d | |||
| cff8227228 | |||
| 51e2c9af02 | |||
| cd89a77189 | |||
| ab303cfeef | |||
|
|
07004a7415 | ||
|
|
d2069a3e7f | ||
|
|
501cee0057 | ||
| ac2454fd0e | |||
| 931a4cf807 | |||
| cb7099264e | |||
| e7aa951e89 | |||
| ef70598180 | |||
| 698fca8787 | |||
| 2f86090f21 | |||
| 2988a3b34e | |||
|
|
4fe5f65867 | ||
|
|
4bb1953d4a | ||
|
|
57a8260f03 | ||
| e4936a23bc | |||
| 9f37cb3f8d | |||
| b128b2436d | |||
| 66158e94dd | |||
| f6b489d950 | |||
| 30312caf82 | |||
| 1d0dd65f5e | |||
|
|
0b245f62af | ||
| 50d90af3a5 | |||
|
|
49b8585522 | ||
|
|
b49f098a5e | ||
| e9a15b950e | |||
| ee676614f8 | |||
| 710abf2323 | |||
| 1a9f06d259 | |||
|
|
eaaa99a946 | ||
|
|
c8e4d624b7 | ||
| f08e36d100 | |||
| de73536d1c | |||
| 6a861305d6 | |||
|
|
367fa130c0 | ||
|
|
d04987a3e2 | ||
| b0fa185d36 | |||
| 094718e67f | |||
| e4a8bf80e9 | |||
|
|
c04b102a81 | ||
|
|
da9b3a04b4 | ||
|
|
234a030801 | ||
|
|
5ea8e851d7 | ||
|
|
afeeef4af7 | ||
| ee1e2f8556 | |||
| 1157b41730 | |||
| ca4d75c63f | |||
| f45bd7acc4 | |||
| 7bdd62d4a9 | |||
| b01ee9129b | |||
|
|
214c93923c | ||
|
|
bfa1d67b5c | ||
| a0a1a1a06a | |||
| 468ad385d7 | |||
| fa94667c0f | |||
| c997fbf1cb | |||
| 7283ace072 | |||
|
|
69743d4ef0 | ||
|
|
e033671ffb | ||
|
|
15cde37af7 | ||
|
|
7fb74bc7d8 | ||
| 43968995ea | |||
| 0574180e4b | |||
| 5b653272ee | |||
| d28a6051f1 | |||
| f9063ad26e | |||
| 2f4d060ca3 | |||
| fcd0cf4836 | |||
| 26f50c2acb | |||
| c3eb1fc78c | |||
| b15cc542e1 | |||
|
|
ec094d7471 | ||
|
|
3dc432131c | ||
|
|
1a19604163 | ||
| fdebb2b215 | |||
| 7d8f047087 | |||
| bf8af41f3f | |||
|
|
cdf29d2b0d | ||
|
|
7194049127 | ||
|
|
e34cec812f | ||
|
|
11a5b53d2a | ||
| efe22de0a0 | |||
|
|
82f7571612 | ||
|
|
2ac54a50ec | ||
| 5dd1e10b61 | |||
| e9e8e87719 | |||
| 3c0fa205d1 | |||
| 47ca7bde41 | |||
|
|
9e28d579d1 | ||
|
|
1c96522447 | ||
|
|
1a568621ca | ||
| eecefee674 | |||
| ea0332851d | |||
| a25a7dc023 | |||
|
|
b4fe1ca199 | ||
|
|
b315ae5644 | ||
|
|
f7113601f3 | ||
|
|
8d6e3e9644 | ||
|
|
23b864d378 | ||
|
|
be1119be6f | ||
|
|
b836e7fd67 | ||
| a237b4041d | |||
| ea4cd29723 | |||
| 13397fa1f7 | |||
| 2fab472150 | |||
| 8c71b4845a | |||
| b70f909a32 | |||
| 1b15aed6a2 | |||
| 5019fbd3fc | |||
| 2ea19dcf03 | |||
| 4d756d5624 | |||
| f6f759110f | |||
| 8d27b5b51e | |||
|
|
9f064609e7 | ||
|
|
fd10d4dbc4 | ||
|
|
fadb5faf0d | ||
| e5e514b522 | |||
| 74e6d5a1da | |||
| 3cf0570912 | |||
|
|
8da51a0a82 | ||
|
|
37ff7fbb91 | ||
|
|
8c20b51e13 | ||
| 391a28d659 | |||
| 4535fb7dfb | |||
| 546d1b4c44 | |||
| 5fe605c130 | |||
| 4408c0ecc6 | |||
| 49f08d1191 | |||
| 903bafb245 | |||
| 59b02b3f28 | |||
| 4a23e33080 | |||
| 1e1b06fc48 | |||
| 7c56d2bf4e | |||
| 4add0a806c | |||
| c9bb7fe502 | |||
| a8a7a129c9 | |||
| c3c5532cb5 | |||
| 78bfc16287 | |||
|
|
6c6162df74 | ||
|
|
a837aff0fb | ||
|
|
f673afc7fc | ||
|
|
0ea96c728c | ||
|
|
77e71e3296 | ||
|
|
ce870b9acf | ||
|
|
7f889b54e0 | ||
|
|
ebd596d0d2 | ||
| 04ce95caa4 | |||
| 29bf172187 | |||
|
|
41619febb9 | ||
|
|
0f4b769d49 | ||
| 66cd8217b9 | |||
| e684c4e547 | |||
| c439f41d69 | |||
| 823e96b014 | |||
| 0cca24ee30 | |||
| 66cccd0867 | |||
| 463ddf3cb2 | |||
|
|
f0e0987f31 | ||
|
|
16bea58ab5 | ||
|
|
f2bbbaaeb6 | ||
|
|
5098f6f4a1 | ||
|
|
bc1815ae1b | ||
| 6256c90958 | |||
| f0bfe249e0 | |||
| 7bbfd7c506 | |||
| 3c7cbe28a1 | |||
| c834aacf3a | |||
| 90b0ef41e3 | |||
|
|
2aeeec4468 | ||
| a7c5723e10 | |||
|
|
ba595b9d2c | ||
|
|
d9118aaf93 | ||
| 1bdc71998b | |||
| a597f7a67a | |||
| 195964ef5a | |||
|
|
c27ca77717 | ||
|
|
7d0d460e5c | ||
| 5ac9df3056 | |||
| b8d64f1f28 | |||
| c8dd9664be | |||
| 8899a3a6bb | |||
|
|
67f56f9b84 | ||
|
|
fd12b9e571 | ||
| fb329c94aa | |||
| 16d06582ee | |||
|
|
6f98f111dd | ||
|
|
51a1c1d8e1 | ||
|
|
8445c23984 | ||
| 961f8f874e | |||
| 283b6244b1 | |||
| f19a15dbe8 | |||
| 791d44df02 | |||
|
|
28e67db78d | ||
|
|
63a120c652 | ||
|
|
f60cabbbc0 | ||
| 06db759749 | |||
|
|
373608d989 | ||
|
|
45e3dde03f | ||
|
|
3625180a6e | ||
| aae1442a6f | |||
| 08dcd03468 | |||
|
|
deda511e32 | ||
|
|
9bcf615b4b | ||
|
|
70912c4c43 | ||
|
|
fa013c1974 | ||
| 11fbeb1ed0 | |||
| f432b8cce6 | |||
|
|
05ddd33bcd | ||
|
|
9f5c901502 | ||
| 33d5d3a2ea | |||
| 2668dd3c47 | |||
| 2bc5fef175 | |||
| ea92e1f20d | |||
| 9f1cf1d90e | |||
| b43f9baead | |||
|
|
41ffe335fc | ||
|
|
56d0dcd25f | ||
|
|
4304acbe6f | ||
| 2f1b22d423 | |||
| 3eb8626831 | |||
| 5a86f2e649 | |||
| 93e9c61943 | |||
|
|
c5e9941adb | ||
| 56679808e9 | |||
|
|
40f5705e7a | ||
|
|
32975d9900 | ||
|
|
72db49e7f5 | ||
|
|
472f5e65b4 | ||
| e2f8fb082c | |||
| a47e53b19e | |||
| c01acec9e6 | |||
| 969d7586fc | |||
| 31a40ff7ca | |||
| afb9c99228 | |||
| 8ee6a529a0 | |||
| cebc05d132 | |||
| d23ac82b1b | |||
| e86b576050 | |||
| 8a774aa328 | |||
| c5da68dc47 | |||
| 28df672a7d | |||
| 203c88dd70 | |||
| fa86a2af45 | |||
| ac28ba233c | |||
|
|
47dc2bfc6e | ||
| 085dac0630 | |||
| 7b27b1362d | |||
| 704e3c25bf | |||
| 7e5b10aab3 | |||
| 3ccbd8f905 | |||
| 89a234e77d | |||
| 388dd54bfa | |||
| 035c4412f1 | |||
| d4eb7c846b | |||
| fef50d7a37 | |||
| e5a95972a5 | |||
|
|
36a796b38b | ||
| 94927ae0a4 | |||
| 4d7153661c | |||
| 76062136d6 | |||
| ea4ba74989 | |||
|
|
5a09d7a195 | ||
|
|
e0332b9ddf | ||
|
|
685ba09f7d | ||
|
|
7aa1fc0342 | ||
|
|
653264151a | ||
|
|
fada00c738 | ||
| a10d30337a | |||
| 85ffed114b | |||
| 1093ac68b6 | |||
| 71e6abd816 | |||
| 00c948116e | |||
| b6efd598bd | |||
| bfe7ad4aa6 | |||
| 018ccef46a | |||
| feb836f849 | |||
| 0106f21b86 | |||
| 1b5f64785d | |||
| 77a4aee88e | |||
|
|
ce1e2f3eab | ||
|
|
75fbd2844b | ||
|
|
832f331603 | ||
| 9a2d58d922 | |||
|
|
34c4cd47ef | ||
| c46f65600f | |||
| aa1eb7411e |
176
.gitea/workflows/develop_build_manual.yaml
Normal file
176
.gitea/workflows/develop_build_manual.yaml
Normal file
@@ -0,0 +1,176 @@
|
||||
name: 手动 AiDA back-java 开发分支构建部署
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build_and_deploy:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
build_status: ${{ job.status }}
|
||||
build_url: ${{ gitea.server_url }}/${{ gitea.repository.owner.name }}/${{ gitea.repository.name }}/actions/runs/${{ gitea.run_id }}
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
env:
|
||||
REMOTE_DEPLOY_PATH: /workspace/workspace_aida/DevelopVersion/develop-version-aida-back
|
||||
|
||||
steps:
|
||||
- name: 0.记录开始时间
|
||||
id: build_start_time
|
||||
run: echo "current_time=$(TZ='Asia/Hong_Kong' date '+%Y-%m-%d %H:%M:%S %Z')" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: 1.检出代码
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: dev/3.1_release_merge
|
||||
|
||||
- name: 2.Set up JDK 21
|
||||
uses: actions/setup-java@v5
|
||||
with:
|
||||
java-version: '21'
|
||||
distribution: 'temurin'
|
||||
|
||||
- name: 3.设置JAVA Maven 环境
|
||||
run: |
|
||||
# 适配root/普通用户
|
||||
SUDO=""
|
||||
[ "$(id -u)" != "0" ] && SUDO="sudo"
|
||||
|
||||
# 安装依赖
|
||||
$SUDO apt update && $SUDO apt install -y wget tar --no-install-recommends
|
||||
|
||||
# 下载Maven
|
||||
MAVEN_VERSION="3.9.11"
|
||||
MAVEN_TAR="apache-maven-${MAVEN_VERSION}-bin.tar.gz"
|
||||
MAVEN_URL="https://archive.apache.org/dist/maven/maven-3/${MAVEN_VERSION}/binaries/${MAVEN_TAR}"
|
||||
wget --no-verbose -O /tmp/${MAVEN_TAR} ${MAVEN_URL}
|
||||
|
||||
# 解压+软链接
|
||||
$SUDO tar -xzf /tmp/${MAVEN_TAR} -C /usr/local/
|
||||
$SUDO ln -sf /usr/local/apache-maven-${MAVEN_VERSION} /usr/local/maven
|
||||
|
||||
# 配置PATH
|
||||
echo "/usr/local/maven/bin" >> $GITHUB_PATH
|
||||
export PATH="/usr/local/maven/bin:$PATH"
|
||||
|
||||
# 验证
|
||||
mvn -v
|
||||
|
||||
- name: 4.构建jar包
|
||||
run: |
|
||||
echo "===== 开始构建JAR包 ====="
|
||||
# 新增:打印当前构建分支(两种方式双重确认)
|
||||
echo "当前工作目录分支:$(git branch --show-current)"
|
||||
echo "Gitea检出分支:${{ github.ref_name }}"
|
||||
echo "预期构建分支:dev/3.1_release_merge"
|
||||
echo "========================"
|
||||
mvn -B clean install -DskipTests -Pdev 2>&1
|
||||
# 检查构建是否成功
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "JAR包构建失败!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: 5.生成Dockerfile
|
||||
run: |
|
||||
echo "===== 生成Dockerfile ====="
|
||||
cat > Dockerfile << 'EOF'
|
||||
FROM openjdk:21-ea-21-jdk-slim
|
||||
VOLUME /tmp
|
||||
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
|
||||
RUN echo 'Asia/Shanghai' > /etc/timezone
|
||||
ADD ./aida-0.0.1-SNAPSHOT.jar /app.jar
|
||||
ENTRYPOINT ["java","-jar","/app.jar"]
|
||||
EOF
|
||||
echo "Dockerfile内容:"
|
||||
cat Dockerfile
|
||||
|
||||
- name: 6.生成docker-compose.yml
|
||||
run: |
|
||||
echo "===== 生成docker-compose.yml ====="
|
||||
cat > docker-compose.yml << 'EOF'
|
||||
version: '3'
|
||||
services:
|
||||
aida_back:
|
||||
container_name: develop-version-aida-back
|
||||
build: .
|
||||
volumes:
|
||||
# 数据挂载
|
||||
- ./log:/log
|
||||
- ./temp:/temp
|
||||
- ./uploads:/temp/uploads
|
||||
ports:
|
||||
- '10090:5567'
|
||||
restart: always
|
||||
EOF
|
||||
# 验证docker-compose.yml生成
|
||||
echo "docker-compose.yml内容:"
|
||||
cat docker-compose.yml
|
||||
|
||||
- name: 7.安装SSH工具
|
||||
run: |
|
||||
$SUDO apt install -y sshpass openssh-client --no-install-recommends
|
||||
# 配置SSH免密
|
||||
mkdir -p ~/.ssh
|
||||
echo "${{ secrets.SSH_KEY }}" > ~/.ssh/id_rsa
|
||||
chmod 600 ~/.ssh/id_rsa
|
||||
ssh-keyscan -H ${{ secrets.SERVER_HOST }} >> ~/.ssh/known_hosts
|
||||
|
||||
- name: 8.同步文件到远程服务器
|
||||
run: |
|
||||
echo "===== 同步文件到远程服务器 ====="
|
||||
# 使用scp同步文件
|
||||
scp -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
|
||||
./target/*.jar ./Dockerfile ./docker-compose.yml \
|
||||
${{ secrets.SERVER_USER }}@${{ secrets.SERVER_HOST }}:${{ env.REMOTE_DEPLOY_PATH }} 2>&1
|
||||
|
||||
- name: 9.部署和运行服务
|
||||
run: |
|
||||
echo "===== 开始部署服务 ====="
|
||||
# SSH执行部署命令
|
||||
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
|
||||
${{ secrets.SERVER_USER }}@${{ secrets.SERVER_HOST }} << 'EOF_SSH'
|
||||
cd ${{ env.REMOTE_DEPLOY_PATH }}
|
||||
echo "停止旧容器..."
|
||||
docker compose down || true
|
||||
echo "清理Docker资源..."
|
||||
docker system prune -f
|
||||
echo "构建镜像..."
|
||||
docker compose build --no-cache
|
||||
echo "启动服务..."
|
||||
docker compose up -d
|
||||
echo "验证容器状态..."
|
||||
docker compose ps
|
||||
echo "部署完成!"
|
||||
EOF_SSH
|
||||
|
||||
- name: 10.发送构建结果邮件
|
||||
if: always() # 无论上一步是否失败,都执行此步骤
|
||||
uses: dawidd6/action-send-mail@v3
|
||||
with:
|
||||
|
||||
from: ${{ secrets.MAIL_USERNAME }}
|
||||
# --- 邮件配置 ---
|
||||
server_address: smtp.gmail.com # 替换为你的SMTP服务器地址
|
||||
server_port: 465 # 替换为你的SMTP端口 (通常是465或587)
|
||||
username: ${{ secrets.MAIL_USERNAME }} # 存储在Secrets中的邮箱用户名
|
||||
password: ${{ secrets.MAIL_PASSWORD }} # 存储在Secrets中的邮箱密码
|
||||
subject: 'Gitea Actions 构建通知: ${{ job.status }} - AiDA back-java Develop'
|
||||
# 收件人列表,可以根据需要更改
|
||||
to: 'cgzhou@aidlab.hk,zchengrong@yeah.net' # 替换为实际收件人邮箱
|
||||
|
||||
# --- 邮件正文内容 ---
|
||||
body: |
|
||||
项目: AiDA back-java Develop
|
||||
分支: dev/3.1_release_merge
|
||||
|
||||
🎉 构建结果: ${{ job.status }}
|
||||
|
||||
📅 构建时间: ${{ steps.build_start_time.outputs.current_time }}
|
||||
|
||||
🔗 构建链接: ${{ gitea.server_url }}/${{ gitea.repository.owner.name }}/${{ gitea.repository.name }}/actions/runs/${{ gitea.run_id }}
|
||||
|
||||
# 确保邮件内容为纯文本,或者你可以设置为 html: true 并调整 body
|
||||
content_type: text/plain
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -48,3 +48,4 @@ log
|
||||
temp/
|
||||
|
||||
docker-compose.yml
|
||||
/src/main/resources/application-local.properties
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
FROM openjdk:8
|
||||
VOLUME /tmp
|
||||
#时区设置
|
||||
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
|
||||
RUN echo 'Asia/Shanghai' > /etc/timezone
|
||||
ADD ./target/aida-0.0.1-SNAPSHOT.jar /app.jar
|
||||
ENTRYPOINT ["java","-jar","/app.jar"]
|
||||
8
aida.iml
8
aida.iml
@@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module version="4">
|
||||
<component name="FacetManager">
|
||||
<facet type="Spring" name="Spring">
|
||||
<configuration />
|
||||
</facet>
|
||||
</component>
|
||||
</module>
|
||||
804
pom.xml
804
pom.xml
@@ -1,324 +1,480 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.2.2.RELEASE</version>
|
||||
<relativePath/> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<groupId>com.aida</groupId>
|
||||
<artifactId>aida</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<name>aida</name>
|
||||
<description>ai da</description>
|
||||
<properties>
|
||||
<java.version>8</java.version>
|
||||
<mybatis.plus.version>3.5.2</mybatis.plus.version>
|
||||
<hutool.version>5.8.2</hutool.version>
|
||||
<wx.java.version>4.2.7.B</wx.java.version>
|
||||
<fastjson.version>2.0.6.graal</fastjson.version>
|
||||
<security.jwt.version>1.1.1.RELEASE</security.jwt.version>
|
||||
<jjwt.version>0.9.1</jjwt.version>
|
||||
<guava.version>31.1-jre</guava.version>
|
||||
|
||||
<jaxb-api>2.4.0-b180830.0359</jaxb-api>
|
||||
<jaxb-impl>4.0.0</jaxb-impl>
|
||||
<jaxb-core>4.0.0</jaxb-core>
|
||||
<activation>1.1.1</activation>
|
||||
<easy-captcha>1.6.2</easy-captcha>
|
||||
<aws.java.sdk.version>2.20.43</aws.java.sdk.version>
|
||||
</properties>
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>software.amazon.awssdk</groupId>
|
||||
<artifactId>bom</artifactId>
|
||||
<version>${aws.java.sdk.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-jwt</artifactId>
|
||||
<version>${security.jwt.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-boot-starter</artifactId>
|
||||
<version>${mybatis.plus.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-logging</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
<version>${hutool.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>fastjson</artifactId>
|
||||
<version>${fastjson.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt</artifactId>
|
||||
<version>${jjwt.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
<artifactId>okhttp</artifactId>
|
||||
<version>3.14.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.xml.bind</groupId>
|
||||
<artifactId>jaxb-api</artifactId>
|
||||
<version>${jaxb-api}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.sun.xml.bind</groupId>
|
||||
<artifactId>jaxb-impl</artifactId>
|
||||
<version>${jaxb-impl}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.sun.xml.bind</groupId>
|
||||
<artifactId>jaxb-core</artifactId>
|
||||
<version>${jaxb-core}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.activation</groupId>
|
||||
<artifactId>activation</artifactId>
|
||||
<version>${activation}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.whvcse</groupId>
|
||||
<artifactId>easy-captcha</artifactId>
|
||||
<version>${easy-captcha}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
<version>${guava.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.github.xiaoymin</groupId>
|
||||
<artifactId>knife4j-spring-boot-starter</artifactId>
|
||||
<version>3.0.3</version>
|
||||
</dependency>
|
||||
<!-- /**发送邮件**/-->
|
||||
<dependency>
|
||||
<groupId>com.tencentcloudapi</groupId>
|
||||
<artifactId>tencentcloud-sdk-java-ses</artifactId>
|
||||
<version>3.1.572</version>
|
||||
</dependency>
|
||||
|
||||
<!--minio-->
|
||||
<dependency>
|
||||
<groupId>io.minio</groupId>
|
||||
<artifactId>minio</artifactId>
|
||||
<version>8.0.3</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>commons-fileupload</groupId>
|
||||
<artifactId>commons-fileupload</artifactId>
|
||||
<version>1.4</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.microsoft.sqlserver</groupId>
|
||||
<artifactId>mssql-jdbc</artifactId>
|
||||
<version>9.2.1.jre8</version>
|
||||
</dependency>
|
||||
|
||||
<!-- RabbitMQ -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-amqp</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- redis 连接池 -->
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-pool2</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!--支付宝 SDK-->
|
||||
<dependency>
|
||||
<groupId>com.alipay.sdk</groupId>
|
||||
<artifactId>alipay-sdk-java</artifactId>
|
||||
<version>4.22.57.ALL</version>
|
||||
</dependency>
|
||||
|
||||
<!--PayPal SDK-->
|
||||
<dependency>
|
||||
<groupId>com.paypal.sdk</groupId>
|
||||
<artifactId>checkout-sdk</artifactId>
|
||||
<version>1.0.5</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.paypal.sdk</groupId>
|
||||
<artifactId>rest-api-sdk</artifactId>
|
||||
<version>LATEST</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.json</groupId>
|
||||
<artifactId>json</artifactId>
|
||||
<version>20230618</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-api</artifactId>
|
||||
<version>2.17.1</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.poi</groupId>
|
||||
<artifactId>poi</artifactId>
|
||||
<version>5.2.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.poi</groupId>
|
||||
<artifactId>poi-ooxml</artifactId>
|
||||
<version>5.2.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
<version>2.11.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-api</artifactId>
|
||||
<version>2.17.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.stripe</groupId>
|
||||
<artifactId>stripe-java</artifactId>
|
||||
<version>26.2.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- aws s3 -->
|
||||
<!-- S3 dependency -->
|
||||
<dependency>
|
||||
<groupId>software.amazon.awssdk</groupId>
|
||||
<artifactId>s3</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>software.amazon.awssdk</groupId>
|
||||
<artifactId>s3-transfer-manager</artifactId>
|
||||
<version>2.17.103-PREVIEW</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>software.amazon.awssdk</groupId>
|
||||
<artifactId>kms</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>software.amazon.awssdk</groupId>
|
||||
<artifactId>s3control</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>2.10.1</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<excludes>
|
||||
<exclude>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<!-- 本地开发环境 -->
|
||||
<id>dev</id>
|
||||
<properties>
|
||||
<profiles.active>dev</profiles.active>
|
||||
</properties>
|
||||
<activation>
|
||||
<activeByDefault>true</activeByDefault>
|
||||
</activation>
|
||||
</profile>
|
||||
<profile>
|
||||
<!-- 测试环境 -->
|
||||
<id>test</id>
|
||||
<properties>
|
||||
<profiles.active>test</profiles.active>
|
||||
</properties>
|
||||
</profile>
|
||||
<profile>
|
||||
<!-- 生产环境 -->
|
||||
<id>prod</id>
|
||||
<properties>
|
||||
<profiles.active>prod</profiles.active>
|
||||
</properties>
|
||||
</profile>
|
||||
</profiles>
|
||||
</project>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>3.1.6</version>
|
||||
<relativePath/> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<groupId>com.aida</groupId>
|
||||
<artifactId>aida</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<name>aida</name>
|
||||
<description>ai da</description>
|
||||
<properties>
|
||||
<java.version>21</java.version>
|
||||
<mybatis.plus.version>3.5.5</mybatis.plus.version>
|
||||
<hutool.version>5.8.23</hutool.version>
|
||||
<wx.java.version>4.2.7.B</wx.java.version>
|
||||
<fastjson.version>2.0.43</fastjson.version>
|
||||
<jjwt.version>0.12.3</jjwt.version>
|
||||
<guava.version>32.1.3-jre</guava.version>
|
||||
|
||||
<easy-captcha>1.6.2</easy-captcha>
|
||||
<aws.java.sdk.version>2.20.43</aws.java.sdk.version>
|
||||
|
||||
<javacv.version>1.5.5</javacv.version>
|
||||
<system.windowsx64>windows-x86_64</system.windowsx64>
|
||||
<javacpp.platform.linux-x86_64>linux-x86_64</javacpp.platform.linux-x86_64>
|
||||
</properties>
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>software.amazon.awssdk</groupId>
|
||||
<artifactId>bom</artifactId>
|
||||
<version>${aws.java.sdk.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.mysql</groupId>
|
||||
<artifactId>mysql-connector-j</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-boot-starter</artifactId>
|
||||
<version>${mybatis.plus.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-logging</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
<version>${hutool.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>fastjson</artifactId>
|
||||
<version>${fastjson.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-api</artifactId>
|
||||
<version>${jjwt.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-impl</artifactId>
|
||||
<version>${jjwt.version}</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-jackson</artifactId>
|
||||
<version>${jjwt.version}</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
<artifactId>okhttp</artifactId>
|
||||
<version>3.14.2</version>
|
||||
</dependency>
|
||||
<!-- JAXB is included in Java 11+ but needs explicit dependency for Java 9-10 -->
|
||||
<!-- For Java 21, these are not needed as JAXB is part of JDK -->
|
||||
<dependency>
|
||||
<groupId>com.github.whvcse</groupId>
|
||||
<artifactId>easy-captcha</artifactId>
|
||||
<version>${easy-captcha}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
<version>${guava.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.github.xiaoymin</groupId>
|
||||
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
|
||||
<version>4.4.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springdoc</groupId>
|
||||
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
|
||||
<version>2.3.0</version>
|
||||
</dependency>
|
||||
<!-- Swagger 2 annotations for backward compatibility -->
|
||||
<dependency>
|
||||
<groupId>io.swagger</groupId>
|
||||
<artifactId>swagger-annotations</artifactId>
|
||||
<version>1.6.14</version>
|
||||
</dependency>
|
||||
<!-- /**发送邮件**/-->
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>com.tencentcloudapi</groupId>-->
|
||||
<!-- <artifactId>tencentcloud-sdk-java-ses</artifactId>-->
|
||||
<!-- <version>3.1.572</version>-->
|
||||
<!-- </dependency>-->
|
||||
|
||||
<dependency>
|
||||
<groupId>com.tencentcloudapi</groupId>
|
||||
<artifactId>tencentcloud-sdk-java-ses</artifactId>
|
||||
<version>3.1.572</version>
|
||||
</dependency>
|
||||
|
||||
<!--minio-->
|
||||
<dependency>
|
||||
<groupId>io.minio</groupId>
|
||||
<artifactId>minio</artifactId>
|
||||
<version>8.0.3</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>commons-fileupload</groupId>
|
||||
<artifactId>commons-fileupload</artifactId>
|
||||
<version>1.4</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.microsoft.sqlserver</groupId>
|
||||
<artifactId>mssql-jdbc</artifactId>
|
||||
<version>12.4.2.jre11</version>
|
||||
</dependency>
|
||||
|
||||
<!-- RabbitMQ -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-amqp</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- redis 连接池 -->
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-pool2</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!--支付宝 SDK-->
|
||||
<dependency>
|
||||
<groupId>com.alipay.sdk</groupId>
|
||||
<artifactId>alipay-sdk-java</artifactId>
|
||||
<version>4.22.57.ALL</version>
|
||||
</dependency>
|
||||
|
||||
<!--PayPal SDK-->
|
||||
<dependency>
|
||||
<groupId>com.paypal.sdk</groupId>
|
||||
<artifactId>checkout-sdk</artifactId>
|
||||
<version>1.0.5</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.paypal.sdk</groupId>
|
||||
<artifactId>rest-api-sdk</artifactId>
|
||||
<version>LATEST</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.json</groupId>
|
||||
<artifactId>json</artifactId>
|
||||
<version>20230618</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.poi</groupId>
|
||||
<artifactId>poi</artifactId>
|
||||
<version>5.2.4</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.poi</groupId>
|
||||
<artifactId>poi-ooxml</artifactId>
|
||||
<version>5.2.4</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
<version>2.15.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.stripe</groupId>
|
||||
<artifactId>stripe-java</artifactId>
|
||||
<version>26.2.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- aws s3 -->
|
||||
<!-- S3 dependency -->
|
||||
<dependency>
|
||||
<groupId>software.amazon.awssdk</groupId>
|
||||
<artifactId>s3</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>software.amazon.awssdk</groupId>
|
||||
<artifactId>s3-transfer-manager</artifactId>
|
||||
<version>2.17.103-PREVIEW</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>software.amazon.awssdk</groupId>
|
||||
<artifactId>kms</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>software.amazon.awssdk</groupId>
|
||||
<artifactId>s3control</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>2.10.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.itextpdf</groupId>
|
||||
<artifactId>itextpdf</artifactId>
|
||||
<version>5.5.13.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-websocket</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.google.auth</groupId>
|
||||
<artifactId>google-auth-library-oauth2-http</artifactId>
|
||||
<version>1.8.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.google.api-client</groupId>
|
||||
<artifactId>google-api-client</artifactId>
|
||||
<version>1.32.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.oauth-client</groupId>
|
||||
<artifactId>google-oauth-client</artifactId>
|
||||
<version>1.32.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.http-client</groupId>
|
||||
<artifactId>google-http-client-jackson2</artifactId>
|
||||
<version>1.41.5</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 邮件发送 -->
|
||||
<!-- thymeleaf -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-thymeleaf</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-mail</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>easyexcel</artifactId>
|
||||
<version>3.3.3</version>
|
||||
</dependency>
|
||||
<!-- JSON 转义恢复 -->
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-text</artifactId>
|
||||
<version>1.10.0</version> <!-- 使用最新版本 -->
|
||||
</dependency>
|
||||
|
||||
<!-- 最新版本号:https://mvnrepository.com/artifact/com.alibaba/dashscope-sdk-java -->
|
||||
<!-- 万象SDK -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>dashscope-sdk-java</artifactId>
|
||||
<version>2.20.1</version>
|
||||
</dependency>
|
||||
|
||||
<!-- FFmpeg封装(JavaCV) 视频转gif 全部依赖-->
|
||||
<!--<dependency>
|
||||
<groupId>org.bytedeco</groupId>
|
||||
<artifactId>javacv-platform</artifactId>
|
||||
<version>1.5.9</version>
|
||||
</dependency>-->
|
||||
|
||||
<!-- javacv+javacpp核心库-->
|
||||
<dependency>
|
||||
<groupId>org.bytedeco</groupId>
|
||||
<artifactId>javacv</artifactId>
|
||||
<version>${javacv.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bytedeco</groupId>
|
||||
<artifactId>javacpp-platform</artifactId>
|
||||
<version>${javacv.version}</version>
|
||||
</dependency>
|
||||
<!-- 最小opencv依赖包 ,必须包含上面的javacv+javacpp -->
|
||||
<dependency>
|
||||
<groupId>org.bytedeco</groupId>
|
||||
<artifactId>opencv</artifactId>
|
||||
<version>4.5.1-${javacv.version}</version>
|
||||
<!--<classifier>${system.windowsx64}</classifier>-->
|
||||
<classifier>${javacpp.platform.linux-x86_64}</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bytedeco</groupId>
|
||||
<artifactId>openblas</artifactId>
|
||||
<version>0.3.13-${javacv.version}</version>
|
||||
<!--<classifier>${system.windowsx64}</classifier>-->
|
||||
<classifier>${javacpp.platform.linux-x86_64}</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bytedeco</groupId>
|
||||
<artifactId>flycapture</artifactId>
|
||||
<version>2.13.3.31-${javacv.version}</version>
|
||||
<!--<classifier>${system.windowsx64}</classifier>-->
|
||||
<classifier>${javacpp.platform.linux-x86_64}</classifier>
|
||||
</dependency>
|
||||
<!-- FFmpeg(视频处理,解决你的报错) -->
|
||||
<dependency>
|
||||
<groupId>org.bytedeco</groupId>
|
||||
<artifactId>ffmpeg</artifactId>
|
||||
<version>4.4-1.5.6</version>
|
||||
<!--<classifier>${system.windowsx64}</classifier>-->
|
||||
<classifier>${javacpp.platform.linux-x86_64}</classifier>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.volcengine</groupId>
|
||||
<artifactId>volcengine-java-sdk-ark-runtime</artifactId>
|
||||
<version>0.2.43</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Google 认证库 -->
|
||||
<dependency>
|
||||
<groupId>com.google.auth</groupId>
|
||||
<artifactId>google-auth-library-oauth2-http</artifactId>
|
||||
<version>1.38.0</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- GIFEncoder 视频转gif-->
|
||||
<dependency>
|
||||
<groupId>com.madgag</groupId>
|
||||
<artifactId>animated-gif-lib</artifactId>
|
||||
<version>1.4</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Jakarta WebSocket API -->
|
||||
<dependency>
|
||||
<groupId>jakarta.websocket</groupId>
|
||||
<artifactId>jakarta.websocket-api</artifactId>
|
||||
<version>2.1.1</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring MockMultipartFile 等测试工具,生产代码中也有引用 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-test</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- BouncyCastle 加密库 -->
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcprov-jdk18on</artifactId>
|
||||
<version>1.78.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcpkix-jdk18on</artifactId>
|
||||
<version>1.78.1</version>
|
||||
</dependency>
|
||||
<!-- AOP -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-aop</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<excludes>
|
||||
<exclude>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<!-- 本地开发环境 -->
|
||||
<id>dev</id>
|
||||
<properties>
|
||||
<profiles.active>dev</profiles.active>
|
||||
</properties>
|
||||
<activation>
|
||||
<activeByDefault>true</activeByDefault>
|
||||
</activation>
|
||||
</profile>
|
||||
<profile>
|
||||
<!-- 测试环境 -->
|
||||
<id>test</id>
|
||||
<properties>
|
||||
<profiles.active>test</profiles.active>
|
||||
</properties>
|
||||
</profile>
|
||||
<profile>
|
||||
<!-- 生产环境 -->
|
||||
<id>prod</id>
|
||||
<properties>
|
||||
<profiles.active>prod</profiles.active>
|
||||
</properties>
|
||||
</profile>
|
||||
</profiles>
|
||||
</project>
|
||||
|
||||
@@ -3,11 +3,13 @@ package com.ai.da;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.scheduling.annotation.EnableAsync;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
|
||||
@Slf4j
|
||||
@SpringBootApplication
|
||||
@EnableScheduling
|
||||
@EnableAsync
|
||||
public class AiDaApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
package com.ai.da.common.RabbitMQ;
|
||||
|
||||
|
||||
import com.ai.da.common.utils.MailUtil;
|
||||
import com.ai.da.model.dto.BasicEmailParamDTO;
|
||||
import com.ai.da.service.EmailService;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.rabbitmq.client.Channel;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.amqp.rabbit.annotation.RabbitListener;
|
||||
import org.springframework.core.io.InputStreamSource;
|
||||
import org.springframework.stereotype.Component;
|
||||
import software.amazon.awssdk.core.exception.RetryableException;
|
||||
import org.springframework.amqp.core.Message;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class EmailRetryConsumer {
|
||||
|
||||
@Resource
|
||||
private MailUtil mailUtil;
|
||||
@Resource
|
||||
private MQPublisher mqPublisher;
|
||||
@Resource
|
||||
private EmailService emailService;
|
||||
|
||||
// @RabbitListener(queues = "#{rabbitMQProperties.deadLetter.queue}")
|
||||
public void handleRetry(Map<String, String> mailParams, Message message, Channel channel) throws IOException {
|
||||
long tag = message.getMessageProperties().getDeliveryTag();
|
||||
try {
|
||||
log.info("死信队列收到消息:{}", message);
|
||||
// 处理邮件发送参数
|
||||
BasicEmailParamDTO basicEmailParamDTO = JSONObject.parseObject(mailParams.get("dto"), BasicEmailParamDTO.class);
|
||||
String fileName = mailParams.get("filename");
|
||||
InputStreamSource inputStreamSource = Objects.isNull(mailParams.get("source")) ?
|
||||
null : JSONObject.parseObject(mailParams.get("source"), InputStreamSource.class);
|
||||
JSONObject templateParams = JSONObject.parseObject(mailParams.get("templateParams"), JSONObject.class);
|
||||
String templateName = mailParams.get("templatePath");
|
||||
long logId = Long.parseLong(mailParams.get("logId"));
|
||||
basicEmailParamDTO.setContent(mailUtil.setContent(templateParams, templateName));
|
||||
// 发邮件
|
||||
int lastReturnCode = mailUtil.sendMail(basicEmailParamDTO, fileName, inputStreamSource);
|
||||
if (lastReturnCode == 250) {
|
||||
log.info("邮件发送成功!Subject : {}", basicEmailParamDTO.getSubject());
|
||||
emailService.updateStatus(logId, EmailService.DELIVERED);
|
||||
} else if (lastReturnCode == 450) {
|
||||
log.info("目标邮箱 {} 暂时不可用,请稍后重试", (Object) basicEmailParamDTO.getMailTo());
|
||||
// 重试
|
||||
retry(mailParams, message, channel, tag, logId);
|
||||
} else if (lastReturnCode == 550) {
|
||||
log.info("目标邮箱 {} 不可用,邮件发送失败", (Object) basicEmailParamDTO.getMailTo());
|
||||
emailService.updateStatus(logId, EmailService.FAILED);
|
||||
} else {
|
||||
log.info("邮件发送失败,Subject : {}, 状态码: {}", basicEmailParamDTO.getSubject(), lastReturnCode);
|
||||
retry(mailParams, message, channel, tag, logId);
|
||||
emailService.updateStatus(logId, EmailService.FAILED);
|
||||
}
|
||||
channel.basicAck(tag, false);
|
||||
} catch (RetryableException e) {
|
||||
log.info("邮件重试发生异常:RetryableException -> {}", e.getMessage());
|
||||
channel.basicAck(tag, false); // 确认原消息
|
||||
} catch (Exception e) {
|
||||
log.info("邮件重试发生异常:Exception -> {}", e.getMessage());
|
||||
channel.basicAck(tag, false); // 确认原消息
|
||||
}
|
||||
}
|
||||
|
||||
private int getRetryAttempt(Message message) {
|
||||
Integer attempt = message.getMessageProperties()
|
||||
.getHeader("x-retry-attempt");
|
||||
return attempt != null ? attempt : 1;
|
||||
}
|
||||
|
||||
private void retry(Map<String, String> mailParams, Message message, Channel channel, long tag, long logId) throws IOException{
|
||||
int attempt = getRetryAttempt(message);
|
||||
if (attempt >= 3) { // 最大重试次数
|
||||
channel.basicReject(tag, false);
|
||||
emailService.updateStatus(logId, EmailService.FAILED);
|
||||
log.error("重试结束,邮件最终发送失败: {}", mailParams);
|
||||
} else {
|
||||
log.info("重新将邮件信息发送到重试队列");
|
||||
mqPublisher.sendEmailMsg(mailParams, attempt);
|
||||
channel.basicAck(tag, false); // 确认原消息
|
||||
// 更新数据库
|
||||
emailService.updateRetryCount(logId, attempt + 1);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -5,33 +5,50 @@ import com.ai.da.common.constant.CommonConstant;
|
||||
import com.ai.da.common.utils.RedisUtil;
|
||||
import com.ai.da.model.dto.GenerateThroughImageTextDTO;
|
||||
import com.ai.da.model.vo.GenerateResultVO;
|
||||
import com.ai.da.service.GenerateService;
|
||||
import com.ai.da.model.vo.PoseTransformationVO;
|
||||
import com.ai.da.service.*;
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.fasterxml.jackson.core.JsonParseException;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.google.gson.Gson;
|
||||
import com.rabbitmq.client.Channel;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.tomcat.jni.Time;
|
||||
import org.apache.commons.text.StringEscapeUtils;
|
||||
import org.springframework.amqp.core.Message;
|
||||
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
|
||||
import org.springframework.amqp.rabbit.annotation.RabbitListener;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class GenerateConsumer {
|
||||
|
||||
@Resource
|
||||
private GenerateService generateService;
|
||||
private final GenerateService generateService;
|
||||
|
||||
@Resource
|
||||
private RedisUtil redisUtil;
|
||||
private final UserLikeGroupService userLikeGroupService;
|
||||
|
||||
private final DesignService designService;
|
||||
|
||||
private final CloudTaskService cloudTaskService;
|
||||
|
||||
private final RabbitMQProperties rabbitMQProperties;
|
||||
|
||||
private final RedisUtil redisUtil;
|
||||
|
||||
private final MessageCenterService messageCenterService;
|
||||
|
||||
@Value("${redis.key.orderForGenerate}")
|
||||
private String consumptionOrderKey;
|
||||
@@ -52,9 +69,9 @@ public class GenerateConsumer {
|
||||
public void generate(Message msg, Channel channel, String consumerName) {
|
||||
log.info("============start listening==========");
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
GenerateThroughImageTextDTO generateThroughImageTextDTO = JSONObject.parseObject(msg.getBody(), GenerateThroughImageTextDTO.class);
|
||||
String uniqueId = generateThroughImageTextDTO.getUniqueId();
|
||||
Map<String, String> resp = jsonBytesToMap(msg, channel);
|
||||
String uniqueId = resp.get("tasks_id");
|
||||
// String uniqueId = generateThroughImageTextDTO.getUniqueId();
|
||||
log.info("From " + consumerName + " : " + uniqueId);
|
||||
|
||||
try {
|
||||
@@ -68,6 +85,7 @@ public class GenerateConsumer {
|
||||
log.error("手动确认,不返回队列重新消费");
|
||||
}
|
||||
} else {
|
||||
GenerateThroughImageTextDTO generateThroughImageTextDTO = JSONObject.parseObject(msg.getBody(), GenerateThroughImageTextDTO.class);
|
||||
generateService.generateThroughImageText(generateThroughImageTextDTO);
|
||||
// 将消息从redis排队队列中删除,需保证被消费的消息存储到db之后再从redis删除
|
||||
redisUtil.removeFromZSet(consumptionOrderKey, uniqueId);
|
||||
@@ -82,13 +100,13 @@ public class GenerateConsumer {
|
||||
// 将消息从redis排队队列中删除,需保证被消费的消息存储到db之后再从redis删除
|
||||
redisUtil.removeFromZSet(consumptionOrderKey, uniqueId);
|
||||
String key = generateResultKey + ":" + uniqueId;
|
||||
GenerateResultVO generateResultVO = new GenerateResultVO(generateThroughImageTextDTO.getUniqueId(), null, null, "Fail");
|
||||
GenerateResultVO generateResultVO = new GenerateResultVO(uniqueId, null, null, "Fail");
|
||||
redisUtil.addToString(key, new Gson().toJson(generateResultVO), CommonConstant.GENERATE_RESULT_EXPIRE_TIME);
|
||||
} catch (IOException exception) {
|
||||
log.error("手动确认,取消返回队列,不再重新消费");
|
||||
}
|
||||
// 将入参和错误信息存入数据库
|
||||
String exceptionMessage = JSONObject.toJSONString(generateThroughImageTextDTO) +
|
||||
String exceptionMessage = JSONObject.toJSONString(resp) +
|
||||
" Exception message : " + e.getMessage();
|
||||
HashMap<String, String> exceptionInfo = new HashMap<>();
|
||||
exceptionInfo.put(String.valueOf(uniqueId), exceptionMessage);
|
||||
@@ -105,7 +123,7 @@ public class GenerateConsumer {
|
||||
log.info("============ProcessGenerateResult listening==========");
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
Map<String, String> generateResult = JSONObject.parseObject(msg.getBody(), Map.class);
|
||||
Map<String, String> generateResult = jsonBytesToMap(msg, channel);
|
||||
log.info("generate response : {}", generateResult);
|
||||
|
||||
try {
|
||||
@@ -153,7 +171,7 @@ public class GenerateConsumer {
|
||||
log.info("============processToProductImageResult listening==========");
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
Map<String, String> generateResult = JSONObject.parseObject(msg.getBody(), Map.class);
|
||||
Map<String, String> generateResult = jsonBytesToMap(msg, channel);
|
||||
log.info("toProductImage response : {}", generateResult);
|
||||
|
||||
try {
|
||||
@@ -163,9 +181,13 @@ public class GenerateConsumer {
|
||||
String taskId = generateResult.get("tasks_id");
|
||||
String category = generateResult.get("category");
|
||||
generateService.processToProductImageResult(taskId, url, category);
|
||||
} else if (generateResult.get("status").equals("NO_FACE")) {
|
||||
String taskId = generateResult.get("tasks_id");
|
||||
userLikeGroupService.toProduct(taskId);
|
||||
} else {
|
||||
// 修改redis中的数据状态为exception
|
||||
String key = toProductImageResultKey + ":" + generateResult.get("tasks_id");
|
||||
generateService.updateToProductTaskStatus(generateResult.get("tasks_id"), "Fail");
|
||||
redisUtil.addToString(key, new Gson().toJson(new GenerateResultVO(generateResult.get("tasks_id"), null, null, "Fail")), CommonConstant.GENERATE_RESULT_EXPIRE_TIME);
|
||||
// 将异常信息存到exception中
|
||||
HashMap<String, String> exceptionInfo = new HashMap<>();
|
||||
@@ -200,7 +222,7 @@ public class GenerateConsumer {
|
||||
log.info("============processRelightResult listening==========");
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
Map<String, String> generateResult = JSONObject.parseObject(msg.getBody(), Map.class);
|
||||
Map<String, String> generateResult = jsonBytesToMap(msg, channel);
|
||||
log.info("toProductImage response : {}", generateResult);
|
||||
|
||||
try {
|
||||
@@ -210,9 +232,13 @@ public class GenerateConsumer {
|
||||
String taskId = generateResult.get("tasks_id");
|
||||
String category = generateResult.get("category");
|
||||
generateService.processRelightResult(taskId, url, category);
|
||||
} else if (generateResult.get("status").equals("NO_FACE")) {
|
||||
String taskId = generateResult.get("tasks_id");
|
||||
userLikeGroupService.relight(taskId);
|
||||
} else {
|
||||
// 修改redis中的数据状态为exception
|
||||
String key = relightResultKey + ":" + generateResult.get("tasks_id");
|
||||
generateService.updateToProductTaskStatus(generateResult.get("tasks_id"), "Fail");
|
||||
redisUtil.addToString(key, new Gson().toJson(new GenerateResultVO(generateResult.get("tasks_id"), null, null, "Fail")), CommonConstant.GENERATE_RESULT_EXPIRE_TIME);
|
||||
// 将异常信息存到exception中
|
||||
HashMap<String, String> exceptionInfo = new HashMap<>();
|
||||
@@ -243,75 +269,393 @@ public class GenerateConsumer {
|
||||
log.info("============ProcessRelightResult End listening==========");
|
||||
}
|
||||
|
||||
@RabbitListener(queues = MQConfig.GENERATE_QUEUE)
|
||||
public void processPoseTransformResult(Message msg, Channel channel) {
|
||||
log.info("============ProcessPoseTransformResult listening==========");
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
Map<String, String> generateResult = jsonBytesToMap(msg, channel);
|
||||
log.info("PoseTransformation response : {}", generateResult);
|
||||
|
||||
try {
|
||||
log.info("tasks_id : {} start ", generateResult.get("tasks_id"));
|
||||
if (generateResult.get("status").equals("SUCCESS")) {
|
||||
String gifUrl = generateResult.get("gif_url");
|
||||
String taskId = generateResult.get("tasks_id");
|
||||
String videoUrl = generateResult.get("video_url");
|
||||
String imageUrl = generateResult.get("image_url");
|
||||
generateService.processPoseTransformResult(taskId, gifUrl, videoUrl, imageUrl);
|
||||
} else {
|
||||
// 修改redis中的数据状态为exception
|
||||
String key = generateResultKey + ":" + generateResult.get("tasks_id");
|
||||
generateService.updatePoseTransferStatus(generateResult.get("tasks_id"), "Fail", null);
|
||||
redisUtil.addToString(key, new Gson().toJson(new PoseTransformationVO(null, generateResult.get("tasks_id"),null, null, null, (byte)0, "Fail")), CommonConstant.GENERATE_RESULT_EXPIRE_TIME);
|
||||
// 将异常信息存到exception中
|
||||
HashMap<String, String> exceptionInfo = new HashMap<>();
|
||||
exceptionInfo.put(generateResult.get("tasks_id"), generateResult.get("message"));
|
||||
// 存redis
|
||||
redisUtil.addToMap(exceptionMapKey, exceptionInfo);
|
||||
// 记录失败状态并向用户发送提示消息
|
||||
generateService.processPTFailSituation(generateResult.get("tasks_id"));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
log.error(e.getMessage());
|
||||
try {
|
||||
channel.basicAck(msg.getMessageProperties().getDeliveryTag(), false);
|
||||
// 将消息从redis排队队列中删除,需保证被消费的消息存储到db之后再从redis删除
|
||||
redisUtil.removeFromZSet(consumptionOrderKey, generateResult.get("tasks_id"));
|
||||
} catch (IOException exception) {
|
||||
log.error("手动确认,取消返回队列,不再重新消费");
|
||||
}
|
||||
// 将入参和错误信息存入数据库
|
||||
String exceptionMessage = JSONObject.toJSONString(generateResult) +
|
||||
" Exception message : " + e.getMessage();
|
||||
HashMap<String, String> exceptionInfo = new HashMap<>();
|
||||
exceptionInfo.put(String.valueOf(generateResult.get("tasks_id")), exceptionMessage);
|
||||
// 存redis
|
||||
redisUtil.addToMap(exceptionMapKey, exceptionInfo);
|
||||
// 记录失败状态并向用户发送提示消息
|
||||
generateService.processPTFailSituation(generateResult.get("tasks_id"));
|
||||
}
|
||||
|
||||
long end = System.currentTimeMillis();
|
||||
log.info("tasks_id : {}, end , message : {}, 执行时长: {} 毫秒", generateResult.get("tasks_id"), generateResult.get("message"), (end - start));
|
||||
log.info("============ProcessPoseTransformResult End listening==========");
|
||||
|
||||
}
|
||||
|
||||
public static Map<String, String> jsonBytesToMap(Message msg, Channel channel) {
|
||||
try {
|
||||
// 1. byte[] -> String
|
||||
String jsonString = new String(msg.getBody(), StandardCharsets.UTF_8).trim();
|
||||
// 2. 处理可能的双重转义
|
||||
if (jsonString.startsWith("\"") && jsonString.endsWith("\"")) {
|
||||
jsonString = jsonString.substring(1, jsonString.length() - 1);
|
||||
// 使用 Apache Commons Text, 将 JSON 字符串中的转义字符还原为原始字符
|
||||
jsonString = StringEscapeUtils.unescapeJson(jsonString);
|
||||
}
|
||||
// 3. 验证 JSON 格式
|
||||
if (!isValidJson(jsonString)) {
|
||||
throw new IllegalArgumentException("Invalid JSON format");
|
||||
}
|
||||
// 4. 解析为 Map
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
return mapper.readValue(jsonString, new TypeReference<Map<String, String>>() {});
|
||||
} catch (Exception e) {
|
||||
log.error("消息解析失败: {}", e.getMessage(), e);
|
||||
try {
|
||||
// 仅对不可恢复错误(如非 JSON 数据)进行 ACK
|
||||
if (e instanceof JsonParseException || e instanceof IllegalArgumentException) {
|
||||
channel.basicAck(msg.getMessageProperties().getDeliveryTag(), false);
|
||||
log.warn("因消息格式错误,已确认并丢弃消息。原始消息为:{}", msg);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
log.error("消息确认失败: {}", ex.getMessage(), ex);
|
||||
}
|
||||
throw new RuntimeException("Failed to parse JSON to Map", e);
|
||||
}
|
||||
}
|
||||
|
||||
// 辅助方法:验证字符串是否为合法 JSON
|
||||
private static boolean isValidJson(String json) {
|
||||
try {
|
||||
new ObjectMapper().readTree(json);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void processDesignBatchResult(Message msg, Channel channel) {
|
||||
log.info("============processDesignBatchResult listening==========");
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
Map<String, Object> generateResult = JSONObject.parseObject(msg.getBody(), Map.class);
|
||||
log.info("designBatch response : {}", generateResult);
|
||||
designService.processDesignBatch(generateResult);
|
||||
}
|
||||
|
||||
private void processToProductImageBatchResult(Message msg, Channel channel) {
|
||||
log.info("============processToProductImageResultBatch listening==========");
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
JSONObject generateResult = JSONObject.parseObject(msg.getBody(), JSONObject.class);
|
||||
log.info("toProductImageBatch response : {}", generateResult);
|
||||
|
||||
try {
|
||||
log.info("task_id : {} start ", generateResult.get("task_id"));
|
||||
if (!StringUtils.isEmpty(generateResult.getString("progress"))) {
|
||||
String progress = generateResult.getString("progress");
|
||||
String url = null;
|
||||
if (!progress.startsWith("0") && !progress.equals("OK")) {
|
||||
JSONObject result = generateResult.getJSONObject("result");
|
||||
if (Objects.nonNull(result)) {
|
||||
url = result.getString("product_img");
|
||||
String taskId = generateResult.getString("task_id");
|
||||
userLikeGroupService.toProductBatch(taskId, url, progress);
|
||||
}
|
||||
} else if (progress.startsWith("0/")) {
|
||||
String batchTaskId = generateResult.getString("task_id");
|
||||
if (!StringUtils.isEmpty(batchTaskId)) {
|
||||
cloudTaskService.startTask(batchTaskId);
|
||||
}
|
||||
} else if (progress.equals("OK")) {
|
||||
String batchTaskId = generateResult.getString("task_id");
|
||||
if (!StringUtils.isEmpty(batchTaskId)) {
|
||||
cloudTaskService.completeTask(batchTaskId);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 修改redis中的数据状态为exception
|
||||
String key = toProductImageResultKey + ":" + generateResult.get("task_id");
|
||||
generateService.updateToProductTaskStatus(generateResult.getString("task_id"), "Fail");
|
||||
redisUtil.addToString(key, new Gson().toJson(new GenerateResultVO(generateResult.getString("tasks_id"), null, null, "Fail")), CommonConstant.GENERATE_RESULT_EXPIRE_TIME);
|
||||
// 将异常信息存到exception中
|
||||
HashMap<String, String> exceptionInfo = new HashMap<>();
|
||||
exceptionInfo.put(generateResult.getString("task_id"), generateResult.getString("data"));
|
||||
// 存redis
|
||||
redisUtil.addToMap(exceptionMapKey, exceptionInfo);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage());
|
||||
try {
|
||||
channel.basicAck(msg.getMessageProperties().getDeliveryTag(), false);
|
||||
// 将消息从redis排队队列中删除,需保证被消费的消息存储到db之后再从redis删除
|
||||
redisUtil.removeFromZSet(consumptionOrderKey, generateResult.getString("task_id"));
|
||||
} catch (IOException exception) {
|
||||
log.error("手动确认,取消返回队列,不再重新消费");
|
||||
}
|
||||
// 将入参和错误信息存入数据库
|
||||
String exceptionMessage = JSONObject.toJSONString(generateResult) +
|
||||
" Exception message : " + e.getMessage();
|
||||
HashMap<String, String> exceptionInfo = new HashMap<>();
|
||||
exceptionInfo.put(String.valueOf(generateResult.get("task_id")), exceptionMessage);
|
||||
// 存redis
|
||||
redisUtil.addToMap(exceptionMapKey, exceptionInfo);
|
||||
}
|
||||
|
||||
long end = System.currentTimeMillis();
|
||||
log.info("task_id : {}, end , message : {}, 执行时长: {} 毫秒", generateResult.get("task_id"), generateResult.get("message"), (end - start));
|
||||
log.info("============ProcessToProductImageBatchResult End listening==========");
|
||||
}
|
||||
|
||||
private void processRelightBatchResult(Message msg, Channel channel) {
|
||||
log.info("============processRelightResult listening==========");
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
JSONObject generateResult = JSONObject.parseObject(msg.getBody(), JSONObject.class);
|
||||
log.info("relightBatch response : {}", generateResult);
|
||||
|
||||
try {
|
||||
log.info("task_id : {} start ", generateResult.get("task_id"));
|
||||
if (!StringUtils.isEmpty(generateResult.getString("progress"))) {
|
||||
String progress = generateResult.getString("progress");
|
||||
String url = null;
|
||||
if (!progress.startsWith("0") && !progress.equals("OK")) {
|
||||
JSONObject result = generateResult.getJSONObject("result");
|
||||
if (Objects.nonNull(result)) {
|
||||
url = result.getString("relight_img");
|
||||
String taskId = generateResult.getString("task_id");
|
||||
userLikeGroupService.relightBatch(taskId, url, progress);
|
||||
}
|
||||
} else if (progress.startsWith("0/")) {
|
||||
String batchTaskId = generateResult.getString("task_id");
|
||||
if (!StringUtils.isEmpty(batchTaskId)) {
|
||||
cloudTaskService.startTask(batchTaskId);
|
||||
}
|
||||
} else if (progress.equals("OK")) {
|
||||
String batchTaskId = generateResult.getString("task_id");
|
||||
if (!StringUtils.isEmpty(batchTaskId)) {
|
||||
cloudTaskService.completeTask(batchTaskId);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 修改redis中的数据状态为exception
|
||||
String key = relightResultKey + ":" + generateResult.get("task_id");
|
||||
generateService.updateToProductTaskStatus(generateResult.getString("task_id"), "Fail");
|
||||
redisUtil.addToString(key, new Gson().toJson(new GenerateResultVO(generateResult.getString("tasks_id"), null, null, "Fail")), CommonConstant.GENERATE_RESULT_EXPIRE_TIME);
|
||||
// 将异常信息存到exception中
|
||||
HashMap<String, String> exceptionInfo = new HashMap<>();
|
||||
exceptionInfo.put(generateResult.getString("task_id"), generateResult.getString("data"));
|
||||
// 存redis
|
||||
redisUtil.addToMap(exceptionMapKey, exceptionInfo);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage());
|
||||
try {
|
||||
channel.basicAck(msg.getMessageProperties().getDeliveryTag(), false);
|
||||
// 将消息从redis排队队列中删除,需保证被消费的消息存储到db之后再从redis删除
|
||||
redisUtil.removeFromZSet(consumptionOrderKey, generateResult.getString("task_id"));
|
||||
} catch (IOException exception) {
|
||||
log.error("手动确认,取消返回队列,不再重新消费");
|
||||
}
|
||||
// 将入参和错误信息存入数据库
|
||||
String exceptionMessage = JSONObject.toJSONString(generateResult) +
|
||||
" Exception message : " + e.getMessage();
|
||||
HashMap<String, String> exceptionInfo = new HashMap<>();
|
||||
exceptionInfo.put(String.valueOf(generateResult.get("task_id")), exceptionMessage);
|
||||
// 存redis
|
||||
redisUtil.addToMap(exceptionMapKey, exceptionInfo);
|
||||
}
|
||||
|
||||
long end = System.currentTimeMillis();
|
||||
log.info("task_id : {}, end , message : {}, 执行时长: {} 毫秒", generateResult.get("task_id"), generateResult.get("message"), (end - start));
|
||||
log.info("============ProcessRelightBatchResult End listening==========");
|
||||
}
|
||||
|
||||
private void processPoseTransformBatchResult(Message msg, Channel channel) {
|
||||
log.info("============ProcessPoseTransformBatchResult listening==========");
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
JSONObject generateResult = JSONObject.parseObject(msg.getBody(), JSONObject.class);
|
||||
log.info("PoseTransformationBatch response : {}", generateResult);
|
||||
|
||||
try {
|
||||
log.info("task_id : {} start ", generateResult.get("task_id"));
|
||||
if (!StringUtils.isEmpty(generateResult.getString("progress"))) {
|
||||
String progress = generateResult.getString("progress");
|
||||
String taskId = generateResult.getString("task_id");
|
||||
generateService.processPoseTransformResultBatch(progress, taskId);
|
||||
|
||||
JSONArray result = generateResult.getJSONArray("result");
|
||||
if (!StringUtils.isEmpty(result)) {
|
||||
JSONObject jsonObject = result.getJSONObject(0);
|
||||
String gifUrl = jsonObject.getString("gif_url");
|
||||
String videoUrl = jsonObject.getString("video_url");
|
||||
String imageUrl = jsonObject.getString("first_image_url");
|
||||
generateService.processPoseTransformResultBatch(taskId, gifUrl, videoUrl, imageUrl, progress);
|
||||
}
|
||||
} else {
|
||||
// 修改redis中的数据状态为exception
|
||||
String key = generateResultKey + ":" + generateResult.getString("task_id");
|
||||
generateService.updatePoseTransferStatus(generateResult.getString("task_id"), "Fail", null);
|
||||
redisUtil.addToString(key, new Gson().toJson(new PoseTransformationVO(null, generateResult.getString("task_id"),null, null, null, (byte)0, "Fail")), CommonConstant.GENERATE_RESULT_EXPIRE_TIME);
|
||||
// 将异常信息存到exception中
|
||||
HashMap<String, String> exceptionInfo = new HashMap<>();
|
||||
exceptionInfo.put(generateResult.getString("task_id"), generateResult.getString("message"));
|
||||
// 存redis
|
||||
redisUtil.addToMap(exceptionMapKey, exceptionInfo);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage());
|
||||
try {
|
||||
channel.basicAck(msg.getMessageProperties().getDeliveryTag(), false);
|
||||
// 将消息从redis排队队列中删除,需保证被消费的消息存储到db之后再从redis删除
|
||||
redisUtil.removeFromZSet(consumptionOrderKey, generateResult.getString("task_id"));
|
||||
} catch (IOException exception) {
|
||||
log.error("手动确认,取消返回队列,不再重新消费");
|
||||
}
|
||||
// 将入参和错误信息存入数据库
|
||||
String exceptionMessage = JSONObject.toJSONString(generateResult) +
|
||||
" Exception message : " + e.getMessage();
|
||||
HashMap<String, String> exceptionInfo = new HashMap<>();
|
||||
exceptionInfo.put(generateResult.getString("task_id"), exceptionMessage);
|
||||
// 存redis
|
||||
redisUtil.addToMap(exceptionMapKey, exceptionInfo);
|
||||
}
|
||||
|
||||
long end = System.currentTimeMillis();
|
||||
log.info("tasks_id : {}, end , message : {}, 执行时长: {} 毫秒", generateResult.get("tasks_id"), generateResult.get("message"), (end - start));
|
||||
log.info("============ProcessPoseTransformResult End listening==========");
|
||||
}
|
||||
|
||||
@RabbitListener(queues = "#{rabbitMQProperties.queues.generate}")
|
||||
@RabbitHandler
|
||||
public void generateConsumer1(Message msg, Channel channel) {
|
||||
generate(msg, channel, "consumer 1");
|
||||
}
|
||||
|
||||
@RabbitListener(queues = MQConfig.GENERATE_QUEUE)
|
||||
@RabbitListener(queues = "#{rabbitMQProperties.queues.generate}")
|
||||
@RabbitHandler
|
||||
public void generateConsumer2(Message msg, Channel channel) {
|
||||
generate(msg, channel, "consumer 2");
|
||||
}
|
||||
|
||||
@RabbitListener(queues = MQConfig.GENERATE_QUEUE)
|
||||
@RabbitListener(queues = "#{rabbitMQProperties.queues.generate}")
|
||||
@RabbitHandler
|
||||
public void generateConsumer3(Message msg, Channel channel) {
|
||||
generate(msg, channel, "consumer 3");
|
||||
}
|
||||
|
||||
@RabbitListener(queues = MQConfig.GENERATE_QUEUE)
|
||||
@RabbitListener(queues = "#{rabbitMQProperties.queues.generate}")
|
||||
@RabbitHandler
|
||||
public void generateConsumer4(Message msg, Channel channel) {
|
||||
generate(msg, channel, "consumer 4");
|
||||
}
|
||||
|
||||
@RabbitListener(queues = MQConfig.GENERATE_QUEUE)
|
||||
@RabbitListener(queues = "#{rabbitMQProperties.queues.generate}")
|
||||
@RabbitHandler
|
||||
public void generateConsumer5(Message msg, Channel channel) {
|
||||
generate(msg, channel, "consumer 5");
|
||||
}
|
||||
|
||||
@RabbitListener(queues = MQConfig.GENERATE_QUEUE)
|
||||
@RabbitListener(queues = "#{rabbitMQProperties.queues.generate}")
|
||||
@RabbitHandler
|
||||
public void generateConsumer6(Message msg, Channel channel) {
|
||||
generate(msg, channel, "consumer 6");
|
||||
}
|
||||
|
||||
@RabbitListener(queues = MQConfig.GENERATE_QUEUE)
|
||||
@RabbitListener(queues = "#{rabbitMQProperties.queues.generate}")
|
||||
@RabbitHandler
|
||||
public void generateConsumer7(Message msg, Channel channel) {
|
||||
generate(msg, channel, "consumer 7");
|
||||
}
|
||||
|
||||
@RabbitListener(queues = MQConfig.GENERATE_QUEUE)
|
||||
@RabbitListener(queues = "#{rabbitMQProperties.queues.generate}")
|
||||
@RabbitHandler
|
||||
public void generateConsumer8(Message msg, Channel channel) {
|
||||
generate(msg, channel, "consumer 8");
|
||||
}
|
||||
|
||||
@RabbitListener(queues = MQConfig.GENERATE_QUEUE)
|
||||
@RabbitListener(queues = "#{rabbitMQProperties.queues.generate}")
|
||||
@RabbitHandler
|
||||
public void generateConsumer9(Message msg, Channel channel) {
|
||||
generate(msg, channel, "consumer 9");
|
||||
}
|
||||
|
||||
@RabbitListener(queues = MQConfig.GENERATE_RESULT_QUEUE)
|
||||
@RabbitListener(queues = "#{rabbitMQProperties.queues.generateResult}")
|
||||
@RabbitHandler
|
||||
public void getGenerateResult(Message msg, Channel channel) {
|
||||
processGenerateResult(msg, channel);
|
||||
}
|
||||
|
||||
@RabbitListener(queues = MQConfig.TO_PRODUCT_IMAGE_RESULT_QUEUE)
|
||||
@RabbitListener(queues = "#{rabbitMQProperties.queues.toProductImageResult}")
|
||||
@RabbitHandler
|
||||
public void getToProductImageResult(Message msg, Channel channel) {
|
||||
processToProductImageResult(msg, channel);
|
||||
}
|
||||
|
||||
@RabbitListener(queues = MQConfig.RELIGHT_RESULT_QUEUE)
|
||||
@RabbitListener(queues = "#{rabbitMQProperties.queues.relightResult}")
|
||||
@RabbitHandler
|
||||
public void getRelightResult(Message msg, Channel channel) {
|
||||
processRelightResult(msg, channel);
|
||||
}
|
||||
|
||||
@RabbitListener(queues = "#{rabbitMQProperties.queues.poseTransform}")
|
||||
@RabbitHandler
|
||||
public void getPoseTransformationResult(Message msg, Channel channel) {
|
||||
processPoseTransformResult(msg, channel);
|
||||
}
|
||||
// @RabbitListener(queues = "#{rabbitMQProperties.queues.designBatch}")
|
||||
// @RabbitHandler
|
||||
// public void getDesignBatchResult(Message msg, Channel channel) {
|
||||
// processDesignBatchResult(msg, channel);
|
||||
// }
|
||||
// @RabbitListener(queues = "#{rabbitMQProperties.queues.toProductImageBatch}")
|
||||
// @RabbitHandler
|
||||
// public void getToProductImageBatchResult(Message msg, Channel channel) {
|
||||
// processToProductImageBatchResult(msg, channel);
|
||||
// }
|
||||
//
|
||||
// @RabbitListener(queues = "#{rabbitMQProperties.queues.relightBatch}")
|
||||
// @RabbitHandler
|
||||
// public void getRelightBatchResult(Message msg, Channel channel) {
|
||||
// processRelightBatchResult(msg, channel);
|
||||
// }
|
||||
//
|
||||
// @RabbitListener(queues = "#{rabbitMQProperties.queues.poseTransformBatch}")
|
||||
// @RabbitHandler
|
||||
// public void getPoseTransformBatchResult(Message msg, Channel channel) {
|
||||
// processPoseTransformBatchResult(msg, channel);
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -1,66 +1,82 @@
|
||||
package com.ai.da.common.RabbitMQ;
|
||||
|
||||
import org.springframework.amqp.core.Queue;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
public class MQConfig {
|
||||
|
||||
public static final String GENERATE_EXCHANGE_FANOUT = "generate-exchange";
|
||||
// public static final String GENERATE_QUEUE = "generate-queue-prod";
|
||||
// public static final String GENERATE_QUEUE = "generate-queue-test";
|
||||
// ==================================================================
|
||||
// public static final String GENERATE_QUEUE = "generate-queue-local";
|
||||
// public static final String GENERATE_QUEUE = "generate-queue-dev";
|
||||
public static final String GENERATE_QUEUE = "generate-queue-prod";
|
||||
//
|
||||
// public static final String SR_QUEUE = "SR-queue-local";
|
||||
// public static final String SR_QUEUE = "SR-queue-dev";
|
||||
public static final String SR_QUEUE = "SR-queue-prod";
|
||||
//
|
||||
// public static final String SR_RESULT_QUEUE = "SuperResolution-local";
|
||||
// public static final String SR_RESULT_QUEUE = "SuperResolution-dev";
|
||||
public static final String SR_RESULT_QUEUE = "SuperResolution-prod";
|
||||
//
|
||||
// public static final String GENERATE_RESULT_QUEUE = "GenerateImage-local";
|
||||
public static final String GENERATE_RESULT_QUEUE = "GenerateImage-prod";
|
||||
|
||||
public static final String TO_PRODUCT_IMAGE_RESULT_QUEUE = "ToProductImage-prod";
|
||||
|
||||
public static final String RELIGHT_RESULT_QUEUE = "Relight-prod";
|
||||
public MQConfig() {
|
||||
}
|
||||
|
||||
// @Bean
|
||||
// FanoutExchange fanoutRasaExchange() {
|
||||
// return new FanoutExchange(GENERATE_EXCHANGE_FANOUT);
|
||||
// }
|
||||
|
||||
/**
|
||||
* 创建队列,使用工作模式,不用定义交换机
|
||||
*/
|
||||
@Bean
|
||||
public Queue generateQueue() {
|
||||
return new Queue(GENERATE_QUEUE);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Queue SRQueue() {
|
||||
return new Queue(SR_QUEUE);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Queue SRResultQueue() {
|
||||
return new Queue(SR_RESULT_QUEUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将队列绑定到交换机上【队列订阅交换机】
|
||||
*/
|
||||
// @Bean
|
||||
// Binding bindingExchangeRasa() {
|
||||
// return BindingBuilder.bind(queueRasa()).to(fanoutRasaExchange());
|
||||
// }
|
||||
|
||||
}
|
||||
package com.ai.da.common.RabbitMQ;
|
||||
|
||||
import org.springframework.amqp.core.*;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
public class MQConfig {
|
||||
|
||||
@Autowired
|
||||
private RabbitMQProperties rabbitMQProperties;
|
||||
|
||||
@Bean
|
||||
public Queue generateQueue() {
|
||||
return new Queue(rabbitMQProperties.getQueues().getGenerate());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Queue SRQueue() {
|
||||
return new Queue(rabbitMQProperties.getQueues().getSr());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Queue SRResultQueue() {
|
||||
return new Queue(rabbitMQProperties.getQueues().getSrResult());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Queue generateResultQueue() {
|
||||
return new Queue(rabbitMQProperties.getQueues().getGenerateResult());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Queue toProductImageResultQueue() {
|
||||
return new Queue(rabbitMQProperties.getQueues().getToProductImageResult());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Queue relightResultQueue() {
|
||||
return new Queue(rabbitMQProperties.getQueues().getRelightResult());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Queue poseTransformQueue() {
|
||||
return new Queue(rabbitMQProperties.getQueues().getPoseTransform());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Queue mailRetryQueue() {
|
||||
// 普通队列,不绑定DLX(首次失败后才进入MQ)
|
||||
// durable 持久化队列
|
||||
return QueueBuilder.durable(rabbitMQProperties.getQueues().getEmailRetry())
|
||||
// 关键参数:绑定死信交换机
|
||||
.withArgument("x-dead-letter-exchange", rabbitMQProperties.getDeadLetter().getExchange())
|
||||
// 可选:指定死信路由键(默认使用原消息的路由键)
|
||||
.withArgument("x-dead-letter-routing-key", rabbitMQProperties.getDeadLetter().getRoutingKey())
|
||||
.build();
|
||||
}
|
||||
|
||||
// 新增死信交换机
|
||||
@Bean
|
||||
public DirectExchange deadLetterExchange() {
|
||||
return new DirectExchange(rabbitMQProperties.getDeadLetter().getExchange());
|
||||
}
|
||||
|
||||
// 新增死信队列
|
||||
@Bean
|
||||
public Queue deadLetterQueue() {
|
||||
return QueueBuilder.durable(rabbitMQProperties.getDeadLetter().getQueue()).build();
|
||||
}
|
||||
|
||||
// 绑定死信队列
|
||||
@Bean
|
||||
public Binding deadLetterBinding() {
|
||||
return BindingBuilder.bind(deadLetterQueue())
|
||||
.to(deadLetterExchange())
|
||||
.with(rabbitMQProperties.getDeadLetter().getRoutingKey());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,28 +1,61 @@
|
||||
package com.ai.da.common.RabbitMQ;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.amqp.core.AmqpTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class MQPublisher {
|
||||
|
||||
// private final String url = "http://localhost:15672/api/queues/%2f/generate-queue";
|
||||
|
||||
@Resource
|
||||
private AmqpTemplate amqpTemplate;
|
||||
|
||||
public void sendGenerateMessage(String mm) {
|
||||
log.info("send message:" + mm);
|
||||
amqpTemplate.convertAndSend(MQConfig.GENERATE_QUEUE, mm);
|
||||
}
|
||||
|
||||
public void sendSRMessage(String mm) {
|
||||
log.info("send message:" + mm);
|
||||
amqpTemplate.convertAndSend(MQConfig.SR_QUEUE, mm);
|
||||
}
|
||||
|
||||
}
|
||||
package com.ai.da.common.RabbitMQ;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.amqp.core.AmqpTemplate;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import java.util.Map;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class MQPublisher {
|
||||
|
||||
@Autowired
|
||||
private RabbitMQProperties rabbitMQProperties;
|
||||
|
||||
@Autowired
|
||||
private AmqpTemplate amqpTemplate;
|
||||
|
||||
public void sendGenerateMessage(String mm) {
|
||||
log.info("send generate message: {}", mm);
|
||||
amqpTemplate.convertAndSend(rabbitMQProperties.getQueues().getGenerate(), mm);
|
||||
}
|
||||
|
||||
public void sendSRMessage(String mm) {
|
||||
log.info("send message: {}", mm);
|
||||
amqpTemplate.convertAndSend(rabbitMQProperties.getQueues().getSr(), mm);
|
||||
}
|
||||
|
||||
public void sendGenerateResultMessage(String mm) {
|
||||
log.info("send generate result message: {}", mm);
|
||||
amqpTemplate.convertAndSend(rabbitMQProperties.getQueues().getGenerateResult(), mm);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param mailParams 含有的字段
|
||||
* {"dto": basicEmailParamDTO, "filename": fileName, "source": inputStreamSource,
|
||||
* "templateParams": jsonObject, "templatePath": path}
|
||||
* 邮件发送参数,附件文件名,附件数据
|
||||
* @param retryTimes 重试次数(初始为0)
|
||||
*/
|
||||
public void sendEmailMsg(Map<String, String> mailParams, int retryTimes){
|
||||
log.info("send email MQ message: {} ", mailParams);
|
||||
// // 重新入队(指数退避) 时间单位:毫秒
|
||||
long newDelay = (long) (5000 * Math.pow(2, retryTimes + 1));
|
||||
log.info("send email MQ delay: {} ms, retry attempt: {}", newDelay, retryTimes + 1);
|
||||
amqpTemplate.convertAndSend(
|
||||
rabbitMQProperties.getQueues().getEmailRetry(),
|
||||
mailParams,
|
||||
m -> {
|
||||
m.getMessageProperties().setExpiration(String.valueOf(newDelay));
|
||||
m.getMessageProperties().setHeader("x-retry-attempt", retryTimes + 1);
|
||||
return m;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.ai.da.common.RabbitMQ;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "rabbitmq")
|
||||
@Data
|
||||
public class RabbitMQProperties {
|
||||
|
||||
private Queues queues;
|
||||
private Exchange exchange;
|
||||
private DeadLetter deadLetter; // 新增死信配置
|
||||
|
||||
@Data
|
||||
public static class Queues {
|
||||
private String generate;
|
||||
private String sr;
|
||||
private String srResult;
|
||||
private String generateResult;
|
||||
private String toProductImageResult;
|
||||
private String relightResult;
|
||||
private String poseTransform;
|
||||
private String emailRetry;
|
||||
private String designBatch;
|
||||
private String relightBatch;
|
||||
private String toProductImageBatch;
|
||||
private String poseTransformBatch;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class Exchange {
|
||||
private String generate;
|
||||
}
|
||||
|
||||
// 新增死信配置内部类
|
||||
@Data
|
||||
public static class DeadLetter {
|
||||
private String exchange;
|
||||
private String queue;
|
||||
private String routingKey;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,10 +15,11 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.amqp.core.Message;
|
||||
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
|
||||
import org.springframework.amqp.rabbit.annotation.RabbitListener;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import jakarta.annotation.Resource;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.HashMap;
|
||||
@@ -27,6 +28,9 @@ import java.util.HashMap;
|
||||
@Component
|
||||
public class SRConsumer {
|
||||
|
||||
@Autowired
|
||||
private RabbitMQProperties rabbitMQProperties;
|
||||
|
||||
@Resource
|
||||
private RedisUtil redisUtil;
|
||||
|
||||
@@ -204,7 +208,7 @@ public class SRConsumer {
|
||||
redisUtil.removeFromZSet(consumptionOrderKey, uniqueId);
|
||||
} catch (Exception exception) {
|
||||
log.error("手动确认,取消返回队列,不再重新消费");
|
||||
throw new BusinessException("发生错误,手动确认消息");
|
||||
throw new BusinessException("message.confirm.fail");
|
||||
}
|
||||
// 将入参和错误信息存入redis
|
||||
String exceptionMessage = JSONObject.toJSONString(superResolutionDTO) +
|
||||
@@ -218,14 +222,13 @@ public class SRConsumer {
|
||||
taskListService.updateTaskStatusOrOutputRedis(uniqueId, "fail", null);
|
||||
}
|
||||
|
||||
@RabbitListener(queues = MQConfig.SR_QUEUE)
|
||||
@RabbitListener(queues = "#{rabbitMQProperties.queues.sr}")
|
||||
@RabbitHandler
|
||||
public void SRConsumer1(Message msg, Channel channel) {
|
||||
superResolution(msg, channel, "consumer 1");
|
||||
}
|
||||
|
||||
|
||||
@RabbitListener(queues = MQConfig.SR_RESULT_QUEUE)
|
||||
@RabbitListener(queues = "#{rabbitMQProperties.queues.srResult}")
|
||||
@RabbitHandler
|
||||
public void SRResultConsumer1(Message msg, Channel channel) {
|
||||
getSRResult(msg, channel, "consumer 1");
|
||||
|
||||
@@ -0,0 +1,170 @@
|
||||
package com.ai.da.common.aspect;
|
||||
|
||||
import com.ai.da.common.context.UserContext;
|
||||
import com.ai.da.model.vo.AuthPrincipalVo;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.aspectj.lang.JoinPoint;
|
||||
import org.aspectj.lang.annotation.*;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Controller日志切面
|
||||
* 记录所有Controller接口的请求参数和用户信息
|
||||
*/
|
||||
@Aspect
|
||||
@Component
|
||||
public class ControllerLoggingAspect {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(ControllerLoggingAspect.class);
|
||||
|
||||
/**
|
||||
* 定义切点:所有Controller方法
|
||||
*/
|
||||
@Pointcut("execution(* com.ai.da.controller..*(..))")
|
||||
public void controllerMethods() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Controller方法执行前记录日志
|
||||
*/
|
||||
// @Before("controllerMethods()")
|
||||
public void logControllerBefore(JoinPoint joinPoint) {
|
||||
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
|
||||
if (attributes != null) {
|
||||
HttpServletRequest request = attributes.getRequest();
|
||||
|
||||
// 获取当前用户ID
|
||||
Long userId = null;
|
||||
AuthPrincipalVo authPrincipalVo = UserContext.getUserHolder();
|
||||
if (authPrincipalVo != null) {
|
||||
userId = authPrincipalVo.getId();
|
||||
}
|
||||
|
||||
// 获取请求参数
|
||||
Map<String, Object> params = getRequestParams(joinPoint, request);
|
||||
|
||||
logger.info("=== 请求开始 ===");
|
||||
logger.info("用户ID: {}", userId);
|
||||
logger.info("请求URL: {}", request.getRequestURL().toString());
|
||||
logger.info("请求方法: {}", request.getMethod());
|
||||
logger.info("请求IP: {}", getClientIpAddress(request));
|
||||
logger.info("调用方法: {}.{}", joinPoint.getSignature().getDeclaringType().getSimpleName(), joinPoint.getSignature().getName());
|
||||
logger.info("请求参数: {}", params);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求参数
|
||||
*/
|
||||
private Map<String, Object> getRequestParams(JoinPoint joinPoint, HttpServletRequest request) {
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
|
||||
// 1. 获取Query String参数
|
||||
String queryString = request.getQueryString();
|
||||
if (queryString != null && !queryString.isEmpty()) {
|
||||
params.put("queryString", queryString);
|
||||
}
|
||||
|
||||
// 2. 获取方法参数(包含 @PathVariable, @RequestParam, @RequestBody 等)
|
||||
Object[] args = joinPoint.getArgs();
|
||||
|
||||
if (args != null && args.length > 0) {
|
||||
Map<String, Object> methodParams = new HashMap<>();
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
Object arg = args[i];
|
||||
// 过滤掉不可序列化的参数
|
||||
if (arg != null) {
|
||||
if (isIgnorable(arg)) {
|
||||
// 对于可忽略的类型,记录类型名
|
||||
methodParams.put("arg" + i, "[" + arg.getClass().getSimpleName() + "]");
|
||||
} else {
|
||||
try {
|
||||
methodParams.put("arg" + i, arg);
|
||||
} catch (Exception e) {
|
||||
methodParams.put("arg" + i, arg.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!methodParams.isEmpty()) {
|
||||
params.put("methodParams", methodParams);
|
||||
}
|
||||
}
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否需要过滤的参数类型
|
||||
*/
|
||||
private boolean isIgnorable(Object obj) {
|
||||
return obj instanceof HttpServletRequest
|
||||
|| obj instanceof HttpServletResponse
|
||||
|| obj instanceof MultipartFile
|
||||
|| obj instanceof MultipartFile[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Controller方法抛出异常时记录日志
|
||||
*/
|
||||
@AfterThrowing(pointcut = "controllerMethods()", throwing = "exception")
|
||||
public void logControllerAfterThrowing(JoinPoint joinPoint, Throwable exception) {
|
||||
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
|
||||
|
||||
Long userId = null;
|
||||
AuthPrincipalVo authPrincipalVo = UserContext.getUserHolder();
|
||||
if (authPrincipalVo != null) {
|
||||
userId = authPrincipalVo.getId();
|
||||
}
|
||||
|
||||
// 获取请求参数
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
if (attributes != null) {
|
||||
HttpServletRequest request = attributes.getRequest();
|
||||
params = getRequestParams(joinPoint, request);
|
||||
}
|
||||
|
||||
logger.error("=== 请求异常 ===");
|
||||
logger.error("用户ID: {}", userId);
|
||||
logger.error("调用方法: {}.{}", joinPoint.getSignature().getDeclaringType().getSimpleName(), joinPoint.getSignature().getName());
|
||||
logger.error("请求参数: {}", params);
|
||||
logger.error("异常信息: ", exception);
|
||||
logger.error("=== 异常结束 ===");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取客户端真实IP地址
|
||||
*/
|
||||
private String getClientIpAddress(HttpServletRequest request) {
|
||||
String xForwardedFor = request.getHeader("X-Forwarded-For");
|
||||
if (xForwardedFor != null && !xForwardedFor.isEmpty() && !"unknown".equalsIgnoreCase(xForwardedFor)) {
|
||||
return xForwardedFor.split(",")[0];
|
||||
}
|
||||
|
||||
String xRealIp = request.getHeader("X-Real-IP");
|
||||
if (xRealIp != null && !xRealIp.isEmpty() && !"unknown".equalsIgnoreCase(xRealIp)) {
|
||||
return xRealIp;
|
||||
}
|
||||
|
||||
String proxyClientIp = request.getHeader("Proxy-Client-IP");
|
||||
if (proxyClientIp != null && !proxyClientIp.isEmpty() && !"unknown".equalsIgnoreCase(proxyClientIp)) {
|
||||
return proxyClientIp;
|
||||
}
|
||||
|
||||
String wlProxyClientIp = request.getHeader("WL-Proxy-Client-IP");
|
||||
if (wlProxyClientIp != null && !wlProxyClientIp.isEmpty() && !"unknown".equalsIgnoreCase(wlProxyClientIp)) {
|
||||
return wlProxyClientIp;
|
||||
}
|
||||
|
||||
return request.getRemoteAddr();
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.PropertySource;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import jakarta.annotation.Resource;
|
||||
|
||||
@Configuration
|
||||
//加载配置文件
|
||||
|
||||
21
src/main/java/com/ai/da/common/config/AsyncConfig.java
Normal file
21
src/main/java/com/ai/da/common/config/AsyncConfig.java
Normal file
@@ -0,0 +1,21 @@
|
||||
package com.ai.da.common.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
@Configuration
|
||||
public class AsyncConfig {
|
||||
@Bean("asyncTaskExecutor")
|
||||
public Executor asyncTaskExecutor() {
|
||||
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
|
||||
executor.setCorePoolSize(5);
|
||||
executor.setMaxPoolSize(10);
|
||||
executor.setQueueCapacity(100);
|
||||
executor.setThreadNamePrefix("Async-ImageToSketch-");
|
||||
executor.initialize();
|
||||
return executor;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
260
src/main/java/com/ai/da/common/config/ThreeDSave.java
Normal file
260
src/main/java/com/ai/da/common/config/ThreeDSave.java
Normal file
@@ -0,0 +1,260 @@
|
||||
package com.ai.da.common.config;
|
||||
|
||||
import com.ai.da.common.utils.MinioUtil;
|
||||
import com.ai.da.mapper.primary.ThreeDDetailMapper;
|
||||
import com.ai.da.mapper.primary.ThreeDLayoutMapper;
|
||||
import com.ai.da.mapper.primary.ThreeDPatternLayoutMapper;
|
||||
import com.ai.da.mapper.primary.ThreeDSimpleMapper;
|
||||
import com.ai.da.mapper.primary.entity.ThreeDDetail;
|
||||
import com.ai.da.mapper.primary.entity.ThreeDLayout;
|
||||
import com.ai.da.mapper.primary.entity.ThreeDPatternLayout;
|
||||
import com.ai.da.mapper.primary.entity.ThreeDSimple;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import jakarta.annotation.Resource;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class ThreeDSave {
|
||||
|
||||
@Resource
|
||||
private ThreeDDetailMapper threeDDetailMapper;
|
||||
@Resource
|
||||
private ThreeDSimpleMapper threeDSimpleMapper;
|
||||
@Resource
|
||||
private MinioUtil minioUtil;
|
||||
@Resource
|
||||
private ThreeDLayoutMapper threeDLayoutMapper;
|
||||
@Resource
|
||||
private ThreeDPatternLayoutMapper threeDPatternLayoutMapper;
|
||||
@PostConstruct
|
||||
public void test() {
|
||||
// minioSave();
|
||||
// frontBackDataCreate();
|
||||
// patternDataCreate();
|
||||
}
|
||||
|
||||
private void patternDataCreate() {
|
||||
QueryWrapper<ThreeDSimple> qw = new QueryWrapper<>();
|
||||
qw.lambda().eq(ThreeDSimple::getGender, "female");
|
||||
qw.lambda().ne(ThreeDSimple::getId, 1);
|
||||
List<ThreeDSimple> threeDSimpleList = threeDSimpleMapper.selectList(qw);
|
||||
List<Integer> numbers = new ArrayList<>();
|
||||
String patternPath = "C:\\workspace\\3D\\3D虚拟 1-7\\6.版文件、dxf文件\\女装系列(1)\\女装系列\\56款打版截图";
|
||||
|
||||
File directory = new File(patternPath);
|
||||
|
||||
if (directory.exists() && directory.isDirectory()) {
|
||||
// 获取目录下的所有文件名,并建立编号 -> 文件名的映射
|
||||
Map<Integer, String> fileMap = Arrays.stream(directory.listFiles())
|
||||
.filter(File::isFile) // 只获取文件
|
||||
.map(File::getName) // 获取文件名
|
||||
.collect(Collectors.toMap(
|
||||
name -> {
|
||||
Matcher matcher = Pattern.compile("^\\d+").matcher(name);
|
||||
return matcher.find() ? Integer.parseInt(matcher.group()) : -1;
|
||||
},
|
||||
name -> name,
|
||||
(existing, replacement) -> existing // 处理重复情况,保留原有值
|
||||
));
|
||||
|
||||
for (ThreeDSimple threeDSimple : threeDSimpleList) {
|
||||
String name = threeDSimple.getName();
|
||||
Pattern pattern = Pattern.compile("^\\d+"); // 匹配开头的数字
|
||||
Matcher matcher = pattern.matcher(name);
|
||||
if (matcher.find()) {
|
||||
int number = Integer.parseInt(matcher.group());
|
||||
numbers.add(number);
|
||||
// 匹配对应文件夹,并获取文件
|
||||
if (fileMap.containsKey(number)) {
|
||||
String matchedFolder = fileMap.get(number);
|
||||
File folder = new File(directory, matchedFolder);
|
||||
|
||||
ThreeDPatternLayout threeDPatternLayout = new ThreeDPatternLayout();
|
||||
threeDPatternLayout.setThreeDSimpleId(threeDSimple.getId());
|
||||
threeDPatternLayout.setName(matchedFolder);
|
||||
String minioUrl = "aida-threed/female/pattern-layout/" + matchedFolder;
|
||||
minioUtil.upload(minioUrl, folder);
|
||||
threeDPatternLayout.setUrl(minioUrl);
|
||||
// threeDPatternLayoutMapper.insert(threeDPatternLayout);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println("文件编号映射:" + fileMap);
|
||||
}
|
||||
}
|
||||
|
||||
private void frontBackDataCreate() {
|
||||
QueryWrapper<ThreeDSimple> qw = new QueryWrapper<>();
|
||||
qw.lambda().eq(ThreeDSimple::getGender, "female");
|
||||
// qw.lambda().ne(ThreeDSimple::getId, 1);
|
||||
List<ThreeDSimple> threeDSimpleList = threeDSimpleMapper.selectList(qw);
|
||||
List<Integer> numbers = new ArrayList<>();
|
||||
|
||||
// 文件夹名列表
|
||||
String path = "C:\\workspace\\3D\\3D虚拟 1-7\\3D服装真反面整理\\female"; // 目标路径
|
||||
File directory = new File(path);
|
||||
|
||||
if (directory.exists() && directory.isDirectory()) {
|
||||
// 获取文件夹名列表,并建立编号 -> 文件夹名的映射
|
||||
Map<Integer, String> folderMap = Arrays.stream(directory.listFiles())
|
||||
.filter(File::isDirectory)
|
||||
.map(File::getName)
|
||||
.collect(Collectors.toMap(
|
||||
name -> {
|
||||
Matcher matcher = Pattern.compile("^\\d+").matcher(name);
|
||||
return matcher.find() ? Integer.parseInt(matcher.group()) : -1;
|
||||
},
|
||||
name -> name,
|
||||
(existing, replacement) -> existing // 处理重复情况,保留原有值
|
||||
));
|
||||
|
||||
System.out.println("文件夹编号映射:" + folderMap);
|
||||
|
||||
for (ThreeDSimple threeDSimple : threeDSimpleList) {
|
||||
String name = threeDSimple.getName();
|
||||
Pattern pattern = Pattern.compile("^\\d+"); // 匹配开头的数字
|
||||
Matcher matcher = pattern.matcher(name);
|
||||
if (matcher.find()) {
|
||||
int number = Integer.parseInt(matcher.group());
|
||||
numbers.add(number);
|
||||
// 匹配对应文件夹,并获取文件
|
||||
if (folderMap.containsKey(number)) {
|
||||
String matchedFolder = folderMap.get(number);
|
||||
File folder = new File(directory, matchedFolder);
|
||||
if (folder.exists() && folder.isDirectory()) {
|
||||
|
||||
for (File file : folder.listFiles()) {
|
||||
String fileName = file.getName(); // 去掉后缀
|
||||
ThreeDLayout threeDLayout = new ThreeDLayout();
|
||||
threeDLayout.setGender("female");
|
||||
threeDLayout.setThreeDSimpleId(threeDSimple.getId());
|
||||
threeDLayout.setName(file.getName());
|
||||
if (fileName.startsWith("前")) {
|
||||
threeDLayout.setType("front");
|
||||
}else if (fileName.startsWith("后")) {
|
||||
threeDLayout.setType("back");
|
||||
}
|
||||
String minioUrl = "aida-threed/female/layout/" + folderMap.get(number) + " " + file.getName();
|
||||
minioUtil.upload(minioUrl, file);
|
||||
threeDLayout.setUrl(minioUrl);
|
||||
// threeDLayoutMapper.insert(threeDLayout);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
System.out.println(numbers);
|
||||
} else {
|
||||
System.out.println("路径不存在或不是文件夹");
|
||||
}
|
||||
}
|
||||
|
||||
private void minioSave() {
|
||||
// 指定目标文件夹路径
|
||||
String folderPath = "C:\\workspace\\3D\\3D虚拟 1-7\\3D服装整理\\maleZip";
|
||||
|
||||
// 创建文件对象
|
||||
File folder = new File(folderPath);
|
||||
|
||||
// 检查文件夹是否存在且是目录
|
||||
if (folder.exists() && folder.isDirectory()) {
|
||||
// 获取所有 .zip 文件
|
||||
File[] zipFiles = folder.listFiles((dir, name) -> name.toLowerCase().endsWith(".zip"));
|
||||
|
||||
List<String> zipFileNameList = new ArrayList<>();
|
||||
// 输出文件名
|
||||
if (zipFiles != null) {
|
||||
for (File file : zipFiles) {
|
||||
String zipFileName = file.getName();
|
||||
String[] split = zipFileName.split("_");
|
||||
String zipName = split[0];
|
||||
String sizeWithSuffix = split[1];
|
||||
String[] sizeWithSuffixSplit = sizeWithSuffix.split("\\.");
|
||||
String size = sizeWithSuffixSplit[0];
|
||||
// 找到第一个字母的位置
|
||||
int index = 0;
|
||||
while (index < size.length() && Character.UnicodeBlock.of(size.charAt(index)) == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS) {
|
||||
index++;
|
||||
}
|
||||
|
||||
if (index > 0 && index < size.length()) {
|
||||
String prefix = size.substring(0, index); // "亚码"
|
||||
String suffix = size.substring(index); // "L"
|
||||
|
||||
ThreeDDetail threeDDetail = new ThreeDDetail();
|
||||
threeDDetail.setName(zipFileName);
|
||||
String url = "aida-threed/male/zip/" + zipFileName;
|
||||
threeDDetail.setGender("male");
|
||||
threeDDetail.setSizeType(prefix);
|
||||
threeDDetail.setSize(suffix);
|
||||
threeDDetail.setUrl(url);
|
||||
// threeDDetailMapper.insert(threeDDetail);
|
||||
minioUtil.upload(url, file);
|
||||
|
||||
QueryWrapper<ThreeDSimple> qw = new QueryWrapper<>();
|
||||
qw.lambda().eq(ThreeDSimple::getName, zipName);
|
||||
qw.lambda().eq(ThreeDSimple::getGender, "male");
|
||||
List<ThreeDSimple> threeDSimples = threeDSimpleMapper.selectList(qw);
|
||||
|
||||
if (CollectionUtils.isEmpty(threeDSimples)) {
|
||||
ThreeDSimple threeDSimple = new ThreeDSimple();
|
||||
threeDSimple.setName(zipName);
|
||||
threeDSimple.setGender("male");
|
||||
|
||||
String glbPath = "C:\\workspace\\3D\\3D虚拟 1-7\\3D服装整理\\male\\" + zipName + "\\" +"亚码L";
|
||||
File glbFolder = new File(glbPath);
|
||||
|
||||
// 查找 .glb 文件
|
||||
if (glbFolder.exists() && glbFolder.isDirectory()) {
|
||||
File[] glbFiles = glbFolder.listFiles((dir, name) -> name.toLowerCase().endsWith(".glb"));
|
||||
if (glbFiles != null && glbFiles.length > 0) {
|
||||
for (File glbFile : glbFiles) {
|
||||
String name = glbFile.getName();
|
||||
String glbUrl = "aida-threed/male/glb/" + name;
|
||||
threeDSimple.setUrl(glbUrl);
|
||||
minioUtil.upload(glbUrl, glbFile);
|
||||
}
|
||||
} else {
|
||||
System.out.println("未找到 GLB 文件。" + glbFolder);
|
||||
}
|
||||
} else {
|
||||
System.out.println("GLB 文件夹不存在: " + glbPath);
|
||||
}
|
||||
// threeDSimpleMapper.insert(threeDSimple);
|
||||
threeDDetail.setThreeDSimpleId(threeDSimple.getId());
|
||||
threeDDetailMapper.updateById(threeDDetail);
|
||||
}else {
|
||||
Long id = threeDSimples.get(0).getId();
|
||||
threeDDetail.setThreeDSimpleId(id);
|
||||
threeDDetailMapper.updateById(threeDDetail);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
System.out.println("未找到 ZIP 文件。");
|
||||
}
|
||||
log.info("aaa");
|
||||
} else {
|
||||
System.out.println("文件夹不存在或不是一个目录。");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,42 +1,42 @@
|
||||
package com.ai.da.common.config;
|
||||
|
||||
|
||||
import org.hibernate.validator.HibernateValidator;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
|
||||
import org.springframework.web.servlet.config.annotation.CorsRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
|
||||
|
||||
import javax.validation.Validation;
|
||||
import javax.validation.Validator;
|
||||
import javax.validation.ValidatorFactory;
|
||||
|
||||
@Configuration
|
||||
public class WebConfig extends WebMvcConfigurerAdapter {
|
||||
|
||||
static final String ORIGINS[] = new String[]{"GET", "POST", "PUT", "DELETE"};
|
||||
|
||||
@Override
|
||||
public void addCorsMappings(CorsRegistry registry) {
|
||||
registry.addMapping("/**").allowedOrigins("*").allowCredentials(true).allowedMethods(ORIGINS).maxAge(3600);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Validator validator() {
|
||||
ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class)
|
||||
.configure()
|
||||
//failFast为true出现校验失败的情况,立即结束校验,不再进行后续的校验
|
||||
.failFast(true)
|
||||
.buildValidatorFactory();
|
||||
|
||||
return validatorFactory.getValidator();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public MethodValidationPostProcessor methodValidationPostProcessor() {
|
||||
MethodValidationPostProcessor methodValidationPostProcessor = new MethodValidationPostProcessor();
|
||||
methodValidationPostProcessor.setValidator(validator());
|
||||
return methodValidationPostProcessor;
|
||||
}
|
||||
}
|
||||
package com.ai.da.common.config;
|
||||
|
||||
|
||||
import org.hibernate.validator.HibernateValidator;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
|
||||
import org.springframework.web.servlet.config.annotation.CorsRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
import jakarta.validation.Validation;
|
||||
import jakarta.validation.Validator;
|
||||
import jakarta.validation.ValidatorFactory;
|
||||
|
||||
@Configuration
|
||||
public class WebConfig implements WebMvcConfigurer {
|
||||
|
||||
static final String ORIGINS[] = new String[]{"GET", "POST", "PUT", "DELETE"};
|
||||
|
||||
@Override
|
||||
public void addCorsMappings(CorsRegistry registry) {
|
||||
registry.addMapping("/**").allowedOriginPatterns("*").allowCredentials(true).allowedMethods(ORIGINS).maxAge(3600);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Validator validator() {
|
||||
ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class)
|
||||
.configure()
|
||||
//failFast为true出现校验失败的情况,立即结束校验,不再进行后续的校验
|
||||
.failFast(true)
|
||||
.buildValidatorFactory();
|
||||
|
||||
return validatorFactory.getValidator();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public MethodValidationPostProcessor methodValidationPostProcessor() {
|
||||
MethodValidationPostProcessor methodValidationPostProcessor = new MethodValidationPostProcessor();
|
||||
methodValidationPostProcessor.setValidator(validator());
|
||||
return methodValidationPostProcessor;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.ai.da.common.response.ResultEnum;
|
||||
import com.ai.da.model.enums.Language;
|
||||
import com.ai.da.model.vo.AuthPrincipalVo;
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
@@ -20,6 +21,7 @@ import java.util.ResourceBundle;
|
||||
* @create: 2020-01-01 17:24
|
||||
**/
|
||||
@Data
|
||||
@Slf4j
|
||||
public class BusinessException extends RuntimeException {
|
||||
|
||||
private Integer code;
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.ai.da.common.config.exception;
|
||||
|
||||
public class TokenMissingOrExpiredException extends RuntimeException {
|
||||
public TokenMissingOrExpiredException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Throwable fillInStackTrace() {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
package com.ai.da.common.config.swagger;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import springfox.documentation.builders.ApiInfoBuilder;
|
||||
import springfox.documentation.builders.PathSelectors;
|
||||
import springfox.documentation.builders.RequestHandlerSelectors;
|
||||
import springfox.documentation.service.Contact;
|
||||
import springfox.documentation.spi.DocumentationType;
|
||||
import springfox.documentation.spring.web.plugins.Docket;
|
||||
import springfox.documentation.swagger2.annotations.EnableSwagger2WebMvc;
|
||||
|
||||
@Configuration
|
||||
@EnableSwagger2WebMvc
|
||||
public class AidaConfiguration {
|
||||
|
||||
@Bean(value = "IntelligentCurtainApis")
|
||||
public Docket gxyd5aThemeApis() {
|
||||
Contact contact = new Contact("Mr.Y", "", "136");
|
||||
|
||||
Docket docket = new Docket(DocumentationType.SWAGGER_2)
|
||||
.apiInfo(new ApiInfoBuilder()
|
||||
.description("aida接口文档")
|
||||
.contact(contact)
|
||||
.termsOfServiceUrl("暂无")
|
||||
.version("1.0")
|
||||
.build())
|
||||
//分组名称
|
||||
.groupName("1.0")
|
||||
.select()
|
||||
//这里指定Controller扫描包路径
|
||||
.apis(RequestHandlerSelectors.basePackage("com.ai.da.controller"))
|
||||
.paths(PathSelectors.any())
|
||||
.build();
|
||||
return docket;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.ai.da.common.constant;
|
||||
|
||||
public class AffiliateConstants {
|
||||
public static final String STATUS_ACTIVE = "Active";
|
||||
public static final String STATUS_INACTIVE = "Inactive";
|
||||
public static final String STATUS_DELETE = "Delete";
|
||||
public static final Integer DELETED = 1;
|
||||
public static final Integer NOT_DELETED = 0;
|
||||
}
|
||||
@@ -1,5 +1,8 @@
|
||||
package com.ai.da.common.constant;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class CommonConstant {
|
||||
// 单位 秒 10分钟过期
|
||||
// public static final Long TASK_EXPIRE_TIME = 24 * 60 * 60L;
|
||||
@@ -10,6 +13,8 @@ public class CommonConstant {
|
||||
public static final Integer MINIO_IMAGE_EXPIRE_TIME = 24 * 60;
|
||||
// 单位 秒 一天过期 in redis
|
||||
public static final Long GENERATE_RESULT_EXPIRE_TIME = 24 * 60 * 60L;
|
||||
// 单位 秒 7天过期
|
||||
public static final Long REDIS_SET_EXPIRE_TIME = 24 * 60 * 60 * 7L;
|
||||
|
||||
public static class Numbers{
|
||||
public static final Integer NUMBER_10 = 10;
|
||||
@@ -18,6 +23,7 @@ public class CommonConstant {
|
||||
}
|
||||
|
||||
public static final String GENERATE_PATH = "/api/generate_image";
|
||||
public static final String GENERATE_PATH_FLUX2_KLEIN = "/api/generate_image_flux2_klein";
|
||||
|
||||
public static final String GENERATE_SINGLE_LOGO = "/api/generate_single_logo";
|
||||
|
||||
@@ -27,10 +33,66 @@ public class CommonConstant {
|
||||
|
||||
public static final String GENERATE_LOGO_SINGLE_CANCEL = "/api/generate_single_logo_cancel/";
|
||||
|
||||
// public static final String POSE_TRANSFORMATION_CANCEL = "/api/pose_transform_cancel/";
|
||||
public static final String POSE_TRANSFORMATION_CANCEL = "/api/comfyui_i_2_video_cancel/";
|
||||
|
||||
public static final String PYTHON_PORT_9996 = "9996";
|
||||
|
||||
public static final String PYTHON_PORT_9997 = "9997";
|
||||
|
||||
public static final List<String> AGES_EN = Arrays.asList("Below 20", "20-30", "30-40", "40+");
|
||||
|
||||
public static final List<String> AGES_CN = Arrays.asList("20岁以下", "20-30岁", "30-40岁", "40+岁");
|
||||
public static final List<String> IF_HELPFUL_EN = Arrays.asList("Easy to learn and use", "Easy to get trend information",
|
||||
"Lots of creative design proposals","The AIGC functions for moodboard is helpful","The AIGC functions for design sketches is helpful",
|
||||
"Easy to select the right color","The Chatbot function is helpful","The print position function is helpful",
|
||||
"The drawing function is helpful","The export function is useful","Easy to edit the design","Others");
|
||||
|
||||
public static final List<String> IF_HELPFUL_CN = Arrays.asList("易于学习和使用", "容易获取趋势信息",
|
||||
"提供大量创意设计方案","AIGC功能对灵感板有帮助","AIGC功能对设计草图有帮助",
|
||||
"容易选择合适的颜色","聊天机器人功能有帮助","打印位置功能有帮助",
|
||||
"绘图功能有帮助","导出功能有用","设计编辑简单","其他");
|
||||
|
||||
public static final List<String> IF_IMPROVE_EN = Arrays.asList("Proposed designs are boring, need more interesting designs",
|
||||
"Difficult to make changes on design","Only 2D output, no 3D results","Difficult to apply keywords for AIGC generation",
|
||||
"Clothing is not in the right proportion","Not compatible with pattern making solutions","Improved user interface for better navigation",
|
||||
"Lack of responsive customer support","Insufficient tutorial or guidance for new users","Limited personalization options for designs","Others");
|
||||
|
||||
public static final List<String> IF_IMPROVE_CN = Arrays.asList("提供的设计很无聊,需要更多有趣的设计",
|
||||
"设计修改困难","只有2D输出,没有3D结果","难以选择合适的关键词应用于AIGC生成",
|
||||
"服装比例不正确","与打版解决方案不兼容","改进用户界面以便更好导航",
|
||||
"客户支持响应不及时","对新用户的教程或指导不足","设计个性化选项有限","其他");
|
||||
|
||||
public static final List<String> IS_SUBSCRIBE = Arrays.asList("yes", "no");
|
||||
|
||||
// public static final String DEFAULT_AVATAR = "aida-users/87/avatar/default.jpg";
|
||||
public static final String DEFAULT_AVATAR = "aida-users/87/avatar/default.png";
|
||||
|
||||
/* 截止至2024/08/26,在Code-Create DB中pmr_users表中最大的用户id */
|
||||
public static final Long MAXIMUM_USER_ID = 704L;
|
||||
// public static final Long MAXIMUM_USER_ID = 225L;
|
||||
|
||||
// 激活更改邮箱 链接有效期 毫秒 3天
|
||||
public static final Long CHANGE_MAILBOX_LINK_VALIDITY = 259200000L;
|
||||
|
||||
public static final String RCA_WORKSHOP_TAG = "#RCAworkshop_2024";
|
||||
|
||||
public static final String PORTFOLIO_DELETED_EN = "Portfolio has been deleted";
|
||||
|
||||
public static final String PORTFOLIO_DELETED_CN = "作品已删除";
|
||||
|
||||
public static final String TIME_FORMAT_MMM_dd_yyyy_EEEE = "MMM. dd, yyyy, EEEE";
|
||||
|
||||
public static final String TIME_FORMAT_MMM_dd_yyyy = "MMM. dd, yyyy";
|
||||
|
||||
public static final String TIME_FORMAT_yyyy_MM_dd_HH_mm_ss = "yyyy-MM-dd HH:mm:ss";
|
||||
|
||||
public static final String AFFILIATE_LINK = "https://www.aida.com.hk?ref=";
|
||||
|
||||
public static final String PARTIAL_DESIGN_FILENAME = "PartialDesign";
|
||||
|
||||
public static final String PARTIAL_DESIGN_PREVIEW_FILENAME = "Preview";
|
||||
|
||||
public static final String senderEmail = "info@aida.com.hk";
|
||||
|
||||
}
|
||||
|
||||
42
src/main/java/com/ai/da/common/constant/ModelConstants.java
Normal file
42
src/main/java/com/ai/da/common/constant/ModelConstants.java
Normal file
@@ -0,0 +1,42 @@
|
||||
package com.ai.da.common.constant;
|
||||
|
||||
/**
|
||||
* 模型相关常量类
|
||||
* 用于存放 chooseModelAndPrompt 方法中的字符串常量
|
||||
*/
|
||||
public class ModelConstants {
|
||||
|
||||
// 类型常量
|
||||
public static final String PRINTBOARD = "Printboard";
|
||||
public static final String MOODBOARD = "Moodboard";
|
||||
public static final String SKETCHBOARD = "Sketchboard";
|
||||
|
||||
// 模型级别常量
|
||||
public static final String ADVANCED = "advanced";
|
||||
public static final String HIGH = "high";
|
||||
public static final String NORMAL = "normal";
|
||||
|
||||
// 模型名称常量
|
||||
public static final String PRINTBOARD_ADVANCED_T2I = "qwen-image";
|
||||
public static final String MOODBOARD_ADVANCED = "doubao-seedream-3-0-t2i-250415";
|
||||
public static final String PRINTBOARD_HIGH_T2I = "doubao-seedream-3-0-t2i-250415";
|
||||
public static final String PRINTBOARD_HIGH_I2I = "doubao-seedream-4-0-250828-fast";
|
||||
public static final String PRINTBOARD_ADVANCED_I2I = "doubao-seedream-4-0-250828";
|
||||
public static final String IMAGEN_MODEL = "imagen-4.0-generate-001";
|
||||
public static final String NANO_BANANA = "gemini-2.5-flash-image";
|
||||
public static final String LOCAL_MODEL = "local";
|
||||
|
||||
// 风格常量
|
||||
public static final String PAINTING_STYLE = "Painting Style";
|
||||
public static final String ILLUSTRATION_STYLE = "Illustration Style";
|
||||
public static final String REAL_STYLE = "Real Style";
|
||||
|
||||
// 映射键
|
||||
public static final String PROMPT = "prompt";
|
||||
public static final String USE_MODEL = "UseModel";
|
||||
|
||||
// 防止实例化
|
||||
private ModelConstants() {
|
||||
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,21 @@ public enum AuthenticationOperationTypeEnum {
|
||||
/**
|
||||
* 忘记密码
|
||||
*/
|
||||
FORGET_PWD;
|
||||
FORGET_PWD,
|
||||
/**
|
||||
* 更改邮箱
|
||||
*/
|
||||
CHANGE_MAILBOX,
|
||||
/**
|
||||
* 填写用户国家和职业
|
||||
*/
|
||||
UPDATE_USERINFO,
|
||||
|
||||
REGISTER,
|
||||
/**
|
||||
* Global_Award 活动验证
|
||||
*/
|
||||
GLOBAL_AWARD;
|
||||
|
||||
public static AuthenticationOperationTypeEnum of(String name) {
|
||||
return Stream.of(AuthenticationOperationTypeEnum.values()).filter(v -> v.name().equals(name)).findFirst().orElse(null);
|
||||
|
||||
@@ -27,7 +27,9 @@ public enum CollectionLevel1TypeEnum {
|
||||
/**
|
||||
* 市场
|
||||
*/
|
||||
MARKETING_SKETCH("MarketingSketch");
|
||||
MARKETING_SKETCH("MarketingSketch"),
|
||||
|
||||
MODEL("Models");
|
||||
|
||||
private String realName;
|
||||
|
||||
|
||||
@@ -68,4 +68,7 @@ public enum CollectionLevel2TypeEnum {
|
||||
public static List<String> printType() {
|
||||
return Arrays.asList(LOGO.getRealName(), SLOGAN.getRealName(), Pattern.getRealName());
|
||||
}
|
||||
public static CollectionLevel2TypeEnum ofWithLoweCase(String realName) {
|
||||
return Stream.of(CollectionLevel2TypeEnum.values()).filter(v -> v.getRealName().toLowerCase().equals(realName)).findFirst().orElse(null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,23 +3,35 @@ package com.ai.da.common.enums;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public enum CreditsEventsEnum {
|
||||
|
||||
PRICE("price","6"),
|
||||
PRICE("price","10"),
|
||||
// PRICE("price","1"),// for test
|
||||
// PRICE("price","0.1"),
|
||||
|
||||
BUY_CREDITS("Buy Credits","60"),
|
||||
BUY_CREDITS("Buy Credits","50"),
|
||||
// BUY_CREDITS("Buy Credits","10"),// for test
|
||||
|
||||
REFUND("Refund","60"),
|
||||
REFUND("Refund","50"),
|
||||
// BUY_CREDITS("Buy Credits","10"),
|
||||
|
||||
// 每月更新
|
||||
INIT_YEARLY("init_yearly", "6000"),
|
||||
INIT_MONTHLY("init_monthly", "5000"),
|
||||
INIT_YEARLY("init_yearly", "50000"),
|
||||
INIT_MONTHLY("init_monthly", "3500"),
|
||||
INIT_MONTHLY_ECO("init_monthly_eco", "500"),
|
||||
INIT_QUARTERLY("init_quarterly", "12000"),
|
||||
INIT_MONTHLY_EDU("init_monthly_edu", "3500"),
|
||||
INIT_TRIAL("init_trial", "100"),
|
||||
INIT_WEEKLY("init_weekly","6000"),
|
||||
RESET_YEAR_CREDITS("reset_year_credits","6000"),
|
||||
|
||||
// SUPER_RESOLUTION("Super Resolution","30"),
|
||||
SUPER_RESOLUTION("Super Resolution","10"),
|
||||
@@ -29,15 +41,51 @@ public enum CreditsEventsEnum {
|
||||
MOOD_BOARD("MoodBoard","5"),
|
||||
SKETCH_BOARD("SketchBoard","5"),
|
||||
TO_PRODUCT_IMAGE("ToProductImage","5"),
|
||||
TO_PRODUCT_IMAGE_ADVANCED("ToProductImageAdvanced","15"),
|
||||
RELIGHT("Relight","5"),
|
||||
RELIGHT_FLUX("RelightFlux","10"),
|
||||
QUESTIONNAIRE("Questionnaire","100"),
|
||||
IMAGE_TO_SKETCH("ImageToSketch","5"),
|
||||
IMAGE_TO_SKETCH_FLUX("ImageToSketchFlux","10"),
|
||||
POSE_TRANSFORMATION("PoseTransformation","10"),
|
||||
OTHER("Other","5"),
|
||||
|
||||
OTHER("Other","5");
|
||||
SCETCH_TEXT2IMG("SketchText2Image","10"),
|
||||
SCETCH_IMG2IMG("SketchImg2Image","15"),
|
||||
WX_TEXT2IMG("WX_Text2Image", "5"),
|
||||
QWEN_TEXT2IMG("QWEN_Text2Image", "10"),
|
||||
DOUBAO_TEXT2IMG("Doubao_Text2Image", "10"),
|
||||
DOUBAO_IMG2IMG_ADVANCED("Doubao_img2image_advanced", "10"),
|
||||
DOUBAO_IMG2IMG_HIGH("Doubao_img2image_high", "15"),
|
||||
WX_ANIMATION("WX_Animation", "30"),
|
||||
FREEPIK_IMG2IMG("Freepik_img2img", "20"),
|
||||
FLUX_IMG2IMG("Flux_img2img","10"),
|
||||
|
||||
private String name;
|
||||
LOCAL_TEXT2IMG("Local_text2img","1.25"),
|
||||
LOCAL_IMG2IMG("Local_img2img","1.25"),
|
||||
LOCAL_TEXT2IMG_HIGH("Local_text2img_high","5"),
|
||||
LOCAL_IMG2IMG_HIGH("Local_img2img_high","5"),
|
||||
LOCAL_ANIMATION("Local_Animation","15"),
|
||||
|
||||
LLM_CONVERSATION("LLM_Conversation", "0")
|
||||
;
|
||||
|
||||
private final String name;
|
||||
|
||||
/**
|
||||
* 对应事件需要消耗or获得的积分
|
||||
*/
|
||||
private String value;
|
||||
private final String value;
|
||||
|
||||
public static List<String> generateFunctionNames() {
|
||||
return Arrays.asList(SLOGAN.name, LOGO.name, PATTERN.name, MOOD_BOARD.name, SKETCH_BOARD.name,
|
||||
TO_PRODUCT_IMAGE.name, RELIGHT.name, IMAGE_TO_SKETCH.name, POSE_TRANSFORMATION.name);
|
||||
}
|
||||
|
||||
private static final Map<String, CreditsEventsEnum> BY_TASK_NAME = Arrays.stream(values())
|
||||
.collect(Collectors.toMap(CreditsEventsEnum::getName, Function.identity()));
|
||||
|
||||
public static CreditsEventsEnum getByTaskName(String taskName){
|
||||
return BY_TASK_NAME.get(taskName);
|
||||
}
|
||||
}
|
||||
|
||||
34
src/main/java/com/ai/da/common/enums/FluxTaskStatusEnum.java
Normal file
34
src/main/java/com/ai/da/common/enums/FluxTaskStatusEnum.java
Normal file
@@ -0,0 +1,34 @@
|
||||
package com.ai.da.common.enums;
|
||||
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public enum FluxTaskStatusEnum {
|
||||
|
||||
SUCCESS("Ready"),
|
||||
|
||||
TASK_NOT_FOUND("Task not found"),
|
||||
|
||||
REQUEST_MODERATED("Request Moderated"),
|
||||
|
||||
CONTENT_MODERATED("Content Moderated"),
|
||||
|
||||
ERROR("Error"),
|
||||
|
||||
PENDING_F("Pending");
|
||||
|
||||
private final String name;
|
||||
|
||||
public static FluxTaskStatusEnum fromName(String name) {
|
||||
for (FluxTaskStatusEnum status : values()) {
|
||||
if (status.name.equalsIgnoreCase(name)) {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
// 或者返回默认值
|
||||
return TASK_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,9 @@ public enum LibraryLevel1TypeEnum {
|
||||
* 模特
|
||||
*/
|
||||
MODELS("Models"),
|
||||
DESIGN_ELEMENTS("DesignElements");
|
||||
DESIGN_ELEMENTS("DesignElements"),
|
||||
|
||||
BRAND_DNA("BrandDNA");
|
||||
|
||||
private String realName;
|
||||
|
||||
|
||||
29
src/main/java/com/ai/da/common/enums/MotionModeEnum.java
Normal file
29
src/main/java/com/ai/da/common/enums/MotionModeEnum.java
Normal file
@@ -0,0 +1,29 @@
|
||||
package com.ai.da.common.enums;
|
||||
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@Getter
|
||||
public enum MotionModeEnum {
|
||||
POSE_TO_VIDEO(1, "/api/comfyui_image_pose_2_video"),
|
||||
|
||||
PROMPT_TO_VIDEO(2, "/api/comfyui_image_2_video"),
|
||||
|
||||
FIRST_LAST_FRAME_TO_VIDEO(3, "/api/comfyui_flf_2_video")
|
||||
;
|
||||
|
||||
private int code;
|
||||
|
||||
private String url;
|
||||
|
||||
MotionModeEnum(int code, String url) {
|
||||
this.code = code;
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public static MotionModeEnum of(int code) {
|
||||
return Stream.of(MotionModeEnum.values()).filter(v -> v.getCode()== code).findFirst().orElse(null);
|
||||
}
|
||||
}
|
||||
@@ -10,38 +10,34 @@ public enum OrderStatusEnum {
|
||||
* 未支付
|
||||
*/
|
||||
NOT_PAY("未支付"),
|
||||
|
||||
|
||||
/**
|
||||
* 支付成功
|
||||
*/
|
||||
SUCCESS("支付成功"),
|
||||
|
||||
/**
|
||||
* 支付失败
|
||||
*/
|
||||
FAILURE("支付失败"),
|
||||
/**
|
||||
* 已关闭
|
||||
*/
|
||||
TIMEOUT_CLOSED("超时已关闭"),
|
||||
|
||||
/**
|
||||
* 已取消
|
||||
*/
|
||||
CANCEL("用户已取消"),
|
||||
|
||||
/**
|
||||
* 退款中
|
||||
*/
|
||||
REFUND_PROCESSING("退款中"),
|
||||
|
||||
/**
|
||||
* 已退款
|
||||
*/
|
||||
REFUND_SUCCESS("已退款"),
|
||||
|
||||
/**
|
||||
* 退款异常
|
||||
*/
|
||||
REFUND_ABNORMAL("退款异常"),
|
||||
|
||||
/**
|
||||
* paypal订单状态为 APPROVED
|
||||
*/
|
||||
|
||||
81
src/main/java/com/ai/da/common/enums/PoseEnum.java
Normal file
81
src/main/java/com/ai/da/common/enums/PoseEnum.java
Normal file
@@ -0,0 +1,81 @@
|
||||
package com.ai.da.common.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public enum PoseEnum {
|
||||
|
||||
// POSE_1(1, "aida-sys-image/pose/pose-1.mp4", "aida-sys-image/pose/pose-1.gif", "aida-sys-image/pose/pose-1-first_frame.jpeg", "AACT.8090e67b.-E3pujumEfCbDTI_rjSH-A.LwIlGT3j"),
|
||||
POSE_1(1, "aida-sys-image/pose/pose-1-1.mp4", "aida-sys-image/pose/pose-1-1.gif", "aida-sys-image/pose/pose-1-first_frame.jpeg", "AACT.8090e67b.qNMWJlyKEfCuORaRJeW4dg.x3wUteVO"),
|
||||
// POSE_2(2, "aida-sys-image/pose/pose-2.mp4", "aida-sys-image/pose/pose-2.gif", "aida-sys-image/pose/pose-2-first_frame.jpeg", "AACT.8090e67b.TwJLxEv3EfCbDTI_rjSH-A.IOQZCYhf"),
|
||||
POSE_2(2, "aida-sys-image/pose/pose-2-1.mp4", "aida-sys-image/pose/pose-2-1.gif", "aida-sys-image/pose/pose-2-first_frame.jpeg", "AACT.8090e67b.QpaGOlyLEfCuuJo8eQGF2Q.62EiJj-6"),
|
||||
// POSE_3(3, "aida-sys-image/pose/pose-3.mp4", "aida-sys-image/pose/pose-3.gif", "aida-sys-image/pose/pose-3-first_frame.jpeg", "AACT.8090e67b.gd3OCkv4EfCxyZo8eQGF2Q.qMm-a1XI"),
|
||||
POSE_3(3, "aida-sys-image/pose/pose-3-1.mp4", "aida-sys-image/pose/pose-3-1.gif", "aida-sys-image/pose/pose-3-first_frame.jpeg", "AACT.8090e67b.2q5qjFyLEfCImjI_rjSH-A.5cFMwOvi"),
|
||||
// POSE_4(4, "aida-sys-image/pose/pose-4.mp4", "aida-sys-image/pose/pose-4.gif", "aida-sys-image/pose/pose-4-first_frame.jpeg", "AACT.8090e67b.AUDnuEwDEfCEHBaRJeW4dg.rlx36xEY"),
|
||||
POSE_4(4, "aida-sys-image/pose/pose-4-1.mp4", "aida-sys-image/pose/pose-4-1.gif", "aida-sys-image/pose/pose-4-first_frame.jpeg", "AACT.8090e67b.KoYMplyPEfCuORaRJeW4dg.MuuBTG78"),
|
||||
// POSE_5(5, "aida-sys-image/pose/pose-5.mp4", "aida-sys-image/pose/pose-5.gif", "aida-sys-image/pose/pose-5-first_frame.jpeg", "AACT.8090e67b.G8BvkEwEEfCxyZo8eQGF2Q.fo4ryrgR"),
|
||||
POSE_5(5, "aida-sys-image/pose/pose-5-1.mp4", "aida-sys-image/pose/pose-5-1.gif", "aida-sys-image/pose/pose-5-first_frame.jpeg", "AACT.8090e67b.x54FNFyPEfCuuJo8eQGF2Q.P1egmEZ_"),
|
||||
// POSE_6(6, "aida-sys-image/pose/pose-6.mp4", "aida-sys-image/pose/pose-6.gif", "aida-sys-image/pose/pose-6-first_frame.jpeg", "AACT.8090e67b.yBIPnEwEEfCxyZo8eQGF2Q.boSFwTG9");
|
||||
POSE_6(6, "aida-sys-image/pose/pose-6-1.mp4", "aida-sys-image/pose/pose-6-1.gif", "aida-sys-image/pose/pose-6-first_frame.jpeg", "AACT.8090e67b.QSCvBlyQEfCImjI_rjSH-A.G9-Z5ffW");
|
||||
|
||||
|
||||
private final Integer id;
|
||||
|
||||
private final String videoPath;
|
||||
|
||||
private final String gifPath;
|
||||
|
||||
private final String firstFramePath;
|
||||
|
||||
private final String templateId;
|
||||
|
||||
private static final List<Map<String, String>> PROPERTY_LIST;
|
||||
|
||||
static {
|
||||
PROPERTY_LIST = Arrays.stream(values())
|
||||
.map(PoseEnum::toMap)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private static final Map<Integer, PoseEnum> BY_ID = Arrays.stream(values())
|
||||
.collect(Collectors.toMap(PoseEnum::getId, Function.identity()));
|
||||
|
||||
private static final Map<String, PoseEnum> BY_VIDEO_PATH = Arrays.stream(values())
|
||||
.collect(Collectors.toMap(PoseEnum::getVideoPath, Function.identity()));
|
||||
|
||||
private static final List<String> VIDEO_PATH = Arrays.stream(values())
|
||||
.map(PoseEnum::getVideoPath).collect(Collectors.toList());
|
||||
|
||||
public static PoseEnum getById(Integer id) {
|
||||
return BY_ID.get(id);
|
||||
}
|
||||
|
||||
public static PoseEnum getByVideoPath(String videoPath) {
|
||||
return BY_VIDEO_PATH.get(videoPath);
|
||||
}
|
||||
|
||||
private Map<String, String> toMap() {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
map.put("id", String.valueOf(id));
|
||||
map.put("gif", gifPath);
|
||||
map.put("video", videoPath);
|
||||
map.put("firstFrame", firstFramePath);
|
||||
return map;
|
||||
}
|
||||
|
||||
public static List<Map<String, String>> getPropertyList() {
|
||||
return PROPERTY_LIST.stream()
|
||||
.map(HashMap::new) // 深拷贝每个 Map
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static List<String> getVideoList() {
|
||||
return new ArrayList<>(VIDEO_PATH); // 返回副本以保证不可变性
|
||||
}
|
||||
}
|
||||
33
src/main/java/com/ai/da/common/enums/ProductEnum.java
Normal file
33
src/main/java/com/ai/da/common/enums/ProductEnum.java
Normal file
@@ -0,0 +1,33 @@
|
||||
package com.ai.da.common.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum ProductEnum {
|
||||
// 积分购买
|
||||
CreditsProduct("AiDA credits purchase", 10L, 50L),
|
||||
// 年度订阅
|
||||
AnnualSubscription("AiDA Annual Subscription", 5000L, 50000L),
|
||||
// 月度订阅(订阅费500,每月3500 积分)
|
||||
MonthlySubscription("AiDA Monthly Subscription", 500L, 3500L),
|
||||
// 月度订阅 -- 经济实惠版 (订阅费100,每月500 积分)
|
||||
Eco_MonthlySubscription("AiDA Eco Monthly Subscription", 100L, 500L),
|
||||
// 季度订阅
|
||||
QuarterlySubscription("AiDA Quarterly Subscription", 1500L, 12000L),
|
||||
// 月度订阅 -- 教育版
|
||||
EDUMonthlySubscription("AiDA Edu Monthly Subscription", 200L, 3500L),
|
||||
// 测试
|
||||
DailySubscription("AiDA Daily Subscription", 5L, 100L),
|
||||
;
|
||||
|
||||
/**
|
||||
* 类型
|
||||
*/
|
||||
private final String name;
|
||||
|
||||
private final Long price;
|
||||
|
||||
private final Long credits;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.ai.da.common.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public enum WangXiangTaskStatusEnum {
|
||||
|
||||
SUCCEEDED("SUCCEEDED"),
|
||||
|
||||
UNKNOWN_W("UNKNOWN"),
|
||||
|
||||
FAILED("FAILED"),
|
||||
|
||||
RUNNING("RUNNING"),
|
||||
|
||||
PENDING_W("PENDING");
|
||||
|
||||
private final String name;
|
||||
|
||||
// 通过name查找枚举的静态方法
|
||||
public static WangXiangTaskStatusEnum fromName(String name) {
|
||||
for (WangXiangTaskStatusEnum status : values()) {
|
||||
if (status.name.equalsIgnoreCase(name)) {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
// 或者返回默认值
|
||||
return UNKNOWN_W;
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,7 @@
|
||||
package com.ai.da.common.response;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@@ -16,17 +15,17 @@ import java.util.List;
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@ApiModel("分页响应结果")
|
||||
@Schema(description = "分页响应结果")
|
||||
public class PageBaseResponse<T> {
|
||||
@ApiModelProperty("页码")
|
||||
@Schema(description = "页码")
|
||||
private long page;
|
||||
@ApiModelProperty("每页数量")
|
||||
@Schema(description = "每页数量")
|
||||
private long size;
|
||||
@ApiModelProperty("总页数")
|
||||
@Schema(description = "总页数")
|
||||
private long pages;
|
||||
@ApiModelProperty("总条数")
|
||||
@Schema(description = "总条数")
|
||||
private long total;
|
||||
@ApiModelProperty("结果集")
|
||||
@Schema(description = "结果集")
|
||||
private List<T> content;
|
||||
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
package com.ai.da.common.response;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@@ -16,18 +15,18 @@ import java.util.List;
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@ApiModel("分页响应结果")
|
||||
@Schema(description = "分页响应结果")
|
||||
public class PageResponse<T> extends Response<List<T>> {
|
||||
@ApiModelProperty("页码")
|
||||
@Schema(description = "页码")
|
||||
private long page;
|
||||
@ApiModelProperty("每页数量")
|
||||
@Schema(description = "每页数量")
|
||||
private long size;
|
||||
@ApiModelProperty("总页数")
|
||||
@Schema(description = "总页数")
|
||||
private long pages;
|
||||
@ApiModelProperty("总条数")
|
||||
@Schema(description = "总条数")
|
||||
private long total;
|
||||
|
||||
@ApiModelProperty("结果集")
|
||||
@Schema(description = "结果集")
|
||||
private List<T> content;
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.ai.da.common.response;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
@@ -17,14 +16,14 @@ import java.io.Serializable;
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@ApiModel("响应结果")
|
||||
@Schema(description = "响应结果")
|
||||
public class Response<T> implements Serializable {
|
||||
|
||||
@ApiModelProperty("响应状态码 0:成功 -1:失败")
|
||||
@Schema(description = "响应状态码 0:成功 -1:失败")
|
||||
private int errCode;
|
||||
@ApiModelProperty("提示消息")
|
||||
@Schema(description = "提示消息")
|
||||
private String errMsg;
|
||||
@ApiModelProperty("数据结果")
|
||||
@Schema(description = "数据结果")
|
||||
private T data;
|
||||
|
||||
public static <T> Response<T> success() {
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.ai.da.common.response;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@Schema(description = "交易记录分页响应结果")
|
||||
public class TransactionPageResponse<T> extends PageBaseResponse<T> {
|
||||
|
||||
private BigDecimal totalAmount;
|
||||
}
|
||||
@@ -7,8 +7,8 @@ import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.web.access.AccessDeniedHandler;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
|
||||
@@ -7,9 +7,9 @@ import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
|
||||
@@ -5,7 +5,7 @@ import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import jakarta.annotation.Resource;
|
||||
|
||||
/**
|
||||
* @author: dangweijian
|
||||
|
||||
@@ -10,9 +10,9 @@ import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
|
||||
@@ -8,10 +8,10 @@ import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,84 +1,107 @@
|
||||
package com.ai.da.common.security.config;
|
||||
|
||||
import com.ai.da.common.security.*;
|
||||
import com.ai.da.common.security.filter.AuthenticationFilter;
|
||||
import com.ai.da.common.security.filter.UserAuthenticationProcessingFilter;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.builders.WebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||
import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
@EnableWebSecurity
|
||||
@EnableGlobalMethodSecurity(prePostEnabled = true)
|
||||
@EnableConfigurationProperties(SecurityProperties.class)
|
||||
public class SecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Resource
|
||||
private SecurityProperties securityProperties;
|
||||
@Resource
|
||||
private UserLoginSuccessHandler userLoginSuccessHandler;
|
||||
@Resource
|
||||
private UserLoginFailureHandler userLoginFailureHandler;
|
||||
@Resource
|
||||
private UserAuthAccessDeniedHandler userAuthAccessDeniedHandler;
|
||||
@Resource
|
||||
private UserAuthenticationEntryPointHandler userAuthenticationEntryPointHandler;
|
||||
@Resource
|
||||
private UserAuthenticationManager userAuthenticationManager;
|
||||
@Resource
|
||||
private UserAuthenticationProcessingFilter userAuthenticationProcessingFilter;
|
||||
|
||||
/**
|
||||
* 不通过注入spring管理 让Security来管理 这样自定义的Filter就不会走,.permitAll()才能起作用
|
||||
*/
|
||||
@Resource
|
||||
private AuthenticationFilter authenticationFilter;
|
||||
@Resource
|
||||
private UserPermissionEvaluator userPermissionEvaluator;
|
||||
|
||||
|
||||
@Override
|
||||
public AuthenticationManager authenticationManagerBean() throws Exception {
|
||||
return this.userAuthenticationManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity httpSecurity/*, WebSecurity web*/) throws Exception {
|
||||
// web.ignoring().antMatchers("/test/**");//禁止所有过滤器
|
||||
httpSecurity.cors().disable()//禁用 CSRF
|
||||
.authorizeRequests()//认证请求
|
||||
.antMatchers(securityProperties.getIgnorePaths()).permitAll()//忽略的请求
|
||||
.anyRequest().authenticated()//其余所有的请求都需要认证
|
||||
.and().headers().frameOptions().disable()// 防止iframe 造成跨域
|
||||
.and().exceptionHandling().authenticationEntryPoint(userAuthenticationEntryPointHandler)//未登录请求处理
|
||||
.accessDeniedHandler(userAuthAccessDeniedHandler)//无权限访问处理类 (此配置可以忽略,全局异常会先于Security框架处理异常,全局异常已特殊处理)
|
||||
.and().formLogin().loginProcessingUrl(securityProperties.getAuthApi())//指定认证接口
|
||||
.successHandler(userLoginSuccessHandler)//登录成功处理器
|
||||
.failureHandler(userLoginFailureHandler)//登录失败处理器
|
||||
.and().cors().and().csrf().disable();//允许跨域
|
||||
//自定义过滤器在登录时认证用户名、密码
|
||||
httpSecurity.addFilterAt(userAuthenticationProcessingFilter, UsernamePasswordAuthenticationFilter.class)
|
||||
.addFilterBefore(authenticationFilter, BasicAuthenticationFilter.class);
|
||||
//不创建session会话
|
||||
httpSecurity.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
|
||||
//取消头缓存控制
|
||||
httpSecurity.headers().cacheControl();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public DefaultWebSecurityExpressionHandler userSecurityExpressionHandler() {
|
||||
DefaultWebSecurityExpressionHandler handler = new DefaultWebSecurityExpressionHandler();
|
||||
handler.setPermissionEvaluator(userPermissionEvaluator);
|
||||
return handler;
|
||||
}
|
||||
}
|
||||
package com.ai.da.common.security.config;
|
||||
|
||||
import com.ai.da.common.security.*;
|
||||
import com.ai.da.common.security.filter.AuthenticationFilter;
|
||||
import com.ai.da.common.security.filter.UserAuthenticationProcessingFilter;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.config.Customizer;
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer;
|
||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
@EnableMethodSecurity(prePostEnabled = true)
|
||||
@EnableConfigurationProperties(SecurityProperties.class)
|
||||
public class SecurityConfig {
|
||||
|
||||
@Resource
|
||||
private SecurityProperties securityProperties;
|
||||
@Resource
|
||||
private UserLoginSuccessHandler userLoginSuccessHandler;
|
||||
@Resource
|
||||
private UserLoginFailureHandler userLoginFailureHandler;
|
||||
@Resource
|
||||
private UserAuthAccessDeniedHandler userAuthAccessDeniedHandler;
|
||||
@Resource
|
||||
private UserAuthenticationEntryPointHandler userAuthenticationEntryPointHandler;
|
||||
@Resource
|
||||
private UserAuthenticationManager userAuthenticationManager;
|
||||
@Resource
|
||||
private UserAuthenticationProcessingFilter userAuthenticationProcessingFilter;
|
||||
|
||||
/**
|
||||
* 不通过注入spring管理 让Security来管理 这样自定义的Filter就不会走,.permitAll()才能起作用
|
||||
*/
|
||||
@Resource
|
||||
private AuthenticationFilter authenticationFilter;
|
||||
@Resource
|
||||
private UserPermissionEvaluator userPermissionEvaluator;
|
||||
|
||||
@Bean
|
||||
public AuthenticationManager authenticationManager() throws Exception {
|
||||
return this.userAuthenticationManager;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
|
||||
httpSecurity
|
||||
.cors(Customizer.withDefaults())
|
||||
.authorizeHttpRequests(auth -> auth
|
||||
.requestMatchers(
|
||||
new AntPathRequestMatcher("/doc.html"),
|
||||
new AntPathRequestMatcher("/swagger-ui.html"),
|
||||
new AntPathRequestMatcher("/swagger-ui/**"),
|
||||
new AntPathRequestMatcher("/swagger-resources/**"),
|
||||
new AntPathRequestMatcher("/v2/api-docs"),
|
||||
new AntPathRequestMatcher("/v3/api-docs/**"),
|
||||
new AntPathRequestMatcher("/webjars/**")
|
||||
).permitAll()
|
||||
.requestMatchers(securityProperties.getIgnorePaths()).permitAll()
|
||||
.anyRequest().authenticated()
|
||||
)
|
||||
.headers(headers -> headers
|
||||
.frameOptions(HeadersConfigurer.FrameOptionsConfig::disable)
|
||||
.cacheControl(cache -> cache.disable())
|
||||
)
|
||||
.exceptionHandling(exception -> exception
|
||||
.authenticationEntryPoint(userAuthenticationEntryPointHandler)
|
||||
.accessDeniedHandler(userAuthAccessDeniedHandler)
|
||||
)
|
||||
.formLogin(form -> form
|
||||
.loginProcessingUrl(securityProperties.getAuthApi())
|
||||
.successHandler(userLoginSuccessHandler)
|
||||
.failureHandler(userLoginFailureHandler)
|
||||
)
|
||||
.csrf(AbstractHttpConfigurer::disable)
|
||||
.sessionManagement(session -> session
|
||||
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
|
||||
);
|
||||
|
||||
//自定义过滤器在登录时认证用户名、密码
|
||||
httpSecurity.addFilterAt(userAuthenticationProcessingFilter, UsernamePasswordAuthenticationFilter.class)
|
||||
.addFilterBefore(authenticationFilter, BasicAuthenticationFilter.class);
|
||||
|
||||
return httpSecurity.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public DefaultWebSecurityExpressionHandler userSecurityExpressionHandler() {
|
||||
DefaultWebSecurityExpressionHandler handler = new DefaultWebSecurityExpressionHandler();
|
||||
handler.setPermissionEvaluator(userPermissionEvaluator);
|
||||
return handler;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,133 +1,165 @@
|
||||
package com.ai.da.common.security.filter;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.ai.da.common.context.UserContext;
|
||||
import com.ai.da.common.security.config.SecurityProperties;
|
||||
import com.ai.da.common.security.jwt.JWTTokenHelper;
|
||||
import com.ai.da.common.utils.LocalCacheUtils;
|
||||
import com.ai.da.common.utils.MultiReadHttpServletRequest;
|
||||
import com.ai.da.common.utils.MultiReadHttpServletResponse;
|
||||
import com.ai.da.common.utils.RequestInfoUtil;
|
||||
import com.ai.da.model.vo.AuthPrincipalVo;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.util.StopWatch;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.security.sasl.AuthenticationException;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author: dangweijian
|
||||
* @description: 认证拦截器
|
||||
* @create: 2020-07-10 16:50
|
||||
**/
|
||||
@Slf4j
|
||||
@Configuration
|
||||
public class AuthenticationFilter extends OncePerRequestFilter {
|
||||
|
||||
@Resource
|
||||
private JWTTokenHelper jwtTokenHelper;
|
||||
@Resource
|
||||
private SecurityProperties properties;
|
||||
|
||||
private static final List<String> FILTER_URL =
|
||||
Arrays.asList("/favicon.ico", "/doc.html", "api/account/login", "api/account/preLogin", "api/account/sendEmail","api/account/noLoginRequired",
|
||||
"/webjars/", "/swagger-resources", "/v2/api-docs", "api/account/resetPwd",
|
||||
"/api/python/saveGeneratePicture", "/api/python/getLibraryByUserId",
|
||||
"/api/third/party/addUser","/api/third/party/addTrialUser", "/api/third/party/editUser", "/api/element/initDefaultSysFile",
|
||||
"/api/third/party/addNoLoginRequiredNew","/api/third/party/deleteNoLoginRequiredNew",
|
||||
"/api/third/party/existNoLoginRequired","/api/third/party/getRedirectUrl",
|
||||
"/api/python/flush","/api/account/healthy","/api/ali-pay/trade/notify","/api/paypal/ipn/back","/api/alipay-hk/trade/notify",
|
||||
"/api/portfolio/page", "/api/portfolio/detail", "/api/portfolio/commentPage", "/api/portfolio/viewsIncrease",
|
||||
"/api/account/designWorksRegister","/api/account/questionnaire","/api/stripe/trade/notify"
|
||||
);
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest httpServletRequest, @NonNull HttpServletResponse httpServletResponse, @NonNull FilterChain filterChain) throws ServletException, IOException {
|
||||
String requestURI = httpServletRequest.getRequestURI();
|
||||
|
||||
if (calculateUrl(requestURI) || hasAuthorizationToken(httpServletRequest)) {
|
||||
StopWatch stopWatch = new StopWatch();
|
||||
HttpServletRequest wrappedRequest = httpServletRequest;
|
||||
HttpServletResponse wrappedResponse = httpServletResponse;
|
||||
try {
|
||||
stopWatch.start();
|
||||
if ((httpServletRequest.getContentType() == null && httpServletRequest.getContentLength() > 0) || (httpServletRequest.getContentType() != null && !httpServletRequest.getContentType().contains("application/json"))) {
|
||||
extracted(wrappedRequest);
|
||||
filterChain.doFilter(wrappedRequest, wrappedResponse);
|
||||
} else {
|
||||
wrappedRequest = new MultiReadHttpServletRequest(httpServletRequest);
|
||||
wrappedResponse = new MultiReadHttpServletResponse(httpServletResponse);
|
||||
extracted(wrappedRequest);
|
||||
filterChain.doFilter(wrappedRequest, wrappedResponse);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
SecurityContextHolder.clearContext();
|
||||
throw e;
|
||||
} finally {
|
||||
stopWatch.stop();
|
||||
}
|
||||
} else {
|
||||
filterChain.doFilter(httpServletRequest, httpServletResponse);
|
||||
}
|
||||
}
|
||||
|
||||
private Boolean calculateUrl(String requestURI) {
|
||||
String filterUrl = FILTER_URL.stream().filter(url -> requestURI.contains(url)).findFirst().orElse(null);
|
||||
return null == filterUrl ? Boolean.TRUE : Boolean.FALSE;
|
||||
}
|
||||
|
||||
private boolean hasAuthorizationToken(HttpServletRequest request) {
|
||||
String authorizationHeader = request.getHeader("Authorization");
|
||||
return authorizationHeader != null && authorizationHeader.startsWith("Bearer");
|
||||
}
|
||||
|
||||
private void extracted(HttpServletRequest request) throws AuthenticationException {
|
||||
String jwtToken = request.getHeader(properties.getJwtTokenHeader());
|
||||
// log.debug("后台检查令牌:{}", jwtToken);
|
||||
|
||||
if (StrUtil.isBlank(jwtToken)) {
|
||||
String ipAddress = RequestInfoUtil.getIpAddress(request);
|
||||
log.info("本次请求的ip为 : " + ipAddress);
|
||||
throw new RuntimeException("请传入token!");
|
||||
}
|
||||
if(jwtToken.equals("Bearer-eyJhbGciOiJIUzUxMiJ9.eyJqdGkiOiIyIiwic3ViIjoie1wiaWRcIjoyLFwidXNlcm5hbWVcIjpcImxpcnNcIn0iLCJpYXQiOjE2NjU3NDEwODcsImlzcyI6IkRXSiIsImF1dGhvcml0aWVzIjoiW10iLCJleHAiOjE2NzQzODEwODd9.ShM9R_NNFD7oo1OvxrEgg7PFeWinOuAKkuInUCMQupp66s64Hhv8tN0Wwr83nIN4rHPqtn95wmd4msWcvaFYJA")){
|
||||
//写死 暂时放行
|
||||
return;
|
||||
}
|
||||
// 检查token
|
||||
boolean validate = jwtTokenHelper.validateToken(jwtToken);
|
||||
if (validate) {
|
||||
AuthPrincipalVo principal = jwtTokenHelper.parserToUser(jwtToken);
|
||||
if (principal == null) {
|
||||
throw new RuntimeException("TOKEN已过期,请重新登录!");
|
||||
}
|
||||
//先清空当前线程变量,防止上一个线程遗留
|
||||
UserContext.delete();
|
||||
//存取用户信息到缓存
|
||||
UserContext.setUserHolder(principal);
|
||||
//校验token
|
||||
String cacheToken = LocalCacheUtils.getTokenCache(String.valueOf(principal.getId()));
|
||||
|
||||
if(StringUtils.isEmpty(cacheToken)){
|
||||
throw new RuntimeException("TOKEN已过期,请重新登录!");
|
||||
}
|
||||
if(!cacheToken.equals(jwtToken) ){
|
||||
throw new RuntimeException("TOKEN已过期,请重新登录!");
|
||||
}
|
||||
// UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(null, null);
|
||||
// SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||
}
|
||||
}
|
||||
}
|
||||
package com.ai.da.common.security.filter;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.ai.da.common.config.exception.TokenMissingOrExpiredException;
|
||||
import com.ai.da.common.context.UserContext;
|
||||
import com.ai.da.common.security.config.SecurityProperties;
|
||||
import com.ai.da.common.security.jwt.JWTTokenHelper;
|
||||
import com.ai.da.common.utils.LocalCacheUtils;
|
||||
import com.ai.da.common.utils.RedisUtil;
|
||||
import com.ai.da.common.utils.MultiReadHttpServletRequest;
|
||||
import com.ai.da.common.utils.MultiReadHttpServletResponse;
|
||||
import com.ai.da.common.utils.RequestInfoUtil;
|
||||
import com.ai.da.model.vo.AuthPrincipalVo;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.util.StopWatch;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author: dangweijian
|
||||
* @description: 认证拦截器
|
||||
* @create: 2020-07-10 16:50
|
||||
**/
|
||||
@Slf4j
|
||||
@Configuration
|
||||
public class AuthenticationFilter extends OncePerRequestFilter {
|
||||
|
||||
@Resource
|
||||
private JWTTokenHelper jwtTokenHelper;
|
||||
@Resource
|
||||
private SecurityProperties properties;
|
||||
@Resource
|
||||
private RedisUtil redisUtil;
|
||||
|
||||
private static final List<String> FILTER_URL =
|
||||
Arrays.asList("/favicon.ico", "/doc.html", "/swagger-ui.html",
|
||||
"/swagger-resources", "/swagger-resources/", "/swagger-resources/configuration/ui", "/swagger-resources/configuration/security",
|
||||
"/webjars/", "/v2/api-docs", "/v3/api-docs", "/v3/api-docs/swagger-config",
|
||||
"/api/account/login", "/api/account/preLogin", "api/account/sendEmail","api/account/noLoginRequired",
|
||||
"/api/account/resetPwd",
|
||||
"/api/python/saveGeneratePicture", "/api/python/getLibraryByUserId",
|
||||
"/api/third/party/addUser","/api/third/party/addTrialUser", "/api/third/party/editUser", "/api/element/initDefaultSysFile",
|
||||
"/api/third/party/addNoLoginRequiredNew","/api/third/party/deleteNoLoginRequiredNew","/api/third/party/updateNoLoginRequiredNew",
|
||||
"/api/third/party/existNoLoginRequired","/api/third/party/getRedirectUrl",
|
||||
"/api/python/flush","/api/account/healthy","/api/ali-pay/trade/notify","/api/paypal/ipn/back","/api/alipay-hk/trade/notify",
|
||||
"/api/portfolio/page", "/api/portfolio/detail", "/api/portfolio/commentPage", "/api/portfolio/viewsIncrease",
|
||||
"/api/account/designWorksRegister","/api/account/questionnaire","/api/stripe/trade/notify",
|
||||
"/notification","/api/account/activateNewEmail","/api/third/party/auth/google_callback","/api/third/party/parseGoogleCredential","/api/third/party/receiveDesignResults","/api/third/party/parseWeChatCode","/api/third/party/receiveDesignParams"
|
||||
, "/api/account/schoolLogin", "/api/account/enterpriseLogin", "/api/account/organizationNameSearch",
|
||||
"/api/llm/stream",
|
||||
//GlobalAwardController
|
||||
"/api/global-award/uploads/pdf/init", "/api/global-award/uploads/pdf/chunk", "/api/global-award/uploads/pdf/complete", "/api/global-award/uploads/pdf/status",
|
||||
"/api/global-award/uploads/video/init", "/api/global-award/uploads/video/chunk", "/api/global-award/uploads/video/complete", "/api/global-award/uploads/video/status",
|
||||
"/api/global-award/contestants/save", "/api/global-award/contestants/by-email", "/api/global-award/checkEmail", "/api/global-award/checkCode","/api/global-award/contestants/export",
|
||||
"/api/global-award/contestants/export/files", "/api/global-award/contestants/count"
|
||||
);
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest httpServletRequest, @NonNull HttpServletResponse httpServletResponse, @NonNull FilterChain filterChain) throws ServletException, IOException {
|
||||
String requestURI = httpServletRequest.getRequestURI();
|
||||
|
||||
if (calculateUrl(requestURI)/* || hasAuthorizationToken(httpServletRequest)*/) {
|
||||
StopWatch stopWatch = new StopWatch();
|
||||
HttpServletRequest wrappedRequest = httpServletRequest;
|
||||
HttpServletResponse wrappedResponse = httpServletResponse;
|
||||
try {
|
||||
stopWatch.start();
|
||||
if ((httpServletRequest.getContentType() == null && httpServletRequest.getContentLength() > 0) || (httpServletRequest.getContentType() != null && !httpServletRequest.getContentType().contains("application/json"))) {
|
||||
extracted(wrappedRequest);
|
||||
filterChain.doFilter(wrappedRequest, wrappedResponse);
|
||||
} else {
|
||||
wrappedRequest = new MultiReadHttpServletRequest(httpServletRequest);
|
||||
wrappedResponse = new MultiReadHttpServletResponse(httpServletResponse);
|
||||
extracted(wrappedRequest);
|
||||
// excel导出使用原始response,不对响应做包装
|
||||
if (requestURI.equals("/api/account/exportAccountsToExcel")) {
|
||||
filterChain.doFilter(httpServletRequest, httpServletResponse); // 不包装
|
||||
} else {
|
||||
filterChain.doFilter(wrappedRequest, wrappedResponse);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
SecurityContextHolder.clearContext();
|
||||
throw e;
|
||||
} finally {
|
||||
stopWatch.stop();
|
||||
}
|
||||
} else {
|
||||
//先清空当前线程变量,防止上一个线程遗留
|
||||
UserContext.delete();
|
||||
filterChain.doFilter(httpServletRequest, httpServletResponse);
|
||||
}
|
||||
}
|
||||
|
||||
private Boolean calculateUrl(String requestURI) {
|
||||
String filterUrl = FILTER_URL.stream().filter(url -> requestURI.contains(url)).findFirst().orElse(null);
|
||||
return null == filterUrl ? Boolean.TRUE : Boolean.FALSE;
|
||||
}
|
||||
|
||||
private boolean hasAuthorizationToken(HttpServletRequest request) {
|
||||
String authorizationHeader = request.getHeader("Authorization");
|
||||
return authorizationHeader != null && authorizationHeader.startsWith("Bearer");
|
||||
}
|
||||
|
||||
private void extracted(HttpServletRequest request) {
|
||||
String jwtToken = request.getHeader(properties.getJwtTokenHeader());
|
||||
// log.debug("后台检查令牌:{}", jwtToken);
|
||||
|
||||
if (StrUtil.isBlank(jwtToken)) {
|
||||
String ipAddress = RequestInfoUtil.getIpAddress(request);
|
||||
log.info("本次请求的ip为 : " + ipAddress);
|
||||
// throw new RuntimeException("请传入token!");
|
||||
throw new TokenMissingOrExpiredException("请传入token!");
|
||||
}
|
||||
if(jwtToken.equals("Bearer-eyJhbGciOiJIUzUxMiJ9.eyJqdGkiOiIyIiwic3ViIjoie1wiaWRcIjoyLFwidXNlcm5hbWVcIjpcImxpcnNcIn0iLCJpYXQiOjE2NjU3NDEwODcsImlzcyI6IkRXSiIsImF1dGhvcml0aWVzIjoiW10iLCJleHAiOjE2NzQzODEwODd9.ShM9R_NNFD7oo1OvxrEgg7PFeWinOuAKkuInUCMQupp66s64Hhv8tN0Wwr83nIN4rHPqtn95wmd4msWcvaFYJA")){
|
||||
//写死 暂时放行
|
||||
return;
|
||||
}
|
||||
// 检查token
|
||||
boolean validate = jwtTokenHelper.validateToken(jwtToken);
|
||||
if (validate) {
|
||||
AuthPrincipalVo principal = jwtTokenHelper.parserToUser(jwtToken);
|
||||
if (principal == null) {
|
||||
// throw new RuntimeException("TOKEN已过期,请重新登录!");
|
||||
throw new TokenMissingOrExpiredException("TOKEN已过期,请重新登录!(token without userInfo)");
|
||||
}
|
||||
//先清空当前线程变量,防止上一个线程遗留
|
||||
UserContext.delete();
|
||||
//存取用户信息到缓存
|
||||
UserContext.setUserHolder(principal);
|
||||
// 校验 token:先查本地缓存,再查 Redis,保证服务重启后仍然有效
|
||||
String userIdStr = String.valueOf(principal.getId());
|
||||
String cacheToken = LocalCacheUtils.getTokenCache(userIdStr);
|
||||
|
||||
if (StringUtils.isEmpty(cacheToken)) {
|
||||
// 本地缓存为空时,尝试从 Redis 读取
|
||||
cacheToken = redisUtil.getLoginToken(principal.getId());
|
||||
if (StringUtils.isEmpty(cacheToken)) {
|
||||
// throw new RuntimeException("TOKEN已过期,请重新登录!");
|
||||
throw new TokenMissingOrExpiredException("TOKEN已过期,请重新登录!(cache & redis empty)");
|
||||
}
|
||||
// 将 Redis 中的 token 回填到本地缓存,减少后续 Redis 访问
|
||||
LocalCacheUtils.setTokenCache(userIdStr, cacheToken);
|
||||
}
|
||||
if(!cacheToken.equals(jwtToken) ){
|
||||
// throw new RuntimeException("TOKEN已过期,请重新登录!");
|
||||
throw new TokenMissingOrExpiredException("TOKEN已过期,请重新登录!(token not match local cache)");
|
||||
}
|
||||
// UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(null, null);
|
||||
// SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,69 +1,69 @@
|
||||
package com.ai.da.common.security.filter;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.ai.da.common.security.UserLoginSuccessHandler;
|
||||
import com.ai.da.common.security.config.SecurityProperties;
|
||||
import com.ai.da.common.utils.RedisCacheUtils;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.ai.da.common.security.UserAuthenticationManager;
|
||||
import com.ai.da.common.security.UserLoginFailureHandler;
|
||||
import com.ai.da.common.utils.MultiReadHttpServletRequest;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.security.authentication.AuthenticationServiceException;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* @author: dangweijian
|
||||
* @description: 用户认证过滤器
|
||||
* @create: 2020-07-10 15:58
|
||||
**/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class UserAuthenticationProcessingFilter extends AbstractAuthenticationProcessingFilter {
|
||||
|
||||
/**
|
||||
* @param securityProperties 配置(从配置中读取登录url)
|
||||
* @param authenticationManager 认证管理器
|
||||
* @param adminAuthenticationSuccessHandler 认证成功处理器
|
||||
* @param adminAuthenticationFailureHandler 认证失败处理器
|
||||
*/
|
||||
public UserAuthenticationProcessingFilter(SecurityProperties securityProperties, UserAuthenticationManager authenticationManager, UserLoginSuccessHandler adminAuthenticationSuccessHandler, UserLoginFailureHandler adminAuthenticationFailureHandler) {
|
||||
super(new AntPathRequestMatcher(securityProperties.getAuthApi(), HttpMethod.POST.name()));
|
||||
this.setAuthenticationManager(authenticationManager);
|
||||
this.setAuthenticationSuccessHandler(adminAuthenticationSuccessHandler);
|
||||
this.setAuthenticationFailureHandler(adminAuthenticationFailureHandler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
|
||||
if (request.getContentType() == null || !request.getContentType().contains("application/json")) {
|
||||
throw new AuthenticationServiceException("请求头类型不支持: " + request.getContentType());
|
||||
}
|
||||
UsernamePasswordAuthenticationToken authRequest;
|
||||
try {
|
||||
MultiReadHttpServletRequest wrappedRequest = new MultiReadHttpServletRequest(request);
|
||||
// 将前端传递的数据转换成jsonBean数据格式
|
||||
JSONObject jsonObject = JSONObject.parseObject(wrappedRequest.getBodyJsonStrByJson(wrappedRequest));
|
||||
String code = jsonObject.getString("code");
|
||||
String uuid = jsonObject.getString("uuid");
|
||||
if (StrUtil.isEmpty(code) || StrUtil.isEmpty(uuid) || !code.equals(RedisCacheUtils.get("code-key-" + uuid, String.class))) {
|
||||
throw new AuthenticationServiceException("验证码错误");
|
||||
}
|
||||
RedisCacheUtils.delete("code-key-" + uuid);
|
||||
authRequest = new UsernamePasswordAuthenticationToken(jsonObject.get("username"), jsonObject.get("password"), null);
|
||||
authRequest.setDetails(authenticationDetailsSource.buildDetails(wrappedRequest));
|
||||
} catch (Exception e) {
|
||||
throw new AuthenticationServiceException(e.getMessage());
|
||||
}
|
||||
return this.getAuthenticationManager().authenticate(authRequest);
|
||||
}
|
||||
}
|
||||
package com.ai.da.common.security.filter;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.ai.da.common.security.UserLoginSuccessHandler;
|
||||
import com.ai.da.common.security.config.SecurityProperties;
|
||||
import com.ai.da.common.utils.RedisCacheUtils;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.ai.da.common.security.UserAuthenticationManager;
|
||||
import com.ai.da.common.security.UserLoginFailureHandler;
|
||||
import com.ai.da.common.utils.MultiReadHttpServletRequest;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.security.authentication.AuthenticationServiceException;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* @author: dangweijian
|
||||
* @description: 用户认证过滤器
|
||||
* @create: 2020-07-10 15:58
|
||||
**/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class UserAuthenticationProcessingFilter extends AbstractAuthenticationProcessingFilter {
|
||||
|
||||
/**
|
||||
* @param securityProperties 配置(从配置中读取登录url)
|
||||
* @param authenticationManager 认证管理器
|
||||
* @param adminAuthenticationSuccessHandler 认证成功处理器
|
||||
* @param adminAuthenticationFailureHandler 认证失败处理器
|
||||
*/
|
||||
public UserAuthenticationProcessingFilter(SecurityProperties securityProperties, UserAuthenticationManager authenticationManager, UserLoginSuccessHandler adminAuthenticationSuccessHandler, UserLoginFailureHandler adminAuthenticationFailureHandler) {
|
||||
super(new AntPathRequestMatcher(securityProperties.getAuthApi(), HttpMethod.POST.name()));
|
||||
this.setAuthenticationManager(authenticationManager);
|
||||
this.setAuthenticationSuccessHandler(adminAuthenticationSuccessHandler);
|
||||
this.setAuthenticationFailureHandler(adminAuthenticationFailureHandler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
|
||||
if (request.getContentType() == null || !request.getContentType().contains("application/json")) {
|
||||
throw new AuthenticationServiceException("请求头类型不支持: " + request.getContentType());
|
||||
}
|
||||
UsernamePasswordAuthenticationToken authRequest;
|
||||
try {
|
||||
MultiReadHttpServletRequest wrappedRequest = new MultiReadHttpServletRequest(request);
|
||||
// 将前端传递的数据转换成jsonBean数据格式
|
||||
JSONObject jsonObject = JSONObject.parseObject(wrappedRequest.getBodyJsonStrByJson(wrappedRequest));
|
||||
String code = jsonObject.getString("code");
|
||||
String uuid = jsonObject.getString("uuid");
|
||||
if (StrUtil.isEmpty(code) || StrUtil.isEmpty(uuid) || !code.equals(RedisCacheUtils.get("code-key-" + uuid, String.class))) {
|
||||
throw new AuthenticationServiceException("验证码错误");
|
||||
}
|
||||
RedisCacheUtils.delete("code-key-" + uuid);
|
||||
authRequest = new UsernamePasswordAuthenticationToken(jsonObject.get("username"), jsonObject.get("password"), null);
|
||||
authRequest.setDetails(authenticationDetailsSource.buildDetails(wrappedRequest));
|
||||
} catch (Exception e) {
|
||||
throw new AuthenticationServiceException(e.getMessage());
|
||||
}
|
||||
return this.getAuthenticationManager().authenticate(authRequest);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,68 +1,108 @@
|
||||
package com.ai.da.common.security.jwt;
|
||||
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.ai.da.common.security.config.SecurityProperties;
|
||||
import com.ai.da.model.vo.AuthPrincipalVo;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import io.jsonwebtoken.Claims;
|
||||
import io.jsonwebtoken.Jwts;
|
||||
import io.jsonwebtoken.SignatureAlgorithm;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @author: dangweijian
|
||||
* @description: JWT工具
|
||||
* @create: 2020-07-09 09:27
|
||||
**/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class JWTTokenHelper {
|
||||
|
||||
@Resource
|
||||
private SecurityProperties securityProperties;
|
||||
|
||||
private static final String ISSUER = "DWJ";
|
||||
private static final String AUTHORITIES = "authorities";
|
||||
|
||||
public String createToken(AuthPrincipalVo principal) {
|
||||
String token = Jwts.builder()
|
||||
.setId(String.valueOf(principal.getId()))
|
||||
.setSubject(JSONObject.toJSONString(principal))
|
||||
.setIssuedAt(new Date())
|
||||
.setIssuer(ISSUER)
|
||||
.claim(AUTHORITIES, JSON.toJSONString(new ArrayList<>()))//自定义属性 权限
|
||||
.setExpiration(new Date(System.currentTimeMillis() + securityProperties.getJwtExpiration()))
|
||||
.signWith(SignatureAlgorithm.HS512, securityProperties.getJwtSecret())
|
||||
.compact();
|
||||
token = securityProperties.getJwtTokenPrefix() + token;
|
||||
return token;
|
||||
}
|
||||
|
||||
public boolean validateToken(String token) {
|
||||
Claims claims = parser(token);
|
||||
if (MapUtil.isEmpty(claims)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public AuthPrincipalVo parserToUser(String token) {
|
||||
String subject = parser(token).getSubject();
|
||||
if (StrUtil.isNotEmpty(subject)) {
|
||||
return JSONObject.parseObject(subject, AuthPrincipalVo.class);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Claims parser(String token) {
|
||||
token = token.replaceAll(securityProperties.getJwtTokenPrefix(), "");
|
||||
return Jwts.parser().setSigningKey(securityProperties.getJwtSecret()).parseClaimsJws(token).getBody();
|
||||
}
|
||||
}
|
||||
package com.ai.da.common.security.jwt;
|
||||
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.crypto.digest.DigestUtil;
|
||||
import com.ai.da.common.constant.CommonConstant;
|
||||
import com.ai.da.common.security.config.SecurityProperties;
|
||||
import com.ai.da.model.vo.AuthPrincipalVo;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import io.jsonwebtoken.Claims;
|
||||
import io.jsonwebtoken.Jwts;
|
||||
import io.jsonwebtoken.security.Keys;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import javax.crypto.SecretKey;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @author: dangweijian
|
||||
* @description: JWT工具
|
||||
* @create: 2020-07-09 09:27
|
||||
**/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class JWTTokenHelper {
|
||||
|
||||
@Resource
|
||||
private SecurityProperties securityProperties;
|
||||
|
||||
private static final String ISSUER = "DWJ";
|
||||
private static final String AUTHORITIES = "authorities";
|
||||
private static final String CHANGE_MAILBOX = "changeMailbox";
|
||||
|
||||
public String createToken(AuthPrincipalVo principal) {
|
||||
SecretKey key = buildSigningKey();
|
||||
String token = Jwts.builder()
|
||||
.id(String.valueOf(principal.getId()))
|
||||
.subject(JSONObject.toJSONString(principal))
|
||||
.issuedAt(new Date())
|
||||
.issuer(ISSUER)
|
||||
.claim(AUTHORITIES, JSON.toJSONString(new ArrayList<>()))//自定义属性 权限
|
||||
.expiration(new Date(System.currentTimeMillis() + securityProperties.getJwtExpiration()))
|
||||
.signWith(key)
|
||||
.compact();
|
||||
token = securityProperties.getJwtTokenPrefix() + token;
|
||||
return token;
|
||||
}
|
||||
|
||||
public boolean validateToken(String token) {
|
||||
Claims claims = parser(token);
|
||||
if (MapUtil.isEmpty(claims)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public AuthPrincipalVo parserToUser(String token) {
|
||||
String subject = parser(token).getSubject();
|
||||
if (StrUtil.isNotEmpty(subject)) {
|
||||
return JSONObject.parseObject(subject, AuthPrincipalVo.class);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Claims parser(String token) {
|
||||
token = token.replaceAll(securityProperties.getJwtTokenPrefix(), "");
|
||||
SecretKey key = buildSigningKey();
|
||||
return Jwts.parser()
|
||||
.verifyWith(key)
|
||||
.build()
|
||||
.parseSignedClaims(token)
|
||||
.getPayload();
|
||||
}
|
||||
|
||||
public String createToken(Long userId, String userEmail){
|
||||
SecretKey key = buildSigningKey();
|
||||
String token = Jwts.builder()
|
||||
.id(String.valueOf(userId))
|
||||
.subject(userEmail + "_" + userId)
|
||||
.issuedAt(new Date())
|
||||
.issuer(ISSUER)
|
||||
.claim(CHANGE_MAILBOX, JSON.toJSONString(new ArrayList<>()))//自定义属性 权限
|
||||
.expiration(new Date(System.currentTimeMillis() + CommonConstant.CHANGE_MAILBOX_LINK_VALIDITY))
|
||||
.signWith(key)
|
||||
.compact();
|
||||
return token;
|
||||
}
|
||||
|
||||
public String parseToEmailAndId(String token) {
|
||||
return parser(token).getSubject();
|
||||
}
|
||||
|
||||
/**
|
||||
* JWT 要求 HMAC-SHA 的密钥至少 256 bit,这里统一扩展/哈希密钥长度避免 WeakKeyException。
|
||||
*/
|
||||
private SecretKey buildSigningKey() {
|
||||
byte[] raw = securityProperties.getJwtSecret().getBytes(StandardCharsets.UTF_8);
|
||||
if (raw.length < 32) {
|
||||
raw = DigestUtil.sha256(raw);
|
||||
}
|
||||
return Keys.hmacShaKeyFor(raw);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
package com.ai.da.common.task;
|
||||
|
||||
import com.ai.da.common.utils.RedisUtil;
|
||||
import com.ai.da.mapper.primary.entity.Account;
|
||||
import com.ai.da.service.AccountService;
|
||||
import com.ai.da.service.SubscriptionPlanService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import jakarta.annotation.Resource;
|
||||
import java.util.List;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
@@ -13,19 +17,86 @@ public class AccountTask {
|
||||
|
||||
@Resource
|
||||
private AccountService accountService;
|
||||
@Resource
|
||||
private RedisUtil redisUtil;
|
||||
@Resource
|
||||
private SubscriptionPlanService subscriptionPlanService;
|
||||
|
||||
/** 每周日晚上刷新 年付用户、月付用户的积分 */
|
||||
@Scheduled(cron = "59 59 23 ? * SUN")
|
||||
// @Scheduled(cron = "59 59 23 * * ?")
|
||||
public void refreshCreditsMonthly(){
|
||||
log.info("每周日晚11:59:59刷新付费用户积分为 6000");
|
||||
accountService.refreshCreditsWeekly();
|
||||
/**
|
||||
* 每周日晚上刷新 年付用户、月付用户的积分
|
||||
* 替换为
|
||||
* 每个月月初只刷新教育子账号的积分
|
||||
*/
|
||||
// @Scheduled(cron = "0 25 14 * * ?")
|
||||
@Scheduled(cron = "0 0 0 1 * ?")
|
||||
public void refreshCreditsMonthly() {
|
||||
log.info("每月1号0点 重置教育版子账号为默认积分");
|
||||
accountService.refreshCreditsMonthly();
|
||||
}
|
||||
|
||||
// todo 多久执行一次?
|
||||
@Scheduled(cron = "0 */5 * * * *") // Run every 5 minutes
|
||||
public void getPaidUser(){
|
||||
// @Scheduled(cron = "0 */5 * * * *") // Run every 5 minutes
|
||||
public void getPaidUser() {
|
||||
// 获取code-create 表中 指定日期之后 订单状态为wc-processing的订单
|
||||
accountService.extendValidityForCC();
|
||||
}
|
||||
|
||||
// 每天凌晨0点执行一次 目前已没有角色类型为4的用户
|
||||
/*@Scheduled(cron = "0 0 0 * * ?")
|
||||
public void cancelActivityBenefits() {
|
||||
// 1、查询当前所有参与了活动且过期的用户
|
||||
List<Account> accountList = accountService.getExpiredUserBySystemUser(4);
|
||||
|
||||
// 2、将到期用户置为游客
|
||||
for (Account account : accountList) {
|
||||
log.info("参与活动的用户{} : {} 于 {} 账号有效期到期,置为游客", account.getId(), account.getUserEmail(), account.getValidEndTime());
|
||||
accountService.toVisitor(account);
|
||||
}
|
||||
}*/
|
||||
|
||||
// 每天检测正式用户到期情况,每天凌晨0点执行
|
||||
@Scheduled(cron = "0 0 0 * * ?")
|
||||
public void paidUserToVisitor() {
|
||||
// 1、查询当前已过期正式用户或试用用户
|
||||
List<Account> accountList = accountService.getExpiredUserBySystemUser(1);
|
||||
accountList.addAll(accountService.getExpiredUserBySystemUser(2));
|
||||
accountList.addAll(accountService.getExpiredUserBySystemUser(3));
|
||||
|
||||
// 2、将到期用户置为游客
|
||||
for (Account account : accountList) {
|
||||
log.info("用户{} : {} 于 {}账号有效期到期,置为游客", account.getId(), account.getUserEmail(), account.getValidEndTime());
|
||||
accountService.toVisitor(account);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* !!关闭此定时器!!不再从Code-Create上默认创建AiDA游客
|
||||
* 将Code-Create上注册的用户添加为AiDA的游客
|
||||
*/
|
||||
// @Scheduled(cron = "0 */5 * * * *") // Run every 5 minutes
|
||||
public void registerUserToVisitor() {
|
||||
accountService.registerUserToVisitor();
|
||||
}
|
||||
|
||||
@Scheduled(cron = "0 0 0 1 * ?")
|
||||
// 每月初刷新所有用户用户名剩余修改次数
|
||||
public void resetUsernameModifyTimes(){
|
||||
log.info("重置所有用户的用户名修改次数");
|
||||
redisUtil.batchDeleteKeysWithSamePrefix(RedisUtil.NICKNAME_MODIFY_TIMES);
|
||||
}
|
||||
|
||||
// @Scheduled(cron = "0 35 14 * * ?")
|
||||
@Scheduled(cron = "0 5 0 * * ?")
|
||||
public void checkEduAdminExpireStatus() {
|
||||
accountService.checkEduAdminExpireStatus();
|
||||
}
|
||||
|
||||
@Scheduled(cron = "0 5 0 * * ?")
|
||||
public void activeSubscriptionPlan() {
|
||||
subscriptionPlanService.activeSubscriptionPlan(null);
|
||||
}
|
||||
|
||||
@Scheduled(cron = "0 */5 * * * *") // Run every 5 minutes
|
||||
public void expireSubscription() {
|
||||
subscriptionPlanService.expireSubscription();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
package com.ai.da.common.task;
|
||||
|
||||
import com.ai.da.mapper.primary.entity.OrderInfo;
|
||||
import com.ai.da.common.enums.PayTypeEnum;
|
||||
import com.ai.da.service.AliPayService;
|
||||
import com.ai.da.service.OrderInfoService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class AliPayTask {
|
||||
|
||||
@Resource
|
||||
private OrderInfoService orderInfoService;
|
||||
|
||||
@Resource
|
||||
private AliPayService aliPayService;
|
||||
|
||||
/**
|
||||
* 从第0秒开始每隔30秒执行1次,查询创建超过5分钟,并且未支付的订单
|
||||
*/
|
||||
// @Scheduled(cron = "0/30 * * * * ?")
|
||||
public void orderConfirm(){
|
||||
|
||||
// log.info("Alipay orderConfirm 被执行......");
|
||||
|
||||
List<OrderInfo> orderInfoList = orderInfoService.getNoPayOrderByDuration(5, PayTypeEnum.ALIPAY.getType());
|
||||
|
||||
for (OrderInfo orderInfo : orderInfoList) {
|
||||
String orderNo = orderInfo.getOrderNo();
|
||||
log.warn("超时订单 ===> {}", orderNo);
|
||||
|
||||
//核实订单状态:调用支付宝查单接口
|
||||
aliPayService.checkOrderStatus(orderNo);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,21 +1,140 @@
|
||||
package com.ai.da.common.task;
|
||||
|
||||
import com.ai.da.common.config.exception.BusinessException;
|
||||
import com.ai.da.common.utils.DateUtil;
|
||||
import com.ai.da.mapper.primary.PoseTransformationMapper;
|
||||
import com.ai.da.mapper.primary.ToProductImageResultMapper;
|
||||
import com.ai.da.mapper.primary.entity.*;
|
||||
import com.ai.da.model.vo.PoseTransformationVO;
|
||||
import com.ai.da.service.*;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import io.netty.util.internal.StringUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import static com.ai.da.common.enums.CreditsEventsEnum.TO_PRODUCT_IMAGE;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class GenerateTask {
|
||||
private final APIGenerateService apiGenerateService;
|
||||
private final CreditsService creditsService;
|
||||
private final GenerateService generateService;
|
||||
private final PoseTransformationMapper poseTransformationMapper;
|
||||
private final ToProductImageResultMapper toProductImageResultMapper;
|
||||
|
||||
// @Scheduled(cron = "0 0 */1 * * ?")
|
||||
public void generateScheduled(){
|
||||
log.info("测试定时器:generate");
|
||||
|
||||
try{
|
||||
/*
|
||||
* 对于使用了第三方api的允许异步获得结果的生成功能,可能在第三方接口的结果Ready时没有及时存储结果,导致第三方链接失效
|
||||
* 万相 24h失效,
|
||||
* flux 10mins失效 (使用了flux接口的功能 ToProductImage || Relight, Pattern这里不做补偿)
|
||||
* 故这里通过定时任务做补偿
|
||||
* flux五分钟查询一次,万相1小时查询一次
|
||||
*/
|
||||
@Scheduled(cron = "0 */4 * * * ?")
|
||||
public void fluxCompensationMechanism(){
|
||||
// 1、查所有 任务还没成功、还没失败,正在等待或者执行中的任务id有哪些
|
||||
// (由于获取结果的polling_url在redis中只存一天,大部分结果超过一天之后就无法再找到任务,小部分可以通过公共路径查到结果)
|
||||
List<APIGenerate> apiGenerates = apiGenerateService.getPendingTaskByStatus("flux");
|
||||
if (apiGenerates != null && !apiGenerates.isEmpty()){
|
||||
for (APIGenerate apiGenerate : apiGenerates){
|
||||
String taskId = apiGenerate.getTaskId();
|
||||
// 1. 根据taskId查toProductImageResult, 判断当前任务状态与超时状态
|
||||
ToProductImageResult toProductImageResult = toProductImageResultMapper.selectOne(new QueryWrapper<ToProductImageResult>().eq("task_id", taskId));
|
||||
if (Objects.nonNull(toProductImageResult) && "Pending".equals(toProductImageResult.getStatus())){
|
||||
// 判断当前任务的超时状态
|
||||
if (!DateUtil.isMoreThanOneDayApart(toProductImageResult.getCreateTime())){
|
||||
// 1. 未超时,获取当前任务结果
|
||||
String fileName = toProductImageResult.getResultType().equals(TO_PRODUCT_IMAGE.getName()) ? "product_image" : "relight_image";
|
||||
String objectName = apiGenerate.getAccountId() + "/" + fileName +"/" + taskId + ".png";
|
||||
String fluxResult = generateService.getFluxResult(taskId, objectName);
|
||||
|
||||
}catch(Exception e){
|
||||
// 2. 成功,获取结果,下载图片,上传至minio,更新toProductImageResult表
|
||||
if (StringUtil.isNullOrEmpty(fluxResult) || fluxResult.equals("Fail")){
|
||||
toProductImageResult.setStatus("Fail");
|
||||
toProductImageResultMapper.updateById(toProductImageResult);
|
||||
|
||||
apiGenerate.setStatus("Fail");
|
||||
apiGenerate.setUpdateTime(LocalDateTime.now());
|
||||
apiGenerateService.updateById(apiGenerate);
|
||||
} else if (!fluxResult.equals("Pending")){
|
||||
if (StringUtil.isNullOrEmpty(toProductImageResult.getUrl())){
|
||||
toProductImageResult.setStatus("Success");
|
||||
toProductImageResult.setUrl(fluxResult);
|
||||
toProductImageResultMapper.updateById(toProductImageResult);
|
||||
|
||||
apiGenerate.setStatus("Success");
|
||||
apiGenerate.setUpdateTime(LocalDateTime.now());
|
||||
apiGenerateService.updateById(apiGenerate);
|
||||
}
|
||||
// 扣积分
|
||||
Boolean flag = creditsService.taskCreditsDeduction(apiGenerate.getAccountId(), taskId);
|
||||
if (flag) creditsService.updateChangedCredits(String.valueOf(apiGenerate.getAccountId()), taskId);
|
||||
}
|
||||
} else {
|
||||
// 超时,设置状态为失败
|
||||
toProductImageResult.setStatus("Fail");
|
||||
toProductImageResultMapper.updateById(toProductImageResult);
|
||||
|
||||
apiGenerate.setStatus("Fail");
|
||||
apiGenerate.setUpdateTime(LocalDateTime.now());
|
||||
apiGenerateService.updateById(apiGenerate);
|
||||
}
|
||||
// 将积分暂扣区的积分移除
|
||||
if (toProductImageResult.getStatus().equals("Fail")){
|
||||
creditsService.deleteCreditsDeduction(apiGenerate.getAccountId(), taskId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 万相 -> pose transformation 补偿 当前任务执行完后,5分钟再执行一次(不会出现任务重叠的情况)
|
||||
@Scheduled(fixedDelay = 5 * 60 * 1000)
|
||||
public void wxCompensationMechanism(){
|
||||
List<APIGenerate> apiGenerates = apiGenerateService.getPendingTaskByStatus("wx");
|
||||
if (apiGenerates != null && !apiGenerates.isEmpty()){
|
||||
log.info("=====万相补偿获取结果开始=====");
|
||||
for (APIGenerate apiGenerate : apiGenerates){
|
||||
String taskId = apiGenerate.getTaskId();
|
||||
PoseTransformation poseTransformation = poseTransformationMapper.selectOne(new QueryWrapper<PoseTransformation>().eq("unique_id", taskId));
|
||||
if (Objects.nonNull(poseTransformation) && ("Pending".equals(poseTransformation.getTaskStatus()) || "Executing".equals(poseTransformation.getTaskStatus()))){
|
||||
// 判断当前任务的超时状态
|
||||
if (!DateUtil.isMoreThanOneDayApart(poseTransformation.getCreateTime())){
|
||||
try {
|
||||
// 方法中已经完成了pose_transformation和api_generate表的更新,不用额外做处理
|
||||
PoseTransformationVO animateResult = generateService.getAnimateResult(taskId);
|
||||
if (animateResult.getStatus().equals("Success")){
|
||||
log.info("补偿获取结果成功,发送系统消息");
|
||||
}
|
||||
} catch (BusinessException e){
|
||||
log.warn("万相 animation 生成失败,原因:{}", e.getMessage());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
poseTransformation.setTaskStatus("Fail");
|
||||
poseTransformation.setUpdateTime(LocalDateTime.now());
|
||||
poseTransformationMapper.updateById(poseTransformation);
|
||||
|
||||
apiGenerate.setStatus("Fail");
|
||||
apiGenerate.setUpdateTime(LocalDateTime.now());
|
||||
apiGenerateService.updateById(apiGenerate);
|
||||
generateService.sendSysMsgForPT(poseTransformation);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
216
src/main/java/com/ai/da/common/task/MoveDataScheduler.java
Normal file
216
src/main/java/com/ai/da/common/task/MoveDataScheduler.java
Normal file
@@ -0,0 +1,216 @@
|
||||
//package com.ai.da.common.task;
|
||||
//
|
||||
//import com.ai.da.common.utils.CopyUtil;
|
||||
//import com.ai.da.common.utils.ExcelReader;
|
||||
//import com.ai.da.common.utils.MinioUtil;
|
||||
//import com.ai.da.common.utils.SendEmailUtil;
|
||||
//import com.ai.da.mapper.primary.*;
|
||||
//import com.ai.da.mapper.primary.entity.*;
|
||||
//import com.ai.da.mapper.secondary.AttributeRetrievalMapper;
|
||||
//import com.ai.da.mapper.third.*;
|
||||
//import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
//import org.apache.commons.lang3.StringUtils;
|
||||
//import org.apache.poi.ss.usermodel.Row;
|
||||
//import org.apache.poi.ss.usermodel.Sheet;
|
||||
//import org.apache.poi.ss.usermodel.Workbook;
|
||||
//import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
||||
//import org.springframework.stereotype.Component;
|
||||
//import org.springframework.util.CollectionUtils;
|
||||
//
|
||||
//import jakarta.annotation.PostConstruct;
|
||||
//import jakarta.annotation.Resource;
|
||||
//import java.io.FileOutputStream;
|
||||
//import java.io.IOException;
|
||||
//import java.nio.file.Files;
|
||||
//import java.nio.file.Paths;
|
||||
//import java.time.LocalDate;
|
||||
//import java.time.format.DateTimeFormatter;
|
||||
//import java.util.List;
|
||||
//import java.util.concurrent.ExecutorService;
|
||||
//import java.util.concurrent.Executors;
|
||||
//import java.util.concurrent.TimeUnit;
|
||||
//import java.util.stream.Collectors;
|
||||
//
|
||||
//@Component
|
||||
//public class MoveDataScheduler {
|
||||
//
|
||||
// @PostConstruct
|
||||
// public void test() {
|
||||
// moveData();
|
||||
// }
|
||||
//
|
||||
// @Resource
|
||||
// private UserLikeGroupMapper userLikeGroupMapper;
|
||||
// @Resource
|
||||
// private UserLikeGroupZSMapper userLikeGroupZSMapper;
|
||||
// @Resource
|
||||
// private UserLikeMapper userLikeMapper;
|
||||
// @Resource
|
||||
// private UserLikeZSMapper userLikeZSMapper;
|
||||
// @Resource
|
||||
// private CollectionElementMapper collectionElementMapper;
|
||||
// @Resource
|
||||
// private CollectionElementZSMapper collectionElementZSMapper;
|
||||
//
|
||||
// @Resource
|
||||
// private CollectionMapper collectionMapper;
|
||||
//
|
||||
// @Resource
|
||||
// private CollectionZSMapper collectionZSMapper;
|
||||
//
|
||||
// @Resource
|
||||
// private DesignMapper designMapper;
|
||||
//
|
||||
// @Resource
|
||||
// private DesignZSMapper designZSMapper;
|
||||
//
|
||||
// @Resource
|
||||
// private DesignItemMapper designItemMapper;
|
||||
//
|
||||
// @Resource
|
||||
// private DesignItemZSMapper designItemZSMapper;
|
||||
//
|
||||
// @Resource
|
||||
// private TDesignPythonOutfitMapper designPythonOutfitMapper;
|
||||
//
|
||||
// @Resource
|
||||
// private TDesignPythonOutfitZSMapper designPythonOutfitZSMapper;
|
||||
//
|
||||
// @Resource
|
||||
// private TDesignPythonOutfitDetailMapper designPythonOutfitDetailMapper;
|
||||
//
|
||||
// @Resource
|
||||
// private TDesignPythonOutfitDetailZSMapper designPythonOutfitDetailZSMapper;
|
||||
//
|
||||
// @Resource
|
||||
// private DesignItemDetailMapper designItemDetailMapper;
|
||||
// @Resource
|
||||
// private DesignItemZSDetailMapper designItemDetailZSMapper;
|
||||
//
|
||||
// @Resource
|
||||
// private DesignItemDetailPrintMapper designItemDetailPrintMapper;
|
||||
//
|
||||
// @Resource
|
||||
// private DesignItemDetailPrintZSMapper designItemDetailPrintZSMapper;
|
||||
//
|
||||
//
|
||||
// private void moveData() {
|
||||
// // 查询用户所有history记录
|
||||
// QueryWrapper<UserLikeGroup> userLikeGroupQueryWrapper = new QueryWrapper<>();
|
||||
// userLikeGroupQueryWrapper.lambda().eq(UserLikeGroup::getAccountId, 11411);
|
||||
// List<UserLikeGroup> userLikeGroupList = userLikeGroupMapper.selectList(userLikeGroupQueryWrapper);
|
||||
//
|
||||
// for (UserLikeGroup userLikeGroup : userLikeGroupList) {
|
||||
//
|
||||
// com.ai.da.mapper.third.entity.UserLikeGroup userLikeGroupZS = CopyUtil.copyObject(userLikeGroup, com.ai.da.mapper.third.entity.UserLikeGroup.class);
|
||||
// userLikeGroupZS.setAccountId(11420L);
|
||||
// userLikeGroupZS.setId(null);
|
||||
// Collection collection = collectionMapper.selectById(userLikeGroup.getCollectionId());
|
||||
// com.ai.da.mapper.third.entity.Collection collectionZS = CopyUtil.copyObject(collection, com.ai.da.mapper.third.entity.Collection.class);
|
||||
// collectionZS.setAccountId(11420L);
|
||||
// collectionZS.setId(null);
|
||||
//
|
||||
// if (collection.getMoodTemplateId() != null) {
|
||||
// CollectionElement element = collectionElementMapper.selectById(collection.getMoodTemplateId());
|
||||
// com.ai.da.mapper.third.entity.CollectionElement collectionElementZS = CopyUtil.copyObject(element, com.ai.da.mapper.third.entity.CollectionElement.class);
|
||||
// collectionElementZS.setAccountId(11420L);
|
||||
// collectionElementZS.setId(null);
|
||||
// collectionElementZSMapper.insert(collectionElementZS);
|
||||
// collectionZS.setMoodTemplateId(String.valueOf(collectionElementZS.getId()));
|
||||
// }
|
||||
// collectionZSMapper.insert(collectionZS);
|
||||
// userLikeGroupZS.setCollectionId(collectionZS.getId());
|
||||
// userLikeGroupZSMapper.insert(userLikeGroupZS);
|
||||
//
|
||||
// // 生成元素
|
||||
// QueryWrapper<CollectionElement> collectionElementQueryWrapper = new QueryWrapper<>();
|
||||
// collectionElementQueryWrapper.lambda().eq(CollectionElement::getCollectionId, userLikeGroup.getCollectionId());
|
||||
// List<CollectionElement> collectionElements = collectionElementMapper.selectList(collectionElementQueryWrapper);
|
||||
//
|
||||
// for (CollectionElement collectionElement : collectionElements) {
|
||||
// com.ai.da.mapper.third.entity.CollectionElement collectionElementZS = CopyUtil.copyObject(collectionElement, com.ai.da.mapper.third.entity.CollectionElement.class);
|
||||
// collectionElementZS.setId(null);
|
||||
// collectionElementZS.setAccountId(11420L);
|
||||
// collectionElementZS.setCollectionId(collectionZS.getId());
|
||||
// collectionElementZSMapper.insert(collectionElementZS);
|
||||
// }
|
||||
//
|
||||
// // 查询记录关联
|
||||
// QueryWrapper<UserLike> userLikeQueryWrapper = new QueryWrapper<>();
|
||||
// userLikeQueryWrapper.lambda().eq(UserLike::getUserLikeGroupId, userLikeGroup.getId());
|
||||
// List<UserLike> userLikes = userLikeMapper.selectList(userLikeQueryWrapper);
|
||||
// for (UserLike userLike : userLikes) {
|
||||
// Design design = designMapper.selectById(userLike.getDesignId());
|
||||
// com.ai.da.mapper.third.entity.Design designZS = CopyUtil.copyObject(design, com.ai.da.mapper.third.entity.Design.class);
|
||||
// designZS.setId(null);
|
||||
// designZS.setAccountId(11420L);
|
||||
// designZS.setCollectionId(collectionZS.getId());
|
||||
// designZSMapper.insert(designZS);
|
||||
//
|
||||
// com.ai.da.mapper.third.entity.UserLike userLikeZS = CopyUtil.copyObject(userLike, com.ai.da.mapper.third.entity.UserLike.class);
|
||||
//// Design design = designMapper.selectById(userLike.getDesignId());
|
||||
// userLikeZS.setId(null);
|
||||
// DesignItem designItem = designItemMapper.selectById(userLike.getDesignItemId());
|
||||
// com.ai.da.mapper.third.entity.DesignItem designItemZS = CopyUtil.copyObject(designItem, com.ai.da.mapper.third.entity.DesignItem.class);
|
||||
// designItemZS.setId(null);
|
||||
// designItemZS.setAccountId(11420L);
|
||||
// designItemZS.setDesignId(designZS.getId());
|
||||
// designItemZSMapper.insert(designItemZS);
|
||||
//
|
||||
// QueryWrapper<DesignItemDetail> designItemDetailQueryWrapper = new QueryWrapper<>();
|
||||
// designItemDetailQueryWrapper.lambda().eq(DesignItemDetail::getDesignItemId, designItem.getId());
|
||||
// List<DesignItemDetail> designItemDetails = designItemDetailMapper.selectList(designItemDetailQueryWrapper);
|
||||
// for (DesignItemDetail designItemDetail : designItemDetails) {
|
||||
// com.ai.da.mapper.third.entity.DesignItemDetail designItemDetailZS = CopyUtil.copyObject(designItemDetail, com.ai.da.mapper.third.entity.DesignItemDetail.class);
|
||||
// designItemDetailZS.setId(null);
|
||||
// designItemDetailZS.setDesignItemId(designItemZS.getId());
|
||||
// designItemDetailZS.setAccountId(11420L);
|
||||
// designItemDetailZS.setDesignId(designZS.getId());
|
||||
// designItemDetailZSMapper.insert(designItemDetailZS);
|
||||
//
|
||||
// QueryWrapper<DesignItemDetailPrint> designItemDetailPrintQueryWrapper = new QueryWrapper<>();
|
||||
// designItemDetailPrintQueryWrapper.lambda().eq(DesignItemDetailPrint::getDesignItemDetailId, designItemDetail.getId());
|
||||
// List<DesignItemDetailPrint> designItemDetailPrintList = designItemDetailPrintMapper.selectList(designItemDetailPrintQueryWrapper);
|
||||
// if (!CollectionUtils.isEmpty(designItemDetailPrintList)) {
|
||||
// for (DesignItemDetailPrint designItemDetailPrint : designItemDetailPrintList) {
|
||||
// com.ai.da.mapper.third.entity.DesignItemDetailPrint designItemDetailPrintZS = CopyUtil.copyObject(designItemDetailPrint, com.ai.da.mapper.third.entity.DesignItemDetailPrint.class);
|
||||
// designItemDetailPrintZS.setId(null);
|
||||
// designItemDetailPrintZS.setDesignItemDetailId(designItemDetailZS.getId());
|
||||
// designItemDetailPrintZSMapper.insert(designItemDetailPrintZS);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// userLikeZS.setDesignItemId(designItemZS.getId());
|
||||
// TDesignPythonOutfit tDesignPythonOutfit = designPythonOutfitMapper.selectById(userLike.getDesignOutfitId());
|
||||
// com.ai.da.mapper.third.entity.TDesignPythonOutfit tDesignPythonOutfitZS = CopyUtil.copyObject(tDesignPythonOutfit, com.ai.da.mapper.third.entity.TDesignPythonOutfit.class);
|
||||
// tDesignPythonOutfitZS.setId(null);
|
||||
// tDesignPythonOutfitZS.setDesignItemId(designItemZS.getId());
|
||||
// tDesignPythonOutfitZS.setCollectionId(collectionZS.getId());
|
||||
// tDesignPythonOutfitZS.setUserId(11420L);
|
||||
// tDesignPythonOutfitZS.setDesignId(designZS.getId());
|
||||
// designPythonOutfitZSMapper.insert(tDesignPythonOutfitZS);
|
||||
//
|
||||
// userLikeZS.setUserLikeGroupId(userLikeGroupZS.getId());
|
||||
// userLikeZS.setDesignId(designZS.getId());
|
||||
// userLikeZS.setDesignOutfitId(tDesignPythonOutfitZS.getId());
|
||||
// userLikeZSMapper.insert(userLikeZS);
|
||||
//
|
||||
// QueryWrapper<TDesignPythonOutfitDetail> designPythonOutfitDetailQueryWrapper = new QueryWrapper<>();
|
||||
// designPythonOutfitDetailQueryWrapper.lambda().eq(TDesignPythonOutfitDetail::getDesignPythonOutfitId, tDesignPythonOutfit.getId());
|
||||
// List<TDesignPythonOutfitDetail> tDesignPythonOutfitDetails = designPythonOutfitDetailMapper.selectList(designPythonOutfitDetailQueryWrapper);
|
||||
// for (TDesignPythonOutfitDetail tDesignPythonOutfitDetail : tDesignPythonOutfitDetails) {
|
||||
// com.ai.da.mapper.third.entity.TDesignPythonOutfitDetail tDesignPythonOutfitDetailZS = CopyUtil.copyObject(tDesignPythonOutfitDetail, com.ai.da.mapper.third.entity.TDesignPythonOutfitDetail.class);
|
||||
// tDesignPythonOutfitDetailZS.setDesignPythonOutfitId(tDesignPythonOutfitZS.getId());
|
||||
// tDesignPythonOutfitDetailZS.setId(null);
|
||||
// tDesignPythonOutfitDetailZS.setDesignId(designZS.getId());
|
||||
// designPythonOutfitDetailZSMapper.insert(tDesignPythonOutfitDetailZS);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//
|
||||
// }
|
||||
//
|
||||
//
|
||||
//}
|
||||
129
src/main/java/com/ai/da/common/task/PaymentTask.java
Normal file
129
src/main/java/com/ai/da/common/task/PaymentTask.java
Normal file
@@ -0,0 +1,129 @@
|
||||
package com.ai.da.common.task;
|
||||
|
||||
import com.ai.da.common.enums.PayTypeEnum;
|
||||
import com.ai.da.mapper.primary.entity.OrderInfo;
|
||||
import com.ai.da.service.*;
|
||||
import com.paypal.http.exceptions.SerializeException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class PaymentTask {
|
||||
|
||||
@Resource
|
||||
private OrderInfoService orderInfoService;
|
||||
|
||||
@Resource
|
||||
private StripeService stripeService;
|
||||
|
||||
@Resource
|
||||
private AffiliateService affiliateService;
|
||||
|
||||
// 考虑删除该定时任务(原因:之后的订单列允许用户查看发票,发票未过期时仍可以支付,所以不需要手动使订单过期)
|
||||
// @Scheduled(cron = "0/30 * * * * ?")
|
||||
public void orderConfirmForStripe() throws SerializeException {
|
||||
|
||||
// 查看超过30分钟以上仍未支付的订单 置为超时订单
|
||||
List<OrderInfo> orderInfoList = orderInfoService.getNoPayOrderByDuration(30, PayTypeEnum.STRIPE.getType());
|
||||
|
||||
for (OrderInfo orderInfo : orderInfoList) {
|
||||
String orderNo = orderInfo.getOrderNo();
|
||||
log.warn("超时订单 ===> {}", orderNo);
|
||||
|
||||
//核实订单状态:调用支付宝查单接口
|
||||
stripeService.checkOrderStatus(orderNo);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Resource
|
||||
private PayPalCheckoutService payPalCheckoutService;
|
||||
|
||||
// @Scheduled(cron = "0/30 * * * * ?")
|
||||
public void orderConfirmForPaypal() throws SerializeException {
|
||||
|
||||
// log.info("PayPal orderConfirm 被执行......");
|
||||
|
||||
List<OrderInfo> orderInfoList = orderInfoService.getNoPayOrderByDuration(30, PayTypeEnum.PAYPAL.getType());
|
||||
|
||||
for (OrderInfo orderInfo : orderInfoList) {
|
||||
String orderNo = orderInfo.getOrderNo();
|
||||
log.warn("超时订单 ===> {}", orderNo);
|
||||
|
||||
//核实订单状态:调用支付宝查单接口
|
||||
payPalCheckoutService.checkOrderStatus(orderNo);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Resource
|
||||
private AliPayService aliPayService;
|
||||
|
||||
/**
|
||||
* 从第0秒开始每隔30秒执行1次,查询创建超过5分钟,并且未支付的订单
|
||||
*/
|
||||
// @Scheduled(cron = "0/30 * * * * ?")
|
||||
public void orderConfirmForAlipay(){
|
||||
/*
|
||||
log.info("Alipay orderConfirm 被执行......");
|
||||
|
||||
List<OrderInfo> orderInfoList = orderInfoService.getNoPayOrderByDuration(5, PayTypeEnum.ALIPAY.getType());
|
||||
|
||||
for (OrderInfo orderInfo : orderInfoList) {
|
||||
String orderNo = orderInfo.getOrderNo();
|
||||
log.warn("超时订单 ===> {}", orderNo);
|
||||
|
||||
//核实订单状态:调用支付宝查单接口
|
||||
aliPayService.checkOrderStatus(orderNo);
|
||||
}*/
|
||||
}
|
||||
|
||||
// !!关闭此定时器,改为提前三天站内信提醒!!
|
||||
// 提前7天向用户发送提醒邮件,每天早上8点执行
|
||||
// @Scheduled(cron = "0 0 8 * * ?")
|
||||
// public void subscriptionReminder(){
|
||||
// stripeService.subscriptionReminder();
|
||||
// }
|
||||
|
||||
|
||||
// 如果有订阅已创建,但是没有发邮件通知的,需要主动获取回调信息并向用户发送邮件
|
||||
public void checkSubscriptionPayment(){
|
||||
//
|
||||
}
|
||||
|
||||
// @Scheduled(cron = "0 */5 * * * *") // Run every 5 minutes
|
||||
public void updateAffiliateInfoWithPayment(){
|
||||
// log.info("佣金计算定时器");
|
||||
affiliateService.updateAffiliateInfoWithPayment();
|
||||
}
|
||||
|
||||
// 定时同步(每分钟一次)
|
||||
@Scheduled(fixedRate = 60000)
|
||||
public void syncLinkViewCountToDB(){
|
||||
affiliateService.syncLinkViewCountToDB();
|
||||
}
|
||||
|
||||
// @Scheduled(cron = "0 0 8 28-31 * ?")
|
||||
public void commissionSummaryReminder(){
|
||||
// 每个月末的最后一天的早上八点执行
|
||||
LocalDate today = LocalDate.now();
|
||||
// 判断是否为月底
|
||||
if (today.plusDays(1).getDayOfMonth() == 1) {
|
||||
log.info("今天是月底,执行佣金结算提醒任务!");
|
||||
affiliateService.commissionCalculation(null, null);
|
||||
}
|
||||
}
|
||||
|
||||
@Scheduled(cron = "0 */5 * * * *") // Run every 5 minutes
|
||||
public void calcCouponsCommission(){
|
||||
// log.info("优惠券佣金计算定时器");
|
||||
affiliateService.calcCouponsCommission();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
package com.ai.da.common.task;
|
||||
|
||||
import com.ai.da.common.enums.PayTypeEnum;
|
||||
import com.ai.da.mapper.primary.entity.OrderInfo;
|
||||
import com.ai.da.service.OrderInfoService;
|
||||
import com.ai.da.service.PayPalCheckoutService;
|
||||
import com.paypal.http.exceptions.SerializeException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class PaypalTask {
|
||||
|
||||
@Resource
|
||||
private OrderInfoService orderInfoService;
|
||||
|
||||
@Resource
|
||||
private PayPalCheckoutService payPalCheckoutService;
|
||||
|
||||
@Scheduled(cron = "0/30 * * * * ?")
|
||||
public void orderConfirm() throws SerializeException {
|
||||
|
||||
// log.info("PayPal orderConfirm 被执行......");
|
||||
|
||||
List<OrderInfo> orderInfoList = orderInfoService.getNoPayOrderByDuration(30, PayTypeEnum.PAYPAL.getType());
|
||||
|
||||
for (OrderInfo orderInfo : orderInfoList) {
|
||||
String orderNo = orderInfo.getOrderNo();
|
||||
log.warn("超时订单 ===> {}", orderNo);
|
||||
|
||||
//核实订单状态:调用支付宝查单接口
|
||||
payPalCheckoutService.checkOrderStatus(orderNo);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
package com.ai.da.common.task;
|
||||
|
||||
import com.ai.da.common.enums.PayTypeEnum;
|
||||
import com.ai.da.mapper.primary.entity.OrderInfo;
|
||||
import com.ai.da.service.OrderInfoService;
|
||||
import com.ai.da.service.StripeService;
|
||||
import com.paypal.http.exceptions.SerializeException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class StripeTask {
|
||||
|
||||
@Resource
|
||||
private OrderInfoService orderInfoService;
|
||||
|
||||
@Resource
|
||||
private StripeService stripeService;
|
||||
|
||||
@Scheduled(cron = "0/30 * * * * ?")
|
||||
public void orderConfirm() throws SerializeException {
|
||||
|
||||
// 查看超过30分钟以上仍未支付的订单 置为超时订单
|
||||
List<OrderInfo> orderInfoList = orderInfoService.getNoPayOrderByDuration(30, PayTypeEnum.STRIPE.getType());
|
||||
|
||||
for (OrderInfo orderInfo : orderInfoList) {
|
||||
String orderNo = orderInfo.getOrderNo();
|
||||
log.warn("超时订单 ===> {}", orderNo);
|
||||
|
||||
//核实订单状态:调用支付宝查单接口
|
||||
stripeService.checkOrderStatus(orderNo);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
package com.ai.da.common.task;
|
||||
|
||||
import com.ai.da.common.utils.SendEmailUtil;
|
||||
import com.ai.da.mapper.primary.AccountMapper;
|
||||
import com.ai.da.mapper.primary.SubscriptionInfoMapper;
|
||||
import com.ai.da.mapper.primary.entity.Account;
|
||||
import com.ai.da.mapper.primary.entity.SubscriptionInfo;
|
||||
import com.ai.da.service.StripeService;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import io.netty.util.internal.StringUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class SubscriptionReminderTask {
|
||||
|
||||
public final AccountMapper accountMapper;
|
||||
|
||||
public final SubscriptionInfoMapper subscriptionInfoMapper;
|
||||
|
||||
public final StripeService stripeService;
|
||||
|
||||
// 订阅类型与提前天数的映射配置
|
||||
private static final Map<String, Integer> REMINDER_DAYS_CONFIG = new HashMap<>();
|
||||
|
||||
static {
|
||||
REMINDER_DAYS_CONFIG.put("month", 7);
|
||||
REMINDER_DAYS_CONFIG.put("year", 14);
|
||||
}
|
||||
|
||||
// @Scheduled(cron = "0 0 9 * * ?")
|
||||
public void subscriptionReminder() {
|
||||
// 获取所有需要通知的订阅
|
||||
List<SubscriptionInfo> subscriptionInfos = getDueSubscriptions();
|
||||
|
||||
for (SubscriptionInfo subscriptionInfo : subscriptionInfos) {
|
||||
Integer daysBefore = REMINDER_DAYS_CONFIG.get(subscriptionInfo.getType());
|
||||
if (daysBefore == null) {
|
||||
log.warn("未知的订阅类型: {}", subscriptionInfo.getType());
|
||||
continue;
|
||||
}
|
||||
String emailType = subscriptionInfo.getStatus().equals("active") ? "reminder_subscriber" : subscriptionInfo.getStatus().equals("canceled") ? "reminder_expire" : null;
|
||||
if (StringUtil.isNullOrEmpty(emailType)) {
|
||||
log.warn("未知订阅状态:{}", subscriptionInfo.getStatus());
|
||||
continue;
|
||||
}
|
||||
|
||||
boolean success = stripeService.sendEmail(subscriptionInfo.getSubscriptionId(), emailType, subscriptionInfo.getOrderNo());
|
||||
if (success) {
|
||||
log.info("提前{}天向用户 {} 发送续订通知邮件,订阅类型: {}",
|
||||
daysBefore, subscriptionInfo.getAccountId(), subscriptionInfo.getType());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<SubscriptionInfo> getDueSubscriptions() {
|
||||
List<SubscriptionInfo> results = new ArrayList<>();
|
||||
|
||||
for (Map.Entry<String, Integer> entry : REMINDER_DAYS_CONFIG.entrySet()) {
|
||||
String subscriptionType = entry.getKey();
|
||||
int daysBefore = entry.getValue();
|
||||
|
||||
results.addAll(getSubscriptionsDueInDays(subscriptionType, daysBefore));
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
private List<SubscriptionInfo> getSubscriptionsDueInDays(String subscriptionType, int daysBefore) {
|
||||
LocalDateTime targetDate = LocalDateTime.now().plusDays(daysBefore);
|
||||
LocalDateTime startOfDay = targetDate.toLocalDate().atStartOfDay();
|
||||
LocalDateTime endOfDay = targetDate.toLocalDate().atTime(23, 59, 59);
|
||||
|
||||
// 使用系统默认时区
|
||||
ZoneId zoneId = ZoneId.systemDefault();
|
||||
long startTimestamp = startOfDay.atZone(zoneId).toEpochSecond();
|
||||
long endTimestamp = endOfDay.atZone(zoneId).toEpochSecond();
|
||||
|
||||
QueryWrapper<SubscriptionInfo> qw = new QueryWrapper<>();
|
||||
qw.lambda().ge(SubscriptionInfo::getCurrentPeriodEnd, startTimestamp);
|
||||
qw.lambda().lt(SubscriptionInfo::getCurrentPeriodEnd, endTimestamp);
|
||||
// qw.eq("status", "active");
|
||||
qw.lambda().eq(SubscriptionInfo::getType, subscriptionType);
|
||||
|
||||
return subscriptionInfoMapper.selectList(qw);
|
||||
}
|
||||
|
||||
// @Scheduled(cron = "0 0 9 * * ?")
|
||||
public void trialReminder() {
|
||||
// 今天的 00:00:00 和 23:59:59
|
||||
LocalDateTime startOfDay = LocalDateTime.now().toLocalDate().atStartOfDay();
|
||||
LocalDateTime endOfDay = LocalDateTime.now().toLocalDate().atTime(23, 59, 59);
|
||||
|
||||
// 使用系统默认时区
|
||||
ZoneId zoneId = ZoneId.systemDefault();
|
||||
long startTimestamp = startOfDay.atZone(zoneId).toEpochSecond() * 1000;
|
||||
long endTimestamp = endOfDay.atZone(zoneId).toEpochSecond() * 1000;
|
||||
|
||||
QueryWrapper<Account> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.lambda().gt(Account::getValidEndTime, startTimestamp)
|
||||
.lt(Account::getValidEndTime, endTimestamp)
|
||||
.eq(Account::getSystemUser, 3);
|
||||
|
||||
List<Account> accounts = accountMapper.selectList(queryWrapper);
|
||||
for (Account account : accounts) {
|
||||
String language = stripeService.getLanguage(account.getLanguage(), account.getCountry(), "reminder_trial");
|
||||
SendEmailUtil.subscriptionEmailReminder("reminder_trial", null, language, account.getUserEmail());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* public void subscriptionReminder(){
|
||||
// 提前7天的 00:00:00 和 23:59:59
|
||||
LocalDateTime startOfDay = LocalDateTime.now().plusDays(7).toLocalDate().atStartOfDay();
|
||||
LocalDateTime endOfDay = LocalDateTime.now().plusDays(7).toLocalDate().atTime(23, 59, 59);
|
||||
|
||||
// 转为时间戳
|
||||
long startTimestamp = startOfDay.toEpochSecond(ZoneOffset.UTC);
|
||||
long endTimestamp = endOfDay.toEpochSecond(ZoneOffset.UTC);
|
||||
|
||||
QueryWrapper<SubscriptionInfo> qw = new QueryWrapper<>();
|
||||
qw.ge("current_period_end", startTimestamp);
|
||||
qw.lt("current_period_end", endTimestamp);
|
||||
qw.eq("status", "active");
|
||||
|
||||
List<SubscriptionInfo> subscriptionInfos = subscriptionInfoMapper.selectList(qw);
|
||||
for (SubscriptionInfo subscriptionInfo : subscriptionInfos) {
|
||||
boolean b = sendEmail(subscriptionInfo.getSubscriptionId(), "reminder", null);
|
||||
if (b) log.info("提前7天向用户 {} 发送续订通知邮件", subscriptionInfo.getAccountId());
|
||||
}
|
||||
}*/
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package com.ai.da.common.utils;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class ComprehensivePunctuationConverter {
|
||||
private static final Map<Character, Character> FULL_TO_HALF_MAP = new HashMap<>();
|
||||
|
||||
static {
|
||||
// 中文标点到英文标点的映射(扩展版)
|
||||
FULL_TO_HALF_MAP.put(',', ',');
|
||||
FULL_TO_HALF_MAP.put('。', '.');
|
||||
FULL_TO_HALF_MAP.put(';', ';');
|
||||
FULL_TO_HALF_MAP.put(':', ':');
|
||||
FULL_TO_HALF_MAP.put('?', '?');
|
||||
FULL_TO_HALF_MAP.put('!', '!');
|
||||
FULL_TO_HALF_MAP.put('(', '(');
|
||||
FULL_TO_HALF_MAP.put(')', ')');
|
||||
FULL_TO_HALF_MAP.put('【', '[');
|
||||
FULL_TO_HALF_MAP.put('】', ']');
|
||||
FULL_TO_HALF_MAP.put('「', '\'');
|
||||
FULL_TO_HALF_MAP.put('」', '\'');
|
||||
FULL_TO_HALF_MAP.put('『', '"');
|
||||
FULL_TO_HALF_MAP.put('』', '"');
|
||||
FULL_TO_HALF_MAP.put('、', '\\');
|
||||
FULL_TO_HALF_MAP.put('~', '~');
|
||||
FULL_TO_HALF_MAP.put('—', '-');
|
||||
FULL_TO_HALF_MAP.put('.', '.');
|
||||
FULL_TO_HALF_MAP.put('〈', '<');
|
||||
FULL_TO_HALF_MAP.put('〉', '>');
|
||||
FULL_TO_HALF_MAP.put('《', '«');
|
||||
FULL_TO_HALF_MAP.put('》', '»');
|
||||
FULL_TO_HALF_MAP.put('〝', '"');
|
||||
FULL_TO_HALF_MAP.put('〞', '"');
|
||||
FULL_TO_HALF_MAP.put('﹁', '"');
|
||||
FULL_TO_HALF_MAP.put('﹂', '"');
|
||||
FULL_TO_HALF_MAP.put('…', '.');
|
||||
FULL_TO_HALF_MAP.put('﹏', '_');
|
||||
|
||||
// 全角字母和数字
|
||||
for (char c = 'A'; c <= 'Z'; c++) {
|
||||
FULL_TO_HALF_MAP.put(c, (char)(c - 'A' + 'A'));
|
||||
}
|
||||
for (char c = 'a'; c <= 'z'; c++) {
|
||||
FULL_TO_HALF_MAP.put(c, (char)(c - 'a' + 'a'));
|
||||
}
|
||||
for (char c = '0'; c <= '9'; c++) {
|
||||
FULL_TO_HALF_MAP.put(c, (char)(c - '0' + '0'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字符串中的全角字符(包括标点、字母、数字)转换为半角字符
|
||||
*/
|
||||
public static String convertToHalfWidth(String input) {
|
||||
if (input == null || input.isEmpty()) {
|
||||
return input;
|
||||
}
|
||||
|
||||
StringBuilder result = new StringBuilder();
|
||||
for (int i = 0; i < input.length(); i++) {
|
||||
char c = input.charAt(i);
|
||||
|
||||
// 检查映射表
|
||||
if (FULL_TO_HALF_MAP.containsKey(c)) {
|
||||
result.append(FULL_TO_HALF_MAP.get(c));
|
||||
}
|
||||
// 处理全角空格(Unicode 12288)
|
||||
else if (c == ' ') {
|
||||
result.append(' ');
|
||||
}
|
||||
// 其他字符保持不变
|
||||
else {
|
||||
result.append(c);
|
||||
}
|
||||
}
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
// String text = "这是一个全角示例,包含:中文标点、全角字母(ABC)、全角数字(123) 还有全角空格!";
|
||||
String text = "birds,yellow";
|
||||
String converted = convertToHalfWidth(text);
|
||||
System.out.println("原始文本: " + text);
|
||||
System.out.println("转换后: " + converted);
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,14 @@
|
||||
package com.ai.da.common.utils;
|
||||
|
||||
import com.ai.da.common.constant.CommonConstant;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.*;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
import java.util.TimeZone;
|
||||
|
||||
@Slf4j
|
||||
@@ -18,6 +16,8 @@ public class DateUtil {
|
||||
public static final String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";
|
||||
public static final String YYYYMM = "yyyyMM";
|
||||
public static final String YYYY_MM_DD = "yyyyMMdd";
|
||||
public static final String YYYY_MM_DD_HH = "yyyyMMddHH";
|
||||
public static final String YYYY_MM_DD_hh_mm_ss = "yyyyMMddHHMMss";
|
||||
|
||||
/**
|
||||
* LocalDate -> Date
|
||||
@@ -81,4 +81,27 @@ public class DateUtil {
|
||||
return String.valueOf(epochSecond).substring(0, 10);
|
||||
}
|
||||
|
||||
public static String changeTimeStampFormat(Long timeStamp, String type, String format){
|
||||
// 将秒级时间戳转换为毫秒级
|
||||
if (type.equals("seconds")){
|
||||
timeStamp = timeStamp * 1000;
|
||||
}
|
||||
// 输出格式
|
||||
SimpleDateFormat outputFormat = new SimpleDateFormat(format, Locale.ENGLISH);
|
||||
// 创建Date对象
|
||||
Date date = new Date(timeStamp);
|
||||
// 格式化输出
|
||||
return outputFormat.format(date);
|
||||
}
|
||||
|
||||
public static String changeTimeStampFormat(LocalDateTime localDate){
|
||||
return localDate.format(DateTimeFormatter.ofPattern(CommonConstant.TIME_FORMAT_MMM_dd_yyyy_EEEE, Locale.US));
|
||||
}
|
||||
|
||||
public static boolean isMoreThanOneDayApart(LocalDateTime givenDateTime) {
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
Duration duration = Duration.between(givenDateTime, now);
|
||||
return duration.toHours() >= 24;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.ai.da.common.utils;
|
||||
|
||||
import lombok.Data;
|
||||
import org.apache.poi.ss.usermodel.Cell;
|
||||
import org.apache.poi.ss.usermodel.Row;
|
||||
import org.apache.poi.ss.usermodel.Sheet;
|
||||
import org.apache.poi.ss.usermodel.Workbook;
|
||||
@@ -23,9 +24,28 @@ public class ExcelReader {
|
||||
for (int i = 0; i < numberOfColumns; i++) {
|
||||
List<String> columnData = new ArrayList<>();
|
||||
for (Row row : sheet) {
|
||||
columnData.add(row.getCell(i).getStringCellValue());
|
||||
Cell cell = row.getCell(i);
|
||||
if (cell != null) {
|
||||
switch (cell.getCellType()) {
|
||||
case STRING:
|
||||
columnData.add(cell.getStringCellValue());
|
||||
break;
|
||||
case NUMERIC:
|
||||
columnData.add(String.valueOf(cell.getNumericCellValue()));
|
||||
break;
|
||||
case BOOLEAN:
|
||||
columnData.add(String.valueOf(cell.getBooleanCellValue()));
|
||||
break;
|
||||
default:
|
||||
// 跳过空单元格或其他类型(比如错误类型)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 只有当这一列有数据时,才添加到结果列表中
|
||||
if (!columnData.isEmpty()) {
|
||||
data.add(columnData);
|
||||
}
|
||||
data.add(columnData);
|
||||
}
|
||||
}
|
||||
return data;
|
||||
|
||||
@@ -1,313 +1,325 @@
|
||||
package com.ai.da.common.utils;
|
||||
|
||||
import cn.hutool.core.exceptions.ExceptionUtil;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.ai.da.common.config.exception.BusinessException;
|
||||
import com.ai.da.model.vo.FileVO;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import org.springframework.web.multipart.commons.CommonsMultipartFile;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.*;
|
||||
import java.net.URL;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
|
||||
public class FileUtil extends cn.hutool.core.io.FileUtil {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(com.ai.da.common.utils.FileUtil.class);
|
||||
|
||||
/**
|
||||
* 系统临时目录
|
||||
* <br>
|
||||
* windows 包含路径分割符,但Linux 不包含,
|
||||
* 在windows \\==\ 前提下,
|
||||
* 为安全起见 同意拼装 路径分割符,
|
||||
* <pre>
|
||||
* java.io.tmpdir
|
||||
* windows : C:\Users/xxx\AppData\Local\Temp\
|
||||
* linux: /temp
|
||||
* </pre>
|
||||
*/
|
||||
public static final String SYS_TEM_DIR = System.getProperty("java.io.tmpdir") + File.separator;
|
||||
/**
|
||||
* 定义GB的计算常量
|
||||
*/
|
||||
private static final int GB = 1024 * 1024 * 1024;
|
||||
/**
|
||||
* 定义MB的计算常量
|
||||
*/
|
||||
private static final int MB = 1024 * 1024;
|
||||
/**
|
||||
* 定义KB的计算常量
|
||||
*/
|
||||
private static final int KB = 1024;
|
||||
|
||||
/**
|
||||
* 格式化小数
|
||||
*/
|
||||
private static final DecimalFormat DF = new DecimalFormat("0.00");
|
||||
|
||||
public static final String IMAGE = "图片";
|
||||
public static final String TXT = "文档";
|
||||
public static final String MUSIC = "音乐";
|
||||
public static final String VIDEO = "视频";
|
||||
public static final String OTHER = "其他";
|
||||
|
||||
|
||||
/**
|
||||
* MultipartFile转File
|
||||
*/
|
||||
public static File toFile(MultipartFile multipartFile) {
|
||||
// 获取文件名
|
||||
String fileName = multipartFile.getOriginalFilename();
|
||||
// 获取文件后缀
|
||||
String prefix = "." + getExtensionName(fileName);
|
||||
File file = null;
|
||||
try {
|
||||
// 用uuid作为文件名,防止生成的临时文件重复
|
||||
file = new File(SYS_TEM_DIR + IdUtil.simpleUUID() + prefix);
|
||||
// MultipartFile to File
|
||||
multipartFile.transferTo(file);
|
||||
} catch (IOException e) {
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
// public static void main(String[] args) {
|
||||
// File file = new File(
|
||||
// "http://18.162.111.141:5568/download/202211/userFile/collection/Printboard/1/a3c9838c-2171-44d7-af54-c94ee6affcd9print_2.jpg.png");
|
||||
// FileUtil.getFileSize()
|
||||
// }
|
||||
|
||||
/**
|
||||
* 获取文件扩展名,不带 .
|
||||
*/
|
||||
public static String getExtensionName(String filename) {
|
||||
if ((filename != null) && (filename.length() > 0)) {
|
||||
int dot = filename.lastIndexOf('.');
|
||||
if ((dot > -1) && (dot < (filename.length() - 1))) {
|
||||
return filename.substring(dot + 1);
|
||||
}
|
||||
}
|
||||
return filename;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* inputStream 转 File
|
||||
*/
|
||||
static File inputStreamToFile(InputStream ins, String name) {
|
||||
File file = new File(SYS_TEM_DIR + name);
|
||||
if (file.exists()) {
|
||||
return file;
|
||||
}
|
||||
OutputStream os = null;
|
||||
try {
|
||||
os = new FileOutputStream(file);
|
||||
int bytesRead;
|
||||
int len = 8192;
|
||||
byte[] buffer = new byte[len];
|
||||
while ((bytesRead = ins.read(buffer, 0, len)) != -1) {
|
||||
os.write(buffer, 0, bytesRead);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
CloseUtil.close(os);
|
||||
CloseUtil.close(ins);
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件尺寸
|
||||
*/
|
||||
public static FileVO getFileSize(MultipartFile file) {
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
try {
|
||||
// 图片对象
|
||||
BufferedImage bufferedImage = ImageIO.read(file.getInputStream());
|
||||
// 宽度
|
||||
width = bufferedImage.getWidth();
|
||||
// 高度
|
||||
height = bufferedImage.getHeight();
|
||||
} catch (IOException ioException) {
|
||||
log.error("获取文件尺寸异常###{}", ExceptionUtil.stacktraceToString(ioException));
|
||||
}
|
||||
return new FileVO(height, width);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件尺寸
|
||||
*/
|
||||
public static FileVO getFileSize(InputStream inputStream) {
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
try {
|
||||
// 图片对象
|
||||
BufferedImage bufferedImage = ImageIO.read(inputStream);
|
||||
// 宽度
|
||||
width = bufferedImage.getWidth();
|
||||
// 高度
|
||||
height = bufferedImage.getHeight();
|
||||
} catch (IOException ioException) {
|
||||
log.error("获取文件尺寸异常###{}", ExceptionUtil.stacktraceToString(ioException));
|
||||
}
|
||||
return new FileVO(height, width);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取远程文件流
|
||||
*/
|
||||
public static InputStream getOriginFile(String path) {
|
||||
|
||||
try {
|
||||
//远程
|
||||
URL url = new URL(path);
|
||||
return url.openStream();
|
||||
} catch (IOException ioException) {
|
||||
log.error("获取文件尺寸异常###{}###path##{}", ExceptionUtil.stacktraceToString(ioException), path);
|
||||
throw new BusinessException("get.file.failed");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将文件名解析成文件的上传路径
|
||||
*/
|
||||
public static File upload(MultipartFile file, String filePath) {
|
||||
Date date = new Date();
|
||||
SimpleDateFormat format = new SimpleDateFormat("yyyyMMddhhmmssS");
|
||||
String suffix = getExtensionName(file.getOriginalFilename());
|
||||
String nowStr = format.format(date) + "-";
|
||||
try {
|
||||
String fileName = file.getOriginalFilename();
|
||||
String fileSuffix = fileName.substring(fileName.lastIndexOf("."));
|
||||
String path = filePath + fileSuffix;
|
||||
// getCanonicalFile 可解析正确各种路径
|
||||
File dest = new File(path).getCanonicalFile();
|
||||
// 检测是否存在目录
|
||||
if (!dest.getParentFile().exists()) {
|
||||
if (!dest.getParentFile().mkdirs()) {
|
||||
System.out.println("was not successful.");
|
||||
}
|
||||
}
|
||||
// 文件写入
|
||||
file.transferTo(dest);
|
||||
return dest;
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将文件名解析成文件的上传路径
|
||||
*/
|
||||
public static File upload2(MultipartFile file, String filePath) {
|
||||
Date date = new Date();
|
||||
SimpleDateFormat format = new SimpleDateFormat("yyyyMMddhhmmssS");
|
||||
String suffix = getExtensionName(file.getOriginalFilename());
|
||||
String nowStr = format.format(date) + "-";
|
||||
try {
|
||||
String fileName = file.getOriginalFilename();
|
||||
String path = filePath + fileName;
|
||||
// getCanonicalFile 可解析正确各种路径
|
||||
File dest = new File(path).getCanonicalFile();
|
||||
// 检测是否存在目录
|
||||
if (!dest.getParentFile().exists()) {
|
||||
if (!dest.getParentFile().mkdirs()) {
|
||||
System.out.println("was not successful.");
|
||||
}
|
||||
}
|
||||
// 文件写入
|
||||
file.transferTo(dest);
|
||||
return dest;
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除文件
|
||||
*/
|
||||
public static boolean delete(String path) {
|
||||
File file = new File(path);
|
||||
if (file.exists()) {
|
||||
return file.delete();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定文件夹下所有文件,不含文件夹里的文件
|
||||
*
|
||||
* @param dirFilePath 文件夹路径
|
||||
* @return
|
||||
*/
|
||||
public static List<File> getAllFile(String dirFilePath) {
|
||||
if (StrUtil.isBlank(dirFilePath)) {
|
||||
return null;
|
||||
}
|
||||
return getAllFile(new File(dirFilePath));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定文件夹下所有文件,不含文件夹里的文件
|
||||
*
|
||||
* @param dirFile 文件夹
|
||||
* @return
|
||||
*/
|
||||
public static List<File> getAllFile(File dirFile) {
|
||||
// 如果文件夹不存在或着不是文件夹,则返回 null
|
||||
if (Objects.isNull(dirFile) || !dirFile.exists() || dirFile.isFile()) {
|
||||
return null;
|
||||
}
|
||||
File[] childrenFiles = dirFile.listFiles();
|
||||
if (Objects.isNull(childrenFiles) || childrenFiles.length == 0) {
|
||||
return null;
|
||||
}
|
||||
List<File> files = new ArrayList<>();
|
||||
for (File childFile : childrenFiles) {
|
||||
// 如果是文件,直接添加到结果集合
|
||||
if (childFile.isFile()) {
|
||||
files.add(childFile);
|
||||
}
|
||||
//以下几行代码取消注释后可以将所有子文件夹里的文件也获取到列表里
|
||||
else {
|
||||
// 如果是文件夹,则将其内部文件添加进结果集合
|
||||
List<File> cFiles = getAllFile(childFile);
|
||||
if (Objects.isNull(cFiles) || cFiles.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
files.addAll(cFiles);
|
||||
}
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
// 判断文件是否存在
|
||||
public static boolean isFileExists(String filePath) {
|
||||
File file = new File(filePath);
|
||||
return file.exists() && file.isFile();
|
||||
}
|
||||
|
||||
// 根据路径获取文件
|
||||
public static File getFile(String filePath) {
|
||||
File file = new File(filePath);
|
||||
if (file.exists() && file.isFile()) {
|
||||
return file;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
package com.ai.da.common.utils;
|
||||
|
||||
import cn.hutool.core.exceptions.ExceptionUtil;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.ai.da.common.config.exception.BusinessException;
|
||||
import com.ai.da.model.vo.FileVO;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.mock.web.MockMultipartFile;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.*;
|
||||
import java.net.URL;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
|
||||
public class FileUtil extends cn.hutool.core.io.FileUtil {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(com.ai.da.common.utils.FileUtil.class);
|
||||
|
||||
/**
|
||||
* 系统临时目录
|
||||
* <br>
|
||||
* windows 包含路径分割符,但Linux 不包含,
|
||||
* 在windows \\==\ 前提下,
|
||||
* 为安全起见 同意拼装 路径分割符,
|
||||
* <pre>
|
||||
* java.io.tmpdir
|
||||
* windows : C:\Users/xxx\AppData\Local\Temp\
|
||||
* linux: /temp
|
||||
* </pre>
|
||||
*/
|
||||
public static final String SYS_TEM_DIR = System.getProperty("java.io.tmpdir") + File.separator;
|
||||
/**
|
||||
* 定义GB的计算常量
|
||||
*/
|
||||
private static final int GB = 1024 * 1024 * 1024;
|
||||
/**
|
||||
* 定义MB的计算常量
|
||||
*/
|
||||
private static final int MB = 1024 * 1024;
|
||||
/**
|
||||
* 定义KB的计算常量
|
||||
*/
|
||||
private static final int KB = 1024;
|
||||
|
||||
/**
|
||||
* 格式化小数
|
||||
*/
|
||||
private static final DecimalFormat DF = new DecimalFormat("0.00");
|
||||
|
||||
public static final String IMAGE = "图片";
|
||||
public static final String TXT = "文档";
|
||||
public static final String MUSIC = "音乐";
|
||||
public static final String VIDEO = "视频";
|
||||
public static final String OTHER = "其他";
|
||||
|
||||
|
||||
/**
|
||||
* MultipartFile转File
|
||||
*/
|
||||
public static File toFile(MultipartFile multipartFile) {
|
||||
// 获取文件名
|
||||
String fileName = multipartFile.getOriginalFilename();
|
||||
// 获取文件后缀
|
||||
String prefix = "." + getExtensionName(fileName);
|
||||
File file = null;
|
||||
try {
|
||||
// 用uuid作为文件名,防止生成的临时文件重复
|
||||
file = new File(SYS_TEM_DIR + IdUtil.simpleUUID() + prefix);
|
||||
// MultipartFile to File
|
||||
multipartFile.transferTo(file);
|
||||
} catch (IOException e) {
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
// public static void main(String[] args) {
|
||||
// File file = new File(
|
||||
// "http://18.162.111.141:5568/download/202211/userFile/collection/Printboard/1/a3c9838c-2171-44d7-af54-c94ee6affcd9print_2.jpg.png");
|
||||
// FileUtil.getFileSize()
|
||||
// }
|
||||
|
||||
/**
|
||||
* 获取文件扩展名,不带 .
|
||||
*/
|
||||
public static String getExtensionName(String filename) {
|
||||
if ((filename != null) && (filename.length() > 0)) {
|
||||
int dot = filename.lastIndexOf('.');
|
||||
if ((dot > -1) && (dot < (filename.length() - 1))) {
|
||||
return filename.substring(dot + 1);
|
||||
}
|
||||
}
|
||||
return filename;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* inputStream 转 File
|
||||
*/
|
||||
static File inputStreamToFile(InputStream ins, String name) {
|
||||
File file = new File(SYS_TEM_DIR + name);
|
||||
if (file.exists()) {
|
||||
return file;
|
||||
}
|
||||
OutputStream os = null;
|
||||
try {
|
||||
os = new FileOutputStream(file);
|
||||
int bytesRead;
|
||||
int len = 8192;
|
||||
byte[] buffer = new byte[len];
|
||||
while ((bytesRead = ins.read(buffer, 0, len)) != -1) {
|
||||
os.write(buffer, 0, bytesRead);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
CloseUtil.close(os);
|
||||
CloseUtil.close(ins);
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件尺寸
|
||||
*/
|
||||
public static FileVO getFileSize(MultipartFile file) {
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
try {
|
||||
// 图片对象
|
||||
BufferedImage bufferedImage = ImageIO.read(file.getInputStream());
|
||||
// 宽度
|
||||
width = bufferedImage.getWidth();
|
||||
// 高度
|
||||
height = bufferedImage.getHeight();
|
||||
} catch (IOException ioException) {
|
||||
log.error("获取文件尺寸异常###{}", ExceptionUtil.stacktraceToString(ioException));
|
||||
}
|
||||
return new FileVO(height, width);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件尺寸
|
||||
*/
|
||||
public static FileVO getFileSize(InputStream inputStream) {
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
try {
|
||||
// 图片对象
|
||||
BufferedImage bufferedImage = ImageIO.read(inputStream);
|
||||
// 宽度
|
||||
width = bufferedImage.getWidth();
|
||||
// 高度
|
||||
height = bufferedImage.getHeight();
|
||||
} catch (IOException ioException) {
|
||||
log.error("获取文件尺寸异常###{}", ExceptionUtil.stacktraceToString(ioException));
|
||||
}
|
||||
return new FileVO(height, width);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取远程文件流
|
||||
*/
|
||||
public static InputStream getOriginFile(String path) {
|
||||
|
||||
try {
|
||||
//远程
|
||||
URL url = new URL(path);
|
||||
return url.openStream();
|
||||
} catch (IOException ioException) {
|
||||
log.error("获取源文件异常###{}###path##{}", ExceptionUtil.stacktraceToString(ioException), path);
|
||||
throw new BusinessException("get.file.failed");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将文件名解析成文件的上传路径
|
||||
*/
|
||||
public static File upload(MultipartFile file, String filePath) {
|
||||
Date date = new Date();
|
||||
SimpleDateFormat format = new SimpleDateFormat("yyyyMMddhhmmssS");
|
||||
String suffix = getExtensionName(file.getOriginalFilename());
|
||||
String nowStr = format.format(date) + "-";
|
||||
try {
|
||||
String fileName = file.getOriginalFilename();
|
||||
String fileSuffix = fileName.substring(fileName.lastIndexOf("."));
|
||||
String path = filePath + fileSuffix;
|
||||
// getCanonicalFile 可解析正确各种路径
|
||||
File dest = new File(path).getCanonicalFile();
|
||||
// 检测是否存在目录
|
||||
if (!dest.getParentFile().exists()) {
|
||||
if (!dest.getParentFile().mkdirs()) {
|
||||
System.out.println("was not successful.");
|
||||
}
|
||||
}
|
||||
// 文件写入
|
||||
file.transferTo(dest);
|
||||
return dest;
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将文件名解析成文件的上传路径
|
||||
*/
|
||||
public static File upload2(MultipartFile file, String filePath) {
|
||||
Date date = new Date();
|
||||
SimpleDateFormat format = new SimpleDateFormat("yyyyMMddhhmmssS");
|
||||
String suffix = getExtensionName(file.getOriginalFilename());
|
||||
String nowStr = format.format(date) + "-";
|
||||
try {
|
||||
String fileName = file.getOriginalFilename();
|
||||
String path = filePath + fileName;
|
||||
// getCanonicalFile 可解析正确各种路径
|
||||
File dest = new File(path).getCanonicalFile();
|
||||
// 检测是否存在目录
|
||||
if (!dest.getParentFile().exists()) {
|
||||
if (!dest.getParentFile().mkdirs()) {
|
||||
System.out.println("was not successful.");
|
||||
}
|
||||
}
|
||||
// 文件写入
|
||||
file.transferTo(dest);
|
||||
return dest;
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除文件
|
||||
*/
|
||||
public static boolean delete(String path) {
|
||||
File file = new File(path);
|
||||
if (file.exists()) {
|
||||
return file.delete();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定文件夹下所有文件,不含文件夹里的文件
|
||||
*
|
||||
* @param dirFilePath 文件夹路径
|
||||
* @return
|
||||
*/
|
||||
public static List<File> getAllFile(String dirFilePath) {
|
||||
if (StrUtil.isBlank(dirFilePath)) {
|
||||
return null;
|
||||
}
|
||||
return getAllFile(new File(dirFilePath));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定文件夹下所有文件,不含文件夹里的文件
|
||||
*
|
||||
* @param dirFile 文件夹
|
||||
* @return
|
||||
*/
|
||||
public static List<File> getAllFile(File dirFile) {
|
||||
// 如果文件夹不存在或着不是文件夹,则返回 null
|
||||
if (Objects.isNull(dirFile) || !dirFile.exists() || dirFile.isFile()) {
|
||||
return null;
|
||||
}
|
||||
File[] childrenFiles = dirFile.listFiles();
|
||||
if (Objects.isNull(childrenFiles) || childrenFiles.length == 0) {
|
||||
return null;
|
||||
}
|
||||
List<File> files = new ArrayList<>();
|
||||
for (File childFile : childrenFiles) {
|
||||
// 如果是文件,直接添加到结果集合
|
||||
if (childFile.isFile()) {
|
||||
files.add(childFile);
|
||||
}
|
||||
//以下几行代码取消注释后可以将所有子文件夹里的文件也获取到列表里
|
||||
else {
|
||||
// 如果是文件夹,则将其内部文件添加进结果集合
|
||||
List<File> cFiles = getAllFile(childFile);
|
||||
if (Objects.isNull(cFiles) || cFiles.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
files.addAll(cFiles);
|
||||
}
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
// 判断文件是否存在
|
||||
public static boolean isFileExists(String filePath) {
|
||||
File file = new File(filePath);
|
||||
return file.exists() && file.isFile();
|
||||
}
|
||||
|
||||
// 根据路径获取文件
|
||||
public static File getFile(String filePath) {
|
||||
File file = new File(filePath);
|
||||
if (file.exists() && file.isFile()) {
|
||||
return file;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 FileItem 转换为 MultipartFile
|
||||
* 用于 Spring Boot 3.x 兼容性(CommonsMultipartFile 已移除)
|
||||
*/
|
||||
public static MultipartFile fileItemToMultipartFile(org.apache.commons.fileupload.FileItem fileItem) {
|
||||
String fieldName = fileItem.getFieldName();
|
||||
String fileName = fileItem.getName();
|
||||
String contentType = fileItem.getContentType();
|
||||
byte[] content = fileItem.get();
|
||||
return new MockMultipartFile(fieldName, fileName, contentType, content);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import com.alibaba.fastjson.JSON;
|
||||
import com.ai.da.common.response.Response;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import javax.servlet.ServletResponse;
|
||||
import jakarta.servlet.ServletResponse;
|
||||
import java.io.PrintWriter;
|
||||
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ public final class LocalCacheUtils {
|
||||
private static LoadingCache<String, String> loadTokenCache() {
|
||||
LoadingCache<String, String> tokenCache = CacheBuilder.newBuilder()
|
||||
.concurrencyLevel(10)
|
||||
.expireAfterWrite(24 * 100, TimeUnit.HOURS)
|
||||
.expireAfterWrite(24 * 7 - 1, TimeUnit.HOURS)
|
||||
.initialCapacity(100)
|
||||
.maximumSize(10000)
|
||||
.recordStats()
|
||||
|
||||
178
src/main/java/com/ai/da/common/utils/MailUtil.java
Normal file
178
src/main/java/com/ai/da/common/utils/MailUtil.java
Normal file
@@ -0,0 +1,178 @@
|
||||
package com.ai.da.common.utils;
|
||||
|
||||
import com.ai.da.common.constant.CommonConstant;
|
||||
import com.ai.da.model.dto.BasicEmailParamDTO;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.sun.mail.smtp.SMTPTransport;
|
||||
import io.netty.util.internal.StringUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.core.io.InputStreamSource;
|
||||
import org.springframework.mail.MailException;
|
||||
import org.springframework.mail.javamail.JavaMailSender;
|
||||
import org.springframework.mail.javamail.JavaMailSenderImpl;
|
||||
import org.springframework.mail.javamail.MimeMessageHelper;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.thymeleaf.TemplateEngine;
|
||||
import org.thymeleaf.context.Context;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.mail.MessagingException;
|
||||
import jakarta.mail.internet.*;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class MailUtil {
|
||||
|
||||
@Resource
|
||||
private JavaMailSender javaMailSender;
|
||||
|
||||
@Resource
|
||||
private TemplateEngine templateEngine;
|
||||
|
||||
/**
|
||||
* 发送邮件 - 默认发件人
|
||||
*
|
||||
* @param basicEmailParamDTO 发送邮件所需参数
|
||||
* @param inputStreamSource 附件(如果有)
|
||||
*/
|
||||
public int sendMail(BasicEmailParamDTO basicEmailParamDTO, String fileName, InputStreamSource inputStreamSource) throws MessagingException {
|
||||
MimeMessage mimeMessage = createSimpleMail(basicEmailParamDTO, fileName, inputStreamSource);
|
||||
// 提取配置
|
||||
String host;
|
||||
String username;
|
||||
String password;
|
||||
if (StringUtil.isNullOrEmpty(basicEmailParamDTO.getServiceAddress())) {
|
||||
host = ((JavaMailSenderImpl) javaMailSender).getHost();
|
||||
} else {
|
||||
host = basicEmailParamDTO.getServiceAddress();
|
||||
}
|
||||
if (StringUtil.isNullOrEmpty(basicEmailParamDTO.getSenderUser())) {
|
||||
username = ((JavaMailSenderImpl) javaMailSender).getUsername();
|
||||
} else {
|
||||
username = basicEmailParamDTO.getSenderUser();
|
||||
}
|
||||
if (StringUtil.isNullOrEmpty(basicEmailParamDTO.getServiceAddress())) {
|
||||
password = ((JavaMailSenderImpl) javaMailSender).getPassword();
|
||||
} else {
|
||||
password = basicEmailParamDTO.getPassword();
|
||||
}
|
||||
return sendMail(mimeMessage, host, username, password);
|
||||
}
|
||||
|
||||
private int sendMail(MimeMessage mimeMessage, String host, String username, String password) throws MessagingException {
|
||||
SMTPTransport transport = null;
|
||||
try {
|
||||
// 获取 SMTPTransport
|
||||
transport = (SMTPTransport) mimeMessage.getSession().getTransport("smtp");
|
||||
// 连接到 SMTP 服务器
|
||||
transport.connect(host, username, password);
|
||||
// 发送邮件
|
||||
transport.sendMessage(mimeMessage, mimeMessage.getAllRecipients());
|
||||
// 获取 SMTP 服务器的响应
|
||||
String lastServerResponse = transport.getLastServerResponse();
|
||||
int lastReturnCode = transport.getLastReturnCode();
|
||||
|
||||
log.info("SMTP 状态码: {}, SMTP 服务器响应: {}", lastReturnCode, lastServerResponse);
|
||||
return lastReturnCode;
|
||||
} catch (MailException | MessagingException e) {
|
||||
// 记录日志或执行其他补偿逻辑
|
||||
log.info("邮件发送失败:{}", e.getMessage());
|
||||
} finally {
|
||||
// 关闭连接
|
||||
assert transport != null;
|
||||
transport.close();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一封邮件
|
||||
*
|
||||
* @param basicEmailParamDTO 创建邮件需要的参数
|
||||
* @param inputStreamSource 附件(如果有)
|
||||
* @return 一封邮件
|
||||
*/
|
||||
private MimeMessage createSimpleMail(BasicEmailParamDTO basicEmailParamDTO, String fileName, InputStreamSource inputStreamSource) throws MessagingException {
|
||||
// 创建邮件对象
|
||||
MimeMessage message = javaMailSender.createMimeMessage();
|
||||
// 使用 MimeMessageHelper 简化邮件内容和附件的设置
|
||||
MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(message, true);
|
||||
// 设置发件人
|
||||
mimeMessageHelper.setFrom(new InternetAddress(basicEmailParamDTO.getSenderUserMail()));
|
||||
// 设置收件人
|
||||
mimeMessageHelper.setTo(basicEmailParamDTO.getMailTo());
|
||||
// 设置抄送人
|
||||
if (basicEmailParamDTO.getCc() != null && basicEmailParamDTO.getCc().length > 0) {
|
||||
mimeMessageHelper.setCc(basicEmailParamDTO.getCc());
|
||||
}
|
||||
// 设置暗送人
|
||||
if (basicEmailParamDTO.getBcc() != null && basicEmailParamDTO.getBcc().length > 0) {
|
||||
mimeMessageHelper.setBcc(basicEmailParamDTO.getBcc());
|
||||
}
|
||||
// 设置邮件主题
|
||||
mimeMessageHelper.setSubject(basicEmailParamDTO.getSubject());
|
||||
// 设置邮件内容(HTML 格式)
|
||||
mimeMessageHelper.setText(basicEmailParamDTO.getContent(), true);
|
||||
// 设置附件
|
||||
if (inputStreamSource != null) {
|
||||
mimeMessageHelper.addAttachment(fileName, inputStreamSource);
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 设置实体参数
|
||||
*
|
||||
* @param mailTo 接收邮件的邮箱地址
|
||||
* @param jsonObject 模板中变量的值
|
||||
* @return 返回一个MailEntity
|
||||
* @throws AddressException 邮箱地址值异常
|
||||
*/
|
||||
public BasicEmailParamDTO setBasicEmailParams(List<String> mailTo, JSONObject jsonObject, String templatePath, String title) throws AddressException {
|
||||
BasicEmailParamDTO basicEmailParamDTO = new BasicEmailParamDTO();
|
||||
// basicEmailParamDTO.setSenderUserMail("info@aida.com.hk");
|
||||
basicEmailParamDTO.setSenderUserMail(CommonConstant.senderEmail);
|
||||
basicEmailParamDTO.setMailTo(getInternetAddressList(mailTo));
|
||||
basicEmailParamDTO.setSubject(title);
|
||||
// todo 邮件模板不存在的报错与重试机制
|
||||
basicEmailParamDTO.setContent(setContent(jsonObject, templatePath));
|
||||
return basicEmailParamDTO;
|
||||
}
|
||||
|
||||
public BasicEmailParamDTO setBasicEmailParams(List<String> mailTo, String title) throws AddressException {
|
||||
BasicEmailParamDTO basicEmailParamDTO = new BasicEmailParamDTO();
|
||||
basicEmailParamDTO.setSenderUserMail("info@aida.com.hk");
|
||||
basicEmailParamDTO.setMailTo(getInternetAddressList(mailTo));
|
||||
basicEmailParamDTO.setSubject(title);
|
||||
return basicEmailParamDTO;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将地址转换为InternetAddress类型
|
||||
*
|
||||
* @param addressList 普通的地址字符串列表
|
||||
* @return InternetAddress类型的地址列表
|
||||
* @throws AddressException 地址异常
|
||||
*/
|
||||
public InternetAddress[] getInternetAddressList(List<String> addressList) throws AddressException {
|
||||
InternetAddress[] toAddress = new InternetAddress[addressList.size()];
|
||||
for (String address : addressList) {
|
||||
toAddress[addressList.indexOf(address)] = new InternetAddress(address);
|
||||
}
|
||||
return toAddress;
|
||||
}
|
||||
|
||||
public String setContent(JSONObject jsonObject, String templatePath) {
|
||||
Context context = new Context();
|
||||
if (Objects.nonNull(jsonObject)) {
|
||||
for (String key : jsonObject.keySet()) {
|
||||
context.setVariable(key, jsonObject.get(key));
|
||||
}
|
||||
}
|
||||
return templateEngine.process(templatePath, context);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,26 +1,33 @@
|
||||
package com.ai.da.common.utils;
|
||||
|
||||
import com.ai.da.common.config.exception.BusinessException;
|
||||
import com.ai.da.common.constant.CommonConstant;
|
||||
import com.ai.da.common.response.ResultEnum;
|
||||
import com.ai.da.mapper.primary.entity.ObjectItem;
|
||||
import io.minio.*;
|
||||
import io.minio.errors.MinioException;
|
||||
import io.minio.errors.*;
|
||||
import io.minio.http.Method;
|
||||
import io.minio.messages.DeleteError;
|
||||
import io.minio.messages.DeleteObject;
|
||||
import io.minio.messages.Item;
|
||||
import io.netty.util.internal.StringUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.RenderingHints;
|
||||
import java.io.*;
|
||||
import java.net.URL;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Base64;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -34,6 +41,13 @@ public class MinioUtil {
|
||||
@Autowired
|
||||
private MinioClient minioClient;
|
||||
|
||||
/**
|
||||
* 获取MinIO客户端实例
|
||||
*/
|
||||
public MinioClient getMinioClient() {
|
||||
return minioClient;
|
||||
}
|
||||
|
||||
/**
|
||||
* description: 判断bucket是否存在,不存在则创建
|
||||
*
|
||||
@@ -357,7 +371,7 @@ public class MinioUtil {
|
||||
|
||||
public void deleteObject(String path) {
|
||||
if (!path.contains("/")) {
|
||||
throw new BusinessException("The path is error!");
|
||||
throw new BusinessException("the.path.is.error");
|
||||
}
|
||||
int index = path.indexOf("/");
|
||||
String bucketName = path.substring(0, index);
|
||||
@@ -375,16 +389,38 @@ public class MinioUtil {
|
||||
*/
|
||||
public String getPreSignedUrl(String bucketName, String fileName, int expiry) {
|
||||
try {
|
||||
return minioClient.getPresignedObjectUrl(
|
||||
GetPresignedObjectUrlArgs.builder()
|
||||
.bucket(bucketName)
|
||||
.object(fileName)
|
||||
.expiry(expiry, TimeUnit.MINUTES)
|
||||
.method(Method.GET)
|
||||
.build()
|
||||
);
|
||||
} catch (MinioException | InvalidKeyException | IOException | NoSuchAlgorithmException e) {
|
||||
|
||||
String lowerName = fileName.toLowerCase();
|
||||
boolean isImage = lowerName.endsWith(".jpg") || lowerName.endsWith(".jpeg")
|
||||
|| lowerName.endsWith(".png") || lowerName.endsWith(".gif");
|
||||
|
||||
GetPresignedObjectUrlArgs.Builder builder = GetPresignedObjectUrlArgs.builder()
|
||||
.bucket(bucketName)
|
||||
.object(fileName)
|
||||
.expiry(expiry, TimeUnit.MINUTES)
|
||||
.method(Method.GET);
|
||||
|
||||
if (isImage) {
|
||||
// 根据后缀名设置正确的 content-type
|
||||
String contentType = "image/jpeg";
|
||||
if (lowerName.endsWith(".png")) {
|
||||
contentType = "image/png";
|
||||
} else if (lowerName.endsWith(".gif")) { // 新增 GIF
|
||||
contentType = "image/gif";
|
||||
}
|
||||
|
||||
Map<String, String> queryParams = new HashMap<>();
|
||||
queryParams.put("response-content-type", contentType);
|
||||
queryParams.put("response-content-disposition", "inline");
|
||||
|
||||
builder.extraQueryParams(queryParams);
|
||||
}
|
||||
|
||||
return minioClient.getPresignedObjectUrl(builder.build());
|
||||
} catch (MinioException | InvalidKeyException
|
||||
| IOException | NoSuchAlgorithmException | IllegalArgumentException e) {
|
||||
e.printStackTrace();
|
||||
log.error("bucket: {}, object:{}", bucketName, fileName);
|
||||
throw new BusinessException(e.getMessage());
|
||||
}
|
||||
}
|
||||
@@ -394,7 +430,7 @@ public class MinioUtil {
|
||||
return LocalCacheUtils.getPresignedUrlCache(path);
|
||||
} else {
|
||||
if (!path.contains("/")) {
|
||||
throw new BusinessException("The path is error!");
|
||||
throw new BusinessException("the.path.is.error");
|
||||
}
|
||||
int index = path.indexOf("/");
|
||||
String bucketName = path.substring(0, index);
|
||||
@@ -408,7 +444,7 @@ public class MinioUtil {
|
||||
public String getPreSignedUrl(String path, int expiry, boolean resetCache) {
|
||||
if (resetCache || LocalCacheUtils.getPresignedUrlCache(path) == null) {
|
||||
if (!path.contains("/")) {
|
||||
throw new BusinessException("The path is error!");
|
||||
throw new BusinessException("the.path.is.error");
|
||||
}
|
||||
int index = path.indexOf("/");
|
||||
String bucketName = path.substring(0, index);
|
||||
@@ -441,6 +477,16 @@ public class MinioUtil {
|
||||
return getPreSignedUrl(bucketName, String.valueOf(fileName), expiry);
|
||||
}
|
||||
|
||||
public boolean doesObjectExist(String path) {
|
||||
if (!path.contains("/")) {
|
||||
throw new BusinessException("the.path.is.error");
|
||||
}
|
||||
int index = path.indexOf("/");
|
||||
String bucketName = path.substring(0, index);
|
||||
String objectName = path.substring(index + 1);
|
||||
return doesObjectExist(bucketName, objectName);
|
||||
}
|
||||
|
||||
public boolean doesObjectExist(String bucketName, String objectName) {
|
||||
try {
|
||||
minioClient.statObject(
|
||||
@@ -480,10 +526,438 @@ public class MinioUtil {
|
||||
|
||||
return bucketName + "/" + fileName;
|
||||
} catch (Exception e) {
|
||||
log.info("base64上传minio失败:{}",e.getMessage());
|
||||
log.error(e.getMessage());
|
||||
return null; // or throw an exception
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 MinIO 下载对象到本地路径
|
||||
*
|
||||
* @param bucketName 存储桶名称
|
||||
* @param objectName MinIO 上对象的名称
|
||||
* @param localFilePath 本地文件路径
|
||||
*/
|
||||
public void downloadMinioObjectToLocal(String bucketName, String objectName, String localFilePath) {
|
||||
File localFile = new File(localFilePath);
|
||||
File parentDir = localFile.getParentFile();
|
||||
if (parentDir != null) {
|
||||
parentDir.mkdirs(); // 创建文件夹,确保路径结构与 MinIO 一致
|
||||
}
|
||||
|
||||
try (InputStream stream = minioClient.getObject(
|
||||
GetObjectArgs.builder().bucket(bucketName).object(objectName).build());
|
||||
FileOutputStream out = new FileOutputStream(localFile)) {
|
||||
|
||||
byte[] buffer = new byte[1024];
|
||||
int bytesRead;
|
||||
while ((bytesRead = stream.read(buffer)) != -1) {
|
||||
out.write(buffer, 0, bytesRead);
|
||||
}
|
||||
log.info("Downloaded object {} to {}", objectName, localFilePath);
|
||||
} catch (Exception e) {
|
||||
log.error("Error while downloading object {}: {}", objectName, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从路径中提取存储桶名称
|
||||
*
|
||||
* @param path MinIO 路径
|
||||
* @return 存储桶名称
|
||||
*/
|
||||
public String getBucketNameFromPath(String path) {
|
||||
int index = path.indexOf("/");
|
||||
return path.substring(0, index); // 获取第一级路径作为 bucket 名称
|
||||
}
|
||||
|
||||
/**
|
||||
* 从路径中提取对象名称
|
||||
*
|
||||
* @param path MinIO 路径
|
||||
* @return 对象名称
|
||||
*/
|
||||
public String getObjectNameFromPath(String path) {
|
||||
int index = path.indexOf("/");
|
||||
return path.substring(index + 1); // 获取路径的其余部分作为对象名称
|
||||
}
|
||||
|
||||
public List<Integer> getImagesWidthAndHeight(String path){
|
||||
int index = path.indexOf("/");
|
||||
String bucketName = path.substring(0, index);
|
||||
String objectName = path.substring(index + 1);
|
||||
try {
|
||||
// 从 MinIO 下载图片
|
||||
GetObjectResponse response = minioClient.getObject(
|
||||
GetObjectArgs.builder()
|
||||
.bucket(bucketName)
|
||||
.object(objectName)
|
||||
.build()
|
||||
);
|
||||
|
||||
BufferedImage image = ImageIO.read(response);
|
||||
int width = image.getWidth();
|
||||
int height = image.getHeight();
|
||||
|
||||
log.info("Image path {}, Width: {}, Height: {}", path, width, height);
|
||||
response.close();
|
||||
return Arrays.asList(width, height);
|
||||
} catch (MinioException | IOException | NoSuchAlgorithmException | InvalidKeyException e) {
|
||||
log.error("图片:{}, 获取宽高异常", path);
|
||||
throw new BusinessException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void upload(String url, File file) {
|
||||
try {
|
||||
// 分割桶名和对象路径
|
||||
int firstSlashIndex = url.indexOf("/");
|
||||
if (firstSlashIndex == -1) {
|
||||
throw new IllegalArgumentException("URL 格式不正确,无法解析桶名和对象路径");
|
||||
}
|
||||
String bucketName = url.substring(0, firstSlashIndex);
|
||||
String objectName = url.substring(firstSlashIndex + 1);
|
||||
|
||||
// 读取文件流
|
||||
try (FileInputStream fileInputStream = new FileInputStream(file)) {
|
||||
// 上传到 MinIO
|
||||
minioClient.putObject(
|
||||
PutObjectArgs.builder()
|
||||
.bucket(bucketName)
|
||||
.object(objectName)
|
||||
.stream(fileInputStream, file.length(), -1)
|
||||
.contentType("application/zip")
|
||||
.build()
|
||||
);
|
||||
System.out.println("文件上传成功: " + url);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
System.err.println("文件上传失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public String getImageAsBase64(String path) throws IOException {
|
||||
int index = path.indexOf("/");
|
||||
String bucketName = path.substring(0, index);
|
||||
String fileName = path.substring(index + 1);
|
||||
|
||||
// 检查桶是否存在
|
||||
boolean found = doesObjectExist(bucketName, fileName);
|
||||
if (!found) {
|
||||
throw new IOException("Bucket " + bucketName + " does not exist");
|
||||
}
|
||||
|
||||
try (InputStream stream = minioClient.getObject(
|
||||
GetObjectArgs.builder()
|
||||
.bucket(bucketName)
|
||||
.object(fileName)
|
||||
.build())) {
|
||||
|
||||
byte[] bytes = IOUtils.toByteArray(stream);
|
||||
return Base64.getEncoder().encodeToString(bytes);
|
||||
} catch (ServerException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (InsufficientDataException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (ErrorResponseException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (InvalidKeyException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (InvalidResponseException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (XmlParserException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (InternalException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取压缩后的图片Base64编码
|
||||
* @param path 图片的minio路径
|
||||
* @param targetWidth 目标宽度
|
||||
* @param targetHeight 目标高度
|
||||
* @return 压缩后的图片Base64编码
|
||||
* @throws IOException
|
||||
*/
|
||||
public String getCompressedImageAsBase64(String path, int targetWidth, int targetHeight) throws IOException {
|
||||
int index = path.indexOf("/");
|
||||
String bucketName = path.substring(0, index);
|
||||
String fileName = path.substring(index + 1);
|
||||
|
||||
// 检查桶是否存在
|
||||
boolean found = doesObjectExist(bucketName, fileName);
|
||||
if (!found) {
|
||||
throw new IOException("Bucket " + bucketName + " does not exist");
|
||||
}
|
||||
|
||||
try (InputStream stream = minioClient.getObject(
|
||||
GetObjectArgs.builder()
|
||||
.bucket(bucketName)
|
||||
.object(fileName)
|
||||
.build())) {
|
||||
|
||||
// 读取原始图片
|
||||
BufferedImage originalImage = ImageIO.read(stream);
|
||||
if (originalImage == null) {
|
||||
throw new IOException("无法读取图片: " + path);
|
||||
}
|
||||
|
||||
// 计算压缩比例,保持宽高比
|
||||
int originalWidth = originalImage.getWidth();
|
||||
int originalHeight = originalImage.getHeight();
|
||||
|
||||
double scaleX = (double) targetWidth / originalWidth;
|
||||
double scaleY = (double) targetHeight / originalHeight;
|
||||
double scale = Math.min(scaleX, scaleY); // 选择较小的缩放比例以保持宽高比
|
||||
|
||||
int newWidth = (int) (originalWidth * scale);
|
||||
int newHeight = (int) (originalHeight * scale);
|
||||
|
||||
// 创建压缩后的图片
|
||||
BufferedImage compressedImage = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_RGB);
|
||||
Graphics2D g2d = compressedImage.createGraphics();
|
||||
|
||||
// 设置高质量渲染
|
||||
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
|
||||
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
|
||||
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
|
||||
// 绘制压缩后的图片
|
||||
g2d.drawImage(originalImage, 0, 0, newWidth, newHeight, null);
|
||||
g2d.dispose();
|
||||
|
||||
// 转换为Base64
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
ImageIO.write(compressedImage, "JPEG", baos); // 使用JPEG格式以减小文件大小
|
||||
byte[] imageBytes = baos.toByteArray();
|
||||
|
||||
log.info("图片压缩完成: {} -> {}x{} (原始: {}x{})", path, newWidth, newHeight, originalWidth, originalHeight);
|
||||
return Base64.getEncoder().encodeToString(imageBytes);
|
||||
|
||||
} catch (ServerException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (InsufficientDataException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (ErrorResponseException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (InvalidKeyException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (InvalidResponseException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (XmlParserException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (InternalException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 压缩base64格式的图片
|
||||
* @param base64Image 包含前缀的base64图片字符串 (如: "data:image/png;base64,...")
|
||||
* @param targetWidth 目标宽度
|
||||
* @param targetHeight 目标高度
|
||||
* @return 压缩后的base64图片字符串(保持原有前缀格式)
|
||||
*/
|
||||
public String compressBase64Image(String base64Image, int targetWidth, int targetHeight) {
|
||||
if (base64Image == null || base64Image.isEmpty()) {
|
||||
return base64Image;
|
||||
}
|
||||
|
||||
try {
|
||||
// 解析base64字符串
|
||||
String[] parts = base64Image.split(",");
|
||||
if (parts.length != 2) {
|
||||
log.warn("无效的base64图片格式: {}", base64Image.substring(0, Math.min(50, base64Image.length())));
|
||||
return base64Image;
|
||||
}
|
||||
|
||||
String prefix = parts[0] + ","; // 保留前缀,如 "data:image/png;base64,"
|
||||
String base64Data = parts[1];
|
||||
|
||||
// 解码base64数据
|
||||
byte[] imageBytes = Base64.getDecoder().decode(base64Data);
|
||||
|
||||
// 读取图片
|
||||
BufferedImage originalImage = ImageIO.read(new ByteArrayInputStream(imageBytes));
|
||||
if (originalImage == null) {
|
||||
log.warn("无法读取base64图片数据");
|
||||
return base64Image;
|
||||
}
|
||||
|
||||
// 计算压缩比例,保持宽高比
|
||||
int originalWidth = originalImage.getWidth();
|
||||
int originalHeight = originalImage.getHeight();
|
||||
|
||||
double scaleX = (double) targetWidth / originalWidth;
|
||||
double scaleY = (double) targetHeight / originalHeight;
|
||||
double scale = Math.min(scaleX, scaleY); // 选择较小的缩放比例以保持宽高比
|
||||
|
||||
int newWidth = (int) (originalWidth * scale);
|
||||
int newHeight = (int) (originalHeight * scale);
|
||||
|
||||
// 如果图片已经比目标尺寸小,则不进行压缩
|
||||
if (scale >= 1.0) {
|
||||
log.info("图片尺寸 {}x{} 已小于目标尺寸 {}x{},无需压缩", originalWidth, originalHeight, targetWidth, targetHeight);
|
||||
return base64Image;
|
||||
}
|
||||
|
||||
// 创建压缩后的图片
|
||||
BufferedImage compressedImage = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_RGB);
|
||||
Graphics2D g2d = compressedImage.createGraphics();
|
||||
|
||||
// 设置高质量渲染
|
||||
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
|
||||
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
|
||||
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
|
||||
// 绘制压缩后的图片
|
||||
g2d.drawImage(originalImage, 0, 0, newWidth, newHeight, null);
|
||||
g2d.dispose();
|
||||
|
||||
// 转换为Base64
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
ImageIO.write(compressedImage, "PNG", baos); // 保持PNG格式以支持透明度
|
||||
byte[] compressedBytes = baos.toByteArray();
|
||||
String compressedBase64 = Base64.getEncoder().encodeToString(compressedBytes);
|
||||
|
||||
log.info("Base64图片压缩完成: {}x{} -> {}x{} (压缩比: {:.2f})",
|
||||
originalWidth, originalHeight, newWidth, newHeight, scale);
|
||||
|
||||
return prefix + compressedBase64;
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("压缩base64图片失败", e);
|
||||
return base64Image; // 出错时返回原图
|
||||
}
|
||||
}
|
||||
|
||||
public void uploadToMinio(byte[] data, String bucket, String objectName, String contentType) /*throws Exception*/ {
|
||||
try {
|
||||
minioClient.putObject(PutObjectArgs.builder()
|
||||
.bucket(bucket)
|
||||
.object(objectName)
|
||||
.stream(new ByteArrayInputStream(data), data.length, -1)
|
||||
.contentType(contentType)
|
||||
.build());
|
||||
} catch (MinioException | IOException | NoSuchAlgorithmException | InvalidKeyException e){
|
||||
log.error("图片上传到minio出错,{}", e.getMessage());
|
||||
throw new BusinessException("file.upload.fail");
|
||||
}
|
||||
}
|
||||
|
||||
public String changeToWhiteBackground(String minioPath){
|
||||
try {
|
||||
// 1. 使用URL读取远程图片
|
||||
BufferedImage originalImage = ImageIO.read(new URL(getPreSignedUrl(minioPath, CommonConstant.MINIO_IMAGE_EXPIRE_TIME)));
|
||||
|
||||
// 2. 透明图检查(新增核心逻辑)
|
||||
if (!hasTransparency(originalImage)) {
|
||||
log.info("图片 {} 无透明通道,无需添加白底", minioPath);
|
||||
return null; // 返回空
|
||||
}
|
||||
// 3. 处理图片(例如:转换为白底)
|
||||
BufferedImage newImage = new BufferedImage(
|
||||
originalImage.getWidth(),
|
||||
originalImage.getHeight(),
|
||||
BufferedImage.TYPE_INT_RGB
|
||||
);
|
||||
|
||||
// 4. 填充白色背景
|
||||
java.awt.Graphics2D g = newImage.createGraphics();
|
||||
g.setColor(java.awt.Color.WHITE);
|
||||
g.fillRect(0, 0, newImage.getWidth(), newImage.getHeight());
|
||||
g.drawImage(originalImage, 0, 0, null);
|
||||
g.dispose();
|
||||
|
||||
// 5. 输出为Base64
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
ImageIO.write(newImage, "PNG", baos);
|
||||
String base64Image = java.util.Base64.getEncoder().encodeToString(baos.toByteArray());
|
||||
log.info("为图片 {} 添加白色背景", minioPath);
|
||||
// System.out.println("data:image/png;base64," + base64Image);
|
||||
return "data:image/png;base64," + base64Image;
|
||||
// return base64Image;
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage());
|
||||
throw new BusinessException("white.bg.add.fail", ResultEnum.PROMPT.getCode());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测图片是否包含透明通道
|
||||
*/
|
||||
private boolean hasTransparency(BufferedImage image) {
|
||||
// 情况1:图像本身支持透明(如TYPE_INT_ARGB)
|
||||
if (image.getTransparency() == Transparency.TRANSLUCENT) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 情况2:检查像素级透明度(适用于TYPE_INT_RGB等格式)
|
||||
if (image.getColorModel().hasAlpha()) {
|
||||
// 抽样检查前100x100像素(避免全图扫描的性能问题)
|
||||
int width = Math.min(image.getWidth(), 100);
|
||||
int height = Math.min(image.getHeight(), 100);
|
||||
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
if ((image.getRGB(x, y) >> 24) == 0x00) {
|
||||
return true; // 发现透明像素
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将A桶中的对象复制到B桶中
|
||||
* @return
|
||||
*/
|
||||
public void copyObject(String sourceBucket, String sourceObject, String targetBucket, String targetObject) {
|
||||
// 检查目标桶是否存在
|
||||
boolean found;
|
||||
try {
|
||||
found = minioClient.bucketExists(BucketExistsArgs.builder()
|
||||
.bucket(targetBucket)
|
||||
.build());
|
||||
} catch (Exception e) {
|
||||
log.error("目标桶{},不存在", targetBucket);
|
||||
throw new BusinessException("Copy object failed");
|
||||
}
|
||||
|
||||
if (found) {
|
||||
// 复制对象
|
||||
try {
|
||||
minioClient.copyObject(
|
||||
CopyObjectArgs.builder()
|
||||
.bucket(targetBucket)
|
||||
.object(targetObject)
|
||||
.source(
|
||||
CopySource.builder()
|
||||
.bucket(sourceBucket)
|
||||
.object(sourceObject)
|
||||
.build()
|
||||
)
|
||||
.build()
|
||||
);
|
||||
} catch (Exception e) {
|
||||
log.error("对象复制失败");
|
||||
throw new BusinessException("Copy object failed");
|
||||
}
|
||||
log.info("对象复制成功");
|
||||
} else {
|
||||
log.error("目标桶{},不存在", targetBucket);
|
||||
throw new BusinessException("Copy object failed");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -3,11 +3,11 @@ package com.ai.da.common.utils;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import javax.servlet.ReadListener;
|
||||
import javax.servlet.ServletInputStream;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletRequestWrapper;
|
||||
import jakarta.servlet.ReadListener;
|
||||
import jakarta.servlet.ServletInputStream;
|
||||
import jakarta.servlet.ServletRequest;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletRequestWrapper;
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Enumeration;
|
||||
|
||||
@@ -3,10 +3,10 @@ package com.ai.da.common.utils;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.servlet.ServletOutputStream;
|
||||
import javax.servlet.WriteListener;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpServletResponseWrapper;
|
||||
import jakarta.servlet.ServletOutputStream;
|
||||
import jakarta.servlet.WriteListener;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpServletResponseWrapper;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
|
||||
@@ -28,4 +28,70 @@ public class PantoneUtils {
|
||||
return new int[]{Math.round(hsv[0]), Math.round(hsv[1] * 100), Math.round(hsv[2] * 100)};
|
||||
}
|
||||
|
||||
public static int[] hsvToRgb(int h, int s, int v) {
|
||||
// 确保 h 在 [0, 360) 范围内
|
||||
h = h % 360;
|
||||
if (h < 0) {
|
||||
h += 360;
|
||||
}
|
||||
|
||||
// 确保 s 和 v 在 [0, 100] 范围内
|
||||
s = Math.max(0, Math.min(100, s));
|
||||
v = Math.max(0, Math.min(100, v));
|
||||
|
||||
// 将 s 和 v 映射到 [0, 1] 范围
|
||||
float sNorm = s / 100.0f;
|
||||
float vNorm = v / 100.0f;
|
||||
|
||||
// 计算色相所在的区间
|
||||
int hi = (h / 60) % 6;
|
||||
float f = (h / 60.0f) - hi;
|
||||
float p = vNorm * (1 - sNorm);
|
||||
float q = vNorm * (1 - f * sNorm);
|
||||
float t = vNorm * (1 - (1 - f) * sNorm);
|
||||
|
||||
float r, g, b;
|
||||
switch (hi) {
|
||||
case 0:
|
||||
r = vNorm;
|
||||
g = t;
|
||||
b = p;
|
||||
break;
|
||||
case 1:
|
||||
r = q;
|
||||
g = vNorm;
|
||||
b = p;
|
||||
break;
|
||||
case 2:
|
||||
r = p;
|
||||
g = vNorm;
|
||||
b = t;
|
||||
break;
|
||||
case 3:
|
||||
r = p;
|
||||
g = q;
|
||||
b = vNorm;
|
||||
break;
|
||||
case 4:
|
||||
r = t;
|
||||
g = p;
|
||||
b = vNorm;
|
||||
break;
|
||||
case 5:
|
||||
r = vNorm;
|
||||
g = p;
|
||||
b = q;
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException("Invalid HSV values");
|
||||
}
|
||||
|
||||
// 将 RGB 值从 [0, 1] 转换为 [0, 255]
|
||||
int red = Math.round(r * 255);
|
||||
int green = Math.round(g * 255);
|
||||
int blue = Math.round(b * 255);
|
||||
|
||||
return new int[]{red, green, blue};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,16 +1,28 @@
|
||||
package com.ai.da.common.utils;
|
||||
|
||||
import com.ai.da.model.dto.ProgressDTO;
|
||||
import com.ai.da.python.vo.DesignPythonObject;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import io.netty.util.internal.StringUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.core.ValueOperations;
|
||||
import org.springframework.data.redis.core.ZSetOperations;
|
||||
import org.springframework.data.redis.core.script.DefaultRedisScript;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import jakarta.annotation.Resource;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.time.Duration;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -21,6 +33,13 @@ public class RedisUtil {
|
||||
@Resource
|
||||
private RedisTemplate<String, String> redisTemplate;
|
||||
|
||||
public final static String FLUX_POLLING_URL = "Flux:";
|
||||
/**
|
||||
* 登录 token 在 Redis 中的前缀:
|
||||
* 最终 key 结构为 login:token:{userId}
|
||||
*/
|
||||
public final static String LOGIN_TOKEN_KEY = "login:token:";
|
||||
|
||||
public Boolean hasKey(String key){
|
||||
return redisTemplate.hasKey(key);
|
||||
}
|
||||
@@ -83,11 +102,15 @@ public class RedisUtil {
|
||||
|
||||
//- - - - - - - - - - - - - - - - - - - - - set类型 - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public final static String VIDEO_FINISHED_TASKS = "VideoFinishedTasks";
|
||||
|
||||
/**
|
||||
* 将数据放入set缓存
|
||||
*/
|
||||
public void addToSet(String key, String value) {
|
||||
public void addToSet(String key, String value, Long expiresIn) {
|
||||
redisTemplate.opsForSet().add(key, value);
|
||||
// 设置过期时间
|
||||
redisTemplate.expire(key, expiresIn, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -154,6 +177,8 @@ public class RedisUtil {
|
||||
return redisTemplate.keys(key);
|
||||
}
|
||||
|
||||
public Long getSize(String key){return redisTemplate.opsForSet().size(key);}
|
||||
|
||||
public List<String> getMultiValue(Set<String> keys){
|
||||
return redisTemplate.opsForValue().multiGet(keys);
|
||||
}
|
||||
@@ -166,6 +191,40 @@ public class RedisUtil {
|
||||
redisTemplate.delete(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存登录 token
|
||||
*
|
||||
* @param userId 用户 ID
|
||||
* @param token token 字符串
|
||||
* @param expireMillis 过期时间(毫秒,通常与 JWT 保持一致)
|
||||
*/
|
||||
public void setLoginToken(Long userId, String token, long expireMillis) {
|
||||
if (expireMillis <= 0) {
|
||||
// 不设置过期时间,直到手动删除(不推荐)
|
||||
addToString(LOGIN_TOKEN_KEY + userId, token);
|
||||
return;
|
||||
}
|
||||
long expireSeconds = expireMillis / 1000;
|
||||
if (expireSeconds <= 0) {
|
||||
expireSeconds = 1;
|
||||
}
|
||||
addToString(LOGIN_TOKEN_KEY + userId, token, expireSeconds);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取登录 token
|
||||
*/
|
||||
public String getLoginToken(Long userId) {
|
||||
return getFromString(LOGIN_TOKEN_KEY + userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除登录 token
|
||||
*/
|
||||
public void deleteLoginToken(Long userId) {
|
||||
removeFromString(LOGIN_TOKEN_KEY + userId);
|
||||
}
|
||||
|
||||
public final static String PORTFOLIO_LIKE_KEY = "portfolio:like:";
|
||||
|
||||
public void likePost(Long portfolioId, Long userId) {
|
||||
@@ -216,4 +275,385 @@ public class RedisUtil {
|
||||
return redisTemplate.opsForValue().increment(key, 0);
|
||||
}
|
||||
|
||||
}
|
||||
public Long getViewCount(String key) {
|
||||
Object value = redisTemplate.opsForValue().get(key);
|
||||
if (value instanceof Integer) {
|
||||
return Long.valueOf((Integer) value);
|
||||
} else if (value instanceof Long) {
|
||||
return (Long) value;
|
||||
} else if (value instanceof String) {
|
||||
return Long.valueOf((String) value);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unexpected value type");
|
||||
}
|
||||
}
|
||||
|
||||
public final static String PERSONAL_HOMEPAGE_VIEW_KEY = "PersonalHomepage:view:";
|
||||
|
||||
public void increasePersonalHomepageViewCount(Long accountId) {
|
||||
String key = PERSONAL_HOMEPAGE_VIEW_KEY + accountId;
|
||||
redisTemplate.opsForValue().increment(key);
|
||||
}
|
||||
|
||||
public Long getPersonalHomepageViewCount(Long accountId) {
|
||||
String key = PERSONAL_HOMEPAGE_VIEW_KEY + accountId;
|
||||
return redisTemplate.opsForValue().increment(key, 0);
|
||||
}
|
||||
|
||||
public final static String MOODBOARD_POSITION_KEY = "moodboard:position:";
|
||||
|
||||
public void saveMoodboardPosition(Long id, String moodboardPosition) {
|
||||
addToString(MOODBOARD_POSITION_KEY + id, moodboardPosition);
|
||||
}
|
||||
|
||||
public String getMoodboardPosition(Long id) {
|
||||
return getFromString(MOODBOARD_POSITION_KEY + id);
|
||||
}
|
||||
public final static String NICKNAME_MODIFY_TIMES = "NicknameModifyTimes:";
|
||||
public final static String UNNAMED_PROJECT_SEQ = "Project:UnnamedProjectSeq:";
|
||||
public Long increaseCount(String key) {
|
||||
return redisTemplate.opsForValue().increment(key);
|
||||
}
|
||||
|
||||
public Long getIncrementCount(String key) {
|
||||
return redisTemplate.opsForValue().increment(key, 0);
|
||||
}
|
||||
|
||||
public void setKeyExpire(String key, Long expire) {
|
||||
redisTemplate.expire(key, expire, TimeUnit.DAYS);
|
||||
}
|
||||
|
||||
public final static String CHANGE_MAILBOX = "ChangeMailbox:";
|
||||
|
||||
// 每天允许通知3次
|
||||
public final static String UPLOAD_TIMEOUT_REMINDER_COUNTER = "UploadTimeoutReminderCounter";
|
||||
|
||||
public void addProcessId(String processId, int progress) {
|
||||
// Redis 中的键,可以通过 processId 来唯一标识
|
||||
String redisKey = "process:progress:" + processId;
|
||||
|
||||
// 将当前进度存储到 Redis
|
||||
redisTemplate.opsForValue().set(redisKey, String.valueOf(progress));
|
||||
|
||||
// 设置过期时间为 5 分钟(300 秒)
|
||||
redisTemplate.expire(redisKey, 5, TimeUnit.MINUTES);
|
||||
}
|
||||
|
||||
public void addPathToCache(Long collectionId, Long userId, String path) {
|
||||
// Redis 中的键,唯一标识由 collectionId 和 userId 组成
|
||||
String redisKey = "path:cache:" + collectionId + ":" + userId;
|
||||
|
||||
// 增加路径的计数
|
||||
redisTemplate.opsForHash().increment(redisKey, path, 1);
|
||||
|
||||
// 设置过期时间为 2 小时(7200 秒)
|
||||
redisTemplate.expire(redisKey, 8, TimeUnit.HOURS);
|
||||
}
|
||||
|
||||
public int getPathUsageCount(Long collectionId, Long userId, String path) {
|
||||
String redisKey = "path:cache:" + collectionId + ":" + userId;
|
||||
|
||||
// 获取路径的使用次数
|
||||
Object count = redisTemplate.opsForHash().get(redisKey, path);
|
||||
return count != null ? Integer.parseInt(count.toString()) : 0;
|
||||
}
|
||||
|
||||
public void addAssembledObjects(Long collectionId, Set<DesignPythonObject> assembledObjects) {
|
||||
// Redis 中的键,使用 collectionId 来唯一标识
|
||||
String redisKey = "collection:assembledObjects:" + collectionId;
|
||||
|
||||
// 将 assembledObjects 转换为 JSON 格式存储,避免直接存储对象
|
||||
String assembledObjectsJson = convertToJson(assembledObjects);
|
||||
|
||||
// 使用 Redis 的 set 操作更新集合
|
||||
redisTemplate.opsForValue().set(redisKey, assembledObjectsJson);
|
||||
|
||||
// 设置过期时间为 5 分钟(300 秒)
|
||||
redisTemplate.expire(redisKey, 30, TimeUnit.MINUTES);
|
||||
}
|
||||
|
||||
// 将 Set<DesignPythonObject> 转换为 JSON 格式
|
||||
private String convertToJson(Set<DesignPythonObject> assembledObjects) {
|
||||
try {
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
return objectMapper.writeValueAsString(assembledObjects);
|
||||
} catch (JsonProcessingException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public Set<DesignPythonObject> getAssembledObjects(Long collectionId) {
|
||||
// Redis 中的键,使用 collectionId 来唯一标识
|
||||
String redisKey = "collection:assembledObjects:" + collectionId;
|
||||
|
||||
// 从 Redis 获取存储的 JSON 字符串
|
||||
String assembledObjectsJson = (String) redisTemplate.opsForValue().get(redisKey);
|
||||
|
||||
if (assembledObjectsJson == null) {
|
||||
return new HashSet<>(); // 如果没有找到数据,返回一个空的 Set
|
||||
}
|
||||
|
||||
// 将 JSON 字符串转换为 Set<DesignPythonObject>
|
||||
return convertFromJson(assembledObjectsJson);
|
||||
}
|
||||
|
||||
// 将 JSON 字符串转换为 Set<DesignPythonObject>
|
||||
private Set<DesignPythonObject> convertFromJson(String json) {
|
||||
try {
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
// 使用 TypeReference 来指定目标类型是 Set<DesignPythonObject>
|
||||
return objectMapper.readValue(json, new TypeReference<Set<DesignPythonObject>>() {});
|
||||
} catch (JsonProcessingException e) {
|
||||
e.printStackTrace();
|
||||
return new HashSet<>(); // 如果转换失败,返回空的 Set
|
||||
}
|
||||
}
|
||||
|
||||
public final static String PAYMENT_INFO_LAST_SCAN_TIME = "PaymentInfoLastScanTime";
|
||||
|
||||
public final static String AFFILIATE_LINK_VIEW_KEY = "AffiliateLink:view:";
|
||||
|
||||
public void increaseAffiliateLinkViewCount(Long accountId) {
|
||||
String key = AFFILIATE_LINK_VIEW_KEY + accountId;
|
||||
redisTemplate.opsForValue().increment(key);
|
||||
}
|
||||
|
||||
public Long getAffiliateLinkViewCount(Long accountId) {
|
||||
String key = AFFILIATE_LINK_VIEW_KEY + accountId;
|
||||
return redisTemplate.opsForValue().increment(key, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录任务的耗时到Redis
|
||||
* @param taskKey 任务标识,如 "taskA"
|
||||
* @param elapsedTime 本次耗时,单位为毫秒
|
||||
*/
|
||||
public void recordTaskElapsedTime(String taskKey, long elapsedTime) {
|
||||
String hashKey = "task:stats";
|
||||
|
||||
// 累加总耗时
|
||||
redisTemplate.opsForHash().increment(hashKey, taskKey + ":totalTime", elapsedTime);
|
||||
|
||||
// 增加计数器
|
||||
redisTemplate.opsForHash().increment(hashKey, taskKey + ":count", 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取任务的平均耗时
|
||||
* @param taskKey 任务标识,如 "taskA"
|
||||
* @return 平均耗时(毫秒)
|
||||
*/
|
||||
public double getTaskAverageTime(String taskKey) {
|
||||
String hashKey = "task:stats";
|
||||
|
||||
// 获取总耗时和计数
|
||||
Object totalTime = redisTemplate.opsForHash().get(hashKey, taskKey + ":totalTime");
|
||||
Object count = redisTemplate.opsForHash().get(hashKey, taskKey + ":count");
|
||||
|
||||
// 计算平均值
|
||||
if (totalTime == null || count == null) {
|
||||
return 0;
|
||||
}
|
||||
return Double.parseDouble(totalTime.toString()) / Long.parseLong(count.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除指定任务的统计数据
|
||||
* @param taskKey 任务标识,如 "taskA"
|
||||
*/
|
||||
public void clearTaskStats(String taskKey) {
|
||||
String hashKey = "task:stats";
|
||||
|
||||
// 删除总耗时和计数器
|
||||
redisTemplate.opsForHash().delete(hashKey, taskKey + ":totalTime", taskKey + ":count");
|
||||
}
|
||||
|
||||
public void recordTaskElapsedTime(String taskKey, double elapsedTimeInSeconds) {
|
||||
// 将耗时转换为 BigDecimal,并四舍五入保留四位小数
|
||||
BigDecimal elapsedTime = new BigDecimal(elapsedTimeInSeconds).setScale(4, RoundingMode.HALF_UP);
|
||||
|
||||
// 累加总耗时(以毫秒为单位)
|
||||
redisTemplate.opsForHash().increment("task:stats", taskKey + ":totalTime", elapsedTime.doubleValue());
|
||||
|
||||
// 增加计数器
|
||||
redisTemplate.opsForHash().increment("task:stats", taskKey + ":count", 1);
|
||||
}
|
||||
|
||||
// 获取第一部分(Sketch)耗时
|
||||
public double getFirstSketchTime() {
|
||||
// 获取 "firstSketchTime:totalTime" 对应的值,并返回(单位为秒)
|
||||
Object time = redisTemplate.opsForHash().get("task:stats", "firstSketchTime:totalTime");
|
||||
return time != null ? (double) time : 0.0;
|
||||
}
|
||||
|
||||
// 获取第二部分(获取特征值)耗时
|
||||
public double getGetAttributeRecognitionTime() {
|
||||
// 获取 "getAttributeRecognitionTime:totalTime" 对应的值,并返回(单位为秒)
|
||||
Object time = redisTemplate.opsForHash().get("task:stats", "getAttributeRecognitionTime:totalTime");
|
||||
return time != null ? (double) time : 0.0;
|
||||
}
|
||||
|
||||
// 获取第三部分(搭配 Sketch)耗时
|
||||
public double getOtherSketchTime() {
|
||||
// 获取 "otherSketchTime:totalTime" 对应的值,并返回(单位为秒)
|
||||
Object time = redisTemplate.opsForHash().get("task:stats", "otherSketchTime:totalTime");
|
||||
return time != null ? (double) time : 0.0;
|
||||
}
|
||||
|
||||
// 清理三部分的缓存
|
||||
public void clearTaskElapsedTimeCache() {
|
||||
// 删除第一部分的缓存
|
||||
redisTemplate.opsForHash().delete("task:stats", "firstSketchTime:totalTime");
|
||||
redisTemplate.opsForHash().delete("task:stats", "firstSketchTime:count");
|
||||
|
||||
// 删除第二部分的缓存
|
||||
redisTemplate.opsForHash().delete("task:stats", "getAttributeRecognitionTime:totalTime");
|
||||
redisTemplate.opsForHash().delete("task:stats", "getAttributeRecognitionTime:count");
|
||||
|
||||
// 删除第三部分的缓存
|
||||
redisTemplate.opsForHash().delete("task:stats", "otherSketchTime:totalTime");
|
||||
redisTemplate.opsForHash().delete("task:stats", "otherSketchTime:count");
|
||||
}
|
||||
|
||||
public boolean incrementLikeCount(Long userId, String sketchPath) {
|
||||
String redisKey = "user_like_count:" + userId;
|
||||
try {
|
||||
redisTemplate.opsForHash().increment(redisKey, sketchPath, 1);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
log.error("Error incrementing like count for userId {} and sketchPath {}: {}", userId, sketchPath, e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public int getLikeCount(Long userId, String sketchPath) {
|
||||
String redisKey = "user_like_count:" + userId;
|
||||
Object count = redisTemplate.opsForHash().get(redisKey, sketchPath);
|
||||
return count != null ? Integer.parseInt(count.toString()) : 0;
|
||||
}
|
||||
|
||||
public void storeMaxLikeCount(Long userId, int maxLikeCount) {
|
||||
String redisKey = "user_max_like_count:" + userId;
|
||||
redisTemplate.opsForValue().set(redisKey, String.valueOf(maxLikeCount));
|
||||
}
|
||||
|
||||
public int getMaxLikeCount(Long userId) {
|
||||
String redisKey = "user_max_like_count:" + userId;
|
||||
String maxLikeCount = redisTemplate.opsForValue().get(redisKey);
|
||||
return maxLikeCount != null ? Integer.parseInt(maxLikeCount) : 0;
|
||||
}
|
||||
|
||||
public final static String IMAGE_SEGMENTATION = "ImageSegmentation:";
|
||||
|
||||
public final static String STRIPE_EXCEPTION_LOG = "StripeException:";
|
||||
public final static String SUBSCRIPTION_SENT_EMAIL_TYPE = "SubscriptionEmailSentType:";
|
||||
|
||||
public void batchDeleteKeysWithSamePrefix(String prefix){
|
||||
Set<String> keys = redisTemplate.keys(prefix + "*");
|
||||
assert keys != null;
|
||||
if (!keys.isEmpty()){
|
||||
redisTemplate.delete(keys);
|
||||
}
|
||||
}
|
||||
|
||||
public void setTaskProgressDTO(String taskId, ProgressDTO dto) {
|
||||
String key = "task:progress:" + taskId;
|
||||
redisTemplate.opsForValue().set(key, JSON.toJSONString(dto), Duration.ofDays(1));
|
||||
}
|
||||
|
||||
public ProgressDTO getTaskProgressDTO(String taskId) {
|
||||
String key = "task:progress:" + taskId;
|
||||
String json = redisTemplate.opsForValue().get(key);
|
||||
if (StringUtils.isBlank(json)) {
|
||||
// return new ProgressDTO(0, 0, false);
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return JSON.parseObject(json, ProgressDTO.class);
|
||||
} catch (Exception e) {
|
||||
log.warn("任务进度解析失败 key={}, json={}", key, json);
|
||||
return new ProgressDTO(0, 0, false, null);
|
||||
}
|
||||
}
|
||||
|
||||
// Lua脚本(原子化操作)
|
||||
/*private static final String RATE_LIMIT_SCRIPT =
|
||||
"local current = redis.call('INCR', KEYS[1])\n" +
|
||||
"if tonumber(current) == 1 then\n" +
|
||||
" redis.call('EXPIRE', KEYS[1], ARGV[1])\n" +
|
||||
"end\n" +
|
||||
"return tonumber(current) <= tonumber(ARGV[2])";*/
|
||||
private static final String RATE_LIMIT_SCRIPT =
|
||||
"local current = redis.call('INCR', KEYS[1])\n" +
|
||||
"local ttl = redis.call('TTL', KEYS[1])\n" +
|
||||
"if tonumber(current) == 1 or tonumber(ttl) == -1 then\n" +
|
||||
" redis.call('EXPIRE', KEYS[1], ARGV[1])\n" +
|
||||
"end\n" +
|
||||
"return tonumber(current) <= tonumber(ARGV[2])";
|
||||
|
||||
/**
|
||||
* 检查是否允许发送
|
||||
* @param userId 用户ID
|
||||
* @return true-允许发送,false-已超限
|
||||
*/
|
||||
public boolean allowSend(Long userId) {
|
||||
String hourKey = getCurrentHourKey(userId);
|
||||
|
||||
// 执行Lua脚本
|
||||
List<String> keys = Collections.singletonList(hourKey);
|
||||
List<Long> args = Arrays.asList(
|
||||
3600L, // 1小时过期
|
||||
10L // 限制数量 一小时只能向普通用户发10封
|
||||
);
|
||||
|
||||
Boolean result = redisTemplate.execute(
|
||||
new DefaultRedisScript<>(RATE_LIMIT_SCRIPT, Boolean.class),
|
||||
keys,
|
||||
args.toArray()
|
||||
);
|
||||
|
||||
return Boolean.TRUE.equals(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前小时的Key
|
||||
* 格式:email_limit:{userId}:{yyyyMMddHH}
|
||||
*/
|
||||
private String getCurrentHourKey(Long userId) {
|
||||
String hour = LocalDateTime.now()
|
||||
.format(DateTimeFormatter.ofPattern("yyyyMMddHH"));
|
||||
return String.format("email_limit:%s:%s", userId, hour);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前已发送数量
|
||||
*/
|
||||
public int getCurrentCount(Long userId) {
|
||||
String key = getCurrentHourKey(userId);
|
||||
String val = redisTemplate.opsForValue().get(key);
|
||||
int count;
|
||||
if (StringUtils.isBlank(val)){
|
||||
count = 0;
|
||||
}else {
|
||||
count = Integer.parseInt(val);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
public boolean allowRequest(String apiKey) {
|
||||
String key = "rate_limit:" + apiKey;
|
||||
ValueOperations<String, String> ops = redisTemplate.opsForValue();
|
||||
|
||||
// 使用Redis的INCR命令
|
||||
Long count = ops.increment(key, 1);
|
||||
|
||||
if (count == 1) {
|
||||
// 第一次调用,设置过期时间
|
||||
redisTemplate.expire(key, 1, TimeUnit.MINUTES);
|
||||
}
|
||||
|
||||
return count <= 3;
|
||||
}
|
||||
|
||||
}
|
||||
743
src/main/java/com/ai/da/common/utils/RedisUtilEnhance.java
Normal file
743
src/main/java/com/ai/da/common/utils/RedisUtilEnhance.java
Normal file
@@ -0,0 +1,743 @@
|
||||
package com.ai.da.common.utils;
|
||||
|
||||
import com.ai.da.model.dto.ProgressDTO;
|
||||
import com.ai.da.python.vo.DesignPythonObject;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.data.redis.core.*;
|
||||
import org.springframework.data.redis.core.script.DefaultRedisScript;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import jakarta.annotation.Resource;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class RedisUtilEnhance {
|
||||
|
||||
@Resource
|
||||
private RedisTemplate<String, Object> redisTemplate;
|
||||
|
||||
private ValueOperations<String, Object> valueOperations;
|
||||
private SetOperations<String, Object> setOperations;
|
||||
private HashOperations<String, Object, Object> hashOperations;
|
||||
private ZSetOperations<String, Object> zSetOperations;
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
this.valueOperations = redisTemplate.opsForValue();
|
||||
this.setOperations = redisTemplate.opsForSet();
|
||||
this.hashOperations = redisTemplate.opsForHash();
|
||||
this.zSetOperations = redisTemplate.opsForZSet();
|
||||
}
|
||||
|
||||
public final static String FLUX_POLLING_URL = "Flux:";
|
||||
|
||||
public Boolean hasKey(String key) {
|
||||
return redisTemplate.hasKey(key);
|
||||
}
|
||||
|
||||
//- - - - - - - - - - - - - - - - - - - - - ZSet类型 - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
/**
|
||||
* 向ZSet中添加元素
|
||||
*/
|
||||
public void addToZSet(String key, Object value, Double score) {
|
||||
zSetOperations.add(key, value, score);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从ZSet中删除元素
|
||||
*/
|
||||
public void removeFromZSet(String key, Object value) {
|
||||
zSetOperations.remove(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定元素的当前排列顺序
|
||||
*/
|
||||
public Long getRank(String key, Object value) {
|
||||
return zSetOperations.rank(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前ZSet中的最大score
|
||||
*/
|
||||
public Double getMaxScore(String key) {
|
||||
Set<ZSetOperations.TypedTuple<Object>> set = zSetOperations.reverseRangeWithScores(key, 0, 0);
|
||||
|
||||
if (!CollectionUtils.isEmpty(set)) {
|
||||
Iterator<ZSetOperations.TypedTuple<Object>> iterator = set.iterator();
|
||||
if (iterator.hasNext()) {
|
||||
Double score = iterator.next().getScore();
|
||||
return score != null ? score + 1.0 : 1.0;
|
||||
}
|
||||
}
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断元素是否存在
|
||||
*/
|
||||
public Boolean isElementExistsInZSet(String key, Object value) {
|
||||
return zSetOperations.score(key, value) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前ZSet中数据量的总和
|
||||
*/
|
||||
public Long getZSetTotalCount(String key) {
|
||||
return zSetOperations.zCard(key);
|
||||
}
|
||||
|
||||
public Set<Object> getZSetTotalData(String key) {
|
||||
return zSetOperations.range(key, 0, -1);
|
||||
}
|
||||
|
||||
//- - - - - - - - - - - - - - - - - - - - - set类型 - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
/**
|
||||
* 将数据放入set缓存
|
||||
*/
|
||||
public void addToSet(String key, Object value) {
|
||||
setOperations.add(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 弹出变量中的元素
|
||||
*/
|
||||
public void removeFromSet(String key, Object value) {
|
||||
setOperations.remove(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查给定的元素是否在变量中。
|
||||
*/
|
||||
public Boolean isElementExistsInSet(String key, Object obj) {
|
||||
return setOperations.isMember(key, obj);
|
||||
}
|
||||
|
||||
//- - - - - - - - - - - - - - - - - - - - - hash类型 - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
/**
|
||||
* 加入缓存
|
||||
*/
|
||||
public void addToMap(String key, Map<String, String> map) {
|
||||
hashOperations.putAll(key, map);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证指定 key 下 有没有指定的 hashkey
|
||||
*/
|
||||
public Boolean isElementExistsInMap(String key, Object hashKey) {
|
||||
return hashOperations.hasKey(key, hashKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定key的值string
|
||||
*/
|
||||
public String getMapValue(String key1, Object key2) {
|
||||
Object value = hashOperations.get(key1, key2);
|
||||
return value != null ? value.toString() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除指定 hash 的 HashKey
|
||||
*
|
||||
* @return 删除成功的 数量
|
||||
*/
|
||||
public Long removeFromMap(String key, Object hashKeys) {
|
||||
return hashOperations.delete(key, hashKeys);
|
||||
}
|
||||
|
||||
//- - - - - - - - - - - - - - - - - - - - - String/Long/Integer类型支持 - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
/**
|
||||
* 设置字符串值
|
||||
*/
|
||||
public void setString(String key, String value) {
|
||||
valueOperations.set(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置字符串值并设置过期时间
|
||||
*/
|
||||
public void setString(String key, String value, long timeout, TimeUnit unit) {
|
||||
valueOperations.set(key, value, timeout, unit);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置Long值
|
||||
*/
|
||||
public void setLong(String key, Long value) {
|
||||
valueOperations.set(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置Long值并设置过期时间
|
||||
*/
|
||||
public void setLong(String key, Long value, long timeout, TimeUnit unit) {
|
||||
valueOperations.set(key, value, timeout, unit);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置Integer值
|
||||
*/
|
||||
public void setInteger(String key, Integer value) {
|
||||
valueOperations.set(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置Integer值并设置过期时间
|
||||
*/
|
||||
public void setInteger(String key, Integer value, long timeout, TimeUnit unit) {
|
||||
valueOperations.set(key, value, timeout, unit);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置对象值
|
||||
*/
|
||||
public void setObject(String key, Object value, long timeout, TimeUnit unit) {
|
||||
valueOperations.set(key, value, timeout, unit);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取字符串值
|
||||
*/
|
||||
public String getString(String key) {
|
||||
Object value = valueOperations.get(key);
|
||||
return value != null ? value.toString() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Long值
|
||||
*/
|
||||
public Long getLong(String key) {
|
||||
Object value = valueOperations.get(key);
|
||||
if (value instanceof Long) {
|
||||
return (Long) value;
|
||||
} else if (value instanceof Integer) {
|
||||
return ((Integer) value).longValue();
|
||||
} else if (value instanceof String) {
|
||||
try {
|
||||
return Long.parseLong((String) value);
|
||||
} catch (NumberFormatException e) {
|
||||
log.warn("无法将字符串转换为Long: key={}, value={}", key, value);
|
||||
return null;
|
||||
}
|
||||
} else if (value != null) {
|
||||
log.warn("不支持的类型转换到Long: key={}, type={}", key, value.getClass().getName());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Long值,带默认值
|
||||
*/
|
||||
public Long getLong(String key, Long defaultValue) {
|
||||
try {
|
||||
Long value = getLong(key);
|
||||
return value != null ? value : defaultValue;
|
||||
} catch (Exception e) {
|
||||
log.warn("获取Long值失败: key={}", key, e);
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Integer值
|
||||
*/
|
||||
public Integer getInteger(String key) {
|
||||
Object value = valueOperations.get(key);
|
||||
if (value instanceof Integer) {
|
||||
return (Integer) value;
|
||||
} else if (value instanceof Long) {
|
||||
return ((Long) value).intValue();
|
||||
} else if (value instanceof String) {
|
||||
try {
|
||||
return Integer.parseInt((String) value);
|
||||
} catch (NumberFormatException e) {
|
||||
log.warn("无法将字符串转换为Integer: key={}, value={}", key, value);
|
||||
return null;
|
||||
}
|
||||
} else if (value != null) {
|
||||
log.warn("不支持的类型转换到Integer: key={}, type={}", key, value.getClass().getName());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Integer值,带默认值
|
||||
*/
|
||||
public Integer getInteger(String key, Integer defaultValue) {
|
||||
try {
|
||||
Integer value = getInteger(key);
|
||||
return value != null ? value : defaultValue;
|
||||
} catch (Exception e) {
|
||||
log.warn("获取Integer值失败: key={}", key, e);
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 递增操作
|
||||
*/
|
||||
public Long increment(String key, long delta) {
|
||||
return valueOperations.increment(key, delta);
|
||||
}
|
||||
|
||||
/**
|
||||
* 递增操作(double)
|
||||
*/
|
||||
public Double increment(String key, double delta) {
|
||||
return valueOperations.increment(key, delta);
|
||||
}
|
||||
|
||||
//- - - - - - - - - - - - - - - - - - - - - 原有方法适配 - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
public void addToString(String key, String value) {
|
||||
setString(key, value);
|
||||
}
|
||||
|
||||
public void addToString(String key, String value, Long expiresIn) {
|
||||
setString(key, value, expiresIn, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
public String getFromString(String key) {
|
||||
return getString(key);
|
||||
}
|
||||
|
||||
public Set<String> getKeysFromString(String pattern) {
|
||||
Set<String> keys = redisTemplate.keys(pattern);
|
||||
return keys != null ? keys : Collections.emptySet();
|
||||
}
|
||||
|
||||
public Long getSize(String key) {
|
||||
return setOperations.size(key);
|
||||
}
|
||||
|
||||
public List<Object> getMultiValue(Set<String> keys) {
|
||||
return valueOperations.multiGet(keys);
|
||||
}
|
||||
|
||||
public Long getExpire(String key) {
|
||||
return redisTemplate.getExpire(key);
|
||||
}
|
||||
|
||||
public void removeFromString(String key) {
|
||||
redisTemplate.delete(key);
|
||||
}
|
||||
|
||||
public final static String PORTFOLIO_LIKE_KEY = "portfolio:like:";
|
||||
|
||||
public void likePost(Long portfolioId, Long userId) {
|
||||
setOperations.add(PORTFOLIO_LIKE_KEY + portfolioId, userId.toString());
|
||||
}
|
||||
|
||||
public Long getLikeCount(Long portfolioId) {
|
||||
String key = PORTFOLIO_LIKE_KEY + portfolioId;
|
||||
return setOperations.size(key);
|
||||
}
|
||||
|
||||
public List<Long> getLikedPortfolios(Long userId) {
|
||||
// 获取所有包含PORTFOLIO_LIKE_KEY的键
|
||||
Set<String> likedPortfolios = redisTemplate.keys(PORTFOLIO_LIKE_KEY + "*");
|
||||
|
||||
// 如果没有喜欢的,返回空列表
|
||||
if (likedPortfolios == null || likedPortfolios.isEmpty()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
// 过滤出包含指定用户ID的键,并提取投资组合ID
|
||||
return likedPortfolios.stream()
|
||||
.filter(key -> setOperations.isMember(key, userId.toString()))
|
||||
.map(key -> Long.valueOf(key.replace(PORTFOLIO_LIKE_KEY, "")))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public void unLikePost(Long portfolioId, Long userId) {
|
||||
setOperations.remove(PORTFOLIO_LIKE_KEY + portfolioId, userId.toString());
|
||||
}
|
||||
|
||||
// 检查用户是否喜欢某个作品
|
||||
public boolean isPostLikedByUser(Long portfolioId, Long userId) {
|
||||
String key = PORTFOLIO_LIKE_KEY + portfolioId;
|
||||
Boolean isMember = setOperations.isMember(key, userId.toString());
|
||||
return isMember != null && isMember;
|
||||
}
|
||||
|
||||
public final static String PORTFOLIO_VIEW_KEY = "portfolio:view:";
|
||||
|
||||
public void increaseViewCount(Long portfolioId) {
|
||||
String key = PORTFOLIO_VIEW_KEY + portfolioId;
|
||||
increment(key, 1);
|
||||
}
|
||||
|
||||
public Long getViewCount(Long portfolioId) {
|
||||
String key = PORTFOLIO_VIEW_KEY + portfolioId;
|
||||
return getLong(key, 0L);
|
||||
}
|
||||
|
||||
public Long getViewCount(String key) {
|
||||
return getLong(key, 0L);
|
||||
}
|
||||
|
||||
public final static String PERSONAL_HOMEPAGE_VIEW_KEY = "PersonalHomepage:view:";
|
||||
|
||||
public void increasePersonalHomepageViewCount(Long accountId) {
|
||||
String key = PERSONAL_HOMEPAGE_VIEW_KEY + accountId;
|
||||
increment(key, 1);
|
||||
}
|
||||
|
||||
public Long getPersonalHomepageViewCount(Long accountId) {
|
||||
String key = PERSONAL_HOMEPAGE_VIEW_KEY + accountId;
|
||||
return getLong(key, 0L);
|
||||
}
|
||||
|
||||
public final static String MOODBOARD_POSITION_KEY = "moodboard:position:";
|
||||
|
||||
public void saveMoodboardPosition(Long id, String moodboardPosition) {
|
||||
setString(MOODBOARD_POSITION_KEY + id, moodboardPosition);
|
||||
}
|
||||
|
||||
public String getMoodboardPosition(Long id) {
|
||||
return getString(MOODBOARD_POSITION_KEY + id);
|
||||
}
|
||||
|
||||
public final static String NICKNAME_MODIFY_TIMES = "NicknameModifyTimes:";
|
||||
|
||||
public void increaseCount(String key) {
|
||||
increment(key, 1);
|
||||
}
|
||||
|
||||
public Long getIncrementCount(String key) {
|
||||
return getLong(key, 0L);
|
||||
}
|
||||
|
||||
public void setKeyExpire(String key, Long expire) {
|
||||
redisTemplate.expire(key, expire, TimeUnit.DAYS);
|
||||
}
|
||||
|
||||
public final static String CHANGE_MAILBOX = "ChangeMailbox:";
|
||||
|
||||
// 每天允许通知3次
|
||||
public final static String UPLOAD_TIMEOUT_REMINDER_COUNTER = "UploadTimeoutReminderCounter";
|
||||
|
||||
public void addProcessId(String processId, int progress) {
|
||||
// Redis 中的键,可以通过 processId 来唯一标识
|
||||
String redisKey = "process:progress:" + processId;
|
||||
setInteger(redisKey, progress);
|
||||
redisTemplate.expire(redisKey, 5, TimeUnit.MINUTES);
|
||||
}
|
||||
|
||||
public void addPathToCache(Long collectionId, Long userId, String path) {
|
||||
// Redis 中的键,唯一标识由 collectionId 和 userId 组成
|
||||
String redisKey = "path:cache:" + collectionId + ":" + userId;
|
||||
// 增加路径的计数
|
||||
hashOperations.increment(redisKey, path, 1);
|
||||
// 设置过期时间为 2 小时(7200 秒)
|
||||
redisTemplate.expire(redisKey, 2, TimeUnit.HOURS);
|
||||
}
|
||||
|
||||
public int getPathUsageCount(Long collectionId, Long userId, String path) {
|
||||
String redisKey = "path:cache:" + collectionId + ":" + userId;
|
||||
// 获取路径的使用次数
|
||||
Object count = hashOperations.get(redisKey, path);
|
||||
return count != null ? ((Number) count).intValue() : 0;
|
||||
}
|
||||
|
||||
public void addAssembledObjects(Long collectionId, Set<DesignPythonObject> assembledObjects) {
|
||||
// Redis 中的键,使用 collectionId 来唯一标识
|
||||
String redisKey = "collection:assembledObjects:" + collectionId;
|
||||
|
||||
// 将 assembledObjects 转换为 JSON 格式存储,避免直接存储对象
|
||||
String assembledObjectsJson = convertToJson(assembledObjects);
|
||||
if (assembledObjectsJson != null) {
|
||||
// 使用 Redis 的 set 操作更新集合
|
||||
setString(redisKey, assembledObjectsJson);
|
||||
// 设置过期时间为 5 分钟(300 秒)
|
||||
redisTemplate.expire(redisKey, 30, TimeUnit.MINUTES);
|
||||
}
|
||||
}
|
||||
|
||||
private String convertToJson(Set<DesignPythonObject> assembledObjects) {
|
||||
try {
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
return objectMapper.writeValueAsString(assembledObjects);
|
||||
} catch (JsonProcessingException e) {
|
||||
log.error("JSON转换失败", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public Set<DesignPythonObject> getAssembledObjects(Long collectionId) {
|
||||
String redisKey = "collection:assembledObjects:" + collectionId;
|
||||
String assembledObjectsJson = getString(redisKey);
|
||||
if (assembledObjectsJson == null) {
|
||||
return new HashSet<>();
|
||||
}
|
||||
return convertFromJson(assembledObjectsJson);
|
||||
}
|
||||
|
||||
private Set<DesignPythonObject> convertFromJson(String json) {
|
||||
try {
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
return objectMapper.readValue(json, new TypeReference<Set<DesignPythonObject>>() {});
|
||||
} catch (JsonProcessingException e) {
|
||||
log.error("JSON解析失败", e);
|
||||
return new HashSet<>();
|
||||
}
|
||||
}
|
||||
|
||||
public final static String PAYMENT_INFO_LAST_SCAN_TIME = "PaymentInfoLastScanTime";
|
||||
public final static String AFFILIATE_LINK_VIEW_KEY = "AffiliateLink:view:";
|
||||
|
||||
public void increaseAffiliateLinkViewCount(Long accountId) {
|
||||
String key = AFFILIATE_LINK_VIEW_KEY + accountId;
|
||||
increment(key, 1);
|
||||
}
|
||||
|
||||
public Long getAffiliateLinkViewCount(Long accountId) {
|
||||
String key = AFFILIATE_LINK_VIEW_KEY + accountId;
|
||||
return getLong(key, 0L);
|
||||
}
|
||||
|
||||
public Long getAndSetKey(String key, Long count) {
|
||||
Object oldValue = valueOperations.getAndSet(key, count);
|
||||
if (oldValue instanceof Long) {
|
||||
return (Long) oldValue;
|
||||
} else if (oldValue instanceof Integer) {
|
||||
return ((Integer) oldValue).longValue();
|
||||
} else if (oldValue instanceof String) {
|
||||
try {
|
||||
return Long.parseLong((String) oldValue);
|
||||
} catch (NumberFormatException e) {
|
||||
log.warn("无法将字符串转换为Long: key={}, value={}", key, oldValue);
|
||||
return 0L;
|
||||
}
|
||||
}
|
||||
return 0L;
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录任务的耗时到Redis
|
||||
*/
|
||||
public void recordTaskElapsedTime(String taskKey, long elapsedTime) {
|
||||
String hashKey = "task:stats";
|
||||
hashOperations.increment(hashKey, taskKey + ":totalTime", elapsedTime);
|
||||
hashOperations.increment(hashKey, taskKey + ":count", 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取任务的平均耗时
|
||||
*/
|
||||
public double getTaskAverageTime(String taskKey) {
|
||||
String hashKey = "task:stats";
|
||||
Object totalTime = hashOperations.get(hashKey, taskKey + ":totalTime");
|
||||
Object count = hashOperations.get(hashKey, taskKey + ":count");
|
||||
|
||||
if (totalTime == null || count == null) {
|
||||
return 0;
|
||||
}
|
||||
try {
|
||||
double total = ((Number) totalTime).doubleValue();
|
||||
long cnt = ((Number) count).longValue();
|
||||
return cnt > 0 ? total / cnt : 0;
|
||||
} catch (Exception e) {
|
||||
log.warn("计算平均耗时失败: taskKey={}", taskKey, e);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除指定任务的统计数据
|
||||
* @param taskKey 任务标识,如 "taskA"
|
||||
*/
|
||||
public void clearTaskStats(String taskKey) {
|
||||
String hashKey = "task:stats";
|
||||
// 删除总耗时和计数器
|
||||
hashOperations.delete(hashKey, taskKey + ":totalTime", taskKey + ":count");
|
||||
}
|
||||
|
||||
public void recordTaskElapsedTime(String taskKey, double elapsedTimeInSeconds) {
|
||||
// 将耗时转换为 BigDecimal,并四舍五入保留四位小数
|
||||
BigDecimal elapsedTime = new BigDecimal(elapsedTimeInSeconds).setScale(4, RoundingMode.HALF_UP);
|
||||
hashOperations.increment("task:stats", taskKey + ":totalTime", elapsedTime.doubleValue());
|
||||
hashOperations.increment("task:stats", taskKey + ":count", 1);
|
||||
}
|
||||
|
||||
// 获取第一部分(Sketch)耗时
|
||||
public double getFirstSketchTime() {
|
||||
Object time = hashOperations.get("task:stats", "firstSketchTime:totalTime");
|
||||
return time != null ? ((Number) time).doubleValue() : 0.0;
|
||||
}
|
||||
|
||||
// 获取第二部分(获取特征值)耗时
|
||||
public double getGetAttributeRecognitionTime() {
|
||||
Object time = hashOperations.get("task:stats", "getAttributeRecognitionTime:totalTime");
|
||||
return time != null ? ((Number) time).doubleValue() : 0.0;
|
||||
}
|
||||
|
||||
// 获取第三部分(搭配 Sketch)耗时
|
||||
public double getOtherSketchTime() {
|
||||
Object time = hashOperations.get("task:stats", "otherSketchTime:totalTime");
|
||||
return time != null ? ((Number) time).doubleValue() : 0.0;
|
||||
}
|
||||
|
||||
// 清理三部分的缓存
|
||||
public void clearTaskElapsedTimeCache() {
|
||||
String hashKey = "task:stats";
|
||||
Object[] keysToDelete = {
|
||||
"firstSketchTime:totalTime", "firstSketchTime:count",
|
||||
"getAttributeRecognitionTime:totalTime", "getAttributeRecognitionTime:count",
|
||||
"otherSketchTime:totalTime", "otherSketchTime:count"
|
||||
};
|
||||
hashOperations.delete(hashKey, keysToDelete);
|
||||
}
|
||||
|
||||
public boolean incrementLikeCount(Long userId, String sketchPath) {
|
||||
String redisKey = "user_like_count:" + userId;
|
||||
try {
|
||||
hashOperations.increment(redisKey, sketchPath, 1);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
log.error("Error incrementing like count for userId {} and sketchPath {}: {}", userId, sketchPath, e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public int getLikeCount(Long userId, String sketchPath) {
|
||||
String redisKey = "user_like_count:" + userId;
|
||||
Object count = hashOperations.get(redisKey, sketchPath);
|
||||
return count != null ? ((Number) count).intValue() : 0;
|
||||
}
|
||||
|
||||
public void storeMaxLikeCount(Long userId, int maxLikeCount) {
|
||||
String redisKey = "user_max_like_count:" + userId;
|
||||
setInteger(redisKey, maxLikeCount);
|
||||
}
|
||||
|
||||
public int getMaxLikeCount(Long userId) {
|
||||
String redisKey = "user_max_like_count:" + userId;
|
||||
return getInteger(redisKey, 0);
|
||||
}
|
||||
|
||||
public final static String IMAGE_SEGMENTATION = "ImageSegmentation:";
|
||||
public final static String STRIPE_EXCEPTION_LOG = "StripeException:";
|
||||
|
||||
public void batchDeleteKeysWithSamePrefix(String prefix) {
|
||||
Set<String> keys = redisTemplate.keys(prefix + "*");
|
||||
if (keys != null && !keys.isEmpty()) {
|
||||
redisTemplate.delete(keys);
|
||||
}
|
||||
}
|
||||
|
||||
public void setTaskProgressDTO(String taskId, ProgressDTO dto) {
|
||||
String key = "task:progress:" + taskId;
|
||||
setString(key, JSON.toJSONString(dto));
|
||||
redisTemplate.expire(key, 1, TimeUnit.DAYS);
|
||||
}
|
||||
|
||||
public ProgressDTO getTaskProgressDTO(String taskId) {
|
||||
String key = "task:progress:" + taskId;
|
||||
String json = getString(key);
|
||||
if (StringUtils.isBlank(json)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return JSON.parseObject(json, ProgressDTO.class);
|
||||
} catch (Exception e) {
|
||||
log.warn("任务进度解析失败 key={}, json={}", key, json);
|
||||
return new ProgressDTO(0, 0, false, null);
|
||||
}
|
||||
}
|
||||
|
||||
// Lua脚本
|
||||
private static final String RATE_LIMIT_SCRIPT =
|
||||
"local current = redis.call('INCR', KEYS[1])\n" +
|
||||
"local ttl = redis.call('TTL', KEYS[1])\n" +
|
||||
"if tonumber(current) == 1 or tonumber(ttl) == -1 then\n" +
|
||||
" redis.call('EXPIRE', KEYS[1], ARGV[1])\n" +
|
||||
"end\n" +
|
||||
"return tonumber(current) <= tonumber(ARGV[2])";
|
||||
|
||||
/**
|
||||
* 检查是否允许发送
|
||||
* @param userId 用户ID
|
||||
* @return true-允许发送,false-已超限
|
||||
*/
|
||||
public boolean allowSend(Long userId) {
|
||||
String hourKey = getCurrentHourKey(userId);
|
||||
|
||||
// 执行Lua脚本
|
||||
List<String> keys = Collections.singletonList(hourKey);
|
||||
// 1小时过期,限制数量 一小时只能向普通用户发10封
|
||||
String[] args = new String[]{"3600", "10"};
|
||||
|
||||
Boolean result = redisTemplate.execute(
|
||||
new DefaultRedisScript<>(RATE_LIMIT_SCRIPT, Boolean.class),
|
||||
keys,
|
||||
args
|
||||
);
|
||||
|
||||
return Boolean.TRUE.equals(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前小时的Key
|
||||
*/
|
||||
private String getCurrentHourKey(Long userId) {
|
||||
String hour = LocalDateTime.now()
|
||||
.format(DateTimeFormatter.ofPattern("yyyyMMddHH"));
|
||||
return String.format("email_limit:%s:%s", userId, hour);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前已发送数量
|
||||
*/
|
||||
public int getCurrentCount(Long userId) {
|
||||
String key = getCurrentHourKey(userId);
|
||||
return getInteger(key, 0);
|
||||
}
|
||||
|
||||
public boolean allowRequest(String apiKey) {
|
||||
String key = "rate_limit:" + apiKey;
|
||||
// 使用Redis的INCR命令
|
||||
Long count = increment(key, 1);
|
||||
if (count == 1) {
|
||||
redisTemplate.expire(key, 1, TimeUnit.MINUTES);
|
||||
}
|
||||
return count <= 3;
|
||||
}
|
||||
|
||||
// 新增方法:安全删除key
|
||||
public boolean safeDelete(String key) {
|
||||
try {
|
||||
return Boolean.TRUE.equals(redisTemplate.delete(key));
|
||||
} catch (Exception e) {
|
||||
log.error("删除Redis key失败: {}", key, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 新增方法:批量设置过期时间
|
||||
public void batchExpire(Set<String> keys, long timeout, TimeUnit unit) {
|
||||
if (keys != null && !keys.isEmpty()) {
|
||||
for (String key : keys) {
|
||||
redisTemplate.expire(key, timeout, unit);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,16 @@
|
||||
package com.ai.da.common.utils;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.util.Map;
|
||||
|
||||
@Slf4j
|
||||
public class RequestInfoUtil {
|
||||
|
||||
/**
|
||||
@@ -45,4 +54,58 @@ public class RequestInfoUtil {
|
||||
return ip;
|
||||
}
|
||||
|
||||
/**
|
||||
* 免费 API 服务可能有请求频率限制,如果你需要处理大量 IP 地址,可能需要考虑使用付费服务或购买 IP 地理位置数据库。此外,始终要遵守 API 提供商的使用条款和隐私政策
|
||||
* @param ip https://ip-api.com/docs/api:json 使用的接口api
|
||||
* @return
|
||||
* {
|
||||
* "query": "24.48.0.1",
|
||||
* "status": "success",
|
||||
* "country": "Canada",
|
||||
* "countryCode": "CA",
|
||||
* "region": "QC",
|
||||
* "regionName": "Quebec",
|
||||
* "city": "Montreal",
|
||||
* "zip": "H1L",
|
||||
* "lat": 45.6026,
|
||||
* "lon": -73.5167,
|
||||
* "timezone": "America/Toronto",
|
||||
* "isp": "Le Groupe Videotron Ltee",
|
||||
* "org": "Videotron Ltee",
|
||||
* "as": "AS5769 Videotron Ltee"
|
||||
* }
|
||||
*/
|
||||
public static Map getIPLocation(String ip) {
|
||||
// String ip = "117.143.125.1"; // 替换为你想查询的 IP 地址
|
||||
// String ip = "194.5.48.180"; // 替换为你想查询的 IP 地址
|
||||
String apiURL = "http://ip-api.com/json/" + ip;
|
||||
|
||||
try {
|
||||
URL url = new URL(apiURL);
|
||||
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
||||
conn.setRequestMethod("GET");
|
||||
conn.setRequestProperty("Accept", "application/json");
|
||||
|
||||
if (conn.getResponseCode() != 200) {
|
||||
throw new RuntimeException("Failed : HTTP error code : " + conn.getResponseCode());
|
||||
}
|
||||
|
||||
BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
|
||||
String output;
|
||||
StringBuilder outputBuilder = new StringBuilder();
|
||||
// System.out.println("Output from Server .... \n");
|
||||
while ((output = br.readLine()) != null) {
|
||||
outputBuilder.append(output);
|
||||
System.out.println(output);
|
||||
}
|
||||
conn.disconnect();
|
||||
Map map = JSONObject.parseObject(outputBuilder.toString(), Map.class);
|
||||
log.info("map: {}", map);
|
||||
return map;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ import software.amazon.awssdk.services.s3.presigner.S3Presigner;
|
||||
import software.amazon.awssdk.services.s3.presigner.model.GetObjectPresignRequest;
|
||||
import software.amazon.awssdk.services.s3.presigner.model.PresignedGetObjectRequest;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import java.io.*;
|
||||
import java.time.Duration;
|
||||
import java.util.*;
|
||||
@@ -215,7 +215,7 @@ public class S3Util {
|
||||
|
||||
public void deleteObject(String path) {
|
||||
if (!path.contains("/")) {
|
||||
throw new BusinessException("The path is error!");
|
||||
throw new BusinessException("the.path.is.error");
|
||||
}
|
||||
int index = path.indexOf("/");
|
||||
String bucketName = path.substring(0, index);
|
||||
@@ -244,7 +244,7 @@ public class S3Util {
|
||||
return LocalCacheUtils.getPresignedUrlCache(path);
|
||||
} else {
|
||||
if (!path.contains("/")) {
|
||||
throw new BusinessException("The path is error!");
|
||||
throw new BusinessException("the.path.is.error");
|
||||
}
|
||||
int index = path.indexOf("/");
|
||||
String bucketName = path.substring(0, index);
|
||||
@@ -255,6 +255,22 @@ public class S3Util {
|
||||
}
|
||||
}
|
||||
|
||||
public String getPreSignedUrl(String path, int expiry, boolean resetCache) {
|
||||
if (resetCache || LocalCacheUtils.getPresignedUrlCache(path) == null) {
|
||||
if (!path.contains("/")) {
|
||||
throw new BusinessException("the.path.is.error");
|
||||
}
|
||||
int index = path.indexOf("/");
|
||||
String bucketName = path.substring(0, index);
|
||||
String fileName = path.substring(index + 1);
|
||||
String presignedUrl = getPreSignatureUrl(bucketName, fileName, expiry);
|
||||
LocalCacheUtils.setPresignedUrlCache(path, presignedUrl);
|
||||
return presignedUrl;
|
||||
} else {
|
||||
return LocalCacheUtils.getPresignedUrlCache(path);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param keyName key名称: test/2022/06/123.pdf
|
||||
* @param signatureDurationTime 有效期 单位:秒
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
207
src/main/java/com/ai/da/common/utils/SendRequestUtil.java
Normal file
207
src/main/java/com/ai/da/common/utils/SendRequestUtil.java
Normal file
@@ -0,0 +1,207 @@
|
||||
package com.ai.da.common.utils;
|
||||
|
||||
import cn.hutool.http.Header;
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class SendRequestUtil {
|
||||
|
||||
@Value("${ALIYUN_API_KEY}")
|
||||
private String ALIYUN_API_KEY;
|
||||
@Value("${FREEPIK_API_KEY}")
|
||||
private String FREEPIK_API_KEY;
|
||||
|
||||
public String sendAliYunPostAsync(String apiUrl, String requestBody){
|
||||
// 发送POST请求 todo 异常处理
|
||||
HttpResponse execute = HttpRequest.post(apiUrl)
|
||||
.header(Header.AUTHORIZATION, "Bearer " + ALIYUN_API_KEY)
|
||||
.header("X-DashScope-Async", "enable")
|
||||
.header(Header.CONTENT_TYPE, "application/json")
|
||||
.body(requestBody)
|
||||
.timeout(20000) // 设置超时时间20秒
|
||||
.execute();
|
||||
int status = execute.getStatus();
|
||||
if (status == 200){
|
||||
String body = execute.body();
|
||||
JSONObject bodyJson = JSONUtil.parseObj(body);
|
||||
return body;
|
||||
}
|
||||
log.warn("请求失败,状态码为 : {}", status);
|
||||
return null;
|
||||
}
|
||||
|
||||
public String sendAliYunPost(String apiUrl, String requestBody){
|
||||
// 发送POST请求 todo 异常处理
|
||||
HttpResponse execute = HttpRequest.post(apiUrl)
|
||||
.header(Header.AUTHORIZATION, "Bearer " + ALIYUN_API_KEY)
|
||||
.header(Header.CONTENT_TYPE, "application/json")
|
||||
.body(requestBody)
|
||||
.timeout(20000) // 设置超时时间20秒
|
||||
.execute();
|
||||
int status = execute.getStatus();
|
||||
if (status == 200){
|
||||
String body = execute.body();
|
||||
JSONObject bodyJson = JSONUtil.parseObj(body);
|
||||
return body;
|
||||
}
|
||||
log.warn("请求失败,状态码为 : {}", status);
|
||||
return null;
|
||||
}
|
||||
|
||||
public static final String FREE_PIK = "https://api.freepik.com/v1/ai/beta/text-to-image/reimagine-flux";
|
||||
public String sendFreepikPost( String requestBody){
|
||||
// 发送POST请求 todo 异常处理
|
||||
HttpResponse execute = HttpRequest.post(FREE_PIK)
|
||||
.header(Header.CONTENT_TYPE, "application/json")
|
||||
.header("x-freepik-api-key", FREEPIK_API_KEY)
|
||||
.body(requestBody)
|
||||
.timeout(20000) // 设置超时时间20秒
|
||||
.execute();
|
||||
int status = execute.getStatus();
|
||||
if (status == 200){
|
||||
return execute.body();
|
||||
}
|
||||
log.warn("请求失败,状态码为 : {}", status);
|
||||
return null;
|
||||
}
|
||||
|
||||
public String sendAliYunGet(String fullUrl){
|
||||
// 发送GET请求 todo 异常处理
|
||||
HttpResponse httpResponse = HttpRequest.get(fullUrl)
|
||||
.header(Header.AUTHORIZATION, "Bearer " + ALIYUN_API_KEY)
|
||||
.timeout(20000) // 设置超时时间20秒
|
||||
.execute();
|
||||
int status = httpResponse.getStatus();
|
||||
if (status == 200){
|
||||
return httpResponse.body();
|
||||
}else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/*public String sendFluxPost(String url, String requestBodyStr){
|
||||
int status;
|
||||
String body;
|
||||
try (HttpResponse execute = HttpRequest.post(url)
|
||||
.header(Header.CONTENT_TYPE, "application/json")
|
||||
.header("x-key", "d447a0ac-2291-4f1c-9a36-f7614c385989")
|
||||
.body(requestBodyStr) // Hutool 会自动处理 JSON 序列化
|
||||
.timeout(180000) // 设置超时(毫秒)
|
||||
.execute()) {
|
||||
|
||||
status = execute.getStatus();
|
||||
body = execute.body();
|
||||
if (status == 200) {
|
||||
return body;
|
||||
}
|
||||
if (status == 402 || status == 403) {
|
||||
SendEmailUtil.commonExceptionReminder("Flux账户积分不足,flux生成任务",
|
||||
new String[]{"xupei3360@163.com, fangjianliao@aidlab.hk, investigation@aidlab.hk"});
|
||||
}
|
||||
}
|
||||
log.warn("请求失败,状态码为 : {}", status);
|
||||
return null;
|
||||
}*/
|
||||
|
||||
public String sendFluxPost(String url, String requestBodyStr) {
|
||||
// 尝试两个API key
|
||||
String[] apiKeys = {"84e8f5d5-b0b3-49aa-b244-ab7ba27e7ae7",
|
||||
"d447a0ac-2291-4f1c-9a36-f7614c385989"};
|
||||
boolean[] notified = {false, false}; // 记录是否已发送过不足提醒
|
||||
|
||||
for (int i = 0; i < apiKeys.length; i++) {
|
||||
int status;
|
||||
String body;
|
||||
|
||||
try (HttpResponse execute = HttpRequest.post(url)
|
||||
.header(Header.CONTENT_TYPE, "application/json")
|
||||
.header("x-key", apiKeys[i])
|
||||
.body(requestBodyStr)
|
||||
.timeout(180000)
|
||||
.execute()) {
|
||||
|
||||
status = execute.getStatus();
|
||||
body = execute.body();
|
||||
|
||||
if (status == 200) {
|
||||
return body;
|
||||
}
|
||||
|
||||
// 余额不足处理
|
||||
if (status == 402 || status == 403) {
|
||||
if (!notified[i]) {
|
||||
SendEmailUtil.commonExceptionReminder(
|
||||
"Flux账户积分不足,flux生成任务失败。(key:)" + apiKeys[i],
|
||||
new String[]{"xupei3360@163.com", "fangjianliao@aidlab.hk", "investigation@aidlab.hk"}
|
||||
);
|
||||
notified[i] = true;
|
||||
}
|
||||
continue; // 尝试下一个key
|
||||
}
|
||||
|
||||
// 其他错误直接返回null
|
||||
log.warn("请求失败,状态码为:{},使用key:{}", status, apiKeys[i]);
|
||||
return null;
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("请求异常,使用key:{}", apiKeys[i], e);
|
||||
if (i == apiKeys.length - 1) return null; // 最后一个key也失败则返回null
|
||||
}
|
||||
}
|
||||
|
||||
log.warn("所有API key均余额不足");
|
||||
return null;
|
||||
}
|
||||
|
||||
public String sendPost(String url, String requestBodyStr){
|
||||
int status;
|
||||
String body;
|
||||
try (HttpResponse execute = HttpRequest.post(url)
|
||||
.header("Content-Type", "application/json") // 必须设置 Content-Type
|
||||
.body(requestBodyStr) // Hutool 会自动处理 JSON 序列化
|
||||
.timeout(180000) // 设置超时(毫秒)
|
||||
.execute()) {
|
||||
|
||||
status = execute.getStatus();
|
||||
body = execute.body();
|
||||
if (status == 200) {
|
||||
return body;
|
||||
}
|
||||
}
|
||||
log.warn("请求失败,状态码为 : {}, body: {}", status, body);
|
||||
return null;
|
||||
}
|
||||
|
||||
public String sendGet(String url, Map<String, Object> params) {
|
||||
int status;
|
||||
String body;
|
||||
try (HttpResponse execute = HttpRequest.get(url)
|
||||
.form(params) // 直接传入Map,Hutool会正确处理
|
||||
.timeout(180000)
|
||||
.execute()) {
|
||||
|
||||
status = execute.getStatus();
|
||||
body = execute.body();
|
||||
if (status == 200) {
|
||||
return body;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("请求发生异常: {}", e.getMessage(), e);
|
||||
return null;
|
||||
}
|
||||
log.warn("请求失败,状态码为: {}, body: {}", status, body);
|
||||
return body;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
package com.ai.da.common.websocket;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import jakarta.websocket.*;
|
||||
import jakarta.websocket.server.PathParam;
|
||||
import jakarta.websocket.server.ServerEndpoint;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
|
||||
@ServerEndpoint(value = "/notification/{id}")
|
||||
@Component
|
||||
@Slf4j
|
||||
public class NotificationConnection {
|
||||
|
||||
//连接超时
|
||||
public static final long MAX_TIME_OUT = 3 * 60 * 1000;
|
||||
|
||||
private Session session;
|
||||
private Long userId;
|
||||
// 这里用ConcurrentHashMap 因为他是一个线程安全的Map
|
||||
private static ConcurrentHashMap<Long, NotificationConnection> websockets = new ConcurrentHashMap<>();
|
||||
|
||||
@OnOpen
|
||||
public void onOpen(Session session, @PathParam("id") String id) { // 接收到前端传来的用户ID
|
||||
this.session = session;
|
||||
this.userId = Long.valueOf(id);
|
||||
this.session.setMaxIdleTimeout(MAX_TIME_OUT);
|
||||
websockets.put(Long.parseLong(id), this); //将ID作为key,当前的对象作为Value
|
||||
log.info("【建立连接】 用户为:{}", this.session);
|
||||
log.info("【建立连接】 用户Id为:{}", id);
|
||||
log.info("【当前连接总数】 为:{}", websockets.size());
|
||||
}
|
||||
|
||||
@OnClose
|
||||
public void onClose(CloseReason reason) {
|
||||
log.info("【连接断开】 用户为:{}, sessionId: {}, 原因为{}", this.userId, this.session.getId(), reason);
|
||||
log.info("【当前连接总数】 为:{}", websockets.size());
|
||||
websockets.remove(this.userId); // 将当前的对象从集合中删除
|
||||
}
|
||||
|
||||
/**
|
||||
* 错误时调用
|
||||
* @param throwable 异常
|
||||
*/
|
||||
@OnError
|
||||
public void onError(Throwable throwable) {
|
||||
log.info("【连接异常】 用户为:{} , sessionId: {}", this.userId, this.session.getId(), throwable);
|
||||
websockets.remove(this.userId); // 将当前的对象从集合中删除
|
||||
log.info("【当前连接总数】 为:{}", websockets.size());
|
||||
}
|
||||
|
||||
//收到了客户端消息执行的操作
|
||||
@OnMessage
|
||||
public void onMessage(String text){
|
||||
Map<String, String> textMap = JSONObject.parseObject(text, Map.class);
|
||||
log.info("收到了一条来自 {} 的消息:{}, sessionId:{}", this.userId, textMap.get("text"), this.session.getId());
|
||||
// return "已收到你的消息";
|
||||
if (textMap.get("text").equals("PING")){
|
||||
sendMsg(JSON.toJSONString("PONG"), this.userId);
|
||||
}
|
||||
}
|
||||
|
||||
public void sendMsg(String message, Long userId) {
|
||||
if (userId == null) { // 如果等于null则证明是群发
|
||||
// 获取当前Map的一个迭代器,遍历Map的方式有很多种,看着来
|
||||
// 这个就是遍历这个集合的过程....
|
||||
for (Map.Entry<Long, NotificationConnection> entry : websockets.entrySet()) {
|
||||
// 获取每一个Entry实例
|
||||
// 获取每一个Value,而这个Value就是WebSocket的实例
|
||||
NotificationConnection webSocket = entry.getValue();
|
||||
// 接下来就是遍历群发
|
||||
log.info("广播消息 【给用户】 :{}发送消息【{}】", webSocket, message);
|
||||
try {
|
||||
webSocket.session.getBasicRemote().sendText(message); // 发送!!!!!!!!!
|
||||
} catch (IOException e) {
|
||||
log.error("Failed to send message to session {}: {}", webSocket.session.getId(), e.getMessage());
|
||||
}
|
||||
}
|
||||
} else { // 如果不是群发,则判断ID,其余步骤一致
|
||||
// 获取当前Map的一个迭代器,遍历Map的方式有很多种,看着来
|
||||
// 这个就是遍历这个集合的过程....
|
||||
for (Map.Entry<Long, NotificationConnection> entry : websockets.entrySet()) {
|
||||
// 获取每一个Entry实例
|
||||
// 获取每一个Value,而这个Value就是WebSocket的实例
|
||||
NotificationConnection webSocket = entry.getValue();
|
||||
// 获取每一个Key,这个Key就是用户ID
|
||||
Long key = entry.getKey();
|
||||
// 判断用户ID与当前的Key相等
|
||||
if (userId.equals(key)) {
|
||||
log.info("私发消息 【给用户】 :{}发送消息【{}】", key, message); // 打印
|
||||
if (webSocket.session.isOpen()){
|
||||
// 避免因为网络问题或其他原因导致连接突然关闭而报错
|
||||
try {
|
||||
webSocket.session.getBasicRemote().sendText(message); // 则发送给当前的用户即可
|
||||
} catch (IOException e) {
|
||||
log.error("Failed to send message to session {}: {}", webSocket.session.getId(), e.getMessage());
|
||||
// 这里可以选择移除关闭的 session
|
||||
websockets.remove(entry.getKey());
|
||||
}
|
||||
}else {
|
||||
log.info("连接已关闭,sessionId:{}, userId:{}", webSocket.session.getId(), key);
|
||||
websockets.remove(entry.getKey());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.ai.da.common.websocket.config;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
|
||||
|
||||
/**
|
||||
* Configuration of WebSocket
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
|
||||
@ConditionalOnProperty(name = "websocket.enabled", havingValue = "true", matchIfMissing = true)
|
||||
public class WebSocketConfig {
|
||||
@Bean
|
||||
public ServerEndpointExporter serverEndpointExporter() {
|
||||
return new ServerEndpointExporter();
|
||||
}
|
||||
}
|
||||
@@ -1,27 +1,37 @@
|
||||
package com.ai.da.controller;
|
||||
|
||||
import com.ai.da.common.config.exception.BusinessException;
|
||||
import com.ai.da.common.context.UserContext;
|
||||
import com.ai.da.common.response.PageBaseResponse;
|
||||
import com.ai.da.common.response.Response;
|
||||
import com.ai.da.mapper.primary.entity.Account;
|
||||
import com.ai.da.mapper.primary.entity.AccountExtend;
|
||||
import com.ai.da.mapper.primary.entity.TrialOrder;
|
||||
import com.ai.da.model.dto.*;
|
||||
import com.ai.da.model.vo.AccountLoginVO;
|
||||
import com.ai.da.model.vo.AccountPreLoginVO;
|
||||
import com.ai.da.model.vo.BindEmailVO;
|
||||
import com.ai.da.model.vo.PersonalHomepageVO;
|
||||
import com.ai.da.service.AccountService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.validation.Valid;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
|
||||
@Api(tags = "Account模块")
|
||||
@Tag(name = "Account模块")
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/api/account")
|
||||
@@ -30,151 +40,363 @@ public class AccountController {
|
||||
@Resource
|
||||
private AccountService accountService;
|
||||
|
||||
@ApiOperation(value = "预先登入")
|
||||
@Operation(summary = "预先登入")
|
||||
@PostMapping("/preLogin")
|
||||
public Response<AccountPreLoginVO> preLogin(@Valid @RequestBody AccountPreLoginDTO accountDTO) {
|
||||
return Response.success(accountService.preLogin(accountDTO));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "登入")
|
||||
@Operation(summary = "登入")
|
||||
@PostMapping("/login")
|
||||
public Response<AccountLoginVO> login(@Valid @RequestBody AccountLoginDTO accountDTO, HttpServletRequest request) {
|
||||
return Response.success(accountService.login(accountDTO, request));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "绑定邮箱")
|
||||
@Operation(summary = "绑定邮箱")
|
||||
@PostMapping("/bindEmail")
|
||||
public Response<Boolean> bindEmail(@Valid @RequestBody AccountBindEmailDTO accountBindEmailDTO) {
|
||||
return Response.success(accountService.bindEmail(accountBindEmailDTO));
|
||||
public Response<BindEmailVO> bindEmail(@Valid @RequestBody AccountBindEmailDTO accountBindEmailDTO, HttpServletRequest request) {
|
||||
return Response.success(accountService.bindEmail(accountBindEmailDTO, request));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "忘记密码")
|
||||
@Operation(summary = "忘记密码")
|
||||
@PostMapping("/resetPwd")
|
||||
public Response<Boolean> resetPwd(@Valid @RequestBody AccountRegisterDTO accountRegisterDTO) {
|
||||
return Response.success(accountService.forgetPwd(accountRegisterDTO));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "发送邮件")
|
||||
@Operation(summary = "发送邮件")
|
||||
@PostMapping("/sendEmail")
|
||||
public Response<Boolean> sendEmail(@Valid @RequestBody EmailSendDTO emailSendDTO) {
|
||||
return Response.success(accountService.sendEmail(emailSendDTO));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "登出")
|
||||
@Operation(summary = "登出")
|
||||
@PostMapping("/logout")
|
||||
public Response<Boolean> logout(@Valid @RequestBody AccountLogoutDTO accountLogoutDTO) {
|
||||
return Response.success(accountService.logout(accountLogoutDTO));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "是否登入")
|
||||
@Operation(summary = "是否登入")
|
||||
@PostMapping("/isLogin")
|
||||
public Response<Boolean> isLogin(@Valid @RequestBody AccountLogoutDTO accountLogoutDTO) {
|
||||
return Response.success(accountService.isLogin(accountLogoutDTO));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "获取当前用户语言")
|
||||
@Operation(summary = "获取当前用户语言")
|
||||
@PostMapping("/getUserLanguage")
|
||||
public Response<String> getUserLanguage() {
|
||||
return Response.success(accountService.getUserLanguage());
|
||||
}
|
||||
|
||||
@ApiOperation(value = "切换当前用户语言")
|
||||
@Operation(summary = "切换当前用户语言")
|
||||
@GetMapping("/changeUserLanguage")
|
||||
public Response<String> changeUserLanguage(String language) {
|
||||
return Response.success(accountService.changeUserLanguage(language));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "试用用户退出登录")
|
||||
@Operation(summary = "试用用户退出登录")
|
||||
@GetMapping("/trialUserLogout")
|
||||
public Response<Boolean> trialUserLogout() {
|
||||
return Response.success(accountService.trialUserLogout());
|
||||
}
|
||||
|
||||
@ApiOperation(value = "完成新手教程")
|
||||
@Operation(summary = "完成新手教程")
|
||||
@PostMapping("/completeGuidance")
|
||||
public Response<Boolean> completeGuidance() {
|
||||
return Response.success(accountService.completeGuidance());
|
||||
}
|
||||
|
||||
@ApiOperation(value = "试用订单列表")
|
||||
@Operation(summary = "试用订单列表")
|
||||
@PostMapping("/trialOrderList")
|
||||
public Response<PageBaseResponse<TrialOrder>> trialOrderList(@Valid @RequestBody TrialOrderDTO trialOrderDTO) {
|
||||
return Response.success(PageBaseResponse.success(accountService.trialOrderList(trialOrderDTO)));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "通过试用订单审批")
|
||||
@Operation(summary = "通过试用订单审批")
|
||||
@PostMapping("/trialOrderApproval")
|
||||
public Response<Boolean> trialOrderApproval(@RequestParam("ids") List<Long> ids) {
|
||||
return Response.success(accountService.trialOrderApproval(ids));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "拒绝试用订单审批")
|
||||
@Operation(summary = "拒绝试用订单审批")
|
||||
@PostMapping("/trialOrderRefuse")
|
||||
public Response<Boolean> trialOrderRefuse(@RequestParam("ids") List<Long> ids) {
|
||||
return Response.success(accountService.trialOrderRefuse(ids));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "获取是否自动审评")
|
||||
@Operation(summary = "获取是否自动审评")
|
||||
@PostMapping("/getIsAutoApproval")
|
||||
public Response<Boolean> getIsAutoApproval() {
|
||||
return Response.success(accountService.getIsAutoApproval());
|
||||
}
|
||||
|
||||
@ApiOperation(value = "切换是否自动审评")
|
||||
@Operation(summary = "切换是否自动审评")
|
||||
@PostMapping("/switchIsAutoApproval")
|
||||
public Response<Boolean> switchIsAutoApproval() {
|
||||
return Response.success(accountService.switchIsAutoApproval());
|
||||
}
|
||||
|
||||
@ApiOperation(value = "aws状态检测")
|
||||
@Operation(summary = "aws状态检测")
|
||||
@GetMapping("/healthy")
|
||||
@ResponseStatus(HttpStatus.OK)
|
||||
public Response<Map<String,Integer>> checkStatus(){
|
||||
Map<String,Integer> returnMap = new HashMap<>();
|
||||
returnMap.put("code",200);
|
||||
public Response<Map<String, Integer>> checkStatus() {
|
||||
Map<String, Integer> returnMap = new HashMap<>();
|
||||
returnMap.put("code", 200);
|
||||
return Response.success(returnMap);
|
||||
}
|
||||
|
||||
@ApiOperation(value = "查询账号到期时间")
|
||||
@Operation(summary = "查询账号到期时间")
|
||||
@PostMapping("/getExpiredTime")
|
||||
public Response<Long> getExpiredTime(){
|
||||
public Response<Long> getExpiredTime() {
|
||||
return Response.success(accountService.getExpiredTime());
|
||||
}
|
||||
|
||||
@ApiOperation(value = "免密登录")
|
||||
@Operation(summary = "免密登录")
|
||||
@PostMapping("/noLoginRequired")
|
||||
public Response<AccountLoginVO> noLoginRequired(@RequestBody NoLoginRequiredDTO noLoginRequiredDTO, HttpServletRequest request){
|
||||
public Response<AccountLoginVO> noLoginRequired(@RequestBody NoLoginRequiredDTO noLoginRequiredDTO, HttpServletRequest request) {
|
||||
return Response.success(accountService.noLoginRequired(noLoginRequiredDTO, request));
|
||||
}
|
||||
|
||||
@PostMapping("upgradeNotification")
|
||||
@ApiOperation(value = "升级邮件通知")
|
||||
@Operation(summary = "升级邮件通知")
|
||||
public Response<Boolean> upgradeNotification() {
|
||||
accountService.upgradeNotification();
|
||||
return Response.success(true);
|
||||
}
|
||||
|
||||
@CrossOrigin
|
||||
@ApiOperation(value = "广场用户注册")
|
||||
@Operation(summary = "广场用户注册")
|
||||
@PostMapping("/designWorksRegister")
|
||||
public Response<Boolean> designWorksRegister(@Valid @RequestBody AccountDesignWorksRegisterDTO accountDesignWorksRegisterDTO) {
|
||||
return Response.success(accountService.designWorksRegister(accountDesignWorksRegisterDTO));
|
||||
}
|
||||
|
||||
@CrossOrigin
|
||||
@ApiOperation(value = "广场用户注册")
|
||||
@Operation(summary = "广场用户注册")
|
||||
@PostMapping("/designWorksRegisterCode")
|
||||
public Response<AccountLoginVO> designWorksRegisterCode(@Valid @RequestBody AccountDesignWorksRegisterDTO accountDesignWorksRegisterDTO) {
|
||||
return Response.success(accountService.designWorksRegisterCode(accountDesignWorksRegisterDTO));
|
||||
public Response<AccountLoginVO> designWorksRegisterCode(@Valid @RequestBody AccountDesignWorksRegisterDTO accountDesignWorksRegisterDTO,
|
||||
HttpServletRequest request) {
|
||||
return Response.success(accountService.designWorksRegisterCode(accountDesignWorksRegisterDTO, request));
|
||||
}
|
||||
|
||||
/**
|
||||
* 填写调查问卷
|
||||
* @return
|
||||
*/
|
||||
@ApiOperation(value = "填写调查问卷")
|
||||
/* @Operation(summary = "填写调查问卷")
|
||||
@PostMapping("/questionnaire")
|
||||
public Response<Boolean> questionnaire(@Valid @RequestBody String questionnaireInfo){
|
||||
return Response.success(accountService.collectQuestionnaires(questionnaireInfo));
|
||||
}*/
|
||||
|
||||
/**
|
||||
* 参与活动 获取福利
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
/* @Operation(summary = "参与活动 获取福利")
|
||||
@GetMapping("/activity")
|
||||
public Response<String> getActivityBenefits(){
|
||||
return Response.success(accountService.getActivityBenefits());
|
||||
}*/
|
||||
|
||||
@Operation(summary = "将用户账号过期时间设置为过期当天的23:59:59")
|
||||
@GetMapping("/setUserValidToDayEnd")
|
||||
public Response<List<Long>> setUserValidToDayEnd() {
|
||||
return Response.success(accountService.setUserValidToDayEnd());
|
||||
}
|
||||
|
||||
// 用户上传头像
|
||||
@Operation(summary = "上传头像")
|
||||
@PostMapping(path = "/uploadAvatar")
|
||||
public Response<String> uploadAvatar(@RequestParam("file") MultipartFile file) {
|
||||
if (null == file || StringUtils.isEmpty(file.getOriginalFilename())) {
|
||||
throw new BusinessException("file.cannot.be.empty");
|
||||
}
|
||||
return Response.success(accountService.uploadAvatar(file));
|
||||
}
|
||||
|
||||
@Operation(summary = "个人主页浏览量增加")
|
||||
@GetMapping("/viewsIncrease")
|
||||
public Response<Boolean> viewsGet(@RequestParam("id") Long id) {
|
||||
return Response.success(accountService.viewsIncrease(id));
|
||||
}
|
||||
|
||||
@Operation(summary = "获取个人主页信息")
|
||||
@GetMapping("/personalHomepage")
|
||||
public Response<PersonalHomepageVO> getPersonalHomepage(@RequestParam("id") Long id) {
|
||||
return Response.success(accountService.getPersonalHomepage(id));
|
||||
}
|
||||
|
||||
@Operation(summary = "getUsernameModifyTimes")
|
||||
@GetMapping("/getNicknameModifyTimes")
|
||||
public Response<Long> getNicknameModifyTimes() {
|
||||
return Response.success(accountService.getNicknameModifyTimes());
|
||||
}
|
||||
|
||||
@Operation(summary = "editUserName")
|
||||
@GetMapping("/editUserName")
|
||||
public Response<String> editUserName(@RequestParam("newUserName") String newUserName) {
|
||||
accountService.editUserName(newUserName);
|
||||
return Response.success("success");
|
||||
}
|
||||
|
||||
/*@ApiOperation(value = "verifyUserEmail")
|
||||
@GetMapping("/verifyUserEmail")
|
||||
public Response<String> verifyUserEmail(@RequestParam("verifyCode") String verifyCode){
|
||||
accountService.verifyUserEmail(verifyCode);
|
||||
return Response.success("success");
|
||||
}
|
||||
|
||||
@Operation(summary = "changeUserEmail")
|
||||
@GetMapping("/changeUserEmail")
|
||||
public Response<String> changeUserEmail(@RequestParam("newMailbox") String newMailbox){
|
||||
accountService.changeUserEmail(newMailbox);
|
||||
return Response.success("success");
|
||||
}
|
||||
|
||||
@Operation(summary = "activateNewEmail")
|
||||
@GetMapping("/activateNewEmail")
|
||||
public Response<String> activateNewEmail(@RequestParam("token") String token){
|
||||
accountService.activateNewEmail(token);
|
||||
return Response.success("success");
|
||||
}*/
|
||||
|
||||
@PostMapping("halfPricePromotion")
|
||||
@Operation(summary = "十月半价活动")
|
||||
public Response<Boolean> halfPricePromotion() {
|
||||
accountService.halfPricePromotion();
|
||||
return Response.success(true);
|
||||
}
|
||||
|
||||
@PostMapping("temporaryUpgrade")
|
||||
@Operation(summary = "临时升级")
|
||||
public Response<Boolean> temporaryUpgrade() {
|
||||
accountService.temporaryUpgrade();
|
||||
return Response.success(true);
|
||||
}
|
||||
|
||||
@PostMapping("enterpriseLogin")
|
||||
@Operation(summary = "企业登录")
|
||||
public Response<AccountPreLoginVO> enterpriseLogin(@Valid @RequestBody AccountLoginDTO accountDTO) {
|
||||
return Response.success(accountService.enterpriseLogin(accountDTO));
|
||||
}
|
||||
|
||||
@PostMapping("schoolLogin")
|
||||
@Operation(summary = "学校登录")
|
||||
public Response<AccountPreLoginVO> schoolLogin(@Valid @RequestBody AccountLoginDTO accountDTO) {
|
||||
return Response.success(accountService.schoolLogin(accountDTO));
|
||||
}
|
||||
|
||||
@PostMapping("organizationNameSearch")
|
||||
@Operation(summary = "组织名模糊查询")
|
||||
public Response<Set<String>> organizationNameSearch(@RequestParam("type") String type, @RequestParam(value = "name", required = false) String name) {
|
||||
return Response.success(accountService.organizationNameSearch(type, name));
|
||||
}
|
||||
|
||||
@PostMapping("addOrUpdateSubAccount")
|
||||
@Operation(summary = "子账号新增")
|
||||
public Response<Boolean> addSubAccount(@Valid @RequestBody AddSubAccountDTO addSubAccountDTO) {
|
||||
return Response.success(accountService.addSubAccount(addSubAccountDTO));
|
||||
}
|
||||
|
||||
@PostMapping("deleteSubAccount")
|
||||
@Operation(summary = "子账号删除")
|
||||
public Response<Boolean> deleteSubAccount(@Valid @RequestBody AddSubAccountDTO addSubAccountDTO) {
|
||||
// return Response.success(accountService.deleteSubAccount(addSubAccountDTO));
|
||||
accountService.removeSubAccount(addSubAccountDTO, UserContext.getUserHolder().getId());
|
||||
return Response.success();
|
||||
}
|
||||
|
||||
@PostMapping("subAccountList")
|
||||
@Operation(summary = "子账号查询")
|
||||
public Response<PageBaseResponse<Account>> subAccountList(@Valid @RequestBody SubAccountPageDTO subAccountPageDTO) {
|
||||
return Response.success(accountService.subAccountList(subAccountPageDTO));
|
||||
}
|
||||
|
||||
@GetMapping("accountDetail")
|
||||
@Operation(summary = "账号详情")
|
||||
public Response<Account> accountDetail(@RequestParam("id") Long id) {
|
||||
return Response.success(accountService.accountDetail(id));
|
||||
}
|
||||
|
||||
@PostMapping("getAccountDetail")
|
||||
@Operation(summary = "获取账户信息")
|
||||
public Response<AccountLoginVO> getAccountDetail() {
|
||||
return Response.success(accountService.getAccountDetail());
|
||||
}
|
||||
|
||||
@GetMapping("/bindGoogle")
|
||||
@Operation(summary = "绑定谷歌")
|
||||
public Response<AccountExtend> bindGoogle(@RequestParam("credential") String credential) {
|
||||
return Response.success(accountService.bindGoogle(credential));
|
||||
}
|
||||
|
||||
@GetMapping("/bindWeChat")
|
||||
@Operation(summary = "绑定微信")
|
||||
public Response<AccountExtend> bindWeChat(@RequestParam("code") String code) {
|
||||
return Response.success(accountService.bindWeChat(code));
|
||||
}
|
||||
|
||||
@GetMapping("/bindEmail")
|
||||
@Operation(summary = "绑定邮箱")
|
||||
public Response<BindEmailVO> bindEmail(@RequestParam("email") String email) {
|
||||
return Response.success(accountService.bindEmail(email));
|
||||
}
|
||||
|
||||
@GetMapping("/unbindWeChat")
|
||||
@Operation(summary = "解除绑定微信")
|
||||
public Response<Boolean> unbindWeChat() {
|
||||
return Response.success(accountService.unbindWeChat());
|
||||
}
|
||||
|
||||
@GetMapping("/unbindGoogle")
|
||||
@Operation(summary = "解除绑定谷歌")
|
||||
public Response<Boolean> unbindGoogle() {
|
||||
return Response.success(accountService.unbindGoogle());
|
||||
}
|
||||
|
||||
@PostMapping("/updateUserInfo")
|
||||
@Operation(summary = "更新用户国家、职业信息")
|
||||
public Response<Boolean> updateUserInfo(@Valid @RequestBody UpdateUserInfoDTO updateUserInfoDTO) {
|
||||
return Response.success(accountService.updateUserInfo(updateUserInfoDTO));
|
||||
}
|
||||
|
||||
@GetMapping("/subAccountImportExcelDownload")
|
||||
@Operation(summary = "模板下载")
|
||||
public void subAccountImportExcelDownload(HttpServletResponse response) {
|
||||
accountService.subAccountImportExcelDownload(response);
|
||||
}
|
||||
|
||||
@GetMapping("/exportAccountsToExcel")
|
||||
@Operation(summary = "下载子账号信息")
|
||||
public void exportAccountsToExcel(HttpServletResponse response) {
|
||||
accountService.exportAccountsToExcel(response);
|
||||
}
|
||||
|
||||
@PostMapping("/subAccountImport")
|
||||
@Operation(summary = "模板导入")
|
||||
public Response<Boolean> subAccountImport(@RequestParam("file") MultipartFile file) {
|
||||
return Response.success(accountService.subAccountImport(file));
|
||||
}
|
||||
|
||||
/*@GetMapping("/send618Email")
|
||||
@Operation(summary = "618邮件发送")
|
||||
public Response<String> send618PromotionEmailTemp() {
|
||||
accountService.send618PromotionEmailTemp();
|
||||
return Response.success("success");
|
||||
}*/
|
||||
|
||||
/*@GetMapping("/refreshCreditsMonthly")
|
||||
@Operation(summary = "刷新子账号积分")
|
||||
public void refreshCreditsMonthly() {
|
||||
accountService.refreshCreditsMonthly();
|
||||
}*/
|
||||
/*@GetMapping("/checkEduAdminExpireStatus")
|
||||
@Operation(summary = "检查教育管理员账号到期情况")
|
||||
public void checkEduAdminExpireStatus() {
|
||||
accountService.checkEduAdminExpireStatus();
|
||||
}*/
|
||||
|
||||
|
||||
}
|
||||
|
||||
136
src/main/java/com/ai/da/controller/AffiliateController.java
Normal file
136
src/main/java/com/ai/da/controller/AffiliateController.java
Normal file
@@ -0,0 +1,136 @@
|
||||
package com.ai.da.controller;
|
||||
|
||||
|
||||
import com.ai.da.common.response.PageBaseResponse;
|
||||
import com.ai.da.common.response.Response;
|
||||
import com.ai.da.model.dto.AffiliateQueryDTO;
|
||||
import com.ai.da.model.dto.EditReferralDTO;
|
||||
import com.ai.da.model.dto.ReferralPageQueryDTO;
|
||||
import com.ai.da.model.vo.AffiliateInvitationDetailsVO;
|
||||
import com.ai.da.model.vo.AffiliateVO;
|
||||
import com.ai.da.model.vo.ReferralPageQueryVO;
|
||||
import com.ai.da.service.AffiliateService;
|
||||
import com.ai.da.service.ReferralService;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/api/affiliate")
|
||||
@Tag(name = "Affiliate模块")
|
||||
public class AffiliateController {
|
||||
|
||||
@Resource
|
||||
private AffiliateService affiliateService;
|
||||
|
||||
@Resource
|
||||
private ReferralService referralService;
|
||||
|
||||
@Operation(summary = "注册成为affiliate")
|
||||
@GetMapping("/registration")
|
||||
public Response<Boolean> completeGuidance(@RequestParam(value = "promotionMethod", required = false) String promotionMethod) {
|
||||
return Response.success(affiliateService.registerAsAnAffiliate(promotionMethod));
|
||||
}
|
||||
|
||||
@Operation(summary = "获取affiliate列表")
|
||||
@PostMapping("/list")
|
||||
public Response<PageBaseResponse<AffiliateVO>> getAffiliateList(@Validated @RequestBody AffiliateQueryDTO affiliateQueryDTO) {
|
||||
return Response.success(affiliateService.getAffiliateList(affiliateQueryDTO));
|
||||
}
|
||||
|
||||
@Operation(summary = "获取affiliate个人中心")
|
||||
@GetMapping("/personalCenter")
|
||||
public Response<AffiliateVO> personalAffiliateCenter() {
|
||||
return Response.success(affiliateService.personalAffiliateCenter());
|
||||
}
|
||||
|
||||
@Operation(summary = "获取个人佣金图表数据")
|
||||
@GetMapping("/getPersonalMonthlyIncome")
|
||||
public Response<BigDecimal[]> getPersonalMonthlyIncome(@RequestParam("year") int year) {
|
||||
return Response.success(affiliateService.getPersonalMonthlyIncome(year));
|
||||
}
|
||||
|
||||
@Operation(summary = "审批affiliate申请")
|
||||
@GetMapping("/approval")
|
||||
public Response<Boolean> applicationApproval(@RequestParam("id") Long id,
|
||||
@RequestParam("isApproved") Boolean isApproved,
|
||||
@RequestParam(value = "commission", required = false) Float commission) {
|
||||
return Response.success(affiliateService.applicationApproval(id, isApproved, commission));
|
||||
}
|
||||
|
||||
@Operation(summary = "编辑affiliate")
|
||||
@GetMapping("/editAffiliate")
|
||||
public Response<String> editAffiliate(@RequestParam("id") Long id,
|
||||
@Parameter(description = "佣金比例", example = "25")
|
||||
@RequestParam(value = "commission", required = false) Float commission,
|
||||
@Parameter(description = "操作类型", example = "Active",
|
||||
schema = @Schema(allowableValues = {"Active", "Inactive", "Delete"}))
|
||||
@RequestParam(value = "operationType", required = false) String operationType) {
|
||||
affiliateService.editAffiliate(id, commission, operationType);
|
||||
return Response.success("success");
|
||||
}
|
||||
|
||||
/*@ApiOperation(value = "定时计算佣金")
|
||||
@GetMapping("/testTask")
|
||||
public Response<String> testTask() {
|
||||
affiliateService.updateAffiliateInfoWithPayment();
|
||||
return Response.success("success ");
|
||||
}*/
|
||||
|
||||
/*@ApiOperation(value = "每月发送结算邮件")
|
||||
@GetMapping("/commissionCalculation")
|
||||
public Response<String> commissionCalculation(@RequestParam("year") Integer year, @RequestParam("month") Integer month) {
|
||||
affiliateService.commissionCalculation(year, month);
|
||||
return Response.success("success ");
|
||||
}*/
|
||||
|
||||
@Operation(summary = "affiliate链接浏览量增加")
|
||||
@GetMapping("/viewsIncrease")
|
||||
public Response<Boolean> viewsGet(@RequestParam("id") Long id) {
|
||||
return Response.success(affiliateService.affiliateLinkViewsIncrease(id));
|
||||
}
|
||||
|
||||
@Operation(summary = "获取每个affiliate产生的收入")
|
||||
@PostMapping("/getEachAffiliateGeneratedRevenue")
|
||||
public Response<IPage<AffiliateInvitationDetailsVO>> getEachAffiliateGeneratedRevenue(@Validated @RequestBody AffiliateQueryDTO affiliateQueryDTO) {
|
||||
return Response.success(affiliateService.getEachAffiliateGeneratedRevenue(affiliateQueryDTO));
|
||||
}
|
||||
|
||||
@Operation(summary = "分页获取所有的referral")
|
||||
@PostMapping("/getReferrals")
|
||||
public Response<IPage<ReferralPageQueryVO>> getReferrals(@RequestBody ReferralPageQueryDTO referralPageQueryDTO) {
|
||||
return Response.success(referralService.queryByPage(referralPageQueryDTO));
|
||||
}
|
||||
|
||||
@Operation(summary = "编辑单个referral")
|
||||
@PostMapping("/editReferral")
|
||||
public Response<String> editReferral(@Validated @RequestBody EditReferralDTO editReferralDTO) {
|
||||
referralService.editReferral(editReferralDTO);
|
||||
return Response.success();
|
||||
}
|
||||
|
||||
@Operation(summary = "(批量)删除referral")
|
||||
@PostMapping("/batchDeleteReferral")
|
||||
public Response<String> batchDeleteReferral(@RequestBody List<Long> idList) {
|
||||
referralService.deleteReferral(idList);
|
||||
return Response.success();
|
||||
}
|
||||
|
||||
@Operation(summary = "获取所有affiliate用户名")
|
||||
@GetMapping("/getAllAffiliateUsername")
|
||||
public Response<List<Map<String, Object>>> getAllAffiliateUsername() {
|
||||
return Response.success(affiliateService.getAllAffiliateUsername());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,38 +1,41 @@
|
||||
package com.ai.da.controller;
|
||||
|
||||
import com.ai.da.common.response.Response;
|
||||
import com.ai.da.model.dto.ProductPurchaseDTO;
|
||||
import com.ai.da.service.AliPayService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.validation.Valid;
|
||||
import java.util.Map;
|
||||
|
||||
@CrossOrigin
|
||||
@RestController
|
||||
@RequestMapping("/api/ali-pay")
|
||||
@Api(tags = "网站支付宝支付")
|
||||
@Tag(name = "网站支付宝支付")
|
||||
@Slf4j
|
||||
public class AliPayController {
|
||||
|
||||
@Resource
|
||||
private AliPayService aliPayService;
|
||||
|
||||
@ApiOperation("统一收单下单并支付页面接口的调用")
|
||||
@PostMapping("/trade/page/pay/{amount}")
|
||||
public Response<String> tradePagePay(@PathVariable Integer amount, @RequestParam String returnUrl){
|
||||
@Operation(summary = "统一收单下单并支付页面接口的调用")
|
||||
@PostMapping("/trade/page/pay")
|
||||
public Response<String> tradePagePay(@Valid @RequestBody ProductPurchaseDTO productPurchaseDTO, HttpServletRequest request){
|
||||
log.info("统一收单下单并支付页面接口的调用");
|
||||
//支付宝开放平台接受 request 请求对象后
|
||||
// 会为开发者生成一个html 形式的 form表单,包含自动提交的脚本
|
||||
String formStr = aliPayService.tradeCreate(amount, returnUrl);
|
||||
String formStr = aliPayService.tradeCreate(productPurchaseDTO, request);
|
||||
//我们将form表单字符串返回给前端程序,之后前端将会调用自动提交脚本,进行表单的提交
|
||||
//此时,表单会自动提交到action属性所指向的支付宝开放平台中,从而为用户展示一个支付页面
|
||||
return Response.success(formStr);
|
||||
}
|
||||
|
||||
@ApiOperation("支付通知")
|
||||
@Operation(summary = "支付通知")
|
||||
@PostMapping("/trade/notify")
|
||||
public String tradeNotify(@RequestParam Map<String, String> params){
|
||||
return aliPayService.tradeNotify(params);
|
||||
@@ -43,7 +46,7 @@ public class AliPayController {
|
||||
* @param orderNo
|
||||
* @return
|
||||
*/
|
||||
@ApiOperation("用户取消订单")
|
||||
@Operation(summary = "用户取消订单")
|
||||
@PostMapping("/trade/close/{orderNo}")
|
||||
public Response<String> cancel(@PathVariable String orderNo){
|
||||
log.info("取消订单");
|
||||
@@ -56,7 +59,7 @@ public class AliPayController {
|
||||
* @param orderNo
|
||||
* @return
|
||||
*/
|
||||
@ApiOperation("查询订单:测试订单状态用")
|
||||
@Operation(summary = "查询订单:测试订单状态用")
|
||||
@GetMapping("/trade/query/{orderNo}")
|
||||
public Response<String> queryOrder(@PathVariable String orderNo) {
|
||||
log.info("查询订单");
|
||||
@@ -71,7 +74,7 @@ public class AliPayController {
|
||||
* @param reason
|
||||
* @return
|
||||
*/
|
||||
@ApiOperation("申请退款")
|
||||
@Operation(summary = "申请退款")
|
||||
@PostMapping("/trade/refund/{orderNo}/{reason}")
|
||||
public Response<String> refunds(@PathVariable String orderNo, @PathVariable String reason){
|
||||
log.info("申请退款");
|
||||
@@ -85,7 +88,7 @@ public class AliPayController {
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
@ApiOperation("查询退款:测试用")
|
||||
@Operation(summary = "查询退款:测试用")
|
||||
@GetMapping("/trade/fastpay/refund/{orderNo}")
|
||||
public Response<String> queryRefund(@PathVariable String orderNo) {
|
||||
log.info("查询退款");
|
||||
@@ -100,7 +103,7 @@ public class AliPayController {
|
||||
* @param type
|
||||
* @return
|
||||
*/
|
||||
@ApiOperation("获取账单url")
|
||||
@Operation(summary = "获取账单url")
|
||||
@GetMapping("/bill/downloadurl/query/{billDate}/{type}")
|
||||
public Response<String> queryTradeBill(
|
||||
@PathVariable String billDate,
|
||||
|
||||
@@ -1,39 +1,42 @@
|
||||
package com.ai.da.controller;
|
||||
|
||||
import com.ai.da.common.response.Response;
|
||||
import com.ai.da.model.dto.ProductPurchaseDTO;
|
||||
import com.ai.da.service.AlipayHKService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.validation.Valid;
|
||||
|
||||
@CrossOrigin
|
||||
@RestController
|
||||
@RequestMapping("/api/alipay-hk")
|
||||
@Api(tags = "网站支付 香港支付宝")
|
||||
@Tag(name = "网站支付 香港支付宝")
|
||||
@Slf4j
|
||||
public class AlipayHKController {
|
||||
|
||||
@Resource
|
||||
private AlipayHKService alipayHKService;
|
||||
|
||||
@ApiOperation(value = "创建订单")
|
||||
@PostMapping(value = "/createOrder/{wallet}/{amount}")
|
||||
public Response<String> createOrder(@PathVariable Integer amount, @PathVariable String wallet) {
|
||||
String order = alipayHKService.createOrder(amount, wallet);
|
||||
@Operation(summary = "创建订单")
|
||||
@PostMapping(value = "/createOrder")
|
||||
public Response<String> createOrder(@Valid @RequestBody ProductPurchaseDTO productPurchaseDTO, HttpServletRequest request) {
|
||||
String order = alipayHKService.createOrder(productPurchaseDTO, request);
|
||||
return Response.success(order);
|
||||
}
|
||||
|
||||
@ApiOperation("支付通知")
|
||||
@Operation(summary = "支付通知")
|
||||
@PostMapping("/trade/notify")
|
||||
public String callback(@RequestBody String paramString){
|
||||
log.info("alipay-hk callback parameter : {}", paramString);
|
||||
return alipayHKService.callback(paramString);
|
||||
}
|
||||
|
||||
@ApiOperation("订单查询")
|
||||
@Operation(summary = "订单查询")
|
||||
@PostMapping("/trade/query/{orderRef}")
|
||||
public Response<String> queryOrder(@PathVariable String orderRef){
|
||||
String s = alipayHKService.queryDetail(orderRef);
|
||||
|
||||
@@ -5,15 +5,14 @@ import com.ai.da.model.dto.ClassificationDTO;
|
||||
import com.ai.da.model.vo.ClassificationVO;
|
||||
import com.ai.da.service.ClassificationService;
|
||||
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.validation.Valid;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -26,7 +25,7 @@ import java.util.List;
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@RequestMapping("/api/classification")
|
||||
@Api(value = "", tags = "分类")
|
||||
@Tag(name = "分类")
|
||||
public class ClassificationController {
|
||||
|
||||
@Resource
|
||||
@@ -37,7 +36,7 @@ public class ClassificationController {
|
||||
*/
|
||||
@PostMapping("/saveOrUpdate")
|
||||
@ApiOperationSupport(order = 1)
|
||||
@ApiOperation(value = "新增修改", notes = "传入ClassificationDTO")
|
||||
@Operation(summary = "新增修改", description = "传入ClassificationDTO")
|
||||
public Response<Boolean> saveOrUpdate(@Valid @RequestBody ClassificationDTO classificationDTO) {
|
||||
return Response.success(classificationService.saveOrUpdate(classificationDTO));
|
||||
}
|
||||
@@ -47,28 +46,28 @@ public class ClassificationController {
|
||||
*/
|
||||
@PostMapping("/delete")
|
||||
@ApiOperationSupport(order = 2)
|
||||
@ApiOperation(value = "删除", notes = "传入ClassificationDTO")
|
||||
@Operation(summary = "删除", description = "传入ClassificationDTO")
|
||||
public Response<Boolean> delete(@Valid @RequestBody ClassificationDTO classificationDTO) {
|
||||
return Response.success(classificationService.delete(classificationDTO));
|
||||
}
|
||||
|
||||
@PostMapping("/queryClassification")
|
||||
@ApiOperationSupport(order = 3)
|
||||
@ApiModelProperty(value = "查询", notes = "传入ClassificationDTO")
|
||||
@Operation(summary = "查询", description = "传入ClassificationDTO")
|
||||
public Response<List<ClassificationVO>> queryClassification(@Valid @RequestBody ClassificationDTO classificationDTO) {
|
||||
return Response.success(classificationService.queryClassification(classificationDTO));
|
||||
}
|
||||
|
||||
@PostMapping("/relationLibrary")
|
||||
@ApiOperationSupport(order = 4)
|
||||
@ApiModelProperty(value = "关联", notes = "传入ClassificationDTO")
|
||||
@Operation(summary = "关联", description = "传入ClassificationDTO")
|
||||
public Response<Boolean> relationLibrary(@Valid @RequestBody ClassificationDTO classificationDTO) {
|
||||
return Response.success(classificationService.relationLibrary(classificationDTO));
|
||||
}
|
||||
|
||||
@PostMapping("/getRelClassificationIdList")
|
||||
@ApiOperationSupport(order = 5)
|
||||
@ApiModelProperty(value = "获取关联分类IDList", notes = "传入ClassificationDTO")
|
||||
@Operation(summary = "获取关联分类IDList", description = "传入ClassificationDTO")
|
||||
public Response<List<Long>> getRelClassificationIdList(@Valid @RequestBody ClassificationDTO classificationDTO) {
|
||||
return Response.success(classificationService.getRelClassificationIdList(classificationDTO));
|
||||
}
|
||||
@@ -80,7 +79,7 @@ public class ClassificationController {
|
||||
*/
|
||||
@PostMapping("/getRelPublicClassificationIdList")
|
||||
@ApiOperationSupport(order = 5)
|
||||
@ApiModelProperty(value = "获取关联公共分类IDList", notes = "传入ClassificationDTO")
|
||||
@Operation(summary = "获取关联公共分类IDList", description = "传入ClassificationDTO")
|
||||
public Response<List<Long>> getRelPublicClassificationIdList(@Valid @RequestBody ClassificationDTO classificationDTO) {
|
||||
return Response.success(classificationService.getRelPublicClassificationIdList(classificationDTO));
|
||||
}
|
||||
@@ -92,7 +91,7 @@ public class ClassificationController {
|
||||
*/
|
||||
@PostMapping("/editRelPublicClassificationIdList")
|
||||
@ApiOperationSupport(order = 5)
|
||||
@ApiModelProperty(value = "编辑关联公共分类IDList", notes = "传入ClassificationDTO")
|
||||
@Operation(summary = "编辑关联公共分类IDList", description = "传入ClassificationDTO")
|
||||
public Response<Boolean> editRelPublicClassificationIdList(@Valid @RequestBody ClassificationDTO classificationDTO) {
|
||||
return Response.success(classificationService.editRelPublicClassificationIdList(classificationDTO));
|
||||
}
|
||||
|
||||
@@ -2,62 +2,230 @@ package com.ai.da.controller;
|
||||
|
||||
|
||||
import com.ai.da.common.context.UserContext;
|
||||
import com.ai.da.common.response.PageBaseResponse;
|
||||
import com.ai.da.common.response.Response;
|
||||
import com.ai.da.mapper.primary.DesignMapper;
|
||||
import com.ai.da.mapper.primary.TrialOrderMapper;
|
||||
import com.ai.da.mapper.primary.entity.Account;
|
||||
import com.ai.da.mapper.primary.entity.Organization;
|
||||
import com.ai.da.mapper.primary.entity.TrialOrder;
|
||||
import com.ai.da.model.dto.UserDesignStatisticDTO;
|
||||
import com.ai.da.model.dto.*;
|
||||
import com.ai.da.model.vo.PaymentInfoVO;
|
||||
import com.ai.da.model.vo.QuestionnaireFeedbackVO;
|
||||
import com.ai.da.model.vo.QuestionnaireVO;
|
||||
import com.ai.da.model.vo.QueryUserConditionsVO;
|
||||
import com.ai.da.service.AccountService;
|
||||
import com.ai.da.service.ConvenientInquiryService;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import io.netty.util.internal.StringUtil;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import jakarta.annotation.Nullable;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.Valid;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
||||
@Api(tags = "便利查询")
|
||||
@Tag(name = "便利查询")
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/api/inquiry")
|
||||
public class ConvenientInquiryController {
|
||||
|
||||
@Resource
|
||||
private TrialOrderMapper trialOrderMapper;
|
||||
|
||||
@Resource
|
||||
private DesignMapper designMapper;
|
||||
@Resource
|
||||
private ConvenientInquiryService convenientInquiryService;
|
||||
@Resource
|
||||
private AccountService accountService;
|
||||
|
||||
@ApiOperation("获取当前所有试用用户")
|
||||
@GetMapping("/getTrial")
|
||||
public Response<List<TrialOrder>> getTrial(){
|
||||
Long accountId = UserContext.getUserHolder().getId();
|
||||
if (accountId.equals(31L) || accountId.equals(87L) || accountId.equals(83L) || accountId.equals(6L) || accountId.equals(4L) || accountId.equals(73L)){
|
||||
List<TrialOrder> trialOrders = trialOrderMapper.selectList(null);
|
||||
return Response.success(trialOrders);
|
||||
}else {
|
||||
return Response.fail("Sorry, you don't have permission");
|
||||
}
|
||||
|
||||
@Operation(summary = "获取当前所有试用用户")
|
||||
@PostMapping("/getTrial")
|
||||
public Response<IPage<TrialOrder>> getTrial(@Valid @RequestBody QueryUserConditionsVO queryUserConditionsVO) {
|
||||
return Response.success(convenientInquiryService.getTrial(queryUserConditionsVO));
|
||||
}
|
||||
|
||||
@ApiOperation("获取指定时间区间内所有用户design的使用情况")
|
||||
@Operation(summary = "获取指定时间区间内所有用户design的使用情况")
|
||||
@GetMapping("/getDesignStatistic")
|
||||
public Response<List<UserDesignStatisticDTO>> getDesignStatistic(@RequestParam String startTime,@RequestParam String endTime){
|
||||
Long accountId = UserContext.getUserHolder().getId();
|
||||
if (accountId.equals(31L) || accountId.equals(87L) || accountId.equals(83L) || accountId.equals(6L) || accountId.equals(4L) || accountId.equals(73L)){
|
||||
public Response<List<UserDesignStatisticDTO>> getDesignStatistic(@RequestParam(required = false) String startTime, @RequestParam(required = false) String endTime,
|
||||
@RequestParam(required = false) List<Long> ids, @RequestParam(required = false) String email,
|
||||
@RequestParam(required = false) String organizationName) {
|
||||
/*Long accountId = UserContext.getUserHolder().getId();
|
||||
String userEmail = accountService.getById(accountId).getUserEmail();
|
||||
if (accountId.equals(31L) || accountId.equals(87L) || accountId.equals(83L)
|
||||
|| accountId.equals(6L) || accountId.equals(4L) || accountId.equals(73L)
|
||||
|| userEmail.equals("joho8228@hotmail.com")
|
||||
|| userEmail.equals("chelseayu@code-create.com.hk")
|
||||
|| userEmail.equals("cheungzt007@gmail.com")
|
||||
) {
|
||||
if (StringUtil.isNullOrEmpty(startTime)) startTime = "2024-02-01 00:00:00";
|
||||
if (StringUtil.isNullOrEmpty(endTime)){
|
||||
if (StringUtil.isNullOrEmpty(endTime)) {
|
||||
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
|
||||
Date date = new Date();
|
||||
endTime = simpleDateFormat.format(date);
|
||||
}
|
||||
List<UserDesignStatisticDTO> designStatistic = designMapper.getDesignStatistic(startTime, endTime);
|
||||
if (!StringUtil.isNullOrEmpty(email)){
|
||||
email = email.trim();
|
||||
}
|
||||
List<UserDesignStatisticDTO> designStatistic = designMapper.getDesignStatistic(startTime, endTime, ids, email,"prsn", null);
|
||||
return Response.success(designStatistic);
|
||||
}else {
|
||||
} else {
|
||||
return Response.fail("Sorry, you don't have permission");
|
||||
}*/
|
||||
return Response.success(convenientInquiryService.getDesignStatistic(startTime, endTime, ids, email, organizationName));
|
||||
}
|
||||
|
||||
|
||||
//调查问卷
|
||||
@Operation(summary = "获取调查问卷统计详情")
|
||||
@GetMapping("/getQuestionnaireStatistic")
|
||||
public Response<QuestionnaireFeedbackVO> getQuestionnaire() {
|
||||
return Response.success(convenientInquiryService.getQuestionnaireInfo());
|
||||
}
|
||||
|
||||
@Operation(summary = "获取所有调查问卷")
|
||||
@GetMapping("/getAllQuestionnaire")
|
||||
public Response<List<QuestionnaireVO>> getAllQuestionnaire() {
|
||||
return Response.success(convenientInquiryService.getAllQuestionnaire());
|
||||
}
|
||||
|
||||
@Operation(summary = "获取近期新用户")
|
||||
@PostMapping("/recentNewUser")
|
||||
public Response<IPage<Account>> recentNewUser(@Valid @RequestBody QueryUserConditionsVO queryUserConditionsVO) {
|
||||
return Response.success(convenientInquiryService.recentNewUser(queryUserConditionsVO));
|
||||
}
|
||||
|
||||
@Operation(summary = "获取近期新用户图表数据")
|
||||
@GetMapping("/recentNewUserChart")
|
||||
public Response<Map<String, Object>> recentNewUserChart(@Parameter(description = "startTime") @RequestParam @Nullable String startTime,
|
||||
@Parameter(description = "endTime") @RequestParam @Nullable String endTime,
|
||||
@Parameter(description = "userType") @RequestParam @Nullable Integer userType) {
|
||||
return Response.success(convenientInquiryService.recentNewUserChart(startTime, endTime, userType));
|
||||
}
|
||||
|
||||
@Operation(summary = "获取近期活跃用户")
|
||||
@PostMapping("/recentActiveUser")
|
||||
public Response<IPage<Account>> recentActiveUser(@Valid @RequestBody QueryUserConditionsVO queryUserConditionsVO) {
|
||||
return Response.success(convenientInquiryService.recentActiveUser(queryUserConditionsVO));
|
||||
}
|
||||
|
||||
@Operation(summary = "获取近期活跃用户图表数据")
|
||||
@GetMapping("/recentActiveUserChart")
|
||||
public Response<Integer> recentActiveUserChart(@Parameter(description = "startTime") @RequestParam @Nullable String startTime,
|
||||
@Parameter(description = "endTime") @RequestParam @Nullable String endTime) {
|
||||
return Response.success(convenientInquiryService.recentActiveUserChart(startTime, endTime));
|
||||
}
|
||||
|
||||
@Operation(summary = "获取用户的各模块功能使用详情")
|
||||
@GetMapping("/getActiveUserFunc")
|
||||
public Response<Map<String, List<Object>>> getActiveUserFunc(@Parameter(description = "startTime") @RequestParam @Nullable String startTime,
|
||||
@Parameter(description = "endTime") @RequestParam @Nullable String endTime,
|
||||
@Parameter(description = "userIdList") @RequestParam(required = false) @Nullable List<Long> userIdList) {
|
||||
return Response.success(convenientInquiryService.getActiveUserFunc(startTime, endTime, userIdList));
|
||||
}
|
||||
|
||||
@Operation(summary = "试用用户到正式用户的转化率")
|
||||
@GetMapping("/conversionRate")
|
||||
public Response<Map<String, Object>> conversionRate(@Parameter(description = "startTime") @RequestParam(required = false) @Nullable String startTime,
|
||||
@Parameter(description = "endTime") @RequestParam(required = false) @Nullable String endTime) {
|
||||
return Response.success(convenientInquiryService.conversionRate(startTime, endTime));
|
||||
}
|
||||
|
||||
@Operation(summary = "试用用户国家/城市分布")
|
||||
@GetMapping("/trialUserCountry")
|
||||
public Response<Map<String, List<Object>>> trialUserCountry(@Parameter(description = "startTime") @RequestParam(required = false) @Nullable String startTime,
|
||||
@Parameter(description = "endTime") @RequestParam(required = false) @Nullable String endTime) {
|
||||
return Response.success(convenientInquiryService.trialUserCountry(startTime, endTime));
|
||||
}
|
||||
|
||||
@Operation(summary = "添加用户")
|
||||
@PostMapping("/addUser")
|
||||
public Response<Boolean> addUser(@Valid @RequestBody AccountAddDTO accountAddDTO) {
|
||||
Long userAccountId = UserContext.getUserHolder().getId();
|
||||
if (userAccountId.equals(31L) || userAccountId.equals(87L) || userAccountId.equals(83L)
|
||||
|| userAccountId.equals(6L) || userAccountId.equals(4L) || userAccountId.equals(73L)
|
||||
) {
|
||||
return Response.success(convenientInquiryService.addUser(accountAddDTO));
|
||||
} else {
|
||||
return Response.fail("Sorry, you don't have permission");
|
||||
}
|
||||
}
|
||||
|
||||
@Operation(summary = "修改用户信息")
|
||||
@PostMapping("/modifyUser")
|
||||
public Response<Boolean> modifyUser(@Parameter(description = "用户id") @RequestParam @Nullable Long accountId,
|
||||
@Parameter(description = "有效期截止时间的毫秒级unix格式") @RequestParam @Nullable Long validEndTime,
|
||||
@Parameter(description = "用户类型 1/2/3/0 -> yearly/monthly/trial/visitor") @RequestParam @Nullable Integer systemUser,
|
||||
@Parameter(description = "积分") @RequestParam @Nullable Long credits) {
|
||||
Long userAccountId = UserContext.getUserHolder().getId();
|
||||
if (userAccountId.equals(31L) || userAccountId.equals(87L) || userAccountId.equals(83L)
|
||||
|| userAccountId.equals(6L) || userAccountId.equals(4L) || userAccountId.equals(73L)
|
||||
) {
|
||||
return Response.success(convenientInquiryService.modifyUser(accountId, validEndTime, systemUser, credits));
|
||||
} else {
|
||||
return Response.fail("Sorry, you don't have permission");
|
||||
}
|
||||
}
|
||||
|
||||
@Operation(summary = "获取用户信息")
|
||||
@PostMapping("/getUserInfo")
|
||||
public Response<IPage<Account>> getUserInfo(@Valid @RequestBody QueryUserConditionsVO queryUserConditionsVO) {
|
||||
return Response.success(convenientInquiryService.getUserInfo(queryUserConditionsVO));
|
||||
}
|
||||
|
||||
@Operation(summary = "获取所有用户id")
|
||||
@GetMapping("/getAllUserId")
|
||||
public Response<IPage<Map<String, Object>>> getAllUserIdList(@Parameter(description = "page") @RequestParam Integer page,
|
||||
@Parameter(description = "size") @RequestParam Integer size,
|
||||
@Parameter(description = "email 模糊查询") @RequestParam(required = false) String email) {
|
||||
return Response.success(convenientInquiryService.getAllUserIdList(page, size, email));
|
||||
}
|
||||
|
||||
@Operation(summary = "获取所有交易信息")
|
||||
@PostMapping("/queryTransaction")
|
||||
public Response<PageBaseResponse<PaymentInfoVO>> queryTransactionRecords(@Valid @RequestBody QueryPaymentInfoDTO queryPaymentInfoDTO){
|
||||
return Response.success(convenientInquiryService.queryTransactionRecords(queryPaymentInfoDTO));
|
||||
}
|
||||
|
||||
@Operation(summary = "获取所有国家、城市")
|
||||
@GetMapping("/getCities")
|
||||
public Response<Map<String, List<String>>> getCities(){
|
||||
return Response.success(convenientInquiryService.getCities());
|
||||
}
|
||||
|
||||
@Operation(summary = "下载交易记录")
|
||||
@PostMapping("/queryTransaction/download")
|
||||
public Response<String> exportTransactionRecords(@Valid @RequestBody QueryPaymentInfoDTO queryPaymentInfoDTO, HttpServletResponse response){
|
||||
return Response.success(convenientInquiryService.exportTransactionRecords(queryPaymentInfoDTO, response));
|
||||
}
|
||||
|
||||
@Operation(summary = "获取生成功能使用频次")
|
||||
@PostMapping("/getGenerateFrequency")
|
||||
public Response<PageBaseResponse<AccountCreditsUsageDTO>> getGenerateFrequency(@Valid @RequestBody AccountCreditsUsageQueryDTO queryDTO){
|
||||
return Response.success(convenientInquiryService.getGenerateFrequency(queryDTO));
|
||||
}
|
||||
|
||||
@Operation(summary = "获取所有生成功能的名字")
|
||||
@GetMapping("/getAllGenerateFuncName")
|
||||
public Response<List<String>> getAllGenerateFuncName(){
|
||||
return Response.success(convenientInquiryService.getAllGenerateFuncName());
|
||||
}
|
||||
|
||||
@Operation(summary = "添加组织机构")
|
||||
@GetMapping("/addOrganization")
|
||||
public Response<String> addOrganization(@Parameter(description = "机构名") @RequestParam String name, @Parameter(description = "Enterprise || Education") @RequestParam String type){
|
||||
convenientInquiryService.addOrganization(name, type);
|
||||
return Response.success();
|
||||
}
|
||||
|
||||
@Operation(summary = "查询所有企业或教育机构")
|
||||
@PostMapping("/queryOrganization")
|
||||
public Response<IPage<Organization>> queryOrganization(@RequestBody QueryOrganizationPageDTO queryOrganizationPageDTO){
|
||||
return Response.success(convenientInquiryService.queryOrganization(queryOrganizationPageDTO));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -6,32 +6,32 @@ import com.ai.da.common.response.Response;
|
||||
import com.ai.da.mapper.primary.entity.CreditsDetail;
|
||||
import com.ai.da.model.dto.QueryIncomeOrExpenditureDTO;
|
||||
import com.ai.da.service.CreditsService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.validation.Valid;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
|
||||
@CrossOrigin
|
||||
@RestController
|
||||
@RequestMapping("/api/credits")
|
||||
@Api(tags = "积分")
|
||||
@Tag(name = "积分")
|
||||
@Slf4j
|
||||
public class CreditsController {
|
||||
|
||||
@Resource
|
||||
private CreditsService creditsService;
|
||||
|
||||
@ApiOperation("获取当前积分")
|
||||
@Operation(summary = "获取当前积分")
|
||||
@GetMapping("/getCredits")
|
||||
public Response<String> getCredits() {
|
||||
String credits = creditsService.getCredits(UserContext.getUserHolder().getId());
|
||||
return Response.success(credits);
|
||||
}
|
||||
|
||||
@ApiOperation("获取积分详细")
|
||||
@Operation(summary = "获取积分详细")
|
||||
@PostMapping("/getCreditsDetail")
|
||||
public Response<PageBaseResponse<CreditsDetail>> getCreditsDetail(@Valid @RequestBody QueryIncomeOrExpenditureDTO queryPageByTimeDTO) {
|
||||
PageBaseResponse<CreditsDetail> credits = creditsService.queryCreditsDetailsPage(queryPageByTimeDTO);
|
||||
|
||||
@@ -1,86 +1,130 @@
|
||||
package com.ai.da.controller;
|
||||
|
||||
import com.ai.da.common.config.exception.BusinessException;
|
||||
import com.ai.da.common.response.PageBaseResponse;
|
||||
import com.ai.da.common.response.Response;
|
||||
import com.ai.da.mapper.primary.entity.CloudTask;
|
||||
import com.ai.da.mapper.primary.entity.CollectionSort;
|
||||
import com.ai.da.model.dto.*;
|
||||
import com.ai.da.model.vo.CollectionSketchVO;
|
||||
import com.ai.da.model.vo.DesignCollectionVO;
|
||||
import com.ai.da.model.vo.DesignLikeVO;
|
||||
import com.ai.da.model.vo.*;
|
||||
import com.ai.da.service.DesignService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.annotations.ApiParam;
|
||||
import com.ai.da.service.UserLikeGroupService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.validation.Valid;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@Api(tags = "design模块")
|
||||
@Tag(name = "design模块")
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/api/design")
|
||||
public class DesignController {
|
||||
@Resource
|
||||
private DesignService designService;
|
||||
@Resource
|
||||
private UserLikeGroupService userLikeGroupService;
|
||||
|
||||
@ApiOperation(value = "设计 Conllection")
|
||||
@Operation(summary = "设计 Conllection")
|
||||
@PostMapping("/designCollection")
|
||||
@CrossOrigin
|
||||
public Response<DesignCollectionVO> designCollection(@Valid @RequestBody DesignCollectionDTO designDTO) {
|
||||
public Response<String> designCollection(@Valid @RequestBody DesignCollectionDTO designDTO) {
|
||||
return Response.success(designService.designCollection(designDTO));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "design进度条")
|
||||
@Operation(summary = "design进度条")
|
||||
@GetMapping("/designProcess")
|
||||
public Response<Integer> designProcess(@RequestParam("processId") String processId) {
|
||||
return Response.success(designService.designProcess(processId));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "重新设计 Collection")
|
||||
@Operation(summary = "重新设计 Collection")
|
||||
@PostMapping("/reDesignCollection")
|
||||
public Response<DesignCollectionVO> reDesignCollection(@Valid @RequestBody ReDesignCollectionDTO reDesignDTO) {
|
||||
public Response<String> reDesignCollection(@Valid @RequestBody ReDesignCollectionDTO reDesignDTO) {
|
||||
return Response.success(designService.reDesignCollection(reDesignDTO));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "designItem list,刷新用")
|
||||
@Operation(summary = "designItem list,刷新用")
|
||||
@GetMapping("/designItemList")
|
||||
public Response<DesignCollectionVO> designItemList(@ApiParam("designId") @RequestParam("designId") Long designId) {
|
||||
public Response<DesignCollectionVO> designItemList(@Parameter(description = "designId") @RequestParam("designId") Long designId) {
|
||||
return Response.success(designService.designItemList(designId));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "统计design进度")
|
||||
@Operation(summary = "统计design进度")
|
||||
@PostMapping("/countDesignProcess")
|
||||
public Response<BigDecimal> countDesignProcess() {
|
||||
return Response.success(designService.countDesignProcess());
|
||||
}
|
||||
|
||||
@ApiOperation(value = "Design Like")
|
||||
@Operation(summary = "Design Like")
|
||||
@PostMapping("/like")
|
||||
public Response<DesignLikeVO> like(@Valid @RequestBody DesignLikeDTO designLikeDTO) {
|
||||
return Response.success(designService.like(designLikeDTO));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "Design Dislike")
|
||||
@Operation(summary = "Design Dislike")
|
||||
@PostMapping("/dislike")
|
||||
public Response<Boolean> dislike(@Valid @RequestBody DisDesignLikeDTO disDesignLikeDTO) {
|
||||
return Response.success(designService.dislike(disDesignLikeDTO));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "sketchBoard upload generate design前裁剪")
|
||||
@PostMapping("/sketchBoardsBoundingBox")
|
||||
public Response<List<CollectionSketchVO>> sketchesBoundingBox(@Valid @RequestBody SketchesBoundingBoxDTO sketchesBoundingBoxDTO) {
|
||||
return Response.success(designService.sketchesBoundingBox(sketchesBoundingBoxDTO));
|
||||
@Operation(summary = "Design sort")
|
||||
@PostMapping("/sort")
|
||||
public Response<Boolean> sort(@Valid @RequestBody CollectionSortDTO userLikeSortDTO) {
|
||||
return Response.success(designService.sort(userLikeSortDTO));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "通过designItemId获取模特图")
|
||||
@Operation(summary = "sketchBoard upload generate design前裁剪")
|
||||
@PostMapping("/sketchBoardsBoundingBox")
|
||||
public Response<List<CollectionSketchVO>> sketchesBoundingBox(@Valid @RequestBody ReDesignCollectionDTO reDesignCollectionDTO) {
|
||||
return Response.success(designService.sketchesBoundingBox(reDesignCollectionDTO));
|
||||
}
|
||||
|
||||
@Operation(summary = "通过designItemId获取模特图")
|
||||
@PostMapping("/getModel")
|
||||
public Response<List<String>> getModel(@RequestBody List<Long> designItemIdList){
|
||||
return Response.success(designService.getModel(designItemIdList));
|
||||
}
|
||||
|
||||
@Operation(summary = "获取design结果")
|
||||
@GetMapping("/getDesignResult")
|
||||
public Response<DesignCollectionVO> getDesignResult(@RequestParam("requestId") String requestId, @RequestParam("objectSignList") List<String> objectSignList){
|
||||
return Response.success(designService.getDesignResult(requestId, objectSignList));
|
||||
}
|
||||
|
||||
@Operation(summary = "云生成")
|
||||
@PostMapping("/designCloud")
|
||||
public Response<String> designCloud(@Valid @RequestBody CloudTaskDTO cloudTaskDTO) {
|
||||
return Response.success(designService.designCloud(cloudTaskDTO));
|
||||
}
|
||||
|
||||
@Operation(summary = "云生成修改任务名")
|
||||
@PostMapping("/cloudTaskNameUpdate")
|
||||
public Response<Boolean> cloudTaskNameUpdate(@Valid @RequestBody CloudTaskDTO cloudTaskDTO) {
|
||||
return Response.success(designService.cloudTaskNameUpdate(cloudTaskDTO));
|
||||
}
|
||||
|
||||
@Operation(summary = "云生成删除任务")
|
||||
@PostMapping("/cloudTaskDelete")
|
||||
public Response<Boolean> cloudTaskDelete(@Valid @RequestBody CloudTaskDTO cloudTaskDTO) {
|
||||
return Response.success(designService.cloudTaskDelete(cloudTaskDTO));
|
||||
}
|
||||
|
||||
@Operation(summary = "云生成page")
|
||||
@PostMapping("/cloudPage")
|
||||
public Response<PageBaseResponse<CloudTaskVO>> cloudPage(@Valid @RequestBody CloudPageDTO cloudPageDTO) {
|
||||
return Response.success(PageBaseResponse.success(designService.cloudPage(cloudPageDTO)));
|
||||
}
|
||||
|
||||
@Operation(summary = "获取design云生成结果")
|
||||
@PostMapping("/getDesignCloudResult")
|
||||
public Response<CloudTaskResultVO> getDesignCloudResult(@Valid @RequestBody DesignCloudResultQuery designCloudResultQuery) {
|
||||
return Response.success(designService.getDesignCloudResult(designCloudResultQuery));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,18 +5,18 @@ import com.ai.da.model.dto.*;
|
||||
import com.ai.da.model.vo.*;
|
||||
import com.ai.da.service.DesignItemService;
|
||||
import com.ai.da.service.DesignService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.annotations.ApiParam;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.validation.Valid;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import java.io.IOException;
|
||||
|
||||
|
||||
@Api(tags = "design Detail模块")
|
||||
@Tag(name = "design Detail模块")
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/api/design/detail")
|
||||
@@ -26,7 +26,7 @@ public class DesignDetailController {
|
||||
@Resource
|
||||
private DesignItemService designItemService;
|
||||
|
||||
@ApiOperation(value = "生成高级design图片")
|
||||
@Operation(summary = "生成高级design图片")
|
||||
@PostMapping("/generateHighDesign")
|
||||
public Response<String> generateHighDesign(@Valid @RequestBody GenerateHighDesignDTO generateHighDesignDTO) {
|
||||
Response response = new Response();
|
||||
@@ -34,7 +34,7 @@ public class DesignDetailController {
|
||||
return response;
|
||||
}
|
||||
|
||||
@ApiOperation(value = "删除高级design图片")
|
||||
@Operation(summary = "删除高级design图片")
|
||||
@PostMapping("/deleteHighDesign")
|
||||
public Response<Boolean> deleteHighDesign(@Valid @RequestBody GenerateHighDesignDTO generateHighDesignDTO) {
|
||||
Response response = new Response();
|
||||
@@ -42,28 +42,28 @@ public class DesignDetailController {
|
||||
return response;
|
||||
}
|
||||
|
||||
@ApiOperation(value = "查询design详情")
|
||||
@Operation(summary = "查询design详情")
|
||||
@GetMapping("/getDetail")
|
||||
public Response<DesignItemDetailVO> getDetail(@ApiParam("designItemId") @RequestParam("designItemId") Long designItemId,
|
||||
@ApiParam("designPythonOutfitId") @RequestParam(value = "designPythonOutfitId", required = false) Long designPythonOutfitId) {
|
||||
public Response<DesignItemDetailVO> getDetail(@Parameter(description = "designItemId") @RequestParam("designItemId") Long designItemId,
|
||||
@Parameter(description = "designPythonOutfitId") @RequestParam(value = "designPythonOutfitId", required = false) Long designPythonOutfitId) {
|
||||
return Response.success(designService.detail(designPythonOutfitId, designItemId));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "切换系统的element")
|
||||
@Operation(summary = "切换系统的element")
|
||||
@GetMapping("/getNextSysElement")
|
||||
public Response<GetNextSysElementVO> getNextSysElement(@ApiParam("要切换的系统element 类型") @RequestParam("type") String type,
|
||||
@ApiParam("要切换的系统element id") @RequestParam("id") Long id,
|
||||
@ApiParam("操作类型 PREV 上一步 NEXT 下一步") @RequestParam("operateType") String operateType) {
|
||||
public Response<GetNextSysElementVO> getNextSysElement(@Parameter(description = "要切换的系统element 类型") @RequestParam("type") String type,
|
||||
@Parameter(description = "要切换的系统element id") @RequestParam("id") Long id,
|
||||
@Parameter(description = "操作类型 PREV 上一步 NEXT 下一步") @RequestParam("operateType") String operateType) {
|
||||
return Response.success(designItemService.getNextSysElement(id, type, operateType));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "单个design")
|
||||
@Operation(summary = "单个design")
|
||||
@PostMapping("/designSingle")
|
||||
public Response<DesignSingleVO> designSingle(@Valid @RequestBody DesignSingleIncludeLayersDTO designSingleIncludeLayersDTO) {
|
||||
return Response.success(designItemService.designSingleIncludeLayers(designSingleIncludeLayersDTO));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "print打点")
|
||||
@Operation(summary = "print打点")
|
||||
@PostMapping("/printDot")
|
||||
public Response<String> printDot(@Valid @RequestBody DesignSingleDTO designSingleDTO) {
|
||||
Response<String> response = new Response();
|
||||
@@ -72,9 +72,23 @@ public class DesignDetailController {
|
||||
return response;
|
||||
}
|
||||
|
||||
@ApiOperation(value = "编辑图层大小和位置 废弃")
|
||||
@Operation(summary = "编辑图层大小和位置 废弃")
|
||||
@PostMapping("/editLayers")
|
||||
public Response<ComposeLayersVO> editPositionAndScale(@Valid @RequestBody EditLayersPositionAndScaleVO positionAndScaleVO) throws IOException {
|
||||
return Response.success(designItemService.editLayersPositionAndScale(positionAndScaleVO));
|
||||
}
|
||||
|
||||
@Operation(summary = "mask数据兼容")
|
||||
@GetMapping("/convertWithoutGradient")
|
||||
public Response<String> convertHistoryMaskWithoutGradient(){
|
||||
designItemService.convertHistoryMaskWithoutGradient();
|
||||
return Response.success("success");
|
||||
}
|
||||
|
||||
@Operation(summary = "mask数据库路径更新")
|
||||
@GetMapping("/updateMaskUrl")
|
||||
public Response<String> updateMaskUrl(){
|
||||
designItemService.updateMaskUrl();
|
||||
return Response.success("success");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,20 +8,23 @@ import com.ai.da.model.vo.*;
|
||||
import com.ai.da.service.CollectionElementService;
|
||||
import com.ai.da.service.PanToneService;
|
||||
import com.ai.da.service.SysFileService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.annotations.ApiParam;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.validation.Valid;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
||||
@Api(tags = "collection模块")
|
||||
@Tag(name = "collection模块")
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/api/element")
|
||||
@@ -34,7 +37,7 @@ public class ElementController {
|
||||
@Resource
|
||||
private SysFileService sysFileService;
|
||||
|
||||
@ApiOperation(value = "初始化系统文件")
|
||||
@Operation(summary = "初始化系统文件")
|
||||
@PostMapping("/initDefaultSysFile")
|
||||
public Response<Boolean> initDefaultSysFile(@RequestParam("validate") String validateUse) {
|
||||
if (validateUse.equals("da_fl_mm_dad88888")) {
|
||||
@@ -44,64 +47,97 @@ public class ElementController {
|
||||
return Response.success();
|
||||
}
|
||||
|
||||
@ApiOperation(value = "element文件上传")
|
||||
@Operation(summary = "element文件上传")
|
||||
@PostMapping("/upload")
|
||||
public Response<CollectionElementVO> upload(@RequestParam("file") MultipartFile file,
|
||||
@ApiParam("一级类型 Moodboard Printboard Sketchboard MarketingSketch Colorboard")
|
||||
@Parameter(description = "一级类型 Moodboard Printboard Sketchboard MarketingSketch Colorboard")
|
||||
@RequestParam(value = "level1Type") String level1Type,
|
||||
@RequestParam(name = "gender", required = false) String gender,
|
||||
@ApiParam("本地时区,比如 'Asia/Tokyo' 东京时间 , 'Asia/Shanghai' 北京时间 由js本地获取")
|
||||
@RequestParam(name = "level2Type", required = false) String level2Type,
|
||||
@RequestParam(name = "projectId", required = false) Long projectId,
|
||||
@Parameter(description = "本地时区,比如 'Asia/Tokyo' 东京时间 , 'Asia/Shanghai' 北京时间 由js本地获取")
|
||||
@RequestParam(value = "timeZone") String timeZone) {
|
||||
if (null == file || StringUtils.isEmpty(file.getOriginalFilename())) {
|
||||
throw new BusinessException("file.cannot.be.empty");
|
||||
}
|
||||
return Response.success(collectionElementService.upload(
|
||||
new CollectionElementUploadDTO(file, level1Type, gender, timeZone, MD5Utils.encryptFile(file))));
|
||||
new CollectionElementUploadDTO(file, projectId, level1Type, level2Type, gender, timeZone, MD5Utils.encryptFile(file))));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "element文件删除")
|
||||
@Operation(summary = "element文件删除")
|
||||
@PostMapping("/delete")
|
||||
public Response<Boolean> delete(@Valid @RequestBody CollectionDeleteFileDTO deleteFileDTO) {
|
||||
collectionElementService.delete(deleteFileDTO.getId());
|
||||
return Response.success();
|
||||
}
|
||||
|
||||
@ApiOperation(value = "生成印花")
|
||||
/** 该功能已删除 */
|
||||
@Deprecated
|
||||
@Operation(summary = "生成印花")
|
||||
@PostMapping("/generatePrint")
|
||||
public Response<GenerateCollectionItemVO> generatePrint(@Valid @RequestBody CollectionGeneratePrintDTO generatePrintDTO) {
|
||||
return Response.success(collectionElementService.generatePrint(generatePrintDTO));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "保存印花")
|
||||
@Operation(summary = "保存印花")
|
||||
@PostMapping("/savePrint")
|
||||
public Response<Boolean> savePrint(@Valid @RequestBody CollectionSavePrintDTO savePrintDTO) {
|
||||
return Response.success(collectionElementService.savePrint(savePrintDTO));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "通过tcx值获取潘通信息")
|
||||
@Operation(summary = "通过tcx值获取潘通信息")
|
||||
@GetMapping("/getRgbByTcx")
|
||||
public Response<PantoneVO> getRgbByHsv(@ApiParam("tcx") @RequestParam("tcx") String tcx) {
|
||||
public Response<PantoneVO> getRgbByHsv(@Parameter(description = "tcx") @RequestParam("tcx") String tcx) {
|
||||
return Response.success(panToneService.getByTCX(tcx));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "通过hsv值获取潘通信息")
|
||||
@Operation(summary = "通过hsv值获取潘通信息")
|
||||
@GetMapping("/getRgbByHsv")
|
||||
public Response<PantoneVO> getRgbByHsv(@RequestParam("h") Integer h,
|
||||
@RequestParam("s") Integer s, @RequestParam("v") Integer v) {
|
||||
return Response.success(panToneService.getByHSV(h, s, v));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "通过hsv值数组批量获取潘通信息")
|
||||
@Operation(summary = "通过hsv值数组批量获取潘通信息")
|
||||
@PostMapping("/getRgbByHsvBatch")
|
||||
public Response<List<PantoneVO>> getRgbByHsvBatch(@RequestBody @Valid List<GetRgbByHsvBatchDTO> rgbByHsvBatch) {
|
||||
return Response.success(panToneService.getRgbByHsvBatch(rgbByHsvBatch));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "刷新历史数据")
|
||||
@Operation(summary = "刷新历史数据")
|
||||
@PostMapping("/refreshHistoryData")
|
||||
public Response<Boolean> refreshHistoryData() {
|
||||
collectionElementService.refreshHistoryData();
|
||||
return Response.success();
|
||||
}
|
||||
|
||||
@Operation(summary = "图片分割")
|
||||
@PostMapping("/imageSegmentation")
|
||||
public Response<List<CollectionElementVO>> selectedImageSeg(
|
||||
@RequestPart(value = "file", required = false) MultipartFile[] file,
|
||||
@RequestParam(value = "type", required = false) @Pattern(regexp = "sketch|product", message = "Please choose the image type") String type,
|
||||
@RequestParam(value = "id", required = false) Long id,
|
||||
@RequestParam(value = "sourceType", required = false) @Pattern(regexp = "Library|Upload", message = "Select an image from the library or upload one.") String sourceType) {
|
||||
// 过滤空文件
|
||||
List<MultipartFile> nonEmptyFiles = Arrays.stream(file)
|
||||
.filter(item -> !item.isEmpty())
|
||||
.collect(Collectors.toList());
|
||||
// 参数校验
|
||||
if ((nonEmptyFiles.isEmpty()) && id == null) {
|
||||
throw new BusinessException("必须提供文件上传或ID");
|
||||
}
|
||||
if (!nonEmptyFiles.isEmpty() && id != null) {
|
||||
throw new BusinessException("不能同时提供文件上传和ID");
|
||||
}
|
||||
|
||||
return Response.success(collectionElementService.selectedImageSeg(nonEmptyFiles, id, type, sourceType));
|
||||
}
|
||||
|
||||
@Operation(summary = "更新element level2type")
|
||||
@GetMapping("/updateElementLevel2Type")
|
||||
public Response<String> updateLibraryLevel2Type(@RequestParam(value = "elementId") Long elementId, @RequestParam(value = "level2Type") String level2Type) {
|
||||
collectionElementService.updateElementLevel2Type(elementId, level2Type);
|
||||
return Response.success("success");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
42
src/main/java/com/ai/da/controller/EmailController.java
Normal file
42
src/main/java/com/ai/da/controller/EmailController.java
Normal file
@@ -0,0 +1,42 @@
|
||||
package com.ai.da.controller;
|
||||
|
||||
import com.ai.da.service.EmailService;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.thymeleaf.context.Context;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import java.util.Collections;
|
||||
|
||||
@Tag(name = "邮件模块")
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/api/email")
|
||||
public class EmailController {
|
||||
|
||||
@Resource
|
||||
private EmailService emailService;
|
||||
|
||||
@GetMapping("/loadSingleTemplate")
|
||||
public void loadSingleEmailTemplate(){
|
||||
emailService.loadSingleEmailTemplate("templates\\upgrade\\122899_AiDA发版完成通知中文版.html");
|
||||
}
|
||||
|
||||
@GetMapping("/loadTemplate")
|
||||
public void loadTemplatesFromResources(){
|
||||
emailService.loadTemplatesFromResources("templates");
|
||||
}
|
||||
|
||||
@GetMapping("/sendEmailTest")
|
||||
public void sendEmailTest(){
|
||||
Context context = new Context();
|
||||
context.setVariable("username", "小白");
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
jsonObject.put("username", "小白");
|
||||
emailService.sendEmail(Collections.singletonList("xupei3360@163.com"), jsonObject, "132124_affiliate_accepted_en.html", "测试邮件", null, null );
|
||||
}
|
||||
}
|
||||
@@ -1,87 +1,240 @@
|
||||
package com.ai.da.controller;
|
||||
|
||||
import com.ai.da.common.response.Response;
|
||||
import com.ai.da.model.dto.GenerateLikeDTO;
|
||||
import com.ai.da.model.dto.GenerateThroughImageTextDTO;
|
||||
import com.ai.da.model.vo.*;
|
||||
import com.ai.da.service.GenerateService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.annotations.ApiParam;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.validation.Valid;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author XP
|
||||
*/
|
||||
@Api(tags = "Generate模块")
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/api/generate")
|
||||
public class GenerateController {
|
||||
|
||||
@Resource
|
||||
private GenerateService generateService;
|
||||
|
||||
/*@ApiOperation("自动识别sketch的caption 暂时未上")
|
||||
@PostMapping("/caption")
|
||||
public Response<GenerateCaptionVO> generateCaption(@RequestParam Long sketchElementId) {
|
||||
return Response.success(generateService.generateCaption(sketchElementId));
|
||||
}*/
|
||||
|
||||
/*@ApiOperation("通过文字、图片生成图片")
|
||||
@PostMapping("/sketchAndPrint")
|
||||
public void generateThroughImageText(@Valid @RequestBody GenerateThroughImageTextDTO generateThroughImageTextDTO) {
|
||||
// return Response.success(generateService.generateThroughImageText(generateThroughImageTextDTO));
|
||||
generateService.generateThroughImageText(generateThroughImageTextDTO);
|
||||
}*/
|
||||
|
||||
@ApiOperation("喜欢生成的图片")
|
||||
@PostMapping("/like")
|
||||
public Response<GenerateLikeVO> like(@Valid @RequestBody GenerateLikeDTO generateLikeDTO) {
|
||||
return Response.success(generateService.generateLike(generateLikeDTO));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "取消喜欢")
|
||||
@GetMapping("/dislike")
|
||||
public Response<Boolean> dislike(@ApiParam("generateDetailId") @RequestParam Long generateDetailId,
|
||||
@ApiParam("timeZone") @RequestParam String timeZone) {
|
||||
return Response.success(generateService.generateDislike(generateDetailId, timeZone));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "发起生成请求,异步获取结果")
|
||||
@PostMapping("/prepare")
|
||||
public Response<PrepareForGenerateVO> prepareForGenerate(@Valid @RequestBody GenerateThroughImageTextDTO generateThroughImageTextDTO) {
|
||||
return Response.success(generateService.prepareForGenerate(generateThroughImageTextDTO));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "取消继续生成")
|
||||
@GetMapping("/stopWaiting")
|
||||
public Response<String> stopWaiting(@RequestParam("userId") Long userId,
|
||||
@RequestParam("uniqueId") List<String> uniqueId,
|
||||
@RequestParam("timeZone") String timeZone,
|
||||
@RequestParam("type") String type) {
|
||||
generateService.cancelGenerate(userId, uniqueId, timeZone, type);
|
||||
return Response.success("stop waiting successfully");
|
||||
}
|
||||
|
||||
/*@ApiOperation(value = "获取生成结果")
|
||||
@GetMapping("/result")
|
||||
public Response<GenerateCollectionVO> getGenerateResult(@RequestParam("uniqueId") String uniqueId) {
|
||||
GenerateCollectionVO generateResult = generateService.getGenerateResult(uniqueId);
|
||||
return Response.success(generateResult);
|
||||
}*/
|
||||
|
||||
@ApiOperation(value = "获取生成结果")
|
||||
@PostMapping("/result")
|
||||
public Response<List<GenerateResultVO>> getGenerateResults(@Valid @RequestBody List<String> taskIdList) {
|
||||
List<GenerateResultVO> generateResult = generateService.getGenerateResultList(taskIdList);
|
||||
return Response.success(generateResult);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
package com.ai.da.controller;
|
||||
|
||||
import com.ai.da.common.enums.CreditsEventsEnum;
|
||||
import com.ai.da.common.response.Response;
|
||||
import com.ai.da.mapper.primary.entity.CollectionSort;
|
||||
import com.ai.da.model.dto.*;
|
||||
import com.ai.da.model.vo.*;
|
||||
import com.ai.da.service.GenerateService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author XP
|
||||
*/
|
||||
@Tag(name = "Generate模块")
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/api/generate")
|
||||
public class GenerateController {
|
||||
|
||||
@Resource
|
||||
private GenerateService generateService;
|
||||
|
||||
/*@ApiOperation("自动识别sketch的caption 暂时未上")
|
||||
@PostMapping("/caption")
|
||||
public Response<GenerateCaptionVO> generateCaption(@RequestParam Long sketchElementId) {
|
||||
return Response.success(generateService.generateCaption(sketchElementId));
|
||||
}*/
|
||||
|
||||
/*@ApiOperation("通过文字、图片生成图片")
|
||||
@PostMapping("/sketchAndPrint")
|
||||
public void generateThroughImageText(@Valid @RequestBody GenerateThroughImageTextDTO generateThroughImageTextDTO) {
|
||||
// return Response.success(generateService.generateThroughImageText(generateThroughImageTextDTO));
|
||||
generateService.generateThroughImageText(generateThroughImageTextDTO);
|
||||
}*/
|
||||
|
||||
@Operation(summary = "喜欢生成的图片")
|
||||
@PostMapping("/like")
|
||||
public Response<GenerateLikeVO> like(@Valid @RequestBody GenerateLikeDTO generateLikeDTO) {
|
||||
return Response.success(generateService.generateLike(generateLikeDTO));
|
||||
}
|
||||
|
||||
@Operation(summary = "取消喜欢")
|
||||
@GetMapping("/dislike")
|
||||
public Response<Boolean> dislike(@Parameter(description = "generateDetailId") @RequestParam Long generateDetailId,
|
||||
@Parameter(description = "timeZone") @RequestParam String timeZone) {
|
||||
return Response.success(generateService.generateDislike(generateDetailId, timeZone));
|
||||
}
|
||||
|
||||
@Operation(summary = "发起生成请求,异步获取结果")
|
||||
@PostMapping("/prepare")
|
||||
public ResponseEntity<Response<PrepareForGenerateVO>> prepareForGenerate(@Valid @RequestBody GenerateThroughImageTextDTO generateThroughImageTextDTO) {
|
||||
PrepareForGenerateVO prepareForGenerateVO = generateService.prepareForGenerate(generateThroughImageTextDTO);
|
||||
if (prepareForGenerateVO.getStatus().equals(200)){
|
||||
prepareForGenerateVO.setStatus(0);
|
||||
return ResponseEntity.ok(Response.success(prepareForGenerateVO));
|
||||
} else {
|
||||
return ResponseEntity.status(prepareForGenerateVO.getStatus()).body(Response.fail("The rate limit has been exceeded. Please wait 1 minute before retrying."));
|
||||
}
|
||||
}
|
||||
|
||||
@Operation(summary = "取消继续生成")
|
||||
@GetMapping("/stopWaiting")
|
||||
public Response<String> stopWaiting(@RequestParam("userId") Long userId,
|
||||
@RequestParam("uniqueId") List<String> uniqueId,
|
||||
@RequestParam("timeZone") String timeZone,
|
||||
@RequestParam("type") String type) {
|
||||
generateService.cancelGenerate(userId, uniqueId, timeZone, type);
|
||||
return Response.success("stop waiting successfully");
|
||||
}
|
||||
|
||||
/*@ApiOperation(value = "获取生成结果")
|
||||
@GetMapping("/result")
|
||||
public Response<GenerateCollectionVO> getGenerateResult(@RequestParam("uniqueId") String uniqueId) {
|
||||
GenerateCollectionVO generateResult = generateService.getGenerateResult(uniqueId);
|
||||
return Response.success(generateResult);
|
||||
}*/
|
||||
|
||||
@Operation(summary = "获取生成结果")
|
||||
@PostMapping("/result")
|
||||
public Response<List<GenerateResultVO>> getGenerateResults(@Valid @RequestBody List<String> taskIdList) {
|
||||
List<GenerateResultVO> generateResult = generateService.getGenerateResultList(taskIdList);
|
||||
return Response.success(generateResult);
|
||||
}
|
||||
|
||||
@Operation(summary = "imageToSketch")
|
||||
@PostMapping("/imageToSketch")
|
||||
public Response<String> imageToSketch(@Valid @RequestBody ImageToSketchDTO imageToSketchDTO) {
|
||||
return Response.success(generateService.imageToSketchAsync(imageToSketchDTO, null, null));
|
||||
// return Response.success(generateService.imageToSketch(imageToSketchDTO, null, null));
|
||||
}
|
||||
|
||||
// modifySketch
|
||||
@Operation(summary = "modifySketch")
|
||||
@PostMapping("/modifySketch")
|
||||
public Response<GenerateResultVO> modifySketch(@Valid @RequestBody GenerateModifyDTO generateModifyDTO) {
|
||||
return Response.success(generateService.modifySketch(generateModifyDTO));
|
||||
}
|
||||
|
||||
@Operation(summary = "请求进行姿势变换")
|
||||
@PostMapping("/poseTransform")
|
||||
public Response<ToProductImageResultVO> poseTransform(@Valid @RequestBody PoseTransformDTO poseTransformDTO) {
|
||||
return Response.success(generateService.poseTransform(poseTransformDTO));
|
||||
}
|
||||
|
||||
@Operation(summary = "获取姿势变换生成结果")
|
||||
@PostMapping("/poseTransformResult")
|
||||
public Response<List<PoseTransformationVO>> getPoseTransformationResults(@Valid @RequestBody List<String> taskIdList) {
|
||||
List<PoseTransformationVO> generateResult = generateService.getPoseTransformationResult(taskIdList, null, null);
|
||||
return Response.success(generateResult);
|
||||
}
|
||||
|
||||
@Operation(summary = "喜欢或取消喜欢姿势变换生成的图片")
|
||||
@PostMapping("/likeOrDislike")
|
||||
public Response<CollectionSort> likeOrDislike(@Parameter(description = "id") @RequestParam Long transformedId, @Parameter(description = "like || dislike") @RequestParam String likeOrDislike, @RequestParam("projectId") Long projectId, @RequestParam(value = "collectionSortParentId", required = false) Long collectionSortParentId) {
|
||||
return Response.success(generateService.disOrLikePose(transformedId, likeOrDislike, projectId, collectionSortParentId));
|
||||
}
|
||||
|
||||
@Operation(summary = "修改模特比例")
|
||||
@PostMapping("/modifyProportion")
|
||||
public Response<String> modifyModelProportion(@Valid @RequestBody ModifyModelProportionDTO proportionDTO){
|
||||
String path = generateService.modifyModelProportion(proportionDTO);
|
||||
return Response.success(path);
|
||||
}
|
||||
|
||||
@Operation(summary = "拼贴图生成线稿")
|
||||
@PostMapping("/genSketchRecon")
|
||||
public Response<GenerateResultVO> sketchReconstructionGenerate(@Valid @RequestBody SketchReconstructionDTO sketchReconstructionDTO){
|
||||
GenerateResultVO generateResultVO = generateService.sketchReconstructionGenerate(sketchReconstructionDTO);
|
||||
return Response.success(generateResultVO);
|
||||
}
|
||||
|
||||
@Operation(summary = "获取拼贴图最后一次生成结果")
|
||||
@GetMapping("/getReconLastResult")
|
||||
public Response<SketchReconstructionVO> getSketchReconstruction(@RequestParam("projectId") Long projectId){
|
||||
SketchReconstructionVO sketchReconstruction = generateService.getSketchReconstruction(projectId);
|
||||
return Response.success(sketchReconstruction);
|
||||
}
|
||||
|
||||
@Operation(summary = "获取pose transfer的所有pose")
|
||||
@GetMapping("/getAllPose")
|
||||
public Response<List<Map<String, String>>> getAllPose(){
|
||||
return Response.success(generateService.getAllPose());
|
||||
}
|
||||
|
||||
@Operation(summary = "删除pose transfer的结果")
|
||||
@GetMapping("/deleteResult")
|
||||
public Response<String> deleteToProductRelightResult(@RequestParam("projectId") @NotNull Long projectId,
|
||||
@RequestParam("id") @NotNull Long id){
|
||||
try{
|
||||
generateService.deleteGeneratedPose(projectId, id);
|
||||
return Response.success();
|
||||
}catch (Exception e){
|
||||
return Response.fail(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/*@ApiOperation(value = "万象 t2i 创建异步任务")
|
||||
@GetMapping("/createAsyncTask")
|
||||
public Response<String> createAsyncTask(@RequestParam("prompt") String prompt){
|
||||
return Response.success(generateService.createAsyncTask(87L, prompt, ""));
|
||||
}
|
||||
|
||||
@Operation(summary = "万象 t2i 获取异步任务结果")
|
||||
@GetMapping("/waitAsyncTask")
|
||||
public Response<GenerateResultVO> waitAsyncTask(@RequestParam("taskId") String taskId){
|
||||
return Response.success(generateService.getAsyncTaskResult(taskId));
|
||||
}
|
||||
|
||||
@Operation(summary = "万象 图生动图")
|
||||
@GetMapping("/animateAnyone")
|
||||
public Response<String> animateAnyone(@Valid @RequestBody PoseTransformDTO poseTransformDTO){
|
||||
return Response.success(generateService.animateAnyone(poseTransformDTO, null));
|
||||
}
|
||||
|
||||
@Operation(summary = "万象 获取动图模板id")
|
||||
@GetMapping("/getVideoTemplateId")
|
||||
public Response<String> getVideoTemplateId(@RequestParam("videoPath") String videoPath){
|
||||
return Response.success(generateService.getVideoTemplateId(videoPath));
|
||||
}
|
||||
|
||||
@Operation(summary = "万象 获取动图结果")
|
||||
@GetMapping("/getAnimateResult")
|
||||
public Response<PoseTransformationVO> getAnimateResult(@RequestParam("taskId") String taskId){
|
||||
return Response.success(generateService.getAnimateResult(taskId));
|
||||
}
|
||||
|
||||
@Operation(summary = "freepik toProductImage")
|
||||
@GetMapping("/reimagineFreePik")
|
||||
public Response<String> reimagineFreePik(@RequestParam("path") String path,
|
||||
@RequestParam("prompt") String prompt,
|
||||
@RequestParam("style") String style) throws IOException {
|
||||
return Response.success(generateService.reimagineFreePik(path, prompt, style));
|
||||
}
|
||||
|
||||
@Operation(summary = "获取图片描述")
|
||||
@GetMapping("/getImageDescription")
|
||||
public Response<String> getImageDescription(@RequestParam("path") String path) {
|
||||
return Response.success(generateService.getImageDescription(path));
|
||||
}*/
|
||||
|
||||
// @Operation(summary = "试用flux")
|
||||
// @GetMapping("/flux")
|
||||
public Response<String> flux(@RequestParam(value = "path", required = false) String path,
|
||||
@RequestParam("type") int type,
|
||||
@RequestParam(value = "prompt", required = false) String prompt){
|
||||
CreditsEventsEnum typeEnum = CreditsEventsEnum.RELIGHT;
|
||||
switch (type){
|
||||
case 1:
|
||||
typeEnum = CreditsEventsEnum.TO_PRODUCT_IMAGE;
|
||||
break;
|
||||
case 2:
|
||||
typeEnum = CreditsEventsEnum.IMAGE_TO_SKETCH;
|
||||
break;
|
||||
case 3:
|
||||
typeEnum = CreditsEventsEnum.PATTERN;
|
||||
break;
|
||||
}
|
||||
return Response.success(generateService.flux(typeEnum, prompt, path, false));
|
||||
}
|
||||
|
||||
// @Operation(summary = "获取flux结果")
|
||||
// @GetMapping("/fluxResult")
|
||||
public Response<String> fluxResult(@RequestParam("taskId") String taskId){
|
||||
return Response.success(generateService.getFluxResult(taskId, "87/" + taskId + ".png"));
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
203
src/main/java/com/ai/da/controller/GlobalAwardController.java
Normal file
203
src/main/java/com/ai/da/controller/GlobalAwardController.java
Normal file
@@ -0,0 +1,203 @@
|
||||
package com.ai.da.controller;
|
||||
|
||||
import com.ai.da.common.response.Response;
|
||||
import com.ai.da.model.dto.*;
|
||||
import com.ai.da.model.dto.ContestantDTO;
|
||||
import com.ai.da.model.vo.CheckOTPVO;
|
||||
import com.ai.da.model.vo.ContestantCountVO;
|
||||
import com.ai.da.service.GlobalAwardService;
|
||||
import com.ai.da.service.upload.UploadService;
|
||||
import com.ai.da.service.upload.UploadTask;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.annotations.ApiParam;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/global-award")
|
||||
@Api(tags = "全球奖项API", description = "全球奖项大赛管理和文件上传")
|
||||
public class GlobalAwardController {
|
||||
|
||||
@Resource
|
||||
private GlobalAwardService globalAwardService;
|
||||
|
||||
@Resource
|
||||
private UploadService uploadService;
|
||||
|
||||
// @PostMapping("/uploads/pdf")
|
||||
// public Response<String> uploadPdf(@RequestParam("file") MultipartFile file,
|
||||
// @RequestParam(value = "email", required = false) String email) throws Exception {
|
||||
// return Response.success(globalAwardService.uploadPdf(file, email));
|
||||
// }
|
||||
//
|
||||
// @PostMapping("/uploads/video")
|
||||
// public Response<String> uploadVideo(@RequestParam("file") MultipartFile file,
|
||||
// @RequestParam(value = "email", required = false) String email) throws Exception {
|
||||
// return Response.success(globalAwardService.uploadVideo(file, email));
|
||||
// }
|
||||
|
||||
// ===== 新增分片上传接口 =====
|
||||
|
||||
// ===== PDF分片上传接口 =====
|
||||
|
||||
/** 初始化PDF上传任务 */
|
||||
@PostMapping("/uploads/pdf/init")
|
||||
@ApiOperation(value = "初始化PDF上传", notes = "创建新的PDF上传任务并返回上传参数")
|
||||
public Response<UploadInitResponse> initPdfUpload(@ApiParam(value = "PDF上传初始化请求", required = true) @RequestBody UploadInitRequest request) {
|
||||
UploadTask uploadTask = uploadService.initPdfUpload(request);
|
||||
return Response.success(UploadInitResponse.builder()
|
||||
.uploadId(uploadTask.getUploadId())
|
||||
.chunkSize(uploadTask.getChunkSize())
|
||||
.totalChunks(uploadTask.getTotalChunks())
|
||||
.expiresAt(uploadTask.getExpiresAt())
|
||||
.build());
|
||||
}
|
||||
|
||||
/** 上传PDF分片 */
|
||||
@PostMapping("/uploads/pdf/chunk")
|
||||
@ApiOperation(value = "上传PDF分片", notes = "上传PDF文件的单个分片")
|
||||
public Response<UploadChunkResponse> uploadPdfChunk(
|
||||
@ApiParam(value = "PDF文件分片", required = true) @RequestParam("chunk") MultipartFile chunk,
|
||||
@ApiParam(value = "上传任务ID", required = true) @RequestParam("uploadId") String uploadId,
|
||||
@ApiParam(value = "分片索引(从0开始)", required = true) @RequestParam("chunkIndex") int chunkIndex,
|
||||
@ApiParam(value = "分片总数", required = true) @RequestParam("totalChunks") int totalChunks) {
|
||||
|
||||
UploadChunkResponse uploadChunkResponse = uploadService.uploadPdfChunk(uploadId, chunk, chunkIndex, totalChunks);
|
||||
return Response.success(uploadChunkResponse);
|
||||
}
|
||||
|
||||
/** 完成PDF上传 */
|
||||
@PostMapping("/uploads/pdf/complete")
|
||||
@ApiOperation(value = "完成PDF上传", notes = "完成PDF上传并合并所有分片")
|
||||
public Response<UploadCompleteResponse> completePdfUpload(@ApiParam(value = "PDF上传完成请求", required = true) @RequestBody UploadCompleteRequest request) {
|
||||
UploadCompleteResponse uploadCompleteResponse = uploadService.completePdfUpload(
|
||||
request.getUploadId(),
|
||||
request.getFileName(),
|
||||
request.getTotalSize(),
|
||||
request.getEmail(),
|
||||
request.getSecureToken());
|
||||
return Response.success(uploadCompleteResponse);
|
||||
}
|
||||
|
||||
/** 查询PDF上传状态 */
|
||||
@GetMapping("/uploads/pdf/status/{uploadId}")
|
||||
@ApiOperation(value = "查询PDF上传状态", notes = "获取PDF上传任务的当前状态")
|
||||
public Response<UploadStatusResponse> getPdfUploadStatus(@ApiParam(value = "上传任务ID", required = true) @PathVariable String uploadId) {
|
||||
UploadStatusResponse pdfUploadStatus = uploadService.getPdfUploadStatus(uploadId);
|
||||
return Response.success(pdfUploadStatus);
|
||||
}
|
||||
|
||||
// ===== 视频分片上传接口 =====
|
||||
|
||||
/** 初始化视频上传任务 */
|
||||
@PostMapping("/uploads/video/init")
|
||||
@ApiOperation(value = "初始化视频上传", notes = "创建新的视频上传任务并返回上传参数")
|
||||
public Response<UploadInitResponse> initVideoUpload(@ApiParam(value = "视频上传初始化请求", required = true) @RequestBody UploadInitRequest request) {
|
||||
UploadTask uploadTask = uploadService.initVideoUpload(request);
|
||||
return Response.success(UploadInitResponse.builder()
|
||||
.uploadId(uploadTask.getUploadId())
|
||||
.chunkSize(uploadTask.getChunkSize())
|
||||
.totalChunks(uploadTask.getTotalChunks())
|
||||
.expiresAt(uploadTask.getExpiresAt())
|
||||
.build());
|
||||
}
|
||||
|
||||
/** 上传视频分片 */
|
||||
@PostMapping("/uploads/video/chunk")
|
||||
@ApiOperation(value = "上传视频分片", notes = "上传视频文件的单个分片")
|
||||
public Response<UploadChunkResponse> uploadVideoChunk(
|
||||
@ApiParam(value = "视频文件分片", required = true) @RequestParam("chunk") MultipartFile chunk,
|
||||
@ApiParam(value = "上传任务ID", required = true) @RequestParam("uploadId") String uploadId,
|
||||
@ApiParam(value = "分片索引(从0开始)", required = true) @RequestParam("chunkIndex") int chunkIndex,
|
||||
@ApiParam(value = "分片总数", required = true) @RequestParam("totalChunks") int totalChunks) {
|
||||
|
||||
UploadChunkResponse uploadChunkResponse = uploadService.uploadVideoChunk(uploadId, chunk, chunkIndex, totalChunks);
|
||||
return Response.success(uploadChunkResponse);
|
||||
}
|
||||
|
||||
/** 完成视频上传 */
|
||||
@PostMapping("/uploads/video/complete")
|
||||
@ApiOperation(value = "完成视频上传", notes = "完成视频上传并合并所有分片")
|
||||
public Response<UploadCompleteResponse> completeVideoUpload(@ApiParam(value = "视频上传完成请求", required = true) @RequestBody UploadCompleteRequest request) {
|
||||
UploadCompleteResponse uploadCompleteResponse = uploadService.completeVideoUpload(
|
||||
request.getUploadId(),
|
||||
request.getFileName(),
|
||||
request.getTotalSize(),
|
||||
request.getEmail(),
|
||||
request.getSecureToken());
|
||||
return Response.success(uploadCompleteResponse);
|
||||
}
|
||||
|
||||
/** 查询视频上传状态 */
|
||||
@GetMapping("/uploads/video/status/{uploadId}")
|
||||
@ApiOperation(value = "查询视频上传状态", notes = "获取视频上传任务的当前状态")
|
||||
public Response<UploadStatusResponse> getVideoUploadStatus(@ApiParam(value = "上传任务ID", required = true) @PathVariable String uploadId) {
|
||||
UploadStatusResponse videoUploadStatus = uploadService.getVideoUploadStatus(uploadId);
|
||||
return Response.success(videoUploadStatus);
|
||||
}
|
||||
|
||||
@PostMapping("/contestants/save")
|
||||
@ApiOperation(value = "保存参赛者信息", notes = "保存或更新参赛者信息及已上传的文件")
|
||||
public Response<Map<String,Object>> submit(@ApiParam(value = "参赛者信息", required = true) @RequestBody ContestantDTO request) {
|
||||
return Response.success(globalAwardService.saveContestant(request));
|
||||
}
|
||||
|
||||
@GetMapping("/contestants/{id}")
|
||||
@ApiOperation(value = "根据id查询参赛者", notes = "根据id获取参赛者信息")
|
||||
public Response<ContestantDTO> getContestantByID(@ApiParam(value = "参赛者id", required = true) @PathVariable("id") String id) {
|
||||
ContestantDTO dto = globalAwardService.getContestantByID(id);
|
||||
return Response.success(dto);
|
||||
}
|
||||
|
||||
@GetMapping("/checkEmail")
|
||||
public Response<String> checkEmail(@RequestParam("email") String email) {
|
||||
globalAwardService.checkEmail(email);
|
||||
return Response.success();
|
||||
}
|
||||
|
||||
@GetMapping("/checkCode")
|
||||
public Response<CheckOTPVO> checkCode(@RequestParam("email") String email, @RequestParam("code") String code) {
|
||||
return Response.success(globalAwardService.checkCode(email, code));
|
||||
}
|
||||
|
||||
@GetMapping("/contestants/export")
|
||||
@ApiOperation(value = "导出参赛者列表为Excel", notes = "导出所有参赛者信息为xlsx并触发下载")
|
||||
public void exportContestants(HttpServletResponse response) throws Exception {
|
||||
byte[] data = globalAwardService.exportContestants();
|
||||
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
|
||||
response.setHeader("Content-Disposition", "attachment; filename=\"contestants.xlsx\"");
|
||||
response.setContentLength(data.length);
|
||||
response.getOutputStream().write(data);
|
||||
response.getOutputStream().flush();
|
||||
}
|
||||
|
||||
@PostMapping("/contestants/export/files")
|
||||
@ApiOperation(value = "导出参赛者文件为ZIP", notes = "根据参赛者编号范围导出PDF、视频和信息文件为ZIP,直接响应给浏览器")
|
||||
public void exportContestantFiles(@ApiParam(value = "参赛者文件导出请求", required = true) @RequestBody ContestantExportRequest request, HttpServletResponse response) throws Exception {
|
||||
byte[] zipData = globalAwardService.exportContestantFilesAsZip(request.getMinContestantNumber(), request.getMaxContestantNumber());
|
||||
if (zipData.length == 0) {
|
||||
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
||||
response.getWriter().write("No contestants found in the specified range.");
|
||||
return;
|
||||
}
|
||||
response.setContentType("application/zip");
|
||||
response.setHeader("Content-Disposition", "attachment; filename=\"contestants.zip\"");
|
||||
response.setContentLength(zipData.length);
|
||||
response.getOutputStream().write(zipData);
|
||||
response.getOutputStream().flush();
|
||||
}
|
||||
|
||||
@GetMapping("/contestants/count")
|
||||
@ApiOperation(value = "查询参赛者总数", notes = "查询数据库中参赛者的总数量和最大参赛者编号")
|
||||
public Response<ContestantCountVO> getContestantCount() {
|
||||
return Response.success(globalAwardService.getContestantCount());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
74
src/main/java/com/ai/da/controller/LLMController.java
Normal file
74
src/main/java/com/ai/da/controller/LLMController.java
Normal file
@@ -0,0 +1,74 @@
|
||||
package com.ai.da.controller;
|
||||
|
||||
import com.ai.da.common.response.PageBaseResponse;
|
||||
import com.ai.da.common.response.Response;
|
||||
import com.ai.da.mapper.primary.entity.ChatMessage;
|
||||
import com.ai.da.model.dto.*;
|
||||
import com.ai.da.service.LLMService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import java.util.*;
|
||||
|
||||
|
||||
@Tag(name = "llm模块")
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/api/llm")
|
||||
public class LLMController {
|
||||
|
||||
@Resource
|
||||
private LLMService llmService;
|
||||
|
||||
@Operation(summary = "对话")
|
||||
@CrossOrigin
|
||||
@GetMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
|
||||
public SseEmitter streamPrompt(@RequestParam String prompt,
|
||||
@RequestParam(required = false) Long projectId,
|
||||
@RequestParam(required = false) String fileUrl,
|
||||
@RequestParam(required = false) List<String> imageUrlList,
|
||||
@RequestParam(required = false) String process,
|
||||
@RequestParam String token,
|
||||
@RequestParam Boolean enableThinking) {
|
||||
return llmService.stream(prompt, projectId, fileUrl, imageUrlList, token, enableThinking, process);
|
||||
}
|
||||
|
||||
@Operation(summary = "对话")
|
||||
@CrossOrigin
|
||||
@GetMapping(value = "/streamNew", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
|
||||
public SseEmitter streamNew(@RequestParam String prompt,
|
||||
@RequestParam(required = false) Long projectId,
|
||||
@RequestParam(required = false) String fileUrl,
|
||||
@RequestParam(required = false) List<String> imageUrlList,
|
||||
@RequestParam(required = false) String process,
|
||||
@RequestParam String token,
|
||||
@RequestParam Boolean enableThinking) {
|
||||
return llmService.streamNew(prompt, projectId, fileUrl, imageUrlList, token, enableThinking, process);
|
||||
}
|
||||
|
||||
@Operation(summary = "对话创建项目")
|
||||
@GetMapping(value = "/chatCreateProject")
|
||||
public Response<Long> chatCreateProject(@RequestParam String prompt, @RequestParam String process,
|
||||
@RequestParam(required = false) String fileUrl,
|
||||
@RequestParam(required = false) List<String> imageUrlList) {
|
||||
return Response.success(llmService.chatCreateProject(prompt, process, fileUrl, imageUrlList));
|
||||
}
|
||||
|
||||
@Operation(summary = "上传文件")
|
||||
@PostMapping(value = "/uploadFile")
|
||||
public Response<List<String>> uploadFile(@RequestParam("file") MultipartFile file) {
|
||||
return Response.success(llmService.uploadFile(file));
|
||||
}
|
||||
|
||||
@Operation(summary = "获取历史聊天记录")
|
||||
@PostMapping(value = "/getChatHistory")
|
||||
public Response<PageBaseResponse<ChatMessage>> getChatHistory(@RequestBody ChatHistoryDTO chatHistoryDTO) {
|
||||
return Response.success(llmService.getChatHistory(chatHistoryDTO));
|
||||
}
|
||||
}
|
||||
@@ -14,25 +14,24 @@ import com.ai.da.model.vo.*;
|
||||
import com.ai.da.service.LibraryModelPointService;
|
||||
import com.ai.da.service.LibraryService;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.annotations.ApiParam;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.validation.Valid;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import java.io.File;
|
||||
import java.text.ParseException;
|
||||
import java.util.Date;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import java.util.*;
|
||||
|
||||
|
||||
@Api(tags = "library模块")
|
||||
@Tag(name = "library模块")
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/api/library")
|
||||
@@ -50,29 +49,30 @@ public class LibraryController {
|
||||
@Value("${minio.bucketName.sysImage}")
|
||||
private String sysImage;
|
||||
|
||||
@ApiOperation(value = "Library分页列表")
|
||||
@Operation(summary = "Library分页列表")
|
||||
@PostMapping("/queryLibraryPage")
|
||||
public Response<PageBaseResponse<QueryLibraryPageVO>> queryLibraryPage(@Valid @RequestBody QueryLibraryPageDTO query) {
|
||||
return Response.success(libraryService.queryLibraryPage(CopyUtil.copyObject(query, QueryLibraryPageServiceDTO.class)));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "Library分页列表(查询top和bottom)")
|
||||
@Operation(summary = "Library分页列表(查询top和bottom)")
|
||||
@PostMapping("/queryLibraryTopAndBottomPage")
|
||||
public Response<PageBaseResponse<QueryLibraryPageVO>> queryLibraryTopAndBottomPage(@Valid @RequestBody QueryLibraryTopPageDTO query) {
|
||||
return Response.success(libraryService.queryLibraryPage(CopyUtil.copyObject(query, QueryLibraryPageServiceDTO.class)));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "Library文件上传")
|
||||
@Operation(summary = "Library文件上传")
|
||||
@PostMapping("/upload")
|
||||
public Response<LibraryUpdateVo> upload(@RequestParam("file") MultipartFile file,
|
||||
@ApiParam("一级类型 Moodboard Printboard Sketchboard MarketingSketch Models")
|
||||
@Parameter(description = "一级类型 Moodboard Printboard Sketchboard MarketingSketch Models")
|
||||
@RequestParam(value = "level1Type") String level1Type,
|
||||
@ApiParam("二级类型 争对 Sketchboard; 有 Outwear Dress Blouse Skirt Trousers")
|
||||
@Parameter(description = "二级类型 争对 Sketchboard; 有 Outwear Dress Blouse Skirt Trousers")
|
||||
@RequestParam(value = "level2Type", required = false) String level2Type,
|
||||
@ApiParam("本地时区,比如 'Asia/Tokyo' 东京时间 , 'Asia/Shanghai' 北京时间 由js本地获取")
|
||||
@Parameter(description = "本地时区,比如 'Asia/Tokyo' 东京时间 , 'Asia/Shanghai' 北京时间 由js本地获取")
|
||||
@RequestParam(value = "timeZone") String timeZone,
|
||||
@RequestParam(value = "modelType") String modelType,
|
||||
@RequestParam(value = "sex") String sex,
|
||||
@RequestParam(value = "ageGroup") String ageGroup,
|
||||
@RequestParam(value = "checkMd5") Integer checkMd5) {
|
||||
if (null == file || StringUtils.isEmpty(file.getOriginalFilename())) {
|
||||
throw new BusinessException("file.cannot.be.empty");
|
||||
@@ -99,23 +99,23 @@ public class LibraryController {
|
||||
}
|
||||
}
|
||||
return Response.success(libraryService.upload(new LibraryUploadDTO(file, level1Type, level2Type,
|
||||
timeZone, md5, high, width, modelType, sex)));
|
||||
timeZone, md5, high, width, modelType, sex, ageGroup)));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "保存或者编辑template打点")
|
||||
@Operation(summary = "保存或者编辑template打点")
|
||||
@PostMapping("/saveOrEditTemplatePoint")
|
||||
public Response<LibraryModelPointVO> saveOrEditTemplatePoint(@Valid @RequestBody LibraryModelPointDTO libraryModelPoint) {
|
||||
return Response.success(libraryModelPointService.saveOrEditTemplatePoint(libraryModelPoint));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "批量Library修改用户文件名")
|
||||
@Operation(summary = "批量Library修改用户文件名")
|
||||
@PostMapping("/batchUpdateLibraryName")
|
||||
public Response<Boolean> batchUpdateLibraryName(@Valid @RequestBody LibraryUpdateDTO libraryUpdateDTO) {
|
||||
libraryService.updateLibraryName(libraryUpdateDTO);
|
||||
return Response.success(Boolean.TRUE);
|
||||
}
|
||||
|
||||
@ApiOperation(value = "批量删除library")
|
||||
@Operation(summary = "批量删除library")
|
||||
@PostMapping("/batchDeleteLibrary")
|
||||
public Response<Boolean> batchDeleteLibrary(@Valid @RequestBody LibraryDeleteDTO deleteDTO) {
|
||||
libraryService.batchDeleteLibrary(deleteDTO);
|
||||
@@ -123,10 +123,10 @@ public class LibraryController {
|
||||
}
|
||||
|
||||
|
||||
@ApiOperation(value = "Models打点预览")
|
||||
@Operation(summary = "Models打点预览")
|
||||
@PostMapping("/modelsDot")
|
||||
public Response<String> modelsDot(@ApiParam("file") @RequestPart(value = "file", required = false) MultipartFile file,
|
||||
@ApiParam("models对象") @RequestPart("models") ModelsDotDTO modelsDotDTO) {
|
||||
public Response<String> modelsDot(@Parameter(description = "file") @RequestPart(value = "file", required = false) MultipartFile file,
|
||||
@Parameter(description = "models对象") @RequestPart("models") ModelsDotDTO modelsDotDTO) {
|
||||
if (Objects.nonNull(file)) {
|
||||
if (StringUtils.isEmpty(file.getOriginalFilename())) {
|
||||
throw new BusinessException("file.cannot.be.empty");
|
||||
@@ -167,9 +167,9 @@ public class LibraryController {
|
||||
return response;
|
||||
}
|
||||
|
||||
@ApiOperation(value = "更新sketchboard level2type")
|
||||
@Operation(summary = "更新sketchboard level2type")
|
||||
@PostMapping("/updateLibraryLevel2Type")
|
||||
public Response<Boolean> updateLibraryLevel2Type(@RequestBody LibraryLevel2TypeUpdateDTO libraryLevel2TypeUpdateDTO) {
|
||||
public Response<Boolean> updateLibraryLevel2Type(@Validated @RequestBody LibraryLevel2TypeUpdateDTO libraryLevel2TypeUpdateDTO) {
|
||||
return Response.success(libraryService.updateLibraryLevel2Type(libraryLevel2TypeUpdateDTO));
|
||||
}
|
||||
|
||||
@@ -196,10 +196,57 @@ public class LibraryController {
|
||||
}
|
||||
|
||||
@PostMapping("moveLibraryData")
|
||||
@ApiOperation(value = "用户library数据迁移")
|
||||
@Operation(summary = "用户library数据迁移")
|
||||
public Response<Boolean> moveLibraryDate() throws ParseException {
|
||||
libraryService.moveLibraryDate();
|
||||
return Response.success(true);
|
||||
}
|
||||
|
||||
@Operation(summary = "将系统模特添加到个人library")
|
||||
@GetMapping("addSysModelToLib")
|
||||
public Response<Map<String, String>> addSysModelToLib(@Parameter(description = "系统模特id") @RequestParam("sysModelId")long sysModelId){
|
||||
return Response.success(libraryService.addSysModelToLib(sysModelId));
|
||||
}
|
||||
|
||||
@Operation(summary = "将个人library元素添加到公共库")
|
||||
@GetMapping("addToPublicLibrary")
|
||||
public Response<String> addToPublicLibrary(@Parameter(description = "元素的libraryId") @RequestParam("libraryId") Long libraryId){
|
||||
boolean b = libraryService.saveToOrganizationLibrary(libraryId);
|
||||
if (b){
|
||||
return Response.success("success");
|
||||
}else {
|
||||
return Response.success("fail");
|
||||
}
|
||||
}
|
||||
|
||||
@Operation(summary = "将个人library元素从公共库中删除")
|
||||
@GetMapping("deleteFromPublicLib")
|
||||
public Response<String> deleteFromPublicLib(@Parameter(description = "元素的libraryId") @RequestParam("libraryId") Long libraryId){
|
||||
libraryService.deleteFromPublicLib(libraryId);
|
||||
return Response.success("success");
|
||||
}
|
||||
|
||||
@Operation(summary = "获取公共库")
|
||||
@GetMapping("getPublicLib")
|
||||
public Response<PageBaseResponse<Library>> getPublicLib(@Parameter(description = "排序" ) @RequestParam(value = "order", defaultValue = "DESC", required = false) String order,
|
||||
@Parameter(description = "page") @RequestParam(value = "page", defaultValue = "1", required = false) Long page,
|
||||
@Parameter(description = "size") @RequestParam(value = "size", defaultValue = "20", required = false) Long size){
|
||||
|
||||
return Response.success(libraryService.getPublicLib(order, page, size));
|
||||
}
|
||||
|
||||
@Operation(summary = "管理员获取所有子账号lib元素")
|
||||
@GetMapping("getAllSubAccLib")
|
||||
public Response<PageBaseResponse<Library>> getAllSubAccLib(@Parameter(description = "排序") @RequestParam(value = "order", defaultValue = "DESC", required = false) String order,
|
||||
@Parameter(description = "page") @RequestParam(value = "page", defaultValue = "1", required = false) Long page,
|
||||
@Parameter(description = "size") @RequestParam(value = "size", defaultValue = "20", required = false) Long size){
|
||||
return Response.success(libraryService.getAllSubAccLib(order, page, size));
|
||||
}
|
||||
|
||||
@Operation(summary = "将系统sketch添加到library")
|
||||
@GetMapping("addSysSketchToLibrary")
|
||||
public Response<String> addSysSketchToLibrary(Long clothId, String path) {
|
||||
return Response.success(libraryService.addSysSketchToLibrary(clothId, path));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.ai.da.controller;
|
||||
|
||||
import com.ai.da.common.response.PageBaseResponse;
|
||||
import com.ai.da.common.response.Response;
|
||||
import com.ai.da.model.dto.GetNotificationDTO;
|
||||
import com.ai.da.model.vo.NotificationVO;
|
||||
import com.ai.da.model.dto.PublishSysNotificationDTO;
|
||||
import com.ai.da.service.MessageCenterService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Tag(name = "消息中心模块")
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/api/message")
|
||||
public class MessageCenterController {
|
||||
|
||||
@Resource
|
||||
private MessageCenterService messageCenterService;
|
||||
|
||||
// 获取未读消息总数
|
||||
@Operation(summary = "获取未读消息数")
|
||||
@GetMapping("/getUnreadCount")
|
||||
public Response<Map<String, Long>> getUnreadMessage(){
|
||||
return Response.success(messageCenterService.getAllTypeMessageUnreadCount());
|
||||
}
|
||||
|
||||
// 获取历史消息
|
||||
@Operation(summary = "获取历史消息")
|
||||
@PostMapping("/getHistoryNotification")
|
||||
public Response<PageBaseResponse<NotificationVO>> getHistoryNotification(@Valid @RequestBody GetNotificationDTO getNotificationDTO) {
|
||||
return Response.success(messageCenterService.getHistoryNotification(getNotificationDTO));
|
||||
}
|
||||
|
||||
// 已读消息
|
||||
@Operation(summary = "设置消息状态为已读")
|
||||
@PostMapping("/setReadStatus")
|
||||
public Response<Boolean> setReadStatus(@RequestParam("notificationIdList") List<Long> notificationIdList, @RequestParam("type") String type) {
|
||||
return Response.success(messageCenterService.setReadStatus(notificationIdList, type));
|
||||
}
|
||||
|
||||
// 发布系统消息
|
||||
@Operation(summary = "发布系统消息")
|
||||
@PostMapping("/publishSysMessage")
|
||||
public Response<String> publishSysMessage(@Valid @RequestBody PublishSysNotificationDTO message) {
|
||||
messageCenterService.publishSystemNotification(message);
|
||||
return Response.success("success");
|
||||
}
|
||||
|
||||
@Operation(summary = "一键已读")
|
||||
@PostMapping("/oneClickRead")
|
||||
public Response<String> setReadAll(@RequestParam("type") String type) {
|
||||
messageCenterService.setReadAll(type);
|
||||
return Response.success("success");
|
||||
}
|
||||
}
|
||||
@@ -3,18 +3,19 @@ package com.ai.da.controller;
|
||||
import com.ai.da.common.enums.OrderStatusEnum;
|
||||
import com.ai.da.common.response.PageBaseResponse;
|
||||
import com.ai.da.common.response.Response;
|
||||
import com.ai.da.mapper.primary.entity.OrderInfo;
|
||||
import com.ai.da.model.dto.QueryPageByTimeDTO;
|
||||
import com.ai.da.model.vo.OrderListVO;
|
||||
import com.ai.da.service.OrderInfoService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import com.ai.da.service.PaymentInfoService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.validation.Valid;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
|
||||
@CrossOrigin //开放前端的跨域访问
|
||||
@Api(tags = "商品订单管理")
|
||||
@Tag(name = "商品订单管理")
|
||||
@RestController
|
||||
@RequestMapping("/api/order-info")
|
||||
public class OrderInfoController {
|
||||
@@ -22,10 +23,13 @@ public class OrderInfoController {
|
||||
@Resource
|
||||
private OrderInfoService orderInfoService;
|
||||
|
||||
@ApiOperation("订单列表")
|
||||
@Resource
|
||||
private PaymentInfoService paymentInfoService;
|
||||
|
||||
@Operation(summary = "订单列表")
|
||||
@PostMapping("/list")
|
||||
public Response<PageBaseResponse<OrderInfo>> list(@Valid @RequestBody QueryPageByTimeDTO queryPageByTimeDTO){
|
||||
PageBaseResponse<OrderInfo> orderByAccountId = orderInfoService.getOrderByPage(queryPageByTimeDTO);
|
||||
public Response<PageBaseResponse<OrderListVO>> list(@Valid @RequestBody QueryPageByTimeDTO queryPageByTimeDTO){
|
||||
PageBaseResponse<OrderListVO> orderByAccountId = paymentInfoService.getPaymentInfo(queryPageByTimeDTO);
|
||||
// List<OrderInfo> list = orderInfoService.listOrderByCreateTimeDesc();
|
||||
return Response.success(orderByAccountId);
|
||||
}
|
||||
@@ -35,7 +39,7 @@ public class OrderInfoController {
|
||||
* @param orderNo
|
||||
* @return
|
||||
*/
|
||||
@ApiOperation("查询本地订单状态")
|
||||
@Operation(summary = "查询本地订单状态")
|
||||
@GetMapping("/query-order-status/{orderNo}")
|
||||
public Response<String> queryOrderStatus(@PathVariable String orderNo){
|
||||
String orderStatus = orderInfoService.getOrderStatus(orderNo);
|
||||
|
||||
@@ -1,38 +1,40 @@
|
||||
package com.ai.da.controller;
|
||||
|
||||
import com.ai.da.common.response.Response;
|
||||
import com.ai.da.model.dto.ProductPurchaseDTO;
|
||||
import com.ai.da.service.PayPalCheckoutService;
|
||||
import com.paypal.http.HttpResponse;
|
||||
import com.paypal.http.exceptions.SerializeException;
|
||||
import com.paypal.orders.Order;
|
||||
import com.paypal.payments.Refund;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.Valid;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
|
||||
@RestController
|
||||
@Api(tags = "PayPalCheckout接口")
|
||||
@Tag(name = "PayPalCheckout接口")
|
||||
@RequestMapping("/api/paypal")
|
||||
public class PayPalCheckoutController {
|
||||
|
||||
@Resource
|
||||
private PayPalCheckoutService payPalCheckoutService;
|
||||
|
||||
@ApiOperation(value = "创建订单")
|
||||
@PostMapping(value = "/trade/{amount}")
|
||||
public Response<HashMap<String, String>> createOrder(@PathVariable Integer amount, @RequestParam String returnUrl) throws SerializeException {
|
||||
HashMap<String, String> approvalUrl = payPalCheckoutService.createOrder(amount,returnUrl);
|
||||
@Operation(summary = "创建订单")
|
||||
@PostMapping(value = "/trade")
|
||||
public Response<HashMap<String, String>> createOrder(@Valid @RequestBody ProductPurchaseDTO productPurchaseDTO, HttpServletRequest request) throws SerializeException {
|
||||
HashMap<String, String> approvalUrl = payPalCheckoutService.createOrder(productPurchaseDTO, request);
|
||||
return Response.success(approvalUrl);
|
||||
}
|
||||
|
||||
@ApiOperation(value = "ipn异步回调")
|
||||
@Operation(summary = "ipn异步回调")
|
||||
@PostMapping(value = "/ipn/back")
|
||||
public Response<String> callback(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
||||
Boolean result = payPalCheckoutService.doPost(request, response);
|
||||
@@ -43,7 +45,7 @@ public class PayPalCheckoutController {
|
||||
}
|
||||
}
|
||||
|
||||
@ApiOperation(value = "查询指定订单")
|
||||
@Operation(summary = "查询指定订单")
|
||||
@PostMapping(value = "/trade/query/{orderNo}")
|
||||
public Response<Order> queryOrder(@PathVariable String orderNo) throws SerializeException {
|
||||
Order s = payPalCheckoutService.queryOrder(orderNo);
|
||||
@@ -51,7 +53,7 @@ public class PayPalCheckoutController {
|
||||
}
|
||||
|
||||
/** 不提供退款接口 */
|
||||
@ApiOperation("申请退款")
|
||||
@Operation(summary = "申请退款")
|
||||
@PostMapping("/trade/refund/{orderNo}/{reason}")
|
||||
public Response<HttpResponse<Refund>> refund(@PathVariable String orderNo, @PathVariable String reason) throws IOException {
|
||||
Boolean response = payPalCheckoutService.refundOrder(orderNo,reason);
|
||||
@@ -62,7 +64,7 @@ public class PayPalCheckoutController {
|
||||
}
|
||||
}
|
||||
|
||||
@ApiOperation("执行扣款")
|
||||
@Operation(summary = "执行扣款")
|
||||
@PostMapping("/trade/capture/{orderNo}")
|
||||
public Response<com.paypal.orders.Order> captureOrder(@PathVariable String orderNo) throws IOException {
|
||||
Order response = payPalCheckoutService.captureOrder(orderNo);
|
||||
|
||||
@@ -1,23 +1,25 @@
|
||||
package com.ai.da.controller;
|
||||
|
||||
import com.ai.da.common.config.exception.BusinessException;
|
||||
import com.ai.da.common.response.PageBaseResponse;
|
||||
import com.ai.da.common.response.Response;
|
||||
import com.ai.da.model.dto.*;
|
||||
import com.ai.da.model.vo.*;
|
||||
import com.ai.da.model.vo.CommentVO;
|
||||
import com.ai.da.model.vo.PortfolioVO;
|
||||
import com.ai.da.model.vo.UserLikeChooseVO;
|
||||
import com.ai.da.model.vo.UserLikeGroupVO;
|
||||
import com.ai.da.service.PortfolioService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import com.ai.da.service.UserFollowService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.validation.Valid;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import java.util.List;
|
||||
|
||||
@Api(tags = "Portfolio模块")
|
||||
@Tag(name = "Portfolio模块")
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/api/portfolio")
|
||||
@@ -26,95 +28,139 @@ public class PortfolioController {
|
||||
@Resource
|
||||
private PortfolioService portfolioService;
|
||||
|
||||
@ApiOperation(value = "发布作品集")
|
||||
@Resource
|
||||
private UserFollowService userFollowService;
|
||||
|
||||
@Operation(summary = "发布作品集")
|
||||
@PostMapping("/publish")
|
||||
public Response<Boolean> preLogin(@RequestParam("file") MultipartFile canvas, @RequestParam("data") String data) {
|
||||
public Response<Long> publish(@RequestParam("file") MultipartFile canvas, @RequestParam("data") String data) {
|
||||
return Response.success(portfolioService.publish(canvas, data));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "删除作品集")
|
||||
@Operation(summary = "删除作品集")
|
||||
@GetMapping("/delete")
|
||||
public Response<Boolean> delete(@RequestParam("id") Long id) {
|
||||
return Response.success(portfolioService.delete(id));
|
||||
}
|
||||
|
||||
@CrossOrigin
|
||||
@ApiOperation(value = "作品集page")
|
||||
@Operation(summary = "作品集page")
|
||||
@PostMapping("/page")
|
||||
public Response<PageBaseResponse<PortfolioVO>> page(@Valid @RequestBody QueryPortfolioPageDTO query) {
|
||||
return Response.success(portfolioService.page(query));
|
||||
}
|
||||
|
||||
@CrossOrigin
|
||||
@ApiOperation(value = "作品详情")
|
||||
@Operation(summary = "作品详情")
|
||||
@PostMapping("/detail")
|
||||
public Response<PortfolioVO> detail(@Valid @RequestBody PortfolioDTO portfolioDTO) {
|
||||
return Response.success(portfolioService.detail(portfolioDTO));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "选择作品")
|
||||
@Operation(summary = "选择作品")
|
||||
@PostMapping("/choose")
|
||||
public Response<UserLikeChooseVO> choose(@Valid @RequestBody PortfolioDTO portfolioDTO) {
|
||||
public Response<ProjectChooseVO> choose(@Valid @RequestBody PortfolioDTO portfolioDTO) {
|
||||
return Response.success(portfolioService.choose(portfolioDTO));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "更新作品")
|
||||
@Operation(summary = "更新作品")
|
||||
@PostMapping("/update")
|
||||
public Response<PortfolioVO> update(@Valid @RequestBody PortfolioDTO portfolioDTO) {
|
||||
return Response.success(portfolioService.update(portfolioDTO));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "广场用户注册")
|
||||
@Operation(summary = "广场用户注册")
|
||||
@PostMapping("/designWorksRegister")
|
||||
public Response<Boolean> designWorksRegister(@Valid @RequestBody DesignWorksRegisterDTO designWorksRegisterDTO) {
|
||||
return Response.success(portfolioService.designWorksRegister(designWorksRegisterDTO));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "喜欢")
|
||||
@Operation(summary = "喜欢")
|
||||
@GetMapping("/like")
|
||||
public Response<Boolean> like(@RequestParam("id") Long id) {
|
||||
return Response.success(portfolioService.like(id));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "不喜欢")
|
||||
@Operation(summary = "不喜欢")
|
||||
@GetMapping("/unlike")
|
||||
public Response<Boolean> unlike(@RequestParam("id") Long id) {
|
||||
return Response.success(portfolioService.unlike(id));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "获取点赞数")
|
||||
@Operation(summary = "获取点赞数")
|
||||
@GetMapping("/getLikeCount")
|
||||
public Response<Long> getLikeCount(@RequestParam("id") Long id) {
|
||||
return Response.success(portfolioService.getLikeCount(id));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "评论")
|
||||
@Operation(summary = "评论")
|
||||
@PostMapping("/comment")
|
||||
public Response<Boolean> comment(@Valid @RequestBody CommentDTO commentDTO) {
|
||||
return Response.success(portfolioService.comment(commentDTO));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "评论列表")
|
||||
@Operation(summary = "评论列表")
|
||||
@PostMapping("/commentPage")
|
||||
public Response<PageBaseResponse<CommentVO>> commentPage(@Valid @RequestBody CommentPageDTO commentPageDTO) {
|
||||
return Response.success(portfolioService.commentPage(commentPageDTO));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "增加浏览量")
|
||||
@Operation(summary = "增加浏览量")
|
||||
@GetMapping("/viewsIncrease")
|
||||
public Response<Boolean> viewsIncrease(@RequestParam("id") Long id) {
|
||||
return Response.success(portfolioService.viewsIncrease(id));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "浏览量获取")
|
||||
@Operation(summary = "浏览量获取")
|
||||
@GetMapping("/viewsGet")
|
||||
public Response<Long> viewsGet(@RequestParam("id") Long id) {
|
||||
return Response.success(portfolioService.viewsGet(id));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "删除评论")
|
||||
@Operation(summary = "删除评论")
|
||||
@PostMapping("/commentDelete")
|
||||
public Response<Boolean> commentDelete(@Valid @RequestBody CommentDTO commentDTO) {
|
||||
return Response.success(portfolioService.commentDelete(commentDTO));
|
||||
}
|
||||
|
||||
@Operation(summary = "关注")
|
||||
@GetMapping("/follow")
|
||||
public Response<String> follow(@RequestParam("followeeId") Long followeeId) {
|
||||
portfolioService.follow(followeeId);
|
||||
return Response.success(BusinessException.getMessageFromResource("subscription.success"));
|
||||
}
|
||||
|
||||
@Operation(summary = "取消关注")
|
||||
@GetMapping("/cancelFollow")
|
||||
public Response<String> cancelFollow(@RequestParam("followeeId") Long followeeId) {
|
||||
portfolioService.cancelFollow(followeeId);
|
||||
return Response.success(BusinessException.getMessageFromResource("unsubscribe.success"));
|
||||
}
|
||||
|
||||
@Operation(summary = "获取关注列表")
|
||||
@PostMapping("/getFolloweeList")
|
||||
public Response<List<AccountFollowVO>> getFolloweeList(@Valid @RequestBody GetFollowListDTO getFollowListDTO) {
|
||||
return Response.success(userFollowService.getFolloweeList(getFollowListDTO));
|
||||
}
|
||||
|
||||
@Operation(summary = "获取粉丝列表")
|
||||
@PostMapping("/getFollowerList")
|
||||
public Response<List<AccountFollowVO>> getFollowerList(@Valid @RequestBody GetFollowListDTO getFollowListDTO) {
|
||||
return Response.success(userFollowService.getFollowerList(getFollowListDTO));
|
||||
}
|
||||
|
||||
|
||||
/* @Operation(summary = "按标签名查询作品")
|
||||
@GetMapping("/queryPortfolioByTag")
|
||||
public Response<List<PortfolioVO>> queryPortfolioByTag(@RequestParam(value = "tagName", required = false) String tagName,
|
||||
@RequestParam(value = "tagId", required = false) Long tagId) {
|
||||
return Response.success(portfolioService.queryPortfolioByTag(tagName, tagId));
|
||||
}*/
|
||||
|
||||
@Operation(summary = "将企业或教育版中的作品发布到公共gallery")
|
||||
@GetMapping("/toPublic")
|
||||
public Response<String> setPortfolioToPublic(@RequestParam("portfolioId") Long portfolioId) {
|
||||
portfolioService.setPortfolioToPublic(portfolioId);
|
||||
return Response.success("success");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,19 +4,19 @@ package com.ai.da.controller;
|
||||
import com.ai.da.common.response.Response;
|
||||
import com.ai.da.mapper.primary.entity.Product;
|
||||
import com.ai.da.service.ProductService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.web.bind.annotation.CrossOrigin;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import jakarta.annotation.Resource;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
@CrossOrigin //开放前端的跨域访问
|
||||
@Api(tags = "商品管理")
|
||||
@Tag(name = "商品管理")
|
||||
@RestController
|
||||
@RequestMapping("/api/product")
|
||||
public class ProductController {
|
||||
@@ -24,14 +24,14 @@ public class ProductController {
|
||||
@Resource
|
||||
private ProductService productService;
|
||||
|
||||
@ApiOperation("测试接口")
|
||||
@Operation(summary = "测试接口")
|
||||
@GetMapping("/test")
|
||||
public Response<String> test(){
|
||||
|
||||
return Response.success("now" + new Date());
|
||||
}
|
||||
|
||||
@ApiOperation("商品列表")
|
||||
@Operation(summary = "商品列表")
|
||||
@GetMapping("/list")
|
||||
public Response<List<Product>> list(){
|
||||
|
||||
|
||||
152
src/main/java/com/ai/da/controller/ProjectController.java
Normal file
152
src/main/java/com/ai/da/controller/ProjectController.java
Normal file
@@ -0,0 +1,152 @@
|
||||
package com.ai.da.controller;
|
||||
|
||||
import com.ai.da.common.config.MyTaskScheduler;
|
||||
import com.ai.da.common.response.PageBaseResponse;
|
||||
import com.ai.da.common.response.Response;
|
||||
import com.ai.da.model.dto.*;
|
||||
import com.ai.da.model.vo.*;
|
||||
import com.ai.da.service.ProjectService;
|
||||
import com.ai.da.service.UserLikeGroupService;
|
||||
import com.ai.da.service.WorkspaceService;
|
||||
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
|
||||
import io.minio.errors.MinioException;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.Valid;
|
||||
import java.io.IOException;
|
||||
|
||||
@Tag(name = "Project模块")
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/api/project")
|
||||
public class ProjectController {
|
||||
|
||||
@Resource
|
||||
private WorkspaceService workspaceService;
|
||||
|
||||
@Resource
|
||||
private UserLikeGroupService userLikeGroupService;
|
||||
|
||||
@Resource
|
||||
private ProjectService projectService;
|
||||
|
||||
@PostMapping("/saveOrUpdate")
|
||||
@ApiOperationSupport(order = 1)
|
||||
@Operation(summary = "新增或编辑", description = "传入project")
|
||||
public Response<SaveOrUpdateProjectVO> saveOrUpdateProject(@Valid @RequestBody ProjectDTO projectDTO) {
|
||||
return Response.success(workspaceService.saveOrUpdateProject(projectDTO));
|
||||
}
|
||||
|
||||
@PostMapping("/page")
|
||||
@ApiOperationSupport(order = 2)
|
||||
@Operation(summary = "分页查询", description = "传入project")
|
||||
public Response<PageBaseResponse<ProjectVO>> page(@Valid @RequestBody ProjectQueryDTO projectQueryDTO) {
|
||||
return Response.success(PageBaseResponse.success(userLikeGroupService.getPage(projectQueryDTO)));
|
||||
}
|
||||
|
||||
// @PostMapping("/detail")
|
||||
// @ApiOperationSupport(order = 3)
|
||||
// @ApiOperation(value = "详情", notes = "传入project")
|
||||
// public Response saveOrUpdateProject(@Valid @RequestBody ProjectDTO projectDTO) {
|
||||
// return Response.success(workspaceService.saveOrUpdateProject(projectDTO));
|
||||
// }
|
||||
//
|
||||
@PostMapping("/choose")
|
||||
@ApiOperationSupport(order = 4)
|
||||
@Operation(summary = "选择", description = "传入project")
|
||||
public Response<ProjectChooseVO> choose(@Valid @RequestBody ProjectDTO projectDTO) {
|
||||
return Response.success(userLikeGroupService.choose(projectDTO));
|
||||
}
|
||||
|
||||
@PostMapping("/getModuleContent")
|
||||
@ApiOperationSupport(order = 5)
|
||||
@Operation(summary = "获取模块内容", description = "传入project")
|
||||
public Response<ModuleChooseVO> getModuleContent(@Valid @RequestBody ProjectDTO projectDTO) {
|
||||
return Response.success(userLikeGroupService.getModuleContent(projectDTO));
|
||||
}
|
||||
|
||||
@PostMapping("/saveModuleContent")
|
||||
@ApiOperationSupport(order = 6)
|
||||
@Operation(summary = "存储模块内容", description = "传入project")
|
||||
public Response<ModuleChooseVO> saveModuleContent(@Valid @RequestBody ModuleSaveDTO moduleSaveDTO) {
|
||||
return Response.success(userLikeGroupService.saveModuleContent(moduleSaveDTO));
|
||||
}
|
||||
//
|
||||
// @PostMapping("/delete")
|
||||
// @ApiOperationSupport(order = 5)
|
||||
// @ApiOperation(value = "删除", notes = "传入project")
|
||||
// public Response saveOrUpdateProject(@Valid @RequestBody ProjectDTO projectDTO) {
|
||||
// return Response.success(workspaceService.saveOrUpdateProject(projectDTO));
|
||||
// }
|
||||
|
||||
@PostMapping("/getMannequinDetail")
|
||||
@ApiOperationSupport(order = 7)
|
||||
@Operation(summary = "获取模特详情", description = "传入mannequinId")
|
||||
public Response<QueryLibraryPageVO> getMannequinDetail(@Valid @RequestBody MannequinDTO mannequinDTO) {
|
||||
return Response.success(userLikeGroupService.getMannequinDetail(mannequinDTO));
|
||||
}
|
||||
|
||||
@PostMapping("/threeDPage")
|
||||
@ApiOperationSupport(order = 8)
|
||||
@Operation(summary = "获取3Dpage", description = "传入ThreeDLayoutQueryDTO")
|
||||
public Response<PageBaseResponse<ThreeDLayoutVO>> threeDPage(@Valid @RequestBody ThreeDLayoutQueryDTO threeDLayoutQueryDTO) {
|
||||
return Response.success(PageBaseResponse.success(userLikeGroupService.getThreeDLayoutPage(threeDLayoutQueryDTO)));
|
||||
}
|
||||
|
||||
@PostMapping("/getLayoutDetail")
|
||||
@ApiOperationSupport(order = 9)
|
||||
@Operation(summary = "获取3D详情", description = "传入project")
|
||||
public Response<ThreeDVO> getLayoutDetail(@RequestParam(value = "threeDSimpleId") Long threeDSimpleId) {
|
||||
return Response.success(userLikeGroupService.getLayoutDetail(threeDSimpleId));
|
||||
}
|
||||
|
||||
@PostMapping("/getThreeDSize")
|
||||
@ApiOperationSupport(order = 10)
|
||||
@Operation(summary = "获取尺码", description = "传入project")
|
||||
public Response<ThreeDSizeVO> getThreeDSize(@RequestParam(value = "threeDSimpleId") Long threeDSimpleId) {
|
||||
return Response.success(userLikeGroupService.getThreeDSize(threeDSimpleId));
|
||||
}
|
||||
|
||||
@GetMapping("/getThreeDGlb")
|
||||
@ApiOperationSupport(order = 11)
|
||||
@Operation(summary = "获取GLB", description = "传入project")
|
||||
public void getThreeDGlb(@RequestParam(value = "threeDSimpleId") Long threeDSimpleId, HttpServletResponse response) throws MinioException, IOException {
|
||||
userLikeGroupService.getThreeDGlb(threeDSimpleId, response);
|
||||
}
|
||||
|
||||
@GetMapping("/downloadZip")
|
||||
@ApiOperationSupport(order = 11)
|
||||
@Operation(summary = "下载", description = "传入project")
|
||||
public Response<String> downloadZip(@RequestParam(value = "threeDSimpleId") Long threeDSimpleId, @RequestParam(value = "sizeType") String sizeType, @RequestParam(value = "size") String size, HttpServletResponse response) throws MinioException, IOException {
|
||||
return Response.success(userLikeGroupService.downloadZip(threeDSimpleId, sizeType, size, response));
|
||||
}
|
||||
|
||||
@PostMapping("/delete")
|
||||
@ApiOperationSupport(order = 12)
|
||||
@Operation(summary = "删除", description = "传入project")
|
||||
public Response<Boolean> delete(@RequestParam("projectId") Long projectId) {
|
||||
return Response.success(userLikeGroupService.delete(projectId));
|
||||
}
|
||||
|
||||
|
||||
@Resource
|
||||
private MyTaskScheduler myTaskScheduler;
|
||||
|
||||
/*@GetMapping("/dataMigration")
|
||||
public String dataMigration(){
|
||||
myTaskScheduler.projectDataCreate();
|
||||
return "success";
|
||||
}*/
|
||||
|
||||
@GetMapping("/getNextSequence")
|
||||
public Response<Integer> getNextSequence(){
|
||||
return Response.success(projectService.getOrCreateSequence());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -16,14 +16,17 @@ import com.ai.da.service.ChatRobotService;
|
||||
import com.ai.da.service.LibraryService;
|
||||
import com.ai.da.service.SuperResolutionService;
|
||||
import com.ai.da.service.SysFileService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.annotations.ApiParam;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
@@ -31,7 +34,7 @@ import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
||||
@Api(tags = "python对接模块")
|
||||
@Tag(name = "python对接模块")
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/api/python")
|
||||
@@ -49,17 +52,17 @@ public class PythonController {
|
||||
@Resource
|
||||
private SuperResolutionService superResolutionService;
|
||||
|
||||
@ApiOperation(value = "python服务保存图片到java服务")
|
||||
@Operation(summary = "python服务保存图片到java服务")
|
||||
@PostMapping("/saveGeneratePicture")
|
||||
public Response<String> upload(@RequestParam("file") MultipartFile file,
|
||||
@ApiParam("操作类型 generatePrint ->生成印花 " +
|
||||
@Parameter(description = "操作类型 generatePrint ->生成印花 " +
|
||||
"designCollection ->设计collection generateAdvancedDesign ->生成高级design" +
|
||||
"generateSketch -> 生成草图")
|
||||
@RequestParam(value = "operateType") String operateType) {
|
||||
return Response.success(pythonService.upload(file, operateType));
|
||||
}
|
||||
|
||||
// @ApiOperation(value = "python服务保存多张图片到java服务")
|
||||
// @Operation(summary = "python服务保存多张图片到java服务")
|
||||
// @PostMapping("/saveMultiGeneratePicture")
|
||||
// public Response<List<String>> uploadMultiple(@RequestParam("files") MultipartFile[] files,
|
||||
// @ApiParam("操作类型 generatePrint ->生成印花 " +
|
||||
@@ -69,13 +72,13 @@ public class PythonController {
|
||||
// return Response.success(pythonService.upload(files, operateType));
|
||||
// }
|
||||
|
||||
@ApiOperation(value = "通过文件类型获取系统文件")
|
||||
@Operation(summary = "通过文件类型获取系统文件")
|
||||
@GetMapping("/getSysFileByLevel2Type")
|
||||
public Response<List<SysFileVO>> getSysFileByLevel2Type(/*@RequestParam(value = "level2Type",required = false) String level2Type*/) {
|
||||
return Response.success(sysFileService.getByLevel2Type("All"));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "通过用户id获取library")
|
||||
@Operation(summary = "通过用户id获取library")
|
||||
@GetMapping("/getLibraryByUserId")
|
||||
public Response<Map<String, List<String>>> getLibraryByUserId(@RequestParam(value = "userId") Long userId) {
|
||||
List<PythonLibraryVo> response = CopyUtil.copyList(libraryService.selectByAccountIdAnd1TypeList(userId,
|
||||
@@ -86,37 +89,47 @@ public class PythonController {
|
||||
}
|
||||
|
||||
@CrossOrigin
|
||||
@ApiOperation(value = "发送用户输入消息")
|
||||
@Operation(summary = "发送用户输入消息")
|
||||
@PostMapping("/chatStream")
|
||||
public Response<ChatRobotVO> MessageToPythonChatStream(@RequestBody ChatSendDTO chatSendDTO) {
|
||||
log.info(chatSendDTO.toString());
|
||||
return Response.success(chatRobotService.sendMessageToChatRobot(chatSendDTO));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "血条")
|
||||
@Operation(summary = "血条")
|
||||
@GetMapping("/getBloodBars")
|
||||
public Response<BigDecimal> getBloodBars(@RequestParam(value = "userId") Long userId) {
|
||||
return Response.success(chatRobotService.getBloodBars(userId));
|
||||
}
|
||||
|
||||
@CrossOrigin
|
||||
@ApiOperation(value = "likeOrUnlike")
|
||||
@Operation(summary = "likeOrUnlike")
|
||||
@PostMapping("/pictureLikeOrUnLike")
|
||||
public Response<Library> pictureLikeOrUnLike(@RequestBody ChatRobotLibraryDTO chatRobotLibraryDTO) {
|
||||
return Response.success(chatRobotService.pictureLikeOrUnLike(chatRobotLibraryDTO));
|
||||
}
|
||||
|
||||
@CrossOrigin
|
||||
@ApiOperation(value = "刷新会话缓存")
|
||||
@Operation(summary = "刷新会话缓存")
|
||||
@PostMapping("/flush")
|
||||
public Response<String> ChatBufferFlush(@RequestBody ChatFlushDTO chatFlushDTO) {
|
||||
return Response.success(chatRobotService.chatBufferFlush(chatFlushDTO));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "超分辨率")
|
||||
@Operation(summary = "超分辨率")
|
||||
@PostMapping("/prepareForSR")
|
||||
public Response<List<String>> superResolution(@RequestBody List<SuperResolutionDTO> superResolutionDTO) {
|
||||
return Response.success(superResolutionService.prepareForSR(superResolutionDTO));
|
||||
}
|
||||
|
||||
@CrossOrigin
|
||||
@Operation(summary = "Seg Anything 转发接口")
|
||||
@PostMapping("/segAnything")
|
||||
public Response<String> segAnything(@RequestBody Map<String, Object> payload) {
|
||||
// 将前端传来的 Map 转为 fastjson JSONObject 并转发给 python 服务
|
||||
JSONObject requestJson = (JSONObject) JSON.toJSON(payload);
|
||||
String url = pythonService.segAnything(requestJson);
|
||||
return Response.success(url);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,24 +17,29 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.google.common.base.Function;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.annotations.ApiParam;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.validation.Valid;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
||||
@Api(tags = "History模块(saved Collection)")
|
||||
@Tag(name = "History模块(saved Collection)")
|
||||
@Slf4j
|
||||
@RestController
|
||||
@Validated
|
||||
@RequestMapping("/api/history")
|
||||
public class SavedCollectionController {
|
||||
@Resource
|
||||
@@ -52,7 +57,7 @@ public class SavedCollectionController {
|
||||
@Resource
|
||||
private PortfolioService portfolioService;
|
||||
|
||||
@ApiOperation(value = "History用户分页分组列表")
|
||||
@Operation(summary = "History用户分页分组列表")
|
||||
@PostMapping("/queryUserGroup")
|
||||
public Response<PageBaseResponse<UserLikeGroupVO>> queryUserGroup(@Valid @RequestBody QueryHistoryPageDTO query) {
|
||||
AuthPrincipalVo authPrincipalVo = UserContext.getUserHolder();
|
||||
@@ -104,9 +109,9 @@ public class SavedCollectionController {
|
||||
}
|
||||
List<Long> groupIds = page.getRecords().stream().map(UserLikeGroup::getId).collect(Collectors.toList());
|
||||
List<UserLikeVO> groupDetails = userLikeService.getGroupDetails(groupIds);
|
||||
if (CollectionUtils.isEmpty(groupDetails)) {
|
||||
throw new BusinessException("groupDetails.not.found");
|
||||
}
|
||||
// if (CollectionUtils.isEmpty(groupDetails)) {
|
||||
// throw new BusinessException("groupDetails.not.found");
|
||||
// }
|
||||
Map<Long, List<UserLikeVO>> groupDetailMap = groupDetails.stream()
|
||||
.collect(Collectors.groupingBy(UserLikeVO::getUserLikeGroupId));
|
||||
|
||||
@@ -117,13 +122,17 @@ public class SavedCollectionController {
|
||||
userLikeGroupVO.setUpdateDate(group.getUpdateDate().getTime());
|
||||
userLikeGroupVO.setAuthor(account.getUserName());
|
||||
//count 和detail
|
||||
List<UserLikeVO> details = groupDetailMap.get(group.getId());
|
||||
for (UserLikeVO detail : details) {
|
||||
TDesignPythonOutfit tDesignPythonOutfit = designPythonOutfitMapper.selectById(detail.getDesignOutfitId());
|
||||
detail.setUrl(minioUtil.getPreSignedUrl(tDesignPythonOutfit.getDesignUrl(), 24 * 60));
|
||||
if (groupDetailMap.keySet().contains(group.getId())) {
|
||||
List<UserLikeVO> details = groupDetailMap.get(group.getId());
|
||||
for (UserLikeVO detail : details) {
|
||||
TDesignPythonOutfit tDesignPythonOutfit = designPythonOutfitMapper.selectById(detail.getDesignOutfitId());
|
||||
detail.setUrl(minioUtil.getPreSignedUrl(tDesignPythonOutfit.getDesignUrl(), 24 * 60));
|
||||
}
|
||||
userLikeGroupVO.setGroupDetails(details);
|
||||
userLikeGroupVO.setSketchCount(CollectionUtils.isEmpty(details) ? 0 : details.size());
|
||||
}else {
|
||||
userLikeGroupVO.setSketchCount(0);
|
||||
}
|
||||
userLikeGroupVO.setGroupDetails(details);
|
||||
userLikeGroupVO.setSketchCount(CollectionUtils.isEmpty(details) ? 0 : details.size());
|
||||
if (userLikeGroupVO.getOriginal() == 0) {
|
||||
userLikeGroupVO.setOriginalAccountName(accountService.getById(userLikeGroupVO.getOriginalAccountId()).getUserName());
|
||||
Portfolio byId = portfolioService.getByIdAll(userLikeGroupVO.getOriginalPortfolioId());
|
||||
@@ -139,21 +148,21 @@ public class SavedCollectionController {
|
||||
return Response.success(PageBaseResponse.success(convert));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "History用户分组详情,目前弃用 ")
|
||||
@Operation(summary = "History用户分组详情,目前弃用 ")
|
||||
@GetMapping("/getUserGroupDetail")
|
||||
public Response<List<UserLikeVO>> getUserGroupDetail(
|
||||
@ApiParam("用户分组id") @RequestParam("userGroupId") Long userGroupId) {
|
||||
@Parameter(description = "用户分组id") @RequestParam("userGroupId") Long userGroupId) {
|
||||
return Response.success(userLikeService.getGroupDetail(userGroupId));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "History修改用户分组名")
|
||||
@Operation(summary = "History修改用户分组名")
|
||||
@PostMapping("/updateUserGroupName")
|
||||
public Response<HistoryUpdateVO> updateUserGroupName(@Valid @RequestBody HistoryUpdateDTO historyUpdateDTO) {
|
||||
return Response.success(userLikeGroupService.updateUserGroupName(historyUpdateDTO.getUserGroupId()
|
||||
, historyUpdateDTO.getUserGroupName(), historyUpdateDTO.getTimeZone()));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "History删除用户分组")
|
||||
@Operation(summary = "History删除用户分组")
|
||||
@PostMapping("/deleteUserGroup")
|
||||
public Response<Boolean> deleteUserGroup(@Valid @RequestBody HistoryDeleteDTO deleteDTO) {
|
||||
userLikeGroupService.deleteUserGroup(deleteDTO.getUserGroupId());
|
||||
@@ -161,78 +170,175 @@ public class SavedCollectionController {
|
||||
return Response.success(Boolean.TRUE);
|
||||
}
|
||||
|
||||
@ApiOperation(value = "History choose")
|
||||
@Operation(summary = "History choose")
|
||||
@GetMapping("/choose")
|
||||
public Response<UserLikeChooseVO> choose(
|
||||
@ApiParam("用户分组id") @RequestParam("userGroupId") Long userGroupId) {
|
||||
@Parameter(description = "用户分组id") @RequestParam("userGroupId") Long userGroupId) {
|
||||
return Response.success(userLikeGroupService.choose(userGroupId));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "exportSave")
|
||||
@Operation(summary = "exportSave")
|
||||
@PostMapping("/exportSave")
|
||||
public Response<Boolean> exportSave(@RequestParam("file") MultipartFile file, @RequestParam("userLikeGroupId") Long userLikeGroupId) {
|
||||
return Response.success(userLikeGroupService.exportSave(file, userLikeGroupId));
|
||||
public Response<Long> exportSave(@RequestParam("file") MultipartFile file, @RequestParam(value = "projectId", required = false) Long projectId,
|
||||
@RequestParam("module") String module, @RequestParam(value = "designItemDetailId", required = false) Long designItemDetailId) {
|
||||
return Response.success(userLikeGroupService.exportSave(file, projectId, module, designItemDetailId));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "exportSearch")
|
||||
@Operation(summary = "exportSearch")
|
||||
@PostMapping("/exportSearch")
|
||||
public Response<JSONObject> exportSearch(@Valid @RequestBody ExportSearchDTO exportSearchDTO) {
|
||||
return Response.success(userLikeGroupService.exportSearch(exportSearchDTO.getUserLikeGroupId()));
|
||||
return Response.success(userLikeGroupService.exportSearch(exportSearchDTO));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "toProduct")
|
||||
@Operation(summary = "toProduct")
|
||||
@PostMapping("/toProduct")
|
||||
public Response<List<ToProductImageResult>> toProduct(@Valid @RequestBody ToProductImageDTO toProductImageDTO) {
|
||||
public Response<List<ToProductImageResultVO>> toProduct(@Valid @RequestBody ToProductImageDTO toProductImageDTO) {
|
||||
return Response.success(userLikeGroupService.toProduct(toProductImageDTO));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "toProductImageElementUpload")
|
||||
@Operation(summary = "toProductImageElementUpload")
|
||||
@PostMapping("/toProductImageElementUpload")
|
||||
public Response<ToProductElementVO> toProductImageElementUpload(@RequestParam("file") MultipartFile file, @RequestParam(value = "userlikeGroupId") Long userLikeGroupId) {
|
||||
return Response.success(userLikeGroupService.toProductImageElementUpload(file, userLikeGroupId));
|
||||
public Response<ToProductElementVO> toProductImageElementUpload(@RequestParam("file") MultipartFile file,
|
||||
@RequestParam(value = "projectId", required = false) Long projectId,
|
||||
@Parameter(description = "首尾帧", example = "first",
|
||||
schema = @Schema(allowableValues = {"first", "last"}))
|
||||
@RequestParam(value = "type", required = false) String type) {
|
||||
return Response.success(userLikeGroupService.toProductImageElementUpload(file, projectId, type));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "productImageLike")
|
||||
@Operation(summary = "toProductImageElementDelete")
|
||||
@PostMapping("/toProductImageElementDelete")
|
||||
public Response<Boolean> toProductImageElementDelete(@RequestParam(value = "id") Long id) {
|
||||
return Response.success(userLikeGroupService.toProductImageElementDelete(id));
|
||||
}
|
||||
|
||||
@Operation(summary = "productImageLike")
|
||||
@PostMapping("/productImageLike")
|
||||
public Response<Boolean> productImageLike(@Valid @RequestBody ProductImageLikeDTO productImageLikeDTO) {
|
||||
return Response.success(userLikeGroupService.productImageLike(productImageLikeDTO));
|
||||
public Response<CollectionSort> productImageLike(@Valid @RequestBody ProductImageLikeDTO productImageLikeDTO) {
|
||||
CollectionSort collectionSort = userLikeGroupService.productImageLike(productImageLikeDTO);
|
||||
return Response.success(collectionSort);
|
||||
}
|
||||
|
||||
@ApiOperation(value = "productImageUnLike")
|
||||
@Operation(summary = "collectionLikeUpdate")
|
||||
@PostMapping("/collectionLikeUpdate")
|
||||
public Response<Boolean> collectionLikeUpdate(@Valid @RequestBody CollectionLikeUpdateDTO collectionLikeUpdateDTO) {
|
||||
return Response.success(userLikeGroupService.collectionLikeUpdate(collectionLikeUpdateDTO));
|
||||
}
|
||||
|
||||
@Operation(summary = "productImageUnLike")
|
||||
@PostMapping("/productImageUnLike")
|
||||
public Response<Boolean> productImageUnLike(@Valid @RequestBody ProductImageLikeDTO productImageLikeDTO) {
|
||||
return Response.success(userLikeGroupService.productImageUnLike(productImageLikeDTO));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "获取生成结果")
|
||||
@Operation(summary = "获取生成结果")
|
||||
@PostMapping("/toProductImageResult")
|
||||
public Response<List<MagicToolResultVO>> getToProductImageResult(@Valid @RequestBody List<String> taskIdList) {
|
||||
List<MagicToolResultVO> magicToolResultVOList = userLikeGroupService.getToProductImageResultList(taskIdList);
|
||||
return Response.success(magicToolResultVOList);
|
||||
}
|
||||
|
||||
@ApiOperation(value = "画布用户上传元素")
|
||||
@Operation(summary = "画布用户上传元素")
|
||||
@PostMapping("/canvasElementUpload")
|
||||
public Response<CanvasElementUpload> canvasElementUpload(@RequestParam("file") MultipartFile file) {
|
||||
return Response.success(userLikeGroupService.canvasElementUpload(file));
|
||||
}
|
||||
|
||||
@ApiOperation("productImageLikeList")
|
||||
@Operation(summary = "productImageLikeList")
|
||||
@PostMapping("/productImageLikeList")
|
||||
public Response<List<ToProductImageResultVO>> productImageLikeList(@Valid @RequestBody ToProductImageDTO toProductImageDTO) {
|
||||
return Response.success(userLikeGroupService.productImageLikeList(toProductImageDTO));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "relight")
|
||||
@Operation(summary = "relight")
|
||||
@PostMapping("/relight")
|
||||
public Response<List<ToProductImageResult>> relight(@Valid @RequestBody ToProductImageDTO toProductImageDTO) {
|
||||
public Response<List<ToProductImageResultVO>> relight(@Valid @RequestBody ToProductImageDTO toProductImageDTO) {
|
||||
return Response.success(userLikeGroupService.relight(toProductImageDTO));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "获取relight结果")
|
||||
@Operation(summary = "转relight元素")
|
||||
@PostMapping("/convertRelightElement")
|
||||
public Response<ToProductElementVO> convertRelightElement(@RequestParam("id") Long id) {
|
||||
return Response.success(userLikeGroupService.convertRelightElement(id));
|
||||
}
|
||||
|
||||
@Operation(summary = "获取relight结果")
|
||||
@PostMapping("/relightResult")
|
||||
public Response<List<MagicToolResultVO>> getRelightResult(@Valid @RequestBody List<String> taskIdList) {
|
||||
List<MagicToolResultVO> magicToolResultVOList = userLikeGroupService.getRelightResult(taskIdList);
|
||||
return Response.success(magicToolResultVOList);
|
||||
}
|
||||
|
||||
@Operation(summary = "删除toProduct Relight的结果")
|
||||
@GetMapping("/deleteResult")
|
||||
public Response<String> deleteResult(@RequestParam("id") Long id,
|
||||
@RequestParam("projectId") Long projectId,
|
||||
@RequestParam("type") @Pattern(regexp = "^(Relight|ToProductImage)$", message = "Please choose type from 'Relight|ToProductImage'") String type){
|
||||
try{
|
||||
userLikeGroupService.deleteToProductRelightResult(id, projectId, type);
|
||||
return Response.success();
|
||||
}catch (Exception e){
|
||||
return Response.fail(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Operation(summary = "likeHistoryRelSketch")
|
||||
@PostMapping("/likeHistoryRelSketch")
|
||||
public Response<String> likeHistoryRelSketch() {
|
||||
return Response.success(userLikeGroupService.likeHistoryRelSketch());
|
||||
}
|
||||
|
||||
@Operation(summary = "download")
|
||||
@PostMapping("/download")
|
||||
public Response<String> download() {
|
||||
return Response.success(userLikeGroupService.download());
|
||||
}
|
||||
|
||||
@Operation(summary = "productImageInitialize")
|
||||
@PostMapping("/productImageInitialize")
|
||||
public Response<Boolean> productImageUpload(@Valid @RequestBody ProductImageInitializeDTO productImageInitializeDTO) {
|
||||
return Response.success(userLikeGroupService.productImageInitialize(productImageInitializeDTO));
|
||||
}
|
||||
|
||||
@Operation(summary = "getInitializeProgress")
|
||||
@PostMapping("/getInitializeProgress")
|
||||
public Response<InitializeProgressVO> getInitializeProgress(@Valid @RequestBody ProductImageInitializeDTO productImageInitializeDTO) {
|
||||
return Response.success(userLikeGroupService.getInitializeProgress(productImageInitializeDTO));
|
||||
}
|
||||
|
||||
@Operation(summary = "brandDNASaveOrUpdate")
|
||||
@PostMapping("/brandDNASaveOrUpdate")
|
||||
public Response<Boolean> brandDNASaveOrUpdate(@Valid @RequestBody BrandDNADTO brandDNADTO) {
|
||||
return Response.success(userLikeGroupService.brandDNASaveOrUpdate(brandDNADTO));
|
||||
}
|
||||
|
||||
@Operation(summary = "brandDNADelete")
|
||||
@PostMapping("/brandDNADelete")
|
||||
public Response<Boolean> brandDNADelete(@Valid @RequestBody BrandDNADTO brandDNADTO) {
|
||||
return Response.success(userLikeGroupService.brandDNADelete(brandDNADTO));
|
||||
}
|
||||
|
||||
@Operation(summary = "brandDNAPage")
|
||||
@PostMapping("/brandDNAPage")
|
||||
public Response<PageBaseResponse<BrandDNAVO>> brandDNAPage(@Valid @RequestBody BrandDNAQueryDTO brandDNAQueryDTO) {
|
||||
return Response.success(userLikeGroupService.brandDNAPage(brandDNAQueryDTO));
|
||||
}
|
||||
|
||||
@Operation(summary = "brandLogoUpload")
|
||||
@PostMapping("/brandLogoUpload")
|
||||
public Response<BrandLogoUploadVO> brandDNASaveOrUpdate(@RequestParam("file") MultipartFile file) {
|
||||
return Response.success(userLikeGroupService.brandLogoUpload(file));
|
||||
}
|
||||
|
||||
@Operation(summary = "brandDNAUpload")
|
||||
@PostMapping("/brandDNAUpload")
|
||||
public Response<LibraryUpdateVo> brandDNAUpload(@RequestParam("file") MultipartFile file, @RequestParam("brandId") Long brandId) throws IOException {
|
||||
return Response.success(userLikeGroupService.brandDNAUpload(file, brandId));
|
||||
}
|
||||
|
||||
@Operation(summary = "brandDNAGenerate")
|
||||
@PostMapping("/brandDNAGenerate")
|
||||
public Response<BrandDNAGenerateVO> brandDNAGenerate(@RequestParam("prompt") String prompt) {
|
||||
return Response.success(userLikeGroupService.brandDNAGenerate(prompt));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,47 +1,83 @@
|
||||
package com.ai.da.controller;
|
||||
|
||||
import com.ai.da.common.response.Response;
|
||||
import com.ai.da.common.utils.DateUtil;
|
||||
import com.ai.da.common.utils.RedisUtil;
|
||||
import com.ai.da.common.utils.SendEmailUtil;
|
||||
import com.ai.da.mapper.primary.entity.ProductCoupons;
|
||||
import com.ai.da.model.dto.CreateCouponDTO;
|
||||
import com.ai.da.model.dto.ProductPurchaseDTO;
|
||||
import com.ai.da.model.dto.QueryCouponsPageDTO;
|
||||
import com.ai.da.model.vo.CheckCouponsVO;
|
||||
import com.ai.da.service.StripeService;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.paypal.http.HttpResponse;
|
||||
import com.paypal.payments.Refund;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import com.stripe.exception.StripeException;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.Valid;
|
||||
import java.io.IOException;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
@Api(tags = "Stripe模块")
|
||||
@Tag(name = "Stripe模块")
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/api/stripe")
|
||||
//@ApiIgnore
|
||||
public class StripeController {
|
||||
|
||||
@Resource
|
||||
private StripeService stripeService;
|
||||
@Resource
|
||||
private RedisUtil redisUtil;
|
||||
|
||||
@ApiOperation("创建支付链接")
|
||||
@PostMapping("/createOrder/{amount}")
|
||||
public Response<String> pay(@PathVariable Integer amount, @RequestParam String returnUrl) {
|
||||
return Response.success(stripeService.pay(amount, returnUrl));
|
||||
@Operation(summary = "创建支付链接")
|
||||
@PostMapping("/createOrder")
|
||||
public Response<String> pay(@Valid @RequestBody ProductPurchaseDTO productPurchaseDTO, HttpServletRequest request) {
|
||||
return Response.success(stripeService.pay(productPurchaseDTO, request));
|
||||
}
|
||||
|
||||
@ApiOperation("支付通知")
|
||||
@Value("${stripe.webhook.fail.reminder}")
|
||||
private String webhookReminderFlag;
|
||||
|
||||
@Operation(summary = "支付通知")
|
||||
@PostMapping("/trade/notify")
|
||||
public Response<String> callback(HttpServletRequest request) throws ServletException, IOException {
|
||||
Boolean result = stripeService.notify(request);
|
||||
if (result){
|
||||
return Response.success(200,"success");
|
||||
}else {
|
||||
return Response.fail(400,"failure");
|
||||
public void callback(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
||||
try{
|
||||
Boolean result = stripeService.notify(request);
|
||||
if (result){
|
||||
response.setStatus(HttpServletResponse.SC_OK);
|
||||
}else {
|
||||
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}catch (Exception e){
|
||||
log.error("Stripe Controller层异常捕捉, {}", e.getMessage());
|
||||
e.printStackTrace();
|
||||
String key_1 = RedisUtil.STRIPE_EXCEPTION_LOG + DateUtil.dateToStr(new Date(), DateUtil.YYYY_MM_DD_HH);
|
||||
String key_2 = key_1 + ":" + DateUtil.dateToStr(new Date(), DateUtil.YYYY_MM_DD_hh_mm_ss);
|
||||
String stackTrace = stripeService.getStackTrace(e, 10);
|
||||
redisUtil.addToString(key_2, stackTrace);
|
||||
Long size = redisUtil.getSize(key_1);
|
||||
// 给我发送邮件
|
||||
if (webhookReminderFlag.equals("1") && size == 3){
|
||||
SendEmailUtil.commonExceptionReminder("Stripe Webhook 回调处理出现异常", new String[]{"xupei3360@163.com"});
|
||||
}
|
||||
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
@ApiOperation("申请退款")
|
||||
@PostMapping("/trade/refund/{orderNo}/{reason}")
|
||||
@Operation(summary = "申请退款")
|
||||
@GetMapping("/trade/refund/{orderNo}/{reason}")
|
||||
public Response<HttpResponse<Refund>> refund(@PathVariable String orderNo, @PathVariable String reason) throws IOException {
|
||||
String response = stripeService.refund(null,orderNo,reason);
|
||||
if (response.equals("退款成功")){
|
||||
@@ -51,4 +87,105 @@ public class StripeController {
|
||||
}
|
||||
}
|
||||
|
||||
@Operation(summary = "获取订阅")
|
||||
@GetMapping("/getSubscription")
|
||||
public Response<List<String>> getSubscription(@RequestParam String name, @RequestParam String email) {
|
||||
try {
|
||||
return Response.success(stripeService.getSubscriptionIds(name, email));
|
||||
} catch (StripeException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Operation(summary = "取消订阅")
|
||||
@GetMapping("/cancelSubscription")
|
||||
public Response<String> cancelSubscription(@RequestParam String subscriptionId, @RequestParam(required = false) String reason) {
|
||||
stripeService.cancelSubscription(subscriptionId, reason);
|
||||
return Response.success("success");
|
||||
}
|
||||
|
||||
@Operation(summary = "创建推广码")
|
||||
@PostMapping("/createCoupon")
|
||||
public Response<String> createCoupon(@Valid @RequestBody CreateCouponDTO createCouponDTO){
|
||||
return Response.success(stripeService.createCoupon(createCouponDTO));
|
||||
}
|
||||
|
||||
@Operation(summary = "检查推广码")
|
||||
@GetMapping("/checkCoupon")
|
||||
public Response<CheckCouponsVO> checkCoupon(@RequestParam String promotionCode, @RequestParam Long price){
|
||||
return Response.success(stripeService.checkProductCoupon(promotionCode, price));
|
||||
}
|
||||
|
||||
@Operation(summary = "获取所有推广码")
|
||||
@PostMapping("/getAllCoupons")
|
||||
public Response<IPage<ProductCoupons>> getAllCoupons(@RequestBody QueryCouponsPageDTO queryCouponsPageDTO){
|
||||
return Response.success(stripeService.getAllCoupons(queryCouponsPageDTO));
|
||||
}
|
||||
|
||||
@Operation(summary = "检索优惠券")
|
||||
@GetMapping("/retrieveCoupon")
|
||||
public Response<String> retrieveCoupon(@RequestParam String couponId){
|
||||
return Response.success(stripeService.retrieveCoupon(couponId));
|
||||
}
|
||||
|
||||
@Operation(summary = "检索推广码")
|
||||
@GetMapping("/retrievePromotionCode")
|
||||
public Response<String> retrievePromotionCode(@RequestParam String retrievePromotionCode){
|
||||
return Response.success(stripeService.retrievePromotionCode(retrievePromotionCode));
|
||||
}
|
||||
|
||||
@Operation(summary = "更新推广码信息")
|
||||
@GetMapping("/updatePromCodeInfo")
|
||||
public Response<ProductCoupons> updateCouponsInfo(@RequestParam Long id, @RequestParam(required = false) String paidCommission,
|
||||
@RequestParam(required = false) String cooperator,
|
||||
@RequestParam(required = false) String remark,
|
||||
@RequestParam(required = false) Long startTime){
|
||||
return Response.success(stripeService.updateCouponsInfo(id, paidCommission, cooperator, remark, startTime));
|
||||
}
|
||||
|
||||
@Operation(summary = "删除推广码")
|
||||
@GetMapping("/deletePromCode")
|
||||
public Response<String> deleteCoupon(@RequestParam Long id){
|
||||
stripeService.deleteCoupon(id);
|
||||
return Response.success("success");
|
||||
}
|
||||
|
||||
/*@ApiOperation("临时 取消订阅")
|
||||
@GetMapping("/cancelSubscriptionTemp")
|
||||
public Response<String> cancelSubscriptionTemp(@RequestParam String subscriptionId) {
|
||||
stripeService.cancelSubscriptionTemp(subscriptionId);
|
||||
return Response.success("success");
|
||||
}
|
||||
|
||||
@Operation(summary = "创建订阅 临时")
|
||||
@GetMapping("/createSubscriptionTemp")
|
||||
public Response<String> createSubscriptionTemp(@RequestParam String name, @RequestParam String email) {
|
||||
return Response.success(stripeService.createSubscriptionTemp(name, email));
|
||||
}
|
||||
|
||||
@Operation(summary = "修改用户默认支付方式 临时")
|
||||
@GetMapping("/changeCustomerPayment")
|
||||
public Response<String> changeCustomerPayment(@RequestParam String name, @RequestParam String email) {
|
||||
return Response.success(stripeService.changeCustomerPayment(name, email));
|
||||
}
|
||||
|
||||
@Operation(summary = "临时 发送续订失败邮件")
|
||||
@GetMapping("/sendRenewalFailEmail")
|
||||
public Response<Boolean> sendRenewalFailEmail(@RequestParam String invoiceId, @RequestParam String subscriptionId, @RequestParam String orderNo) {
|
||||
return Response.success(stripeService.sendRenewalFailEmail(invoiceId, subscriptionId,orderNo));
|
||||
}
|
||||
|
||||
@Operation(summary = "临时 查询指定用户绑定的付款方式")
|
||||
@GetMapping("/getCustomerPaymentMethod")
|
||||
public Response<List<Map<String,String>>> getCustomerPaymentMethod(@RequestParam String name, @RequestParam String email) {
|
||||
return Response.success(stripeService.getCustomerPaymentMethod(name, email));
|
||||
}
|
||||
|
||||
@Operation(summary = "临时 解绑指定用户绑定的所有付款方式")
|
||||
@GetMapping("/detachCustomerAllPaymentMethod")
|
||||
public Response<String> detachCustomerAllPaymentMethod(@RequestParam String name, @RequestParam String email) {
|
||||
return Response.success(stripeService.detachCustomerAllPaymentMethod(name, email));
|
||||
}*/
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
package com.ai.da.controller;
|
||||
|
||||
import com.ai.da.common.response.Response;
|
||||
import com.ai.da.common.task.SubscriptionReminderTask;
|
||||
import com.ai.da.mapper.primary.entity.SubscriptionPlan;
|
||||
import com.ai.da.model.dto.SubscriptionPlanDTO;
|
||||
import com.ai.da.model.dto.SubscriptionPlanPageQuery;
|
||||
import com.ai.da.model.dto.UpdateSubscriptionPlanDTO;
|
||||
import com.ai.da.model.vo.SubscriptionPlanVO;
|
||||
import com.ai.da.service.SubscriptionPlanService;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import io.swagger.v3.oas.annotations.Hidden;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Tag(name = "订阅计划模块")
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/api/subscription_plan")
|
||||
public class SubscriptionPlanController {
|
||||
|
||||
private final SubscriptionPlanService subscriptionPlanService;
|
||||
|
||||
private final SubscriptionReminderTask subscriptionReminderTask;
|
||||
|
||||
@Operation(summary = "创建订阅计划")
|
||||
@PostMapping("/createPlan")
|
||||
public Response<String> createPlan(@Valid @RequestBody SubscriptionPlanDTO subscriptionPlanDTO) {
|
||||
subscriptionPlanService.createPlan(subscriptionPlanDTO);
|
||||
return Response.success();
|
||||
}
|
||||
|
||||
@Operation(summary = "更新订阅计划")
|
||||
@PostMapping("/updatePlan")
|
||||
public Response<String> updatePlan(@Valid @RequestBody UpdateSubscriptionPlanDTO updateDTO) {
|
||||
subscriptionPlanService.updatePlan(updateDTO);
|
||||
return Response.success();
|
||||
}
|
||||
|
||||
@Operation(summary = "搜索订阅计划")
|
||||
@PostMapping("/searchByOrganizationIdAndStatus")
|
||||
public Response<List<SubscriptionPlan>> searchByOrganizationIdAndStatus(@Valid @RequestBody SubscriptionPlanPageQuery subscriptionPlanPageQuery) {
|
||||
return Response.success(subscriptionPlanService.searchByOrganizationIdAndStatus(subscriptionPlanPageQuery));
|
||||
}
|
||||
|
||||
@Operation(summary = "分页搜索订阅计划")
|
||||
@PostMapping("/searchByPage")
|
||||
public Response<IPage<SubscriptionPlanVO>> searchByPage(@Valid @RequestBody SubscriptionPlanPageQuery subscriptionPlanPageQuery) {
|
||||
IPage<SubscriptionPlanVO> subscriptionPlanVOIPage = subscriptionPlanService.searchByPage(subscriptionPlanPageQuery);
|
||||
return Response.success(subscriptionPlanVOIPage);
|
||||
}
|
||||
|
||||
@Operation(summary = "删除订阅计划")
|
||||
@GetMapping("/deletePlan")
|
||||
public Response<String> deletePlan(@RequestParam Long id) {
|
||||
subscriptionPlanService.deletePlan(id);
|
||||
return Response.success();
|
||||
}
|
||||
|
||||
@Operation(summary = "管理员切换订阅计划")
|
||||
@GetMapping("/switchSubscriptionPlan")
|
||||
public Response<String> switchSubscriptionPlan(@RequestParam Long targetSubscriptionPlanId, @RequestParam(required = false) Long adminAccId) {
|
||||
subscriptionPlanService.switchSubscriptionPlan(targetSubscriptionPlanId, adminAccId);
|
||||
return Response.success();
|
||||
}
|
||||
|
||||
@Operation(summary = "子账号切换订阅计划")
|
||||
@GetMapping("/switchSubAccSubscriptionPlan")
|
||||
public Response<String> switchSubAccSubscriptionPlan(@RequestParam Long targetSubscriptionPlanId, @RequestParam Long subAccId) {
|
||||
subscriptionPlanService.switchSubAccSubscriptionPlan(targetSubscriptionPlanId, subAccId);
|
||||
return Response.success();
|
||||
}
|
||||
|
||||
// @Hidden
|
||||
@Operation(summary = "activeSubscriptionPlan")
|
||||
@GetMapping("/activeSubscriptionPlan")
|
||||
public Response<String> activeSubscriptionPlan() {
|
||||
subscriptionPlanService.activeSubscriptionPlan(null);
|
||||
return Response.success();
|
||||
}
|
||||
|
||||
// @Hidden
|
||||
@Operation(summary = "expireSubscription")
|
||||
@GetMapping("/expireSubscription")
|
||||
public Response<String> expireSubscription() {
|
||||
subscriptionPlanService.expireSubscription();
|
||||
return Response.success();
|
||||
}
|
||||
|
||||
@Operation(summary = "subscriptionReminder")
|
||||
@GetMapping("/subscriptionReminder")
|
||||
public Response<String> subscriptionReminder() {
|
||||
subscriptionReminderTask.subscriptionReminder();
|
||||
return Response.success();
|
||||
}
|
||||
|
||||
@Operation(summary = "trialReminder")
|
||||
@GetMapping("/trialReminder")
|
||||
public Response<String> trialReminder() {
|
||||
subscriptionReminderTask.trialReminder();
|
||||
return Response.success();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user