Compare commits
577 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
ed44b84d31 | |
|
|
0a9205ddee | |
|
|
3dd5f2efa5 | |
|
|
f285a948d6 | |
|
|
f20310775d | |
|
|
75de76d1c2 | |
|
|
84a9a09d10 | |
|
|
685067b25d | |
|
|
4ed2f9b1bb | |
|
|
470c256c2d | |
|
|
7066636c2f | |
|
|
7c530de03e | |
|
|
4ca489334a | |
|
|
e15b9ef6c3 | |
|
|
99085ec821 | |
|
|
b421a943a7 | |
|
|
1c52f0adc6 | |
|
|
4b17fbd9bf | |
|
|
085d3ddb73 | |
|
|
97a1747424 | |
|
|
0a94a58afa | |
|
|
ca021f71a9 | |
|
|
21163dc79a | |
|
|
9e39917583 | |
|
|
2cef642b4d | |
|
|
355340e1c5 | |
|
|
4a65fa66c0 | |
|
|
c873859c1a | |
|
|
90150e36e0 | |
|
|
5be1f63f04 | |
|
|
e149fd412c | |
|
|
34a947d5a0 | |
|
|
6bfd3fe7fd | |
|
|
0fe1508d9e | |
|
|
40b5563852 | |
|
|
ef67a3c666 | |
|
|
3c5de0b144 | |
|
|
913de4afe0 | |
|
|
ad380958a0 | |
|
|
9ff1a88233 | |
|
|
d5278f4594 | |
|
|
b4e56b8303 | |
|
|
e7a362519b | |
|
|
018247a7d8 | |
|
|
2f6fff3644 | |
|
|
b710bd8793 | |
|
|
2eefff46e2 | |
|
|
a51f5d7834 | |
|
|
f7247315cf | |
|
|
0a79405533 | |
|
|
9959fb0977 | |
|
|
7b8ae707bf | |
|
|
a0728d9d23 | |
|
|
6fac29bc9b | |
|
|
7cfaee0413 | |
|
|
2067410617 | |
|
|
6c85629a37 | |
|
|
db551ab887 | |
|
|
388ff8bac4 | |
|
|
4f625560e3 | |
|
|
b33db300be | |
|
|
7e383f6e51 | |
|
|
cb6a810977 | |
|
|
f18d07b003 | |
|
|
39a6d78f2b | |
|
|
890f723716 | |
|
|
f7cb6049df | |
|
|
2c4adc9da3 | |
|
|
72a6da5ce4 | |
|
|
1b960df589 | |
|
|
4107f7f1ba | |
|
|
9cab8c7aa4 | |
|
|
6f96e22f5f | |
|
|
760549de2a | |
|
|
144341501d | |
|
|
e9c1c63a69 | |
|
|
cb61887221 | |
|
|
d57225b9a9 | |
|
|
ee77d285fa | |
|
|
f9d5511a91 | |
|
|
819f92484d | |
|
|
2b02cf7bf6 | |
|
|
8c5396729e | |
|
|
b7c52ce699 | |
|
|
31fb6e77da | |
|
|
d091564610 | |
|
|
3e9bb2b762 | |
|
|
e627fdd6a3 | |
|
|
4ca7aec637 | |
|
|
5e5c13d9dc | |
|
|
854e5a9e7e | |
|
|
adbdc0e4bd | |
|
|
62cd85166d | |
|
|
e1fc391984 | |
|
|
8f3ca4b199 | |
|
|
01b599cc0c | |
|
|
3a32575bab | |
|
|
5c2253cf92 | |
|
|
abc720f88b | |
|
|
b743aca6fa | |
|
|
748ca3052b | |
|
|
09a8ef9e39 | |
|
|
17f3a0216f | |
|
|
e5e73e70b5 | |
|
|
1dc83a8c23 | |
|
|
89e0d8d598 | |
|
|
be358c9984 | |
|
|
881600758d | |
|
|
87820f6836 | |
|
|
792ce23435 | |
|
|
cb1135e9c8 | |
|
|
ba8d349273 | |
|
|
cfac0a3be9 | |
|
|
387703f761 | |
|
|
8b85ab5295 | |
|
|
2fc97caa7c | |
|
|
41e0411566 | |
|
|
4099f7eca0 | |
|
|
5e43a7d6d2 | |
|
|
ca8f9955cb | |
|
|
6329e6b37f | |
|
|
dc152617f3 | |
|
|
8adc2b176b | |
|
|
d72757e738 | |
|
|
c68c996892 | |
|
|
6b2622b1a3 | |
|
|
f965bf8b7f | |
|
|
af64c0eee7 | |
|
|
015c62ed60 | |
|
|
21bb6f53eb | |
|
|
32c3b6c7a8 | |
|
|
4e479ffaa8 | |
|
|
bd18369dca | |
|
|
92b319f6c3 | |
|
|
1d8862ab7b | |
|
|
8996df0279 | |
|
|
1b9a4fdd80 | |
|
|
48d03c16ce | |
|
|
540d873eac | |
|
|
4847e4eced | |
|
|
b366cadb9d | |
|
|
d348f247fe | |
|
|
343df76e71 | |
|
|
d3be7d733b | |
|
|
644075a199 | |
|
|
d7a1f7ce86 | |
|
|
3aa1ad9520 | |
|
|
153dd55b4d | |
|
|
769af27bf9 | |
|
|
ac00969597 | |
|
|
87222148ec | |
|
|
f418a277c3 | |
|
|
68b95dc87c | |
|
|
d094dbd5ff | |
|
|
4639e6a347 | |
|
|
07b62a9725 | |
|
|
3410510d81 | |
|
|
c18f25d55e | |
|
|
5ff8c8a0e6 | |
|
|
5ce7dfbb88 | |
|
|
7ee72814bd | |
|
|
8dc9431614 | |
|
|
a1271d1fc8 | |
|
|
0bd9444026 | |
|
|
6feb2d479b | |
|
|
585fe2fb97 | |
|
|
16fe9ff561 | |
|
|
bd72f853a5 | |
|
|
03828585b1 | |
|
|
7cb8ee4202 | |
|
|
9325bde2a3 | |
|
|
11ff1c8540 | |
|
|
c222b06fab | |
|
|
b70eda503b | |
|
|
994e47b4d0 | |
|
|
3362eec301 | |
|
|
15de8ab277 | |
|
|
7a02c6047c | |
|
|
69c4171a20 | |
|
|
0df46d71ba | |
|
|
e440b477f4 | |
|
|
5eebfb7b91 | |
|
|
70c2f65dbf | |
|
|
d11ff47f0c | |
|
|
c556912b1b | |
|
|
c89fb9f27a | |
|
|
607fe3a146 | |
|
|
41df5bff23 | |
|
|
451169b22d | |
|
|
73a0321d99 | |
|
|
216500e4d1 | |
|
|
f4cecc138b | |
|
|
5924ee0c11 | |
|
|
e3085019f4 | |
|
|
28968842ed | |
|
|
84451f8aaa | |
|
|
a7e9fc653b | |
|
|
2d34354ef2 | |
|
|
08a35b4070 | |
|
|
2e7f368e06 | |
|
|
a8621cf93d | |
|
|
832827bee2 | |
|
|
91f7db4caa | |
|
|
b523b035ad | |
|
|
3b6c9923af | |
|
|
b0a1fdaef7 | |
|
|
3120c34de5 | |
|
|
9c62904de5 | |
|
|
1a70fa9375 | |
|
|
0add233ebe | |
|
|
fb4b094aed | |
|
|
6b9e51ffed | |
|
|
a4e4dbda83 | |
|
|
8dfe852318 | |
|
|
729a23e410 | |
|
|
0c01053615 | |
|
|
ae34f4153a | |
|
|
83295e7443 | |
|
|
77e2b82aa9 | |
|
|
664dfa5159 | |
|
|
9cfd31b1e7 | |
|
|
2fa0d7f62e | |
|
|
f85cf646f9 | |
|
|
da92e535bb | |
|
|
bcd58ef49d | |
|
|
fb8330888b | |
|
|
c209c645f7 | |
|
|
fb81a548a5 | |
|
|
517559eed1 | |
|
|
21743caae9 | |
|
|
b20fa8ad35 | |
|
|
a5e48469f1 | |
|
|
465192e6fd | |
|
|
295923cccc | |
|
|
bc4c6451e5 | |
|
|
ffc56c74de | |
|
|
5bc5bf77dd | |
|
|
19a3ecc0df | |
|
|
be5fcdf414 | |
|
|
23b8bda2a0 | |
|
|
a6123f5b1c | |
|
|
96c60af6a3 | |
|
|
86ae352bcc | |
|
|
46e6e8f450 | |
|
|
ff2d245133 | |
|
|
bbf0c0f9e7 | |
|
|
48b5d897d6 | |
|
|
ba25cc46ac | |
|
|
4cac90f2da | |
|
|
0bea08e91e | |
|
|
3e9376ec56 | |
|
|
bb4b857d9c | |
|
|
647fb437f2 | |
|
|
22626fcc05 | |
|
|
fa4449deb0 | |
|
|
10852fac3b | |
|
|
6dcb6f1b38 | |
|
|
ba06b05e8f | |
|
|
51717f223c | |
|
|
c5c045a874 | |
|
|
edee30f156 | |
|
|
3ccef3d345 | |
|
|
05d9d6ff6b | |
|
|
87c22cad47 | |
|
|
5243d6b063 | |
|
|
c6e72948e0 | |
|
|
9bacaf120e | |
|
|
258344421d | |
|
|
0d4a5e3528 | |
|
|
b2f6021d3d | |
|
|
50bb64d6e6 | |
|
|
1db4523885 | |
|
|
29078f7003 | |
|
|
8c9cd3b345 | |
|
|
e9b5ea3e7d | |
|
|
f278be1cb7 | |
|
|
a498b53ce4 | |
|
|
06c43184e1 | |
|
|
6113645400 | |
|
|
5096983c56 | |
|
|
d90f22a424 | |
|
|
5b85eb954b | |
|
|
0ee4a5781b | |
|
|
8f4d6f8035 | |
|
|
eba81c7ee0 | |
|
|
35eb9f28f8 | |
|
|
35d3ef1501 | |
|
|
f7f4287034 | |
|
|
d119955f1d | |
|
|
dfc8341119 | |
|
|
d9edfb0b71 | |
|
|
7fb061c9a3 | |
|
|
3f9f83dd94 | |
|
|
5b2b5d0ba0 | |
|
|
2af26fb728 | |
|
|
721808e543 | |
|
|
3ac5a26048 | |
|
|
a17ead31f1 | |
|
|
33d456b5d3 | |
|
|
1a07ff016d | |
|
|
6c735d38f2 | |
|
|
1cddb6ae1e | |
|
|
6e368dcc15 | |
|
|
3bec99c982 | |
|
|
f662f3a480 | |
|
|
879c7b09df | |
|
|
d47f785669 | |
|
|
9d3cea8669 | |
|
|
01320b9e04 | |
|
|
a4390dbb8b | |
|
|
86b8fb30d8 | |
|
|
41c1be4763 | |
|
|
6183b7cf4d | |
|
|
7abaf972a4 | |
|
|
4f71228ff1 | |
|
|
2cdb04c522 | |
|
|
b58a3f20a4 | |
|
|
a91114d8ae | |
|
|
bab72020ad | |
|
|
45233650d7 | |
|
|
6dd9cfbbee | |
|
|
1a8343cda8 | |
|
|
9d0e5e6f76 | |
|
|
4d07148789 | |
|
|
73a23aef4a | |
|
|
ab031b6048 | |
|
|
526634aee3 | |
|
|
90d03a07bc | |
|
|
609fe56f75 | |
|
|
aa2d5a5069 | |
|
|
a1750d6959 | |
|
|
a8dae49f45 | |
|
|
27aff98a72 | |
|
|
856d0058cb | |
|
|
e0e9cc0102 | |
|
|
07e48bb62b | |
|
|
31c2da9027 | |
|
|
345f7c6da4 | |
|
|
e059a5f64a | |
|
|
413b816a10 | |
|
|
71eb344f0c | |
|
|
37143f17fa | |
|
|
2c131e30c8 | |
|
|
70dbaf72c0 | |
|
|
240cd23fb6 | |
|
|
17e6902994 | |
|
|
e511aae2d4 | |
|
|
f469b36833 | |
|
|
3eb170f4bd | |
|
|
d1b0380c4d | |
|
|
bedabb1d17 | |
|
|
bbf8875e30 | |
|
|
5a438ce485 | |
|
|
7569500d48 | |
|
|
6bccfe3d82 | |
|
|
fa0bd7bc62 | |
|
|
e92f4f4fff | |
|
|
cff1310b2e | |
|
|
090663c725 | |
|
|
44ca97b7d0 | |
|
|
ec15531eac | |
|
|
c00d947678 | |
|
|
964f3cd5cf | |
|
|
508a838f2f | |
|
|
17afd86f85 | |
|
|
1e45fdb222 | |
|
|
17bf37004f | |
|
|
8a389f04b0 | |
|
|
fbe1d8712e | |
|
|
5494d7079e | |
|
|
78d3015aa4 | |
|
|
472e684449 | |
|
|
81cc61348a | |
|
|
d856e002e3 | |
|
|
15ca495271 | |
|
|
0edea515c8 | |
|
|
457381a85c | |
|
|
4f9caac1b0 | |
|
|
ccd3157649 | |
|
|
3eaffae096 | |
|
|
33a9f1516b | |
|
|
8a64a2b32e | |
|
|
0700044a0e | |
|
|
40cbe4e8ba | |
|
|
1bcd5c768d | |
|
|
06f5dd2d5f | |
|
|
c2b2d52195 | |
|
|
d21b849bba | |
|
|
bd83a242c0 | |
|
|
194b508f55 | |
|
|
1cb90ef6f3 | |
|
|
580af7f577 | |
|
|
8ec8f2a980 | |
|
|
95dbc27dcc | |
|
|
572f8db83b | |
|
|
418ab72562 | |
|
|
eb10f82c4c | |
|
|
87e3b7a4ca | |
|
|
fbd8d0c413 | |
|
|
1fecc06a17 | |
|
|
17ba4bc361 | |
|
|
323691d061 | |
|
|
70b3b82dcf | |
|
|
0afa6e0c68 | |
|
|
084b6f5874 | |
|
|
f0910f9ca8 | |
|
|
77ed8f70cf | |
|
|
d5ffbbd7d6 | |
|
|
4fadebfea0 | |
|
|
03216c1815 | |
|
|
3b672e7e05 | |
|
|
091e0ef0ce | |
|
|
82b3ae0731 | |
|
|
e5291d07f4 | |
|
|
564a5b6d82 | |
|
|
2e648c09c3 | |
|
|
b29c603199 | |
|
|
0ed61a0f2c | |
|
|
bd377b5b81 | |
|
|
107ed2cae0 | |
|
|
7b927290bb | |
|
|
e3f1959582 | |
|
|
0d1870f5c8 | |
|
|
a8ccd564c7 | |
|
|
2ca8e988e7 | |
|
|
6e2fcf5ca1 | |
|
|
16d16890ce | |
|
|
57ca0180cb | |
|
|
3c9f5c8792 | |
|
|
1588b9b7bd | |
|
|
901ae51019 | |
|
|
f88f4d84b9 | |
|
|
3ad7757cf2 | |
|
|
c3c8389737 | |
|
|
0597083258 | |
|
|
8d294ef297 | |
|
|
514e7bb724 | |
|
|
3e8e4d111d | |
|
|
6624a5193f | |
|
|
0de4d12b85 | |
|
|
e9c99d7542 | |
|
|
866459211a | |
|
|
05b23b3cde | |
|
|
084635fa6a | |
|
|
072c8c9fee | |
|
|
98c3374bfb | |
|
|
9e101492b2 | |
|
|
e8967efc9d | |
|
|
c9bd4612f2 | |
|
|
c8a6b557a9 | |
|
|
51f79d2cd4 | |
|
|
77a9782136 | |
|
|
8e964dd8a8 | |
|
|
6a15c47799 | |
|
|
11f07197c0 | |
|
|
c668d614a3 | |
|
|
f6a8cc5cb7 | |
|
|
587c3fe2c9 | |
|
|
3925ba5bb0 | |
|
|
dc0ab16dca | |
|
|
61df9c223c | |
|
|
d3587c2c72 | |
|
|
190d9fc065 | |
|
|
5928e407fe | |
|
|
c20daf71fa | |
|
|
9e895e7114 | |
|
|
4350b11ec6 | |
|
|
97fcb32db9 | |
|
|
982e1c3877 | |
|
|
4940e90a1e | |
|
|
332416bc8f | |
|
|
02aeb7db1e | |
|
|
e27872a414 | |
|
|
ff51cb7c8e | |
|
|
daabf7c9fe | |
|
|
a603f7d955 | |
|
|
3e876ed49a | |
|
|
f6e758777f | |
|
|
41aa1d379e | |
|
|
efd8dd03ec | |
|
|
288d5e7005 | |
|
|
14b2491d07 | |
|
|
6e71d8ae44 | |
|
|
6c1dde9834 | |
|
|
90a398800b | |
|
|
5dfd3c39c3 | |
|
|
8c2aff8d35 | |
|
|
a0f6e86754 | |
|
|
b228b83f5b | |
|
|
f5de5b58fd | |
|
|
1ae27c1c30 | |
|
|
53a323cae4 | |
|
|
5256a965d7 | |
|
|
22ef786d2e | |
|
|
a6bbb6819a | |
|
|
527544f989 | |
|
|
d8c8ec8f6e | |
|
|
874abf205f | |
|
|
fcea60ddee | |
|
|
9b607a61ed | |
|
|
5dbd4762b4 | |
|
|
d6a70119ce | |
|
|
d61cd68faa | |
|
|
8ebf0127de | |
|
|
d382acfd2d | |
|
|
549f24da52 | |
|
|
27743f103a | |
|
|
803a8706b1 | |
|
|
09bf8b90fc | |
|
|
e8e894b8ec | |
|
|
da2fe44408 | |
|
|
83a23f7431 | |
|
|
1ee1fd6e5b | |
|
|
9af1f23476 | |
|
|
f2ec06e062 | |
|
|
0c1243419e | |
|
|
50c6f1f995 | |
|
|
a3852ede6e | |
|
|
5e584c1353 | |
|
|
83f007b423 | |
|
|
df9d7d0b59 | |
|
|
8d421a8adc | |
|
|
73ce021b25 | |
|
|
159fece2dd | |
|
|
1800084933 | |
|
|
f04ad17edb | |
|
|
9d22d81cce | |
|
|
5abc4151ea | |
|
|
84927b7d30 | |
|
|
f51c470d66 | |
|
|
357bab2626 | |
|
|
87acc327b8 | |
|
|
5d639570a2 | |
|
|
9851c1f61b | |
|
|
9b8823cc29 | |
|
|
26f3d17ec6 | |
|
|
6fbd79f3a1 | |
|
|
e9e86d3fc5 | |
|
|
d551c83294 | |
|
|
1ee31eea6f | |
|
|
f362b6b573 | |
|
|
d5267dede1 | |
|
|
f7def09458 | |
|
|
6ac3b3a4cd | |
|
|
056004a0b2 | |
|
|
110fdd5221 | |
|
|
d34f77853f | |
|
|
eecda8daab | |
|
|
a6c258fd29 | |
|
|
6b65f24fa5 | |
|
|
b4f92d78e6 | |
|
|
e18a7ec246 | |
|
|
fcb3b028e0 | |
|
|
d6bfb1ff1d | |
|
|
2f8750b1b1 | |
|
|
f7136f5827 | |
|
|
0ce8934364 | |
|
|
36e6589c49 | |
|
|
7808865b32 | |
|
|
effe78a62a | |
|
|
e96e3dc777 | |
|
|
ce81f41693 | |
|
|
ec182fabad | |
|
|
dd27b4293c | |
|
|
f840cbf0ae | |
|
|
064d9b10b7 | |
|
|
84f53cc4fc | |
|
|
7c764e53b7 | |
|
|
ad6bb8b928 | |
|
|
7b8c29f0f8 | |
|
|
0e55f5c468 | |
|
|
67215ca534 | |
|
|
1d61bd84f4 | |
|
|
5d875ea1b0 | |
|
|
822ab544fe | |
|
|
e6f3d59205 | |
|
|
b62bf1c793 |
|
|
@ -25,8 +25,11 @@ Thumbs.db
|
|||
bin/
|
||||
cert/
|
||||
log
|
||||
configs_prd
|
||||
configs/config_pro.yaml
|
||||
configs/config_pre.yaml
|
||||
configs/config_pre2.yaml
|
||||
configs/config_mock.yaml
|
||||
api/**/*.go
|
||||
/cmd/server/wire_gen.go
|
||||
cmd/server/wire_gen.go
|
||||
/internal/conf/*.go
|
||||
/third_party/swagger_ui/openapi.yaml
|
||||
15
Dockerfile
15
Dockerfile
|
|
@ -1,20 +1,11 @@
|
|||
FROM registry.cn-chengdu.aliyuncs.com/lansexiongdi/build:1.22.2 AS builder
|
||||
|
||||
ENV GOCACHE /root/.cache/go-build
|
||||
FROM registry.cn-chengdu.aliyuncs.com/pkgtool/build:1.23.6 AS builder
|
||||
|
||||
COPY . /src
|
||||
WORKDIR /src
|
||||
|
||||
RUN make build
|
||||
|
||||
RUN make build
|
||||
|
||||
FROM registry.cn-chengdu.aliyuncs.com/lansexiongdi/work:v1
|
||||
|
||||
RUN echo 'http://mirrors.ustc.edu.cn/alpine/v3.5/main' > /etc/apk/repositories \
|
||||
&& echo 'http://mirrors.ustc.edu.cn/alpine/v3.5/community' >>/etc/apk/repositories \
|
||||
&& apk update && apk add tzdata \
|
||||
&& ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
|
||||
&& echo "Asia/Shanghai" > /etc/timezone
|
||||
FROM registry.cn-chengdu.aliyuncs.com/pkgtool/work:v0.0.1
|
||||
|
||||
COPY --from=builder /src/bin /app
|
||||
#删除config文件,线上是通过挂载进来的
|
||||
|
|
|
|||
7
Makefile
7
Makefile
|
|
@ -27,7 +27,7 @@ init:
|
|||
go install github.com/envoyproxy/protoc-gen-validate@v1.0.2
|
||||
|
||||
.PHONY: config
|
||||
# generate internal proto
|
||||
# generate cpn proto
|
||||
config:
|
||||
protoc --proto_path=./internal \
|
||||
--proto_path=./third_party \
|
||||
|
|
@ -76,6 +76,11 @@ all:
|
|||
make generate;
|
||||
make wire;
|
||||
|
||||
.PHONY: test
|
||||
# test
|
||||
test:
|
||||
sh ./stress_test.sh $(t)
|
||||
|
||||
.DEFAULT_GOAL := help
|
||||
# show help
|
||||
help:
|
||||
|
|
|
|||
60
READEME.md
60
READEME.md
|
|
@ -1,59 +1,5 @@
|
|||
# <p align="center">营销系统后台API</p>
|
||||
|
||||
### 参与开发
|
||||
[请参阅](https://tvd8jq9lqkp.feishu.cn/wiki/LNWVweZ64iY2UBkkTkZcezy0n5h?from=from_copylink)
|
||||
# <p align="center">招行立减金券系统</p>
|
||||
* * *
|
||||
### 主要工作
|
||||
+ 后台接口API
|
||||
* * *
|
||||
### 规则说明
|
||||
+ 路由前缀都为 __/admin__ 开始,路由规则全小写+下划线,例如:/admin/v1/demo_1
|
||||
* * *
|
||||
### 构建部署
|
||||
+ 采用多阶段构建,以获得最小体积的容器镜像
|
||||
````bash
|
||||
cd /项目根目录 && make deploy folder=./configs_dev marketing=marketing_backend container_name=marketing_backend http_port=8090
|
||||
````
|
||||
* * *
|
||||
### docker环境下开发
|
||||
+ 一、[下载Docker Desktop安装程序](https://www.docker.com/products/docker-desktop)
|
||||
+ 二、在项目根目录下执行命令
|
||||
```shell
|
||||
docker build -f Dockerfile_win -t 镜像名称 .
|
||||
docker run --privileged -itd --name 容器名称 --restart=always -v ./:/src 镜像名称
|
||||
docker ps
|
||||
docker exec -it 容器名称 sh
|
||||
make init
|
||||
make all
|
||||
```
|
||||
|
||||
### windows非docker开发
|
||||
1 安装插件(配置goproxy,GOPROXY=https://goproxy.cn,direct)
|
||||
```shell
|
||||
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
|
||||
go install github.com/go-kratos/kratos/cmd/kratos/v2@latest
|
||||
go install github.com/go-kratos/kratos/cmd/protoc-gen-go-http/v2@latest
|
||||
go install github.com/google/gnostic/cmd/protoc-gen-openapi@latest
|
||||
go install github.com/google/wire/cmd/wire@latest
|
||||
go install github.com/go-kratos/kratos/cmd/protoc-gen-go-errors/v2@latest
|
||||
go install gorm.io/gen/tools/gentool@latest
|
||||
```
|
||||
2生成相应rpo
|
||||
|
||||
命令:kratos proto client api/helloworld/v1/demo.proto
|
||||
|
||||
位置:api和internal下面的conf
|
||||
|
||||
3 wire生成依赖
|
||||
cd cmd/server
|
||||
wire
|
||||
|
||||
4 配置编译
|
||||
|
||||

|
||||
|
||||
5 生成service
|
||||
kratos proto server api/helloworld/v1/demo.proto -t internal/service
|
||||
|
||||
|
||||
|
||||
+ 发券API
|
||||
* * *
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
syntax = "proto3";
|
||||
package api.err;
|
||||
|
||||
import "errors/errors.proto";
|
||||
option go_package = "voucher/api/err";
|
||||
|
||||
enum CmbErr{
|
||||
option (errors.default_code) = 499;
|
||||
// 未知错误,请联系平台处理
|
||||
CMB_UNKNOWN = 0;
|
||||
// 参数错误, 请检查公共请求参数是否正确
|
||||
CMB_PARAM_FAIL = 1 [(errors.code) = 400];
|
||||
// 验签失败,请检查加签密钥是否正确
|
||||
CMB_VERIFY_FAIL = 2 [(errors.code) = 401];
|
||||
// 业务数据解密失败 请检查加密密钥是否正确
|
||||
CMB_BIZ_CONTENT_DECRYPT_FAIL = 3 [(errors.code) = 401];
|
||||
// 业务数据转换失败, 请检查业务数据格式,字段类型不一致等
|
||||
CMB_BIZ_CONTENT_CONVERT_FAIL = 4 [(errors.code) = 401];
|
||||
// 业务参数传递有误,请检查业务参数是否正确传递
|
||||
CMB_BIZ_CONTENT_FAIL = 5 [(errors.code) = 401];
|
||||
|
||||
// 订单不存在
|
||||
CMB_ORDER_NOT_EXIST = 6 [(errors.code) = 404];
|
||||
// 券商品不存在
|
||||
CMB_PRODUCT_NOT_EXIST = 7 [(errors.code) = 404];
|
||||
// 券商品没有去权限
|
||||
CMB_PRODUCT_NOT_AUTH = 8 [(errors.code) = 401];
|
||||
// 券商品不支持
|
||||
CMB_PRODUCT_NOT_SUPPORTED = 9 [(errors.code) = 401];
|
||||
}
|
||||
|
|
@ -11,15 +11,12 @@ enum Err {
|
|||
// 系统panic错误
|
||||
SYSTEM_PANIC = 0 [(errors.code) = 599];
|
||||
|
||||
// 没权限
|
||||
NOT_LOGIN = 1 [(errors.code) = 401];
|
||||
|
||||
// DB数据未找到
|
||||
DB_NOT_FOUND = 2 [(errors.code) = 404];
|
||||
DB_NOT_FOUND = 1 [(errors.code) = 404];
|
||||
}
|
||||
|
||||
// 参数错误
|
||||
PARAM = 3 [(errors.code) = 400];
|
||||
|
||||
// 通用错误
|
||||
COMMON = 4 [(errors.code) = 555];
|
||||
}
|
||||
enum NotifyConsumeErr{
|
||||
option (errors.default_code) = 1;
|
||||
// 需要重试通知错误
|
||||
NeedRetryNotify = 0 [(errors.code) = 500];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,41 @@
|
|||
syntax = "proto3";
|
||||
package api.err;
|
||||
|
||||
import "errors/errors.proto";
|
||||
option go_package = "voucher/api/err";
|
||||
|
||||
enum WechatErr{
|
||||
option (errors.default_code) = 1;
|
||||
WechatFAIL = 0 [(errors.code) = 500];
|
||||
|
||||
WechatUserIllegal = 1 [(errors.code) = 500];
|
||||
WechatAppIDMchIDMismatch = 2 [(errors.code) = 500];
|
||||
WechatOpenIDAppIDMismatch = 3 [(errors.code) = 500];
|
||||
WechatInvalidMerchantID = 4 [(errors.code) = 500];
|
||||
WechatHighFrequency = 5 [(errors.code) = 500];
|
||||
WechatActivityInactive = 6 [(errors.code) = 500];
|
||||
WechatBatchInfoError = 7 [(errors.code) = 500];
|
||||
WechatAppIDRequired = 8 [(errors.code) = 500];
|
||||
WechatOpenIDRequired = 9 [(errors.code) = 500];
|
||||
WechatBatchIDRequired = 10 [(errors.code) = 500];
|
||||
WechatMerchantIDRequired = 11 [(errors.code) = 500];
|
||||
WechatInvalidBatchStatus = 12 [(errors.code) = 500];
|
||||
WechatMchNotExists = 13 [(errors.code) = 500];
|
||||
WechatBatchBudgetInsufficient = 14 [(errors.code) = 500];
|
||||
WechatDailyLimitExceeded = 15 [(errors.code) = 500];
|
||||
WechatAccountBalanceInsufficient = 16 [(errors.code) = 500];
|
||||
WechatBatchBudgetDepleted = 17 [(errors.code) = 500];
|
||||
WechatMerchantNoPermission = 18 [(errors.code) = 500];
|
||||
WechatCrossMerchantNotSupported = 19 [(errors.code) = 500];
|
||||
WechatUserReceiveLimit = 20 [(errors.code) = 500];
|
||||
WechatAPIChannelNotSupported = 21 [(errors.code) = 500];
|
||||
WechatSpecifiedDenominationNotSupported = 22 [(errors.code) = 500];
|
||||
WechatOnlyAdvertisingBatch = 23 [(errors.code) = 500];
|
||||
WechatUserMaxCoupons = 24 [(errors.code) = 500];
|
||||
WechatNaturalPersonRuleBlocked = 25 [(errors.code) = 500];
|
||||
WechatResourceNotExists = 26 [(errors.code) = 500];
|
||||
WechatFrequencyLimited = 27 [(errors.code) = 500];
|
||||
WechatAccountFail = 28 [(errors.code) = 500];
|
||||
// 微信返回错误,需要通知的错误
|
||||
WechatNeedNoticeFail = 29 [(errors.code) = 500];
|
||||
}
|
||||
|
|
@ -0,0 +1,226 @@
|
|||
syntax = "proto3";
|
||||
|
||||
package api.v1;
|
||||
option go_package = "voucher/api/v1;v1";
|
||||
import "validate/validate.proto";
|
||||
import "google/api/annotations.proto";
|
||||
import "v1/common.proto";
|
||||
|
||||
service Cmb {
|
||||
|
||||
rpc Order (CmbRequest) returns (CmbReply) {
|
||||
option (google.api.http) = {
|
||||
post: "/voucher/cmb/v1/order",
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
|
||||
rpc Query (CmbRequest) returns (CmbReply) {
|
||||
option (google.api.http) = {
|
||||
post: "/voucher/cmb/v1/query",
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
|
||||
rpc QueryProduct (CmbRequest) returns (CmbReply) {
|
||||
option (google.api.http) = {
|
||||
post: "/voucher/cmb/v1/product/query",
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
|
||||
rpc OrderRetry (OrderRetryRequest) returns (Empty) {
|
||||
option (google.api.http) = {
|
||||
post: "/voucher/cmb/v1/orderRetry",
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
|
||||
rpc OrderMock (CmbOrderRequest) returns (CmbRequest) {
|
||||
option (google.api.http) = {
|
||||
post: "/voucher/cmb/v1/orderMock",
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
rpc QueryMock (CmbQueryRequest) returns (CmbRequest) {
|
||||
option (google.api.http) = {
|
||||
post: "/voucher/cmb/v1/queryMock",
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
rpc QueryProductMock (CmbQueryProductRequest) returns (CmbRequest) {
|
||||
option (google.api.http) = {
|
||||
post: "/voucher/cmb/v1/product/queryMock",
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
|
||||
rpc DecryptBody (EncryptBodyRequest) returns (DecryptBodyReply) {
|
||||
option (google.api.http) = {
|
||||
post: "/voucher/cmb/v1/decryptBody",
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
message OrderRetryRequest {
|
||||
repeated string transactionIds = 1 [json_name = "transactionIds"];
|
||||
}
|
||||
|
||||
message CmbRequest {
|
||||
// 请求公共参数
|
||||
// 合作方唯一ID,32位定长
|
||||
string mid = 1 [json_name = "mid"];
|
||||
// 应用唯一ID,32位定长
|
||||
string aid = 2 [json_name = "aid"];
|
||||
// 时间戳 yyyyMMddHHmmss
|
||||
string date = 3 [json_name = "date"];
|
||||
// 随机字符串,保证签名不可预测,不长于32位
|
||||
string random = 4 [json_name = "random"];
|
||||
// 合作方密钥对别名
|
||||
string keyAlias = 5 [json_name = "keyAlias"];
|
||||
// 掌上生活密钥对别名
|
||||
string cmbKeyAlias = 6 [json_name = "cmbKeyAlias"];
|
||||
// 加密报文,是否需要加密,请查看各API的说明文档
|
||||
string encryptBody = 7 [json_name = "encryptBody"];
|
||||
// 签名,具体详见签名规范
|
||||
string sign = 8 [json_name = "sign"];
|
||||
}
|
||||
|
||||
message CmbReply {
|
||||
// 响应公共参数
|
||||
// 接口调用返回码,1000 成功,1001 失败
|
||||
string respCode = 1 [json_name = "respCode"];
|
||||
// 返回话术,失败信息落此字段
|
||||
string respMsg = 2 [json_name = "respMsg"];
|
||||
// 时间戳 yyyyMMddHHmmss
|
||||
string date = 3 [json_name = "date"];
|
||||
// 合作方密钥对别名
|
||||
string keyAlias = 5 [json_name = "keyAlias"];
|
||||
// 掌上生活密钥对别名
|
||||
string cmbKeyAlias = 6 [json_name = "cmbKeyAlias"];
|
||||
// 加密报文,是否需要加密,请查看各API的说明文档
|
||||
string encryptBody = 7 [json_name = "encryptBody"];
|
||||
// 签名,具体详见签名规范
|
||||
string sign = 8 [json_name = "sign"];
|
||||
}
|
||||
|
||||
|
||||
message CmbOrderRequest {
|
||||
// 业务参数
|
||||
// 唯一流水号,需支持14天内幂等
|
||||
string transactionId = 9 [json_name = "transactionId", (validate.rules).string = {min_len: 1,max_len: 50}];
|
||||
// 外部合作方权益批次号
|
||||
string activityId = 10 [json_name = "activityId", (validate.rules).string = {min_len: 1,max_len: 32}];
|
||||
// 招商银行用户号 用户标识,比如手机号、支付宝openId
|
||||
string cmbUid = 11 [json_name = "cmbUid", (validate.rules).string = {min_len: 1,max_len: 50}];
|
||||
// 应用id
|
||||
string appId = 14 [json_name = "appId", (validate.rules).string = {min_len: 1,max_len: 20}];
|
||||
// 用户标识类型,0-手机号,1-支付宝openId
|
||||
string cmbUidType = 12 [json_name = "cmbUidType", (validate.rules).string = {min_len: 1,max_len: 10}];
|
||||
// 时间戳,长度为13位,精度为毫秒
|
||||
string timestamp = 13 [json_name = "timestamp", (validate.rules).string = {min_len: 1,max_len: 14}];
|
||||
// 拓展参数
|
||||
string attach = 15 [json_name = "attach"];
|
||||
}
|
||||
message CmbOrderReply {
|
||||
// 业务参数
|
||||
// 接口调用返回码,1000 成功,1001 失败
|
||||
string respCode = 1 [json_name = "respCode"];
|
||||
// 返回话术,失败信息落此字段
|
||||
string respMsg = 2 [json_name = "respMsg"];
|
||||
// 权益标识,优惠券券码,最大长度为50位
|
||||
string codeNo = 9 [json_name = "codeNo"];
|
||||
// 错误码
|
||||
string thirdErrCode = 3 [json_name = "thirdErrCode"];
|
||||
}
|
||||
|
||||
message CmbQueryRequest {
|
||||
// 业务参数
|
||||
// 外部合作方权益批次号
|
||||
string codeNo = 9 [json_name = "codeNo", (validate.rules).string = {min_len: 1,max_len: 32}];
|
||||
}
|
||||
message CmbQueryReply {
|
||||
// 优惠券券码,codeNo
|
||||
string ticket = 9 [json_name = "ticket"];
|
||||
// 更新后串码状态,0:可使用,1:已使用
|
||||
string status = 10 [json_name = "status"];
|
||||
// 验码日期,格式yyyy-mm-dd hh:mm:ss.sss
|
||||
string transDate = 11 [json_name = "transDate"];
|
||||
// 发码机构号,固定值,掌上生活优惠券系统提供
|
||||
string orgNo = 12 [json_name = "orgNo"];
|
||||
// 扩展字段
|
||||
string ext = 13 [json_name = "ext"];
|
||||
}
|
||||
|
||||
|
||||
message CmbQueryProductRequest {
|
||||
// 业务参数
|
||||
// 外部合作方权益批次号
|
||||
string activityId = 9 [json_name = "activityId", (validate.rules).string = {min_len: 1,max_len: 32}];
|
||||
}
|
||||
message CmbQueryProductReply {
|
||||
// 接口调用返回码,1000 成功,1001 失败
|
||||
string respCode = 1 [json_name = "respCode"];
|
||||
// 返回话术,失败信息落此字段
|
||||
string respMsg = 2 [json_name = "respMsg"];
|
||||
// 业务参数
|
||||
// 批次名称
|
||||
string activityName = 9 [json_name = "activityName"];
|
||||
// 外部合作方权益批次号
|
||||
string activityId = 10 [json_name = "activityId"];
|
||||
// 面额,单位为分
|
||||
string amount = 11 [json_name = "amount"];
|
||||
// 门槛,单位为分
|
||||
string minAmount = 12 [json_name = "minAmount"];
|
||||
// 有效期形式,0:固定有效期,1:动态有效期
|
||||
string availableType = 13 [json_name = "availableType"];
|
||||
// 动态有效期天数-非必填
|
||||
string availableDays = 14 [json_name = "availableDays"];
|
||||
// 有效期开始时间-非必填
|
||||
string startTime = 15 [json_name = "startTime"];
|
||||
// 有效期结束时间-非必填
|
||||
string endTime = 16 [json_name = "endTime"];
|
||||
// 当前可用库存
|
||||
string availableStock = 17 [json_name = "availableStock"];
|
||||
// 细则描述
|
||||
string detail = 18 [json_name = "detail"];
|
||||
|
||||
// 批次开始日期 格式yyyy-mm-dd hh:mm:ss.sss
|
||||
string saleStartTime = 19 [json_name = "saleStartTime"];
|
||||
// 批次结束时间,格式yyyy-mm-dd hh:mm:ss.sss
|
||||
string saleEndTime = 20 [json_name = "saleEndTime"];
|
||||
|
||||
// 错误码
|
||||
string thirdErrCode = 21 [json_name = "thirdErrCode"];
|
||||
}
|
||||
|
||||
|
||||
message CmbNotifyRequest {
|
||||
// 优惠券券码,codeNo
|
||||
string ticket = 9 [json_name = "ticket"];
|
||||
// 更新后串码状态,0:可使用,1:已使用
|
||||
string status = 10 [json_name = "status"];
|
||||
// 验码日期,格式yyyy-mm-dd hh:mm:ss.sss
|
||||
string transDate = 11 [json_name = "transDate"];
|
||||
// 发码机构号,固定值,掌上生活优惠券系统提供
|
||||
string orgNo = 12 [json_name = "orgNo"];
|
||||
// 扩展字段
|
||||
string ext = 13 [json_name = "ext"];
|
||||
string attach = 14 [json_name = "attach"];
|
||||
}
|
||||
message CmbNotifyReply {
|
||||
// 接口调用返回码,1000 成功,1001 失败
|
||||
string respCode = 1 [json_name = "respCode"];
|
||||
// 返回信息,失败信息落此字段
|
||||
string respMsg = 2 [json_name = "respMsg"];
|
||||
}
|
||||
|
||||
|
||||
message EncryptBodyRequest {
|
||||
string encryptBody = 1 [json_name = "encryptBody"];
|
||||
}
|
||||
message DecryptBodyReply {
|
||||
string decryptBody = 1 [json_name = "decryptBody"];
|
||||
}
|
||||
|
|
@ -4,44 +4,4 @@ package api.v1;
|
|||
option go_package = "voucher/api/v1;v1";
|
||||
|
||||
// 空请求或返回
|
||||
message Empty {}
|
||||
|
||||
// 空参数请求
|
||||
message ReqEmpty {}
|
||||
|
||||
// 空返回
|
||||
message RespEmpty {}
|
||||
|
||||
// 响应选项数据
|
||||
message RespOption {
|
||||
// 名称
|
||||
string label = 1;
|
||||
// 值
|
||||
string value = 2;
|
||||
// 是否选择
|
||||
bool select = 3;
|
||||
// 禁止操作
|
||||
bool disable = 4;
|
||||
//下级选项数据
|
||||
repeated RespOption children = 5;
|
||||
}
|
||||
|
||||
// 响应选项数据列表
|
||||
message RespOptions {
|
||||
// 选项列表
|
||||
repeated RespOption list = 1;
|
||||
}
|
||||
|
||||
// ReqPage 分页响应参数
|
||||
message RespPage {
|
||||
//页码
|
||||
int32 page = 1;
|
||||
//每页数量
|
||||
int32 limit = 2;
|
||||
//总数
|
||||
int32 total = 3;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
message Empty {}
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
syntax = "proto3";
|
||||
|
||||
package api.v1;
|
||||
option go_package = "voucher/api/v1;v1";
|
||||
|
||||
import "google/api/annotations.proto";
|
||||
import "validate/validate.proto";
|
||||
import "v1/common.proto";
|
||||
|
||||
service Order {
|
||||
rpc Order (OrderRequest) returns (OrderReply) {
|
||||
option (google.api.http) = {
|
||||
post: "/openapi/v1/voucher/order",
|
||||
body:"*"
|
||||
};
|
||||
}
|
||||
|
||||
rpc Query (QueryRequest) returns (QueryReply) {
|
||||
option (google.api.http) = {
|
||||
post: "/openapi/v1/voucher/query",
|
||||
body:"*"
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
message OrderRequest {
|
||||
|
||||
}
|
||||
message OrderReply {
|
||||
|
||||
}
|
||||
|
||||
|
||||
message QueryRequest {
|
||||
|
||||
}
|
||||
message QueryReply {
|
||||
|
||||
}
|
||||
|
|
@ -12,6 +12,7 @@ import (
|
|||
"github.com/nacos-group/nacos-sdk-go/common/constant"
|
||||
_ "go.uber.org/automaxprocs"
|
||||
"gopkg.in/yaml.v2"
|
||||
"html/template"
|
||||
"os"
|
||||
"voucher/internal/conf"
|
||||
log2 "voucher/internal/pkg/log"
|
||||
|
|
@ -46,7 +47,7 @@ const (
|
|||
func init() {
|
||||
flag.StringVar(&nacosIp, "nacosIp", "120.55.12.245", "nacos ip address")
|
||||
flag.IntVar(&nacosPort, "nacosPort", 8848, "nacos port")
|
||||
flag.StringVar(&nacosSpace, "nacosSpace", "voucher", "nacos space")
|
||||
flag.StringVar(&nacosSpace, "nacosSpace", "voucher-test", "nacos space")
|
||||
flag.StringVar(&nacosUsername, "nacosUsername", "nacos", "nacos passowrd")
|
||||
flag.StringVar(&nacosPassword, "nacosPassword", "LsxdNacos@2025", "nacos passowrd")
|
||||
flag.Parse()
|
||||
|
|
@ -57,6 +58,9 @@ func newApp(
|
|||
logger log.Logger,
|
||||
httpServer *http.Server,
|
||||
consumerServer *server.Consumer,
|
||||
cronServer *server.CronServer,
|
||||
wechatNotifyConsumer *server.WechatNotifyConsumer,
|
||||
rdbConsumer *server.RdbConsumer,
|
||||
) *kratos.App {
|
||||
return kratos.New(
|
||||
kratos.ID(id),
|
||||
|
|
@ -66,6 +70,9 @@ func newApp(
|
|||
kratos.Server(
|
||||
httpServer,
|
||||
consumerServer,
|
||||
cronServer,
|
||||
wechatNotifyConsumer,
|
||||
rdbConsumer,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -114,10 +121,14 @@ func loadConfig() *conf.Bootstrap {
|
|||
|
||||
func main() {
|
||||
bc := loadConfig()
|
||||
businessLogger := log2.NewBusinessLogger(bc.Logs.Business, Name, Name, Version)
|
||||
accessLogger := log2.NewAccessLogger(bc.Logs.Business, id, Name, Version)
|
||||
if bc == nil {
|
||||
panic("配置文件加载失败")
|
||||
}
|
||||
|
||||
app, cleanup, err := wireApp(bc, businessLogger, accessLogger)
|
||||
businessLogger := log2.NewBusinessLogger(bc.Logs.Business, Name, Name, Version)
|
||||
accessLogger := log2.NewAccessLogger(bc.Logs.Access, id, Name, Version)
|
||||
|
||||
app, cleanup, err := wireApp(bc, temp(), businessLogger, accessLogger)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
|
@ -128,3 +139,17 @@ func main() {
|
|||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func temp() *template.Template {
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("获取当前工作目录失败:", err))
|
||||
}
|
||||
|
||||
templatePath := fmt.Sprintf("%s/templates/index.tmpl", wd)
|
||||
tmpl, err := template.ParseFiles(templatePath)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return tmpl
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,26 +9,35 @@ import (
|
|||
"github.com/go-kratos/kratos/v2"
|
||||
"github.com/go-kratos/kratos/v2/log"
|
||||
"github.com/google/wire"
|
||||
"github.com/robfig/cron"
|
||||
"html/template"
|
||||
"voucher/internal/biz"
|
||||
"voucher/internal/biz/cmb"
|
||||
"voucher/internal/biz/timeslicequery"
|
||||
"voucher/internal/conf"
|
||||
"voucher/internal/data"
|
||||
"voucher/internal/data/repositoryimpl"
|
||||
"voucher/internal/data/thirdrepositoryimpl"
|
||||
"voucher/internal/data/mixrepoimpl"
|
||||
"voucher/internal/data/repoimpl"
|
||||
"voucher/internal/data/wechatrepoimpl"
|
||||
log2 "voucher/internal/pkg/log"
|
||||
"voucher/internal/server"
|
||||
"voucher/internal/service"
|
||||
)
|
||||
|
||||
// wireApp init kratos application.
|
||||
func wireApp(*conf.Bootstrap, log.Logger, *log2.AccessLogger) (*kratos.App, func(), error) {
|
||||
func wireApp(*conf.Bootstrap, *template.Template, log.Logger, *log2.AccessLogger) (*kratos.App, func(), error) {
|
||||
panic(wire.Build(
|
||||
server.ProviderSetServer,
|
||||
service.ProviderSetService,
|
||||
cmb.ProviderSetCmb,
|
||||
biz.ProviderSetBiz,
|
||||
repositoryimpl.ProviderRepoImplSet,
|
||||
thirdrepositoryimpl.ProviderThirdRepositoryImplSet,
|
||||
data.ProviderDataSet,
|
||||
repoimpl.ProviderRepoImplSet,
|
||||
wechatrepoimpl.ProviderWechatReposImplSet,
|
||||
mixrepoimpl.ProviderMixRepoImplSet,
|
||||
timeslicequery.ProviderSetTimeSliceQuery,
|
||||
log2.NewLogHelper,
|
||||
cron.New,
|
||||
newApp,
|
||||
))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,49 +0,0 @@
|
|||
// Code generated by Wire. DO NOT EDIT.
|
||||
|
||||
//go:generate go run -mod=mod github.com/google/wire/cmd/wire
|
||||
//go:build !wireinject
|
||||
// +build !wireinject
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/go-kratos/kratos/v2"
|
||||
"github.com/go-kratos/kratos/v2/log"
|
||||
"voucher/internal/biz"
|
||||
"voucher/internal/conf"
|
||||
"voucher/internal/data"
|
||||
"voucher/internal/data/repositoryimpl"
|
||||
"voucher/internal/data/thirdrepositoryimpl"
|
||||
log2 "voucher/internal/pkg/log"
|
||||
"voucher/internal/server"
|
||||
"voucher/internal/service"
|
||||
)
|
||||
|
||||
import (
|
||||
_ "go.uber.org/automaxprocs"
|
||||
)
|
||||
|
||||
// Injectors from wire.go:
|
||||
|
||||
// wireApp init kratos application.
|
||||
func wireApp(bootstrap *conf.Bootstrap, logger log.Logger, accessLogger *log2.AccessLogger) (*kratos.App, func(), error) {
|
||||
helper := log2.NewLogHelper(logger)
|
||||
gormDb, cleanup := data.NewGormDb(bootstrap)
|
||||
db := data.NewDb(gormDb)
|
||||
orderRepo := repositoryimpl.NewOrderRepoImpl(db)
|
||||
rocketMQ, cleanup2, err := data.NewRocketMQ(bootstrap)
|
||||
if err != nil {
|
||||
cleanup()
|
||||
return nil, nil, err
|
||||
}
|
||||
thirdMQSend := thirdrepositoryimpl.NewMQSendImpl(rocketMQ)
|
||||
voucherBiz := biz.NewVoucherBiz(orderRepo, thirdMQSend)
|
||||
voucherService := service.NewVoucherService(voucherBiz)
|
||||
httpServer := server.NewHTTPServer(bootstrap, helper, accessLogger, voucherService)
|
||||
consumer := server.NewConsumer(helper, bootstrap, voucherService)
|
||||
app := newApp(logger, httpServer, consumer)
|
||||
return app, func() {
|
||||
cleanup2()
|
||||
cleanup()
|
||||
}, nil
|
||||
}
|
||||
|
|
@ -8,20 +8,20 @@ server:
|
|||
data:
|
||||
db:
|
||||
driver: mysql
|
||||
source: root:lansexiongdi6,@tcp(47.97.27.195:3306)/merketing?parseTime=True&loc=Local
|
||||
maxIdle: 5 #最大的空闲连接数
|
||||
maxOpen: 100 #最大连接数,0表示不受限制
|
||||
maxLifetime: 5s #连接复用的最大生命周期
|
||||
isDebug: true
|
||||
source: root:lansexiongdi6,@tcp(47.108.53.72:3306)/voucher?parseTime=True&loc=Local
|
||||
maxIdle: 200 #最大的空闲连接数
|
||||
maxOpen: 1000 #最大连接数,0表示不受限制
|
||||
maxLifetime: 300s #连接复用的最大生命周期
|
||||
isDebug: false
|
||||
redis: #没有则注释此属性
|
||||
addr: 47.97.27.195:6379
|
||||
addr: 47.108.53.72:6379
|
||||
password: lansexiongdi@666
|
||||
readTimeout: 3s
|
||||
writeTimeout: 3s
|
||||
poolSize: 5 #连接池大小,不配置,或配置为0表示不启用连接池
|
||||
minIdleConns: 2 #最小空闲连接数
|
||||
connMaxIdleTime: 30s #每个连接最大空闲时间,如果超过了这个时间会被关闭
|
||||
db: 1
|
||||
readTimeout: 5s
|
||||
writeTimeout: 5s
|
||||
poolSize: 200 #连接池大小,不配置,或配置为0表示不启用连接池
|
||||
minIdleConns: 50 #最小空闲连接数
|
||||
connMaxIdleTime: 60s #每个连接最大空闲时间,如果超过了这个时间会被关闭
|
||||
db: 3
|
||||
|
||||
rocketMQ:
|
||||
addr: "http://rmq-cn-nwy3fn4ex09.cn-chengdu.rmq.aliyuncs.com:8080"
|
||||
|
|
@ -29,25 +29,99 @@ rocketMQ:
|
|||
secretKey: "Z3596KCFA9RAUR6k"
|
||||
secretToken: ""
|
||||
eventMap:
|
||||
order:
|
||||
topic: voucher_order_create
|
||||
group: voucher_order_create_group
|
||||
isOpenConsumer: false #是否启动消费 true/false
|
||||
PerCoroutineCnt: 5 #协程数量,不配置默认为20
|
||||
RetryCnt: 3 #重试次数,不配置默认38
|
||||
query:
|
||||
topic: voucher_order_query
|
||||
group: voucher_order_query_group
|
||||
isOpenConsumer: false #是否启动消费 true/false
|
||||
PerCoroutineCnt: 5 #协程数量,不配置默认为20
|
||||
RetryCnt: 3 #重试次数,不配置默认38
|
||||
notify:
|
||||
topic: voucher_order_notify
|
||||
group: voucher_order_notify_group
|
||||
isOpenConsumer: false #是否启动消费 true/false
|
||||
PerCoroutineCnt: 5 #协程数量,不配置默认为20
|
||||
notifyRetry: # 重试延迟队列
|
||||
topic: voucher_order_notifyRetry
|
||||
group: voucher_order_notifyRetry_group
|
||||
isOpenConsumer: true #是否启动消费 true/false
|
||||
PerCoroutineCnt: 2 #协程数量,不配置默认为20
|
||||
RetryCnt: 3 #重试次数,不配置默认38
|
||||
|
||||
wechatNotifyMQ:
|
||||
accessKeyId: "LTAI5tPyV7FynQNTfEvbEBuX"
|
||||
accessKeySecret: "tZmTh8cV98xAQgtlRU0soWcb6Tpd4T"
|
||||
endPoint: "http://1389288909295870.mqrest.cn-hangzhou.aliyuncs.com"
|
||||
regionId: "hangzhou"
|
||||
instanceId: "MQ_INST_1389288909295870_BYSoMttI"
|
||||
topic: "notify"
|
||||
groupId: "GID_voucher"
|
||||
tag: "voucher_notify_dev"
|
||||
isOpenConsumer: true #是否启动消费 true/false
|
||||
registerTagUrl: "https://wpcallbacks.api.1688sup.com/wechatPay/register_tag"
|
||||
|
||||
wechat:
|
||||
mchID: "1710953361" # 证书所属商户 蓝色兄弟服务商立减金配置
|
||||
mchCertificateSerialNumber: "6006B8208815DB5EAC5BF2E783CB9D34082C3772" #商户证书序列号
|
||||
wechatPayPublicKeyID: "PUB_KEY_ID_0117109533612025031800326400002563" #微信支付公钥ID
|
||||
|
||||
cmb:
|
||||
sm2Prk: "8d39ff3d2559258c163f4510f082727f51531e1953ab203d5ab1ea4a6d94fd73"
|
||||
sm2Puk: "04d827a7dbaaa358ce45b8c7794a7f54819f5c175005a702370e47f135ef6f5f9732758b1474f218419fe9e87f90c28c3b05f08254c651db27df35fae67b77b2e4" # 公钥,给到招行密钥
|
||||
# cmbSm2Pik: "" # 招行私钥mock使用,生产不会有该值
|
||||
# cmbSm2Puk: "0416445bc16cbf42e47002ad9fe7c7af67d902b48be1eb69b98f6a006b0918630e1127f5f2fff83b2ecb30fc7fd72c34c33f37c7c355dffde3589f66800f0036ca" # 招行公钥
|
||||
cmbSm2Pik: "f6a8d2f412e289686aba6a0f33cad1a64367d0ba012046ee0fbbefd3ffd675bd" # 招行私钥mock使用,生产不会有该值
|
||||
cmbSm2Puk: "043b2fade30067b6bd8e61b42771b1e953116fc5a0f9ed6939fceb9254b8d7d6989902c913642c3c68c42a2b56364512675ea0b517dd4469e73b73c888a2f4e8e3" # 招行公钥-mock使用
|
||||
mid: "d6fdd78b6fd13a808818286b9cad9687"
|
||||
aid: "5efaa21263b94f669a1c90ed0279df20"
|
||||
keyAlias: "CO_PUB_KEY_SM2"
|
||||
cmbKeyAlias: "SM2_CMBLIFE"
|
||||
orgNo: "LANSEXIONGDI" # 发码机构号,固定值,掌上生活优惠券系统提供
|
||||
notifyUrl: "https://sandbox.cdcc.cmbchina.com/AccessGateway/transIn/updateCodeStatus.json" # 招行测试回调地址
|
||||
noticeStartDays: 7
|
||||
noticeEndDays: 1
|
||||
|
||||
#告警配置
|
||||
alarm:
|
||||
webhookURL: "https://oapi.dingtalk.com/robot/send?access_token=5f10c2213cbf8168985cb2d061ebb1a5f70bd1dd47ec7cef58fa6fe545d52588"
|
||||
secret: "SEC77b63d70a9e22317144e712b4538ce1e0013db885c65f7f9bae283e8958b39eb"
|
||||
isAll: false
|
||||
atMobiles:
|
||||
- "18666173766"
|
||||
warningMobiles:
|
||||
- "13474987525"
|
||||
|
||||
cron:
|
||||
isOpen: true #是否启动,控制全局
|
||||
commandMap:
|
||||
orderNotice:
|
||||
isOpen: true #是否启动 true/false
|
||||
command: "0 0 1 * * ?" # 每天凌晨1点执行一次
|
||||
warningBudget:
|
||||
isOpen: true #是否启动 true/false
|
||||
command: "0 */5 * * * ?" #cron表达式,每5分钟执行一次
|
||||
|
||||
rdsMQ:
|
||||
wechatQuery:
|
||||
name: "wechatQuery"
|
||||
retryNum: 1 #重试次数
|
||||
numWorkers: 2 #协程数量,不配置默认为10
|
||||
waitTime: 1s #处理完成后等待时间
|
||||
isOpen: false #是否启动消费 true/false
|
||||
wechatTimeSliceQuery:
|
||||
name: "wechatTimeSliceQuery"
|
||||
retryNum: 1 #重试次数
|
||||
numWorkers: 3 #协程数量,不配置默认为10
|
||||
waitTime: 1s #处理完成后等待时间
|
||||
isOpen: false #是否启动消费 true/false
|
||||
orderRetry:
|
||||
name: "orderRetry"
|
||||
retryNum: 1 #重试次数
|
||||
numWorkers: 2 #协程数量,不配置默认为10
|
||||
waitTime: 1s #处理完成后等待时间
|
||||
isOpen: false #是否启动消费 true/false
|
||||
retryNotify:
|
||||
name: "retryNotify"
|
||||
retryNum: 1 #重试次数
|
||||
numWorkers: 1 #协程数量,不配置默认为10
|
||||
waitTime: 1s #处理完成后等待时间
|
||||
isOpen: false #是否启动消费 true/false
|
||||
|
||||
aliYunSms:
|
||||
accessKeyId:
|
||||
accessKeySecret:
|
||||
endpoint: dysmsapi.aliyuncs.com
|
||||
signName: 蓝色兄弟
|
||||
templateWarning: "SMS_489660721"
|
||||
|
||||
#配置日志
|
||||
logs:
|
||||
business: business.log #业务日志路径:如果不写日志,则不配置或配置为空
|
||||
|
|
|
|||
|
|
@ -0,0 +1,117 @@
|
|||
server:
|
||||
http:
|
||||
addr: 0.0.0.0:13000
|
||||
timeout: 30s
|
||||
isOpenSwagger: true #是否开启api文档,仅用于开发、测试,线上禁止启用
|
||||
isResponseReqHeaders: true #是否开启响应请求头,仅用于开发调试,线上禁止启用
|
||||
|
||||
data:
|
||||
db:
|
||||
driver: mysql
|
||||
source: root:lansexiongdi6,@tcp(47.97.27.195:3306)/voucher?parseTime=True&loc=Local
|
||||
maxIdle: 200 #最大的空闲连接数
|
||||
maxOpen: 1000 #最大连接数,0表示不受限制
|
||||
maxLifetime: 300s #连接复用的最大生命周期
|
||||
isDebug: false
|
||||
redis: #没有则注释此属性
|
||||
addr: 47.97.27.195:6379
|
||||
password: lansexiongdi@666
|
||||
readTimeout: 5s
|
||||
writeTimeout: 5s
|
||||
poolSize: 200 #连接池大小,不配置,或配置为0表示不启用连接池
|
||||
minIdleConns: 50 #最小空闲连接数
|
||||
connMaxIdleTime: 60s #每个连接最大空闲时间,如果超过了这个时间会被关闭
|
||||
db: 3
|
||||
|
||||
rocketMQ:
|
||||
addr: "http://rmq-cn-nwy3fn4ex09.cn-chengdu.rmq.aliyuncs.com:8080"
|
||||
accessKey: "Qecl4cea2IAZPKoD"
|
||||
secretKey: "Z3596KCFA9RAUR6k"
|
||||
secretToken: ""
|
||||
eventMap:
|
||||
notifyRetry: # 重试延迟队列
|
||||
topic: voucher_order_notifyRetry
|
||||
group: voucher_order_notifyRetry_group
|
||||
isOpenConsumer: true #是否启动消费 true/false
|
||||
PerCoroutineCnt: 2 #协程数量,不配置默认为20
|
||||
RetryCnt: 3 #重试次数,不配置默认38
|
||||
|
||||
wechatNotifyMQ:
|
||||
accessKeyId: "LTAI5tPyV7FynQNTfEvbEBuX"
|
||||
accessKeySecret: "tZmTh8cV98xAQgtlRU0soWcb6Tpd4T"
|
||||
endPoint: "http://1389288909295870.mqrest.cn-hangzhou.aliyuncs.com"
|
||||
regionId: "hangzhou"
|
||||
instanceId: "MQ_INST_1389288909295870_BYSoMttI"
|
||||
topic: "notify"
|
||||
groupId: "GID_voucher"
|
||||
tag: "voucher_notify_dev"
|
||||
isOpenConsumer: true #是否启动消费 true/false
|
||||
registerTagUrl: "https://wpcallbacks.api.1688sup.com/wechatPay/register_tag"
|
||||
|
||||
wechat:
|
||||
mchID: "1710953361" # 证书所属商户 蓝色兄弟服务商立减金配置
|
||||
mchCertificateSerialNumber: "6006B8208815DB5EAC5BF2E783CB9D34082C3772" #商户证书序列号
|
||||
wechatPayPublicKeyID: "PUB_KEY_ID_0117109533612025031800326400002563" #微信支付公钥ID
|
||||
|
||||
cmb:
|
||||
sm2Prk: "8d39ff3d2559258c163f4510f082727f51531e1953ab203d5ab1ea4a6d94fd73"
|
||||
sm2Puk: "04d827a7dbaaa358ce45b8c7794a7f54819f5c175005a702370e47f135ef6f5f9732758b1474f218419fe9e87f90c28c3b05f08254c651db27df35fae67b77b2e4" # 公钥,给到招行密钥
|
||||
# cmbSm2Pik: "" # 招行私钥mock使用,生产不会有该值
|
||||
# cmbSm2Puk: "0416445bc16cbf42e47002ad9fe7c7af67d902b48be1eb69b98f6a006b0918630e1127f5f2fff83b2ecb30fc7fd72c34c33f37c7c355dffde3589f66800f0036ca" # 招行公钥
|
||||
cmbSm2Pik: "f6a8d2f412e289686aba6a0f33cad1a64367d0ba012046ee0fbbefd3ffd675bd" # 招行私钥mock使用,生产不会有该值
|
||||
cmbSm2Puk: "043b2fade30067b6bd8e61b42771b1e953116fc5a0f9ed6939fceb9254b8d7d6989902c913642c3c68c42a2b56364512675ea0b517dd4469e73b73c888a2f4e8e3" # 招行公钥-mock使用
|
||||
mid: "d6fdd78b6fd13a808818286b9cad9687"
|
||||
aid: "5efaa21263b94f669a1c90ed0279df20"
|
||||
keyAlias: "CO_PUB_KEY_SM2"
|
||||
cmbKeyAlias: "SM2_CMBLIFE"
|
||||
orgNo: "LANSEXIONGDI" # 发码机构号,固定值,掌上生活优惠券系统提供
|
||||
notifyUrl: "https://sandbox.cdcc.cmbchina.com/AccessGateway/transIn/updateCodeStatus.json" # 招行测试回调地址
|
||||
noticeStartDays: 7
|
||||
noticeEndDays: 1
|
||||
|
||||
#告警配置
|
||||
alarm:
|
||||
webhookURL: "https://oapi.dingtalk.com/robot/send?access_token=5f10c2213cbf8168985cb2d061ebb1a5f70bd1dd47ec7cef58fa6fe545d52588"
|
||||
secret: "SEC77b63d70a9e22317144e712b4538ce1e0013db885c65f7f9bae283e8958b39eb"
|
||||
isAll: false
|
||||
atMobiles:
|
||||
- "18666173766"
|
||||
|
||||
cron:
|
||||
isOpen: true #是否启动,控制全局
|
||||
commandMap:
|
||||
orderNotice:
|
||||
isOpen: true #是否启动 true/false
|
||||
command: "0 0 1 * * ?" # 每天凌晨1点执行一次
|
||||
|
||||
rdsMQ:
|
||||
wechatQuery: #发放结算
|
||||
name: "wechatQuery"
|
||||
retryNum: 1 #重试次数
|
||||
numWorkers: 1 #协程数量,不配置默认为10
|
||||
waitTime: 1s #处理完成后等待时间
|
||||
isOpen: true #是否启动消费 true/false
|
||||
wechatTimeSliceQuery:
|
||||
name: "wechatTimeSliceQuery"
|
||||
retryNum: 1 #重试次数
|
||||
numWorkers: 3 #协程数量,不配置默认为10
|
||||
waitTime: 1s #处理完成后等待时间
|
||||
isOpen: true #是否启动消费 true/false
|
||||
retryNotify:
|
||||
name: "retryNotify"
|
||||
retryNum: 1 #重试次数
|
||||
numWorkers: 1 #协程数量,不配置默认为10
|
||||
waitTime: 1s #处理完成后等待时间
|
||||
isOpen: false #是否启动消费 true/false
|
||||
|
||||
aliYunSms:
|
||||
accessKeyId: LTAI5tM1X4HuqUwT8D74qXAH
|
||||
accessKeySecret: gxsC1QK12NSKH1HkCqKR1EnMdAy3ad
|
||||
endpoint: dysmsapi.aliyuncs.com
|
||||
signName: 蓝色兄弟
|
||||
templateWarning: "SMS_489660721"
|
||||
|
||||
#配置日志
|
||||
logs:
|
||||
business: business.log #业务日志路径:如果不写日志,则不配置或配置为空
|
||||
access: access.log #访问日志路径:如果不写日志,则不配置或配置为空
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
#!/bin/bash
|
||||
|
||||
CI_COMMIT_ID=$1
|
||||
|
||||
server="voucher"
|
||||
image="voucher"
|
||||
http_port=13000
|
||||
docker pull registry.cn-chengdu.aliyuncs.com/lsxdjr/$image:"${CI_COMMIT_ID}"
|
||||
docker stop $server && docker rm $server
|
||||
# docker run
|
||||
docker run --network=merketing-network -itd --name $server --restart=always \
|
||||
-p $http_port:13000 \
|
||||
-v /var/www/marketing/cert:/app/cert \
|
||||
-v /var/www/marketing/templates:/app/templates \
|
||||
registry.cn-chengdu.aliyuncs.com/lsxdjr/$image:"${CI_COMMIT_ID}" \
|
||||
./server -nacosIp "47.110.74.203" -nacosPort 8848 -nacosSpace "voucher" -nacosUsername "" -nacosPassword ""
|
||||
docker image prune -f
|
||||
docker container prune -f
|
||||
|
||||
#测试 /var/www/marketing/cert
|
||||
#压测环境 /var/www/cert
|
||||
#线上 /var/www/cert
|
||||
|
||||
docker run -itd --name "voucher" --restart=always \
|
||||
-p 13000:13000 \
|
||||
-v /var/www/cert:/app/cert \
|
||||
registry.cn-chengdu.aliyuncs.com/lsxdjr/voucher-pre:50bb64d6 \
|
||||
./server -nacosIp "172.16.0.239" -nacosPort 8848 -nacosSpace "pre" -nacosUsername "nacos" -nacosPassword "nacos"
|
||||
|
||||
|
||||
# mysql
|
||||
docker run -itd --name "mysql" --restart=always \
|
||||
-p 3306:3306 \
|
||||
-v /data/mysql/data:/var/lib/mysql:rw \
|
||||
-v /data/mysql/mysql/conf:/etc/mysql/conf.d:rw \
|
||||
-v /data/mysql/mysql/logs:/logs:rw \
|
||||
mysql:8.0.27
|
||||
|
|
@ -0,0 +1 @@
|
|||
docker run -d --name nginx -p 80:80 -p 443:443 -p 9200:9200 -v /var/www/web/vhost:/etc/nginx/conf.d -v /var/www/web/sites:/usr/share/nginx/html nginx
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
upstream voucherServer{
|
||||
least_conn;
|
||||
# 第一个后端服务器
|
||||
server 172.29.42.89:9200;
|
||||
# 新增第二个后端服务器
|
||||
server 172.29.42.90:9200;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443;
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name voucher.86698.cn;
|
||||
ssl_certificate /etc/nginx/conf/certs/voucher.pem;
|
||||
ssl_certificate_key /etc/nginx/conf/certs/voucher.key;
|
||||
ssl_session_timeout 5m;
|
||||
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
|
||||
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
|
||||
ssl_prefer_server_ciphers on;
|
||||
#access_log /var/log/nginx/host.access.log main;
|
||||
|
||||
location / {
|
||||
root /usr/share/nginx/html/web;
|
||||
index index.html index.html;
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
location /voucher {
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header referer $http_referer;
|
||||
proxy_pass http://voucherServer;
|
||||
}
|
||||
|
||||
error_page 500 502 503 504 /50x.html;
|
||||
location = /50x.html {
|
||||
root /usr/share/nginx/html;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
upstream voucher{
|
||||
# 第一个后端服务器
|
||||
server 172.29.42.89:13000 backup;
|
||||
# 新增第二个后端服务器
|
||||
server 172.29.42.90:13000;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 9200;
|
||||
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
|
||||
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
|
||||
ssl_prefer_server_ciphers on;
|
||||
|
||||
location / {
|
||||
root /usr/share/nginx/html/web;
|
||||
index index.html index.html;
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
location /voucher {
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header referer $http_referer;
|
||||
proxy_pass http://voucher;
|
||||
}
|
||||
|
||||
error_page 500 502 503 504 /50x.html;
|
||||
location = /50x.html {
|
||||
root /usr/share/nginx/html;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
|
||||
upstream voucher{
|
||||
# 第一个后端服务器
|
||||
server 172.29.42.89:13000;
|
||||
# 新增第二个后端服务器
|
||||
server 172.29.42.90:13000 backup;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 9200;
|
||||
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
|
||||
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
|
||||
ssl_prefer_server_ciphers on;
|
||||
|
||||
location / {
|
||||
root /usr/share/nginx/html/web;
|
||||
index index.html index.html;
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
location /voucher {
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header referer $http_referer;
|
||||
proxy_pass http://voucher;
|
||||
}
|
||||
|
||||
error_page 500 502 503 504 /50x.html;
|
||||
location = /50x.html {
|
||||
root /usr/share/nginx/html;
|
||||
}
|
||||
}
|
||||
55
go.mod
55
go.mod
|
|
@ -3,78 +3,99 @@ module voucher
|
|||
go 1.22.2
|
||||
|
||||
require (
|
||||
gitea.cdlsxd.cn/self-tools/l_crypt v1.0.0
|
||||
github.com/ZZMarquis/gm v1.3.2
|
||||
github.com/aliyunmq/mq-http-go-sdk v1.0.3
|
||||
github.com/apache/rocketmq-client-go/v2 v2.1.2
|
||||
github.com/brianvoe/gofakeit/v6 v6.28.0
|
||||
github.com/bwmarrin/snowflake v0.3.0
|
||||
github.com/duke-git/lancet/v2 v2.2.8
|
||||
github.com/emmansun/gmsm v0.29.8
|
||||
github.com/envoyproxy/protoc-gen-validate v1.0.4
|
||||
github.com/go-kratos/kratos/contrib/config/nacos/v2 v2.0.0-20241105072421-f8b97f675b32
|
||||
github.com/go-kratos/kratos/v2 v2.8.2
|
||||
github.com/go-playground/validator/v10 v10.25.0
|
||||
github.com/gogap/errors v0.0.0-20210818113853-edfbba0ddea9
|
||||
github.com/gogf/gf v1.16.9
|
||||
github.com/google/wire v0.6.0
|
||||
github.com/gorilla/handlers v1.5.1
|
||||
github.com/nacos-group/nacos-sdk-go v1.1.4
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/redis/go-redis/v9 v9.1.0
|
||||
github.com/robfig/cron v1.2.0
|
||||
github.com/tjfoc/gmsm v1.4.1
|
||||
github.com/wechatpay-apiv3/wechatpay-go v0.2.20
|
||||
go.opentelemetry.io/otel v1.28.0
|
||||
go.opentelemetry.io/otel/exporters/jaeger v1.17.0
|
||||
go.opentelemetry.io/otel/sdk v1.28.0
|
||||
go.opentelemetry.io/otel/trace v1.28.0
|
||||
go.uber.org/automaxprocs v1.5.1
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094
|
||||
golang.org/x/time v0.5.0
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917
|
||||
google.golang.org/protobuf v1.34.2
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
gorm.io/driver/mysql v1.5.7
|
||||
gorm.io/gorm v1.25.12
|
||||
gorm.io/hints v1.1.2
|
||||
)
|
||||
|
||||
require (
|
||||
dario.cat/mergo v1.0.0 // indirect
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.61.18 // indirect
|
||||
github.com/andybalholm/brotli v1.1.1 // indirect
|
||||
github.com/buger/jsonparser v1.1.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/emirpasic/gods v1.12.0 // indirect
|
||||
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.1 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
|
||||
github.com/go-errors/errors v1.0.1 // indirect
|
||||
github.com/go-kratos/aegis v0.2.0 // indirect
|
||||
github.com/go-logr/logr v1.4.2 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-playground/assert/v2 v2.2.0 // indirect
|
||||
github.com/go-playground/form/v4 v4.2.0 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-sql-driver/mysql v1.7.0 // indirect
|
||||
github.com/gogap/stack v0.0.0-20150131034635-fef68dddd4f8 // indirect
|
||||
github.com/golang/mock v1.3.1 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00 // indirect
|
||||
github.com/gorilla/mux v1.8.1 // indirect
|
||||
github.com/hashicorp/errwrap v1.0.0 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/compress v1.17.11 // indirect
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
|
||||
github.com/rogpeppe/go-internal v1.12.0 // indirect
|
||||
github.com/sirupsen/logrus v1.4.2 // indirect
|
||||
github.com/smartystreets/assertions v1.1.0 // indirect
|
||||
github.com/sirupsen/logrus v1.4.0 // indirect
|
||||
github.com/tidwall/gjson v1.13.0 // indirect
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/pretty v1.2.0 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasthttp v1.59.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.28.0 // indirect
|
||||
go.uber.org/atomic v1.6.0 // indirect
|
||||
go.uber.org/multierr v1.5.0 // indirect
|
||||
go.uber.org/zap v1.15.0 // indirect
|
||||
golang.org/x/crypto v0.33.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20221208152030-732eee02a75a // indirect
|
||||
golang.org/x/lint v0.0.0-20241112194109-818c5a804067 // indirect
|
||||
golang.org/x/net v0.32.0 // indirect
|
||||
golang.org/x/sync v0.10.0 // indirect
|
||||
golang.org/x/sys v0.28.0 // indirect
|
||||
golang.org/x/text v0.21.0 // indirect
|
||||
golang.org/x/tools v0.28.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect
|
||||
google.golang.org/grpc v1.64.0 // indirect
|
||||
gopkg.in/ini.v1 v1.56.0 // indirect
|
||||
golang.org/x/net v0.35.0 // indirect
|
||||
golang.org/x/sync v0.11.0 // indirect
|
||||
golang.org/x/sys v0.30.0 // indirect
|
||||
golang.org/x/term v0.29.0 // indirect
|
||||
golang.org/x/text v0.22.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20231212172506-995d672761c0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917 // indirect
|
||||
google.golang.org/grpc v1.61.1 // indirect
|
||||
gopkg.in/ini.v1 v1.42.0 // indirect
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
stathat.com/c/consistent v1.0.0 // indirect
|
||||
|
|
|
|||
157
go.sum
157
go.sum
|
|
@ -1,10 +1,21 @@
|
|||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
|
||||
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
||||
gitea.cdlsxd.cn/self-tools/l_crypt v1.0.0 h1:U0SBdxgrWNmbzPjEMuAtA51GeQ8PpRoZgN+SNoyqrQM=
|
||||
gitea.cdlsxd.cn/self-tools/l_crypt v1.0.0/go.mod h1:Q7sEfyYLXGZ7i97HBJ7OaYLYyc4f9scUJK9Ev3ZGyDM=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I=
|
||||
github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
github.com/ZZMarquis/gm v1.3.2 h1:lFtpzg5zeeVMZ/gKi0gtYcKLBEo9XTqsZDHDz6s3Gow=
|
||||
github.com/ZZMarquis/gm v1.3.2/go.mod h1:wWbjZYgruQVd7Bb8UkSN8ujU931kx2XUW6nZLCiDE0Q=
|
||||
github.com/agiledragon/gomonkey v2.0.2+incompatible h1:eXKi9/piiC3cjJD1658mEE2o3NjkJ5vDLgYjCQu0Xlw=
|
||||
github.com/agiledragon/gomonkey v2.0.2+incompatible/go.mod h1:2NGfXu1a80LLr2cmWXGBDaHEjb1idR6+FVlX5T3D9hw=
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.61.18 h1:zOVTBdCKFd9JbCKz9/nt+FovbjPFmb7mUnp8nH9fQBA=
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.61.18/go.mod h1:v8ESoHo4SyHmuB4b1tJqDHxfTGEciD+yhvOU/5s1Rfk=
|
||||
github.com/aliyunmq/mq-http-go-sdk v1.0.3 h1:/uhH7DUoaw9XTtsPgDp7zdPUyG5FBKj2GmJJph9z+6o=
|
||||
github.com/aliyunmq/mq-http-go-sdk v1.0.3/go.mod h1:JYfRMQoPexERvnNNBcal0ZQ2TVQ5ialDiW9ScjaadEM=
|
||||
github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
|
||||
github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
|
||||
github.com/apache/rocketmq-client-go/v2 v2.1.2 h1:yt73olKe5N6894Dbm+ojRf/JPiP0cxfDNNffKwhpJVg=
|
||||
github.com/apache/rocketmq-client-go/v2 v2.1.2/go.mod h1:6I6vgxHR3hzrvn+6n/4mrhS+UTulzK/X9LB2Vk1U5gE=
|
||||
github.com/brianvoe/gofakeit/v6 v6.28.0 h1:Xib46XXuQfmlLS2EXRuJpqcw8St6qSZz75OUo0tgAW4=
|
||||
|
|
@ -15,6 +26,9 @@ github.com/bsm/gomega v1.26.0 h1:LhQm+AFcgV2M0WyKroMASzAzCAJVpAxQXv4SaI9a69Y=
|
|||
github.com/bsm/gomega v1.26.0/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
|
||||
github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
|
||||
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
|
||||
github.com/bwmarrin/snowflake v0.3.0 h1:xm67bEhkKh6ij1790JB83OujPR5CzNe8QuQqAgISZN0=
|
||||
github.com/bwmarrin/snowflake v0.3.0/go.mod h1:NdZxfVWX+oR6y2K0o6qAYv6gIOP9rjG0/E9WsDpxqwE=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
|
|
@ -23,6 +37,8 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P
|
|||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/clbanning/mxj v1.8.5-0.20200714211355-ff02cfb8ea28 h1:LdXxtjzvZYhhUaonAaAKArG3pyC67kGL3YY+6hGG8G4=
|
||||
github.com/clbanning/mxj v1.8.5-0.20200714211355-ff02cfb8ea28/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
|
|
@ -33,8 +49,15 @@ github.com/duke-git/lancet/v2 v2.2.8 h1:wlruXhliDe4zls1e2cYmz4qLc+WtcvrpcCnk1VJd
|
|||
github.com/duke-git/lancet/v2 v2.2.8/go.mod h1:zGa2R4xswg6EG9I6WnyubDbFO/+A/RROxIbXcwryTsc=
|
||||
github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
|
||||
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
|
||||
github.com/emmansun/gmsm v0.29.8 h1:py9RwKHe4sIxjgD9mtWSIF5bmspP7IXvQ70Rx8x22Ow=
|
||||
github.com/emmansun/gmsm v0.29.8/go.mod h1:7UTFG3GmF8yxyZVB4HgpdeU0JoergL/i2OMGm5w02RA=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/envoyproxy/protoc-gen-validate v1.0.4 h1:gVPz/FMfvh57HdSJQyvBtF00j8JU4zdyUgIUNhlgg0A=
|
||||
github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew=
|
||||
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojtoVVWjGfOF9635RETekkoH6Cc9SX0A=
|
||||
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqLaRiH3MsBH8va0n7s1pQYcu3uTb8G4tygF4Zg=
|
||||
github.com/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc=
|
||||
github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
|
||||
github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ=
|
||||
|
|
@ -43,6 +66,8 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo
|
|||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
|
||||
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
|
||||
github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM=
|
||||
github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
|
||||
github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=
|
||||
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
|
||||
github.com/go-kratos/aegis v0.2.0 h1:dObzCDWn3XVjUkgxyBp6ZeWtx/do0DPZ7LY3yNSJLUQ=
|
||||
|
|
@ -61,17 +86,31 @@ github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lY
|
|||
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/form/v4 v4.2.0 h1:N1wh+Goz61e6w66vo8vJkQt+uwZSoLz50kZPJWR8eic=
|
||||
github.com/go-playground/form/v4 v4.2.0/go.mod h1:q1a2BY+AQUUzhl6xA/6hBetay6dEIhMHjgvJiGo6K7U=
|
||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.25.0 h1:5Dh7cjvzR7BRZadnsVOzPhWsrwUr0nmsZJxEAnFLNO8=
|
||||
github.com/go-playground/validator/v10 v10.25.0/go.mod h1:GGzBIJMuE98Ic/kJsBXbz1x/7cByt++cQ+YOuDM5wus=
|
||||
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
|
||||
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc=
|
||||
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
github.com/gogap/errors v0.0.0-20210818113853-edfbba0ddea9 h1:qvGIRaCYFKkyFK9SgRXJCc/lmQCeeg2cl3mwBKQd5W0=
|
||||
github.com/gogap/errors v0.0.0-20210818113853-edfbba0ddea9/go.mod h1:tbRYYYC7g/H7QlCeX0Z2zaThWKowF4QQCFIsGgAsqRo=
|
||||
github.com/gogap/stack v0.0.0-20150131034635-fef68dddd4f8 h1:AuxION6c7in+AsPmFjQTUKT6/o1suT8XEEpfU0pWsHA=
|
||||
github.com/gogap/stack v0.0.0-20150131034635-fef68dddd4f8/go.mod h1:6q1WEv2BiAO4FSdwLQTJbWQYAn1/qDNJHUGJNXCj9kM=
|
||||
github.com/gogf/gf v1.16.9 h1:Q803UmmRo59+Ws08sMVFOcd8oNpkSWL9vS33hlo/Cyk=
|
||||
github.com/gogf/gf v1.16.9/go.mod h1:8Q/kw05nlVRp+4vv7XASBsMe9L1tsVKiGoeP2AHnlkk=
|
||||
github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.3.1 h1:qGJ6qTW+x6xX/my+8YUVl4WNpX9B7+/l2tRsHGZ7f2s=
|
||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
|
|
@ -80,6 +119,8 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq
|
|||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/gomodule/redigo v1.8.5 h1:nRAxCa+SVsyjSBrtZmG/cqb6VbTmuRzpg/PoTFlpumc=
|
||||
github.com/gomodule/redigo v1.8.5/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
|
|
@ -93,14 +134,15 @@ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN
|
|||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/subcommands v1.2.0 h1:vWQspBTo2nEqTUFita5/KeEWlUL8kQObDFbub/EN9oE=
|
||||
github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/wire v0.6.0 h1:HBkoIh4BdSxoyo9PveV8giw7ZsaBOvzWKfcg/6MrVwI=
|
||||
github.com/google/wire v0.6.0/go.mod h1:F4QhpQ9EDIdJ1Mbop/NZBRB+5yrR6qg3BnctaoUk6NA=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00 h1:l5lAOZEym3oK3SQ2HBHWsJUfbNBiTXJDeW2QDxw9AQ0=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4=
|
||||
github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q=
|
||||
|
|
@ -110,16 +152,19 @@ github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0U
|
|||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/grokify/html-strip-tags-go v0.0.1 h1:0fThFwLbW7P/kOiTBs03FsJSV9RM2M/Q/MOnCQxKMo0=
|
||||
github.com/grokify/html-strip-tags-go v0.0.1/go.mod h1:2Su6romC5/1VXOQMaWL2yb618ARB8iVo6/DR99A6d78=
|
||||
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
|
||||
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
|
||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
||||
github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
|
|
@ -128,6 +173,8 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm
|
|||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
|
||||
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
|
|
@ -135,15 +182,20 @@ github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfn
|
|||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
|
||||
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||
github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
|
|
@ -174,22 +226,27 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
|
|||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
|
||||
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/redis/go-redis/v9 v9.1.0 h1:137FnGdk+EQdCbye1FW+qOEcY5S+SpY9T0NiuqvtfMY=
|
||||
github.com/redis/go-redis/v9 v9.1.0/go.mod h1:urWj3He21Dj5k4TK1y59xH8Uj6ATueP8AH1cY3lZl4c=
|
||||
github.com/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ=
|
||||
github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
||||
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
|
||||
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
|
||||
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||
github.com/sirupsen/logrus v1.4.0 h1:yKenngtzGh+cUSSh6GWbxW2abRqhYUSR/t/6+2QqNvE=
|
||||
github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/assertions v1.1.0 h1:MkTeG1DMwsrdH7QtLXy5W+fUxWq+vmb6cLmyJ7aRtF0=
|
||||
github.com/smartystreets/assertions v1.1.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo=
|
||||
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
|
|
@ -197,6 +254,9 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
|
|||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/tidwall/gjson v1.13.0 h1:3TFY9yxOQShrvmjdM76K+jc66zJeT6D3/VFFYCGQf7M=
|
||||
|
|
@ -205,6 +265,18 @@ github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
|||
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
|
||||
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho=
|
||||
github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE=
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/valyala/fasthttp v1.59.0 h1:Qu0qYHfXvPk1mSLNqcFtEk6DpxgA26hy6bmydotDpRI=
|
||||
github.com/valyala/fasthttp v1.59.0/go.mod h1:GTxNb9Bc6r2a9D0TWNSPwDz78UxnTGBViY3xZNEqyYU=
|
||||
github.com/wechatpay-apiv3/wechatpay-go v0.2.20 h1:gS8oFn1bHGnyapR2Zb4aqTV6l4kJWgbtqjCq6k1L9DQ=
|
||||
github.com/wechatpay-apiv3/wechatpay-go v0.2.20/go.mod h1:A254AUBVB6R+EqQFo3yTgeh7HtyqRRtN2w9hQSOrd4Q=
|
||||
github.com/wechatpay-apiv3/wechatpay-go v0.2.21 h1:uIyMpzvcaHA33W/QPtHstccw+X52HO1gFdvVL9O6Lfs=
|
||||
github.com/wechatpay-apiv3/wechatpay-go v0.2.21/go.mod h1:A254AUBVB6R+EqQFo3yTgeh7HtyqRRtN2w9hQSOrd4Q=
|
||||
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
|
||||
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
go.opentelemetry.io/otel v1.0.0/go.mod h1:AjRVh9A5/5DE7S+mZtTR6t8vpKKryam+0lREnfmS4cg=
|
||||
|
|
@ -235,13 +307,20 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
|
|||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
||||
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
|
||||
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
|
||||
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20221208152030-732eee02a75a h1:4iLhBPcpqFmylhnkbY3W0ONLUYYkDAW9xMFLfxgsvCw=
|
||||
golang.org/x/exp v0.0.0-20221208152030-732eee02a75a/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20241112194109-818c5a804067 h1:adDmSQyFTCiv19j015EGKJBoaa7ElV0Q1Wovb/4G7NA=
|
||||
golang.org/x/lint v0.0.0-20241112194109-818c5a804067/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
|
|
@ -250,13 +329,17 @@ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91
|
|||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
|
||||
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
|
||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||
|
|
@ -266,17 +349,22 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
|||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
||||
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
|
||||
golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI=
|
||||
golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs=
|
||||
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
|
||||
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
|
||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
|
||||
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
|
|
@ -301,14 +389,17 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
|
||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
||||
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
|
||||
golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
|
||||
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
|
|
@ -317,12 +408,18 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
|||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
||||
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
|
|
@ -333,17 +430,31 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
|
|||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||
golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps=
|
||||
golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||
golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 h1:0+ozOGcrp+Y8Aq8TLNN2Aliibms5LEzsq99ZZmAGYm0=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20231212172506-995d672761c0 h1:YJ5pD9rF8o9Qtta0Cmy9rdBwkSjrTCT6XTiUQVOtIos=
|
||||
google.golang.org/genproto v0.0.0-20231212172506-995d672761c0/go.mod h1:l/k7rMz0vFTBPy+tFSGvXEd3z+BcoG1k7EHbqm+YBsY=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917 h1:rcS6EyEaoCO52hQDupoSfrxI3R6C2Tq741is7X8OvnM=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917/go.mod h1:CmlNWB9lSezaYELKS5Ym1r44VrrbPUa7JTvw+6MbpJ0=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094/go.mod h1:fJ/e3If/Q67Mj99hin0hMhiNyCRmt6BQ2aWIJshUSJw=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 h1:BwIjyKYGsK9dMCBOorzRri8MQwmi7mT9rGHsCEinZkA=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917 h1:6G8oQ016D88m1xAKljMlBOOGWDZkes4kMhgGFlf8WcQ=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917/go.mod h1:xtjpI3tXFPP051KaWnhvxkiubL/6dJ18vLVf7q2pTOU=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY=
|
||||
google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.61.1 h1:kLAiWrZs7YeDM6MumDe7m3y4aM6wacLzM1Y/wiLP9XY=
|
||||
google.golang.org/grpc v1.61.1/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs=
|
||||
google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
|
|
@ -361,8 +472,8 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntN
|
|||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/ini.v1 v1.42.0 h1:7N3gPTt50s8GuLortA00n8AqRTk75qOP98+mTPpgzRk=
|
||||
gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.56.0 h1:DPMeDvGTM54DXbPkVIZsp19fp/I2K7zwA/itHYHKo8Y=
|
||||
gopkg.in/ini.v1 v1.56.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
|
||||
|
|
@ -379,9 +490,17 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
|||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gorm.io/driver/mysql v1.5.7 h1:MndhOPYOfEp2rHKgkZIhJ16eVUIRf2HmzgoPmh7FCWo=
|
||||
gorm.io/driver/mysql v1.5.7/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM=
|
||||
gorm.io/driver/sqlite v1.5.0 h1:zKYbzRCpBrT1bNijRnxLDJWPjVfImGEn0lSnUY5gZ+c=
|
||||
gorm.io/driver/sqlite v1.5.0/go.mod h1:kDMDfntV9u/vuMmz8APHtHF0b4nyBB7sfCieC6G8k8I=
|
||||
gorm.io/gorm v1.24.7-0.20230306060331-85eaf9eeda11/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
|
||||
gorm.io/gorm v1.25.0/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
|
||||
gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
|
||||
gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8=
|
||||
gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ=
|
||||
gorm.io/hints v1.1.2 h1:b5j0kwk5p4+3BtDtYqqfY+ATSxjj+6ptPgVveuynn9o=
|
||||
gorm.io/hints v1.1.2/go.mod h1:/ARdpUHAtyEMCh5NNi3tI7FsGh+Cj/MIUlvNxCNCFWg=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
stathat.com/c/consistent v1.0.0 h1:ezyc51EGcRPJUxfHGSgJjWzJdj3NiMU9pNfLNGiXV0c=
|
||||
|
|
|
|||
31
gorm.sh
31
gorm.sh
|
|
@ -101,7 +101,7 @@ echo "领域实体Bo代码生成完成:${bo_file}"
|
|||
# 生成 CRUD 操作代码部分
|
||||
echo "正在生成 CRUD 操作代码..."
|
||||
|
||||
repo_file="${repo_path}/${table}_repo.go"
|
||||
repo_file="${repo_path}/${table}.go"
|
||||
mkdir -p "${repo_path}"
|
||||
cat > "${repo_file}" <<EOL
|
||||
package repo
|
||||
|
|
@ -120,7 +120,7 @@ type ${table_capitalized}Repo interface {
|
|||
EOL
|
||||
|
||||
|
||||
crud_file="${repo_impl_path}/${table}_repoImpl.go"
|
||||
crud_file="${repo_impl_path}/${table}.go"
|
||||
mkdir -p "${repo_impl_path}"
|
||||
|
||||
# 使用heredoc方式向文件中写入CRUD操作代码,定义了针对指定表的各种数据库操作方法,包括创建、根据ID获取、更新、删除、列表查询以及按字段查询等功能
|
||||
|
|
@ -130,20 +130,27 @@ package repoimpl
|
|||
import (
|
||||
"context"
|
||||
"voucher/internal/data/model"
|
||||
"voucher/internal/biz/repository"
|
||||
"voucher/internal/biz/repo"
|
||||
"voucher/internal/biz/bo"
|
||||
"voucher/internal/data"
|
||||
err2 "voucher/api/err"
|
||||
)
|
||||
|
||||
// ${table_capitalized}RepoImpl 定义了针对 ${table_capitalized} 表的 CRUD 操作
|
||||
// ${table_capitalized}RepoImpl .
|
||||
type ${table_capitalized}RepoImpl struct {
|
||||
Base[model.${table_capitalized}, bo.${table_capitalized}Bo]
|
||||
db *data.Db
|
||||
}
|
||||
|
||||
// New${table_capitalized}RepoImpl 创建一个新的 ${table_capitalized}RepoImpl 实例
|
||||
func New${table_capitalized}RepoImpl() repository.${table_capitalized}Repo {
|
||||
// New${table_capitalized}RepoImpl .
|
||||
func New${table_capitalized}RepoImpl() repo.${table_capitalized}Repo {
|
||||
return &${table_capitalized}RepoImpl{}
|
||||
}
|
||||
|
||||
func (r *${table_capitalized}RepoImpl) DB(ctx context.Context) *gorm.DB {
|
||||
return p.db.DB(ctx).WithContext(ctx).Model(model.${table_capitalized}{})
|
||||
}
|
||||
|
||||
func (r *${table_capitalized}RepoImpl) Create(ctx context.Context, req *bo.${table_capitalized}Bo) (*bo.${table_capitalized}Bo, error) {
|
||||
// todo 待实现
|
||||
return nil, nil
|
||||
|
|
@ -152,7 +159,17 @@ func (r *${table_capitalized}RepoImpl) Create(ctx context.Context, req *bo.${tab
|
|||
// GetByID 根据 ID 获取 ${table_capitalized}
|
||||
func (r *${table_capitalized}RepoImpl) GetByID(ctx context.Context,id int32) (*bo.${table_capitalized}Bo, error) {
|
||||
var item model.${table_capitalized}
|
||||
// todo 待实现
|
||||
|
||||
tx := p.DB(ctx).Where(model.${table_capitalized}{ID: id}).First(&item)
|
||||
|
||||
if tx.Error != nil {
|
||||
return nil, fmt.Errorf("b fail %w", tx.Error)
|
||||
}
|
||||
|
||||
if tx.RowsAffected == 0 {
|
||||
return nil, err2.ErrorDbNotFound("数据不存在")
|
||||
}
|
||||
|
||||
return r.ToBo(&item), nil
|
||||
}
|
||||
EOL
|
||||
|
|
|
|||
|
|
@ -0,0 +1,79 @@
|
|||
package biz
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"strings"
|
||||
"voucher/internal/biz/bo"
|
||||
"voucher/internal/biz/vo"
|
||||
"voucher/internal/pkg/lock"
|
||||
)
|
||||
|
||||
func (this *VoucherBiz) alarm(ctx context.Context, order *bo.OrderBo, errMsg string) error {
|
||||
|
||||
// 1小时 内 指定的批次号 发放 发生错误 预警
|
||||
c := vo.OrderConsumeFailAlarmKey.BuildCache([]string{order.ProductNo})
|
||||
|
||||
_, err := this.rdb.Rdb.Get(ctx, c.Key).Result()
|
||||
|
||||
if err == nil {
|
||||
// 缓存存在,直接返回
|
||||
return nil
|
||||
}
|
||||
|
||||
if err != redis.Nil {
|
||||
return fmt.Errorf(fmt.Sprintf("alarm 获取redis缓存%s异常:%v", c.Key, err))
|
||||
}
|
||||
|
||||
cl := vo.OrderConsumeFailAlarmLockKey.BuildCache([]string{order.ProductNo})
|
||||
|
||||
return lock.NewMutex(this.rdb.Rdb, cl.TTL).Lock(ctx, cl.Key, func(ctx context.Context) error {
|
||||
// 二次获取,判定处理,以免获取锁后又执行了一次
|
||||
|
||||
cacheValue, err3 := this.rdb.Rdb.Get(ctx, c.Key).Result()
|
||||
|
||||
if err3 != nil && err3 != redis.Nil {
|
||||
return fmt.Errorf(fmt.Sprintf("alarm 二次获取redis缓存%s异常:%v", c.Key, err))
|
||||
}
|
||||
|
||||
if len(cacheValue) > 0 {
|
||||
return nil // 有直接返回
|
||||
}
|
||||
|
||||
// 通知
|
||||
title := fmt.Sprintf("券发放异常")
|
||||
if err = this.DingMixRepo.SendMarkdownMessage(ctx, title, alarmText(order, errMsg)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = this.rdb.Rdb.Set(ctx, c.Key, order.ProductNo, c.TTL).Err(); err != nil {
|
||||
return fmt.Errorf(fmt.Sprintf("设置redis缓存%s异常:%v", c.Key, err))
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func alarmText(order *bo.OrderBo, errMsg string) string {
|
||||
var card strings.Builder
|
||||
|
||||
// 警示信息(简化版)
|
||||
card.WriteString("⚠️ **券发放异常** ⚠️\n\n")
|
||||
|
||||
// 订单信息(通过引用和代码块模拟小字体)
|
||||
card.WriteString("> **订单信息:**\n\n")
|
||||
card.WriteString(fmt.Sprintf("> AppID: `%s`\n\n", order.AppID))
|
||||
card.WriteString(fmt.Sprintf("> OpenID: `%s`\n\n", order.Account))
|
||||
card.WriteString(fmt.Sprintf("> 批次号: `%s`\n\n", order.BatchNo))
|
||||
card.WriteString(fmt.Sprintf("> 活动号: `%s`\n\n", order.ProductNo))
|
||||
card.WriteString(fmt.Sprintf("> 订单号: `%s`\n\n", order.OrderNo))
|
||||
card.WriteString(fmt.Sprintf("> 招行订单号: `%s`\n", order.OutBizNo))
|
||||
card.WriteString("\n")
|
||||
|
||||
// 报警原因(通过引用和代码块模拟小字体)
|
||||
card.WriteString("> **❗ 报警原因:**\n\n")
|
||||
card.WriteString(fmt.Sprintf("> `%s`\n", errMsg))
|
||||
|
||||
return card.String()
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package bo
|
||||
|
||||
import "voucher/internal/biz/vo"
|
||||
|
||||
type CmbRequestBo struct {
|
||||
FuncName vo.CmbFuncName
|
||||
BizContent string
|
||||
}
|
||||
|
||||
type CmbResponseBo struct {
|
||||
RespCode string
|
||||
RespMsg string
|
||||
BizContent string
|
||||
}
|
||||
|
|
@ -1,18 +1,57 @@
|
|||
package bo
|
||||
|
||||
import "time"
|
||||
import (
|
||||
"time"
|
||||
"voucher/internal/biz/vo"
|
||||
)
|
||||
|
||||
// OrderBo 领域实体Bo结构,字段和模型字段保持一致
|
||||
type OrderBo struct {
|
||||
ID int32
|
||||
OrderNo string
|
||||
ID uint64
|
||||
OrderNo string
|
||||
OutBizNo string
|
||||
VoucherNo string
|
||||
ProductNo string
|
||||
BatchNo string
|
||||
ActivityId string
|
||||
Account string
|
||||
Type vo.OrderType
|
||||
AccountType vo.OrderAccountType
|
||||
Status vo.OrderStatus
|
||||
AppID string
|
||||
MerchantNo string
|
||||
NotifyUrl string
|
||||
Channel vo.Channel
|
||||
Attach string
|
||||
Remark string
|
||||
TransactionId string
|
||||
ReceiveSuccessTime *time.Time
|
||||
LastUseTime *time.Time
|
||||
CreateTime *time.Time
|
||||
UpdateTime *time.Time
|
||||
}
|
||||
|
||||
type OrderCreateReqBo struct {
|
||||
OutBizNo string
|
||||
ProductNo string
|
||||
Account string
|
||||
AccountType bool
|
||||
Status bool
|
||||
AppID string
|
||||
MerchantNo string
|
||||
Channel bool
|
||||
CreateTime time.Time
|
||||
Type vo.OrderType
|
||||
AccountType vo.OrderAccountType
|
||||
NotifyUrl string
|
||||
Attach string
|
||||
}
|
||||
|
||||
type FindInBatchesUseBo struct {
|
||||
StartTime *time.Time
|
||||
EndTime *time.Time
|
||||
}
|
||||
|
||||
type FindInBatchesBo struct {
|
||||
StartTime string `json:"start_time"`
|
||||
EndTime string `json:"end_time"`
|
||||
ProductNo string `json:"product_no"`
|
||||
OrderNos []string `json:"order_nos"`
|
||||
OutBizNos []string `json:"out_biz_nos"`
|
||||
VoucherNos []string `json:"voucher_nos"`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
package bo
|
||||
|
||||
import (
|
||||
"time"
|
||||
"voucher/internal/biz/vo"
|
||||
)
|
||||
|
||||
// OrderNotifyBo 领域实体Bo结构,字段和模型字段保持一致
|
||||
type OrderNotifyBo struct {
|
||||
ID uint64
|
||||
OrderNo string
|
||||
Status vo.OrderNotifyStatus
|
||||
Request string
|
||||
Event vo.OrderNotifyEvent
|
||||
Channel vo.Channel
|
||||
Type vo.OrderType
|
||||
Responses string
|
||||
Remark string
|
||||
NotifyUrl string
|
||||
CreateTime *time.Time
|
||||
UpdateTime *time.Time
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
package bo
|
||||
|
||||
import (
|
||||
"time"
|
||||
"voucher/internal/biz/vo"
|
||||
)
|
||||
|
||||
// ProductBo 领域实体Bo结构,字段和模型字段保持一致
|
||||
type ProductBo struct {
|
||||
ID int32
|
||||
Name string
|
||||
ProductNo string
|
||||
BatchName string
|
||||
BatchNo string
|
||||
ActivityId string
|
||||
MchId string
|
||||
Channel vo.Channel
|
||||
AvailableType vo.AvailableType
|
||||
AvailableDays uint32
|
||||
Amount int64
|
||||
AllBudget int64
|
||||
AvailableBudget int64
|
||||
WarningBudget int64
|
||||
WarningPerson string
|
||||
StartTime *time.Time
|
||||
EndTime *time.Time
|
||||
CreateTime *time.Time
|
||||
UpdateTime *time.Time
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package bo
|
||||
|
||||
import (
|
||||
"time"
|
||||
"voucher/internal/biz/vo"
|
||||
)
|
||||
|
||||
// WechatNotifyRegisterTagBo 领域实体Bo结构,字段和模型字段保持一致
|
||||
type WechatNotifyRegisterTagBo struct {
|
||||
ID int32
|
||||
StockID string
|
||||
StockCreatorMchID string
|
||||
Tag string
|
||||
Status vo.WechatNotifyRegisterTagStatus
|
||||
Remark string
|
||||
CreateTime *time.Time
|
||||
UpdateTime *time.Time
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
package bo
|
||||
|
||||
import "voucher/internal/biz/vo"
|
||||
|
||||
// ConsumeInformation 定义消费信息结构体
|
||||
type ConsumeInformation struct {
|
||||
ConsumeTime string `json:"consume_time"`
|
||||
ConsumeMchid string `json:"consume_mchid"`
|
||||
TransactionID string `json:"transaction_id"`
|
||||
}
|
||||
|
||||
// PlainText 定义明文数据结构体
|
||||
type PlainText struct {
|
||||
StockCreatorMchid string `json:"stock_creator_mchid"`
|
||||
StockID string `json:"stock_id"`
|
||||
CouponID string `json:"coupon_id"`
|
||||
CouponName string `json:"coupon_name"`
|
||||
Description string `json:"description"`
|
||||
Status vo.WechatVoucherStatus `json:"status"`
|
||||
CreateTime string `json:"create_time"`
|
||||
CouponType string `json:"coupon_type"`
|
||||
NoCash bool `json:"no_cash"`
|
||||
Singleitem bool `json:"singleitem"`
|
||||
ConsumeInformation ConsumeInformation `json:"consume_information,omitempty"`
|
||||
}
|
||||
|
||||
type WechatVoucherNotifyBo struct {
|
||||
ID string `json:"id"`
|
||||
CreateTime string `json:"create_time"`
|
||||
ResourceType string `json:"resource_type"`
|
||||
EventType string `json:"event_type"`
|
||||
Summary string `json:"summary"`
|
||||
OriginalType string `json:"original_type"`
|
||||
AssociatedData string `json:"associated_data"`
|
||||
PlainText PlainText `json:"plain_text"`
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
package cmb
|
||||
|
||||
import (
|
||||
"voucher/internal/biz/mixrepos"
|
||||
"voucher/internal/biz/repo"
|
||||
"voucher/internal/biz/wechatrepo"
|
||||
"voucher/internal/conf"
|
||||
"voucher/internal/data"
|
||||
)
|
||||
|
||||
// Cmb .
|
||||
// @link https://open.cmbchina.com/Platform/#/resource/document/approvalAPI
|
||||
// @link https://open.cmbchina.com/Platform/#/resource/document/guide
|
||||
type Cmb struct {
|
||||
bc *conf.Bootstrap
|
||||
rdb *data.Rdb
|
||||
OrderRepo repo.OrderRepo
|
||||
ProductRepo repo.ProductRepo
|
||||
OrderNotifyRepo repo.OrderNotifyRepo
|
||||
WechatCpnRepo wechatrepo.WechatCpnRepo
|
||||
CmbMixRepo mixrepos.CmbMixRepo
|
||||
}
|
||||
|
||||
func NewCmb(
|
||||
bc *conf.Bootstrap,
|
||||
rdb *data.Rdb,
|
||||
orderRepo repo.OrderRepo,
|
||||
ProductRepo repo.ProductRepo,
|
||||
OrderNotifyRepo repo.OrderNotifyRepo,
|
||||
WechatCpnRepo wechatrepo.WechatCpnRepo,
|
||||
CmbMixRepo mixrepos.CmbMixRepo,
|
||||
) *Cmb {
|
||||
return &Cmb{
|
||||
bc: bc,
|
||||
rdb: rdb,
|
||||
OrderRepo: orderRepo,
|
||||
ProductRepo: ProductRepo,
|
||||
OrderNotifyRepo: OrderNotifyRepo,
|
||||
WechatCpnRepo: WechatCpnRepo,
|
||||
CmbMixRepo: CmbMixRepo,
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,148 @@
|
|||
package cmb
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/go-kratos/kratos/v2/log"
|
||||
"time"
|
||||
err2 "voucher/api/err"
|
||||
v1 "voucher/api/v1"
|
||||
"voucher/internal/biz/bo"
|
||||
"voucher/internal/biz/vo"
|
||||
)
|
||||
|
||||
func (v *Cmb) Notify(ctx context.Context, order *bo.OrderBo) (*bo.OrderNotifyBo, error) {
|
||||
|
||||
if !order.Status.IsCanNotify() {
|
||||
return nil, fmt.Errorf("订单状态不允许通知,orderNo:%s,orderStatusText:%s", order.OrderNo, order.Status.GetText())
|
||||
}
|
||||
|
||||
event, err := order.Status.GetOrderNotifyEvent()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req := &bo.OrderNotifyBo{
|
||||
OrderNo: order.OrderNo,
|
||||
NotifyUrl: order.NotifyUrl,
|
||||
Channel: order.Channel,
|
||||
Event: event,
|
||||
Type: order.Type,
|
||||
}
|
||||
|
||||
request, orderNotify, err := v.notifyCreate(ctx, order, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
reply, err := v.CmbMixRepo.Request(ctx, request, order.NotifyUrl)
|
||||
if err != nil {
|
||||
return orderNotify, v.notifyFail(ctx, orderNotify.ID, err.Error())
|
||||
}
|
||||
|
||||
if err = v.CmbMixRepo.VerifyResponse(ctx, reply); err != nil {
|
||||
log.Errorf("回调通知招行返回验证结果发生错误,rep:%+v error:%s", reply, err.Error())
|
||||
return orderNotify, v.notifyFail(ctx, orderNotify.ID, err.Error())
|
||||
}
|
||||
|
||||
if reply.RespCode == vo.CmbResponseStatusSuccess.GetValue() {
|
||||
|
||||
replyJson, _ := json.Marshal(reply)
|
||||
return orderNotify, v.notifySuccess(ctx, orderNotify.ID, string(replyJson))
|
||||
}
|
||||
|
||||
return orderNotify, v.notifyFail(ctx, orderNotify.ID, reply.RespMsg)
|
||||
}
|
||||
|
||||
func (v *Cmb) bizContent(_ context.Context, order *bo.OrderBo, orderNotify *bo.OrderNotifyBo) (string, error) {
|
||||
|
||||
cmbStatus, err := orderNotify.Event.GetCmbStatusText()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
req := &v1.CmbNotifyRequest{
|
||||
Ticket: orderNotify.OrderNo,
|
||||
Status: cmbStatus.GetValue(),
|
||||
TransDate: "", // 格式yyyy-mm-dd hh:mm:ss.sss
|
||||
OrgNo: v.bc.Cmb.OrgNo,
|
||||
Attach: order.Attach,
|
||||
Ext: "",
|
||||
}
|
||||
|
||||
if cmbStatus == vo.CmbStatusUse {
|
||||
if order.LastUseTime == nil || order.LastUseTime.IsZero() {
|
||||
req.TransDate = time.Now().Format("2006-01-02 15:04:05.000")
|
||||
} else {
|
||||
req.TransDate = order.LastUseTime.Format("2006-01-02 15:04:05.000")
|
||||
}
|
||||
} else {
|
||||
req.TransDate = time.Now().Format("2006-01-02 15:04:05.000")
|
||||
}
|
||||
|
||||
bizJsonBytes, err := json.Marshal(req)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(bizJsonBytes), nil
|
||||
}
|
||||
|
||||
func (v *Cmb) NotifyRequest(ctx context.Context, order *bo.OrderBo, orderNotify *bo.OrderNotifyBo) (*v1.CmbRequest, error) {
|
||||
|
||||
bizContent, err := v.bizContent(ctx, order, orderNotify)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
request, err := v.CmbMixRepo.GetRequest(ctx, &bo.CmbRequestBo{
|
||||
FuncName: vo.CmbNotifyFuncName,
|
||||
BizContent: bizContent,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return request, nil
|
||||
}
|
||||
|
||||
func (v *Cmb) notifyCreate(ctx context.Context, order *bo.OrderBo, req *bo.OrderNotifyBo) (*v1.CmbRequest, *bo.OrderNotifyBo, error) {
|
||||
|
||||
request, err := v.NotifyRequest(ctx, order, req)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
requestBytes, err := json.Marshal(request)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
req.Request = string(requestBytes)
|
||||
|
||||
orderNotify, err := v.OrderNotifyRepo.Create(ctx, req)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return request, orderNotify, err
|
||||
}
|
||||
|
||||
func (v *Cmb) notifySuccess(ctx context.Context, notifyId uint64, responses string) error {
|
||||
|
||||
if len(responses) == 0 {
|
||||
responses = "{}"
|
||||
}
|
||||
|
||||
return v.OrderNotifyRepo.Success(ctx, notifyId, responses)
|
||||
}
|
||||
|
||||
func (v *Cmb) notifyFail(ctx context.Context, notifyId uint64, errMsg string) error {
|
||||
|
||||
if err := v.OrderNotifyRepo.Fail(ctx, notifyId, errMsg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return err2.ErrorNeedRetryNotify(errMsg)
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
package cmb
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"github.com/go-kratos/kratos/v2/log"
|
||||
"voucher/internal/biz/bo"
|
||||
"voucher/internal/biz/vo"
|
||||
)
|
||||
|
||||
func (v *Cmb) NotifyRetryConsume(ctx context.Context, order *bo.OrderBo, orderNotify *bo.OrderNotifyBo) (*bo.OrderNotifyBo, error) {
|
||||
|
||||
req := &bo.OrderNotifyBo{
|
||||
OrderNo: orderNotify.OrderNo,
|
||||
NotifyUrl: order.NotifyUrl,
|
||||
Channel: order.Channel,
|
||||
Event: orderNotify.Event,
|
||||
Type: order.Type,
|
||||
Request: "",
|
||||
}
|
||||
|
||||
request, orderNotify, err := v.notifyCreate(ctx, order, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
reply, err := v.CmbMixRepo.Request(ctx, request, order.NotifyUrl)
|
||||
if err != nil {
|
||||
return orderNotify, v.notifyFail(ctx, orderNotify.ID, err.Error())
|
||||
}
|
||||
|
||||
if err = v.CmbMixRepo.VerifyResponse(ctx, reply); err != nil {
|
||||
log.Errorf("NotifyRetryConsume CmbMixRepo.VerifyResponse error:%s", err.Error())
|
||||
return orderNotify, v.notifyFail(ctx, orderNotify.ID, err.Error())
|
||||
}
|
||||
|
||||
if reply.RespCode != vo.CmbResponseStatusSuccess.GetValue() {
|
||||
return orderNotify, v.notifyFail(ctx, orderNotify.ID, reply.RespMsg)
|
||||
}
|
||||
|
||||
replyJson, _ := json.Marshal(reply)
|
||||
|
||||
return orderNotify, v.notifySuccess(ctx, orderNotify.ID, string(replyJson))
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package cmb
|
||||
|
||||
import (
|
||||
"github.com/google/wire"
|
||||
)
|
||||
|
||||
// ProviderSetCmb is biz providers.
|
||||
var ProviderSetCmb = wire.NewSet(NewCmb)
|
||||
|
|
@ -1 +0,0 @@
|
|||
package consume
|
||||
|
|
@ -1 +0,0 @@
|
|||
package consume
|
||||
|
|
@ -1 +0,0 @@
|
|||
package consume
|
||||
|
|
@ -1 +0,0 @@
|
|||
package consume
|
||||
|
|
@ -0,0 +1,241 @@
|
|||
package biz
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/go-kratos/kratos/v2/log"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"golang.org/x/sync/errgroup"
|
||||
"runtime"
|
||||
"time"
|
||||
"voucher/internal/biz/bo"
|
||||
"voucher/internal/biz/vo"
|
||||
"voucher/internal/pkg/lock"
|
||||
)
|
||||
|
||||
func (this *VoucherBiz) isCanNotice(ctx context.Context) error {
|
||||
|
||||
if this.bc.Cmb.NoticeStartDays == 0 {
|
||||
return errors.New("订单定时通知,noticeStartDays eq 0")
|
||||
}
|
||||
|
||||
if this.bc.Cmb.NoticeEndDays == 0 {
|
||||
return errors.New("订单定时通知,noticeEndDays eq 0")
|
||||
}
|
||||
|
||||
cache := vo.CmbBatchNoticeCacheKey.BuildCache([]string{""})
|
||||
|
||||
_, err := this.rdb.Rdb.Get(ctx, cache.Key).Result()
|
||||
|
||||
if err == nil {
|
||||
return fmt.Errorf("订单定时通知,notice 获取redis缓存存在,已被执行,本台服务不做执行")
|
||||
}
|
||||
|
||||
if err != redis.Nil {
|
||||
return fmt.Errorf(fmt.Sprintf("订单定时通知,notice 获取redis缓存%s异常:%v", cache.Key, err))
|
||||
}
|
||||
|
||||
c := vo.CmbBatchNoticeLockKey.BuildCache([]string{""})
|
||||
|
||||
return lock.NewMutex(this.rdb.Rdb, c.TTL).Lock(ctx, c.Key, func(ctx context.Context) error {
|
||||
|
||||
// 二次获取,判定处理,以免获取锁后又执行了一次
|
||||
|
||||
cacheValue, err2 := this.rdb.Rdb.Get(ctx, cache.Key).Result()
|
||||
|
||||
if err2 != nil && err2 != redis.Nil {
|
||||
return fmt.Errorf(fmt.Sprintf("订单定时通知,notice 二次获取redis缓存%s异常:%v", cache.Key, err2))
|
||||
}
|
||||
|
||||
if len(cacheValue) > 0 {
|
||||
return fmt.Errorf("订单定时通知,notice 二次获取redis缓存存在,已被执行,本台服务不做执行")
|
||||
}
|
||||
|
||||
if err = this.rdb.Rdb.Set(ctx, cache.Key, fmt.Sprintf("%d_%d", this.bc.Cmb.NoticeStartDays, this.bc.Cmb.NoticeEndDays), c.TTL).Err(); err != nil {
|
||||
return fmt.Errorf(fmt.Sprintf("notice 设置redis缓存%s异常:%v", cache.Key, err))
|
||||
}
|
||||
|
||||
log.Warnf("订单定时通知,notice 获取redis缓存,不存在,开始处理")
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (this *VoucherBiz) Notice(ctx context.Context) error {
|
||||
|
||||
if err := this.isCanNotice(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
|
||||
// 获取 NoticeStartDays 天前的日期
|
||||
noticeStartDay := now.AddDate(0, 0, int(-this.bc.Cmb.NoticeStartDays))
|
||||
// 获取 NoticeStartDays 天前 00:00:00 的时间
|
||||
startTime := time.Date(noticeStartDay.Year(), noticeStartDay.Month(), noticeStartDay.Day(), 0, 0, 0, 0, noticeStartDay.Location())
|
||||
|
||||
// 获取 NoticeEndDays 天前的日期
|
||||
noticeEndDay := now.AddDate(0, 0, int(-this.bc.Cmb.NoticeEndDays))
|
||||
// 获取 NoticeEndDays 天 23:59:59 的时间
|
||||
endTime := time.Date(noticeEndDay.Year(), noticeEndDay.Month(), noticeEndDay.Day(), 23, 59, 59, 0, noticeEndDay.Location())
|
||||
|
||||
return this.timeSliceQuery(ctx, startTime, endTime)
|
||||
}
|
||||
|
||||
func (this *VoucherBiz) timeSliceQuery(ctx context.Context, startTime, endTime time.Time) error {
|
||||
|
||||
log.Warnf("订单定时通知,开始处理,按每两个小时分片处理,范围:%s到%s", startTime.Format(time.DateTime), endTime.Format(time.DateTime))
|
||||
|
||||
duration := 2 * time.Hour
|
||||
|
||||
eg := new(errgroup.Group)
|
||||
eg.SetLimit(10)
|
||||
|
||||
for start := startTime; start.Before(endTime); start = start.Add(duration) {
|
||||
|
||||
end := start.Add(duration) // 计算每次请求的结束时间
|
||||
if end.After(endTime) {
|
||||
end = endTime
|
||||
} else {
|
||||
end = end.Add(-1 * time.Second)
|
||||
}
|
||||
|
||||
eg.Go(func() error {
|
||||
|
||||
req := &bo.FindInBatchesUseBo{
|
||||
StartTime: &start,
|
||||
EndTime: &end,
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
_, file, line, _ := runtime.Caller(1) // 1 表示获取当前调用者的调用信息
|
||||
log.Errorf("订单定时通知,发生错误:req:%+v,err:%v,file:%s,line:%d", req, err, file, line)
|
||||
}
|
||||
}()
|
||||
|
||||
return this.ExecuteNotice(ctx, req)
|
||||
})
|
||||
}
|
||||
|
||||
return eg.Wait() // 仅返回第一个错误
|
||||
}
|
||||
|
||||
func (this *VoucherBiz) ExecuteNotice(ctx context.Context, req *bo.FindInBatchesUseBo) error {
|
||||
|
||||
start := time.Now()
|
||||
|
||||
num := 0
|
||||
useNum := 0
|
||||
sucNum := 0
|
||||
|
||||
err := this.OrderRepo.FindInBatches(ctx, req, func(ctx context.Context, rows []*bo.OrderBo) error {
|
||||
|
||||
for _, order := range rows {
|
||||
|
||||
num += 1
|
||||
if err := this.notice(ctx, order, &useNum, &sucNum); err != nil {
|
||||
log.Errorf("订单定时通知,err:%v", err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
logFields := map[string]interface{}{
|
||||
"sTime": req.StartTime.Format(time.DateTime) + "到" + req.EndTime.Format(time.DateTime),
|
||||
"num": num, // 查询总量
|
||||
"useNum": useNum, // 核销通知数量
|
||||
"sucNum": sucNum, // 重置为成功数量
|
||||
"elapsed": time.Now().Sub(start).String(),
|
||||
}
|
||||
log.Warnf("订单定时通知,%+v", logFields)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (this *VoucherBiz) notice(ctx context.Context, order *bo.OrderBo, useNum, sucNum *int) (respErr error) {
|
||||
// 批量通知不做数据存储,量会很大
|
||||
|
||||
status, err := this.WechatCpnRepo.Query(ctx, order)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if order.Status == status {
|
||||
return nil // 券状态未改变,忽略不处理
|
||||
}
|
||||
|
||||
order.Status = status
|
||||
|
||||
event, err := status.GetOrderNotifyEvent()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
notify := &bo.OrderNotifyBo{
|
||||
OrderNo: order.OrderNo,
|
||||
NotifyUrl: order.NotifyUrl,
|
||||
Channel: order.Channel,
|
||||
Event: event,
|
||||
Type: order.Type,
|
||||
}
|
||||
|
||||
if err = this.request(ctx, order, notify); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = this.UpdateOrderStatus(ctx, order.ID, status); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if event.IsUsed() {
|
||||
*useNum += 1
|
||||
} else if event.IsSendDEd() || event.IsExpired() {
|
||||
*sucNum += 1
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *VoucherBiz) request(ctx context.Context, order *bo.OrderBo, notify *bo.OrderNotifyBo) (respErr error) {
|
||||
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
// 打印堆栈信息
|
||||
stackBuf := make([]byte, 1024)
|
||||
stackSize := runtime.Stack(stackBuf, false)
|
||||
// 获取调用栈信息
|
||||
_, file, line, _ := runtime.Caller(1) // 1 表示获取当前调用者的调用信息
|
||||
respErr = fmt.Errorf("request panic:%v, orderNo:%s, file:%s, line:%d, stack: %s", err, order.OrderNo, file, line, stackBuf[:stackSize])
|
||||
}
|
||||
}()
|
||||
|
||||
if order == nil {
|
||||
return fmt.Errorf("request order is nil")
|
||||
}
|
||||
|
||||
if notify == nil {
|
||||
return fmt.Errorf("notify is nil")
|
||||
}
|
||||
|
||||
if !notify.Event.CanNotify() {
|
||||
return nil // 不可通知,忽略
|
||||
}
|
||||
|
||||
request, err := this.Cmb.NotifyRequest(ctx, order, notify)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if request == nil {
|
||||
return fmt.Errorf("request is nil,orderNo:%s,outBizNo:%s,stockId:%s", order.OrderNo, order.OutBizNo, order.BatchNo)
|
||||
}
|
||||
|
||||
if _, err = this.CmbMixRepo.Request(ctx, request, order.NotifyUrl); err != nil {
|
||||
return fmt.Errorf("orderNo:%s,outBizNo:%s,stockId:%s,err:%s", order.OrderNo, order.OutBizNo, order.BatchNo, err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
package do
|
||||
|
||||
import "time"
|
||||
|
||||
type WxResp struct {
|
||||
Amount int64 // 券面额
|
||||
AllBudget int64 // 总预算
|
||||
AllStock int64 // 总库存
|
||||
UsedStock int64 // 已使用库存
|
||||
UsedBudget int64 // 已使用预算
|
||||
AvailableStock int64 // 可用库存
|
||||
AvailableBudget int64 // 可用预算
|
||||
StockUsageRate float64 // 券使用率
|
||||
StartTime *time.Time
|
||||
EndTime *time.Time
|
||||
}
|
||||
|
||||
type WarningPerson struct {
|
||||
Mobile string `json:"mobile"`
|
||||
Name string `json:"name"`
|
||||
Tag string `json:"tag"`
|
||||
}
|
||||
|
||||
type WarningBudget struct {
|
||||
StockName string // 券名称
|
||||
StockId string // 券ID
|
||||
StockNo string // 券编号
|
||||
Amount int64 // 券面额
|
||||
AllBudget int64 // 总预算
|
||||
AllStock int64 // 总库存
|
||||
UsedStock int64 // 已使用库存
|
||||
UsedBudget int64 // 已使用预算
|
||||
AvailableStock int64 // 可用库存
|
||||
RemainingBudget int64 // 剩余预算
|
||||
StockUsageRate float64 // 券使用率
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package do
|
||||
|
||||
type WechatQuery struct {
|
||||
ProductNo string `json:"product_no"`
|
||||
BatchNo string `json:"batch_no"`
|
||||
StartTime string `json:"start_time"`
|
||||
EndTime string `json:"end_time"`
|
||||
OrderNo string `json:"order_no"`
|
||||
}
|
||||
|
||||
type RdsWechatQuery struct {
|
||||
ProductNo string `json:"product_no"`
|
||||
BatchNo string `json:"batch_no"`
|
||||
StartTime string `json:"start_time"`
|
||||
EndTime string `json:"end_time"`
|
||||
GoNum int `json:"go_num"` // 并发数
|
||||
TimeSliceHours int64 `json:"time_slice_hours"` // 时间片"小时"
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
package mixrepos
|
||||
|
||||
import (
|
||||
"context"
|
||||
v1 "voucher/api/v1"
|
||||
"voucher/internal/biz/bo"
|
||||
)
|
||||
|
||||
type CmbMixRepo interface {
|
||||
// OrderVerify cmb 券订单参数验证
|
||||
OrderVerify(ctx context.Context, req *v1.CmbRequest) (*v1.CmbOrderRequest, error)
|
||||
// QueryVerify cmb 券订单参数验证
|
||||
QueryVerify(ctx context.Context, req *v1.CmbRequest) (*v1.CmbQueryRequest, error)
|
||||
// ProductQueryVerify cmb 券产品查询参数验证
|
||||
ProductQueryVerify(ctx context.Context, req *v1.CmbRequest) (*v1.CmbQueryProductRequest, error)
|
||||
// GetMockRequest cmb 券参数mock构建
|
||||
GetMockRequest(ctx context.Context, bizContent string) (*v1.CmbRequest, error)
|
||||
// GetRequest 我们请求cmb参数构建处理
|
||||
GetRequest(ctx context.Context, reqBo *bo.CmbRequestBo) (*v1.CmbRequest, error)
|
||||
// GetResponse cmb 请求我们响应返回结果处理
|
||||
GetResponse(ctx context.Context, reqBo *bo.CmbResponseBo) (*v1.CmbReply, error)
|
||||
// VerifyResponse cmb 请求响应返回结果验证
|
||||
VerifyResponse(ctx context.Context, req *v1.CmbReply) error
|
||||
// Request cmb 请求
|
||||
Request(ctx context.Context, req *v1.CmbRequest, uri string) (*v1.CmbReply, error)
|
||||
// Decrypt cmb 业务参数 解密
|
||||
Decrypt(ctx context.Context, encryptBody string) (string, error)
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package mixrepos
|
||||
|
||||
import "context"
|
||||
|
||||
type DingMixRepo interface {
|
||||
SendMessage(_ context.Context, title, text string) error
|
||||
SendMarkdownMessage(ctx context.Context, title, text string) error
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package mixrepos
|
||||
|
||||
import "context"
|
||||
|
||||
// GenerateMixRepo interface
|
||||
type GenerateMixRepo interface {
|
||||
GeneratorString(ctx context.Context, uid string) string
|
||||
GeneratorNumber(ctx context.Context, uid string) int64
|
||||
}
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
package thirdrepository
|
||||
package mixrepos
|
||||
|
||||
import (
|
||||
"context"
|
||||
"voucher/internal/pkg/mq"
|
||||
)
|
||||
|
||||
type ThirdMQSend interface {
|
||||
type MQSendMixRepo interface {
|
||||
// SendASync 异步发送消息,出错会自动写日志,errFn 中可以不用再写失败日志,可以处理一些数据纠正的操作等
|
||||
SendASync(ctx context.Context, topicName string, body []byte, errFn func(error), sendOptions ...mq.SendOption) error
|
||||
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package mixrepos
|
||||
|
||||
import "context"
|
||||
|
||||
type SmsMixRepo interface {
|
||||
Send(ctx context.Context, phoneNumbers []string, templateCode string, params map[string]string) error
|
||||
}
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
package biz
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"strconv"
|
||||
errPb "voucher/api/err"
|
||||
"voucher/internal/biz/bo"
|
||||
"voucher/internal/biz/vo"
|
||||
"voucher/internal/pkg/lock"
|
||||
"voucher/internal/pkg/mq"
|
||||
)
|
||||
|
||||
func (this *VoucherBiz) PushNotifyRetryDelayMQ(ctx context.Context, level int, orderNotifyId uint64) error {
|
||||
|
||||
str := strconv.FormatUint(orderNotifyId, 10)
|
||||
|
||||
eventMap := this.bc.RocketMQ.EventMap["notifyRetry"]
|
||||
sendOption := []mq.SendOption{
|
||||
mq.WithSendShardingKeysOption(str),
|
||||
mq.WithOpenTelemetryOption(trace.SpanFromContext(ctx).SpanContext().TraceID().String()),
|
||||
mq.WithSendDelayLevelOption(level),
|
||||
}
|
||||
|
||||
if err := this.MqSendMixRepo.SendSync(ctx, eventMap.Topic, []byte("{}"), sendOption...); err != nil {
|
||||
return fmt.Errorf("回调通知延迟队列,投递消息出错:err=%s", err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *VoucherBiz) NotifyRetryConsume(ctx context.Context, orderNotifyId uint64) error {
|
||||
|
||||
var (
|
||||
err error
|
||||
orderNotify *bo.OrderNotifyBo
|
||||
c = vo.NotifyRetryConsume.BuildCacheUint64([]uint64{orderNotifyId})
|
||||
)
|
||||
|
||||
err = lock.NewMutex(this.rdb.Rdb, c.TTL).Lock(ctx, c.Key, func(ctx context.Context) error {
|
||||
|
||||
orderNotify, err = this.OrderNotifyRepo.GetByID(ctx, orderNotifyId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
order, err2 := this.OrderRepo.GetByOrderNo(ctx, orderNotify.OrderNo)
|
||||
if err2 != nil {
|
||||
return err2
|
||||
}
|
||||
|
||||
if order.Type.IsCmb() {
|
||||
|
||||
orderNotify, err2 = this.Cmb.NotifyRetryConsume(ctx, order, orderNotify)
|
||||
return err2
|
||||
}
|
||||
|
||||
return fmt.Errorf("订单类型错误:%s", order.Type.GetText())
|
||||
})
|
||||
|
||||
if !errPb.IsNeedRetryNotify(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
level, err2 := this.level(ctx, orderNotify)
|
||||
if err2 != nil {
|
||||
return err2
|
||||
}
|
||||
|
||||
return this.PushNotifyRetryDelayMQ(ctx, level, orderNotify.ID)
|
||||
}
|
||||
|
||||
func (this *VoucherBiz) level(ctx context.Context, orderNotify *bo.OrderNotifyBo) (int, error) {
|
||||
// 状态回调接口失败需要支持重试 重试间隔为1分钟、2分钟、12分钟、60分钟、360分钟
|
||||
|
||||
count, err := this.OrderNotifyRepo.GetCountByOrderNoAndEvent(ctx, orderNotify.OrderNo, orderNotify.Event)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
switch count {
|
||||
case 1:
|
||||
return 60, nil
|
||||
case 2:
|
||||
return 120, nil
|
||||
case 3:
|
||||
return 720, nil
|
||||
case 4:
|
||||
return 3600, nil
|
||||
case 5:
|
||||
return 21600, nil
|
||||
}
|
||||
|
||||
return 0, fmt.Errorf("回调通知失败次数超过5次,不再重试")
|
||||
}
|
||||
|
|
@ -0,0 +1,168 @@
|
|||
package biz
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
err2 "voucher/api/err"
|
||||
"voucher/internal/biz/bo"
|
||||
"voucher/internal/biz/vo"
|
||||
)
|
||||
|
||||
func (this *VoucherBiz) CmbOrder(ctx context.Context, req *bo.OrderCreateReqBo) (orderNo string, err error) {
|
||||
|
||||
order, err3 := this.GetByOutBizNo(ctx, req)
|
||||
if err3 != nil {
|
||||
return "", err3
|
||||
}
|
||||
|
||||
if order != nil {
|
||||
|
||||
if order.Status.IsFail() || order.Status.IsIng() {
|
||||
|
||||
if err4 := this.orderRetry(ctx, order); err4 != nil {
|
||||
return "", err4
|
||||
}
|
||||
}
|
||||
|
||||
return order.OrderNo, err
|
||||
}
|
||||
|
||||
product, err3 := this.ProductRepo.GetByProductNo(ctx, req.ProductNo)
|
||||
if err3 != nil {
|
||||
return "", err3
|
||||
}
|
||||
|
||||
order, err3 = this.order(ctx, req, product)
|
||||
if err3 != nil {
|
||||
return "", err3
|
||||
}
|
||||
|
||||
return order.OrderNo, nil
|
||||
}
|
||||
|
||||
func (this *VoucherBiz) order(ctx context.Context, req *bo.OrderCreateReqBo, product *bo.ProductBo) (*bo.OrderBo, error) {
|
||||
|
||||
order, err := this.create(ctx, req, product)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
voucherNo, err := this.send(ctx, order)
|
||||
if err != nil {
|
||||
if err3 := this.fail(ctx, order, err); err3 != nil {
|
||||
return nil, err3
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = this.success(ctx, order, voucherNo); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return order, nil
|
||||
}
|
||||
|
||||
func (this *VoucherBiz) send(ctx context.Context, order *bo.OrderBo) (string, error) {
|
||||
|
||||
if order.ActivityId == "" {
|
||||
return this.WechatCpnRepo.Order(ctx, order)
|
||||
}
|
||||
|
||||
return this.BankMultiActivityRepo.Order(order)
|
||||
}
|
||||
|
||||
func (this *VoucherBiz) orderRetry(ctx context.Context, order *bo.OrderBo) error {
|
||||
|
||||
voucherNo, err := this.send(ctx, order)
|
||||
|
||||
if err != nil {
|
||||
if err3 := this.fail(ctx, order, err); err3 != nil {
|
||||
return err3
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
return this.success(ctx, order, voucherNo)
|
||||
}
|
||||
|
||||
func (this *VoucherBiz) create(ctx context.Context, req *bo.OrderCreateReqBo, product *bo.ProductBo) (*bo.OrderBo, error) {
|
||||
|
||||
o := &bo.OrderBo{
|
||||
OrderNo: this.GenerateMixRepo.GeneratorString(ctx, fmt.Sprintf("%d%s", req.Type, req.OutBizNo)),
|
||||
OutBizNo: req.OutBizNo,
|
||||
ProductNo: req.ProductNo,
|
||||
Account: req.Account,
|
||||
AppID: req.AppID,
|
||||
MerchantNo: product.MchId,
|
||||
Channel: product.Channel,
|
||||
BatchNo: product.BatchNo,
|
||||
NotifyUrl: req.NotifyUrl,
|
||||
AccountType: vo.OrderAccountTypeOpenId,
|
||||
Type: req.Type,
|
||||
Status: vo.OrderStatusIng, // 同步发放,状态至为发放中
|
||||
Attach: req.Attach,
|
||||
|
||||
ActivityId: product.ActivityId, // 多笔立减活动
|
||||
}
|
||||
|
||||
return this.OrderRepo.Create(ctx, o)
|
||||
}
|
||||
|
||||
func (this *VoucherBiz) ing(ctx context.Context, id uint64) error {
|
||||
|
||||
return this.OrderRepo.Ing(ctx, id)
|
||||
}
|
||||
|
||||
func (this *VoucherBiz) success(ctx context.Context, order *bo.OrderBo, voucherNo string) error {
|
||||
|
||||
return this.OrderRepo.Success(ctx, order.ID, voucherNo)
|
||||
}
|
||||
|
||||
func (this *VoucherBiz) fail(ctx context.Context, order *bo.OrderBo, errReq error) error {
|
||||
|
||||
errMsg := errReq.Error()
|
||||
|
||||
if err := this.OrderRepo.Fail(ctx, order.ID, errMsg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if errMsg == `Post "https://api.mch.weixin.qq.com/v3/marketing/favor/users/oW97fjrv_ntYBPjMsaaEMRSj6TPA/coupons": EOF` {
|
||||
// 微信:不同微信号,领取了很多次,自然人限领,发放频率限制,微信研发排期,后续这个错误信息应该会更新,近期没那么快同步上去
|
||||
return nil
|
||||
}
|
||||
|
||||
if err2.IsWechatAccountFail(errReq) || err2.IsWechatUserMaxCoupons(errReq) {
|
||||
return nil // 过滤调该类型错误通知
|
||||
}
|
||||
|
||||
return this.alarm(ctx, order, errReq.Error())
|
||||
}
|
||||
|
||||
func (this *VoucherBiz) GetByOutBizNo(ctx context.Context, req *bo.OrderCreateReqBo) (*bo.OrderBo, error) {
|
||||
|
||||
order, err := this.OrderRepo.GetByOutBizNo(ctx, vo.OrderTypeCmb, req.OutBizNo)
|
||||
|
||||
if err != nil && !err2.IsDbNotFound(err) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return order, nil
|
||||
}
|
||||
|
||||
func (this *VoucherBiz) UpdateOrderStatus(ctx context.Context, orderId uint64, status vo.OrderStatus) error {
|
||||
|
||||
if status.IsSuccess() {
|
||||
|
||||
return this.OrderRepo.Available(ctx, orderId)
|
||||
|
||||
} else if status.IsUse() {
|
||||
|
||||
return this.OrderRepo.Used(ctx, orderId)
|
||||
|
||||
} else if status.IsExpired() {
|
||||
|
||||
return this.OrderRepo.Expired(ctx, orderId)
|
||||
}
|
||||
|
||||
return fmt.Errorf("notice 未知券状态,orderId:%d,statuText:%s", orderId, status.GetText())
|
||||
}
|
||||
|
|
@ -0,0 +1,117 @@
|
|||
package biz
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/wechatpay-apiv3/wechatpay-go/services/cashcoupons"
|
||||
"time"
|
||||
v1 "voucher/api/v1"
|
||||
"voucher/internal/biz/do"
|
||||
"voucher/internal/biz/vo"
|
||||
"voucher/internal/pkg/lock"
|
||||
)
|
||||
|
||||
func (this *VoucherBiz) CmbProductQuery(ctx context.Context, productNo string) (reps *v1.CmbQueryProductReply, err error) {
|
||||
|
||||
c := vo.CmbProductQueryLockKey.BuildCache([]string{productNo})
|
||||
|
||||
err = lock.NewMutex(this.rdb.Rdb, c.TTL).Lock(ctx, c.Key, func(ctx context.Context) error {
|
||||
|
||||
product, err3 := this.ProductRepo.GetByProductNo(ctx, productNo)
|
||||
if err3 != nil {
|
||||
return err3
|
||||
}
|
||||
|
||||
if !product.Channel.IsWeChat() {
|
||||
return fmt.Errorf("只支持微信")
|
||||
}
|
||||
|
||||
wechatResp, err4 := this.WechatCpnRepo.QueryProduct(ctx, product.MchId, product.BatchNo)
|
||||
if err4 != nil {
|
||||
return err4
|
||||
}
|
||||
|
||||
reps = &v1.CmbQueryProductReply{
|
||||
RespCode: vo.CmbResponseStatusSuccess.GetValue(),
|
||||
RespMsg: "成功",
|
||||
ActivityName: product.Name,
|
||||
ActivityId: product.ProductNo,
|
||||
Amount: "",
|
||||
MinAmount: "",
|
||||
AvailableType: "",
|
||||
AvailableDays: "", // 动态有效期天数
|
||||
StartTime: "",
|
||||
EndTime: "",
|
||||
AvailableStock: "",
|
||||
Detail: *wechatResp.Description,
|
||||
}
|
||||
|
||||
inputFormat := time.RFC3339
|
||||
|
||||
if wechatResp.AvailableBeginTime != nil {
|
||||
|
||||
availableBeginTime, _ := time.Parse(inputFormat, *wechatResp.AvailableBeginTime)
|
||||
reps.StartTime = availableBeginTime.Format("2006-01-02 15:04:05.000")
|
||||
reps.SaleStartTime = reps.StartTime
|
||||
}
|
||||
|
||||
if wechatResp.AvailableEndTime != nil {
|
||||
availableEndTime, _ := time.Parse(inputFormat, *wechatResp.AvailableEndTime)
|
||||
reps.EndTime = availableEndTime.Format("2006-01-02 15:04:05.000")
|
||||
reps.SaleEndTime = reps.EndTime
|
||||
}
|
||||
|
||||
reps.Amount = fmt.Sprintf("%d", *wechatResp.StockUseRule.FixedNormalCoupon.CouponAmount)
|
||||
reps.MinAmount = fmt.Sprintf("%d", *wechatResp.StockUseRule.FixedNormalCoupon.TransactionMinimum)
|
||||
|
||||
availableStock := *wechatResp.StockUseRule.MaxCoupons - *wechatResp.DistributedCoupons
|
||||
reps.AvailableStock = fmt.Sprintf("%d", availableStock)
|
||||
|
||||
availableType, err3 := product.AvailableType.GetCmbAvailableType()
|
||||
if err3 != nil {
|
||||
return err3
|
||||
}
|
||||
|
||||
reps.AvailableType = availableType.GetValue()
|
||||
reps.AvailableDays = fmt.Sprintf("%d", product.AvailableDays)
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (this *VoucherBiz) WxResp(wxResp *cashcoupons.Stock) (reps *do.WxResp) {
|
||||
|
||||
availableStock := *wxResp.StockUseRule.MaxCoupons - *wxResp.DistributedCoupons
|
||||
couponAmount := *wxResp.StockUseRule.FixedNormalCoupon.CouponAmount / 100
|
||||
|
||||
remainingBudget := availableStock * couponAmount
|
||||
|
||||
stockUsageRate := float64(*wxResp.DistributedCoupons) / float64(*wxResp.StockUseRule.MaxCoupons) * 100
|
||||
|
||||
req := &do.WxResp{
|
||||
Amount: couponAmount,
|
||||
AllBudget: *wxResp.StockUseRule.MaxAmount / 100,
|
||||
AllStock: *wxResp.StockUseRule.MaxCoupons,
|
||||
UsedStock: *wxResp.DistributedCoupons,
|
||||
UsedBudget: *wxResp.DistributedCoupons * couponAmount,
|
||||
AvailableStock: availableStock,
|
||||
AvailableBudget: remainingBudget,
|
||||
StockUsageRate: stockUsageRate,
|
||||
}
|
||||
|
||||
inputFormat := time.RFC3339
|
||||
|
||||
if wxResp.AvailableBeginTime != nil {
|
||||
availableBeginTime, _ := time.Parse(inputFormat, *wxResp.AvailableBeginTime)
|
||||
req.StartTime = &availableBeginTime
|
||||
}
|
||||
|
||||
if wxResp.AvailableEndTime != nil {
|
||||
availableEndTime, _ := time.Parse(inputFormat, *wxResp.AvailableEndTime)
|
||||
req.EndTime = &availableEndTime
|
||||
}
|
||||
|
||||
return req
|
||||
}
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
package biz
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/go-kratos/kratos/v2/log"
|
||||
"time"
|
||||
v1 "voucher/api/v1"
|
||||
"voucher/internal/biz/bo"
|
||||
"voucher/internal/biz/vo"
|
||||
"voucher/internal/pkg/lock"
|
||||
)
|
||||
|
||||
func (this *VoucherBiz) CmbQuery(ctx context.Context, orderNo string) (resp *v1.CmbQueryReply, err error) {
|
||||
|
||||
c := vo.CmbQueryLockKey.BuildCache([]string{orderNo})
|
||||
|
||||
err = lock.NewMutex(this.rdb.Rdb, c.TTL).Lock(ctx, c.Key, func(ctx context.Context) error {
|
||||
|
||||
order, err3 := this.OrderRepo.GetByOrderNo(ctx, orderNo)
|
||||
if err3 != nil {
|
||||
return err3
|
||||
}
|
||||
|
||||
if err = this.Query(ctx, order); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
status, err3 := order.Status.GetCmbStatusText()
|
||||
if err3 != nil {
|
||||
return err3
|
||||
}
|
||||
|
||||
resp = &v1.CmbQueryReply{
|
||||
Ticket: order.OrderNo,
|
||||
Status: status.GetValue(),
|
||||
TransDate: time.Now().Format("20060102150405"),
|
||||
OrgNo: this.bc.Cmb.OrgNo,
|
||||
Ext: "",
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (this *VoucherBiz) Query(ctx context.Context, order *bo.OrderBo) error {
|
||||
|
||||
status, err := this.WechatCpnRepo.Query(ctx, order)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if order.Status == status {
|
||||
log.Warnf("券状态未改变:%s,忽略不处理,orderNo:%s", order.Status.GetText(), order.OrderNo)
|
||||
return nil
|
||||
}
|
||||
|
||||
if err = this.UpdateOrderStatus(ctx, order.ID, status); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
order.Status = status
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *VoucherBiz) QueryOrder(ctx context.Context, orderNo string) (string, error) {
|
||||
|
||||
order, err3 := this.OrderRepo.GetByOrderNo(ctx, orderNo)
|
||||
if err3 != nil {
|
||||
return "", err3
|
||||
}
|
||||
|
||||
status, err := this.WechatCpnRepo.Query(ctx, order)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return fmt.Sprintf("orderNo:%s,订单状态:%s,微信查询返回状态:%s", orderNo, order.Status.GetText(), status.GetText()), nil
|
||||
}
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
package biz
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
err2 "voucher/api/err"
|
||||
"voucher/internal/biz/bo"
|
||||
"voucher/internal/biz/vo"
|
||||
"voucher/internal/pkg/lock"
|
||||
)
|
||||
|
||||
func (this *VoucherBiz) RegisterTag(ctx context.Context, id int32) error {
|
||||
|
||||
stock, err := this.ProductRepo.GetById(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
wxResp, err := this.WechatCpnRepo.QueryProduct(ctx, stock.MchId, stock.BatchNo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = this.ProductRepo.UpdateByWxResp(ctx, stock.ID, this.WxResp(wxResp)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = this.registerNotifyTag(ctx, stock.MchId, stock.BatchNo); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = this.ProductRepo.GetByProductNo(ctx, stock.ProductNo)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (this *VoucherBiz) registerNotifyTag(ctx context.Context, stockCreatorMchID, stockID string) error {
|
||||
|
||||
cl := vo.WechatNotifyRegisterTagCacheLockKey.BuildCache([]string{this.bc.WechatNotifyMQ.Tag, stockCreatorMchID, stockID})
|
||||
|
||||
return lock.NewMutex(this.rdb.Rdb, cl.TTL).Lock(ctx, cl.Key, func(ctx context.Context) error {
|
||||
|
||||
wechatNotifyTag, err3 := this.WechatNotifyRegisterTagRepo.GetByStockIdAndMchId(ctx, stockCreatorMchID, stockID)
|
||||
if err3 != nil && !err2.IsDbNotFound(err3) {
|
||||
return err3
|
||||
}
|
||||
|
||||
if wechatNotifyTag != nil {
|
||||
if wechatNotifyTag.Tag != this.bc.WechatNotifyMQ.Tag {
|
||||
return fmt.Errorf("tag不一致,请检查tag配置:%s", wechatNotifyTag.Tag)
|
||||
}
|
||||
|
||||
if wechatNotifyTag.Status.IsSuccess() {
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
wechatNotifyTag, err3 = this.createRegisterTag(ctx, stockCreatorMchID, stockID)
|
||||
if err3 != nil {
|
||||
return err3
|
||||
}
|
||||
}
|
||||
|
||||
if err := this.WechatCpnRepo.RegisterNotifyTag(ctx, stockID); err != nil {
|
||||
return this.WechatNotifyRegisterTagRepo.Fail(ctx, wechatNotifyTag.ID, err.Error())
|
||||
}
|
||||
|
||||
return this.WechatNotifyRegisterTagRepo.Success(ctx, wechatNotifyTag.ID)
|
||||
})
|
||||
}
|
||||
|
||||
func (this *VoucherBiz) createRegisterTag(ctx context.Context, stockCreatorMchID, stockID string) (*bo.WechatNotifyRegisterTagBo, error) {
|
||||
return this.WechatNotifyRegisterTagRepo.Create(ctx, &bo.WechatNotifyRegisterTagBo{
|
||||
StockID: stockID,
|
||||
StockCreatorMchID: stockCreatorMchID,
|
||||
Tag: this.bc.WechatNotifyMQ.Tag,
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
package repo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"voucher/internal/biz/bo"
|
||||
"voucher/internal/biz/do"
|
||||
"voucher/internal/biz/vo"
|
||||
)
|
||||
|
||||
type OrderRepo interface {
|
||||
SpecifyFindInBatches(ctx context.Context, w *bo.FindInBatchesBo, fun func(ctx context.Context, rows []*bo.OrderBo) error) error
|
||||
FinSucByStockIdInBatches(ctx context.Context, req *do.WechatQuery, fun func(ctx context.Context, rows []*bo.OrderBo) error) error
|
||||
FinFailByStockIdInBatches(ctx context.Context, batchNo string, fun func(ctx context.Context, rows []*bo.OrderBo) error) error
|
||||
FindIngInBatches(ctx context.Context, fun func(ctx context.Context, rows []*bo.OrderBo) error) error
|
||||
FindInBatches(ctx context.Context, w *bo.FindInBatchesUseBo, fun func(ctx context.Context, rows []*bo.OrderBo) error) error
|
||||
GetByOutBizNo(ctx context.Context, t vo.OrderType, outBizNo string) (*bo.OrderBo, error)
|
||||
GetByOrderNo(ctx context.Context, orderNo string) (*bo.OrderBo, error)
|
||||
GetByCouponId(ctx context.Context, merchantNo, batchNo, voucherNo string) (*bo.OrderBo, error)
|
||||
GetByTransactionId(ctx context.Context, stockCreatorMchId, stockID, transactionId string) (*bo.OrderBo, error)
|
||||
Create(ctx context.Context, req *bo.OrderBo) (*bo.OrderBo, error)
|
||||
GetByID(ctx context.Context, id uint64) (*bo.OrderBo, error)
|
||||
Ing(ctx context.Context, id uint64) error
|
||||
Success(ctx context.Context, id uint64, voucherNo string) error
|
||||
Fail(ctx context.Context, id uint64, remark string) error
|
||||
Used(ctx context.Context, id uint64) error
|
||||
NotifyUsed(ctx context.Context, id uint64, transactionId string) error
|
||||
Available(ctx context.Context, id uint64) error
|
||||
Expired(ctx context.Context, id uint64) error
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
package repo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"voucher/internal/biz/bo"
|
||||
)
|
||||
|
||||
type OrderBakRepo interface {
|
||||
SpecifyFindInBatches(ctx context.Context, w *bo.FindInBatchesBo, fun func(ctx context.Context, rows []*bo.OrderBo) error) error
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package repo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"voucher/internal/biz/bo"
|
||||
"voucher/internal/biz/vo"
|
||||
)
|
||||
|
||||
type OrderNotifyRepo interface {
|
||||
GetByID(ctx context.Context, id uint64) (*bo.OrderNotifyBo, error)
|
||||
GetCountByOrderNoAndEvent(ctx context.Context, orderNo string, event vo.OrderNotifyEvent) (int64, error)
|
||||
Create(ctx context.Context, req *bo.OrderNotifyBo) (*bo.OrderNotifyBo, error)
|
||||
Success(ctx context.Context, id uint64, responses string) error
|
||||
Fail(ctx context.Context, id uint64, remark string) error
|
||||
}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
package repo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"voucher/internal/biz/bo"
|
||||
)
|
||||
|
||||
type OrderRepo interface {
|
||||
// Create 创建 Order
|
||||
Create(ctx context.Context, req *bo.OrderBo) (*bo.OrderBo, error)
|
||||
// GetByID 根据 ID 获取 Order
|
||||
GetByID(ctx context.Context, id int32) (*bo.OrderBo, error)
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package repo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"voucher/internal/biz/bo"
|
||||
"voucher/internal/biz/do"
|
||||
)
|
||||
|
||||
type ProductRepo interface {
|
||||
GetById(ctx context.Context, id int32) (*bo.ProductBo, error)
|
||||
FindWarningBudget(ctx context.Context, fun func(ctx context.Context, rows []*bo.ProductBo) error) error
|
||||
GetByBatchNo(ctx context.Context, batchNo string) (*bo.ProductBo, error)
|
||||
GetByProductNo(ctx context.Context, productNo string) (*bo.ProductBo, error)
|
||||
UpdateByWxResp(ctx context.Context, id int32, req *do.WxResp) error
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
package repo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"voucher/internal/biz/bo"
|
||||
)
|
||||
|
||||
type WechatNotifyRegisterTagRepo interface {
|
||||
GetByStockIdAndMchId(ctx context.Context, stockCreatorMchID, stockId string) (*bo.WechatNotifyRegisterTagBo, error)
|
||||
Create(ctx context.Context, req *bo.WechatNotifyRegisterTagBo) (*bo.WechatNotifyRegisterTagBo, error)
|
||||
Success(ctx context.Context, id int32) error
|
||||
Fail(ctx context.Context, id int32, remark string) error
|
||||
}
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
package repository
|
||||
|
||||
type OrderRepo interface {
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
package biz
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
func (this *VoucherBiz) OrderRetry(ctx context.Context, outBizNos []string) error {
|
||||
|
||||
return nil
|
||||
|
||||
//if len(outBizNos) > 0 {
|
||||
//
|
||||
// for _, outBizNo := range outBizNos {
|
||||
//
|
||||
// order, err := this.OrderRepo.GetByOutBizNo(ctx, vo.OrderTypeCmb, outBizNo)
|
||||
// if err != nil {
|
||||
// return fmt.Errorf(fmt.Sprintf("获取订单%s异常:%v", outBizNo, err))
|
||||
// }
|
||||
//
|
||||
// if !order.Status.IsIng() {
|
||||
// return fmt.Errorf(fmt.Sprintf("订单%s状态异常:%s", order.OrderNo, order.Status))
|
||||
// }
|
||||
//
|
||||
// if err4 := this.orderRetry(ctx, order); err4 != nil {
|
||||
// return err4
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return nil
|
||||
//}
|
||||
//
|
||||
//return this.OrderRepo.FindIngInBatches(ctx, func(ctx context.Context, rows []*bo.OrderBo) error {
|
||||
//
|
||||
// for _, order := range rows {
|
||||
//
|
||||
// if err4 := this.orderRetry(ctx, order); err4 != nil {
|
||||
// return err4
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return nil
|
||||
//})
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,122 @@
|
|||
package biz
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/go-kratos/kratos/v2/log"
|
||||
"github.com/go-kratos/kratos/v2/transport/http"
|
||||
"time"
|
||||
"voucher/internal/biz/bo"
|
||||
)
|
||||
|
||||
func (this *VoucherBiz) PushRetryNotify(ctx http.Context, req *bo.FindInBatchesBo) error {
|
||||
|
||||
queue := this.bc.RdsMQ.GetRetryNotify()
|
||||
if queue == nil {
|
||||
return fmt.Errorf("队列不存在")
|
||||
}
|
||||
|
||||
msg, err := json.Marshal(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
strMsg := string(msg)
|
||||
|
||||
_, err = this.rdb.Rdb.RPush(ctx, queue.Name, strMsg).Result()
|
||||
if err != nil {
|
||||
return fmt.Errorf("添加到队列失败:%v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *VoucherBiz) RetryNotify(ctx context.Context, msg string) error {
|
||||
|
||||
var req *bo.FindInBatchesBo
|
||||
|
||||
if err := json.Unmarshal([]byte(msg), &req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := this.RetryNotifyOrder(ctx, req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return this.RetryNotifyOrderBack(ctx, req)
|
||||
}
|
||||
|
||||
func (this *VoucherBiz) RetryNotifyOrder(ctx context.Context, req *bo.FindInBatchesBo) error {
|
||||
|
||||
start := time.Now()
|
||||
startStr := time.Now().String()
|
||||
|
||||
log.Warnf("重试通知处理:%s", startStr)
|
||||
fmt.Printf("重试通知处理:%s", startStr)
|
||||
|
||||
n := 0
|
||||
eNum := 0
|
||||
notifyNum := 0
|
||||
err := this.OrderRepo.SpecifyFindInBatches(ctx, req, func(ctx context.Context, rows []*bo.OrderBo) error {
|
||||
|
||||
n += 1
|
||||
for _, order := range rows {
|
||||
|
||||
eNum += 1
|
||||
if _, err := this.Cmb.Notify(ctx, order); err != nil {
|
||||
log.Errorf("重试通知处理,发生错误,orderNo:%s,outBizNo:%s,voucherNo:%s,err:%v", order.OrderNo, order.OutBizNo, order.VoucherNo, err)
|
||||
} else {
|
||||
notifyNum += 1
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
log.Warnf("重试通知处理,第:%d组,已执行条数:%d,通知成功条数:%d,", n, eNum, notifyNum)
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
endTime := time.Now()
|
||||
log.Warnf("重试通知处理,耗时:%s,结束时间:%s,处理%d组,处理%d单,通知成功条数:%d", endTime.Sub(start).String(), endTime.String(), n, eNum, notifyNum)
|
||||
fmt.Printf("重试通知处理,耗时:%s,结束时间%s,处理%d组,处理%d单,通知成功条数:%d", endTime.Sub(start).String(), endTime.String(), n, eNum, notifyNum)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (this *VoucherBiz) RetryNotifyOrderBack(ctx context.Context, req *bo.FindInBatchesBo) error {
|
||||
|
||||
start := time.Now()
|
||||
startStr := time.Now().String()
|
||||
|
||||
log.Warnf("重试通知处理Bak:%s", startStr)
|
||||
fmt.Printf("重试通知处理Bak:%s", startStr)
|
||||
|
||||
n := 0
|
||||
eNum := 0
|
||||
notifyNum := 0
|
||||
err := this.OrderBakRepo.SpecifyFindInBatches(ctx, req, func(ctx context.Context, rows []*bo.OrderBo) error {
|
||||
|
||||
n += 1
|
||||
for _, order := range rows {
|
||||
|
||||
eNum += 1
|
||||
if _, err := this.Cmb.Notify(ctx, order); err != nil {
|
||||
log.Errorf("重试通知处理Bak,发生错误,orderNo:%s,outBizNo:%s,voucherNo:%s,err:%v", order.OrderNo, order.OutBizNo, order.VoucherNo, err)
|
||||
} else {
|
||||
notifyNum += 1
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
log.Warnf("重试通知处理Bak,第:%d组,已执行条数:%d,通知成功条数:%d,", n, eNum, notifyNum)
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
endTime := time.Now()
|
||||
log.Warnf("重试通知处理Bak,耗时:%s,结束时间:%s,处理%d组,处理%d单,通知成功条数:%d", endTime.Sub(start).String(), endTime.String(), n, eNum, notifyNum)
|
||||
fmt.Printf("重试通知处理Bak,耗时:%s,结束时间%s,处理%d组,处理%d单,通知成功条数:%d", endTime.Sub(start).String(), endTime.String(), n, eNum, notifyNum)
|
||||
|
||||
return err
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
# <p align="center">券状态同步</p>
|
||||
* * *
|
||||
### 主要工作
|
||||
+ 券状态查询同步
|
||||
* * *
|
||||
### 规则说明
|
||||
+ 按照时间分片处理,按照2小时为一个时间片启用一个协程消费处理
|
||||
+ 协程最大可同时运行指定数量,设置为2
|
||||
* * *
|
||||
### 使用方式
|
||||
+ 消费处理,按照时间范围上报消费
|
||||
+ 每次请求按照时间片分别启用2个协程消费处理,也就是每个请求可以同时启用2个协程消费处理
|
||||
+ 请不要无休止的访问,请按照时间片进行访问,并且不要重复的时间片访问,增加系统负载,特殊发券日期量较大,建议缩短时间范围上报,多切分上报处理
|
||||
* * *
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
package timeslicequery
|
||||
|
||||
import (
|
||||
"github.com/nacos-group/nacos-sdk-go/util"
|
||||
"sync"
|
||||
"voucher/internal/biz/cmb"
|
||||
"voucher/internal/biz/mixrepos"
|
||||
"voucher/internal/biz/repo"
|
||||
"voucher/internal/biz/wechatrepo"
|
||||
"voucher/internal/conf"
|
||||
"voucher/internal/data"
|
||||
)
|
||||
|
||||
type Query struct {
|
||||
mu sync.RWMutex
|
||||
queryMap map[string]bool
|
||||
|
||||
bc *conf.Bootstrap
|
||||
rdb *data.Rdb
|
||||
cmb *cmb.Cmb
|
||||
|
||||
productRepo repo.ProductRepo
|
||||
orderRepo repo.OrderRepo
|
||||
|
||||
wechatCpnRepo wechatrepo.WechatCpnRepo
|
||||
mqSendMixRepo mixrepos.MQSendMixRepo
|
||||
}
|
||||
|
||||
func NewQuery(
|
||||
bc *conf.Bootstrap,
|
||||
rdb *data.Rdb,
|
||||
cmb *cmb.Cmb,
|
||||
productRepo repo.ProductRepo,
|
||||
orderRepo repo.OrderRepo,
|
||||
wechatCpnRepo wechatrepo.WechatCpnRepo,
|
||||
mqSendMixRepo mixrepos.MQSendMixRepo) *Query {
|
||||
return &Query{
|
||||
queryMap: make(map[string]bool),
|
||||
bc: bc,
|
||||
rdb: rdb,
|
||||
cmb: cmb,
|
||||
productRepo: productRepo,
|
||||
orderRepo: orderRepo,
|
||||
wechatCpnRepo: wechatCpnRepo,
|
||||
mqSendMixRepo: mqSendMixRepo}
|
||||
}
|
||||
|
||||
func (v *Query) uid(no string) string {
|
||||
return util.Md5("query" + no)
|
||||
}
|
||||
|
||||
func (this *Query) GetAll() map[string]bool {
|
||||
return this.queryMap
|
||||
}
|
||||
|
||||
func (this *Query) Get(uid string) bool {
|
||||
|
||||
this.mu.Lock()
|
||||
defer this.mu.Unlock()
|
||||
|
||||
if _, ok := this.queryMap[uid]; ok {
|
||||
return ok
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (this *Query) Add(uid string) {
|
||||
|
||||
this.mu.Lock()
|
||||
defer this.mu.Unlock()
|
||||
|
||||
this.queryMap[uid] = true
|
||||
}
|
||||
|
||||
func (this *Query) Remove(uid string) {
|
||||
|
||||
this.mu.Lock()
|
||||
defer this.mu.Unlock()
|
||||
|
||||
if _, ok := this.queryMap[uid]; ok {
|
||||
delete(this.queryMap, uid)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
package timeslicequery
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/go-kratos/kratos/v2/log"
|
||||
"time"
|
||||
"voucher/internal/biz/bo"
|
||||
"voucher/internal/biz/do"
|
||||
"voucher/internal/pkg/timeslice"
|
||||
)
|
||||
|
||||
func (v *Query) execute(ctx context.Context, req *timeslice.Manager) error {
|
||||
|
||||
start := time.Now()
|
||||
|
||||
managerStartStr := req.StartTime.Format(time.DateTime)
|
||||
managerEndStr := req.EndTime.Format(time.DateTime)
|
||||
|
||||
log.Warnf("微信券查询,%s到%s,开始", managerStartStr, managerEndStr)
|
||||
fmt.Printf("微信券查询,%s到%s,开始\n", managerStartStr, managerEndStr)
|
||||
|
||||
taskCount, err := timeslice.NewManager(v.callbackFunc).Run(ctx, req)
|
||||
if err != nil {
|
||||
log.Errorf("微信券查询,%s到%s,失败:%v", managerStartStr, managerEndStr, err)
|
||||
}
|
||||
|
||||
elapsed := time.Now().Sub(start).String()
|
||||
|
||||
log.Warnf("微信券查询,%s到%s,总任务数:%d,总耗时:%s", managerStartStr, managerEndStr, taskCount, elapsed)
|
||||
fmt.Printf("微信券查询,%s到%s,总任务数:%d,总耗时:%s\n", managerStartStr, managerEndStr, taskCount, elapsed)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *Query) callbackFunc(ctx context.Context, req *timeslice.Task) error {
|
||||
|
||||
startTimeStr := req.Process.Manager.StartTime.Format(time.DateTime)
|
||||
endTimeStr := req.Process.Manager.EndTime.Format(time.DateTime)
|
||||
|
||||
currentStartTimeStr := req.CurrentStartTime.Format(time.DateTime)
|
||||
currentEndTimeStr := req.CurrentEndTime.Format(time.DateTime)
|
||||
|
||||
start := time.Now()
|
||||
|
||||
bReq := &do.WechatQuery{
|
||||
StartTime: currentStartTimeStr,
|
||||
EndTime: currentEndTimeStr,
|
||||
ProductNo: req.Process.Manager.ProductNo,
|
||||
BatchNo: req.Process.Manager.BatchNo,
|
||||
}
|
||||
|
||||
num := 0
|
||||
errNum := 0
|
||||
useNum := 0
|
||||
|
||||
err := v.orderRepo.FinSucByStockIdInBatches(ctx, bReq, func(ctx context.Context, rows []*bo.OrderBo) error {
|
||||
|
||||
for _, order := range rows {
|
||||
|
||||
num += 1
|
||||
if err := v.wechatQuery(ctx, order, &useNum); err != nil {
|
||||
|
||||
errNum += 1
|
||||
|
||||
logFields := map[string]string{
|
||||
"order_no": order.OrderNo,
|
||||
"coupon_id": order.VoucherNo,
|
||||
"open_id": order.Account,
|
||||
"stock_id": order.BatchNo,
|
||||
"err": err.Error(),
|
||||
}
|
||||
log.Errorf("微信券查询,%s到%s,taskId:%d,错误:%+v", startTimeStr, endTimeStr, req.TaskID, logFields)
|
||||
|
||||
if errNum > 20 {
|
||||
return fmt.Errorf("微信券查询,%s到%s,第%d个任务,已经连续发生20次错误%+v", startTimeStr, endTimeStr, req.TaskID, logFields)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
end := time.Now()
|
||||
|
||||
logFields := map[string]any{
|
||||
"sTime": currentStartTimeStr + "到" + currentEndTimeStr,
|
||||
"num": num,
|
||||
"useNum": useNum,
|
||||
"errNum": errNum,
|
||||
"batchNo": req.Process.Manager.BatchNo,
|
||||
"elapsed": end.Sub(start).String(),
|
||||
}
|
||||
log.Warnf("微信券查询,%s到%s,taskId:%d,处理完毕:%+v", startTimeStr, endTimeStr, req.TaskID, logFields)
|
||||
|
||||
return err
|
||||
}
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
package timeslicequery
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/go-kratos/kratos/v2/log"
|
||||
"github.com/go-kratos/kratos/v2/transport/http"
|
||||
"time"
|
||||
"voucher/internal/biz/do"
|
||||
"voucher/internal/pkg/timeslice"
|
||||
)
|
||||
|
||||
func (v *Query) Push(ctx http.Context, req *do.RdsWechatQuery) (string, error) {
|
||||
|
||||
if req.StartTime == "" || req.EndTime == "" {
|
||||
return "", fmt.Errorf("时间参数不能为空")
|
||||
}
|
||||
|
||||
queue := v.bc.RdsMQ.GetWechatTimeSliceQuery()
|
||||
if queue == nil {
|
||||
return "", fmt.Errorf("队列不存在")
|
||||
}
|
||||
|
||||
if queue.Name == "" {
|
||||
return "", fmt.Errorf("队列不存在")
|
||||
}
|
||||
|
||||
if queue.IsOpen == false {
|
||||
return "", fmt.Errorf("队列未开启")
|
||||
}
|
||||
|
||||
if req.ProductNo != "" {
|
||||
_, err := v.productRepo.GetByProductNo(ctx, req.ProductNo)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
b, err := json.Marshal(req)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
strMsg := string(b)
|
||||
|
||||
uid := v.uid(strMsg)
|
||||
if v.Get(uid) {
|
||||
return "", fmt.Errorf("此台服务队列正在处理中,%s-%s,ip:%s", uid, strMsg, ctx.Header().Get("X-Forwarded-For"))
|
||||
}
|
||||
|
||||
v.Add(uid)
|
||||
|
||||
_, err = v.rdb.Rdb.RPush(ctx, queue.Name, strMsg).Result()
|
||||
if err != nil {
|
||||
v.Remove(uid)
|
||||
return "", fmt.Errorf("添加到队列失败:%v", err)
|
||||
}
|
||||
|
||||
return strMsg, nil
|
||||
}
|
||||
|
||||
func (v *Query) getManager(msg string) (*timeslice.Manager, error) {
|
||||
|
||||
var req *do.RdsWechatQuery
|
||||
|
||||
if err := json.Unmarshal([]byte(msg), &req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if req.StartTime == "" || req.EndTime == "" {
|
||||
return nil, fmt.Errorf("时间参数不能为空")
|
||||
}
|
||||
|
||||
start, err := time.Parse(time.DateTime, req.StartTime)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
end, err := time.Parse(time.DateTime, req.EndTime)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
manager := ×lice.Manager{
|
||||
StartTime: start,
|
||||
EndTime: end,
|
||||
ProductNo: req.ProductNo,
|
||||
BatchNo: req.BatchNo,
|
||||
GoNum: timeslice.DefaultGoNum, // 协程数量
|
||||
TimeSliceHours: timeslice.DefaultTimeSliceHours, // 时间间隔
|
||||
}
|
||||
|
||||
if req.GoNum > 0 {
|
||||
manager.GoNum = req.GoNum
|
||||
}
|
||||
|
||||
if req.TimeSliceHours > 0 {
|
||||
manager.TimeSliceHours = req.TimeSliceHours
|
||||
}
|
||||
|
||||
return manager, nil
|
||||
}
|
||||
|
||||
func (v *Query) Consumer(ctx context.Context, msg string) error {
|
||||
|
||||
defer v.Remove(v.uid(msg))
|
||||
|
||||
req, err := v.getManager(msg)
|
||||
if err != nil {
|
||||
log.Errorf("微信券查询,前置参数处理失败,msg:%s,err:%v", msg, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
if err = v.execute(ctx, req); err != nil {
|
||||
log.Errorf("微信券查询,失败,msg:%s,err:%v", msg, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package timeslicequery
|
||||
|
||||
import (
|
||||
"github.com/google/wire"
|
||||
)
|
||||
|
||||
var ProviderSetTimeSliceQuery = wire.NewSet(NewQuery)
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
package timeslicequery
|
||||
|
||||
import (
|
||||
"context"
|
||||
"voucher/internal/biz/bo"
|
||||
)
|
||||
|
||||
func (v *Query) wechatQuery(ctx context.Context, order *bo.OrderBo, useNum *int) error {
|
||||
|
||||
status, err := v.wechatCpnRepo.Query(ctx, order)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if status.IsUse() {
|
||||
if err = v.queryUsed(ctx, order); err != nil {
|
||||
return err
|
||||
}
|
||||
*useNum += 1
|
||||
} else if status.IsExpired() {
|
||||
return v.queryExpired(ctx, order)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *Query) queryUsed(ctx context.Context, order *bo.OrderBo) error {
|
||||
|
||||
if order.Status.IsUse() {
|
||||
return v.notify(ctx, order)
|
||||
}
|
||||
|
||||
if err := v.orderRepo.Used(ctx, order.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return v.notify(ctx, order)
|
||||
}
|
||||
|
||||
func (v *Query) queryExpired(ctx context.Context, order *bo.OrderBo) error {
|
||||
|
||||
if order.Status.IsExpired() {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := v.orderRepo.Expired(ctx, order.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return v.notify(ctx, order)
|
||||
}
|
||||
|
||||
func (v *Query) notify(ctx context.Context, order *bo.OrderBo) error {
|
||||
|
||||
order, err := v.orderRepo.GetByID(ctx, order.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err = v.cmb.Notify(ctx, order); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
package vo
|
||||
|
||||
import "errors"
|
||||
|
||||
type AvailableType uint8
|
||||
|
||||
const (
|
||||
AvailableTypeFixed AvailableType = iota + 1
|
||||
AvailableTypeDynamic
|
||||
)
|
||||
|
||||
var AvailableTypeMap = map[AvailableType]string{
|
||||
AvailableTypeFixed: "固定有效期",
|
||||
AvailableTypeDynamic: "动态有效期",
|
||||
}
|
||||
|
||||
func (s AvailableType) GetText() string {
|
||||
if t, ok := AvailableTypeMap[s]; ok {
|
||||
return t
|
||||
}
|
||||
return "未知券领取类型"
|
||||
}
|
||||
|
||||
func (s AvailableType) GetValue() uint8 {
|
||||
return uint8(s)
|
||||
}
|
||||
|
||||
func (s AvailableType) IsFixed() bool {
|
||||
return s == AvailableTypeFixed
|
||||
}
|
||||
|
||||
func (s AvailableType) IsDynamic() bool {
|
||||
return s == AvailableTypeDynamic
|
||||
}
|
||||
|
||||
var AvailableCmbTypeMap = map[AvailableType]CmbAvailableType{
|
||||
AvailableTypeFixed: CmbAvailableTypeFixed,
|
||||
AvailableTypeDynamic: CmbAvailableTypeDynamic,
|
||||
}
|
||||
|
||||
func (s AvailableType) GetCmbAvailableType() (CmbAvailableType, error) {
|
||||
if t, ok := AvailableCmbTypeMap[s]; ok {
|
||||
return t, nil
|
||||
}
|
||||
return "", errors.New("未知券领取类型")
|
||||
}
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
package vo
|
||||
|
||||
import (
|
||||
"time"
|
||||
"voucher/internal/pkg/helper"
|
||||
)
|
||||
|
||||
type CacheKey string
|
||||
|
||||
const (
|
||||
CmbOrderLockKey CacheKey = "cmb_order"
|
||||
CmbQueryLockKey CacheKey = "cmb_query"
|
||||
CmbProductQueryLockKey CacheKey = "cmb_product_query"
|
||||
CmbBatchNoticeCacheKey CacheKey = "cmb_batch_notice"
|
||||
CmbBatchNoticeLockKey CacheKey = "cmb_batch_notice_lock"
|
||||
|
||||
NotifyRetryConsume CacheKey = "notify_retry_consume"
|
||||
|
||||
OrderConsumeFailAlarmKey CacheKey = "order_consume_fail_alarm"
|
||||
OrderConsumeFailAlarmLockKey CacheKey = "order_consume_fail_alarm_lock"
|
||||
|
||||
WechatNotifyRegisterTagCacheLockKey CacheKey = "register_tag_lock"
|
||||
|
||||
WechatNotifyConsumeLockKey CacheKey = "wechat_notify_consume"
|
||||
)
|
||||
|
||||
const (
|
||||
ProductQueryKey CacheKey = "product_query"
|
||||
ProductQueryLockKey CacheKey = "product_query_lock"
|
||||
)
|
||||
|
||||
var (
|
||||
WarningBudgetCron CacheKey = "warning_budget_cron"
|
||||
WarningBudgetSendIncr CacheKey = "warning_budget_incr"
|
||||
)
|
||||
|
||||
var CacheKeyMap = map[CacheKey]time.Duration{
|
||||
CmbOrderLockKey: 30 * time.Second,
|
||||
CmbQueryLockKey: 30 * time.Second,
|
||||
CmbProductQueryLockKey: 30 * time.Second,
|
||||
CmbBatchNoticeCacheKey: 21600 * time.Second, // 6小时
|
||||
CmbBatchNoticeLockKey: 300 * time.Second,
|
||||
OrderConsumeFailAlarmKey: 3 * time.Hour, // 3小时
|
||||
OrderConsumeFailAlarmLockKey: 60 * time.Second,
|
||||
NotifyRetryConsume: 60 * time.Second,
|
||||
WechatNotifyRegisterTagCacheLockKey: 60 * time.Second,
|
||||
WechatNotifyConsumeLockKey: 30 * time.Second,
|
||||
|
||||
ProductQueryKey: 30 * 86400 * time.Second, // 30天
|
||||
ProductQueryLockKey: 30 * time.Second,
|
||||
|
||||
WarningBudgetSendIncr: 3 * time.Hour,
|
||||
WarningBudgetCron: 5 * time.Minute,
|
||||
}
|
||||
|
||||
type Cache struct {
|
||||
Key string
|
||||
TTL time.Duration
|
||||
}
|
||||
|
||||
func (s CacheKey) GetValue() string {
|
||||
return string(s)
|
||||
}
|
||||
|
||||
func (s CacheKey) BuildCache(strArr []string) *Cache {
|
||||
|
||||
k := s.GetValue()
|
||||
|
||||
if len(strArr) > 0 {
|
||||
k = helper.BuildStr(k, strArr)
|
||||
}
|
||||
|
||||
c := &Cache{
|
||||
Key: k,
|
||||
}
|
||||
|
||||
ttl, ok := CacheKeyMap[s]
|
||||
if !ok {
|
||||
c.TTL = 30 // 默认30秒
|
||||
}
|
||||
|
||||
c.TTL = ttl
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
func (s CacheKey) BuildCacheUint64(ids []uint64) *Cache {
|
||||
|
||||
k := s.GetValue()
|
||||
|
||||
if len(ids) > 0 {
|
||||
k = helper.BuildStr(k, ids)
|
||||
}
|
||||
|
||||
c := &Cache{
|
||||
Key: k,
|
||||
}
|
||||
|
||||
ttl, ok := CacheKeyMap[s]
|
||||
if !ok {
|
||||
c.TTL = 30 // 默认30秒
|
||||
}
|
||||
|
||||
c.TTL = ttl
|
||||
|
||||
return c
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
package vo
|
||||
|
||||
type Channel uint8
|
||||
|
||||
const (
|
||||
OrderChannelWechat Channel = iota + 1
|
||||
OrderChannelAlipay
|
||||
)
|
||||
|
||||
var OrderChannelMap = map[Channel]string{
|
||||
OrderChannelWechat: "微信",
|
||||
OrderChannelAlipay: "支付宝",
|
||||
}
|
||||
|
||||
func (s Channel) GetText() string {
|
||||
if t, ok := OrderChannelMap[s]; ok {
|
||||
return t
|
||||
}
|
||||
return "未知商品渠道类型"
|
||||
}
|
||||
|
||||
func (s Channel) GetValue() uint8 {
|
||||
return uint8(s)
|
||||
}
|
||||
|
||||
func (s Channel) IsWeChat() bool {
|
||||
return s == OrderChannelWechat
|
||||
}
|
||||
|
||||
func (s Channel) IsAlipay() bool {
|
||||
return s == OrderChannelAlipay
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
package vo
|
||||
|
||||
// CmbFuncName . 招行接口名称
|
||||
type CmbFuncName string
|
||||
|
||||
const (
|
||||
// CmbNotifyFuncName . 券状态回调通知方法
|
||||
CmbNotifyFuncName CmbFuncName = "updateCodeStatus.json"
|
||||
)
|
||||
|
||||
func (s CmbFuncName) GetValue() string {
|
||||
return string(s)
|
||||
}
|
||||
|
||||
// CmbStatus . 券通知状态
|
||||
type CmbStatus string
|
||||
|
||||
const (
|
||||
CmbStatusSuccess CmbStatus = "0" // 券可用
|
||||
CmbStatusUse CmbStatus = "1" // 券使用
|
||||
//CmbStatusExpired CmbStatus = "2" // 券过期-待确认是否通知
|
||||
CmbStatusExpired CmbStatus = "0" // 券过期-等于券没有使用
|
||||
)
|
||||
|
||||
func (s CmbStatus) GetValue() string {
|
||||
return string(s)
|
||||
}
|
||||
|
||||
// CmbResponseStatus . 响应状态
|
||||
type CmbResponseStatus string
|
||||
|
||||
const (
|
||||
CmbResponseStatusSuccess CmbResponseStatus = "1000" // 响应成功
|
||||
CmbResponseStatusFail CmbResponseStatus = "1001" // 响应失败
|
||||
)
|
||||
|
||||
func (s CmbResponseStatus) GetValue() string {
|
||||
return string(s)
|
||||
}
|
||||
|
||||
// CmbAvailableType . 有效期形式,0:固定有效期,1:动态有效期
|
||||
type CmbAvailableType string
|
||||
|
||||
const (
|
||||
CmbAvailableTypeFixed CmbAvailableType = "0" // 固定有效期
|
||||
CmbAvailableTypeDynamic CmbAvailableType = "1" // 动态有效期
|
||||
)
|
||||
|
||||
func (s CmbAvailableType) GetValue() string {
|
||||
return string(s)
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
package vo
|
||||
|
||||
type OrderAccountType uint8
|
||||
|
||||
const (
|
||||
OrderAccountTypeOpenId OrderAccountType = iota + 1
|
||||
OrderAccountTypePhone
|
||||
)
|
||||
|
||||
var OrderAccountTypeMap = map[OrderAccountType]string{
|
||||
OrderAccountTypeOpenId: "openid/userid",
|
||||
OrderAccountTypePhone: "手机号",
|
||||
}
|
||||
|
||||
func (s OrderAccountType) GetText() string {
|
||||
if t, ok := OrderAccountTypeMap[s]; ok {
|
||||
return t
|
||||
}
|
||||
return "未知账号类型"
|
||||
}
|
||||
|
||||
func (s OrderAccountType) GetValue() uint8 {
|
||||
return uint8(s)
|
||||
}
|
||||
|
||||
func (s OrderAccountType) IsOpenId() bool {
|
||||
return s == OrderAccountTypeOpenId
|
||||
}
|
||||
|
||||
func (s OrderAccountType) IsPhone() bool {
|
||||
return s == OrderAccountTypePhone
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
package vo
|
||||
|
||||
import "fmt"
|
||||
|
||||
type OrderNotifyEvent uint8
|
||||
|
||||
const (
|
||||
OrderNotifyEventSendDEd OrderNotifyEvent = iota + 1
|
||||
OrderNotifyEventUsed
|
||||
OrderNotifyEventExpired
|
||||
)
|
||||
|
||||
var OrderNotifyEventMap = map[OrderNotifyEvent]string{
|
||||
OrderNotifyEventSendDEd: "可用",
|
||||
OrderNotifyEventUsed: "已实扣",
|
||||
OrderNotifyEventExpired: "已过期",
|
||||
}
|
||||
|
||||
func (s OrderNotifyEvent) GetText() string {
|
||||
if t, ok := OrderNotifyEventMap[s]; ok {
|
||||
return t
|
||||
}
|
||||
return "未知通知事件"
|
||||
}
|
||||
|
||||
func (s OrderNotifyEvent) GetValue() uint8 {
|
||||
return uint8(s)
|
||||
}
|
||||
|
||||
func (s OrderNotifyEvent) IsSendDEd() bool {
|
||||
return s == OrderNotifyEventSendDEd
|
||||
}
|
||||
|
||||
func (s OrderNotifyEvent) IsUsed() bool {
|
||||
return s == OrderNotifyEventUsed
|
||||
}
|
||||
|
||||
func (s OrderNotifyEvent) IsExpired() bool {
|
||||
return s == OrderNotifyEventExpired
|
||||
}
|
||||
|
||||
func (s OrderNotifyEvent) CanNotify() bool {
|
||||
return s.IsSendDEd() || s.IsUsed() || s.IsExpired()
|
||||
}
|
||||
|
||||
var OrderNotifyEventMapCmbStatus = map[OrderNotifyEvent]CmbStatus{
|
||||
OrderNotifyEventSendDEd: CmbStatusSuccess,
|
||||
OrderNotifyEventUsed: CmbStatusUse,
|
||||
OrderNotifyEventExpired: CmbStatusExpired,
|
||||
}
|
||||
|
||||
func (s OrderNotifyEvent) GetCmbStatusText() (CmbStatus, error) {
|
||||
if t, ok := OrderNotifyEventMapCmbStatus[s]; ok {
|
||||
return t, nil
|
||||
}
|
||||
return "", fmt.Errorf("cmbStatus[%s]未定义", s)
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
package vo
|
||||
|
||||
type OrderNotifyStatus uint8
|
||||
|
||||
const (
|
||||
OrderNotifyStatusWait OrderNotifyStatus = iota + 1
|
||||
OrderNotifyStatusSuccess
|
||||
OrderNotifyStatusFail
|
||||
)
|
||||
|
||||
var OrderNotifyStatusMap = map[OrderNotifyStatus]string{
|
||||
OrderNotifyStatusWait: "待请求",
|
||||
OrderNotifyStatusSuccess: "请求成功",
|
||||
OrderNotifyStatusFail: "请求失败",
|
||||
}
|
||||
|
||||
func (s OrderNotifyStatus) GetText() string {
|
||||
if t, ok := OrderNotifyStatusMap[s]; ok {
|
||||
return t
|
||||
}
|
||||
return "未知请求状态"
|
||||
}
|
||||
|
||||
func (s OrderNotifyStatus) GetValue() uint8 {
|
||||
return uint8(s)
|
||||
}
|
||||
|
||||
func (s OrderNotifyStatus) IsWait() bool {
|
||||
return s == OrderNotifyStatusWait
|
||||
}
|
||||
|
||||
func (s OrderNotifyStatus) IsSuccess() bool {
|
||||
return s == OrderNotifyStatusSuccess
|
||||
}
|
||||
|
||||
func (s OrderNotifyStatus) IsFail() bool {
|
||||
return s == OrderNotifyStatusFail
|
||||
}
|
||||
|
|
@ -1 +1,88 @@
|
|||
package vo
|
||||
|
||||
import "fmt"
|
||||
|
||||
type OrderStatus uint8
|
||||
|
||||
const (
|
||||
OrderStatusWait OrderStatus = iota + 1
|
||||
OrderStatusIng
|
||||
OrderStatusSuccess
|
||||
OrderStatusFail
|
||||
OrderStatusUse
|
||||
OrderStatusExpired
|
||||
)
|
||||
|
||||
var OrderStatusMap = map[OrderStatus]string{
|
||||
OrderStatusWait: "待发放",
|
||||
OrderStatusIng: "发放中",
|
||||
OrderStatusSuccess: "发放成功",
|
||||
OrderStatusFail: "发放失败",
|
||||
OrderStatusUse: "已使用",
|
||||
OrderStatusExpired: "已过期",
|
||||
}
|
||||
|
||||
func (s OrderStatus) GetText() string {
|
||||
if t, ok := OrderStatusMap[s]; ok {
|
||||
return t
|
||||
}
|
||||
return "未知状态"
|
||||
}
|
||||
|
||||
func (s OrderStatus) GetValue() uint8 {
|
||||
return uint8(s)
|
||||
}
|
||||
|
||||
func (s OrderStatus) IsWait() bool {
|
||||
return s == OrderStatusWait
|
||||
}
|
||||
|
||||
func (s OrderStatus) IsIng() bool {
|
||||
return s == OrderStatusIng
|
||||
}
|
||||
|
||||
func (s OrderStatus) IsSuccess() bool {
|
||||
return s == OrderStatusSuccess
|
||||
}
|
||||
|
||||
func (s OrderStatus) IsFail() bool {
|
||||
return s == OrderStatusFail
|
||||
}
|
||||
|
||||
func (s OrderStatus) IsUse() bool {
|
||||
return s == OrderStatusUse
|
||||
}
|
||||
|
||||
func (s OrderStatus) IsExpired() bool {
|
||||
return s == OrderStatusExpired
|
||||
}
|
||||
|
||||
func (s OrderStatus) IsCanNotify() bool {
|
||||
return s.IsSuccess() || s.IsUse() || s.IsExpired()
|
||||
}
|
||||
|
||||
var OrderStatusMapOrderNotifyEvent = map[OrderStatus]OrderNotifyEvent{
|
||||
OrderStatusSuccess: OrderNotifyEventSendDEd,
|
||||
OrderStatusUse: OrderNotifyEventUsed,
|
||||
OrderStatusExpired: OrderNotifyEventExpired,
|
||||
}
|
||||
|
||||
func (s OrderStatus) GetOrderNotifyEvent() (OrderNotifyEvent, error) {
|
||||
if t, ok := OrderStatusMapOrderNotifyEvent[s]; ok {
|
||||
return t, nil
|
||||
}
|
||||
return 0, fmt.Errorf("CmbStatus[%s]未定义", s)
|
||||
}
|
||||
|
||||
var OrderStatusMapCmbStatus = map[OrderStatus]CmbStatus{
|
||||
OrderStatusSuccess: CmbStatusSuccess,
|
||||
OrderStatusUse: CmbStatusUse,
|
||||
OrderStatusExpired: CmbStatusExpired,
|
||||
}
|
||||
|
||||
func (s OrderStatus) GetCmbStatusText() (CmbStatus, error) {
|
||||
if t, ok := OrderStatusMapCmbStatus[s]; ok {
|
||||
return t, nil
|
||||
}
|
||||
return "", fmt.Errorf("cmbStatus[%s]未定义", s)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,34 @@
|
|||
package vo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type OrderType uint8
|
||||
|
||||
const (
|
||||
OrderTypeCmb OrderType = iota + 1
|
||||
)
|
||||
|
||||
var OrderTypeMap = map[OrderType]string{
|
||||
OrderTypeCmb: "招行",
|
||||
}
|
||||
|
||||
func (s OrderType) GetText() string {
|
||||
if t, ok := OrderTypeMap[s]; ok {
|
||||
return t
|
||||
}
|
||||
return "未知类型"
|
||||
}
|
||||
|
||||
func (s OrderType) String() string {
|
||||
return fmt.Sprintf("%d", s)
|
||||
}
|
||||
|
||||
func (s OrderType) GetValue() uint8 {
|
||||
return uint8(s)
|
||||
}
|
||||
|
||||
func (s OrderType) IsCmb() bool {
|
||||
return s == OrderTypeCmb
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
package vo
|
||||
|
||||
type WechatNotifyRegisterTagStatus uint8
|
||||
|
||||
const (
|
||||
WechatNotifyRegisterTagStatusWait WechatNotifyRegisterTagStatus = iota + 1
|
||||
WechatNotifyRegisterTagStatusSuccess
|
||||
WechatNotifyRegisterTagStatusFail
|
||||
)
|
||||
|
||||
var WechatNotifyRegisterTagStatusMap = map[WechatNotifyRegisterTagStatus]string{
|
||||
WechatNotifyRegisterTagStatusWait: "待请求",
|
||||
WechatNotifyRegisterTagStatusSuccess: "请求成功",
|
||||
WechatNotifyRegisterTagStatusFail: "请求失败",
|
||||
}
|
||||
|
||||
func (s WechatNotifyRegisterTagStatus) GetText() string {
|
||||
if t, ok := WechatNotifyRegisterTagStatusMap[s]; ok {
|
||||
return t
|
||||
}
|
||||
return "未知请求状态"
|
||||
}
|
||||
|
||||
func (s WechatNotifyRegisterTagStatus) GetValue() uint8 {
|
||||
return uint8(s)
|
||||
}
|
||||
|
||||
func (s WechatNotifyRegisterTagStatus) IsWait() bool {
|
||||
return s == WechatNotifyRegisterTagStatusWait
|
||||
}
|
||||
|
||||
func (s WechatNotifyRegisterTagStatus) IsSuccess() bool {
|
||||
return s == WechatNotifyRegisterTagStatusSuccess
|
||||
}
|
||||
|
||||
func (s WechatNotifyRegisterTagStatus) IsFail() bool {
|
||||
return s == WechatNotifyRegisterTagStatusFail
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
package vo
|
||||
|
||||
type WechatVoucherStatus string
|
||||
|
||||
const (
|
||||
WechatVoucherStatusSended WechatVoucherStatus = "SENDED"
|
||||
WechatVoucherStatusUsed WechatVoucherStatus = "USED"
|
||||
WechatVoucherStatusExpired WechatVoucherStatus = "EXPIRED"
|
||||
)
|
||||
|
||||
var VoucherStatusMap = map[WechatVoucherStatus]string{
|
||||
WechatVoucherStatusSended: "可用",
|
||||
WechatVoucherStatusUsed: "已实扣",
|
||||
WechatVoucherStatusExpired: "已过期",
|
||||
}
|
||||
|
||||
func (s WechatVoucherStatus) GetText() string {
|
||||
if t, ok := VoucherStatusMap[s]; ok {
|
||||
return t
|
||||
}
|
||||
return "未知类型"
|
||||
}
|
||||
|
||||
func (s WechatVoucherStatus) GetValue() string {
|
||||
return string(s)
|
||||
}
|
||||
|
||||
func (s WechatVoucherStatus) IsSended() bool {
|
||||
return s == WechatVoucherStatusSended
|
||||
}
|
||||
|
||||
func (s WechatVoucherStatus) IsUsed() bool {
|
||||
return s == WechatVoucherStatusUsed
|
||||
}
|
||||
|
||||
func (s WechatVoucherStatus) IsExpired() bool {
|
||||
return s == WechatVoucherStatusExpired
|
||||
}
|
||||
|
|
@ -1,17 +1,98 @@
|
|||
package biz
|
||||
|
||||
import (
|
||||
"voucher/internal/biz/repository"
|
||||
"voucher/internal/biz/thirdrepository"
|
||||
"sync"
|
||||
"voucher/internal/biz/cmb"
|
||||
"voucher/internal/biz/mixrepos"
|
||||
"voucher/internal/biz/repo"
|
||||
"voucher/internal/biz/wechatrepo"
|
||||
"voucher/internal/conf"
|
||||
"voucher/internal/data"
|
||||
)
|
||||
|
||||
type VoucherBiz struct {
|
||||
OrderRepo repository.OrderRepo
|
||||
ThirdMQSend thirdrepository.ThirdMQSend
|
||||
bc *conf.Bootstrap
|
||||
rdb *data.Rdb
|
||||
Cmb *cmb.Cmb
|
||||
ProductRepo repo.ProductRepo
|
||||
OrderRepo repo.OrderRepo
|
||||
OrderBakRepo repo.OrderBakRepo
|
||||
OrderNotifyRepo repo.OrderNotifyRepo
|
||||
WechatNotifyRegisterTagRepo repo.WechatNotifyRegisterTagRepo
|
||||
MqSendMixRepo mixrepos.MQSendMixRepo
|
||||
GenerateMixRepo mixrepos.GenerateMixRepo
|
||||
WechatCpnRepo wechatrepo.WechatCpnRepo
|
||||
BankMultiActivityRepo wechatrepo.BankMultiActivityRepo
|
||||
DingMixRepo mixrepos.DingMixRepo
|
||||
CmbMixRepo mixrepos.CmbMixRepo
|
||||
SmsMixRepo mixrepos.SmsMixRepo
|
||||
|
||||
mu sync.RWMutex
|
||||
queryMap map[string]bool
|
||||
}
|
||||
|
||||
func NewVoucherBiz(orderRepo repository.OrderRepo, thirdMQSend thirdrepository.ThirdMQSend) *VoucherBiz {
|
||||
return &VoucherBiz{OrderRepo: orderRepo, ThirdMQSend: thirdMQSend}
|
||||
func NewVoucherBiz(
|
||||
bc *conf.Bootstrap,
|
||||
rdb *data.Rdb,
|
||||
Cmb *cmb.Cmb,
|
||||
ProductRepo repo.ProductRepo,
|
||||
OrderRepo repo.OrderRepo,
|
||||
OrderBakRepo repo.OrderBakRepo,
|
||||
OrderNotifyRepo repo.OrderNotifyRepo,
|
||||
WechatNotifyRegisterTagRepo repo.WechatNotifyRegisterTagRepo,
|
||||
MqSendMixRepo mixrepos.MQSendMixRepo,
|
||||
GenerateMixRepo mixrepos.GenerateMixRepo,
|
||||
WechatCpnRepo wechatrepo.WechatCpnRepo,
|
||||
BankMultiActivityRepo wechatrepo.BankMultiActivityRepo,
|
||||
DingMixRepo mixrepos.DingMixRepo,
|
||||
CmbMixRepo mixrepos.CmbMixRepo,
|
||||
SmsMixRepo mixrepos.SmsMixRepo,
|
||||
) *VoucherBiz {
|
||||
return &VoucherBiz{
|
||||
bc: bc,
|
||||
rdb: rdb,
|
||||
Cmb: Cmb,
|
||||
ProductRepo: ProductRepo,
|
||||
OrderRepo: OrderRepo,
|
||||
OrderBakRepo: OrderBakRepo,
|
||||
OrderNotifyRepo: OrderNotifyRepo,
|
||||
WechatNotifyRegisterTagRepo: WechatNotifyRegisterTagRepo,
|
||||
MqSendMixRepo: MqSendMixRepo,
|
||||
GenerateMixRepo: GenerateMixRepo,
|
||||
WechatCpnRepo: WechatCpnRepo,
|
||||
BankMultiActivityRepo: BankMultiActivityRepo,
|
||||
DingMixRepo: DingMixRepo,
|
||||
CmbMixRepo: CmbMixRepo,
|
||||
SmsMixRepo: SmsMixRepo,
|
||||
|
||||
queryMap: make(map[string]bool),
|
||||
}
|
||||
}
|
||||
|
||||
// 1:收单
|
||||
func (this *VoucherBiz) Get(uid string) bool {
|
||||
this.mu.Lock()
|
||||
defer this.mu.Unlock()
|
||||
|
||||
if _, ok := this.queryMap[uid]; ok {
|
||||
return ok
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (this *VoucherBiz) Add(uid string) {
|
||||
this.mu.Lock()
|
||||
defer this.mu.Unlock()
|
||||
|
||||
this.queryMap[uid] = true
|
||||
}
|
||||
|
||||
func (this *VoucherBiz) Remove(uid string) {
|
||||
|
||||
this.mu.Lock()
|
||||
defer this.mu.Unlock()
|
||||
|
||||
if _, ok := this.queryMap[uid]; ok {
|
||||
delete(this.queryMap, uid)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,251 @@
|
|||
package biz
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/go-kratos/kratos/v2/log"
|
||||
"github.com/wechatpay-apiv3/wechatpay-go/services/cashcoupons"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"voucher/internal/biz/bo"
|
||||
"voucher/internal/biz/do"
|
||||
"voucher/internal/biz/vo"
|
||||
)
|
||||
|
||||
func (this *VoucherBiz) Warning(ctx context.Context, id int32) error {
|
||||
|
||||
product, err := this.ProductRepo.GetById(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return this.WarningBudget(ctx, product)
|
||||
}
|
||||
|
||||
func (this *VoucherBiz) WarningBudgetIncr(ctx context.Context, key string, ttl time.Duration) (int64, error) {
|
||||
|
||||
// 增加发送计数
|
||||
count, err := this.rdb.Rdb.IncrBy(ctx, key, 1).Result()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// 如果是第一次发送,设置 过期时间
|
||||
if count == 1 {
|
||||
if err = this.rdb.Rdb.Expire(ctx, key, ttl).Err(); err != nil {
|
||||
return 0, fmt.Errorf("设置过期时间失败: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// 如果发送次数超过 “指定” 条,清除再来
|
||||
if count > 24 { // 大约2小时
|
||||
return 0, this.WarningBudgetIncrDel(ctx, key)
|
||||
}
|
||||
|
||||
return count, nil
|
||||
}
|
||||
|
||||
func (this *VoucherBiz) WarningBudgetIncrDel(ctx context.Context, key string) error {
|
||||
|
||||
// 检查键是否存在
|
||||
exists, err := this.rdb.Rdb.Exists(ctx, key).Result()
|
||||
if err != nil {
|
||||
return fmt.Errorf("检查键存在性失败: %w", err)
|
||||
}
|
||||
|
||||
// 如果键不存在,直接返回成功
|
||||
if exists == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if _, err = this.rdb.Rdb.Del(ctx, key).Result(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *VoucherBiz) CronWarningBudget(ctx context.Context) {
|
||||
|
||||
uid := "warningBudget"
|
||||
|
||||
if b := this.Get(uid); b {
|
||||
log.Warn("预警查询,上波还未执行完毕,此次暂不执行")
|
||||
return
|
||||
}
|
||||
|
||||
this.Add(uid)
|
||||
defer this.Remove(uid)
|
||||
|
||||
start := time.Now()
|
||||
log.Warnf("预警查询,执行开始: %s", start.Format(time.DateTime))
|
||||
|
||||
if err := this.cronWarningBudget(ctx); err != nil {
|
||||
log.Errorf("预警查询,执行失败: %s", err)
|
||||
}
|
||||
|
||||
end := time.Now()
|
||||
elapsed := end.Sub(start)
|
||||
log.Warnf("预警查询,开始执行时间%s,执行结束时间%s,代码块执行耗时: %s", start.Format(time.DateTime), end.Format(time.DateTime), elapsed)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (this *VoucherBiz) cronWarningBudget(ctx context.Context) error {
|
||||
|
||||
return this.ProductRepo.FindWarningBudget(ctx, func(ctx context.Context, rows []*bo.ProductBo) error {
|
||||
|
||||
for _, row := range rows {
|
||||
|
||||
if err := this.WarningBudget(ctx, row); err != nil {
|
||||
log.Context(ctx).Errorf("预警查询,处理失败: %s", err)
|
||||
}
|
||||
time.Sleep(time.Second * 2)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (this *VoucherBiz) WarningBudget(ctx context.Context, product *bo.ProductBo) (wErr error) {
|
||||
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
_, file, line, _ := runtime.Caller(1) // 1 表示获取当前调用者的调用信息
|
||||
log.Errorf("预警查询,发生错误:req:%s,err:%v,file:%s,line:%d", product.BatchNo, err, file, line)
|
||||
wErr = fmt.Errorf("预警查询,panic: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
if product.WarningBudget == 0 {
|
||||
return fmt.Errorf("no warning budget")
|
||||
}
|
||||
|
||||
if product.WarningBudget == 0 {
|
||||
return fmt.Errorf("no warning budget")
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
if product.StartTime == nil || product.EndTime == nil {
|
||||
return fmt.Errorf("no start or end time")
|
||||
}
|
||||
if now.Before(*product.StartTime) {
|
||||
return fmt.Errorf("not start")
|
||||
}
|
||||
if now.After(*product.EndTime) {
|
||||
return fmt.Errorf("expired")
|
||||
}
|
||||
|
||||
wxResp, err := this.WechatCpnRepo.QueryProduct(ctx, product.MchId, product.BatchNo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return this.Calculate(ctx, product, wxResp)
|
||||
}
|
||||
|
||||
func (this *VoucherBiz) Calculate(ctx context.Context, product *bo.ProductBo, wxResp *cashcoupons.Stock) error {
|
||||
|
||||
w := this.WxResp(wxResp)
|
||||
|
||||
b := vo.WarningBudgetSendIncr.BuildCache([]string{product.BatchNo})
|
||||
key := b.Key
|
||||
ttl := b.TTL
|
||||
|
||||
if w.AllBudget > product.AllBudget {
|
||||
if err := this.WarningBudgetIncrDel(ctx, key); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := this.ProductRepo.UpdateByWxResp(ctx, product.ID, w); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if product.WarningBudget >= w.AvailableBudget {
|
||||
|
||||
count, err2 := this.WarningBudgetIncr(ctx, key, ttl)
|
||||
if err2 != nil {
|
||||
return err2
|
||||
}
|
||||
|
||||
if count == 1 {
|
||||
return this.WarningSend(ctx, product, w)
|
||||
} else {
|
||||
log.Warnf("预警查询,当前达到预警第[%d]次,暂不做通知", count)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *VoucherBiz) WarningSend(ctx context.Context, product *bo.ProductBo, wxResp *do.WxResp) error {
|
||||
|
||||
title := fmt.Sprintf("券预算预警通知:%s", product.BatchName)
|
||||
if err := this.DingMixRepo.SendMessage(ctx, title, formatAsCard(product, wxResp)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var warningPerson []do.WarningPerson
|
||||
if err := json.Unmarshal([]byte(product.WarningPerson), &warningPerson); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var mobileList []string
|
||||
for _, person := range warningPerson {
|
||||
mobileList = append(mobileList, person.Mobile)
|
||||
}
|
||||
|
||||
if len(mobileList) > 0 {
|
||||
return this.SmsMixRepo.Send(ctx, mobileList, this.bc.AliYunSms.TemplateWarning, buildTemplateParams(product, wxResp))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func buildTemplateParams(product *bo.ProductBo, req *do.WxResp) map[string]string {
|
||||
return map[string]string{
|
||||
"stock_name": product.BatchName,
|
||||
"stock_no": product.ProductNo,
|
||||
"amount": strconv.Itoa(int(req.Amount)),
|
||||
"all_stock": strconv.Itoa(int(req.AllStock)),
|
||||
"all_budget": strconv.Itoa(int(req.AllBudget)),
|
||||
"used_stock": strconv.Itoa(int(req.UsedStock)),
|
||||
"used_budget": strconv.Itoa(int(req.UsedBudget)),
|
||||
"available_stock": strconv.Itoa(int(req.AvailableStock)),
|
||||
"available_budget": strconv.Itoa(int(req.AvailableBudget)),
|
||||
"budget_usage_rate": fmt.Sprintf("%.1f", req.StockUsageRate),
|
||||
}
|
||||
}
|
||||
|
||||
func formatAsCard(product *bo.ProductBo, req *do.WxResp) string {
|
||||
var card strings.Builder
|
||||
|
||||
card.WriteString("### " + product.BatchName + "\n\n")
|
||||
|
||||
// 基本信息
|
||||
card.WriteString("#### 🎫 基本信息\n")
|
||||
card.WriteString(fmt.Sprintf("- **批次号**: %s\n", product.BatchNo))
|
||||
card.WriteString(fmt.Sprintf("- **活动号**: %s\n", product.ProductNo))
|
||||
card.WriteString(fmt.Sprintf("- **预警值**: %d元\n", product.WarningBudget))
|
||||
card.WriteString(fmt.Sprintf("- **面额**: %d元\n", req.Amount))
|
||||
card.WriteString(fmt.Sprintf("- **总预算**: %d元\n", req.AllBudget))
|
||||
card.WriteString(fmt.Sprintf("- **总库存**: %d张\n", req.AllStock))
|
||||
card.WriteString("\n")
|
||||
|
||||
// 使用情况
|
||||
card.WriteString("#### 📊 使用情况\n")
|
||||
card.WriteString(fmt.Sprintf("- **已发券数**: %d张\n", req.UsedStock))
|
||||
card.WriteString(fmt.Sprintf("- **已发券金额**: %d元\n", req.UsedBudget))
|
||||
card.WriteString(fmt.Sprintf("- **剩余库存**: %d张\n", req.AvailableStock))
|
||||
card.WriteString(fmt.Sprintf("- **剩余预算**: %d元\n", req.AvailableBudget))
|
||||
card.WriteString("\n")
|
||||
|
||||
card.WriteString("#### 📈 使用率\n")
|
||||
card.WriteString(fmt.Sprintf("- **使用率**: %.1f%%\n", req.StockUsageRate))
|
||||
|
||||
return card.String()
|
||||
}
|
||||
|
|
@ -0,0 +1,146 @@
|
|||
package biz
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"gorm.io/gorm"
|
||||
errPb "voucher/api/err"
|
||||
"voucher/internal/biz/bo"
|
||||
"voucher/internal/biz/vo"
|
||||
"voucher/internal/pkg/lock"
|
||||
)
|
||||
|
||||
func (this *VoucherBiz) WechatNotifyConsumer(ctx context.Context, tag string, req *bo.WechatVoucherNotifyBo) error {
|
||||
|
||||
c := vo.WechatNotifyConsumeLockKey.BuildCache([]string{tag, req.PlainText.StockID, req.PlainText.CouponID})
|
||||
|
||||
return lock.NewMutex(this.rdb.Rdb, c.TTL).Lock(ctx, c.Key, func(ctx context.Context) error {
|
||||
|
||||
order, err := this.getOrder(ctx, req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if req.PlainText.Status.IsSended() {
|
||||
|
||||
return this.available(ctx, order)
|
||||
|
||||
} else if req.PlainText.Status.IsUsed() {
|
||||
|
||||
return this.notifyUsed(ctx, order, req)
|
||||
|
||||
} else if req.PlainText.Status.IsExpired() {
|
||||
|
||||
return this.expired(ctx, order)
|
||||
|
||||
}
|
||||
|
||||
return fmt.Errorf("未知通知类型:%s", req.PlainText.Status.GetText())
|
||||
})
|
||||
}
|
||||
|
||||
func (this *VoucherBiz) getOrder(ctx context.Context, req *bo.WechatVoucherNotifyBo) (*bo.OrderBo, error) {
|
||||
|
||||
order, err := this.OrderRepo.GetByCouponId(ctx, req.PlainText.StockCreatorMchid, req.PlainText.StockID, req.PlainText.CouponID)
|
||||
|
||||
if err != nil {
|
||||
|
||||
if !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
order, err = this.OrderRepo.GetByTransactionId(ctx, req.PlainText.StockCreatorMchid, req.PlainText.StockID, req.PlainText.ConsumeInformation.TransactionID)
|
||||
|
||||
if err != nil {
|
||||
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
|
||||
return nil, fmt.Errorf("微信回调消费,订单不存在,StockCreatorMchid:%s,StockID:%s,CouponID:%s,CreateTime:%s",
|
||||
req.PlainText.StockCreatorMchid,
|
||||
req.PlainText.StockID,
|
||||
req.PlainText.CouponID,
|
||||
req.PlainText.CreateTime,
|
||||
)
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return order, nil
|
||||
}
|
||||
|
||||
return order, nil
|
||||
}
|
||||
|
||||
func (this *VoucherBiz) notifyUsed(ctx context.Context, order *bo.OrderBo, req *bo.WechatVoucherNotifyBo) error {
|
||||
|
||||
if order.Status.IsUse() {
|
||||
// 券状态已是已使用,忽略不处理
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := this.OrderRepo.NotifyUsed(ctx, order.ID, req.PlainText.ConsumeInformation.TransactionID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return this.notify(ctx, order)
|
||||
}
|
||||
|
||||
func (this *VoucherBiz) available(ctx context.Context, order *bo.OrderBo) error {
|
||||
|
||||
if order.Status.IsSuccess() {
|
||||
// 券状态已是可使用,忽略不处理
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := this.OrderRepo.Available(ctx, order.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return this.notify(ctx, order)
|
||||
}
|
||||
|
||||
func (this *VoucherBiz) expired(ctx context.Context, order *bo.OrderBo) error {
|
||||
|
||||
if order.Status.IsExpired() {
|
||||
// 券状态已是已过期,忽略不处理
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := this.OrderRepo.Expired(ctx, order.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return this.notify(ctx, order)
|
||||
}
|
||||
|
||||
func (this *VoucherBiz) notify(ctx context.Context, order *bo.OrderBo) error {
|
||||
|
||||
if order.ActivityId == "" {
|
||||
return nil // 多笔立减活动,不做通知(?不知道有没有核销通知,多笔核销情况未知)
|
||||
}
|
||||
|
||||
return this.cmbNotify(ctx, order.ID)
|
||||
}
|
||||
|
||||
func (this *VoucherBiz) cmbNotify(ctx context.Context, orderId uint64) error {
|
||||
|
||||
order, err := this.OrderRepo.GetByID(ctx, orderId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if orderNotify, err2 := this.Cmb.Notify(ctx, order); err != nil {
|
||||
|
||||
if !errPb.IsNeedRetryNotify(err2) {
|
||||
return err2
|
||||
}
|
||||
|
||||
// 第一次通知失败重试入队
|
||||
// 状态回调接口失败需要支持重试 重试间隔为1分钟、2分钟、12分钟、60分钟、360分钟
|
||||
return this.PushNotifyRetryDelayMQ(ctx, 60, orderNotify.ID)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -0,0 +1,130 @@
|
|||
package biz
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/go-kratos/kratos/v2/log"
|
||||
"github.com/go-kratos/kratos/v2/transport/http"
|
||||
"github.com/nacos-group/nacos-sdk-go/util"
|
||||
"time"
|
||||
"voucher/internal/biz/bo"
|
||||
"voucher/internal/biz/do"
|
||||
)
|
||||
|
||||
func (this *VoucherBiz) uid(_ context.Context, msg string) string {
|
||||
return util.Md5(msg)
|
||||
}
|
||||
|
||||
func (this *VoucherBiz) PushWechatQuery(ctx http.Context, req *do.WechatQuery) error {
|
||||
|
||||
if req.ProductNo != "" {
|
||||
_, err := this.ProductRepo.GetByProductNo(ctx, req.ProductNo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
queue := this.bc.RdsMQ.GetWechatQuery()
|
||||
if queue == nil {
|
||||
return fmt.Errorf("队列不存在")
|
||||
}
|
||||
|
||||
msg, err := json.Marshal(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
strMsg := string(msg)
|
||||
|
||||
uid := this.uid(ctx, strMsg)
|
||||
if this.Get(uid) {
|
||||
return fmt.Errorf("此台服务队列正在处理中,key:%s,ip:%s", uid, ctx.Header().Get("X-Forwarded-For"))
|
||||
}
|
||||
|
||||
this.Add(uid)
|
||||
|
||||
_, err = this.rdb.Rdb.RPush(ctx, queue.Name, strMsg).Result()
|
||||
if err != nil {
|
||||
this.Remove(uid)
|
||||
return fmt.Errorf("添加到队列失败:%v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *VoucherBiz) WechatQuery(ctx context.Context, msg string) error {
|
||||
|
||||
defer this.Remove(this.uid(ctx, msg))
|
||||
|
||||
var req *do.WechatQuery
|
||||
|
||||
if err := json.Unmarshal([]byte(msg), &req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
start := time.Now()
|
||||
startStr := time.Now().String()
|
||||
|
||||
log.Warnf("微信券查询处理开始:%s,msg:%s", startStr, msg)
|
||||
fmt.Printf("微信券查询处理开始:%s,msg:%s", startStr, msg)
|
||||
|
||||
n := 0
|
||||
num := 0
|
||||
notifyNum := 0
|
||||
err := this.OrderRepo.FinSucByStockIdInBatches(ctx, req, func(ctx context.Context, rows []*bo.OrderBo) error {
|
||||
|
||||
n += 1
|
||||
for _, order := range rows {
|
||||
|
||||
num += 1
|
||||
if err := this.wechatQuery(ctx, order, ¬ifyNum); err != nil {
|
||||
log.Errorf("微信查询券订单状态发生错误,msg:%s,orderNo:%s,couponId:%s,appId:%s,openId:%s,stockId:%s,err:%v",
|
||||
msg, order.OrderNo, order.VoucherNo, order.AppID, order.Account, order.BatchNo, err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
groupTime := time.Now()
|
||||
log.Warnf("微信券查询处理第:%d组,已执行条数:%d,核销通知条数:%d,执行开始时间:%s,当前时间:%s,已耗时:%s", n, num, notifyNum, startStr, groupTime.String(), groupTime.Sub(start).String())
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
endTime := time.Now()
|
||||
log.Warnf("微信券查询处理耗时:%s,结束时间:%s,处理%d组,处理%d单,核销通知条数:%d,msg:%s", endTime.Sub(start).String(), endTime.String(), n, num, notifyNum, msg)
|
||||
fmt.Printf("微信券查询处理耗时:%s,结束时间%s,处理%d组,处理%d单,核销通知条数:%d,msg:%s", endTime.Sub(start).String(), endTime.String(), n, num, notifyNum, msg)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (this *VoucherBiz) wechatQuery(ctx context.Context, order *bo.OrderBo, notifyNum *int) error {
|
||||
|
||||
status, err := this.WechatCpnRepo.Query(ctx, order)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if status.IsUse() {
|
||||
return this.queryUsed(ctx, order, notifyNum)
|
||||
} else if status.IsExpired() {
|
||||
return this.expired(ctx, order)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *VoucherBiz) queryUsed(ctx context.Context, order *bo.OrderBo, notifyNum *int) error {
|
||||
|
||||
*notifyNum += 1
|
||||
|
||||
if order.Status.IsUse() {
|
||||
return this.notify(ctx, order)
|
||||
}
|
||||
|
||||
if err := this.OrderRepo.Used(ctx, order.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return this.notify(ctx, order)
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
package biz
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func (this *VoucherBiz) PushWechatRetry(ctx context.Context, batchNo string) error {
|
||||
|
||||
product, err := this.ProductRepo.GetByBatchNo(ctx, batchNo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
queue := this.bc.RdsMQ.GetWechatRetry()
|
||||
if queue == nil {
|
||||
return fmt.Errorf("队列不存在")
|
||||
}
|
||||
|
||||
_, err = this.rdb.Rdb.RPush(ctx, queue.Name, product.BatchNo).Result()
|
||||
if err != nil {
|
||||
return fmt.Errorf("添加到队列失败:%v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *VoucherBiz) WechatRetry(ctx context.Context, batchNo string) error {
|
||||
|
||||
return nil
|
||||
//start := time.Now()
|
||||
//log.Warnf("失败订单重试开始:%s,batchNo:%s", start.String(), batchNo)
|
||||
//fmt.Printf("失败订单重试开始:%s,batchNo:%s", start.String(), batchNo)
|
||||
//
|
||||
//num := 0
|
||||
//err := this.OrderRepo.FinFailByStockIdInBatches(ctx, batchNo, func(ctx context.Context, rows []*bo.OrderBo) error {
|
||||
//
|
||||
// if len(rows) == 0 {
|
||||
// log.Infof("微信查询券订单状态,batchNo[%s],已处理[%d]单,无订单,结束执行", batchNo, num)
|
||||
// return nil
|
||||
// }
|
||||
//
|
||||
// for _, order := range rows {
|
||||
//
|
||||
// num += 1
|
||||
// if err := this.orderRetry(ctx, order); err != nil {
|
||||
// log.Errorf("失败订单重试发生错误,batchNo:%s,orderNo:%s,appId:%s,openId:%s,err:%v",
|
||||
// batchNo, order.OrderNo, order.AppID, order.Account, err)
|
||||
// }
|
||||
//
|
||||
// }
|
||||
//
|
||||
// time.Sleep(1 * time.Second)
|
||||
//
|
||||
// return nil
|
||||
//})
|
||||
//
|
||||
//log.Warnf("微信券查询处理耗时:%s,batchNo:%s,处理%d单", time.Now().Sub(start).String(), batchNo, num)
|
||||
//fmt.Printf("微信券查询处理耗时:%s,batchNo:%s,处理%d单", time.Now().Sub(start).String(), batchNo, num)
|
||||
//
|
||||
//return err
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package wechatrepo
|
||||
|
||||
import (
|
||||
"voucher/internal/biz/bo"
|
||||
)
|
||||
|
||||
type BankMultiActivityRepo interface {
|
||||
Order(order *bo.OrderBo) (couponId string, err error)
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package wechatrepo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/wechatpay-apiv3/wechatpay-go/services/cashcoupons"
|
||||
"voucher/internal/biz/bo"
|
||||
"voucher/internal/biz/vo"
|
||||
)
|
||||
|
||||
type WechatCpnRepo interface {
|
||||
Order(ctx context.Context, order *bo.OrderBo) (couponId string, err error)
|
||||
Query(ctx context.Context, order *bo.OrderBo) (vo.OrderStatus, error)
|
||||
QueryCoupon(ctx context.Context, orderWechat *bo.OrderBo) (*cashcoupons.Coupon, error)
|
||||
QueryProduct(ctx context.Context, stockCreatorMchId, stockId string) (*cashcoupons.Stock, error)
|
||||
QueryCallback(ctx context.Context) (*cashcoupons.Callback, error)
|
||||
SetCallback(ctx context.Context, url string) (*cashcoupons.SetCallbackResponse, error)
|
||||
RegisterNotifyTag(ctx context.Context, stockID string) error
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,7 +1,7 @@
|
|||
syntax = "proto3";
|
||||
package voucher.config;
|
||||
|
||||
option go_package = "voucher/internal/conf;conf";
|
||||
option go_package = "voucher/cpn/conf;conf";
|
||||
|
||||
import "google/protobuf/duration.proto";
|
||||
|
||||
|
|
@ -10,6 +10,13 @@ message Bootstrap {
|
|||
Logs logs = 2;
|
||||
Data data = 3;
|
||||
RocketMQ rocketMQ = 4;
|
||||
Wechat wechat = 5;
|
||||
Cmb cmb = 6;
|
||||
WechatNotifyMQ wechatNotifyMQ = 7;
|
||||
Alarm alarm = 8;
|
||||
Cron cron = 9;
|
||||
RdsMQ rdsMQ = 10;
|
||||
AliYunSms aliYunSms = 11;
|
||||
}
|
||||
|
||||
message Server {
|
||||
|
|
@ -63,6 +70,80 @@ message EventMap {
|
|||
bool isOpenConsumer = 4;
|
||||
}
|
||||
|
||||
message Wechat {
|
||||
string mchID = 1;
|
||||
string mchCertificateSerialNumber = 2;
|
||||
string wechatPayPublicKeyID = 3;
|
||||
string name = 4;
|
||||
}
|
||||
|
||||
message Cmb {
|
||||
string mid = 1;
|
||||
string aid = 2;
|
||||
string sm2Prk = 3;
|
||||
string sm2Puk = 4;
|
||||
string cmbSm2Pik = 5;
|
||||
string cmbSm2Puk = 6;
|
||||
string keyAlias = 7;
|
||||
string cmbKeyAlias = 8;
|
||||
string orgNo = 9;
|
||||
string notifyUrl = 10;
|
||||
int64 noticeStartDays = 11;
|
||||
int64 noticeEndDays = 12;
|
||||
}
|
||||
|
||||
message WechatNotifyMQ {
|
||||
string accessKeyId = 1;
|
||||
string accessKeySecret = 2;
|
||||
string endPoint = 3;
|
||||
string regionId = 4;
|
||||
string instanceId = 5;
|
||||
string topic = 6;
|
||||
string tag = 7;
|
||||
string groupId = 8;
|
||||
string registerTagUrl = 10;
|
||||
bool isOpenConsumer = 11;
|
||||
}
|
||||
|
||||
message Alarm {
|
||||
string webhookURL = 1;
|
||||
string secret = 2;
|
||||
bool atAll = 3;
|
||||
repeated string atMobiles = 4;
|
||||
repeated string warningMobiles = 5;
|
||||
}
|
||||
|
||||
message Cron {
|
||||
message CommandMap {
|
||||
bool isOpen = 1;
|
||||
string command = 2;
|
||||
}
|
||||
bool isOpen = 1;
|
||||
map<string, CommandMap> commandMap = 2;
|
||||
}
|
||||
|
||||
message RdsMQ {
|
||||
message Queue {
|
||||
string name = 1;
|
||||
bool isOpen = 2;
|
||||
uint32 retryNum = 3;
|
||||
uint32 numWorkers = 4;
|
||||
google.protobuf.Duration waitTime = 5;
|
||||
}
|
||||
Queue wechatQuery = 1;
|
||||
Queue wechatTimeSliceQuery = 2;
|
||||
Queue wechatRetry = 3;
|
||||
Queue retryNotify = 4;
|
||||
}
|
||||
|
||||
message AliYunSms {
|
||||
string accessKeyId = 1;
|
||||
string accessKeySecret = 2;
|
||||
string endpoint = 3;
|
||||
string signName = 4;
|
||||
string templateWarning = 5;
|
||||
}
|
||||
|
||||
message Logs {
|
||||
string business = 1;
|
||||
string access = 2;
|
||||
|
|
|
|||
|
|
@ -15,12 +15,6 @@ func NewDb(db *GormDb) *Db {
|
|||
}
|
||||
}
|
||||
|
||||
type contextTxKey struct{}
|
||||
|
||||
func (d *Db) DB(ctx context.Context) *gorm.DB {
|
||||
tx, ok := ctx.Value(contextTxKey{}).(*gorm.DB)
|
||||
if ok {
|
||||
return tx
|
||||
}
|
||||
func (d *Db) DB(_ context.Context) *gorm.DB {
|
||||
return d.db.Client
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,9 @@
|
|||
package data
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"gorm.io/driver/mysql"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/logger"
|
||||
"time"
|
||||
"voucher/internal/conf"
|
||||
)
|
||||
|
||||
|
|
@ -14,22 +11,20 @@ type GormDb struct {
|
|||
Client *gorm.DB
|
||||
}
|
||||
|
||||
func NewGormDb(c *conf.Bootstrap) (*GormDb, func()) {
|
||||
db1, mf := db(c.Data.Db)
|
||||
cleanup := func() {
|
||||
mf()
|
||||
}
|
||||
func NewGormDb(c *conf.Bootstrap) *GormDb {
|
||||
return &GormDb{
|
||||
Client: db1,
|
||||
}, cleanup
|
||||
Client: db(c.Data.Db),
|
||||
}
|
||||
}
|
||||
|
||||
func db(data *conf.Data_Database) (*gorm.DB, func()) {
|
||||
mysqlConn, err := sql.Open(data.Driver, data.Source)
|
||||
func db(data *conf.Data_Database) *gorm.DB {
|
||||
|
||||
gormDB, err := gorm.Open(
|
||||
mysql.New(mysql.Config{Conn: mysqlConn}),
|
||||
&gorm.Config{Logger: logger.Default.LogMode(logger.Info)},
|
||||
mysql.Open(data.Source),
|
||||
&gorm.Config{
|
||||
Logger: logger.Default.LogMode(logger.Info),
|
||||
SkipDefaultTransaction: true,
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
|
|
@ -41,19 +36,8 @@ func db(data *conf.Data_Database) (*gorm.DB, func()) {
|
|||
panic("failed to gormDB " + err.Error())
|
||||
}
|
||||
|
||||
// SetMaxIdleConns sets the maximum number of connections in the idle connection pool.
|
||||
sqlDB.SetMaxIdleConns(int(data.MaxIdle))
|
||||
// SetMaxOpenConns sets the maximum number of openapi connections to the database.
|
||||
sqlDB.SetMaxOpenConns(int(data.MaxOpen))
|
||||
// SetConnMaxLifetime sets the maximum amount of time a connection may be reused.
|
||||
sqlDB.SetConnMaxLifetime(time.Hour)
|
||||
sqlDB.SetMaxIdleConns(100)
|
||||
sqlDB.SetMaxOpenConns(1000)
|
||||
|
||||
return gormDB, func() {
|
||||
if mysqlConn != nil {
|
||||
fmt.Println("关闭 db")
|
||||
if err := mysqlConn.Close(); err != nil {
|
||||
fmt.Printf("关闭 db 失败:%v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return gormDB
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,216 @@
|
|||
package data
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"google.golang.org/protobuf/types/known/durationpb"
|
||||
"gorm.io/driver/mysql"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/logger"
|
||||
"gorm.io/hints"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
"voucher/internal/biz/vo"
|
||||
"voucher/internal/conf"
|
||||
"voucher/internal/data/model"
|
||||
)
|
||||
|
||||
var source = "voucher:Lsxd@2024@tcp(voucher.rwlb.cn-chengdu.rds.aliyuncs.com:3306)/voucher?parseTime=True&loc=Local"
|
||||
|
||||
func Test_gorm_db_create(t *testing.T) {
|
||||
|
||||
gormDB, err := gorm.Open(
|
||||
mysql.Open(source),
|
||||
&gorm.Config{
|
||||
Logger: logger.Default.LogMode(logger.Info),
|
||||
SkipDefaultTransaction: true,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal("failed to connect database " + err.Error())
|
||||
}
|
||||
|
||||
sqlDB, err := gormDB.DB()
|
||||
if err != nil {
|
||||
t.Fatal("failed to gormDB " + err.Error())
|
||||
}
|
||||
|
||||
sqlDB.SetMaxIdleConns(20)
|
||||
sqlDB.SetMaxOpenConns(100)
|
||||
|
||||
start2 := time.Now()
|
||||
|
||||
concurrency := 1000 // 调整并发数
|
||||
errCount := 0
|
||||
|
||||
stats := sqlDB.Stats()
|
||||
fmt.Printf("order create 当前打开连接数:%d, 空闲连接数:%d\n", stats.OpenConnections, stats.Idle)
|
||||
|
||||
go func() {
|
||||
ticker := time.NewTicker(50 * time.Millisecond)
|
||||
defer ticker.Stop()
|
||||
for range ticker.C {
|
||||
fmt.Printf("监控: 当前打开连接数:%d, 空闲连接数:%d\n", stats.OpenConnections, stats.Idle)
|
||||
}
|
||||
}()
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(concurrency)
|
||||
|
||||
for i := 0; i < concurrency; i++ {
|
||||
go func(i int) {
|
||||
defer wg.Done()
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
now := time.Now()
|
||||
|
||||
info := &model.Order{
|
||||
OrderNo: fmt.Sprintf("test_create_10_%d", i),
|
||||
OutBizNo: fmt.Sprintf("374252390990605ngywYrSAGE8310_%d", i),
|
||||
ProductNo: "001",
|
||||
BatchNo: "001",
|
||||
Account: "oO3vO5K2nE131-9uMoeYymLhlbYk",
|
||||
AccountType: 1,
|
||||
Status: 2,
|
||||
Type: 1,
|
||||
AppID: "",
|
||||
MerchantNo: "wx9ed74283ad25bca1",
|
||||
Channel: 1,
|
||||
NotifyUrl: "https://sandbox.cdcc.cmbchina.com/AccessGateway/transIn/updateCodeStatus.json",
|
||||
Attach: "{}",
|
||||
CreateTime: &now,
|
||||
UpdateTime: &now,
|
||||
}
|
||||
|
||||
if info.ProductNo == "001" {
|
||||
info.VoucherNo = info.OrderNo
|
||||
info.Status = vo.OrderStatusSuccess.GetValue()
|
||||
}
|
||||
|
||||
ddd := gormDB.WithContext(ctx).Model(model.Order{})
|
||||
tx := ddd.Create(info)
|
||||
|
||||
if tx.Error != nil {
|
||||
fmt.Printf("写入错误: %v\n", tx.Error)
|
||||
errCount += 1
|
||||
}
|
||||
|
||||
sqlDBx, _ := ddd.DB()
|
||||
fmt.Printf("order create 当前打开连接数:%d, 空闲连接数:%d\n", sqlDBx.Stats().OpenConnections, sqlDBx.Stats().Idle)
|
||||
}(i)
|
||||
}
|
||||
|
||||
fmt.Printf("order create 当前打开连接数:%d, 空闲连接数:%d\n", stats.OpenConnections, stats.Idle)
|
||||
|
||||
wg.Wait()
|
||||
t.Logf("\n--------------连接请求,总请求耗时: %v,总数: %d, 失败次数: %d--------------\n", time.Since(start2), concurrency, errCount)
|
||||
}
|
||||
|
||||
func Test_gorm_db(t *testing.T) {
|
||||
// 镜像mysql
|
||||
//data := &conf.Data_Database{
|
||||
// Driver: "mysql",
|
||||
// Source: "root:lsxddb123.@tcp(47.108.53.72:3306)/voucher?parseTime=True&loc=Local",
|
||||
// MaxIdle: 20, // 空闲连接池
|
||||
// MaxOpen: 200, // 最大连接数
|
||||
// MaxLifetime: durationpb.New(60), // 5分钟
|
||||
// IsDebug: false,
|
||||
//}
|
||||
// 测试
|
||||
//data := &conf.Data_Database{
|
||||
// Driver: "mysql",
|
||||
// Source: "root:lansexiongdi6,@tcp(47.97.27.195:3306)/voucher?parseTime=True&loc=Local",
|
||||
// MaxIdle: 2, // 空闲连接池
|
||||
// MaxOpen: 10, // 最大连接数
|
||||
// MaxLifetime: durationpb.New(60),
|
||||
// IsDebug: false,
|
||||
//}
|
||||
data := &conf.Data_Database{
|
||||
Driver: "mysql",
|
||||
Source: "voucher:Lsxd@2024@tcp(voucher.rwlb.cn-chengdu.rds.aliyuncs.com:3306)/voucher?parseTime=True&loc=Local",
|
||||
MaxIdle: 20, // 空闲连接池
|
||||
MaxOpen: 200, // 最大连接数
|
||||
MaxLifetime: durationpb.New(60), // 5分钟
|
||||
IsDebug: false,
|
||||
}
|
||||
gormDb := db(data)
|
||||
|
||||
start2 := time.Now()
|
||||
|
||||
concurrency := 10000 // 调整并发数
|
||||
errCount := 0
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(concurrency)
|
||||
|
||||
for i := 0; i < concurrency; i++ {
|
||||
go func(i int) {
|
||||
defer wg.Done()
|
||||
|
||||
//ctx := context.Background()
|
||||
var info model.Order
|
||||
tx := gormDb.
|
||||
//WithContext(ctx).
|
||||
Model(model.Order{}).
|
||||
//Clauses(hints.UseIndex("udx_out_biz_no_type")).
|
||||
Where(model.Order{Type: vo.OrderTypeCmb.GetValue(), OutBizNo: fmt.Sprintf("174252390990605ngywYrSAGE83e1%d", i)}).
|
||||
First(&info)
|
||||
|
||||
if tx.Error != nil {
|
||||
if errors.Is(tx.Error, gorm.ErrRecordNotFound) {
|
||||
t.Errorf("未找到记录")
|
||||
} else {
|
||||
fmt.Printf("请求错误: %v\n", tx.Error)
|
||||
errCount += 1
|
||||
}
|
||||
}
|
||||
time.Sleep(1 * time.Microsecond)
|
||||
}(i)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
t.Logf("\n--------------连接请求,总请求耗时: %v,总数: %d, 失败次数: %d--------------\n", time.Since(start2), concurrency, errCount)
|
||||
}
|
||||
|
||||
func Test_db(t *testing.T) {
|
||||
maxLifetime := durationpb.New(300) // 5分钟
|
||||
data := &conf.Data_Database{
|
||||
Driver: "mysql",
|
||||
Source: "root:lansexiongdi6,@tcp(47.97.27.195:3306)/voucher?parseTime=True&loc=Local",
|
||||
MaxIdle: 2, // 空闲连接池
|
||||
MaxOpen: 10, // 最大连接数
|
||||
MaxLifetime: maxLifetime,
|
||||
IsDebug: false,
|
||||
}
|
||||
gormDb := db(data)
|
||||
|
||||
start2 := time.Now()
|
||||
|
||||
errCount := 0
|
||||
const concurrency = 1
|
||||
|
||||
for i := 0; i < concurrency; i++ {
|
||||
ctx := context.Background()
|
||||
|
||||
var info model.Order
|
||||
tx := gormDb.
|
||||
WithContext(ctx).
|
||||
Model(model.Order{}).
|
||||
Clauses(hints.ForceIndex("udx_out_biz_no_type")).
|
||||
Where(model.Order{Type: vo.OrderTypeCmb.GetValue(), OutBizNo: fmt.Sprintf("174252390990605ngywYrSAGE83e1%d", i)}).
|
||||
First(&info)
|
||||
if tx.Error != nil {
|
||||
if errors.Is(tx.Error, gorm.ErrRecordNotFound) {
|
||||
t.Errorf("未找到记录")
|
||||
} else {
|
||||
fmt.Printf("请求错误: %v\n", tx.Error)
|
||||
errCount += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
t.Logf("\n--------------连接请求,总请求耗时: %v,总数: %d, 失败次数: %d--------------\n", time.Since(start2), concurrency, errCount)
|
||||
}
|
||||
|
|
@ -0,0 +1,322 @@
|
|||
package mixrepoimpl
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/go-kratos/kratos/v2/log"
|
||||
http2 "github.com/go-kratos/kratos/v2/transport/http"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
err2 "voucher/api/err"
|
||||
v1 "voucher/api/v1"
|
||||
"voucher/internal/biz/bo"
|
||||
"voucher/internal/biz/mixrepos"
|
||||
"voucher/internal/biz/vo"
|
||||
"voucher/internal/conf"
|
||||
"voucher/internal/pkg/cmb"
|
||||
"voucher/internal/pkg/helper"
|
||||
"voucher/internal/pkg/request"
|
||||
)
|
||||
|
||||
type CmbMixRepoImpl struct {
|
||||
bc *conf.Bootstrap
|
||||
|
||||
// 连接池复用(优化网络开销)
|
||||
options *request.Options
|
||||
}
|
||||
|
||||
func NewCmbMixRepoImpl(bc *conf.Bootstrap) mixrepos.CmbMixRepo {
|
||||
|
||||
h := http.Header{
|
||||
"Content-Type": []string{"application/json"},
|
||||
}
|
||||
hc := &http.Client{
|
||||
Timeout: 20 * time.Second,
|
||||
Transport: &http.Transport{
|
||||
MaxIdleConns: 100, // 最大空闲连接数
|
||||
MaxIdleConnsPerHost: 20, // 每个主机的最大空闲连接数
|
||||
IdleConnTimeout: 30 * time.Second, // 空闲连接超时时间
|
||||
},
|
||||
}
|
||||
|
||||
return &CmbMixRepoImpl{
|
||||
bc: bc,
|
||||
options: request.NewOptions(request.WithHeaders(h), request.WithHttpClient(hc)),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *CmbMixRepoImpl) recordBody(ctx context.Context) {
|
||||
|
||||
httpRequest, ok := http2.RequestFromServerContext(ctx)
|
||||
if !ok {
|
||||
log.Errorf("read body not ok")
|
||||
return
|
||||
}
|
||||
|
||||
bodyBytes, err := io.ReadAll(httpRequest.Body)
|
||||
if err != nil {
|
||||
log.Errorf("read body not ok, %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Errorf("body %v", string(bodyBytes))
|
||||
}
|
||||
|
||||
func (s *CmbMixRepoImpl) OrderVerify(ctx context.Context, req *v1.CmbRequest) (*v1.CmbOrderRequest, error) {
|
||||
|
||||
bizStr, err := s.Verify(ctx, req)
|
||||
if err != nil {
|
||||
return nil, err2.ErrorCmbVerifyFail(err.Error())
|
||||
}
|
||||
|
||||
if len(bizStr) == 0 {
|
||||
s.recordBody(ctx)
|
||||
return nil, err2.ErrorCmbBizContentFail("业务参数获取异常,请检查参数是否正确传递")
|
||||
}
|
||||
|
||||
var bizContent *v1.CmbOrderRequest
|
||||
if err = json.Unmarshal([]byte(bizStr), &bizContent); err != nil {
|
||||
return nil, err2.ErrorCmbBizContentFail(err.Error())
|
||||
}
|
||||
|
||||
if err = bizContent.Validate(); err != nil {
|
||||
return nil, err2.ErrorCmbBizContentFail(err.Error())
|
||||
}
|
||||
|
||||
return bizContent, nil
|
||||
}
|
||||
|
||||
func (s *CmbMixRepoImpl) QueryVerify(ctx context.Context, req *v1.CmbRequest) (*v1.CmbQueryRequest, error) {
|
||||
|
||||
bizStr, err := s.Verify(ctx, req)
|
||||
if err != nil {
|
||||
return nil, err2.ErrorCmbVerifyFail(err.Error())
|
||||
}
|
||||
|
||||
if len(bizStr) == 0 {
|
||||
s.recordBody(ctx)
|
||||
return nil, err2.ErrorCmbBizContentFail("业务参数获取异常,请检查参数是否正确传递")
|
||||
}
|
||||
|
||||
var bizContent *v1.CmbQueryRequest
|
||||
if err = json.Unmarshal([]byte(bizStr), &bizContent); err != nil {
|
||||
return nil, err2.ErrorCmbBizContentFail(err.Error())
|
||||
}
|
||||
|
||||
if err = bizContent.Validate(); err != nil {
|
||||
return nil, err2.ErrorCmbBizContentFail(err.Error())
|
||||
}
|
||||
|
||||
return bizContent, nil
|
||||
}
|
||||
|
||||
func (s *CmbMixRepoImpl) ProductQueryVerify(ctx context.Context, req *v1.CmbRequest) (*v1.CmbQueryProductRequest, error) {
|
||||
|
||||
bizStr, err := s.Verify(ctx, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(bizStr) == 0 {
|
||||
s.recordBody(ctx)
|
||||
return nil, err2.ErrorCmbBizContentFail("业务参数获取异常,请检查参数是否正确传递")
|
||||
}
|
||||
|
||||
var bizContent *v1.CmbQueryProductRequest
|
||||
if err = json.Unmarshal([]byte(bizStr), &bizContent); err != nil {
|
||||
return nil, err2.ErrorCmbBizContentFail(err.Error())
|
||||
}
|
||||
|
||||
if err = bizContent.Validate(); err != nil {
|
||||
return nil, err2.ErrorCmbBizContentFail(err.Error())
|
||||
}
|
||||
|
||||
return bizContent, nil
|
||||
}
|
||||
|
||||
func (s *CmbMixRepoImpl) Verify(_ context.Context, req *v1.CmbRequest) (string, error) {
|
||||
|
||||
str := cmb.SortStructStr(req)
|
||||
|
||||
b, err := cmb.VerifyBody(str, req.Sign, s.bc.Cmb.CmbSm2Puk)
|
||||
if err != nil {
|
||||
return "", err2.ErrorCmbVerifyFail(err.Error())
|
||||
}
|
||||
|
||||
if !b {
|
||||
return "", err2.ErrorCmbVerifyFail("签名验证失败")
|
||||
}
|
||||
|
||||
bizBytes, err := cmb.DecryptBody(&cmb.Decrypts{EncryptBody: req.EncryptBody, PrivateKey: s.bc.Cmb.Sm2Prk})
|
||||
if err != nil {
|
||||
return "", err2.ErrorCmbBizContentDecryptFail(err.Error())
|
||||
}
|
||||
|
||||
return string(bizBytes), nil
|
||||
}
|
||||
|
||||
func (s *CmbMixRepoImpl) GetRequest(_ context.Context, reqBo *bo.CmbRequestBo) (*v1.CmbRequest, error) {
|
||||
|
||||
encryptBody, err := cmb.EncryptBody(&cmb.Encrypts{SoaPubKey: s.bc.Cmb.CmbSm2Puk, JsonParam: reqBo.BizContent})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req := &v1.CmbRequest{
|
||||
Mid: s.bc.Cmb.Mid,
|
||||
Aid: s.bc.Cmb.Aid,
|
||||
Date: time.Now().Format("20060102150405"),
|
||||
Random: string(cmb.RandomBytes(16)),
|
||||
KeyAlias: s.bc.Cmb.KeyAlias,
|
||||
CmbKeyAlias: s.bc.Cmb.CmbKeyAlias,
|
||||
EncryptBody: encryptBody,
|
||||
Sign: "",
|
||||
}
|
||||
|
||||
str := fmt.Sprintf("%s?%s", reqBo.FuncName, cmb.SortStructStr(req))
|
||||
|
||||
sign, err := cmb.SignBody(str, s.bc.Cmb.Sm2Prk)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req.Sign = sign
|
||||
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func (s *CmbMixRepoImpl) GetMockRequest(_ context.Context, bizContent string) (*v1.CmbRequest, error) {
|
||||
|
||||
if len(s.bc.Cmb.Sm2Puk) == 0 {
|
||||
return nil, errors.New("mock sm2 puk is empty")
|
||||
}
|
||||
|
||||
if len(s.bc.Cmb.CmbSm2Pik) == 0 {
|
||||
return nil, errors.New("mock cmb sm2 pik is empty")
|
||||
}
|
||||
|
||||
encryptBody, err := cmb.EncryptBody(&cmb.Encrypts{SoaPubKey: s.bc.Cmb.Sm2Puk, JsonParam: bizContent})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req := &v1.CmbRequest{
|
||||
Mid: s.bc.Cmb.Mid,
|
||||
Aid: s.bc.Cmb.Aid,
|
||||
Date: time.Now().Format("20060102150405"),
|
||||
Random: string(cmb.RandomBytes(16)),
|
||||
KeyAlias: s.bc.Cmb.KeyAlias,
|
||||
CmbKeyAlias: s.bc.Cmb.CmbKeyAlias,
|
||||
EncryptBody: encryptBody,
|
||||
Sign: "",
|
||||
}
|
||||
|
||||
sign, err := cmb.SignBody(cmb.SortStructStr(req), s.bc.Cmb.CmbSm2Pik)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req.Sign = sign
|
||||
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func (s *CmbMixRepoImpl) VerifyResponse(_ context.Context, req *v1.CmbReply) error {
|
||||
|
||||
str := cmb.SortStructStr(req)
|
||||
|
||||
b, err := cmb.VerifyBody(str, req.Sign, s.bc.Cmb.CmbSm2Puk)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !b {
|
||||
return errors.New("签名验证失败")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *CmbMixRepoImpl) GetResponse(_ context.Context, reqBo *bo.CmbResponseBo) (*v1.CmbReply, error) {
|
||||
|
||||
reply := &v1.CmbReply{
|
||||
RespCode: reqBo.RespCode,
|
||||
RespMsg: reqBo.RespMsg,
|
||||
Date: time.Now().Format("20060102150405"),
|
||||
KeyAlias: s.bc.Cmb.KeyAlias,
|
||||
CmbKeyAlias: s.bc.Cmb.CmbKeyAlias,
|
||||
EncryptBody: "",
|
||||
Sign: "",
|
||||
}
|
||||
|
||||
if len(reqBo.BizContent) > 0 {
|
||||
encryptBody, err := cmb.EncryptBody(&cmb.Encrypts{SoaPubKey: s.bc.Cmb.CmbSm2Puk, JsonParam: reqBo.BizContent})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
reply.EncryptBody = encryptBody
|
||||
}
|
||||
|
||||
sign, err := cmb.SignBody(cmb.SortStructStr(reply), s.bc.Cmb.Sm2Prk)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
reply.Sign = sign
|
||||
|
||||
return reply, nil
|
||||
}
|
||||
|
||||
func (s *CmbMixRepoImpl) Request(ctx context.Context, req *v1.CmbRequest, uri string) (*v1.CmbReply, error) {
|
||||
|
||||
kvRows := helper.SortStructFieldsByKey(req)
|
||||
|
||||
uv := url.Values{}
|
||||
|
||||
for _, kv := range kvRows {
|
||||
uv.Set(kv.Key, fmt.Sprintf("%v", kv.Value))
|
||||
}
|
||||
|
||||
h := http.Header{
|
||||
"Content-Type": []string{"application/x-www-form-urlencoded"},
|
||||
}
|
||||
|
||||
r := uri + "?" + uv.Encode()
|
||||
|
||||
_, bodyBytes, err := request.Post(ctx, r, nil, request.WithHeaders(h), request.WithTimeout(time.Second*20))
|
||||
if err != nil {
|
||||
log.Errorf("请求掌上生活报错,url:%s,err:%v", r, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var response *v1.CmbReply
|
||||
if err = json.Unmarshal(bodyBytes, &response); err != nil {
|
||||
log.Errorf("请求掌上生活返回数据解析报错:%s,url:%s,bodyBytes:%s", err.Error(), r, string(bodyBytes))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if response.RespCode != vo.CmbResponseStatusSuccess.GetValue() {
|
||||
log.Errorf("请求掌上生活返回报错:msg:%s,url:%s,bodyBytes:%s", response.RespMsg, r, string(bodyBytes))
|
||||
return nil, fmt.Errorf(response.RespMsg)
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (s *CmbMixRepoImpl) Decrypt(_ context.Context, encryptBody string) (string, error) {
|
||||
|
||||
if len(s.bc.Cmb.CmbSm2Pik) == 0 {
|
||||
return "", errors.New("mock CmbSm2Pik is empty")
|
||||
}
|
||||
|
||||
rs, err := cmb.DecryptBody(&cmb.Decrypts{EncryptBody: encryptBody, PrivateKey: s.bc.Cmb.CmbSm2Pik})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(rs), nil
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
package mixrepoimpl
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"voucher/internal/biz/mixrepos"
|
||||
"voucher/internal/conf"
|
||||
"voucher/internal/pkg/ding"
|
||||
)
|
||||
|
||||
type DingMixRepoImpl struct {
|
||||
bc *conf.Bootstrap
|
||||
|
||||
client *ding.TalkClient
|
||||
}
|
||||
|
||||
func NewDingMixRepoImpl(bc *conf.Bootstrap) mixrepos.DingMixRepo {
|
||||
client := ding.NewDingTalkClient(bc.Alarm.WebhookURL, bc.Alarm.Secret)
|
||||
|
||||
return &DingMixRepoImpl{bc: bc, client: client}
|
||||
}
|
||||
|
||||
func (s *DingMixRepoImpl) SendMessage(_ context.Context, title, text string) error {
|
||||
|
||||
if err := s.client.SendMarkdownMessage(title, text, s.bc.Alarm.WarningMobiles, false); err != nil {
|
||||
return fmt.Errorf("markdown 消息发送失败: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *DingMixRepoImpl) SendMarkdownMessage(_ context.Context, title, text string) error {
|
||||
|
||||
isAtAll := false
|
||||
if len(s.bc.Alarm.AtMobiles) == 0 {
|
||||
isAtAll = true
|
||||
}
|
||||
|
||||
if err := s.client.SendMarkdownMessage(title, text, s.bc.Alarm.AtMobiles, isAtAll); err != nil {
|
||||
return fmt.Errorf("markdown 消息发送失败: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
package mixrepoimpl
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/bwmarrin/snowflake"
|
||||
"os"
|
||||
"voucher/internal/biz/mixrepos"
|
||||
"voucher/internal/pkg/helper"
|
||||
)
|
||||
|
||||
type GenerateRepoImpl struct {
|
||||
node *snowflake.Node
|
||||
}
|
||||
|
||||
func NewGenerateMixRepoImpl() (mixrepos.GenerateMixRepo, error) {
|
||||
g := &GenerateRepoImpl{}
|
||||
|
||||
name, err := os.Hostname()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
serverId := helper.HashMod(name)
|
||||
|
||||
node, err := snowflake.NewNode(int64(serverId))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
g.node = node
|
||||
|
||||
return g, nil
|
||||
}
|
||||
|
||||
// GeneratorString 生成字符串
|
||||
func (s *GenerateRepoImpl) GeneratorString(_ context.Context, uid string) string {
|
||||
id := helper.HashMod(uid)
|
||||
return fmt.Sprintf("%s%d", s.node.Generate().String(), id)
|
||||
}
|
||||
|
||||
// GeneratorNumber 生成 int64
|
||||
func (s *GenerateRepoImpl) GeneratorNumber(_ context.Context, uid string) int64 {
|
||||
id := helper.HashMod(uid)
|
||||
return s.node.Generate().Int64() + int64(id)
|
||||
}
|
||||
|
|
@ -1,28 +1,28 @@
|
|||
package thirdrepositoryimpl
|
||||
package mixrepoimpl
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/go-kratos/kratos/v2/log"
|
||||
"voucher/internal/biz/thirdrepository"
|
||||
"voucher/internal/biz/mixrepos"
|
||||
"voucher/internal/data"
|
||||
"voucher/internal/pkg/mq"
|
||||
)
|
||||
|
||||
type ThirdMQSendImpl struct {
|
||||
type MQSendMixRepoImpl struct {
|
||||
mq *data.RocketMQ
|
||||
}
|
||||
|
||||
func NewMQSendImpl(mq *data.RocketMQ) thirdrepository.ThirdMQSend {
|
||||
return &ThirdMQSendImpl{
|
||||
func NewMQSendMixRepoImpl(mq *data.RocketMQ) mixrepos.MQSendMixRepo {
|
||||
return &MQSendMixRepoImpl{
|
||||
mq: mq,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ThirdMQSendImpl) SendSync(ctx context.Context, topicName string, body []byte, sendOptions ...mq.SendOption) error {
|
||||
func (s *MQSendMixRepoImpl) SendSync(ctx context.Context, topicName string, body []byte, sendOptions ...mq.SendOption) error {
|
||||
return s.mq.MqProducer.SendSync(ctx, topicName, body, sendOptions...)
|
||||
}
|
||||
|
||||
func (s *ThirdMQSendImpl) SendASync(ctx context.Context, topicName string, body []byte, errFn func(error), sendOptions ...mq.SendOption) error {
|
||||
func (s *MQSendMixRepoImpl) SendASync(ctx context.Context, topicName string, body []byte, errFn func(error), sendOptions ...mq.SendOption) error {
|
||||
err := s.mq.MqProducer.SendAsync(ctx, topicName, body, func(err error) {
|
||||
if err == nil {
|
||||
return
|
||||
|
|
@ -33,7 +33,7 @@ func (s *ThirdMQSendImpl) SendASync(ctx context.Context, topicName string, body
|
|||
return err
|
||||
}
|
||||
|
||||
func (s *ThirdMQSendImpl) SendAsyncNotFn(ctx context.Context, topic string, body []byte, sendOptions ...mq.SendOption) error {
|
||||
func (s *MQSendMixRepoImpl) SendAsyncNotFn(ctx context.Context, topic string, body []byte, sendOptions ...mq.SendOption) error {
|
||||
err := s.mq.MqProducer.SendAsync(ctx, topic, body, func(err error) {
|
||||
if err == nil {
|
||||
return
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package mixrepoimpl
|
||||
|
||||
import (
|
||||
"github.com/google/wire"
|
||||
)
|
||||
|
||||
// ProviderMixRepoImplSet is providers.
|
||||
var ProviderMixRepoImplSet = wire.NewSet(
|
||||
NewGenerateMixRepoImpl,
|
||||
NewMQSendMixRepoImpl,
|
||||
NewCmbMixRepoImpl,
|
||||
NewDingMixRepoImpl,
|
||||
NewSmsMixRepoImpl,
|
||||
)
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
package mixrepoimpl
|
||||
|
||||
import (
|
||||
"context"
|
||||
"voucher/internal/biz/mixrepos"
|
||||
"voucher/internal/conf"
|
||||
"voucher/internal/pkg/sms"
|
||||
)
|
||||
|
||||
type SmsMixRepoImpl struct {
|
||||
smsService sms.Service
|
||||
}
|
||||
|
||||
func NewSmsMixRepoImpl(bc *conf.Bootstrap) (mixrepos.SmsMixRepo, error) {
|
||||
|
||||
config := sms.Config{
|
||||
AccessKeyID: bc.AliYunSms.AccessKeyId,
|
||||
AccessKeySecret: bc.AliYunSms.AccessKeySecret,
|
||||
Endpoint: bc.AliYunSms.Endpoint,
|
||||
SignName: bc.AliYunSms.SignName,
|
||||
RetryTimes: 1,
|
||||
Timeout: 15,
|
||||
}
|
||||
|
||||
smsService, err := sms.NewService(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &SmsMixRepoImpl{smsService: smsService}, nil
|
||||
}
|
||||
|
||||
func (s *SmsMixRepoImpl) Send(ctx context.Context, phoneNumbers []string, templateCode string, params map[string]string) error {
|
||||
return s.smsService.SendSMS(ctx, phoneNumbers, templateCode, params)
|
||||
}
|
||||
|
|
@ -12,17 +12,28 @@ const TableNameOrder = "order"
|
|||
|
||||
// Order mapped from table <order>
|
||||
type Order struct {
|
||||
ID int64 `gorm:"column:id;primaryKey;autoIncrement:true" json:"id"`
|
||||
OrderNo string `gorm:"column:order_no;not null" json:"order_no"`
|
||||
OutBizNo string `gorm:"column:out_biz_no;not null;comment:外部交易号" json:"out_biz_no"` // 外部交易号
|
||||
ProductNo string `gorm:"column:product_no;not null;comment:商品编号" json:"product_no"` // 商品编号
|
||||
Account string `gorm:"column:account;not null;comment:充值账号" json:"account"` // 充值账号
|
||||
AccountType uint8 `gorm:"column:account_type;not null;comment:1:oepnid/userid 2:手机号" json:"account_type"` // 1:oepnid/userid 2:手机号
|
||||
Status uint8 `gorm:"column:status;not null;comment:1:待发放 2:发放中 3:发放成功 4:发放失败" json:"status"` // 1:待发放 2:发放中 3:发放成功 4:发放失败
|
||||
AppID string `gorm:"column:app_id;not null;comment:批次所属应用" json:"app_id"` // 批次所属应用
|
||||
MerchantNo string `gorm:"column:merchant_no;not null;comment:创建批次号的商户号" json:"merchant_no"` // 创建批次号的商户号
|
||||
Channel uint8 `gorm:"column:channel;not null;comment:1:微信 2:支付宝" json:"channel"` // 1:微信 2:支付宝
|
||||
CreateTime *time.Time `gorm:"column:create_time" json:"create_time"`
|
||||
ID uint64 `gorm:"column:id;primaryKey;autoIncrement:true" json:"id"`
|
||||
OrderNo string `gorm:"column:order_no;not null" json:"order_no"`
|
||||
VoucherNo string `gorm:"column:voucher_no;not null" json:"voucher_no"`
|
||||
OutBizNo string `gorm:"column:out_biz_no;not null;comment:外部交易号" json:"out_biz_no"` // 外部交易号
|
||||
ProductNo string `gorm:"column:product_no;not null;comment:商品编号" json:"product_no"` // 商品编号
|
||||
BatchNo string `gorm:"column:batch_no;not null;comment:立减金批次号" json:"batch_no"` // 立减金批次号
|
||||
ActivityId string `gorm:"column:activity_id;not null;comment:activity_id" json:"activity_id"` // activity_id
|
||||
Account string `gorm:"column:account;not null;comment:充值账号" json:"account"` // 充值账号
|
||||
AccountType uint8 `gorm:"column:account_type;not null;comment:1:oepnid/userid 2:手机号" json:"account_type"` // 1:oepnid/userid 2:手机号
|
||||
Type uint8 `gorm:"column:type;not null;comment:1:招行" json:"type"`
|
||||
Status uint8 `gorm:"column:status;not null;comment:1:待发放 2:发放中 3:发放成功 4:发放失败" json:"status"` // 1:待发放 2:发放中 3:发放成功 4:发放失败
|
||||
AppID string `gorm:"column:app_id;not null;comment:批次所属应用" json:"app_id"` // 批次所属应用
|
||||
MerchantNo string `gorm:"column:merchant_no;not null;comment:创建批次号的商户号" json:"merchant_no"` // 创建批次号的商户号
|
||||
NotifyUrl string `gorm:"column:notify_url;not null;comment:回调地址" json:"notify_url"`
|
||||
Channel uint8 `gorm:"column:channel;not null;comment:1:微信 2:支付宝" json:"channel"` // 1:微信 2:支付宝
|
||||
Remark string `gorm:"column:remark;not null;comment:remark" json:"remark"`
|
||||
Attach string `gorm:"column:attach;not null;comment:attach" json:"attach"`
|
||||
ReceiveSuccessTime *time.Time `gorm:"column:receive_success_time" json:"receive_success_time"`
|
||||
LastUseTime *time.Time `gorm:"column:last_use_time" json:"last_use_time"`
|
||||
TransactionId string `gorm:"column:transaction_id;not null" json:"transaction_id"`
|
||||
CreateTime *time.Time `gorm:"column:create_time" json:"create_time"`
|
||||
UpdateTime *time.Time `gorm:"column:update_time" json:"update_time"`
|
||||
}
|
||||
|
||||
// TableName Order's table name
|
||||
|
|
|
|||
|
|
@ -0,0 +1,42 @@
|
|||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
const TableNameOrderBak = "order_bak"
|
||||
|
||||
// Order mapped from table <order>
|
||||
type OrderBak struct {
|
||||
ID uint64 `gorm:"column:id;primaryKey;autoIncrement:true" json:"id"`
|
||||
OrderNo string `gorm:"column:order_no;not null" json:"order_no"`
|
||||
VoucherNo string `gorm:"column:voucher_no;not null" json:"voucher_no"`
|
||||
OutBizNo string `gorm:"column:out_biz_no;not null;comment:外部交易号" json:"out_biz_no"` // 外部交易号
|
||||
ProductNo string `gorm:"column:product_no;not null;comment:商品编号" json:"product_no"` // 商品编号
|
||||
BatchNo string `gorm:"column:batch_no;not null;comment:立减金批次号" json:"batch_no"` // 立减金批次号
|
||||
ActivityId string `gorm:"column:activity_id;not null;comment:activity_id" json:"activity_id"` // activity_id
|
||||
Account string `gorm:"column:account;not null;comment:充值账号" json:"account"` // 充值账号
|
||||
AccountType uint8 `gorm:"column:account_type;not null;comment:1:oepnid/userid 2:手机号" json:"account_type"` // 1:oepnid/userid 2:手机号
|
||||
Type uint8 `gorm:"column:type;not null;comment:1:招行" json:"type"`
|
||||
Status uint8 `gorm:"column:status;not null;comment:1:待发放 2:发放中 3:发放成功 4:发放失败" json:"status"` // 1:待发放 2:发放中 3:发放成功 4:发放失败
|
||||
AppID string `gorm:"column:app_id;not null;comment:批次所属应用" json:"app_id"` // 批次所属应用
|
||||
MerchantNo string `gorm:"column:merchant_no;not null;comment:创建批次号的商户号" json:"merchant_no"` // 创建批次号的商户号
|
||||
NotifyUrl string `gorm:"column:notify_url;not null;comment:回调地址" json:"notify_url"`
|
||||
Channel uint8 `gorm:"column:channel;not null;comment:1:微信 2:支付宝" json:"channel"` // 1:微信 2:支付宝
|
||||
Remark string `gorm:"column:remark;not null;comment:remark" json:"remark"`
|
||||
Attach string `gorm:"column:attach;not null;comment:attach" json:"attach"`
|
||||
ReceiveSuccessTime *time.Time `gorm:"column:receive_success_time" json:"receive_success_time"`
|
||||
LastUseTime *time.Time `gorm:"column:last_use_time" json:"last_use_time"`
|
||||
TransactionId string `gorm:"column:transaction_id;not null" json:"transaction_id"`
|
||||
CreateTime *time.Time `gorm:"column:create_time" json:"create_time"`
|
||||
UpdateTime *time.Time `gorm:"column:update_time" json:"update_time"`
|
||||
}
|
||||
|
||||
// TableName Order's table name
|
||||
func (*OrderBak) TableName() string {
|
||||
return TableNameOrderBak
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
const TableNameOrderNotify = "order_notify"
|
||||
|
||||
// OrderNotify mapped from table <order_notify>
|
||||
type OrderNotify struct {
|
||||
ID uint64 `gorm:"column:id;primaryKey;autoIncrement:true" json:"id"`
|
||||
OrderNo string `gorm:"column:order_no;not null" json:"order_no"`
|
||||
Status uint8 `gorm:"column:status;not null;comment:状态" json:"status"`
|
||||
Event uint8 `gorm:"column:event;not null;comment:event" json:"event"`
|
||||
Channel uint8 `gorm:"column:channel;not null;comment:channel" json:"channel"`
|
||||
Type uint8 `gorm:"column:type;not null;comment:1:招行" json:"type"`
|
||||
Request string `gorm:"column:request;not null" json:"request"`
|
||||
Responses string `gorm:"column:responses" json:"responses"`
|
||||
Remark string `gorm:"column:remark" json:"remark"`
|
||||
NotifyUrl string `gorm:"column:notify_url;not null;comment:回调地址" json:"notify_url"`
|
||||
CreateTime *time.Time `gorm:"column:create_time;not null" json:"create_time"`
|
||||
UpdateTime *time.Time `gorm:"column:update_time" json:"update_time"`
|
||||
}
|
||||
|
||||
// TableName OrderNotify's table name
|
||||
func (*OrderNotify) TableName() string {
|
||||
return TableNameOrderNotify
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
const TableNameProduct = "product"
|
||||
|
||||
// Product mapped from table <product>
|
||||
type Product struct {
|
||||
ID int32 `gorm:"column:id;primaryKey;autoIncrement:true" json:"id"`
|
||||
Name string `gorm:"column:name;not null;comment:商品名称" json:"name"` // 商品名称
|
||||
ProductNo string `gorm:"column:product_no;not null;comment:商品编号" json:"product_no"` // 商品编号
|
||||
BatchName string `gorm:"column:batch_name;not null;comment:批次名称" json:"batch_name"` // 批次名称
|
||||
BatchNo string `gorm:"column:batch_no;not null;comment:立减金批次号" json:"batch_no"` // 立减金批次号
|
||||
ActivityId string `gorm:"column:activity_id;not null;comment:activity_id" json:"activity_id"` // activity_id
|
||||
MchId string `gorm:"column:mch_id;not null;comment:商户号,创建批次的商户号" json:"mch_id"` // 商户号,创建批次的商户号
|
||||
Channel uint8 `gorm:"column:channel;not null;comment:1:微信 2:支付宝" json:"channel"` // 1:微信 2:支付宝
|
||||
AvailableType uint8 `gorm:"column:available_type;not null;comment:1:固定有效期 2:动态有效期" json:"available_type"`
|
||||
AvailableDays uint32 `gorm:"column:available_days;not null;comment:领取后多少天内" json:"available_days"`
|
||||
Amount int64 `gorm:"column:amount;not null;default:0" json:"amount"`
|
||||
AllBudget int64 `gorm:"column:all_budget;not null;default:0" json:"all_budget"`
|
||||
AvailableBudget int64 `gorm:"column:available_budget;not null;default:0" json:"available_budget"`
|
||||
WarningBudget int64 `gorm:"column:warning_budget;not null;default:0" json:"warning_budget"` // 预警预算=0不做预警
|
||||
WarningPerson string `gorm:"column:warning_person" json:"warning_person"`
|
||||
StartTime *time.Time `gorm:"column:start_time;not null" json:"start_time"`
|
||||
EndTime *time.Time `gorm:"column:end_time;not null" json:"end_time"`
|
||||
CreateTime *time.Time `gorm:"column:create_time;not null" json:"create_time"`
|
||||
UpdateTime *time.Time `gorm:"column:update_time" json:"update_time"`
|
||||
}
|
||||
|
||||
// TableName Product's table name
|
||||
func (*Product) TableName() string {
|
||||
return TableNameProduct
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
const TableNameWechatNotifyRegisterTag = "wechat_notify_register_tag"
|
||||
|
||||
// WechatNotifyRegisterTag mapped from table <wechat_notify_register_tag>
|
||||
type WechatNotifyRegisterTag struct {
|
||||
ID int32 `gorm:"column:id;primaryKey;autoIncrement:true" json:"id"`
|
||||
StockID string `gorm:"column:stock_id;not null" json:"stock_id"`
|
||||
StockCreatorMchID string `gorm:"column:stock_creator_mch_id;not null" json:"stock_creator_mch_id"`
|
||||
Tag string `gorm:"column:tag;not null" json:"tag"`
|
||||
Status uint8 `gorm:"column:status;not null" json:"status"`
|
||||
Remark string `gorm:"column:remark;not null" json:"remark"`
|
||||
CreateTime *time.Time `gorm:"column:create_time;not null" json:"create_time"`
|
||||
UpdateTime *time.Time `gorm:"column:update_time" json:"update_time"`
|
||||
}
|
||||
|
||||
// TableName WechatNotifyRegisterTag's table name
|
||||
func (*WechatNotifyRegisterTag) TableName() string {
|
||||
return TableNameWechatNotifyRegisterTag
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue