Compare commits
1989 Commits
5e987ac99e
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cde7d11563 | ||
|
|
cc437c32c2 | ||
|
|
dfa7cf159e | ||
|
|
b1e0bf5cb5 | ||
|
|
738c35a25a | ||
|
|
1b87bde859 | ||
|
|
936813003b | ||
|
|
411dbaa068 | ||
|
|
634e7c8a6b | ||
|
|
fe15525b8e | ||
|
|
ea3098ec66 | ||
|
|
55a2d4b80f | ||
|
|
863c2c44d4 | ||
|
|
c40ee75121 | ||
|
|
3a3d1d2f4f | ||
|
|
9170b072d7 | ||
|
|
d38b283460 | ||
|
|
140fda8927 | ||
|
|
b067647050 | ||
|
|
e0fe502f49 | ||
|
|
a0fa918322 | ||
|
|
8698b920a9 | ||
|
|
e8d7abe61b | ||
|
|
0ee1912fae | ||
|
|
068c25356b | ||
|
|
cdc91763d1 | ||
|
|
b26842caf8 | ||
|
|
7b4fecc3ec | ||
|
|
691e65f369 | ||
|
|
9d85e74fb0 | ||
|
|
77888cc17c | ||
|
|
aaa6cd8178 | ||
|
|
5ef56867a0 | ||
|
|
cc429e785d | ||
|
|
2068ea6bf6 | ||
|
|
78e9c177ea | ||
|
|
59af1a146c | ||
|
|
6cffb31d83 | ||
|
|
f45fa869a1 | ||
|
|
8754f1949e | ||
|
|
5b7ac4700e | ||
|
|
09e4414d0e | ||
|
|
07635aa5ba | ||
|
|
70ac32e833 | ||
|
|
adec1bf126 | ||
|
|
66fdea2164 | ||
|
|
f4e77ec21e | ||
|
|
fb73d38d31 | ||
|
|
72960b6ced | ||
|
|
885a5e885b | ||
|
|
4f58c18c52 | ||
|
|
c0d0669e21 | ||
|
|
49b9213c52 | ||
|
|
d23b51252e | ||
|
|
9c385b3198 | ||
|
|
e659cf3f7f | ||
|
|
89a1ec23b1 | ||
|
|
e30bd47ea6 | ||
|
|
f57995ddb5 | ||
|
|
1947db34c0 | ||
|
|
4ad32dc2b7 | ||
|
|
39271aa7c7 | ||
|
|
cc77ae075d | ||
|
|
bc710e48b5 | ||
|
|
b74879f91a | ||
|
|
e294beba0d | ||
|
|
d9f04a9842 | ||
|
|
ee34c419bb | ||
|
|
e61fcb74b7 | ||
|
|
82a4bf47de | ||
|
|
f9b7b1d32e | ||
|
|
86f58bd376 | ||
|
|
8a179a496a | ||
|
|
e5b00c0a79 | ||
|
|
d12902d678 | ||
|
|
db16f2950f | ||
|
|
8274bc46db | ||
|
|
ff5debfe44 | ||
|
|
bc38e21668 | ||
|
|
fb3ac9051d | ||
|
|
1b1301fc0e | ||
|
|
901171fa6e | ||
|
|
3f4ae785b0 | ||
|
|
8a81ab9c3b | ||
|
|
04b41d6e44 | ||
|
|
517c443332 | ||
|
|
3e47450078 | ||
|
|
e5467de15e | ||
|
|
c40e026dc5 | ||
|
|
4119ae3eb6 | ||
|
|
d1fcacc441 | ||
|
|
9cfeafb01c | ||
|
|
8bd114f281 | ||
|
|
7397b232d9 | ||
|
|
230328c38a | ||
|
|
6f92cd6f0d | ||
|
|
a09223b091 | ||
|
|
a41fe05e63 | ||
|
|
d81855d486 | ||
|
|
68f8294c20 | ||
|
|
d9e565a5c4 | ||
|
|
763ab8569c | ||
|
|
9f5438ce92 | ||
|
|
e12bb0a2dd | ||
|
|
e58d009928 | ||
|
|
2853d14e60 | ||
|
|
473c3291b8 | ||
|
|
16b0f7ef33 | ||
|
|
39a7164d4a | ||
|
|
19546f82f6 | ||
|
|
2232af6d1c | ||
|
|
ede44f40f7 | ||
|
|
00647ebaf4 | ||
|
|
3ceef530d4 | ||
|
|
d57beb9a4a | ||
|
|
fb1cffd140 | ||
|
|
ac7075fab4 | ||
|
|
143a0f38dc | ||
|
|
eacb7e0ccb | ||
|
|
f060920812 | ||
|
|
f350ed7044 | ||
|
|
8260d2e2dd | ||
|
|
8acd6030f4 | ||
|
|
c15a96fcf5 | ||
|
|
f0718ef83c | ||
|
|
7b645cc5da | ||
|
|
10ca5606c2 | ||
|
|
9c8704f252 | ||
|
|
2176d6ea6f | ||
|
|
a913ce904f | ||
|
|
4b7ff1aed3 | ||
|
|
1190b8910a | ||
|
|
d48eff03b8 | ||
|
|
71bee4fef2 | ||
|
|
4301c9a90a | ||
|
|
cec46e4654 | ||
|
|
4728178c0b | ||
|
|
7352ecc7fa | ||
|
|
71cb042b46 | ||
|
|
a2cf621fb7 | ||
|
|
778f17f3f6 | ||
|
|
64106dcd64 | ||
|
|
33e6ca5104 | ||
|
|
0ab78607e1 | ||
|
|
8b133e6b6a | ||
|
|
3401ad1086 | ||
|
|
66ff82e0be | ||
|
|
677d73e180 | ||
|
|
b6c9982390 | ||
|
|
263b1daa96 | ||
|
|
6c687b9431 | ||
|
|
6889d62b08 | ||
|
|
9a08503a2f | ||
|
|
36602a9cea | ||
|
|
d7df047549 | ||
|
|
20af1c2fe1 | ||
|
|
40611d9418 | ||
|
|
d00d1cd16d | ||
|
|
2608d1ed2f | ||
|
|
1bb6c86b4a | ||
|
|
d5d9f509d7 | ||
|
|
2d8838c3d7 | ||
|
|
38891c676a | ||
|
|
145211bbe2 | ||
|
|
e19d03ebba | ||
|
|
10fb5c7cb1 | ||
|
|
91abfc8835 | ||
|
|
d946c5018a | ||
|
|
7d8b48bb36 | ||
|
|
cd1c03c834 | ||
|
|
0c41bf7c42 | ||
|
|
a7f28b59de | ||
|
|
10811a669b | ||
|
|
14ec99a8be | ||
|
|
89f9dd5b6e | ||
|
|
828264a51b | ||
|
|
e83f7b7a17 | ||
|
|
5ac2885861 | ||
|
|
2780df1dbd | ||
|
|
9dd291b132 | ||
|
|
ffe72bcdbd | ||
|
|
b7bd0015ef | ||
|
|
71cb0a62a4 | ||
|
|
3172f67e20 | ||
|
|
91b95bb2cc | ||
|
|
aad04d5043 | ||
|
|
06dca66631 | ||
|
|
c1fbee7d22 | ||
|
|
5f2ce8e415 | ||
|
|
c4ac39eea8 | ||
|
|
71419171c8 | ||
|
|
25fea4d861 | ||
|
|
b3901e6a7c | ||
|
|
3c67bc1b65 | ||
|
|
a7827d893d | ||
|
|
e1d865e42a | ||
|
|
2b82822fb4 | ||
|
|
9210794b35 | ||
|
|
f6ce015ef5 | ||
|
|
511528848a | ||
|
|
c0024d2421 | ||
|
|
3cf5025ff2 | ||
|
|
10f7928e3a | ||
|
|
a0e179819e | ||
|
|
8919115a58 | ||
|
|
29bf5da9e5 | ||
|
|
e1241c7af1 | ||
|
|
a7f116a3ac | ||
|
|
30deedf220 | ||
|
|
cbf8818d0d | ||
|
|
d2b5df3393 | ||
|
|
d8a2b91ebf | ||
|
|
a19207eccc | ||
|
|
c390178dc6 | ||
|
|
e3c7ae9181 | ||
|
|
896273f2c2 | ||
|
|
2b143c0fd2 | ||
|
|
6193d17b8b | ||
|
|
9b2f3bb49a | ||
|
|
2026d07e47 | ||
|
|
106caf9666 | ||
|
|
d49b104930 | ||
|
|
5b5a132fe6 | ||
|
|
43638334fc | ||
|
|
88c1aa2e5e | ||
|
|
46287cbb46 | ||
|
|
048a84a026 | ||
|
|
0bf5783a78 | ||
|
|
dcc1b509fa | ||
|
|
cc07e1c177 | ||
|
|
f8727f9604 | ||
|
|
fbf4932724 | ||
|
|
a593d57a33 | ||
|
|
273072882c | ||
|
|
d16d91bf00 | ||
|
|
41f919deca | ||
|
|
4e29eea5ed | ||
|
|
e2edd85b73 | ||
|
|
48d7bfc8d5 | ||
|
|
f6c3442ef5 | ||
|
|
b60f2e2d78 | ||
|
|
d2b841b688 | ||
|
|
06615102ca | ||
|
|
96428eecc3 | ||
|
|
323fe01518 | ||
|
|
6fae4fd46b | ||
|
|
184c35bc9b | ||
|
|
32eb8cefe7 | ||
|
|
b79603caf5 | ||
|
|
ebabe353c8 | ||
|
|
e60f57b9db | ||
|
|
7ff36b93b7 | ||
|
|
9dbaaeda71 | ||
|
|
2f23954966 | ||
|
|
58f646d930 | ||
|
|
c5ba6bf317 | ||
|
|
4e2a0919e6 | ||
|
|
36ec8f5036 | ||
|
|
725d842374 | ||
|
|
3d082a954a | ||
|
|
4cedb56314 | ||
|
|
3a7d377b91 | ||
|
|
fd0ab123fc | ||
|
|
f4bb820b56 | ||
|
|
40d9677a26 | ||
|
|
c51fc666b1 | ||
|
|
87cf0e98d7 | ||
|
|
1859e57aa3 | ||
|
|
29e11663fc | ||
|
|
3f4b979c67 | ||
|
|
1276210d49 | ||
|
|
3a2dfb1534 | ||
|
|
c2749ba5d1 | ||
|
|
dcb5377bd5 | ||
|
|
06671a4e9b | ||
|
|
c06e60cc8e | ||
|
|
a52fcd06b6 | ||
|
|
51e9cb4304 | ||
|
|
469b99849c | ||
|
|
6323b4fdc8 | ||
|
|
45f0cddfd6 | ||
|
|
63b5e1aa54 | ||
|
|
3c2978bcf5 | ||
|
|
0252ab0f53 | ||
|
|
38a162969b | ||
|
|
998ebe942f | ||
|
|
56fb0eb6ad | ||
|
|
1062523b2f | ||
|
|
ecf155e9a9 | ||
|
|
f7a9f3c033 | ||
|
|
f8a8907e75 | ||
|
|
2586ba37db | ||
|
|
a2025bf705 | ||
|
|
256dc9a102 | ||
|
|
05604201cb | ||
|
|
794a31a74e | ||
|
|
2df3e2d6c8 | ||
|
|
4f07f32834 | ||
|
|
cf23588f4c | ||
|
|
4282e226b0 | ||
|
|
b2e420c0cc | ||
|
|
1dbe19eb4f | ||
|
|
396a02a40d | ||
|
|
22c0a41f35 | ||
|
|
f5ce85306c | ||
|
|
2daf502fa4 | ||
|
|
e2140a3723 | ||
|
|
e684b3c15c | ||
|
|
2a349c0f44 | ||
|
|
b7b34bd6d2 | ||
|
|
0999414560 | ||
|
|
050da7cc59 | ||
|
|
4df658305a | ||
|
|
5f230d68d9 | ||
|
|
7a0bc2ec02 | ||
|
|
b2bd2facfe | ||
|
|
f89fd3adab | ||
|
|
565e3b66d6 | ||
|
|
676c7bc46d | ||
|
|
fb445b6e2f | ||
|
|
ee1e9f6427 | ||
|
|
38dbdcbdc9 | ||
|
|
ea0426a88e | ||
|
|
9e75e768e3 | ||
|
|
27336a8da6 | ||
|
|
e0c802ad71 | ||
|
|
cb76826acb | ||
|
|
71d5990552 | ||
|
|
391a5a74a3 | ||
|
|
f06350751e | ||
|
|
dc257c74c1 | ||
|
|
184339d848 | ||
|
|
c187a3418d | ||
|
|
ee33b5d3a7 | ||
|
|
c634c56114 | ||
|
|
4e90f3e6d8 | ||
|
|
9cd5285598 | ||
|
|
db08ac99a5 | ||
|
|
5aaf6c1d2c | ||
|
|
e885b0e055 | ||
|
|
bfd2f930c1 | ||
|
|
5aeeb4a941 | ||
|
|
7feb689ed3 | ||
|
|
eea15cf7c0 | ||
|
|
734b37c609 | ||
|
|
6f72310df7 | ||
|
|
5396545a7c | ||
|
|
6d1023deea | ||
|
|
be99f08a31 | ||
|
|
48fe15173a | ||
|
|
e86f6e3d4c | ||
|
|
c827df8e74 | ||
|
|
1926e3b312 | ||
|
|
80a967622c | ||
|
|
e63c7a0286 | ||
|
|
559c5b7f2b | ||
|
|
4910288c94 | ||
|
|
00ee8aba4f | ||
|
|
cee47b50b9 | ||
|
|
6c5310f64c | ||
|
|
aac18a21dc | ||
|
|
ad88235942 | ||
|
|
f388a43480 | ||
|
|
faa10e4069 | ||
|
|
90a3720de6 | ||
|
|
0de165f792 | ||
|
|
cf0a41ff80 | ||
|
|
1b32594473 | ||
|
|
9f78cd10c2 | ||
|
|
02f0e297e2 | ||
|
|
cff1aed9e9 | ||
|
|
5b1e37ffd7 | ||
|
|
b1f65097d4 | ||
|
|
cd4eac9e34 | ||
|
|
863c062a33 | ||
|
|
abac0b5576 | ||
|
|
8424c617da | ||
|
|
03ef52656c | ||
|
|
921255c4e8 | ||
|
|
eefcf2ec1e | ||
|
|
5a311c8e34 | ||
|
|
4579a52315 | ||
|
|
a63ac58140 | ||
|
|
e9e8859406 | ||
|
|
4a779a08a5 | ||
|
|
921a3bfffd | ||
|
|
177156dcba | ||
|
|
8229f06272 | ||
|
|
761bd82a96 | ||
|
|
ab855c884f | ||
|
|
521039d001 | ||
|
|
3fa4dd77ef | ||
|
|
f47d85d691 | ||
|
|
30d521fc44 | ||
|
|
4022b8eab9 | ||
|
|
df0f832242 | ||
|
|
c037b8df1f | ||
|
|
ee614ea369 | ||
|
|
63f8ec3cea | ||
|
|
36c3b3cc78 | ||
|
|
cfeb6f8858 | ||
|
|
4a907607e3 | ||
|
|
31e615d3de | ||
|
|
9d5e9b10c0 | ||
|
|
c57d3dabb5 | ||
|
|
b4688c4a77 | ||
|
|
2c25f3bfa3 | ||
|
|
8f6f185a30 | ||
|
|
02f40a608a | ||
|
|
09c3db51c9 | ||
|
|
897ecdcca0 | ||
|
|
afff3d43c1 | ||
|
|
4f2ef2ddda | ||
|
|
e32b46635a | ||
|
|
7a6622724f | ||
|
|
a3078bc1b6 | ||
|
|
992642d3f8 | ||
|
|
01084ffc8a | ||
|
|
f39e49d72f | ||
|
|
c63e52d3ec | ||
|
|
75693625c0 | ||
|
|
5cec9e6f3c | ||
|
|
edd20e84ce | ||
|
|
a04caf8675 | ||
|
|
624182a528 | ||
|
|
1bce2e36af | ||
|
|
fd783476e4 | ||
|
|
76b04adb5d | ||
|
|
a76b6c5aaa | ||
|
|
b2d5395c29 | ||
|
|
79af943453 | ||
|
|
41549bb2d4 | ||
|
|
c099780258 | ||
|
|
1b2b61b5d8 | ||
|
|
db38799bf8 | ||
|
|
959563bc88 | ||
|
|
f9bb1d3dd2 | ||
|
|
ba9db17339 | ||
|
|
2cefd8e2cd | ||
|
|
d77bffabf2 | ||
|
|
bfa22217fa | ||
|
|
5f1f3f744d | ||
|
|
545c625a5b | ||
|
|
1ef746957f | ||
|
|
8c766e815f | ||
|
|
73d51012d6 | ||
|
|
74c0565ad0 | ||
|
|
e1a26c1444 | ||
|
|
cf8a9d9504 | ||
|
|
7065dc8cd4 | ||
|
|
2299750c68 | ||
|
|
9e21b8ed11 | ||
|
|
89a036f0ac | ||
|
|
4279c55757 | ||
|
|
dcaa6ee324 | ||
|
|
68783034bc | ||
|
|
1844055a4a | ||
|
|
c4eecaffef | ||
|
|
a5e63f1484 | ||
|
|
9f37c3de71 | ||
|
|
bd72b79987 | ||
|
|
d70bba284f | ||
|
|
bc5b5caa99 | ||
|
|
a912bbf2d2 | ||
|
|
b53c570a39 | ||
|
|
a103a0cf56 | ||
|
|
bd428e9a0d | ||
|
|
6e6a16b7f1 | ||
|
|
a2226b77bd | ||
|
|
875fffeed3 | ||
|
|
2688ec472c | ||
|
|
4fa827cbc3 | ||
|
|
bc3071788f | ||
|
|
17c4e54468 | ||
|
|
58b4ea5856 | ||
|
|
8c222ece76 | ||
|
|
2ac889968e | ||
|
|
508d81474c | ||
|
|
621a40e399 | ||
|
|
0043d5abc6 | ||
|
|
320aae9b18 | ||
|
|
6a0e97f555 | ||
|
|
e0bba30696 | ||
|
|
f4aab63317 | ||
|
|
384aa5500a | ||
|
|
588f71abcf | ||
|
|
48be414a4f | ||
|
|
d57530a6b1 | ||
|
|
71da34111c | ||
|
|
86eb5f6951 | ||
|
|
3c39e645ff | ||
|
|
34e3dc9910 | ||
|
|
c219481ad5 | ||
|
|
f36caf0a90 | ||
|
|
866fe39ce5 | ||
|
|
15c4ea1456 | ||
|
|
7fc33acb6d | ||
|
|
4df9db3ece | ||
|
|
3297672751 | ||
|
|
588a2a1f72 | ||
|
|
92d8144065 | ||
|
|
23d8bed1f4 | ||
|
|
279b6028cd | ||
|
|
94cde0f6d7 | ||
|
|
b8f0d43a0d | ||
|
|
e9495a4db2 | ||
|
|
567687efad | ||
|
|
3212452527 | ||
|
|
8281dc3136 | ||
|
|
213136e6fa | ||
|
|
5acc738576 | ||
|
|
fbcc7ac754 | ||
|
|
ce5eb03e12 | ||
|
|
607194d611 | ||
|
|
6ab8265cfa | ||
|
|
2e567bb2f4 | ||
|
|
77a0571a60 | ||
|
|
28040a7b8b | ||
|
|
16aab5156c | ||
|
|
3edfaadbfa | ||
|
|
4dd8e861fe | ||
|
|
f1c26fceec | ||
|
|
c0929371cc | ||
|
|
9eac3c98b4 | ||
|
|
35d5a5cdc6 | ||
|
|
8e57df5a8d | ||
|
|
0dd82e1d7b | ||
|
|
3dc1c1b4c9 | ||
|
|
a3bf097bc0 | ||
|
|
badb8ae4ca | ||
|
|
f7e824b88c | ||
|
|
99d0f32a7e | ||
|
|
166a8ca76b | ||
|
|
304fac1dd8 | ||
|
|
21990fa123 | ||
|
|
469209fe82 | ||
|
|
a8f0a39204 | ||
|
|
e477f80a24 | ||
|
|
d8ccfa3234 | ||
|
|
77f43f2aee | ||
|
|
ed7a761ab6 | ||
|
|
d43f03fdda | ||
|
|
b2efe6b5b3 | ||
|
|
6d366d902f | ||
|
|
4e6f7ae859 | ||
|
|
e4e959a9f8 | ||
|
|
d0752a0912 | ||
|
|
e4cc9d3dd9 | ||
|
|
ad4deafa87 | ||
|
|
bb525bf3e3 | ||
|
|
5dee48bde2 | ||
|
|
ab678fcd7b | ||
|
|
3ed482c6f9 | ||
|
|
eb5231d847 | ||
|
|
c1908dcd8f | ||
|
|
d986be7cad | ||
|
|
5fe8987522 | ||
|
|
f5673437d3 | ||
|
|
7ed59caeb1 | ||
|
|
1485e5431e | ||
|
|
7f365354c3 | ||
|
|
b889e5a50e | ||
|
|
ff62d8c472 | ||
|
|
2d45a39017 | ||
|
|
194d79293d | ||
|
|
a83d409423 | ||
|
|
fc7c2238b1 | ||
|
|
1fefc30644 | ||
|
|
0084921d13 | ||
|
|
1026f491f4 | ||
|
|
6540dfde8a | ||
|
|
f3bdc35c35 | ||
|
|
4223d912d1 | ||
|
|
eaddb49b26 | ||
|
|
654ae03bf1 | ||
|
|
0b5701f2bb | ||
|
|
51e559a032 | ||
|
|
cb6a2ef8f7 | ||
|
|
0fd76bd77f | ||
|
|
3517ab6a33 | ||
|
|
3271c472b3 | ||
|
|
fa09361fe5 | ||
|
|
0bfc44beb5 | ||
|
|
83183f29bc | ||
|
|
e04494342f | ||
|
|
d639dd788d | ||
|
|
20e61f1317 | ||
|
|
80e08d5b18 | ||
|
|
037e6af401 | ||
|
|
68b74c6d68 | ||
|
|
6455a5fc26 | ||
|
|
b20449bdfd | ||
|
|
01b0b01187 | ||
|
|
b08499204e | ||
|
|
87ace3d7c4 | ||
|
|
7884e7ba1a | ||
|
|
2d0b50568e | ||
|
|
c056a682d7 | ||
|
|
24c264e39e | ||
|
|
ce6edfe6a5 | ||
|
|
48a2fb0a8f | ||
|
|
32906db9e1 | ||
|
|
cfbf7baf3b | ||
|
|
1f89f20354 | ||
|
|
4b2af44a88 | ||
|
|
145195d338 | ||
|
|
1cad95ef99 | ||
|
|
3ee892c2d0 | ||
|
|
c2c5242b9b | ||
|
|
ac60f2d8ed | ||
|
|
e313c61083 | ||
|
|
f98b1dfa2e | ||
|
|
07ebec3775 | ||
|
|
5d44dfc76c | ||
|
|
16956c2f08 | ||
|
|
0bd761d7d7 | ||
|
|
be9c90488f | ||
|
|
ad74e90c19 | ||
|
|
20004df8c5 | ||
|
|
6d52187f19 | ||
|
|
83bd9726ac | ||
|
|
ec0df984ab | ||
|
|
86513089ae | ||
|
|
d74eed1b69 | ||
|
|
1b4a20183c | ||
|
|
506bf03829 | ||
|
|
a5b28a9f66 | ||
|
|
0bc820b3e5 | ||
|
|
842d27f8b6 | ||
|
|
4741634fdf | ||
|
|
b00a60966b | ||
|
|
a353963998 | ||
|
|
6e485be7bb | ||
|
|
4a495cb077 | ||
|
|
87f5b06c52 | ||
|
|
050c66a28b | ||
|
|
80e6fc77c9 | ||
|
|
861bffc87b | ||
|
|
38d0f56e8e | ||
|
|
7ddad6a679 | ||
|
|
b4941c1548 | ||
|
|
0ddd3e15d0 | ||
|
|
721957baf1 | ||
|
|
f9bdd78e2d | ||
|
|
0390cfbf67 | ||
|
|
8d3dfe406a | ||
|
|
56515fc628 | ||
|
|
1652f1c5b1 | ||
|
|
b43d885436 | ||
|
|
9293116e03 | ||
|
|
a1801d13ac | ||
|
|
44c7ac3fd2 | ||
|
|
5b928a3e39 | ||
|
|
287b9c6f8e | ||
|
|
02c4ad310e | ||
|
|
baffc6750f | ||
|
|
54e2a0ffb9 | ||
|
|
49fa0f3514 | ||
|
|
89ff19ae6d | ||
|
|
b00882859f | ||
|
|
0bc913e993 | ||
|
|
066d9c16e3 | ||
|
|
85a5cb0226 | ||
|
|
3d97d7d13d | ||
|
|
9e550a16aa | ||
|
|
76715b1dc6 | ||
|
|
b8909a6bde | ||
|
|
fa8f4f2f01 | ||
|
|
6c16ce1652 | ||
|
|
9ba84219b8 | ||
|
|
46b20853ef | ||
|
|
5bc0a91365 | ||
|
|
46cdde3d39 | ||
|
|
33dab7754a | ||
|
|
f4cde621a2 | ||
|
|
1f8bf71865 | ||
|
|
f581387aa8 | ||
|
|
deece80b16 | ||
|
|
20718f4e9d | ||
|
|
93d66d1c6a | ||
|
|
c84d75e161 | ||
|
|
1e0f5a0513 | ||
|
|
851c581e2d | ||
|
|
7654e279a3 | ||
|
|
687e88d49b | ||
|
|
26940b3a12 | ||
|
|
1dd5b592a8 | ||
|
|
a2a50f639b | ||
|
|
5b6eec3d96 | ||
|
|
9ba7cdb7db | ||
|
|
78fd668f8b | ||
|
|
52630908f6 | ||
|
|
820abd00d6 | ||
|
|
85d96a9e91 | ||
|
|
abf6587cc0 | ||
|
|
e8901253dd | ||
|
|
ff5a74c39e | ||
|
|
ba2914c1d9 | ||
|
|
0a395ad67a | ||
|
|
cb563936c0 | ||
|
|
6c11ac29be | ||
|
|
dbada6a3a8 | ||
|
|
88d600d00c | ||
|
|
64a251da34 | ||
|
|
199ac9e495 | ||
|
|
d9f3294893 | ||
|
|
63e293ac74 | ||
|
|
18987c669e | ||
|
|
3ab5515ee6 | ||
|
|
d4dec7cf2a | ||
|
|
db4ee9ae55 | ||
|
|
5590c170b9 | ||
|
|
e5d02223b7 | ||
|
|
4095161df5 | ||
|
|
d0bd92a49f | ||
|
|
e178ddc87a | ||
|
|
e41a9a64d6 | ||
|
|
bedf8f26b1 | ||
|
|
334c174f67 | ||
|
|
c5383d74ff | ||
|
|
bcf7cf6137 | ||
|
|
f7725bcfc4 | ||
|
|
58585baea9 | ||
|
|
48df8aac52 | ||
|
|
2b7ca32373 | ||
|
|
0e9358131f | ||
|
|
566c822533 | ||
|
|
87b0ebaef8 | ||
|
|
ab746f2af2 | ||
|
|
9a3c42b7a5 | ||
|
|
ae03393043 | ||
|
|
e38f3db539 | ||
|
|
627963b856 | ||
|
|
d302e881e7 | ||
|
|
2fc88aeec5 | ||
|
|
51c7cf1932 | ||
|
|
c6cbc1035c | ||
|
|
53c032e710 | ||
|
|
b4b806531a | ||
|
|
af2862bed8 | ||
|
|
20241e1728 | ||
|
|
7644a79884 | ||
|
|
ccaab14b99 | ||
|
|
81ed9d41be | ||
|
|
19e5f5c140 | ||
|
|
e5115da909 | ||
|
|
f4ea8f5c73 | ||
|
|
8588dbb843 | ||
|
|
72388ffeae | ||
|
|
884f63cace | ||
|
|
be0f151537 | ||
|
|
dfcabde3c2 | ||
|
|
ae50d7f2b7 | ||
|
|
1aa365993c | ||
|
|
f22645a54b | ||
|
|
809c046994 | ||
|
|
1b3c71f470 | ||
|
|
6caf80bfab | ||
|
|
fb10b933f1 | ||
|
|
2c14e19f7a | ||
|
|
e4d95199b6 | ||
|
|
9e26d3c97f | ||
|
|
661c6b477e | ||
|
|
93271b13cc | ||
|
|
c8edb512fd | ||
|
|
abbd00ce49 | ||
|
|
b309641e5c | ||
|
|
2101a5282e | ||
|
|
25ca1abdf7 | ||
|
|
5efc05444f | ||
|
|
b074de535b | ||
|
|
806789f756 | ||
|
|
4f7fd9f1e2 | ||
|
|
d56f4ab906 | ||
|
|
8f75cbe3e6 | ||
|
|
9f98a6bda0 | ||
|
|
c07095187b | ||
|
|
42ddb8f4b3 | ||
|
|
82325109ef | ||
|
|
6c313cfe2b | ||
|
|
6369593549 | ||
|
|
ed9eb3946e | ||
|
|
9dbca78044 | ||
|
|
812432837d | ||
|
|
accfdcb4ab | ||
|
|
050eaa21f3 | ||
|
|
23f3fc0373 | ||
|
|
2d81f1d452 | ||
|
|
a679116c83 | ||
|
|
7c1d855ccf | ||
|
|
777ce0a036 | ||
|
|
4ab7a3cb34 | ||
|
|
e802d32dc0 | ||
|
|
fd4caa1a73 | ||
|
|
9839e1ed91 | ||
|
|
6cdc6038da | ||
|
|
47bc850dca | ||
|
|
52e2ef46d8 | ||
|
|
5368642312 | ||
|
|
fdf8022695 | ||
|
|
77fa1137ac | ||
|
|
ba144fe63b | ||
|
|
927d1b82b7 | ||
|
|
dc8ab361b7 | ||
|
|
d8b757cbc2 | ||
|
|
101b0e5c24 | ||
|
|
dc1f8df8f6 | ||
|
|
4078d6f8e3 | ||
|
|
f062cb371c | ||
|
|
b1868591d0 | ||
|
|
c670855e11 | ||
|
|
71c79f62d4 | ||
|
|
038f60595d | ||
|
|
a160037979 | ||
|
|
4722561a6e | ||
|
|
a71f903e00 | ||
|
|
855ad3a265 | ||
|
|
07f9e1a5f3 | ||
|
|
3cf833dac7 | ||
|
|
d8e891a30e | ||
|
|
51898baf86 | ||
|
|
0dfd67c7a2 | ||
|
|
0857bf1048 | ||
|
|
4a0eda6233 | ||
|
|
69cead93dc | ||
|
|
1f63dc8158 | ||
|
|
a11d5312be | ||
|
|
1e76399ef4 | ||
|
|
6dc8f9d451 | ||
|
|
95c60f62af | ||
|
|
868488acae | ||
|
|
66a56c2a61 | ||
|
|
07a6fd04fd | ||
|
|
7c6513e2b4 | ||
|
|
98f443379d | ||
|
|
b5aa8748ec | ||
|
|
4b25b4dbf7 | ||
|
|
809abf0795 | ||
|
|
d477b90ea2 | ||
|
|
553aa5c8fb | ||
|
|
d72afc0cfa | ||
|
|
8e1419f495 | ||
|
|
d030002bf6 | ||
|
|
d1bf1cc6b3 | ||
|
|
a66e7bc13d | ||
|
|
b84ff3e0f7 | ||
|
|
24c4c27efd | ||
|
|
3f895f0dc4 | ||
|
|
6fa79a7a0a | ||
|
|
2952285378 | ||
|
|
c4bd9f6c76 | ||
|
|
d302095877 | ||
|
|
ffc647b832 | ||
|
|
8c5d4abe7e | ||
|
|
d5a5a259e7 | ||
|
|
20b14bcb86 | ||
|
|
41dc1bfb0f | ||
|
|
00b540d313 | ||
|
|
115c5ea945 | ||
|
|
73653d654a | ||
|
|
51267259c7 | ||
|
|
e066132005 | ||
|
|
6f3fb65b57 | ||
|
|
789c101429 | ||
|
|
65cfe8e369 | ||
|
|
ad13f8eb57 | ||
|
|
be626824e4 | ||
|
|
d4a861e8af | ||
|
|
e66e0d68d7 | ||
|
|
91930ee307 | ||
|
|
8429a5a27c | ||
|
|
0a92b20480 | ||
|
|
0bfaefe163 | ||
|
|
f1b842d90c | ||
|
|
aeb4b1b4b0 | ||
|
|
4ba5545c7d | ||
|
|
04ae92b33c | ||
|
|
f46afb51ed | ||
|
|
f33267afab | ||
|
|
78a077d30e | ||
|
|
721b9a7357 | ||
|
|
51c282c62b | ||
|
|
02965fa230 | ||
|
|
9ebee52e94 | ||
|
|
c5671965ac | ||
|
|
e4f9c547a9 | ||
|
|
cc12d82076 | ||
|
|
11f607481f | ||
|
|
999765eb08 | ||
|
|
b784fa92ca | ||
|
|
e3d995c85d | ||
|
|
6bf4ce45a5 | ||
|
|
9863f02757 | ||
|
|
3cb180a48d | ||
|
|
1208b4357e | ||
|
|
423c1f8845 | ||
|
|
ebb7424282 | ||
|
|
90f16f87f2 | ||
|
|
d6d2688545 | ||
|
|
9564d04538 | ||
|
|
6378fa103d | ||
|
|
bc951c1a59 | ||
|
|
125c335c80 | ||
|
|
0dd7235485 | ||
|
|
b9f7d7f53a | ||
|
|
02bdeb7b9d | ||
|
|
06685fa638 | ||
|
|
5bad08f487 | ||
|
|
eaa5d71a82 | ||
|
|
ac2bb2bcfe | ||
|
|
c56500548c | ||
|
|
f7b3a4df3f | ||
|
|
37c1599279 | ||
|
|
34841c3bfd | ||
|
|
69bcb8a6aa | ||
|
|
f15d8654a3 | ||
|
|
90ec474f19 | ||
|
|
f0fcf84ddb | ||
|
|
1e663b5f57 | ||
|
|
213c1b902f | ||
|
|
fcc482a36a | ||
|
|
103a8d14a5 | ||
|
|
e213e20df2 | ||
|
|
c05a3e14ec | ||
|
|
8cb61ef62d | ||
|
|
d56beac926 | ||
|
|
e45d85e999 | ||
|
|
559908276d | ||
|
|
8e561af698 | ||
|
|
9aa44c38d1 | ||
|
|
14b364724d | ||
|
|
d85011dd0a | ||
|
|
5afc312f8d | ||
|
|
e552c8453e | ||
|
|
b3c7d41958 | ||
|
|
11740281ca | ||
|
|
1100724061 | ||
|
|
7da263c887 | ||
|
|
6551ec2d93 | ||
|
|
b0986a3da8 | ||
|
|
44b9677c3c | ||
|
|
0be1343529 | ||
|
|
4adacb7cec | ||
|
|
597b78f1f0 | ||
|
|
5c13c89954 | ||
|
|
c269768e5b | ||
|
|
ba8e3cb957 | ||
|
|
bf71bcd0e4 | ||
|
|
7dd8c32cab | ||
|
|
d6380f1a56 | ||
|
|
a6062d17af | ||
|
|
ded9520700 | ||
|
|
86712582f2 | ||
|
|
51d70c03d1 | ||
|
|
42f4c89771 | ||
|
|
dd038c8de2 | ||
|
|
e61db3ce10 | ||
|
|
964f13e2f1 | ||
|
|
735a35e97e | ||
|
|
8f0e1ecb2f | ||
|
|
f2b8089536 | ||
|
|
604317bb78 | ||
|
|
6da9b98dc9 | ||
|
|
fba334121e | ||
|
|
6ec18af939 | ||
|
|
9450b30d49 | ||
|
|
304b1dfd56 | ||
|
|
b540ba28f0 | ||
|
|
185d51dd49 | ||
|
|
fe8ee55d90 | ||
|
|
ab52769654 | ||
|
|
27aceae1d3 | ||
|
|
c647fdb17e | ||
|
|
0bf471365c | ||
|
|
989277c929 | ||
|
|
e35d761fea | ||
|
|
d54c9a8d07 | ||
|
|
ca8f2c8f9d | ||
|
|
4c19bd32a0 | ||
|
|
baae588ec4 | ||
|
|
e5495ef351 | ||
|
|
1f74c01397 | ||
|
|
17d31987b6 | ||
|
|
9647764894 | ||
|
|
08e2bc5dea | ||
|
|
67a11f61dd | ||
|
|
03f4ea45ad | ||
|
|
ec111a543a | ||
|
|
dda5e519bb | ||
|
|
bba3e273f6 | ||
|
|
08ddc2308d | ||
|
|
97a9605712 | ||
|
|
b40e508ac2 | ||
|
|
58cf995091 | ||
|
|
2211b15f4c | ||
|
|
9cba4e483c | ||
|
|
bbd2ca3487 | ||
|
|
34cab3adf9 | ||
|
|
be3476e351 | ||
|
|
c4fe124508 | ||
|
|
cc9f5d77b9 | ||
|
|
7c97ebc3d8 | ||
|
|
51837e2f15 | ||
|
|
2d3cecd1da | ||
|
|
3a8c83434c | ||
|
|
1cc13e5811 | ||
|
|
6dd3b55876 | ||
|
|
7e7cdce60e | ||
|
|
08bec358e7 | ||
|
|
2539799443 | ||
|
|
b076524fa0 | ||
|
|
531aee3f71 | ||
|
|
e184f7a856 | ||
|
|
bd2c116afe | ||
|
|
452fef7336 | ||
|
|
0f4806c178 | ||
|
|
6446b02cdc | ||
|
|
ee63b713de | ||
|
|
119c569618 | ||
|
|
b1f87f4334 | ||
|
|
89306f76b6 | ||
|
|
9ae3335206 | ||
|
|
b4efae54a1 | ||
|
|
dd34822b86 | ||
|
|
2ba2ff5994 | ||
|
|
8dba6fe19f | ||
|
|
c2954f52d2 | ||
|
|
e6b7893243 | ||
|
|
1fc7e9f5a0 | ||
|
|
ef53db347a | ||
|
|
7e20fb35fe | ||
|
|
3c6d727423 | ||
|
|
875c29f655 | ||
|
|
1f5f972402 | ||
|
|
4d4c5d6b9d | ||
|
|
ab9d270aa9 | ||
|
|
e1fced0a71 | ||
|
|
0f21a0df0b | ||
|
|
7bb3c81f00 | ||
|
|
fe489660b8 | ||
|
|
86ef0528a2 | ||
|
|
c87c32ead1 | ||
|
|
a17211c679 | ||
|
|
79291abfbc | ||
|
|
ab74e6517e | ||
|
|
bb59d1e341 | ||
|
|
dddd9fd117 | ||
|
|
2ab1018498 | ||
|
|
6c767e160a | ||
|
|
757983c756 | ||
|
|
e3ab368dda | ||
|
|
d690f8cfb7 | ||
|
|
c4bcc120a4 | ||
|
|
f0d41bc043 | ||
|
|
e0a1393eee | ||
|
|
f73783b2c9 | ||
|
|
371800dcf7 | ||
|
|
f0b659c276 | ||
|
|
9edf96c9d4 | ||
|
|
556fa0913d | ||
|
|
2c92e8ec94 | ||
|
|
1990f986f5 | ||
|
|
1ae86dcdf5 | ||
|
|
d40a381552 | ||
|
|
9dc520daa3 | ||
|
|
bb24236869 | ||
|
|
313667604a | ||
|
|
385d15fd52 | ||
|
|
ada4d6e023 | ||
|
|
c4e876ea2f | ||
|
|
67dc00e495 | ||
|
|
1441e31bae | ||
|
|
30e4b26ebf | ||
|
|
435a3a1a04 | ||
|
|
f02062d9f4 | ||
|
|
90860121bd | ||
|
|
3edd03b8e0 | ||
|
|
4a77161781 | ||
|
|
6f3577c92b | ||
|
|
9d370d88a8 | ||
|
|
2b53dbb708 | ||
|
|
71a11a333e | ||
|
|
4c52700e6a | ||
|
|
11f4c44da2 | ||
|
|
0c2ecff553 | ||
|
|
4f6001e413 | ||
|
|
8a17e0584b | ||
|
|
4f26f319ce | ||
|
|
aedb4e7649 | ||
|
|
a1b173fdd9 | ||
|
|
0672c4bdcb | ||
|
|
d9dd5859e0 | ||
|
|
fbe40b226c | ||
|
|
e136f2f2cd | ||
|
|
6b080b576d | ||
|
|
da6196275d | ||
|
|
d312acaf42 | ||
|
|
3a417753b7 | ||
|
|
26de1f03de | ||
|
|
8c526bc31f | ||
|
|
0d402ec65f | ||
|
|
09f5bf4edc | ||
|
|
341db8e564 | ||
|
|
375801ad27 | ||
|
|
99d4b4dd04 | ||
|
|
cd87619ac6 | ||
|
|
f47e36d04e | ||
|
|
47ce54c9af | ||
|
|
a5e37278aa | ||
|
|
e97baae8d1 | ||
|
|
3d39ce347e | ||
|
|
3cb0904e6f | ||
|
|
b93fba4bb5 | ||
|
|
303ec79cce | ||
|
|
a63206756f | ||
|
|
3016590f89 | ||
|
|
848848df8f | ||
|
|
66da206735 | ||
|
|
acb75f1e1f | ||
|
|
795893424d | ||
|
|
656e679140 | ||
|
|
52efee5f96 | ||
|
|
4b14277f29 | ||
|
|
ce5adfaafe | ||
|
|
452b61f9c9 | ||
|
|
91ea354d63 | ||
|
|
344ecb0d13 | ||
|
|
41c27faab7 | ||
|
|
a2978a94e5 | ||
|
|
a391a7b86d | ||
|
|
3fb52de653 | ||
|
|
f79e750ace | ||
|
|
33f6a0f304 | ||
|
|
136f54f7fe | ||
|
|
cd5356da62 | ||
|
|
132d2780f2 | ||
|
|
7469a2d11f | ||
|
|
7917274c26 | ||
|
|
dc7e916471 | ||
|
|
f25e490867 | ||
|
|
acc063bd09 | ||
|
|
c7737f1686 | ||
|
|
3df18ef931 | ||
|
|
1224e340c0 | ||
|
|
2364d4eae2 | ||
|
|
acacc3b2de | ||
|
|
a76e30bc35 | ||
|
|
8859a41bdf | ||
|
|
f52c1ad837 | ||
|
|
2f59fd1d51 | ||
|
|
9e17d72597 | ||
|
|
882fa65ea5 | ||
|
|
4d41b0dfcd | ||
|
|
e5e68f83f5 | ||
|
|
154a938979 | ||
|
|
176d39b7ed | ||
|
|
99e53d869e | ||
|
|
3080a707d2 | ||
|
|
527596becc | ||
|
|
6316344b94 | ||
|
|
dc523889f3 | ||
|
|
c7dbd7405d | ||
|
|
88ff46c0ee | ||
|
|
050cd4db15 | ||
|
|
a09bc75d82 | ||
|
|
54167eade1 | ||
|
|
9243b4b5f9 | ||
|
|
d66dfa7d44 | ||
|
|
d520dae549 | ||
|
|
55828db155 | ||
|
|
5cdade9b04 | ||
|
|
f409f00ef9 | ||
|
|
493f2ba97b | ||
|
|
89d722c729 | ||
|
|
f298706e06 | ||
|
|
660de753d4 | ||
|
|
3c355a16c5 | ||
|
|
83cbb071b0 | ||
|
|
244a874040 | ||
|
|
38c0ef8fa9 | ||
|
|
e0f6b09ef5 | ||
|
|
d5c90df9b7 | ||
|
|
9e48e96001 | ||
|
|
9d8377d453 | ||
|
|
767e3c1441 | ||
|
|
f2f46cb867 | ||
|
|
defbdb0462 | ||
|
|
952fc7183e | ||
|
|
c4c5ab3bfd | ||
|
|
eb0bc0b7c8 | ||
|
|
b968e844ac | ||
|
|
891286ea51 | ||
|
|
981ccb0e46 | ||
|
|
4c06b4a8c6 | ||
|
|
74db22c236 | ||
|
|
bc1ba8c524 | ||
|
|
8fd0057740 | ||
|
|
f9f37fa2b7 | ||
|
|
71f4d08d68 | ||
|
|
4f36ffc6fa | ||
|
|
ad40d2e0b2 | ||
|
|
1a0083fffd | ||
|
|
b2e83a782e | ||
|
|
5cc1cbe805 | ||
|
|
af38c5993d | ||
|
|
37a678bad9 | ||
|
|
bfdd404d68 | ||
|
|
05fb4dbb25 | ||
|
|
d5401d902e | ||
|
|
2c035f55fa | ||
|
|
490ea5cdb8 | ||
|
|
a2af241bdc | ||
|
|
67e33a0be0 | ||
|
|
40cfa42170 | ||
|
|
b5bf9ade80 | ||
|
|
3735a0fcaa | ||
|
|
dda3d11583 | ||
|
|
f65c9f8355 | ||
|
|
a58ee4a597 | ||
|
|
8b9207608b | ||
|
|
cbdfdff5fd | ||
|
|
caebd3921f | ||
|
|
2a3b838dca | ||
|
|
8c3706ce86 | ||
|
|
54937a1e7b | ||
|
|
c4650f5181 | ||
|
|
72d839e049 | ||
|
|
262db00540 | ||
|
|
399ddf968a | ||
|
|
61b9933717 | ||
|
|
a1b17255d2 | ||
|
|
d43b57660a | ||
|
|
8547d2d3b0 | ||
|
|
59a4c6c59c | ||
|
|
7fa0849e17 | ||
|
|
f37545de19 | ||
|
|
cfb02afc84 | ||
|
|
67f1333ef3 | ||
|
|
52a3d02eac | ||
|
|
5cb65919c2 | ||
|
|
1f82ec3f0d | ||
|
|
0ce761b01e | ||
|
|
c68e02b409 | ||
|
|
b959fd60dc | ||
|
|
dba862d6cd | ||
|
|
9ed0b9e3b3 | ||
|
|
29ab696bfd | ||
|
|
dd1e4d9c23 | ||
|
|
603b27eb38 | ||
|
|
3243044438 | ||
|
|
38f5f58644 | ||
|
|
af45ed3350 | ||
|
|
ada5d570b8 | ||
|
|
78898fb39f | ||
|
|
5c856aaf12 | ||
|
|
c6ad936d08 | ||
|
|
b9c6eced38 | ||
|
|
0409aed138 | ||
|
|
c55e09e208 | ||
|
|
b4141f448e | ||
|
|
fb342d813b | ||
|
|
96dfa7ad5a | ||
|
|
423e0de993 | ||
|
|
30c5645eef | ||
|
|
5ff8a1b7c3 | ||
|
|
774a265c69 | ||
|
|
92d2509b3c | ||
|
|
fab9303d60 | ||
|
|
6c004f8c87 | ||
|
|
161238482b | ||
|
|
9a9edbd117 | ||
|
|
7996e34d5d | ||
|
|
46278ea30f | ||
|
|
63ad2f9f07 | ||
|
|
7d2c15935b | ||
|
|
2060998750 | ||
|
|
5ce6668185 | ||
|
|
e57beb588a | ||
|
|
8f1d5a72d4 | ||
|
|
966a11f394 | ||
|
|
8d3d64e381 | ||
|
|
7f55d919f0 | ||
|
|
578ba51f9a | ||
|
|
3653a85af2 | ||
|
|
5ae8814ef4 | ||
|
|
1ab1992917 | ||
|
|
6b77f9f7c9 | ||
|
|
70a95b1de0 | ||
|
|
605a5b1d48 | ||
|
|
655300e414 | ||
|
|
aeeb13cd0b | ||
|
|
04839ce472 | ||
|
|
3d240a86a1 | ||
|
|
d123824cab | ||
|
|
47d4a11d1d | ||
|
|
3858c45274 | ||
|
|
7d7ed04f75 | ||
|
|
d2c1998dc8 | ||
|
|
cdbb242778 | ||
|
|
58f4d1ab56 | ||
|
|
78ae4f073d | ||
|
|
7f94d2b48f | ||
|
|
eb93724cbb | ||
|
|
24262256e5 | ||
|
|
2773d5c9ca | ||
|
|
a0f9e96fcb | ||
|
|
38f703c482 | ||
|
|
c05c826cd9 | ||
|
|
4b7ed6ead5 | ||
|
|
2b32adefcb | ||
|
|
933f9ab820 | ||
|
|
dc420cdf3a | ||
|
|
a0de3392bc | ||
|
|
e465c58262 | ||
|
|
833df033c5 | ||
|
|
47f26f8f97 | ||
|
|
e3397e581a | ||
|
|
6f98ae9300 | ||
|
|
6833df3548 | ||
|
|
63fea2e47b | ||
|
|
1b3bcb8b6a | ||
|
|
ea374f228a | ||
|
|
a2c7eecdca | ||
|
|
9c6e6e6c65 | ||
|
|
20015b8463 | ||
|
|
f4ef3b2433 | ||
|
|
f69aca7c08 | ||
|
|
86f6319077 | ||
|
|
e5bd36ded0 | ||
|
|
85f9ac8dd4 | ||
|
|
66194c55f9 | ||
|
|
0ca51ca7fc | ||
|
|
a4fae9be65 | ||
|
|
fd2967faae | ||
|
|
feeb4ee6e9 | ||
|
|
e2842c75be | ||
|
|
0aabc8ad2c | ||
|
|
b7852f2f65 | ||
|
|
fdfc3ab5d1 | ||
|
|
1f521cf6bc | ||
|
|
a8529b69d9 | ||
|
|
c96dd64fd8 | ||
|
|
0373cebca9 | ||
|
|
3afec3e583 | ||
|
|
80eea88ccb | ||
|
|
c62d18d03b | ||
|
|
dd0189c1df | ||
|
|
0242da3d45 | ||
|
|
1f3de84373 | ||
|
|
03dd2c45d0 | ||
|
|
6004f8937b | ||
|
|
3c93e0b692 | ||
|
|
180ddce248 | ||
|
|
a768a893eb | ||
|
|
d115bf7faf | ||
|
|
3bf0a49837 | ||
|
|
530ea46779 | ||
|
|
48e4f5962a | ||
|
|
0c90169369 | ||
|
|
f118613de8 | ||
|
|
61d7a50ca6 | ||
|
|
35103bd225 | ||
|
|
05f2b04969 | ||
|
|
a1c5ded380 | ||
|
|
9e92a9a0e0 | ||
|
|
fd6efea36e | ||
|
|
e6d3190691 | ||
|
|
8ae64f3f18 | ||
|
|
725458dfe3 | ||
|
|
ba41fe49a7 | ||
|
|
a614ae4e1a | ||
|
|
d87bc9da58 | ||
|
|
f4af57cb08 | ||
|
|
306c0eefff | ||
|
|
5fd18dea64 | ||
|
|
473ba9bf5e | ||
|
|
813e8c6e0c | ||
|
|
c7a751933b | ||
|
|
7280e99863 | ||
|
|
0a5d3dc32c | ||
|
|
feebefb10d | ||
|
|
d7a04e164e | ||
|
|
2a5a123447 | ||
|
|
0e7ad07e19 | ||
|
|
fb7ad6b750 | ||
|
|
a388b89225 | ||
|
|
c566c4b16e | ||
|
|
1c918447d2 | ||
|
|
921c50ac4d | ||
|
|
45c879a7ac | ||
|
|
42a264f1f0 | ||
|
|
90162a6c4f | ||
|
|
2709883f7f | ||
|
|
8c4fb7871f | ||
|
|
87d43927bc | ||
|
|
485330a942 | ||
|
|
9b1ccb8ec8 | ||
|
|
5a45cf3c9a | ||
|
|
c8ab2b2310 | ||
|
|
5b45811b8d | ||
|
|
10a1a1ba5c | ||
|
|
e7a0c3cd27 | ||
|
|
09e751b83a | ||
|
|
6856ef2f8f | ||
|
|
fd3b8a2141 | ||
|
|
9cad16bbaf | ||
|
|
f14fc933dd | ||
|
|
58411e0d85 | ||
|
|
5129eb9c64 | ||
|
|
e540736c59 | ||
|
|
a45d4a7464 | ||
|
|
b61d7f56c9 | ||
|
|
689e7b4bc7 | ||
|
|
fb55dd6a85 | ||
|
|
1c9f710b45 | ||
|
|
aaadd0ed32 | ||
|
|
bde302b557 | ||
|
|
32d08027bf | ||
|
|
f992f6a0e2 | ||
|
|
51c096dae4 | ||
|
|
5972883978 | ||
|
|
3260643e63 | ||
|
|
00766183c7 | ||
|
|
faef2c35a8 | ||
|
|
b222dfe962 | ||
|
|
e37bc83bbb | ||
|
|
8cc0b3f0da | ||
|
|
70ecd6910b | ||
|
|
555eb6670f | ||
|
|
f2be9f0248 | ||
|
|
786a0cadf4 | ||
|
|
2e59fe535b | ||
|
|
21c7f69ac8 | ||
|
|
b3d6017cf1 | ||
|
|
353670289a | ||
|
|
0e5c9e80bb | ||
|
|
b345f1a245 | ||
|
|
dffdbd2818 | ||
|
|
7f25da11a1 | ||
|
|
252a5d1b33 | ||
|
|
4724d57094 | ||
|
|
61f32cb7a6 | ||
|
|
d46ced3522 | ||
|
|
418bcbca29 | ||
|
|
be029c8f2d | ||
|
|
8418420928 | ||
|
|
a236f0b14c | ||
|
|
de0cc168f3 | ||
|
|
8f2970c504 | ||
|
|
e2760bb7a4 | ||
|
|
8f32bb232e | ||
|
|
5d6ff365c1 | ||
|
|
a32c7f7983 | ||
|
|
599749191b | ||
|
|
7deeb4fbd3 | ||
|
|
90ac1df1cf | ||
|
|
a24754705a | ||
|
|
61c44e257d | ||
|
|
812ad592f2 | ||
|
|
e38ab5104d | ||
|
|
7772f9bfaa | ||
|
|
60172d7c6b | ||
|
|
d2955b2a2f | ||
|
|
8ec5e4f3c6 | ||
|
|
24bdadb1db | ||
|
|
e6a2040027 | ||
|
|
0226aab316 | ||
|
|
720c23a67e | ||
|
|
9ab6b39845 | ||
|
|
616f0f6466 | ||
|
|
00770ef83f | ||
|
|
c8ab4ad146 | ||
|
|
fb6aae1930 | ||
|
|
30435d4ae4 | ||
|
|
3e0707e6a7 | ||
|
|
f9b2959e58 | ||
|
|
f423d0944e | ||
|
|
f6d1d0a272 | ||
|
|
5f4d0bcad3 | ||
|
|
6238b359d7 | ||
|
|
ffbc982804 | ||
|
|
52846991ba | ||
|
|
799bcb0aa3 | ||
|
|
c9c803cf45 | ||
|
|
dacd55f239 | ||
|
|
af15ad0e84 | ||
|
|
7aace1bf49 | ||
|
|
a54b831eec | ||
|
|
44e9eea0f1 | ||
|
|
a1fce2b48c | ||
|
|
df6b94ac39 | ||
|
|
575b80d890 | ||
|
|
7788b79993 | ||
|
|
e169216f08 | ||
|
|
a7f43036c7 | ||
|
|
ed8368c565 | ||
|
|
b12669e584 | ||
|
|
61d01e7cbf | ||
|
|
6462231c86 | ||
|
|
c793fc5a04 | ||
|
|
7109baf227 | ||
|
|
ac880a4387 | ||
|
|
6d9a3d2313 | ||
|
|
2dcbd1f8fb | ||
|
|
e758d5d8ed | ||
|
|
7bc90281ff | ||
|
|
a7ea8301b1 | ||
|
|
370b606575 | ||
|
|
618ae4c251 | ||
|
|
a1ddba2c78 | ||
|
|
1dc91f6f9a | ||
|
|
f9f636499d | ||
|
|
80d11533c1 | ||
|
|
56d9e1b643 | ||
|
|
519592bc31 | ||
|
|
f85fa9e12a | ||
|
|
ada312f753 | ||
|
|
5eeb60c12d | ||
|
|
091e46b74d | ||
|
|
1ed3146ed4 | ||
|
|
0eb5c39953 | ||
|
|
3f1045530d | ||
|
|
f3f091f6ab | ||
|
|
a32f1d098d | ||
|
|
17dc583f86 | ||
|
|
142990b2a6 | ||
|
|
15068e28f9 | ||
|
|
cb8a31a1da | ||
|
|
c9f9c435d5 | ||
|
|
4f61f1f35d | ||
|
|
ca415b259f | ||
|
|
5859c589da | ||
|
|
729cce6695 | ||
|
|
724123aff7 | ||
|
|
d27aa2c7ef | ||
|
|
b87c390946 | ||
|
|
84c98fa1a8 | ||
|
|
3ea7abddfa | ||
|
|
e149095088 | ||
|
|
c2417f70aa | ||
|
|
bb2fab1ef3 | ||
|
|
e902e02fc3 | ||
|
|
bf5aaf2a1c | ||
|
|
c3319e4201 | ||
|
|
74b902e4e5 | ||
|
|
f3dd1b3e1e | ||
|
|
af4b21ebb7 | ||
|
|
d3d3fe0a7c | ||
|
|
e488701115 | ||
|
|
28c5f80d86 | ||
|
|
40c7d367aa | ||
|
|
d407d2a35d | ||
|
|
1e30bd93f7 | ||
|
|
ebeb53a92b | ||
|
|
e7e372ea72 | ||
|
|
389864e413 | ||
|
|
e48d74601f | ||
|
|
78d54c54ad | ||
|
|
b769147e2c | ||
|
|
6ae3ee1246 | ||
|
|
a3c9346792 | ||
|
|
e5133e7006 | ||
|
|
0c6ff86deb | ||
|
|
56caf8380f | ||
|
|
b206fa3cc7 | ||
|
|
c5b66cfd3b | ||
|
|
c8c1746e83 | ||
|
|
b1a3820fb1 | ||
|
|
154791b487 | ||
|
|
6bbc45ad59 | ||
|
|
1050f5501f | ||
|
|
ccd8ed553f | ||
|
|
7ce41f81d3 | ||
|
|
431cde11bd | ||
|
|
bee46dd1d5 | ||
|
|
b161e4d55a | ||
|
|
9f73470bc2 | ||
|
|
6cd1be12b8 | ||
|
|
32fd6ac88a | ||
|
|
607e2f4396 | ||
|
|
16bb40b4a7 | ||
|
|
25bb55f92d | ||
|
|
5f801aa354 | ||
|
|
60e3f1ad91 | ||
|
|
f161adef8c | ||
|
|
f20b816f56 | ||
|
|
ccf6902020 | ||
|
|
a90fb644e2 | ||
|
|
3745e741c4 | ||
|
|
2c2645d4b2 | ||
|
|
e3266af9ae | ||
|
|
3f331702fd | ||
|
|
abd0fcdf4a | ||
|
|
e8cfe85be5 | ||
|
|
e3145683c7 | ||
|
|
820c1c3043 | ||
|
|
0f177a14f7 | ||
|
|
c8827012b0 | ||
|
|
453c6a2e9c | ||
|
|
acdb733286 | ||
|
|
d32a52c36e | ||
|
|
57cb826087 | ||
|
|
dbe86ab33b | ||
|
|
3b095fef4f | ||
|
|
64fb854ab7 | ||
|
|
5753470c8b | ||
|
|
756b39711d | ||
|
|
dd9306927d | ||
|
|
baef83d986 | ||
|
|
ac6e76a4a6 | ||
|
|
41d3c9cdd2 | ||
|
|
c8af8bd800 | ||
|
|
d8c8a36063 | ||
|
|
8720663f07 | ||
|
|
91bcf6a09b | ||
|
|
3ba96cfe70 | ||
|
|
d33a2cddae | ||
|
|
290dc66f8f | ||
|
|
05140d0baf | ||
|
|
036e25cae1 | ||
|
|
c3c45852b4 | ||
|
|
2b275a7b03 | ||
|
|
b36f3623f2 | ||
|
|
ee0bc23493 | ||
|
|
0f96fb73d0 | ||
|
|
dda1a1fc5d | ||
|
|
15601e9d58 | ||
|
|
3458c82cea | ||
|
|
e639e0e666 | ||
|
|
ada4e33bbe | ||
|
|
bdfc2db33b | ||
|
|
b96804792c | ||
|
|
3b277d8753 | ||
|
|
01c87b3415 | ||
|
|
09494dc4e5 | ||
|
|
e73225be50 | ||
|
|
57a9d70c74 | ||
|
|
341004583d | ||
|
|
289134241b | ||
|
|
540d18acbc | ||
|
|
10a6db8ce6 | ||
|
|
0e70ef5c83 | ||
|
|
292c490bd8 | ||
|
|
bc8baccc47 | ||
|
|
87b691f8ec | ||
|
|
3ab74d5a65 | ||
|
|
bb9a7b45d1 | ||
|
|
dfe1e719ef | ||
|
|
753cefe861 | ||
|
|
61deea342c | ||
|
|
1865f1d3fc | ||
|
|
48defb7af3 | ||
|
|
bf6c68930c | ||
|
|
0b5b3884cf | ||
|
|
a798baac79 | ||
|
|
699c1a358b | ||
|
|
944cc6f562 | ||
|
|
7b27513b76 | ||
|
|
f4debfde7b | ||
|
|
e515a2a446 | ||
|
|
482c2271f0 | ||
|
|
2ea40c3025 | ||
|
|
9cae5e65ad | ||
|
|
a597d160ee | ||
|
|
6148eb0cae | ||
|
|
1c21251eee | ||
|
|
21fbb8e075 | ||
|
|
0601ba4473 | ||
|
|
3a098b1a42 | ||
|
|
e9f7afa492 | ||
|
|
267186da39 | ||
|
|
b37e288834 | ||
|
|
770a773ebe | ||
|
|
3240ece070 | ||
|
|
b38b644543 | ||
|
|
e2b49ea137 | ||
|
|
d74f8c926c | ||
|
|
8bdb84d514 | ||
|
|
16aa6cd3f6 | ||
|
|
dbe5427946 | ||
|
|
2454ea7388 | ||
|
|
2324534e2b | ||
|
|
4d6332996e | ||
|
|
1756f1acb0 | ||
|
|
0e1142038b | ||
|
|
17938ffaa8 | ||
|
|
be59c52814 | ||
|
|
047ab618e1 | ||
|
|
76da2af43e | ||
|
|
649d07dd87 | ||
|
|
4c3d8d6dc7 | ||
|
|
6b6d2d6b06 | ||
|
|
9ed6bb4974 | ||
|
|
fe202ad41e | ||
|
|
6d98bdd210 | ||
|
|
836aa2f9df | ||
|
|
b4f6d79665 | ||
|
|
d29d94926c | ||
|
|
8c30565ae8 | ||
|
|
30b8c25003 | ||
|
|
82cc3572b3 | ||
|
|
e939fa5ab2 | ||
|
|
c5587d0073 | ||
|
|
ea6a871246 | ||
|
|
dd5f3c38ac | ||
|
|
d986178f8d | ||
|
|
fa9bc1fd16 | ||
|
|
7e92f3cf1a | ||
|
|
219d0fb9cb | ||
|
|
8a481f8225 | ||
|
|
6ae6d067b4 | ||
|
|
503d91b3fa | ||
|
|
a73cba5328 | ||
|
|
e99739d3ef | ||
|
|
546cfd774c | ||
|
|
99d712b5de | ||
|
|
77183df91d | ||
|
|
001dcc006c | ||
|
|
5a9a0ffb41 | ||
|
|
f9834f82f1 | ||
|
|
a44c12e83b | ||
|
|
113d6458dc | ||
|
|
61fbdca849 | ||
|
|
ebe859cebf | ||
|
|
068ede19e7 | ||
|
|
c3bcc39877 | ||
|
|
2937ad574c | ||
|
|
7d6bc7e68a | ||
|
|
f2e52b14b4 | ||
|
|
3af9c45fdb | ||
|
|
b0f753c238 | ||
|
|
f50321dcc8 | ||
|
|
08c5c00192 | ||
|
|
9eb51d0748 | ||
|
|
9404bb53b7 | ||
|
|
542a6b1ecc | ||
|
|
845068b29a | ||
|
|
53f7119f71 | ||
|
|
de86bf0b91 | ||
|
|
06cf6bec1e | ||
|
|
303a638aca | ||
|
|
43186f5b12 | ||
|
|
017ba5966a | ||
|
|
89d0da0d3f | ||
|
|
9b4a39fe6b | ||
|
|
b357fe0f94 | ||
|
|
b8de0d25fa | ||
|
|
0bb932bcb4 | ||
|
|
ad896f8d3a | ||
|
|
4103fd356b | ||
|
|
a6604db886 | ||
|
|
4dacdc71d3 | ||
|
|
0a8fadd843 | ||
|
|
ad4d6fad08 | ||
|
|
21efbad32b | ||
|
|
de502031aa | ||
|
|
934dc75ee5 | ||
|
|
6016df9311 | ||
|
|
e620f9f74e | ||
|
|
1a91a66b83 | ||
|
|
de9c3173e7 | ||
|
|
d031a0a265 | ||
|
|
bae7d7f2ee | ||
|
|
6fa46bebb6 | ||
|
|
2b1dde2931 | ||
|
|
90efd04463 | ||
|
|
b94492b9ce | ||
|
|
462938dce0 | ||
|
|
777fcf52f7 | ||
|
|
7688fe50c2 | ||
|
|
9e23a344ee | ||
|
|
3d41d6d340 | ||
|
|
ae65790f08 | ||
|
|
728c907ac1 | ||
|
|
566c6d454f | ||
|
|
7c65775a25 | ||
|
|
cf6c376757 | ||
|
|
a7109fc2dc | ||
|
|
ae9f838b3f | ||
|
|
2ca9e25744 | ||
|
|
6a1cd0f578 | ||
|
|
38e6925011 | ||
|
|
4d83a0a577 | ||
|
|
007fc99f3a | ||
|
|
b8179e66b0 | ||
|
|
237f3a85e0 | ||
|
|
6fa5863809 | ||
|
|
f2ab362045 | ||
|
|
8e4e1e8a4c | ||
|
|
6835d01ee1 | ||
|
|
eef37bf4ad | ||
|
|
fc40813a4a | ||
|
|
45053920af | ||
|
|
743c966034 | ||
|
|
0a96b9f403 | ||
|
|
49a636922d | ||
|
|
b5575b363e | ||
|
|
eb577573ef | ||
|
|
58e3e3dfb2 | ||
|
|
f4f76fd6bb | ||
|
|
8a51c158a4 | ||
|
|
aa19958d42 | ||
|
|
351b69efde | ||
|
|
b519864234 | ||
|
|
6fd6a85b84 | ||
|
|
09b5536cc1 | ||
|
|
681277d355 | ||
|
|
c241149092 | ||
|
|
ebec62c922 | ||
|
|
fe6681b95d | ||
|
|
ab977da6e2 | ||
|
|
47bba8385e | ||
|
|
e25a6cf680 | ||
|
|
f3bbb33c75 | ||
|
|
ee3cc67527 | ||
|
|
6f3409b852 | ||
|
|
e58b124056 | ||
|
|
ce7e2f619d | ||
|
|
728c779002 | ||
|
|
c8ceeb6453 | ||
|
|
66d917aea0 | ||
|
|
6b91919789 | ||
|
|
f1cb1cfd67 | ||
|
|
f9b2917c99 | ||
|
|
d6634b5844 | ||
|
|
67a9c517f2 | ||
|
|
694bc68ef9 | ||
|
|
2fbdab5501 | ||
|
|
7bcef2fa97 | ||
|
|
2df28fccfd | ||
|
|
b202891cae | ||
|
|
a14edae2a2 | ||
|
|
a3cfcab631 | ||
|
|
d14bb10be7 | ||
|
|
5a6dae8d53 | ||
|
|
81b7fae762 | ||
|
|
ab98c19187 | ||
|
|
67cdb501a7 | ||
|
|
7968b7bbc2 | ||
|
|
00665fd1f9 | ||
|
|
9c6ea62a7b | ||
|
|
7c2256d12f | ||
|
|
d2e6cdc9c3 | ||
|
|
2ef49a83d0 | ||
|
|
878b5d85de | ||
|
|
83e3c9c9d3 | ||
|
|
f3219dda82 | ||
|
|
5f42850da8 | ||
|
|
2b74307ce6 | ||
|
|
abdaa91181 | ||
|
|
cc8705e929 | ||
|
|
796477af03 | ||
|
|
ab89f3efa7 | ||
|
|
d3e8f56453 | ||
|
|
559aef5a7c | ||
|
|
7ad2c7508f | ||
|
|
75cba5dfb4 | ||
|
|
0498bd3b77 | ||
|
|
b41d28ff30 | ||
|
|
20dbfa5c29 | ||
|
|
f7e49363ac | ||
|
|
ea3ad9e0ec | ||
|
|
1ed629c921 | ||
|
|
216a95f230 | ||
|
|
6b6f4275b5 | ||
|
|
aba0dc681c | ||
|
|
e1c673497e | ||
|
|
682cf5d233 | ||
|
|
d28224a1f3 | ||
|
|
e44f7ad94d | ||
|
|
12bca0fa70 | ||
|
|
61d417c5d0 | ||
|
|
b63a12d9d7 | ||
|
|
59c66f71fc | ||
|
|
02fb1a9873 | ||
|
|
fe4acaac27 | ||
|
|
11f148cea9 | ||
|
|
16ec045108 | ||
|
|
f6b3e99a32 | ||
|
|
7085da94f3 | ||
|
|
33abf02a8f | ||
|
|
7633cffee1 | ||
|
|
a350224fc3 | ||
|
|
fa7200f3c6 | ||
|
|
1e77a962e7 | ||
|
|
866a7ed173 | ||
|
|
56d1785e19 | ||
|
|
6eb1ffa810 | ||
|
|
4b2e4dc8e8 | ||
|
|
f13a5fb3a2 | ||
|
|
5f0a9dd2bd | ||
|
|
734e7b3d5f | ||
|
|
9428021532 | ||
|
|
93549d78c8 | ||
|
|
2441a1a7d8 | ||
|
|
5a3e88fd25 | ||
|
|
0e1067ad96 | ||
|
|
788c73a734 | ||
|
|
19c58e4b4a | ||
|
|
8749f1d4be | ||
|
|
efd55100d7 | ||
|
|
0f46f03493 | ||
|
|
f3eae584a2 | ||
|
|
99bf4c4d7c | ||
|
|
74f536584c | ||
|
|
172b5650b6 | ||
|
|
ea29ab48dd | ||
|
|
216ca31b35 | ||
|
|
7e51b093e5 | ||
|
|
adb3c1b6dd | ||
|
|
32ed7d2f11 | ||
|
|
96d364fac4 | ||
|
|
0e2b692adc | ||
|
|
7600f6768f | ||
|
|
77663fa9b9 | ||
|
|
70977349be | ||
|
|
9c36dc1417 | ||
|
|
7363cac26f | ||
|
|
239c9fa0e0 | ||
|
|
c888fa59ca | ||
|
|
4c53a868c6 | ||
|
|
5dd90ba32c | ||
|
|
d2d6ee0bc0 | ||
|
|
81143786f3 | ||
|
|
ddd1fd3924 | ||
|
|
06f764ee94 | ||
|
|
929595069f | ||
|
|
ddc832936a | ||
|
|
48ad86b6f7 | ||
|
|
057f49072d | ||
|
|
386467e1c7 | ||
|
|
b69f6701dc | ||
|
|
8c42f34725 | ||
|
|
6232cec620 | ||
|
|
6962940384 | ||
|
|
791a41cb83 | ||
|
|
ebdead42cb | ||
|
|
6276cf09ff | ||
|
|
e00088ca93 | ||
|
|
7ec4495706 | ||
|
|
a8421b7f12 | ||
|
|
9a0daa6b22 | ||
|
|
ce8a5517f0 | ||
|
|
f81a78a0bd | ||
|
|
367a949fb8 | ||
|
|
56144b8e1b | ||
|
|
a05a04fd6c | ||
|
|
e2114ff395 | ||
|
|
50dd0d0844 | ||
|
|
f6feb80971 | ||
|
|
30433d0ebf | ||
|
|
3c6fe84d2f | ||
|
|
5908e49da8 | ||
|
|
d202109225 | ||
|
|
d0abf3f7ff | ||
|
|
ee8f342617 | ||
|
|
9890a1c3cf | ||
|
|
a293c48428 | ||
|
|
f589df1c7f | ||
|
|
6fe58c9e88 | ||
|
|
34bfbda252 | ||
|
|
b86e8b8413 | ||
|
|
c27ffb51eb | ||
|
|
14ae1bfe12 | ||
|
|
e504547190 | ||
|
|
31088be645 | ||
|
|
adf23c0831 | ||
|
|
25f97e8d6e | ||
|
|
79ff77f17c | ||
|
|
63e38075d9 | ||
|
|
5c12ff9c91 | ||
|
|
14a0254ae6 | ||
|
|
05c8f2343c | ||
|
|
dde30ae1ad | ||
|
|
4e9d0965b0 | ||
|
|
dd3d1e9d49 | ||
|
|
556fbf8908 | ||
|
|
a928837c60 | ||
|
|
b3dd78d37c | ||
|
|
602f6e3537 | ||
|
|
e1184a1195 | ||
|
|
bd83ec9b18 | ||
|
|
c6375e437e | ||
|
|
c422b5e3f3 | ||
|
|
903bcdf3dc | ||
|
|
d2aa139c37 | ||
|
|
690c776d13 | ||
|
|
f58ec8a9b7 | ||
|
|
6f50903ef4 |
@@ -4,14 +4,13 @@ import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
"git.rosy.net.cn/jx-callback/business/dao"
|
||||
"git.rosy.net.cn/jx-callback/business/model"
|
||||
"git.rosy.net.cn/jx-callback/business/model/dao"
|
||||
"git.rosy.net.cn/jx-callback/globals"
|
||||
"git.rosy.net.cn/jx-callback/globals/api"
|
||||
"github.com/dchest/captcha"
|
||||
@@ -213,6 +212,9 @@ func SendVerifyCode(authToken, captchaID, captchaValue, authID string) (verfifyC
|
||||
if handler := authers[authType]; handler == nil {
|
||||
err = ErrIllegalAuthType
|
||||
} else {
|
||||
//if user, _ := dao.GetUserByID(dao.GetDB(), "mobile", authID); user != nil {
|
||||
// return "", authInfo, fmt.Errorf("该用户已存在,请勿重复注册!")
|
||||
//}
|
||||
verfifyCode, err = handler.SendVerifyCode(authID)
|
||||
}
|
||||
}
|
||||
@@ -244,6 +246,7 @@ func LoginInternal(ctx *Context, authType, authID, authIDType, authSecret string
|
||||
realAuthID = user.GetID()
|
||||
}
|
||||
if authBindEx, err = handler.VerifySecret(realAuthID, authSecret); err == nil {
|
||||
globals.SugarLogger.Debugf("Login authBindEx", utils.Format4Output(authBindEx, false))
|
||||
if authBindEx == nil { // mobile, email会返回nil(表示不会新建AuthBind实体)
|
||||
user = userProvider.GetUser(authID, authIDType)
|
||||
authBindEx = &AuthBindEx{
|
||||
@@ -266,6 +269,10 @@ func LoginInternal(ctx *Context, authType, authID, authIDType, authSecret string
|
||||
// if user != nil {
|
||||
// authBindEx.UserID = user.GetID()
|
||||
// }
|
||||
if user2 := userProvider.GetUser(authBindEx.UserHint.Email, UserIDMobile); user2 != nil {
|
||||
user = user2
|
||||
}
|
||||
|
||||
} else if authBindEx.UserID != "" {
|
||||
user = userProvider.GetUser(authBindEx.UserID, UserIDID)
|
||||
}
|
||||
@@ -277,9 +284,9 @@ func LoginInternal(ctx *Context, authType, authID, authIDType, authSecret string
|
||||
//如果是小程序
|
||||
if authType == "weixinmini" || authType == "weixinapp" {
|
||||
appID := strings.Split(authSecret, ",")[0]
|
||||
if appID == "wxa4a76d7b4c88604e" || appID == "wx2d6949f724b2541d" || appID == "wx18111a41fd17f24f" { //菜市或者果园
|
||||
if user != nil {
|
||||
binds, err := dao.GetUserBindAuthInfo(dao.GetDB(), user.GetID(), 0, nil, "", "", "wx2bb99eb5d2c9b82c")
|
||||
if appID == "wx08a5c2a8581414ff" || appID == "wx2d6949f724b2541d" || appID == "wx18111a41fd17f24f" || appID == "wx4b5930c13f8b1170" { //菜市或者果园
|
||||
if authInfo.AuthBindInfo.UserID != "" {
|
||||
binds, err := dao.GetUserBindAuthInfo(dao.GetDB(), authInfo.AuthBindInfo.UserID, 0, nil, "", "", []string{"wx2bb99eb5d2c9b82c", "wx4b5930c13f8b1170"})
|
||||
if err != nil {
|
||||
return authInfo, err
|
||||
}
|
||||
@@ -340,6 +347,18 @@ func AddAuthBind(user IUser, newAuthInfo *AuthInfo) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
func AddAuthBindWithMobile(authInfo *AuthInfo, mobile string) (err error) {
|
||||
if handler := authers[authInfo.AuthBindInfo.Type]; handler != nil {
|
||||
user, _ := dao.GetUserByID(dao.GetDB(), "mobile", mobile)
|
||||
authInfo.AuthBindInfo.UserID = user.UserID
|
||||
//handler.UnbindAuth(user.GetID(), newAuthInfo.GetAuthType(), newAuthInfo.GetAuthTypeID(), user.GetName())
|
||||
err = handler.AddAuthBind(authInfo.AuthBindInfo, user.GetName())
|
||||
} else {
|
||||
err = ErrIllegalAuthType
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func UnbindAuth(userID, authType, authTypeID, userName string) (err error) {
|
||||
globals.SugarLogger.Debugf("UnbindAuth userID:%s, authType:%s, authTypeID:%s, userName:%s", userID, authType, authTypeID, userName)
|
||||
if handler := authers[authType]; handler != nil {
|
||||
@@ -463,7 +482,7 @@ func DisableUser(userID, operatorUserName string) (err error) {
|
||||
}
|
||||
|
||||
func GetUserBindAuthInfo(userID string) (authList []*model.AuthBind, err error) {
|
||||
return dao.GetUserBindAuthInfo(dao.GetDB(), userID, model.AuthBindTypeAuth, nil, "", "", "")
|
||||
return dao.GetUserBindAuthInfo(dao.GetDB(), userID, model.AuthBindTypeAuth, nil, "", "", nil)
|
||||
}
|
||||
|
||||
func DeletedTokenInfoWithoutParam(authInfo *AuthInfo) (err error) {
|
||||
@@ -476,16 +495,3 @@ func DeletedTokenInfoWithoutParam(authInfo *AuthInfo) (err error) {
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func CheckWeixinminiAuthBind(userID string) (err error) {
|
||||
var (
|
||||
db = dao.GetDB()
|
||||
)
|
||||
authBinds, err := dao.GetUserBindAuthInfo(db, userID, model.AuthBindTypeAuth, []string{"weixinmini", "weixinapp"}, "", "", "")
|
||||
if len(authBinds) == 0 {
|
||||
return fmt.Errorf("请绑定微信认证方式!")
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
42
business/auth2/authprovider/alipay/alipay.go
Normal file
42
business/auth2/authprovider/alipay/alipay.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package alipay
|
||||
|
||||
import (
|
||||
"git.rosy.net.cn/baseapi/platformapi/alipayapi"
|
||||
"git.rosy.net.cn/jx-callback/business/auth2"
|
||||
"git.rosy.net.cn/jx-callback/business/auth2/authprovider"
|
||||
"git.rosy.net.cn/jx-callback/globals"
|
||||
"git.rosy.net.cn/jx-callback/globals/api"
|
||||
)
|
||||
|
||||
const (
|
||||
AuthType = "alipaycode"
|
||||
|
||||
AuthKey = "GHp3ojlVYRRu2XID4FX2ew=="
|
||||
)
|
||||
|
||||
type Auther struct {
|
||||
authprovider.DefAuther
|
||||
}
|
||||
|
||||
var (
|
||||
AutherObj *Auther
|
||||
)
|
||||
|
||||
func init() {
|
||||
AutherObj = new(Auther)
|
||||
auth2.RegisterAuther(AuthType, AutherObj)
|
||||
}
|
||||
|
||||
func (a *Auther) VerifySecret(dummy, code string) (authBindEx *auth2.AuthBindEx, err error) {
|
||||
globals.SugarLogger.Debugf("VerifySecret dummy:%s, code:%s", dummy, code)
|
||||
tokenInfo, err := api.AliPayAPI.SystemAuthToken(alipayapi.GrantTypeCode, code, "")
|
||||
if err == nil {
|
||||
//userInfo, err2 := api.AliPayAPI.UserInfoShare(tokenInfo.AccessToken)
|
||||
//if err = err2; err == nil {
|
||||
if authBindEx, err = a.UnionFindAuthBind(AuthType, api.AliPayAPI.GetAppID(), nil, tokenInfo.UserID, tokenInfo.AlipayUserID, tokenInfo); err == nil {
|
||||
authBindEx.AuthSecret = tokenInfo.AccessToken
|
||||
authBindEx.AuthSecret2 = tokenInfo.RefreshToken
|
||||
}
|
||||
}
|
||||
return authBindEx, err
|
||||
}
|
||||
@@ -9,8 +9,8 @@ import (
|
||||
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
"git.rosy.net.cn/jx-callback/business/auth2"
|
||||
"git.rosy.net.cn/jx-callback/business/dao"
|
||||
"git.rosy.net.cn/jx-callback/business/model"
|
||||
"git.rosy.net.cn/jx-callback/business/model/dao"
|
||||
"git.rosy.net.cn/jx-callback/globals"
|
||||
"git.rosy.net.cn/jx-callback/globals/api"
|
||||
)
|
||||
@@ -82,7 +82,7 @@ func (a *DefAuther) UnionFindAuthBind(curAuthType, curAuthTypeID string, unionAu
|
||||
} else if dao.IsNoRowsError(err) { // 直接找不到,尝试unionID
|
||||
if unionID != "" { // 且有unionID
|
||||
var authBindList []*model.AuthBind
|
||||
if authBindList, err = dao.GetUserBindAuthInfo(db, "", model.AuthBindTypeAuth, unionAuthTypeList, "", unionID, ""); err == nil && len(authBindList) > 0 { // 通过unionID找到至少一个认证方式
|
||||
if authBindList, err = dao.GetUserBindAuthInfo(db, "", model.AuthBindTypeAuth, unionAuthTypeList, "", unionID, nil); err == nil && len(authBindList) > 0 { // 通过unionID找到至少一个认证方式
|
||||
authBind = authBindList[0]
|
||||
authBind.Type = curAuthType
|
||||
authBind.TypeID = curAuthTypeID
|
||||
|
||||
6
business/auth2/authprovider/dingding/dingding.go
Normal file
6
business/auth2/authprovider/dingding/dingding.go
Normal file
@@ -0,0 +1,6 @@
|
||||
package dingding
|
||||
|
||||
const (
|
||||
AuthTypeStaff = "ddstaff" // 钉钉企业登录
|
||||
AuthTypeQRCode = "ddqrcode"
|
||||
)
|
||||
37
business/auth2/authprovider/dingding/qrcode.go
Normal file
37
business/auth2/authprovider/dingding/qrcode.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package dingding
|
||||
|
||||
import (
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
"git.rosy.net.cn/jx-callback/business/auth2"
|
||||
"git.rosy.net.cn/jx-callback/business/auth2/authprovider"
|
||||
"git.rosy.net.cn/jx-callback/globals"
|
||||
"git.rosy.net.cn/jx-callback/globals/api"
|
||||
)
|
||||
|
||||
type QRCodeAuther struct {
|
||||
authprovider.DefAuther
|
||||
}
|
||||
|
||||
var (
|
||||
AutherObjQRCode *QRCodeAuther
|
||||
)
|
||||
|
||||
func init() {
|
||||
AutherObjQRCode = new(QRCodeAuther)
|
||||
auth2.RegisterAuther(AuthTypeQRCode, AutherObjQRCode)
|
||||
}
|
||||
|
||||
func (a *QRCodeAuther) VerifySecret(dummy, code string) (authBindEx *auth2.AuthBindEx, err error) {
|
||||
globals.SugarLogger.Debugf("dingding qrcode VerifySecret code:%s", code)
|
||||
|
||||
userQRInfo, err := api.DingDingQRCodeAPI.GetUserInfoByCode(code)
|
||||
if err == nil {
|
||||
globals.SugarLogger.Debugf("dingding qrcode VerifySecret code:%s, userQRInfo:%s", code, utils.Format4Output(userQRInfo, false))
|
||||
if authBindEx, err = a.UnionFindAuthBind(AuthTypeQRCode, api.DingDingQRCodeAPI.GetAppID(), []string{AuthTypeStaff, AuthTypeQRCode}, userQRInfo.OpenID, userQRInfo.UnionID, userQRInfo); err == nil {
|
||||
authBindEx.UserHint = &auth2.UserBasic{
|
||||
Name: userQRInfo.Nickname,
|
||||
}
|
||||
}
|
||||
}
|
||||
return authBindEx, err
|
||||
}
|
||||
47
business/auth2/authprovider/dingding/staff.go
Normal file
47
business/auth2/authprovider/dingding/staff.go
Normal file
@@ -0,0 +1,47 @@
|
||||
package dingding
|
||||
|
||||
import (
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
"git.rosy.net.cn/jx-callback/business/auth2"
|
||||
"git.rosy.net.cn/jx-callback/business/auth2/authprovider"
|
||||
"git.rosy.net.cn/jx-callback/business/model"
|
||||
"git.rosy.net.cn/jx-callback/globals"
|
||||
"git.rosy.net.cn/jx-callback/globals/api"
|
||||
)
|
||||
|
||||
type StaffAuther struct {
|
||||
authprovider.DefAuther
|
||||
}
|
||||
|
||||
var (
|
||||
AutherObjStaff *StaffAuther
|
||||
)
|
||||
|
||||
func init() {
|
||||
AutherObjStaff = new(StaffAuther)
|
||||
auth2.RegisterAuther(AuthTypeStaff, AutherObjStaff)
|
||||
}
|
||||
|
||||
func (a *StaffAuther) VerifySecret(dummy, code string) (authBindEx *auth2.AuthBindEx, err error) {
|
||||
globals.SugarLogger.Debugf("dingding staff VerifySecret code:%s", code)
|
||||
|
||||
userID, err := api.DingDingAPI.GetUserID(code)
|
||||
if err == nil {
|
||||
userDetail, err2 := api.DingDingAPI.GetUserDetail(userID.UserID)
|
||||
if err = err2; err == nil {
|
||||
if authBindEx, err = a.UnionFindAuthBind(AuthTypeStaff, api.DingDingQRCodeAPI.GetAppID(), []string{AuthTypeStaff, AuthTypeQRCode}, userID.UserID, utils.Interface2String(userDetail["unionid"]), userDetail); err == nil {
|
||||
authBindEx.UserHint = &auth2.UserBasic{
|
||||
UserID2: userID.UserID,
|
||||
Mobile: utils.Interface2String(userDetail["mobile"]),
|
||||
Email: utils.Interface2String(userDetail["email"]),
|
||||
Name: utils.Interface2String(userDetail["name"]),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return authBindEx, err
|
||||
}
|
||||
|
||||
func (a *StaffAuther) GetUserType() (userType int8) {
|
||||
return model.UserTypeOperator
|
||||
}
|
||||
90
business/auth2/authprovider/mobile/mobile.go
Normal file
90
business/auth2/authprovider/mobile/mobile.go
Normal file
@@ -0,0 +1,90 @@
|
||||
package mobile
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
"git.rosy.net.cn/jx-callback/business/auth2"
|
||||
"git.rosy.net.cn/jx-callback/business/auth2/authprovider"
|
||||
"git.rosy.net.cn/jx-callback/globals"
|
||||
aliyunsmsclient "github.com/KenmyZhang/aliyun-communicate"
|
||||
)
|
||||
|
||||
const (
|
||||
AuthType = auth2.AuthTypeMobile
|
||||
TestVerifyCode = "123456"
|
||||
)
|
||||
|
||||
var (
|
||||
warningMap = map[string]int{
|
||||
"isv.AMOUNT_NOT_ENOUGH": 1,
|
||||
"isv.ACCOUNT_ABNORMAL": 1,
|
||||
"isv.OUT_OF_SERVICE": 1,
|
||||
"isv.DAY_LIMIT_CONTROL": 1,
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
ErrVerifyCodeIsWrong = errors.New("验证码错")
|
||||
)
|
||||
|
||||
type Auther struct {
|
||||
authprovider.DefAuther
|
||||
}
|
||||
|
||||
var (
|
||||
AutherObj *Auther
|
||||
)
|
||||
|
||||
func init() {
|
||||
AutherObj = new(Auther)
|
||||
auth2.RegisterAuther(AuthType, AutherObj)
|
||||
}
|
||||
|
||||
// 特殊接口
|
||||
func (a *Auther) SendVerifyCode(mobileNumber string) (verifyCode string, err error) {
|
||||
verifyCode = a.GenerateVerifyCode(mobileNumber)
|
||||
smsClient := aliyunsmsclient.New("http://dysmsapi.aliyuncs.com/")
|
||||
response, err := smsClient.Execute(globals.AliKey, globals.AliSecret, mobileNumber, globals.SMSSignName, globals.SMSMobileVerifyTemplate, string(utils.MustMarshal(map[string]interface{}{
|
||||
"code": verifyCode,
|
||||
})))
|
||||
a.SaveVerifyCode(mobileNumber, verifyCode)
|
||||
if err == nil && response.Code == aliyunsmsclient.ResponseCodeOk {
|
||||
// a.SaveVerifyCode(mobileNumber, verifyCode)
|
||||
} else {
|
||||
if err == nil {
|
||||
if warningMap[response.Code] == 1 {
|
||||
globals.SugarLogger.Warnf("SendVerifyCode mobileNumber:%s failed with response:%s", mobileNumber, utils.Format4Output(response, false))
|
||||
} else {
|
||||
globals.SugarLogger.Infof("SendVerifyCode mobileNumber:%s failed with response:%s", mobileNumber, utils.Format4Output(response, false))
|
||||
}
|
||||
err = fmt.Errorf("发送短信出错:%s", response.Message)
|
||||
} else {
|
||||
globals.SugarLogger.Warnf("SendVerifyCode mobileNumber:%s failed with error:%v", mobileNumber, err)
|
||||
}
|
||||
}
|
||||
return verifyCode, err
|
||||
}
|
||||
|
||||
func (a *Auther) VerifySecret(mobileNumber, code string) (authBindEx *auth2.AuthBindEx, err error) {
|
||||
globals.SugarLogger.Debugf("VerifySecret mobileNumber:%s, code:%s", mobileNumber, code)
|
||||
|
||||
err = ErrVerifyCodeIsWrong
|
||||
if (code == auth2.InternalAuthSecret ||
|
||||
auth2.TestMobileMap[mobileNumber] == 1 && code == TestVerifyCode) ||
|
||||
a.VerifyCode(mobileNumber, code) {
|
||||
err = nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 此函数为空
|
||||
func (a *Auther) AddAuthBind(authBindEx *auth2.AuthBindEx, userName string) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
// 此函数为空
|
||||
func (a *Auther) UnbindAuth(userID, authType, authTypeID, userName string) (err error) {
|
||||
return err
|
||||
}
|
||||
88
business/auth2/authprovider/password/password.go
Normal file
88
business/auth2/authprovider/password/password.go
Normal file
@@ -0,0 +1,88 @@
|
||||
package password
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
"git.rosy.net.cn/jx-callback/business/auth2"
|
||||
"git.rosy.net.cn/jx-callback/business/auth2/authprovider"
|
||||
"git.rosy.net.cn/jx-callback/business/model"
|
||||
"git.rosy.net.cn/jx-callback/business/model/dao"
|
||||
"git.rosy.net.cn/jx-callback/globals"
|
||||
)
|
||||
|
||||
const (
|
||||
AuthType = auth2.AuthTypePassword
|
||||
)
|
||||
|
||||
type Auther struct {
|
||||
authprovider.DefAuther
|
||||
}
|
||||
|
||||
var (
|
||||
AutherObj *Auther
|
||||
)
|
||||
|
||||
var (
|
||||
ErrUserAndPassNotMatch = errors.New("用户名密码不匹配")
|
||||
)
|
||||
|
||||
func init() {
|
||||
AutherObj = new(Auther)
|
||||
auth2.RegisterAuther(AuthType, AutherObj)
|
||||
}
|
||||
|
||||
func (a *Auther) VerifySecret(userID, passMD5 string) (authBindEx *auth2.AuthBindEx, err error) {
|
||||
globals.SugarLogger.Debugf("localpass VerifySecret userID:%s", userID)
|
||||
var authBind *model.AuthBind
|
||||
if authBind, err = dao.GetAuthBind(dao.GetDB(), model.AuthBindTypeAuth, AuthType, userID); err == nil {
|
||||
if err = a.checkPassword(authBind, passMD5); err == nil {
|
||||
authBindEx = &auth2.AuthBindEx{
|
||||
AuthBind: *authBind,
|
||||
}
|
||||
}
|
||||
} else if dao.IsNoRowsError(err) {
|
||||
err = auth2.ErrUserAuthTypeNotExist
|
||||
}
|
||||
return authBindEx, err
|
||||
}
|
||||
|
||||
// 特殊接口
|
||||
func (a *Auther) ChangePassword(userID, userName, oldPassMD5, newPassMD5 string) (err error) {
|
||||
var authBind *model.AuthBind
|
||||
db := dao.GetDB()
|
||||
salt := utils.GetUUID()
|
||||
encryptPwd := a.encryptPassword(newPassMD5, salt)
|
||||
if authBind, err = dao.GetAuthBind(db, model.AuthBindTypeAuth, AuthType, userID); err == nil {
|
||||
if err = a.checkPassword(authBind, oldPassMD5); err == nil || authBind.AuthSecret == "" { // 如果原密码为空,不判断原密码,代表重置密码
|
||||
_, err = dao.UpdateEntityLogically(db, authBind, map[string]interface{}{
|
||||
"AuthSecret": encryptPwd,
|
||||
"AuthSecret2": salt,
|
||||
}, userName, nil)
|
||||
}
|
||||
} else if dao.IsNoRowsError(err) {
|
||||
err = a.AddAuthBind(&auth2.AuthBindEx{
|
||||
AuthBind: model.AuthBind{
|
||||
UserID: userID,
|
||||
Type: AuthType,
|
||||
AuthID: userID,
|
||||
AuthSecret: encryptPwd,
|
||||
AuthSecret2: salt,
|
||||
},
|
||||
}, userName)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (a *Auther) encryptPassword(password, salt string) string {
|
||||
return fmt.Sprintf("%x", sha1.Sum([]byte(password+salt)))
|
||||
}
|
||||
|
||||
func (a *Auther) checkPassword(authBind *model.AuthBind, passMD5 string) (err error) {
|
||||
if authBind.AuthSecret != a.encryptPassword(passMD5, authBind.AuthSecret2) {
|
||||
return ErrUserAndPassNotMatch
|
||||
}
|
||||
return nil
|
||||
}
|
||||
105
business/auth2/authprovider/weixin/weixin.go
Normal file
105
business/auth2/authprovider/weixin/weixin.go
Normal file
@@ -0,0 +1,105 @@
|
||||
package weixin
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"git.rosy.net.cn/baseapi/platformapi/weixinapi"
|
||||
"git.rosy.net.cn/jx-callback/business/auth2"
|
||||
"git.rosy.net.cn/jx-callback/business/auth2/authprovider"
|
||||
"git.rosy.net.cn/jx-callback/business/model"
|
||||
"git.rosy.net.cn/jx-callback/globals"
|
||||
"git.rosy.net.cn/jx-callback/globals/api"
|
||||
)
|
||||
|
||||
const (
|
||||
AuthTypeWeixin = "wxqrcode" // 微信扫码
|
||||
AuthTypeMP = "weixinsns" // 公众号
|
||||
AuthTypeWXNative = "wxnative" // 微信APP
|
||||
AuthTypeWxApp = "weixinapp" //app微信登录
|
||||
)
|
||||
|
||||
type Auther struct {
|
||||
authprovider.DefAuther
|
||||
authType string
|
||||
}
|
||||
|
||||
var (
|
||||
AutherObjWX *Auther
|
||||
AutherObjMP *Auther
|
||||
AutherObjNative *Auther
|
||||
AutherObjApp *Auther
|
||||
)
|
||||
|
||||
var (
|
||||
ErrStateIsWrong = errors.New("登录state非法")
|
||||
)
|
||||
|
||||
func init() {
|
||||
AutherObjWX = &Auther{
|
||||
authType: AuthTypeWeixin,
|
||||
}
|
||||
auth2.RegisterAuther(AuthTypeWeixin, AutherObjWX)
|
||||
|
||||
AutherObjMP = &Auther{
|
||||
authType: AuthTypeMP,
|
||||
}
|
||||
auth2.RegisterAuther(AuthTypeMP, AutherObjMP)
|
||||
|
||||
AutherObjNative = &Auther{
|
||||
authType: AuthTypeWXNative,
|
||||
}
|
||||
auth2.RegisterAuther(AuthTypeWXNative, AutherObjNative)
|
||||
|
||||
AutherObjApp = &Auther{
|
||||
authType: AuthTypeWxApp,
|
||||
}
|
||||
auth2.RegisterAuther(AuthTypeWxApp, AutherObjApp)
|
||||
}
|
||||
|
||||
func (a *Auther) VerifySecret(id, secret string) (authBindEx *auth2.AuthBindEx, err error) {
|
||||
globals.SugarLogger.Debugf("weixin VerifySecret id:%s secret:%s", id, secret)
|
||||
var openID, accessToken string
|
||||
_, jsCode := SplitJsCode(secret)
|
||||
if a.authType != AuthTypeWXNative {
|
||||
state := id
|
||||
code := jsCode
|
||||
if state == "" {
|
||||
token, err2 := a.getAPI().SNSRetrieveToken(code)
|
||||
if err = err2; err == nil {
|
||||
openID = token.OpenID
|
||||
accessToken = token.AccessToken
|
||||
}
|
||||
} else {
|
||||
err = ErrStateIsWrong
|
||||
}
|
||||
} else {
|
||||
openID = id
|
||||
accessToken = secret
|
||||
}
|
||||
if err == nil {
|
||||
wxUserinfo, err2 := a.getAPI().SNSGetUserInfo(accessToken, openID)
|
||||
if err = err2; err == nil {
|
||||
if authBindEx, err = a.UnionFindAuthBind(a.authType, a.getAPI().GetAppID(), []string{AuthTypeWeixin, AuthTypeMP, AuthTypeMini, AuthTypeWXNative, AuthTypeWxApp}, wxUserinfo.OpenID, wxUserinfo.UnionID, wxUserinfo); err == nil {
|
||||
authBindEx.UserHint = &auth2.UserBasic{
|
||||
Name: wxUserinfo.NickName,
|
||||
Avatar: wxUserinfo.HeadImgURL,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return authBindEx, err
|
||||
}
|
||||
|
||||
func (a *Auther) getAPI() *weixinapi.API {
|
||||
if a.authType == AuthTypeWeixin {
|
||||
return api.WeixinPageAPI
|
||||
}
|
||||
if a.authType == AuthTypeWxApp {
|
||||
return api.WeixinApp
|
||||
}
|
||||
return api.WeixinAPI
|
||||
}
|
||||
|
||||
func (a *Auther) GetUserType() (userType int8) {
|
||||
return model.UserTypeStoreBoss
|
||||
}
|
||||
139
business/auth2/authprovider/weixin/weixin_mini.go
Normal file
139
business/auth2/authprovider/weixin/weixin_mini.go
Normal file
@@ -0,0 +1,139 @@
|
||||
package weixin
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"git.rosy.net.cn/baseapi/platformapi/weixinapi"
|
||||
"git.rosy.net.cn/jx-callback/business/auth2"
|
||||
"git.rosy.net.cn/jx-callback/business/auth2/authprovider"
|
||||
"git.rosy.net.cn/jx-callback/business/model"
|
||||
"git.rosy.net.cn/jx-callback/globals"
|
||||
"git.rosy.net.cn/jx-callback/globals/api"
|
||||
)
|
||||
|
||||
const (
|
||||
AuthTypeMini = "weixinmini" // 小程序
|
||||
)
|
||||
|
||||
type MiniAuther struct {
|
||||
authprovider.DefAuther
|
||||
}
|
||||
|
||||
var (
|
||||
ErrAuthTypeShouldBeMini = errors.New("当前操作要求是小程序登录方式")
|
||||
)
|
||||
|
||||
var (
|
||||
AutherObjMini *MiniAuther
|
||||
)
|
||||
|
||||
func init() {
|
||||
AutherObjMini = new(MiniAuther)
|
||||
auth2.RegisterAuther(AuthTypeMini, AutherObjMini)
|
||||
}
|
||||
|
||||
func (a *MiniAuther) VerifySecret(dummy, jsCode string) (authBindEx *auth2.AuthBindEx, err error) {
|
||||
globals.SugarLogger.Debugf("weixin mini VerifySecret jsCode:%s", jsCode)
|
||||
|
||||
appID, jsCode := SplitJsCode(jsCode)
|
||||
sessionInfo, err := getWxApp(appID).SNSCode2Session(jsCode)
|
||||
if err == nil {
|
||||
sessionKey := sessionInfo.SessionKey
|
||||
sessionInfo.SessionKey = ""
|
||||
if authBindEx, err = a.UnionFindAuthBind(AuthTypeMini, getWxApp(appID).GetAppID(), []string{AuthTypeWeixin, AuthTypeMP, AuthTypeMini, AuthTypeWXNative}, sessionInfo.OpenID, sessionInfo.UnionID, sessionInfo); err == nil {
|
||||
authBindEx.UserData = sessionKey
|
||||
}
|
||||
}
|
||||
return authBindEx, err
|
||||
}
|
||||
|
||||
// 特殊接口
|
||||
func (a *MiniAuther) DecryptData(authInfo *auth2.AuthInfo, jsCode, encryptedData, iv string) (decryptedDataBase64 string, err error) {
|
||||
globals.SugarLogger.Debugf("weixin mini DecryptData jsCode:%s, encryptedData:%s, iv:%s", jsCode, encryptedData, iv)
|
||||
var sessionKey string
|
||||
appID, jsCode := SplitJsCode(jsCode)
|
||||
if jsCode != "" {
|
||||
sessionInfo, err := getWxApp(appID).SNSCode2Session(jsCode)
|
||||
if err == nil {
|
||||
// if authBindEx, err := a.UnionFindAuthBind(AuthTypeMini, getWxApp(appID).GetAppID(), []string{AuthTypeMini}, sessionInfo.OpenID, "", nil); err == nil {
|
||||
// if authBindEx.UserID != authInfo.GetID() {
|
||||
// return "", fmt.Errorf("jsCode与token不匹配")
|
||||
// }
|
||||
// } else {
|
||||
// return "", err
|
||||
// }
|
||||
sessionKey = sessionInfo.SessionKey
|
||||
} else {
|
||||
return "", err
|
||||
}
|
||||
} else {
|
||||
if authInfo.AuthBindInfo.Type != AuthTypeMini {
|
||||
// return "", ErrAuthTypeShouldBeMini
|
||||
}
|
||||
sessionKey = authInfo.AuthBindInfo.UserData.(string)
|
||||
}
|
||||
// decryptedData, err := ProxySNSDecodeMiniProgramData(encryptedData, sessionKey, iv)
|
||||
decryptedData, err := weixinapi.SNSDecodeMiniProgramData(encryptedData, sessionKey, iv)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// authInfo.AuthBindInfo.UserData = sessionKey
|
||||
return string(decryptedData), nil
|
||||
}
|
||||
|
||||
func (a *MiniAuther) GetUserType() (userType int8) {
|
||||
return model.UserTypeStoreBoss
|
||||
}
|
||||
|
||||
func getWxApp(appID string) (miniApi *weixinapi.API) {
|
||||
miniApi = api.WeixinMiniAPI
|
||||
if len(appID) > 0 && appID == api.WeixinMiniAppID2 {
|
||||
miniApi = api.WeixinMiniAPI2
|
||||
}
|
||||
if len(appID) > 0 && appID == api.WeixinMiniAppIDsc {
|
||||
miniApi = api.WeixinMiniAPIsc
|
||||
}
|
||||
return miniApi
|
||||
}
|
||||
|
||||
func SplitJsCode(jsCode string) (appID, realJsCode string) {
|
||||
list := strings.Split(jsCode, ",")
|
||||
if len(list) == 2 {
|
||||
appID = list[0]
|
||||
realJsCode = list[1]
|
||||
} else if len(list) == 1 {
|
||||
realJsCode = jsCode
|
||||
} else {
|
||||
globals.SugarLogger.Warnf("SplitJsCode abnormal jsCode:%s", jsCode)
|
||||
}
|
||||
return appID, realJsCode
|
||||
}
|
||||
|
||||
func ComposeJsCode(appID, jsCode string) (composedCode string) {
|
||||
composedCode = strings.Join([]string{
|
||||
appID,
|
||||
jsCode,
|
||||
}, ",")
|
||||
return composedCode
|
||||
}
|
||||
|
||||
// func ProxySNSCode2Session(jsCode string) (sessionInfo *weixinapi.SessionInfo, err error) {
|
||||
// miniApi := api.WeixinMiniAPI
|
||||
// list := strings.Split(jsCode, ",")
|
||||
// if len(list) >= 2 && len(list[0]) == len("wx4b5930c13f8b1170") {
|
||||
// if list[0] == api.WeixinMiniAppID2 {
|
||||
// miniApi = api.WeixinMiniAPI2
|
||||
// }
|
||||
// miniApi = getWxApp(list[0])
|
||||
// jsCode = strings.Join(list[1:], ",")
|
||||
// }
|
||||
// sessionInfo, err = miniApi.SNSCode2Session(jsCode)
|
||||
// return sessionInfo, err
|
||||
// }
|
||||
|
||||
// func ProxySNSDecodeMiniProgramData(encryptedData, sessionKey, iv string) (decryptedData []byte, err error) {
|
||||
// globals.SugarLogger.Debugf("ProxySNSDecodeMiniProgramData, encryptedData:%s, sessionKey:%s, iv:%s", encryptedData, sessionKey, iv)
|
||||
// decryptedData, err = api.WeixinMiniAPI.SNSDecodeMiniProgramData(encryptedData, sessionKey, iv)
|
||||
// return decryptedData, err
|
||||
// }
|
||||
@@ -6,8 +6,8 @@ import (
|
||||
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
"git.rosy.net.cn/jx-callback/business/authz"
|
||||
"git.rosy.net.cn/jx-callback/business/dao"
|
||||
"git.rosy.net.cn/jx-callback/business/model"
|
||||
"git.rosy.net.cn/jx-callback/business/model/dao"
|
||||
)
|
||||
|
||||
func GetRoleDescription(name string, storeID int) (description string) {
|
||||
|
||||
171
business/authz/casbinauth/adapter.go
Normal file
171
business/authz/casbinauth/adapter.go
Normal file
@@ -0,0 +1,171 @@
|
||||
package casbinauth
|
||||
|
||||
import (
|
||||
jxmodel "git.rosy.net.cn/jx-callback/business/model"
|
||||
"git.rosy.net.cn/jx-callback/globals"
|
||||
"github.com/astaxie/beego/client/orm"
|
||||
"github.com/casbin/casbin/model"
|
||||
"github.com/casbin/casbin/persist"
|
||||
)
|
||||
|
||||
type Adapter struct {
|
||||
}
|
||||
|
||||
// finalizer is the destructor for Adapter.
|
||||
func finalizer(a *Adapter) {
|
||||
}
|
||||
|
||||
func NewAdapter() *Adapter {
|
||||
return &Adapter{}
|
||||
}
|
||||
|
||||
func loadPolicyLine(line jxmodel.CasbinRule, model model.Model) {
|
||||
lineText := line.PType
|
||||
if line.V0 != "" {
|
||||
lineText += ", " + line.V0
|
||||
}
|
||||
if line.V1 != "" {
|
||||
lineText += ", " + line.V1
|
||||
}
|
||||
if line.V2 != "" {
|
||||
lineText += ", " + line.V2
|
||||
}
|
||||
if line.V3 != "" {
|
||||
lineText += ", " + line.V3
|
||||
}
|
||||
if line.V4 != "" {
|
||||
lineText += ", " + line.V4
|
||||
}
|
||||
if line.V5 != "" {
|
||||
lineText += ", " + line.V5
|
||||
}
|
||||
|
||||
persist.LoadPolicyLine(lineText, model)
|
||||
}
|
||||
|
||||
func (a *Adapter) LoadPolicy(model model.Model) error {
|
||||
var lines []jxmodel.CasbinRule
|
||||
o := orm.NewOrm()
|
||||
_, err := o.QueryTable("casbin_rule").Limit(-1).All(&lines)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, line := range lines {
|
||||
loadPolicyLine(line, model)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func savePolicyLine(ptype string, rule []string) jxmodel.CasbinRule {
|
||||
line := jxmodel.CasbinRule{}
|
||||
|
||||
line.PType = ptype
|
||||
if len(rule) > 0 {
|
||||
line.V0 = rule[0]
|
||||
}
|
||||
if len(rule) > 1 {
|
||||
line.V1 = rule[1]
|
||||
}
|
||||
if len(rule) > 2 {
|
||||
line.V2 = rule[2]
|
||||
}
|
||||
if len(rule) > 3 {
|
||||
line.V3 = rule[3]
|
||||
}
|
||||
if len(rule) > 4 {
|
||||
line.V4 = rule[4]
|
||||
}
|
||||
if len(rule) > 5 {
|
||||
line.V5 = rule[5]
|
||||
}
|
||||
|
||||
return line
|
||||
}
|
||||
|
||||
func (a *Adapter) clearAll(o orm.Ormer) (err error) {
|
||||
_, err = o.Raw(`
|
||||
DELETE t1
|
||||
FROM casbin_rule t1
|
||||
`).Exec()
|
||||
return err
|
||||
}
|
||||
|
||||
// SavePolicy saves policy to database.
|
||||
func (a *Adapter) SavePolicy(model model.Model) error {
|
||||
globals.SugarLogger.Debugf("SavePolicy")
|
||||
o := orm.NewOrm()
|
||||
|
||||
a.clearAll(o)
|
||||
var lines []jxmodel.CasbinRule
|
||||
|
||||
for ptype, ast := range model["p"] {
|
||||
for _, rule := range ast.Policy {
|
||||
line := savePolicyLine(ptype, rule)
|
||||
lines = append(lines, line)
|
||||
}
|
||||
}
|
||||
|
||||
for ptype, ast := range model["g"] {
|
||||
for _, rule := range ast.Policy {
|
||||
line := savePolicyLine(ptype, rule)
|
||||
lines = append(lines, line)
|
||||
}
|
||||
}
|
||||
|
||||
_, err := o.InsertMulti(len(lines), lines)
|
||||
return err
|
||||
}
|
||||
|
||||
// AddPolicy adds a policy rule to the storage.
|
||||
func (a *Adapter) AddPolicy(sec string, ptype string, rule []string) error {
|
||||
o := orm.NewOrm()
|
||||
line := savePolicyLine(ptype, rule)
|
||||
_, err := o.Insert(&line)
|
||||
return err
|
||||
}
|
||||
|
||||
// RemovePolicy removes a policy rule from the storage.
|
||||
func (a *Adapter) RemovePolicy(sec string, ptype string, rule []string) error {
|
||||
o := orm.NewOrm()
|
||||
line := savePolicyLine(ptype, rule)
|
||||
_, err := o.Delete(&line, "p_type", "v0", "v1", "v2", "v3", "v4", "v5")
|
||||
return err
|
||||
}
|
||||
|
||||
// RemoveFilteredPolicy removes policy rules that match the filter from the storage.
|
||||
func (a *Adapter) RemoveFilteredPolicy(sec string, ptype string, fieldIndex int, fieldValues ...string) error {
|
||||
line := jxmodel.CasbinRule{}
|
||||
|
||||
line.PType = ptype
|
||||
filter := []string{}
|
||||
filter = append(filter, "p_type")
|
||||
if fieldIndex <= 0 && 0 < fieldIndex+len(fieldValues) {
|
||||
line.V0 = fieldValues[0-fieldIndex]
|
||||
filter = append(filter, "v0")
|
||||
}
|
||||
if fieldIndex <= 1 && 1 < fieldIndex+len(fieldValues) {
|
||||
line.V1 = fieldValues[1-fieldIndex]
|
||||
filter = append(filter, "v1")
|
||||
}
|
||||
if fieldIndex <= 2 && 2 < fieldIndex+len(fieldValues) {
|
||||
line.V2 = fieldValues[2-fieldIndex]
|
||||
filter = append(filter, "v2")
|
||||
}
|
||||
if fieldIndex <= 3 && 3 < fieldIndex+len(fieldValues) {
|
||||
line.V3 = fieldValues[3-fieldIndex]
|
||||
filter = append(filter, "v3")
|
||||
}
|
||||
if fieldIndex <= 4 && 4 < fieldIndex+len(fieldValues) {
|
||||
line.V4 = fieldValues[4-fieldIndex]
|
||||
filter = append(filter, "v4")
|
||||
}
|
||||
if fieldIndex <= 5 && 5 < fieldIndex+len(fieldValues) {
|
||||
line.V5 = fieldValues[5-fieldIndex]
|
||||
filter = append(filter, "v5")
|
||||
}
|
||||
|
||||
o := orm.NewOrm()
|
||||
_, err := o.Delete(&line, filter...)
|
||||
return err
|
||||
}
|
||||
49
business/authz/casbinauth/casbinauth.go
Normal file
49
business/authz/casbinauth/casbinauth.go
Normal file
@@ -0,0 +1,49 @@
|
||||
package casbinauth
|
||||
|
||||
import (
|
||||
"git.rosy.net.cn/jx-callback/business/authz"
|
||||
"git.rosy.net.cn/jx-callback/business/authz/autils"
|
||||
"github.com/casbin/casbin"
|
||||
"github.com/casbin/casbin/errors"
|
||||
)
|
||||
|
||||
type CasbinAuthz struct {
|
||||
enforcer *casbin.SyncedEnforcer
|
||||
}
|
||||
|
||||
func New(modelFile string) (authObj authz.IAuthz, err error) {
|
||||
obj := &CasbinAuthz{}
|
||||
obj.enforcer, err = casbin.NewSyncedEnforcer(modelFile, NewAdapter())
|
||||
return obj, err
|
||||
}
|
||||
|
||||
func (c *CasbinAuthz) AddRole4User(userID string, r *authz.RoleInfo) (err error) {
|
||||
_, err = c.enforcer.AddRoleForUser(userID, r.GetFullName())
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *CasbinAuthz) DeleteRole4User(userID string, r *authz.RoleInfo) (err error) {
|
||||
_, err = c.enforcer.DeleteRoleForUser(userID, r.GetFullName())
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *CasbinAuthz) GetUserRoleList(userID string) (roleList []*authz.RoleInfo, err error) {
|
||||
roleNameList, err := c.enforcer.GetRolesForUser(userID)
|
||||
if err == nil && len(roleNameList) > 0 {
|
||||
roleList = autils.FullRoleName2RoleList(roleNameList)
|
||||
}
|
||||
return roleList, err
|
||||
}
|
||||
|
||||
func (c *CasbinAuthz) GetRoleUserList(r *authz.RoleInfo) (userIDList []string, err error) {
|
||||
// globals.SugarLogger.Debug(roleFullName)
|
||||
userIDList, err = c.enforcer.GetUsersForRole(r.GetFullName())
|
||||
if err == errors.ERR_NAME_NOT_FOUND {
|
||||
err = nil
|
||||
}
|
||||
return userIDList, err
|
||||
}
|
||||
|
||||
// func (c *CasbinAuthz) GetAllRoleList() (roleList []*authz.RoleInfo) {
|
||||
// return authz.FullRoleName2RoleList(c.enforcer.GetAllRoles())
|
||||
// }
|
||||
128
business/cs/weimob_order.go
Normal file
128
business/cs/weimob_order.go
Normal file
@@ -0,0 +1,128 @@
|
||||
package cs
|
||||
|
||||
import (
|
||||
"git.rosy.net.cn/baseapi/platformapi/weimobapi"
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
"git.rosy.net.cn/jx-callback/business/authz/autils"
|
||||
"git.rosy.net.cn/jx-callback/business/jxstore/cms"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
|
||||
"git.rosy.net.cn/jx-callback/business/model"
|
||||
"git.rosy.net.cn/jx-callback/business/model/dao"
|
||||
"git.rosy.net.cn/jx-callback/globals"
|
||||
"git.rosy.net.cn/jx-callback/globals/api"
|
||||
)
|
||||
|
||||
const (
|
||||
minCSOrderPayment = 0 // 供货订单的最小金额(单位为元)
|
||||
maxUnitPrice = 3000 // 为防止误填单价,限制单价最高(单位为分)
|
||||
)
|
||||
|
||||
func OnCallbackMsg(msg *weimobapi.CallbackMsg) (response *weimobapi.CallbackResponse) {
|
||||
orderID := utils.Int64ToStr(msg.OrderNo)
|
||||
jxutils.CallMsgHandler(func() {
|
||||
response = onOrderMsg(msg)
|
||||
}, jxutils.ComposeUniversalOrderID(orderID, model.VendorIDWSC))
|
||||
return response
|
||||
}
|
||||
|
||||
func onOrderMsg(msg *weimobapi.CallbackMsg) (response *weimobapi.CallbackResponse) {
|
||||
globals.SugarLogger.Debugf("onOrderMsg:%s", utils.Format4Output(msg, true))
|
||||
if msg.Event == weimobapi.MsgEventOrderStatusChange {
|
||||
if utils.ForceInterface2Int64(msg.MsgBody["orderStatus"]) == weimobapi.MsgOrderStatusFinished {
|
||||
if orderDetail, err := api.WeimobAPI.QueryOrderDetail2(msg.OrderNo, false); err == nil {
|
||||
if orderDetail.OrderStatus == weimobapi.OrderStatusFinished && orderDetail.PaymentAmount >= minCSOrderPayment {
|
||||
changeStoreSkusByOrder(orderDetail)
|
||||
}
|
||||
} else {
|
||||
globals.SugarLogger.Debugf("onOrderMsg order:%s failed with err:%v", msg.OrderNo, err)
|
||||
response = weimobapi.Err2CallbackResponse(err, "")
|
||||
}
|
||||
}
|
||||
}
|
||||
return response
|
||||
}
|
||||
|
||||
func changeStoreSkusByOrder(order *weimobapi.OrderDetail) {
|
||||
globals.SugarLogger.Debugf("changeStoreSkusByOrder order:%s", utils.Format4Output(order, true))
|
||||
receiverMobile := order.DeliveryDetail.LogisticsDeliveryDetail.ReceiverMobile
|
||||
ctx := jxcontext.NewWithUserName(nil, utils.LimitStringLen(utils.Int64ToStr(order.OrderNo), 32), nil, nil)
|
||||
if storeList, err := GetStoreList4Mobile(dao.GetDB(), []string{receiverMobile}); err == nil {
|
||||
if len(storeList) >= 1 {
|
||||
var skuBindInfos []*cms.StoreSkuBindInfo
|
||||
storeID := storeList[0].ID
|
||||
globals.SugarLogger.Debugf("changeStoreSkusByOrder storeID:%d", storeID)
|
||||
for _, v := range order.ItemList {
|
||||
nameID := int(utils.Str2Int64WithDefault(v.SkuCode, 0))
|
||||
unitPrice := v.CostPrice
|
||||
if nameID > 0 && (unitPrice > 0 && unitPrice < maxUnitPrice) {
|
||||
skuBindInfos = append(skuBindInfos, &cms.StoreSkuBindInfo{
|
||||
StoreID: storeID,
|
||||
NameID: nameID,
|
||||
UnitPrice: int(jxutils.StandardPrice2Int(unitPrice)),
|
||||
IsFocus: 1,
|
||||
IsSale: 1,
|
||||
})
|
||||
} else {
|
||||
globals.SugarLogger.Infof("[运营],微商城订单:%d,商品:%s没有设置正确的SkuName编码或单价,当前商家编码:%s,市场价:%s", order.OrderNo, v.SkuNum, v.SkuCode, jxutils.IntPrice2StandardString(jxutils.StandardPrice2Int(unitPrice)))
|
||||
}
|
||||
}
|
||||
if len(skuBindInfos) > 0 {
|
||||
var nameIDs []int
|
||||
for _, v := range skuBindInfos {
|
||||
nameIDs = append(nameIDs, v.NameID)
|
||||
}
|
||||
if skuNamesInfo, err := cms.GetSkuNames(ctx, "", false, false, map[string]interface{}{
|
||||
"nameIDs": string(utils.MustMarshal(nameIDs)),
|
||||
}, 0, 0); err == nil {
|
||||
for _, skuName := range skuNamesInfo.SkuNames {
|
||||
if skuName.Status != model.SkuStatusNormal {
|
||||
cms.UpdateSkuName(ctx, skuName.ID, map[string]interface{}{
|
||||
"status": model.SkuStatusNormal,
|
||||
}, false)
|
||||
}
|
||||
for _, sku := range skuName.Skus {
|
||||
if sku.Status != model.SkuStatusNormal {
|
||||
cms.UpdateSku(ctx, sku.ID, map[string]interface{}{
|
||||
"status": model.SkuStatusNormal,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
cms.UpdateStoreSkus(ctx, 0, storeID, skuBindInfos, true, true)
|
||||
} else {
|
||||
globals.SugarLogger.Debugf("changeStoreSkusByOrder orderID:%d, storeID:%d is empty", order.OrderNo, storeID)
|
||||
}
|
||||
} else {
|
||||
globals.SugarLogger.Infof("[运营],微商城订单:%d,手机:%s找不到唯一一个本地门店%d", order.OrderNo, receiverMobile, len(storeList))
|
||||
}
|
||||
} else {
|
||||
globals.SugarLogger.Warnf("changeStoreSkusByOrder orderNo:%d, receiverMobile:%s failed with err:%v", order.OrderNo, receiverMobile, err)
|
||||
}
|
||||
}
|
||||
|
||||
func GetStoreList4Mobile(db *dao.DaoDB, mobileList []string) (storeList []*model.Store, err error) {
|
||||
sql := `
|
||||
SELECT t1.*
|
||||
FROM store t1
|
||||
WHERE t1.deleted_at = ? /*AND t1.change_price_type = ?*/`
|
||||
sqlParams := []interface{}{
|
||||
utils.DefaultTimeValue,
|
||||
// model.StoreChangePriceTypeBossDisabled,
|
||||
}
|
||||
if len(mobileList) > 0 {
|
||||
questionMarks := dao.GenQuestionMarks(len(mobileList))
|
||||
sql += " AND (t1.tel1 IN (" + questionMarks + ") OR t1.tel2 IN (" + questionMarks + `)
|
||||
OR (SELECT
|
||||
COUNT(*)
|
||||
FROM casbin_rule t2
|
||||
JOIN user t3 ON t3.user_id = t2.v0 AND t3.mobile IN (` + questionMarks + `)
|
||||
WHERE t2.v1 = CONCAT(?, t1.id)
|
||||
) > 0)
|
||||
`
|
||||
sqlParams = append(sqlParams, mobileList, mobileList, mobileList, autils.NewStoreBossRole(-1).GetFullName())
|
||||
}
|
||||
err = dao.GetRows(db, &storeList, sql, sqlParams...)
|
||||
return storeList, err
|
||||
}
|
||||
24
business/cs/weimob_order_test.go
Normal file
24
business/cs/weimob_order_test.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package cs
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
"git.rosy.net.cn/jx-callback/business/model/dao"
|
||||
"git.rosy.net.cn/jx-callback/globals/api2"
|
||||
"git.rosy.net.cn/jx-callback/globals/testinit"
|
||||
)
|
||||
|
||||
func init() {
|
||||
testinit.Init()
|
||||
api2.Init()
|
||||
}
|
||||
|
||||
func TestGetStoreList4Mobile(t *testing.T) {
|
||||
storeList, err := GetStoreList4Mobile(dao.GetDB(), []string{"18180948107"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(utils.Format4Output(storeList, false))
|
||||
t.Log(len(storeList))
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
package dao
|
||||
|
||||
import (
|
||||
"git.rosy.net.cn/jx-callback/business/model"
|
||||
)
|
||||
|
||||
func GetDataResource(db *DaoDB, hashCode, fullURL string) (dataRes *model.DataResource, err error) {
|
||||
sql := `
|
||||
SELECT t1.*
|
||||
FROM data_resource t1
|
||||
WHERE 0 = 1`
|
||||
sqlParams := []interface{}{}
|
||||
if hashCode != "" {
|
||||
sql += " OR t1.hash_code = ?"
|
||||
sqlParams = append(sqlParams, hashCode)
|
||||
}
|
||||
if fullURL != "" {
|
||||
sql += " OR t1.main_url = ? OR t1.qiniu_url = ? OR t1.ebai_url = ? OR t1.mtwm_url = ?"
|
||||
sqlParams = append(sqlParams, fullURL, fullURL, fullURL, fullURL)
|
||||
}
|
||||
err = GetRow(db, &dataRes, sql, sqlParams...)
|
||||
return dataRes, err
|
||||
}
|
||||
|
||||
func GetNeedUploadDataResource(db *DaoDB) (dataResList []*model.DataResource, err error) {
|
||||
sql := `
|
||||
SELECT t1.*
|
||||
FROM data_resource t1
|
||||
WHERE t1.use_type <> 0 AND (t1.ebai_url = '' OR t1.mtwm_url = '')`
|
||||
err = GetRows(db, &dataResList, sql)
|
||||
return dataResList, err
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
package dao
|
||||
|
||||
import (
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
"git.rosy.net.cn/jx-callback/business/model"
|
||||
"git.rosy.net.cn/jx-callback/globals"
|
||||
)
|
||||
|
||||
// QueryUserPrinter 查询用户打印机
|
||||
func QueryUserPrinter(userId, printNo string) (*model.Printer, error) {
|
||||
sql := `
|
||||
SELECT *
|
||||
FROM printer
|
||||
WHERE 1 = 1 AND deleted_at = ?
|
||||
`
|
||||
sqlParams := []interface{}{
|
||||
utils.DefaultTimeValue,
|
||||
}
|
||||
|
||||
if printNo != "" {
|
||||
sql += " AND print_no = ?"
|
||||
sqlParams = append(sqlParams, printNo)
|
||||
}
|
||||
if userId != "" {
|
||||
sql += " AND user_id = ?"
|
||||
sqlParams = append(sqlParams, userId)
|
||||
}
|
||||
var printer = &model.Printer{}
|
||||
err := GetRow(GetDB(), &printer, sql, sqlParams)
|
||||
return printer, err
|
||||
}
|
||||
|
||||
func GetPrinters(db *DaoDB, appID int, printNo string, status, statusNeq int) (printers []*model.Printer, err error) {
|
||||
sql := `
|
||||
SELECT *
|
||||
FROM printer
|
||||
WHERE 1 = 1 AND deleted_at = ?
|
||||
`
|
||||
sqlParams := []interface{}{
|
||||
utils.DefaultTimeValue,
|
||||
}
|
||||
if appID != 0 {
|
||||
sql += " AND app_id = ?"
|
||||
sqlParams = append(sqlParams, appID)
|
||||
}
|
||||
if printNo != "" {
|
||||
sql += " AND print_no = ?"
|
||||
sqlParams = append(sqlParams, printNo)
|
||||
}
|
||||
if status != 0 {
|
||||
sql += " AND status = ?"
|
||||
sqlParams = append(sqlParams, status)
|
||||
}
|
||||
if statusNeq != 0 {
|
||||
sql += " AND status <> ?"
|
||||
sqlParams = append(sqlParams, statusNeq)
|
||||
}
|
||||
err = GetRows(db, &printers, sql, sqlParams)
|
||||
|
||||
globals.SugarLogger.Debugf("======printers==== %s", utils.Format4Output(printers, false))
|
||||
return printers, err
|
||||
}
|
||||
|
||||
func GetPrintMsgs(db *DaoDB, printNo, msgID string, status, statusNeq int) (printMsgs []*model.PrintMsg, err error) {
|
||||
sql := `
|
||||
SELECT *
|
||||
FROM print_msg
|
||||
WHERE 1 = 1 AND deleted_at = ?
|
||||
`
|
||||
sqlParams := []interface{}{
|
||||
utils.DefaultTimeValue,
|
||||
}
|
||||
if status != model.PrintMsgAll {
|
||||
sql += " AND status = ?"
|
||||
sqlParams = append(sqlParams, status)
|
||||
}
|
||||
if statusNeq != model.PrintMsgAll {
|
||||
sql += " AND status <> ?"
|
||||
sqlParams = append(sqlParams, statusNeq)
|
||||
}
|
||||
if printNo != "" {
|
||||
sql += " AND print_no = ?"
|
||||
sqlParams = append(sqlParams, printNo)
|
||||
}
|
||||
if msgID != "" {
|
||||
sql += " AND msg_id = ?"
|
||||
sqlParams = append(sqlParams, msgID)
|
||||
}
|
||||
err = GetRows(db, &printMsgs, sql, sqlParams)
|
||||
return printMsgs, err
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package dao
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
"git.rosy.net.cn/jx-callback/business/model"
|
||||
)
|
||||
|
||||
func GetPrintSetting(printNo string) (*model.PrintSettingObj, error) {
|
||||
sql := ` SELECT * FROM print_setting WHERE print_no = ? and deleted_at = ? `
|
||||
sqlParams := []interface{}{
|
||||
printNo,
|
||||
utils.DefaultTimeValue,
|
||||
}
|
||||
|
||||
var printSetting *model.PrintSetting
|
||||
if err := GetRow(GetDB(), &printSetting, sql, sqlParams...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if printSetting == nil {
|
||||
return nil, errors.New("数据查询异常")
|
||||
}
|
||||
|
||||
return model.UnMarshalString2Json(printSetting)
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
package dao
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
"git.rosy.net.cn/jx-callback/business/model"
|
||||
)
|
||||
|
||||
// SelectUserDefaultTemp 查询用户默认模板
|
||||
func SelectUserDefaultTemp(userId string, tempType string) (*model.SystemTemp, bool, error) {
|
||||
var result *model.SystemTemp
|
||||
if err := GetRow(GetDB(), &result, `SELECT * FROM system_temp WHERE user_id = ? AND temp_type = ? AND is_use = ? AND deleted_at = ? ORDER BY created_at desc `, []interface{}{userId, tempType, 1, utils.DefaultTimeValue}...); err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
return result, true, nil
|
||||
}
|
||||
|
||||
// QuerySystemTemp 查询系统模板
|
||||
func QuerySystemTemp() (*model.SystemTemp, error) {
|
||||
var result []*model.SystemTemp
|
||||
if err := GetRows(GetDB(), &result, `SELECT * FROM system_temp WHERE user_id = ? AND temp_type = ? AND is_use = ? AND deleted_at = ? ORDER BY created_at desc `, []interface{}{"system_user", "user_store", 1, utils.DefaultTimeValue}...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(result) > 0 {
|
||||
return result[0], nil
|
||||
}
|
||||
return nil, errors.New("模板获取异常")
|
||||
}
|
||||
|
||||
// AddTemp 添加模板数据
|
||||
func AddTemp(param *model.SystemTemp) error {
|
||||
return CreateEntity(GetDB(), param)
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
package dao
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGetUsers(t *testing.T) {
|
||||
aa := "{\"title\":\"<center><b>京西菜市</b></center><br><center>手机买菜上京西</center><br><center>极速到家送惊喜</center><br>--------------------------------<br>\",\"divider\":\"--------------------------------<br>\",\"payOrderTime\":\"<left>下单时间:%s</left>\",\"trySendTime\":\"<left>预计送达时间:%s</left>\",\"consigneeName\":\"<left>客户名称:%s</left>\",\"consigneeMobile\":\"<left>客户电话:%s</left>\",\"consigneeAddress\":\"<left>客户地址:%s</left>\",\"buyerComment\":\"<left>客户备注:%s</left>\",\"qrcOrder\":\"<qrl>%s</qrl>\",\"goodsListDetail\":\"商品列表:<br>品名 数量 单价 小计<br>--------------------------------<br>\",\"skuName\":\"<b>%s</b><br>\",\"skuNumber\":\"<b>x%s</b>\",\"skuPrice\":\"<b>¥%s</b>\",\"skuAllPrice\":\"<b>¥%s</b>\",\"skuUpc\":\"upc码: %s<br>\",\"allSkuTypeCount\":\"<br><br><b>共 %s 种\",\"allSkuCount\":\" %s 件商品</b><br>\",\"storeName\":\"<center><b>商品质量问题请联系:</b></center><br><center><b>%s:\",\"storeTel\":\"%s</b></center><br><br>更多信息请关注官方微信:\",\"officialName\":\"<b>%s</b><br><br><br>--------------------------------<br>--------------------------------<br><br>\"}"
|
||||
|
||||
conte := make(map[string]string, 0)
|
||||
|
||||
if err := json.Unmarshal([]byte(aa), &conte); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
t.Log(utils.Format4Output(conte, false))
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
package dao
|
||||
|
||||
import (
|
||||
"git.rosy.net.cn/jx-callback/business/model"
|
||||
)
|
||||
|
||||
func CheckHeard(printNo string) (bool, error) {
|
||||
var data *model.PrintActivation
|
||||
err := GetRow(GetDB(), &data, `SELECT * FROM print_activation WHERE print_no = ?`, []interface{}{printNo}...)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if data == nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
package dao
|
||||
|
||||
import (
|
||||
"git.rosy.net.cn/jx-callback/business/model"
|
||||
"time"
|
||||
)
|
||||
|
||||
// QueryPrintBindStore 查询绑定门店
|
||||
func QueryPrintBindStore(printNo string) ([]*model.PrintBindStore, error) {
|
||||
var data []*model.PrintBindStore
|
||||
if err := GetRows(GetDB(), &data, `SELECT * FROM print_bind_store WHERE print_no = ?`, []interface{}{printNo}...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// BindStoreList 绑定门店信息
|
||||
func BindStoreList(req *model.AddPrinterParam, userId string) error {
|
||||
param := &model.PrintBindStore{
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
StoreID: req.StoreId,
|
||||
StoreName: req.Name,
|
||||
StoreVendor: 9, // 绑定平台,全平台
|
||||
PrintNo: req.PrintNo,
|
||||
StoreStatus: 1, // 门店开启
|
||||
BindStatus: 1, // 绑定状态
|
||||
}
|
||||
if userId != "" {
|
||||
param.UserId = userId
|
||||
} else {
|
||||
param.UserId = "system"
|
||||
}
|
||||
return CreateEntity(GetDB(), param)
|
||||
}
|
||||
|
||||
// DeleteStoreList 删除绑定门店
|
||||
func DeleteStoreList(printNo string, storeId string) error {
|
||||
sql := ` DELETE FROM print_bind_store WHERE print_no = ? AND store_id = ? `
|
||||
_, err := ExecuteSQL(GetDB(), sql, []interface{}{printNo, storeId}...)
|
||||
return err
|
||||
}
|
||||
@@ -1,539 +0,0 @@
|
||||
package dao
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
"git.rosy.net.cn/jx-callback/business/model"
|
||||
"git.rosy.net.cn/jx-print/globals"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var SystemTempObj map[string]*model.SystemTemp
|
||||
|
||||
func Init() {
|
||||
SystemTempObj = make(map[string]*model.SystemTemp, 0)
|
||||
sysTempList, err := QuerySystemTemp()
|
||||
if err != nil {
|
||||
globals.SugarLogger.Debug("query system temp err :", err)
|
||||
return
|
||||
}
|
||||
SystemTempObj[sysTempList.TempSize] = sysTempList
|
||||
|
||||
//now := time.Now()
|
||||
//param := &model.SystemTemp{
|
||||
// CreatedAt: &now,
|
||||
// UpdatedAt: &now,
|
||||
// LastOperator: "system",
|
||||
// DeletedAt: &utils.DefaultTimeValue,
|
||||
// TempName: "",
|
||||
// TempRank: model.SystemTempKey,
|
||||
// Temp: "",
|
||||
// UserId: "system_user",
|
||||
// TempType: model.TempTypeMerchant,
|
||||
// TempSize: model.SystemTempSizeBig,
|
||||
// PrintSn: "system",
|
||||
// IsUse: 1,
|
||||
//}
|
||||
|
||||
// 初始化大字体模板
|
||||
//if _, v := temp[model.SystemTempSizeBig]; !v {
|
||||
// param.TempName = "system" + model.SystemTempSizeBig
|
||||
// param.Temp = model.SystemTempValue
|
||||
// if err := AddTemp(param); err != nil {
|
||||
// globals.SugarLogger.Debug("init system temp err :", err)
|
||||
// }
|
||||
// SystemTempObj[model.SystemTempSizeBig] = param
|
||||
//}
|
||||
|
||||
// 初始化中字体模板
|
||||
//if _, v := temp[model.SystemTempSizeMedium]; !v {
|
||||
// param.TempName = "system" + model.SystemTempSizeMedium
|
||||
// medium := strings.Replace(model.SystemTempValue, "<b>", "<hb>", -1)
|
||||
// medium2 := strings.Replace(medium, "</b>", "</hb>", -1)
|
||||
// param.Temp = medium2
|
||||
// param.TempSize = model.SystemTempSizeMedium
|
||||
// param.IsUse = 2
|
||||
// if err := AddTemp(param); err != nil {
|
||||
// globals.SugarLogger.Debug("init system temp err :", err)
|
||||
// }
|
||||
// SystemTempObj[model.SystemTempSizeMedium] = param
|
||||
//}
|
||||
|
||||
// 初始化小字体模板
|
||||
//if _, v := temp[model.SystemTempSizeSmall]; !v {
|
||||
// param.TempName = "system" + model.SystemTempSizeSmall
|
||||
// medium := strings.Replace(model.SystemTempValue, "<b>", " ", -1)
|
||||
// medium2 := strings.Replace(medium, "</b>", " ", -1)
|
||||
// param.Temp = medium2
|
||||
// param.TempSize = model.SystemTempSizeSmall
|
||||
// param.IsUse = 2
|
||||
// if err := AddTemp(param); err != nil {
|
||||
// globals.SugarLogger.Debug("init system temp err :", err)
|
||||
// }
|
||||
// SystemTempObj[model.SystemTempSizeSmall] = param
|
||||
//}
|
||||
|
||||
}
|
||||
|
||||
// MakePrintMsgOnTemp 将打印数据渲染到模板当中
|
||||
func MakePrintMsgOnTemp(param map[string]string, userId string) (string, error) {
|
||||
// 查询用户默认模板,不存在则使用系统默认模板
|
||||
userTemp := &model.SystemTemp{}
|
||||
userTemp, isHave, err := SelectUserDefaultTemp(userId, model.TempTypeMerchantUser)
|
||||
if err != nil || !isHave {
|
||||
userTemp, err = QuerySystemTemp()
|
||||
}
|
||||
if userTemp == nil || !isHave || err != nil {
|
||||
if userTemp.TempType != "" {
|
||||
userTemp = SystemTempObj[userTemp.TempSize]
|
||||
} else {
|
||||
userTemp = SystemTempObj[model.SystemTempSizeBig]
|
||||
}
|
||||
}
|
||||
// 需要打印数据
|
||||
printMsg := ""
|
||||
printValue := make([]interface{}, 0, 0)
|
||||
userTempMap := make(map[string]string, 0)
|
||||
if err := json.Unmarshal([]byte(userTemp.Temp), &userTempMap); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
for _, v := range strings.Split(userTemp.TempRank, ",") {
|
||||
switch v {
|
||||
case "skuName", "skuNumber", "skuPrice", "skuAllPrice", "allSkuTypeCount", "allSkuCount", "skuUpc", "userPayMoney":
|
||||
continue
|
||||
case "goodsListDetail":
|
||||
printMsg += userTempMap[v]
|
||||
skuList := make([]*model.SkuListPrintOrder, 0, 0)
|
||||
if err := json.Unmarshal([]byte(param["skuList"]), &skuList); err != nil {
|
||||
return "", err
|
||||
}
|
||||
for i := 0; i < len(skuList); i++ {
|
||||
printMsg += userTempMap["skuName"]
|
||||
printMsg += userTempMap["skuNumber"]
|
||||
printMsg += userTempMap["skuPrice"]
|
||||
printMsg += userTempMap["skuAllPrice"]
|
||||
printValue = append(printValue, skuList[i].SkuName, skuList[i].SkuCount, skuList[i].SalePrice, skuList[i].TotalCountPrice)
|
||||
if skuList[i].Upc != "" {
|
||||
printMsg += userTempMap["skuUpc"]
|
||||
printValue = append(printValue, skuList[i].Upc)
|
||||
}
|
||||
|
||||
}
|
||||
printMsg += userTempMap["allSkuTypeCount"]
|
||||
printValue = append(printValue, param["allSkuTypeCount"])
|
||||
printMsg += userTempMap["allSkuCount"]
|
||||
printValue = append(printValue, param["allSkuCount"])
|
||||
printMsg += userTempMap["userPayMoney"]
|
||||
printValue = append(printValue, param["userPayMoney"])
|
||||
case "businessType":
|
||||
if param[v] == "2" { // 是预订单
|
||||
printMsg += userTempMap[v]
|
||||
printValue = append(printValue, param[v])
|
||||
}
|
||||
case "divider":
|
||||
printMsg += userTempMap[v]
|
||||
case "title":
|
||||
printMsg += userTempMap[v]
|
||||
case "qrcOrder": // 老版打印机展示不要
|
||||
//printMsg += userTempMap[v]
|
||||
//printValue = append(printValue, param[v])
|
||||
printMsg += `<b>%s #%s</b>`
|
||||
printValue = append(printValue, param["vendorName"], param["vendorOrderNo"])
|
||||
default:
|
||||
printMsg += userTempMap[v]
|
||||
printValue = append(printValue, param[v])
|
||||
}
|
||||
|
||||
}
|
||||
return strings.Replace(fmt.Sprintf(strings.Replace(printMsg, "\n", "", -1), printValue...), "\\n", "\r\n", -1), nil
|
||||
//}
|
||||
}
|
||||
|
||||
// MakePrintMsgOnTempVoice 制作平台语音
|
||||
func MakePrintMsgOnTempVoice(param map[string]string, setting *model.PrintSettingObj, userId string) (string, error) {
|
||||
// 打订单
|
||||
if param[model.OrderStatusPrint] != "" {
|
||||
// 订单提示设置
|
||||
printMsg, err := PrinterOrderVoice(param, setting, userId)
|
||||
if err != nil {
|
||||
globals.SugarLogger.Debug("err Unmarshal userTemp.printMsg", err)
|
||||
return "", err
|
||||
}
|
||||
return printMsg, nil
|
||||
}
|
||||
// 打运单
|
||||
if param[model.WayBillStatusPrint] != "" {
|
||||
switch param[model.WayBillStatusPrint] {
|
||||
case utils.Int2Str(model.WaybillStatusCourierAssigned): // 分配骑手
|
||||
return PrintWayBillOrderStatus(param, setting), nil
|
||||
case utils.Int2Str(model.WaybillStatusDeliverReminder): // 催单
|
||||
printVoiceMsg := ``
|
||||
printVoiceValue := make([]interface{}, 0, 0)
|
||||
//printVoiceMsg, printVoiceValue = SyntheticSpeech(printVoiceMsg, printVoiceValue, param)
|
||||
printVoiceMsg += `<sound>%d</sound>`
|
||||
printVoiceValue = append(printVoiceValue, model.ReminderOrderVoice)
|
||||
return strings.Replace(fmt.Sprintf(strings.Replace(printVoiceMsg, "\n", "", -1), printVoiceValue...), "\\n", "\r\n", -1), nil
|
||||
case utils.Int2Str(model.WaybillStatusDelivered): // 送达
|
||||
printVoiceMsg := ``
|
||||
printVoiceValue := make([]interface{}, 0, 0)
|
||||
//printVoiceMsg, printVoiceValue = SyntheticSpeech(printVoiceMsg, printVoiceValue, param)
|
||||
printVoiceMsg += `<sound>%d</sound>`
|
||||
printVoiceValue = append(printVoiceValue, model.FinialsOrderVoice)
|
||||
return strings.Replace(fmt.Sprintf(strings.Replace(printVoiceMsg, "\n", "", -1), printVoiceValue...), "\\n", "\r\n", -1), nil
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 门店状态
|
||||
if param[model.StoreStatusPrint] != "" {
|
||||
return PrintStoreStatus(param, setting), nil
|
||||
}
|
||||
|
||||
// 进店咨询
|
||||
if param[model.EnterTheStorePrint] != "" && setting.VoiceSetting.ConsultingPrint == model.SettingOpen {
|
||||
return `<sound>19</sound>`, nil
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// PrinterOrderVoice 打印机订单提示设置
|
||||
func PrinterOrderVoice(param map[string]string, setting *model.PrintSettingObj, userId string) (string, error) {
|
||||
var (
|
||||
printVoiceMsg string //语音信息
|
||||
printVoiceValue = make([]interface{}, 0, 0)
|
||||
textMsg string // 文本信息
|
||||
err error
|
||||
)
|
||||
|
||||
// 订单状态
|
||||
switch param[model.OrderStatusPrint] {
|
||||
// 新订单(待接单)
|
||||
case utils.Int2Str(model.OrderStatusNew): // utils.Int2Str(model.OrderStatusFinishedPickup), utils.Int2Str(model.OrderStatusAccepted)
|
||||
//if param[model.OrderStatusPrint] != utils.Int2Str(model.OrderStatusNew) && param[model.VendorIDPrint] == utils.Int64ToStr(model.VendorIDMTWM) {
|
||||
// return "", err
|
||||
//}
|
||||
//if param[model.OrderStatusPrint] == utils.Int2Str(model.OrderStatusFinishedPickup) && param[model.VendorIDPrint] == utils.Int64ToStr(model.VendorIDEBAI) {
|
||||
// return "", err
|
||||
//}
|
||||
// 称谓设置/平台语音设置
|
||||
if setting.CallNameSetting == 64 || setting.CallNameSetting == 65 || setting.CallNameSetting == 66 {
|
||||
// 老板
|
||||
printVoiceMsg += `<sound>%d</sound>`
|
||||
printVoiceValue = append(printVoiceValue, setting.CallNameSetting)
|
||||
}
|
||||
// 订单设置
|
||||
if setting.PrintSetting.WaitOrderPrint == model.SettingOpen { // 打印订单
|
||||
textMsg, err = MakePrintMsgOnTemp(param, userId)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
if setting.VoiceSetting.WaitOrderVoice == model.SettingOpen { // 订单通知
|
||||
printVoiceMsg += `<sound>%d</sound>` // 你来新订单了
|
||||
printVoiceValue = append(printVoiceValue, model.NewOrderVoice)
|
||||
}
|
||||
// 申请取消
|
||||
case utils.Int2Str(model.ApplyOrderCancel):
|
||||
// 称谓设置/平台语音设置
|
||||
if setting.CallNameSetting == 64 || setting.CallNameSetting == 65 || setting.CallNameSetting == 66 {
|
||||
// 老板
|
||||
printVoiceMsg += `<sound>%d</sound>`
|
||||
printVoiceValue = append(printVoiceValue, setting.CallNameSetting)
|
||||
}
|
||||
// 订单设置
|
||||
if setting.PrintSetting.ApplyUserCancelOrder == model.SettingOpen { // 申请取消打印
|
||||
rejection := ``
|
||||
rejectionValue := make([]interface{}, 0, 0)
|
||||
rejection += `<center><b>客户申请取消订单:</b></center><br>`
|
||||
rejection += `<center><b>订单号: %s</b></center><br>`
|
||||
rejection += `<center><b>订单来源: %s # %s</b></center><br>`
|
||||
rejection += `<center><b>取消时间: %s</b></center><br>`
|
||||
rejectionValue = append(rejectionValue, param[model.OrderNoPrint], param[model.VendorNamePrint], param[model.VendorOrderNoPrint], utils.Time2DateStr(time.Now()))
|
||||
textMsg = strings.Replace(fmt.Sprintf(strings.Replace(rejection, "\n", "", -1), rejectionValue...), "\\n", "\r\n", -1)
|
||||
}
|
||||
if setting.VoiceSetting.ApplyUserOrderCancelVoice == model.SettingOpen { // 申请取消语音
|
||||
printVoiceMsg += `<sound>%d</sound>`
|
||||
printVoiceValue = append(printVoiceValue, model.ApplyCancelVoice)
|
||||
//printVoiceMsg, printVoiceValue = SyntheticSpeech(printVoiceMsg, printVoiceValue, param)
|
||||
}
|
||||
// 申请退货
|
||||
case utils.Int2Str(model.ApplyOrderRefundGoods):
|
||||
// 称谓设置/平台语音设置
|
||||
if setting.CallNameSetting == 64 || setting.CallNameSetting == 65 || setting.CallNameSetting == 66 {
|
||||
// 老板
|
||||
printVoiceMsg += `<sound>%d</sound>`
|
||||
printVoiceValue = append(printVoiceValue, setting.CallNameSetting)
|
||||
}
|
||||
rejection := ``
|
||||
rejectionValue := make([]interface{}, 0, 0)
|
||||
rejection += `<center><b>客户申请退货:</b></center><br>`
|
||||
rejection += `<center><b>订单号: %s</b></center><br>`
|
||||
rejection += `<center><b>订单来源: %s # %s</b></center><br>`
|
||||
rejection += `<center><b>原因: %s</b></center><br>`
|
||||
rejectionValue = append(rejectionValue, param[model.OrderNoPrint], param[model.VendorNamePrint], param[model.VendorOrderNoPrint], param[model.RejectionReasonPrint])
|
||||
textMsg = strings.Replace(fmt.Sprintf(strings.Replace(rejection, "\n", "", -1), rejectionValue...), "\\n", "\r\n", -1)
|
||||
|
||||
if setting.VoiceSetting.ApplyRefundGoodsVoice == model.SettingOpen { // 申请退货语音
|
||||
printVoiceMsg += `<sound>%d</sound>`
|
||||
printVoiceValue = append(printVoiceValue, model.ApplyReturnGoodsVoice)
|
||||
// printVoiceMsg, printVoiceValue = SyntheticSpeech(printVoiceMsg, printVoiceValue, param)
|
||||
}
|
||||
// 申请退款
|
||||
case utils.Int2Str(model.ApplyOrderRefund):
|
||||
// 称谓设置/平台语音设置
|
||||
if setting.CallNameSetting == 64 || setting.CallNameSetting == 65 || setting.CallNameSetting == 66 {
|
||||
// 老板
|
||||
printVoiceMsg += `<sound>%d</sound>`
|
||||
printVoiceValue = append(printVoiceValue, setting.CallNameSetting)
|
||||
}
|
||||
// 订单设置
|
||||
if setting.PrintSetting.ApplyUserRefund == model.SettingOpen { // 取消退款订单
|
||||
textMsg, err = MakePrintMsgOnTemp(param, userId)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
if setting.VoiceSetting.ApplyRefundOrderVoice == model.SettingOpen {
|
||||
//printVoiceMsg, printVoiceValue = SyntheticSpeech(printVoiceMsg, printVoiceValue, param)
|
||||
printVoiceMsg += `<sound>%d</sound>`
|
||||
printVoiceValue = append(printVoiceValue, model.ApplyRefundVoice)
|
||||
}
|
||||
// 取消打印
|
||||
|
||||
case utils.Int2Str(model.OrderStatusRejection): // 拒收
|
||||
// 称谓设置/平台语音设置
|
||||
if setting.CallNameSetting == 64 || setting.CallNameSetting == 65 || setting.CallNameSetting == 66 {
|
||||
// 老板
|
||||
printVoiceMsg += `<sound>%d</sound>`
|
||||
printVoiceValue = append(printVoiceValue, setting.CallNameSetting)
|
||||
}
|
||||
//if setting.VoiceSetting.CustomerRejectionVoice == model.SettingOpen && setting.SystemVoice == model.SettingOpen {
|
||||
// // 暂无语音打印
|
||||
//}
|
||||
// 拒收暂无语音设置,使用文本提示
|
||||
if setting.PrintSetting.CustomerRejectionPrint == model.SettingOpen { // 客户拒收打印
|
||||
rejection := ``
|
||||
rejectionValue := make([]interface{}, 0, 0)
|
||||
rejection += `<center><b>客户拒收信息:</b></center><br>`
|
||||
rejection += `<center><b>订单号: %s</b></center><br>`
|
||||
rejection += `<center><b>订单来源: %s # %s</b></center><br>`
|
||||
rejection += `<center><b>拒收原因: %s</b></center><br>`
|
||||
rejectionValue = append(rejectionValue, param[model.OrderNoPrint], param[model.VendorNamePrint], param[model.VendorOrderNoPrint], param[model.RejectionReasonPrint])
|
||||
textMsg = strings.Replace(fmt.Sprintf(strings.Replace(rejection, "\n", "", -1), rejectionValue...), "\\n", "\r\n", -1)
|
||||
}
|
||||
|
||||
case utils.Int2Str(model.OrderStatusCustomerService): // 客服退款
|
||||
// 称谓设置/平台语音设置
|
||||
if setting.CallNameSetting == 64 || setting.CallNameSetting == 65 || setting.CallNameSetting == 66 {
|
||||
// 老板
|
||||
printVoiceMsg += `<sound>%d</sound>`
|
||||
printVoiceValue = append(printVoiceValue, setting.CallNameSetting)
|
||||
}
|
||||
if setting.VoiceSetting.CusterRefundVoice == model.SettingOpen {
|
||||
printVoiceMsg += `<sound>%d</sound>`
|
||||
printVoiceValue = append(printVoiceValue, model.BusinessResponsibilityVoice)
|
||||
}
|
||||
if setting.PrintSetting.CusterRefundPrint == model.SettingOpen {
|
||||
rejection := ``
|
||||
rejectionValue := make([]interface{}, 0, 0)
|
||||
rejection += `<center><b>客服退款详情:</b></center><br>`
|
||||
rejection += `<center><b>订单号: %s</b></center><br>`
|
||||
rejection += `<center><b>订单来源: %s # %s</b></center><br>`
|
||||
rejection += `<center><b>退款原因: %s</b></center><br>`
|
||||
rejection += `<center><b>退款时间: %s</b></center><br>`
|
||||
rejectionValue = append(rejectionValue, param[model.OrderNoPrint], param[model.VendorNamePrint], param[model.VendorOrderNoPrint], param[model.CustcareRefundReasonPrint], utils.Time2DateStr(time.Now()))
|
||||
textMsg = strings.Replace(fmt.Sprintf(strings.Replace(rejection, "\n", "", -1), rejectionValue...), "\\n", "\r\n", -1)
|
||||
}
|
||||
case utils.Int2Str(model.OrderStatusCanceled), utils.Int2Str(model.CancelOrderSuccess): // 取消订单成功
|
||||
// 称谓设置/平台语音设置
|
||||
if setting.CallNameSetting == 64 || setting.CallNameSetting == 65 || setting.CallNameSetting == 66 {
|
||||
// 老板
|
||||
printVoiceMsg += `<sound>%d</sound>`
|
||||
printVoiceValue = append(printVoiceValue, setting.CallNameSetting)
|
||||
if setting.VoiceSetting.RefundGoodsVoice == model.SettingOpen {
|
||||
printVoiceMsg += `<sound>%d</sound>`
|
||||
printVoiceValue = append(printVoiceValue, model.CancelOrderVoice)
|
||||
}
|
||||
}
|
||||
if setting.PrintSetting.CusterRefundPrint == model.SettingOpen {
|
||||
rejection := ``
|
||||
rejectionValue := make([]interface{}, 0, 0)
|
||||
rejection += `<center><b>订单取消成功:</b></center><br>`
|
||||
rejection += `<center><b>订单号: %s</b></center><br>`
|
||||
rejection += `<center><b>订单来源: %s # %s</b></center><br>`
|
||||
rejection += `<center><b>取消原因: %s</b></center><br>`
|
||||
rejection += `<center><b>取消成功时间: %s</b></center><br>`
|
||||
rejectionValue = append(rejectionValue, param[model.OrderNoPrint], param[model.VendorNamePrint], param[model.VendorOrderNoPrint], param[model.CustcareRefundReasonPrint], utils.Time2DateStr(time.Now()))
|
||||
textMsg = strings.Replace(fmt.Sprintf(strings.Replace(rejection, "\n", "", -1), rejectionValue...), "\\n", "\r\n", -1)
|
||||
}
|
||||
case utils.Int2Str(model.BusinessCancelOrder): // 商家取消打印
|
||||
// 称谓设置/平台语音设置
|
||||
if setting.CallNameSetting == 64 || setting.CallNameSetting == 65 || setting.CallNameSetting == 66 {
|
||||
// 老板
|
||||
printVoiceMsg += `<sound>%d</sound>`
|
||||
printVoiceValue = append(printVoiceValue, setting.CallNameSetting)
|
||||
}
|
||||
if setting.PrintSetting.BusinessOrderCancel == model.SettingOpen {
|
||||
rejection := ``
|
||||
rejectionValue := make([]interface{}, 0, 0)
|
||||
rejection += `<center><b>商家侧取消订单:</b></center><br>`
|
||||
rejection += `<center><b>订单号: %s</b></center><br>`
|
||||
rejection += `<center><b>订单来源: %s # %s</b></center><br>`
|
||||
rejection += `<center><b>取消原因: %s</b></center><br>`
|
||||
rejection += `<center><b>取消成功时间: %s</b></center><br>`
|
||||
rejectionValue = append(rejectionValue, param[model.OrderNoPrint], param[model.VendorNamePrint], param[model.VendorOrderNoPrint], param[model.CustcareRefundReasonPrint], utils.Time2DateStr(time.Now()))
|
||||
textMsg = strings.Replace(fmt.Sprintf(strings.Replace(rejection, "\n", "", -1), rejectionValue...), "\\n", "\r\n", -1)
|
||||
}
|
||||
case utils.Int2Str(model.OrderRefundMoneySuccess): // 订单退款成功打印
|
||||
// 称谓设置/平台语音设置
|
||||
if setting.CallNameSetting == 64 || setting.CallNameSetting == 65 || setting.CallNameSetting == 66 {
|
||||
// 老板
|
||||
printVoiceMsg += `<sound>%d</sound>`
|
||||
printVoiceValue = append(printVoiceValue, setting.CallNameSetting)
|
||||
}
|
||||
if setting.PrintSetting.OrderCancelSuccess == model.SettingOpen {
|
||||
rejection := ``
|
||||
rejectionValue := make([]interface{}, 0, 0)
|
||||
rejection += `<center><b>退款成功:</b></center><br>`
|
||||
rejection += `<center><b>订单号: %s</b></center><br>`
|
||||
rejection += `<center><b>订单来源: %s # %s</b></center><br>`
|
||||
rejectionValue = append(rejectionValue, param[model.OrderNoPrint], param[model.VendorNamePrint], param[model.VendorOrderNoPrint])
|
||||
textMsg = strings.Replace(fmt.Sprintf(strings.Replace(rejection, "\n", "", -1), rejectionValue...), "\\n", "\r\n", -1)
|
||||
}
|
||||
}
|
||||
|
||||
voice := strings.Replace(fmt.Sprintf(strings.Replace(printVoiceMsg, "\n", "", -1), printVoiceValue...), "\\n", "\r\n", -1)
|
||||
return voice + textMsg, nil
|
||||
}
|
||||
|
||||
// PrintWayBillOrderStatus 打印运单类通知消息
|
||||
func PrintWayBillOrderStatus(param map[string]string, setting *model.PrintSettingObj) string {
|
||||
var (
|
||||
printVoiceMsg string //语音信息
|
||||
printVoiceValue = make([]interface{}, 0, 0)
|
||||
textMsg string // 文本信息
|
||||
textMsgValue = make([]interface{}, 0, 0) // 文本信息
|
||||
)
|
||||
|
||||
switch param[model.WayBillStatusPrint] {
|
||||
case utils.Int2Str(model.WaybillStatusAccepted), utils.Int2Str(model.WaybillStatusCourierAssigned): // 分配骑手
|
||||
if setting.PrintSetting.RiderTakeOrder == model.SettingOpen { // 打印订单
|
||||
textMsg += `<center><b>接单骑手信息:</b></center><br>`
|
||||
textMsg += `<center><b>骑手姓名: %s</b></center><br>`
|
||||
textMsg += `<center><b>骑手电话: %s</b></center><br>`
|
||||
textMsg += `<center><b>接单时间: %s</b></center><br>`
|
||||
textMsg += `<center><b>订单来源: %s</b></center><br>`
|
||||
textMsg += `<center><b>单号: #%s</b></center><br>`
|
||||
textMsgValue = append(textMsgValue, param[model.RiderNamePrint], param[model.RiderPhonePrint], utils.Time2DateStr(time.Now()), param[model.VendorNamePrint], param[model.VendorOrderNoPrint])
|
||||
}
|
||||
}
|
||||
if setting.VoiceSetting.RiderTakeOrderVoice == model.SettingOpen { // 骑手接单语音通知
|
||||
printVoiceMsg += `<sound>%d</sound><sound>%d</sound>` // 骑手已经接单了
|
||||
printVoiceValue = append(printVoiceValue, model.RiderGetOrderVoice)
|
||||
}
|
||||
|
||||
msg := strings.Replace(fmt.Sprintf(strings.Replace(textMsg, "\n", "", -1), textMsgValue...), "\\n", "\r\n", -1)
|
||||
voice := strings.Replace(fmt.Sprintf(strings.Replace(printVoiceMsg, "\n", "", -1), printVoiceValue...), "\\n", "\r\n", -1)
|
||||
return voice + msg
|
||||
}
|
||||
|
||||
// PrintStoreStatus 打印门店状态
|
||||
func PrintStoreStatus(param map[string]string, setting *model.PrintSettingObj) string {
|
||||
var (
|
||||
printVoiceMsg string //语音信息
|
||||
printVoiceValue = make([]interface{}, 0, 0)
|
||||
textMsg string // 文本信息
|
||||
textMsgValue = make([]interface{}, 0, 0)
|
||||
)
|
||||
|
||||
switch utils.Str2Int(param[model.StoreStatusPrint]) {
|
||||
case -9: // 丢失授权
|
||||
var voice string
|
||||
if setting.VoiceSetting.LoseAuthorization == model.SettingOpen {
|
||||
printVoiceMsg += `<sound>%d</sound>`
|
||||
printVoiceValue = append(printVoiceValue, model.LoseTokenVoice)
|
||||
voice = strings.Replace(fmt.Sprintf(strings.Replace(printVoiceMsg, "\n", "", -1), printVoiceValue...), "\\n", "\r\n", -1)
|
||||
}
|
||||
|
||||
textMsg += `<left><b>门店丢失授权通知</b></left><br>`
|
||||
textMsg += `<left><b>门店:%s</b></left><br>`
|
||||
textMsg += `<left><b>平台:%s</b></left><br>`
|
||||
textMsg += `<left><b>下线时间:%s</b></left><br>`
|
||||
textMsg += `<left><b>授权丢失,将无法继续打压订单!!!!</b></left><br>`
|
||||
textMsgValue = append(textMsgValue, param[model.StoreNamePrint], param[model.VendorNamePrint], utils.Time2DateStr(time.Now()))
|
||||
msg := strings.Replace(fmt.Sprintf(strings.Replace(textMsg, "\n", "", -1), textMsgValue...), "\\n", "\r\n", -1)
|
||||
return voice + msg
|
||||
default:
|
||||
// 离线打印文本开启
|
||||
textMsg += `<center><b>门店下线通知</b></center><br>`
|
||||
textMsg += `<center><b>门店:%s</b></center><br>`
|
||||
textMsg += `<center><b>平台:%s</b></center><br>`
|
||||
textMsg += `<center><b>下线时间:%s</b></center><br>`
|
||||
textMsgValue = append(textMsgValue, param[model.StoreNamePrint], param[model.VendorNamePrint], utils.Time2DateStr(time.Now()))
|
||||
// 离线打印语音开启
|
||||
printVoiceMsg += `<sound>%d</sound>`
|
||||
printVoiceValue = append(printVoiceValue, model.StoreOfflineVoice)
|
||||
voice := strings.Replace(fmt.Sprintf(strings.Replace(printVoiceMsg, "\n", "", -1), printVoiceValue...), "\\n", "\r\n", -1)
|
||||
msg := strings.Replace(fmt.Sprintf(strings.Replace(textMsg, "\n", "", -1), textMsgValue...), "\\n", "\r\n", -1)
|
||||
|
||||
return voice + msg
|
||||
}
|
||||
}
|
||||
|
||||
// SyntheticSpeech 合成语音 (美团xxx号订单)
|
||||
func SyntheticSpeech(printVoiceMsg string, printVoiceValue []interface{}, param map[string]string) (string, []interface{}) {
|
||||
printVoiceMsg += `<sound>%d</sound>` // 美团
|
||||
switch param[model.VendorIDPrint] {
|
||||
case utils.Int2Str(model.VendorIDJD): // 京东
|
||||
printVoiceValue = append(printVoiceValue, model.JdVoice)
|
||||
case utils.Int2Str(model.VendorIDMTWM): // 美团
|
||||
printVoiceValue = append(printVoiceValue, model.MtVoice)
|
||||
case utils.Int2Str(model.VendorIDELM): // 饿了么
|
||||
printVoiceValue = append(printVoiceValue, model.ElmVoice)
|
||||
case utils.Int2Str(model.VendorIDEBAI): // 饿百
|
||||
printVoiceValue = append(printVoiceValue, model.ElmVoice)
|
||||
case utils.Int2Str(model.VendorIDJDShop): // 京东商城
|
||||
printVoiceValue = append(printVoiceValue, model.JdToHose)
|
||||
case utils.Int2Str(model.VendorIDTT): // 抖音
|
||||
// 暂无
|
||||
}
|
||||
if param[model.VendorOrderNoPrint] != "" {
|
||||
switch len(param[model.VendorOrderNoPrint]) {
|
||||
case 1:
|
||||
printVoiceMsg += `<sound>%d</sound>`
|
||||
printVoiceValue = append(printVoiceValue, model.NumberVoiceMap[param[model.VendorOrderNoPrint]])
|
||||
case 2:
|
||||
printVoiceMsg += `<sound>%d</sound>`
|
||||
printVoiceValue = append(printVoiceValue, model.NumberVoiceMap[param[model.VendorOrderNoPrint][:1]+"0"])
|
||||
if param[model.VendorOrderNoPrint][1:] != "0" {
|
||||
printVoiceMsg += `<sound>%d</sound>`
|
||||
printVoiceValue = append(printVoiceValue, model.NumberVoiceMap[param[model.VendorOrderNoPrint][1:]])
|
||||
}
|
||||
case 3:
|
||||
printVoiceMsg += `<sound>%d</sound>`
|
||||
printVoiceValue = append(printVoiceValue, model.NumberVoiceMap[param[model.VendorOrderNoPrint][0:1]+"00"])
|
||||
if param[model.VendorOrderNoPrint][1:2] == "0" && param[model.VendorOrderNoPrint][2:] == "0" {
|
||||
|
||||
} else if param[model.VendorOrderNoPrint][1:2] == "0" && param[model.VendorOrderNoPrint][2:] != "0" {
|
||||
printVoiceMsg += `<sound>%d</sound>`
|
||||
printVoiceValue = append(printVoiceValue, model.NumberVoiceMap[param[model.VendorOrderNoPrint][1:2]])
|
||||
printVoiceMsg += `<sound>%d</sound>`
|
||||
printVoiceValue = append(printVoiceValue, model.NumberVoiceMap[param[model.VendorOrderNoPrint][2:]])
|
||||
} else if param[model.VendorOrderNoPrint][1:2] != "0" && param[model.VendorOrderNoPrint][2:] == "0" {
|
||||
printVoiceMsg += `<sound>%d</sound>`
|
||||
printVoiceValue = append(printVoiceValue, model.NumberVoiceMap[param[model.VendorOrderNoPrint][1:2]+"0"])
|
||||
} else if param[model.VendorOrderNoPrint][1:2] != "0" && param[model.VendorOrderNoPrint][2:] != "0" {
|
||||
printVoiceMsg += `<sound>%d</sound>`
|
||||
printVoiceValue = append(printVoiceValue, model.NumberVoiceMap[param[model.VendorOrderNoPrint][1:2]+"0"])
|
||||
printVoiceMsg += `<sound>%d</sound>`
|
||||
printVoiceValue = append(printVoiceValue, model.NumberVoiceMap[param[model.VendorOrderNoPrint][2:]])
|
||||
}
|
||||
}
|
||||
}
|
||||
printVoiceMsg += `<sound>%d</sound>`
|
||||
printVoiceValue = append(printVoiceValue, model.OrderNoVoice)
|
||||
return printVoiceMsg, printVoiceValue
|
||||
}
|
||||
74
business/jxcallback/orderman/fake_jd_order.go
Normal file
74
business/jxcallback/orderman/fake_jd_order.go
Normal file
@@ -0,0 +1,74 @@
|
||||
package orderman
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/netprinter"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/weixinmsg"
|
||||
"git.rosy.net.cn/jx-callback/business/model"
|
||||
"git.rosy.net.cn/jx-callback/business/model/dao"
|
||||
"git.rosy.net.cn/jx-callback/business/partner/purchase/jd"
|
||||
"git.rosy.net.cn/jx-callback/globals"
|
||||
"git.rosy.net.cn/jx-callback/globals/api"
|
||||
)
|
||||
|
||||
func (c *OrderManager) OnNewFakeJdOrder(vendorOrderID string) (err error) {
|
||||
utils.CallFuncAsync(func() {
|
||||
orderInfo, err := api.FakeJdAPI.FakeQuerySingleOrderRaw(vendorOrderID)
|
||||
if err == nil {
|
||||
order := jd.Map2Order(orderInfo)
|
||||
jxutils.RefreshOrderSkuRelated(order)
|
||||
err = c.notifyNewFakeJdOrder(order)
|
||||
}
|
||||
if err != nil {
|
||||
globals.SugarLogger.Warnf("OnNewFakeJdOrder failed with err:%v", err)
|
||||
}
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *OrderManager) notifyNewFakeJdOrder(order *model.GoodsOrder) (err error) {
|
||||
db := dao.GetDB()
|
||||
storeDetail, err := dao.GetStoreDetailByVendorStoreID(db, order.VendorStoreID, model.VendorIDJD, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
realStoreID := storeDetail.ID
|
||||
if storeDetail.LinkStoreID != 0 {
|
||||
realStoreID = storeDetail.LinkStoreID
|
||||
}
|
||||
notifyWxNewFakeJdOrder(order, realStoreID)
|
||||
netprinter.PrintOrderByOrder4Store(jxcontext.AdminCtx, order, realStoreID)
|
||||
return err
|
||||
}
|
||||
|
||||
func notifyWxNewFakeJdOrder(order *model.GoodsOrder, storeID int) (err error) {
|
||||
globals.SugarLogger.Debugf("notifyWxNewFakeJdOrder orderID:%s", order.VendorOrderID)
|
||||
|
||||
sb := new(strings.Builder)
|
||||
sb.WriteString("老板,你有新订单了\n")
|
||||
sb.WriteString(fmt.Sprintf("订单号:%s\n", order.VendorOrderID))
|
||||
sb.WriteString("送达时间:")
|
||||
if order.BusinessType == model.BusinessTypeDingshida {
|
||||
sb.WriteString(utils.Time2Str(order.ExpectedDeliveredTime))
|
||||
} else {
|
||||
sb.WriteString("立即达")
|
||||
}
|
||||
sb.WriteString("\n")
|
||||
sb.WriteString(fmt.Sprintf("买家:%s\n", order.ConsigneeName))
|
||||
sb.WriteString(fmt.Sprintf("电话:%s\n", order.ConsigneeMobile))
|
||||
sb.WriteString(fmt.Sprintf("收货地址:%s\n", order.ConsigneeAddress))
|
||||
sb.WriteString("商品详情:\n")
|
||||
for _, sku := range order.Skus {
|
||||
sb.WriteString(fmt.Sprintf("\t%s*%d\n", sku.SkuName, sku.Count))
|
||||
}
|
||||
title := fmt.Sprintf("你有到家菜市新订单%d", order.OrderSeq)
|
||||
content := sb.String()
|
||||
// globals.SugarLogger.Debugf("notifyWxNewFakeJdOrder, orderID:%s, content:%s", order.VendorOrderID, content)
|
||||
_, err = weixinmsg.SendStoreMessage(jxcontext.AdminCtx, title, content, []int{storeID}, nil, "", model.MessageTypeStore, true, true)
|
||||
return err
|
||||
}
|
||||
14
business/jxcallback/orderman/fake_jd_order_test.go
Normal file
14
business/jxcallback/orderman/fake_jd_order_test.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package orderman
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestOnNewFakeJdOrder(t *testing.T) {
|
||||
err := FixedOrderManager.OnNewFakeJdOrder("2002984074001021")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
time.Sleep(3 * time.Second)
|
||||
}
|
||||
262
business/jxcallback/orderman/financial.go
Normal file
262
business/jxcallback/orderman/financial.go
Normal file
@@ -0,0 +1,262 @@
|
||||
package orderman
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
"git.rosy.net.cn/jx-callback/business/model"
|
||||
"git.rosy.net.cn/jx-callback/business/model/dao"
|
||||
"git.rosy.net.cn/jx-callback/business/partner"
|
||||
"git.rosy.net.cn/jx-callback/globals"
|
||||
)
|
||||
|
||||
const (
|
||||
TotalRate = 200 // 总费率(千分之200)= 第三方平台费+京西品牌费+京西服务费,现阶段都是合计200‰
|
||||
MaxFreightMoneyFromJxByShop = 7 // 商家自送实际转美团/达达后由商家承担的运费金额上限
|
||||
)
|
||||
|
||||
// 处理正向订单结账信息
|
||||
func (c *OrderManager) SaveOrderFinancialInfo(order *model.OrderFinancial, operation string) (err error) {
|
||||
db := dao.GetDB()
|
||||
txDB , _ := dao.Begin(db)
|
||||
defer func() {
|
||||
if r := recover(); r != nil || err != nil {
|
||||
dao.Rollback(db, txDB)
|
||||
if r != nil {
|
||||
panic(r)
|
||||
}
|
||||
}
|
||||
}()
|
||||
if operation == partner.UpdatedPeration {
|
||||
// db := orm.NewOrm()
|
||||
err = utils.CallFuncLogError(func() error {
|
||||
_, err = dao.ExecuteSQL(db, "DELETE FROM order_financial WHERE vendor_order_id = ? AND vendor_id = ?", order.VendorOrderID, order.VendorID)
|
||||
return err
|
||||
}, "SaveOrderFinancialInfo delete order_financial, orderID:%s", order.VendorOrderID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = utils.CallFuncLogError(func() error {
|
||||
_, err = dao.ExecuteSQL(db, "DELETE FROM order_sku_financial WHERE vendor_order_id = ? AND vendor_id = ? AND is_afs_order = 0", order.VendorOrderID, order.VendorID)
|
||||
return err
|
||||
}, "SaveOrderFinancialInfo delete order_sku_financial, orderID:%s", order.VendorOrderID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = utils.CallFuncLogError(func() error {
|
||||
_, err = dao.ExecuteSQL(db, "DELETE FROM order_discount_financial WHERE vendor_order_id = ? AND vendor_id = ?", order.VendorOrderID, order.VendorID)
|
||||
return err
|
||||
}, "SaveOrderFinancialInfo delete order_discount_financial, orderID:%s", order.VendorOrderID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for _, activity := range order.Discounts {
|
||||
if err = dao.CreateEntity(db, activity); err != nil {
|
||||
if !dao.IsDuplicateError(err) {
|
||||
globals.SugarLogger.Warnf("On SaveOrderDiscountFinancialInfo order.VendorOrderID:%s err: order is err", order.VendorOrderID)
|
||||
return err
|
||||
}
|
||||
dao.Rollback(db, txDB)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
order.ShopMoneyByCal = order.SalePriceMoney - order.TotalDiscountMoney - order.PointsDeductionMoney + order.PmFreightDiscountMoney - order.DistanceFreightMoney - order.FreightTipsMoney - order.DonationMoney + order.SelfDeliveryDiscountMoney + order.PmSubsidyMoney + order.SkuBoxMoney + order.BoxMoney - order.PmMoney
|
||||
order.JxPmMoney = utils.Float64TwoInt64(float64(order.ShopMoney+order.PmMoney)*TotalRate/1000) - order.PmMoney // 京西平台费 = 总金额*20%-第三方平台费
|
||||
if order.JxPmMoney < 0 { // 如果算出京西平台费为负数,则置0
|
||||
order.JxPmMoney = 0
|
||||
}
|
||||
|
||||
order.JxShopMoney = order.ShopMoney - order.JxPmMoney + order.JxSubsidyMoney - order.JxFreightMoneyByShop
|
||||
// 订单结算金额拆分计算,拆分到单条sku
|
||||
// 先获取订单主体优惠金额
|
||||
platOrderGoodsDiscountMoney := order.PmSubsidyMoney - order.PmSkuSubsidyMoney + order.SelfDeliveryDiscountMoney
|
||||
// 结算平台扣除项汇总---平台佣金,商家设置运费减免,远距离费,小费,公益捐款、、、、
|
||||
deductionsByPm := order.FreightDiscountMoney + order.DistanceFreightMoney + order.FreightTipsMoney + order.DonationMoney + order.PmMoney
|
||||
// 结算京西扣除项汇总---京西佣金,京西配送运费
|
||||
deductionsByJx := order.JxPmMoney + order.JxFreightMoneyByShop
|
||||
for _, sku := range order.Skus[1:] {
|
||||
// 用户支付菜品金额,用户为这一条sku支付的菜品金额
|
||||
sku.UserMoney = utils.Float64TwoInt64(float64(order.SalePriceMoney-order.DiscountMoney-order.PointsDeductionMoney+order.BoxMoney+order.SkuBoxMoney) * float64(sku.SalePrice*int64(sku.Count)+sku.SkuBoxMoney) / float64(order.SalePriceMoney+order.SkuBoxMoney)) // 林峰确认,比如49-2满减,算餐盒费满49元,不算餐盒费不足49元,是可以参加满减活动的,那么餐盒费也应该和商品金额一起进行折算
|
||||
order.Skus[0].UserMoney += sku.UserMoney
|
||||
// 计算平台订单主体补贴拆分到单条sku的金额 + sku.PmSubsidyMoneyForSku
|
||||
sku.PmSubsidyMoney = sku.PmSkuSubsidyMoney + utils.Float64TwoInt64(float64(platOrderGoodsDiscountMoney*sku.SalePrice*int64(sku.Count)+sku.SkuBoxMoney)/float64(order.SalePriceMoney+order.SkuBoxMoney))
|
||||
order.Skus[0].PmSubsidyMoney += sku.PmSubsidyMoney
|
||||
// 计算京西满减补贴拆分到单条sku的金额 + sku.JxSubsidyMoneyForSku
|
||||
sku.JxSubsidyMoney = sku.JxSkuSubsidyMoney + utils.Float64TwoInt64(float64(order.JxSubsidyMoney-order.PmSkuSubsidyMoney)*float64(sku.SalePrice*int64(sku.Count)+sku.SkuBoxMoney)/float64(order.SalePriceMoney+order.SkuBoxMoney))
|
||||
order.Skus[0].JxSubsidyMoney += sku.JxSubsidyMoney
|
||||
// 计算单条sku需要承担的平台扣费金额
|
||||
sku.PmDeductionsMoney = utils.Float64TwoInt64(float64(deductionsByPm) * (float64(sku.UserMoney + sku.PmSubsidyMoney)) / (float64(order.SalePriceMoney + order.BoxMoney + order.SkuBoxMoney - order.DiscountMoney - order.PointsDeductionMoney + order.PmSubsidyMoney)))
|
||||
order.Skus[0].PmDeductionsMoney += sku.PmDeductionsMoney
|
||||
// 计算单条sku平台应该结算给京西的金额
|
||||
sku.ShopMoneyByCal = sku.UserMoney + sku.PmSubsidyMoney - sku.PmDeductionsMoney
|
||||
order.Skus[0].ShopMoneyByCal += sku.ShopMoneyByCal
|
||||
// j计算单条sku需要承担的京西扣费金额
|
||||
sku.JxDeductionsMoney = utils.Float64TwoInt64(float64(deductionsByJx*sku.ShopMoneyByCal) / float64(order.ShopMoney))
|
||||
order.Skus[0].JxDeductionsMoney += sku.JxDeductionsMoney
|
||||
// 计算单条sku京西应该结算给商家的金额
|
||||
sku.JxShopMoney = sku.ShopMoneyByCal + sku.JxSubsidyMoney - sku.JxDeductionsMoney
|
||||
order.Skus[0].JxShopMoney += sku.JxShopMoney
|
||||
if sku.SkuID >= math.MaxInt32 {
|
||||
sku.SkuID = sku.JxSkuID
|
||||
}
|
||||
if err = dao.CreateEntity(db, sku); err != nil {
|
||||
if !dao.IsDuplicateError(err) {
|
||||
globals.SugarLogger.Warnf("On SaveOrderSkuFinancialInfo order.VendorOrderID:%s err:%v", order.VendorOrderID, err)
|
||||
return err
|
||||
}
|
||||
dao.Rollback(db, txDB)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if len(order.Skus) > 0 {
|
||||
sku := order.Skus[0]
|
||||
if sku.SkuID > math.MaxInt32 {
|
||||
sku.SkuID = sku.JxSkuID
|
||||
}
|
||||
sku.UserMoney = order.SalePriceMoney - order.DiscountMoney - sku.UserMoney
|
||||
sku.PmSubsidyMoney = platOrderGoodsDiscountMoney + order.SelfDeliveryDiscountMoney - sku.PmSubsidyMoney
|
||||
sku.JxSubsidyMoney = order.JxSubsidyMoney - sku.JxSubsidyMoney
|
||||
sku.PmDeductionsMoney = deductionsByPm - sku.PmDeductionsMoney
|
||||
sku.ShopMoneyByCal = order.ShopMoney - sku.ShopMoneyByCal
|
||||
sku.JxDeductionsMoney = deductionsByJx - sku.JxDeductionsMoney
|
||||
sku.JxShopMoney = order.JxShopMoney - sku.JxShopMoney
|
||||
if err = dao.CreateEntity(db, sku); err != nil {
|
||||
if !dao.IsDuplicateError(err) {
|
||||
globals.SugarLogger.Warnf("On SaveOrderSkuFinancialInfo order.VendorOrderID:%s err:%v", order.VendorOrderID, err)
|
||||
return err
|
||||
}
|
||||
dao.Rollback(db, txDB)
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
globals.SugarLogger.Warnf("On SaveOrderSkuFinancialInfo order.VendorOrderID:%s err: order have no sku", order.VendorOrderID)
|
||||
}
|
||||
// 加上京西对单条sku的补贴,再存数据库
|
||||
order.JxSubsidyMoney += order.JxSkuSubsidyMoney
|
||||
if err = dao.CreateEntity(db, order); err != nil {
|
||||
if !dao.IsDuplicateError(err) {
|
||||
globals.SugarLogger.Warnf("On SaveOrderFinancialInfo order.VendorOrderID:%s err: order is err", order.VendorOrderID)
|
||||
return err
|
||||
}
|
||||
dao.Rollback(db, txDB)
|
||||
return nil
|
||||
}
|
||||
dao.Commit(db, txDB)
|
||||
return err
|
||||
}
|
||||
|
||||
// 处理售后订单结账信息
|
||||
func (c *OrderManager) SaveAfsOrderFinancialInfo(afsOrder *model.AfsOrder) (err error) {
|
||||
globals.SugarLogger.Debug(afsOrder.AfsOrderID)
|
||||
db := dao.GetDB()
|
||||
txDB , _ := dao.Begin(db)
|
||||
defer func() {
|
||||
if r := recover(); r != nil || err != nil {
|
||||
dao.Rollback(db, txDB)
|
||||
if r != nil {
|
||||
panic(r)
|
||||
}
|
||||
}
|
||||
}()
|
||||
// 平台结算扣除汇总--平台补贴,售后产生运费,平台收包装费,同城运费、、、
|
||||
deductionsByPm := afsOrder.PmSubsidyMoney + afsOrder.AfsFreightMoney + afsOrder.BoxMoney + afsOrder.TongchengFreightMoney
|
||||
afsOrder.RefundMoneyByCal = afsOrder.SkuUserMoney + afsOrder.FreightUserMoney + deductionsByPm - afsOrder.PmRefundMoney
|
||||
// order.TotalMoney += order.SkuJxMoney // 退款单京西补贴部分先不作计算
|
||||
if err = dao.CreateEntity(db, afsOrder); err != nil {
|
||||
if !dao.IsDuplicateError(err) {
|
||||
globals.SugarLogger.Warnf("On SaveAfsOrderFinancialInfo afsOrder.AfsOrderID:%s err: SaveAfsOrder is err", afsOrder.AfsOrderID)
|
||||
return err
|
||||
}
|
||||
dao.Rollback(db, txDB)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 京西结算扣除汇总,先不作计算,计算单条sku最终扣款金额(+该条sku承担的平台结算扣除金额)
|
||||
for _, orderSku := range afsOrder.Skus[1:] {
|
||||
orderSku.RefundMoneyByCal = orderSku.PmSkuSubsidyMoney +
|
||||
utils.Float64TwoInt64(float64(afsOrder.RefundMoneyByCal-afsOrder.PmSkuSubsidyMoney)*float64(orderSku.UserMoney+orderSku.PmSubsidyMoney-orderSku.PmSkuSubsidyMoney)/float64(afsOrder.SkuUserMoney+afsOrder.PmSubsidyMoney-afsOrder.PmSkuSubsidyMoney))
|
||||
afsOrder.Skus[0].RefundMoneyByCal += orderSku.RefundMoneyByCal
|
||||
if err = dao.CreateEntity(db, orderSku); err != nil {
|
||||
if !dao.IsDuplicateError(err) {
|
||||
globals.SugarLogger.Warnf("On SaveAfsOrderFinancialInfo afsOrder.AfsOrderID:%s err: SaveAfsOrderSku is err", afsOrder.AfsOrderID)
|
||||
return err
|
||||
}
|
||||
dao.Rollback(db, txDB)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if len(afsOrder.Skus) > 0 {
|
||||
orderSku := afsOrder.Skus[0]
|
||||
orderSku.RefundMoneyByCal = afsOrder.RefundMoneyByCal - orderSku.RefundMoneyByCal
|
||||
if err = dao.CreateEntity(db, orderSku); err != nil {
|
||||
if !dao.IsDuplicateError(err) {
|
||||
globals.SugarLogger.Warnf("On SaveAfsOrderFinancialInfo afsOrder.AfsOrderID:%s err: SaveAfsOrderSku is err", afsOrder.AfsOrderID)
|
||||
return err
|
||||
}
|
||||
dao.Rollback(db, txDB)
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
globals.SugarLogger.Warnf("On SaveAfsOrderFinancialInfo afsOrder.AfsOrderID:%s err: afsOrder have no sku", afsOrder.AfsOrderID)
|
||||
}
|
||||
dao.Commit(db, txDB)
|
||||
return err
|
||||
}
|
||||
|
||||
// 转自送实际使用美团/达达配送,调用此方法
|
||||
func (c *OrderManager) UpdataOrderFinancialInfo(orderFinancial *model.OrderFinancial, wayBill *model.Waybill) (err error) {
|
||||
// 通过本地数据库去取是否转美团/达达,并计算运费,写在上级调用函数里面传递过来
|
||||
// wayBill, err2 := partner.CurOrderManager.LoadWaybill(orderFinancial.VendorOrderID, orderFinancial.VendorID)
|
||||
// if err = err2; err == nil {
|
||||
// orderFinancial.JxFreightMoney = wayBill.DesiredFee
|
||||
// }
|
||||
db := dao.GetDB()
|
||||
txDB , _ := dao.Begin(db)
|
||||
defer func() {
|
||||
if r := recover(); r != nil || err != nil {
|
||||
dao.Rollback(db, txDB)
|
||||
if r != nil {
|
||||
panic(r)
|
||||
}
|
||||
}
|
||||
}()
|
||||
orderFinancial.JxFreightMoney = wayBill.DesiredFee
|
||||
if (orderFinancial.JxFreightMoney - orderFinancial.SelfDeliveryDiscountMoney) > MaxFreightMoneyFromJxByShop {
|
||||
orderFinancial.JxFreightMoneyByShop = orderFinancial.SelfDeliveryDiscountMoney + MaxFreightMoneyFromJxByShop
|
||||
} else if (orderFinancial.JxFreightMoney - orderFinancial.SelfDeliveryDiscountMoney) < 0 {
|
||||
orderFinancial.JxFreightMoneyByShop = orderFinancial.SelfDeliveryDiscountMoney
|
||||
} else {
|
||||
orderFinancial.JxFreightMoneyByShop = orderFinancial.JxFreightMoney
|
||||
}
|
||||
orderFinancial.JxShopMoney -= orderFinancial.JxFreightMoneyByShop
|
||||
// orderFinancial.JxFreightMoneyByShop 的改变直接影响到单条sku的京西结算金额的改变
|
||||
jxDecMoney := orderFinancial.JxFreightMoneyByShop
|
||||
for _, sku := range orderFinancial.Skus[1:] {
|
||||
// 重新计算单条sku京西应该结算的金额
|
||||
skuJxDecMoney := utils.Float64TwoInt64(float64(jxDecMoney*sku.SalePrice*int64(sku.Count)+sku.SkuBoxMoney) / float64(orderFinancial.SalePriceMoney+orderFinancial.SkuBoxMoney))
|
||||
sku.JxDeductionsMoney += skuJxDecMoney
|
||||
sku.JxShopMoney -= skuJxDecMoney
|
||||
jxDecMoney -= skuJxDecMoney
|
||||
if _, err = dao.UpdateEntity(db, sku); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if len(orderFinancial.Skus) > 0 {
|
||||
sku := orderFinancial.Skus[0]
|
||||
sku.JxDeductionsMoney += jxDecMoney
|
||||
sku.JxShopMoney -= jxDecMoney
|
||||
if _, err = dao.UpdateEntity(db, sku); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err = dao.CreateEntity(db, orderFinancial); err != nil {
|
||||
globals.SugarLogger.Warnf("On SaveOrderFinancialInfo err: order is err")
|
||||
return err
|
||||
}
|
||||
dao.Commit(db, txDB)
|
||||
// orderFinancial 和 OrderSkuFinancial 数据计算完毕,准备进行更新
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
2311
business/jxcallback/orderman/order.go
Normal file
2311
business/jxcallback/orderman/order.go
Normal file
File diff suppressed because it is too large
Load Diff
440
business/jxcallback/orderman/order_afs.go
Normal file
440
business/jxcallback/orderman/order_afs.go
Normal file
@@ -0,0 +1,440 @@
|
||||
package orderman
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
"git.rosy.net.cn/jx-callback/business/jxcallback/scheduler"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils"
|
||||
"git.rosy.net.cn/jx-callback/business/model"
|
||||
"git.rosy.net.cn/jx-callback/business/model/dao"
|
||||
"git.rosy.net.cn/jx-callback/business/partner"
|
||||
"git.rosy.net.cn/jx-callback/globals"
|
||||
"github.com/astaxie/beego/client/orm"
|
||||
)
|
||||
|
||||
func (c *OrderManager) LoadAfsOrder(vendorAfsOrderID string, vendorID int) (afsOrder *model.AfsOrder, err error) {
|
||||
return c.loadAfsOrder(dao.GetDB(), vendorAfsOrderID, vendorID)
|
||||
}
|
||||
|
||||
func (c *OrderManager) loadAfsOrder(db *dao.DaoDB, vendorAfsOrderID string, vendorID int) (afsOrder *model.AfsOrder, err error) {
|
||||
afsOrder = &model.AfsOrder{
|
||||
AfsOrderID: vendorAfsOrderID,
|
||||
VendorID: vendorID,
|
||||
}
|
||||
if err = dao.GetEntity(db, afsOrder, "AfsOrderID", "VendorID"); err != nil {
|
||||
afsOrder = nil
|
||||
}
|
||||
return afsOrder, err
|
||||
}
|
||||
|
||||
func (c *OrderManager) OnAfsOrderAdjust(afsOrder *model.AfsOrder, orderStatus *model.OrderStatus) (err error) {
|
||||
return c.onAfsOrderNew(afsOrder, orderStatus, true)
|
||||
}
|
||||
|
||||
func (c *OrderManager) OnAfsOrderNew(afsOrder *model.AfsOrder, orderStatus *model.OrderStatus) (err error) {
|
||||
return c.onAfsOrderNew(afsOrder, orderStatus, false)
|
||||
}
|
||||
|
||||
func (c *OrderManager) onAfsOrderNew(afsOrder *model.AfsOrder, orderStatus *model.OrderStatus, isAdjust bool) (err error) {
|
||||
db := dao.GetDB()
|
||||
globals.SugarLogger.Debugf("onAfsOrderNew1 orderID:%s", afsOrder.VendorOrderID)
|
||||
c.setAfsOrderID(db, orderStatus)
|
||||
if afsOrder.AfsOrderID == "" {
|
||||
afsOrder.AfsOrderID = orderStatus.VendorOrderID
|
||||
}
|
||||
if afsOrder.VendorStatus == "" {
|
||||
afsOrder.VendorStatus = orderStatus.VendorStatus
|
||||
}
|
||||
if afsOrder.Status == model.OrderStatusUnknown {
|
||||
afsOrder.Status = orderStatus.Status
|
||||
}
|
||||
globals.SugarLogger.Debugf("onAfsOrderNew2 orderID:%s", afsOrder.VendorOrderID)
|
||||
//
|
||||
if order, _ := c.LoadOrder(afsOrder.VendorOrderID, afsOrder.VendorID); order != nil {
|
||||
if order.ConsigneeMobile2 == "" {
|
||||
if handler := partner.GetPurchaseOrderHandlerFromVendorID(order.VendorID); handler != nil {
|
||||
if order2, _ := handler.GetOrder(order.VendorOrgCode, order.VendorOrderID, order.VendorStoreID); order2 != nil && order.ConsigneeMobile != order2.ConsigneeMobile {
|
||||
order.ConsigneeMobile = order2.ConsigneeMobile
|
||||
c.UpdateOrderFields(order, []string{"ConsigneeMobile"})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//
|
||||
txDB, _ := dao.Begin(db)
|
||||
defer func() {
|
||||
if r := recover(); r != nil || err != nil {
|
||||
dao.Rollback(db, txDB)
|
||||
panic(r)
|
||||
}
|
||||
}()
|
||||
isDuplicated, err := addOrderOrWaybillStatus(orderStatus, db)
|
||||
globals.SugarLogger.Debugf("onAfsOrderNew afsOrderID:%s, isDuplicated:%t", afsOrder.AfsOrderID, isDuplicated)
|
||||
if err != nil || isDuplicated {
|
||||
if err == nil {
|
||||
dao.Commit(db, txDB)
|
||||
}
|
||||
return err
|
||||
}
|
||||
var existAfsOrder *model.AfsOrder
|
||||
if existAfsOrder, err = c.loadAfsOrder(db, afsOrder.AfsOrderID, afsOrder.VendorID); err != nil {
|
||||
if !dao.IsNoRowsError(err) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if existAfsOrder != nil {
|
||||
// todo 可能导致状态回绕
|
||||
existAfsOrder.Status = afsOrder.Status
|
||||
existAfsOrder.VendorStatus = afsOrder.VendorStatus
|
||||
if _, err = dao.UpdateEntity(db, existAfsOrder, "Status", "VendorStatus"); err != nil {
|
||||
return err
|
||||
}
|
||||
afsOrder = existAfsOrder
|
||||
} else {
|
||||
// 全退都要先全删除再建
|
||||
if afsOrder.RefundType == model.AfsTypeFullRefund {
|
||||
isAdjust = true
|
||||
}
|
||||
if err = c.SaveAfsOrder(db, afsOrder, isAdjust); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
dao.Commit(db, txDB)
|
||||
scheduler.CurrentScheduler.OnAfsOrderNew(afsOrder, false)
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *OrderManager) SaveAfsOrder(db *dao.DaoDB, afsOrder *model.AfsOrder, isDeleteFirst bool) (err error) {
|
||||
globals.SugarLogger.Debug(afsOrder.AfsOrderID)
|
||||
if db == nil {
|
||||
db = dao.GetDB()
|
||||
}
|
||||
if err = c.updateAfsOrderOtherInfo(db, afsOrder); err != nil {
|
||||
return err
|
||||
}
|
||||
globals.SugarLogger.Debugf("SaveAfsOrder afsorder :%v", utils.Format4Output(afsOrder, true))
|
||||
txDB, _ := dao.Begin(db)
|
||||
defer func() {
|
||||
if r := recover(); r != nil || err != nil {
|
||||
dao.Rollback(db, txDB)
|
||||
if r != nil {
|
||||
panic(r)
|
||||
}
|
||||
}
|
||||
}()
|
||||
if isDeleteFirst {
|
||||
err = utils.CallFuncLogError(func() error {
|
||||
_, err = dao.DeleteEntity(db, afsOrder, "VendorOrderID", "VendorID")
|
||||
return err
|
||||
}, "SaveAfsOrder delete AfsOrder, afsOrderID:%s", afsOrder.AfsOrderID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = utils.CallFuncLogError(func() error {
|
||||
_, err = dao.DeleteEntity(db, &model.OrderSkuFinancial{
|
||||
VendorOrderID: afsOrder.VendorOrderID,
|
||||
VendorID: afsOrder.VendorID,
|
||||
IsAfsOrder: 1,
|
||||
}, "VendorOrderID", "VendorID", "IsAfsOrder")
|
||||
return err
|
||||
}, "SaveAfsOrder delete OrderSkuFinancial, afsOrderID:%s", afsOrder.AfsOrderID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// 平台结算扣除汇总--平台补贴,售后产生运费,平台收包装费,同城运费、、、
|
||||
deductionsByPm := afsOrder.PmSubsidyMoney + afsOrder.AfsFreightMoney + afsOrder.BoxMoney + afsOrder.TongchengFreightMoney
|
||||
afsOrder.RefundMoneyByCal = afsOrder.SkuUserMoney + afsOrder.FreightUserMoney + deductionsByPm - afsOrder.PmRefundMoney
|
||||
// order.TotalMoney += order.SkuJxMoney // 退款单京西补贴部分先不作计算
|
||||
if err = dao.CreateEntity(db, afsOrder); err != nil {
|
||||
globals.SugarLogger.Warnf("On SaveAfsOrder afsOrder.AfsOrderID:%s err:%v", afsOrder.AfsOrderID, err)
|
||||
return err
|
||||
}
|
||||
|
||||
// 京西结算扣除汇总,先不作计算,计算单条sku最终扣款金额(+该条sku承担的平台结算扣除金额)
|
||||
for _, orderSku := range afsOrder.Skus[1:] {
|
||||
orderSku.RefundMoneyByCal = orderSku.PmSkuSubsidyMoney +
|
||||
utils.Float64TwoInt64(float64(afsOrder.RefundMoneyByCal-afsOrder.PmSkuSubsidyMoney)*float64(orderSku.UserMoney+orderSku.PmSubsidyMoney-orderSku.PmSkuSubsidyMoney)/float64(afsOrder.SkuUserMoney+afsOrder.PmSubsidyMoney-afsOrder.PmSkuSubsidyMoney))
|
||||
afsOrder.Skus[0].RefundMoneyByCal += orderSku.RefundMoneyByCal
|
||||
if err = dao.CreateEntity(db, orderSku); err != nil {
|
||||
globals.SugarLogger.Warnf("On SaveAfsOrder afsOrder.AfsOrderID:%s err:%v, orderSku:%s", afsOrder.AfsOrderID, err, utils.Format4Output(orderSku, true))
|
||||
return err
|
||||
}
|
||||
}
|
||||
if len(afsOrder.Skus) > 0 {
|
||||
orderSku := afsOrder.Skus[0]
|
||||
orderSku.RefundMoneyByCal = afsOrder.RefundMoneyByCal - orderSku.RefundMoneyByCal
|
||||
if err = dao.CreateEntity(db, orderSku); err != nil {
|
||||
globals.SugarLogger.Warnf("On SaveAfsOrder afsOrder.AfsOrderID:%s err:%v, orderSku:%s", afsOrder.AfsOrderID, err, utils.Format4Output(orderSku, true))
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
globals.SugarLogger.Warnf("On SaveAfsOrder afsOrder.AfsOrderID:%s err: afsOrder have no sku", afsOrder.AfsOrderID)
|
||||
}
|
||||
dao.Commit(db, txDB)
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *OrderManager) OnAfsOrderStatusChanged(orderStatus *model.OrderStatus) (err error) {
|
||||
db := dao.GetDB()
|
||||
c.setAfsOrderID(db, orderStatus)
|
||||
txDB, _ := dao.Begin(db)
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
dao.Rollback(db, txDB)
|
||||
panic(r)
|
||||
}
|
||||
}()
|
||||
isDuplicated, afsOrder, err := c.addAfsOrderStatus(db, orderStatus)
|
||||
if err != nil || isDuplicated {
|
||||
if err == nil {
|
||||
dao.Commit(db, txDB)
|
||||
} else {
|
||||
dao.Rollback(db, txDB)
|
||||
}
|
||||
return err
|
||||
}
|
||||
dao.Commit(db, txDB)
|
||||
scheduler.CurrentScheduler.OnAfsOrderStatusChanged(afsOrder, orderStatus, false)
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *OrderManager) addAfsOrderStatus(db *dao.DaoDB, orderStatus *model.OrderStatus) (isDuplicated bool, order *model.AfsOrder, err error) {
|
||||
globals.SugarLogger.Debugf("addAfsOrderStatus refOrderID:%s, orderID:%s", orderStatus.RefVendorOrderID, orderStatus.VendorOrderID)
|
||||
if db == nil {
|
||||
db = dao.GetDB()
|
||||
}
|
||||
isDuplicated, err = addOrderOrWaybillStatus(orderStatus, db)
|
||||
if err == nil && !isDuplicated && (orderStatus.Status != model.OrderStatusUnknown && orderStatus.Status != model.OrderStatusMsg) {
|
||||
order = &model.AfsOrder{
|
||||
AfsOrderID: orderStatus.VendorOrderID,
|
||||
VendorID: orderStatus.VendorID,
|
||||
}
|
||||
if err = db.Db.ReadForUpdate(order, "AfsOrderID", "VendorID"); err == nil {
|
||||
if orderStatus.Status > model.OrderStatusUnknown { // todo 要求status不能回绕
|
||||
order.VendorStatus = orderStatus.VendorStatus
|
||||
order.Status = orderStatus.Status
|
||||
updateFields := []string{
|
||||
"VendorStatus",
|
||||
"Status",
|
||||
}
|
||||
if model.IsAfsOrderFinalStatus(orderStatus.Status) {
|
||||
order.AfsFinishedAt = orderStatus.StatusTime
|
||||
if utils.IsTimeZero(order.AfsFinishedAt) {
|
||||
order.AfsFinishedAt = time.Now()
|
||||
}
|
||||
updateFields = append(updateFields, "AfsFinishedAt")
|
||||
if handler := partner.GetPurchaseOrderHandlerFromVendorID(order.VendorID); handler != nil {
|
||||
if orderAfsInfo, err := handler.GetOrderAfsInfo(nil, order.VendorOrderID, order.AfsOrderID); err == nil && orderAfsInfo.AfsTotalShopMoney != 0 {
|
||||
order.AfsTotalShopMoney = orderAfsInfo.AfsTotalShopMoney
|
||||
}
|
||||
updateFields = append(updateFields, "AfsTotalShopMoney")
|
||||
}
|
||||
}
|
||||
utils.CallFuncLogError(func() error {
|
||||
_, err = dao.UpdateEntity(db, order, updateFields...)
|
||||
return err
|
||||
}, "addAfsOrderStatus update orderID:%s, status:%v", order.VendorOrderID, orderStatus)
|
||||
} else {
|
||||
isDuplicated = true
|
||||
}
|
||||
} else {
|
||||
if dao.IsNoRowsError(err) { // todo 消息错序
|
||||
err = nil
|
||||
} else {
|
||||
globals.SugarLogger.Warnf("addAfsOrderStatus orderID:%s read failed with error:%v", order.VendorOrderID, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return isDuplicated, order, err
|
||||
}
|
||||
|
||||
func (c *OrderManager) updateAfsOrderSkuOtherInfo(db *dao.DaoDB, order *model.AfsOrder) (err error) {
|
||||
globals.SugarLogger.Debugf("updateAfsOrderSkuOtherInfo orderID:%s, VendorStoreID:%s", order.VendorOrderID, order.VendorStoreID)
|
||||
jxStoreID := jxutils.GetSaleStoreIDFromAfsOrder(order)
|
||||
opNumStr := "2"
|
||||
if jxStoreID == 0 {
|
||||
globals.SugarLogger.Infof("updateAfsOrderSkuOtherInfo [运营%s]订单在京西与平台都找不到京西门店信息orderID:%s, VendorStoreID:%s", opNumStr, order.VendorOrderID, order.VendorStoreID)
|
||||
return nil
|
||||
}
|
||||
orderSkus := order.Skus
|
||||
var vendorSkuIDs []string
|
||||
skuIDMap := make(map[int]int)
|
||||
for _, v := range orderSkus {
|
||||
if v.VendorSkuID != "" {
|
||||
vendorSkuIDs = append(vendorSkuIDs, v.VendorSkuID)
|
||||
}
|
||||
|
||||
if skuID := jxutils.GetSkuIDFromOrderSkuFinancial(v); skuID > 0 {
|
||||
skuIDMap[skuID] = 1
|
||||
}
|
||||
}
|
||||
if len(vendorSkuIDs) > 0 {
|
||||
var vendorStoreID string
|
||||
if order.VendorID == model.VendorIDJDShop {
|
||||
vendorStoreID = model.JdShopMainVendorStoreID
|
||||
} else {
|
||||
vendorStoreID = order.VendorStoreID
|
||||
}
|
||||
l, err := dao.GetStoreSkuPriceAndWeight(db, vendorStoreID, order.VendorID, vendorSkuIDs)
|
||||
if err != nil {
|
||||
globals.SugarLogger.Warnf("updateAfsOrderSkuOtherInfo orderID:%s failed with err:%v", order.VendorOrderID, err)
|
||||
return err
|
||||
}
|
||||
skumapper := storeSkuPriceAndWeight2Map(l)
|
||||
|
||||
var actStoreSkuMap *jxutils.ActStoreSkuMap
|
||||
if len(skuIDMap) > 0 {
|
||||
if order2, err2 := c.LoadOrder(order.VendorOrderID, order.VendorID); err2 == nil {
|
||||
actStoreSkuList, err := dao.GetEffectiveActStoreSkuInfo(db, 0, []int{order.VendorID}, model.ActTypeAll, []int{jxStoreID}, jxutils.IntMap2List(skuIDMap), order2.OrderCreatedAt, order2.OrderCreatedAt)
|
||||
if err != nil {
|
||||
globals.SugarLogger.Errorf("updateAfsOrderSkuOtherInfo can not get sku promotion info for error:%v", err)
|
||||
return err
|
||||
}
|
||||
actStoreSkuMap = jxutils.NewActStoreSkuMap(actStoreSkuList, false)
|
||||
}
|
||||
}
|
||||
for _, v := range orderSkus {
|
||||
v.AfsOrderID = order.AfsOrderID
|
||||
v.VendorID = order.VendorID
|
||||
v.VendorOrderID = order.VendorOrderID
|
||||
v.IsAfsOrder = 1
|
||||
v.VendorStoreID = order.VendorStoreID
|
||||
v.StoreID = order.StoreID
|
||||
v.JxStoreID = jxStoreID
|
||||
|
||||
intVendorSkuID := utils.Str2Int64WithDefault(v.VendorSkuID, 0)
|
||||
if intVendorSkuID != 0 && v.VendorSkuID != "-70000" { // todo hard code
|
||||
skuBindInfo := skumapper[v.VendorSkuID]
|
||||
if skuBindInfo == nil {
|
||||
globals.SugarLogger.Infof("updateAfsOrderSkuOtherInfo [运营%s]%s订单sku找不到门店价格(或商品映射),orderID:%s, StoreID:%d, VendorSkuID:%s, sku:%v", opNumStr, model.VendorChineseNames[order.VendorID], order.VendorOrderID, jxStoreID, v.VendorSkuID, v)
|
||||
} else {
|
||||
v.JxSkuID = skuBindInfo.SkuID
|
||||
v.ShopPrice = int64(skuBindInfo.Price)
|
||||
}
|
||||
}
|
||||
if actStoreSkuMap != nil {
|
||||
if skuID := jxutils.GetSkuIDFromOrderSkuFinancial(v); skuID > 0 && v.StoreSubName != "" {
|
||||
if actStoreSku := actStoreSkuMap.GetActStoreSku(jxStoreID, skuID, order.VendorID); actStoreSku != nil {
|
||||
v.StoreSubID = actStoreSku.ActID
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *OrderManager) updateAfsOrderOtherInfo(db *dao.DaoDB, afsOrder *model.AfsOrder) (err error) {
|
||||
globals.SugarLogger.Debugf("updateAfsOrderOtherInfo orderID:%s, VendorStoreID:%s", afsOrder.VendorOrderID, afsOrder.VendorStoreID)
|
||||
if afsOrder.VendorStoreID != "" {
|
||||
if storeDetail, err := dao.GetStoreDetailByVendorStoreID(db, afsOrder.VendorStoreID, 0, ""); err == nil {
|
||||
afsOrder.JxStoreID = storeDetail.Store.ID
|
||||
}
|
||||
}
|
||||
if afsOrder.StoreID == 0 && afsOrder.JxStoreID == 0 {
|
||||
if order, err2 := c.LoadOrder(afsOrder.VendorOrderID, afsOrder.VendorID); err2 == nil {
|
||||
afsOrder.JxStoreID = order.JxStoreID
|
||||
if afsOrder.StoreID == 0 {
|
||||
afsOrder.StoreID = order.StoreID
|
||||
}
|
||||
if afsOrder.VendorStoreID == "" {
|
||||
afsOrder.VendorStoreID = order.VendorStoreID
|
||||
}
|
||||
}
|
||||
}
|
||||
if err == nil {
|
||||
if handler := partner.GetPurchaseOrderHandlerFromVendorID(afsOrder.VendorID); handler != nil {
|
||||
if orderAfsInfo, err := handler.GetOrderAfsInfo(nil, afsOrder.VendorOrderID, afsOrder.AfsOrderID); err == nil && orderAfsInfo.AfsTotalShopMoney != 0 {
|
||||
afsOrder.AfsTotalShopMoney = orderAfsInfo.AfsTotalShopMoney
|
||||
}
|
||||
}
|
||||
if err = c.updateAfsOrderSkuOtherInfo(db, afsOrder); err == nil {
|
||||
jxutils.RefreshAfsOrderSkuRelated(afsOrder)
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *OrderManager) UpdateAfsOrderFields(afsOrder *model.AfsOrder, fieldList []string) (err error) {
|
||||
db := orm.NewOrm()
|
||||
utils.CallFuncLogError(func() error {
|
||||
_, err = db.Update(afsOrder, fieldList...)
|
||||
return err
|
||||
}, "UpdateAfsOrderFields orderID:%s failed with error:%v", afsOrder.VendorOrderID, err)
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *OrderManager) setAfsOrderID(db *dao.DaoDB, orderStatus *model.OrderStatus) {
|
||||
// globals.SugarLogger.Debugf("setAfsOrderID1 orderStatus:%v", utils.Format4Output(orderStatus, true))
|
||||
if dao.IsVendorThingIDEmpty(orderStatus.VendorOrderID) {
|
||||
index := 1
|
||||
if afsOrderList, err2 := dao.GetAfsOrders(db, orderStatus.RefVendorID, orderStatus.RefVendorOrderID, ""); err2 == nil {
|
||||
if len(afsOrderList) > 0 {
|
||||
list := strings.Split(afsOrderList[0].AfsOrderID, "-")
|
||||
if len(list) > 1 {
|
||||
index = int(utils.Str2Int64WithDefault(list[1], 0))
|
||||
if afsOrderList[0].Status >= model.AfsOrderStatusFinished {
|
||||
index++
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
globals.SugarLogger.Warnf("setAfsOrderID err2:%v", err2)
|
||||
}
|
||||
orderStatus.VendorOrderID = composeAfsOrderID(orderStatus.RefVendorOrderID, index)
|
||||
}
|
||||
// globals.SugarLogger.Debugf("setAfsOrderID2 orderStatus:%v", utils.Format4Output(orderStatus, true))
|
||||
}
|
||||
|
||||
func composeAfsOrderID(vendorOrderID string, index int) (afsOrderID string) {
|
||||
return strings.Join([]string{
|
||||
vendorOrderID,
|
||||
utils.Int2Str(index),
|
||||
}, "-")
|
||||
}
|
||||
|
||||
func (c *OrderManager) CreateAfsOrderFromOrder(vendorOrderID string, vendorID int) (afsOrder *model.AfsOrder, err error) {
|
||||
order, err := c.LoadOrder(vendorOrderID, vendorID)
|
||||
// globals.SugarLogger.Debug(utils.Format4Output(order, false))
|
||||
if err == nil {
|
||||
afsOrder = &model.AfsOrder{
|
||||
VendorID: vendorID,
|
||||
VendorOrderID: vendorOrderID,
|
||||
JxStoreID: order.JxStoreID,
|
||||
VendorStoreID: order.VendorStoreID,
|
||||
StoreID: order.StoreID,
|
||||
VendorOrgCode: order.VendorOrgCode,
|
||||
}
|
||||
} else {
|
||||
globals.SugarLogger.Warnf("CreateAfsOrderFromOrder, orderID:%s is not found from partner.CurOrderManager.LoadOrder", vendorOrderID)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, sku := range order.Skus {
|
||||
orderSkuFinancial := &model.OrderSkuFinancial{
|
||||
VendorID: sku.VendorID,
|
||||
VendorOrderID: sku.VendorOrderID,
|
||||
// OrderFinancialID: sku.VendorOrderID,
|
||||
// ConfirmTime: afsOrder.AfsCreateAt,
|
||||
VendorStoreID: afsOrder.VendorStoreID,
|
||||
StoreID: afsOrder.StoreID,
|
||||
JxStoreID: afsOrder.JxStoreID,
|
||||
VendorSkuID: sku.VendorSkuID,
|
||||
SkuID: sku.SkuID,
|
||||
PromotionType: sku.PromotionType,
|
||||
Name: sku.SkuName,
|
||||
ShopPrice: sku.ShopPrice,
|
||||
SalePrice: sku.SalePrice,
|
||||
Count: sku.Count,
|
||||
// UserMoney: sku.UserMoney,
|
||||
// PmSubsidyMoney: sku.PmSubsidyMoney,
|
||||
IsAfsOrder: 1,
|
||||
}
|
||||
afsOrder.Skus = append(afsOrder.Skus, orderSkuFinancial)
|
||||
}
|
||||
return afsOrder, nil
|
||||
}
|
||||
213
business/jxcallback/orderman/order_comment.go
Normal file
213
business/jxcallback/orderman/order_comment.go
Normal file
@@ -0,0 +1,213 @@
|
||||
package orderman
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/weixinmsg"
|
||||
"git.rosy.net.cn/jx-callback/business/model"
|
||||
"git.rosy.net.cn/jx-callback/business/model/dao"
|
||||
"git.rosy.net.cn/jx-callback/business/model/legacymodel"
|
||||
"git.rosy.net.cn/jx-callback/business/partner"
|
||||
"git.rosy.net.cn/jx-callback/globals"
|
||||
)
|
||||
|
||||
const (
|
||||
COMMENT_NOT_RESOLVED = 0 //未解决的差评状态
|
||||
COMMENT_RESOLVED = 1 //已解决的差评状态
|
||||
|
||||
JX_BAD_COMMENTS_MAX_LEVEL = 2 // 差评最大分
|
||||
JX_MIDDLE_COMMENTS_MAX_LEVEL = 4 // 中评最大分
|
||||
|
||||
COMMENTS_SCORE_ONE_ORTWO_BEGIN_DELAY_TIME = 1 * 60 //评论回复一星或二星回复延迟开始时间区间
|
||||
COMMENTS_SCORE_ONE_ORTWO_END_DELAY_TIME = 3 * 60 //评论回复一星或二星回复延迟结束时间区间
|
||||
COMMENTS_SCORE_THREE_BEGIN_DELAY_TIME = 2 * 60 //评论回复三星回复延迟开始时间区间
|
||||
COMMENTS_SCORE_THREE_END_DELAY_TIME = 4 * 60 //评论回复三星回复延迟结束时间区间
|
||||
COMMENTS_SCORE_FOUR_ORFIVE_BEGIN_DELAY_TIME = 2 * 60 //评论回复四星或五星回复延迟开始时间区间
|
||||
COMMENTS_SCORE_FOUR_ORFIVE_END_DELAY_TIME = 5 * 60 //评论回复四星或五星回复延迟结束时间区间
|
||||
|
||||
MAX_REAPLY_TIME = 18 * time.Hour
|
||||
)
|
||||
|
||||
type tReplyConfig struct {
|
||||
delayGapBegin int
|
||||
delayGapEnd int
|
||||
comments []string
|
||||
}
|
||||
|
||||
var (
|
||||
replyConfig = map[int]*tReplyConfig{
|
||||
1: &tReplyConfig{
|
||||
delayGapBegin: COMMENTS_SCORE_ONE_ORTWO_BEGIN_DELAY_TIME,
|
||||
delayGapEnd: COMMENTS_SCORE_ONE_ORTWO_END_DELAY_TIME,
|
||||
comments: []string{
|
||||
"非常抱歉让您没有得到十分满意的购物体验,我们会及时与您联系进行确认并解决问题!",
|
||||
},
|
||||
},
|
||||
3: &tReplyConfig{
|
||||
delayGapBegin: COMMENTS_SCORE_THREE_BEGIN_DELAY_TIME,
|
||||
delayGapEnd: COMMENTS_SCORE_THREE_END_DELAY_TIME,
|
||||
comments: []string{
|
||||
"感谢您对我们的肯定,祝您生活愉快!欢迎再次光临,谢谢!",
|
||||
fmt.Sprintf("感谢您对%s的关照,我们会更加精益求精。", globals.StoreName),
|
||||
"感谢您的光临,您的支持是我们前进的动力!",
|
||||
},
|
||||
},
|
||||
4: &tReplyConfig{
|
||||
delayGapBegin: COMMENTS_SCORE_FOUR_ORFIVE_BEGIN_DELAY_TIME,
|
||||
delayGapEnd: COMMENTS_SCORE_FOUR_ORFIVE_END_DELAY_TIME,
|
||||
comments: []string{
|
||||
"感谢您的信赖!我们会不断提升菜品质量以及优质的服务,期待与您的再次相遇!",
|
||||
"感谢您的支持,愿您天天好心情!",
|
||||
"感谢您的认可!您的支持是我们前进的动力。",
|
||||
"感谢您的肯定与支持!我们会坚持把最好的服务带给您,期待和您的再次相遇!",
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func (c *OrderManager) OnOrderComments(orderCommentList []*model.OrderComment) (err error) {
|
||||
globals.SugarLogger.Debug("OnOrderComments")
|
||||
db := dao.GetDB()
|
||||
for _, orderComment := range orderCommentList {
|
||||
globals.SugarLogger.Debugf("OnOrderComments, orderID:%s", orderComment.VendorOrderID)
|
||||
comment2 := &legacymodel.JxBadComments{
|
||||
OrderId: orderComment.VendorOrderID,
|
||||
}
|
||||
err = dao.GetEntity(db, comment2, "OrderId")
|
||||
if err == nil || dao.IsNoRowsError(err) {
|
||||
isNewComment := false
|
||||
if dao.IsNoRowsError(err) {
|
||||
err = nil
|
||||
isNewComment = true
|
||||
if orderComment.IsReplied == 0 && time.Now().Sub(orderComment.CommentCreatedAt) < time.Duration(orderComment.ModifyDuration)*time.Hour {
|
||||
if storeDetail, err2 := dao.GetStoreDetail(db, orderComment.StoreID, orderComment.VendorID, ""); err2 == nil {
|
||||
if storeDetail.AutoReplyType == model.AutoReplyAll ||
|
||||
orderComment.Score > JX_BAD_COMMENTS_MAX_LEVEL && storeDetail.AutoReplyType == model.AutoReplyGoodComment {
|
||||
c.replyOrderComment(storeDetail.VendorOrgCode, orderComment)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if isNewComment /*&& orderComment.Score <= JX_BAD_COMMENTS_MAX_LEVEL*/ || !isNewComment && orderComment.Score > JX_BAD_COMMENTS_MAX_LEVEL { // 如果是直接非差评,或补评仍然是差评,忽略
|
||||
if isNewComment {
|
||||
comment2.Createtime = utils.Time2Str(orderComment.CommentCreatedAt)
|
||||
comment2.Score = int(orderComment.Score)
|
||||
comment2.Scorecontent = orderComment.Content
|
||||
comment2.Vendertags = orderComment.TagList
|
||||
comment2.Msg = orderComment.OriginalMsg
|
||||
|
||||
comment2.Status = COMMENT_NOT_RESOLVED
|
||||
comment2.OrderFlag = utils.Int2Str(orderComment.VendorID)
|
||||
comment2.Maxmodifytime = int(orderComment.ModifyDuration)
|
||||
|
||||
order, _ := partner.CurOrderManager.LoadOrder(orderComment.VendorOrderID, orderComment.VendorID)
|
||||
if order != nil {
|
||||
orderComment.StoreID = jxutils.GetSaleStoreIDFromOrder(order)
|
||||
if order.ConsigneeMobile2 != "" {
|
||||
orderComment.ConsigneeMobile = order.ConsigneeMobile2
|
||||
} else {
|
||||
if handler := partner.GetPurchaseOrderHandlerFromVendorID(order.VendorID); handler != nil {
|
||||
if order2, _ := handler.GetOrder(order.VendorOrgCode, order.VendorOrderID, order.VendorStoreID); order2 != nil && order.ConsigneeMobile != order2.ConsigneeMobile {
|
||||
order.ConsigneeMobile = order2.ConsigneeMobile
|
||||
partner.CurOrderManager.UpdateOrderFields(order, []string{"ConsigneeMobile"})
|
||||
}
|
||||
}
|
||||
orderComment.ConsigneeMobile = order.ConsigneeMobile
|
||||
}
|
||||
} else {
|
||||
if storeDetail, err := dao.GetStoreDetailByVendorStoreID(db, orderComment.VendorStoreID, orderComment.VendorID, ""); err == nil {
|
||||
orderComment.StoreID = storeDetail.ID
|
||||
}
|
||||
}
|
||||
if orderComment.StoreID > 0 {
|
||||
comment2.Jxstoreid = utils.Int2Str(orderComment.StoreID)
|
||||
}
|
||||
comment2.Userphone = orderComment.ConsigneeMobile
|
||||
if comment2.Jxstoreid != "" && orderComment.Score <= JX_MIDDLE_COMMENTS_MAX_LEVEL && time.Now().Sub(orderComment.CommentCreatedAt) < MAX_REAPLY_TIME {
|
||||
comment2.LastPushTime = utils.Time2Str(time.Now())
|
||||
comment2.PushNo = 1
|
||||
weixinmsg.PushJDBadCommentToWeiXin(comment2, orderComment.Score <= JX_BAD_COMMENTS_MAX_LEVEL, order)
|
||||
}
|
||||
} else { // 修改评价,高于JX_BAD_COMMENTS_MAX_LEVEL
|
||||
if orderComment.CommentCreatedAt.Sub(str2Time(comment2.Createtime)) == 0 ||
|
||||
orderComment.CommentCreatedAt.Sub(str2Time(comment2.Updatetime)) == 0 {
|
||||
comment2 = nil // 重复
|
||||
} else {
|
||||
comment2.Updatetime = utils.Time2Str(orderComment.CommentCreatedAt)
|
||||
comment2.UpdatedMsg = orderComment.OriginalMsg
|
||||
comment2.UpdatedScore = int(orderComment.Score)
|
||||
comment2.UpdatedScorecontent = orderComment.Content
|
||||
comment2.UpdatedVendertags = orderComment.TagList
|
||||
comment2.Status = COMMENT_RESOLVED
|
||||
|
||||
if comment2.Jxstoreid != "" && orderComment.Score <= JX_MIDDLE_COMMENTS_MAX_LEVEL && time.Now().Sub(orderComment.CommentCreatedAt) < MAX_REAPLY_TIME {
|
||||
comment2.LastPushTime = utils.Time2Str(time.Now())
|
||||
comment2.PushNo++
|
||||
|
||||
comment3 := *comment2
|
||||
comment3.Createtime = comment2.Updatetime
|
||||
comment3.Score = comment2.UpdatedScore
|
||||
comment3.Scorecontent = comment2.UpdatedScorecontent
|
||||
comment3.Vendertags = comment2.UpdatedVendertags
|
||||
order, _ := partner.CurOrderManager.LoadOrder(orderComment.VendorOrderID, orderComment.VendorID)
|
||||
weixinmsg.PushJDBadCommentToWeiXin(&comment3, orderComment.Score <= JX_BAD_COMMENTS_MAX_LEVEL, order)
|
||||
}
|
||||
}
|
||||
}
|
||||
if err == nil {
|
||||
if isNewComment {
|
||||
err = dao.CreateEntity(db, comment2)
|
||||
} else if comment2 != nil {
|
||||
_, err = dao.UpdateEntity(db, comment2)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
globals.SugarLogger.Warnf("OnOrderComments orderID:%s failed with error:%v", orderComment.VendorOrderID, err)
|
||||
break
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *OrderManager) replyOrderComment(vendorOrgCode string, orderComment *model.OrderComment) (err error) {
|
||||
score := int(orderComment.Score)
|
||||
if score <= 2 {
|
||||
score = 1
|
||||
} else if score >= 5 {
|
||||
score = 4
|
||||
}
|
||||
config := replyConfig[score]
|
||||
delaySeconds := config.delayGapBegin + rand.Intn(config.delayGapEnd-config.delayGapBegin)
|
||||
content := config.comments[rand.Intn(len(config.comments))]
|
||||
globals.SugarLogger.Debugf("replyOrderComment orderID:%s, delaySeconds:%d, content:%s", orderComment.VendorOrderID, delaySeconds, content)
|
||||
utils.AfterFuncWithRecover(time.Duration(delaySeconds)*time.Second, func() {
|
||||
if handler := partner.GetPurchaseOrderHandlerFromVendorID(orderComment.VendorID); handler != nil {
|
||||
if err = handler.ReplyOrderComment(jxcontext.AdminCtx, vendorOrgCode, orderComment, content); err != nil {
|
||||
globals.SugarLogger.Debugf("replyOrderComment orderID:%s, error:%v", orderComment.VendorOrderID, err)
|
||||
}
|
||||
} else {
|
||||
globals.SugarLogger.Warnf("replyOrderComment can not find handler orderID:%s", orderComment.VendorOrderID)
|
||||
}
|
||||
})
|
||||
// todo 这里直接延时,可以导致服务器重启时漏掉回复,但如果用管理任务,又会导致大量评价任务存在与任务列表中
|
||||
// task := tasksch.NewParallelTask(fmt.Sprintf("回复订单:%s评价", orderComment.VendorOrderID), nil, jxcontext.AdminCtx,
|
||||
// func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
|
||||
// return retVal, err
|
||||
// }, []int{0})
|
||||
// tasksch.HandleTask(task, nil, true).Run()
|
||||
return err
|
||||
}
|
||||
|
||||
func str2Time(timeStr string) time.Time {
|
||||
if timeStr == "" {
|
||||
return utils.DefaultTimeValue
|
||||
}
|
||||
return utils.Str2Time(timeStr)
|
||||
}
|
||||
180
business/jxcallback/orderman/orderman.go
Normal file
180
business/jxcallback/orderman/orderman.go
Normal file
@@ -0,0 +1,180 @@
|
||||
package orderman
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
"git.rosy.net.cn/jx-callback/business/jxcallback/scheduler"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils"
|
||||
"git.rosy.net.cn/jx-callback/business/model"
|
||||
"git.rosy.net.cn/jx-callback/business/model/dao"
|
||||
"git.rosy.net.cn/jx-callback/business/partner"
|
||||
"git.rosy.net.cn/jx-callback/globals"
|
||||
)
|
||||
|
||||
const (
|
||||
pendingOrderGapMax = 2 * 24 * time.Hour // 每次重启机子时,要检查几天内的订单状态
|
||||
maxTimeHandlePendingOrder = 2 * time.Second //处理pending order的最长时间
|
||||
maxSleepGapHandlePendingOrder = 5 * time.Millisecond // 每个pending order的最长时间间隙
|
||||
)
|
||||
|
||||
var (
|
||||
ErrCanNotFindOrder = errors.New("找不到相应订单")
|
||||
ErrCanNotFindWaybill = errors.New("找不到相应运单")
|
||||
)
|
||||
|
||||
var (
|
||||
FixedOrderManager *OrderManager
|
||||
)
|
||||
|
||||
// 所有公共接口调用前,要求在order里或status中设置合适的Status
|
||||
type OrderManager struct {
|
||||
}
|
||||
|
||||
func NewOrderManager() *OrderManager {
|
||||
return &OrderManager{}
|
||||
}
|
||||
|
||||
type IStatusTimer interface {
|
||||
GetStatusTime() time.Time
|
||||
}
|
||||
|
||||
type StatusTimerSlice []IStatusTimer
|
||||
|
||||
func (s StatusTimerSlice) Len() int {
|
||||
return len(s)
|
||||
}
|
||||
|
||||
func (s StatusTimerSlice) Less(i, j int) bool {
|
||||
return s[i].GetStatusTime().Sub(s[j].GetStatusTime()) < 0
|
||||
}
|
||||
|
||||
func (s StatusTimerSlice) Swap(i, j int) {
|
||||
tmp := s[i]
|
||||
s[i] = s[j]
|
||||
s[j] = tmp
|
||||
}
|
||||
|
||||
func init() {
|
||||
FixedOrderManager = NewOrderManager()
|
||||
partner.InitOrderManager(FixedOrderManager)
|
||||
}
|
||||
|
||||
func addOrderOrWaybillStatus(status *model.OrderStatus, db *dao.DaoDB) (isDuplicated bool, err error) {
|
||||
if status.OrderType == model.OrderTypeOrder {
|
||||
globals.SugarLogger.Debugf("addOrderStatus order:%v", status)
|
||||
} else if status.OrderType == model.OrderTypeWaybill {
|
||||
globals.SugarLogger.Debugf("addOrderStatus waybill:%v", status)
|
||||
} else {
|
||||
globals.SugarLogger.Debugf("addOrderStatus afsOrder:%v", status)
|
||||
}
|
||||
txDB , _ := dao.Begin(db)
|
||||
defer func() {
|
||||
if r := recover(); r != nil || err != nil {
|
||||
globals.SugarLogger.Debug("rollback")
|
||||
dao.Rollback(db, txDB)
|
||||
if r != nil {
|
||||
panic(r)
|
||||
}
|
||||
}
|
||||
}()
|
||||
status.ID = 0
|
||||
status.Remark = utils.LimitUTF8StringLen(status.Remark, 255)
|
||||
created, _, err := db.Db.ReadOrCreate(status, "VendorOrderID", "VendorID", "OrderType", "Status", "VendorStatus", "StatusTime")
|
||||
if err == nil {
|
||||
if !created {
|
||||
globals.SugarLogger.Debugf("duplicated event:%v", status)
|
||||
isDuplicated = true
|
||||
status.DuplicatedCount++
|
||||
utils.CallFuncLogError(func() error {
|
||||
_, err = db.Db.Update(status, "DuplicatedCount")
|
||||
return err
|
||||
}, "addOrderOrWaybillStatus update DuplicatedCount, status:%v", status)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
// todo 这里居然会有主键重复错误,逻辑上是不应该的
|
||||
globals.SugarLogger.Warnf("addOrderOrWaybillStatus status:%v, access db error:%v", status, err)
|
||||
} else {
|
||||
dao.Commit(db, txDB)
|
||||
}
|
||||
return isDuplicated, err
|
||||
}
|
||||
|
||||
func (c *OrderManager) GetStatusDuplicatedCount(status *model.OrderStatus) (duplicatedCount int) {
|
||||
if status == nil {
|
||||
return 0
|
||||
}
|
||||
db := dao.GetDB()
|
||||
if err := dao.GetEntity(db, status, "VendorOrderID", "VendorID", "OrderType", "VendorStatus", "StatusTime"); err == nil {
|
||||
return status.DuplicatedCount
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// todo 最好还是改成全事件回放算了
|
||||
func LoadPendingOrders() {
|
||||
orders, err := dao.LoadPendingOrders(dao.GetDB(), time.Now().Add(-pendingOrderGapMax), model.OrderStatusEndBegin)
|
||||
globals.SugarLogger.Infof("LoadPendingOrders orders count:%d, err:%v", len(orders), err)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
ordersCount := len(orders)
|
||||
if ordersCount > 0 {
|
||||
bills := FixedOrderManager.LoadPendingWaybills()
|
||||
globals.SugarLogger.Infof("LoadPendingOrders waybills count:%d", len(bills))
|
||||
var sortOrders StatusTimerSlice
|
||||
orderMap := make(map[string]*model.GoodsOrder)
|
||||
for _, order := range orders {
|
||||
if order.Status > model.OrderStatusNew {
|
||||
status := model.Order2Status(order)
|
||||
sortOrders = append(sortOrders, status)
|
||||
}
|
||||
// order.Status = model.OrderStatusNew // 就是要以实际order状态来调用scheduler.OnOrderNew
|
||||
order.StatusTime = order.OrderCreatedAt
|
||||
sortOrders = append(sortOrders, order)
|
||||
orderMap[jxutils.ComposeUniversalOrderID(order.VendorOrderID, order.VendorID)] = order
|
||||
}
|
||||
for _, bill := range bills {
|
||||
if bill.Status > model.WaybillStatusNew {
|
||||
bill2 := *bill
|
||||
sortOrders = append(sortOrders, &bill2)
|
||||
}
|
||||
bill.Status = model.WaybillStatusNew
|
||||
bill.StatusTime = bill.WaybillCreatedAt
|
||||
sortOrders = append(sortOrders, bill)
|
||||
}
|
||||
sort.Sort(sortOrders)
|
||||
sleepGap := maxTimeHandlePendingOrder / time.Duration(ordersCount)
|
||||
if sleepGap > maxSleepGapHandlePendingOrder {
|
||||
sleepGap = maxSleepGapHandlePendingOrder
|
||||
}
|
||||
lastTime := time.Now()
|
||||
for _, item := range sortOrders {
|
||||
if order, ok := item.(*model.GoodsOrder); ok {
|
||||
jxutils.CallMsgHandlerAsync(func() {
|
||||
scheduler.CurrentScheduler.OnOrderNew(order, true, true)
|
||||
}, jxutils.ComposeUniversalOrderID(order.VendorOrderID, order.VendorID))
|
||||
} else if status, ok := item.(*model.OrderStatus); ok {
|
||||
jxutils.CallMsgHandlerAsync(func() {
|
||||
order := orderMap[jxutils.ComposeUniversalOrderID(status.VendorOrderID, status.VendorID)]
|
||||
scheduler.CurrentScheduler.OnOrderStatusChanged(order, status, true)
|
||||
}, jxutils.ComposeUniversalOrderID(status.RefVendorOrderID, status.RefVendorID))
|
||||
} else {
|
||||
bill := item.(*model.Waybill)
|
||||
jxutils.CallMsgHandlerAsync(func() {
|
||||
scheduler.CurrentScheduler.OnWaybillStatusChanged(bill, true)
|
||||
}, jxutils.ComposeUniversalOrderID(bill.VendorOrderID, bill.OrderVendorID))
|
||||
}
|
||||
curTime := time.Now()
|
||||
timeout := sleepGap - curTime.Sub(lastTime)
|
||||
if timeout > 0 {
|
||||
time.Sleep(timeout)
|
||||
}
|
||||
lastTime = curTime
|
||||
}
|
||||
}
|
||||
}
|
||||
1593
business/jxcallback/orderman/orderman_ext.go
Normal file
1593
business/jxcallback/orderman/orderman_ext.go
Normal file
File diff suppressed because it is too large
Load Diff
17
business/jxcallback/orderman/orderman_test.go
Normal file
17
business/jxcallback/orderman/orderman_test.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package orderman
|
||||
|
||||
import (
|
||||
"git.rosy.net.cn/jx-callback/globals/api2"
|
||||
"git.rosy.net.cn/jx-callback/globals/testinit"
|
||||
|
||||
_ "git.rosy.net.cn/jx-callback/business/partner/purchase/ebai"
|
||||
_ "git.rosy.net.cn/jx-callback/business/partner/purchase/elm"
|
||||
_ "git.rosy.net.cn/jx-callback/business/partner/purchase/jd"
|
||||
_ "git.rosy.net.cn/jx-callback/business/partner/purchase/mtwm"
|
||||
_ "git.rosy.net.cn/jx-callback/business/partner/purchase/weimob/wsc"
|
||||
)
|
||||
|
||||
func init() {
|
||||
testinit.Init()
|
||||
api2.Init()
|
||||
}
|
||||
271
business/jxcallback/orderman/waybill.go
Normal file
271
business/jxcallback/orderman/waybill.go
Normal file
@@ -0,0 +1,271 @@
|
||||
package orderman
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"git.rosy.net.cn/jx-callback/business/partner"
|
||||
|
||||
"git.rosy.net.cn/baseapi/platformapi/dadaapi"
|
||||
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
"git.rosy.net.cn/jx-callback/business/jxcallback/scheduler"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
|
||||
"git.rosy.net.cn/jx-callback/business/model"
|
||||
"git.rosy.net.cn/jx-callback/business/model/dao"
|
||||
"git.rosy.net.cn/jx-callback/globals"
|
||||
"github.com/astaxie/beego/client/orm"
|
||||
)
|
||||
|
||||
var (
|
||||
waybillOrderStatusMap = map[int]int{
|
||||
model.WaybillStatusApplyFailedGetGoods: model.OrderStatusApplyFailedGetGoods,
|
||||
model.WaybillStatusAgreeFailedGetGoods: model.OrderStatusAgreeFailedGetGoods,
|
||||
model.WaybillStatusRefuseFailedGetGoods: model.OrderStatusRefuseFailedGetGoods,
|
||||
model.WaybillStatusDeliverFailed: model.OrderStatusDeliverFailed,
|
||||
}
|
||||
complaintReasonsMap = map[int]string{
|
||||
1: "骑手态度恶劣",
|
||||
2: "骑手接单后未取货",
|
||||
3: "骑手取货太慢",
|
||||
4: "骑手送货太慢",
|
||||
5: "货品未送达",
|
||||
6: "货品有损坏",
|
||||
7: "骑手违规收取顾客其他费用",
|
||||
69: "骑手恶意取消订单",
|
||||
71: "骑手提前点击取货/送达",
|
||||
}
|
||||
)
|
||||
|
||||
func (w *OrderManager) LoadPendingWaybills() []*model.Waybill {
|
||||
db := orm.NewOrm()
|
||||
var bills []*model.Waybill
|
||||
tillTime := time.Now().Add(-pendingOrderGapMax)
|
||||
_, err := db.Raw(`
|
||||
SELECT t1.*
|
||||
FROM waybill t1
|
||||
JOIN goods_order t2 ON t2.vendor_order_id = t1.vendor_order_id
|
||||
AND t2.vendor_id = t1.order_vendor_id
|
||||
AND t2.order_created_at >= ?
|
||||
AND t2.status < ?
|
||||
WHERE t1.waybill_created_at >= ?
|
||||
AND t1.status < ?
|
||||
`, tillTime, model.OrderStatusEndBegin, tillTime, model.WaybillStatusEndBegin).QueryRows(&bills)
|
||||
if err != nil {
|
||||
globals.SugarLogger.Warnf("LoadPendingWaybills load pending waybills error:%v", err)
|
||||
return nil
|
||||
}
|
||||
return bills
|
||||
}
|
||||
|
||||
func (w *OrderManager) onWaybillNew(bill2 *model.Waybill, db *dao.DaoDB) (isDuplicated bool, err error) {
|
||||
globals.SugarLogger.Debugf("onWaybillNew bill:%v", bill2)
|
||||
isDuplicated, err = addOrderOrWaybillStatus(model.Waybill2Status(bill2), db)
|
||||
if err == nil && !isDuplicated {
|
||||
bill2.ID = 0
|
||||
bill2.WaybillCreatedAt = bill2.StatusTime
|
||||
bill2.WaybillFinishedAt = utils.DefaultTimeValue
|
||||
billCopied := *bill2
|
||||
bill := &billCopied
|
||||
created, _, err2 := db.Db.ReadOrCreate(bill, "VendorWaybillID", "WaybillVendorID")
|
||||
if err = err2; err == nil {
|
||||
if !created {
|
||||
bill.DuplicatedCount++
|
||||
if bill2.VendorOrderID == bill2.VendorWaybillID { // 购物平台(比如京东)重新建的运单,单号始终是与订单相同的
|
||||
globals.SugarLogger.Infof("onWaybillNew duplicated1, DuplicatedCount:%d, bill:%v msg received", bill2.DuplicatedCount, bill2)
|
||||
bill2.ID = bill.ID
|
||||
bill2.CreatedAt = bill.CreatedAt
|
||||
bill2.DuplicatedCount = bill.DuplicatedCount
|
||||
err = utils.CallFuncLogError(func() error {
|
||||
_, err = db.Db.Update(bill2) //更新所有字段
|
||||
return err
|
||||
}, "onWaybillNew Update1")
|
||||
} else {
|
||||
globals.SugarLogger.Infof("onWaybillNew duplicated2 DuplicatedCount:%d, bill:%v msg received", bill.DuplicatedCount, bill2)
|
||||
isDuplicated = true
|
||||
err = utils.CallFuncLogError(func() error {
|
||||
_, err = db.Db.Update(bill, "DuplicatedCount")
|
||||
return err
|
||||
}, "onWaybillNew Update2")
|
||||
}
|
||||
} else {
|
||||
*bill2 = *bill
|
||||
globals.SugarLogger.Debugf("onWaybillNew created bill:%v", bill2)
|
||||
}
|
||||
} else {
|
||||
globals.SugarLogger.Warnf("onWaybillNew create bill:%v, error:%v", bill2, err)
|
||||
}
|
||||
}
|
||||
return isDuplicated, err
|
||||
}
|
||||
|
||||
func (w *OrderManager) OnWaybillStatusChanged(bill *model.Waybill) (err error) {
|
||||
var isDuplicated bool
|
||||
if bill.ActualFee == 0 {
|
||||
bill.ActualFee = bill.DesiredFee + bill.TipFee
|
||||
}
|
||||
bill.CourierMobile = jxutils.FormalizeMobile(bill.CourierMobile)
|
||||
db := dao.GetDB()
|
||||
txDB, _ := dao.Begin(db)
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
dao.Rollback(db, txDB)
|
||||
panic(r)
|
||||
}
|
||||
}()
|
||||
duplicatedCount := 0
|
||||
if bill.Status == model.WaybillStatusNew {
|
||||
isDuplicated, err = w.onWaybillNew(bill, db)
|
||||
if isDuplicated {
|
||||
duplicatedCount = 1
|
||||
}
|
||||
} else {
|
||||
existingBill, err2 := w.LoadWaybill(bill.VendorWaybillID, bill.WaybillVendorID)
|
||||
// todo
|
||||
if err2 == nil {
|
||||
bill.DeliveryFlag = existingBill.DeliveryFlag
|
||||
}
|
||||
if bill.Status == model.WaybillStatusAccepted { // 处理美团配送丢失新运单消息的情况
|
||||
if err2 != nil {
|
||||
if dao.IsNoRowsError(err2) || err2 == ErrCanNotFindWaybill {
|
||||
existingBill = bill
|
||||
billCopy := *bill
|
||||
billCopy.Status = model.WaybillStatusNew
|
||||
if isDuplicated, err = w.onWaybillNew(&billCopy, db); err != nil {
|
||||
dao.Rollback(db, txDB)
|
||||
return err
|
||||
}
|
||||
dao.Commit(db, txDB)
|
||||
// 进运单调度器OnWaybillStatusChanged之前要确保事务是提交了的,否则会导致死锁
|
||||
scheduler.CurrentScheduler.OnWaybillStatusChanged(&billCopy, false)
|
||||
dao.Begin(db)
|
||||
} else {
|
||||
dao.Rollback(db, txDB)
|
||||
return err2
|
||||
}
|
||||
}
|
||||
// 运单消息错序,之前已经结束了,直接返回
|
||||
if existingBill.Status >= model.WaybillStatusEndBegin {
|
||||
dao.Commit(db, txDB)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
addParams := orm.Params{}
|
||||
if bill.Status >= model.WaybillStatusAccepted {
|
||||
if bill.Status == model.WaybillStatusAccepted {
|
||||
if bill.DesiredFee > 0 {
|
||||
addParams["desired_fee"] = bill.DesiredFee
|
||||
}
|
||||
if bill.ActualFee > 0 {
|
||||
addParams["actual_fee"] = bill.ActualFee
|
||||
}
|
||||
}
|
||||
if bill.CourierMobile != "" {
|
||||
addParams["courier_name"] = bill.CourierName
|
||||
addParams["courier_mobile"] = bill.CourierMobile
|
||||
}
|
||||
if bill.Status >= model.WaybillStatusEndBegin {
|
||||
addParams["waybill_finished_at"] = bill.StatusTime
|
||||
}
|
||||
}
|
||||
duplicatedCount, err = w.addWaybillStatus(bill, db, addParams)
|
||||
if err != nil {
|
||||
dao.Rollback(db, txDB)
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err == nil {
|
||||
dao.Commit(db, txDB)
|
||||
if duplicatedCount == 0 {
|
||||
scheduler.CurrentScheduler.OnWaybillStatusChanged(bill, false)
|
||||
}
|
||||
} else {
|
||||
dao.Rollback(db, txDB)
|
||||
}
|
||||
if bill.VendorOrderID == bill.VendorWaybillID {
|
||||
if status, ok := waybillOrderStatusMap[bill.Status]; ok {
|
||||
fakeOrderStatus := &model.OrderStatus{
|
||||
VendorOrderID: bill.VendorOrderID,
|
||||
VendorID: bill.OrderVendorID,
|
||||
OrderType: model.OrderTypeOrder,
|
||||
RefVendorOrderID: bill.VendorOrderID,
|
||||
RefVendorID: bill.OrderVendorID,
|
||||
Status: status,
|
||||
VendorStatus: bill.VendorStatus,
|
||||
StatusTime: bill.StatusTime,
|
||||
Remark: bill.Remark,
|
||||
}
|
||||
w.OnOrderStatusChanged(bill.VendorOrgCode, fakeOrderStatus)
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (w *OrderManager) addWaybillStatus(bill *model.Waybill, db *dao.DaoDB, addParams orm.Params) (duplicatedCount int, err error) {
|
||||
waybillStatus := model.Waybill2Status(bill)
|
||||
isDuplicated, err := addOrderOrWaybillStatus(waybillStatus, db)
|
||||
if err == nil && !isDuplicated {
|
||||
if waybillStatus.Status > model.WaybillStatusUnknown { // todo 这里应该和addOrderStatus一样的改法,状态不能回绕
|
||||
params := utils.MergeMaps(orm.Params{
|
||||
"status": bill.Status,
|
||||
"vendor_status": bill.VendorStatus,
|
||||
"status_time": bill.StatusTime,
|
||||
}, addParams)
|
||||
utils.CallFuncLogError(func() error {
|
||||
_, err = db.Db.QueryTable("waybill").Filter("vendor_waybill_id", bill.VendorWaybillID).Filter("waybill_vendor_id", bill.WaybillVendorID).Filter("status__lte", bill.Status).Update(params)
|
||||
return err
|
||||
}, "addWaybillStatus update waybill status, bill:%v", bill)
|
||||
} else {
|
||||
duplicatedCount = -1
|
||||
}
|
||||
} else {
|
||||
duplicatedCount = 1
|
||||
}
|
||||
return duplicatedCount, err
|
||||
}
|
||||
|
||||
func (c *OrderManager) LoadWaybill(vendorWaybillID string, waybillVendorID int) (bill *model.Waybill, err error) {
|
||||
db := orm.NewOrm()
|
||||
bill = &model.Waybill{
|
||||
VendorWaybillID: vendorWaybillID,
|
||||
WaybillVendorID: waybillVendorID,
|
||||
}
|
||||
if err = db.Read(bill, "VendorWaybillID", "WaybillVendorID"); err != nil {
|
||||
bill = nil
|
||||
if err == orm.ErrNoRows {
|
||||
err = ErrCanNotFindWaybill
|
||||
}
|
||||
globals.SugarLogger.Infof("LoadWaybill vendorWaybillID:%s failed with error:%v", vendorWaybillID, err)
|
||||
}
|
||||
return bill, err
|
||||
}
|
||||
|
||||
func GetComplaintReasons() (complaintReasonList []*dadaapi.ComplaintReason) {
|
||||
for k, v := range complaintReasonsMap {
|
||||
complaintReason := &dadaapi.ComplaintReason{
|
||||
ID: k,
|
||||
Reason: v,
|
||||
}
|
||||
complaintReasonList = append(complaintReasonList, complaintReason)
|
||||
}
|
||||
return complaintReasonList
|
||||
}
|
||||
|
||||
func ComplaintRider(ctx *jxcontext.Context, vendorOrderID string, vendorID, waybillVendorID, complaintID int) (err error) {
|
||||
db := dao.GetDB()
|
||||
p := partner.GetDeliveryPlatformFromVendorID(waybillVendorID).Handler
|
||||
wayBillList, err := dao.GetWayBillByOrderID(db, 0, vendorID, waybillVendorID, vendorOrderID)
|
||||
if err == nil && len(wayBillList) > 0 {
|
||||
err = p.ComplaintRider(wayBillList[0], complaintID, complaintReasonsMap[complaintID])
|
||||
} else {
|
||||
return fmt.Errorf("未查询到到相关订单!订单号:[%v] ,厂商:[%v],运送厂商:[%v]", vendorOrderID, vendorID, waybillVendorID)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func ComplaintRiderPlatform(ctx *jxcontext.Context, vendorOrderID string, vendorID, waybillVendorID, complaintID int) (err error) {
|
||||
return fmt.Errorf("只支持三方配送投诉!")
|
||||
//handler := partner.GetPurchaseOrderHandlerFromVendorID(vendorID)
|
||||
//return handler.ComplaintRider(vendorOrderID, complaintID, "")
|
||||
}
|
||||
200
business/jxcallback/scheduler/basesch/basesch.go
Normal file
200
business/jxcallback/scheduler/basesch/basesch.go
Normal file
@@ -0,0 +1,200 @@
|
||||
package basesch
|
||||
|
||||
import (
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
"git.rosy.net.cn/jx-callback/business/jxcallback/scheduler"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils"
|
||||
"git.rosy.net.cn/jx-callback/business/model"
|
||||
"git.rosy.net.cn/jx-callback/business/model/dao"
|
||||
"git.rosy.net.cn/jx-callback/business/partner"
|
||||
"git.rosy.net.cn/jx-callback/globals"
|
||||
)
|
||||
|
||||
var (
|
||||
FixedBaseScheduler *BaseScheduler
|
||||
)
|
||||
|
||||
type BaseScheduler struct {
|
||||
IsReallyCallPlatformAPI bool
|
||||
}
|
||||
|
||||
func (c *BaseScheduler) AcceptOrRefuseOrder(order *model.GoodsOrder, isAcceptIt bool, userName string) (err error) {
|
||||
globals.SugarLogger.Infof("AcceptOrRefuseOrder orderID:%s, isAcceptIt:%t", order.VendorOrderID, isAcceptIt)
|
||||
if /*order.LockStatus == model.OrderStatusUnknown && */ order.Status == model.OrderStatusNew || order.Status == model.OrderStatusWaitAccepted {
|
||||
if c.IsReallyCallPlatformAPI {
|
||||
err = utils.CallFuncLogErrorWithInfo(func() error {
|
||||
return partner.GetPurchaseOrderHandlerFromVendorID(order.VendorID).AcceptOrRefuseOrder(order, isAcceptIt, userName)
|
||||
}, "AcceptOrRefuseOrder orderID:%s, isAcceptIt:%t", order.VendorOrderID, isAcceptIt)
|
||||
}
|
||||
} else {
|
||||
return scheduler.ErrOrderStatusAlreadySatisfyCurOperation
|
||||
globals.SugarLogger.Debugf("AcceptOrRefuseOrder orderID:%s, status:%d is not suitable, isAcceptIt:%t", order.VendorOrderID, order.Status, isAcceptIt)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *BaseScheduler) PickupGoods(order *model.GoodsOrder, isSelfDelivery bool, userName string) (err error) {
|
||||
globals.SugarLogger.Infof("PickupGoods orderID:%s,order :%v", order.VendorOrderID, utils.Format4Output(order, false))
|
||||
if /*order.LockStatus == model.OrderStatusUnknown && */ order.Status == model.OrderStatusAccepted {
|
||||
if c.IsReallyCallPlatformAPI {
|
||||
handler := partner.GetPurchaseOrderHandlerFromVendorID(order.VendorID)
|
||||
err = utils.CallFuncLogErrorWithInfo(func() (err error) {
|
||||
if err = handler.PickupGoods(order, isSelfDelivery, userName); err != nil {
|
||||
if status, err2 := handler.GetOrderStatus(order.VendorOrgCode, order.VendorOrderID); err2 == nil && status >= model.OrderStatusFinished {
|
||||
err = nil
|
||||
}
|
||||
}
|
||||
return err
|
||||
}, "PickupGoods orderID:%s", order.VendorOrderID)
|
||||
}
|
||||
} else {
|
||||
if order.LockStatus != model.OrderStatusUnknown || order.Status < model.OrderStatusAccepted {
|
||||
err = scheduler.ErrOrderStatusIsNotSuitable4CurOperation
|
||||
globals.SugarLogger.Infof("PickupGoods orderID:%s, status:%d is not suitable", order.VendorOrderID, order.Status)
|
||||
} else {
|
||||
err = scheduler.ErrOrderStatusAlreadySatisfyCurOperation
|
||||
globals.SugarLogger.Debugf("PickupGoods orderID:%s status:%d already ok", order.VendorOrderID, order.Status)
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *BaseScheduler) Swtich2SelfDeliver(order *model.GoodsOrder, userName string) (err error) {
|
||||
globals.SugarLogger.Infof("Swtich2SelfDeliver orderID:%s", order.VendorOrderID)
|
||||
if /*order.LockStatus == model.OrderStatusUnknown && */ order.Status >= model.OrderStatusFinishedPickup && order.Status <= model.OrderStatusDelivering {
|
||||
if order.DeliveryFlag&model.OrderDeliveryFlagMaskPurcahseDisabled == 0 && c.IsReallyCallPlatformAPI {
|
||||
err = utils.CallFuncLogErrorWithInfo(func() error {
|
||||
return partner.GetPurchaseOrderHandlerFromVendorID(order.VendorID).Swtich2SelfDeliver(order, userName)
|
||||
}, "Swtich2SelfDeliver orderID:%s", order.VendorOrderID)
|
||||
}
|
||||
if err == nil { // 因为有些平台转自送后,不会再发送订单在配送中消息过来,所以成功后就强制设置状态为配送中
|
||||
order.Status = model.OrderStatusDelivering
|
||||
order.DeliveryFlag |= model.OrderDeliveryFlagMaskPurcahseDisabled
|
||||
err = partner.CurOrderManager.UpdateOrderStatusAndDeliveryFlag(order)
|
||||
}
|
||||
} else {
|
||||
if order.LockStatus != model.OrderStatusUnknown || order.Status < model.OrderStatusFinishedPickup || order.VendorID == order.WaybillVendorID {
|
||||
err = scheduler.ErrOrderStatusIsNotSuitable4CurOperation
|
||||
globals.SugarLogger.Infof("Swtich2SelfDeliver orderID:%s, status:%d is not suitable", order.VendorOrderID, order.Status)
|
||||
} else {
|
||||
err = scheduler.ErrOrderStatusAlreadySatisfyCurOperation
|
||||
globals.SugarLogger.Debugf("Swtich2SelfDeliver orderID:%s status:%d already ok", order.VendorOrderID, order.Status)
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *BaseScheduler) Swtich2SelfDelivered(order *model.GoodsOrder, userName string) (err error) {
|
||||
globals.SugarLogger.Infof("Swtich2SelfDelivered orderID:%s", order.VendorOrderID)
|
||||
if /*order.LockStatus == model.OrderStatusUnknown && */ order.Status == model.OrderStatusDelivering {
|
||||
if c.IsReallyCallPlatformAPI {
|
||||
err = utils.CallFuncLogError(func() error {
|
||||
return partner.GetPurchaseOrderHandlerFromVendorID(order.VendorID).Swtich2SelfDelivered(order, userName)
|
||||
}, "Swtich2SelfDelivered orderID:%s", order.VendorOrderID)
|
||||
}
|
||||
} else {
|
||||
if order.LockStatus != model.OrderStatusUnknown || order.Status < model.OrderStatusDelivering {
|
||||
err = scheduler.ErrOrderStatusIsNotSuitable4CurOperation
|
||||
globals.SugarLogger.Infof("Swtich2SelfDelivered orderID:%s, status:%d is not suitable", order.VendorOrderID, order.Status)
|
||||
} else {
|
||||
err = scheduler.ErrOrderStatusAlreadySatisfyCurOperation
|
||||
globals.SugarLogger.Debugf("Swtich2SelfDelivered orderID:%s status:%d already ok", order.VendorOrderID, order.Status)
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *BaseScheduler) SelfDeliverDelivering(order *model.GoodsOrder, userName string) (err error) {
|
||||
globals.SugarLogger.Infof("SelfDeliverDelivering orderID:%s", order.VendorOrderID)
|
||||
if /*order.LockStatus == model.OrderStatusUnknown && */ order.Status == model.OrderStatusFinishedPickup {
|
||||
if c.IsReallyCallPlatformAPI {
|
||||
err = utils.CallFuncLogError(func() error {
|
||||
return partner.GetPurchaseOrderHandlerFromVendorID(order.VendorID).SelfDeliverDelivering(order, userName)
|
||||
}, "SelfDeliverDelivering orderID:%s", order.VendorOrderID)
|
||||
if err == nil { // 因为有些平台设置配送中后,不会发送订单在配送中消息过来,所以成功后就强制设置状态为配送中
|
||||
order.Status = model.OrderStatusDelivering
|
||||
err = partner.CurOrderManager.UpdateOrderStatusAndDeliveryFlag(order)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if order.LockStatus != model.OrderStatusUnknown || order.Status < model.OrderStatusFinishedPickup {
|
||||
err = scheduler.ErrOrderStatusIsNotSuitable4CurOperation
|
||||
globals.SugarLogger.Infof("SelfDeliverDelivering orderID:%s, status:%d is not suitable", order.VendorOrderID, order.Status)
|
||||
} else {
|
||||
err = scheduler.ErrOrderStatusAlreadySatisfyCurOperation
|
||||
globals.SugarLogger.Debugf("SelfDeliverDelivering orderID:%s, status:%d already ok", order.VendorOrderID, order.Status)
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *BaseScheduler) SelfDeliverDelivered(order *model.GoodsOrder, userName string) (err error) {
|
||||
globals.SugarLogger.Infof("SelfDeliverDelivered orderID:%s", order.VendorOrderID)
|
||||
if /*order.LockStatus == model.OrderStatusUnknown && */ order.Status >= model.OrderStatusFinishedPickup &&
|
||||
order.Status <= model.OrderStatusDelivering {
|
||||
if c.IsReallyCallPlatformAPI {
|
||||
err = utils.CallFuncLogError(func() error {
|
||||
return partner.GetPurchaseOrderHandlerFromVendorID(order.VendorID).SelfDeliverDelivered(order, userName)
|
||||
}, "SelfDeliverDelivered orderID:%s", order.VendorOrderID)
|
||||
}
|
||||
} else {
|
||||
if order.LockStatus != model.OrderStatusUnknown || order.Status < model.OrderStatusDelivering {
|
||||
err = scheduler.ErrOrderStatusIsNotSuitable4CurOperation
|
||||
globals.SugarLogger.Infof("SelfDeliverDelivered orderID:%s, status:%d is not suitable", order.VendorOrderID, order.Status)
|
||||
} else {
|
||||
err = scheduler.ErrOrderStatusAlreadySatisfyCurOperation
|
||||
globals.SugarLogger.Debugf("SelfDeliverDelivered orderID:%s, status:%d already ok", order.VendorOrderID, order.Status)
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *BaseScheduler) CreateWaybill(platformVendorID int, order *model.GoodsOrder, maxDeliveryFee int64) (bill *model.Waybill, err error) {
|
||||
globals.SugarLogger.Infof("CreateWaybill orderID:%s, vendor:%s", order.VendorOrderID, jxutils.GetVendorName(platformVendorID))
|
||||
if !model.IsOrderSolid(order) { // 如果订单是不完整的
|
||||
globals.SugarLogger.Warnf("CreateWaybill orderID:%s, vendorID:%d is not solid!!!", order.VendorOrderID, platformVendorID)
|
||||
return nil, scheduler.ErrOrderIsNotSolid
|
||||
}
|
||||
// if order.DeliveryFlag&model.OrderDeliveryFlagMaskScheduleDisabled != 0 {
|
||||
// waybillList, err := partner.CurOrderManager.GetOrderWaybillInfo(jxcontext.AdminCtx, order.VendorOrderID, order.VendorID, true)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// if len(waybillList) > 0 {
|
||||
// return nil, fmt.Errorf("转商家自送的订单只允许有一个有效运单,当前已经有%s运单", jxutils.GetVendorName(waybillList[0].WaybillVendorID))
|
||||
// }
|
||||
// }
|
||||
handlerInfo := partner.GetDeliveryPlatformFromVendorID(platformVendorID)
|
||||
if handlerInfo != nil && handlerInfo.Use4CreateWaybill {
|
||||
if c.IsReallyCallPlatformAPI {
|
||||
bill, err = handlerInfo.Handler.CreateWaybill(order, maxDeliveryFee)
|
||||
if err != nil {
|
||||
globals.SugarLogger.Infof("CreateWaybill failed orderID:%s vendorID:%d with error:%v", order.VendorOrderID, platformVendorID, err)
|
||||
} else {
|
||||
order.DeliveryFlag |= model.WaybillVendorID2Mask(platformVendorID)
|
||||
err = partner.CurOrderManager.UpdateOrderStatusAndDeliveryFlag(order)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
err = scheduler.ErrDeliverProviderWrong
|
||||
}
|
||||
return bill, err
|
||||
}
|
||||
|
||||
func (c *BaseScheduler) CancelWaybill(bill *model.Waybill, cancelReasonID int, cancelReason string) (err error) {
|
||||
globals.SugarLogger.Infof("CancelWaybill bill:%v, cancelReasonID:%d cancelReason:%s", bill, cancelReasonID, cancelReason)
|
||||
// 部分快递平台在取消成功后有时会不发运单取消消息过来(比如达达,904200512000442),为避免二次取消报错,添加状态判断
|
||||
if c.IsReallyCallPlatformAPI && bill.OrderVendorID != bill.WaybillVendorID && bill.Status != model.WaybillStatusCanceled {
|
||||
if handlerInfo := partner.GetDeliveryPlatformFromVendorID(bill.WaybillVendorID); handlerInfo != nil {
|
||||
if err = utils.CallFuncLogErrorWithInfo(func() error {
|
||||
return handlerInfo.Handler.CancelWaybill(bill, cancelReasonID, cancelReason)
|
||||
}, "CancelWaybill bill:%v", bill); err == nil {
|
||||
bill.Status = model.WaybillStatusCanceled
|
||||
bill.DeliveryFlag |= model.WaybillDeliveryFlagMaskActiveCancel
|
||||
_, err = dao.UpdateEntity(nil, bill, "Status", "DeliveryFlag")
|
||||
}
|
||||
globals.SugarLogger.Debugf("CancelWaybill bill:%v canceled by myself", bill)
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
479
business/jxcallback/scheduler/basesch/basesch_ext.go
Normal file
479
business/jxcallback/scheduler/basesch/basesch_ext.go
Normal file
@@ -0,0 +1,479 @@
|
||||
package basesch
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.rosy.net.cn/baseapi/platformapi/jdshopapi"
|
||||
|
||||
"git.rosy.net.cn/baseapi/platformapi/dingdingapi"
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
"git.rosy.net.cn/baseapi/utils/errlist"
|
||||
"git.rosy.net.cn/jx-callback/business/jxstore/cms"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/ddmsg"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/tasksch"
|
||||
"git.rosy.net.cn/jx-callback/business/model"
|
||||
"git.rosy.net.cn/jx-callback/business/model/dao"
|
||||
"git.rosy.net.cn/jx-callback/business/partner"
|
||||
"git.rosy.net.cn/jx-callback/globals"
|
||||
)
|
||||
|
||||
const (
|
||||
autoSelfTakeCode = "135246"
|
||||
)
|
||||
|
||||
func (c *BaseScheduler) CreateWaybillOnProviders(ctx *jxcontext.Context, order *model.GoodsOrder, courierVendorIDs, excludeCourierVendorIDs []int, maxDeliveryFee int64, createOnlyOne bool) (bills []*model.Waybill, err error) {
|
||||
userName := ctx.GetUserName()
|
||||
globals.SugarLogger.Infof("CreateWaybillOnProviders orderID:%s userName:%s, courierVendorIDs:%v, excludeCourierVendorIDs:%v", order.VendorOrderID, userName, courierVendorIDs, excludeCourierVendorIDs)
|
||||
storeCourierList, err := dao.GetStoreCourierList2(dao.GetDB(), []int{jxutils.GetSaleStoreIDFromOrder(order)}, nil, model.StoreStatusOpened, []int{model.StoreAuditStatusOnline, model.StoreAuditStatusUpdated})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
courierVendorIDMap := jxutils.IntList2Map(courierVendorIDs)
|
||||
excludeCourierVendorIDMap := jxutils.IntList2Map(excludeCourierVendorIDs)
|
||||
errList := errlist.New()
|
||||
for _, storeCourier := range storeCourierList {
|
||||
if (courierVendorIDs == nil || courierVendorIDMap[storeCourier.VendorID] == 1) &&
|
||||
(excludeCourierVendorIDs == nil || excludeCourierVendorIDMap[storeCourier.VendorID] == 0) {
|
||||
if handler := partner.GetDeliveryPlatformFromVendorID(storeCourier.VendorID); handler != nil && handler.Use4CreateWaybill {
|
||||
courierVendorID := storeCourier.VendorID
|
||||
bill, err2 := c.CreateWaybill(courierVendorID, order, maxDeliveryFee)
|
||||
if err = err2; err == nil {
|
||||
globals.SugarLogger.Debugf("CreateWaybillOnProviders orderID:%s userName:%s vendorID:%d bill:%v", order.VendorOrderID, userName, courierVendorID, bill)
|
||||
bills = append(bills, bill)
|
||||
if createOnlyOne {
|
||||
break
|
||||
}
|
||||
} else {
|
||||
globals.SugarLogger.Debugf("CreateWaybillOnProviders orderID:%s userName:%s vendorID:%d failed with error:%v", order.VendorOrderID, userName, courierVendorID, err)
|
||||
errList.AddErr(fmt.Errorf("平台:%s,%s", jxutils.GetVendorName(courierVendorID), err.Error()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(bills) > 0 {
|
||||
err = errList.GetErrListAsOne()
|
||||
if err != nil {
|
||||
partner.CurOrderManager.OnOrderMsg(order, "创建三方运单部分失败", err.Error())
|
||||
}
|
||||
err = nil
|
||||
} else if errList.GetErrListAsOne() == nil {
|
||||
err = fmt.Errorf("orderID:%s没有绑定有效的三方配送门店或没有剩下可用的三方配送", order.VendorOrderID)
|
||||
} else {
|
||||
err = fmt.Errorf("orderID:%s所有运单失败:%s", order.VendorOrderID, errList.GetErrListAsOne().Error())
|
||||
}
|
||||
globals.SugarLogger.Infof("CreateWaybillOnProviders orderID:%s userName:%s error:%v", order.VendorOrderID, userName, err)
|
||||
return bills, err
|
||||
}
|
||||
|
||||
func (c *BaseScheduler) SelfDeliveredAndUpdateStatus(ctx *jxcontext.Context, vendorOrderID string, vendorID int, userName string) (err error) {
|
||||
jxutils.CallMsgHandler(func() {
|
||||
err = func() (err error) {
|
||||
globals.SugarLogger.Infof("SelfDeliveredAndUpdateStatus orderID:%s userName:%s", vendorOrderID, userName)
|
||||
order, err := partner.CurOrderManager.LoadOrder(vendorOrderID, vendorID)
|
||||
if err == nil {
|
||||
if model.IsOrderDeliveryByStore(order) {
|
||||
err = c.SelfDeliverDelivered(order, userName)
|
||||
} else if model.IsOrderDeliveryByPlatform(order) {
|
||||
err = c.Swtich2SelfDelivered(order, userName)
|
||||
}
|
||||
if err == nil {
|
||||
// order.Status = model.OrderStatusFinished // todo 是否需要强制设置完成状态?
|
||||
if err = dao.SetOrderFlag(dao.GetDB(), ctx.GetUserName(), order.VendorOrderID, order.VendorID, model.OrderFlagMaskSetDelivered); err == nil {
|
||||
globals.SugarLogger.Infof("SelfDeliveredAndUpdateStatus orderID:%s userName:%s successfully", vendorOrderID, userName)
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
globals.SugarLogger.Infof("SelfDeliveredAndUpdateStatus orderID:%s userName:%s error:%v", vendorOrderID, userName, err)
|
||||
return err
|
||||
}()
|
||||
}, jxutils.ComposeUniversalOrderID(vendorOrderID, vendorID))
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *BaseScheduler) PickupGoodsAndUpdateStatus(ctx *jxcontext.Context, vendorOrderID string, vendorID int, userName string) (err error) {
|
||||
jxutils.CallMsgHandler(func() {
|
||||
err = func() (err error) {
|
||||
globals.SugarLogger.Infof("PickupGoodsAndUpdateStatus orderID:%s userName:%s", vendorOrderID, userName)
|
||||
order, err := partner.CurOrderManager.LoadOrder(vendorOrderID, vendorID)
|
||||
if err == nil {
|
||||
flag := model.IsOrderDeliveryByStore(order) || model.IsOrderDeliveryBySelf(order)
|
||||
err = c.PickupGoods(order, flag, userName)
|
||||
if err == nil {
|
||||
order.Status = model.OrderStatusFinishedPickup
|
||||
if err = partner.CurOrderManager.UpdateOrderStatusAndDeliveryFlag(order); err == nil {
|
||||
globals.SugarLogger.Infof("PickupGoodsAndUpdateStatus orderID:%s userName:%s successfully", vendorOrderID, userName)
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
globals.SugarLogger.Infof("PickupGoodsAndUpdateStatus orderID:%s userName:%s error:%v", vendorOrderID, userName, err)
|
||||
return err
|
||||
}()
|
||||
}, jxutils.ComposeUniversalOrderID(vendorOrderID, vendorID))
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *BaseScheduler) AdjustOrder(ctx *jxcontext.Context, order *model.GoodsOrder, removedSkuList []*model.OrderSku, reason string) (err error) {
|
||||
if c.IsReallyCallPlatformAPI {
|
||||
err = partner.GetPurchaseOrderHandlerFromVendorID(order.VendorID).AdjustOrder(ctx, order, removedSkuList, reason)
|
||||
if err == nil {
|
||||
var skuIDs []string
|
||||
for _, v := range removedSkuList {
|
||||
skuIDs = append(skuIDs, utils.Int2Str(v.SkuID))
|
||||
}
|
||||
noticeMsg := fmt.Sprintf("商品skuID列表:%v,订单号(点击进入详情):%v", strings.Join(skuIDs, ","), globals.BackstageHost+"/#/ordermanager/"+order.VendorOrderID)
|
||||
user, err := dao.GetUserByID(dao.GetDB(), "mobile", "18982250714")
|
||||
if user != nil && err == nil {
|
||||
ddmsg.SendUserMessage(dingdingapi.MsgTyeText, user.UserID, "调整单调整商品", noticeMsg)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *BaseScheduler) CancelOrder(ctx *jxcontext.Context, order *model.GoodsOrder, reason string) (err error) {
|
||||
if c.IsReallyCallPlatformAPI {
|
||||
if globals.IsAddEvent {
|
||||
err = cms.AddEventDetail(dao.GetDB(), ctx, model.OperateUpdate, order.StoreID, model.ThingTypeOrder, order.StoreID, order.VendorOrderID, order.StoreName)
|
||||
}
|
||||
err = partner.GetPurchaseOrderHandlerFromVendorID(order.VendorID).CancelOrder(ctx, order, reason+","+ctx.GetUserName())
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *BaseScheduler) AcceptOrRefuseFailedGetOrder(ctx *jxcontext.Context, order *model.GoodsOrder, isAcceptIt bool, reason string) (err error) {
|
||||
if c.IsReallyCallPlatformAPI {
|
||||
err = partner.GetPurchaseOrderHandlerFromVendorID(order.VendorID).AcceptOrRefuseFailedGetOrder(ctx, order, isAcceptIt)
|
||||
}
|
||||
if err == nil {
|
||||
flag := model.OrderFlagAgreeFailedGetGoods
|
||||
if !isAcceptIt {
|
||||
flag = model.OrderFlagRefuseFailedGetGoods
|
||||
}
|
||||
dao.SetOrderFlag(dao.GetDB(), ctx.GetUserName(), order.VendorOrderID, order.VendorID, flag)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *BaseScheduler) CallPMCourier(ctx *jxcontext.Context, order *model.GoodsOrder) (err error) {
|
||||
if c.IsReallyCallPlatformAPI {
|
||||
err = partner.GetPurchaseOrderHandlerFromVendorID(order.VendorID).CallCourier(ctx, order)
|
||||
}
|
||||
if err == nil {
|
||||
dao.SetOrderFlag(dao.GetDB(), ctx.GetUserName(), order.VendorOrderID, order.VendorID, model.OrderFlagMaskCallPMCourier)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *BaseScheduler) ConfirmReceiveGoods(ctx *jxcontext.Context, order *model.GoodsOrder) (err error) {
|
||||
if c.IsReallyCallPlatformAPI {
|
||||
err = partner.GetPurchaseOrderHandlerFromVendorID(order.VendorID).ConfirmReceiveGoods(ctx, order)
|
||||
}
|
||||
if err == nil {
|
||||
dao.SetOrderFlag(dao.GetDB(), ctx.GetUserName(), order.VendorOrderID, order.VendorID, model.OrderFlagMaskFailedDeliver)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *BaseScheduler) AgreeOrRefuseCancel(ctx *jxcontext.Context, order *model.GoodsOrder, isAcceptIt bool, reason string) (err error) {
|
||||
if c.IsReallyCallPlatformAPI {
|
||||
err = partner.GetPurchaseOrderHandlerFromVendorID(order.VendorID).AgreeOrRefuseCancel(ctx, order, isAcceptIt, reason)
|
||||
}
|
||||
if err == nil {
|
||||
flag := model.OrderFlagAgreeUserApplyCancel
|
||||
if !isAcceptIt {
|
||||
flag = model.OrderFlagRefuseUserApplyCancel
|
||||
}
|
||||
dao.SetOrderFlag(dao.GetDB(), ctx.GetUserName(), order.VendorOrderID, order.VendorID, flag)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *BaseScheduler) CancelWaybillByID(ctx *jxcontext.Context, vendorWaybillID string, waybillVendorID int, cancelReasonID int, cancelReason string) (err error) {
|
||||
bill, err := partner.CurOrderManager.LoadWaybill(vendorWaybillID, waybillVendorID)
|
||||
if err == nil {
|
||||
err = c.CancelWaybill(bill, cancelReasonID, cancelReason)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *BaseScheduler) AgreeOrRefuseRefund(ctx *jxcontext.Context, afsOrderID string, vendorID, approveType int, reason string) (err error) {
|
||||
afsOrder, err := partner.CurOrderManager.LoadAfsOrder(afsOrderID, vendorID)
|
||||
if err == nil {
|
||||
if c.IsReallyCallPlatformAPI {
|
||||
err = partner.GetPurchaseOrderHandlerFromVendorID(vendorID).AgreeOrRefuseRefund(ctx, afsOrder, approveType, reason)
|
||||
}
|
||||
if err == nil {
|
||||
flag := model.AfsOrderFlagAgreeUserRefund
|
||||
if approveType == partner.AfsApproveTypeRefused {
|
||||
flag = model.AfsOrderFlagRefuseUserRefund
|
||||
afsOrder.RefuseReason = reason
|
||||
partner.CurOrderManager.UpdateAfsOrderFields(afsOrder, []string{"RefuseReason"})
|
||||
} else {
|
||||
if order, _ := partner.CurOrderManager.LoadOrder(afsOrder.VendorOrderID, afsOrder.VendorID); order != nil {
|
||||
var (
|
||||
db = dao.GetDB()
|
||||
)
|
||||
waybills, _ := dao.GetWaybills(db, order.VendorOrderID)
|
||||
//美团的订单如果是同意全部退款,要取消所有三方运单并停止调度
|
||||
if order.VendorID == model.VendorIDMTWM {
|
||||
var (
|
||||
afsCount, orderCount int
|
||||
)
|
||||
skus, _ := dao.GetAfsOrderSkuInfo(db, order.VendorOrderID, afsOrderID, order.VendorID, false)
|
||||
for _, v := range skus {
|
||||
afsCount += v.Count
|
||||
}
|
||||
for _, v := range order.Skus {
|
||||
orderCount += v.Count
|
||||
}
|
||||
//如果售后退款的商品数等于订单商品数,我就当是全部退款了
|
||||
if afsCount == orderCount {
|
||||
for _, v := range waybills {
|
||||
if err = c.CancelWaybill(v, partner.CancelWaybillReasonOther, partner.CancelWaybillReasonStrActive); err == nil {
|
||||
v.DeliveryFlag |= model.WaybillDeliveryFlagMaskActiveCancel
|
||||
_, err = dao.UpdateEntity(db, v, "Status", "DeliveryFlag")
|
||||
} else {
|
||||
globals.SugarLogger.Debugf("AgreeOrRefuseRefund, cancelwaybill error: %v", err)
|
||||
}
|
||||
}
|
||||
order.DeliveryFlag |= model.OrderDeliveryFlagMaskScheduleDisabled
|
||||
partner.CurOrderManager.UpdateOrderFields(order, []string{"DeliveryFlag"})
|
||||
}
|
||||
}
|
||||
if order.EarningType == model.EarningTypePoints {
|
||||
var (
|
||||
skuMap = make(map[int]*model.OrderSku)
|
||||
diff int64
|
||||
)
|
||||
for _, sku := range order.Skus {
|
||||
skuMap[sku.SkuID] = sku
|
||||
}
|
||||
//京东商城和京西要重新算totalshopmoney等
|
||||
if order.VendorID == model.VendorIDJDShop || order.VendorID == model.VendorIDJX {
|
||||
skus, _ := dao.GetAfsOrderSkuInfo(db, order.VendorOrderID, afsOrderID, order.VendorID, false)
|
||||
for _, v := range skus {
|
||||
if skuMap[v.SkuID] != nil {
|
||||
diff += skuMap[v.SkuID].SalePrice * int64(v.Count)
|
||||
}
|
||||
}
|
||||
order.TotalShopMoney = utils.Float64TwoInt64(float64(float64(order.TotalShopMoney)/jdshopapi.JdsPayPercentage-float64(diff)) * jdshopapi.JdsPayPercentage)
|
||||
if len(waybills) > 0 {
|
||||
jxutils.RefreshOrderEarningPrice3(order, order.OrderPayPercentage, waybills[0])
|
||||
} else {
|
||||
jxutils.RefreshOrderEarningPrice2(order, order.OrderPayPercentage)
|
||||
}
|
||||
dao.UpdateEntity(db, order, "TotalShopMoney", "NewEarningPrice")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
dao.SetAfsOrderFlag(dao.GetDB(), ctx.GetUserName(), afsOrderID, vendorID, flag)
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *BaseScheduler) ConfirmReceivedReturnGoods(ctx *jxcontext.Context, afsOrderID string, vendorID int) (err error) {
|
||||
afsOrder, err := partner.CurOrderManager.LoadAfsOrder(afsOrderID, vendorID)
|
||||
if err == nil {
|
||||
if c.IsReallyCallPlatformAPI {
|
||||
err = partner.GetPurchaseOrderHandlerFromVendorID(vendorID).ConfirmReceivedReturnGoods(ctx, afsOrder)
|
||||
}
|
||||
if err == nil {
|
||||
dao.SetAfsOrderFlag(dao.GetDB(), ctx.GetUserName(), afsOrderID, vendorID, model.AfsOrderFlagMaskReturnGoods)
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *BaseScheduler) PartRefundOrder(ctx *jxcontext.Context, order *model.GoodsOrder, refundSkuList []*model.OrderSku, reason string) (err error) {
|
||||
if c.IsReallyCallPlatformAPI {
|
||||
err = partner.GetPurchaseOrderHandlerFromVendorID(order.VendorID).PartRefundOrder(ctx, order, refundSkuList, reason)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *BaseScheduler) RefundOrder(ctx *jxcontext.Context, order *model.GoodsOrder, reason string) (err error) {
|
||||
if c.IsReallyCallPlatformAPI {
|
||||
err = partner.GetPurchaseOrderHandlerFromVendorID(order.VendorID).RefundOrder(ctx, order, reason)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *BaseScheduler) ConfirmSelfTake(ctx *jxcontext.Context, vendorOrderID string, vendorID int, selfTakeCode string) (err error) {
|
||||
order, err2 := partner.CurOrderManager.LoadOrder(vendorOrderID, vendorID)
|
||||
if err = err2; err == nil {
|
||||
err = c.confirmSelfTake(ctx, order, selfTakeCode)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *BaseScheduler) SetOrderWaybillTip(ctx *jxcontext.Context, vendorOrderID string, vendorID int, tipFee int64) (errCode string, err error) {
|
||||
if order, err := partner.CurOrderManager.LoadOrder(vendorOrderID, vendorID); err == nil {
|
||||
if errCode, err = c.CheckStoreBalanceWithTip(ctx, order, tipFee); err == nil {
|
||||
err = c.SetOrderWaybillTipByOrder(ctx, order, tipFee)
|
||||
}
|
||||
}
|
||||
return errCode, err
|
||||
}
|
||||
|
||||
func (c *BaseScheduler) CheckStoreBalanceWithTip(ctx *jxcontext.Context, order *model.GoodsOrder, tipFee int64) (errCode string, err error) {
|
||||
if order.CreateDeliveryType == model.YES {
|
||||
//加小费只判断余额
|
||||
storeAcct, err := cms.GetStoreAcctBalance(ctx, jxutils.GetSaleStoreIDFromOrder(order))
|
||||
if err != nil {
|
||||
return errCode, fmt.Errorf("获取账户余额失败!")
|
||||
}
|
||||
if tipFee > int64(storeAcct.AccountBalance) {
|
||||
return model.ErrCodeAccountBalanceNotEnough, fmt.Errorf("门店账户余额不足,不能加小费!")
|
||||
}
|
||||
}
|
||||
return errCode, err
|
||||
}
|
||||
|
||||
func isWaybillCanAddTip(waybill *model.Waybill) (isCan bool) {
|
||||
isCan = waybill.Status >= model.WaybillStatusNew && waybill.Status < model.WaybillStatusAccepted && partner.GetWaybillTipUpdater(waybill.WaybillVendorID) != nil
|
||||
return isCan
|
||||
}
|
||||
|
||||
func (c *BaseScheduler) SetOrderWaybillTipByOrder(ctx *jxcontext.Context, order *model.GoodsOrder, tipFee int64) (err error) {
|
||||
roundTipFee := tipFee / 100 * 100
|
||||
if roundTipFee != tipFee {
|
||||
return fmt.Errorf("小费必须是1元的整数倍")
|
||||
}
|
||||
if order.WaybillTipMoney >= tipFee {
|
||||
return fmt.Errorf("当前小费已经是%s元,想要设置%s元", jxutils.IntPrice2StandardString(order.WaybillTipMoney), jxutils.IntPrice2StandardString(tipFee))
|
||||
}
|
||||
globals.SugarLogger.Debugf("SetOrderWaybillTipByOrder, orderID %s, tip:%d", order.VendorOrderID, tipFee)
|
||||
db := dao.GetDB()
|
||||
storeDetail, _ := dao.GetStoreDetail(db, jxutils.GetSaleStoreIDFromOrder(order), order.VendorID, "")
|
||||
flag := false
|
||||
// 如果平台支持设置配送小费,必须要成功设置
|
||||
if handler := partner.GetWaybillTipUpdater(order.VendorID); handler != nil {
|
||||
if err = handler.UpdateWaybillTip(ctx, order.VendorOrgCode, order.VendorStoreID, order.VendorOrderID, "", "", utils.Int2Str(storeDetail.CityCode), tipFee); err != nil {
|
||||
return err
|
||||
} else {
|
||||
//加小费成功扣钱
|
||||
if order.CreateDeliveryType == model.YES {
|
||||
if err = partner.CurStoreAcctManager.InsertStoreAcctExpendAndUpdateStoreAcctBalance(ctx, jxutils.GetSaleStoreIDFromOrder(order), 100, partner.StoreAcctTypeExpendCreateWaybillTip, order.VendorOrderID, 0); err == nil {
|
||||
flag = true
|
||||
}
|
||||
}
|
||||
}
|
||||
} //有可能进else,没加得起平台小费,就要到下面扣账户
|
||||
|
||||
order.WaybillTipMoney = tipFee
|
||||
partner.CurOrderManager.UpdateOrderFields(order, []string{"WaybillTipMoney"})
|
||||
|
||||
waybills, err := dao.GetWayBillByOrderID(db, 0, order.VendorID, 0, order.VendorOrderID)
|
||||
if err == nil {
|
||||
var waybills2 []*model.Waybill
|
||||
for _, v := range waybills {
|
||||
// 必须是三方配送
|
||||
if !model.IsWaybillPlatformOwn(v) && isWaybillCanAddTip(v) {
|
||||
waybills2 = append(waybills2, v)
|
||||
}
|
||||
}
|
||||
if len(waybills2) > 0 {
|
||||
task := tasksch.NewParallelTask("SetOrderWaybillTipByOrder", nil, ctx,
|
||||
func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
|
||||
waybill := batchItemList[0].(*model.Waybill)
|
||||
handler := partner.GetWaybillTipUpdater(waybill.WaybillVendorID)
|
||||
if err == nil {
|
||||
err = handler.UpdateWaybillTip(ctx, waybill.VendorOrgCode, storeDetail.VendorStoreID, waybill.VendorOrderID, waybill.VendorWaybillID, waybill.VendorWaybillID2, utils.Int2Str(storeDetail.CityCode), tipFee)
|
||||
}
|
||||
return nil, err
|
||||
}, waybills2)
|
||||
tasksch.HandleTask(task, nil, false).Run()
|
||||
_, err = task.GetResult(0)
|
||||
if err == nil {
|
||||
//加起了至少只扣一次钱
|
||||
if !flag {
|
||||
//加小费成功扣钱
|
||||
if order.CreateDeliveryType == model.YES {
|
||||
partner.CurStoreAcctManager.InsertStoreAcctExpendAndUpdateStoreAcctBalance(ctx, jxutils.GetSaleStoreIDFromOrder(order), 100, partner.StoreAcctTypeExpendCreateWaybillTip, order.VendorOrderID, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *BaseScheduler) confirmSelfTake(ctx *jxcontext.Context, order *model.GoodsOrder, selfTakeCode string) (err error) {
|
||||
globals.SugarLogger.Debugf("confirmSelfTake orderID:%s, selfTakeCode:%s", order.VendorOrderID, selfTakeCode)
|
||||
vendorID := order.VendorID
|
||||
// if vendorID == model.VendorIDJD || vendorID == model.VendorIDJX {
|
||||
if vendorID != model.VendorIDJX {
|
||||
handler := partner.GetPurchaseOrderHandlerFromVendorID(vendorID)
|
||||
if selfTakeCode == autoSelfTakeCode {
|
||||
if selfTakeCode, err = handler.GetSelfTakeCode(ctx, order); err != nil {
|
||||
return fmt.Errorf("获取订单:%s自提货码失败,原始错误:%s", order.VendorOrderID, err.Error())
|
||||
}
|
||||
if selfTakeCode == "" {
|
||||
return fmt.Errorf("订单:%s 自动提货失败,请手动输入自提码", order.VendorOrderID)
|
||||
}
|
||||
}
|
||||
err = handler.ConfirmSelfTake(ctx, order, selfTakeCode)
|
||||
} else {
|
||||
orderStatus := &model.OrderStatus{
|
||||
VendorOrderID: order.VendorOrderID,
|
||||
VendorID: model.VendorIDJX,
|
||||
OrderType: model.OrderTypeOrder,
|
||||
RefVendorOrderID: order.VendorOrderID,
|
||||
RefVendorID: model.VendorIDJX,
|
||||
VendorStatus: utils.Int2Str(model.OrderStatusFinished),
|
||||
Status: model.OrderStatusFinished,
|
||||
StatusTime: time.Now(),
|
||||
Remark: "自提完成",
|
||||
}
|
||||
jxutils.CallMsgHandlerAsync(func() {
|
||||
err = partner.CurOrderManager.OnOrderStatusChanged("", orderStatus)
|
||||
}, jxutils.ComposeUniversalOrderID(order.VendorOrderID, model.VendorIDJX))
|
||||
}
|
||||
// } else {
|
||||
// err = fmt.Errorf("自提核销不支持%s平台订单", model.VendorChineseNames[order.VendorID])
|
||||
// }
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *BaseScheduler) ConfirmSelfTakeOrders(ctx *jxcontext.Context, vendorIDs []int, orderCreatedAfter, orderCreatedBefore time.Time, isAsync, isContinueWhenError bool) (hint string, err error) {
|
||||
orderList, err := dao.GetPendingFakeOrders(dao.GetDB(), vendorIDs, orderCreatedAfter, orderCreatedBefore)
|
||||
if err == nil {
|
||||
if len(orderList) > 0 {
|
||||
task := tasksch.NewParallelTask(fmt.Sprintf("自动完成内部自提单%v,%s", vendorIDs, utils.Time2Str(orderCreatedAfter)), tasksch.NewParallelConfig().SetIsContinueWhenError(isContinueWhenError), ctx,
|
||||
func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
|
||||
order := batchItemList[0].(*model.GoodsOrder)
|
||||
if order.Status == model.OrderStatusAccepted {
|
||||
if handler := partner.GetPurchaseOrderHandlerFromVendorID(order.VendorID); handler != nil {
|
||||
err = handler.AcceptOrRefuseOrder(order, true, ctx.GetUserName())
|
||||
time.Sleep(2 * time.Second)
|
||||
}
|
||||
}
|
||||
if err == nil {
|
||||
if err = c.confirmSelfTake(ctx, order, autoSelfTakeCode); err == nil {
|
||||
retVal = []int{1}
|
||||
}
|
||||
}
|
||||
return retVal, err
|
||||
}, orderList)
|
||||
tasksch.HandleTask(task, nil, true).Run()
|
||||
if isAsync {
|
||||
hint = task.GetID()
|
||||
} else {
|
||||
resultList, err2 := task.GetResult(0)
|
||||
if err = err2; err == nil {
|
||||
hint = utils.Int2Str(len(resultList))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return hint, err
|
||||
}
|
||||
1755
business/jxcallback/scheduler/defsch/defsch.go
Normal file
1755
business/jxcallback/scheduler/defsch/defsch.go
Normal file
File diff suppressed because it is too large
Load Diff
59
business/jxcallback/scheduler/defsch/defsch_afs.go
Normal file
59
business/jxcallback/scheduler/defsch/defsch_afs.go
Normal file
@@ -0,0 +1,59 @@
|
||||
package defsch
|
||||
|
||||
import (
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
|
||||
push "git.rosy.net.cn/jx-callback/business/jxutils/unipush"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/weixinmsg"
|
||||
"git.rosy.net.cn/jx-callback/business/model"
|
||||
"git.rosy.net.cn/jx-callback/business/msghub"
|
||||
"git.rosy.net.cn/jx-callback/business/partner"
|
||||
"git.rosy.net.cn/jx-callback/globals"
|
||||
)
|
||||
|
||||
var (
|
||||
autoRejectSkuMap = map[int]int{
|
||||
33996: 1,
|
||||
33995: 1,
|
||||
33994: 1,
|
||||
33991: 1,
|
||||
}
|
||||
)
|
||||
|
||||
func (s *DefScheduler) OnAfsOrderNew(order *model.AfsOrder, isPending bool) (err error) {
|
||||
if order.Status == model.AfsOrderStatusWait4Approve {
|
||||
if !isPending {
|
||||
if isAutoRejectAfsOrder(order) {
|
||||
if handler := partner.GetPurchaseOrderHandlerFromVendorID(order.VendorID); handler != nil {
|
||||
if err := handler.AgreeOrRefuseRefund(jxcontext.AdminCtx, order, partner.AfsApproveTypeRefused, "抱歉,蟹券不接受退货或换货"); err != nil {
|
||||
globals.SugarLogger.Debugf("OnAfsOrderNew, orderID:%s, afsOrderID:%s failed with err:%v", order.VendorOrderID, order.AfsOrderID, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
msghub.OnNewWait4ApproveAfsOrder(order)
|
||||
weixinmsg.NotifyAfsOrderStatus(order)
|
||||
push.NotifyAfsOrder(order)
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *DefScheduler) OnAfsOrderStatusChanged(order *model.AfsOrder, status *model.OrderStatus, isPending bool) (err error) {
|
||||
if status.Status == model.AfsOrderStatusWait4ReceiveGoods {
|
||||
if !isPending {
|
||||
msghub.OnKeyAfsOrderStatusChanged(order)
|
||||
weixinmsg.NotifyAfsOrderStatus(order)
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func isAutoRejectAfsOrder(order *model.AfsOrder) (isReject bool) {
|
||||
for _, v := range order.Skus {
|
||||
if autoRejectSkuMap[jxutils.GetSkuIDFromOrderSkuFinancial(v)] == 1 {
|
||||
isReject = true
|
||||
break
|
||||
}
|
||||
}
|
||||
return isReject
|
||||
}
|
||||
403
business/jxcallback/scheduler/defsch/defsch_ext.go
Normal file
403
business/jxcallback/scheduler/defsch/defsch_ext.go
Normal file
@@ -0,0 +1,403 @@
|
||||
package defsch
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"time"
|
||||
|
||||
"git.rosy.net.cn/jx-callback/business/jxstore/cms"
|
||||
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
|
||||
"git.rosy.net.cn/jx-callback/business/model/dao"
|
||||
|
||||
"git.rosy.net.cn/jx-callback/business/jxcallback/scheduler"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
|
||||
"git.rosy.net.cn/jx-callback/business/model"
|
||||
"git.rosy.net.cn/jx-callback/business/partner"
|
||||
"git.rosy.net.cn/jx-callback/globals"
|
||||
)
|
||||
|
||||
func (s *DefScheduler) loadSavedOrderByID(vendorOrderID string, vendorID int, isForceLoad bool) *WatchOrderInfo {
|
||||
return s.loadSavedOrderFromMap(&model.OrderStatus{
|
||||
RefVendorOrderID: vendorOrderID,
|
||||
RefVendorID: vendorID,
|
||||
}, isForceLoad)
|
||||
}
|
||||
|
||||
func (s *DefScheduler) SelfDeliveringAndUpdateStatus(ctx *jxcontext.Context, vendorOrderID string, vendorID int, userName string) (err error) {
|
||||
var order *model.GoodsOrder
|
||||
jxutils.CallMsgHandler(func() {
|
||||
err = func() (err error) {
|
||||
globals.SugarLogger.Infof("SelfDeliveringAndUpdateStatus orderID:%s userName:%s", vendorOrderID, userName)
|
||||
savedOrderInfo := s.loadSavedOrderByID(vendorOrderID, vendorID, true)
|
||||
if savedOrderInfo != nil {
|
||||
order = savedOrderInfo.order
|
||||
if err = s.isPossibleSwitch2SelfDelivery(order); err == nil {
|
||||
globals.SugarLogger.Infof("SelfDeliveringAndUpdateStatus ordersavedOrderInfoID:%s", utils.Format4Output(savedOrderInfo, false))
|
||||
err = s.cancelOtherWaybillsCheckOrderDeliveryFlag(savedOrderInfo, nil, partner.CancelWaybillReasonOther, partner.CancelWaybillReasonStrActive)
|
||||
if err == nil {
|
||||
if model.IsOrderDeliveryByStore(order) {
|
||||
if order.Status < model.OrderStatusDelivering {
|
||||
storeDetail, err2 := dao.GetStoreDetail(dao.GetDB(), order.StoreID, order.VendorID, "")
|
||||
phone := userName
|
||||
if err = err2; err == nil {
|
||||
phone = storeDetail.Tel1
|
||||
}
|
||||
err = s.SelfDeliverDelivering(order, phone)
|
||||
}
|
||||
} else {
|
||||
if order.Status < model.OrderStatusDelivering {
|
||||
err = s.Swtich2SelfDeliver(order, userName)
|
||||
} else if order.VendorID == order.WaybillVendorID { // 状态为配送中,且是购物平台运单,不能转自送了
|
||||
err = scheduler.ErrOrderStatusIsNotSuitable4CurOperation
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if err == nil {
|
||||
order.Status = model.OrderStatusDelivering
|
||||
order.DeliveryFlag |= model.OrderDeliveryFlagMaskScheduleDisabled | model.OrderDeliveryFlagMaskPurcahseDisabled
|
||||
if err = partner.CurOrderManager.UpdateOrderStatusAndDeliveryFlag(order); err == nil {
|
||||
s.stopTimer(savedOrderInfo)
|
||||
globals.SugarLogger.Infof("SelfDeliveringAndUpdateStatus orderID:%s userName:%s successfully", vendorOrderID, userName)
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
order = &model.GoodsOrder{
|
||||
VendorOrderID: vendorOrderID,
|
||||
VendorID: vendorID,
|
||||
}
|
||||
err = scheduler.ErrCanNotFindOrder
|
||||
}
|
||||
globals.SugarLogger.Infof("SelfDeliveringAndUpdateStatus orderID:%s userName:%s error:%v", vendorOrderID, userName, err)
|
||||
return err
|
||||
}()
|
||||
}, jxutils.ComposeUniversalOrderID(vendorOrderID, vendorID))
|
||||
vendorStatus := fmt.Sprintf("%s转商户自送成功", ctx.GetUserName())
|
||||
remark := ""
|
||||
if err != nil {
|
||||
vendorStatus = fmt.Sprintf("%s转商户自送失败", ctx.GetUserName())
|
||||
remark = err.Error()
|
||||
}
|
||||
partner.CurOrderManager.OnOrderMsg(order, vendorStatus, remark)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *DefScheduler) canOrderCreateWaybillNormally(order *model.GoodsOrder, savedOrderInfo *WatchOrderInfo) (err error) {
|
||||
if !(order.LockStatus != model.OrderStatusLocked && order.Status >= model.OrderStatusFinishedPickup && order.Status < model.OrderStatusEndBegin) {
|
||||
err = fmt.Errorf("当前订单%s没有处于拣货完成且没有结束没有锁定的订单才能进行召唤配送操作", order.VendorOrderID)
|
||||
} else if model.IsOrderHaveWaybill(order) {
|
||||
err = fmt.Errorf("当前订单%s已经有了有效的承运人%s了", order.VendorOrderID, jxutils.GetVendorName(order.WaybillVendorID))
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *DefScheduler) isPossibleSwitch2SelfDelivery(order *model.GoodsOrder) (err error) {
|
||||
if model.IsOrderDeliveryByPlatform(order) {
|
||||
if order.Status < model.OrderStatusFinishedPickup {
|
||||
err = fmt.Errorf("拣货完成后才能转自配送")
|
||||
} else if order.Status == model.OrderStatusFinishedPickup {
|
||||
if time.Now().Sub(order.StatusTime) < minMinute2Schedule3rdCarrier*time.Minute {
|
||||
err = fmt.Errorf("非自配送门店转3方配送至少要求拣货完成后%d分钟才能操作", minMinute2Schedule3rdCarrier)
|
||||
}
|
||||
} else if order.Status >= model.OrderStatusDelivering && order.Status < model.OrderStatusEndBegin {
|
||||
if model.IsOrderHaveOwnWaybill(order) {
|
||||
err = fmt.Errorf("%s物流已在配送中,不能转自配送", jxutils.GetVendorName(order.VendorID))
|
||||
}
|
||||
} else if order.Status >= model.OrderStatusEndBegin {
|
||||
err = fmt.Errorf("订单%s已经结束,请刷新状态", order.VendorOrderID)
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *DefScheduler) CreateWaybillOnProviders4SavedOrder(ctx *jxcontext.Context, savedOrderInfo *WatchOrderInfo, courierVendorIDs, excludeCourierVendorIDs []int, forceCreate bool, maxDeliveryFee int64) (bills []*model.Waybill, err error) {
|
||||
order := savedOrderInfo.order
|
||||
if !forceCreate {
|
||||
err = s.canOrderCreateWaybillNormally(order, nil)
|
||||
}
|
||||
if err == nil {
|
||||
if forceCreate {
|
||||
maxDeliveryFee = math.MaxInt64
|
||||
}
|
||||
if bills, err = s.CreateWaybillOnProviders(ctx, order, courierVendorIDs, excludeCourierVendorIDs, maxDeliveryFee, forceCreate); err == nil {
|
||||
if forceCreate {
|
||||
order.DeliveryFlag |= model.OrderDeliveryFlagMaskScheduleDisabled
|
||||
err = partner.CurOrderManager.UpdateOrderStatusAndDeliveryFlag(order)
|
||||
}
|
||||
if err == nil {
|
||||
if forceCreate {
|
||||
s.stopTimer(savedOrderInfo)
|
||||
}
|
||||
//门店发单开始扣钱
|
||||
if order.CreateDeliveryType == model.YES {
|
||||
deliveryFeeMap, _ := s.QueryOrderWaybillFeeInfoEx(ctx, order.VendorOrderID, order.VendorID)
|
||||
isEqual, isZero, _ := partner.CurStoreAcctManager.CheckStoreAcctExpendExist(order.VendorOrderID)
|
||||
if !isZero && !isEqual {
|
||||
var newPrice int64
|
||||
if len(courierVendorIDs) == 1 {
|
||||
courierVendorID := courierVendorIDs[0]
|
||||
if _, ok := deliveryFeeMap[courierVendorID]; ok {
|
||||
newPrice = deliveryFeeMap[courierVendorID].DeliveryFee
|
||||
}
|
||||
} else if len(courierVendorIDs) == 0 {
|
||||
var maxFee int64
|
||||
for _, v := range deliveryFeeMap {
|
||||
if v.DeliveryFee > maxFee {
|
||||
v.DeliveryFee = maxFee
|
||||
}
|
||||
}
|
||||
newPrice = maxFee
|
||||
}
|
||||
expend, lastFee, _ := partner.CurStoreAcctManager.GetStoreAcctExpendLastCreateWayBillFee(order.VendorOrderID)
|
||||
if int(newPrice) > lastFee {
|
||||
partner.CurStoreAcctManager.InsertStoreAcctExpendAndUpdateStoreAcctBalance(ctx, jxutils.GetSaleStoreIDFromOrder(order), int(newPrice)-lastFee, partner.StoreAcctTypeExpendCreateWaybill2ndMore, order.VendorOrderID, expend.ID)
|
||||
}
|
||||
} else {
|
||||
if len(courierVendorIDs) == 1 {
|
||||
courierVendorID := courierVendorIDs[0]
|
||||
if _, ok := deliveryFeeMap[courierVendorID]; ok {
|
||||
partner.CurStoreAcctManager.InsertStoreAcctExpendAndUpdateStoreAcctBalance(ctx, jxutils.GetSaleStoreIDFromOrder(order), int(deliveryFeeMap[courierVendorID].DeliveryFee), partner.StoreAcctTypeExpendCreateWaybillEx, order.VendorOrderID, 0)
|
||||
}
|
||||
} else if len(courierVendorIDs) == 0 {
|
||||
var maxFee int64
|
||||
for _, v := range deliveryFeeMap {
|
||||
if v.DeliveryFee > maxFee {
|
||||
v.DeliveryFee = maxFee
|
||||
}
|
||||
}
|
||||
partner.CurStoreAcctManager.InsertStoreAcctExpendAndUpdateStoreAcctBalance(ctx, jxutils.GetSaleStoreIDFromOrder(order), int(maxFee), partner.StoreAcctTypeExpendCreateWaybillEx, order.VendorOrderID, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
globals.SugarLogger.Debugf("CreateWaybillOnProviders4SavedOrder orderID:%s userName:%s successfully", order.VendorOrderID, ctx.GetUserName())
|
||||
return bills, err
|
||||
}
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
globals.SugarLogger.Debugf("CreateWaybillOnProviders4SavedOrder orderID:%s failed with error:%v", order.VendorOrderID, err)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func (s *DefScheduler) CreateWaybillOnProvidersEx(ctx *jxcontext.Context, vendorOrderID string, vendorID int, courierVendorIDs []int, forceCreate bool, maxDeliveryFee int64) (bills []*model.Waybill, errCode string, err error) {
|
||||
savedOrderInfo := s.loadSavedOrderByID(vendorOrderID, vendorID, true)
|
||||
if savedOrderInfo != nil {
|
||||
order := savedOrderInfo.order
|
||||
//1表示为门店发单,需要验证门店账户余额情况
|
||||
if errCode, err = s.CheckStoreBalance(ctx, order, courierVendorIDs); err != nil {
|
||||
return nil, errCode, err
|
||||
}
|
||||
}
|
||||
jxutils.CallMsgHandler(func() {
|
||||
bills, err = func() (bills []*model.Waybill, err error) {
|
||||
userName := ctx.GetUserName()
|
||||
globals.SugarLogger.Debugf("CreateWaybillOnProvidersEx orderID:%s userName:%s", vendorOrderID, userName)
|
||||
if vendorID == model.VendorIDELM {
|
||||
return nil, fmt.Errorf("不要直接使用饿了么订单号,请使用相应的饿百订单号")
|
||||
}
|
||||
savedOrderInfo := s.loadSavedOrderByID(vendorOrderID, vendorID, true)
|
||||
if savedOrderInfo != nil {
|
||||
order := savedOrderInfo.order
|
||||
if order.DeliveryType == model.OrderDeliveryTypeSelfTake {
|
||||
return nil, fmt.Errorf("订单:%s是自提单", vendorOrderID)
|
||||
}
|
||||
if !forceCreate {
|
||||
err = s.isPossibleSwitch2SelfDelivery(order)
|
||||
}
|
||||
if err == nil {
|
||||
if savedOrderInfo.order.VendorID == model.VendorIDEBAI {
|
||||
courierVendorIDs = []int{model.VendorIDMTPS, model.VendorIDDada}
|
||||
}
|
||||
order := savedOrderInfo.order
|
||||
order.DeliveryFlag = 0
|
||||
err = partner.CurOrderManager.UpdateOrderStatusAndDeliveryFlag(order)
|
||||
if bills, err = s.CreateWaybillOnProviders4SavedOrder(ctx, savedOrderInfo, courierVendorIDs, nil, forceCreate, maxDeliveryFee); err == nil && len(bills) > 0 {
|
||||
partner.CurOrderManager.OnOrderMsg(order, "手动创建运单成功", fmt.Sprintf("%s创建%s平台运单,强发:%t,最高限价:%d", ctx.GetUserName(), model.VendorChineseNames[bills[0].WaybillVendorID], forceCreate, maxDeliveryFee))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
err = scheduler.ErrCanNotFindOrder
|
||||
}
|
||||
globals.SugarLogger.Infof("CreateWaybillOnProvidersEx orderID:%s userName:%s error:%v", vendorOrderID, userName, err)
|
||||
return bills, err
|
||||
}()
|
||||
}, jxutils.ComposeUniversalOrderID(vendorOrderID, vendorID))
|
||||
return bills, errCode, err
|
||||
}
|
||||
|
||||
func (s *DefScheduler) CheckStoreBalance(ctx *jxcontext.Context, order *model.GoodsOrder, courierVendorIDs []int) (errCode string, err error) {
|
||||
if order.CreateDeliveryType == model.YES {
|
||||
//暂时这么认为,len courierVendorIDs 为1表示是老板或者运营从小程序上点的立即发单,因为小程序上是点哪个发哪个
|
||||
//京西后台则是点一下发3个,len courierVendorIDs 是0
|
||||
//如果是小程序上点哪个扣哪个平台的钱
|
||||
//如果是后台,则选最高的那个扣
|
||||
storeAcct, err := cms.GetStoreAcctBalance(ctx, jxutils.GetSaleStoreIDFromOrder(order))
|
||||
deliveryFeeMap, _ := s.QueryOrderWaybillFeeInfoEx(ctx, order.VendorOrderID, order.VendorID)
|
||||
if err != nil {
|
||||
return errCode, fmt.Errorf("获取账户余额失败!")
|
||||
}
|
||||
//1、先判断是不是第一次发:查询库里是否有这个订单的运费支出记录,再查询是否有相同金额并且类型为回退的收入记录(取消运单退回)
|
||||
//前者有,后者无, 表示已经发过了,暂未取消,若这这次的发单金额小于上次的金额则不进行判断也不多扣钱,若大于则扣除‘这次金额-上次金额’的钱,余额不足问题也根据这个判断
|
||||
//前者有,后者有,表示发过并且取消过了,是多次发,直接扣
|
||||
//前者无,表示就是第一次发,直接扣
|
||||
//2、小程序里这次金额用发单平台的金额,后台里这次金额用所有平台最高费用
|
||||
isEqual, isZero, err := partner.CurStoreAcctManager.CheckStoreAcctExpendExist(order.VendorOrderID)
|
||||
globals.SugarLogger.Debugf("CheckStoreBalance orderID :%v, isEqual : %v, isZero: %v", order.VendorOrderID, isEqual, isZero)
|
||||
//表示前者有,后者无
|
||||
if !isZero && !isEqual {
|
||||
var newPrice int64
|
||||
if len(courierVendorIDs) == 1 {
|
||||
courierVendorID := courierVendorIDs[0]
|
||||
if _, ok := deliveryFeeMap[courierVendorID]; ok {
|
||||
newPrice = deliveryFeeMap[courierVendorID].DeliveryFee
|
||||
}
|
||||
} else if len(courierVendorIDs) == 0 {
|
||||
var maxFee int64
|
||||
for _, v := range deliveryFeeMap {
|
||||
if v.DeliveryFee > maxFee {
|
||||
v.DeliveryFee = maxFee
|
||||
}
|
||||
}
|
||||
newPrice = maxFee
|
||||
}
|
||||
_, lastFee, _ := partner.CurStoreAcctManager.GetStoreAcctExpendLastCreateWayBillFee(order.VendorOrderID)
|
||||
if int(newPrice) > lastFee {
|
||||
if storeAcct.AccountBalance < int(newPrice)-lastFee {
|
||||
return model.ErrCodeAccountBalanceNotEnough, fmt.Errorf("门店账户余额小于[%v]元,不能发配送!", jxutils.IntPrice2Standard(newPrice-int64(lastFee)))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if storeAcct.AccountBalance < partner.MinCreateWaybillBalance {
|
||||
return model.ErrCodeAccountBalanceNotEnough, fmt.Errorf("门店账户余额小于[%v]元,不能发配送!", jxutils.IntPrice2Standard(partner.MinCreateWaybillBalance))
|
||||
}
|
||||
if len(courierVendorIDs) == 1 {
|
||||
courierVendorID := courierVendorIDs[0]
|
||||
if _, ok := deliveryFeeMap[courierVendorID]; ok {
|
||||
if deliveryFeeMap[courierVendorID].DeliveryFee > int64(storeAcct.AccountBalance) {
|
||||
return model.ErrCodeAccountBalanceNotEnough, fmt.Errorf("门店账户余额小于[%v]元,不能发配送!", jxutils.IntPrice2Standard(deliveryFeeMap[courierVendorID].DeliveryFee))
|
||||
}
|
||||
}
|
||||
} else if len(courierVendorIDs) == 0 {
|
||||
var maxFee int64
|
||||
for _, v := range deliveryFeeMap {
|
||||
if v.DeliveryFee > maxFee {
|
||||
v.DeliveryFee = maxFee
|
||||
}
|
||||
}
|
||||
if maxFee > int64(storeAcct.AccountBalance) {
|
||||
return model.ErrCodeAccountBalanceNotEnough, fmt.Errorf("门店账户余额小于[%v]元,不能发配送!", jxutils.IntPrice2Standard(maxFee))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return errCode, err
|
||||
}
|
||||
|
||||
// todo 这个函数可以和SelfDeliveringAndUpdateStatus合并
|
||||
func (s *DefScheduler) CancelAll3rdWaybills(ctx *jxcontext.Context, vendorOrderID string, vendorID int, isStopSchedule bool) (err error) {
|
||||
jxutils.CallMsgHandler(func() {
|
||||
err = func() (err error) {
|
||||
globals.SugarLogger.Infof("CancelAll3rdWaybills orderID:%s userName:%s", vendorOrderID, ctx.GetUserName())
|
||||
savedOrderInfo := s.loadSavedOrderByID(vendorOrderID, vendorID, true)
|
||||
if savedOrderInfo != nil {
|
||||
err = s.cancelOtherWaybills(savedOrderInfo, nil, partner.CancelWaybillReasonOther, partner.CancelWaybillReasonStrActive)
|
||||
} else {
|
||||
err = scheduler.ErrCanNotFindOrder
|
||||
}
|
||||
globals.SugarLogger.Infof("CancelAll3rdWaybills orderID:%s userName:%s error:%v", vendorOrderID, ctx.GetUserName(), err)
|
||||
if err == nil && isStopSchedule {
|
||||
order := savedOrderInfo.order
|
||||
order.DeliveryFlag |= model.OrderDeliveryFlagMaskScheduleDisabled
|
||||
if err = partner.CurOrderManager.UpdateOrderStatusAndDeliveryFlag(order); err == nil {
|
||||
s.stopTimer(savedOrderInfo)
|
||||
globals.SugarLogger.Infof("CancelAll3rdWaybills orderID:%s userName:%s successfully", vendorOrderID, ctx.GetUserName())
|
||||
}
|
||||
}
|
||||
return err
|
||||
}()
|
||||
}, jxutils.ComposeUniversalOrderID(vendorOrderID, vendorID))
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *DefScheduler) QueryOrderWaybillFeeInfoEx(ctx *jxcontext.Context, vendorOrderID string, vendorID int) (deliveryFeeMap map[int]*partner.WaybillFeeInfo, err error) {
|
||||
jxutils.CallMsgHandler(func() {
|
||||
deliveryFeeMap, err = func() (deliveryFeeMap map[int]*partner.WaybillFeeInfo, err error) {
|
||||
userName := ctx.GetUserName()
|
||||
globals.SugarLogger.Infof("GetWaybillsInfoEx orderID:%s userName:%s", vendorOrderID, userName)
|
||||
|
||||
db := dao.GetDB()
|
||||
order, err := partner.CurOrderManager.LoadOrder(vendorOrderID, vendorID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if order.DeliveryType == model.OrderDeliveryTypeSelfTake {
|
||||
return nil, fmt.Errorf("订单:%s是自提单", vendorOrderID)
|
||||
}
|
||||
storeCourierList, err := dao.GetStoreCourierList(db, []int{jxutils.GetSaleStoreIDFromOrder(order)}, nil, model.StoreStatusAll, model.StoreAuditStatusOnline)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
waybillList, err := partner.CurOrderManager.GetOrderWaybillInfo(ctx, vendorOrderID, vendorID, true, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
waybillMap := make(map[int]*model.Waybill)
|
||||
for _, bill := range waybillList {
|
||||
waybillMap[bill.WaybillVendorID] = &bill.Waybill
|
||||
}
|
||||
deliveryFeeMap = make(map[int]*partner.WaybillFeeInfo)
|
||||
|
||||
var timeoutSecond int
|
||||
if savedOrderInfo := s.loadSavedOrderByID(vendorOrderID, vendorID, false); savedOrderInfo != nil {
|
||||
timeoutSecond = savedOrderInfo.GetCreateWaybillTimeout()
|
||||
}
|
||||
for _, storeCourier := range storeCourierList {
|
||||
var feeInfo *partner.WaybillFeeInfo
|
||||
if waybillMap[storeCourier.VendorID] != nil {
|
||||
feeInfo = &partner.WaybillFeeInfo{
|
||||
Waybill: waybillMap[storeCourier.VendorID],
|
||||
}
|
||||
} else {
|
||||
if storeCourier.Status != model.StoreStatusOpened {
|
||||
feeInfo = &partner.WaybillFeeInfo{
|
||||
ErrCode: partner.WaybillFeeErrCodeCourierNotOpen,
|
||||
ErrStr: fmt.Sprintf("暂未开通,联系运营"),
|
||||
}
|
||||
} else {
|
||||
if handler := partner.GetDeliveryPlatformFromVendorID(storeCourier.VendorID); handler != nil {
|
||||
if handler.Use4CreateWaybill {
|
||||
if feeInfo, err = handler.Handler.GetWaybillFee(order); err != nil {
|
||||
feeInfo = &partner.WaybillFeeInfo{
|
||||
ErrCode: partner.WaybillFeeErrCodeCourierOthers,
|
||||
ErrStr: err.Error(),
|
||||
}
|
||||
} else {
|
||||
feeInfo.TimeoutSecond = timeoutSecond
|
||||
}
|
||||
} else {
|
||||
feeInfo = &partner.WaybillFeeInfo{
|
||||
ErrCode: partner.WaybillFeeErrCodeCourierForbidden,
|
||||
ErrStr: fmt.Sprintf("内部错误,%d不能用于创建运单", storeCourier.VendorID),
|
||||
}
|
||||
}
|
||||
} else {
|
||||
feeInfo = &partner.WaybillFeeInfo{
|
||||
ErrCode: partner.WaybillFeeErrCodeCourierNotSupported,
|
||||
ErrStr: fmt.Sprintf("内部错误,%d不被支持", storeCourier.VendorID),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
deliveryFeeMap[storeCourier.VendorID] = feeInfo
|
||||
}
|
||||
err = nil
|
||||
return deliveryFeeMap, err
|
||||
}()
|
||||
}, jxutils.ComposeUniversalOrderID(vendorOrderID, vendorID))
|
||||
return deliveryFeeMap, err
|
||||
}
|
||||
47
business/jxcallback/scheduler/scheduler.go
Normal file
47
business/jxcallback/scheduler/scheduler.go
Normal file
@@ -0,0 +1,47 @@
|
||||
package scheduler
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"git.rosy.net.cn/jx-callback/business/model"
|
||||
)
|
||||
|
||||
const (
|
||||
StoreDeliveryTypeCrowdSourcing = 0 //缺省,平台众包配送,可转自送
|
||||
StoreDeliveryTypeByPlatform = 1 //平台专送
|
||||
StoreDeliveryTypeByStore = 2 //完全门店自送,这个表示的意思是平台的门店属性(就是购物平台不负责配送),而不是真正是否是老板自己送
|
||||
)
|
||||
|
||||
const (
|
||||
TimerStatusTypeUnknown = -1
|
||||
TimerStatusTypeOrder = 0
|
||||
TimerStatusTypeWaybill = 1
|
||||
)
|
||||
|
||||
var (
|
||||
CurrentScheduler IScheduler
|
||||
)
|
||||
|
||||
var (
|
||||
ErrOrderStatusIsNotSuitable4CurOperation = errors.New("订单锁定或状态不适合当前操作")
|
||||
ErrOrderStatusAlreadySatisfyCurOperation = errors.New("订单当前状态已满足当前操作")
|
||||
|
||||
ErrCanNotCreateAtLeastOneWaybill = errors.New("一个运单都不能创建")
|
||||
ErrCanNotFindOrder = errors.New("不能找到订单(一般是由于事件错序)")
|
||||
ErrCanNotFindWaybill = errors.New("不能找到运单(一般是由于事件错序)")
|
||||
ErrOrderIsNotSolid = errors.New("订单是临时订单,不完整,不能用于创建运单")
|
||||
ErrDeliverProviderWrong = errors.New("快递商不存在或不能用于创建运单")
|
||||
)
|
||||
|
||||
type IScheduler interface {
|
||||
// 以下是订单
|
||||
OnOrderNew(order *model.GoodsOrder, isPending bool, isAuto bool) (err error)
|
||||
OnOrderStatusChanged(order *model.GoodsOrder, status *model.OrderStatus, isPending bool) (err error)
|
||||
|
||||
// 以下是运单
|
||||
OnWaybillStatusChanged(bill *model.Waybill, isPending bool) (err error)
|
||||
|
||||
// 以下是售后单
|
||||
OnAfsOrderNew(order *model.AfsOrder, isPending bool) (err error)
|
||||
OnAfsOrderStatusChanged(order *model.AfsOrder, status *model.OrderStatus, isPending bool) (err error)
|
||||
}
|
||||
2115
business/jxstore/act/act.go
Normal file
2115
business/jxstore/act/act.go
Normal file
File diff suppressed because it is too large
Load Diff
206
business/jxstore/act/act_test.go
Normal file
206
business/jxstore/act/act_test.go
Normal file
@@ -0,0 +1,206 @@
|
||||
package act
|
||||
|
||||
import (
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
|
||||
"git.rosy.net.cn/jx-callback/business/model/dao"
|
||||
"git.rosy.net.cn/jx-callback/globals/testinit"
|
||||
"testing"
|
||||
|
||||
"git.rosy.net.cn/jx-callback/business/model"
|
||||
|
||||
_ "git.rosy.net.cn/jx-callback/business/partner/purchase/ebai"
|
||||
_ "git.rosy.net.cn/jx-callback/business/partner/purchase/jd"
|
||||
_ "git.rosy.net.cn/jx-callback/business/partner/purchase/mtwm"
|
||||
)
|
||||
|
||||
func init() {
|
||||
testinit.Init()
|
||||
}
|
||||
|
||||
func TestInitDb(t *testing.T) {
|
||||
dao.ExecuteSQL(dao.GetDB(), `
|
||||
DROP TABLE IF EXISTS act, act_map, act_order_rule, act_store_sku, act_store_sku_map;
|
||||
`)
|
||||
}
|
||||
|
||||
func TestCreateActOnAlpha(t *testing.T) {
|
||||
actStoreSkuList := []*ActStoreSkuParam{
|
||||
&ActStoreSkuParam{
|
||||
ActStoreSku: model.ActStoreSku{
|
||||
StoreID: 2,
|
||||
SkuID: 2142,
|
||||
},
|
||||
},
|
||||
&ActStoreSkuParam{
|
||||
ActStoreSku: model.ActStoreSku{
|
||||
StoreID: 2,
|
||||
SkuID: 1162,
|
||||
},
|
||||
},
|
||||
&ActStoreSkuParam{
|
||||
ActStoreSku: model.ActStoreSku{
|
||||
StoreID: 2,
|
||||
SkuID: 1167,
|
||||
},
|
||||
},
|
||||
&ActStoreSkuParam{
|
||||
ActStoreSku: model.ActStoreSku{
|
||||
StoreID: 2,
|
||||
SkuID: 1172,
|
||||
},
|
||||
},
|
||||
}
|
||||
t.Log(utils.Format4Output(actStoreSkuList, true))
|
||||
// actID, err := CreateAct(jxcontext.AdminCtx, &model.Act{
|
||||
// Name: "测试活动",
|
||||
// PricePercentage: 80,
|
||||
// Type: model.ActSkuDirectDown,
|
||||
// }, []int{model.VendorIDJD, model.VendorIDMTWM, model.VendorIDEBAI}, nil, actStoreSkuList, false)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// globals.SugarLogger.Debug(actID)
|
||||
}
|
||||
|
||||
func TestCreateActOnDev(t *testing.T) {
|
||||
//actStoreSkuList := []*ActStoreSkuParam{
|
||||
// &ActStoreSkuParam{
|
||||
// ActStoreSku: model.ActStoreSku{
|
||||
// StoreID: 100884,
|
||||
// SkuID: 22716,
|
||||
// },
|
||||
// },
|
||||
// &ActStoreSkuParam{
|
||||
// ActStoreSku: model.ActStoreSku{
|
||||
// StoreID: 100884,
|
||||
// SkuID: 22717,
|
||||
// },
|
||||
// },
|
||||
// &ActStoreSkuParam{
|
||||
// ActStoreSku: model.ActStoreSku{
|
||||
// StoreID: 100920,
|
||||
// SkuID: 22714,
|
||||
// },
|
||||
// },
|
||||
// &ActStoreSkuParam{
|
||||
// ActStoreSku: model.ActStoreSku{
|
||||
// StoreID: 100920,
|
||||
// SkuID: 22715,
|
||||
// },
|
||||
// },
|
||||
// // &ActStoreSkuParam{
|
||||
// // ActStoreSku: model.ActStoreSku{
|
||||
// // StoreID: 100119,
|
||||
// // SkuID: 26595,
|
||||
// // },
|
||||
// // },
|
||||
//}
|
||||
// t.Log(utils.Format4Output(actStoreSkuList, true))
|
||||
//actID, err := CreateAct(jxcontext.AdminCtx, &model.Act{
|
||||
// Name: "测试活动",
|
||||
// PricePercentage: 80,
|
||||
// Type: model.ActSkuDirectDown,
|
||||
// BeginAt: time.Now().Add(-24 * time.Hour),
|
||||
// EndAt: time.Now().Add(10 * 24 * time.Hour),
|
||||
//}, []int{model.VendorIDJD, model.VendorIDMTWM /*, model.VendorIDEBAI*/}, nil, actStoreSkuList, false)
|
||||
//if err != nil {
|
||||
// t.Fatal(err)
|
||||
//}
|
||||
//globals.SugarLogger.Debug(actID)
|
||||
}
|
||||
|
||||
func TestCancelAct(t *testing.T) {
|
||||
err := CancelAct(jxcontext.AdminCtx, 1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteActStoreBind(t *testing.T) {
|
||||
_, err := DeleteActStoreSkuBind(jxcontext.AdminCtx, dao.GetDB(), 1, []*ActStoreSkuParam{
|
||||
// &ActStoreSkuParam{
|
||||
// ActStoreSku: model.ActStoreSku{
|
||||
// StoreID: 100119,
|
||||
// SkuID: 30828,
|
||||
// },
|
||||
// },
|
||||
// &ActStoreSkuParam{
|
||||
// ActStoreSku: model.ActStoreSku{
|
||||
// StoreID: 100119,
|
||||
// SkuID: 30827,
|
||||
// },
|
||||
// },
|
||||
&ActStoreSkuParam{
|
||||
ActStoreSku: model.ActStoreSku{
|
||||
StoreID: 100884,
|
||||
SkuID: 22716,
|
||||
},
|
||||
},
|
||||
&ActStoreSkuParam{
|
||||
ActStoreSku: model.ActStoreSku{
|
||||
StoreID: 100884,
|
||||
SkuID: 22717,
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddActStoreBind(t *testing.T) {
|
||||
err := AddActStoreSkuBind(jxcontext.AdminCtx, dao.GetDB(), 1, []*ActStoreSkuParam{
|
||||
// &ActStoreSkuParam{
|
||||
// ActStoreSku: model.ActStoreSku{
|
||||
// StoreID: 100119,
|
||||
// SkuID: 30828,
|
||||
// },
|
||||
// },
|
||||
// &ActStoreSkuParam{
|
||||
// ActStoreSku: model.ActStoreSku{
|
||||
// StoreID: 100119,
|
||||
// SkuID: 30827,
|
||||
// },
|
||||
// },
|
||||
&ActStoreSkuParam{
|
||||
ActStoreSku: model.ActStoreSku{
|
||||
StoreID: 100884,
|
||||
SkuID: 22716,
|
||||
},
|
||||
},
|
||||
&ActStoreSkuParam{
|
||||
ActStoreSku: model.ActStoreSku{
|
||||
StoreID: 100884,
|
||||
SkuID: 22717,
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSyncAct(t *testing.T) {
|
||||
_, err := SyncAct(jxcontext.AdminCtx, nil, 1, nil, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestForceUpdateVendorPrice(t *testing.T) {
|
||||
//hint, err := ForceUpdateVendorPrice(jxcontext.AdminCtx, model.VendorIDJD, model.ActSkuDirectDown, []*ActStoreSkuParam{
|
||||
// &ActStoreSkuParam{
|
||||
// ActStoreSku: model.ActStoreSku{
|
||||
// StoreID: 100118,
|
||||
// SkuID: 22509,
|
||||
// ActPrice: 9900,
|
||||
// },
|
||||
// VendorPrice: 19900,
|
||||
// },
|
||||
//}, false)
|
||||
//if err != nil {
|
||||
// t.Fatal(err)
|
||||
//}
|
||||
//t.Log(hint)
|
||||
}
|
||||
130
business/jxstore/cms/authz.go
Normal file
130
business/jxstore/cms/authz.go
Normal file
@@ -0,0 +1,130 @@
|
||||
package cms
|
||||
|
||||
import (
|
||||
"git.rosy.net.cn/jx-callback/business/auth2"
|
||||
"git.rosy.net.cn/jx-callback/business/auth2/authprovider/weixin"
|
||||
"git.rosy.net.cn/jx-callback/business/authz/autils"
|
||||
"git.rosy.net.cn/jx-callback/business/model"
|
||||
"git.rosy.net.cn/jx-callback/business/model/dao"
|
||||
"git.rosy.net.cn/jx-callback/business/model/legacymodel"
|
||||
"git.rosy.net.cn/jx-callback/globals"
|
||||
"git.rosy.net.cn/jx-callback/globals/api2"
|
||||
)
|
||||
|
||||
// todo 是否需要将Store.MarketManPhone与OperatorPhone成角色?
|
||||
|
||||
func TransferLegacyWeixins(mobile string) (err error) {
|
||||
globals.SugarLogger.Debugf("TransferLegacyWeixins mobile:%s", mobile)
|
||||
if !globals.EnableWXAuth2 || globals.DisableWXAuth1 {
|
||||
return nil
|
||||
}
|
||||
remark4Transfer := "transfer"
|
||||
// DELETE t1
|
||||
// FROM auth_bind t1
|
||||
// WHERE t1.remark = 'transfer';
|
||||
|
||||
// DELETE t1
|
||||
// FROM user t1
|
||||
// WHERE t1.remark = 'transfer';
|
||||
|
||||
// TRUNCATE TABLE casbin_rule;
|
||||
|
||||
sql := `
|
||||
SELECT t1.*
|
||||
FROM weixins t1
|
||||
LEFT JOIN user t2 ON t2.mobile = t1.tel
|
||||
LEFT JOIN auth_bind t3 ON t3.auth_id = t1.openid AND t3.type = 'weixinsns'
|
||||
LEFT JOIN auth_bind t4 ON t4.auth_id = t1.openid_mini AND t4.type = 'weixinmini'
|
||||
WHERE`
|
||||
sqlParams := []interface{}{}
|
||||
if mobile != "" {
|
||||
remark4Transfer = "transfer2"
|
||||
sql += " t1.tel = ?"
|
||||
sqlParams = append(sqlParams, mobile)
|
||||
} else {
|
||||
sql += " t2.id IS NULL OR (t1.openid <> '' AND t3.id IS NULL) OR (t1.openid_mini <> '' AND t4.id IS NULL)"
|
||||
}
|
||||
sql += " ORDER BY t1.parentid;"
|
||||
var weixinList []*legacymodel.WeiXins
|
||||
db := dao.GetDB()
|
||||
err = dao.GetRows(db, &weixinList, sql, sqlParams...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
parentMap := make(map[int]*legacymodel.WeiXins)
|
||||
for _, v := range weixinList {
|
||||
if v.ParentID == -1 {
|
||||
parentMap[v.ID] = v
|
||||
} else {
|
||||
if parentMap[v.ParentID] != nil {
|
||||
v.JxStoreID = parentMap[v.ParentID].JxStoreID
|
||||
}
|
||||
}
|
||||
if v.Tel != "" {
|
||||
user := &model.User{
|
||||
UserID2: v.Tel,
|
||||
Name: v.NickName,
|
||||
Mobile: &v.Tel,
|
||||
Type: model.UserTypeStoreBoss,
|
||||
Remark: remark4Transfer,
|
||||
}
|
||||
if user.Name == "" {
|
||||
user.Name = user.GetMobile()
|
||||
}
|
||||
userList, _, err2 := dao.GetUsers(db, 0, "", nil, nil, []string{v.Tel}, 0, -1)
|
||||
if err = err2; err != nil {
|
||||
return err
|
||||
}
|
||||
// globals.SugarLogger.Debug(utils.Format4Output(user, false))
|
||||
if len(userList) == 0 {
|
||||
err = CreateUser(user, v.LastOperator)
|
||||
} else {
|
||||
user = userList[0]
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if v.OpenID != "" {
|
||||
auth2.AddAuthBind(user, &auth2.AuthInfo{
|
||||
AuthBindInfo: &auth2.AuthBindEx{
|
||||
AuthBind: model.AuthBind{
|
||||
Type: weixin.AuthTypeMP,
|
||||
AuthID: v.OpenID,
|
||||
AuthID2: v.OpenIDUnion,
|
||||
Remark: remark4Transfer,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
if v.OpenIDMini != "" {
|
||||
auth2.AddAuthBind(user, &auth2.AuthInfo{
|
||||
AuthBindInfo: &auth2.AuthBindEx{
|
||||
AuthBind: model.AuthBind{
|
||||
Type: weixin.AuthTypeMini,
|
||||
AuthID: v.OpenIDMini,
|
||||
AuthID2: v.OpenIDUnion,
|
||||
Remark: remark4Transfer,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
if v.JxStoreID > 0 { // 运营就不加到门店老板组里了
|
||||
if user.Type&model.UserTypeOperator == 0 {
|
||||
api2.RoleMan.AddRole4User(user.GetID(), autils.NewStoreBossRole(v.JxStoreID))
|
||||
}
|
||||
} else {
|
||||
if mobile != "" {
|
||||
rList, err2 := api2.RoleMan.GetUserRoleList(user.GetID())
|
||||
if err = err2; err == nil {
|
||||
for _, role := range rList {
|
||||
if role.StoreID > 0 {
|
||||
api2.RoleMan.DeleteRole4User(user.GetID(), autils.NewStoreBossRole(role.StoreID))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
30
business/jxstore/cms/authz_test.go
Normal file
30
business/jxstore/cms/authz_test.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package cms
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
"git.rosy.net.cn/jx-callback/globals/api2"
|
||||
|
||||
"git.rosy.net.cn/jx-callback/business/authz/autils"
|
||||
_ "git.rosy.net.cn/jx-callback/business/partner/purchase/ebai"
|
||||
_ "git.rosy.net.cn/jx-callback/business/partner/purchase/elm"
|
||||
_ "git.rosy.net.cn/jx-callback/business/partner/purchase/jd"
|
||||
_ "git.rosy.net.cn/jx-callback/business/partner/purchase/mtwm"
|
||||
_ "git.rosy.net.cn/jx-callback/business/partner/purchase/weimob/wsc"
|
||||
)
|
||||
|
||||
func TestTransferLegacyWeixins(t *testing.T) {
|
||||
err := TransferLegacyWeixins("")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCasbin(t *testing.T) {
|
||||
userList, err := api2.RoleMan.GetRoleUserList(autils.NewStoreBossRole(100324))
|
||||
t.Log(utils.Format4Output(userList, false))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
@@ -2,17 +2,32 @@ package cms
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"git.rosy.net.cn/baseapi/platformapi/mtunionapi"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.rosy.net.cn/jx-callback/business/partner/purchase/ebai"
|
||||
|
||||
"git.rosy.net.cn/baseapi/utils/errlist"
|
||||
|
||||
"git.rosy.net.cn/jx-callback/globals/api2"
|
||||
|
||||
"git.rosy.net.cn/baseapi/platformapi/dingdingapi"
|
||||
|
||||
"git.rosy.net.cn/jx-callback/business/auth2/authprovider/mobile"
|
||||
"git.rosy.net.cn/jx-callback/business/authz/autils"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils"
|
||||
"git.rosy.net.cn/jx-callback/business/partner"
|
||||
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
"git.rosy.net.cn/jx-callback/business/dao"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/ddmsg"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/tasksch"
|
||||
"git.rosy.net.cn/jx-callback/business/model"
|
||||
"git.rosy.net.cn/jx-callback/business/model/dao"
|
||||
"git.rosy.net.cn/jx-callback/globals/api"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -28,8 +43,61 @@ type SysConfigLimit struct {
|
||||
}
|
||||
|
||||
var (
|
||||
serviceInfo map[string]interface{}
|
||||
serviceInfo map[string]interface{}
|
||||
allowUpdatePlaceFieldsMap = map[string]bool{
|
||||
"name": true,
|
||||
"enabled": true,
|
||||
"mtpsPrice": true,
|
||||
}
|
||||
|
||||
regexpMsgContentOpID = regexp.MustCompile(`"openid":"(.*?)"`)
|
||||
|
||||
receiveMsgUsersMap = map[string][]string{
|
||||
SendMsgTypeOpenStoreRequest: []string{
|
||||
"石锋",
|
||||
// "x",
|
||||
// "周扬",
|
||||
},
|
||||
SendMsgTypeSuggestRequest: []string{
|
||||
"石锋",
|
||||
// "x",
|
||||
// "周扬",
|
||||
// "苏尹岚",
|
||||
},
|
||||
}
|
||||
needConfirmRequestMap = map[string]int{
|
||||
SendMsgTypeOpenStoreRequest: 1,
|
||||
}
|
||||
|
||||
SysConfigLimitMap = map[string]*SysConfigLimit{
|
||||
model.ConfigSysEbaiBoxFee: &SysConfigLimit{
|
||||
ValueType: reflect.Int,
|
||||
MinValue: 0,
|
||||
MaxValue: 500,
|
||||
AfterChanged: func() (err error) {
|
||||
_, err = dao.SetStoreMapSyncStatus(dao.GetDB(), []int{model.VendorIDEBAI}, nil, model.SyncFlagModifiedMask)
|
||||
return err
|
||||
},
|
||||
},
|
||||
model.ConfigSysMtwmBoxFee: &SysConfigLimit{
|
||||
ValueType: reflect.Int,
|
||||
MinValue: 0,
|
||||
MaxValue: 500,
|
||||
AfterChanged: func() (err error) {
|
||||
_, err = dao.SetStoreMapSyncStatus(dao.GetDB(), []int{model.VendorIDMTWM}, nil, model.SyncFlagModifiedMask)
|
||||
return err
|
||||
},
|
||||
},
|
||||
model.ConfigSysMtwmSkuBoxFee: &SysConfigLimit{
|
||||
ValueType: reflect.Int,
|
||||
MinValue: 0,
|
||||
MaxValue: 50,
|
||||
AfterChanged: func() (err error) {
|
||||
_, err = dao.SetStoreSkuSyncStatus(dao.GetDB(), model.VendorIDMTWM, nil, nil, model.SyncFlagModifiedMask)
|
||||
return err
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func InitServiceInfo(version string, buildTime time.Time, gitCommit string) {
|
||||
@@ -43,41 +111,49 @@ func InitServiceInfo(version string, buildTime time.Time, gitCommit string) {
|
||||
"buildTime": buildTimeStr,
|
||||
"gitCommit": gitCommit,
|
||||
"metaData": map[string]interface{}{
|
||||
"vendorTypeName": model.VendorTypeName,
|
||||
"vendorName": model.VendorChineseNames,
|
||||
//"vendorImg": model.VendorImg,
|
||||
//"vendorColors": model.VendorColors,
|
||||
"skuNamePrefix": model.SkuNamePrefixNames,
|
||||
"skuNameUnit": model.UnitNames,
|
||||
"skuSpecUnit": model.SpecUnitNames,
|
||||
"skuStatus": model.SkuStatusName,
|
||||
"storeDeliveryRangeType": model.DeliveryRangeTypeName,
|
||||
"storeDeliveryType": model.DeliveryTypeName,
|
||||
"storeStatus": model.StoreStatusName,
|
||||
"categoryType": model.CategoryTypeName,
|
||||
"vendorTypeName": model.VendorTypeName,
|
||||
"vendorName": model.VendorChineseNames,
|
||||
"orderStatus": model.OrderStatusName,
|
||||
"waybillStatus": model.WaybillStatusName,
|
||||
"orderTypeName": model.OrderTypeName,
|
||||
"taskStatusName": tasksch.TaskStatusName,
|
||||
"opRequestTypeName": model.RequestTypeName,
|
||||
"opRequestStatusName": model.RequestStatusName,
|
||||
"storeMsgSendStatusName": model.StoreMsgSendStatusName,
|
||||
"shopChineseNames": model.ShopChineseNames,
|
||||
"printerVendorInfo": model.PrinterVendorInfo,
|
||||
"printerStatusName": partner.PrinterStatusName,
|
||||
"purchaseVendorInfo": model.PurchaseVendorInfo,
|
||||
"afsReasonTypeName": model.AfsReasonTypeName,
|
||||
"afsAppealTypeName": model.AfsAppealTypeName,
|
||||
"actTypeName": model.ActTypeName,
|
||||
"actStatusName": model.ActStatusName,
|
||||
"actCreateTypeName": model.ActCreateTypeName,
|
||||
"storeAuditStatusName": model.StoreAuditStatusName,
|
||||
"configTypeName": model.ConfigTypeName,
|
||||
"autoSaleAt": AutoSaleAtStr,
|
||||
"userTypeName": model.UserTypeName,
|
||||
"storePriceTypeName": model.StorePriceTypeName,
|
||||
"payStatusName": model.PayStatusName,
|
||||
"refundStatusName": model.RefundStatusName,
|
||||
"autoReplyTypeName": model.AutoReplyTypeName,
|
||||
"complaintReasons": model.ComplaintReasons,
|
||||
"supplementType": model.SupplementTypeName,
|
||||
"operateType": model.OperateTypeName,
|
||||
"thingType": model.ThingTypeName,
|
||||
"apiFunctionName": model.ApiFunctionName,
|
||||
"vendorStatus": model.VendorStatus,
|
||||
"unionActTypeNames": map[int]map[int]interface{}{
|
||||
model.VendorIDMTWM: map[int]interface{}{
|
||||
mtunionapi.ActTypeQB: "券包推广",
|
||||
},
|
||||
//model.VendorIDTB: map[int]interface{}{
|
||||
// tbunionapi.TbElmActTypeBDH: "本地化",
|
||||
//},
|
||||
//model.VendorIDPDD: map[int]interface{}{
|
||||
// 1: "进行中的活动",
|
||||
//},
|
||||
model.VendorIDJDShop: map[int]interface{}{
|
||||
2: "进行中",
|
||||
},
|
||||
},
|
||||
"unionOrderStatusName": model.UnionOrderStatusName,
|
||||
"couponsStatus": model.CouponStatusName,
|
||||
"ebaiSupplierID": ebai.EbaiSupplierIDMap,
|
||||
"ebaiSupplierInfo": ebai.EbaiSupplierInfo,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -119,15 +195,213 @@ func GetPlaces(ctx *jxcontext.Context, keyword string, includeDisabled bool, par
|
||||
return places, dao.GetRows(nil, &places, sql, sqlParams)
|
||||
}
|
||||
|
||||
func SendMsg2Somebody(ctx *jxcontext.Context, mobileNum, verifyCode, msgType, msgContent string) (err error) {
|
||||
func UpdatePlaces(ctx *jxcontext.Context, places []map[string]interface{}, userName string) (num int64, err error) {
|
||||
if len(places) == 0 {
|
||||
return 0, ErrMissingInput
|
||||
}
|
||||
updateFields := []string{}
|
||||
for k := range places[0] {
|
||||
if allowUpdatePlaceFieldsMap[k] {
|
||||
updateFields = append(updateFields, k)
|
||||
}
|
||||
}
|
||||
for _, place := range places {
|
||||
if place["code"] == nil {
|
||||
return 0, ErrMissingInput
|
||||
}
|
||||
placeid := &model.Place{}
|
||||
valid := dao.NormalMakeMapByFieldList(place, updateFields, userName)
|
||||
if num, err = dao.UpdateEntityLogically(nil, placeid, valid, userName, utils.Params2Map("Code", place["code"])); err != nil {
|
||||
return num, err
|
||||
}
|
||||
}
|
||||
return num, err
|
||||
}
|
||||
|
||||
func UpdatePlace(ctx *jxcontext.Context, placeCode int, payload map[string]interface{}, userName string) (num int64, err error) {
|
||||
payload["code"] = placeCode
|
||||
return UpdatePlaces(ctx, []map[string]interface{}{payload}, userName)
|
||||
}
|
||||
|
||||
func GetCoordinateDistrictCode(ctx *jxcontext.Context, lng, lat float64) (code int, err error) {
|
||||
return api.AutonaviAPI.GetCoordinateDistrictCode(lng, lat), nil
|
||||
}
|
||||
|
||||
func GetCoordinateCityInfo(ctx *jxcontext.Context, lng, lat float64) (name string, err error) {
|
||||
name, _ = api.AutonaviAPI.GetCoordinateCityInfo(lng, lat)
|
||||
return name, err
|
||||
}
|
||||
|
||||
func SendMsg2Somebody(ctx *jxcontext.Context, mobileNum, verifyCode, msgType, msgContent string) (err error) {
|
||||
if needConfirmRequestMap[msgType] == 1 {
|
||||
if _, err = mobile.AutherObj.VerifySecret(mobileNum, verifyCode); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
db := dao.GetDB()
|
||||
//获取门店信息
|
||||
var (
|
||||
stores []*model.Store
|
||||
authBinds []*model.AuthBind
|
||||
order *model.GoodsOrder
|
||||
storeName string
|
||||
storeID int
|
||||
vendorOrderID string
|
||||
)
|
||||
if mobileNum != "" {
|
||||
sql := `
|
||||
SELECT * FROM store WHERE (tel1 = ? OR tel2 = ?) AND deleted_at = ?
|
||||
`
|
||||
sqlParams := []interface{}{mobileNum, mobileNum, utils.DefaultTimeValue}
|
||||
err = dao.GetRows(db, &stores, sql, sqlParams)
|
||||
if len(stores) > 0 {
|
||||
storeName = stores[0].Name
|
||||
storeID = stores[0].ID
|
||||
}
|
||||
if storeID == 0 {
|
||||
results := regexpMsgContentOpID.FindStringSubmatch(msgContent)
|
||||
if len(results) > 0 {
|
||||
sql3 := `
|
||||
SELECT * FROM auth_bind WHERE auth_id = ? OR auth_id2 = ?
|
||||
`
|
||||
sqlParams3 := []interface{}{results[1], results[1]}
|
||||
err = dao.GetRows(db, &authBinds, sql3, sqlParams3)
|
||||
if len(authBinds) > 0 {
|
||||
user, _ := dao.GetUserByID(db, "user_id", authBinds[0].UserID)
|
||||
mobileNum = *user.Mobile
|
||||
sqlParams4 := []interface{}{mobileNum, mobileNum, utils.DefaultTimeValue}
|
||||
err = dao.GetRows(db, &stores, sql, sqlParams4)
|
||||
if len(stores) > 0 {
|
||||
storeName = stores[0].Name
|
||||
storeID = stores[0].ID
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
results := regexpMsgContentOpID.FindStringSubmatch(msgContent)
|
||||
if len(results) > 0 {
|
||||
sql3 := `
|
||||
SELECT * FROM auth_bind WHERE auth_id = ? OR auth_id2 = ?
|
||||
`
|
||||
sqlParams3 := []interface{}{results[1], results[1]}
|
||||
err = dao.GetRows(db, &authBinds, sql3, sqlParams3)
|
||||
if len(authBinds) > 0 {
|
||||
user, _ := dao.GetUserByID(db, "user_id", authBinds[0].UserID)
|
||||
mobileNum = *user.Mobile
|
||||
sql4 := `
|
||||
SELECT * FROM store WHERE (tel1 = ? OR tel2 = ?) AND deleted_at = ?
|
||||
`
|
||||
sqlParams4 := []interface{}{mobileNum, mobileNum, utils.DefaultTimeValue}
|
||||
err = dao.GetRows(db, &stores, sql4, sqlParams4)
|
||||
if len(stores) > 0 {
|
||||
storeName = stores[0].Name
|
||||
storeID = stores[0].ID
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
sql2 := `
|
||||
SELECT *
|
||||
FROM goods_order
|
||||
WHERE IF(store_id <> '', store_id, jx_store_id) = ?
|
||||
ORDER BY order_created_at DESC
|
||||
LIMIT 1
|
||||
`
|
||||
sqlParams2 := []interface{}{storeID}
|
||||
err = dao.GetRow(db, &order, sql2, sqlParams2)
|
||||
if order != nil {
|
||||
vendorOrderID = order.VendorOrderID
|
||||
}
|
||||
if storeID == 0 {
|
||||
vendorOrderID = ""
|
||||
}
|
||||
msgContent = msgContent + " 门店名称:" + storeName + " 门店ID:" + utils.Int2Str(storeID) + " 最新订单号:" + vendorOrderID
|
||||
for _, v := range receiveMsgUsersMap[msgType] {
|
||||
user, err2 := dao.GetUserByID(db, "name", v)
|
||||
if err2 == nil {
|
||||
ddmsg.SendUserMessage(dingdingapi.MsgTyeText, user.GetID(), msgType, msgContent)
|
||||
} else if err == nil {
|
||||
err = err2
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func checkSysConfig(key, value string) (err error) {
|
||||
if limit := SysConfigLimitMap[key]; limit != nil {
|
||||
if limit.ValueType == reflect.Int {
|
||||
int64Value, err2 := strconv.ParseInt(value, 10, 64)
|
||||
if err = err2; err == nil {
|
||||
if int64Value < limit.MinValue || int64Value > limit.MaxValue {
|
||||
err = fmt.Errorf("配置%s,值%s超范围[%d,%d]", key, value, limit.MinValue, limit.MaxValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func onSysConfigChanged(key, value string) (err error) {
|
||||
if limit := SysConfigLimitMap[key]; limit != nil && limit.AfterChanged != nil {
|
||||
err = limit.AfterChanged()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func checkConfig(opFlag int, configType, key, value string) (err error) {
|
||||
switch configType {
|
||||
case model.ConfigTypePricePack:
|
||||
if value != "" {
|
||||
pricePack := dao.PricePercentagePack2Obj(value)
|
||||
if pricePack == nil {
|
||||
err = fmt.Errorf("配置:%s不合法", value)
|
||||
}
|
||||
}
|
||||
case model.ConfigTypeFreightPack:
|
||||
if value != "" {
|
||||
freightPack := dao.FreightDeductionPack2Obj(value)
|
||||
if freightPack == nil {
|
||||
err = fmt.Errorf("配置:%s不合法", value)
|
||||
} else {
|
||||
var lastStage *model.FreightDeductionItem
|
||||
for _, v := range freightPack.FreightDeductionList {
|
||||
if lastStage != nil && lastStage.DeductFreight > v.DeductFreight {
|
||||
err = fmt.Errorf("免运设置不合理:门槛:%s,免运金额:%s,门槛:%s,免运金额:%s",
|
||||
jxutils.IntPrice2StandardString(int64(lastStage.BeginPrice)), jxutils.IntPrice2StandardString(int64(lastStage.DeductFreight)),
|
||||
jxutils.IntPrice2StandardString(int64(v.BeginPrice)), jxutils.IntPrice2StandardString(int64(v.DeductFreight)))
|
||||
return err
|
||||
}
|
||||
lastStage = v
|
||||
}
|
||||
}
|
||||
}
|
||||
case model.ConfigTypeBank:
|
||||
if value != "" {
|
||||
if model.BankName[key] == "" {
|
||||
err = fmt.Errorf("此银行代码:%s不支持,请联系开发", value)
|
||||
}
|
||||
}
|
||||
case model.ConfigTypeRole:
|
||||
case model.ConfigTypeSys:
|
||||
if opFlag&( /*model.SyncFlagNewMask|*/ model.SyncFlagDeletedMask) != 0 {
|
||||
err = fmt.Errorf("系统参数只支持修改或添加,不支持删除")
|
||||
} else {
|
||||
err = checkSysConfig(key, value)
|
||||
}
|
||||
case model.ConfigTypeJxStore:
|
||||
case model.ConfigTypeCookie:
|
||||
case model.ConfigTypeDiscountCard:
|
||||
default:
|
||||
err = fmt.Errorf("当前只支持配置:%s, 传入的配置类型:%s", utils.Format4Output(model.ConfigTypeName, true), configType)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func AddConfig(ctx *jxcontext.Context, key, configType, value string) (err error) {
|
||||
// if err = checkConfig(model.SyncFlagNewMask, configType, key, value); err != nil {
|
||||
// return err
|
||||
// }
|
||||
if err = checkConfig(model.SyncFlagNewMask, configType, key, value); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
db := dao.GetDB()
|
||||
conf := &model.NewConfig{
|
||||
@@ -138,7 +412,75 @@ func AddConfig(ctx *jxcontext.Context, key, configType, value string) (err error
|
||||
dao.WrapAddIDCULDEntity(conf, ctx.GetUserName())
|
||||
err = dao.CreateEntity(db, conf)
|
||||
if configType == model.ConfigTypeSys && err == nil {
|
||||
// err = onSysConfigChanged(key, value)
|
||||
err = onSysConfigChanged(key, value)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func DeleteConfig(ctx *jxcontext.Context, key, configType string) (err error) {
|
||||
if err = checkConfig(model.SyncFlagDeletedMask, configType, key, ""); err != nil {
|
||||
return err
|
||||
}
|
||||
db := dao.GetDB()
|
||||
switch configType {
|
||||
case model.ConfigTypePricePack:
|
||||
storeMapList, err2 := dao.GetStoresMapList(db, nil, nil, nil, model.StoreStatusAll, model.StoreIsSyncYes, key, "", "")
|
||||
if err = err2; err == nil {
|
||||
var storeInfo []string
|
||||
for _, v := range storeMapList {
|
||||
storeInfo = append(storeInfo, fmt.Sprintf("门店:%d, 平台:%s", v.StoreID, model.VendorChineseNames[v.VendorID]))
|
||||
}
|
||||
if len(storeInfo) > 0 {
|
||||
err = fmt.Errorf("还有门店在使用价格包:%s,门店信息:%s", key, strings.Join(storeInfo, ","))
|
||||
}
|
||||
}
|
||||
case model.ConfigTypeFreightPack:
|
||||
storeMapList, err2 := dao.GetStoresMapList(db, nil, nil, nil, model.StoreStatusAll, model.StoreIsSyncYes, "", "", "")
|
||||
if err = err2; err == nil {
|
||||
var storeInfo []string
|
||||
for _, v := range storeMapList {
|
||||
if v.FreightDeductionPack == key {
|
||||
storeInfo = append(storeInfo, fmt.Sprintf("门店:%d, 平台:%s", v.StoreID, model.VendorChineseNames[v.VendorID]))
|
||||
}
|
||||
}
|
||||
if len(storeInfo) > 0 {
|
||||
err = fmt.Errorf("还有门店在使用价格包:%s,门店信息:%s", key, strings.Join(storeInfo, ","))
|
||||
}
|
||||
}
|
||||
case model.ConfigTypeBank:
|
||||
//todo
|
||||
return fmt.Errorf("暂不支持删除银行")
|
||||
case model.ConfigTypeRole:
|
||||
errList := errlist.New()
|
||||
userIDs, err2 := api2.RoleMan.GetRoleUserList(autils.NewRole(key, 0))
|
||||
if err = err2; err == nil && len(userIDs) > 0 {
|
||||
userList, totalCount, err2 := dao.GetUsers(dao.GetDB(), 0, "", userIDs, nil, nil, 0, -1)
|
||||
if err = err2; err == nil && totalCount > 0 {
|
||||
// todo
|
||||
// err = fmt.Errorf("还有人员在使用角色:%s,人员信息:%s", key, utils.MustMarshal(utils.Struct2Map(userList, "compact")))
|
||||
err = fmt.Errorf("还有人员在使用角色:%s,人员信息:%s", key, utils.Format4Output(userList, false))
|
||||
}
|
||||
}
|
||||
errList.AddErr(err)
|
||||
storeList, err2 := dao.GetStoreList(db, nil, nil, nil, nil, nil, key)
|
||||
if err = err2; err == nil && len(storeList) > 0 {
|
||||
storeIDs := make([]int, len(storeList))
|
||||
for k, v := range storeList {
|
||||
storeIDs[k] = v.ID
|
||||
}
|
||||
err = fmt.Errorf("还有门店在使用角色:%s,门店信息:%s", key, utils.MustMarshal(storeIDs))
|
||||
}
|
||||
errList.AddErr(err)
|
||||
err = errList.GetErrListAsOne()
|
||||
}
|
||||
if err == nil {
|
||||
_, err = dao.DeleteEntityLogically(db, &model.NewConfig{}, nil, ctx.GetUserName(), map[string]interface{}{
|
||||
"Key": key,
|
||||
"Type": configType,
|
||||
})
|
||||
}
|
||||
if configType == model.ConfigTypeSys && err == nil {
|
||||
err = onSysConfigChanged(key, "")
|
||||
}
|
||||
return err
|
||||
}
|
||||
@@ -147,10 +489,13 @@ func UpdateConfig(ctx *jxcontext.Context, key, configType, value string) (hint s
|
||||
if key == "" {
|
||||
return "", fmt.Errorf("修改配置必须给定key")
|
||||
}
|
||||
if err = checkConfig(model.SyncFlagModifiedMask, configType, key, value); err != nil {
|
||||
return "", err
|
||||
}
|
||||
hint = "1"
|
||||
|
||||
db := dao.GetDB()
|
||||
txDB, _ := dao.Begin(db)
|
||||
txDB , _ := dao.Begin(db)
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
dao.Rollback(db, txDB)
|
||||
@@ -170,12 +515,54 @@ func UpdateConfig(ctx *jxcontext.Context, key, configType, value string) (hint s
|
||||
}
|
||||
switch configType {
|
||||
case model.ConfigTypePricePack:
|
||||
|
||||
storeMapList, err := dao.GetStoresMapList(db, nil, nil, nil, model.StoreStatusAll, model.StoreIsSyncYes, key, "", "")
|
||||
if err != nil {
|
||||
dao.Rollback(db, txDB)
|
||||
return "", err
|
||||
}
|
||||
dao.Commit(db, txDB)
|
||||
vendorStoreMap := make(map[int][]int)
|
||||
for _, v := range storeMapList {
|
||||
vendorStoreMap[v.VendorID] = append(vendorStoreMap[v.VendorID], v.StoreID)
|
||||
}
|
||||
for vendorID, storeIDs := range vendorStoreMap {
|
||||
if vendorID != model.VendorIDJX {
|
||||
dao.SetStoreSkuSyncStatus(db, vendorID, storeIDs, nil, model.SyncFlagPriceMask)
|
||||
} else {
|
||||
hint, err = ReCalculateJxPrice(db, ctx, storeIDs)
|
||||
}
|
||||
}
|
||||
case model.ConfigTypeFreightPack:
|
||||
dao.Commit(db, txDB)
|
||||
storeMapList, err := dao.GetStoresMapList(db, nil, nil, nil, model.StoreStatusAll, model.StoreIsSyncYes, "", "", "")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
for _, v := range storeMapList {
|
||||
var storeMapList2 []*model.StoreMap
|
||||
if v.FreightDeductionPack == key {
|
||||
storeMapList2 = append(storeMapList2, v)
|
||||
}
|
||||
if len(storeMapList2) > 0 {
|
||||
task := tasksch.NewParallelTask("同步门店配送免运", tasksch.NewParallelConfig().SetIsContinueWhenError(true), ctx,
|
||||
func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
|
||||
storeMap := batchItemList[0].(*model.StoreMap)
|
||||
_, err = CurVendorSync.SyncStore(ctx, db, storeMap.VendorID, storeMap.StoreID, false, ctx.GetUserName())
|
||||
return retVal, err
|
||||
}, storeMapList2)
|
||||
tasksch.HandleTask(task, nil, true).Run()
|
||||
if len(storeMapList2) < 5 {
|
||||
_, err = task.GetResult(0)
|
||||
} else {
|
||||
hint = task.GetID()
|
||||
}
|
||||
}
|
||||
}
|
||||
default:
|
||||
dao.Commit(db, txDB)
|
||||
}
|
||||
if configType == model.ConfigTypeSys && err == nil {
|
||||
// err = onSysConfigChanged(key, value)
|
||||
err = onSysConfigChanged(key, value)
|
||||
}
|
||||
return hint, err
|
||||
}
|
||||
@@ -183,3 +570,17 @@ func UpdateConfig(ctx *jxcontext.Context, key, configType, value string) (hint s
|
||||
func QueryConfigs(key, configType, keyword string) (configList []*model.NewConfig, err error) {
|
||||
return dao.QueryConfigs(dao.GetDB(), key, configType, keyword)
|
||||
}
|
||||
|
||||
func GetCityBankBranches(ctx *jxcontext.Context, cityCode int, bankCode string) (info map[int]map[string][]string, err error) {
|
||||
list, err := dao.GetCityBankBranches(dao.GetDB(), cityCode, bankCode)
|
||||
if err == nil && len(list) > 0 {
|
||||
info = make(map[int]map[string][]string)
|
||||
for _, v := range list {
|
||||
if info[v.CityCode] == nil {
|
||||
info[v.CityCode] = make(map[string][]string)
|
||||
}
|
||||
info[v.CityCode][v.PayeeBankCode] = append(info[v.CityCode][v.PayeeBankCode], v.PayeeBankBranchName)
|
||||
}
|
||||
}
|
||||
return info, err
|
||||
}
|
||||
|
||||
17
business/jxstore/cms/cms_test.go
Normal file
17
business/jxstore/cms/cms_test.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package cms
|
||||
|
||||
import (
|
||||
"git.rosy.net.cn/jx-callback/globals/api2"
|
||||
"git.rosy.net.cn/jx-callback/globals/testinit"
|
||||
|
||||
_ "git.rosy.net.cn/jx-callback/business/partner/purchase/ebai"
|
||||
_ "git.rosy.net.cn/jx-callback/business/partner/purchase/elm"
|
||||
_ "git.rosy.net.cn/jx-callback/business/partner/purchase/jd"
|
||||
_ "git.rosy.net.cn/jx-callback/business/partner/purchase/mtwm"
|
||||
_ "git.rosy.net.cn/jx-callback/business/partner/purchase/weimob/wsc"
|
||||
)
|
||||
|
||||
func init() {
|
||||
testinit.Init()
|
||||
api2.Init()
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
package cms
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestConn(t *testing.T) {
|
||||
err := DelPrinterSeq(1000, "120220915001012")
|
||||
fmt.Println(err)
|
||||
}
|
||||
@@ -4,10 +4,10 @@ import (
|
||||
"time"
|
||||
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
"git.rosy.net.cn/jx-callback/business/dao"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
|
||||
"git.rosy.net.cn/jx-callback/business/model"
|
||||
"git.rosy.net.cn/jx-callback/business/model/dao"
|
||||
)
|
||||
|
||||
type MessageStatusExt struct {
|
||||
@@ -65,20 +65,20 @@ func GetStoreMessages(ctx *jxcontext.Context, msgIDs, storeIDs, types []int, fro
|
||||
pageSize = jxutils.FormalizePageSize(pageSize)
|
||||
sqlParams = append(sqlParams, pageSize, offset)
|
||||
db := dao.GetDB()
|
||||
txDB, _ := dao.Begin(db)
|
||||
txDB , _ := dao.Begin(db)
|
||||
defer dao.Commit(db, txDB)
|
||||
var msgList []*model.Message
|
||||
// globals.SugarLogger.Debug(sql)
|
||||
if err = dao.GetRowsTx(txDB, &msgList, sql, sqlParams...); err == nil {
|
||||
pagedInfo = &model.PagedInfo{
|
||||
TotalCount: dao.GetLastTotalRowCountTx(txDB),
|
||||
TotalCount: dao.GetLastTotalRowCount2(db, txDB),
|
||||
Data: msgList,
|
||||
}
|
||||
}
|
||||
return pagedInfo, err
|
||||
}
|
||||
|
||||
func GetStoreMessageStatuses(ctx *jxcontext.Context, msgIDs, storeIDs []int, fromReadCount, toReadCount int, fromTime, toTime time.Time, keyword string, offset, pageSize int) (pagedInfo *model.PagedInfo, err error) {
|
||||
func GetStoreMessageStatuses(ctx *jxcontext.Context, msgIDs, storeIDs, confirmStatuss []int, fromReadCount, toReadCount int, fromTime, toTime time.Time, keyword string, offset, pageSize int) (pagedInfo *model.PagedInfo, err error) {
|
||||
sql := `
|
||||
SELECT SQL_CALC_FOUND_ROWS
|
||||
t1.*,
|
||||
@@ -92,6 +92,10 @@ func GetStoreMessageStatuses(ctx *jxcontext.Context, msgIDs, storeIDs []int, fro
|
||||
sql += " AND t1.store_id IN (" + dao.GenQuestionMarks(len(storeIDs)) + ")"
|
||||
sqlParams = append(sqlParams, storeIDs)
|
||||
}
|
||||
if len(confirmStatuss) > 0 {
|
||||
sql += " AND t1.confirm_status IN (" + dao.GenQuestionMarks(len(confirmStatuss)) + ")"
|
||||
sqlParams = append(sqlParams, confirmStatuss)
|
||||
}
|
||||
if len(msgIDs) > 0 {
|
||||
sql += " AND t1.message_id IN (" + dao.GenQuestionMarks(len(msgIDs)) + ")"
|
||||
sqlParams = append(sqlParams, msgIDs)
|
||||
@@ -120,14 +124,14 @@ func GetStoreMessageStatuses(ctx *jxcontext.Context, msgIDs, storeIDs []int, fro
|
||||
sql += " LIMIT ? OFFSET ?"
|
||||
sqlParams = append(sqlParams, jxutils.FormalizePageSize(pageSize), offset)
|
||||
db := dao.GetDB()
|
||||
txDB, _ := dao.Begin(db)
|
||||
txDB , _ := dao.Begin(db)
|
||||
defer dao.Commit(db, txDB)
|
||||
var msgStatusList []*MessageStatusExt
|
||||
// globals.SugarLogger.Debug(sql)
|
||||
// globals.SugarLogger.Debug(utils.Format4Output(sqlParams, false))
|
||||
if err = dao.GetRowsTx(txDB, &msgStatusList, sql, sqlParams...); err == nil {
|
||||
pagedInfo = &model.PagedInfo{
|
||||
TotalCount: dao.GetLastTotalRowCountTx(txDB),
|
||||
TotalCount: dao.GetLastTotalRowCount2(db, txDB),
|
||||
Data: msgStatusList,
|
||||
}
|
||||
}
|
||||
|
||||
1
business/jxstore/cms/message_test.go
Normal file
1
business/jxstore/cms/message_test.go
Normal file
@@ -0,0 +1 @@
|
||||
package cms
|
||||
@@ -1,467 +0,0 @@
|
||||
package cms
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
"git.rosy.net.cn/jx-callback/business/dao"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils"
|
||||
"git.rosy.net.cn/jx-callback/business/model"
|
||||
"net"
|
||||
"regexp"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
sounda = "sounda" //提示音a
|
||||
soundb = "soundb" //提示音b
|
||||
soundc = "soundc" //提示音c
|
||||
soundd = "soundd" //提示音d
|
||||
sounde = "sounde" //报警音e
|
||||
soundf = "soundf" //报警音f
|
||||
soundg = "soundg" //报警音g
|
||||
)
|
||||
|
||||
type PrintInfo struct {
|
||||
PrintNo string
|
||||
AppID int
|
||||
}
|
||||
|
||||
var (
|
||||
regexpMobile = regexp.MustCompile("^1(3[0-9]|4[01456879]|5[0-35-9]|6[2567]|7[0-8]|8[0-9]|9[0-35-9])\\d{8}$")
|
||||
soundMap = map[string]string{
|
||||
"sounda": "sounda",
|
||||
"soundb": "soundb",
|
||||
"soundc": "soundc",
|
||||
"soundd": "soundd",
|
||||
"sounde": "sounde",
|
||||
"soundf": "soundf",
|
||||
"soundg": "soundg",
|
||||
}
|
||||
)
|
||||
|
||||
func AddPrinter(appID int, printers []*model.AddPrinterParam) (err error) {
|
||||
var (
|
||||
db = dao.GetDB()
|
||||
errs []error
|
||||
)
|
||||
if len(printers) > 50 {
|
||||
return fmt.Errorf("一次最多只能绑定50台!")
|
||||
}
|
||||
for _, v := range printers {
|
||||
if printers2, _ := dao.GetPrinters(db, appID, v.PrintNo, 0, 0); len(printers2) > 0 {
|
||||
// 代表打印机已经在小程序注册了,查询打印机授权门店
|
||||
bindStoreList, err := dao.QueryPrintBindStore(v.PrintNo)
|
||||
if err != nil {
|
||||
errs = append(errs, fmt.Errorf("QueryPrintBindStore err : %v ", err))
|
||||
continue
|
||||
}
|
||||
if len(bindStoreList) >= 5 {
|
||||
errs = append(errs, fmt.Errorf("当前打印机绑定门店数据超过五个,无法继续绑定"))
|
||||
continue
|
||||
}
|
||||
have := false
|
||||
userId := ""
|
||||
for _, bsl := range bindStoreList {
|
||||
if bsl.StoreID == v.StoreId {
|
||||
have = true
|
||||
userId = bsl.UserId
|
||||
}
|
||||
}
|
||||
if !have {
|
||||
if err := dao.BindStoreList(printers[0], userId); err != nil {
|
||||
errs = append(errs, fmt.Errorf("BindStoreList err : %v ", err))
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
//验证
|
||||
if err = checkPrinterInfo(v.PrintNo, v.Name, "", "", 0); err != nil {
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
// 检查心跳
|
||||
exits, err := dao.CheckHeard(v.PrintNo)
|
||||
if err != nil {
|
||||
errs = append(errs, fmt.Errorf("CheckHeard err : %v ", err))
|
||||
continue
|
||||
}
|
||||
if !exits {
|
||||
errs = append(errs, fmt.Errorf("打印机未激活,请激活后在绑定"))
|
||||
continue
|
||||
}
|
||||
|
||||
printer := &model.Printer{
|
||||
AppID: appID,
|
||||
PrintNo: v.PrintNo,
|
||||
Name: v.Name,
|
||||
IccID: "",
|
||||
Status: model.PrinterStatusOffline,
|
||||
Sound: "sounda",
|
||||
PrintKey: v.SIM,
|
||||
IsOnline: 0,
|
||||
Volume: 1,
|
||||
FlowFlag: 0,
|
||||
OfflineCount: 0,
|
||||
UserId: "system_user",
|
||||
}
|
||||
|
||||
// 创建打印机
|
||||
if err := InitPrint(printer, v); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if len(errs) > 0 {
|
||||
err = jxutils.BuildErr(errs)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func InitPrint(printer *model.Printer, printParam *model.AddPrinterParam) error {
|
||||
txDb, _ := dao.Begin(dao.GetDB())
|
||||
// 创建打印机
|
||||
dao.WrapAddIDCULDEntity(printer, "")
|
||||
if err := dao.CreateEntityTx(txDb, printer); err != nil {
|
||||
txDb.Rollback()
|
||||
return err
|
||||
}
|
||||
|
||||
// 赋予打印机默认配置
|
||||
day := time.Now()
|
||||
param, err := MarshalJson2String(&model.PrintSetting{
|
||||
CreatedAt: day,
|
||||
UpdatedAt: day,
|
||||
DeletedAt: utils.DefaultTimeValue,
|
||||
PrintNo: printer.PrintNo,
|
||||
CallNameSetting: 64, // 老板
|
||||
BusinessOffLineVoice: 1, // 离线开关
|
||||
BalanceNotEnoughVoice: 1, // 余额不足
|
||||
EveryDayGreetVoice: 1, // 问好
|
||||
BusinessPrintNum: 1, // 商户侧打印次数
|
||||
CustomerPrintNum: 1, // 用户侧打印次数
|
||||
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := dao.CreateEntityTx(txDb, param); err != nil {
|
||||
txDb.Rollback()
|
||||
return err
|
||||
}
|
||||
|
||||
// 初始化打印机账户
|
||||
if err := dao.CreateEntityTx(txDb, &model.PrintBill{
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
PrintNo: param.PrintNo,
|
||||
PrintBalance: 20000,
|
||||
UserId: "system",
|
||||
}); err != nil {
|
||||
txDb.Rollback()
|
||||
return err
|
||||
}
|
||||
|
||||
// 初始化绑定信息
|
||||
if err := dao.BindStoreList(printParam, ""); err != nil {
|
||||
txDb.Rollback()
|
||||
return err
|
||||
}
|
||||
|
||||
defer txDb.Commit()
|
||||
return err
|
||||
}
|
||||
|
||||
// MarshalJson2String 工具类
|
||||
func MarshalJson2String(param *model.PrintSetting) (*model.PrintSetting, error) {
|
||||
// 语音设置
|
||||
voiceSetting := &model.VoiceSettingDetail{
|
||||
WaitOrderVoice: model.SettingOpen,
|
||||
RiderTakeOrderVoice: model.SettingOpen,
|
||||
ApplyUserOrderCancelVoice: model.SettingOpen,
|
||||
ApplyRefundOrderVoice: model.SettingOpen,
|
||||
ApplyRefundGoodsVoice: model.SettingOpen,
|
||||
RefundGoodsVoice: model.SettingOpen,
|
||||
ConfirmGoodsVoice: model.SettingOpen,
|
||||
SuccessGoodsVoice: model.SettingOpen,
|
||||
ConsultingPrint: model.SettingOpen,
|
||||
ReminderVoice: model.SettingOpen,
|
||||
CustomerRejectionVoice: model.SettingOpen,
|
||||
CusterRefundVoice: model.SettingOpen,
|
||||
LoseAuthorization: model.SettingOpen,
|
||||
}
|
||||
customerVoiceSettingByte, err := json.Marshal(voiceSetting)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
param.VoiceSetting = string(customerVoiceSettingByte)
|
||||
|
||||
// 打印设置
|
||||
printSetting := &model.PrintSettingDetail{
|
||||
UserOrderCancel: model.SettingOpen,
|
||||
RefundOrder: model.SettingOpen,
|
||||
BusinessOrderCancel: model.SettingOpen,
|
||||
RiderTakeOrder: model.SettingOpen,
|
||||
CusterRefundPrint: model.SettingOpen,
|
||||
WaitOrderPrint: model.SettingOpen,
|
||||
ApplyUserCancelOrder: model.SettingOpen,
|
||||
ApplyUserRefund: model.SettingOpen,
|
||||
OrderCancelSuccess: model.SettingOpen,
|
||||
}
|
||||
pickingSettingByte, err := json.Marshal(printSetting)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
param.PrintSetting = string(pickingSettingByte)
|
||||
return param, nil
|
||||
}
|
||||
|
||||
func checkPrinterInfo(printNo, name, sound, sim string, volume int) (err error) {
|
||||
if printNo != "" {
|
||||
|
||||
}
|
||||
if sim != "" {
|
||||
//if regexpMobile.FindString(sim) == "" {
|
||||
return fmt.Errorf("暂不支持修改sim卡号码!print_no : %v ", printNo)
|
||||
//}
|
||||
}
|
||||
if volume != 0 {
|
||||
if volume <= 0 || volume > 5 {
|
||||
return fmt.Errorf("请输入正确的音量1-5!print_no : %v ", printNo)
|
||||
}
|
||||
}
|
||||
if sound != "" {
|
||||
if soundMap[sound] == "" {
|
||||
return fmt.Errorf("请输入正确的提示音!print_no : %v ", printNo)
|
||||
}
|
||||
}
|
||||
if name != "" {
|
||||
if len(name) > 255 {
|
||||
return fmt.Errorf("打印机备注不能超过255个字符!print_no : %v ", printNo)
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func DelPrinter(appID int, printNos []string, storeId string) (err error) {
|
||||
var (
|
||||
db = dao.GetDB()
|
||||
errs []error
|
||||
)
|
||||
for _, v := range printNos {
|
||||
if printers, _ := dao.GetPrinters(db, appID, v, 0, 0); len(printers) == 0 {
|
||||
errs = append(errs, fmt.Errorf("该应用下未找到该打印机!print_no : %v ", v))
|
||||
continue
|
||||
} else {
|
||||
if err := dao.DeleteStoreList(printers[0].PrintNo, storeId); err != nil {
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(errs) > 0 {
|
||||
err = jxutils.BuildErr(errs)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func UpdatePrinter(appID int, printNo string, name, sim, sound *string, volume *int) (err error) {
|
||||
var (
|
||||
db = dao.GetDB()
|
||||
fields []string
|
||||
)
|
||||
//看有没有
|
||||
if printers, _ := dao.GetPrinters(db, appID, printNo, 0, 0); len(printers) == 0 {
|
||||
return fmt.Errorf("该应用下未找到该打印机!print_no : %v", printNo)
|
||||
} else {
|
||||
if name != nil {
|
||||
if *name != "" {
|
||||
if len(*name) > 20 {
|
||||
return fmt.Errorf("打印机备注不能超过20个字符!print_no : %v ", printNo)
|
||||
}
|
||||
}
|
||||
if printers[0].Name != *name {
|
||||
printers[0].Name = *name
|
||||
fields = append(fields, "name")
|
||||
}
|
||||
}
|
||||
if sim != nil {
|
||||
if *sim != "" {
|
||||
//if regexpMobile.FindString(*sim) == "" {
|
||||
return fmt.Errorf("暂不支持修改sim卡号码!print_no : %v ", printNo)
|
||||
//}
|
||||
}
|
||||
//if printers[0].SIM != *sim {
|
||||
// printers[0].SIM = *sim
|
||||
// fields = append(fields, "sim")
|
||||
//}
|
||||
}
|
||||
if sound != nil {
|
||||
if *sound != "" {
|
||||
if soundMap[*sound] == "" {
|
||||
return fmt.Errorf("请输入正确的提示音!print_no : %v ", printNo)
|
||||
}
|
||||
}
|
||||
if printers[0].Sound != *sound {
|
||||
printers[0].Sound = *sound
|
||||
fields = append(fields, "sound")
|
||||
}
|
||||
}
|
||||
if volume != nil {
|
||||
if *volume <= 0 || *volume > 5 {
|
||||
return fmt.Errorf("请输入正确的音量 1-5!print_no : %v ", printNo)
|
||||
}
|
||||
if printers[0].Volume != *volume {
|
||||
printers[0].Volume = *volume
|
||||
fields = append(fields, "volume")
|
||||
}
|
||||
}
|
||||
if _, err = dao.UpdateEntity(db, printers[0], fields...); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func DelPrinterSeq(appID int, printNo string) (err error) {
|
||||
var (
|
||||
db = dao.GetDB()
|
||||
)
|
||||
//看有没有
|
||||
if printers, _ := dao.GetPrinters(db, appID, printNo, 0, 0); len(printers) == 0 {
|
||||
return fmt.Errorf("该应用下未找到该打印机!print_no : %v", printNo)
|
||||
} else {
|
||||
printMsgs, _ := dao.GetPrintMsgs(db, printNo, "", model.PrintMsgAll, model.PrintMsgSuccess)
|
||||
for _, v := range printMsgs {
|
||||
v.DeletedAt = time.Now()
|
||||
if _, err = dao.UpdateEntity(db, v, "DeletedAt"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 简历连接
|
||||
conn, err := net.Dial("tcp", "www.jxcs.net:8000")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
clearPrint := fmt.Sprintf(`{"print_no_clear":%s}`, printNo)
|
||||
// 发送数据
|
||||
if _, err := conn.Write([]byte(clearPrint)); err != nil {
|
||||
return err
|
||||
}
|
||||
// 等待数据
|
||||
buf := make([]byte, 1024)
|
||||
n, err := conn.Read(buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if string(buf[:n]) != "ok" {
|
||||
return errors.New("缓存清理失败")
|
||||
}
|
||||
defer conn.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
func DoPrintMsg(appID int, msgID, printNo, content string, orderNo string) (err error) {
|
||||
var (
|
||||
db = dao.GetDB()
|
||||
)
|
||||
//打印机必须绑定在该应用下才能打印
|
||||
if printers, _ := dao.GetPrinters(db, appID, printNo, 0, 0); len(printers) == 0 {
|
||||
return fmt.Errorf("未在该应用下获取到此打印机!print_no %v", printNo)
|
||||
}
|
||||
printMsg := &model.PrintMsg{
|
||||
PrintNo: printNo,
|
||||
Content: content,
|
||||
OrderNo: orderNo,
|
||||
MsgID: msgID,
|
||||
}
|
||||
dao.WrapAddIDCULDEntity(printMsg, "")
|
||||
if err = dao.CreateEntity(db, printMsg); err != nil {
|
||||
return err
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
type GetPrintMsgResult struct {
|
||||
MsgID string `json:"msg_id"` //消息ID
|
||||
PrintNo string `json:"print_no"` //打印机编号
|
||||
OrderNo string `json:"order_no"` //订单序号
|
||||
Content string `json:"content"` //订单内容
|
||||
Status int `json:"status"` //打印状态
|
||||
Comment string `json:"comment"` //失败原因
|
||||
}
|
||||
|
||||
func GetPrintMsg(appID int, msgID string) (printMsg *GetPrintMsgResult, err error) {
|
||||
var (
|
||||
db = dao.GetDB()
|
||||
)
|
||||
if printMsgs, _ := dao.GetPrintMsgs(db, "", msgID, model.PrintMsgAll, model.PrintMsgAll); len(printMsgs) > 0 {
|
||||
result := printMsgs[0]
|
||||
printMsg = &GetPrintMsgResult{
|
||||
MsgID: result.MsgID,
|
||||
PrintNo: result.PrintNo,
|
||||
OrderNo: result.OrderNo,
|
||||
Content: result.Content,
|
||||
Status: result.Status,
|
||||
Comment: result.Comment,
|
||||
}
|
||||
return printMsg, err
|
||||
} else {
|
||||
return printMsg, fmt.Errorf("未找到该消息!msg_id :%v", msgID)
|
||||
}
|
||||
}
|
||||
|
||||
func GetPrinterStatus(appID int, printNo string) (status int, err error) {
|
||||
var (
|
||||
db = dao.GetDB()
|
||||
)
|
||||
//看有没有
|
||||
if printers, _ := dao.GetPrinters(db, appID, printNo, 0, 0); len(printers) == 0 {
|
||||
return status, fmt.Errorf("该应用下未找到该打印机!print_no : %v", printNo)
|
||||
} else {
|
||||
return printers[0].Status + printers[0].IsOnline, nil // 当两个值都唯一时->在线正常
|
||||
server := "print.jxcs.net:8000"
|
||||
tcpAddr, err := net.ResolveTCPAddr("tcp4", server)
|
||||
if err != nil {
|
||||
//os.Exit(1)
|
||||
return status, err
|
||||
}
|
||||
conn, err := net.DialTCP("tcp", nil, tcpAddr)
|
||||
if err != nil {
|
||||
return status, err
|
||||
}
|
||||
status = connHandler(conn, &PrintInfo{
|
||||
PrintNo: printNo,
|
||||
AppID: appID,
|
||||
})
|
||||
return status, nil
|
||||
}
|
||||
}
|
||||
|
||||
func connHandler(c net.Conn, printInfo *PrintInfo) (status int) {
|
||||
defer c.Close()
|
||||
//缓冲
|
||||
buf := make([]byte, 1024)
|
||||
data, _ := json.Marshal(printInfo)
|
||||
//写入数据
|
||||
c.Write(data)
|
||||
//服务器端返回的数据写入buf
|
||||
n, _ := c.Read(buf)
|
||||
status = utils.Str2Int(string(buf[:n]))
|
||||
//服务器端回传的信息
|
||||
fmt.Println("server response:", string(buf[:n]))
|
||||
return status
|
||||
}
|
||||
|
||||
//#region 打印机拼装模板
|
||||
|
||||
// QueryPrinterSetting 查询用户设置
|
||||
func QueryPrinterSetting() {
|
||||
|
||||
}
|
||||
|
||||
//#endregion 打印机
|
||||
3301
business/jxstore/cms/sku.go
Normal file
3301
business/jxstore/cms/sku.go
Normal file
File diff suppressed because it is too large
Load Diff
5089
business/jxstore/cms/store.go
Normal file
5089
business/jxstore/cms/store.go
Normal file
File diff suppressed because it is too large
Load Diff
181
business/jxstore/cms/store_acct.go
Normal file
181
business/jxstore/cms/store_acct.go
Normal file
@@ -0,0 +1,181 @@
|
||||
package cms
|
||||
|
||||
import (
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
|
||||
"git.rosy.net.cn/jx-callback/business/model"
|
||||
"git.rosy.net.cn/jx-callback/business/model/dao"
|
||||
"git.rosy.net.cn/jx-callback/business/partner"
|
||||
"git.rosy.net.cn/jx-callback/globals"
|
||||
)
|
||||
|
||||
type StoreAcctManager struct {
|
||||
}
|
||||
|
||||
var (
|
||||
FixedStoreAcctManager *StoreAcctManager
|
||||
)
|
||||
|
||||
func init() {
|
||||
FixedStoreAcctManager = &StoreAcctManager{}
|
||||
partner.InitStoreAcctManager(FixedStoreAcctManager)
|
||||
}
|
||||
|
||||
func (s *StoreAcctManager) InsertStoreAcctIncome(ctx *jxcontext.Context, storeID, price, acctType int, vendorOrderID string, expendID int) (err error) {
|
||||
var (
|
||||
userID, userName string
|
||||
goodsVendorOrderID string
|
||||
db = dao.GetDB()
|
||||
)
|
||||
if ctx != nil {
|
||||
userID = ctx.GetUserID()
|
||||
userName = ctx.GetUserName()
|
||||
goodsVendorOrderID = vendorOrderID
|
||||
} else {
|
||||
storeOrder := &model.StoreAcctOrder{
|
||||
VendorOrderID: vendorOrderID,
|
||||
}
|
||||
if err = dao.GetEntity(db, storeOrder, "VendorOrderID"); err == nil && storeOrder.ID != 0 {
|
||||
userID = storeOrder.UserID
|
||||
userName = storeOrder.LastOperator
|
||||
goodsVendorOrderID = storeOrder.GoodsVendorOrderID
|
||||
}
|
||||
}
|
||||
storeAcctIncome := &model.StoreAcctIncome{
|
||||
StoreID: storeID,
|
||||
IncomePrice: price,
|
||||
Type: acctType,
|
||||
UserID: userID,
|
||||
VendorOrderID: goodsVendorOrderID,
|
||||
}
|
||||
dao.WrapAddIDCULEntity(storeAcctIncome, userName)
|
||||
if expendID != 0 {
|
||||
storeAcctIncome.ExpID = expendID
|
||||
}
|
||||
err = dao.CreateEntity(db, storeAcctIncome)
|
||||
globals.SugarLogger.Debugf("InsertStoreAcctIncome orderID: [%v] , price :[%v] , type :[%v]", vendorOrderID, price, acctType)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *StoreAcctManager) InsertStoreAcctExpend(ctx *jxcontext.Context, storeID, price, acctType int, vendorOrderID string, expendID int) (err error) {
|
||||
var (
|
||||
userID, userName string
|
||||
db = dao.GetDB()
|
||||
)
|
||||
if ctx != nil {
|
||||
userID = ctx.GetUserID()
|
||||
userName = ctx.GetUserName()
|
||||
} else {
|
||||
storeOrder := &model.StoreAcctOrder{
|
||||
VendorOrderID: vendorOrderID,
|
||||
}
|
||||
if err = dao.GetEntity(db, storeOrder, "VendorOrderID"); err == nil && storeOrder.ID != 0 {
|
||||
userID = storeOrder.UserID
|
||||
userName = storeOrder.LastOperator
|
||||
}
|
||||
}
|
||||
storeAcctExpend := &model.StoreAcctExpend{
|
||||
StoreID: storeID,
|
||||
ExpendPrice: price,
|
||||
Type: acctType,
|
||||
UserID: userID,
|
||||
VendorOrderID: vendorOrderID,
|
||||
}
|
||||
dao.WrapAddIDCULEntity(storeAcctExpend, userName)
|
||||
if expendID != 0 {
|
||||
storeAcctExpend.ExpID = expendID
|
||||
}
|
||||
err = dao.CreateEntity(db, storeAcctExpend)
|
||||
globals.SugarLogger.Debugf("InsertStoreAcctExpend orderID: [%v] , price :[%v] , type :[%v]", vendorOrderID, price, acctType)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *StoreAcctManager) UpdateStoreAcctBalance(ctx *jxcontext.Context, storeID, price int, isIncome bool) (err error) {
|
||||
var (
|
||||
db = dao.GetDB()
|
||||
)
|
||||
globals.SugarLogger.Debugf("UpdateStoreAcctBalance storeID: [%v] , price :[%v] ,", storeID, price)
|
||||
if ctx == nil {
|
||||
ctx = jxcontext.AdminCtx
|
||||
}
|
||||
storeAcct := &model.StoreAcct{
|
||||
StoreID: storeID,
|
||||
}
|
||||
txDB, _ := dao.Begin(db)
|
||||
defer func() {
|
||||
if r := recover(); r != nil || err != nil {
|
||||
dao.Rollback(db, txDB)
|
||||
if r != nil {
|
||||
panic(r)
|
||||
}
|
||||
}
|
||||
}()
|
||||
if err = dao.GetEntity(db, storeAcct, "StoreID"); err != nil && dao.IsNoRowsError(err) {
|
||||
//新增门店账单
|
||||
dao.WrapAddIDCULEntity(storeAcct, ctx.GetUserName())
|
||||
if err = dao.CreateEntityTx(txDB, storeAcct); err != nil {
|
||||
dao.Rollback(db, txDB)
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
globals.SugarLogger.Debugf("UpdateStoreAcctBalance1 storeID: [%v] , balance :[%v] ,", storeID, storeAcct.AccountBalance)
|
||||
if isIncome {
|
||||
storeAcct.AccountBalance += price
|
||||
} else {
|
||||
storeAcct.AccountBalance -= price
|
||||
}
|
||||
if _, err = dao.UpdateEntityTx(txDB, storeAcct, "AccountBalance"); err != nil {
|
||||
dao.Rollback(db, txDB)
|
||||
return err
|
||||
}
|
||||
globals.SugarLogger.Debugf("UpdateStoreAcctBalance2 storeID: [%v] , balance :[%v] ,", storeID, storeAcct.AccountBalance)
|
||||
}
|
||||
dao.Commit(db, txDB)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *StoreAcctManager) InsertStoreAcctExpendAndUpdateStoreAcctBalance(ctx *jxcontext.Context, storeID, price, acctType int, vendorOrderID string, expendID int) (err error) {
|
||||
utils.CallFuncAsync(func() {
|
||||
if err = s.InsertStoreAcctExpend(ctx, storeID, price, acctType, vendorOrderID, expendID); err == nil {
|
||||
s.UpdateStoreAcctBalance(ctx, storeID, price, false)
|
||||
}
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *StoreAcctManager) InsertStoreAcctIncomeAndUpdateStoreAcctBalance(ctx *jxcontext.Context, storeID, price, acctType int, vendorOrderID string, expendID int) (err error) {
|
||||
utils.CallFuncAsync(func() {
|
||||
if err = s.InsertStoreAcctIncome(ctx, storeID, price, acctType, vendorOrderID, expendID); err == nil {
|
||||
s.UpdateStoreAcctBalance(ctx, storeID, price, true)
|
||||
}
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *StoreAcctManager) CheckStoreAcctExpendExist(vendorOrderID string) (isEqual, isZero bool, err error) {
|
||||
var (
|
||||
expends, incomes int
|
||||
db = dao.GetDB()
|
||||
)
|
||||
globals.SugarLogger.Debugf("CheckStoreAcctExpendExist orderID:[%v]", vendorOrderID)
|
||||
expends, err = dao.GetStoreAcctExpendTotal(db, 0, []int{partner.StoreAcctTypeExpendCreateWaybillEx, partner.StoreAcctTypeRealFeeExpend}, vendorOrderID, utils.ZeroTimeValue, utils.ZeroTimeValue)
|
||||
incomes, err = dao.GetStoreAcctIncomeTotal(db, 0, []int{partner.StoreAcctTypeRealFeeIncome, partner.StoreAcctTypeIncomeCancelTemp, partner.StoreAcctTypeIncomeCancelReal}, vendorOrderID, utils.ZeroTimeValue, utils.ZeroTimeValue)
|
||||
if expends != incomes {
|
||||
if expends > incomes {
|
||||
return false, false, err
|
||||
} else {
|
||||
globals.SugarLogger.Debugf("CheckStoreAcctExpendExist 收入大于支出! orderID:[%v]", vendorOrderID)
|
||||
}
|
||||
} else {
|
||||
if expends == 0 && incomes == 0 {
|
||||
return true, true, err
|
||||
} else {
|
||||
return true, false, err
|
||||
}
|
||||
}
|
||||
return false, false, err
|
||||
}
|
||||
|
||||
func (s *StoreAcctManager) GetStoreAcctExpendLastCreateWayBillFee(vendorOrderID string) (expend *dao.GetStoreAcctExpendLastCreateWayBillFeeResult, lastFee int, err error) {
|
||||
return dao.GetStoreAcctExpendLastCreateWayBillFee(dao.GetDB(), vendorOrderID)
|
||||
}
|
||||
6190
business/jxstore/cms/store_sku.go
Normal file
6190
business/jxstore/cms/store_sku.go
Normal file
File diff suppressed because it is too large
Load Diff
1347
business/jxstore/cms/store_sku_check.go
Normal file
1347
business/jxstore/cms/store_sku_check.go
Normal file
File diff suppressed because it is too large
Load Diff
35
business/jxstore/cms/store_test.go
Normal file
35
business/jxstore/cms/store_test.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package cms
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
|
||||
"git.rosy.net.cn/jx-callback/business/model"
|
||||
"git.rosy.net.cn/jx-callback/business/model/dao"
|
||||
)
|
||||
|
||||
func TestGetStoresVendorSnapshot(t *testing.T) {
|
||||
result, err := GetStoresVendorSnapshot(jxcontext.AdminCtx, nil, []int{model.VendorIDEBAI}, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(utils.Format4Output(result, false))
|
||||
t.Log(len(result))
|
||||
}
|
||||
|
||||
func TestSendAlarmVendorSnapshot(t *testing.T) {
|
||||
db := dao.GetDB()
|
||||
prevSnapshotList, _ := dao.GetVendorStoreSnapshot(db, utils.Str2Time("2019-09-19 11:00:00"))
|
||||
curSnapshotList, _ := dao.GetVendorStoreSnapshot(db, utils.Str2Time("2019-09-19 15:00:00"))
|
||||
err := SendAlarmVendorSnapshot(jxcontext.AdminCtx, nil, prevSnapshotList, curSnapshotList)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateVendorStoreStatusBySnapshot(t *testing.T) {
|
||||
db := dao.GetDB()
|
||||
curSnapshotList, _ := dao.GetVendorStoreSnapshot(db, utils.Str2Time("2019-07-30 08:00:00"))
|
||||
updateVendorStoreStatusBySnapshot(db, curSnapshotList)
|
||||
}
|
||||
145
business/jxstore/cms/storeman.go
Normal file
145
business/jxstore/cms/storeman.go
Normal file
@@ -0,0 +1,145 @@
|
||||
package cms
|
||||
|
||||
import (
|
||||
"git.rosy.net.cn/baseapi/platformapi/dingdingapi"
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/ddmsg"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
|
||||
"git.rosy.net.cn/jx-callback/business/model"
|
||||
"git.rosy.net.cn/jx-callback/business/model/dao"
|
||||
"git.rosy.net.cn/jx-callback/business/partner"
|
||||
"git.rosy.net.cn/jx-callback/globals"
|
||||
)
|
||||
|
||||
type StoreManager struct {
|
||||
}
|
||||
|
||||
var (
|
||||
FixedStoreManager *StoreManager
|
||||
)
|
||||
|
||||
func init() {
|
||||
FixedStoreManager = &StoreManager{}
|
||||
partner.InitStoreManager(FixedStoreManager)
|
||||
}
|
||||
|
||||
func (s *StoreManager) OnStoreStatusChanged(vendorStoreID string, vendorID int, storeStatus int) (err error) {
|
||||
//return err
|
||||
globals.SugarLogger.Debugf("OnStoreStatusChanged venvendorStoreID:%s, storeStatus:%d", vendorStoreID, storeStatus)
|
||||
db := dao.GetDB()
|
||||
storeDetail, err := dao.GetStoreDetailByVendorStoreID(db, vendorStoreID, vendorID, "")
|
||||
if err == nil {
|
||||
if storeDetail.IsSync == model.NO || storeDetail.Status == model.StoreStatusDisabled || storeDetail.Status == model.StoreStatusHaveRest {
|
||||
return err
|
||||
}
|
||||
var storeKV, storeMapKV map[string]interface{}
|
||||
if storeStatus == model.StoreStatusOpened {
|
||||
if storeDetail.Status != model.StoreStatusOpened {
|
||||
storeKV = map[string]interface{}{
|
||||
"Status": model.StoreStatusOpened,
|
||||
}
|
||||
content := "您的门店 [" + storeDetail.Name + "],ID:[" + utils.Int2Str(storeDetail.ID) + "],在[" + model.VendorChineseNames[vendorID] + "] 平台上营业状态和京西不一致!平台状态:【营业】,京西状态:【非营业】"
|
||||
if user, err := dao.GetUserByID(db, "mobile", storeDetail.MarketManPhone); err == nil {
|
||||
ddmsg.SendUserMessage(dingdingapi.MsgTyeText, user.UserID, "平台门店状态变化", content)
|
||||
}
|
||||
}
|
||||
if storeDetail.VendorStatus != model.StoreStatusOpened {
|
||||
storeMapKV = map[string]interface{}{
|
||||
"Status": model.StoreStatusOpened,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if storeDetail.Status == model.StoreStatusOpened {
|
||||
if storeDetail.VendorStatus != storeStatus {
|
||||
storeMapKV = map[string]interface{}{
|
||||
"Status": storeStatus,
|
||||
}
|
||||
}
|
||||
content := "您的门店 [" + storeDetail.Name + "],ID:[" + utils.Int2Str(storeDetail.ID) + "],在[" + model.VendorChineseNames[vendorID] + "] 平台上营业状态和京西不一致!平台状态:【非营业】,京西状态:【营业】"
|
||||
if user, err := dao.GetUserByID(db, "mobile", storeDetail.MarketManPhone); err == nil {
|
||||
ddmsg.SendUserMessage(dingdingapi.MsgTyeText, user.UserID, "平台门店状态变化", content)
|
||||
}
|
||||
} else if storeDetail.Status <= storeStatus {
|
||||
if storeDetail.VendorStatus != model.StoreStatusOpened {
|
||||
storeMapKV = map[string]interface{}{
|
||||
"Status": model.StoreStatusOpened,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if err == nil && (storeKV != nil || storeMapKV != nil) {
|
||||
txDB, _ := dao.Begin(db)
|
||||
defer func() {
|
||||
if r := recover(); r != nil || err != nil {
|
||||
dao.Rollback(db, txDB)
|
||||
if r != nil {
|
||||
panic(r)
|
||||
}
|
||||
}
|
||||
}()
|
||||
if storeKV != nil {
|
||||
globals.SugarLogger.Debugf("OnStoreStatusChanged venvendorStoreID:%s, storeKV:%s", vendorStoreID, utils.Format4Output(storeKV, true))
|
||||
store := &model.Store{}
|
||||
store.ID = storeDetail.Store.ID
|
||||
if err = utils.CallFuncLogError(func() error {
|
||||
_, err = dao.UpdateEntityLogically(db, store, storeKV, model.AdminName, nil)
|
||||
return err
|
||||
}, "OnStoreStatusChanged Update Store venvendorStoreID:%s", vendorStoreID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if storeMapKV != nil {
|
||||
globals.SugarLogger.Debugf("OnStoreStatusChanged venvendorStoreID:%s, storeMapKV:%s", vendorStoreID, utils.Format4Output(storeMapKV, true))
|
||||
if err = utils.CallFuncLogError(func() error {
|
||||
_, err = dao.UpdateEntityLogically(db, &model.StoreMap{}, storeMapKV, model.AdminName, map[string]interface{}{
|
||||
model.FieldStoreID: storeDetail.Store.ID,
|
||||
model.FieldVendorID: vendorID,
|
||||
model.FieldDeletedAt: utils.DefaultTimeValue,
|
||||
})
|
||||
return err
|
||||
}, "OnStoreStatusChanged Update StoreMap venvendorStoreID:%s", vendorStoreID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if storeStatus != model.StoreStatusOpened {
|
||||
// 因为storeStatus != model.StoreStatusOpened不会修改京西门店的状态,所以直接用storeDetail.Status是合适的
|
||||
if err = utils.CallFuncLogError(func() error {
|
||||
return dao.FormalizeStoreStatus(db, storeDetail.Store.ID, storeDetail.Status)
|
||||
}, "OnStoreStatusChanged FormalizeStoreStatus venvendorStoreID:%s", vendorStoreID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
dao.Commit(db, txDB)
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *StoreManager) OnCourierStoreStatusChanged(ctx *jxcontext.Context, vendorStoreID string, vendorID int, auditStatus int, message string) (err error) {
|
||||
if vendorStoreID != "" {
|
||||
db := dao.GetDB()
|
||||
_, err2 := dao.GetStoreDetail2(db, 0, vendorStoreID, vendorID)
|
||||
if err = err2; err == nil {
|
||||
status := model.StoreStatusOpened
|
||||
if auditStatus != model.StoreAuditStatusOnline && auditStatus != model.StoreAuditStatusUpdated {
|
||||
status = model.StoreStatusDisabled
|
||||
}
|
||||
comment := ""
|
||||
if auditStatus == model.StoreAuditStatusRejected {
|
||||
comment = message
|
||||
}
|
||||
_, err = dao.UpdateEntityLogically(db, &model.StoreCourierMap{}, map[string]interface{}{
|
||||
model.FieldStatus: status,
|
||||
"AuditStatus": auditStatus,
|
||||
"Comment": comment,
|
||||
}, ctx.GetUserName(), map[string]interface{}{
|
||||
model.FieldVendorStoreID: vendorStoreID,
|
||||
model.FieldVendorID: vendorID,
|
||||
})
|
||||
} else if dao.IsNoRowsError(err) {
|
||||
err = nil
|
||||
}
|
||||
}
|
||||
globals.SugarLogger.Debugf("OnCourierStoreStatusChanged vendorStoreID:%s, auditStatus:%d, err:%v", vendorStoreID, auditStatus, err)
|
||||
return err
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
698
business/jxstore/cms/sync2.go
Normal file
698
business/jxstore/cms/sync2.go
Normal file
@@ -0,0 +1,698 @@
|
||||
package cms
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
"git.rosy.net.cn/baseapi/utils/errlist"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/tasksch"
|
||||
"git.rosy.net.cn/jx-callback/business/model"
|
||||
"git.rosy.net.cn/jx-callback/business/model/dao"
|
||||
"git.rosy.net.cn/jx-callback/business/partner"
|
||||
"git.rosy.net.cn/jx-callback/business/partner/putils"
|
||||
"git.rosy.net.cn/jx-callback/globals"
|
||||
)
|
||||
|
||||
type MultiStoreVendorInfo struct {
|
||||
VendorID int
|
||||
OrgCode string
|
||||
}
|
||||
|
||||
func CombineVendorIDAndOrgCode(vendorID int, orgCode string) string {
|
||||
return fmt.Sprintf("%d-%s", vendorID, orgCode)
|
||||
}
|
||||
|
||||
func getMultiStoreVendorInfoList() (list []*MultiStoreVendorInfo) {
|
||||
vendorIDs := partner.GetMultiStoreVendorIDs()
|
||||
for _, vendorID := range vendorIDs {
|
||||
orgCodeList := partner.CurAPIManager.GetAppOrgCodeList(vendorID)
|
||||
for _, v := range orgCodeList {
|
||||
list = append(list, &MultiStoreVendorInfo{
|
||||
VendorID: vendorID,
|
||||
OrgCode: v,
|
||||
})
|
||||
}
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
func syncCategories(ctx *jxcontext.Context, db *dao.DaoDB, parentTask tasksch.ITask, catList []*dao.SkuStoreCatInfo, isAsync bool) (hint string, err error) {
|
||||
if len(catList) > 0 {
|
||||
// todo 按vendorID orgCode合并操作
|
||||
task := tasksch.NewParallelTask(fmt.Sprintf("同步分类2:%d", len(catList)), tasksch.NewParallelConfig().SetIsContinueWhenError(true), ctx,
|
||||
func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
|
||||
catVendorInfo := batchItemList[0].(*dao.SkuStoreCatInfo)
|
||||
if catVendorInfo.Level != 1 && catVendorInfo.ParentVendorCatID == "" {
|
||||
return nil, fmt.Errorf("%d的父目录%d没有同步", catVendorInfo.ID, catVendorInfo.ParentID)
|
||||
}
|
||||
if catVendorInfo.MapID == 0 {
|
||||
return nil, fmt.Errorf("分类:%d的数据异常", catVendorInfo.ID)
|
||||
}
|
||||
//表示是不用京西分类,要用平台分类
|
||||
if catVendorInfo.IsJxCat == model.YES {
|
||||
if catVendorInfo.VendorCategoryID != 0 {
|
||||
catVendorInfo.Seq = catVendorInfo.VendorCategorySeq
|
||||
catVendorInfo.Name = catVendorInfo.VendorCategoryName
|
||||
}
|
||||
}
|
||||
if multiStoresHandler, ok := partner.GetPurchasePlatformFromVendorID(catVendorInfo.VendorID).(partner.IMultipleStoresHandler); ok {
|
||||
if model.IsSyncStatusDelete(catVendorInfo.CatSyncStatus) { //删除
|
||||
if !dao.IsVendorThingIDEmpty(catVendorInfo.VendorCatID) &&
|
||||
model.IsSyncStatusNeedDelete(catVendorInfo.CatSyncStatus) {
|
||||
err = multiStoresHandler.DeleteCategory2(ctx, catVendorInfo.VendorOrgCode, catVendorInfo.VendorCatID)
|
||||
}
|
||||
} else if model.IsSyncStatusNew(catVendorInfo.CatSyncStatus) { // 新增
|
||||
err = multiStoresHandler.CreateCategory2(ctx, catVendorInfo)
|
||||
} else if model.IsSyncStatusUpdate(catVendorInfo.CatSyncStatus) { // 修改
|
||||
err = multiStoresHandler.UpdateCategory2(ctx, catVendorInfo)
|
||||
}
|
||||
} else {
|
||||
err = fmt.Errorf("平台:%d不合法", catVendorInfo.VendorID)
|
||||
}
|
||||
if err = OnThingSync(ctx, dao.GetDB(), SkuCategoryVendor2ThingMap(catVendorInfo), err); err == nil {
|
||||
retVal = []int{1}
|
||||
}
|
||||
return retVal, err
|
||||
}, catList)
|
||||
tasksch.HandleTask(task, parentTask, len(catList) == 0 || len(catList) > 10).Run()
|
||||
if isAsync {
|
||||
hint = task.GetID()
|
||||
} else {
|
||||
resultList, err2 := task.GetResult(0)
|
||||
if err = err2; err == nil {
|
||||
hint = utils.Int2Str(len(resultList))
|
||||
}
|
||||
}
|
||||
}
|
||||
return hint, err
|
||||
}
|
||||
|
||||
func SyncCategories(ctx *jxcontext.Context, parentTask tasksch.ITask, vendorIDs []int, appOrgCodes []string, catIDs []int, isAsync bool) (hint string, err error) {
|
||||
globals.SugarLogger.Debugf("SyncCategories vendorIDs:%v, appOrgCodes:%v, catIDs:%v", vendorIDs, appOrgCodes, catIDs)
|
||||
db := dao.GetDB()
|
||||
catList, err := dao.GetSkuCategoryWithVendor(db, vendorIDs, appOrgCodes, -1, catIDs, true)
|
||||
if err == nil && len(catList) > 0 {
|
||||
//TODO 同一平台不同账号会有影响needSyncParentIDs,暂不处理
|
||||
var needSyncParentIDs []int
|
||||
for _, cat := range catList {
|
||||
if cat.Level == 2 && cat.ParentVendorCatID == "" && cat.IsExdSpec == model.NO {
|
||||
needSyncParentIDs = append(needSyncParentIDs, cat.ParentID)
|
||||
}
|
||||
}
|
||||
if len(needSyncParentIDs) > 0 {
|
||||
task := tasksch.NewSeqTask(fmt.Sprintf("同步分类1:%v", catIDs), ctx,
|
||||
func(task *tasksch.SeqTask, step int, params ...interface{}) (result interface{}, err error) {
|
||||
switch step {
|
||||
case 0:
|
||||
catList2, err := dao.GetSkuCategoryWithVendor(db, vendorIDs, appOrgCodes, -1, needSyncParentIDs, true)
|
||||
if err == nil {
|
||||
_, err = syncCategories(ctx, db, task, catList2, false)
|
||||
}
|
||||
case 1:
|
||||
catList2, err := dao.GetSkuCategoryWithVendor(db, vendorIDs, appOrgCodes, -1, catIDs, true)
|
||||
if err == nil {
|
||||
_, err = syncCategories(ctx, db, task, catList2, false)
|
||||
}
|
||||
}
|
||||
return result, err
|
||||
}, 2)
|
||||
tasksch.HandleTask(task, parentTask, len(catIDs) == 0 || len(catIDs) > 10).Run()
|
||||
if isAsync {
|
||||
hint = task.GetID()
|
||||
} else {
|
||||
resultList, err2 := task.GetResult(0)
|
||||
if err = err2; err == nil {
|
||||
hint = utils.Int2Str(len(resultList))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
hint, err = syncCategories(ctx, db, parentTask, catList, false)
|
||||
}
|
||||
}
|
||||
return hint, err
|
||||
}
|
||||
|
||||
func SyncSkus(ctx *jxcontext.Context, parentTask tasksch.ITask, vendorIDs []int, appOrgCodes []string, nameIDs, skuIDs []int, isAsync bool) (hint string, err error) {
|
||||
globals.SugarLogger.Debugf("SyncSkus vendorIDs:%v, appOrgCodes:%v, nameIDs:%v, skuIDs:%v", vendorIDs, appOrgCodes, nameIDs, skuIDs)
|
||||
db := dao.GetDB()
|
||||
skuList, err := dao.GetSkusWithVendor(db, vendorIDs, appOrgCodes, nameIDs, skuIDs, true)
|
||||
if err == nil && len(skuList) > 0 {
|
||||
// todo 按vendorID orgCode合并操作
|
||||
task := tasksch.NewParallelTask(fmt.Sprintf("同步商品:%v,%v", nameIDs, skuIDs), tasksch.NewParallelConfig().SetIsContinueWhenError(true), ctx,
|
||||
func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
|
||||
skuVendorInfo := batchItemList[0].(*dao.StoreSkuSyncInfo)
|
||||
var failedList []*partner.StoreSkuInfoWithErr
|
||||
// if skuVendorInfo.VendorCatID == "" {
|
||||
// return nil, fmt.Errorf("商品:%d的商家分类没有同步", skuVendorInfo.SkuID)
|
||||
// }
|
||||
if skuVendorInfo.BindID == 0 {
|
||||
return nil, fmt.Errorf("商品:%d的数据异常", skuVendorInfo.SkuID)
|
||||
}
|
||||
if skuVendorInfo.ExdSkuID != "" {
|
||||
return nil, err
|
||||
}
|
||||
if !model.IsSyncStatusNew(skuVendorInfo.SkuSyncStatus) && skuVendorInfo.VendorSkuID == "" {
|
||||
return nil, err
|
||||
}
|
||||
if skuVendorInfo.SkuVendorMapCatID != "" {
|
||||
skuVendorInfo.VendorVendorCatID = utils.Str2Int64(skuVendorInfo.SkuVendorMapCatID)
|
||||
}
|
||||
skuVendorInfo.SkuName = jxutils.ComposeSkuNameSync(skuVendorInfo.Prefix, skuVendorInfo.Name, skuVendorInfo.Comment, skuVendorInfo.Unit, skuVendorInfo.SpecQuality, skuVendorInfo.SpecUnit, 0, skuVendorInfo.ExPrefix, skuVendorInfo.ExPrefixBegin, skuVendorInfo.ExPrefixEnd, true)
|
||||
skuVendorInfo.SkuNameOrigin = jxutils.ComposeSkuNameOriginal(skuVendorInfo.Prefix, skuVendorInfo.Name, skuVendorInfo.Comment, skuVendorInfo.Unit, skuVendorInfo.SpecQuality, skuVendorInfo.SpecUnit, 0)
|
||||
if skuVendorInfo.ImgWatermark != "" {
|
||||
downLoad, _ := uploadImgStandard(skuVendorInfo.ImgWatermark)
|
||||
skuVendorInfo.ImgMix = jxutils.MixWatermarkImg(downLoad, skuVendorInfo.Img, skuVendorInfo.ExPrefixBegin, skuVendorInfo.ExPrefixEnd)
|
||||
}
|
||||
skuVendorInfo.MergedStatus = jxutils.MergeSkuStatus(skuVendorInfo.Status, skuVendorInfo.NameStatus)
|
||||
if multiStoresHandler, ok := partner.GetPurchasePlatformFromVendorID(skuVendorInfo.VendorID).(partner.IMultipleStoresHandler); ok {
|
||||
if model.IsSyncStatusDelete(skuVendorInfo.SkuSyncStatus) { //删除
|
||||
if !dao.IsVendorThingIDEmpty(skuVendorInfo.VendorSkuID) &&
|
||||
model.IsSyncStatusNeedDelete(skuVendorInfo.SkuSyncStatus) {
|
||||
err = multiStoresHandler.DeleteSku2(ctx, skuVendorInfo.VendorOrgCode, storeSkuSyncInfo2Bare(skuVendorInfo))
|
||||
if err != nil {
|
||||
failedList = putils.GetErrMsg2FailedSingleList(skuVendorInfo, err, 0, model.VendorChineseNames[skuVendorInfo.VendorID], "删除商品")
|
||||
}
|
||||
}
|
||||
} else if model.IsSyncStatusNew(skuVendorInfo.SkuSyncStatus) { // 新增
|
||||
err = multiStoresHandler.CreateSku2(ctx, skuVendorInfo)
|
||||
if err != nil {
|
||||
failedList = putils.GetErrMsg2FailedSingleList(skuVendorInfo, err, 0, model.VendorChineseNames[skuVendorInfo.VendorID], "新增商品")
|
||||
}
|
||||
} else if model.IsSyncStatusUpdate(skuVendorInfo.SkuSyncStatus) { // 修改
|
||||
err = multiStoresHandler.UpdateSku2(ctx, skuVendorInfo)
|
||||
if err != nil {
|
||||
failedList = putils.GetErrMsg2FailedSingleList(skuVendorInfo, err, 0, model.VendorChineseNames[skuVendorInfo.VendorID], "修改商品")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
err = fmt.Errorf("平台:%d不合法", skuVendorInfo.VendorID)
|
||||
}
|
||||
if len(failedList) > 0 {
|
||||
task.AddFailedList(failedList)
|
||||
}
|
||||
if err = OnThingSync(ctx, dao.GetDB(), SkuVendor2ThingMap(skuVendorInfo), err); err == nil {
|
||||
retVal = []int{1}
|
||||
}
|
||||
return retVal, err
|
||||
}, skuList)
|
||||
if isAsync {
|
||||
buildSetFinishHook(task, ctx)
|
||||
}
|
||||
tasksch.HandleTask(task, parentTask, len(skuList) == 0 || len(skuList) > 10).Run()
|
||||
if isAsync {
|
||||
hint = task.GetID()
|
||||
} else {
|
||||
resultList, err2 := task.GetResult(0)
|
||||
if len(task.GetFailedList()) > 0 {
|
||||
err2 = buildErrMsg(task)
|
||||
err2 = makeSyncError(err2)
|
||||
}
|
||||
if err = err2; err == nil {
|
||||
hint = utils.Int2Str(len(resultList))
|
||||
}
|
||||
}
|
||||
}
|
||||
return hint, err
|
||||
}
|
||||
|
||||
func SyncReorderCategories(ctx *jxcontext.Context, parentCatID int, isAsync bool) (hint string, err error) {
|
||||
globals.SugarLogger.Debugf("SyncReorderCategories parentCatID:%d", parentCatID)
|
||||
db := dao.GetDB()
|
||||
hint, err = CurVendorSync.LoopMultiStoresVendors(ctx, db, fmt.Sprintf("分类重排序:%d", parentCatID), isAsync, false,
|
||||
func(t *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
|
||||
vendorInfo := batchItemList[0].(*MultiStoreVendorInfo)
|
||||
multiStoresHandler := CurVendorSync.GetMultiStoreHandler(vendorInfo.VendorID)
|
||||
vendorOrgCode, err := dao.GetVendorOrgCode(db, vendorInfo.VendorID, vendorInfo.OrgCode, "")
|
||||
if multiStoresHandler != nil {
|
||||
var vendorCatIDList []string
|
||||
var parentVendorCatID string
|
||||
//如果用平台分类就走else,用京西分类就还是原来的if
|
||||
if vendorOrgCode[0].IsJxCat == model.NO {
|
||||
catList, err2 := dao.GetSkuCategoryWithVendor(db, []int{vendorInfo.VendorID}, []string{vendorInfo.OrgCode}, parentCatID, nil, false)
|
||||
if err = err2; err == nil {
|
||||
for _, v := range catList {
|
||||
if v.VendorCatID != "" {
|
||||
vendorCatIDList = append(vendorCatIDList, v.VendorCatID)
|
||||
}
|
||||
}
|
||||
}
|
||||
parentVendorCatID = catList[0].ParentVendorCatID
|
||||
} else {
|
||||
vendorCatList, _ := dao.GetVendorCategoryMapExt(db, parentCatID, 0, vendorInfo.VendorID, vendorInfo.OrgCode, 0)
|
||||
for _, v := range vendorCatList {
|
||||
if v.VendorThingID != "" {
|
||||
vendorCatIDList = append(vendorCatIDList, v.VendorThingID)
|
||||
}
|
||||
}
|
||||
thingMaps, _ := dao.GetThingMapList(db, model.ThingTypeCategory, []int{vendorInfo.VendorID}, []int{parentCatID}, []string{vendorInfo.OrgCode})
|
||||
parentVendorCatID = thingMaps[0].VendorThingID
|
||||
}
|
||||
if len(vendorCatIDList) > 0 {
|
||||
if err = multiStoresHandler.ReorderCategories2(ctx, vendorInfo.OrgCode, parentVendorCatID, vendorCatIDList); err == nil {
|
||||
retVal = []int{len(vendorCatIDList)}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
err = fmt.Errorf("非法平台:%d", vendorInfo.VendorID)
|
||||
}
|
||||
return retVal, err
|
||||
})
|
||||
return hint, err
|
||||
}
|
||||
|
||||
func SyncReorderCategories2(ctx *jxcontext.Context, parentCatID, vendorID int, vendorOrgCode string) (err error) {
|
||||
globals.SugarLogger.Debugf("SyncReorderCategories parentCatID:%d", parentCatID)
|
||||
db := dao.GetDB()
|
||||
multiStoresHandler := CurVendorSync.GetMultiStoreHandler(vendorID)
|
||||
if multiStoresHandler != nil {
|
||||
var vendorCatIDList []string
|
||||
var parentVendorCatID string
|
||||
vendorCatList, _ := dao.GetVendorCategoryMapExt(db, parentCatID, 0, vendorID, vendorOrgCode, 0)
|
||||
for _, v := range vendorCatList {
|
||||
if v.VendorThingID != "" {
|
||||
vendorCatIDList = append(vendorCatIDList, v.VendorThingID)
|
||||
}
|
||||
}
|
||||
thingMaps, _ := dao.GetThingMapList(db, model.ThingTypeCategory, []int{vendorID}, []int{parentCatID}, []string{vendorOrgCode})
|
||||
if len(thingMaps) > 0 {
|
||||
parentVendorCatID = thingMaps[0].VendorThingID
|
||||
}
|
||||
|
||||
if len(vendorCatIDList) > 0 {
|
||||
err = multiStoresHandler.ReorderCategories2(ctx, vendorOrgCode, parentVendorCatID, vendorCatIDList)
|
||||
}
|
||||
} else {
|
||||
err = fmt.Errorf("非法平台:%d", vendorID)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func getThingMap(db *dao.DaoDB, thingMap *model.ThingMap) (err error) {
|
||||
return dao.GetEntity(db, thingMap, "ThingID", "ThingType", "VendorID", "VendorOrgCode", model.FieldDeletedAt)
|
||||
}
|
||||
|
||||
func OnCreateThing(ctx *jxcontext.Context, db *dao.DaoDB, vendorInfoList []*model.VendorOrgCode, thingID int64, thingType, syncFlag int8, vendorFlag bool) (err error) {
|
||||
if thingType == model.ThingTypeSkuName {
|
||||
return nil
|
||||
}
|
||||
if len(vendorInfoList) == 0 {
|
||||
vendorInfoList, err = dao.GetVendorOrgCode(db, model.VendorIDJD, "", model.VendorOrgTypePlatform)
|
||||
}
|
||||
errList := errlist.New()
|
||||
for _, v := range vendorInfoList {
|
||||
thingMap := &model.ThingMap{
|
||||
ThingID: thingID,
|
||||
ThingType: thingType,
|
||||
VendorID: v.VendorID,
|
||||
VendorOrgCode: v.VendorOrgCode,
|
||||
}
|
||||
if thingType == model.ThingTypeCategory && !vendorFlag {
|
||||
if v.IsJxCat == model.YES {
|
||||
syncFlag = 0
|
||||
}
|
||||
}
|
||||
thingMap.SyncStatus = syncFlag
|
||||
dao.WrapAddIDCULDEntity(thingMap, ctx.GetUserName())
|
||||
if err2 := dao.CreateEntity(db, thingMap); err2 != nil {
|
||||
if dao.IsDuplicateError(err2) {
|
||||
errList.AddErr(onUpdateThing(ctx, db, vendorInfoList, thingID, thingType, syncFlag))
|
||||
} else {
|
||||
errList.AddErr(err2)
|
||||
}
|
||||
}
|
||||
updateThingMapEntity(db, thingMap)
|
||||
}
|
||||
err = errList.GetErrListAsOne()
|
||||
return err
|
||||
}
|
||||
|
||||
func onUpdateThing(ctx *jxcontext.Context, db *dao.DaoDB, vendorInfoList []*model.VendorOrgCode, thingID int64, thingType int8, syncStatus int8) (err error) {
|
||||
if thingType == model.ThingTypeSkuName {
|
||||
return nil
|
||||
}
|
||||
if len(vendorInfoList) == 0 {
|
||||
vendorInfoList, err = dao.GetVendorOrgCode(db, model.VendorIDJD, "", model.VendorOrgTypePlatform)
|
||||
}
|
||||
errList := errlist.New()
|
||||
for _, v := range vendorInfoList {
|
||||
thingMap := &model.ThingMap{
|
||||
ThingID: thingID,
|
||||
ThingType: thingType,
|
||||
VendorID: v.VendorID,
|
||||
VendorOrgCode: v.VendorOrgCode,
|
||||
}
|
||||
thingMap.DeletedAt = utils.DefaultTimeValue
|
||||
if err2 := getThingMap(db, thingMap); err2 == nil {
|
||||
thingMap.SyncStatus |= syncStatus
|
||||
thingMap.LastOperator = ctx.GetUserName()
|
||||
_, err2 = dao.UpdateEntity(db, thingMap)
|
||||
errList.AddErr(err2)
|
||||
|
||||
updateThingMapEntity(db, thingMap)
|
||||
} else {
|
||||
if dao.IsNoRowsError(err2) {
|
||||
err2 = nil
|
||||
} else {
|
||||
errList.AddErr(err2)
|
||||
}
|
||||
}
|
||||
}
|
||||
err = errList.GetErrListAsOne()
|
||||
return err
|
||||
}
|
||||
|
||||
func OnUpdateThing(ctx *jxcontext.Context, db *dao.DaoDB, vendorInfoList []*model.VendorOrgCode, thingID int64, thingType int8) (err error) {
|
||||
return onUpdateThing(ctx, db, vendorInfoList, thingID, thingType, model.SyncFlagModifiedMask)
|
||||
}
|
||||
|
||||
func OnDeleteThing(ctx *jxcontext.Context, db *dao.DaoDB, vendorInfoList []*MultiStoreVendorInfo, thingID int64, thingType int8) (err error) {
|
||||
if thingType == model.ThingTypeSkuName {
|
||||
return nil
|
||||
}
|
||||
if len(vendorInfoList) == 0 {
|
||||
vendorInfoList = getMultiStoreVendorInfoList()
|
||||
}
|
||||
errList := errlist.New()
|
||||
for _, v := range vendorInfoList {
|
||||
thingMap := &model.ThingMap{
|
||||
ThingID: thingID,
|
||||
ThingType: thingType,
|
||||
VendorID: v.VendorID,
|
||||
VendorOrgCode: v.OrgCode,
|
||||
}
|
||||
thingMap.DeletedAt = utils.DefaultTimeValue
|
||||
if err2 := getThingMap(db, thingMap); err2 == nil {
|
||||
if model.IsSyncStatusNew(thingMap.SyncStatus) {
|
||||
thingMap.SyncStatus = 0
|
||||
thingMap.DeletedAt = time.Now()
|
||||
} else {
|
||||
thingMap.SyncStatus |= model.SyncFlagDeletedMask
|
||||
}
|
||||
thingMap.LastOperator = ctx.GetUserName()
|
||||
_, err2 = dao.UpdateEntity(db, thingMap)
|
||||
errList.AddErr(err2)
|
||||
|
||||
updateThingMapEntity(db, thingMap)
|
||||
} else if !dao.IsNoRowsError(err2) {
|
||||
errList.AddErr(err2)
|
||||
}
|
||||
}
|
||||
err = errList.GetErrListAsOne()
|
||||
return err
|
||||
}
|
||||
|
||||
func SkuCategoryVendor2ThingMap(cat *dao.SkuStoreCatInfo) (thingMap *model.ThingMap) {
|
||||
thingMap = &model.ThingMap{
|
||||
ThingID: int64(cat.ID),
|
||||
ThingType: model.ThingTypeCategory,
|
||||
VendorID: cat.VendorID,
|
||||
VendorOrgCode: cat.VendorOrgCode,
|
||||
|
||||
SyncStatus: cat.CatSyncStatus,
|
||||
VendorThingID: cat.VendorCatID,
|
||||
}
|
||||
thingMap.ID = cat.MapID // 一定要赋值
|
||||
return thingMap
|
||||
}
|
||||
|
||||
func SkuVendor2ThingMap(sku *dao.StoreSkuSyncInfo) (thingMap *model.ThingMap) {
|
||||
thingMap = &model.ThingMap{
|
||||
ThingID: int64(sku.SkuID),
|
||||
ThingType: model.ThingTypeSku,
|
||||
VendorID: sku.VendorID,
|
||||
VendorOrgCode: sku.VendorOrgCode,
|
||||
|
||||
SyncStatus: sku.SkuSyncStatus,
|
||||
VendorThingID: sku.VendorSkuID,
|
||||
}
|
||||
thingMap.DeletedAt = utils.DefaultTimeValue
|
||||
thingMap.ID = sku.BindID // 一定要赋值
|
||||
return thingMap
|
||||
}
|
||||
|
||||
func OnThingSync(ctx *jxcontext.Context, db *dao.DaoDB, thingMap *model.ThingMap, syncErr error) (err error) {
|
||||
globals.SugarLogger.Debugf("OnThingSync thingMap:%s", utils.Format4Output(thingMap, true))
|
||||
if syncErr != nil {
|
||||
err = syncErr
|
||||
thingMap.Remark = utils.LimitUTF8StringLen(err.Error(), 255)
|
||||
dao.UpdateEntity(db, thingMap, "Remark")
|
||||
|
||||
if thingMap.VendorOrgCode == globals.JdcsOrgCode {
|
||||
if strings.Contains(err.Error(), "店内分类信息不存在") {
|
||||
err = nil
|
||||
}
|
||||
}
|
||||
} else {
|
||||
updateFields := []string{
|
||||
model.FieldSyncStatus,
|
||||
model.FieldUpdatedAt,
|
||||
model.FieldLastOperator,
|
||||
"Remark",
|
||||
}
|
||||
if model.IsSyncStatusDelete(thingMap.SyncStatus) { //删除
|
||||
thingMap.DeletedAt = time.Now()
|
||||
thingMap.VendorThingID = ""
|
||||
updateFields = append(updateFields, "VendorThingID", model.FieldDeletedAt)
|
||||
} else if model.IsSyncStatusNew(thingMap.SyncStatus) { // 新增
|
||||
updateFields = append(updateFields, "VendorThingID")
|
||||
}
|
||||
thingMap.SyncStatus = 0
|
||||
thingMap.LastOperator = ctx.GetUserName()
|
||||
thingMap.UpdatedAt = time.Now()
|
||||
thingMap.Remark = ""
|
||||
_, err = dao.UpdateEntity(db, thingMap, updateFields...)
|
||||
|
||||
updateThingMapEntity(db, thingMap)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func updateThingMapEntity(db *dao.DaoDB, thingMap *model.ThingMap) {
|
||||
// if thingMap.VendorOrgCode == globals.JdOrgCode {
|
||||
// if thingMap.ThingType == model.ThingTypeCategory {
|
||||
// cat := &model.SkuCategory{
|
||||
// JdID: utils.Str2Int64WithDefault(thingMap.VendorThingID, 0),
|
||||
// JdSyncStatus: thingMap.SyncStatus,
|
||||
// }
|
||||
// cat.ID = int(thingMap.ThingID)
|
||||
// dao.UpdateEntity(db, cat, "JdID", "JdSyncStatus")
|
||||
// } else if thingMap.ThingType == model.ThingTypeSku {
|
||||
// sku := &model.Sku{
|
||||
// JdID: utils.Str2Int64WithDefault(thingMap.VendorThingID, 0),
|
||||
// JdSyncStatus: thingMap.SyncStatus,
|
||||
// }
|
||||
// sku.ID = int(thingMap.ThingID)
|
||||
// dao.UpdateEntity(db, sku, "JdID", "JdSyncStatus")
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
func amendAndPruneVendorStuff(ctx *jxcontext.Context, parentTask tasksch.ITask, storeID, vendorID int, vendorOrgCode string, isAsync, isContinueWhenError bool, opType int, isForceUpdate bool) (hint string, err error) {
|
||||
handler, _ := partner.GetPurchasePlatformFromVendorID(vendorID).(partner.IMultipleStoresHandler)
|
||||
if handler == nil {
|
||||
return "", fmt.Errorf("平台:%s不支持此操作", model.VendorChineseNames[vendorID])
|
||||
}
|
||||
db := dao.GetDB()
|
||||
vendorOrgCodes, _ := dao.GetVendorOrgCode(db, vendorID, vendorOrgCode, model.VendorOrgTypePlatform)
|
||||
var sku2Delete []*partner.StoreSkuInfo
|
||||
var cat2Delete []*partner.BareCategoryInfo
|
||||
task := tasksch.NewParallelTask(fmt.Sprintf("平台:%s,账号:%s上的商品与商家分类", model.VendorChineseNames[vendorID], vendorOrgCode),
|
||||
tasksch.NewParallelConfig().SetParallelCount(1).SetIsContinueWhenError(isContinueWhenError), ctx,
|
||||
func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
|
||||
step := batchItemList[0].(int)
|
||||
switch step {
|
||||
case 0:
|
||||
localSkuList, err := dao.GetSkusWithVendor(db, []int{vendorID}, []string{vendorOrgCode}, nil, nil, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
localSkuMap := make(map[string]*dao.StoreSkuSyncInfo)
|
||||
for _, v := range localSkuList {
|
||||
if v.VendorSkuID != "" {
|
||||
localSkuMap[v.VendorSkuID] = v
|
||||
}
|
||||
}
|
||||
|
||||
remoteSkuList, err2 := handler.GetSkus(ctx, vendorOrgCode, 0, "")
|
||||
if err = err2; err == nil {
|
||||
remoteSkuMap := make(map[string]int)
|
||||
for _, v := range remoteSkuList {
|
||||
if vendorSkuID := v.SkuList[0].VendorSkuID; vendorSkuID != "" {
|
||||
if localSkuMap[vendorSkuID] == nil {
|
||||
sku2Delete = append(sku2Delete, &partner.StoreSkuInfo{
|
||||
SkuID: v.SkuList[0].SkuID,
|
||||
VendorSkuID: vendorSkuID,
|
||||
})
|
||||
} else {
|
||||
remoteSkuMap[vendorSkuID] = 1
|
||||
}
|
||||
} else if v.VendorNameID != "" {
|
||||
sku2Delete = append(sku2Delete, &partner.StoreSkuInfo{
|
||||
SkuID: v.NameID,
|
||||
VendorSkuID: v.VendorNameID,
|
||||
})
|
||||
}
|
||||
}
|
||||
StoreSkuMap := make(map[int]*dao.StoreSkuSyncInfo)
|
||||
if storeID != 0 {
|
||||
skuList2, _ := dao.GetStoreSkuListWithVendor(db, storeID, vendorID, vendorOrgCode)
|
||||
for _, v := range skuList2 {
|
||||
if _, ok := StoreSkuMap[v.ID]; !ok {
|
||||
StoreSkuMap[v.ID] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
getSyncFlag := func(skuID int) int8 {
|
||||
if StoreSkuMap[skuID] != nil {
|
||||
return model.SyncFlagNewMask
|
||||
}
|
||||
return 0
|
||||
}
|
||||
if opType == AmendPruneOnlyAmend || opType == AmendPruneAll {
|
||||
for _, v := range localSkuList {
|
||||
if v.ExdSkuID == "" {
|
||||
if v.BindID != 0 {
|
||||
if !model.IsSyncStatusDelete(v.SkuSyncStatus) {
|
||||
if remoteSkuMap[v.VendorSkuID] == 0 {
|
||||
if !model.IsSyncStatusNew(v.SkuSyncStatus) {
|
||||
OnCreateThing(ctx, db, vendorOrgCodes, int64(v.SkuID), model.ThingTypeSku, getSyncFlag(v.SkuID), false)
|
||||
}
|
||||
} else if isForceUpdate {
|
||||
OnUpdateThing(ctx, db, vendorOrgCodes, int64(v.SkuID), model.ThingTypeSku)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//表示根据某门店的门店商品同步京东商品库中,这个门店关注并可售了这个商品,插到thingmap里同步标志是待创建(SyncFlagNewMask)才会创建
|
||||
//若该门店没关注可售,那插到thingmap里的话就该同步标志为0,就不会创建
|
||||
//getSyncFlag
|
||||
OnCreateThing(ctx, db, vendorOrgCodes, int64(v.SkuID), model.ThingTypeSku, getSyncFlag(v.SkuID), false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
case 1:
|
||||
// if (opType == AmendPruneOnlyPrune || opType == AmendPruneAll) && len(sku2Delete) > 0 {
|
||||
// _, err = putils.FreeBatchStoreSkuInfo("删除商品", func(task tasksch.ITask, batchedStoreSkuList []*partner.StoreSkuInfo) (result interface{}, successCount int, err error) {
|
||||
// if err = handler.DeleteSku2(ctx, vendorOrgCode, batchedStoreSkuList[0]); err == nil {
|
||||
// successCount = 1
|
||||
// }
|
||||
// return nil, successCount, err
|
||||
// }, ctx, task, sku2Delete, 1, isContinueWhenError)
|
||||
// }
|
||||
sku2Delete = nil
|
||||
case 2:
|
||||
localCatList, err := dao.GetSkuCategoryWithVendor(db, []int{vendorID}, []string{vendorOrgCode}, -1, nil, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
localCatMap := make(map[string]*dao.SkuStoreCatInfo)
|
||||
for _, v := range localCatList {
|
||||
localCatMap[v.VendorCatID] = v
|
||||
localCatMap[v.Name] = v
|
||||
localCatMap[utils.Int2Str(v.ID)] = v
|
||||
}
|
||||
|
||||
vendorCatMap := make(map[int]*model.VendorCategoryMap)
|
||||
if vendorOrgCodes[0].IsJxCat == model.YES {
|
||||
vendorCats, _ := dao.GetVendorCategoryMap(db, -1, 0, vendorID, vendorOrgCode, 0)
|
||||
for _, v := range vendorCats {
|
||||
if _, ok := vendorCatMap[v.CategoryID]; !ok {
|
||||
vendorCatMap[v.CategoryID] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
getSyncFlagCat := func(categoryID int) int8 {
|
||||
if vendorCatMap[categoryID] != nil || vendorOrgCodes[0].IsJxCat == model.NO {
|
||||
return model.SyncFlagNewMask
|
||||
}
|
||||
return 0
|
||||
}
|
||||
remoteCatList, err2 := handler.GetAllCategories(ctx, vendorOrgCode)
|
||||
if err = err2; err == nil {
|
||||
remoteCatMap := make(map[string]int)
|
||||
cat2Delete = checkRemoteCatExist(remoteCatMap, localCatMap, remoteCatList)
|
||||
for _, v := range localCatList {
|
||||
if v.IsExdSpec == model.NO {
|
||||
if v.MapID != 0 {
|
||||
if !model.IsSyncStatusDelete(v.CatSyncStatus) {
|
||||
if remoteCatMap[v.VendorCatID] == 0 {
|
||||
if !model.IsSyncStatusNew(v.CatSyncStatus) {
|
||||
OnCreateThing(ctx, db, vendorOrgCodes, int64(v.ID), model.ThingTypeCategory, getSyncFlagCat(v.ID), true)
|
||||
}
|
||||
} else if isForceUpdate && !model.IsSyncStatusUpdate(v.CatSyncStatus) {
|
||||
OnUpdateThing(ctx, db, vendorOrgCodes, int64(v.ID), model.ThingTypeCategory)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
OnCreateThing(ctx, db, vendorOrgCodes, int64(v.ID), model.ThingTypeCategory, getSyncFlagCat(v.ID), true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
case 3:
|
||||
if (opType == AmendPruneOnlyPrune || opType == AmendPruneAll) && len(cat2Delete) > 0 {
|
||||
for i := 0; i < 2; i++ {
|
||||
level := 2 - i
|
||||
var levelCat2Delete []*partner.BareCategoryInfo
|
||||
for _, v := range cat2Delete {
|
||||
if v.Level == level {
|
||||
levelCat2Delete = append(levelCat2Delete, v)
|
||||
}
|
||||
}
|
||||
if len(levelCat2Delete) > 0 {
|
||||
// task4Delete := tasksch.NewParallelTask(fmt.Sprintf("删除商家分类,level:%d", level), tasksch.NewParallelConfig().SetIsContinueWhenError(isContinueWhenError), ctx,
|
||||
// func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
|
||||
// cat := batchItemList[0].(*partner.BareCategoryInfo)
|
||||
// err = handler.DeleteCategory2(ctx, vendorOrgCode, cat.VendorCatID)
|
||||
// return nil, err
|
||||
// }, levelCat2Delete)
|
||||
// tasksch.HandleTask(task4Delete, task, true).Run()
|
||||
// _, err = task4Delete.GetResult(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
cat2Delete = nil
|
||||
}
|
||||
return nil, err
|
||||
}, []int{0, 1, 2, 3})
|
||||
tasksch.HandleTask(task, parentTask, true).Run()
|
||||
if !isAsync {
|
||||
_, err = task.GetResult(0)
|
||||
hint = "1"
|
||||
} else {
|
||||
hint = task.ID
|
||||
}
|
||||
return hint, err
|
||||
}
|
||||
|
||||
func FullSyncVendorStuff(ctx *jxcontext.Context, parentTask tasksch.ITask, storeID, vendorID int, vendorOrgCode string, isAsync, isContinueWhenError bool) (hint string, err error) {
|
||||
multiStoreHandler, _ := partner.GetPurchasePlatformFromVendorID(vendorID).(partner.IMultipleStoresHandler)
|
||||
if multiStoreHandler == nil {
|
||||
return "", fmt.Errorf("vendorID:%d不是多门店平台", vendorID)
|
||||
}
|
||||
task := tasksch.NewParallelTask("FullSyncStoreSkuNew", tasksch.NewParallelConfig().SetParallelCount(1).SetIsContinueWhenError(false), ctx,
|
||||
func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
|
||||
step := batchItemList[0].(int)
|
||||
switch step {
|
||||
case 0:
|
||||
_, err = amendAndPruneVendorStuff(ctx, task, storeID, vendorID, vendorOrgCode, false, isContinueWhenError, AmendPruneAll, false)
|
||||
case 1:
|
||||
_, err = SyncCategories(ctx, task, []int{vendorID}, []string{vendorOrgCode}, nil, false)
|
||||
case 2:
|
||||
_, err = SyncSkus(ctx, task, []int{vendorID}, []string{vendorOrgCode}, nil, nil, false)
|
||||
}
|
||||
return retVal, err
|
||||
}, []int{0, 1, 2})
|
||||
tasksch.HandleTask(task, parentTask, true).Run()
|
||||
if !isAsync {
|
||||
_, err = task.GetResult(0)
|
||||
} else {
|
||||
hint = task.GetID()
|
||||
}
|
||||
return hint, err
|
||||
}
|
||||
59
business/jxstore/cms/sync_store.go
Normal file
59
business/jxstore/cms/sync_store.go
Normal file
@@ -0,0 +1,59 @@
|
||||
package cms
|
||||
|
||||
import (
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/tasksch"
|
||||
"git.rosy.net.cn/jx-callback/business/model"
|
||||
"git.rosy.net.cn/jx-callback/business/model/dao"
|
||||
"git.rosy.net.cn/jx-callback/business/partner"
|
||||
)
|
||||
|
||||
// 如果京西门为打开,打开状态为非营业的平台门店
|
||||
func OpenRemoteStoreByJxStatus(ctx *jxcontext.Context, vendorIDs, storeIDs []int, isForce, isAsync, isContinueWhenError bool) (hint string, err error) {
|
||||
db := dao.GetDB()
|
||||
status := model.StoreStatusAll
|
||||
if !isForce {
|
||||
status = model.StoreStatusClosed
|
||||
}
|
||||
storeMapList, err := dao.GetStoresMapList(db, vendorIDs, storeIDs, nil, status, model.StoreIsSyncYes, "", "", "")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
vendorIDMap := make(map[int]int)
|
||||
if len(vendorIDs) == 0 {
|
||||
for k := range partner.PurchasePlatformHandlers {
|
||||
vendorIDMap[k] = 1
|
||||
}
|
||||
} else {
|
||||
for _, v := range vendorIDs {
|
||||
vendorIDMap[v] = 1
|
||||
}
|
||||
}
|
||||
task := tasksch.NewParallelTask("OpenRemoteStoreByJxStatus", tasksch.NewParallelConfig().SetIsContinueWhenError(isContinueWhenError), ctx,
|
||||
func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
|
||||
storeMap := batchItemList[0].(*model.StoreMap)
|
||||
if handler, _ := partner.GetPurchasePlatformFromVendorID(storeMap.VendorID).(partner.IStoreHandler); handler != nil {
|
||||
storeDetail, err := dao.GetStoreDetail(db, storeMap.StoreID, storeMap.VendorID, "")
|
||||
if err == nil && storeDetail.Status == model.StoreStatusOpened {
|
||||
if err = handler.UpdateStoreStatus(ctx, storeMap.VendorOrgCode, storeMap.StoreID, storeMap.VendorStoreID, model.StoreStatusOpened); err == nil {
|
||||
storeMap.Status = model.StoreStatusOpened
|
||||
dao.UpdateEntity(db, storeMap, model.FieldStatus)
|
||||
retVal = []int{1}
|
||||
}
|
||||
}
|
||||
}
|
||||
return retVal, err
|
||||
}, storeMapList)
|
||||
tasksch.HandleTask(task, nil, true).Run()
|
||||
|
||||
if isAsync {
|
||||
hint = task.GetID()
|
||||
} else {
|
||||
resultList, err2 := task.GetResult(0)
|
||||
if err = err2; err == nil {
|
||||
hint = utils.Int2Str(len(resultList))
|
||||
}
|
||||
}
|
||||
return hint, err
|
||||
}
|
||||
1470
business/jxstore/cms/sync_store_sku.go
Normal file
1470
business/jxstore/cms/sync_store_sku.go
Normal file
File diff suppressed because it is too large
Load Diff
329
business/jxstore/cms/sync_store_sku_fakejd.go
Normal file
329
business/jxstore/cms/sync_store_sku_fakejd.go
Normal file
@@ -0,0 +1,329 @@
|
||||
package cms
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/tasksch"
|
||||
"git.rosy.net.cn/jx-callback/business/model"
|
||||
"git.rosy.net.cn/jx-callback/business/model/dao"
|
||||
"git.rosy.net.cn/jx-callback/business/partner"
|
||||
"git.rosy.net.cn/jx-callback/business/partner/putils"
|
||||
"git.rosy.net.cn/jx-callback/globals/api/apimanager"
|
||||
"github.com/360EntSecGroup-Skylar/excelize"
|
||||
)
|
||||
|
||||
func storeOrSkuMap2List(intStrMap map[int]string) (ids []int) {
|
||||
for k, v := range intStrMap {
|
||||
if v != "" {
|
||||
ids = append(ids, k)
|
||||
}
|
||||
}
|
||||
return ids
|
||||
}
|
||||
|
||||
func getStoreSkus(db *dao.DaoDB, storeID int, skuIDs []int) (skus []*dao.StoreSkuSyncInfo, err error) {
|
||||
sql := `
|
||||
SELECT
|
||||
t1.id bind_id, t1.sku_id, t1.price, t1.unit_price, t1.status store_sku_status,
|
||||
t1.jd_sync_status sku_sync_status, t1.jd_price vendor_price, t1.jd_lock_time lock_time,
|
||||
t1.store_id, t1.deleted_at bind_deleted_at,t1.status_sale_begin,t1.status_sale_end,
|
||||
t2.*,
|
||||
t3.id name_id, t3.prefix, t3.name, t3.unit, t3.upc, t3.status name_status, t3.ex_prefix, t3.ex_prefix_begin, t3.ex_prefix_end
|
||||
FROM store_sku_bind t1
|
||||
LEFT JOIN sku t2 ON t1.sku_id = t2.id AND t2.deleted_at = ?/* AND t2.status = ?*/
|
||||
LEFT JOIN sku_name t3 ON t2.name_id = t3.id AND t3.deleted_at = ?/* AND t3.status = ?*/
|
||||
WHERE 1 = 1
|
||||
AND t1.store_id = ?
|
||||
AND t1.deleted_at = ?
|
||||
AND t1.sku_id IN ( ` + dao.GenQuestionMarks(len(skuIDs)) + `)
|
||||
ORDER BY t1.price`
|
||||
sqlParams := []interface{}{
|
||||
utils.DefaultTimeValue,
|
||||
utils.DefaultTimeValue,
|
||||
storeID,
|
||||
utils.DefaultTimeValue,
|
||||
skuIDs,
|
||||
}
|
||||
if err = dao.GetRows(db, &skus, sql, sqlParams...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return skus, err
|
||||
}
|
||||
|
||||
func SyncStoreSku4FakeJD(ctx *jxcontext.Context, parentTask tasksch.ITask, storeID int, vendorStoreID string, inSkuMap map[int]string, isContinueWhenError bool) (err error) {
|
||||
vendorID := model.VendorIDJD
|
||||
db := dao.GetDB()
|
||||
storeDetail, err := dao.GetStoreDetail(db, storeID, vendorID, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
storeDetail.VendorOrgCode = apimanager.FakeJdOrgCode
|
||||
skuIDs := storeOrSkuMap2List(inSkuMap)
|
||||
skus, err := getStoreSkus(db, storeID, skuIDs)
|
||||
if err != nil || len(skus) == 0 {
|
||||
return err
|
||||
}
|
||||
formalizeStoreSkuList(skus)
|
||||
storeSkuHandler := partner.GetPurchasePlatformFromVendorID(vendorID).(partner.IPurchasePlatformStoreSkuHandler)
|
||||
|
||||
var (
|
||||
stockList, onlineList, offlineList, priceList []*partner.StoreSkuInfo
|
||||
)
|
||||
skuMap := make(map[*partner.StoreSkuInfo]*dao.StoreSkuSyncInfo)
|
||||
now := jxutils.OperationTime2HourMinuteFormat(time.Now())
|
||||
var failedList []*partner.StoreSkuInfoWithErr
|
||||
for _, sku := range skus {
|
||||
sku.SkuSyncStatus = model.SyncFlagSaleMask | model.SyncFlagPriceMask | model.SyncFlagStockMask
|
||||
sku.VendorSkuID = inSkuMap[sku.SkuID]
|
||||
sku.SkuID = int(utils.Str2Int64(sku.VendorSkuID)) // skuID与vendorID一样
|
||||
sku.VendorOrgCode = apimanager.FakeJdOrgCode
|
||||
|
||||
sku.VendorPrice = 0
|
||||
sku.MergedStatus = MergeSkuSaleStatusWithStoreOpTime(sku, storeDetail, now)
|
||||
var bareSku *partner.StoreSkuInfo
|
||||
if isStoreSkuSyncNeedDelete(sku) {
|
||||
if !dao.IsVendorThingIDEmpty(sku.VendorSkuID) {
|
||||
bareSku = storeSkuSyncInfo2Bare(sku)
|
||||
stockList = append(stockList, bareSku)
|
||||
} else {
|
||||
// updateItems = append(updateItems, sku2Update(vendorID, sku, model.SyncFlagDeletedMask))
|
||||
}
|
||||
} else if model.IsSyncStatusNew(sku.SkuSyncStatus) {
|
||||
calVendorPrice4StoreSku(sku, storeDetail.PricePercentagePackObj, int(storeDetail.PricePercentage))
|
||||
if dao.IsVendorThingIDEmpty(sku.VendorSkuID) {
|
||||
// todo 多平台商品库没有正常创建,直接跳过
|
||||
} else {
|
||||
sku.SkuSyncStatus |= model.SyncFlagSaleMask | model.SyncFlagPriceMask
|
||||
bareSku = storeSkuSyncInfo2Bare(sku)
|
||||
stockList = append(stockList, bareSku)
|
||||
priceList = append(priceList, bareSku)
|
||||
if sku.MergedStatus == model.SkuStatusNormal {
|
||||
onlineList = append(onlineList, bareSku)
|
||||
} else {
|
||||
offlineList = append(offlineList, bareSku)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if dao.IsVendorThingIDEmpty(sku.VendorSkuID) {
|
||||
// err = fmt.Errorf("门店:%d,修改没有创建的商品:%d", storeID, sku.SkuID)
|
||||
err = utils.NewErrorCode(fmt.Sprintf("门店:%d,修改没有创建的商品:%d", storeID, sku.SkuID), "-1", 0)
|
||||
failedList = putils.GetErrMsg2FailedSingleList(nil, err, storeID, model.VendorChineseNames[vendorID], "异常同步错误")
|
||||
if parentTask == nil {
|
||||
return err
|
||||
}
|
||||
parentTask.AddBatchErr(err)
|
||||
parentTask.AddFailedList(failedList)
|
||||
} else {
|
||||
isAdded2Update := false
|
||||
if model.IsSyncStatusPrice(sku.SkuSyncStatus) {
|
||||
bareSku = storeSkuSyncInfo2Bare(calVendorPrice4StoreSku(sku, storeDetail.PricePercentagePackObj, int(storeDetail.PricePercentage)))
|
||||
priceList = append(priceList, bareSku)
|
||||
}
|
||||
if !isAdded2Update {
|
||||
if model.IsSyncStatusSale(sku.SkuSyncStatus) {
|
||||
if bareSku == nil {
|
||||
bareSku = storeSkuSyncInfo2Bare(sku)
|
||||
}
|
||||
if sku.MergedStatus == model.SkuStatusNormal {
|
||||
onlineList = append(onlineList, bareSku)
|
||||
stockList = append(stockList, bareSku)
|
||||
} else {
|
||||
offlineList = append(offlineList, bareSku)
|
||||
// 因为京东平台以是否有库存表示是否关注,所以不论是否可售,都要设置库存
|
||||
stockList = append(stockList, bareSku)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if bareSku != nil {
|
||||
skuMap[bareSku] = sku
|
||||
}
|
||||
}
|
||||
bareSku2Sync := func(bareSkuList []*partner.StoreSkuInfo) (skuList []*dao.StoreSkuSyncInfo) {
|
||||
if len(bareSkuList) > 0 {
|
||||
skuList = make([]*dao.StoreSkuSyncInfo, len(bareSkuList))
|
||||
for k, v := range bareSkuList {
|
||||
skuList[k] = skuMap[v]
|
||||
}
|
||||
}
|
||||
return skuList
|
||||
}
|
||||
isContinueWhenError2 := true
|
||||
realStoreID := int(utils.Str2Int64(vendorStoreID))
|
||||
task := tasksch.NewParallelTask("SyncStoreSku4FakeJD", tasksch.NewParallelConfig().SetParallelCount(1).SetIsContinueWhenError(isContinueWhenError2), ctx,
|
||||
func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
|
||||
step := batchItemList[0].(int)
|
||||
// globals.SugarLogger.Debugf("step:%d", step)
|
||||
switch step {
|
||||
case 0:
|
||||
for k, list := range [][]*partner.StoreSkuInfo{stockList /*, onlineList*/} {
|
||||
if len(list) > 0 {
|
||||
_, err = putils.FreeBatchStoreSkuInfo("更新门店商品库存", func(task tasksch.ITask, batchedStoreSkuList []*partner.StoreSkuInfo) (result interface{}, successCount int, err error) {
|
||||
var failedList []*partner.StoreSkuInfoWithErr
|
||||
failedList, err = storeSkuHandler.UpdateStoreSkusStock(ctx, storeDetail.VendorOrgCode, realStoreID, vendorStoreID, batchedStoreSkuList)
|
||||
if len(failedList) > 0 {
|
||||
task.AddFailedList(failedList)
|
||||
}
|
||||
successList := putils.UnselectStoreSkuListByVendorSkuIDs(batchedStoreSkuList, GetVendorSkuIDList(failedList))
|
||||
if k == 0 && len(successList) > 0 {
|
||||
updateStoreSku(dao.GetDB(), vendorID, bareSku2Sync(successList), model.SyncFlagStockMask)
|
||||
}
|
||||
return nil, len(successList), err
|
||||
}, ctx, task, list, storeSkuHandler.GetStoreSkusBatchSize(partner.FuncUpdateStoreSkusStock), isContinueWhenError2)
|
||||
}
|
||||
}
|
||||
case 1, 2:
|
||||
statusList := onlineList
|
||||
status := model.SkuStatusNormal
|
||||
name := "可售门店商品"
|
||||
if step == 2 {
|
||||
statusList = offlineList
|
||||
status = model.SkuStatusDontSale
|
||||
name = "不可售门店商品"
|
||||
}
|
||||
if len(statusList) > 0 {
|
||||
_, err = putils.FreeBatchStoreSkuInfo(name, func(task tasksch.ITask, batchedStoreSkuList []*partner.StoreSkuInfo) (result interface{}, successCount int, err error) {
|
||||
var failedList []*partner.StoreSkuInfoWithErr
|
||||
failedList, err = storeSkuHandler.UpdateStoreSkusStatus(ctx, storeDetail.VendorOrgCode, realStoreID, vendorStoreID, batchedStoreSkuList, status)
|
||||
if len(failedList) > 0 {
|
||||
task.AddFailedList(failedList)
|
||||
}
|
||||
successList := putils.UnselectStoreSkuListByVendorSkuIDs(batchedStoreSkuList, GetVendorSkuIDList(failedList))
|
||||
if len(successList) > 0 {
|
||||
updateStoreSku(dao.GetDB(), vendorID, bareSku2Sync(successList), model.SyncFlagSaleMask)
|
||||
}
|
||||
return nil, len(successList), err
|
||||
}, ctx, task, statusList, storeSkuHandler.GetStoreSkusBatchSize(partner.FuncUpdateStoreSkusStatus), isContinueWhenError2)
|
||||
}
|
||||
case 3:
|
||||
if len(priceList) > 0 {
|
||||
_, err = putils.FreeBatchStoreSkuInfo("更新门店商品价格", func(task tasksch.ITask, batchedStoreSkuList []*partner.StoreSkuInfo) (result interface{}, successCount int, err error) {
|
||||
var failedList []*partner.StoreSkuInfoWithErr
|
||||
failedList, err = storeSkuHandler.UpdateStoreSkusPrice(ctx, storeDetail.VendorOrgCode, realStoreID, vendorStoreID, batchedStoreSkuList)
|
||||
if len(failedList) > 0 {
|
||||
task.AddFailedList(failedList)
|
||||
}
|
||||
successList := putils.UnselectStoreSkuListByVendorSkuIDs(batchedStoreSkuList, GetVendorSkuIDList(failedList))
|
||||
if len(successList) > 0 {
|
||||
updateStoreSku(dao.GetDB(), vendorID, bareSku2Sync(successList), model.SyncFlagPriceMask)
|
||||
}
|
||||
return nil, len(successList), err
|
||||
}, ctx, task, priceList, storeSkuHandler.GetStoreSkusBatchSize(partner.FuncUpdateStoreSkusPrice), isContinueWhenError2)
|
||||
}
|
||||
}
|
||||
return retVal, err
|
||||
}, []int{0, 1, 2, 3})
|
||||
tasksch.HandleTask(task, parentTask, true).Run()
|
||||
_, err = task.GetResult(0)
|
||||
return err
|
||||
}
|
||||
|
||||
func SyncFakeJdStoreSku(ctx *jxcontext.Context, parentTask tasksch.ITask, storeMap, skuMap map[int]string, isAsync, isContinueWhenError bool) (hint string, err error) {
|
||||
storeIDs := storeOrSkuMap2List(storeMap)
|
||||
task := tasksch.NewParallelTask("同步假京东", nil, ctx,
|
||||
func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
|
||||
storeID := batchItemList[0].(int)
|
||||
err = SyncStoreSku4FakeJD(ctx, task, storeID, storeMap[storeID], skuMap, isContinueWhenError)
|
||||
return retVal, err
|
||||
}, storeIDs)
|
||||
tasksch.HandleTask(task, parentTask, true).Run()
|
||||
if isAsync {
|
||||
hint = task.GetID()
|
||||
} else {
|
||||
_, err = task.GetResult(0)
|
||||
hint = utils.Int2Str(len(storeIDs))
|
||||
}
|
||||
return hint, err
|
||||
}
|
||||
|
||||
func excel2FakeJdThingMap(ctx *jxcontext.Context, reader io.Reader) (thingMapList []*model.FakeJdThingMap, err error) {
|
||||
xlsx, err := excelize.OpenReader(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for sheetIndex := 0; sheetIndex < xlsx.SheetCount; sheetIndex++ {
|
||||
rows, err2 := xlsx.GetRows(xlsx.GetSheetName(sheetIndex + 1))
|
||||
if err2 != nil {
|
||||
return nil, err2
|
||||
}
|
||||
for rowNum, row := range rows {
|
||||
thingMap := &model.FakeJdThingMap{
|
||||
JxID: int(utils.Str2Int64WithDefault(row[0], 0)),
|
||||
JdID: utils.Str2Int64WithDefault(row[1], 0),
|
||||
}
|
||||
if thingMap.JxID == 0 || thingMap.JdID == 0 {
|
||||
if rowNum == 0 {
|
||||
continue
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
if sheetIndex == 0 {
|
||||
thingMap.ThingType = model.ThingTypeStore
|
||||
} else {
|
||||
thingMap.ThingType = model.ThingTypeSku
|
||||
}
|
||||
dao.WrapAddIDCULEntity(thingMap, ctx.GetUserName())
|
||||
thingMapList = append(thingMapList, thingMap)
|
||||
}
|
||||
}
|
||||
return thingMapList, err
|
||||
}
|
||||
|
||||
func getFakeThingMap(ctx *jxcontext.Context, db *dao.DaoDB) (storeMap, skuMap map[int]string, err error) {
|
||||
var thingMapList []*model.FakeJdThingMap
|
||||
err = dao.GetRows(db, &thingMapList, "SELECT t1.* FROM fake_jd_thing_map t1")
|
||||
if err == nil {
|
||||
storeMap, skuMap = make(map[int]string), make(map[int]string)
|
||||
for _, v := range thingMapList {
|
||||
if v.ThingType == model.ThingTypeStore {
|
||||
storeMap[v.JxID] = utils.Int64ToStr(v.JdID)
|
||||
} else {
|
||||
skuMap[v.JxID] = utils.Int64ToStr(v.JdID)
|
||||
}
|
||||
}
|
||||
}
|
||||
return storeMap, skuMap, err
|
||||
}
|
||||
|
||||
func UploadFakeJdThingMap(ctx *jxcontext.Context, reader io.Reader, isSyncNow, isAsync, isContinueWhenError bool) (hint string, err error) {
|
||||
thingMapList, err := excel2FakeJdThingMap(ctx, reader)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
db := dao.GetDB()
|
||||
txDB , _ := dao.Begin(db)
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
dao.Rollback(db, txDB)
|
||||
panic(r)
|
||||
}
|
||||
}()
|
||||
_, err = dao.ExecuteSQL(db, "DELETE t1 FROM fake_jd_thing_map t1")
|
||||
if err != nil {
|
||||
dao.Rollback(db, txDB)
|
||||
return "", err
|
||||
}
|
||||
err = dao.CreateMultiEntities(db, thingMapList)
|
||||
if err != nil {
|
||||
dao.Rollback(db, txDB)
|
||||
return "", err
|
||||
}
|
||||
dao.Commit(db, txDB)
|
||||
|
||||
if isSyncNow {
|
||||
if storeMap, skuMap, err2 := getFakeThingMap(ctx, db); err2 == nil {
|
||||
hint, err = SyncFakeJdStoreSku(ctx, nil, storeMap, skuMap, isAsync, isContinueWhenError)
|
||||
}
|
||||
} else {
|
||||
hint = utils.Int2Str(len(thingMapList))
|
||||
}
|
||||
return hint, err
|
||||
}
|
||||
30
business/jxstore/cms/sync_store_sku_fakejd_test.go
Normal file
30
business/jxstore/cms/sync_store_sku_fakejd_test.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package cms
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
|
||||
)
|
||||
|
||||
func TestSyncStoreSku4FakeJD(t *testing.T) {
|
||||
skuMap := map[int]string{
|
||||
22509: "2029937911",
|
||||
}
|
||||
err := SyncStoreSku4FakeJD(jxcontext.AdminCtx, nil, 100118, "11943257", skuMap, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUploadFakeJdThingMap(t *testing.T) {
|
||||
file, err := os.Open("到家菜市门店与商品映射信息(1).xlsx")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
hint, err := UploadFakeJdThingMap(jxcontext.AdminCtx, file, true, false, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(hint)
|
||||
}
|
||||
44
business/jxstore/cms/sync_store_sku_test.go
Normal file
44
business/jxstore/cms/sync_store_sku_test.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package cms
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/tasksch"
|
||||
"git.rosy.net.cn/jx-callback/business/model"
|
||||
"git.rosy.net.cn/jx-callback/business/partner"
|
||||
"git.rosy.net.cn/jx-callback/business/partner/putils"
|
||||
)
|
||||
|
||||
func TestFreeBatchStoreSkuInfo(t *testing.T) {
|
||||
var sku2Delete []*partner.StoreSkuInfo
|
||||
for i := 0; i < 123; i++ {
|
||||
sku2Delete = append(sku2Delete, &partner.StoreSkuInfo{
|
||||
SkuID: i + 1,
|
||||
})
|
||||
}
|
||||
|
||||
ctx := jxcontext.AdminCtx
|
||||
var parentTask tasksch.ITask
|
||||
isContinueWhenError := true
|
||||
handler, _ := partner.GetPurchasePlatformFromVendorID(model.VendorIDEBAI).(partner.ISingleStoreStoreSkuHandler)
|
||||
_, err := putils.FreeBatchStoreSkuInfo("删除门店商品", func(task tasksch.ITask, batchedStoreSkuList []*partner.StoreSkuInfo) (result interface{}, successCount int, err error) {
|
||||
t.Log(len(batchedStoreSkuList))
|
||||
return nil, 0, err
|
||||
}, ctx, parentTask, sku2Delete, handler.GetStoreSkusBatchSize(partner.FuncDeleteStoreSkus), isContinueWhenError)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetTimeMixByInt(t *testing.T) {
|
||||
const (
|
||||
time1 = 1100
|
||||
time2 = 2300
|
||||
time3 = 1200
|
||||
time4 = 2400
|
||||
)
|
||||
a, b := GetTimeMixByInt(time1, time2, time3, time4)
|
||||
fmt.Println(a, b)
|
||||
}
|
||||
210
business/jxstore/cms/user.go
Normal file
210
business/jxstore/cms/user.go
Normal file
@@ -0,0 +1,210 @@
|
||||
package cms
|
||||
|
||||
// var (
|
||||
// LoginTypeFieldMap = map[string]string{
|
||||
// mobile.LoginType: "tel",
|
||||
// weixin.LoginType: "openid",
|
||||
// weixin.LoginTypeMiniProgram: "openid_mini",
|
||||
// }
|
||||
// )
|
||||
|
||||
// func GetStoreUsers(ctx *jxcontext.Context, storeID int) (storeUserInfos []*dao.StoreUserInfo, err error) {
|
||||
// sql := `
|
||||
// SELECT t1.id, t1.jxstoreid, t1.openid, t1.tel, t1.nickname, t1.parentid, t3.tel parent_mobile,
|
||||
// CONCAT("[", GROUP_CONCAT(CONCAT('{"id":', t2.id, ',"parentID":', t2.parentid, ',"tel":"', t2.tel, '","nickname":"', IF(t2.nickname IS NULL, "", t2.nickname), '"}')), "]") members_str
|
||||
// FROM weixins t1
|
||||
// LEFT JOIN weixins t2 ON t2.parentid = t1.id
|
||||
// LEFT JOIN weixins t3 ON t1.parentid = t3.id
|
||||
// WHERE t1.parentid = -1 AND t1.jxstoreid = ?
|
||||
// GROUP BY 1,2,3,4,5,6,7;
|
||||
// `
|
||||
// // globals.SugarLogger.Debug(sql)
|
||||
// if err = dao.GetRows(nil, &storeUserInfos, sql, storeID); err == nil {
|
||||
// for _, storeUserInfo := range storeUserInfos {
|
||||
// if storeUserInfo.MembersStr != "" {
|
||||
// err = utils.UnmarshalUseNumber([]byte(storeUserInfo.MembersStr), &storeUserInfo.Members)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// return storeUserInfos, err
|
||||
// }
|
||||
|
||||
// func GetUserInfo(ctx *jxcontext.Context, mobile string) (storeUserInfo *dao.StoreUserInfo, err error) {
|
||||
// storeUserInfo, err = dao.GetUserStoreInfo(dao.GetDB(), "tel", mobile)
|
||||
// globals.SugarLogger.Debugf("GetUserInfo:%s, token:%s, mobile:%s, storeUserInfo:%s, err:%v", ctx.GetTrackInfo(), ctx.GetToken(), mobile, utils.Format4Output(storeUserInfo, true), err)
|
||||
// return storeUserInfo, err
|
||||
// }
|
||||
|
||||
// func GetSelfInfo(ctx *jxcontext.Context) (storeUserInfo *dao.StoreUserInfo, err error) {
|
||||
// loginInfo := ctx.GetLoginInfo()
|
||||
// if loginInfo == nil {
|
||||
// return nil, auth.ErrAPINeedRealLogin
|
||||
// }
|
||||
// fieldName := LoginTypeFieldMap[loginInfo.GetAuthType()]
|
||||
// if fieldName == "" {
|
||||
// return nil, auth.ErrIllegalLoginType
|
||||
// }
|
||||
// storeUserInfo, err = dao.GetUserStoreInfo(dao.GetDB(), fieldName, loginInfo.GetAuthID())
|
||||
// globals.SugarLogger.Debugf("GetSelfInfo:%s, token:%s, storeUserInfo:%s, err:%v", ctx.GetTrackInfo(), ctx.GetToken(), utils.Format4Output(storeUserInfo, true), err)
|
||||
// return storeUserInfo, err
|
||||
// }
|
||||
|
||||
// func GetMyStoreList(ctx *jxcontext.Context) (storeList []*dao.StoreWithCityName, err error) {
|
||||
// mobileNum, _ := ctx.GetMobileAndUserID()
|
||||
// if mobileNum == "" {
|
||||
// return nil, fmt.Errorf("不能得到用户手机号")
|
||||
// }
|
||||
// storeList, err = dao.GetStoreListByMobile(dao.GetDB(), mobileNum)
|
||||
// return storeList, err
|
||||
// }
|
||||
|
||||
// func UnbindMobile(ctx *jxcontext.Context, mobile string) (num int64, err error) {
|
||||
// db := dao.GetDB()
|
||||
// num, err = dao.UpdateEntityByKV(db, &legacymodel.WeiXins{}, map[string]interface{}{
|
||||
// "JxStoreID": 0,
|
||||
// "ParentID": -1,
|
||||
// }, map[string]interface{}{
|
||||
// "Tel": mobile,
|
||||
// })
|
||||
// if err == nil {
|
||||
// jxutils.HandleUserWXRemark(db, mobile, false)
|
||||
// TransferLegacyWeixins(mobile)
|
||||
// }
|
||||
// return num, err
|
||||
// }
|
||||
|
||||
// func BindMobile2Store(ctx *jxcontext.Context, mobile string, storeID int) (num int64, err error) {
|
||||
// db := dao.GetDB()
|
||||
// user, err2 := verifyMobileIsBlank(db, mobile)
|
||||
// if err = err2; err == nil || err == orm.ErrNoRows {
|
||||
// user.JxStoreID = storeID
|
||||
// if err == nil {
|
||||
// txDB , _ := dao.Begin(db)
|
||||
// defer func() {
|
||||
// if r := recover(); r != nil {
|
||||
// dao.Rollback(db, txDB)
|
||||
// panic(r)
|
||||
// }
|
||||
// }()
|
||||
// if num, err = dao.UpdateEntity(db, user, "JxStoreID"); err == nil {
|
||||
// err = dao.SetWeiXinsEmpty2Null(db, user)
|
||||
// }
|
||||
// if err != nil {
|
||||
// dao.Rollback(db, txDB)
|
||||
// } else {
|
||||
// dao.Commit(db, txDB)
|
||||
// }
|
||||
// } else {
|
||||
// // globals.SugarLogger.Debug(utils.Format4Output(user, false))
|
||||
// dao.WrapAddIDCULEntity(user, ctx.GetUserName())
|
||||
// user.ParentID = -1
|
||||
// if err = dao.CreateWeiXins(db, user); err == nil {
|
||||
// num = 1
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// if err == nil {
|
||||
// jxutils.HandleUserWXRemark(db, mobile, false)
|
||||
// TransferLegacyWeixins(mobile)
|
||||
// }
|
||||
// return num, err
|
||||
// }
|
||||
|
||||
// func AddMobile2Mobile(ctx *jxcontext.Context, parentMobile, mobile string) (num int64, err error) {
|
||||
// db := dao.GetDB()
|
||||
// parentUser := &legacymodel.WeiXins{}
|
||||
// parentUser.Tel = parentMobile
|
||||
// if err = dao.GetEntity(db, parentUser, "Tel"); err == nil {
|
||||
// if parentUser.ParentID == -1 {
|
||||
// globals.SugarLogger.Debug(parentUser)
|
||||
// if err = verifyMobileHasNoMembers(db, mobile); err == nil {
|
||||
// user, err2 := verifyMobileIsBlank(db, mobile)
|
||||
// if err = err2; err == nil || err == orm.ErrNoRows {
|
||||
// user.ParentID = parentUser.ID
|
||||
// if err == nil {
|
||||
// // todo transaction
|
||||
// if num, err = dao.UpdateEntity(db, user, "ParentID"); err == nil {
|
||||
// err = dao.SetWeiXinsEmpty2Null(db, user)
|
||||
// }
|
||||
// } else {
|
||||
// dao.WrapAddIDCULEntity(user, ctx.GetUserName())
|
||||
// if err = dao.CreateWeiXins(db, user); err == nil {
|
||||
// num = 1
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// } else {
|
||||
// err = fmt.Errorf("%s本身是成员", parentMobile)
|
||||
// }
|
||||
// }
|
||||
// if err == nil {
|
||||
// jxutils.HandleUserWXRemark(db, mobile, false)
|
||||
// TransferLegacyWeixins(mobile)
|
||||
// }
|
||||
// return num, err
|
||||
// }
|
||||
|
||||
// func ChangeMobile(ctx *jxcontext.Context, curMobile, expectedMobile string) (num int64, err error) {
|
||||
// num, err = dao.UpdateEntityByKV(nil, &legacymodel.WeiXins{}, map[string]interface{}{
|
||||
// "Tel": expectedMobile,
|
||||
// }, map[string]interface{}{
|
||||
// "Tel": curMobile,
|
||||
// })
|
||||
// if err == nil {
|
||||
// TransferLegacyWeixins(curMobile)
|
||||
// TransferLegacyWeixins(expectedMobile)
|
||||
// }
|
||||
// return num, err
|
||||
// }
|
||||
|
||||
// func verifyMobileIsBlank(db *dao.DaoDB, mobile string) (user *legacymodel.WeiXins, err error) {
|
||||
// if !jxutils.IsStringLikeMobile(mobile) {
|
||||
// return nil, fmt.Errorf("%s看起来不像是一个手机号", mobile)
|
||||
// }
|
||||
// user = &legacymodel.WeiXins{
|
||||
// Tel: mobile,
|
||||
// }
|
||||
// if err = dao.GetEntity(db, user, "Tel"); err == nil {
|
||||
// if user.ParentID != -1 && user.ParentID != 0 {
|
||||
// userParent := &legacymodel.WeiXins{
|
||||
// ID: user.ParentID,
|
||||
// }
|
||||
// if err = dao.GetEntity(db, userParent); err != nil && err != orm.ErrNoRows {
|
||||
// return nil, err
|
||||
// }
|
||||
// if err != orm.ErrNoRows {
|
||||
// err = fmt.Errorf("%s已经是组长:%s,门店:%d的小组成员", mobile, userParent.Tel, userParent.JxStoreID)
|
||||
// } else {
|
||||
// err = nil
|
||||
// }
|
||||
// } else if user.JxStoreID != 0 {
|
||||
// store := &model.Store{}
|
||||
// store.ID = user.JxStoreID
|
||||
// if err = dao.GetEntity(db, store); err == nil {
|
||||
// err = fmt.Errorf("%s本身已经是门店:%d的组长", mobile, user.JxStoreID)
|
||||
// } else if dao.IsNoRowsError(err) {
|
||||
// err = nil
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// return user, err
|
||||
// }
|
||||
|
||||
// func verifyMobileHasNoMembers(db *dao.DaoDB, mobile string) (err error) {
|
||||
// countInfo := &struct{ Ct int }{}
|
||||
// if err = dao.GetRow(db, countInfo, `
|
||||
// SELECT COUNT(*) ct
|
||||
// FROM weixins t1
|
||||
// JOIN weixins t2 ON t1.parentid = t2.id AND t2.tel = ?
|
||||
// `, mobile); err == nil {
|
||||
// if countInfo.Ct > 0 {
|
||||
// user := &legacymodel.WeiXins{
|
||||
// Tel: mobile,
|
||||
// }
|
||||
// dao.GetEntity(db, user, "Tel")
|
||||
// err = fmt.Errorf("%s本身已经是门店:%d组长", mobile, user.JxStoreID)
|
||||
// }
|
||||
// }
|
||||
// return err
|
||||
// }
|
||||
File diff suppressed because it is too large
Load Diff
23
business/jxstore/cms/user2_test.go
Normal file
23
business/jxstore/cms/user2_test.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package cms
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
|
||||
_ "git.rosy.net.cn/jx-callback/globals/api/apimanager"
|
||||
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
|
||||
"git.rosy.net.cn/jx-callback/business/model"
|
||||
)
|
||||
|
||||
func TestTryAddStoreBossRole4User(t *testing.T) {
|
||||
err := TryAddStoreBossRole4User(jxcontext.AdminCtx, &model.User{
|
||||
Type: model.UserTypeStoreBoss,
|
||||
UserID: "24058800CD3711E991B2525400E86DC0",
|
||||
Mobile: utils.String2Pointer("91112345678"),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
50
business/jxstore/cms/validation.go
Normal file
50
business/jxstore/cms/validation.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package cms
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"gopkg.in/go-playground/validator.v9"
|
||||
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils"
|
||||
"git.rosy.net.cn/jx-callback/business/model"
|
||||
)
|
||||
|
||||
var (
|
||||
unitNamesMap map[string]int
|
||||
specUnitNamesMap map[string]int
|
||||
validate = validator.New()
|
||||
)
|
||||
|
||||
func init() {
|
||||
unitNamesMap = jxutils.MakeValidationMapFromSlice(model.UnitNames, 1)
|
||||
specUnitNamesMap = jxutils.MakeValidationMapFromSlice(model.SpecUnitNames, 1)
|
||||
}
|
||||
|
||||
func validateStringInMap(name string, value interface{}, flagMap map[string]int) (err error) {
|
||||
if strValue, ok := value.(string); ok {
|
||||
if flagMap[strValue] == 1 {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("属性%s类型或取值不合法,要求string", name)
|
||||
}
|
||||
|
||||
func ValidateUnit(value interface{}) (err error) {
|
||||
return validateStringInMap("Unit", value, unitNamesMap)
|
||||
}
|
||||
|
||||
func ValidateSpecUnit(value interface{}) (err error) {
|
||||
return validateStringInMap("SpecUnit", value, specUnitNamesMap)
|
||||
}
|
||||
|
||||
func ValidateStruct(value interface{}) (err error) {
|
||||
return validate.Struct(value)
|
||||
}
|
||||
|
||||
func ValidateVar(value interface{}, tag string) (err error) {
|
||||
return validate.Var(value, tag)
|
||||
}
|
||||
|
||||
func ValidateStructPartial(value interface{}, fields ...string) (err error) {
|
||||
return validate.StructPartial(value, fields...)
|
||||
}
|
||||
@@ -6,10 +6,10 @@ import (
|
||||
|
||||
"git.rosy.net.cn/baseapi/platformapi/autonavi"
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
"git.rosy.net.cn/jx-callback/business/dao"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
|
||||
"git.rosy.net.cn/jx-callback/business/model"
|
||||
"git.rosy.net.cn/jx-callback/business/model/dao"
|
||||
"git.rosy.net.cn/jx-callback/globals/api"
|
||||
)
|
||||
|
||||
@@ -67,7 +67,7 @@ func GetNearSupplyGoodsStoreByStoreID(ctx *jxcontext.Context, storeID int) (stor
|
||||
stores []*model.Store
|
||||
db = dao.GetDB()
|
||||
)
|
||||
store2, _ := dao.GetStoreDetail(db, storeID, model.VendorIDJX)
|
||||
store2, _ := dao.GetStoreDetail(db, storeID, model.VendorIDJX, "")
|
||||
if store2 == nil {
|
||||
return nil, fmt.Errorf("该门店未绑定京西平台!storeID: %v", storeID)
|
||||
}
|
||||
@@ -109,7 +109,7 @@ func GetNearSupplyGoodsStoreByStoreID(ctx *jxcontext.Context, storeID int) (stor
|
||||
return store, err
|
||||
}
|
||||
|
||||
func GetStoreListByLocation(ctx *jxcontext.Context, lng, lat float64, maxRadius int, needWalkDistance, isJds bool) (storeList []*Store4User, err error) {
|
||||
func GetStoreListByLocation(ctx *jxcontext.Context, lng, lat float64, maxRadius int, needWalkDistance, isJds bool, brandID int) (storeList []*Store4User, err error) {
|
||||
const (
|
||||
maxStoreCount4User = 5
|
||||
)
|
||||
@@ -134,14 +134,20 @@ func GetStoreListByLocation(ctx *jxcontext.Context, lng, lat float64, maxRadius
|
||||
WHERE t1.deleted_at = ? AND t1.status <> ? AND t1.lng > ? AND t1.lng < ? AND t1.lat > ? AND t1.lat < ?
|
||||
AND sm.is_order <> ?
|
||||
AND t1.id <> ?
|
||||
ORDER BY t1.id
|
||||
`
|
||||
sqlParams = []interface{}{
|
||||
sqlParams = append(sqlParams,
|
||||
model.VendorIDJX, utils.DefaultTimeValue, model.StoreStatusDisabled,
|
||||
utils.DefaultTimeValue, model.StoreStatusDisabled, jxutils.StandardCoordinate2Int(lng1), jxutils.StandardCoordinate2Int(lng2), jxutils.StandardCoordinate2Int(lat1), jxutils.StandardCoordinate2Int(lat2),
|
||||
model.YES,
|
||||
model.MatterStoreID,
|
||||
)
|
||||
if brandID != 0 {
|
||||
sql += " AND t1.brand_id = ?"
|
||||
sqlParams = append(sqlParams, brandID)
|
||||
}
|
||||
sql += `
|
||||
ORDER BY t1.id
|
||||
`
|
||||
} else {
|
||||
sql = `
|
||||
SELECT t1.*,
|
||||
@@ -179,12 +185,12 @@ func GetStoreListByLocation(ctx *jxcontext.Context, lng, lat float64, maxRadius
|
||||
city.name city_name
|
||||
FROM store t1
|
||||
JOIN place city ON city.code = t1.city_code
|
||||
WHERE t1.deleted_at = ? AND t1.status <> ? AND t1.id = ?
|
||||
WHERE t1.deleted_at = ? AND t1.id = ?
|
||||
`
|
||||
sqlParams2 := []interface{}{
|
||||
// model.VendorIDJX, utils.DefaultTimeValue, model.StoreStatusDisabled,
|
||||
utils.DefaultTimeValue,
|
||||
model.StoreStatusDisabled,
|
||||
// model.StoreStatusDisabled,
|
||||
// jxutils.StandardCoordinate2Int(0),
|
||||
// jxutils.StandardCoordinate2Int(10000),
|
||||
// jxutils.StandardCoordinate2Int(0),
|
||||
@@ -224,3 +230,60 @@ func GetStoreListByLocation(ctx *jxcontext.Context, lng, lat float64, maxRadius
|
||||
}
|
||||
return storeList, err
|
||||
}
|
||||
|
||||
func GetVendorOrgCode(ctx *jxcontext.Context, vendorID int, vendorOrgCode, vendorType string) (vendorOrgs []*model.VendorOrgCode, err error) {
|
||||
return dao.GetVendorOrgCode(dao.GetDB(), vendorID, vendorOrgCode, vendorType)
|
||||
}
|
||||
|
||||
func UpdateVendorOrgCode(ctx *jxcontext.Context, ID int, payload map[string]interface{}) (err error) {
|
||||
var (
|
||||
db = dao.GetDB()
|
||||
vendorOrgCode = &model.VendorOrgCode{}
|
||||
)
|
||||
vendorOrgCode.ID = ID
|
||||
err = dao.GetEntity(db, vendorOrgCode)
|
||||
valid := dao.StrictMakeMapByStructObject(payload, vendorOrgCode, ctx.GetUserName())
|
||||
if len(valid) > 0 {
|
||||
if valid["key"] != nil {
|
||||
valid["vendorOrgCode"] = valid["key"]
|
||||
delete(valid, "key")
|
||||
}
|
||||
if valid["code"] != nil {
|
||||
valid["vendorID"] = valid["code"]
|
||||
delete(valid, "code")
|
||||
}
|
||||
if valid["name"] != nil {
|
||||
valid["comment"] = valid["name"]
|
||||
delete(valid, "name")
|
||||
}
|
||||
txDB, _ := dao.Begin(db)
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
dao.Rollback(db, txDB)
|
||||
panic(r)
|
||||
}
|
||||
}()
|
||||
if _, err = dao.UpdateEntityLogically(db, vendorOrgCode, valid, ctx.GetUserName(), nil); err != nil {
|
||||
dao.Rollback(db, txDB)
|
||||
return err
|
||||
}
|
||||
dao.Commit(db, txDB)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func AddVendorOrgCode(ctx *jxcontext.Context, vendorOrgCode *model.VendorOrgCode) (err error) {
|
||||
var (
|
||||
db = dao.GetDB()
|
||||
)
|
||||
list, err := dao.GetVendorOrgCode(db, vendorOrgCode.VendorID, vendorOrgCode.VendorOrgCode, model.VendorOrgTypePlatform)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(list) > 0 {
|
||||
return fmt.Errorf("库里有这个账号了,[%v]", vendorOrgCode.VendorOrgCode)
|
||||
}
|
||||
dao.WrapAddIDCULDEntity(vendorOrgCode, ctx.GetUserName())
|
||||
dao.CreateEntity(db, vendorOrgCode)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -1,29 +1,33 @@
|
||||
package event
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
"git.rosy.net.cn/jx-callback/business/dao"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
|
||||
"git.rosy.net.cn/jx-callback/business/model"
|
||||
"git.rosy.net.cn/jx-callback/business/model/dao"
|
||||
"git.rosy.net.cn/jx-callback/globals"
|
||||
"git.rosy.net.cn/jx-callback/globals/api"
|
||||
)
|
||||
|
||||
var (
|
||||
NoUseEventMap = map[string]string{
|
||||
"CreateQrOrBarCode": "CreateQrOrBarCode",
|
||||
"StatisticsReportForOrders": "StatisticsReportForOrders",
|
||||
"UpdateUser": "UpdateUser",
|
||||
}
|
||||
regexpToken = regexp.MustCompile(`,"token":".*"`)
|
||||
)
|
||||
|
||||
const (
|
||||
sysMessageTitle = ""
|
||||
)
|
||||
type CheckCookie struct {
|
||||
VendorID int `json:"vendorID"`
|
||||
VendorOrgCode string `json:"vendorOrgCode"`
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
func AddOperateEvent(ctx *jxcontext.Context, accessUUID, jsonData string, errCode, errMsg string, useTime int, apiFunctionSpec string) (err error) {
|
||||
var (
|
||||
@@ -63,7 +67,7 @@ func AddOperateEvent(ctx *jxcontext.Context, accessUUID, jsonData string, errCod
|
||||
ErrMsg: errMsg,
|
||||
UseTime: useTime,
|
||||
}
|
||||
txDB, _ := dao.Begin(db)
|
||||
txDB , _ := dao.Begin(db)
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
dao.Rollback(db, txDB)
|
||||
@@ -76,7 +80,7 @@ func AddOperateEvent(ctx *jxcontext.Context, accessUUID, jsonData string, errCod
|
||||
}
|
||||
|
||||
func AddOperateEventDetail(db *dao.DaoDB, operateEventDetail *model.OperateEventDetail) (err error) {
|
||||
txDB, _ := dao.Begin(db)
|
||||
txDB , _ := dao.Begin(db)
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
dao.Rollback(db, txDB)
|
||||
@@ -92,7 +96,7 @@ func AddOperateEventDetail(db *dao.DaoDB, operateEventDetail *model.OperateEvent
|
||||
|
||||
func DeleteOperateEventAndDetail(ctx *jxcontext.Context, deleteTime time.Time) (err error) {
|
||||
db := dao.GetDB()
|
||||
txDB, _ := dao.Begin(db)
|
||||
txDB , _ := dao.Begin(db)
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
dao.Rollback(db, txDB)
|
||||
@@ -129,3 +133,69 @@ func GetOperateEvents(ctx *jxcontext.Context, name string, apiFunctions []string
|
||||
}
|
||||
return pageInfo, err
|
||||
}
|
||||
|
||||
func GetCheckVendorCookie(ctx *jxcontext.Context, vendorIDs []int, isAuto bool) (ccList []*CheckCookie, err error) {
|
||||
var (
|
||||
ebaiOrderID = "1577329467196263592"
|
||||
ebaiErr = "return not json"
|
||||
ebaiErr2 = "系统错误"
|
||||
mtStoreID = "7388603"
|
||||
mtErr = "返回结果格式不正常"
|
||||
jdUpcCode = "6952395700895"
|
||||
jdErr = "请输入用户名"
|
||||
// mtpsErr = "用户未登录"
|
||||
errMsg = ""
|
||||
)
|
||||
for _, v := range vendorIDs {
|
||||
cc := &CheckCookie{}
|
||||
var flag = false
|
||||
switch v {
|
||||
case model.VendorIDEBAI:
|
||||
resultMap, err := api.EbaiAPI.GetStoreOrderInfo(ebaiOrderID)
|
||||
if len(resultMap) < 1 && err != nil {
|
||||
if strings.Contains(err.Error(), ebaiErr) || strings.Contains(err.Error(), ebaiErr2) {
|
||||
errMsg += fmt.Sprintf(" 饿百账号:[%v]的Cookie无效了!")
|
||||
flag = true
|
||||
}
|
||||
}
|
||||
case model.VendorIDMTWM:
|
||||
_, err := api.MtwmAPI.PackagePriceGet(mtStoreID)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), mtErr) {
|
||||
errMsg += fmt.Sprintf(" 美团账号:[%v]的Cookie无效了!")
|
||||
flag = true
|
||||
}
|
||||
globals.SugarLogger.Debugf("cookieCheck", err)
|
||||
}
|
||||
case model.VendorIDJD:
|
||||
result, err := api.JdAPI.GetJdUpcCodeByName("", jdUpcCode, 1, 5)
|
||||
if len(result) < 1 && err != nil {
|
||||
if strings.Contains(err.Error(), jdErr) {
|
||||
errMsg += fmt.Sprintf(" 京东账号:[%v]的Cookie无效了!")
|
||||
flag = true
|
||||
}
|
||||
}
|
||||
case model.VendorIDJDShop:
|
||||
_, err := api.JdShopAPI.OrderDetail("124350112427")
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "登录") {
|
||||
errMsg += fmt.Sprintf("京东商城:[%v]的Cookie无效了!")
|
||||
flag = true
|
||||
}
|
||||
globals.SugarLogger.Debugf("cookieCheck", err)
|
||||
}
|
||||
}
|
||||
cc.VendorID = v
|
||||
cc.VendorOrgCode = ""
|
||||
if flag {
|
||||
cc.Status = "无效"
|
||||
} else {
|
||||
cc.Status = "有效"
|
||||
}
|
||||
ccList = append(ccList, cc)
|
||||
}
|
||||
if isAuto && errMsg != "" {
|
||||
globals.SugarLogger.Warnf("GetCheckVendorCookie:[%v]", errMsg)
|
||||
}
|
||||
return ccList, err
|
||||
}
|
||||
|
||||
119
business/jxstore/financial/financial.go
Normal file
119
business/jxstore/financial/financial.go
Normal file
@@ -0,0 +1,119 @@
|
||||
package financial
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"mime/multipart"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/tasksch"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/weixinmsg"
|
||||
"git.rosy.net.cn/jx-callback/business/model/dao"
|
||||
"git.rosy.net.cn/jx-callback/business/model/legacymodel"
|
||||
"git.rosy.net.cn/jx-callback/globals"
|
||||
"git.rosy.net.cn/jx-callback/globals/api"
|
||||
"github.com/qiniu/api.v7/storage"
|
||||
)
|
||||
|
||||
type tUploadFileInfo struct {
|
||||
FileHeader *multipart.FileHeader
|
||||
StoreID int
|
||||
}
|
||||
|
||||
func SendFilesToStores(ctx *jxcontext.Context, files []*multipart.FileHeader, title, shopName string, isAsync bool, userName string) (hint string, err error) {
|
||||
globals.SugarLogger.Debugf("SendFilesToStores, fileCount:%d isAsync:%t, userName:%s", len(files), isAsync, userName)
|
||||
if len(files) == 0 {
|
||||
return "", errors.New("没有文件上传!")
|
||||
}
|
||||
|
||||
fileList := make([]*tUploadFileInfo, len(files))
|
||||
for k, fileHeader := range files {
|
||||
fileList[k] = &tUploadFileInfo{
|
||||
FileHeader: fileHeader,
|
||||
}
|
||||
fileNameParts := strings.Split(fileHeader.Filename, "_")
|
||||
if len(fileNameParts) < 3 {
|
||||
return "", fmt.Errorf("文件名:%s不规范,没有包含三个必要的部分", fileHeader.Filename)
|
||||
}
|
||||
fileList[k].StoreID = int(utils.Str2Int64WithDefault(fileNameParts[0], 0))
|
||||
if fileList[k].StoreID < 100000 || fileList[k].StoreID > 1000000 {
|
||||
return "", fmt.Errorf("文件名:%s不规范,不以合法的京西门店ID开始", fileHeader.Filename)
|
||||
}
|
||||
}
|
||||
putPolicy := storage.PutPolicy{
|
||||
Scope: globals.QiniuBucket,
|
||||
Expires: 10 * 60,
|
||||
}
|
||||
upToken := putPolicy.UploadToken(api.QiniuAPI)
|
||||
cfg := &storage.Config{}
|
||||
task := tasksch.NewParallelTask("SendFilesToStores", tasksch.NewParallelConfig().SetIsContinueWhenError(true), ctx,
|
||||
func(t *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
|
||||
fileInfo := batchItemList[0].(*tUploadFileInfo)
|
||||
fileHeader := fileInfo.FileHeader
|
||||
db := dao.GetDB()
|
||||
storeID, _ := dao.GetRealLinkStoreID(db, fileInfo.StoreID)
|
||||
file, err := fileHeader.Open()
|
||||
globals.SugarLogger.Debugf("SendFilesToStores upload file:%s", fileHeader.Filename)
|
||||
if err == nil {
|
||||
ret := storage.PutRet{}
|
||||
key := "storeBill/" + utils.Int2Str(storeID) + "_" + strings.ToLower(utils.GetUUID()) + path.Ext(fileHeader.Filename)
|
||||
formUploader := storage.NewFormUploader(cfg)
|
||||
for i := 0; i < 3; i++ {
|
||||
if err = formUploader.Put(context.Background(), &ret, upToken, key, file, fileHeader.Size, &storage.PutExtra{}); err == nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
file.Close()
|
||||
if err == nil {
|
||||
billRec := &legacymodel.StoreBill{
|
||||
Date: time.Now(),
|
||||
Url: strings.Replace(jxutils.ComposeQiniuResURL(ret.Key), "http://", "https://", -1),
|
||||
StoreId: storeID,
|
||||
BillName: fileHeader.Filename,
|
||||
ShopName: shopName,
|
||||
BillTitle: title,
|
||||
}
|
||||
if err = dao.CreateEntity(db, billRec); err == nil {
|
||||
err = weixinmsg.NotifySaleBill(storeID, title, shopName, fmt.Sprintf("%s/billshow/?path=%s", globals.BackstageHost, billRec.Url))
|
||||
if err != nil {
|
||||
globals.SugarLogger.Infof("SendFilesToStores NotifySaleBill file:%s error:%v", fileHeader.Filename, err)
|
||||
}
|
||||
err = nil // 忽略微信发送错误
|
||||
} else {
|
||||
globals.SugarLogger.Warnf("SendFilesToStores CreateEntity file:%s error:%v", fileHeader.Filename, err)
|
||||
}
|
||||
} else {
|
||||
globals.SugarLogger.Warnf("SendFilesToStores file:%s failed with error:%v", fileHeader.Filename, err)
|
||||
}
|
||||
} else {
|
||||
globals.SugarLogger.Warnf("SendFilesToStores open file:%s failed with error:%v", fileHeader.Filename, err)
|
||||
}
|
||||
return retVal, err
|
||||
}, fileList)
|
||||
tasksch.HandleTask(task, nil, true).Run()
|
||||
hint = task.ID
|
||||
if !isAsync {
|
||||
_, err = task.GetResult(0)
|
||||
}
|
||||
return task.ID, err
|
||||
}
|
||||
|
||||
func GetStoreBills(ctx *jxcontext.Context, storeID int) (bills []*legacymodel.StoreBill, err error) {
|
||||
db := dao.GetDB()
|
||||
if err = dao.GetRows(db, &bills, `
|
||||
SELECT *
|
||||
FROM store_bill
|
||||
WHERE store_id = ?
|
||||
ORDER BY date DESC
|
||||
LIMIT 10
|
||||
`, storeID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return bills, err
|
||||
}
|
||||
278
business/jxstore/initdata/initdata.go
Normal file
278
business/jxstore/initdata/initdata.go
Normal file
@@ -0,0 +1,278 @@
|
||||
package initdata
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.rosy.net.cn/baseapi/platformapi/autonavi"
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/tasksch"
|
||||
"git.rosy.net.cn/jx-callback/business/model"
|
||||
"git.rosy.net.cn/jx-callback/business/model/dao"
|
||||
"git.rosy.net.cn/jx-callback/business/partner"
|
||||
"git.rosy.net.cn/jx-callback/globals/api"
|
||||
)
|
||||
|
||||
func TruncateTable(db *dao.DaoDB, tableName string) (err error) {
|
||||
_, err = dao.ExecuteSQL(db, "TRUNCATE TABLE "+tableName)
|
||||
return err
|
||||
}
|
||||
|
||||
func insertPlace(ctx *jxcontext.Context, db *dao.DaoDB, parent *autonavi.District, placeList []*autonavi.District) (err error) {
|
||||
for _, v := range placeList {
|
||||
if v.Level <= autonavi.DistrictLevelDistrict {
|
||||
place := &model.Place{
|
||||
Code: int(utils.Str2Int64(v.Adcode)),
|
||||
Name: v.Name,
|
||||
Level: int8(v.Level),
|
||||
TelCode: v.CityCode,
|
||||
Enabled: 1,
|
||||
}
|
||||
if parent != nil {
|
||||
place.ParentCode = int(utils.Str2Int64(parent.Adcode))
|
||||
}
|
||||
dao.WrapAddIDCULEntity(place, ctx.GetUserName())
|
||||
if err = dao.CreateEntity(db, place); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = insertPlace(ctx, db, v, v.Districts); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func InitPlace(ctx *jxcontext.Context) (err error) {
|
||||
placeList, err2 := api.AutonaviAPI.GetDistricts(autonavi.DistrictLevelDistrict, "")
|
||||
if err = err2; err != nil {
|
||||
return err
|
||||
}
|
||||
placeList = placeList[0].Districts
|
||||
db := dao.GetDB()
|
||||
txDB , _ := dao.Begin(db)
|
||||
defer func() {
|
||||
if r := recover(); r != nil || err != nil {
|
||||
dao.Rollback(db, txDB)
|
||||
if r != nil {
|
||||
panic(r)
|
||||
}
|
||||
}
|
||||
}()
|
||||
if _, err = dao.ExecuteSQL(db, `
|
||||
DELETE t1
|
||||
FROM place t1
|
||||
WHERE code < 9000000;
|
||||
`); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = insertPlace(ctx, db, nil, placeList); err != nil {
|
||||
return err
|
||||
}
|
||||
updateSqls := []string{
|
||||
`
|
||||
UPDATE place t1
|
||||
JOIN jde_city t2 ON t1.code = t2.col_tencentAddressCode
|
||||
SET t1.jd_code = t2.col_areaCode;
|
||||
`,
|
||||
`
|
||||
UPDATE place t1
|
||||
JOIN place t2 ON t1.parent_code = t2.code AND t2.jd_code != 0
|
||||
JOIN jde_district t3 ON t1.name = t3.col_areaName AND t2.jd_code = t3.col_cityCode
|
||||
SET t1.jd_code = t3.col_areaCode
|
||||
WHERE t1.level = 3;
|
||||
`,
|
||||
`
|
||||
UPDATE
|
||||
place t1
|
||||
JOIN ebde_places t2 ON t1.name = t2.col_city_name
|
||||
SET t1.ebai_code = t2.col_city_id
|
||||
WHERE t1.level = 1 OR t1.level = 2;
|
||||
`,
|
||||
`
|
||||
UPDATE
|
||||
place t1
|
||||
JOIN place t1p ON t1.parent_code = t1p.code
|
||||
JOIN ebde_places t2 ON t1.name = t2.col_city_name
|
||||
JOIN ebde_places t2p ON t2.col_parent_id = t2p.col_city_id AND t1p.ebai_code = t2p.col_city_id
|
||||
SET t1.ebai_code = t2.col_city_id
|
||||
WHERE t1.level = 3;
|
||||
`,
|
||||
`
|
||||
UPDATE
|
||||
place t1
|
||||
JOIN mtpsdeliveryprice t2 ON t1.code = t2.citycode
|
||||
SET t1.mtps_price = t2.price;
|
||||
`,
|
||||
`
|
||||
UPDATE
|
||||
place t1
|
||||
JOIN mtpsdeliveryprice t2 ON t1.name LIKE CONCAT(t2.cityname, '%')
|
||||
SET t1.mtps_price = t2.price
|
||||
WHERE t1.level = 2 AND t1.mtps_price = 0;
|
||||
`,
|
||||
`
|
||||
UPDATE place t1
|
||||
LEFT JOIN (
|
||||
SELECT DISTINCT city_code
|
||||
FROM store
|
||||
UNION DISTINCT
|
||||
SELECT DISTINCT place_code city_code
|
||||
FROM sku_name_place_bind
|
||||
) t2 ON t1.code = t2.city_code
|
||||
SET t1.enabled = 0
|
||||
WHERE t1.level = 2 AND t2.city_code IS NULL;
|
||||
`,
|
||||
}
|
||||
for _, v := range updateSqls {
|
||||
if _, err = dao.ExecuteSQL(db, v); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
dao.Commit(db, txDB)
|
||||
return err
|
||||
}
|
||||
|
||||
func InitVendorCategory(ctx *jxcontext.Context, vendorID int, isAsync bool) (hint string, err error) {
|
||||
if handler := partner.GetPurchasePlatformFromVendorID(vendorID); handler != nil {
|
||||
var cats []*model.SkuVendorCategory
|
||||
rootTask := tasksch.NewSeqTask(fmt.Sprintf("创建%s的平台分类", model.VendorChineseNames[vendorID]), ctx,
|
||||
func(task *tasksch.SeqTask, step int, params ...interface{}) (result interface{}, err error) {
|
||||
switch step {
|
||||
case 0:
|
||||
cats, err = handler.GetVendorCategories(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case 1:
|
||||
db := dao.GetDB()
|
||||
txDB , _ := dao.Begin(db)
|
||||
defer func() {
|
||||
if r := recover(); r != nil || err != nil {
|
||||
dao.Rollback(db, txDB)
|
||||
if r != nil {
|
||||
panic(r)
|
||||
}
|
||||
}
|
||||
}()
|
||||
sql := `
|
||||
DELETE
|
||||
FROM sku_vendor_category
|
||||
WHERE vendor_id = ?
|
||||
`
|
||||
if _, err = dao.ExecuteSQL(db, sql, vendorID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, cat := range cats {
|
||||
dao.WrapAddIDCULEntity(cat, ctx.GetUserName())
|
||||
}
|
||||
if err = dao.CreateMultiEntities(db, cats); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dao.Commit(db, txDB)
|
||||
}
|
||||
return nil, err
|
||||
}, 2)
|
||||
tasksch.HandleTask(rootTask, nil, true).Run()
|
||||
if !isAsync {
|
||||
if _, err = rootTask.GetResult(0); err == nil {
|
||||
hint = utils.Int2Str(len(cats))
|
||||
}
|
||||
} else {
|
||||
hint = rootTask.ID
|
||||
}
|
||||
} else {
|
||||
err = fmt.Errorf("找不到平台:%d", vendorID)
|
||||
}
|
||||
return hint, err
|
||||
}
|
||||
|
||||
func UploadImg4Vendors(ctx *jxcontext.Context, isAsync, isContinueWhenError bool) (hint string, err error) {
|
||||
// db := dao.GetDB()
|
||||
// rootTask := tasksch.NewSeqTask("合并SkuName图片至DataResource", ctx,
|
||||
// func(task *tasksch.SeqTask, step int, params ...interface{}) (result interface{}, err error) {
|
||||
// switch step {
|
||||
// case 0: // 计算SkuName中缺失的hashCode(注意是DescImg不是Img)
|
||||
// var skuNameList []*model.SkuName
|
||||
// if err = dao.GetRows(db, &skuNameList, `
|
||||
// SELECT t1.*
|
||||
// FROM sku_name t1
|
||||
// WHERE t1.desc_img <> '' AND t1.img_hash_code = ''
|
||||
// `); err == nil && len(skuNameList) > 0 {
|
||||
// calcTask := tasksch.NewParallelTask("UploadImg4Vendors calc hashCode",
|
||||
// tasksch.NewParallelConfig().SetIsContinueWhenError(isContinueWhenError).SetParallelCount(5), ctx,
|
||||
// func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
|
||||
// skuName := batchItemList[0].(*model.SkuName)
|
||||
// _, skuName.ImgHashCode, err = jxutils.DownloadFileByURL(skuName.DescImg)
|
||||
// if err == nil {
|
||||
// dao.UpdateEntity(db, skuName, "ImgHashCode")
|
||||
// }
|
||||
// return retVal, err
|
||||
// }, skuNameList)
|
||||
// tasksch.HandleTask(calcTask, task, false).Run()
|
||||
// _, err = calcTask.GetResult(0)
|
||||
// }
|
||||
// case 1: // 从SkuName添加缺失的图片至DataResource
|
||||
// _, err = dao.ExecuteSQL(db, `
|
||||
// INSERT INTO data_resource(created_at, updated_at, last_operator, hash_code,
|
||||
// resource_type, name, main_url, ebai_url, qiniu_url, use_type)
|
||||
// SELECT t1.created_at, t1.created_at, t1.last_operator, t1.img_hash_code,
|
||||
// CASE
|
||||
// WHEN INSTR(t1.desc_img, ".jpg") > 0 OR INSTR(t1.desc_img, ".jpeg") > 0 THEN
|
||||
// 'image/jpeg'
|
||||
// WHEN INSTR(t1.desc_img, ".png") > 0 OR INSTR(t1.desc_img, ".peg") > 0 THEN
|
||||
// 'image/png'
|
||||
// WHEN INSTR(t1.desc_img, ".gif") THEN
|
||||
// 'image/gif'
|
||||
// ELSE
|
||||
// ''
|
||||
// END resource_type,
|
||||
// CONCAT(t1.name, '_desc'), desc_img main_url, t1.desc_img_ebai ebai_url,
|
||||
// IF(INSTR(t1.desc_img, "image.jxc4.com") > 0, t1.desc_img, '') qiniu_url, 2
|
||||
// FROM sku_name t1
|
||||
// JOIN (
|
||||
// SELECT img_hash_code, MAX(id) id, COUNT(*) ct
|
||||
// FROM sku_name
|
||||
// WHERE img_hash_code <> '' AND desc_img <> ''
|
||||
// GROUP BY 1
|
||||
// ) t3 ON t3.id = t1.id
|
||||
// LEFT JOIN data_resource t2 ON (t2.main_url <> '' AND t2.main_url = t1.desc_img)
|
||||
// WHERE t1.desc_img <> '' AND t1.img_hash_code <> '' AND t2.id IS NULL;
|
||||
// `)
|
||||
// case 2: // 统一SkuName中同hashCode,不同图片地址至同一地址
|
||||
// _, err = dao.ExecuteSQL(db, `
|
||||
// UPDATE sku_name t1
|
||||
// JOIN data_resource t2 ON t2.hash_code = t1.img_hash_code AND t2.main_url <> ''
|
||||
// SET t1.desc_img = t2.main_url
|
||||
// WHERE t1.img_hash_code <> '' AND t1.desc_img <> t2.main_url;
|
||||
// `)
|
||||
// case 3: // 上传DataResource中缺失的平台图片
|
||||
// dataResList, err2 := dao.GetNeedUploadDataResource(db)
|
||||
// if err = err2; err == nil && len(dataResList) > 0 {
|
||||
// uploadTask := tasksch.NewParallelTask("批量上传图片至平台",
|
||||
// tasksch.NewParallelConfig().SetIsContinueWhenError(isContinueWhenError).SetParallelCount(2), ctx,
|
||||
// func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
|
||||
// dataRes := batchItemList[0].(*model.DataResource)
|
||||
// _, err = datares.UploadImage2Vendors(ctx, task, dataRes, nil, false)
|
||||
// return nil, err
|
||||
// }, dataResList)
|
||||
// tasksch.HandleTask(uploadTask, task, true).Run()
|
||||
// _, err = uploadTask.GetResult(0)
|
||||
// }
|
||||
// }
|
||||
// return result, err
|
||||
// }, 4)
|
||||
// tasksch.HandleTask(rootTask, nil, true).Run()
|
||||
// if !isAsync {
|
||||
// if _, err = rootTask.GetResult(0); err == nil {
|
||||
// hint = "1"
|
||||
// }
|
||||
// } else {
|
||||
// hint = rootTask.ID
|
||||
// }
|
||||
return hint, err
|
||||
}
|
||||
|
||||
func getSkuNameKey(prefix, name, comment, specUnit, unit string, specQuality float32) string {
|
||||
return fmt.Sprintf("%s-%s-%f-%s-%s", prefix, name, specQuality, specUnit, unit)
|
||||
}
|
||||
119
business/jxstore/knowledge/knowledge.go
Normal file
119
business/jxstore/knowledge/knowledge.go
Normal file
@@ -0,0 +1,119 @@
|
||||
package knowledge
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"git.rosy.net.cn/baseapi/platformapi/weixinapi"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/weixinmsg"
|
||||
"git.rosy.net.cn/jx-callback/business/model"
|
||||
"git.rosy.net.cn/jx-callback/business/model/dao"
|
||||
"git.rosy.net.cn/jx-callback/globals/api"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func GetKnowledgeDepot(ctx *jxcontext.Context, keyword string, offset, pageSize int) (page *model.PagedInfo, err error) {
|
||||
return dao.GetKnowledgeDepot(dao.GetDB(), keyword, offset, pageSize)
|
||||
}
|
||||
func AddKnowledgeDepot(ctx *jxcontext.Context, title, content string) (err error) {
|
||||
var (
|
||||
db = dao.GetDB()
|
||||
)
|
||||
know := &model.KnowledgeDepot{
|
||||
Title: title,
|
||||
Content: content,
|
||||
}
|
||||
dao.WrapAddIDCULDEntity(know, ctx.GetUserName())
|
||||
dao.CreateEntity(db, know)
|
||||
return err
|
||||
}
|
||||
|
||||
func UpdateKnowledgeDepot(ctx *jxcontext.Context, id int, content string, isDel bool) (err error) {
|
||||
var (
|
||||
db = dao.GetDB()
|
||||
know = &model.KnowledgeDepot{}
|
||||
)
|
||||
know.ID = id
|
||||
err = dao.GetEntity(db, know)
|
||||
if isDel {
|
||||
dao.DeleteEntity(db, know)
|
||||
} else {
|
||||
if content != know.Content {
|
||||
know.Content = content
|
||||
dao.UpdateEntity(db, know, "Content")
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
type SNSUploadImgResult struct {
|
||||
URL string `json:"url"`
|
||||
MediaID string `json:"mediaID"`
|
||||
}
|
||||
|
||||
func SNSUploadImg(ctx *jxcontext.Context, files []*multipart.FileHeader, isThumb bool) (snsUploadImgResult *SNSUploadImgResult, err error) {
|
||||
var (
|
||||
fileHeader = files[0]
|
||||
fileName = fileHeader.Filename
|
||||
data = make([]byte, fileHeader.Size)
|
||||
)
|
||||
snsUploadImgResult = &SNSUploadImgResult{}
|
||||
file, err := fileHeader.Open()
|
||||
file.Read(data)
|
||||
if len(data) == 0 {
|
||||
return snsUploadImgResult, fmt.Errorf("未读取到文件!")
|
||||
}
|
||||
//如果不是缩略图,只会返url
|
||||
//如果是封面缩略图,则还会返mediaID
|
||||
if !isThumb {
|
||||
if url, err := api.WeixinAPI.CBUploadImg(data, fileName, http.DetectContentType(data)); err == nil && url != "" {
|
||||
snsUploadImgResult.URL = url
|
||||
}
|
||||
} else {
|
||||
if mediaID, url, err := api.WeixinAPI.CBUploadThumb(data, fileName, http.DetectContentType(data)); err == nil && url != "" && mediaID != "" {
|
||||
snsUploadImgResult.URL = url
|
||||
snsUploadImgResult.MediaID = mediaID
|
||||
}
|
||||
}
|
||||
file.Close()
|
||||
return snsUploadImgResult, err
|
||||
}
|
||||
|
||||
func AddMaterial(ctx *jxcontext.Context, knowIDs []int, param *weixinapi.CBAddNewsParam) (err error) {
|
||||
var (
|
||||
db = dao.GetDB()
|
||||
content = ""
|
||||
)
|
||||
if len(knowIDs) == 0 {
|
||||
return fmt.Errorf("请至少勾选一条!")
|
||||
}
|
||||
knows, err := dao.GetKnowledgeDepotNoPage(db, knowIDs)
|
||||
for k, v := range knows {
|
||||
if k != 0 {
|
||||
content += weixinapi.SNSCutLine
|
||||
}
|
||||
content += v.Content
|
||||
}
|
||||
if content != "" {
|
||||
param.Content = content
|
||||
api.WeixinAPI.CBAddNews(param)
|
||||
} else {
|
||||
return fmt.Errorf("读取内容错误!")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func SendSNSMediaMsg(ctx *jxcontext.Context, storeIDs []int, mediaID string) (err error) {
|
||||
var (
|
||||
openIDs []string
|
||||
)
|
||||
for _, v := range storeIDs {
|
||||
if results := weixinmsg.GetWeixinOpenIDsFromStoreID(v); len(results) > 0 {
|
||||
openIDs = append(openIDs, results...)
|
||||
}
|
||||
}
|
||||
if len(openIDs) > 0 {
|
||||
return weixinmsg.MediaMessageSendGroup(openIDs, mediaID)
|
||||
}
|
||||
return err
|
||||
}
|
||||
@@ -1,15 +1,623 @@
|
||||
package misc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"git.rosy.net.cn/jx-callback/business/partner/purchase/jdshop"
|
||||
|
||||
"git.rosy.net.cn/baseapi/platformapi/jdapi"
|
||||
|
||||
"git.rosy.net.cn/jx-callback/business/partner/purchase/jx/localjx"
|
||||
"git.rosy.net.cn/jx-callback/globals/api"
|
||||
|
||||
"git.rosy.net.cn/jx-callback/business/jxstore/report"
|
||||
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
"git.rosy.net.cn/baseapi/utils/errlist"
|
||||
"git.rosy.net.cn/jx-callback/business/jxcallback/orderman"
|
||||
"git.rosy.net.cn/jx-callback/business/jxcallback/scheduler/defsch"
|
||||
"git.rosy.net.cn/jx-callback/business/jxstore/act"
|
||||
"git.rosy.net.cn/jx-callback/business/jxstore/cms"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/netprinter"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/tasksch"
|
||||
"git.rosy.net.cn/jx-callback/business/model"
|
||||
"git.rosy.net.cn/jx-callback/business/model/dao"
|
||||
"git.rosy.net.cn/jx-callback/business/netspider"
|
||||
"git.rosy.net.cn/jx-callback/business/partner"
|
||||
"git.rosy.net.cn/jx-callback/globals"
|
||||
beego "github.com/astaxie/beego/server/web"
|
||||
)
|
||||
|
||||
func Init() {
|
||||
const (
|
||||
SpecialTaskID = "Running"
|
||||
TaskNameSyncStoreSku = "SyncStoreSku"
|
||||
)
|
||||
|
||||
var (
|
||||
dailyHeartbeat = []string{
|
||||
"09:00:00",
|
||||
}
|
||||
dailyWorkTimeList = []string{
|
||||
"21:30:00",
|
||||
}
|
||||
dailyWorkTimeList2 = []string{
|
||||
"2:00:00",
|
||||
}
|
||||
priceReferTimeList = []string{
|
||||
"03:00:00",
|
||||
}
|
||||
checkCookieList = []string{
|
||||
"08:00:00",
|
||||
"12:00:00",
|
||||
"18:00:00",
|
||||
}
|
||||
createStorePriceTimeList = []string{
|
||||
"04:00:00",
|
||||
}
|
||||
refreshPageActTimeList = []string{
|
||||
"7:00:00",
|
||||
"8:00:00",
|
||||
"9:00:00",
|
||||
"10:00:00",
|
||||
"11:00:00",
|
||||
"12:00:00",
|
||||
"13:00:00",
|
||||
"14:00:00",
|
||||
"15:00:00",
|
||||
"16:00:00",
|
||||
"17:00:00",
|
||||
"18:00:00",
|
||||
"19:00:00",
|
||||
"22:00:00",
|
||||
}
|
||||
ChangeStoreSkuSaleStatusList = []string{
|
||||
"7:00:00",
|
||||
"8:00:00",
|
||||
"9:00:00",
|
||||
"10:00:00",
|
||||
"11:00:00",
|
||||
"12:00:00",
|
||||
"13:00:00",
|
||||
"14:00:00",
|
||||
"15:00:00",
|
||||
"16:00:00",
|
||||
"17:00:00",
|
||||
"18:00:00",
|
||||
"19:00:00",
|
||||
"20:00:00",
|
||||
}
|
||||
openRemoteStoreTimeList = []string{
|
||||
"04:30:00",
|
||||
"23:30:00",
|
||||
}
|
||||
updateActStatusTimeList = []string{
|
||||
"00:01:00",
|
||||
}
|
||||
sendSecKillWarnList = []string{
|
||||
"9:00:00",
|
||||
}
|
||||
autoPayForPopluarManList = []string{
|
||||
"10:30:00",
|
||||
}
|
||||
|
||||
autoSaleStoreSkuTimeList = []string{
|
||||
cms.AutoSaleAtStr,
|
||||
}
|
||||
|
||||
backUpStoreSkuBindList = []string{
|
||||
"23:30:00",
|
||||
}
|
||||
|
||||
exSyncList = []string{
|
||||
"11:30:00",
|
||||
}
|
||||
|
||||
importantTaskMap = &sync.Map{}
|
||||
|
||||
cancelPayTimeOutOrderList = localjx.GetHalfHoursList()
|
||||
discountActJxList = localjx.GetDiscountActHoursList()
|
||||
|
||||
//ebaiStorePageCookieExdTOKEN string
|
||||
//ebaiStorePageCookieWMUSS2 string
|
||||
//ebaiStorePageCookieWMSTOKEN2 string
|
||||
ebaiStorePageCookie string
|
||||
//ebaiStorePageCookieWMSTOKEN string
|
||||
mtwmCookieStr string
|
||||
mtpsStoreToken string
|
||||
jd2StorePageCookie string
|
||||
JdStorePageCookie string
|
||||
yinbaoCookie string
|
||||
feiePageCookie string
|
||||
jdStorePageEarning string
|
||||
//jdsCookie string
|
||||
// jdsCookie2 string
|
||||
)
|
||||
|
||||
func GetImportantTaskID(taskName string) string {
|
||||
if value, ok := importantTaskMap.Load(taskName); ok {
|
||||
return value.(string)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func SaveImportantTaskID(taskName, taskID string) {
|
||||
importantTaskMap.Store(taskName, taskID)
|
||||
}
|
||||
|
||||
func IsImportantTaskRunning(taskName string) bool {
|
||||
taskID := GetImportantTaskID(taskName)
|
||||
if taskID == "" {
|
||||
return false
|
||||
} else if taskID == SpecialTaskID {
|
||||
return true
|
||||
}
|
||||
return tasksch.IsTaskRunning(taskID)
|
||||
}
|
||||
|
||||
func Init() {
|
||||
if globals.IsProductEnv() {
|
||||
//ScheduleTimerFunc("doDailyWork2", doDailyWork2, dailyWorkTimeList2)
|
||||
|
||||
//京东的订单信息解密密钥获取
|
||||
ScheduleTimerFuncByInterval(func() {
|
||||
jdshop.InitKey()
|
||||
}, 10*time.Second, 8*time.Hour)
|
||||
|
||||
ScheduleTimerFuncByInterval(func() {
|
||||
RefreshRealMobile(jxcontext.AdminCtx, model.VendorIDEBAI, time.Now().Add(-24*time.Hour), utils.DefaultTimeValue, false, true)
|
||||
report.RefreshStoreManageState(jxcontext.AdminCtx, nil, []int{model.VendorIDJD})
|
||||
}, 5*time.Second, 1*time.Hour)
|
||||
|
||||
ScheduleTimerFuncByInterval(func() {
|
||||
defsch.FixedScheduler.ConfirmSelfTakeOrders(jxcontext.AdminCtx, []int{model.VendorIDJD}, time.Now().Add(-48*time.Hour), time.Now().Add(-30*time.Minute), true, true)
|
||||
}, 5*time.Second, 10*time.Minute)
|
||||
|
||||
ScheduleTimerFuncByInterval(func() {
|
||||
report.RefreshStoreManageState(jxcontext.AdminCtx, nil, []int{model.VendorIDMTWM, model.VendorIDEBAI})
|
||||
}, 5*time.Second, 40*time.Minute)
|
||||
|
||||
ScheduleTimerFuncByInterval(func() {
|
||||
curDate := utils.Time2Date(time.Now())
|
||||
orderman.FixedOrderManager.AmendMissingOrders(jxcontext.AdminCtx, []int{model.VendorIDJD, model.VendorIDMTWM, model.VendorIDEBAI}, 0, curDate, curDate, true, true)
|
||||
orderman.SaveJdsOrders(jxcontext.AdminCtx, time.Now().Add(-30*time.Minute), time.Now())
|
||||
}, 5*time.Second, 10*time.Minute)
|
||||
|
||||
ScheduleTimerFunc("RefreshJdShopOrdersEarningPrice", func() {
|
||||
orderman.RefreshJdShopOrdersEarningPrice(jxcontext.AdminCtx, utils.Time2Str(time.Now().AddDate(0, 0, -2)), utils.Time2Str(time.Now().AddDate(0, 0, -2)))
|
||||
orderman.RefreshJdShopOrdersEarningPrice(jxcontext.AdminCtx, utils.Time2Str(time.Now().AddDate(0, 0, -1)), utils.Time2Str(time.Now().AddDate(0, 0, -1)))
|
||||
}, []string{
|
||||
"05:00:00",
|
||||
})
|
||||
|
||||
ScheduleTimerFunc("auto enable remote store", func() {
|
||||
cms.EnableHaveRestStores(jxcontext.AdminCtx, false, true)
|
||||
// cms.OpenRemoteStoreByJxStatus(jxcontext.AdminCtx, nil, nil, false, false, true)
|
||||
}, openRemoteStoreTimeList)
|
||||
|
||||
// ScheduleTimerFunc("SaveAndSendAlarmVendorSnapshot", func() {
|
||||
// cms.SaveAndSendAlarmVendorSnapshot(jxcontext.AdminCtx, nil, nil, false)
|
||||
// }, cms.WatchVendorStoreTimeList)
|
||||
|
||||
ScheduleTimerFunc("RefreshPageActs", func() {
|
||||
act.RefreshPageActs(jxcontext.AdminCtx, []int{model.VendorIDEBAI}, time.Now().Add(-30*24*time.Hour), false)
|
||||
}, refreshPageActTimeList)
|
||||
ScheduleTimerFunc("UpdateActStatusByTime", func() {
|
||||
dao.UpdateActStatusByTime(dao.GetDB(), time.Now())
|
||||
}, updateActStatusTimeList)
|
||||
ScheduleScoreStore()
|
||||
// ScheduleCheckStoreAlert()
|
||||
ScheduleTimerFunc("ChangeStoreSkuSaleStatus", func() {
|
||||
cms.CurVendorSync.ChangeStoreSkuSaleStatus(jxcontext.AdminCtx, 0, true, true)
|
||||
}, ChangeStoreSkuSaleStatusList)
|
||||
ScheduleTimerFunc("BeginSavePriceRefer", func() {
|
||||
report.BeginSavePriceRefer(jxcontext.AdminCtx, nil, nil, true, true)
|
||||
}, priceReferTimeList)
|
||||
ScheduleTimerFunc("CreateStorePriceScore", func() {
|
||||
cms.CreateStorePriceScore(jxcontext.AdminCtx)
|
||||
}, createStorePriceTimeList)
|
||||
ScheduleTimerFunc("AutoFocusStoreSkusForTopSkus", func() {
|
||||
cms.AutoFocusStoreSkusForTopSkus(jxcontext.AdminCtx, true, true)
|
||||
}, createStorePriceTimeList)
|
||||
//ScheduleTimerFunc("GetCheckVendorCookie", func() {
|
||||
// event.GetCheckVendorCookie(jxcontext.AdminCtx, []int{model.VendorIDEBAI, model.VendorIDJD, model.VendorIDMTWM, model.VendorIDMTPS, model.VendorIDJDShop}, true)
|
||||
//}, checkCookieList)
|
||||
//ScheduleTimerFunc("SendSeckillSkusCountMsg", func() {
|
||||
// cms.SendSeckillSkusCountMsg(jxcontext.AdminCtx, []int{model.VendorIDEBAI, model.VendorIDJD, model.VendorIDMTWM}, false, true)
|
||||
//}, sendSecKillWarnList)
|
||||
// ScheduleTimerFunc("每日报警心跳", func() {
|
||||
// globals.SugarLogger.Warnf("每日报警心跳,这不是报警。启动时间:%s", cms.GetServiceInfo(jxcontext.AdminCtx)["startupTime"])
|
||||
// }, dailyHeartbeat)
|
||||
ScheduleTimerFunc("AutoPayForPopluarMan", func() {
|
||||
localjx.AutoPayForPopluarMan(jxcontext.AdminCtx)
|
||||
}, autoPayForPopluarManList)
|
||||
ScheduleTimerFunc("CancelPayTimeOutOrder", func() {
|
||||
localjx.CancelPayTimeOutOrder(jxcontext.AdminCtx)
|
||||
}, cancelPayTimeOutOrderList)
|
||||
ScheduleTimerFunc("BackUpStoreSkuBind", func() {
|
||||
cms.BackUpStoreSkuBind(jxcontext.AdminCtx, true, true)
|
||||
}, backUpStoreSkuBindList)
|
||||
// ScheduleTimerFunc("SendNoCatSkusToOperater", func() {
|
||||
// cms.SendNoCatSkusToOperater(jxcontext.AdminCtx)
|
||||
// }, autoPayForPopluarManList)
|
||||
ScheduleTimerFunc("CleanStoreIsBoughtMatter", func() {
|
||||
cms.CleanStoreIsBoughtMatter(jxcontext.AdminCtx)
|
||||
}, priceReferTimeList)
|
||||
ScheduleTimerFunc("CleanUserOrderSMSMark", func() {
|
||||
cms.CleanUserOrderSMSMark(jxcontext.AdminCtx)
|
||||
}, priceReferTimeList)
|
||||
ScheduleTimerFunc("CreateOrderByPriceDefend", func() {
|
||||
localjx.CreateOrderByPriceDefend(jxcontext.AdminCtx)
|
||||
}, []string{
|
||||
"22:00:00",
|
||||
})
|
||||
//ScheduleTimerFunc("ChangeJxPriceByDiscountAct", func() {
|
||||
// act.ChangeJxPriceByDiscountAct(jxcontext.AdminCtx)
|
||||
//}, discountActJxList)
|
||||
ScheduleTimerFunc("RefreshUserMemberStatus", func() {
|
||||
cms.RefreshUserMemberStatus(jxcontext.AdminCtx)
|
||||
}, updateActStatusTimeList)
|
||||
//刷优惠券状态
|
||||
ScheduleTimerFunc("RefreshCouponsStatus", func() {
|
||||
localjx.RefreshCouponsStatus(jxcontext.AdminCtx)
|
||||
}, updateActStatusTimeList)
|
||||
//刷新美团商超门店Token
|
||||
ScheduleTimerFunc("RefreshMTWMToken", func() {
|
||||
cms.RefreshMTWMToken(jxcontext.AdminCtx)
|
||||
}, updateActStatusTimeList)
|
||||
//获取最新平台流量活动
|
||||
ScheduleTimerFunc("GetNewVendorPopActs", func() {
|
||||
act.GetNewVendorPopActs(jxcontext.AdminCtx)
|
||||
}, dailyHeartbeat)
|
||||
//企业微信群人数通告
|
||||
ScheduleTimerFunc("SendQywxPeopleCount", func() {
|
||||
cms.SendQywxPeopleCount(jxcontext.AdminCtx)
|
||||
}, dailyHeartbeat)
|
||||
//刷新京东售后单结算价
|
||||
ScheduleTimerFunc("RefreshJdAfsOrderTotalShopMoney", func() {
|
||||
orderman.RefreshJdAfsOrderTotalShopMoney()
|
||||
}, openRemoteStoreTimeList)
|
||||
ScheduleTimerFunc("doDailyWork1", func() {
|
||||
//同步商品额外前缀和水印图(打标记)
|
||||
cms.SyncSkuExperfixAndWatermark(jxcontext.AdminCtx)
|
||||
}, dailyWorkTimeList)
|
||||
ScheduleTimerFunc("BuildFakeMatterOrder", func() {
|
||||
orderman.BuildFakeMatterOrder()
|
||||
}, []string{
|
||||
"09:00:00",
|
||||
"15:00:00",
|
||||
"23:00:00",
|
||||
})
|
||||
ScheduleTimerFunc("doDailyWork2", func() {
|
||||
//刷新京东会员
|
||||
//report.RefreshJDMembers(jxcontext.AdminCtx)
|
||||
cms.SetSingleStoreSkuSyncModifyStatus(dao.GetDB(), []int{1, 3})
|
||||
cms.CurVendorSync.SyncStoresSkus2(jxcontext.AdminCtx, nil, 0, dao.GetDB(), []int{1, 3}, nil, false, nil, nil, model.SyncFlagSaleMask, true, true)
|
||||
}, dailyWorkTimeList)
|
||||
ScheduleTimerFunc("doDailyWork3", func() {
|
||||
dao.SetStoresMapSyncStatus(dao.GetDB(), nil, nil, model.SyncFlagStoreStatus)
|
||||
cms.CurVendorSync.SyncStore2(jxcontext.AdminCtx, dao.GetDB(), []int{model.VendorIDMTWM, model.VendorIDJD, model.VendorIDEBAI}, nil, true, true)
|
||||
|
||||
syncStoreSku()
|
||||
InitEx()
|
||||
cms.SyncStoresCourierInfo(jxcontext.AdminCtx, nil, false, true)
|
||||
netprinter.RebindAllPrinters(jxcontext.AdminCtx, false, true)
|
||||
}, dailyWorkTimeList)
|
||||
ScheduleTimerFunc("doDailyWork4", func() {
|
||||
curDate := utils.Time2Date(time.Now())
|
||||
orderman.FixedOrderManager.AmendMissingOrders(jxcontext.AdminCtx, []int{model.VendorIDJD, model.VendorIDMTWM, model.VendorIDEBAI}, 0, curDate.Add(-72*time.Hour), curDate, true, true)
|
||||
//只传toDate默认刷新toDate到5天以前的订单
|
||||
orderman.RefreshOrdersWithoutJxStoreID(jxcontext.AdminCtx, "", "", true, true)
|
||||
//禁用没有绑定的门店
|
||||
cms.DisabledStoreWithoutVendor(jxcontext.AdminCtx, true, true)
|
||||
}, dailyWorkTimeList)
|
||||
ScheduleTimerFunc("doDailyWork5", func() {
|
||||
//刷新物料订单状态
|
||||
localjx.RefreshAllMatterOrderStatus(jxcontext.AdminCtx)
|
||||
//同步美团配送与否状态及美团门店是否存在
|
||||
//cms.SetMTPSStatus(jxcontext.AdminCtx, 0, 0)
|
||||
cms.SetMTPSStatus2(0)
|
||||
//售后单如果超过12小时没有审核,就自动通过
|
||||
RefreshAfsOrderStatusAccess(jxcontext.AdminCtx)
|
||||
//刷新门店分组管理
|
||||
cms.RefreshStoreBind(jxcontext.AdminCtx)
|
||||
}, dailyWorkTimeList)
|
||||
ScheduleTimerFunc("RrefreshMtwmVendorAct", func() {
|
||||
//刷新美团平台活动
|
||||
act.RrefreshMtwmVendorAct()
|
||||
//刷新饿百平台活动
|
||||
act.RrefreshEbaiVendorAct()
|
||||
}, dailyWorkTimeList2)
|
||||
}
|
||||
ScheduleTimerFunc("AutoSaleStoreSku", func() {
|
||||
cms.AutoSaleStoreSku(jxcontext.AdminCtx, nil, false)
|
||||
}, autoSaleStoreSkuTimeList)
|
||||
|
||||
if beego.BConfig.RunMode == "jxgy" {
|
||||
ScheduleTimerFunc("SyncMatterC4ToGy", func() {
|
||||
cms.SyncMatterC4ToGy(jxcontext.AdminCtx, true, true)
|
||||
}, dailyWorkTimeList)
|
||||
}
|
||||
if beego.BConfig.RunMode == "beta" {
|
||||
//ScheduleTimerFunc("CancelPayTimeOutOrder", func() {
|
||||
// localjx.CancelPayTimeOutOrder(jxcontext.AdminCtx)
|
||||
//}, cancelPayTimeOutOrderList)
|
||||
ScheduleTimerFunc("GetAndStoreCitiesShops", func() {
|
||||
netspider.GetAndStoreCitiesShops(jxcontext.AdminCtx, nil, nil, 0, 0, false, false)
|
||||
}, []string{
|
||||
"04:05:06",
|
||||
})
|
||||
//京东的订单信息解密密钥获取
|
||||
//ScheduleTimerFuncByInterval(func() {
|
||||
// jdshop.InitKey()
|
||||
//}, 10*time.Second, 8*time.Hour)
|
||||
ScheduleTimerFunc("ChangeJxPriceByDiscountAct", func() {
|
||||
act.ChangeJxPriceByDiscountAct(jxcontext.AdminCtx)
|
||||
}, discountActJxList)
|
||||
//ScheduleTimerFunc("CreateOrderByPriceDefend", func() {
|
||||
// localjx.CreateOrderByPriceDefend(jxcontext.AdminCtx)
|
||||
//}, []string{
|
||||
// "22:00:00",
|
||||
//})
|
||||
//刷新门店数据坐标等
|
||||
ScheduleTimerFunc("RefreshPageStore", func() {
|
||||
cms.RefreshPageStore()
|
||||
}, []string{
|
||||
"20:00:00",
|
||||
})
|
||||
}
|
||||
if configs, err := dao.QueryConfigs(dao.GetDB(), "ebaiStorePageCookie", model.ConfigTypeCookie, ""); err == nil {
|
||||
ebaiStorePageCookie = configs[0].Value
|
||||
}
|
||||
if configs, err := dao.QueryConfigs(dao.GetDB(), "mtwmCookieStr", model.ConfigTypeCookie, ""); err == nil {
|
||||
mtwmCookieStr = configs[0].Value
|
||||
}
|
||||
if configs, err := dao.QueryConfigs(dao.GetDB(), "mtpsStoreToken", model.ConfigTypeCookie, ""); err == nil {
|
||||
mtpsStoreToken = configs[0].Value
|
||||
}
|
||||
if configs, err := dao.QueryConfigs(dao.GetDB(), "jdStorePageCookie", model.ConfigTypeCookie, ""); err == nil {
|
||||
JdStorePageCookie = configs[0].Value
|
||||
}
|
||||
if configs, err := dao.QueryConfigs(dao.GetDB(), "jdStorePageEarning", model.ConfigTypeCookie, ""); err == nil {
|
||||
jdStorePageEarning = configs[0].Value
|
||||
}
|
||||
if configs, err := dao.QueryConfigs(dao.GetDB(), "jd2StorePageCookie", model.ConfigTypeCookie, ""); err == nil {
|
||||
jd2StorePageCookie = configs[0].Value
|
||||
}
|
||||
if configs, err := dao.QueryConfigs(dao.GetDB(), "feiePageCookie", model.ConfigTypeCookie, ""); err == nil {
|
||||
feiePageCookie = configs[0].Value
|
||||
}
|
||||
//if configs, err := dao.QueryConfigs(dao.GetDB(), "jdsCookie", model.ConfigTypeCookie, ""); err == nil {
|
||||
// jdsCookie = configs[0].Value
|
||||
// api.JdShopAPI.SetCookieWithStr(jdsCookie)
|
||||
//}
|
||||
//if configs, err := dao.QueryConfigs(dao.GetDB(), "yinbaoCookie", model.ConfigTypeCookie, ""); err == nil {
|
||||
// yinbaoCookie := configs[0].Value
|
||||
// api.YinBaoAPI.SetCookie(".POSPALAUTH30220", yinbaoCookie)
|
||||
//}
|
||||
if globals.Jd2OrgCode != "" {
|
||||
api.Jd2API.SetJdCookie(jd2StorePageCookie)
|
||||
}
|
||||
//api.EbaiAPI.SetCookie("PASSPORT_DELIMONT_TOKEN", ebaiStorePageCookieExdTOKEN)
|
||||
//api.EbaiAPI.SetCookie("WMUSS", ebaiStorePageCookieWMUSS)
|
||||
//api.EbaiAPI.SetCookie("WMSTOKEN", ebaiStorePageCookieWMSTOKEN)
|
||||
//api.Ebai2API.SetCookie("PASSPORT_DELIMONT_TOKEN", ebaiStorePageCookieExdTOKEN)
|
||||
//api.Ebai2API.SetCookie("WMUSS", ebaiStorePageCookieWMUSS2)
|
||||
//api.Ebai2API.SetCookie("WMSTOKEN", ebaiStorePageCookieWMSTOKEN2)
|
||||
api.EbaiAPI.SetCookieWithStr(ebaiStorePageCookie)
|
||||
api.MtwmAPI.SetCookieWithStr(mtwmCookieStr)
|
||||
api.MtpsAPI.SetCookie("token", mtpsStoreToken)
|
||||
//api.JdAPI.SetJdCookie(JdStorePageCookie)
|
||||
api.JdAPI.SetCookieWithStr(JdStorePageCookie)
|
||||
api.JdAPI.SetCookie("user", jdStorePageEarning)
|
||||
api.JdPageAPI.SetCookie(jdapi.AccessStorePageCookieName, JdStorePageCookie)
|
||||
api.JdPageAPI.SetCookie(jdapi.AccessStorePageCookieName2, JdStorePageCookie)
|
||||
api.FeieAPI.SetCookieWithStr(feiePageCookie)
|
||||
}
|
||||
|
||||
func syncStoreSku() {
|
||||
syncFlag := 0
|
||||
if beego.BConfig.RunMode == "jxgy" {
|
||||
//syncFlag = model.SyncFlagPriceMask
|
||||
if true {
|
||||
syncFlag |= model.SyncFlagSaleMask
|
||||
}
|
||||
} else {
|
||||
syncFlag |= model.SyncFlagSaleMask
|
||||
}
|
||||
task := tasksch.NewParallelTask("同步京西与平台数据", nil, jxcontext.AdminCtx,
|
||||
func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
|
||||
step := batchItemList[0].(int)
|
||||
errList := errlist.New()
|
||||
db := dao.GetDB()
|
||||
switch step {
|
||||
case 0:
|
||||
if beego.BConfig.RunMode != "jxgy" {
|
||||
errList.AddErr(cms.DeleteSkuNameExPrefixOverdue(db))
|
||||
errList.AddErr(cms.SetMultiStoreSkuSyncModifyStatus(db, partner.GetMultiStoreVendorIDs()))
|
||||
|
||||
_, err = cms.CurVendorSync.LoopMultiStoresVendors(jxcontext.AdminCtx, db, "同步多门店平台商品库", false, true,
|
||||
func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
|
||||
vendorInfo := batchItemList[0].(*cms.MultiStoreVendorInfo)
|
||||
_, err = cms.FullSyncVendorStuff(jxcontext.AdminCtx, task, 0, vendorInfo.VendorID, vendorInfo.OrgCode, false, true)
|
||||
return retVal, err
|
||||
})
|
||||
errList.AddErr(err)
|
||||
|
||||
_, err = cms.CurVendorSync.SyncStoresSkus2(jxcontext.AdminCtx, nil, 0, db, []int{0}, nil, false, nil, []int{27379}, syncFlag, true, true)
|
||||
//_, err = cms.CurVendorSync.FullSyncStoresSkus(jxcontext.AdminCtx, db, partner.GetMultiStoreVendorIDs(), nil, false, []int{27379}, true, true)
|
||||
errList.AddErr(err)
|
||||
}
|
||||
case 1:
|
||||
//TODO 暂时不同步银豹(可能要从银豹到京西),2020-04-27
|
||||
//errList.AddErr(cms.SetSingleStoreSkuSyncModifyStatus(db, []int{1, 3}))
|
||||
|
||||
// errList.AddErr(cms.SetSingleStoreSkuSyncModifyStatus(db, partner.GetSingleStoreVendorIDs()))
|
||||
// _, err = cms.CurVendorSync.AmendAndPruneStoreStuff(jxcontext.AdminCtx, []int{1, 3}, nil, false, true, cms.AmendPruneAll, false)
|
||||
|
||||
// _, err = cms.CurVendorSync.AmendAndPruneStoreStuff(jxcontext.AdminCtx, partner.GetSingleStoreVendorIDs(), nil, false, true, cms.AmendPruneAll, false)
|
||||
errList.AddErr(err)
|
||||
|
||||
SaveImportantTaskID(TaskNameSyncStoreSku, SpecialTaskID)
|
||||
//taskID, err2 := cms.CurVendorSync.SyncStoresSkus2(jxcontext.AdminCtx, nil, 0, db, []int{1, 3}, nil, false, nil, nil, syncFlag, true, true)
|
||||
// taskID, err2 := cms.CurVendorSync.SyncStoresSkus2(jxcontext.AdminCtx, nil, 0, db, partner.GetSingleStoreVendorIDs(), nil, false, nil, nil, syncFlag, true, true)
|
||||
|
||||
//errList.AddErr(err2)
|
||||
//SaveImportantTaskID(TaskNameSyncStoreSku, taskID)
|
||||
}
|
||||
err = errList.GetErrListAsOne()
|
||||
return retVal, err
|
||||
}, []int{0, 1})
|
||||
tasksch.HandleTask(task, nil, true).Run()
|
||||
}
|
||||
|
||||
func doDailyWork2() {
|
||||
globals.SugarLogger.Debug("doDailyWork2")
|
||||
if beego.BConfig.RunMode == "jxgy" {
|
||||
syncStoreSku()
|
||||
refreshPointOrderNewEarningPrice()
|
||||
}
|
||||
//同步京东商城门店的商品
|
||||
// cms.CurVendorSync.SyncJdsStoresSkus(jxcontext.AdminCtx, nil, true, true)
|
||||
//刷新京东商城的门店库存
|
||||
// cms.SyncJdsStoreStock(jxcontext.AdminCtx, true, true)
|
||||
}
|
||||
|
||||
func doDailyWork() {
|
||||
globals.SugarLogger.Debug("doDailyWork")
|
||||
|
||||
//同步商品额外前缀和水印图(打标记)
|
||||
cms.SyncSkuExperfixAndWatermark(jxcontext.AdminCtx)
|
||||
|
||||
dao.SetStoresMapSyncStatus(dao.GetDB(), nil, nil, model.SyncFlagStoreStatus)
|
||||
cms.CurVendorSync.SyncStore2(jxcontext.AdminCtx, dao.GetDB(), []int{model.VendorIDMTWM, model.VendorIDJD, model.VendorIDEBAI}, nil, true, true)
|
||||
|
||||
if beego.BConfig.RunMode == "prod" {
|
||||
syncStoreSku()
|
||||
}
|
||||
InitEx()
|
||||
cms.SyncStoresCourierInfo(jxcontext.AdminCtx, nil, false, true)
|
||||
netprinter.RebindAllPrinters(jxcontext.AdminCtx, false, true)
|
||||
|
||||
// 每天补全前一天与当天的订单
|
||||
// curDate := utils.Time2Date(time.Now())
|
||||
// orderman.FixedOrderManager.AmendMissingOrders(jxcontext.AdminCtx, nil, 0, curDate.Add(-72*time.Hour), curDate, true, true)
|
||||
//订单门店归属补漏
|
||||
//fromDate, toDate都不传默认刷新当前天5天以前的订单,只传fromDate默认刷新fromDate到当天的订单
|
||||
//只传toDate默认刷新toDate到5天以前的订单
|
||||
orderman.RefreshOrdersWithoutJxStoreID(jxcontext.AdminCtx, "", "", true, true)
|
||||
//刷新京东门店的等级
|
||||
// cms.RefreshJdLevel(jxcontext.AdminCtx)
|
||||
//删除操作日志
|
||||
// event.DeleteOperateEventAndDetail(jxcontext.AdminCtx, time.Now().AddDate(0, -3, 0))
|
||||
//禁用没有绑定的门店
|
||||
cms.DisabledStoreWithoutVendor(jxcontext.AdminCtx, true, true)
|
||||
//刷新物料订单状态
|
||||
localjx.RefreshAllMatterOrderStatus(jxcontext.AdminCtx)
|
||||
//同步银豹到京西
|
||||
// cms.CurVendorSync.SyncStoreSkusFromYb(jxcontext.AdminCtx, nil, true, true)
|
||||
//同步上架京东商城待售商品
|
||||
// cms.RefreshJdsSkusStatus(jxcontext.AdminCtx)
|
||||
//同步美团配送与否状态及美团门店是否存在
|
||||
cms.SetMTPSStatus(jxcontext.AdminCtx, 0, 0)
|
||||
//售后单如果超过12小时没有审核,就自动通过
|
||||
RefreshAfsOrderStatusAccess(jxcontext.AdminCtx)
|
||||
//刷新京东商城订单结算价
|
||||
orderman.RefreshJdShopOrdersEarningPrice(jxcontext.AdminCtx, utils.Time2Str(time.Now().AddDate(0, 0, -2)), utils.Time2Str(time.Now()))
|
||||
//刷新门店分组管理
|
||||
cms.RefreshStoreBind(jxcontext.AdminCtx)
|
||||
//刷新京东会员
|
||||
report.RefreshJDMembers(jxcontext.AdminCtx)
|
||||
}
|
||||
|
||||
func refreshPointOrderNewEarningPrice() {
|
||||
var (
|
||||
db = dao.GetDB()
|
||||
orders []*model.GoodsOrder
|
||||
)
|
||||
sql := `
|
||||
SELECT * FROM goods_order WHERE earning_type = 2 AND status = 110 AND order_created_at > ?
|
||||
`
|
||||
sqlParams := []interface{}{time.Now().AddDate(0, 0, -1)}
|
||||
dao.GetRows(db, &orders, sql, sqlParams)
|
||||
if len(orders) > 0 {
|
||||
for _, v := range orders {
|
||||
if v.NewEarningPrice == 0 {
|
||||
v.NewEarningPrice = int64(int(v.TotalShopMoney) * (100 - v.OrderPayPercentage/2) / 100)
|
||||
dao.UpdateEntity(db, v, "NewEarningPrice")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func RefreshAfsOrderStatusAccess(ctx *jxcontext.Context) {
|
||||
var (
|
||||
offset = 0
|
||||
pageSize = 9999
|
||||
db = dao.GetDB()
|
||||
)
|
||||
afsOrderList, _, err := dao.GetAfsOrdersByPage(db, "", "", "", time.Now().AddDate(0, 0, -7), time.Now(), offset, pageSize)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for _, v := range afsOrderList {
|
||||
if v.Status == model.AfsOrderStatusWait4Approve && time.Now().Sub(v.AfsCreatedAt).Hours() > 12 {
|
||||
defsch.FixedScheduler.AgreeOrRefuseRefund(ctx, v.AfsOrderID, v.VendorID, model.AfsTypePartRefund, "超时系统同意")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func RefreshRealMobile(ctx *jxcontext.Context, vendorID int, fromTime, toTime time.Time, isAsync, isContinueWhenError bool) (hint string, err error) {
|
||||
globals.SugarLogger.Debug("RefreshRealMobile")
|
||||
|
||||
handler := partner.GetPurchasePlatformFromVendorID(vendorID)
|
||||
if handler == nil {
|
||||
return "", fmt.Errorf("不合法的vendorID:%d", vendorID)
|
||||
}
|
||||
sql := `
|
||||
SELECT *
|
||||
FROM goods_order
|
||||
WHERE vendor_id = ? AND consignee_mobile2 = ''
|
||||
`
|
||||
sqlParams := []interface{}{
|
||||
vendorID,
|
||||
}
|
||||
if !utils.IsTimeZero(fromTime) {
|
||||
sql += " AND order_created_at >= ?"
|
||||
sqlParams = append(sqlParams, fromTime)
|
||||
}
|
||||
if !utils.IsTimeZero(toTime) {
|
||||
sql += " AND order_created_at <= ?"
|
||||
sqlParams = append(sqlParams, toTime)
|
||||
}
|
||||
var orderList []*model.GoodsOrder
|
||||
db := dao.GetDB()
|
||||
if err = dao.GetRows(db, &orderList, sql, sqlParams...); err == nil && len(orderList) > 0 {
|
||||
task := tasksch.NewParallelTask("misc RefreshRealMobile", tasksch.NewParallelConfig().SetIsContinueWhenError(isContinueWhenError), ctx,
|
||||
func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
|
||||
order := batchItemList[0].(*model.GoodsOrder)
|
||||
mobile, err2 := handler.GetOrderRealMobile(ctx, order)
|
||||
if err = err2; err == nil {
|
||||
//mobile = jxutils.FormalizeMobile(mobile)
|
||||
//if jxutils.IsStringLikeMobile(mobile) && strings.Index(order.ConsigneeMobile, mobile) == -1 {
|
||||
order.ConsigneeMobile2 = mobile
|
||||
_, err = dao.UpdateEntity(db, order, "ConsigneeMobile2")
|
||||
//}
|
||||
} else {
|
||||
globals.SugarLogger.Infof("RefreshRealMobile orderID:%s failed with error:%v", order.VendorOrderID, err)
|
||||
}
|
||||
return nil, err
|
||||
}, orderList)
|
||||
tasksch.HandleTask(task, nil, true).Run()
|
||||
hint = task.ID
|
||||
if !isAsync {
|
||||
_, err = task.GetResult(0)
|
||||
}
|
||||
}
|
||||
return hint, err
|
||||
}
|
||||
|
||||
// 按时间序列循环
|
||||
|
||||
396
business/jxstore/misc/misc2.go
Normal file
396
business/jxstore/misc/misc2.go
Normal file
@@ -0,0 +1,396 @@
|
||||
package misc
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"git.rosy.net.cn/baseapi"
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
"git.rosy.net.cn/jx-callback/business/jxstore/cms"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/tasksch"
|
||||
"git.rosy.net.cn/jx-callback/business/model"
|
||||
"git.rosy.net.cn/jx-callback/business/partner"
|
||||
"git.rosy.net.cn/jx-callback/business/partner/putils"
|
||||
"git.rosy.net.cn/jx-callback/globals"
|
||||
)
|
||||
|
||||
const (
|
||||
enableScheduleRefreshStore = true
|
||||
taskParallelCount = 4
|
||||
specialSkuNameKeyWord = "温馨提示"
|
||||
startOpStoreStockNumber = 0
|
||||
endOpStoreStockNumber = model.MaxStoreSkuStockQty
|
||||
)
|
||||
|
||||
var (
|
||||
isDaemonRunning = false
|
||||
vendorList = map[int]bool{
|
||||
model.VendorIDMTWM: true,
|
||||
model.VendorIDEBAI: true,
|
||||
}
|
||||
storeListQueueData = map[bool]*StoreListQueueData{
|
||||
true: &StoreListQueueData{},
|
||||
false: &StoreListQueueData{},
|
||||
}
|
||||
// vendorStoreRefreshTimeList = map[int][]string {
|
||||
// model.VendorIDMTWM: []string {
|
||||
// //start and end time for JXGY
|
||||
// "22:00:00",
|
||||
// "00:00:00",
|
||||
// //start and end time for JXCS
|
||||
// "22:10:00",
|
||||
// "00:10:00",
|
||||
// },
|
||||
// model.VendorIDEBAI: []string {
|
||||
// "22:20:00",
|
||||
// "06:00:00",
|
||||
|
||||
// "22:30:00",
|
||||
// "06:10:00",
|
||||
// },
|
||||
// }
|
||||
|
||||
// vendorStartEndStoreTime = map[int][]int16 {
|
||||
// model.VendorIDMTWM: []int16 {
|
||||
// int16(2200),//start time
|
||||
// int16(2355),//end time
|
||||
// },
|
||||
// model.VendorIDEBAI: []int16 {
|
||||
// int16(5),
|
||||
// int16(2355),
|
||||
// },
|
||||
// }
|
||||
)
|
||||
|
||||
type StoreListQueueData struct {
|
||||
waitQueue []*cms.StoreExt
|
||||
processQueue []*cms.StoreExt
|
||||
locker sync.RWMutex
|
||||
}
|
||||
|
||||
func (s *StoreListQueueData) GetProcessQueue() (outQueue []*cms.StoreExt) {
|
||||
s.locker.RLock()
|
||||
defer s.locker.RUnlock()
|
||||
outQueue = append(outQueue, s.processQueue...)
|
||||
return outQueue
|
||||
}
|
||||
|
||||
func (s *StoreListQueueData) InsertToWaitQueue(storeInfo *cms.StoreExt) {
|
||||
s.locker.Lock()
|
||||
defer s.locker.Unlock()
|
||||
s.waitQueue = append(s.waitQueue, storeInfo)
|
||||
}
|
||||
|
||||
func (s *StoreListQueueData) ClearProcessQueue() {
|
||||
s.locker.Lock()
|
||||
defer s.locker.Unlock()
|
||||
if len(s.processQueue) > 0 {
|
||||
s.processQueue = []*cms.StoreExt{}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *StoreListQueueData) TransferWaitQueueToProcessQueue() {
|
||||
s.locker.Lock()
|
||||
defer s.locker.Unlock()
|
||||
if len(s.processQueue) == 0 && len(s.waitQueue) > 0 {
|
||||
for _, value := range s.waitQueue {
|
||||
s.processQueue = append(s.processQueue, value)
|
||||
}
|
||||
s.waitQueue = []*cms.StoreExt{}
|
||||
}
|
||||
}
|
||||
|
||||
func AddOrDelExtraStoreOptime(ctx *jxcontext.Context, vendorID int, vendorOrgCode string, storeID int, vendorStoreID string, storeInfo *model.Store, startOpStoreTime, endOpStoreTime int16, needAddTime bool) bool {
|
||||
opTimeList := storeInfo.GetOpTimeList()
|
||||
if needAddTime {
|
||||
opTimeList = []int16{startOpStoreTime, endOpStoreTime}
|
||||
}
|
||||
handler := partner.GetPurchasePlatformFromVendorID(vendorID).(partner.IStoreHandler)
|
||||
return handler.UpdateStoreOpTime(ctx, vendorOrgCode, storeID, vendorStoreID, opTimeList) == nil
|
||||
}
|
||||
|
||||
func GetStockValue(isStart bool) int {
|
||||
if isStart {
|
||||
return startOpStoreStockNumber
|
||||
} else {
|
||||
return endOpStoreStockNumber
|
||||
}
|
||||
}
|
||||
|
||||
// func GetOpStoreTime(vendorID int) (startTime, endTime int16) {
|
||||
// startTime = vendorStartEndStoreTime[vendorID][0]
|
||||
// endTime = vendorStartEndStoreTime[vendorID][1]
|
||||
// return startTime, endTime
|
||||
// }
|
||||
|
||||
func IsSpecialSku(name string) bool {
|
||||
return strings.Contains(name, specialSkuNameKeyWord)
|
||||
}
|
||||
|
||||
func SetSkuStock(isStart bool, storeSkuNameList []*partner.SkuNameInfo) {
|
||||
for _, skuNameInfo := range storeSkuNameList {
|
||||
for _, skuInfo := range skuNameInfo.SkuList {
|
||||
if IsSpecialSku(skuNameInfo.Name) || IsSpecialSku(skuInfo.SkuName) {
|
||||
skuInfo.Stock = endOpStoreStockNumber
|
||||
} else {
|
||||
skuInfo.Stock = GetStockValue(isStart)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func SetSpecialSkuStatus(ctx *jxcontext.Context, vendorID int, vendorOrgCode string, storeID int, vendorStoreID string, storeSkuNameList []*partner.SkuNameInfo) {
|
||||
singleStoreHandler := partner.GetPurchasePlatformFromVendorID(vendorID).(partner.ISingleStoreStoreSkuHandler)
|
||||
for _, skuNameInfo := range storeSkuNameList {
|
||||
for _, skuInfo := range skuNameInfo.SkuList {
|
||||
if IsSpecialSku(skuNameInfo.Name) || IsSpecialSku(skuInfo.SkuName) {
|
||||
storeSkuList := []*partner.StoreSkuInfo{&skuInfo.StoreSkuInfo}
|
||||
singleStoreHandler.UpdateStoreSkusStatus(ctx, vendorOrgCode, storeID, vendorStoreID, storeSkuList, model.SkuStatusNormal)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func GetFilterStoreSkuList(storeSkuList []*partner.StoreSkuInfo) (storeSkuListOut []*partner.StoreSkuInfo) {
|
||||
for _, value := range storeSkuList {
|
||||
if value.SkuID != 0 {
|
||||
storeSkuListOut = append(storeSkuListOut, value)
|
||||
}
|
||||
}
|
||||
|
||||
return storeSkuListOut
|
||||
}
|
||||
|
||||
func GetStoreList(ctx *jxcontext.Context) (storeList []*cms.StoreExt, err error) {
|
||||
storeInfo, err := cms.GetStores(ctx, "", map[string]interface{}{}, 0, -1, utils.DefaultTimeValue, utils.DefaultTimeValue, 0, 0)
|
||||
storeList = storeInfo.Stores
|
||||
return storeList, err
|
||||
}
|
||||
|
||||
func GetFilterStoreList(storeList []*cms.StoreExt, vendorMap, storeIDMap map[int]bool) (outStoreList []*cms.StoreExt) {
|
||||
for _, storeInfo := range storeList {
|
||||
storeID := storeInfo.ID
|
||||
//filter for storeID
|
||||
if len(storeIDMap) > 0 {
|
||||
if _, ok := storeIDMap[storeID]; !ok {
|
||||
continue
|
||||
}
|
||||
}
|
||||
for _, vendorStoreInfo := range storeInfo.StoreMaps {
|
||||
vendorID := vendorStoreInfo.VendorID
|
||||
//filter for vendorID
|
||||
if len(vendorMap) > 0 {
|
||||
if _, ok := vendorMap[vendorID]; !ok {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if _, ok := vendorList[vendorID]; !ok {
|
||||
continue
|
||||
}
|
||||
temp := *storeInfo
|
||||
newStoreInfo := &temp
|
||||
newStoreInfo.StoreMaps = []*model.StoreMap{vendorStoreInfo}
|
||||
outStoreList = append(outStoreList, newStoreInfo)
|
||||
}
|
||||
}
|
||||
|
||||
return outStoreList
|
||||
}
|
||||
|
||||
func StartOrEndOpStore(ctx *jxcontext.Context, isStart bool, vendorIDList []int, storeIDList []int, startTime, endTime int16, isAsync, isContinueWhenError bool) (retVal interface{}, err error) {
|
||||
storeList, err := GetStoreList(ctx)
|
||||
vendorMap := make(map[int]bool)
|
||||
for _, vendorID := range vendorIDList {
|
||||
vendorMap[vendorID] = true
|
||||
}
|
||||
storeIDMap := make(map[int]bool)
|
||||
for _, storeID := range storeIDList {
|
||||
storeIDMap[storeID] = true
|
||||
}
|
||||
storeList = GetFilterStoreList(storeList, vendorMap, storeIDMap)
|
||||
return StartOrEndOpStoreEx(ctx, isStart, startTime, endTime, isAsync, isContinueWhenError, storeList)
|
||||
}
|
||||
|
||||
func StartOrEndOpStoreEx(ctx *jxcontext.Context, isStart bool, startTime, endTime int16, isAsync, isContinueWhenError bool, storeList []*cms.StoreExt) (retVal interface{}, err error) {
|
||||
startProcessTime := time.Now().Unix()
|
||||
baseapi.SugarLogger.Debugf("StartOrEndOpStore start time: %v", time.Now())
|
||||
|
||||
if len(storeList) > 0 {
|
||||
taskFunc := func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
|
||||
storeListValue := batchItemList[0].(*cms.StoreExt)
|
||||
storeID := storeListValue.ID
|
||||
for _, vendorListValue := range storeListValue.StoreMaps {
|
||||
vendorID := vendorListValue.VendorID
|
||||
startOpStoreTime := vendorListValue.FakeOpenStart
|
||||
endOpStoreTime := vendorListValue.FakeOpenStop
|
||||
//startOpStoreTime, endOpStoreTime := GetOpStoreTime(vendorID)
|
||||
if startTime != 0 && endTime != 0 {
|
||||
startOpStoreTime = startTime
|
||||
endOpStoreTime = endTime
|
||||
baseapi.SugarLogger.Debugf("StartOrEndOpStore SetStoreOptime:%d do:%d", startTime, endTime)
|
||||
}
|
||||
if isStart && (startOpStoreTime == 0 || endOpStoreTime == 0) {
|
||||
continue
|
||||
}
|
||||
vendorStoreID := vendorListValue.VendorStoreID
|
||||
baseapi.SugarLogger.Debugf("StartOrEndOpStore storeID:%d vendorID:%d vendorStoreID:%s", storeID, vendorID, vendorStoreID)
|
||||
singleStoreHandler := partner.GetPurchasePlatformFromVendorID(vendorID).(partner.ISingleStoreStoreSkuHandler)
|
||||
storeSkuNameList, err := singleStoreHandler.GetStoreSkusFullInfo(ctx, task, storeID, vendorStoreID, nil)
|
||||
if err != nil {
|
||||
baseapi.SugarLogger.Errorf("StartOrEndOpStore GetStoreSkusFullInfo error:%v storeID:%d vendorID:%d vendorStoreID:%s", err, storeID, vendorID, vendorStoreID)
|
||||
} else {
|
||||
SetSkuStock(isStart, storeSkuNameList)
|
||||
SetSpecialSkuStatus(ctx, vendorID, vendorListValue.VendorOrgCode, storeID, vendorStoreID, storeSkuNameList)
|
||||
storeSkuList := putils.StoreSkuFullList2Bare(storeSkuNameList)
|
||||
if vendorID == model.VendorIDMTWM {
|
||||
storeSkuList = GetFilterStoreSkuList(storeSkuList)
|
||||
}
|
||||
if len(storeSkuList) > 0 {
|
||||
if !isStart {
|
||||
AddOrDelExtraStoreOptime(ctx, vendorID, vendorListValue.VendorOrgCode, storeID, vendorStoreID, &storeListValue.Store, startOpStoreTime, endOpStoreTime, false)
|
||||
}
|
||||
|
||||
_, err = putils.FreeBatchStoreSkuInfo("更新门店商品库存", func(task tasksch.ITask, batchedStoreSkuList []*partner.StoreSkuInfo) (result interface{}, successCount int, err error) {
|
||||
_, err = singleStoreHandler.UpdateStoreSkusStock(ctx, vendorListValue.VendorOrgCode, storeID, vendorStoreID, batchedStoreSkuList)
|
||||
return nil, 0, err
|
||||
}, ctx, task, storeSkuList, singleStoreHandler.GetStoreSkusBatchSize(partner.FuncUpdateStoreSkusStock), true)
|
||||
|
||||
if isStart {
|
||||
AddOrDelExtraStoreOptime(ctx, vendorID, vendorListValue.VendorOrgCode, storeID, vendorStoreID, &storeListValue.Store, startOpStoreTime, endOpStoreTime, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return retVal, err
|
||||
}
|
||||
taskName := ""
|
||||
if isStart {
|
||||
taskName = "开启平台商店的额外营业时间"
|
||||
} else {
|
||||
taskName = "结束平台商店的额外营业时间"
|
||||
}
|
||||
task := tasksch.NewParallelTask(taskName, tasksch.NewParallelConfig().SetParallelCount(taskParallelCount), ctx, taskFunc, storeList)
|
||||
tasksch.HandleTask(task, nil, true).Run()
|
||||
if isAsync {
|
||||
retVal = task.ID
|
||||
} else {
|
||||
_, err = task.GetResult(0)
|
||||
if err != nil {
|
||||
baseapi.SugarLogger.Debugf("StartOrEndOpStore tasksch error:%v", err)
|
||||
}
|
||||
retVal = "1"
|
||||
}
|
||||
}
|
||||
endProcessTime := time.Now().Unix()
|
||||
diff := endProcessTime - startProcessTime
|
||||
baseapi.SugarLogger.Debugf("StartOrEndOpStore end time: %v", time.Now())
|
||||
baseapi.SugarLogger.Debugf("StartOrEndOpStore cost time: %d sec", diff)
|
||||
|
||||
return retVal, err
|
||||
}
|
||||
|
||||
func IsJXCS() bool {
|
||||
return globals.IsMainProductEnv()
|
||||
}
|
||||
|
||||
// func GetVendorStoreRefreshTime(vendorID int) (startTimeList, stopTimeList []string) {
|
||||
// isJXCS := globals.IsMainProductEnv()
|
||||
// refreshTimeList := vendorStoreRefreshTimeList[vendorID]
|
||||
// if isJXCS {
|
||||
// startTimeList = []string{refreshTimeList[2]}
|
||||
// stopTimeList = []string{refreshTimeList[3]}
|
||||
// } else {
|
||||
// startTimeList = []string{refreshTimeList[0]}
|
||||
// stopTimeList = []string{refreshTimeList[1]}
|
||||
// }
|
||||
// return startTimeList, stopTimeList
|
||||
// }
|
||||
|
||||
// func RefreshStore(vendorID int) {
|
||||
// ctx := jxcontext.AdminCtx
|
||||
// startTimeList, stopTimeList := GetVendorStoreRefreshTime(vendorID)
|
||||
// vendorIDList := []int{vendorID}
|
||||
// storeIDList := []int{}
|
||||
// ScheduleTimerFunc("StartOpStore", func() {
|
||||
// if !IsImportantTaskRunning(TaskNameSyncStoreSku) {
|
||||
// StartOrEndOpStore(ctx, true, vendorIDList, storeIDList, 0, 0, false, true)
|
||||
// }
|
||||
// }, startTimeList)
|
||||
// ScheduleTimerFunc("EndOpStore", func() {
|
||||
// if !IsImportantTaskRunning(TaskNameSyncStoreSku) {
|
||||
// StartOrEndOpStore(ctx, false, vendorIDList, storeIDList, 0, 0, false, true)
|
||||
// }
|
||||
// }, stopTimeList)
|
||||
// }
|
||||
|
||||
func InitEx() {
|
||||
// for index, value := range vendorList {
|
||||
// if value {
|
||||
// RefreshStore(index)
|
||||
// }
|
||||
// }
|
||||
if enableScheduleRefreshStore && IsJXCS() {
|
||||
ctx := jxcontext.AdminCtx
|
||||
storeList, err := GetStoreList(ctx)
|
||||
storeList = GetFilterStoreList(storeList, map[int]bool{}, map[int]bool{})
|
||||
if err == nil {
|
||||
for _, storeInfo := range storeList {
|
||||
for _, vendorStoreInfo := range storeInfo.StoreMaps {
|
||||
startOpStoreTime := vendorStoreInfo.FakeOpenStart
|
||||
endOpStoreTime := vendorStoreInfo.FakeOpenStop
|
||||
if startOpStoreTime == 0 || endOpStoreTime == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
startTime := jxutils.OperationTime2StrWithSecond(startOpStoreTime)
|
||||
ScheduleTimerFuncOnce("StartOpStore", func(param interface{}) {
|
||||
if !IsImportantTaskRunning(TaskNameSyncStoreSku) {
|
||||
storeInfo := param.(*cms.StoreExt)
|
||||
storeListQueueData[true].InsertToWaitQueue(storeInfo)
|
||||
}
|
||||
}, startTime, storeInfo)
|
||||
|
||||
stopTime := jxutils.OperationTime2StrWithSecond(endOpStoreTime)
|
||||
ScheduleTimerFuncOnce("EndOpStore", func(param interface{}) {
|
||||
storeInfo := param.(*cms.StoreExt)
|
||||
storeListQueueData[false].InsertToWaitQueue(storeInfo)
|
||||
}, stopTime, storeInfo)
|
||||
}
|
||||
}
|
||||
if !isDaemonRunning {
|
||||
isDaemonRunning = true
|
||||
taskSeqFunc := func(task *tasksch.SeqTask, step int, params ...interface{}) (result interface{}, err error) {
|
||||
switch step {
|
||||
case 0:
|
||||
periodlyFunc := func() {
|
||||
for isStart, value := range storeListQueueData {
|
||||
storeList := value.GetProcessQueue()
|
||||
if len(storeList) > 0 {
|
||||
StartOrEndOpStoreEx(ctx, isStart, 0, 0, false, true, storeList)
|
||||
}
|
||||
}
|
||||
for _, value := range storeListQueueData {
|
||||
value.ClearProcessQueue()
|
||||
value.TransferWaitQueueToProcessQueue()
|
||||
}
|
||||
}
|
||||
PeriodlyCall(60*time.Second, periodlyFunc)
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
taskSeq := tasksch.NewSeqTask("启动监听-定时刷新平台商店的额外营业时间", ctx, taskSeqFunc, 1)
|
||||
tasksch.HandleTask(taskSeq, nil, true).Run()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func PeriodlyCall(d time.Duration, handler func()) {
|
||||
ticker := time.NewTicker(d)
|
||||
for _ = range ticker.C {
|
||||
handler()
|
||||
}
|
||||
}
|
||||
11
business/jxstore/misc/misc2_test.go
Normal file
11
business/jxstore/misc/misc2_test.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package misc
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
|
||||
)
|
||||
|
||||
func TestStartOrEndOpStore(t *testing.T) {
|
||||
StartOrEndOpStore(jxcontext.AdminCtx, true, nil, nil, 0, 0, false, true)
|
||||
}
|
||||
509
business/jxstore/misc/store_alert_inform.go
Normal file
509
business/jxstore/misc/store_alert_inform.go
Normal file
@@ -0,0 +1,509 @@
|
||||
package misc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"time"
|
||||
|
||||
"git.rosy.net.cn/baseapi"
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
"git.rosy.net.cn/jx-callback/business/jxstore/cms"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/weixinmsg"
|
||||
"git.rosy.net.cn/jx-callback/business/model"
|
||||
"git.rosy.net.cn/jx-callback/business/model/dao"
|
||||
"git.rosy.net.cn/jx-callback/globals/refutil"
|
||||
)
|
||||
|
||||
const (
|
||||
EnableCheckStoreAlert = true
|
||||
EnableSendStoreAlert = true
|
||||
|
||||
IncludeToday = true
|
||||
CheckStoreAlertOneMonthDayNum = 30
|
||||
CheckStoreAlertOneDayNum = 1
|
||||
CheckStoreAlertOneWeekDayNum = 7
|
||||
MonthCheckOnWeekDay = 1
|
||||
|
||||
AlertTypePickTimeOrderDaDa = 0
|
||||
AlertTypeBadCommentOrder = 1
|
||||
AlertTypeAbsentGoodsOrder = 2
|
||||
AlertTypeStandardFinishTimeOrderSelfDelivery = 3
|
||||
AlertTypeStandardPickUpTimeOrderDaDa = 4
|
||||
|
||||
ColorRed = "red"
|
||||
ColorYellow = "yellow"
|
||||
ColorUnknown = "unknown"
|
||||
|
||||
AlertLevelExtraRed = 1
|
||||
AlertLevelRed = 2
|
||||
AlertLevelYellow = 3
|
||||
|
||||
OneDayName = "单日"
|
||||
OneWeekDayName = "七日"
|
||||
OneMonthDayName = "三十日"
|
||||
YellowAlertInfo = "您的店铺-%s,由于%s%s%s%d%%,可能会被系统下线,请及时补救。"
|
||||
RedAlertInfo = "您的店铺-%s,由于%s%s%s%d%%,会被系统下线,需要马上补救。"
|
||||
ExtraRedAlertInfo = "您的店铺-%s,由于%s%s%s%d%%,会被系统下线,需要马上补救。"
|
||||
NoOrderAlertInfo = "您的店铺-%s,由于近%s无订单,会被系统下线,需要马上补救。"
|
||||
RiskOrderAlertInfo = "您的店铺-%s,可能有虚假定单,定单号为:%s,可能会被罚款,请及时与运营联系!"
|
||||
)
|
||||
|
||||
var (
|
||||
checkStoreAlertTimeList = []string{
|
||||
"18:30:00",
|
||||
}
|
||||
|
||||
AlertTypeNameMap = map[int]string{
|
||||
AlertTypePickTimeOrderDaDa: "拣货履约率(达达)",
|
||||
AlertTypeBadCommentOrder: "差评率",
|
||||
AlertTypeAbsentGoodsOrder: "缺货率",
|
||||
AlertTypeStandardFinishTimeOrderSelfDelivery: "按时履约率(商家自送)",
|
||||
AlertTypeStandardPickUpTimeOrderDaDa: "10分钟取货完成率(达达)",
|
||||
}
|
||||
|
||||
AlertTypeExtraNameMap = map[int]string{
|
||||
AlertTypePickTimeOrderDaDa: "拣货超时订单(达达)",
|
||||
AlertTypeBadCommentOrder: "差评订单",
|
||||
AlertTypeAbsentGoodsOrder: "缺货订单",
|
||||
}
|
||||
|
||||
storeAlertDataWrapper StoreAlertDataWrapper
|
||||
)
|
||||
|
||||
type StoreAlertDataWrapper struct {
|
||||
storeAlertList map[int]*model.StoreAlert
|
||||
}
|
||||
|
||||
func (s *StoreAlertDataWrapper) InitData() {
|
||||
s.storeAlertList = make(map[int]*model.StoreAlert)
|
||||
}
|
||||
|
||||
func (s *StoreAlertDataWrapper) ClearData() {
|
||||
s.storeAlertList = nil
|
||||
}
|
||||
|
||||
func (s *StoreAlertDataWrapper) IsStatusField(valueName string) bool {
|
||||
return valueName == model.FieldYellowStatus || valueName == model.FieldRedStatus || valueName == model.FieldExtraRedStatus
|
||||
}
|
||||
|
||||
func (s *StoreAlertDataWrapper) SetData(storeID int, valueName string, value int) {
|
||||
data := s.storeAlertList[storeID]
|
||||
if data == nil {
|
||||
data = &model.StoreAlert{}
|
||||
data.StoreID = storeID
|
||||
data.AlertDate = utils.GetCurDate()
|
||||
s.storeAlertList[storeID] = data
|
||||
}
|
||||
if s.IsStatusField(valueName) {
|
||||
srcFieldValue := refutil.GetObjFieldByName(data, valueName).(int)
|
||||
value |= srcFieldValue
|
||||
}
|
||||
refutil.SetObjFieldByName(data, valueName, value)
|
||||
}
|
||||
|
||||
func (s StoreAlertDataWrapper) InsertStoreAlertList() {
|
||||
for _, value := range s.storeAlertList {
|
||||
dao.InsertStoreAlert(value)
|
||||
}
|
||||
}
|
||||
|
||||
func ConvertListToMap(listData []*model.StoreCount) (mapData map[int]int) {
|
||||
mapData = make(map[int]int)
|
||||
for _, value := range listData {
|
||||
mapData[value.StoreID] = value.Count
|
||||
}
|
||||
|
||||
return mapData
|
||||
}
|
||||
|
||||
func ConvertListToMapEx(listData []*model.StoreOrder) (mapData map[int][]string) {
|
||||
mapData = make(map[int][]string)
|
||||
for _, value := range listData {
|
||||
if mapData[value.StoreID] == nil {
|
||||
mapData[value.StoreID] = []string{}
|
||||
}
|
||||
mapData[value.StoreID] = append(mapData[value.StoreID], value.VendorOrderID)
|
||||
}
|
||||
|
||||
return mapData
|
||||
}
|
||||
|
||||
func GetAlertInfo(dayNum, alertLevel int, storeName string, alertType int, logicCondition string, value int) (info string) {
|
||||
if dayNum == CheckStoreAlertOneDayNum {
|
||||
if alertLevel == AlertLevelExtraRed {
|
||||
info = fmt.Sprintf(ExtraRedAlertInfo, storeName, OneDayName, AlertTypeExtraNameMap[alertType], logicCondition, value)
|
||||
} else if alertLevel == AlertLevelRed {
|
||||
info = fmt.Sprintf(RedAlertInfo, storeName, OneDayName, AlertTypeNameMap[alertType], logicCondition, value)
|
||||
} else if alertLevel == AlertLevelYellow {
|
||||
info = fmt.Sprintf(YellowAlertInfo, storeName, OneDayName, AlertTypeNameMap[alertType], logicCondition, value)
|
||||
}
|
||||
} else if dayNum == CheckStoreAlertOneWeekDayNum {
|
||||
if alertLevel == AlertLevelExtraRed {
|
||||
info = fmt.Sprintf(ExtraRedAlertInfo, storeName, OneWeekDayName, AlertTypeExtraNameMap[alertType], logicCondition, value)
|
||||
} else if alertLevel == AlertLevelRed {
|
||||
info = fmt.Sprintf(RedAlertInfo, storeName, OneWeekDayName, AlertTypeNameMap[alertType], logicCondition, value)
|
||||
} else if alertLevel == AlertLevelYellow {
|
||||
info = fmt.Sprintf(YellowAlertInfo, storeName, OneWeekDayName, AlertTypeNameMap[alertType], logicCondition, value)
|
||||
}
|
||||
} else if dayNum == CheckStoreAlertOneMonthDayNum {
|
||||
info = fmt.Sprintf(NoOrderAlertInfo, storeName, OneMonthDayName)
|
||||
}
|
||||
|
||||
return info
|
||||
}
|
||||
|
||||
func CheckAlert(alertType int, dayNum int, ratio int, count int) (alertLevel int, logicCondtion string, outValue int) {
|
||||
yellowRatio := -1
|
||||
redRatio := -1
|
||||
extraCount := -1
|
||||
conditionLessEqual := false
|
||||
|
||||
switch alertType {
|
||||
case AlertTypePickTimeOrderDaDa:
|
||||
if dayNum == CheckStoreAlertOneDayNum {
|
||||
redRatio = 0
|
||||
} else if dayNum == CheckStoreAlertOneWeekDayNum {
|
||||
yellowRatio = 70
|
||||
redRatio = 50
|
||||
extraCount = 5
|
||||
}
|
||||
conditionLessEqual = true
|
||||
case AlertTypeBadCommentOrder:
|
||||
if dayNum == CheckStoreAlertOneDayNum {
|
||||
redRatio = 100
|
||||
} else if dayNum == CheckStoreAlertOneWeekDayNum {
|
||||
yellowRatio = 1
|
||||
redRatio = 3
|
||||
extraCount = 5
|
||||
}
|
||||
conditionLessEqual = false
|
||||
case AlertTypeAbsentGoodsOrder:
|
||||
if dayNum == CheckStoreAlertOneDayNum {
|
||||
redRatio = 100
|
||||
} else if dayNum == CheckStoreAlertOneWeekDayNum {
|
||||
yellowRatio = 3
|
||||
redRatio = 5
|
||||
extraCount = 5
|
||||
}
|
||||
conditionLessEqual = false
|
||||
case AlertTypeStandardFinishTimeOrderSelfDelivery:
|
||||
yellowRatio = 85
|
||||
redRatio = 70
|
||||
conditionLessEqual = true
|
||||
case AlertTypeStandardPickUpTimeOrderDaDa:
|
||||
yellowRatio = 85
|
||||
redRatio = 70
|
||||
conditionLessEqual = true
|
||||
}
|
||||
|
||||
if conditionLessEqual {
|
||||
logicCondtion = "<="
|
||||
if extraCount != -1 && count >= extraCount {
|
||||
alertLevel = AlertLevelExtraRed
|
||||
outValue = extraCount
|
||||
} else if redRatio != -1 && ratio <= redRatio {
|
||||
alertLevel = AlertLevelRed
|
||||
outValue = redRatio
|
||||
} else if yellowRatio != -1 && ratio <= yellowRatio {
|
||||
alertLevel = AlertLevelYellow
|
||||
outValue = yellowRatio
|
||||
}
|
||||
} else {
|
||||
logicCondtion = ">="
|
||||
if extraCount != -1 && count >= extraCount {
|
||||
alertLevel = AlertLevelExtraRed
|
||||
outValue = extraCount
|
||||
} else if redRatio != -1 && ratio >= redRatio {
|
||||
alertLevel = AlertLevelRed
|
||||
outValue = redRatio
|
||||
} else if yellowRatio != -1 && ratio >= yellowRatio {
|
||||
alertLevel = AlertLevelYellow
|
||||
outValue = yellowRatio
|
||||
}
|
||||
}
|
||||
|
||||
return alertLevel, logicCondtion, outValue
|
||||
}
|
||||
|
||||
func SendAlertInfo(storeID int, storeName, alertInfo string) {
|
||||
if EnableSendStoreAlert {
|
||||
baseapi.SugarLogger.Debugf("SendAlertInfo: %d, %s", storeID, alertInfo)
|
||||
weixinmsg.NotifyStoreAlertMessage(storeID, storeName, "门店警告", alertInfo)
|
||||
}
|
||||
}
|
||||
|
||||
func GetFieldNameByAlertType(alertType, dayNum int) (fieldName string, fieldFlag int) {
|
||||
switch alertType {
|
||||
case AlertTypePickTimeOrderDaDa:
|
||||
if dayNum == CheckStoreAlertOneDayNum {
|
||||
fieldName = model.FieldPickTimeDaDa
|
||||
fieldFlag = model.FlagPickTimeDaDa
|
||||
} else if dayNum == CheckStoreAlertOneWeekDayNum {
|
||||
fieldName = model.FieldPickTimeDaDaOneWeek
|
||||
fieldFlag = model.FlagPickTimeDaDaOneWeek
|
||||
}
|
||||
case AlertTypeBadCommentOrder:
|
||||
if dayNum == CheckStoreAlertOneDayNum {
|
||||
fieldName = model.FieldBadComment
|
||||
fieldFlag = model.FlagBadComment
|
||||
} else if dayNum == CheckStoreAlertOneWeekDayNum {
|
||||
fieldName = model.FieldBadCommentOneWeek
|
||||
fieldFlag = model.FlagBadCommentOneWeek
|
||||
}
|
||||
case AlertTypeAbsentGoodsOrder:
|
||||
if dayNum == CheckStoreAlertOneDayNum {
|
||||
fieldName = model.FieldAbsentGoods
|
||||
fieldFlag = model.FlagAbsentGoods
|
||||
} else if dayNum == CheckStoreAlertOneWeekDayNum {
|
||||
fieldName = model.FieldAbsentGoodsOneWeek
|
||||
fieldFlag = model.FlagAbsentGoodsOneWeek
|
||||
}
|
||||
case AlertTypeStandardFinishTimeOrderSelfDelivery:
|
||||
fieldName = model.FieldStandardFinishTimeSelfDelivery
|
||||
fieldFlag = model.FlagStandardFinishTimeSelfDelivery
|
||||
case AlertTypeStandardPickUpTimeOrderDaDa:
|
||||
fieldName = model.FieldStandardPickUpTimeDaDa
|
||||
fieldFlag = model.FlagStandardPickUpTimeDaDa
|
||||
}
|
||||
|
||||
return fieldName, fieldFlag
|
||||
}
|
||||
|
||||
func SendAlertInfoWrapper(storeID int, storeName string, dayNum, alertType, count, totalCount int) {
|
||||
if totalCount > 0 {
|
||||
ratio := int(math.Round(float64(count) * 100 / float64(totalCount)))
|
||||
alertLevel, logicCondtion, outValue := CheckAlert(alertType, dayNum, ratio, count)
|
||||
if alertLevel != 0 {
|
||||
alertInfo := GetAlertInfo(dayNum, alertLevel, storeName, alertType, logicCondtion, outValue)
|
||||
SendAlertInfo(storeID, storeName, alertInfo)
|
||||
fieldName, fieldFlag := GetFieldNameByAlertType(alertType, dayNum)
|
||||
storeAlertDataWrapper.SetData(storeID, fieldName, ratio)
|
||||
|
||||
if alertLevel == AlertLevelExtraRed {
|
||||
storeAlertDataWrapper.SetData(storeID, model.FieldExtraRedStatus, fieldFlag)
|
||||
} else if alertLevel == AlertLevelRed {
|
||||
storeAlertDataWrapper.SetData(storeID, model.FieldRedStatus, fieldFlag)
|
||||
} else if alertLevel == AlertLevelYellow {
|
||||
storeAlertDataWrapper.SetData(storeID, model.FieldYellowStatus, fieldFlag)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func CheckStoreDayAlert(storeList []*cms.StoreExt, dayNum int) {
|
||||
db := dao.GetDB()
|
||||
//拣货履约订单(达达)
|
||||
pickTimeOrderCountDaDaList, _ := dao.GetStandardPickTimeOrderCountByDaDa(db, dayNum, IncludeToday)
|
||||
pickTimeOrderCountDaDaMapData := ConvertListToMap(pickTimeOrderCountDaDaList)
|
||||
|
||||
//完成订单(达达)
|
||||
finishOrderCountDaDaList, _ := dao.GetFinishOrderCountByDaDa(db, dayNum, IncludeToday)
|
||||
finishOrderCountDaDaMapData := ConvertListToMap(finishOrderCountDaDaList)
|
||||
|
||||
//差评订单
|
||||
badCommentOrderCountList, _ := dao.GetBadCommentOrderCountByDayNum(db, dayNum, IncludeToday)
|
||||
badCommentOrderCountMapData := ConvertListToMap(badCommentOrderCountList)
|
||||
|
||||
//缺货订单
|
||||
absentGoodsOrderCountList, _ := dao.GetAbsentGoodsOrderCountByDayNum(db, dayNum, IncludeToday)
|
||||
absentGoodsOrderCountMapData := ConvertListToMap(absentGoodsOrderCountList)
|
||||
|
||||
//完成订单
|
||||
finishOrderCountList, _ := dao.GetFinishOrderCountByDayNum(db, dayNum, IncludeToday)
|
||||
finishOrderCountMapData := ConvertListToMap(finishOrderCountList)
|
||||
|
||||
var standardFinishTimeOrderCountSelfDeliveryList []*model.StoreCount
|
||||
var standardFinishTimeOrderCountSelfDeliveryMapData map[int]int
|
||||
var finishOrderCountSelfDeliveryList []*model.StoreCount
|
||||
var finishOrderCountSelfDeliveryMapData map[int]int
|
||||
var standardPickUpTimeOrderCountDaDaList []*model.StoreCount
|
||||
var standardPickUpTimeOrderCountDaDaMapData map[int]int
|
||||
isOneWeekDay := dayNum == CheckStoreAlertOneWeekDayNum
|
||||
if isOneWeekDay {
|
||||
//按时履约订单(商家自送)
|
||||
standardFinishTimeOrderCountSelfDeliveryList, _ = dao.GetStandardFinishTimeOrderCountBySelfDelivery(db, dayNum, IncludeToday)
|
||||
standardFinishTimeOrderCountSelfDeliveryMapData = ConvertListToMap(standardFinishTimeOrderCountSelfDeliveryList)
|
||||
|
||||
//完成订单(商家自送)
|
||||
finishOrderCountSelfDeliveryList, _ = dao.GetFinishOrderCountBySelfDelivery(db, dayNum, IncludeToday)
|
||||
finishOrderCountSelfDeliveryMapData = ConvertListToMap(finishOrderCountSelfDeliveryList)
|
||||
|
||||
//10分钟取货完成订单(达达)
|
||||
standardPickUpTimeOrderCountDaDaList, _ = dao.GetStandardPickUpTimeOrderCountByDaDa(db, dayNum, IncludeToday)
|
||||
standardPickUpTimeOrderCountDaDaMapData = ConvertListToMap(standardPickUpTimeOrderCountDaDaList)
|
||||
}
|
||||
|
||||
for _, storeInfo := range storeList {
|
||||
storeID := storeInfo.ID
|
||||
storeName := storeInfo.Name
|
||||
count := pickTimeOrderCountDaDaMapData[storeID]
|
||||
totalCount := finishOrderCountDaDaMapData[storeID]
|
||||
SendAlertInfoWrapper(storeID, storeName, dayNum, AlertTypePickTimeOrderDaDa, count, totalCount)
|
||||
|
||||
count = badCommentOrderCountMapData[storeID]
|
||||
totalCount = finishOrderCountMapData[storeID]
|
||||
SendAlertInfoWrapper(storeID, storeName, dayNum, AlertTypeBadCommentOrder, count, totalCount)
|
||||
count = absentGoodsOrderCountMapData[storeID]
|
||||
SendAlertInfoWrapper(storeID, storeName, dayNum, AlertTypeAbsentGoodsOrder, count, totalCount)
|
||||
|
||||
if isOneWeekDay {
|
||||
count = standardFinishTimeOrderCountSelfDeliveryMapData[storeID]
|
||||
totalCount = finishOrderCountSelfDeliveryMapData[storeID]
|
||||
SendAlertInfoWrapper(storeID, storeName, dayNum, AlertTypeStandardFinishTimeOrderSelfDelivery, count, totalCount)
|
||||
|
||||
count = standardPickUpTimeOrderCountDaDaMapData[storeID]
|
||||
totalCount = finishOrderCountDaDaMapData[storeID]
|
||||
SendAlertInfoWrapper(storeID, storeName, dayNum, AlertTypeStandardPickUpTimeOrderDaDa, count, totalCount)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func CheckStoreMonthAlert(storeList []*cms.StoreExt) {
|
||||
db := dao.GetDB()
|
||||
storeCountList, _ := dao.GetFinishOrderCountByDayNum(db, CheckStoreAlertOneMonthDayNum, IncludeToday)
|
||||
storeCountMapData := make(map[int]int)
|
||||
for _, value := range storeCountList {
|
||||
storeCountMapData[value.StoreID] = value.Count
|
||||
}
|
||||
|
||||
for _, storeInfo := range storeList {
|
||||
storeID := storeInfo.ID
|
||||
storeName := storeInfo.Name
|
||||
if _, ok := storeCountMapData[storeID]; !ok {
|
||||
alertInfo := GetAlertInfo(CheckStoreAlertOneMonthDayNum, AlertLevelRed, storeName, -1, "", -1)
|
||||
SendAlertInfo(storeID, storeName, alertInfo)
|
||||
storeAlertDataWrapper.SetData(storeID, model.FieldNoOrderInMonth, 1)
|
||||
storeAlertDataWrapper.SetData(storeID, model.FieldRedStatus, model.FlagNoOrderInMonth)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func CheckStoreRiskOrderAlert(storeList []*cms.StoreExt, dayNum int) {
|
||||
db := dao.GetDB()
|
||||
storeOrderList, _ := dao.GetRiskOrderCount(db, dayNum, IncludeToday)
|
||||
storeOrderMapData := ConvertListToMapEx(storeOrderList)
|
||||
for _, storeInfo := range storeList {
|
||||
storeID := storeInfo.ID
|
||||
storeName := storeInfo.Name
|
||||
vendorOrderIDList := storeOrderMapData[storeID]
|
||||
if vendorOrderIDList != nil {
|
||||
vendorOrderIDStr := ""
|
||||
for index, vendorOrderID := range vendorOrderIDList {
|
||||
if index == 0 {
|
||||
vendorOrderIDStr += vendorOrderID
|
||||
} else {
|
||||
vendorOrderIDStr += "," + vendorOrderID
|
||||
}
|
||||
}
|
||||
alertInfo := fmt.Sprintf(RiskOrderAlertInfo, storeName, vendorOrderIDStr)
|
||||
SendAlertInfo(storeID, storeName, alertInfo)
|
||||
storeAlertDataWrapper.SetData(storeID, model.FieldRiskOrderCount, len(vendorOrderIDList))
|
||||
storeAlertDataWrapper.SetData(storeID, model.FieldRedStatus, model.FlagRiskOrderCount)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func GetWeekDay() int {
|
||||
return int(time.Now().Weekday())
|
||||
}
|
||||
|
||||
func CheckStoreAlert(ctx *jxcontext.Context, storeIDList []int) {
|
||||
storeAlertDataWrapper.InitData()
|
||||
storeList, _ := GetStoreList(ctx)
|
||||
storeIDMap := jxutils.IntList2Map(storeIDList)
|
||||
storeList = GetFilterStoreListEx(storeList, storeIDMap)
|
||||
if GetWeekDay() == MonthCheckOnWeekDay {
|
||||
CheckStoreMonthAlert(storeList)
|
||||
}
|
||||
CheckStoreDayAlert(storeList, CheckStoreAlertOneDayNum)
|
||||
CheckStoreDayAlert(storeList, CheckStoreAlertOneWeekDayNum)
|
||||
CheckStoreRiskOrderAlert(storeList, CheckStoreAlertOneDayNum)
|
||||
storeAlertDataWrapper.InsertStoreAlertList()
|
||||
storeAlertDataWrapper.ClearData()
|
||||
}
|
||||
|
||||
func ScheduleCheckStoreAlert() {
|
||||
if EnableCheckStoreAlert {
|
||||
ScheduleTimerFunc("ScheduleCheckStoreAlert", func() {
|
||||
CheckStoreAlert(jxcontext.AdminCtx, nil)
|
||||
}, checkStoreAlertTimeList)
|
||||
}
|
||||
}
|
||||
|
||||
func GetValueColorByStatus(extraRedStatus, redStatus, yellowStatus, flag int) (color string) {
|
||||
if (extraRedStatus&flag != 0) || (redStatus&flag != 0) {
|
||||
color = ColorRed
|
||||
} else if yellowStatus&flag != 0 {
|
||||
color = ColorYellow
|
||||
} else {
|
||||
color = ColorUnknown
|
||||
}
|
||||
|
||||
return color
|
||||
}
|
||||
|
||||
func GetStoreAlertList(storeIDList []int, cityCode int, keyWord string, dateTime time.Time, offset, pageSize int) (storeAlertData model.StoreAlertData, err error) {
|
||||
db := dao.GetDB()
|
||||
storeAlertList, err := dao.GetStoreAlertList(db, storeIDList, cityCode, keyWord, dateTime)
|
||||
if err == nil && len(storeAlertList) > 0 {
|
||||
offset = jxutils.FormalizePageOffset(offset)
|
||||
pageSize = jxutils.FormalizePageSize(pageSize)
|
||||
var pagedStoreAlertList []*model.StoreAlertEx
|
||||
for i := offset; i < offset+pageSize && i < len(storeAlertList); i++ {
|
||||
pagedStoreAlertList = append(pagedStoreAlertList, storeAlertList[i])
|
||||
}
|
||||
|
||||
var storeAlertAdvancedList []*model.StoreAlertAdvanced
|
||||
for _, value := range pagedStoreAlertList {
|
||||
storeAlertAdvanced := &model.StoreAlertAdvanced{}
|
||||
storeAlertAdvanced.ID = value.ID
|
||||
storeAlertAdvanced.CreatedTime = value.CreatedTime
|
||||
storeAlertAdvanced.AlertDate = value.AlertDate
|
||||
storeAlertAdvanced.StoreID = value.StoreID
|
||||
storeAlertAdvanced.StoreName = value.StoreName
|
||||
storeAlertAdvanced.CityName = value.CityName
|
||||
|
||||
storeAlertAdvanced.PickTimeDaDa.Value = fmt.Sprintf("%d%%", value.PickTimeDaDa)
|
||||
storeAlertAdvanced.PickTimeDaDa.Color = GetValueColorByStatus(value.ExtraRedStatus, value.RedStatus, value.YellowStatus, model.FlagPickTimeDaDa)
|
||||
|
||||
storeAlertAdvanced.BadComment.Value = fmt.Sprintf("%d%%", value.BadComment)
|
||||
storeAlertAdvanced.BadComment.Color = GetValueColorByStatus(value.ExtraRedStatus, value.RedStatus, value.YellowStatus, model.FlagBadComment)
|
||||
|
||||
storeAlertAdvanced.AbsentGoods.Value = fmt.Sprintf("%d%%", value.AbsentGoods)
|
||||
storeAlertAdvanced.AbsentGoods.Color = GetValueColorByStatus(value.ExtraRedStatus, value.RedStatus, value.YellowStatus, model.FlagAbsentGoods)
|
||||
|
||||
storeAlertAdvanced.PickTimeDaDaOneWeek.Value = fmt.Sprintf("%d%%", value.PickTimeDaDaOneWeek)
|
||||
storeAlertAdvanced.PickTimeDaDaOneWeek.Color = GetValueColorByStatus(value.ExtraRedStatus, value.RedStatus, value.YellowStatus, model.FlagPickTimeDaDaOneWeek)
|
||||
|
||||
storeAlertAdvanced.BadCommentOneWeek.Value = fmt.Sprintf("%d%%", value.BadCommentOneWeek)
|
||||
storeAlertAdvanced.BadCommentOneWeek.Color = GetValueColorByStatus(value.ExtraRedStatus, value.RedStatus, value.YellowStatus, model.FlagBadCommentOneWeek)
|
||||
|
||||
storeAlertAdvanced.AbsentGoodsOneWeek.Value = fmt.Sprintf("%d%%", value.AbsentGoodsOneWeek)
|
||||
storeAlertAdvanced.AbsentGoodsOneWeek.Color = GetValueColorByStatus(value.ExtraRedStatus, value.RedStatus, value.YellowStatus, model.FlagAbsentGoodsOneWeek)
|
||||
|
||||
storeAlertAdvanced.StandardFinishTimeSelfDelivery.Value = fmt.Sprintf("%d%%", value.StandardFinishTimeSelfDelivery)
|
||||
storeAlertAdvanced.StandardFinishTimeSelfDelivery.Color = GetValueColorByStatus(value.ExtraRedStatus, value.RedStatus, value.YellowStatus, model.FlagStandardFinishTimeSelfDelivery)
|
||||
|
||||
storeAlertAdvanced.StandardPickUpTimeDaDa.Value = fmt.Sprintf("%d%%", value.StandardPickUpTimeDaDa)
|
||||
storeAlertAdvanced.StandardPickUpTimeDaDa.Color = GetValueColorByStatus(value.ExtraRedStatus, value.RedStatus, value.YellowStatus, model.FlagStandardPickUpTimeDaDa)
|
||||
|
||||
if value.NoOrderInMonth == 1 {
|
||||
storeAlertAdvanced.NoOrderInMonth.Value = "是"
|
||||
} else {
|
||||
storeAlertAdvanced.NoOrderInMonth.Value = "否"
|
||||
}
|
||||
storeAlertAdvanced.NoOrderInMonth.Color = GetValueColorByStatus(value.ExtraRedStatus, value.RedStatus, value.YellowStatus, model.FlagNoOrderInMonth)
|
||||
|
||||
storeAlertAdvanced.RiskOrderCount.Value = utils.Int2Str(value.RiskOrderCount)
|
||||
storeAlertAdvanced.RiskOrderCount.Color = GetValueColorByStatus(value.ExtraRedStatus, value.RedStatus, value.YellowStatus, model.FlagRiskOrderCount)
|
||||
|
||||
storeAlertAdvancedList = append(storeAlertAdvancedList, storeAlertAdvanced)
|
||||
}
|
||||
storeAlertData.TotalCount = len(storeAlertList)
|
||||
storeAlertData.StoreAlertList = storeAlertAdvancedList
|
||||
}
|
||||
|
||||
return storeAlertData, err
|
||||
}
|
||||
905
business/jxstore/misc/store_score.go
Normal file
905
business/jxstore/misc/store_score.go
Normal file
@@ -0,0 +1,905 @@
|
||||
package misc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"reflect"
|
||||
"sort"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"git.rosy.net.cn/baseapi"
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
"git.rosy.net.cn/jx-callback/business/jxstore/cms"
|
||||
"git.rosy.net.cn/jx-callback/business/jxstore/permission"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/tasksch"
|
||||
"git.rosy.net.cn/jx-callback/business/model"
|
||||
"git.rosy.net.cn/jx-callback/business/model/dao"
|
||||
"git.rosy.net.cn/jx-callback/globals"
|
||||
"git.rosy.net.cn/jx-callback/globals/refutil"
|
||||
)
|
||||
|
||||
const (
|
||||
EnableScheduleScoreStore = true
|
||||
ParallelCount = 1
|
||||
|
||||
GoldMedalScore = 90
|
||||
SilverMedalScore = 80
|
||||
BronzeMedalScore = 70
|
||||
GoldMedalLevel = 1
|
||||
SilverMedalLevel = 2
|
||||
BronzeMedalLevel = 3
|
||||
|
||||
ItemTotalScore = 10
|
||||
|
||||
StoreOpenTimeNormalTime = 12.0 //小时
|
||||
SaleSkuNormalCount = 1000 //数量
|
||||
SaleSkuScorePerUnit = float64(ItemTotalScore) / SaleSkuNormalCount //分数
|
||||
PromotionSkuNormalCount = 20 //数量
|
||||
AveragePickupTimeNormalTime = 10.0 //分钟
|
||||
BadCommentOrderNormalRatio = 0.2 //百分比
|
||||
UnfinishOrderNormalRatio = 1.0 //百分比
|
||||
AbsentGoodsOrderNormalRatio = 1.0 //百分比
|
||||
StoreRangeGoodRadius = 2.0 //千米
|
||||
StoreRangeBadRadius = 1.0 //千米
|
||||
SaleSkuPriceRatio = 90 //千米
|
||||
SaleSkuCheckRange = 5.0 //千米
|
||||
|
||||
WeekNum = 5 //得到门店近期周的数量
|
||||
)
|
||||
|
||||
var (
|
||||
scoreStoreTimeList = []string{
|
||||
"23:00:00",
|
||||
}
|
||||
scoreStoreCheckTimeEnd = "23:30:00"
|
||||
fullVendorList = map[int]bool{
|
||||
model.VendorIDJD: true,
|
||||
model.VendorIDMTWM: true,
|
||||
model.VendorIDEBAI: true,
|
||||
}
|
||||
storeScoreFieldName = []string{
|
||||
model.FieldStoreOpenTime,
|
||||
model.FieldSaleSkuCount,
|
||||
model.FieldAveragePickupTime,
|
||||
model.FieldBadCommentOrder,
|
||||
model.FieldUnfinishOrder,
|
||||
model.FieldAbsentGoodsOrder,
|
||||
model.FieldPromotionSku,
|
||||
model.FieldFullVendor,
|
||||
model.FieldStoreRange,
|
||||
model.FieldSaleSkuPrice,
|
||||
}
|
||||
|
||||
storeScoreDataWrapper StoreScoreDataWrapper
|
||||
allStoreSkusWrapper AllStoreSkusWrapper
|
||||
scoreDate time.Time
|
||||
isScoring bool
|
||||
)
|
||||
|
||||
type AllStoreSkusWrapper struct {
|
||||
allStoreSkus map[int]map[int]int
|
||||
locker sync.RWMutex
|
||||
}
|
||||
|
||||
func (a *AllStoreSkusWrapper) InitData() {
|
||||
a.allStoreSkus = make(map[int]map[int]int)
|
||||
}
|
||||
|
||||
func (a *AllStoreSkusWrapper) ClearData() {
|
||||
a.allStoreSkus = nil
|
||||
}
|
||||
|
||||
func (a *AllStoreSkusWrapper) SetData(storeID int, skuMapData map[int]int) {
|
||||
a.locker.Lock()
|
||||
defer a.locker.Unlock()
|
||||
a.allStoreSkus[storeID] = skuMapData
|
||||
}
|
||||
|
||||
func (a *AllStoreSkusWrapper) GetData(storeID int) map[int]int {
|
||||
a.locker.RLock()
|
||||
defer a.locker.RUnlock()
|
||||
return a.allStoreSkus[storeID]
|
||||
}
|
||||
|
||||
type StoreScoreDataWrapper struct {
|
||||
storeScoreData map[int]*model.StoreScore
|
||||
dailyBadCommentOrderCount map[int]int
|
||||
dailyUnFinishOrderCount map[int]int
|
||||
dailyFinishOrderCount map[int]int
|
||||
dailyAbsentGoodsOrderCount map[int]int
|
||||
locker sync.RWMutex
|
||||
}
|
||||
|
||||
func (s *StoreScoreDataWrapper) InitData() {
|
||||
s.storeScoreData = make(map[int]*model.StoreScore)
|
||||
s.dailyBadCommentOrderCount = make(map[int]int)
|
||||
s.dailyUnFinishOrderCount = make(map[int]int)
|
||||
s.dailyFinishOrderCount = make(map[int]int)
|
||||
s.dailyAbsentGoodsOrderCount = make(map[int]int)
|
||||
}
|
||||
|
||||
func (s *StoreScoreDataWrapper) ClearData() {
|
||||
s.storeScoreData = nil
|
||||
s.dailyBadCommentOrderCount = nil
|
||||
s.dailyUnFinishOrderCount = nil
|
||||
s.dailyFinishOrderCount = nil
|
||||
s.dailyAbsentGoodsOrderCount = nil
|
||||
}
|
||||
|
||||
func (s *StoreScoreDataWrapper) SetData(storeID int, valueName string, value int) {
|
||||
s.locker.Lock()
|
||||
defer s.locker.Unlock()
|
||||
data := s.storeScoreData[storeID]
|
||||
if data == nil {
|
||||
data = &model.StoreScore{}
|
||||
data.StoreID = storeID
|
||||
data.ScoreDate = scoreDate
|
||||
s.storeScoreData[storeID] = data
|
||||
}
|
||||
valueInfo := reflect.ValueOf(data).Elem()
|
||||
valueInfo.FieldByName(valueName).SetInt(int64(value))
|
||||
}
|
||||
|
||||
func (s *StoreScoreDataWrapper) SetDailyBadCommentOrderCount(storeCountList []*model.StoreCount) {
|
||||
s.locker.Lock()
|
||||
defer s.locker.Unlock()
|
||||
for _, value := range storeCountList {
|
||||
s.dailyBadCommentOrderCount[value.StoreID] = value.Count
|
||||
}
|
||||
}
|
||||
|
||||
func (s *StoreScoreDataWrapper) GetDailyBadCommentOrderCount(storeID int) int {
|
||||
s.locker.RLock()
|
||||
defer s.locker.RUnlock()
|
||||
if count, ok := s.dailyBadCommentOrderCount[storeID]; ok {
|
||||
return count
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func (s *StoreScoreDataWrapper) SetDailyUnFinishOrderCount(storeCountList []*model.StoreCount) {
|
||||
for _, value := range storeCountList {
|
||||
s.dailyUnFinishOrderCount[value.StoreID] = value.Count
|
||||
}
|
||||
}
|
||||
|
||||
func (s *StoreScoreDataWrapper) GetDailyUnFinishOrderCount(storeID int) int {
|
||||
s.locker.RLock()
|
||||
defer s.locker.RUnlock()
|
||||
if count, ok := s.dailyUnFinishOrderCount[storeID]; ok {
|
||||
return count
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func (s *StoreScoreDataWrapper) SetDailyFinishOrderCount(storeCountList []*model.StoreCount) {
|
||||
for _, value := range storeCountList {
|
||||
s.dailyFinishOrderCount[value.StoreID] = value.Count
|
||||
}
|
||||
}
|
||||
|
||||
func (s *StoreScoreDataWrapper) GetDailyFinishOrderCount(storeID int) int {
|
||||
s.locker.RLock()
|
||||
defer s.locker.RUnlock()
|
||||
if count, ok := s.dailyFinishOrderCount[storeID]; ok {
|
||||
return count
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func (s *StoreScoreDataWrapper) SetDailyAbsentGoodsOrderCount(storeCountList []*model.StoreCount) {
|
||||
for _, value := range storeCountList {
|
||||
s.dailyAbsentGoodsOrderCount[value.StoreID] = value.Count
|
||||
}
|
||||
}
|
||||
|
||||
func (s *StoreScoreDataWrapper) GetDailyAbsentGoodsOrderCount(storeID int) int {
|
||||
s.locker.RLock()
|
||||
defer s.locker.RUnlock()
|
||||
if count, ok := s.dailyAbsentGoodsOrderCount[storeID]; ok {
|
||||
return count
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func (s *StoreScoreDataWrapper) InsertStoreScore() {
|
||||
for _, value := range s.storeScoreData {
|
||||
dao.InsertStoreScore(value)
|
||||
}
|
||||
}
|
||||
|
||||
//得到所有门店的可售商品(优化内存,只存商品的价格)
|
||||
func GetAllStoreSkus(ctx *jxcontext.Context, parentTask tasksch.ITask, storeList []*cms.StoreExt) {
|
||||
allStoreSkusWrapper.InitData()
|
||||
taskFunc := func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
|
||||
storeInfo := batchItemList[0].(*cms.StoreExt)
|
||||
storeID := storeInfo.ID
|
||||
if jxSkuInfoData, err2 := cms.GetStoreSkus(ctx, storeID, []int{}, true, "", true, false, map[string]interface{}{}, 0, -1); jxSkuInfoData != nil {
|
||||
jxSkuPriceMapData := make(map[int]int)
|
||||
for _, value := range jxSkuInfoData.SkuNames {
|
||||
for _, skuInfo := range value.Skus {
|
||||
saleStatus := jxutils.MergeSkuStatus(skuInfo.SkuStatus, skuInfo.StoreSkuStatus)
|
||||
if saleStatus == model.SkuStatusNormal {
|
||||
jxSkuPriceMapData[skuInfo.SkuID] = skuInfo.BindPrice
|
||||
}
|
||||
}
|
||||
}
|
||||
allStoreSkusWrapper.SetData(storeID, jxSkuPriceMapData)
|
||||
} else {
|
||||
globals.SugarLogger.Warnf("store_score.GetAllStoreSkus %d return empty, err:%v", storeID, err2)
|
||||
}
|
||||
return retVal, err
|
||||
}
|
||||
taskParallel := tasksch.NewParallelTask("得到所有门店商品", tasksch.NewParallelConfig().SetParallelCount(ParallelCount), ctx, taskFunc, storeList)
|
||||
tasksch.HandleTask(taskParallel, parentTask, true).Run()
|
||||
taskParallel.GetResult(0)
|
||||
}
|
||||
|
||||
func GetOpenTime(opTimeList []int16) (opTime float64) {
|
||||
opTime = 0
|
||||
for index, _ := range opTimeList {
|
||||
if index%2 == 1 {
|
||||
endTime := opTimeList[index]
|
||||
startTime := opTimeList[index-1]
|
||||
diffHour := float64(endTime/100 - startTime/100)
|
||||
diffMin := float64(endTime%100-startTime%100) / 60
|
||||
opTime += diffHour + diffMin
|
||||
}
|
||||
}
|
||||
return opTime
|
||||
}
|
||||
|
||||
//营业时间12小时及以上满分,总分10分,每少一个小时扣1分
|
||||
func ScoreStoreOpenTime(storeInfo *cms.StoreExt) {
|
||||
storeID := storeInfo.ID
|
||||
storeStatus := storeInfo.Status
|
||||
isStoreOpen := storeStatus == model.StoreStatusOpened
|
||||
finalScore := 0
|
||||
if isStoreOpen {
|
||||
for _, storeMap := range storeInfo.StoreMaps {
|
||||
isSyncStoreSku := storeMap.IsSync
|
||||
vendorStoreStatus := storeMap.Status
|
||||
isVendorStoreOpen := vendorStoreStatus == model.StoreStatusOpened
|
||||
opTimeList := storeInfo.GetOpTimeList()
|
||||
if len(opTimeList) > 0 && isStoreOpen && isVendorStoreOpen && isSyncStoreSku != 0 {
|
||||
opTime := GetOpenTime(opTimeList)
|
||||
if opTime >= StoreOpenTimeNormalTime {
|
||||
finalScore = ItemTotalScore
|
||||
} else {
|
||||
decScore := int(math.Round(StoreOpenTimeNormalTime - opTime))
|
||||
finalScore = ItemTotalScore - decScore
|
||||
if finalScore < 0 {
|
||||
finalScore = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
storeScoreDataWrapper.SetData(storeID, model.FieldStoreOpenTime, finalScore)
|
||||
}
|
||||
|
||||
//可售商品数量大于1000,总分10分,按比例扣
|
||||
func ScoreSaleSkuCount(storeInfo *cms.StoreExt) {
|
||||
storeID := storeInfo.ID
|
||||
skusMapData := allStoreSkusWrapper.GetData(storeID)
|
||||
finalScore := 0
|
||||
if len(skusMapData) > 0 {
|
||||
saleSkuCount := len(skusMapData)
|
||||
finalScore = int(math.Round(float64(saleSkuCount) * SaleSkuScorePerUnit))
|
||||
if finalScore > ItemTotalScore {
|
||||
finalScore = ItemTotalScore
|
||||
}
|
||||
}
|
||||
storeScoreDataWrapper.SetData(storeID, model.FieldSaleSkuCount, finalScore)
|
||||
}
|
||||
|
||||
//平均捡货时间小于等于拣货截止时间为10分满分,每超出1分钟,减1分
|
||||
func ScoreAveragePickupTime(storeInfo *cms.StoreExt) {
|
||||
storeID := storeInfo.ID
|
||||
db := dao.GetDB()
|
||||
orderList, err := dao.GetDailyFinishOrderList(db, storeID, scoreDate)
|
||||
orderListCount := len(orderList)
|
||||
finalScore := 0
|
||||
if err == nil && orderListCount > 0 {
|
||||
totalScore := 0
|
||||
for _, value := range orderList {
|
||||
statusTime := value.StatusTime.Unix()
|
||||
pickDeadline := value.PickDeadline.Unix()
|
||||
if statusTime <= pickDeadline {
|
||||
totalScore += ItemTotalScore
|
||||
} else {
|
||||
decScore := int(math.Round(float64(statusTime-pickDeadline) / 60))
|
||||
tempScore := ItemTotalScore - decScore
|
||||
if tempScore < 0 {
|
||||
tempScore = 0
|
||||
}
|
||||
totalScore += tempScore
|
||||
}
|
||||
}
|
||||
finalScore = totalScore / orderListCount
|
||||
}
|
||||
storeScoreDataWrapper.SetData(storeID, model.FieldAveragePickupTime, finalScore)
|
||||
}
|
||||
|
||||
//差评订单和完成订单比例小于0.2%,得满分10分,每增加0.1%减1分
|
||||
func ScoreBadCommentOrder(storeInfo *cms.StoreExt) {
|
||||
storeID := storeInfo.ID
|
||||
badCommentOrderCount := storeScoreDataWrapper.GetDailyBadCommentOrderCount(storeID)
|
||||
finishOrderCount := storeScoreDataWrapper.GetDailyFinishOrderCount(storeID)
|
||||
finalScore := 0
|
||||
if finishOrderCount > 0 {
|
||||
badCommentOrderRatio := float64(badCommentOrderCount) * 100 / float64(finishOrderCount)
|
||||
if badCommentOrderRatio <= BadCommentOrderNormalRatio {
|
||||
finalScore = ItemTotalScore
|
||||
} else {
|
||||
decScore := int(math.Round((badCommentOrderRatio - BadCommentOrderNormalRatio) / 0.1))
|
||||
finalScore = ItemTotalScore - decScore
|
||||
if finalScore < 0 {
|
||||
finalScore = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
storeScoreDataWrapper.SetData(storeID, model.FieldBadCommentOrder, finalScore)
|
||||
}
|
||||
|
||||
//未完成订单和完成订单比小于1%,得满分10分,比例每增加5%,分数减1
|
||||
func ScoreUnfinishOrder(storeInfo *cms.StoreExt) {
|
||||
storeID := storeInfo.ID
|
||||
unFinishOrderCount := storeScoreDataWrapper.GetDailyUnFinishOrderCount(storeID)
|
||||
finishOrderCount := storeScoreDataWrapper.GetDailyFinishOrderCount(storeID)
|
||||
finalScore := 0
|
||||
if finishOrderCount > 0 {
|
||||
unfinishOrderRatio := float64(unFinishOrderCount) * 100 / float64(finishOrderCount)
|
||||
if unfinishOrderRatio <= UnfinishOrderNormalRatio {
|
||||
finalScore = ItemTotalScore
|
||||
} else {
|
||||
decScore := int(math.Round((unfinishOrderRatio - UnfinishOrderNormalRatio) / 5))
|
||||
finalScore = ItemTotalScore - decScore
|
||||
if finalScore < 0 {
|
||||
finalScore = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
storeScoreDataWrapper.SetData(storeID, model.FieldUnfinishOrder, finalScore)
|
||||
}
|
||||
|
||||
//缺货订单和完成订单比小于1%得10分,比例每增加0.1%减1分
|
||||
func ScoreAbsentGoodsOrder(storeInfo *cms.StoreExt) {
|
||||
storeID := storeInfo.ID
|
||||
absentGoodsOrderCount := storeScoreDataWrapper.GetDailyAbsentGoodsOrderCount(storeID)
|
||||
finishOrderCount := storeScoreDataWrapper.GetDailyFinishOrderCount(storeID)
|
||||
finalScore := 0
|
||||
if finishOrderCount > 0 {
|
||||
absentGoodsOrderRatio := float64(absentGoodsOrderCount) * 100 / float64(finishOrderCount)
|
||||
if absentGoodsOrderRatio <= AbsentGoodsOrderNormalRatio {
|
||||
finalScore = ItemTotalScore
|
||||
} else {
|
||||
decScore := int(math.Round((absentGoodsOrderRatio - AbsentGoodsOrderNormalRatio) / 0.1))
|
||||
finalScore = ItemTotalScore - decScore
|
||||
if finalScore < 0 {
|
||||
finalScore = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
storeScoreDataWrapper.SetData(storeID, model.FieldAbsentGoodsOrder, finalScore)
|
||||
}
|
||||
|
||||
//促销品数量20个以上为满分10分,每少2个扣1分
|
||||
func ScorePromotionSku(storeInfo *cms.StoreExt) {
|
||||
storeID := storeInfo.ID
|
||||
db := dao.GetDB()
|
||||
beginTime := time.Now()
|
||||
endTime := time.Now()
|
||||
actStoreSkuList, err := dao.GetEffectiveActStoreSkuInfo(db, -1, nil, model.ActTypeAll, []int{storeID}, nil, beginTime, endTime)
|
||||
finalScore := 0
|
||||
if err == nil && len(actStoreSkuList) > 0 {
|
||||
actStoreSkuMap := make(map[int]int)
|
||||
for _, value := range actStoreSkuList {
|
||||
if value.Type != model.ActSkuFake {
|
||||
actStoreSkuMap[value.SkuID] = 1
|
||||
}
|
||||
}
|
||||
promotionSkuCount := len(actStoreSkuMap)
|
||||
if promotionSkuCount >= PromotionSkuNormalCount {
|
||||
finalScore = ItemTotalScore
|
||||
} else {
|
||||
decScore := (PromotionSkuNormalCount - promotionSkuCount) / 2
|
||||
finalScore = ItemTotalScore - decScore
|
||||
if finalScore < 0 {
|
||||
finalScore = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
storeScoreDataWrapper.SetData(storeID, model.FieldPromotionSku, finalScore)
|
||||
}
|
||||
|
||||
//经营全平台满分10分,每少一个平台扣2分(一个平台没有得0分)
|
||||
func ScoreFullVendor(storeInfo *cms.StoreExt) {
|
||||
fullVendorCount := len(fullVendorList)
|
||||
finalScore := 0
|
||||
storeID := storeInfo.ID
|
||||
storeStatus := storeInfo.Status
|
||||
isStoreOpen := storeStatus == model.StoreStatusOpened
|
||||
count := 0
|
||||
for _, storeMap := range storeInfo.StoreMaps {
|
||||
isSyncStoreSku := storeMap.IsSync
|
||||
vendorStoreStatus := storeMap.Status
|
||||
isVendorStoreOpen := vendorStoreStatus == model.StoreStatusOpened
|
||||
opTimeList := storeInfo.GetOpTimeList()
|
||||
if len(opTimeList) > 0 && isStoreOpen && isVendorStoreOpen && isSyncStoreSku != 0 {
|
||||
count++
|
||||
}
|
||||
}
|
||||
if count > 0 {
|
||||
if count == fullVendorCount {
|
||||
finalScore = ItemTotalScore
|
||||
} else {
|
||||
decScore := (fullVendorCount - count) * 2
|
||||
finalScore = ItemTotalScore - decScore
|
||||
}
|
||||
}
|
||||
storeScoreDataWrapper.SetData(storeID, model.FieldFullVendor, finalScore)
|
||||
}
|
||||
|
||||
//经营范围面积大于半径2km的圆得满分10分,低于1km得分0
|
||||
func ScoreStoreRange(storeInfo *cms.StoreExt) {
|
||||
finalScore := 0
|
||||
storeID := storeInfo.ID
|
||||
if storeInfo.DeliveryRangeType == model.DeliveryRangeTypePolygon {
|
||||
if storeInfo.DeliveryRange != "" {
|
||||
points := jxutils.CoordinateStr2Points(storeInfo.DeliveryRange)
|
||||
area := jxutils.CalcPolygonAreaAutonavi(points)
|
||||
goodArea := math.Pi * StoreRangeGoodRadius * StoreRangeGoodRadius
|
||||
badArea := math.Pi * StoreRangeBadRadius * StoreRangeBadRadius
|
||||
if area >= goodArea {
|
||||
finalScore = ItemTotalScore
|
||||
} else if area <= badArea {
|
||||
finalScore = 0
|
||||
} else {
|
||||
diff := goodArea - area
|
||||
ratio := float64(ItemTotalScore) / (goodArea - badArea)
|
||||
finalScore = ItemTotalScore - int(math.Round(diff*ratio))
|
||||
}
|
||||
}
|
||||
} else if storeInfo.DeliveryRangeType == model.DeliveryRangeTypeRadius {
|
||||
deliveryRadius := utils.Str2Float64WithDefault(storeInfo.DeliveryRange, 0) / 1000
|
||||
if deliveryRadius >= StoreRangeGoodRadius {
|
||||
finalScore = ItemTotalScore
|
||||
} else if deliveryRadius <= StoreRangeBadRadius {
|
||||
finalScore = 0
|
||||
} else {
|
||||
diff := StoreRangeGoodRadius - deliveryRadius
|
||||
ratio := float64(ItemTotalScore) / (StoreRangeGoodRadius - StoreRangeBadRadius)
|
||||
finalScore = ItemTotalScore - int(math.Round(diff*ratio))
|
||||
}
|
||||
}
|
||||
if finalScore < 0 {
|
||||
globals.SugarLogger.Infof("ScoreStoreRange abnormal finalScore:%d, storeInfo:%s", finalScore, utils.Format4Output(storeInfo, true))
|
||||
finalScore = 0
|
||||
} else if finalScore > ItemTotalScore {
|
||||
globals.SugarLogger.Infof("ScoreStoreRange abnormal finalScore:%d, storeInfo:%s", finalScore, utils.Format4Output(storeInfo, true))
|
||||
finalScore = ItemTotalScore
|
||||
}
|
||||
storeScoreDataWrapper.SetData(storeID, model.FieldStoreRange, finalScore)
|
||||
}
|
||||
|
||||
//得到距离某个门店多少KM内的所有门店信息
|
||||
func GetRangeStoreList(storeID int, lng, lat, checkRange float64, storeList []*cms.StoreExt) (outStoreList []*cms.StoreExt) {
|
||||
for _, storeInfo := range storeList {
|
||||
if storeInfo.ID == storeID {
|
||||
outStoreList = append(outStoreList, storeInfo)
|
||||
} else {
|
||||
distance := jxutils.EarthDistance(lng, lat, storeInfo.FloatLng, storeInfo.FloatLat)
|
||||
if distance <= checkRange {
|
||||
outStoreList = append(outStoreList, storeInfo)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return outStoreList
|
||||
}
|
||||
|
||||
//得到给定门店列表里的同一SkuID商品的平均价格
|
||||
func GetStoreSkusAveragePrice(storeList []*cms.StoreExt) map[int]int {
|
||||
skusTotalPrice := make(map[int]int)
|
||||
skusCount := make(map[int]int)
|
||||
skusAveragePrice := make(map[int]int)
|
||||
for _, storeInfo := range storeList {
|
||||
storeID := storeInfo.ID
|
||||
storeSkuMapData := allStoreSkusWrapper.GetData(storeID)
|
||||
for skuID, skuPrice := range storeSkuMapData {
|
||||
skusTotalPrice[skuID] += skuPrice
|
||||
skusCount[skuID]++
|
||||
}
|
||||
}
|
||||
for id, totalPrice := range skusTotalPrice {
|
||||
skusAveragePrice[id] = int(math.Round(float64(totalPrice) / float64(skusCount[id])))
|
||||
}
|
||||
return skusAveragePrice
|
||||
}
|
||||
|
||||
func GetSkusCountLessEqualAvgPrice(storeID int, skusAveragePrice map[int]int) (count int) {
|
||||
storeSkuMapData := allStoreSkusWrapper.GetData(storeID)
|
||||
for skuID, skuPrice := range storeSkuMapData {
|
||||
skuAvgPrice := skusAveragePrice[skuID]
|
||||
if skuPrice <= skuAvgPrice {
|
||||
count++
|
||||
}
|
||||
}
|
||||
|
||||
return count
|
||||
}
|
||||
|
||||
//可售商品价格在附近5km内门店比较,价格低于等于平均值的商品数占90%以上满分10分,比例每降低10%减1分,100%超标得0分
|
||||
func ScoreSaleSkuPrice(storeInfo *cms.StoreExt, storeList []*cms.StoreExt) {
|
||||
finalScore := 0
|
||||
storeID := storeInfo.ID
|
||||
totalCount := len(allStoreSkusWrapper.GetData(storeID))
|
||||
if totalCount > 0 {
|
||||
rangeStoreList := GetRangeStoreList(storeID, storeInfo.FloatLng, storeInfo.FloatLat, SaleSkuCheckRange, storeList)
|
||||
skusAveragePrice := GetStoreSkusAveragePrice(rangeStoreList)
|
||||
count := GetSkusCountLessEqualAvgPrice(storeID, skusAveragePrice)
|
||||
if count > 0 {
|
||||
ratio := int(math.Round(float64(count) * 100 / float64(totalCount)))
|
||||
if ratio >= SaleSkuPriceRatio {
|
||||
finalScore = ItemTotalScore
|
||||
} else {
|
||||
decScore := (SaleSkuPriceRatio - ratio) / 10
|
||||
finalScore = ItemTotalScore - decScore
|
||||
if finalScore < 0 {
|
||||
finalScore = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
storeScoreDataWrapper.SetData(storeID, model.FieldSaleSkuPrice, finalScore)
|
||||
}
|
||||
|
||||
func GetFilterStoreListEx(storeList []*cms.StoreExt, storeIDMap map[int]int) (outStoreList []*cms.StoreExt) {
|
||||
for _, storeInfo := range storeList {
|
||||
storeID := storeInfo.ID
|
||||
if len(storeIDMap) > 0 {
|
||||
if _, ok := storeIDMap[storeID]; !ok {
|
||||
continue
|
||||
}
|
||||
}
|
||||
var tempStoreMaps []*model.StoreMap
|
||||
for _, vendorStoreInfo := range storeInfo.StoreMaps {
|
||||
vendorID := vendorStoreInfo.VendorID
|
||||
if _, ok := fullVendorList[vendorID]; !ok {
|
||||
continue
|
||||
}
|
||||
tempStoreMaps = append(tempStoreMaps, vendorStoreInfo)
|
||||
}
|
||||
if len(tempStoreMaps) > 0 {
|
||||
storeInfo.StoreMaps = tempStoreMaps
|
||||
outStoreList = append(outStoreList, storeInfo)
|
||||
}
|
||||
}
|
||||
|
||||
return outStoreList
|
||||
}
|
||||
|
||||
func ScoreStore(ctx *jxcontext.Context, storeIDList []int) (retVal interface{}, err error) {
|
||||
isScoring = true
|
||||
scoreDate = utils.GetCurDate()
|
||||
var storeList []*cms.StoreExt
|
||||
taskCount := 5
|
||||
taskSeqFunc := func(task *tasksch.SeqTask, step int, params ...interface{}) (result interface{}, err error) {
|
||||
switch step {
|
||||
case 0:
|
||||
baseapi.SugarLogger.Debugf("ScoreStore step0 begin")
|
||||
storeScoreDataWrapper.InitData()
|
||||
storeList, err = GetStoreList(ctx)
|
||||
storeIDMap := jxutils.IntList2Map(storeIDList)
|
||||
storeList = GetFilterStoreListEx(storeList, storeIDMap)
|
||||
baseapi.SugarLogger.Debugf("ScoreStore step0 end")
|
||||
case 1:
|
||||
baseapi.SugarLogger.Debugf("ScoreStore step1 begin")
|
||||
GetAllStoreSkus(ctx, task, storeList)
|
||||
baseapi.SugarLogger.Debugf("ScoreStore step1 end")
|
||||
case 2:
|
||||
baseapi.SugarLogger.Debugf("ScoreStore step2 begin")
|
||||
db := dao.GetDB()
|
||||
storeCountList, err := dao.GetDailyBadCommentOrderCount(db, scoreDate)
|
||||
if err == nil {
|
||||
storeScoreDataWrapper.SetDailyBadCommentOrderCount(storeCountList)
|
||||
} else {
|
||||
baseapi.SugarLogger.Debugf("ScoreStore GetDailyBadCommentOrderCount %v", err)
|
||||
}
|
||||
storeCountList, err = dao.GetDailyUnFinishOrderCount(db, scoreDate)
|
||||
if err == nil {
|
||||
storeScoreDataWrapper.SetDailyUnFinishOrderCount(storeCountList)
|
||||
} else {
|
||||
baseapi.SugarLogger.Debugf("ScoreStore GetDailyUnFinishOrderCount %v", err)
|
||||
}
|
||||
storeCountList, err = dao.GetDailyFinishOrderCount(db, scoreDate)
|
||||
if err == nil {
|
||||
storeScoreDataWrapper.SetDailyFinishOrderCount(storeCountList)
|
||||
} else {
|
||||
baseapi.SugarLogger.Debugf("ScoreStore GetDailyFinishOrderCount %v", err)
|
||||
}
|
||||
storeCountList, err = dao.GetDailyAbsentGoodsOrderCount(db, scoreDate)
|
||||
if err == nil {
|
||||
storeScoreDataWrapper.SetDailyAbsentGoodsOrderCount(storeCountList)
|
||||
} else {
|
||||
baseapi.SugarLogger.Debugf("ScoreStore GetDailyAbsentGoodsOrderCount %v", err)
|
||||
}
|
||||
baseapi.SugarLogger.Debugf("ScoreStore step2 end")
|
||||
case 3:
|
||||
baseapi.SugarLogger.Debugf("ScoreStore step3 begin")
|
||||
taskFunc := func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
|
||||
storeInfo := batchItemList[0].(*cms.StoreExt)
|
||||
storeID := storeInfo.ID
|
||||
baseapi.SugarLogger.Debugf("Begin store id:%d", storeID)
|
||||
ScoreStoreOpenTime(storeInfo)
|
||||
ScoreSaleSkuCount(storeInfo)
|
||||
ScoreAveragePickupTime(storeInfo)
|
||||
ScoreBadCommentOrder(storeInfo)
|
||||
ScoreUnfinishOrder(storeInfo)
|
||||
ScoreAbsentGoodsOrder(storeInfo)
|
||||
ScorePromotionSku(storeInfo)
|
||||
ScoreFullVendor(storeInfo)
|
||||
ScoreStoreRange(storeInfo)
|
||||
ScoreSaleSkuPrice(storeInfo, storeList)
|
||||
baseapi.SugarLogger.Debugf("End store id:%d", storeID)
|
||||
return retVal, err
|
||||
}
|
||||
taskParallel := tasksch.NewParallelTask("计算门店得分", tasksch.NewParallelConfig().SetParallelCount(ParallelCount), ctx, taskFunc, storeList)
|
||||
tasksch.HandleTask(taskParallel, task, true).Run()
|
||||
taskParallel.GetResult(0)
|
||||
_, err = taskParallel.GetResult(0)
|
||||
if err != nil {
|
||||
baseapi.SugarLogger.Debugf("ScoreStore taskParallel error:%v", err)
|
||||
}
|
||||
baseapi.SugarLogger.Debugf("ScoreStore step3 end")
|
||||
case 4:
|
||||
baseapi.SugarLogger.Debugf("ScoreStore step4 begin")
|
||||
storeScoreDataWrapper.InsertStoreScore()
|
||||
storeScoreDataWrapper.ClearData()
|
||||
allStoreSkusWrapper.ClearData()
|
||||
baseapi.SugarLogger.Debugf("ScoreStore step4 end")
|
||||
isScoring = false
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
taskSeq := tasksch.NewSeqTask("门店评分-序列任务", ctx, taskSeqFunc, taskCount)
|
||||
tasksch.HandleTask(taskSeq, nil, true).Run()
|
||||
|
||||
return retVal, err
|
||||
}
|
||||
|
||||
func ScheduleScoreStore() {
|
||||
if EnableScheduleScoreStore {
|
||||
ScheduleTimerFunc("ScheduleScoreStore", func() {
|
||||
if !isScoring {
|
||||
ScoreStore(jxcontext.AdminCtx, nil)
|
||||
}
|
||||
}, scoreStoreTimeList)
|
||||
CheckScoreStore()
|
||||
}
|
||||
}
|
||||
|
||||
func CheckScoreStore() {
|
||||
if !isScoring {
|
||||
curTime := time.Now()
|
||||
checkTimeStr1 := fmt.Sprintf("%s %s", utils.Time2DateStr(curTime), scoreStoreTimeList[0])
|
||||
checkTime1 := utils.Str2Time(checkTimeStr1)
|
||||
checkTimeStr2 := fmt.Sprintf("%s %s", utils.Time2DateStr(curTime), scoreStoreCheckTimeEnd)
|
||||
checkTime2 := utils.Str2Time(checkTimeStr2)
|
||||
if curTime.Unix() >= checkTime1.Unix() && curTime.Unix() <= checkTime2.Unix() {
|
||||
db := dao.GetDB()
|
||||
hasStoreScoreData, err := dao.CheckHasStoreScoreData(db, curTime)
|
||||
if err == nil && !hasStoreScoreData {
|
||||
ScoreStore(jxcontext.AdminCtx, nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Time2Week(t time.Time) int {
|
||||
yearDay := t.YearDay()
|
||||
yearFirstDay := t.AddDate(0, 0, -yearDay+1)
|
||||
firstDayInWeek := int(yearFirstDay.Weekday())
|
||||
|
||||
firstWeekDays := 1
|
||||
if firstDayInWeek != 0 {
|
||||
firstWeekDays = 7 - firstDayInWeek + 1
|
||||
}
|
||||
var week int
|
||||
if yearDay <= firstWeekDays {
|
||||
week = 1
|
||||
} else {
|
||||
tempWeek := (float64(yearDay) - float64(firstWeekDays)) / float64(7)
|
||||
week = int(math.Ceil(tempWeek)) + 1
|
||||
}
|
||||
|
||||
return week
|
||||
}
|
||||
|
||||
func SplitToSingleWeekDataList(storeScoreList []*model.StoreScoreEx) (weekDataList [][]*model.StoreScoreEx) {
|
||||
singleWeekData := []*model.StoreScoreEx{}
|
||||
weekIndex := 0
|
||||
for _, value := range storeScoreList {
|
||||
if weekIndex == 0 {
|
||||
weekIndex = Time2Week(value.ScoreDate)
|
||||
}
|
||||
if weekIndex == Time2Week(value.ScoreDate) {
|
||||
singleWeekData = append(singleWeekData, value)
|
||||
} else {
|
||||
weekDataList = append(weekDataList, singleWeekData)
|
||||
singleWeekData = []*model.StoreScoreEx{}
|
||||
weekIndex = 0
|
||||
singleWeekData = append(singleWeekData, value)
|
||||
}
|
||||
}
|
||||
if len(singleWeekData) > 0 {
|
||||
weekDataList = append(weekDataList, singleWeekData)
|
||||
}
|
||||
|
||||
return weekDataList
|
||||
}
|
||||
|
||||
func GetStoreScoreLevel(score int) int {
|
||||
level := 0
|
||||
if score >= GoldMedalScore {
|
||||
level = GoldMedalLevel
|
||||
} else if score >= SilverMedalScore {
|
||||
level = SilverMedalLevel
|
||||
} else if score >= BronzeMedalScore {
|
||||
level = BronzeMedalLevel
|
||||
}
|
||||
|
||||
return level
|
||||
}
|
||||
|
||||
func GetWeeklyStoreScore(storeID, weekIndexParam int) (outWeeklyStoreScoreDataList []*model.WeeklyStoreScore, err error) {
|
||||
db := dao.GetDB()
|
||||
storeScoreList, err := dao.GetWeeklyStoreScoreList(db, storeID, WeekNum)
|
||||
if err == nil && len(storeScoreList) > 0 {
|
||||
weeklyStoreScoreDataList := []*model.WeeklyStoreScore{}
|
||||
weekDataList := SplitToSingleWeekDataList(storeScoreList)
|
||||
for weekIndex, weekData := range weekDataList {
|
||||
weeklyData := &model.WeeklyStoreScore{}
|
||||
weeklyData.ID = weekIndex
|
||||
weeklyData.ItemTotalScore = ItemTotalScore
|
||||
weeklyData.StoreID = storeID
|
||||
weeklyStoreScoreDataList = append(weeklyStoreScoreDataList, weeklyData)
|
||||
weekDataCount := len(weekData)
|
||||
for dayIndex, dayData := range weekData {
|
||||
for _, fieldName := range storeScoreFieldName {
|
||||
srcFieldValue := refutil.GetObjFieldByName(dayData, fieldName).(int)
|
||||
destFieldValue := refutil.GetObjFieldByName(weeklyData, fieldName).(int)
|
||||
refutil.SetObjFieldByName(weeklyData, fieldName, destFieldValue+srcFieldValue)
|
||||
}
|
||||
if weekDataCount == 1 {
|
||||
weeklyData.BeginTime = dayData.ScoreDate
|
||||
weeklyData.EndTime = dayData.ScoreDate
|
||||
} else {
|
||||
if dayIndex == 0 {
|
||||
weeklyData.EndTime = dayData.ScoreDate
|
||||
} else if dayIndex == weekDataCount-1 {
|
||||
weeklyData.BeginTime = dayData.ScoreDate
|
||||
}
|
||||
}
|
||||
weeklyData.StoreName = dayData.StoreName
|
||||
}
|
||||
for _, fieldName := range storeScoreFieldName {
|
||||
destFieldValue := refutil.GetObjFieldByName(weeklyData, fieldName).(int)
|
||||
refutil.SetObjFieldByName(weeklyData, fieldName, int(math.Round(float64(destFieldValue)/float64(weekDataCount))))
|
||||
}
|
||||
for _, fieldName := range storeScoreFieldName {
|
||||
srcFieldValue := refutil.GetObjFieldByName(weeklyData, fieldName).(int)
|
||||
destFieldValue := refutil.GetObjFieldByName(weeklyData, model.FieldTotalScore).(int)
|
||||
refutil.SetObjFieldByName(weeklyData, model.FieldTotalScore, destFieldValue+srcFieldValue)
|
||||
}
|
||||
weeklyData.Level = GetStoreScoreLevel(weeklyData.TotalScore)
|
||||
}
|
||||
if weekIndexParam == -1 {
|
||||
outWeeklyStoreScoreDataList = weeklyStoreScoreDataList
|
||||
} else {
|
||||
fmt.Println("testss", utils.Format4Output(weeklyStoreScoreDataList, false))
|
||||
outWeeklyStoreScoreDataList = []*model.WeeklyStoreScore{weeklyStoreScoreDataList[weekIndexParam]}
|
||||
}
|
||||
}
|
||||
|
||||
return outWeeklyStoreScoreDataList, err
|
||||
}
|
||||
|
||||
func GetStoreTotalScoreList(ctx *jxcontext.Context, storeIDList []int, cityCode int, keyWord string, beginTime, endTime time.Time, isDesc bool, checkScoreLow, checkScoreHigh, offset, pageSize int) (storeTotalScoreEx model.StoreTotalScoreEx, err error) {
|
||||
db := dao.GetDB()
|
||||
//权限
|
||||
if permission.IsRoled(ctx) {
|
||||
if storeIDsMap, err := permission.GetUserStoresResultMap(ctx.GetUserID()); err == nil {
|
||||
var storeIDs2 []int
|
||||
if len(storeIDList) > 0 {
|
||||
for _, v := range storeIDList {
|
||||
if storeIDsMap[v] != 0 {
|
||||
storeIDs2 = append(storeIDs2, v)
|
||||
}
|
||||
}
|
||||
if len(storeIDs2) == 0 {
|
||||
storeIDs2 = append(storeIDs2, -1)
|
||||
}
|
||||
} else {
|
||||
for k, _ := range storeIDsMap {
|
||||
storeIDs2 = append(storeIDs2, k)
|
||||
}
|
||||
}
|
||||
storeIDList = nil
|
||||
storeIDList = storeIDs2
|
||||
}
|
||||
}
|
||||
storeTotalScoreMapData := make(map[int]*model.StoreTotalScore)
|
||||
storeTotalScoreList, err := dao.GetStoreTotalScoreList(db, storeIDList, cityCode, keyWord, beginTime, endTime)
|
||||
var filterStoreTotalScoreList []*model.StoreTotalScore
|
||||
if err == nil && len(storeTotalScoreList) > 0 {
|
||||
countDayNum := make(map[int]int)
|
||||
for _, value := range storeTotalScoreList {
|
||||
storeID := value.StoreID
|
||||
if storeTotalScoreMapData[storeID] == nil {
|
||||
storeTotalScore := &model.StoreTotalScore{}
|
||||
storeTotalScore.StoreID = value.StoreID
|
||||
storeTotalScore.StoreName = value.StoreName
|
||||
storeTotalScore.CityName = value.CityName
|
||||
storeTotalScoreMapData[storeID] = storeTotalScore
|
||||
}
|
||||
storeTotalScore := storeTotalScoreMapData[storeID]
|
||||
storeTotalScore.StoreScore += value.StoreScore
|
||||
countDayNum[storeID]++
|
||||
}
|
||||
for storeID, value := range storeTotalScoreMapData {
|
||||
value.StoreScore = int(math.Round(float64(value.StoreScore) / float64(countDayNum[storeID])))
|
||||
needAdd := true
|
||||
if checkScoreLow > 0 && value.StoreScore < checkScoreLow {
|
||||
needAdd = false
|
||||
}
|
||||
if checkScoreHigh > 0 && value.StoreScore > checkScoreHigh {
|
||||
needAdd = false
|
||||
}
|
||||
if needAdd {
|
||||
filterStoreTotalScoreList = append(filterStoreTotalScoreList, value)
|
||||
}
|
||||
}
|
||||
if isDesc {
|
||||
sort.Slice(filterStoreTotalScoreList, func(i, j int) bool {
|
||||
data1 := filterStoreTotalScoreList[i]
|
||||
data2 := filterStoreTotalScoreList[j]
|
||||
if data1.StoreScore == data2.StoreScore {
|
||||
return data1.StoreID < data2.StoreID
|
||||
} else {
|
||||
return data1.StoreScore > data2.StoreScore
|
||||
}
|
||||
})
|
||||
} else {
|
||||
sort.Slice(filterStoreTotalScoreList, func(i, j int) bool {
|
||||
data1 := filterStoreTotalScoreList[i]
|
||||
data2 := filterStoreTotalScoreList[j]
|
||||
if data1.StoreScore == data2.StoreScore {
|
||||
return data1.StoreID < data2.StoreID
|
||||
} else {
|
||||
return data1.StoreScore < data2.StoreScore
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
offset = jxutils.FormalizePageOffset(offset)
|
||||
pageSize = jxutils.FormalizePageSize(pageSize)
|
||||
var pagedStoreTotalScoreList []*model.StoreTotalScore
|
||||
for i := offset; i < offset+pageSize && i < len(filterStoreTotalScoreList); i++ {
|
||||
pagedStoreTotalScoreList = append(pagedStoreTotalScoreList, filterStoreTotalScoreList[i])
|
||||
}
|
||||
|
||||
storeTotalScoreEx.TotalCount = len(filterStoreTotalScoreList)
|
||||
storeTotalScoreEx.StoreTotalScoreList = pagedStoreTotalScoreList
|
||||
|
||||
return storeTotalScoreEx, err
|
||||
}
|
||||
24
business/jxstore/misc/store_score_test.go
Normal file
24
business/jxstore/misc/store_score_test.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package misc
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
|
||||
"git.rosy.net.cn/jx-callback/globals/api2"
|
||||
"git.rosy.net.cn/jx-callback/globals/testinit"
|
||||
|
||||
_ "git.rosy.net.cn/jx-callback/business/partner/purchase/ebai"
|
||||
_ "git.rosy.net.cn/jx-callback/business/partner/purchase/elm"
|
||||
_ "git.rosy.net.cn/jx-callback/business/partner/purchase/jd"
|
||||
_ "git.rosy.net.cn/jx-callback/business/partner/purchase/mtwm"
|
||||
_ "git.rosy.net.cn/jx-callback/business/partner/purchase/weimob/wsc"
|
||||
)
|
||||
|
||||
func init() {
|
||||
testinit.Init()
|
||||
api2.Init()
|
||||
}
|
||||
|
||||
func TestScoreStore(t *testing.T) {
|
||||
ScoreStore(jxcontext.AdminCtx, []int{})
|
||||
}
|
||||
176
business/jxstore/misc/store_sku_sales.go
Normal file
176
business/jxstore/misc/store_sku_sales.go
Normal file
@@ -0,0 +1,176 @@
|
||||
package misc
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"sync"
|
||||
|
||||
"git.rosy.net.cn/jx-callback/business/jxstore/cms"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/tasksch"
|
||||
"git.rosy.net.cn/jx-callback/business/model"
|
||||
"git.rosy.net.cn/jx-callback/business/model/dao"
|
||||
)
|
||||
|
||||
const (
|
||||
DayNum = 30 //请求天数
|
||||
LimitNum = 100 //最大数据限制
|
||||
)
|
||||
|
||||
func GetStoreSkuSalesInfo(ctx *jxcontext.Context, storeID int) (outStoreSkuSales []*model.StoreSkuSales, err error) {
|
||||
db := dao.GetDB()
|
||||
//得到所有门店
|
||||
storeList, err := GetStoreList(ctx)
|
||||
if err == nil {
|
||||
storeList = GetFilterStoreListEx(storeList, nil)
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
storeMapData := make(map[int]*cms.StoreExt)
|
||||
for _, value := range storeList {
|
||||
storeMapData[value.ID] = value
|
||||
}
|
||||
curStoreInfo := storeMapData[storeID]
|
||||
if curStoreInfo == nil {
|
||||
return nil, errors.New(fmt.Sprintf("未找到商店:[%d]", storeID))
|
||||
}
|
||||
cityCode := curStoreInfo.CityCode
|
||||
|
||||
//获取本市商品总销量
|
||||
citySkuSalesCntMap := make(map[int]int)
|
||||
citySkuSalesCntList, err := dao.GetSkuSalesCntList(db, -1, cityCode, DayNum, LimitNum, nil)
|
||||
citySkuIDs := []int{}
|
||||
if err == nil {
|
||||
for _, value := range citySkuSalesCntList {
|
||||
citySkuSalesCntMap[value.SkuID] = value.Count
|
||||
citySkuIDs = append(citySkuIDs, value.SkuID)
|
||||
}
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
//获取本店商品总销量
|
||||
storeSkuSalesCntMap := make(map[int]int)
|
||||
storeSkuSalesCntList, err := dao.GetSkuSalesCntList(db, storeID, cityCode, DayNum, -1, citySkuIDs)
|
||||
if err == nil {
|
||||
for _, value := range storeSkuSalesCntList {
|
||||
storeSkuSalesCntMap[value.SkuID] = value.Count
|
||||
}
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
//获取本店商品差评数量
|
||||
storeSkuBadCommentCntMap := make(map[int]int)
|
||||
storeSkuBadCommentCntList, err := dao.GetSkuBadCommentCntList(db, storeID, DayNum)
|
||||
if err == nil {
|
||||
for _, value := range storeSkuBadCommentCntList {
|
||||
storeSkuBadCommentCntMap[value.SkuID] = value.Count
|
||||
}
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
//得到当前门店商品数据
|
||||
storeSkuMapData := make(map[int]*dao.StoreSkuNameExt)
|
||||
storeSkuData, err := cms.GetStoreSkus(ctx, storeID, citySkuIDs, true, "", true, false, map[string]interface{}{}, 0, -1)
|
||||
if err == nil {
|
||||
for _, value := range storeSkuData.SkuNames {
|
||||
for _, skuInfo := range value.Skus {
|
||||
storeSkuMapData[skuInfo.SkuID] = value
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
//得到5KM内的所有门店
|
||||
rangeStoreList := GetRangeStoreList(storeID, curStoreInfo.FloatLng, curStoreInfo.FloatLat, SaleSkuCheckRange, storeList)
|
||||
|
||||
//得到5KM内的所有门店的商品的价格
|
||||
allStoreSkus := make(map[int]map[int]int)
|
||||
var locker sync.RWMutex
|
||||
taskFunc := func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
|
||||
storeInfo := batchItemList[0].(*cms.StoreExt)
|
||||
storeID := storeInfo.ID
|
||||
jxSkuInfoData, err := cms.GetStoreSkus(ctx, storeID, citySkuIDs, true, "", true, false, map[string]interface{}{}, 0, -1)
|
||||
jxSkuPriceMapData := make(map[int]int)
|
||||
for _, value := range jxSkuInfoData.SkuNames {
|
||||
for _, skuInfo := range value.Skus {
|
||||
jxSkuPriceMapData[skuInfo.SkuID] = skuInfo.BindPrice
|
||||
}
|
||||
}
|
||||
locker.Lock()
|
||||
defer locker.Unlock()
|
||||
allStoreSkus[storeID] = jxSkuPriceMapData
|
||||
return retVal, err
|
||||
}
|
||||
taskParallel := tasksch.NewParallelTask("得到所有门店商品", nil, ctx, taskFunc, rangeStoreList)
|
||||
taskParallel.Run()
|
||||
_, err = taskParallel.GetResult(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
//计算商品的平均价格
|
||||
skusTotalPrice := make(map[int]int)
|
||||
skusCount := make(map[int]int)
|
||||
skusAveragePrice := make(map[int]int)
|
||||
for _, storeInfo := range rangeStoreList {
|
||||
storeID := storeInfo.ID
|
||||
storeSkuMapData := allStoreSkus[storeID]
|
||||
for skuID, skuPrice := range storeSkuMapData {
|
||||
skusTotalPrice[skuID] += skuPrice
|
||||
skusCount[skuID]++
|
||||
}
|
||||
}
|
||||
for id, totalPrice := range skusTotalPrice {
|
||||
skusAveragePrice[id] = int(math.Round(float64(totalPrice) / float64(skusCount[id])))
|
||||
}
|
||||
|
||||
//输出商品销量统计结果
|
||||
skuAndNameMapData := make(map[int]*model.SkuAndName)
|
||||
if len(storeSkuMapData) < len(citySkuIDs) {
|
||||
skuAndNameList, err := dao.GetSkus(db, citySkuIDs, nil, nil, nil, nil)
|
||||
if err == nil {
|
||||
for _, value := range skuAndNameList {
|
||||
skuAndNameMapData[value.ID] = value
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, value := range citySkuSalesCntList {
|
||||
skuID := value.SkuID
|
||||
storeSkuSales := &model.StoreSkuSales{}
|
||||
storeSkuSales.SkuID = skuID
|
||||
storeSkuInfo := storeSkuMapData[skuID]
|
||||
skuAndNameInfo := skuAndNameMapData[skuID]
|
||||
if storeSkuInfo != nil {
|
||||
skuName := storeSkuInfo.SkuName
|
||||
skuInfo := storeSkuInfo.Skus[0]
|
||||
storeSkuSales.SkuName = jxutils.ComposeSkuName(skuName.Prefix, skuName.Name, skuInfo.Comment, skuName.Unit, skuInfo.SkuSpecQuality, skuInfo.SkuSpecUnit, 0, skuName.ExPrefix, skuName.ExPrefixBegin, skuName.ExPrefixEnd)
|
||||
storeSkuSales.SkuImage = storeSkuInfo.Img
|
||||
storeSkuSales.SkuPrice = jxutils.IntPrice2StandardCurrencyString(int64(storeSkuInfo.Skus[0].BindPrice))
|
||||
} else if skuAndNameInfo != nil {
|
||||
skuNameList, err := dao.GetSkuNames(db, []int{skuAndNameInfo.NameID}, nil, "", false)
|
||||
prefix := ""
|
||||
if err == nil && len(skuNameList) > 0 {
|
||||
storeSkuSales.SkuImage = skuNameList[0].Img
|
||||
prefix = skuNameList[0].Prefix
|
||||
}
|
||||
storeSkuSales.SkuName = jxutils.ComposeSkuName(prefix, skuAndNameInfo.Name, skuAndNameInfo.Comment, skuAndNameInfo.Unit, skuAndNameInfo.SpecQuality, skuAndNameInfo.SpecUnit, 0, skuAndNameInfo.ExPrefix, skuAndNameInfo.ExPrefixBegin, skuAndNameInfo.ExPrefixEnd)
|
||||
storeSkuSales.SkuPrice = "N/A"
|
||||
} else {
|
||||
storeSkuSales.SkuName = "N/A"
|
||||
storeSkuSales.SkuPrice = "N/A"
|
||||
}
|
||||
storeSkuSales.SkuAvgPrice = jxutils.IntPrice2StandardCurrencyString(int64(skusAveragePrice[skuID]))
|
||||
storeSkuSales.BadCommentCnt = storeSkuBadCommentCntMap[skuID]
|
||||
storeSkuSales.StoreSkuSalesCnt = storeSkuSalesCntMap[skuID]
|
||||
storeSkuSales.CitySkuSalesCnt = citySkuSalesCntMap[skuID]
|
||||
outStoreSkuSales = append(outStoreSkuSales, storeSkuSales)
|
||||
}
|
||||
|
||||
return outStoreSkuSales, err
|
||||
}
|
||||
431
business/jxstore/permission/permission.go
Normal file
431
business/jxstore/permission/permission.go
Normal file
@@ -0,0 +1,431 @@
|
||||
package permission
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils"
|
||||
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
|
||||
"git.rosy.net.cn/jx-callback/business/model"
|
||||
"git.rosy.net.cn/jx-callback/business/model/dao"
|
||||
)
|
||||
|
||||
func GetMenu(ctx *jxcontext.Context, userID string) (menus []*model.Menu, err error) {
|
||||
if userID == "" {
|
||||
return dao.GetMenu(dao.GetDB(), "", 0, 0, userID)
|
||||
} else {
|
||||
if user, err := dao.GetUserByID(dao.GetDB(), "user_id", userID); err == nil {
|
||||
if user.Type&model.UserTypeRole != 0 {
|
||||
return dao.GetMenuWithUser(dao.GetDB(), "", 0, 0, userID)
|
||||
} else {
|
||||
return dao.GetMenu(dao.GetDB(), "", 0, 0, "")
|
||||
}
|
||||
}
|
||||
}
|
||||
return dao.GetMenu(dao.GetDB(), "", 0, 0, userID)
|
||||
}
|
||||
|
||||
func AddMenu(ctx *jxcontext.Context, menu *model.Menu) (err error) {
|
||||
var (
|
||||
db = dao.GetDB()
|
||||
)
|
||||
if menu == nil {
|
||||
return fmt.Errorf("添加失败!menu nil")
|
||||
}
|
||||
if menu.Name == "" || menu.Level == 0 {
|
||||
return fmt.Errorf("添加失败!menu 名称和等级必须有值!")
|
||||
}
|
||||
menus, err := dao.GetMenu(db, menu.Name, menu.Level, 0, "")
|
||||
if len(menus) > 0 {
|
||||
return fmt.Errorf("添加失败!已存在相同名称的 menu name : %v", menu.Name)
|
||||
}
|
||||
txDB, _ := dao.Begin(db)
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
dao.Rollback(db, txDB)
|
||||
panic(r)
|
||||
}
|
||||
}()
|
||||
dao.WrapAddIDCULDEntity(menu, ctx.GetUserName())
|
||||
err = dao.CreateEntity(db, menu)
|
||||
dao.Commit(db, txDB)
|
||||
return err
|
||||
}
|
||||
|
||||
func UpdateMenu(ctx *jxcontext.Context, menuID int, payload map[string]interface{}, isDelete bool) (num int64, err error) {
|
||||
var (
|
||||
db = dao.GetDB()
|
||||
)
|
||||
menu := &model.Menu{}
|
||||
menu.ID = menuID
|
||||
err = dao.GetEntity(db, menu)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
txDB, _ := dao.Begin(db)
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
dao.Rollback(db, txDB)
|
||||
panic(r)
|
||||
}
|
||||
}()
|
||||
if !isDelete {
|
||||
valid := dao.StrictMakeMapByStructObject(payload, menu, ctx.GetUserName())
|
||||
if len(valid) > 0 {
|
||||
if num, err = dao.UpdateEntityLogically(db, menu, valid, ctx.GetUserName(), nil); err != nil {
|
||||
dao.Rollback(db, txDB)
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
menu.DeletedAt = time.Now()
|
||||
num, err = dao.UpdateEntity(db, menu, "DeletedAt")
|
||||
}
|
||||
dao.Commit(db, txDB)
|
||||
return num, err
|
||||
}
|
||||
|
||||
func GetRole(ctx *jxcontext.Context, name string) (roles []*model.Role, err error) {
|
||||
var (
|
||||
db = dao.GetDB()
|
||||
)
|
||||
roles, err = dao.GetRole(db, name, "")
|
||||
for _, v := range roles {
|
||||
if v.CityCodes != "" {
|
||||
if cityInfos, err := dao.GetPlaces(db, jxutils.StrListToIntList(strings.Split(v.CityCodes, ","))); err == nil {
|
||||
v.CityInfo = cityInfos
|
||||
}
|
||||
}
|
||||
if v.StoreIDs != "" {
|
||||
if stores, err := dao.GetStoreList(db, jxutils.StrListToIntList(strings.Split(v.StoreIDs, ",")), nil, nil, nil, nil, ""); err == nil {
|
||||
v.Stores = stores
|
||||
}
|
||||
}
|
||||
}
|
||||
return roles, err
|
||||
}
|
||||
|
||||
func AddRole(ctx *jxcontext.Context, name string) (err error) {
|
||||
var (
|
||||
db = dao.GetDB()
|
||||
)
|
||||
roles, err := dao.GetRole(db, "", name)
|
||||
if len(roles) > 0 {
|
||||
return fmt.Errorf("添加失败!已存在相同名称的 role name : %v", name)
|
||||
}
|
||||
role := &model.Role{
|
||||
Name: name,
|
||||
}
|
||||
txDB, _ := dao.Begin(db)
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
dao.Rollback(db, txDB)
|
||||
panic(r)
|
||||
}
|
||||
}()
|
||||
dao.WrapAddIDCULDEntity(role, ctx.GetUserName())
|
||||
err = dao.CreateEntity(db, role)
|
||||
dao.Commit(db, txDB)
|
||||
return err
|
||||
}
|
||||
|
||||
func UpdateRole(ctx *jxcontext.Context, roleID int, name string, isDelete bool, brandID int, cityCodes, storeIDs []int) (num int64, err error) {
|
||||
var (
|
||||
db = dao.GetDB()
|
||||
cityCodesStr []string
|
||||
storeIDsStr []string
|
||||
)
|
||||
for _, v := range cityCodes {
|
||||
cityCodesStr = append(cityCodesStr, utils.Int2Str(v))
|
||||
}
|
||||
for _, v := range storeIDs {
|
||||
storeIDsStr = append(storeIDsStr, utils.Int2Str(v))
|
||||
}
|
||||
if roleID == 1 {
|
||||
return 0, fmt.Errorf("管理员不允许修改!")
|
||||
}
|
||||
role := &model.Role{}
|
||||
role.ID = roleID
|
||||
err = dao.GetEntity(db, role)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
txDB, _ := dao.Begin(db)
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
dao.Rollback(db, txDB)
|
||||
panic(r)
|
||||
}
|
||||
}()
|
||||
if !isDelete {
|
||||
role.Name = name
|
||||
role.UpdatedAt = time.Now()
|
||||
role.LastOperator = ctx.GetUserName()
|
||||
role.BrandID = brandID
|
||||
role.CityCodes = strings.Join(cityCodesStr, ",")
|
||||
role.StoreIDs = strings.Join(storeIDsStr, ",")
|
||||
num, err = dao.UpdateEntity(db, role, "Name", "UpdatedAt", "LastOperator", "BrandID", "CityCodes", "StoreIDs")
|
||||
} else {
|
||||
role.DeletedAt = time.Now()
|
||||
num, err = dao.UpdateEntity(db, role, "DeletedAt")
|
||||
}
|
||||
dao.Commit(db, txDB)
|
||||
return num, err
|
||||
}
|
||||
|
||||
func GetUserRole(ctx *jxcontext.Context, userID string) (userRoles []*model.UserRole, err error) {
|
||||
return dao.GetUserRole(dao.GetDB(), []string{userID}, nil)
|
||||
}
|
||||
|
||||
func UpdateUserRole(ctx *jxcontext.Context, userIDs []string, roleIDs []int) (err error) {
|
||||
var (
|
||||
db = dao.GetDB()
|
||||
roleIDMap = make(map[int]int)
|
||||
nowRoleIDMap = make(map[int]int)
|
||||
userRoleMap = make(map[string][]int)
|
||||
addUserRoleMap = make(map[string][]int)
|
||||
deleteUserRoleMap = make(map[string][]int)
|
||||
)
|
||||
for _, v := range roleIDs {
|
||||
roleIDMap[v] = 1
|
||||
}
|
||||
userRoles, err := dao.GetUserRole(db, userIDs, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(userRoles) > 0 {
|
||||
for _, v := range userRoles {
|
||||
userRoleMap[v.UserID] = append(userRoleMap[v.UserID], v.RoleID)
|
||||
}
|
||||
}
|
||||
for _, userID := range userIDs {
|
||||
nowRoleIDs := userRoleMap[userID]
|
||||
for _, nowRoleID := range nowRoleIDs {
|
||||
if roleIDMap[nowRoleID] == 0 {
|
||||
deleteUserRoleMap[userID] = append(deleteUserRoleMap[userID], nowRoleID)
|
||||
}
|
||||
nowRoleIDMap[nowRoleID] = 1
|
||||
}
|
||||
for roleID, _ := range roleIDMap {
|
||||
if nowRoleIDMap[roleID] == 0 {
|
||||
addUserRoleMap[userID] = append(addUserRoleMap[userID], roleID)
|
||||
}
|
||||
}
|
||||
}
|
||||
txDB, _ := dao.Begin(db)
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
dao.Rollback(db, txDB)
|
||||
panic(r)
|
||||
}
|
||||
}()
|
||||
if len(addUserRoleMap) > 0 {
|
||||
for userID, roleIDs := range addUserRoleMap {
|
||||
for _, roleID := range roleIDs {
|
||||
userRole := &model.UserRole{
|
||||
UserID: userID,
|
||||
RoleID: roleID,
|
||||
}
|
||||
dao.WrapAddIDCULDEntity(userRole, ctx.GetUserName())
|
||||
err = dao.CreateEntity(db, userRole)
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(deleteUserRoleMap) > 0 {
|
||||
for userID, roleIDs := range deleteUserRoleMap {
|
||||
for _, roleID := range roleIDs {
|
||||
userRoles, _ := dao.GetUserRole(db, []string{userID}, []int{roleID})
|
||||
if len(userRoles) > 0 {
|
||||
userRoles[0].DeletedAt = time.Now()
|
||||
userRoles[0].LastOperator = ctx.GetUserName()
|
||||
_, err = dao.UpdateEntity(db, userRoles[0], "DeletedAt", "LastOperator")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, v := range userIDs {
|
||||
if user, err := dao.GetUserByID(db, "user_id", v); err == nil {
|
||||
user.Type = user.Type | model.UserTypeRole
|
||||
dao.UpdateEntity(db, user, "Type")
|
||||
}
|
||||
}
|
||||
dao.Commit(db, txDB)
|
||||
return err
|
||||
}
|
||||
|
||||
func GetRoleMenu(ctx *jxcontext.Context, roleID int) (roleMenus []*model.RoleMenu, err error) {
|
||||
return dao.GetRoleMenu(dao.GetDB(), []int{roleID}, nil)
|
||||
}
|
||||
|
||||
func UpdateRoleMenu(ctx *jxcontext.Context, roleIDs, menuIDs []int) (err error) {
|
||||
var (
|
||||
db = dao.GetDB()
|
||||
menuIDMap = make(map[int]int)
|
||||
nowMenuIDMap = make(map[int]int)
|
||||
roleMenuMap = make(map[int][]int)
|
||||
addRoleMenuMap = make(map[int][]int)
|
||||
deleteRoleMenuMap = make(map[int][]int)
|
||||
)
|
||||
for _, v := range menuIDs {
|
||||
menuIDMap[v] = 1
|
||||
}
|
||||
roleMenus, err := dao.GetRoleMenu(db, roleIDs, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(roleMenus) > 0 {
|
||||
for _, v := range roleMenus {
|
||||
roleMenuMap[v.RoleID] = append(roleMenuMap[v.RoleID], v.MenuID)
|
||||
}
|
||||
}
|
||||
for _, roleID := range roleIDs {
|
||||
nowMenuIDs := roleMenuMap[roleID]
|
||||
for _, nowMenuID := range nowMenuIDs {
|
||||
if menuIDMap[nowMenuID] == 0 {
|
||||
deleteRoleMenuMap[roleID] = append(deleteRoleMenuMap[roleID], nowMenuID)
|
||||
}
|
||||
nowMenuIDMap[nowMenuID] = 1
|
||||
}
|
||||
for menuID, _ := range menuIDMap {
|
||||
if nowMenuIDMap[menuID] == 0 {
|
||||
addRoleMenuMap[roleID] = append(addRoleMenuMap[roleID], menuID)
|
||||
}
|
||||
}
|
||||
}
|
||||
txDB, _ := dao.Begin(db)
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
dao.Rollback(db, txDB)
|
||||
panic(r)
|
||||
}
|
||||
}()
|
||||
if len(addRoleMenuMap) > 0 {
|
||||
for roleID, menuIDs := range addRoleMenuMap {
|
||||
for _, menuID := range menuIDs {
|
||||
roleMenu := &model.RoleMenu{
|
||||
RoleID: roleID,
|
||||
MenuID: menuID,
|
||||
}
|
||||
dao.WrapAddIDCULDEntity(roleMenu, ctx.GetUserName())
|
||||
err = dao.CreateEntity(db, roleMenu)
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(deleteRoleMenuMap) > 0 {
|
||||
for roleID, menuIDs := range deleteRoleMenuMap {
|
||||
for _, menuID := range menuIDs {
|
||||
roleMenus, _ := dao.GetRoleMenu(db, []int{roleID}, []int{menuID})
|
||||
if len(roleMenus) > 0 {
|
||||
roleMenus[0].DeletedAt = time.Now()
|
||||
roleMenus[0].LastOperator = ctx.GetUserName()
|
||||
_, err = dao.UpdateEntity(db, roleMenus[0], "DeletedAt", "LastOperator")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
dao.Commit(db, txDB)
|
||||
return err
|
||||
}
|
||||
|
||||
func GetUserStoresResultMap(userID string) (resultMap map[int]int, err error) {
|
||||
var (
|
||||
db = dao.GetDB()
|
||||
// brandIDMap = make(map[int]int)
|
||||
// cityCodeMap = make(map[int]int)
|
||||
// storeIDMap = make(map[int]int)
|
||||
)
|
||||
resultMap = make(map[int]int)
|
||||
user, _ := dao.GetUserByID(db, "user_id", userID)
|
||||
if user != nil {
|
||||
if user.BindStoreID != "" {
|
||||
for _, v := range strings.Split(user.BindStoreID, ",") {
|
||||
resultMap[utils.Str2Int(v)] = utils.Str2Int(v)
|
||||
}
|
||||
return resultMap, err
|
||||
}
|
||||
}
|
||||
userRoles, err2 := dao.GetUserRole2(db, []string{userID}, nil)
|
||||
err = err2
|
||||
for _, v := range userRoles {
|
||||
var (
|
||||
brandIDs, cityCodes, storeIDs []int
|
||||
)
|
||||
// if _, ok := brandIDMap[v.BrandID]; !ok {
|
||||
// brandIDMap[v.BrandID] = 1
|
||||
// }
|
||||
// if v.CityCodes != "" {
|
||||
// for _, cityCode := range jxutils.StrListToIntList(strings.Split(v.CityCodes, ",")) {
|
||||
// if _, ok := cityCodeMap[cityCode]; !ok {
|
||||
// cityCodeMap[cityCode] = 1
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// if v.StoreIDs != "" {
|
||||
// for _, storeID := range jxutils.StrListToIntList(strings.Split(v.StoreIDs, ",")) {
|
||||
// if _, ok := storeIDMap[storeID]; !ok {
|
||||
// storeIDMap[storeID] = 1
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
if v.CityCodes == "" && v.StoreIDs == "" {
|
||||
continue
|
||||
}
|
||||
if v.BrandID != 0 {
|
||||
brandIDs = append(brandIDs, v.BrandID)
|
||||
}
|
||||
if v.CityCodes != "0" && v.CityCodes != "" {
|
||||
cityCodes = append(cityCodes, jxutils.StrListToIntList(strings.Split(v.CityCodes, ","))...)
|
||||
}
|
||||
if v.StoreIDs != "" {
|
||||
storeIDs = append(storeIDs, jxutils.StrListToIntList(strings.Split(v.StoreIDs, ","))...)
|
||||
}
|
||||
if stores, err := dao.GetStoreList(db, storeIDs, cityCodes, nil, brandIDs, nil, ""); len(stores) > 0 && err == nil {
|
||||
for _, v := range stores {
|
||||
resultMap[v.ID] = v.ID
|
||||
}
|
||||
}
|
||||
}
|
||||
// for k, _ := range brandIDMap {
|
||||
// brandIDs = append(brandIDs, k)
|
||||
// }
|
||||
// for k, _ := range cityCodeMap {
|
||||
// cityCodes = append(cityCodes, k)
|
||||
// }
|
||||
// for k, _ := range storeIDMap {
|
||||
// storeIDs = append(storeIDs, k)
|
||||
// }
|
||||
// if stores1, err := dao.GetStoreList(db, nil, nil, brandIDs, nil, nil, ""); len(stores1) > 0 && err == nil {
|
||||
// stores = append(stores, stores1...)
|
||||
// }
|
||||
// if stores2, err := dao.GetStoreList(db, nil, cityCodes, nil, nil, nil, ""); len(stores2) > 0 && err == nil {
|
||||
// stores = append(stores, stores2...)
|
||||
// }
|
||||
// if stores3, err := dao.GetStoreList(db, storeIDs, nil, nil, nil, nil, ""); len(stores3) > 0 && err == nil {
|
||||
// stores = append(stores, stores3...)
|
||||
// }
|
||||
return resultMap, err
|
||||
}
|
||||
|
||||
func IsRoled(ctx *jxcontext.Context) bool {
|
||||
if ctx.GetUserName() != "jxadmin" {
|
||||
if user, err := dao.GetUserByID(dao.GetDB(), "user_id", ctx.GetUserID()); err == nil {
|
||||
if user.Type&model.UserTypeRole != 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func IsRoledByUserID(userID string) bool {
|
||||
if user, err := dao.GetUserByID(dao.GetDB(), "user_id", userID); err == nil {
|
||||
if user.Type&model.UserTypeRole != 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
772
business/jxstore/report/report.go
Normal file
772
business/jxstore/report/report.go
Normal file
@@ -0,0 +1,772 @@
|
||||
package report
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"git.rosy.net.cn/jx-callback/globals"
|
||||
"git.rosy.net.cn/jx-callback/globals/api"
|
||||
"math"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/ddmsg"
|
||||
|
||||
"git.rosy.net.cn/baseapi/platformapi/dingdingapi"
|
||||
"git.rosy.net.cn/baseapi/platformapi/mtwmapi"
|
||||
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils"
|
||||
|
||||
"git.rosy.net.cn/jx-callback/business/partner"
|
||||
|
||||
"git.rosy.net.cn/jx-callback/business/jxstore/permission"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/tasksch"
|
||||
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
|
||||
"git.rosy.net.cn/jx-callback/business/model"
|
||||
"git.rosy.net.cn/jx-callback/business/model/dao"
|
||||
)
|
||||
|
||||
type tStoreSkuBindAndSkuName struct {
|
||||
CityCode int
|
||||
StoreID int `orm:"column(store_id)"`
|
||||
NameID int `orm:"column(name_id)"`
|
||||
UnitPrice int
|
||||
UnitPriceList []int
|
||||
}
|
||||
|
||||
func GetStatisticsReportForOrders(ctx *jxcontext.Context, storeIDs, vendorIDs []int, fromDate, toDate, marketPhone, jdPhone, mtPhone, ebaiPhone string) (statisticsReportForOrdersList []*dao.StatisticsReportForOrdersList, err error) {
|
||||
db := dao.GetDB()
|
||||
fromDateParm := utils.Str2Time(fromDate)
|
||||
toDateParm := utils.Str2Time(toDate)
|
||||
//若时间间隔大于3个月则不允许查询
|
||||
if math.Ceil(toDateParm.Sub(fromDateParm).Hours()/24) > 92 {
|
||||
return nil, errors.New(fmt.Sprintf("查询间隔时间不允许大于3个月!: 时间范围:[%v] 至 [%v]", fromDate, toDate))
|
||||
}
|
||||
statisticsReportForOrdersList, err = dao.GetStatisticsReportForOrders(db, storeIDs, vendorIDs, fromDateParm, toDateParm, marketPhone, jdPhone, mtPhone, ebaiPhone)
|
||||
return statisticsReportForOrdersList, err
|
||||
}
|
||||
|
||||
func GetStatisticsReportForAfsOrders(ctx *jxcontext.Context, storeIDs []int, fromDate string, toDate string) (statisticsReportForOrdersList []*dao.StatisticsReportForOrdersList, err error) {
|
||||
db := dao.GetDB()
|
||||
fromDateParm := utils.Str2Time(fromDate)
|
||||
toDateParm := utils.Str2Time(toDate)
|
||||
//若时间间隔大于3个月则不允许查询
|
||||
if math.Ceil(toDateParm.Sub(fromDateParm).Hours()/24) > 92 {
|
||||
return nil, errors.New(fmt.Sprintf("查询间隔时间不允许大于3个月!: 时间范围:[%v] 至 [%v]", fromDate, toDate))
|
||||
}
|
||||
statisticsReportForOrdersList, err = dao.GetGetStatisticsReportForAfsOrders(db, storeIDs, fromDateParm, toDateParm)
|
||||
return statisticsReportForOrdersList, err
|
||||
}
|
||||
|
||||
func StatisticsReportForStoreSkusPrice(ctx *jxcontext.Context, cityCodes, skuIDs []int, snapDate string, offset, pageSize int) (pagedInfo *model.PagedInfo, err error) {
|
||||
var snapDateParam time.Time
|
||||
db := dao.GetDB()
|
||||
if snapDate != "" {
|
||||
snapDateParam = utils.Str2Time(snapDate)
|
||||
}
|
||||
priceReferSnapshot, totalCount, err := dao.GetPriceReferSnapshot(db, cityCodes, skuIDs, 0, snapDateParam, offset, pageSize)
|
||||
pagedInfo = &model.PagedInfo{
|
||||
Data: priceReferSnapshot,
|
||||
TotalCount: totalCount,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func BeginSavePriceRefer(ctx *jxcontext.Context, cityCodes, skuIDs []int, isAsync, isContinueWhenError bool) (hint string, err error) {
|
||||
var priceReferSnapshotList []*model.PriceReferSnapshot
|
||||
db := dao.GetDB()
|
||||
snapshotAt := utils.Time2Date(time.Now().AddDate(0, 0, -1))
|
||||
dao.DeletePriceReferHistory(db, utils.Time2Date(snapshotAt.AddDate(0, 0, -7)))
|
||||
priceReferSnapshotDelete := &model.PriceReferSnapshot{SnapshotAt: snapshotAt}
|
||||
dao.DeleteEntity(db, priceReferSnapshotDelete, "SnapshotAt")
|
||||
taskSeqFunc := func(task *tasksch.SeqTask, step int, params ...interface{}) (result interface{}, err error) {
|
||||
switch step {
|
||||
case 0:
|
||||
priceReferSnapshot, err := dao.GetStatisticsReportForStoreSkusPrice(db, cityCodes, skuIDs)
|
||||
if len(priceReferSnapshot) > 0 {
|
||||
txDB, _ := dao.Begin(db)
|
||||
defer func() {
|
||||
if r := recover(); r != nil || err != nil {
|
||||
dao.Rollback(db, txDB)
|
||||
if r != nil {
|
||||
panic(r)
|
||||
}
|
||||
}
|
||||
}()
|
||||
for _, v := range priceReferSnapshot {
|
||||
dao.WrapAddIDCULDEntity(v, ctx.GetUserName())
|
||||
v.SnapshotAt = snapshotAt
|
||||
}
|
||||
dao.CreateMultiEntities(db, priceReferSnapshot)
|
||||
dao.Commit(db, txDB)
|
||||
}
|
||||
case 1:
|
||||
priceReferSnapshotList, err = dao.GetPriceReferSnapshotNoPage(db, nil, nil, nil, snapshotAt)
|
||||
var (
|
||||
citySkuMap = make(map[int]map[int][]int)
|
||||
countryMap = make(map[int][]int)
|
||||
resultMap = make(map[int]map[int]*model.PriceReferSnapshot)
|
||||
resultCountryMap = make(map[int]*model.PriceReferSnapshot)
|
||||
)
|
||||
storeList, err := dao.GetStoreList(db, nil, nil, nil, nil, nil, "")
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
for _, v := range storeList {
|
||||
if v.PayPercentage < 50 {
|
||||
continue
|
||||
}
|
||||
var tList []*tStoreSkuBindAndSkuName
|
||||
sql := `
|
||||
SELECT DISTINCT b.city_code, a.store_id, Round(a.unit_price * IF(b.pay_percentage < 50 , 70, b.pay_percentage) / 100) AS unit_price, c.name_id
|
||||
FROM store_sku_bind a
|
||||
JOIN store b ON b.id = a.store_id AND b.deleted_at = ? AND b.status != ?
|
||||
JOIN sku c ON c.id = a.sku_id
|
||||
WHERE a.store_id = ?
|
||||
AND c.name_id NOT IN(
|
||||
SELECT b.name_id
|
||||
FROM store_sku_bind a
|
||||
JOIN sku b ON a.sku_id = b.id AND b.deleted_at = ?
|
||||
WHERE a.deleted_at = ?
|
||||
AND a.store_id = ?
|
||||
AND b.name_id NOT IN(SELECT DISTINCT b.name_id
|
||||
FROM store_sku_bind a
|
||||
JOIN sku b ON a.sku_id = b.id AND b.deleted_at = ?
|
||||
WHERE a.deleted_at = ?
|
||||
AND a.store_id = ?
|
||||
AND a.status = ?)
|
||||
)
|
||||
AND a.deleted_at = ?
|
||||
`
|
||||
sqlParams := []interface{}{
|
||||
utils.DefaultTimeValue,
|
||||
model.StoreStatusDisabled,
|
||||
v.ID,
|
||||
utils.DefaultTimeValue,
|
||||
utils.DefaultTimeValue,
|
||||
v.ID,
|
||||
utils.DefaultTimeValue,
|
||||
utils.DefaultTimeValue,
|
||||
v.ID,
|
||||
model.StoreSkuBindStatusNormal,
|
||||
utils.DefaultTimeValue,
|
||||
}
|
||||
dao.GetRows(db, &tList, sql, sqlParams...)
|
||||
skuNameMap := make(map[int][]int)
|
||||
if len(tList) > 0 {
|
||||
for _, vv := range tList {
|
||||
skuNameMap[vv.NameID] = append(skuNameMap[vv.NameID], vv.UnitPrice)
|
||||
countryMap[vv.NameID] = append(countryMap[vv.NameID], vv.UnitPrice)
|
||||
}
|
||||
if citySkuMap[v.CityCode] != nil {
|
||||
for nameID, unitPriceList := range skuNameMap {
|
||||
if citySkuMap[v.CityCode][nameID] != nil {
|
||||
citySkuMap[v.CityCode][nameID] = append(citySkuMap[v.CityCode][nameID], unitPriceList...)
|
||||
} else {
|
||||
citySkuMap[v.CityCode][nameID] = unitPriceList
|
||||
}
|
||||
}
|
||||
} else {
|
||||
citySkuMap[v.CityCode] = skuNameMap
|
||||
}
|
||||
}
|
||||
}
|
||||
for k, v := range countryMap {
|
||||
var midUnitPrice int
|
||||
var avgUnitPrice int
|
||||
sort.Ints(v)
|
||||
if len(v)%2 == 0 {
|
||||
midUnitPrice = v[len(v)/2-1]
|
||||
} else {
|
||||
midUnitPrice = v[len(v)/2]
|
||||
}
|
||||
for _, vv := range v {
|
||||
avgUnitPrice += vv
|
||||
}
|
||||
priceRefer := &model.PriceReferSnapshot{
|
||||
MidUnitPrice: midUnitPrice,
|
||||
MaxUnitPrice: v[len(v)-1],
|
||||
MinUnitPrice: v[0],
|
||||
AvgUnitPrice: avgUnitPrice / len(v),
|
||||
}
|
||||
resultCountryMap[k] = priceRefer
|
||||
}
|
||||
for k1, v := range citySkuMap {
|
||||
skuNameMap := make(map[int]*model.PriceReferSnapshot)
|
||||
for k2, _ := range v {
|
||||
var midUnitPrice int
|
||||
var avgUnitPrice int
|
||||
sort.Ints(v[k2])
|
||||
if len(v[k2])%2 == 0 {
|
||||
midUnitPrice = v[k2][len(v[k2])/2-1]
|
||||
} else {
|
||||
midUnitPrice = v[k2][len(v[k2])/2]
|
||||
}
|
||||
for _, vv := range v[k2] {
|
||||
avgUnitPrice += vv
|
||||
}
|
||||
skuNameMap[k2] = &model.PriceReferSnapshot{
|
||||
MidUnitPrice: midUnitPrice,
|
||||
MaxUnitPrice: v[k2][len(v[k2])-1],
|
||||
MinUnitPrice: v[k2][0],
|
||||
AvgUnitPrice: avgUnitPrice / len(v[k2]),
|
||||
}
|
||||
}
|
||||
resultMap[k1] = skuNameMap
|
||||
}
|
||||
if len(priceReferSnapshotList) > 0 {
|
||||
for _, v := range priceReferSnapshotList {
|
||||
if v.CityCode == 0 {
|
||||
if resultCountryMap[v.NameID] != nil {
|
||||
v.MidUnitPrice = resultCountryMap[v.NameID].MidUnitPrice
|
||||
v.MaxUnitPrice = resultCountryMap[v.NameID].MaxUnitPrice
|
||||
v.AvgUnitPrice = resultCountryMap[v.NameID].AvgUnitPrice
|
||||
v.MinUnitPrice = resultCountryMap[v.NameID].MinUnitPrice
|
||||
dao.UpdateEntity(db, v, "MidUnitPrice", "MaxUnitPrice", "MinUnitPrice", "AvgUnitPrice")
|
||||
}
|
||||
continue
|
||||
}
|
||||
if resultMap[v.CityCode][v.NameID] != nil {
|
||||
v.MidUnitPrice = resultMap[v.CityCode][v.NameID].MidUnitPrice
|
||||
v.MaxUnitPrice = resultMap[v.CityCode][v.NameID].MaxUnitPrice
|
||||
v.AvgUnitPrice = resultMap[v.CityCode][v.NameID].AvgUnitPrice
|
||||
v.MinUnitPrice = resultMap[v.CityCode][v.NameID].MinUnitPrice
|
||||
dao.UpdateEntity(db, v, "MidUnitPrice", "MaxUnitPrice", "MinUnitPrice", "AvgUnitPrice")
|
||||
}
|
||||
}
|
||||
}
|
||||
case 2:
|
||||
if len(priceReferSnapshotList) > 0 {
|
||||
for _, v := range priceReferSnapshotList {
|
||||
result, _ := dao.GetPriceReferPrice(db, v.CityCode, v.SkuID, snapshotAt)
|
||||
if result != nil {
|
||||
v.MaxPrice = result.MaxPrice
|
||||
v.MinPrice = result.MinPrice
|
||||
v.AvgPrice = result.AvgPrice
|
||||
v.MidPrice = result.MidPrice
|
||||
dao.UpdateEntity(db, v, "MidPrice", "MaxPrice", "MinPrice", "AvgPrice")
|
||||
}
|
||||
}
|
||||
}
|
||||
//TODO 京东查询接口报错,暂时屏蔽了
|
||||
// case 3:
|
||||
// priceReferSnapshotList, err = dao.GetPriceReferSnapshotNoPage(db, []int{0}, nil, nil, snapshotAt)
|
||||
// taskFunc := func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
|
||||
// v := batchItemList[0].(*model.PriceReferSnapshot)
|
||||
// for _, appOrg := range apimanager.CurAPIManager.GetAppOrgCodeList(model.VendorIDJD) {
|
||||
// directPrice, _ := jd.GetAPI(appOrg).GetJdSkuDirectPrice(v.SkuID)
|
||||
// v.JdDirectPrice = int(directPrice)
|
||||
// dao.UpdateEntity(db, v, "JdDirectPrice")
|
||||
// }
|
||||
// return retVal, err
|
||||
// }
|
||||
// taskParallel := tasksch.NewParallelTask("获取并更新京东指导价格", tasksch.NewParallelConfig(), ctx, taskFunc, priceReferSnapshotList)
|
||||
// tasksch.HandleTask(taskParallel, task, true).Run()
|
||||
// _, err = taskParallel.GetResult(0)
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
taskSeq := tasksch.NewSeqTask2("生成每日价格统计", ctx, isContinueWhenError, taskSeqFunc, 3)
|
||||
tasksch.HandleTask(taskSeq, nil, true).Run()
|
||||
if !isAsync {
|
||||
_, err = taskSeq.GetResult(0)
|
||||
hint = "1"
|
||||
} else {
|
||||
hint = taskSeq.GetID()
|
||||
}
|
||||
return hint, err
|
||||
}
|
||||
|
||||
type GetManageStateResult struct {
|
||||
OpenStoreCount int `json:"openStoreCount"` //营业店铺数
|
||||
HaveRestStoreCount int `json:"haveRestStoreCount"` //临时休息店铺数
|
||||
RestStoreCount int `json:"restStoreCount"` //休息店铺数
|
||||
FinishOrderState *GetManageStateOrderInfo //完成订单情况
|
||||
IngOrderState *GetManageStateOrderInfo //进行中订单情况
|
||||
CancelOrderState *GetManageStateOrderInfo //取消订单情况
|
||||
}
|
||||
|
||||
type GetManageStateOrderInfo struct {
|
||||
ActualPayPrice int `json:"actualPayPrice"` //支付总额
|
||||
TotalShopMoney int `json:"totalShopMoney"` //平台结算
|
||||
DesiredFee int `json:"desiredFee"` //配送费
|
||||
Yhld string `json:"yhld"` //优惠力度(支付总额-平台结算-配送支出)/支付总额*100%
|
||||
}
|
||||
|
||||
func getStoreStatusCount(db *dao.DaoDB, cityCodes []int, vendorID, status int) (count int, err error) {
|
||||
countType := &struct{ Count int }{}
|
||||
sqlParams := []interface{}{}
|
||||
sql := `
|
||||
SELECT COUNT(DISTINCT a.id) count
|
||||
FROM store a
|
||||
LEFT JOIN store_map b ON a.id = b.store_id AND b.deleted_at = ?
|
||||
WHERE a.deleted_at = ?
|
||||
`
|
||||
sqlParams = append(sqlParams, utils.DefaultTimeValue, utils.DefaultTimeValue)
|
||||
if len(cityCodes) > 0 {
|
||||
sql += " AND a.city_code IN (" + dao.GenQuestionMarks(len(cityCodes)) + ")"
|
||||
sqlParams = append(sqlParams, cityCodes)
|
||||
}
|
||||
if vendorID != -1 {
|
||||
sql += " AND b.vendor_id = ? AND b.status = ?"
|
||||
sqlParams = append(sqlParams, vendorID, status)
|
||||
} else {
|
||||
sql += " AND a.status = ?"
|
||||
sqlParams = append(sqlParams, status)
|
||||
}
|
||||
err = dao.GetRow(db, &countType, sql, sqlParams)
|
||||
return countType.Count, err
|
||||
}
|
||||
|
||||
func getOrderStateCount(db *dao.DaoDB, cityCodes []int, vendorID, status int) (getManageStateOrderInfo *GetManageStateOrderInfo, err error) {
|
||||
sqlParams := []interface{}{}
|
||||
getManageStateOrderInfo = &GetManageStateOrderInfo{}
|
||||
endTime := time.Now().AddDate(0, -3, 0)
|
||||
sql := `
|
||||
SELECT SUM(b.actual_pay_price) actual_pay_price, SUM(b.total_shop_money) total_shop_money, SUM(IFNULL(c.desired_fee, 0)) desired_fee
|
||||
FROM store a
|
||||
LEFT JOIN goods_order b ON a.id = IF(b.jx_store_id = 0, b.store_id, b.jx_store_id)
|
||||
LEFT JOIN waybill c ON IF(b.waybill_vendor_id = -1,b.vendor_order_id,b.vendor_waybill_id) = c.vendor_waybill_id
|
||||
WHERE a.deleted_at = ? AND a.status <> ?
|
||||
AND b.order_created_at < ? AND b.order_created_at > ?
|
||||
`
|
||||
sqlParams = append(sqlParams, utils.DefaultTimeValue, model.StoreStatusDisabled, time.Now(), endTime)
|
||||
if len(cityCodes) > 0 {
|
||||
sql += " AND a.city_code IN (" + dao.GenQuestionMarks(len(cityCodes)) + ")"
|
||||
sqlParams = append(sqlParams, cityCodes)
|
||||
}
|
||||
if vendorID != -1 {
|
||||
sql += " AND b.vendor_id = ?"
|
||||
sqlParams = append(sqlParams, vendorID)
|
||||
}
|
||||
if status != model.OrderStatusDelivering {
|
||||
sql += " AND b.status = ?"
|
||||
sqlParams = append(sqlParams, status)
|
||||
} else {
|
||||
sql += " AND b.status < ? AND b.status >= ?"
|
||||
sqlParams = append(sqlParams, model.OrderStatusEndBegin, model.OrderStatusWait4Pay)
|
||||
}
|
||||
err = dao.GetRow(db, &getManageStateOrderInfo, sql, sqlParams)
|
||||
getManageStateOrderInfo.Yhld = utils.Float64ToStr(math.Round((float64(getManageStateOrderInfo.ActualPayPrice-getManageStateOrderInfo.TotalShopMoney-getManageStateOrderInfo.DesiredFee) / float64(getManageStateOrderInfo.ActualPayPrice) * 100))) + "%"
|
||||
return getManageStateOrderInfo, err
|
||||
}
|
||||
|
||||
func GetManageState(ctx *jxcontext.Context, cityCodes []int, vendorID int) (getManageStateResult *GetManageStateResult, err error) {
|
||||
var (
|
||||
db = dao.GetDB()
|
||||
)
|
||||
getManageStateResult = &GetManageStateResult{}
|
||||
if openCount, err := getStoreStatusCount(db, cityCodes, vendorID, model.StoreStatusOpened); err == nil {
|
||||
getManageStateResult.OpenStoreCount = openCount
|
||||
}
|
||||
if haveRestCount, err := getStoreStatusCount(db, cityCodes, vendorID, model.StoreStatusHaveRest); err == nil {
|
||||
getManageStateResult.HaveRestStoreCount = haveRestCount
|
||||
}
|
||||
if restCount, err := getStoreStatusCount(db, cityCodes, vendorID, model.StoreStatusClosed); err == nil {
|
||||
getManageStateResult.RestStoreCount = restCount
|
||||
}
|
||||
if finishCount, err := getOrderStateCount(db, cityCodes, vendorID, model.OrderStatusFinished); err == nil {
|
||||
getManageStateResult.FinishOrderState = finishCount
|
||||
}
|
||||
if ingCount, err := getOrderStateCount(db, cityCodes, vendorID, model.OrderStatusDelivering); err == nil {
|
||||
getManageStateResult.IngOrderState = ingCount
|
||||
}
|
||||
if cancelCount, err := getOrderStateCount(db, cityCodes, vendorID, model.OrderStatusCanceled); err == nil {
|
||||
getManageStateResult.CancelOrderState = cancelCount
|
||||
}
|
||||
return getManageStateResult, err
|
||||
}
|
||||
|
||||
func RefreshStoreManageState(ctx *jxcontext.Context, storeIDs []int, vendorIDs []int) {
|
||||
var (
|
||||
db = dao.GetDB()
|
||||
//vendorIDs = []int{model.VendorIDJD, model.VendorIDMTWM, model.VendorIDEBAI}
|
||||
messageFlag = time.Now().Hour() == 10 && time.Now().Minute() > 0 && time.Now().Minute() < 12
|
||||
)
|
||||
task := tasksch.NewParallelTask("RefreshStoreManageState", tasksch.NewParallelConfig().SetParallelCount(3).SetIsContinueWhenError(true), ctx,
|
||||
func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
|
||||
vendorID := batchItemList[0].(int)
|
||||
storeMaps, err := dao.GetStoresMapList(db, []int{vendorID}, storeIDs, []int{model.StoreStatusOpened, model.StoreStatusClosed, model.StoreStatusHaveRest}, model.StoreStatusAll, model.StoreIsSyncAll, "", "", "")
|
||||
storeManageStates, err := dao.GetStoreManageStateSimple(db, storeIDs, nil, vendorID)
|
||||
var (
|
||||
storeMapsMap = make(map[int]*model.StoreMap)
|
||||
storeManagesMap = make(map[int]*model.StoreManageState)
|
||||
deleteList []int
|
||||
createList, updateList []*model.StoreMap
|
||||
)
|
||||
for _, v := range storeMaps {
|
||||
storeMapsMap[v.StoreID] = v
|
||||
}
|
||||
for _, v := range storeManageStates {
|
||||
storeManagesMap[v.StoreID] = v
|
||||
if storeMapsMap[v.StoreID] != nil {
|
||||
updateList = append(updateList, storeMapsMap[v.StoreID])
|
||||
} else {
|
||||
deleteList = append(deleteList, v.StoreID)
|
||||
}
|
||||
}
|
||||
for _, v := range storeMapsMap {
|
||||
if storeManagesMap[v.StoreID] == nil {
|
||||
createList = append(createList, v)
|
||||
}
|
||||
}
|
||||
task2 := tasksch.NewParallelTask("deleteList", tasksch.NewParallelConfig().SetIsContinueWhenError(true), ctx,
|
||||
func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
|
||||
storeID := batchItemList[0].(int)
|
||||
storeManage := &model.StoreManageState{
|
||||
StoreID: storeID,
|
||||
VendorID: vendorID,
|
||||
}
|
||||
dao.DeleteEntity(db, storeManage, "StoreID", "VendorID")
|
||||
return retVal, err
|
||||
}, deleteList)
|
||||
tasksch.HandleTask(task2, task, true).Run()
|
||||
task2.GetResult(0)
|
||||
task3 := tasksch.NewParallelTask("createList", tasksch.NewParallelConfig().SetParallelCount(20).SetIsContinueWhenError(true), ctx,
|
||||
func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
|
||||
storeMap := batchItemList[0].(*model.StoreMap)
|
||||
if storeMap.VendorOrgCode == "" || storeMap.VendorStoreID == "" {
|
||||
return retVal, err
|
||||
}
|
||||
storeDetail, _ := dao.GetStoreDetail(db, storeMap.StoreID, vendorID, storeMap.VendorOrgCode)
|
||||
storeManage := buildStoreManageState(ctx, db, storeMap, storeDetail, messageFlag)
|
||||
if storeManage == nil {
|
||||
return retVal, err
|
||||
}
|
||||
dao.CreateEntity(db, storeManage)
|
||||
return retVal, err
|
||||
}, createList)
|
||||
tasksch.HandleTask(task3, task, true).Run()
|
||||
task3.GetResult(0)
|
||||
task4 := tasksch.NewParallelTask("updateList", tasksch.NewParallelConfig().SetParallelCount(1).SetIsContinueWhenError(true), ctx,
|
||||
func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
|
||||
storeMap := batchItemList[0].(*model.StoreMap)
|
||||
if storeMap.VendorOrgCode == "" || storeMap.VendorStoreID == "" {
|
||||
return retVal, err
|
||||
}
|
||||
storeDetail, _ := dao.GetStoreDetail(db, storeMap.StoreID, vendorID, storeMap.VendorOrgCode)
|
||||
storeManage := buildStoreManageState(ctx, db, storeMap, storeDetail, messageFlag)
|
||||
if storeManage == nil {
|
||||
return retVal, err
|
||||
}
|
||||
if storeManageStates, err := dao.GetStoreManageStateSimple(db, []int{storeDetail.ID}, nil, vendorID); err == nil && len(storeManageStates) > 0 {
|
||||
storeManage.ID = storeManageStates[0].ID
|
||||
dao.UpdateEntity(db, storeManage)
|
||||
}
|
||||
return retVal, err
|
||||
}, updateList)
|
||||
tasksch.HandleTask(task4, task, true).Run()
|
||||
task4.GetResult(0)
|
||||
return retVal, err
|
||||
}, vendorIDs)
|
||||
tasksch.HandleTask(task, nil, true).Run()
|
||||
task.GetID()
|
||||
}
|
||||
|
||||
func buildStoreManageState(ctx *jxcontext.Context, db *dao.DaoDB, storeMap *model.StoreMap, storeDetail *dao.StoreDetail, messageFlag bool) *model.StoreManageState {
|
||||
var (
|
||||
dayTimeBegin, dayTimeEnd = utils.Str2Time(utils.Time2Str(utils.Time2Date(time.Now())) + "00:00:00"), utils.Str2Time(utils.Time2Str(utils.Time2Date(time.Now())) + "23:59:59")
|
||||
)
|
||||
storeManage := &model.StoreManageState{
|
||||
StoreID: storeMap.StoreID,
|
||||
VendorID: storeMap.VendorID,
|
||||
}
|
||||
dao.WrapAddIDCULEntity(storeManage, ctx.GetUserName())
|
||||
handler := partner.GetPurchasePlatformFromVendorID(storeMap.VendorID)
|
||||
store, err := handler.ReadStore(ctx, storeDetail.VendorOrgCode, storeDetail.VendorStoreID)
|
||||
if err != nil || store == nil {
|
||||
return nil
|
||||
}
|
||||
// if coverAreaFlag {
|
||||
if storeMap.VendorID == model.VendorIDJD && store.DeliveryRangeType != model.DeliveryRangeTypePolygon {
|
||||
storeManage.CoverArea = utils.Str2Float64(fmt.Sprintf("%.2f", math.Pi*utils.Str2Float64WithDefault(store.DeliveryRange, 0)/float64(1000)*utils.Str2Float64WithDefault(store.DeliveryRange, 0)/float64(1000)))
|
||||
} else {
|
||||
storeManage.CoverArea = utils.Str2Float64(fmt.Sprintf("%.2f", CalculateCoverArea(strings.Split(store.DeliveryRange, ";"), storeMap.VendorID)))
|
||||
}
|
||||
// }
|
||||
//营业状态
|
||||
storeManage.VendorStatus = store.Status
|
||||
//不一致发消息
|
||||
if messageFlag {
|
||||
vendorStatus, status := -1, -1
|
||||
if store.Status == model.StoreStatusOpened {
|
||||
vendorStatus = 1
|
||||
}
|
||||
if storeDetail.Status == model.StoreStatusOpened {
|
||||
status = 1
|
||||
}
|
||||
statusMap := map[int]string{
|
||||
1: "营业",
|
||||
-1: "休息",
|
||||
}
|
||||
if vendorStatus != status {
|
||||
content := "您的门店 [" + storeDetail.Name + "],ID:[" + utils.Int2Str(storeDetail.ID) + "],在[" + model.VendorChineseNames[storeMap.VendorID] + "] 平台上营业状态和京西不一致!平台状态:【" + statusMap[vendorStatus] + "】,京西状态:【" + statusMap[status] + "】"
|
||||
if user, err := dao.GetUserByID(db, "mobile", storeDetail.MarketManPhone); err == nil {
|
||||
ddmsg.SendUserMessage(dingdingapi.MsgTyeText, user.UserID, "平台门店状态变化", content)
|
||||
}
|
||||
}
|
||||
}
|
||||
//营业时长
|
||||
optime := jxutils.JxOperationTime2TimeByDate(storeDetail.CloseTime1, time.Now()).Sub(jxutils.JxOperationTime2TimeByDate(storeDetail.OpenTime1, time.Now())).Hours()
|
||||
if storeDetail.CloseTime2 != 0 && storeDetail.OpenTime2 != 0 {
|
||||
optime += jxutils.JxOperationTime2TimeByDate(storeDetail.CloseTime2, time.Now()).Sub(jxutils.JxOperationTime2TimeByDate(storeDetail.OpenTime2, time.Now())).Hours()
|
||||
}
|
||||
storeManage.OpenTime = utils.Str2Float64(fmt.Sprintf("%.2f", optime))
|
||||
//商品数
|
||||
highSkuCount := 0
|
||||
storeSkus, _ := dao.GetStoresSkusForManageState(db, storeMap.StoreID, model.StoreSkuBindStatusNormal)
|
||||
for _, v := range storeSkus {
|
||||
if v.UnitPrice > v.MidUnitPrice {
|
||||
highSkuCount++
|
||||
}
|
||||
}
|
||||
storeManage.SkuCount, storeManage.HighSkuCount = len(storeSkus), highSkuCount
|
||||
//活动丰富度
|
||||
ample, _ := handler.GetActAmple(ctx, storeDetail.VendorStoreID, storeDetail.VendorStoreID)
|
||||
storeManage.ActAmple = ample
|
||||
//订单
|
||||
refuseOrderCount := 0
|
||||
orderList, _ := dao.QueryOrdersForManageState(db, storeMap.StoreID, storeMap.VendorID, model.OrderStatusCanceled, dayTimeBegin, dayTimeEnd)
|
||||
for _, v := range orderList {
|
||||
if v.BindID == 0 {
|
||||
refuseOrderCount++
|
||||
}
|
||||
}
|
||||
storeManage.NullOrderCount, storeManage.RefuseOrderCount = len(orderList), refuseOrderCount
|
||||
//评分(美团)
|
||||
if storeMap.VendorID == model.VendorIDMTWM {
|
||||
mtapi := partner.CurAPIManager.GetAPI(model.VendorIDMTWM, storeDetail.VendorOrgCode).(*mtwmapi.API)
|
||||
if scoreResult, err := mtapi.CommentScore(storeDetail.VendorStoreID); err == nil {
|
||||
storeManage.StoreScore = scoreResult.AvgPoiScore
|
||||
}
|
||||
}
|
||||
return storeManage
|
||||
}
|
||||
|
||||
func GetStoreManageState(ctx *jxcontext.Context, storeIDs, brandIDs []int, vendorID, sortType, offset, pageSize int) (pageInfo *model.PagedInfo, err error) {
|
||||
var (
|
||||
db = dao.GetDB()
|
||||
)
|
||||
//权限
|
||||
if permission.IsRoled(ctx) {
|
||||
if storeIDsMap, err := permission.GetUserStoresResultMap(ctx.GetUserID()); err == nil {
|
||||
var storeIDs2 []int
|
||||
if len(storeIDs) > 0 {
|
||||
for _, v := range storeIDs {
|
||||
if storeIDsMap[v] != 0 {
|
||||
storeIDs2 = append(storeIDs2, v)
|
||||
}
|
||||
}
|
||||
if len(storeIDs2) == 0 {
|
||||
storeIDs2 = append(storeIDs2, -1)
|
||||
}
|
||||
} else {
|
||||
for k, _ := range storeIDsMap {
|
||||
storeIDs2 = append(storeIDs2, k)
|
||||
}
|
||||
}
|
||||
storeIDs = nil
|
||||
storeIDs = storeIDs2
|
||||
}
|
||||
}
|
||||
return dao.GetStoreManageState(db, storeIDs, brandIDs, vendorID, sortType, offset, pageSize)
|
||||
}
|
||||
|
||||
func CalculateCoverArea(coordinate []string, vendorID int) (area float64) {
|
||||
//我先把地理坐标转成了平面坐标(百度的方法)
|
||||
//然后按多个坐标求面积公式求的面积(百度的方法)
|
||||
if len(coordinate) == 0 {
|
||||
return 0
|
||||
}
|
||||
var xyList [][2]float64
|
||||
for _, v := range coordinate {
|
||||
cell := strings.Split(v, ",")
|
||||
if len(cell) == 0 || len(cell) == 1 {
|
||||
continue
|
||||
}
|
||||
var lat, lng float64
|
||||
if vendorID == model.VendorIDJD || vendorID == model.VendorIDEBAI {
|
||||
lng = utils.Str2Float64WithDefault(cell[0], 0)
|
||||
lat = utils.Str2Float64WithDefault(cell[1], 0)
|
||||
} else {
|
||||
lat = utils.Str2Float64WithDefault(cell[0], 0)
|
||||
lng = utils.Str2Float64WithDefault(cell[1], 0)
|
||||
}
|
||||
xys := jxutils.MillierConvertion(lat, lng)
|
||||
xyList = append(xyList, xys)
|
||||
}
|
||||
var sum float64 = 0
|
||||
if len(xyList) == 0 {
|
||||
return 0
|
||||
}
|
||||
for i := 0; i < len(xyList)-1; i++ {
|
||||
// sum += (xyList[i+1][0] - xyList[i][0]) * (xyList[i+1][1] + xyList[i][1])
|
||||
sum += (xyList[i][0]*xyList[i+1][1] - xyList[i+1][0]*xyList[i][1])
|
||||
}
|
||||
// sum += (xyList[0][0] - xyList[len(xyList)-1][0]) * (xyList[0][1] + xyList[len(xyList)-1][1])
|
||||
sum += (xyList[len(xyList)-1][0]*xyList[0][1] - xyList[0][0]*xyList[len(xyList)-1][1])
|
||||
sum /= float64(2)
|
||||
sum = math.Abs(sum)
|
||||
sum = utils.Str2Float64(fmt.Sprintf("%.2f", sum))
|
||||
return sum
|
||||
}
|
||||
|
||||
func RefreshJDMembers(ctx *jxcontext.Context) (err error) {
|
||||
var (
|
||||
db = dao.GetDB()
|
||||
pageSize = 50
|
||||
page int
|
||||
pages []int
|
||||
memberMap = make(map[string]*model.UserMember)
|
||||
)
|
||||
userMemebers, _ := dao.GetUserMemberWithoutDeleted(db, model.VendorIDJD)
|
||||
for _, v := range userMemebers {
|
||||
memberMap[v.Mobile] = v
|
||||
}
|
||||
pageResult, err := api.JdAPI.QueryMemberTransListByCondition("", "", page, pageSize)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "cookie") {
|
||||
globals.SugarLogger.Warnf("京东cookie过期了!")
|
||||
}
|
||||
}
|
||||
if pageResult.Total%pageSize == 0 {
|
||||
page = pageResult.Total / pageSize
|
||||
} else {
|
||||
page = pageResult.Total/pageSize + 1
|
||||
}
|
||||
for ; page > 0; page-- {
|
||||
pages = append(pages, page)
|
||||
}
|
||||
task := tasksch.NewParallelTask2("RefreshJDMembers", tasksch.NewParallelConfig().SetIsContinueWhenError(true), ctx,
|
||||
func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, successCount int, err error) {
|
||||
pageNo := batchItemList[0].(int)
|
||||
pageResult2, err := api.JdAPI.QueryMemberTransListByCondition("", "", pageNo, pageSize)
|
||||
if pageResult2 != nil {
|
||||
for _, v := range pageResult2.Rows {
|
||||
if utils.Str2Time(v.Endtimestr+" 23:59:59").Sub(time.Now()) >= 0 && v.Dealstatus == 0 {
|
||||
if memberMap[v.Merchantcardno] != nil { //库里有这个人的会员信息了
|
||||
if utils.Time2Str(memberMap[v.Merchantcardno].EndAt) != v.Endtimestr+" 23:59:59" { //可能这个人续费了,续存了
|
||||
memberMap[v.Merchantcardno].EndAt = utils.Str2Time(v.Endtimestr + " 23:59:59")
|
||||
memberMap[v.Merchantcardno].DeletedAt = utils.DefaultTimeValue
|
||||
dao.UpdateEntity(db, memberMap[v.Merchantcardno], "EndAt", "DeletedAt")
|
||||
}
|
||||
} else {
|
||||
userMember := &model.UserMember{
|
||||
VendorOrderID: v.Orderid,
|
||||
VendorID: model.VendorIDJD,
|
||||
Mobile: v.Merchantcardno,
|
||||
MemberType: 1,
|
||||
EndAt: utils.Str2Time(v.Endtimestr + " 23:59:59"),
|
||||
IsPay: 1,
|
||||
}
|
||||
dao.WrapAddIDCULDEntity(userMember, "jxadmin")
|
||||
if v.Createtime != "" {
|
||||
userMember.CreatedAt = utils.Str2Time(v.Createtime)
|
||||
} else {
|
||||
userMember.CreatedAt = utils.Str2Time(v.Cardcreatetime)
|
||||
}
|
||||
if userMember.EndAt.Sub(time.Now()) <= 0 {
|
||||
userMember.DeletedAt = time.Now()
|
||||
}
|
||||
dao.CreateEntity(db, userMember)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return retVal, successCount, err
|
||||
}, pages)
|
||||
tasksch.HandleTask(task, nil, true).Run()
|
||||
task.GetID()
|
||||
return err
|
||||
}
|
||||
|
||||
func UserMemberReport(ctx *jxcontext.Context, vendorID int, keyword string, offset, pageSize int) (page *model.PagedInfo, err error) {
|
||||
var (
|
||||
db = dao.GetDB()
|
||||
)
|
||||
var list []*dao.UserMemberReportResult
|
||||
sql := `
|
||||
SELECT SQL_CALC_FOUND_ROWS a.mobile, IF(a.vendor_id = ?, b.name, t1.consignee_name) name, IF(a.deleted_at = ?, ?, ?) status, a.vendor_id, t1.buy_count, t1.buy_price,
|
||||
t1.good_comment_count, t1.bad_comment_count, t2.finished_count, t2.finished_price
|
||||
FROM user_member a
|
||||
LEFT JOIN user b ON a.mobile = b.mobile
|
||||
LEFT JOIN (
|
||||
SELECT a.mobile, MAX(b.consignee_name) consignee_name, COUNT(b.vendor_store_id) buy_count, SUM(b.actual_pay_price) buy_price, COUNT(c.score > 3) good_comment_count, COUNT(c.score < 3) bad_comment_count
|
||||
FROM user_member a
|
||||
LEFT JOIN goods_order b ON a.mobile = b.consignee_mobile2 AND a.vendor_id = b.vendor_id AND b.order_type = ? AND b.store_id <> ?
|
||||
LEFT JOIN jx_bad_comments c ON c.order_id = b.vendor_order_id
|
||||
GROUP BY 1
|
||||
) t1 ON t1.mobile = a.mobile
|
||||
LEFT JOIN (
|
||||
SELECT a.mobile, COUNT(b.vendor_store_id) finished_count, SUM(b.actual_pay_price) finished_price
|
||||
FROM user_member a
|
||||
LEFT JOIN goods_order b ON a.mobile = b.consignee_mobile2 AND a.vendor_id = b.vendor_id AND b.order_type = ? AND status = ? AND b.store_id <> ?
|
||||
GROUP BY 1
|
||||
)t2 ON t2.mobile = a.mobile
|
||||
WHERE 1 = 1
|
||||
`
|
||||
sqlParams := []interface{}{
|
||||
model.VendorIDJX, utils.DefaultTimeValue, model.YES, model.NO,
|
||||
model.OrderTypeNormal, model.MatterStoreID,
|
||||
model.OrderTypeNormal, model.OrderStatusFinished, model.MatterStoreID,
|
||||
}
|
||||
if vendorID != -1 {
|
||||
sql += " AND a.vendor_id = ?"
|
||||
sqlParams = append(sqlParams, vendorID)
|
||||
}
|
||||
if keyword != "" {
|
||||
keywordLike := "%" + keyword + "%"
|
||||
sql += " AND (a.mobile LIKE ? OR name LIKE ?)"
|
||||
sqlParams = append(sqlParams, keywordLike, keywordLike)
|
||||
}
|
||||
sql += `
|
||||
LIMIT ? OFFSET ?
|
||||
`
|
||||
pageSize = jxutils.FormalizePageSize(pageSize)
|
||||
sqlParams = append(sqlParams, pageSize, offset)
|
||||
txDB, _ := dao.Begin(db)
|
||||
defer dao.Commit(db, txDB)
|
||||
if err = dao.GetRowsTx(txDB, &list, sql, sqlParams...); err == nil {
|
||||
page = &model.PagedInfo{
|
||||
TotalCount: dao.GetLastTotalRowCount2(db, txDB),
|
||||
//Data: page,
|
||||
}
|
||||
task := tasksch.NewParallelTask("", tasksch.NewParallelConfig().SetIsContinueWhenError(true), jxcontext.AdminCtx,
|
||||
func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
|
||||
v := batchItemList[0].(*dao.UserMemberReportResult)
|
||||
var good *model.GoodsOrder
|
||||
sql := `
|
||||
SELECT * FROM goods_order WHERE consignee_mobile2 = ? AND vendor_id = ? AND order_type = ? AND store_id <> ? LIMIT 1 OFFSET 0
|
||||
`
|
||||
sqlParams := []interface{}{v.Mobile, v.VendorID, model.OrderTypeNormal, model.MatterStoreID}
|
||||
dao.GetRow(db, &good, sql, sqlParams)
|
||||
if good != nil {
|
||||
v.NewPrice = good.ActualPayPrice
|
||||
}
|
||||
var place *model.Place
|
||||
sql2 := `
|
||||
SELECT a.* FROM place a,(
|
||||
SELECT b.city_code, COUNT(*) count
|
||||
FROM goods_order a
|
||||
LEFT JOIN store b ON b.id = IF(a.jx_store_id = 0, store_id, jx_store_id)
|
||||
WHERE consignee_mobile2 = ? AND vendor_id = ?
|
||||
GROUP BY 1
|
||||
ORDER BY COUNT(*)
|
||||
LIMIT 1 OFFSET 0)b
|
||||
WHERE a.code = b.city_code
|
||||
`
|
||||
dao.GetRow(db, &place, sql2, sqlParams)
|
||||
if place != nil {
|
||||
v.CityName = place.Name
|
||||
v.CityCode = place.Code
|
||||
}
|
||||
return retVal, err
|
||||
}, list)
|
||||
tasksch.HandleTask(task, nil, true).Run()
|
||||
task.GetResult(0)
|
||||
page.Data = list
|
||||
}
|
||||
return page, err
|
||||
}
|
||||
2156
business/jxstore/tempop/tempop.go
Normal file
2156
business/jxstore/tempop/tempop.go
Normal file
File diff suppressed because it is too large
Load Diff
44
business/jxstore/tempop/tempop_test.go
Normal file
44
business/jxstore/tempop/tempop_test.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package tempop
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"git.rosy.net.cn/jx-callback/globals/api2"
|
||||
"git.rosy.net.cn/jx-callback/globals/testinit"
|
||||
|
||||
_ "git.rosy.net.cn/jx-callback/business/partner/purchase/ebai"
|
||||
_ "git.rosy.net.cn/jx-callback/business/partner/purchase/elm"
|
||||
_ "git.rosy.net.cn/jx-callback/business/partner/purchase/jd"
|
||||
_ "git.rosy.net.cn/jx-callback/business/partner/purchase/mtwm"
|
||||
_ "git.rosy.net.cn/jx-callback/business/partner/purchase/weimob/wsc"
|
||||
)
|
||||
|
||||
func init() {
|
||||
testinit.Init()
|
||||
api2.Init()
|
||||
}
|
||||
|
||||
func TestJdStoreInfo1125(t *testing.T) {
|
||||
_, err := JdStoreInfo1125()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Testaa(t *testing.T) {
|
||||
request, _ := http.NewRequest(http.MethodGet, "https://stores.shop.jd.com/stores/updateStoreStatus?storeId=24332466&storeStatus=1", nil)
|
||||
c := &http.Cookie{
|
||||
Name: "thor",
|
||||
Value: "80FAF09E9A09B6E618A68057BDFCFCB8C86E8252DC9F7D3B34572625904FBA0AB6BF053A5325612EC0407791BB05F5301356E71E8B282C40C06D0B5DF3439DEECB102A78FAFF7AC0FC4E2D1FA8DD8BBAE1A011E50B5C74F1870AD982D7BF453F470F31F2241B73AC4C25485025C2ABEBC8A538AF7257824D2FAEE300A1435175B0B451FB5C19B78D729FC83152CA3BAF",
|
||||
}
|
||||
request.AddCookie(c)
|
||||
client := &http.Client{}
|
||||
fmt.Println("test1", request.URL)
|
||||
response, _ := client.Do(request)
|
||||
defer response.Body.Close()
|
||||
bodyData, _ := ioutil.ReadAll(response.Body)
|
||||
fmt.Println("test1", string(bodyData))
|
||||
}
|
||||
1104
business/jxstore/yonghui/yonghui.go
Normal file
1104
business/jxstore/yonghui/yonghui.go
Normal file
File diff suppressed because it is too large
Load Diff
@@ -4,8 +4,8 @@ import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"git.rosy.net.cn/jx-callback/business/dao"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
|
||||
"git.rosy.net.cn/jx-callback/business/model/dao"
|
||||
"git.rosy.net.cn/jx-callback/business/model/legacymodel"
|
||||
)
|
||||
|
||||
|
||||
@@ -9,19 +9,24 @@ import (
|
||||
"image/png"
|
||||
"net/http"
|
||||
|
||||
"git.rosy.net.cn/jx-callback/business/dao"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/tasksch"
|
||||
"git.rosy.net.cn/jx-callback/business/model"
|
||||
"git.rosy.net.cn/jx-callback/business/model/dao"
|
||||
"git.rosy.net.cn/jx-callback/business/partner"
|
||||
"git.rosy.net.cn/jx-callback/globals"
|
||||
"git.rosy.net.cn/jx-callback/globals/api"
|
||||
"github.com/qiniu/api.v7/storage"
|
||||
)
|
||||
|
||||
const (
|
||||
qiniuTokenExpires = 300 // 七牛TOKEN有效时间,5分钟
|
||||
|
||||
MainImgWidth = 800
|
||||
MainImgHeight = 800
|
||||
MainImgWidth = 800
|
||||
MainImgHeight = 800
|
||||
MainImgWidth2 = 500
|
||||
MainImgHeight2 = 500
|
||||
)
|
||||
|
||||
type UploadResTokenInfo struct {
|
||||
@@ -48,24 +53,23 @@ func Binary2Image(binaryData []byte, mimeType string) (img image.Image, outMimeT
|
||||
}
|
||||
|
||||
func GetQiniuUploadToken(ctx *jxcontext.Context, suffix, hashCode string) (upTokenInfo *UploadResTokenInfo, err error) {
|
||||
//imgURL := ""
|
||||
//if hashCode != "" {
|
||||
// imgURL, _ = GetDataResource(ctx, hashCode)
|
||||
//}
|
||||
//
|
||||
//putPolicy := storage.PutPolicy{
|
||||
// Scope: globals.QiniuBucket,
|
||||
// Expires: qiniuTokenExpires,
|
||||
//}
|
||||
//upTokenInfo = &UploadResTokenInfo{
|
||||
// Token: putPolicy.UploadToken(api.QiniuAPI),
|
||||
// Expires: putPolicy.Expires,
|
||||
// FileName: jxutils.GenPicFileName(suffix),
|
||||
// Hit: imgURL != "",
|
||||
// Img: imgURL,
|
||||
//}
|
||||
//return upTokenInfo, err
|
||||
return nil, err
|
||||
imgURL := ""
|
||||
if hashCode != "" {
|
||||
imgURL, _ = GetDataResource(ctx, hashCode)
|
||||
}
|
||||
|
||||
putPolicy := storage.PutPolicy{
|
||||
Scope: globals.QiniuBucket,
|
||||
Expires: qiniuTokenExpires,
|
||||
}
|
||||
upTokenInfo = &UploadResTokenInfo{
|
||||
Token: putPolicy.UploadToken(api.QiniuAPI),
|
||||
Expires: putPolicy.Expires,
|
||||
FileName: jxutils.GenPicFileName(suffix),
|
||||
Hit: imgURL != "",
|
||||
Img: imgURL,
|
||||
}
|
||||
return upTokenInfo, err
|
||||
}
|
||||
|
||||
// 此函数要求resBinary不能空,mimeType与hashCode必须是正确的
|
||||
@@ -79,7 +83,7 @@ func RegisterDataResource(ctx *jxcontext.Context, name, resourceURL, mimeType, h
|
||||
return nil, err
|
||||
}
|
||||
if imgType == model.ImgTypeMain {
|
||||
if img.Bounds().Dx() != MainImgWidth || img.Bounds().Dy() != MainImgHeight {
|
||||
if (img.Bounds().Dx() != MainImgWidth || img.Bounds().Dy() != MainImgHeight) && (img.Bounds().Dx() != MainImgWidth2 || img.Bounds().Dy() != MainImgHeight2) {
|
||||
return nil, fmt.Errorf("图片大小:%dx%d非法,要求必须:%dx%d", img.Bounds().Dx(), img.Bounds().Dy(), MainImgWidth, MainImgHeight)
|
||||
}
|
||||
}
|
||||
@@ -107,10 +111,10 @@ func RegisterDataResource(ctx *jxcontext.Context, name, resourceURL, mimeType, h
|
||||
return dataRes, err
|
||||
}
|
||||
if imgType > 0 {
|
||||
//if globals.EnableStoreWrite {
|
||||
// 忽略上传错误
|
||||
UploadImage2Vendors(ctx, nil, dataRes, resBinary, isAsyncUpload2Vendor)
|
||||
//}
|
||||
if globals.EnableStoreWrite {
|
||||
// 忽略上传错误
|
||||
UploadImage2Vendors(ctx, nil, dataRes, resBinary, isAsyncUpload2Vendor)
|
||||
}
|
||||
}
|
||||
return dataRes, err
|
||||
}
|
||||
@@ -150,82 +154,82 @@ func GetDataResource(ctx *jxcontext.Context, hashCode string) (resourceURL strin
|
||||
|
||||
// 这个函数,可能部分平台成功,部分失败
|
||||
func UploadImage2Vendors(ctx *jxcontext.Context, parentTask tasksch.ITask, dataRes *model.DataResource, imgData []byte, isAsync bool) (hint string, err error) {
|
||||
// var vendorIDs []int
|
||||
// if dataRes.EbaiURL == "" {
|
||||
// vendorIDs = append(vendorIDs, model.VendorIDEBAI)
|
||||
// }
|
||||
// if dataRes.MtwmURL == "" {
|
||||
// vendorIDs = append(vendorIDs, model.VendorIDMTWM)
|
||||
// }
|
||||
var vendorIDs []int
|
||||
if dataRes.EbaiURL == "" {
|
||||
vendorIDs = append(vendorIDs, model.VendorIDEBAI)
|
||||
}
|
||||
if dataRes.MtwmURL == "" {
|
||||
vendorIDs = append(vendorIDs, model.VendorIDMTWM)
|
||||
}
|
||||
// if dataRes.JdsURL == "" {
|
||||
// vendorIDs = append(vendorIDs, model.VendorIDJDShop)
|
||||
// }
|
||||
// if len(vendorIDs) > 0 {
|
||||
// imgName := jxutils.GetShortNameFromURL(dataRes.MainURL)
|
||||
// task := tasksch.NewSeqTask(fmt.Sprintf("上传图片至平台1:%s,%s", dataRes.Name, dataRes.MainURL), ctx,
|
||||
// func(task *tasksch.SeqTask, step int, params ...interface{}) (result interface{}, err error) {
|
||||
// switch step {
|
||||
// case 0:
|
||||
// if imgData == nil {
|
||||
// if imgData, _, err = jxutils.DownloadFileByURL(dataRes.MainURL); err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// }
|
||||
// case 1:
|
||||
// uploadTask := tasksch.NewParallelTask(fmt.Sprintf("上传图片至平台2:%s,%s", dataRes.Name, imgName),
|
||||
// tasksch.NewParallelConfig().SetIsContinueWhenError(true), ctx,
|
||||
// func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
|
||||
// vendorID := batchItemList[0].(int)
|
||||
// if handler := partner.GetPurchasePlatformFromVendorID(vendorID); handler != nil {
|
||||
// // TODO vendorOrgCode
|
||||
// imgHint, err2 := handler.UploadImg(ctx, "", dataRes.MainURL, imgData, imgName, int(dataRes.UseType))
|
||||
// if err = err2; err == nil {
|
||||
// retVal = [][]interface{}{
|
||||
// []interface{}{
|
||||
// vendorID,
|
||||
// imgHint,
|
||||
// },
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// return retVal, err
|
||||
// }, vendorIDs)
|
||||
// tasksch.HandleTask(uploadTask, task, false).Run()
|
||||
// resultList, err2 := uploadTask.GetResult(0)
|
||||
// err = err2
|
||||
// if len(resultList) > 0 {
|
||||
// db := dao.GetDB()
|
||||
// for _, v := range resultList {
|
||||
// result := v.([]interface{})
|
||||
// vendorID := result[0].(int)
|
||||
// imgHint := result[1].(string)
|
||||
// updateField := ""
|
||||
// if vendorID == model.VendorIDEBAI {
|
||||
// dataRes.EbaiURL = imgHint
|
||||
// updateField = "EbaiURL"
|
||||
// } else if vendorID == model.VendorIDMTWM {
|
||||
// dataRes.MtwmURL = imgHint
|
||||
// updateField = "MtwmURL"
|
||||
// }
|
||||
// // else if vendorID == model.VendorIDJDShop {
|
||||
// // dataRes.JdsURL = imgHint
|
||||
// // updateField = "JdsURL"
|
||||
// // }
|
||||
// dao.UpdateEntity(db, dataRes, updateField)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// return result, err
|
||||
// }, 2)
|
||||
// tasksch.HandleTask(task, parentTask, false).Run()
|
||||
// if !isAsync {
|
||||
// if _, err = task.GetResult(0); err == nil {
|
||||
// hint = "1"
|
||||
// }
|
||||
// } else {
|
||||
// hint = task.GetID()
|
||||
// }
|
||||
// }
|
||||
if len(vendorIDs) > 0 {
|
||||
imgName := jxutils.GetShortNameFromURL(dataRes.MainURL)
|
||||
task := tasksch.NewSeqTask(fmt.Sprintf("上传图片至平台1:%s,%s", dataRes.Name, dataRes.MainURL), ctx,
|
||||
func(task *tasksch.SeqTask, step int, params ...interface{}) (result interface{}, err error) {
|
||||
switch step {
|
||||
case 0:
|
||||
if imgData == nil {
|
||||
if imgData, _, err = jxutils.DownloadFileByURL(dataRes.MainURL); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
case 1:
|
||||
uploadTask := tasksch.NewParallelTask(fmt.Sprintf("上传图片至平台2:%s,%s", dataRes.Name, imgName),
|
||||
tasksch.NewParallelConfig().SetIsContinueWhenError(true), ctx,
|
||||
func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
|
||||
vendorID := batchItemList[0].(int)
|
||||
if handler := partner.GetPurchasePlatformFromVendorID(vendorID); handler != nil {
|
||||
// TODO vendorOrgCode
|
||||
imgHint, err2 := handler.UploadImg(ctx, "", dataRes.MainURL, imgData, imgName, int(dataRes.UseType))
|
||||
if err = err2; err == nil {
|
||||
retVal = [][]interface{}{
|
||||
[]interface{}{
|
||||
vendorID,
|
||||
imgHint,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
return retVal, err
|
||||
}, vendorIDs)
|
||||
tasksch.HandleTask(uploadTask, task, false).Run()
|
||||
resultList, err2 := uploadTask.GetResult(0)
|
||||
err = err2
|
||||
if len(resultList) > 0 {
|
||||
db := dao.GetDB()
|
||||
for _, v := range resultList {
|
||||
result := v.([]interface{})
|
||||
vendorID := result[0].(int)
|
||||
imgHint := result[1].(string)
|
||||
updateField := ""
|
||||
if vendorID == model.VendorIDEBAI {
|
||||
dataRes.EbaiURL = imgHint
|
||||
updateField = "EbaiURL"
|
||||
} else if vendorID == model.VendorIDMTWM {
|
||||
dataRes.MtwmURL = imgHint
|
||||
updateField = "MtwmURL"
|
||||
}
|
||||
// else if vendorID == model.VendorIDJDShop {
|
||||
// dataRes.JdsURL = imgHint
|
||||
// updateField = "JdsURL"
|
||||
// }
|
||||
dao.UpdateEntity(db, dataRes, updateField)
|
||||
}
|
||||
}
|
||||
}
|
||||
return result, err
|
||||
}, 2)
|
||||
tasksch.HandleTask(task, parentTask, false).Run()
|
||||
if !isAsync {
|
||||
if _, err = task.GetResult(0); err == nil {
|
||||
hint = "1"
|
||||
}
|
||||
} else {
|
||||
hint = task.GetID()
|
||||
}
|
||||
}
|
||||
return hint, err
|
||||
}
|
||||
|
||||
|
||||
@@ -7,11 +7,17 @@ import (
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
|
||||
"git.rosy.net.cn/jx-callback/business/model"
|
||||
"git.rosy.net.cn/jx-callback/globals/api2"
|
||||
"git.rosy.net.cn/jx-callback/globals/testinit"
|
||||
|
||||
_ "git.rosy.net.cn/jx-callback/business/partner/purchase/ebai"
|
||||
_ "git.rosy.net.cn/jx-callback/business/partner/purchase/jd"
|
||||
_ "git.rosy.net.cn/jx-callback/business/partner/purchase/mtwm"
|
||||
)
|
||||
|
||||
func init() {
|
||||
testinit.Init()
|
||||
api2.Init()
|
||||
}
|
||||
|
||||
func TestGetQiniuUploadToken(t *testing.T) {
|
||||
|
||||
58
business/jxutils/ddmsg/ddmsg.go
Normal file
58
business/jxutils/ddmsg/ddmsg.go
Normal file
@@ -0,0 +1,58 @@
|
||||
package ddmsg
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.rosy.net.cn/baseapi/platformapi/dingdingapi"
|
||||
"git.rosy.net.cn/baseapi/utils/errlist"
|
||||
"git.rosy.net.cn/jx-callback/business/auth2"
|
||||
"git.rosy.net.cn/jx-callback/business/auth2/authprovider/dingding"
|
||||
"git.rosy.net.cn/jx-callback/globals"
|
||||
"git.rosy.net.cn/jx-callback/globals/api"
|
||||
)
|
||||
|
||||
func SendDDUserMessage(msgType, ddUserID, title, content string) (err error) {
|
||||
globals.SugarLogger.Debugf("SendDDUserMessage ddUserID:%s, title:%s", ddUserID, title)
|
||||
if globals.IsProductEnv() {
|
||||
if msgType == dingdingapi.MsgTyeText {
|
||||
err = api.DingDingAPI.CorpAsyncSendSimple(ddUserID, content)
|
||||
} else if msgType == dingdingapi.MsgTypeMarkdown {
|
||||
err = api.DingDingAPI.CorpAsyncSendMarkdown([]string{ddUserID}, nil, false, title, content)
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func SendUserMessage(msgType, userID, title, content string) (err error) {
|
||||
globals.SugarLogger.Debugf("SendUserMessage userID:%s, title:%s", userID, title)
|
||||
authList, err := auth2.GetUserBindAuthInfo(userID)
|
||||
findOneMethod := false
|
||||
if err == nil {
|
||||
for _, auth := range authList {
|
||||
if auth.Type == dingding.AuthTypeStaff /*|| auth.Type == weixin.AuthTypeMP*/ {
|
||||
findOneMethod = true
|
||||
if len(content) > dingdingapi.MaxWorkContentLen {
|
||||
content = content[:dingdingapi.MaxWorkContentLen-4] + "..."
|
||||
}
|
||||
err = SendDDUserMessage(msgType, auth.AuthID, title, content)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if !findOneMethod {
|
||||
err = fmt.Errorf("用户[%s]找不到至少一个有效的通讯方式", userID)
|
||||
}
|
||||
if err != nil {
|
||||
globals.SugarLogger.Infof("SendUserMessage userID:%s, title:%s, content:%s failed with error:%v", userID, title, content, err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func SendUsersMessage(msgType string, userIDs []string, title, content string) (err error) {
|
||||
errList := errlist.New()
|
||||
for _, userID := range userIDs {
|
||||
errList.AddErr(SendUserMessage(msgType, userID, title, content))
|
||||
}
|
||||
err = errList.GetErrListAsOne()
|
||||
return err
|
||||
}
|
||||
@@ -7,8 +7,8 @@ import (
|
||||
"time"
|
||||
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
"git.rosy.net.cn/jx-callback/business/dao"
|
||||
"git.rosy.net.cn/jx-callback/business/model"
|
||||
"git.rosy.net.cn/jx-callback/business/model/dao"
|
||||
"git.rosy.net.cn/jx-callback/business/partner"
|
||||
"git.rosy.net.cn/jx-callback/globals/refutil"
|
||||
)
|
||||
|
||||
@@ -101,12 +101,15 @@ func (h *Hub) GetToken(tokenType, oldToken string, waitTime time.Duration) (toke
|
||||
case EventTypeWXToken:
|
||||
token = api.WeixinAPI.CBGetToken()
|
||||
case EventTypeYLYToken:
|
||||
token = api.YilianyunAPI.GetToken()
|
||||
case EventTypeWeimobToken:
|
||||
if weimobToken := api.WeimobAPI.GetToken(); weimobToken != nil {
|
||||
token = string(utils.MustMarshal(weimobToken))
|
||||
}
|
||||
case EventTypePushToken:
|
||||
token = api.PushAPI.CBGetToken()
|
||||
case EventTypeWX2Token:
|
||||
token = api.WeixinMiniAPI2.CBGetToken()
|
||||
}
|
||||
|
||||
if token != oldToken {
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
"git.rosy.net.cn/jx-callback/business/auth2"
|
||||
"git.rosy.net.cn/jx-callback/business/model"
|
||||
"git.rosy.net.cn/jx-callback/business/model/dao"
|
||||
"git.rosy.net.cn/jx-callback/globals"
|
||||
)
|
||||
|
||||
@@ -24,8 +25,6 @@ type Context struct {
|
||||
|
||||
const (
|
||||
MaxUserNameLen = 30
|
||||
|
||||
RsmDefultToken = "rushSkyMonkeyToken_20201203"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -73,10 +72,7 @@ func New(notUsed interface{}, token string, w http.ResponseWriter, r *http.Reque
|
||||
// }
|
||||
}
|
||||
if err == model.ErrTokenIsInvalid {
|
||||
//if !globals.IsProductEnv() {
|
||||
// err = nil
|
||||
//} else
|
||||
if token == RsmDefultToken {
|
||||
if !globals.IsProductEnv() {
|
||||
err = nil
|
||||
} else {
|
||||
errCode = model.ErrCodeTokenIsInvalid
|
||||
@@ -162,3 +158,14 @@ func (ctx *Context) GetUserID() (userID string) {
|
||||
}
|
||||
return userID
|
||||
}
|
||||
|
||||
func (ctx *Context) GetFullUser() (user *model.User) {
|
||||
token := ctx.GetToken()
|
||||
authInfo, err2 := auth2.GetTokenInfo(token)
|
||||
if err2 == nil {
|
||||
if authInfo.TokenType == auth2.TokenTypeNormal {
|
||||
user, _ = dao.GetUserByID(dao.GetDB(), "user_id", authInfo.GetID())
|
||||
}
|
||||
}
|
||||
return user
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package jxutils
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/aes"
|
||||
"crypto/md5"
|
||||
"encoding/base64"
|
||||
@@ -9,12 +11,19 @@ import (
|
||||
"math"
|
||||
"math/rand"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.rosy.net.cn/baseapi"
|
||||
"git.rosy.net.cn/baseapi/platformapi/autonavi"
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
"git.rosy.net.cn/baseapi/utils/routinepool"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/excel"
|
||||
"git.rosy.net.cn/jx-callback/business/model"
|
||||
"git.rosy.net.cn/jx-callback/globals"
|
||||
"git.rosy.net.cn/jx-callback/globals/api"
|
||||
"github.com/qiniu/api.v7/storage"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -33,22 +42,36 @@ var (
|
||||
model.VendorIDEBAI: []string{
|
||||
"image-star.elemecdn.com",
|
||||
},
|
||||
}
|
||||
|
||||
letterBytes = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
|
||||
flowUnitMap = map[string]string{
|
||||
"KB": "KB",
|
||||
"MB": "MB",
|
||||
"GB": "GB",
|
||||
model.VendorIDYB: []string{
|
||||
"pospalstoreimg.area27.pospal.cn",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
const fileExt = ".xlsx"
|
||||
|
||||
type OrderSkuList []*model.OrderSku
|
||||
|
||||
func (l OrderSkuList) Len() int {
|
||||
return len(l)
|
||||
}
|
||||
|
||||
// Less reports whether the element with
|
||||
// index i should sort before the element with index j.
|
||||
func (l OrderSkuList) Less(i, j int) bool {
|
||||
return l[i].SalePrice < l[j].SalePrice
|
||||
}
|
||||
|
||||
// Swap swaps the elements with indexes i and j.
|
||||
func (l OrderSkuList) Swap(i, j int) {
|
||||
tmp := l[i]
|
||||
l[i] = l[j]
|
||||
l[j] = tmp
|
||||
}
|
||||
|
||||
func init() {
|
||||
rand.Seed(time.Now().Unix())
|
||||
routinePool = routinepool.New(1000, 1000)
|
||||
routinePool = routinepool.New(2000, 2000)
|
||||
|
||||
// Go regex does not support lookarounds.
|
||||
// https://stackoverflow.com/questions/38933898/error-parsing-regexp-invalid-or-unsupported-perl-syntax
|
||||
@@ -57,20 +80,102 @@ func init() {
|
||||
orderNoBeginTimestamp = utils.Str2Time("2010-01-01 00:00:00").Unix()
|
||||
}
|
||||
|
||||
func RandStringBytes(n int) string {
|
||||
b := make([]byte, n)
|
||||
for i := range b {
|
||||
b[i] = letterBytes[rand.Intn(len(letterBytes))]
|
||||
func getJxStoreIDFromOrder(order *model.GoodsOrder) (retVal int) {
|
||||
if order.JxStoreID != 0 {
|
||||
return order.JxStoreID
|
||||
}
|
||||
return string(b)
|
||||
return order.StoreID
|
||||
}
|
||||
|
||||
func GenRand6() (num int) {
|
||||
return utils.Str2Int(fmt.Sprintf("%06v", rand.New(rand.NewSource(time.Now().UnixNano())).Int31n(1000000)))
|
||||
// 此函数得到的是order的销售门店京西ID,与GetJxStoreIDFromOrder的区别是order.StoreID的解释不同,参考其它相关资料
|
||||
func GetSaleStoreIDFromOrder(order *model.GoodsOrder) (retVal int) {
|
||||
return getJxStoreIDFromOrder(order)
|
||||
}
|
||||
|
||||
// 此函数得到的是order的商品的展示门店京西ID,与GetJxStoreIDFromOrder的区别是order.StoreID的解释不同,参考其它相关资料
|
||||
func GetShowStoreIDFromOrder(order *model.GoodsOrder) (retVal int) {
|
||||
return getJxStoreIDFromOrder(order)
|
||||
}
|
||||
|
||||
func GetSkuIDFromOrderSku(sku *model.OrderSku) (skuID int) {
|
||||
if sku.JxSkuID > 0 {
|
||||
return sku.JxSkuID
|
||||
}
|
||||
return sku.SkuID
|
||||
}
|
||||
|
||||
func GetSaleStoreIDFromAfsOrder(order *model.AfsOrder) (retVal int) {
|
||||
if order.JxStoreID > 0 {
|
||||
return order.JxStoreID
|
||||
}
|
||||
return order.StoreID
|
||||
}
|
||||
|
||||
func GetSkuIDFromOrderSkuFinancial(sku *model.OrderSkuFinancial) (skuID int) {
|
||||
if sku.JxSkuID > 0 {
|
||||
return sku.JxSkuID
|
||||
}
|
||||
return sku.SkuID
|
||||
}
|
||||
|
||||
func SplitUniversalOrderID(universalOrderID string) (orderID string, vendorID int) {
|
||||
index := strings.Index(universalOrderID, "|")
|
||||
if index != -1 {
|
||||
orderID = universalOrderID[:index]
|
||||
vendorID = int(utils.Str2Int64(universalOrderID[index+1:]))
|
||||
} else {
|
||||
if vendorID = GetPossibleVendorIDFromVendorOrderID(universalOrderID); vendorID == model.VendorIDUnknown {
|
||||
// globals.SugarLogger.Errorf("unkown order type:%v", universalOrderID)
|
||||
panic(fmt.Sprintf("unkown order type, orderID:%s", universalOrderID))
|
||||
}
|
||||
orderID = universalOrderID
|
||||
}
|
||||
return orderID, vendorID
|
||||
}
|
||||
|
||||
func GetPossibleVendorIDFromVendorOrderID(vendorOrderID string) (vendorID int) {
|
||||
vendorID = model.VendorIDUnknown
|
||||
//if vendorOrderIDInt64 := utils.Str2Int64WithDefault(vendorOrderID, 0); vendorOrderIDInt64 > 0 {
|
||||
orderIDLen := len(vendorOrderID)
|
||||
globals.SugarLogger.Debugf("GetPossibleVendorIDFromVendorOrderID, orderIDLen: %v", orderIDLen)
|
||||
// 5287873015048 13 wsc
|
||||
// 15380342248732 14 old ebai order
|
||||
// 800402581000221 15,16 jd order
|
||||
// 33437032333978492 17 mtwm order
|
||||
// 3022716176275221584 19 elm order, new ebai order
|
||||
|
||||
// 京东到家从2020年开始订单号的长度都会在现有基础上加一位,订单号的前两位取的是当年的最后两位数(如:2020取的20),以适应业务的发展。
|
||||
// 改造点:
|
||||
// 1、订单号位数变化,由原有15位数增加1位数调整为16位数,对接商家需检查是否有对订单号位数做长度校验。
|
||||
// 2、第一位数字发生变化,由原来9开头调整为当年年份后两位数如:2020年订单开头为20;
|
||||
if orderIDLen == len("925265130002541") || orderIDLen == len("1925265130002541") {
|
||||
vendorID = model.VendorIDJD
|
||||
} else if orderIDLen == len("3022716176275221584") {
|
||||
// vendorID = model.VendorIDELM
|
||||
vendorID = model.VendorIDEBAI // 饿百零售开放平台订单接口中订单ID“order_id”字段长度将调整为19位,和饿了么订单ID“eleme_order_id”字段格式保持一致。
|
||||
} else if orderIDLen == len("15380342248732") {
|
||||
if vendorOrderID[:2] == "88" {
|
||||
vendorID = model.VendorIDJX
|
||||
} else {
|
||||
vendorID = model.VendorIDEBAI
|
||||
}
|
||||
} else if orderIDLen == len("33437032333978492") || orderIDLen == len("116379390766579767") {
|
||||
vendorID = model.VendorIDMTWM
|
||||
} else if orderIDLen == len("5287873015048") {
|
||||
vendorID = model.VendorIDWSC
|
||||
} else if orderIDLen == len("1000004390") {
|
||||
vendorID = model.VendorIDJX
|
||||
} else if orderIDLen == len("18100216009800000001") || orderIDLen == len("191075245758000000039") {
|
||||
vendorID = model.VendorIDJDShop
|
||||
}
|
||||
//} else {
|
||||
// globals.SugarLogger.Debugf("GetPossibleVendorIDFromVendorOrderID, 2: %v", vendorOrderID)
|
||||
//}
|
||||
return vendorID
|
||||
}
|
||||
|
||||
func GenOrderNo() (orderNo int64) {
|
||||
var prefix = utils.Str2Int64(time.Now().Format("20060102"))
|
||||
const prefix = 88
|
||||
const randPartNum = 1000
|
||||
orderNo = time.Now().Unix() - orderNoBeginTimestamp
|
||||
orderNo = orderNo * randPartNum
|
||||
@@ -84,6 +189,54 @@ func GenOrderNo() (orderNo int64) {
|
||||
return orderNo
|
||||
}
|
||||
|
||||
func GenAfsOrderNo() (orderNo int64) {
|
||||
const prefix = 80
|
||||
const randPartNum = 100
|
||||
orderNo = time.Now().Unix() - orderNoBeginTimestamp
|
||||
orderNo = orderNo * randPartNum
|
||||
md5Bytes := md5.Sum([]byte(utils.GetUUID()))
|
||||
randPart := 0
|
||||
for k, v := range md5Bytes {
|
||||
randPart += int(v) << ((k % 3) * 8)
|
||||
}
|
||||
orderNo += int64(randPart % randPartNum)
|
||||
orderNo += int64(math.Pow10(int(math.Log10(float64(orderNo)))+1)) * prefix
|
||||
return orderNo
|
||||
}
|
||||
|
||||
func GetPossibleVendorIDFromAfsOrderID(afsOrderID string) (vendorID int) {
|
||||
vendorID = model.VendorIDUnknown
|
||||
if afsOrderIDInt64 := utils.Str2Int64WithDefault(afsOrderID, 0); afsOrderIDInt64 > 0 {
|
||||
orderIDLen := len(afsOrderID)
|
||||
if orderIDLen == len("22586438") { // 8
|
||||
vendorID = model.VendorIDJD
|
||||
} else if orderIDLen == len("1413138834") { // 10
|
||||
vendorID = model.VendorIDEBAI
|
||||
} else if orderIDLen == len("29488498752") { // 11
|
||||
vendorID = model.VendorIDMTWM
|
||||
}
|
||||
}
|
||||
return vendorID
|
||||
}
|
||||
|
||||
func ComposeUniversalOrderID(orderID string, vendorID int) string {
|
||||
// return fmt.Sprintf("%s|%d", orderID, vendorID)
|
||||
return orderID // 当前用长度就能区分,先不加上vendorID
|
||||
// return orderID + utils.Int64ToStr(time.Now().Unix())
|
||||
}
|
||||
|
||||
func GetUniversalOrderIDFromWaybill(bill *model.Waybill) string {
|
||||
return ComposeUniversalOrderID(bill.VendorOrderID, bill.OrderVendorID)
|
||||
}
|
||||
|
||||
func GetUniversalOrderIDFromOrder(order *model.GoodsOrder) string {
|
||||
return ComposeUniversalOrderID(order.VendorOrderID, order.VendorID)
|
||||
}
|
||||
|
||||
func GetUniversalOrderIDFromOrderStatus(status *model.OrderStatus) string {
|
||||
return ComposeUniversalOrderID(status.VendorOrderID, status.VendorID)
|
||||
}
|
||||
|
||||
// distance单位为米
|
||||
func ConvertDistanceToLogLat(lng, lat, distance, angle float64) (newLng, newLat float64) {
|
||||
oneDu := 111319.55 // 单位为米
|
||||
@@ -108,6 +261,36 @@ func EarthDistance(lng1, lat1, lng2, lat2 float64) float64 {
|
||||
return dist * radius
|
||||
}
|
||||
|
||||
// 返回结果单元为公里
|
||||
func WalkingDistance(lng1, lat1, lng2, lat2 float64) (distance float64) {
|
||||
if distance = api.AutonaviAPI.WalkingDistance(lng1, lat1, lng2, lat2); distance == 0 {
|
||||
distance = EarthDistance(lng1, lat1, lng2, lat2) * 1.4
|
||||
} else {
|
||||
distance /= 1000
|
||||
}
|
||||
return distance
|
||||
}
|
||||
|
||||
//经纬度坐标转换到平面坐标
|
||||
func MillierConvertion(lat float64, lon float64) [2]float64 {
|
||||
var L, H, W, temp, mill, x, y float64
|
||||
L = 6371.393 * math.Pi * 2 //地球周长
|
||||
W = L // 平面展开后,x轴等于周长
|
||||
H = L / 2 // y轴约等于周长一半
|
||||
mill = 2.3 // 米勒投影中的一个常数,范围大约在正负2.3之间
|
||||
temp = math.Pi
|
||||
x = lon * temp / 180 // 将经度从度数转换为弧度
|
||||
y = lat * temp / 180 // 将纬度从度数转换为弧度
|
||||
y = 1.25 * math.Log(math.Tan(0.25*temp+0.4*y)) // 米勒投影的转换
|
||||
// 弧度转为实际距离
|
||||
x = (W / 2) + (W/(2*math.Pi))*x
|
||||
y = (H / 2) - (H/(2*mill))*y
|
||||
x = utils.Str2Float64(fmt.Sprintf("%.2f", x))
|
||||
y = utils.Str2Float64(fmt.Sprintf("%.2f", y))
|
||||
var result = [2]float64{x, y}
|
||||
return result
|
||||
}
|
||||
|
||||
func StandardCoordinate2Int(value float64) int {
|
||||
return int(math.Round(value * 1000000))
|
||||
}
|
||||
@@ -116,6 +299,25 @@ func IntCoordinate2Standard(value int) float64 {
|
||||
return float64(value) / 1000000
|
||||
}
|
||||
|
||||
func IntCoordinate2MarsStandard(gpsLng, gpsLat int, coordinateType int) (marsLng, marsLat float64, err error) {
|
||||
marsLng = IntCoordinate2Standard(gpsLng)
|
||||
marsLat = IntCoordinate2Standard(gpsLat)
|
||||
coordSys := ""
|
||||
switch coordinateType {
|
||||
case model.CoordinateTypeGPS:
|
||||
coordSys = autonavi.CoordSysGPS
|
||||
case model.CoordinateTypeMars:
|
||||
return marsLng, marsLat, nil
|
||||
case model.CoordinateTypeBaiDu:
|
||||
coordSys = autonavi.CoordSysBaidu
|
||||
case model.CoordinateTypeMapbar:
|
||||
coordSys = autonavi.CoordSysMapbar
|
||||
default:
|
||||
panic(fmt.Sprintf("known coordinate type:%d", coordinateType))
|
||||
}
|
||||
return api.AutonaviAPI.CoordinateConvert(marsLng, marsLat, coordSys)
|
||||
}
|
||||
|
||||
func IntPrice2Standard(value int64) float64 {
|
||||
return float64(value) / 100
|
||||
}
|
||||
@@ -218,7 +420,7 @@ func ComposeSkuNameOriginal(prefix, name, comment, unit string, spec_quality flo
|
||||
return skuName
|
||||
}
|
||||
|
||||
func ComposeSkuNameForJds(prefix, name, comment, unit string, spec_quality float32, spec_unit string, maxLen int) (skuName string) {
|
||||
func ComposeSkuNameForMTWM(prefix, name, comment, unit string, spec_quality float32, spec_unit string, maxLen int) (skuName string) {
|
||||
strBuilder := &strings.Builder{}
|
||||
if prefix != "" {
|
||||
strBuilder.WriteString("[")
|
||||
@@ -227,15 +429,16 @@ func ComposeSkuNameForJds(prefix, name, comment, unit string, spec_quality float
|
||||
}
|
||||
skuName += name
|
||||
strBuilder.WriteString(name)
|
||||
if comment != "" {
|
||||
strBuilder.WriteString(" ")
|
||||
strBuilder.WriteString(comment)
|
||||
}
|
||||
if unit == "份" {
|
||||
strBuilder.WriteString("约")
|
||||
}
|
||||
strBuilder.WriteString("约")
|
||||
if unit != "" {
|
||||
strBuilder.WriteString(ComposeSkuSpec(spec_quality, spec_unit))
|
||||
strBuilder.WriteString("/")
|
||||
strBuilder.WriteString(unit)
|
||||
}
|
||||
if comment != "" {
|
||||
strBuilder.WriteString("(")
|
||||
strBuilder.WriteString(comment)
|
||||
strBuilder.WriteString(")")
|
||||
}
|
||||
skuName = strBuilder.String()
|
||||
if maxLen > 0 {
|
||||
@@ -254,10 +457,12 @@ func ComposeSkuName(prefix, name, comment, unit string, spec_quality float32, sp
|
||||
return skuName
|
||||
}
|
||||
|
||||
func ComposeSkuNameSync(prefix, name, comment, unit string, spec_quality float32, spec_unit string, maxLen int, exPrefix string, exPrefixBegin, exPrefixEnd *time.Time) (skuName string) {
|
||||
func ComposeSkuNameSync(prefix, name, comment, unit string, spec_quality float32, spec_unit string, maxLen int, exPrefix string, exPrefixBegin, exPrefixEnd *time.Time, isEx bool) (skuName string) {
|
||||
if exPrefix != "" && exPrefixBegin != nil && exPrefixEnd != nil {
|
||||
if utils.Time2Date(time.Now().Add(6*time.Hour)).Sub(*exPrefixBegin) >= 0 && utils.Time2Date(time.Now()).Sub(*exPrefixEnd) <= 0 {
|
||||
skuName = exPrefix
|
||||
if isEx {
|
||||
skuName = exPrefix
|
||||
}
|
||||
}
|
||||
if utils.Time2Date(time.Now().Add(6*time.Hour)).Sub(*exPrefixEnd) > 0 {
|
||||
skuName = ""
|
||||
@@ -267,16 +472,18 @@ func ComposeSkuNameSync(prefix, name, comment, unit string, spec_quality float32
|
||||
return skuName
|
||||
}
|
||||
|
||||
func ComposeSkuNameSync2(prefix, name, comment, unit string, spec_quality float32, spec_unit string, maxLen int, exPrefix string, exPrefixBegin, exPrefixEnd *time.Time) (skuName string) {
|
||||
func ComposeSkuNameSync2(prefix, name, comment, unit string, spec_quality float32, spec_unit string, maxLen int, exPrefix string, exPrefixBegin, exPrefixEnd *time.Time, isEx bool) (skuName string) {
|
||||
if exPrefix != "" && exPrefixBegin != nil && exPrefixEnd != nil {
|
||||
if utils.Time2Date(time.Now().Add(6*time.Hour)).Sub(*exPrefixBegin) >= 0 && utils.Time2Date(time.Now()).Sub(*exPrefixEnd) <= 0 {
|
||||
skuName = exPrefix
|
||||
if isEx {
|
||||
skuName = exPrefix
|
||||
}
|
||||
}
|
||||
if utils.Time2Date(time.Now().Add(6*time.Hour)).Sub(*exPrefixEnd) > 0 {
|
||||
skuName = ""
|
||||
}
|
||||
}
|
||||
skuName += ComposeSkuNameForJds(prefix, name, comment, unit, spec_quality, spec_unit, maxLen)
|
||||
skuName += ComposeSkuNameForMTWM(prefix, name, comment, unit, spec_quality, spec_unit, maxLen)
|
||||
return skuName
|
||||
}
|
||||
|
||||
@@ -377,7 +584,7 @@ func MakeValidationMapFromSlice(validValues []string, flag int) map[string]int {
|
||||
}
|
||||
|
||||
func ComposeQiniuResURL(key string) string {
|
||||
return "https://image.jxc4.com/" + key
|
||||
return "http://image.jxc4.com/" + key
|
||||
}
|
||||
|
||||
func IsLegalMobileNumber(num int64) bool {
|
||||
@@ -417,6 +624,151 @@ func Strings2Objs(strAndObjAddPairs ...interface{}) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
func RefreshOrderSkuRelated(order *model.GoodsOrder) *model.GoodsOrder {
|
||||
order.SkuCount = 0
|
||||
order.GoodsCount = 0
|
||||
order.SalePrice = 0
|
||||
order.VendorPrice = 0
|
||||
order.ShopPrice = 0
|
||||
order.Weight = 0
|
||||
order.EarningPrice = 0
|
||||
for _, sku := range order.Skus {
|
||||
if sku.SkuID > math.MaxInt32 {
|
||||
sku.SkuID = sku.JxSkuID
|
||||
}
|
||||
sku.OrderCreatedAt = order.OrderCreatedAt
|
||||
sku.VendorID = order.VendorID
|
||||
sku.VendorOrderID = order.VendorOrderID
|
||||
order.SkuCount++
|
||||
order.GoodsCount += sku.Count
|
||||
order.SalePrice += sku.SalePrice * int64(sku.Count)
|
||||
order.VendorPrice += sku.VendorPrice * int64(sku.Count)
|
||||
order.ShopPrice += sku.ShopPrice * int64(sku.Count)
|
||||
order.EarningPrice += sku.EarningPrice * int64(sku.Count)
|
||||
order.Weight += sku.Weight * sku.Count
|
||||
}
|
||||
return order
|
||||
}
|
||||
|
||||
func RefreshOrderEarningPrice2(order *model.GoodsOrder, payPercentage int) *model.GoodsOrder {
|
||||
if order.EarningType == model.EarningTypePoints {
|
||||
// if order.VendorID == model.VendorIDJDShop || order.VendorID == model.VendorIDJX {
|
||||
// order.NewEarningPrice = order.TotalShopMoney * int64((100 - payPercentage)) / 100
|
||||
// } else {
|
||||
order.NewEarningPrice = order.TotalShopMoney * int64((100 - payPercentage/2)) / 100
|
||||
// }
|
||||
} else {
|
||||
order.NewEarningPrice = order.EarningPrice
|
||||
}
|
||||
return order
|
||||
}
|
||||
|
||||
func RefreshOrderEarningPrice3(order *model.GoodsOrder, payPercentage int, bill *model.Waybill) *model.GoodsOrder {
|
||||
if order.EarningType == model.EarningTypePoints {
|
||||
// if order.VendorID == model.VendorIDJDShop || order.VendorID == model.VendorIDJX {
|
||||
// order.NewEarningPrice = (order.TotalShopMoney - bill.DesiredFee) * int64((100 - payPercentage)) / 100
|
||||
// } else {
|
||||
order.NewEarningPrice = order.TotalShopMoney*int64((100-payPercentage/2))/100 - bill.DesiredFee
|
||||
// }
|
||||
} else {
|
||||
order.NewEarningPrice = order.EarningPrice
|
||||
}
|
||||
return order
|
||||
}
|
||||
|
||||
func RefreshAfsOrderSkuRelated(afsOrder *model.AfsOrder) *model.AfsOrder {
|
||||
afsOrder.SkuUserMoney = 0
|
||||
afsOrder.PmSkuSubsidyMoney = 0
|
||||
for _, orderSku := range afsOrder.Skus {
|
||||
if orderSku.SkuID > math.MaxInt32 {
|
||||
orderSku.SkuID = orderSku.JxSkuID
|
||||
}
|
||||
afsOrder.SkuUserMoney += orderSku.UserMoney
|
||||
afsOrder.PmSkuSubsidyMoney += orderSku.PmSkuSubsidyMoney
|
||||
}
|
||||
return afsOrder
|
||||
}
|
||||
|
||||
func RemoveSkuFromOrder(order *model.GoodsOrder, removedSkuList []*model.OrderSku) *model.GoodsOrder {
|
||||
removedSkuMap := make(map[int]*model.OrderSku)
|
||||
removedSkuMap2 := make(map[string]*model.OrderSku)
|
||||
for _, sku := range removedSkuList {
|
||||
if skuID := GetSkuIDFromOrderSku(sku); skuID > 0 {
|
||||
if removedSkuMap[skuID] == nil {
|
||||
removedSkuMap[skuID] = sku
|
||||
} else {
|
||||
removedSkuMap[skuID].Count += sku.Count
|
||||
}
|
||||
}
|
||||
if vendorSkuID := sku.VendorSkuID; vendorSkuID != "" {
|
||||
if removedSkuMap2[vendorSkuID] == nil {
|
||||
removedSkuMap2[vendorSkuID] = sku
|
||||
} else {
|
||||
removedSkuMap2[vendorSkuID].Count += sku.Count
|
||||
}
|
||||
}
|
||||
}
|
||||
var skuList []*model.OrderSku
|
||||
sort.Sort(sort.Reverse(OrderSkuList(order.Skus)))
|
||||
for _, sku := range order.Skus {
|
||||
var removedSku *model.OrderSku
|
||||
if skuID := GetSkuIDFromOrderSku(sku); skuID > 0 {
|
||||
removedSku = removedSkuMap[skuID]
|
||||
}
|
||||
if removedSku == nil {
|
||||
if vendorSkuID := sku.VendorSkuID; vendorSkuID != "" {
|
||||
removedSku = removedSkuMap2[vendorSkuID]
|
||||
}
|
||||
}
|
||||
copiedSku := *sku
|
||||
tmp := &copiedSku
|
||||
if removedSku != nil {
|
||||
if removedSku.Count >= sku.Count {
|
||||
tmp = nil
|
||||
removedSku.Count -= sku.Count
|
||||
} else {
|
||||
tmp.Count -= removedSku.Count
|
||||
removedSku.Count = 0
|
||||
}
|
||||
}
|
||||
if tmp != nil {
|
||||
skuList = append(skuList, tmp)
|
||||
}
|
||||
}
|
||||
order.Skus = skuList
|
||||
return RefreshOrderSkuRelated(order)
|
||||
}
|
||||
|
||||
func UploadExportContent(content []byte, key string) (downloadURL string, err error) {
|
||||
putPolicy := storage.PutPolicy{
|
||||
Scope: globals.QiniuBucket,
|
||||
Expires: 10 * 60,
|
||||
DeleteAfterDays: 1,
|
||||
}
|
||||
upToken := putPolicy.UploadToken(api.QiniuAPI)
|
||||
cfg := &storage.Config{}
|
||||
formUploader := storage.NewFormUploader(cfg)
|
||||
ret := storage.PutRet{}
|
||||
for i := 0; i < 3; i++ {
|
||||
if err = formUploader.Put(context.Background(), &ret, upToken, key, bytes.NewReader(content), int64(len(content)), &storage.PutExtra{}); err == nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
if err == nil {
|
||||
downloadURL = ComposeQiniuResURL(key)
|
||||
}
|
||||
return downloadURL, err
|
||||
}
|
||||
|
||||
func UploadExeclAndPushMsg(sheetList []*excel.Obj2ExcelSheetConfig, name string) (downloadURL, fileName string, err error) {
|
||||
excelBin := excel.Obj2Excel(sheetList)
|
||||
timeStr := utils.Int64ToStr(time.Now().Unix())
|
||||
fileName = name + timeStr + fileExt
|
||||
baseapi.SugarLogger.Debugf("WriteToExcel:save %s success", fileName)
|
||||
downloadURL, err = UploadExportContent(excelBin, fileName)
|
||||
return downloadURL, fileName, err
|
||||
}
|
||||
|
||||
func TaskResult2Hint(resultList []interface{}) (hint string) {
|
||||
strList := make([]string, len(resultList))
|
||||
for k, v := range resultList {
|
||||
@@ -474,15 +826,42 @@ func OperationTime2Str2(openTime, closeTime int16) (str string) {
|
||||
return str
|
||||
}
|
||||
|
||||
func OperationTimeStr4VendorStore(v *model.VendorStoreSnapshot) (str string) {
|
||||
str = fmt.Sprintf("%s", OperationTime2Str2(v.OpenTime1, v.CloseTime1))
|
||||
if v.OpenTime2 > 0 && v.CloseTime2 > 0 {
|
||||
str += fmt.Sprintf(",%s", OperationTime2Str2(v.OpenTime2, v.CloseTime2))
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
func OperationTime2HourMinuteFormat(time time.Time) (i int16) {
|
||||
return int16(time.Hour()*100 + time.Minute())
|
||||
}
|
||||
|
||||
// 得到饿百订单的取货码
|
||||
func GetEbaiOrderGetCode(order *model.GoodsOrder) (getCode string) {
|
||||
if order.VendorID == model.VendorIDEBAI && len(order.VendorOrderID2) >= 4 {
|
||||
getCode = order.VendorOrderID2[len(order.VendorOrderID2)-4:]
|
||||
}
|
||||
return getCode
|
||||
}
|
||||
|
||||
func WriteFile(fileName string, binData []byte) error {
|
||||
err := ioutil.WriteFile(fileName, binData, 0666)
|
||||
return err
|
||||
}
|
||||
|
||||
func GetRealMobile4Order(order *model.GoodsOrder) (mobileNumber string) {
|
||||
mobileNumber = order.ConsigneeMobile2
|
||||
if mobileNumber == "" {
|
||||
mobileNumber = order.ConsigneeMobile
|
||||
}
|
||||
if !IsStringLikeMobile(mobileNumber) {
|
||||
mobileNumber = ""
|
||||
}
|
||||
return mobileNumber
|
||||
}
|
||||
|
||||
func GuessDataResourceVendor(resourceURL string) (vendorID int) {
|
||||
vendorID = -1
|
||||
for tmpVendorID, urlList := range resourceTypeMap {
|
||||
@@ -545,23 +924,30 @@ func GetOneEmailFromStr(str string) (email string) {
|
||||
|
||||
// 计算一个坐标点距离一个门店的距离,单位为米,如果不在有效范围内,则返回0
|
||||
func Point2StoreDistance(lng, lat float64, intStoreLng, intStoreLat int, deliveryRangeType int8, deliveryRange string) (distance int) {
|
||||
// storeLng := IntCoordinate2Standard(intStoreLng)
|
||||
// storeLat := IntCoordinate2Standard(intStoreLat)
|
||||
// if deliveryRangeType == model.DeliveryRangeTypeRadius {
|
||||
// maxDistance := int(utils.Str2Int64WithDefault(deliveryRange, 0))
|
||||
// distance = int(EarthDistance(lng, lat, storeLng, storeLat) * 1000)
|
||||
// if distance > maxDistance {
|
||||
// distance = 0
|
||||
// }
|
||||
// } else {
|
||||
// points := CoordinateStr2Points(deliveryRange)
|
||||
// if utils.IsPointInPolygon(lng, lat, points) {
|
||||
// distance = int(EarthDistance(lng, lat, storeLng, storeLat) * 1000)
|
||||
// }
|
||||
// }
|
||||
storeLng := IntCoordinate2Standard(intStoreLng)
|
||||
storeLat := IntCoordinate2Standard(intStoreLat)
|
||||
if deliveryRangeType == model.DeliveryRangeTypeRadius {
|
||||
maxDistance := int(utils.Str2Int64WithDefault(deliveryRange, 0))
|
||||
distance = int(EarthDistance(lng, lat, storeLng, storeLat) * 1000)
|
||||
if distance > maxDistance {
|
||||
distance = 0
|
||||
}
|
||||
} else {
|
||||
points := CoordinateStr2Points(deliveryRange)
|
||||
if utils.IsPointInPolygon(lng, lat, points) {
|
||||
distance = int(EarthDistance(lng, lat, storeLng, storeLat) * 1000)
|
||||
}
|
||||
}
|
||||
return distance
|
||||
}
|
||||
|
||||
func TranslateStorePriceType(storePriceType int8) int8 {
|
||||
if storePriceType == model.StoreChangePriceTypeManagedStore {
|
||||
storePriceType = model.StoreChangePriceTypeBossDisabled
|
||||
}
|
||||
return storePriceType
|
||||
}
|
||||
|
||||
func TranslateSoundSize(vendorID, soundPercentage int) (soundSize string) {
|
||||
if vendorID == model.VendorIDYiLianYun || vendorID == model.VendorIDFeiE {
|
||||
if soundPercentage == 0 {
|
||||
@@ -576,6 +962,25 @@ func TranslateSoundSize(vendorID, soundPercentage int) (soundSize string) {
|
||||
if soundPercentage > 66 && soundPercentage <= 100 {
|
||||
soundSize = "3"
|
||||
}
|
||||
} else if vendorID == model.VendorIDJxprint {
|
||||
if soundPercentage == 0 {
|
||||
soundSize = "0"
|
||||
}
|
||||
if soundPercentage > 0 && soundPercentage <= 20 {
|
||||
soundSize = "1"
|
||||
}
|
||||
if soundPercentage > 20 && soundPercentage <= 40 {
|
||||
soundSize = "2"
|
||||
}
|
||||
if soundPercentage > 40 && soundPercentage <= 60 {
|
||||
soundSize = "3"
|
||||
}
|
||||
if soundPercentage > 60 && soundPercentage <= 80 {
|
||||
soundSize = "4"
|
||||
}
|
||||
if soundPercentage > 80 && soundPercentage <= 100 {
|
||||
soundSize = "5"
|
||||
}
|
||||
}
|
||||
return soundSize
|
||||
}
|
||||
@@ -612,9 +1017,43 @@ func PKCS5UnPadding(origData []byte) []byte {
|
||||
return origData[:(length - unpadding)]
|
||||
}
|
||||
|
||||
func GetIssue() (issue int) {
|
||||
year, month, _ := time.Now().Date()
|
||||
return year*100 + int(month)
|
||||
//合成水印图
|
||||
func MixWatermarkImg(imgWatermark, img string, exPrefixBegin, exPrefixEnd *time.Time) (imgMix string) {
|
||||
if exPrefixBegin != nil && exPrefixEnd != nil {
|
||||
if utils.Time2Date(time.Now().Add(6*time.Hour)).Sub(*exPrefixBegin) >= 0 && utils.Time2Date(time.Now()).Sub(*exPrefixEnd) <= 0 {
|
||||
baseURL := base64.URLEncoding.EncodeToString([]byte(imgWatermark))
|
||||
var imgUrl string
|
||||
if strings.Contains(img, "?") {
|
||||
imgUrl = img + "/imageView2/0/q/75|watermark/1/image/" + baseURL + "/dissolve/100/gravity/Center/dx/0/dy/0"
|
||||
} else {
|
||||
imgUrl = img + "?imageView2/0/q/75|watermark/1/image/" + baseURL + "/dissolve/100/gravity/Center/dx/0/dy/0"
|
||||
}
|
||||
if resBinary, _, err := DownloadFileByURL(imgUrl); err == nil {
|
||||
if downloadURL, err := UploadExportContent(resBinary, utils.Int64ToStr(time.Now().Unix())+img[strings.LastIndex(img, "/")+1:len(img)]); err == nil {
|
||||
if err == nil {
|
||||
return downloadURL
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if utils.Time2Date(time.Now().Add(6*time.Hour)).Sub(*exPrefixEnd) > 0 {
|
||||
return imgMix
|
||||
}
|
||||
}
|
||||
return imgMix
|
||||
}
|
||||
|
||||
func GetDefendPriceIssue() (issue int) {
|
||||
if time.Now().Hour() >= 22 {
|
||||
issue = utils.Str2Int(time.Now().AddDate(0, 0, 1).Format("20060102"))
|
||||
} else {
|
||||
issue = utils.Str2Int(time.Now().Format("20060102"))
|
||||
}
|
||||
return issue
|
||||
}
|
||||
|
||||
func GetLastDefendPriceIssue() (issue int) {
|
||||
return utils.Str2Int(time.Now().AddDate(0, 0, 1).Format("20060102"))
|
||||
}
|
||||
|
||||
//根据一堆坐标求面积
|
||||
@@ -647,74 +1086,16 @@ func polarTriangleArea(tan1, lng1, tan2, lng2 float64) (s float64) {
|
||||
return 2 * math.Atan2(t*math.Sin(deltaLng), 1+t*math.Cos(deltaLng))
|
||||
}
|
||||
|
||||
func GenRandomString(l int) string {
|
||||
str := "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
bytes := []byte(str)
|
||||
result := []byte{}
|
||||
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
for i := 0; i < l; i++ {
|
||||
result = append(result, bytes[r.Intn(len(bytes))])
|
||||
func IntListToStrList(i []int) (s []string) {
|
||||
for _, v := range i {
|
||||
s = append(s, utils.Int2Str(v))
|
||||
}
|
||||
return string(result)
|
||||
return s
|
||||
}
|
||||
|
||||
func GetDayTime() (dayTimeBegin, dayTimeEnd time.Time) {
|
||||
dayTimeBegin = utils.Str2Time(utils.Time2Str(utils.Time2Date(time.Now())) + " 00:00:00")
|
||||
dayTimeEnd = dayTimeBegin.AddDate(0, 0, 1)
|
||||
return dayTimeBegin, dayTimeEnd
|
||||
}
|
||||
|
||||
func GetWeekTime() (weekTimeBegin, weekTimeEnd time.Time) {
|
||||
offset := int(time.Now().Weekday() - 1)
|
||||
if offset == -1 {
|
||||
offset = -6
|
||||
func StrListToIntList(s []string) (i []int) {
|
||||
for _, v := range s {
|
||||
i = append(i, utils.Str2Int(v))
|
||||
}
|
||||
weekTimeBegin = time.Now().AddDate(0, 0, offset)
|
||||
weekTimeEnd = weekTimeBegin.AddDate(0, 0, 7)
|
||||
return weekTimeBegin, weekTimeEnd
|
||||
}
|
||||
|
||||
func BuildErr(errs []error) (err error) {
|
||||
var errStr = strings.Builder{}
|
||||
for _, v := range errs {
|
||||
errStr.WriteString(v.Error())
|
||||
}
|
||||
return fmt.Errorf(errStr.String())
|
||||
}
|
||||
|
||||
func SplitFlowAndUnit(flowStr string) (flow float64, unit string) {
|
||||
for _, v := range flowUnitMap {
|
||||
if strings.Contains(flowStr, v) {
|
||||
return utils.Str2Float64WithDefault(flowStr[:len(flowStr)-2], 0), flowStr[len(flowStr)-2:]
|
||||
}
|
||||
}
|
||||
return flow, unit
|
||||
}
|
||||
|
||||
func Flow2KB(flow float64, unit string) (flowKB float64) {
|
||||
if unit == "KB" {
|
||||
return flow
|
||||
} else if unit == "MB" {
|
||||
return flow * 1024
|
||||
} else if unit == "GB" {
|
||||
return flow * 1024 * 1024
|
||||
}
|
||||
return flowKB
|
||||
}
|
||||
|
||||
func FlowKB2Other(flowKB float64) (flow float64, unit string) {
|
||||
if flowKB < 1024 {
|
||||
return flowKB, "KB"
|
||||
} else {
|
||||
flowMB := math.Round(flowKB / float64(1024))
|
||||
if flowMB < 1024 {
|
||||
return flowMB, "MB"
|
||||
} else {
|
||||
flowGB := math.Round(flowMB / float64(1024))
|
||||
if flowGB < 1024 {
|
||||
return flowGB, "GB"
|
||||
}
|
||||
}
|
||||
}
|
||||
return flow, unit
|
||||
return i
|
||||
}
|
||||
|
||||
41
business/jxutils/jxutils_act.go
Normal file
41
business/jxutils/jxutils_act.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package jxutils
|
||||
|
||||
import (
|
||||
"git.rosy.net.cn/jx-callback/business/model"
|
||||
)
|
||||
|
||||
type ActStoreSkuMap struct {
|
||||
actStoreSkuMap map[int64]map[int]*model.ActStoreSku2
|
||||
}
|
||||
|
||||
// isActPrice为true表示是活动,false表示是结算
|
||||
func NewActStoreSkuMap(actStoreSkuList []*model.ActStoreSku2, isActPrice bool) (actMap *ActStoreSkuMap) {
|
||||
actMap = &ActStoreSkuMap{}
|
||||
actStoreSkuMap := make(map[int64]map[int]*model.ActStoreSku2)
|
||||
for _, v := range actStoreSkuList {
|
||||
index := Combine2Int(v.StoreID, v.SkuID)
|
||||
if actStoreSkuMap[index] == nil {
|
||||
actStoreSkuMap[index] = make(map[int]*model.ActStoreSku2)
|
||||
}
|
||||
if (isActPrice && v.ActualActPrice > 0 && (actStoreSkuMap[index][v.VendorID] == nil || actStoreSkuMap[index][v.VendorID].ActualActPrice > v.ActualActPrice)) ||
|
||||
(!isActPrice && v.EarningPrice > 0 && (actStoreSkuMap[index][v.VendorID] == nil || actStoreSkuMap[index][v.VendorID].EarningPrice > v.EarningPrice)) {
|
||||
actStoreSkuMap[index][v.VendorID] = v
|
||||
}
|
||||
}
|
||||
actMap.actStoreSkuMap = actStoreSkuMap
|
||||
return actMap
|
||||
}
|
||||
|
||||
func (a *ActStoreSkuMap) GetActStoreSku(storeID, skuID, vendorID int) (storeSku *model.ActStoreSku2) {
|
||||
index := Combine2Int(storeID, skuID)
|
||||
if a.actStoreSkuMap[index] != nil {
|
||||
if vendorID < 0 {
|
||||
for k := range a.actStoreSkuMap[index] {
|
||||
vendorID = k
|
||||
break
|
||||
}
|
||||
}
|
||||
storeSku = a.actStoreSkuMap[index][vendorID]
|
||||
}
|
||||
return storeSku
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
beego "github.com/astaxie/beego/server/web"
|
||||
"image"
|
||||
"image/png"
|
||||
"io/ioutil"
|
||||
@@ -20,6 +21,7 @@ import (
|
||||
"git.rosy.net.cn/baseapi/platformapi"
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
"git.rosy.net.cn/jx-callback/business/model"
|
||||
"git.rosy.net.cn/jx-callback/globals"
|
||||
"github.com/boombuler/barcode"
|
||||
"github.com/boombuler/barcode/code128"
|
||||
"github.com/boombuler/barcode/qr"
|
||||
@@ -96,6 +98,20 @@ func SplitStoreName(fullName, separator, defaultPrefix string) (prefix, bareName
|
||||
return TrimDecorationChar(prefix), TrimDecorationChar(bareName)
|
||||
}
|
||||
|
||||
func ComposeStoreName(bareName string, vendorID int) (fullName string) {
|
||||
bareName = TrimDecorationChar(strings.Trim(bareName, "-"))
|
||||
storeName := globals.StoreName
|
||||
if vendorID == model.VendorIDJD {
|
||||
fullName = storeName + "-" + bareName
|
||||
} else {
|
||||
if globals.IsMainProductEnv() && model.ShopChineseNames[vendorID] != "" {
|
||||
storeName = model.ShopChineseNames[vendorID]
|
||||
}
|
||||
fullName = storeName + "(" + bareName + ")"
|
||||
}
|
||||
return fullName
|
||||
}
|
||||
|
||||
func StrTime2JxOperationTime(strTime string, defValue int16) int16 {
|
||||
if timeValue, err := time.Parse("15:04:05", strTime); err == nil {
|
||||
return int16(timeValue.Hour()*100 + timeValue.Minute())
|
||||
@@ -201,6 +217,42 @@ func RegularizeSkuQuality(specQuality float32, specUnit string) (g int) {
|
||||
return int(specQuality)
|
||||
}
|
||||
|
||||
// 计算SKU价格,unitPrice为一斤的单价,specQuality为质量,单位为克
|
||||
func CaculateSkuPrice(unitPrice int, specQuality float32, specUnit string, skuNameUnit string) int {
|
||||
if skuNameUnit != model.SpecialUnit {
|
||||
return unitPrice
|
||||
}
|
||||
specQuality2 := RegularizeSkuQuality(specQuality, specUnit)
|
||||
floatPrice := float64(unitPrice) * float64(specQuality2) / float64(model.SpecialSpecQuality)
|
||||
// if specQuality2 < 250 {
|
||||
// floatPrice = floatPrice * 110 / 100
|
||||
// } else if specQuality2 < 500 {
|
||||
// floatPrice = floatPrice * 105 / 100
|
||||
// }
|
||||
if floatPrice <= 1 {
|
||||
floatPrice = 1
|
||||
}
|
||||
return int(math.Round(floatPrice))
|
||||
}
|
||||
|
||||
// 计算SKU标准价格,CaculateSkuPrice的逆过程
|
||||
func CaculateUnitPrice(skuPrice int, specQuality float32, specUnit string, skuNameUnit string) (unitPrice int) {
|
||||
if skuNameUnit != model.SpecialUnit {
|
||||
return skuPrice
|
||||
}
|
||||
specQuality2 := RegularizeSkuQuality(specQuality, specUnit)
|
||||
unitPrice = skuPrice * model.SpecialSpecQuality / specQuality2
|
||||
// if specQuality2 < 250 {
|
||||
// unitPrice = unitPrice * 100 / 110
|
||||
// } else if specQuality2 < 500 {
|
||||
// unitPrice = unitPrice * 100 / 105
|
||||
// }
|
||||
if unitPrice <= 1 {
|
||||
unitPrice = 1
|
||||
}
|
||||
return unitPrice
|
||||
}
|
||||
|
||||
func ConstrainPricePercentage(percentage int) int {
|
||||
if percentage < model.MinVendorPricePercentage || percentage > model.MaxVendorPricePercentage {
|
||||
percentage = model.DefVendorPricePercentage
|
||||
@@ -236,6 +288,65 @@ func CaculateSkuPriceFromVendor(vendorPrice, percentage, priceAdd int) (price in
|
||||
return price
|
||||
}
|
||||
|
||||
func GetPricePercentage(l model.PricePercentagePack, price int, defPricePercentage int) (pricePercentage, priceAdd int) {
|
||||
pricePercentage = defPricePercentage
|
||||
itemLen := len(l)
|
||||
if itemLen > 0 {
|
||||
low := 0
|
||||
high := itemLen - 1
|
||||
mid := 0
|
||||
for low <= high {
|
||||
mid = low + (high-low)/2
|
||||
if mid < 0 || mid >= itemLen-1 {
|
||||
break
|
||||
}
|
||||
if price >= l[mid].BeginPrice {
|
||||
if price < l[mid+1].BeginPrice {
|
||||
break
|
||||
} else {
|
||||
low = mid + 1
|
||||
}
|
||||
} else {
|
||||
high = mid - 1
|
||||
}
|
||||
}
|
||||
if mid >= 0 && mid <= itemLen-1 && low <= high {
|
||||
pricePercentage = l[mid].PricePercentage
|
||||
priceAdd = l[mid].PriceAdd
|
||||
}
|
||||
}
|
||||
return pricePercentage, priceAdd
|
||||
}
|
||||
|
||||
func GetPricePercentageByVendorPrice(l model.PricePercentagePack, vendorPrice int, defPricePercentage int) (pricePercentage, priceAdd int) {
|
||||
pricePercentage = defPricePercentage
|
||||
if len(l) > 0 {
|
||||
var lastItem *model.PricePercentageItem
|
||||
for _, v := range l {
|
||||
if CaculateSkuVendorPrice(v.BeginPrice, v.PricePercentage, v.PriceAdd) > vendorPrice {
|
||||
break
|
||||
}
|
||||
lastItem = v
|
||||
}
|
||||
if lastItem != nil {
|
||||
pricePercentage = lastItem.PricePercentage
|
||||
priceAdd = lastItem.PriceAdd
|
||||
}
|
||||
}
|
||||
return pricePercentage, priceAdd
|
||||
}
|
||||
|
||||
func CaculatePriceByPricePack(l model.PricePercentagePack, defPricePercentage, price int) (outPrice int) {
|
||||
pricePercentage, priceAdd := GetPricePercentage(l, price, defPricePercentage)
|
||||
return CaculateSkuVendorPrice(price, pricePercentage, priceAdd)
|
||||
}
|
||||
|
||||
func CaculateJxPriceByPricePack(l model.PricePercentagePack, defPricePercentage, vendorPrice int) (jxPrice int) {
|
||||
pricePercentage, priceAdd := GetPricePercentageByVendorPrice(l, vendorPrice, defPricePercentage)
|
||||
jxPrice = CaculateSkuPriceFromVendor(vendorPrice, pricePercentage, priceAdd)
|
||||
return jxPrice
|
||||
}
|
||||
|
||||
func ConstrainPayPercentage(payPerCentage int) int {
|
||||
if payPerCentage <= 50 {
|
||||
payPerCentage = 70
|
||||
@@ -243,6 +354,10 @@ func ConstrainPayPercentage(payPerCentage int) int {
|
||||
return payPerCentage
|
||||
}
|
||||
|
||||
func IsSkuSpecial(specQuality float32, specUnit string) bool {
|
||||
return int(specQuality) == model.SpecialSpecQuality && (specUnit == model.SpecialSpecUnit || specUnit == model.SpecialSpecUnit2)
|
||||
}
|
||||
|
||||
var lastFakeID int64
|
||||
var lastFakeIDMutex sync.RWMutex
|
||||
|
||||
@@ -326,6 +441,28 @@ func FormatSkuWeight(specQuality float32, specUnit string) int {
|
||||
return RegularizeSkuQuality(specQuality, specUnit)
|
||||
}
|
||||
|
||||
type SkuList []*model.Sku
|
||||
|
||||
func (s SkuList) Len() int {
|
||||
return len(s)
|
||||
}
|
||||
|
||||
func (s SkuList) Less(i, j int) bool {
|
||||
if s[i].NameID == s[j].NameID {
|
||||
if s[i].SpecUnit == s[j].SpecUnit {
|
||||
return s[i].SpecQuality < s[j].SpecQuality
|
||||
}
|
||||
return s[i].SpecUnit < s[j].SpecUnit
|
||||
}
|
||||
return s[i].NameID < s[j].NameID
|
||||
}
|
||||
|
||||
func (s SkuList) Swap(i, j int) {
|
||||
tmp := s[i]
|
||||
s[i] = s[j]
|
||||
s[j] = tmp
|
||||
}
|
||||
|
||||
func DownloadFileByURL(fileURL string) (bodyData []byte, fileMD5 string, err error) {
|
||||
response, err := http.Get(fileURL)
|
||||
if err == nil {
|
||||
@@ -367,9 +504,19 @@ func GetVendorName(vendorID int) (vendorName string) {
|
||||
}
|
||||
|
||||
func CaculateSkuEarningPrice(shopPrice, salePrice int64, storePayPercentage int) (earningPrice int64) {
|
||||
earningPrice = salePrice
|
||||
if salePrice != 0 {
|
||||
if shopPrice > 0 && shopPrice < earningPrice {
|
||||
//TODO 2021-07-05 16:50菜市和果园一样,取低的
|
||||
//TODO 2021-07-05 18:48 还是改回来
|
||||
if beego.BConfig.RunMode == "jxgy" {
|
||||
earningPrice = salePrice
|
||||
if salePrice != 0 {
|
||||
if shopPrice > 0 && shopPrice < earningPrice {
|
||||
earningPrice = shopPrice
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if shopPrice == 0 {
|
||||
earningPrice = salePrice * 70 / 100
|
||||
} else {
|
||||
earningPrice = shopPrice
|
||||
}
|
||||
}
|
||||
|
||||
238
business/jxutils/netprinter/netprinter.go
Normal file
238
business/jxutils/netprinter/netprinter.go
Normal file
@@ -0,0 +1,238 @@
|
||||
package netprinter
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/tasksch"
|
||||
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
"git.rosy.net.cn/jx-callback/business/partner"
|
||||
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
|
||||
"git.rosy.net.cn/jx-callback/business/model"
|
||||
"git.rosy.net.cn/jx-callback/business/model/dao"
|
||||
"git.rosy.net.cn/jx-callback/globals"
|
||||
)
|
||||
|
||||
const (
|
||||
testVendorOrderID = "test"
|
||||
realTestVendorOrderID = "901234567890123"
|
||||
realTestOrderVendorID = model.VendorIDJD
|
||||
)
|
||||
|
||||
const (
|
||||
PrinterNotifyUserApplyCancel = 1
|
||||
PrinterNotifyNewAfsOrder = 2
|
||||
)
|
||||
|
||||
func PrintOrder(ctx *jxcontext.Context, vendorOrderID string, vendorID int) (printResult *partner.PrinterStatus, err error) {
|
||||
storeID := vendorID
|
||||
if vendorOrderID == testVendorOrderID {
|
||||
vendorOrderID = realTestVendorOrderID
|
||||
vendorID = realTestOrderVendorID
|
||||
}
|
||||
order, err := partner.CurOrderManager.LoadOrder(vendorOrderID, vendorID)
|
||||
if err == nil {
|
||||
if vendorOrderID == realTestVendorOrderID {
|
||||
order.StoreID = storeID
|
||||
order.JxStoreID = storeID
|
||||
}
|
||||
printResult, err = PrintOrderByOrder(ctx, order)
|
||||
}
|
||||
return printResult, err
|
||||
}
|
||||
|
||||
func PrintOrderByOrder(ctx *jxcontext.Context, order *model.GoodsOrder) (printResult *partner.PrinterStatus, err error) {
|
||||
return PrintOrderByOrder4Store(ctx, order, jxutils.GetSaleStoreIDFromOrder(order))
|
||||
}
|
||||
|
||||
func getStore4Print(db *dao.DaoDB, storeID int) (store *model.Store, err error) {
|
||||
for i := 0; i < 3; i++ {
|
||||
store2 := &model.Store{}
|
||||
store2.ID = storeID
|
||||
if err = dao.GetEntity(db, store2); err == nil {
|
||||
store = store2
|
||||
if store.LinkStoreID != 0 {
|
||||
storeID = store.LinkStoreID
|
||||
} else {
|
||||
break
|
||||
}
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
if store != nil {
|
||||
err = nil
|
||||
}
|
||||
return store, err
|
||||
}
|
||||
|
||||
func PrintOrderByOrder4Store(ctx *jxcontext.Context, order *model.GoodsOrder, storeID int) (printResult *partner.PrinterStatus, err error) {
|
||||
globals.SugarLogger.Debugf("PrintOrderByOrder4Store orderID:%s", order.VendorOrderID)
|
||||
db := dao.GetDB()
|
||||
store, err := getStore4Print(db, storeID)
|
||||
if err == nil {
|
||||
handler, err := GetHandlerFromStore(store)
|
||||
if err != nil {
|
||||
return &partner.PrinterStatus{
|
||||
PrintResult: partner.PrintResultNoPrinter,
|
||||
}, nil
|
||||
}
|
||||
storeDetail, _ := dao.GetStoreDetail(db, storeID, order.VendorID, order.VendorOrgCode)
|
||||
if storeDetail == nil && order.VendorOrderID == testVendorOrderID {
|
||||
storeDetail = &dao.StoreDetail{
|
||||
BrandName: testVendorOrderID,
|
||||
BrandIsPrint: model.NO,
|
||||
}
|
||||
}
|
||||
printResult, err = handler.PrintOrder(ctx, store, storeDetail, order)
|
||||
if err == nil {
|
||||
dao.SetOrderPrintFlag(db, ctx.GetUserName(), order.VendorOrderID, order.VendorID, true)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
globals.SugarLogger.Infof("PrintOrderByOrder4Store orderID:%s failed with error:%v", order.VendorOrderID, err)
|
||||
}
|
||||
return printResult, err
|
||||
}
|
||||
|
||||
func GetNetPrinterStatus(ctx *jxcontext.Context, storeID int) (printResult *partner.PrinterStatus, err error) {
|
||||
store := &model.Store{}
|
||||
store.ID = storeID
|
||||
db := dao.GetDB()
|
||||
if err = dao.GetEntity(db, store); err == nil {
|
||||
return getNetPrinterStatus(ctx, store)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func getNetPrinterStatus(ctx *jxcontext.Context, store *model.Store) (printResult *partner.PrinterStatus, err error) {
|
||||
handler, err := GetHandlerFromStore(store)
|
||||
if err != nil {
|
||||
return &partner.PrinterStatus{
|
||||
PrintResult: partner.PrintResultNoPrinter,
|
||||
}, nil
|
||||
}
|
||||
return handler.GetPrinterStatus(ctx, store.PrinterSN, store.PrinterKey)
|
||||
}
|
||||
|
||||
func GetHandlerFromStore(store *model.Store) (printerHandler partner.IPrinterHandler, err error) {
|
||||
if store.IsPrinterDisabled() {
|
||||
return nil, fmt.Errorf("门店%s没有启用网络打印机", store.Name)
|
||||
}
|
||||
if printerHandler = partner.GetPrinterPlatformFromVendorID(store.PrinterVendorID); printerHandler == nil {
|
||||
return nil, fmt.Errorf("门店%s没有配置网络打印机", store.Name)
|
||||
}
|
||||
return printerHandler, nil
|
||||
}
|
||||
|
||||
func BindPrinter(ctx *jxcontext.Context, storeID int, data string) (printResult *partner.PrinterStatus, err error) {
|
||||
store := &model.Store{}
|
||||
store.ID = storeID
|
||||
db := dao.GetDB()
|
||||
if err = dao.GetEntity(db, store); err == nil {
|
||||
var mapData map[string]interface{}
|
||||
if err = utils.UnmarshalUseNumber([]byte(data), &mapData); err == nil {
|
||||
printerVendorID := model.VendorIDUnknown
|
||||
if mapData["machineCode"] != nil {
|
||||
printerVendorID = model.VendorIDYiLianYun
|
||||
}
|
||||
if handler := partner.GetPrinterPlatformFromVendorID(printerVendorID); handler != nil {
|
||||
if store.PrinterVendorID > 0 && store.PrinterVendorID != printerVendorID {
|
||||
err = fmt.Errorf("门店:%d已经绑定了%s打印机,如果需要重新绑定,请联系运营先解绑", storeID, model.VendorChineseNames[store.PrinterVendorID])
|
||||
} else {
|
||||
bindResult, err2 := handler.BindPrinter(ctx, mapData)
|
||||
if err = err2; err == nil {
|
||||
store.PrinterVendorID = printerVendorID
|
||||
store.PrinterSN = bindResult.PrinterSN
|
||||
store.PrinterKey = bindResult.PrinterKey
|
||||
store.PrinterBindInfo = string(utils.MustMarshal(bindResult))
|
||||
if _, err = dao.UpdateEntity(db, store); err == nil {
|
||||
err = handler.EmptyPrintList(ctx, bindResult.PrinterSN, bindResult.PrinterKey)
|
||||
printResult, err = handler.GetPrinterStatus(ctx, bindResult.PrinterSN, bindResult.PrinterKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
err = fmt.Errorf("打印机类型:%d当前不支持", printerVendorID)
|
||||
}
|
||||
} else {
|
||||
err = fmt.Errorf("请扫描正确的二维码,如需要帮助,请联系运营!")
|
||||
}
|
||||
}
|
||||
return printResult, err
|
||||
}
|
||||
|
||||
func RebindAllPrinters(ctx *jxcontext.Context, isForce, isAsync bool) (hint string, err error) {
|
||||
storeList, err := dao.GetRebindPrinterStoreList(dao.GetDB())
|
||||
if err == nil {
|
||||
var needRebindList []*model.Store
|
||||
bindResultMap := make(map[int]*partner.BindPrinterResult)
|
||||
now := time.Now()
|
||||
for _, v := range storeList {
|
||||
var bindResult partner.BindPrinterResult
|
||||
if err = utils.UnmarshalUseNumber([]byte(v.PrinterBindInfo), &bindResult); err == nil {
|
||||
if isForce || now.Sub(utils.Timestamp2Time(bindResult.ExpiresAt)) > -3*24*time.Hour {
|
||||
needRebindList = append(needRebindList, v)
|
||||
bindResultMap[v.ID] = &bindResult
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
globals.SugarLogger.Debugf("RebindAllPrinters len(needRebindList):%d", len(needRebindList))
|
||||
if len(needRebindList) > 0 {
|
||||
db := dao.GetDB()
|
||||
task := tasksch.NewParallelTask("RebindAllPrinters", tasksch.NewParallelConfig().SetIsContinueWhenError(true).SetParallelCount(4), ctx,
|
||||
func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
|
||||
store := batchItemList[0].(*model.Store)
|
||||
if handler := partner.GetPrinterPlatformFromVendorID(store.PrinterVendorID); handler != nil {
|
||||
bindResult, err2 := handler.RebindPrinter(ctx, bindResultMap[store.ID])
|
||||
globals.SugarLogger.Debugf("RebindAllPrinters storeID:%d, result:%s, err:%v", store.ID, utils.Format4Output(bindResult, true), err2)
|
||||
if err = err2; err == nil {
|
||||
store.PrinterSN = bindResult.PrinterSN
|
||||
store.PrinterKey = bindResult.PrinterKey
|
||||
store.PrinterBindInfo = string(utils.MustMarshal(bindResult))
|
||||
if _, err = dao.UpdateEntity(db, store); err == nil {
|
||||
retVal = []int{1}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
globals.SugarLogger.Warnf("RebindAllPrinters strange PrinterVendorID:%d", store.PrinterVendorID)
|
||||
}
|
||||
return retVal, err
|
||||
}, needRebindList)
|
||||
|
||||
tasksch.ManageTask(task).Run()
|
||||
if !isAsync {
|
||||
hint = task.GetID()
|
||||
} else {
|
||||
result, err2 := task.GetResult(0)
|
||||
if err = err2; err == nil {
|
||||
hint = utils.Int2Str(len(result))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return hint, err
|
||||
}
|
||||
|
||||
func NofityOrderMsg(ctx *jxcontext.Context, storeID int, orderID, notifyMsg string) (err error) {
|
||||
if notifyMsg != "" {
|
||||
notifyMsg = strings.ReplaceAll(notifyMsg, ",", "")
|
||||
db := dao.GetDB()
|
||||
store := &model.Store{}
|
||||
store.ID = storeID
|
||||
if err = dao.GetEntity(db, store); err == nil {
|
||||
handler, err := GetHandlerFromStore(store)
|
||||
if err == nil {
|
||||
if globals.EnableStoreWrite {
|
||||
_, err = handler.PlayText(ctx, store.PrinterSN, store.PrinterKey, orderID, notifyMsg)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
@@ -8,6 +8,8 @@ import (
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
"git.rosy.net.cn/baseapi/utils/errlist"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils"
|
||||
"git.rosy.net.cn/jx-callback/business/model"
|
||||
"git.rosy.net.cn/jx-callback/business/model/dao"
|
||||
"git.rosy.net.cn/jx-callback/globals"
|
||||
"git.rosy.net.cn/jx-callback/globals/api"
|
||||
)
|
||||
@@ -21,7 +23,7 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
func SendSMSMsg(mobileList []string, signName, templateCode string, templateParam map[string]interface{}) (err error) {
|
||||
func SendSMSMsg(mobileList []string, signName, templateCode string, templateParam map[string]interface{}, order *model.GoodsOrder) (err error) {
|
||||
if len(mobileList) > 0 {
|
||||
errList := errlist.New()
|
||||
mobileList = jxutils.StringMap2List(jxutils.StringList2Map(mobileList))
|
||||
@@ -41,6 +43,11 @@ func SendSMSMsg(mobileList []string, signName, templateCode string, templatePara
|
||||
globals.SugarLogger.Infof(errMsg)
|
||||
}
|
||||
}
|
||||
// else {
|
||||
// if order != nil {
|
||||
// err = updateStoreSMSNotifyMark(order)
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -48,3 +55,174 @@ func SendSMSMsg(mobileList []string, signName, templateCode string, templatePara
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func getOrderNotifyPhone(order *model.GoodsOrder) (phoneList []string) {
|
||||
return dao.GetOrderNotifyPhones(dao.GetDB(), jxutils.GetSaleStoreIDFromOrder(order))
|
||||
}
|
||||
|
||||
func NotifyNewOrder(order *model.GoodsOrder) (err error) {
|
||||
if isPushSMS(order) {
|
||||
temp := ""
|
||||
var price int64
|
||||
store, _ := dao.GetStoreDetail(dao.GetDB(), jxutils.GetSaleStoreIDFromOrder(order), order.VendorID, order.VendorOrgCode)
|
||||
if store.VendorPayPercentage < 50 && store.VendorPayPercentage != 0 {
|
||||
temp = globals.SMSNewOrderTemplate
|
||||
price = order.ActualPayPrice
|
||||
} else if store.VendorPayPercentage > 50 {
|
||||
temp = globals.SMSNewOrderTemplateQ
|
||||
price = order.ShopPrice
|
||||
} else if store.VendorPayPercentage == 0 {
|
||||
if store.PayPercentage > 50 {
|
||||
temp = globals.SMSNewOrderTemplateQ
|
||||
price = order.ShopPrice
|
||||
} else {
|
||||
temp = globals.SMSNewOrderTemplate
|
||||
price = order.ActualPayPrice
|
||||
}
|
||||
}
|
||||
|
||||
err = SendSMSMsg(getOrderNotifyPhone(order), globals.SMSSignName, temp, map[string]interface{}{
|
||||
"daySeq": order.OrderSeq,
|
||||
"consigneeName": order.ConsigneeName,
|
||||
"payMoney": jxutils.IntPrice2StandardString(price),
|
||||
}, order)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func NotifyOrderCanceled(order *model.GoodsOrder) (err error) {
|
||||
err = SendSMSMsg(getOrderNotifyPhone(order), globals.SMSSignName, globals.SMSOrderCanceledTemplate, map[string]interface{}{
|
||||
"vendorName": model.VendorChineseNames[order.VendorID],
|
||||
"seq": order.OrderSeq,
|
||||
"orderID": order.VendorOrderID,
|
||||
}, order)
|
||||
return err
|
||||
}
|
||||
|
||||
func isPushSMS(order *model.GoodsOrder) bool {
|
||||
storeID := 0
|
||||
if order.StoreID == 0 {
|
||||
storeID = order.JxStoreID
|
||||
} else {
|
||||
storeID = order.StoreID
|
||||
}
|
||||
stores, _ := dao.GetStoresMapList(dao.GetDB(), []int{order.VendorID}, []int{storeID}, nil, model.StoreStatusAll, model.StoreIsSyncAll, "", "", "")
|
||||
if len(stores) > 0 {
|
||||
if stores[0].IsOrder == model.NO {
|
||||
if storeID == model.MatterStoreID || storeID == model.JdShopMainStoreID {
|
||||
return false
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func updateStoreSMSNotifyMark(order *model.GoodsOrder) (err error) {
|
||||
var db = dao.GetDB()
|
||||
stores, _ := dao.GetStoreList(db, []int{order.StoreID}, nil, nil, nil, nil, "")
|
||||
if len(stores) > 0 {
|
||||
stores[0].SMSNotifyMark = model.YES
|
||||
_, err = dao.UpdateEntity(db, stores[0], "SMSNotifyMark")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
//每月向用户发送
|
||||
func NotifyNewUserOrder(order *model.GoodsOrder) (err error) {
|
||||
var (
|
||||
db = dao.GetDB()
|
||||
storeTel string
|
||||
storeID int
|
||||
mobile string
|
||||
)
|
||||
if order.StoreID == 0 {
|
||||
storeID = order.JxStoreID
|
||||
} else {
|
||||
storeID = order.StoreID
|
||||
}
|
||||
if order.ConsigneeMobile2 != "" {
|
||||
mobile = order.ConsigneeMobile2
|
||||
uoSMS, err := dao.GetUserOrderSMS(db, mobile, "")
|
||||
stores, _ := dao.GetStoreList(db, []int{storeID}, nil, nil, nil, nil, "")
|
||||
if len(stores) > 0 {
|
||||
if stores[0].Tel1 == "" {
|
||||
storeTel = stores[0].Tel2
|
||||
} else {
|
||||
storeTel = stores[0].Tel1
|
||||
}
|
||||
}
|
||||
if uoSMS == nil {
|
||||
uoSMSc := &model.UserOrderSms{
|
||||
Mobile: mobile,
|
||||
Name: order.ConsigneeName,
|
||||
VendorUserID: order.VendorUserID,
|
||||
TotalCount: 0,
|
||||
SMSMark: model.NO,
|
||||
}
|
||||
err = dao.CreateEntity(db, uoSMSc)
|
||||
err = SendSMSMsg([]string{uoSMSc.Mobile}, globals.SMSSignName, globals.SMSNewUserOrderTemplate, map[string]interface{}{
|
||||
"tel": storeTel,
|
||||
}, nil)
|
||||
if err == nil {
|
||||
uoSMS2, _ := dao.GetUserOrderSMS(db, mobile, "")
|
||||
uoSMS2.SMSMark = model.YES
|
||||
uoSMS2.TotalCount++
|
||||
_, err = dao.UpdateEntity(db, uoSMS2, "SMSMark", "TotalCount")
|
||||
}
|
||||
} else {
|
||||
if uoSMS.SMSMark != model.YES {
|
||||
err = SendSMSMsg([]string{uoSMS.Mobile}, globals.SMSSignName, globals.SMSNewUserOrderTemplate, map[string]interface{}{
|
||||
"tel": storeTel,
|
||||
}, nil)
|
||||
if err == nil {
|
||||
uoSMS.SMSMark = model.YES
|
||||
uoSMS.TotalCount++
|
||||
_, err = dao.UpdateEntity(db, uoSMS, "SMSMark", "TotalCount")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
//给配送员发短信
|
||||
func NotifyNewCourierOrder(bill *model.Waybill) (err error) {
|
||||
var (
|
||||
db = dao.GetDB()
|
||||
storeTel string
|
||||
storeID int
|
||||
)
|
||||
order := &model.GoodsOrder{}
|
||||
order.VendorOrderID = bill.VendorOrderID
|
||||
err = dao.GetEntity(db, order, "VendorOrderID")
|
||||
if order.StoreID == 0 {
|
||||
storeID = order.JxStoreID
|
||||
} else {
|
||||
storeID = order.StoreID
|
||||
}
|
||||
stores, _ := dao.GetStoreList(db, []int{storeID}, nil, nil, nil, nil, "")
|
||||
if len(stores) > 0 {
|
||||
if stores[0].Tel1 == "" {
|
||||
storeTel = stores[0].Tel2
|
||||
} else {
|
||||
storeTel = stores[0].Tel1
|
||||
}
|
||||
}
|
||||
err = SendSMSMsg([]string{bill.CourierMobile}, globals.SMSSignName, globals.SMSNewOrderTemplate, map[string]interface{}{
|
||||
"tel": storeTel,
|
||||
}, nil)
|
||||
return err
|
||||
}
|
||||
|
||||
//京西订单配送员取货后,给用户发短信提醒
|
||||
func NotifyJxOrder(order *model.GoodsOrder, bill *model.Waybill) (err error) {
|
||||
err = SendSMSMsg([]string{order.ConsigneeMobile}, globals.SMSSignName, globals.SMSJxOrderDelivering, map[string]interface{}{
|
||||
"phone": bill.CourierMobile,
|
||||
}, order)
|
||||
return err
|
||||
}
|
||||
|
||||
39
business/jxutils/storeskulock/storeskulock.go
Normal file
39
business/jxutils/storeskulock/storeskulock.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package storeskulock
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"git.rosy.net.cn/jx-callback/globals"
|
||||
"git.rosy.net.cn/jx-callback/globals/api"
|
||||
)
|
||||
|
||||
const (
|
||||
cacheKeyPrefix = "jdpromotion"
|
||||
)
|
||||
|
||||
func LockJdStoreSku(jdStoreID string, jdSkuID int64, expire time.Time) {
|
||||
return
|
||||
globals.SugarLogger.Debug(expire, " ", time.Now())
|
||||
duration := expire.Sub(time.Now())
|
||||
if duration > 0 {
|
||||
api.Cacher.Set(genCacheKey(jdStoreID, jdSkuID), 1, duration)
|
||||
}
|
||||
}
|
||||
|
||||
func UnlockJdStoreSku(jdStoreID string, jdSkuID int64) {
|
||||
api.Cacher.Del(genCacheKey(jdStoreID, jdSkuID))
|
||||
}
|
||||
|
||||
func IsJdStoreSkuLocked(jdStoreID string, jdSkuID int64) bool {
|
||||
return false
|
||||
return api.Cacher.Get(genCacheKey(jdStoreID, jdSkuID)) != nil
|
||||
}
|
||||
|
||||
func ClearJdStoreSkuLock() {
|
||||
api.Cacher.FlushKeys(cacheKeyPrefix)
|
||||
}
|
||||
|
||||
func genCacheKey(jdStoreID string, jdSkuID int64) string {
|
||||
return fmt.Sprintf("%s.%s.%d", cacheKeyPrefix, jdStoreID, jdSkuID)
|
||||
}
|
||||
@@ -8,18 +8,19 @@ import (
|
||||
|
||||
"git.rosy.net.cn/baseapi/platformapi"
|
||||
|
||||
"git.rosy.net.cn/jx-callback/business/dao"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/eventhub/syseventhub"
|
||||
"git.rosy.net.cn/jx-callback/business/model"
|
||||
"git.rosy.net.cn/jx-callback/business/model/dao"
|
||||
"git.rosy.net.cn/jx-callback/business/model/legacymodel"
|
||||
"git.rosy.net.cn/jx-callback/globals/api"
|
||||
|
||||
"git.rosy.net.cn/baseapi/platformapi/weimobapi"
|
||||
"git.rosy.net.cn/baseapi/platformapi/yilianyunapi"
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
"git.rosy.net.cn/jx-callback/globals"
|
||||
|
||||
beego "github.com/astaxie/beego/adapter"
|
||||
"github.com/astaxie/beego/client/orm"
|
||||
beego "github.com/astaxie/beego/server/web"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -28,6 +29,7 @@ const (
|
||||
weimobTokenExpires = 7200 * time.Second
|
||||
yilianyunTokenExpires = 30 * 24 * 3600 * time.Second
|
||||
pushTokenExpires = 7200 * time.Second
|
||||
fnTokenExpires = 1 * time.Hour
|
||||
|
||||
maxRefreshGap = 5 * 60 * time.Second
|
||||
errRefreshGap = 10 * time.Second
|
||||
@@ -138,6 +140,92 @@ func RefreshWeixinToken() (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
func RefreshFnToken() (err error) {
|
||||
if api.FnAPI != nil {
|
||||
err = RefreshConfig("fn", fnTokenExpires, func() (token string, expireTimeStr string) {
|
||||
globals.SugarLogger.Debugf("RefreshFnToken RunMode:%s", beego.BConfig.RunMode)
|
||||
if globals.IsMainProductEnv() {
|
||||
if tokenInfo, err := api.FnAPI.GetAccessToken(); err == nil {
|
||||
globals.SugarLogger.Debugf("RefreshFnToken tokenInfo:%s", utils.Format4Output(tokenInfo, true))
|
||||
token = tokenInfo.AccessToken
|
||||
} else {
|
||||
globals.SugarLogger.Errorf("RefreshFnToken RefreshToken failed with error:%v", err)
|
||||
}
|
||||
sql := `
|
||||
UPDATE jxgy.config SET token = ? WHERE thirdparty = ?
|
||||
`
|
||||
sqlParams := []interface{}{
|
||||
token, "fn",
|
||||
}
|
||||
dao.ExecuteSQL(dao.GetDB(), sql, sqlParams)
|
||||
}
|
||||
if beego.BConfig.RunMode == "jxgy" {
|
||||
config := &legacymodel.Config{}
|
||||
sql := `
|
||||
SELECT * FROM config WHERE thirdparty = 'fn'
|
||||
`
|
||||
if err := dao.GetRow(dao.GetDB(), &config, sql, nil); err == nil {
|
||||
token = config.Token
|
||||
}
|
||||
}
|
||||
return token, expireTimeStr
|
||||
}, func(value string) {
|
||||
globals.SugarLogger.Debugf("RefreshFnToken setter value:%s", value)
|
||||
// syseventhub.SysEventHub.OnNewWXToken(value)
|
||||
api.FnAPI.SetToken(value)
|
||||
})
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func RefreshQywxToken() (err error) {
|
||||
if api.QywxAPI != nil {
|
||||
err = RefreshConfig("qywx", weixinTokenExpires, func() (token string, expireTimeStr string) {
|
||||
globals.SugarLogger.Debugf("RefreshQywxToken RunMode:%s", beego.BConfig.RunMode)
|
||||
if globals.IsMainProductEnv() {
|
||||
if tokenInfo, err := api.QywxAPI.GetToken(); err == nil {
|
||||
globals.SugarLogger.Debugf("RefreshQywxToken tokenInfo:%s", utils.Format4Output(tokenInfo, true))
|
||||
token = tokenInfo
|
||||
} else {
|
||||
globals.SugarLogger.Errorf("RefreshQywxToken RefreshToken failed with error:%v", err)
|
||||
}
|
||||
}
|
||||
return token, expireTimeStr
|
||||
}, func(value string) {
|
||||
globals.SugarLogger.Debugf("RefreshQywxToken setter value:%s", value)
|
||||
api.QywxAPI.SetToken(value)
|
||||
})
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func RefreshWeixin2Token() (err error) {
|
||||
if api.WeixinMiniAPI2 != nil {
|
||||
err = RefreshConfig("wechat2", weixinTokenExpires, func() (token string, expireTimeStr string) {
|
||||
globals.SugarLogger.Debugf("RefreshWeixin2Token RunMode:%s", beego.BConfig.RunMode)
|
||||
if globals.IsMainProductEnv() {
|
||||
if tokenInfo, err := api.WeixinMiniAPI2.CBRetrieveToken(); err == nil {
|
||||
globals.SugarLogger.Debugf("RefreshWeixin2Token tokenInfo:%s", utils.Format4Output(tokenInfo, true))
|
||||
token = tokenInfo.AccessToken
|
||||
} else {
|
||||
globals.SugarLogger.Errorf("RefreshWeixin2Token RefreshToken failed with error:%v", err)
|
||||
}
|
||||
} else {
|
||||
if tokenInfo := getWX2TokenFromRemote(api.WeixinMiniAPI2.CBGetToken()); tokenInfo != nil {
|
||||
expireTimeStr = utils.Time2Str(time.Now().Add(-weixinTokenExpires))
|
||||
token = tokenInfo.Token
|
||||
}
|
||||
}
|
||||
return token, expireTimeStr
|
||||
}, func(value string) {
|
||||
globals.SugarLogger.Debugf("RefreshWeixinToken setter value:%s", value)
|
||||
syseventhub.SysEventHub.OnNewWX2Token(value)
|
||||
api.WeixinMiniAPI2.CBSetToken(value)
|
||||
})
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func RefreshPushToken() (err error) {
|
||||
if api.PushAPI != nil {
|
||||
err = RefreshConfig("push", pushTokenExpires, func() (token string, expireTimeStr string) {
|
||||
@@ -254,6 +342,33 @@ func SaveWeimobToken(token *weimobapi.TokenInfo) (err error) {
|
||||
return dao.CreateOrUpdate(db, config)
|
||||
}
|
||||
|
||||
func RefreshYilianyunToken() (err error) {
|
||||
return RefreshConfig("yilianyun", yilianyunTokenExpires, func() (token string, expireTimeStr string) {
|
||||
globals.SugarLogger.Debugf("RefreshYilianyunToken RunMode:%s", beego.BConfig.RunMode)
|
||||
if globals.IsMainProductEnv() { // 只有京西菜市刷新易联云key
|
||||
if tokenInfo, err := api.YilianyunAPI.RetrieveToken(); err == nil {
|
||||
token = string(utils.MustMarshal(tokenInfo))
|
||||
} else {
|
||||
globals.SugarLogger.Errorf("RefreshYilianyunToken RefreshToken failed with error:%v", err)
|
||||
}
|
||||
} else {
|
||||
if tokenInfo := getYLYTokenFromRemote(api.YilianyunAPI.GetToken()); tokenInfo != nil {
|
||||
expireTimeStr = utils.Time2Str(time.Now().Add(-yilianyunTokenExpires))
|
||||
token = tokenInfo.Token
|
||||
}
|
||||
}
|
||||
return token, expireTimeStr
|
||||
}, func(value string) {
|
||||
token := value
|
||||
var tokenInfo *yilianyunapi.TokenInfo
|
||||
if err := utils.TryUnmarshalUseNumber([]byte(value), &tokenInfo); err == nil {
|
||||
token = tokenInfo.AccessToken
|
||||
}
|
||||
syseventhub.SysEventHub.OnNewYLYToken(token)
|
||||
api.YilianyunAPI.SetToken(token)
|
||||
})
|
||||
}
|
||||
|
||||
func PollingRemotEvent(remoteURL string, waitSecond int, params map[string]interface{}) (tokenInfo *syseventhub.TokenInfo) {
|
||||
if waitSecond == 0 {
|
||||
waitSecond = 5 * 60
|
||||
|
||||
@@ -7,7 +7,9 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"git.rosy.net.cn/baseapi/platformapi/dingdingapi"
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/ddmsg"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
|
||||
"git.rosy.net.cn/jx-callback/globals"
|
||||
)
|
||||
@@ -484,20 +486,20 @@ func SendMessage(t *BaseTask) {
|
||||
time.Sleep(10 * time.Millisecond) // 等待GetResult中的isGetResultCalled赋值
|
||||
globals.SugarLogger.Debugf("BaseTask task ID:%s, name:%s finished, isGetResultCalled:%t", t.ID, t.Name, t.isGetResultCalled)
|
||||
if !t.isGetResultCalled && t.parent == nil && len(GetTasks(t.ID, TaskStatusBegin, TaskStatusEnd, 24, "")) > 0 {
|
||||
//if authInfo, err := t.ctx.GetV2AuthInfo(); err == nil { // 这里应该是不管登录类型,直接以可能的方式发消息
|
||||
// var content string
|
||||
// taskDesc := fmt.Sprintf("你的异步任务[%s],ID[%s],开始于:%s,结束于:%s,", t.Name, t.ID, utils.Time2Str(t.CreatedAt), utils.Time2Str(t.TerminatedAt))
|
||||
// content = fmt.Sprintf("%s执行%s", taskDesc, TaskStatusName[t.Status])
|
||||
// if t.Error() == "" {
|
||||
// noticeMsg := t.GetNoticeMsg()
|
||||
// if noticeMsg != "" {
|
||||
// content += ",通知消息:" + noticeMsg
|
||||
// }
|
||||
// } else {
|
||||
// content += ",\n" + t.Error()
|
||||
// }
|
||||
// ddmsg.SendUserMessage(dingdingapi.MsgTyeText, authInfo.UserID, "异步任务完成", content)
|
||||
//}
|
||||
if authInfo, err := t.ctx.GetV2AuthInfo(); err == nil { // 这里应该是不管登录类型,直接以可能的方式发消息
|
||||
var content string
|
||||
taskDesc := fmt.Sprintf("你的异步任务[%s],ID[%s],开始于:%s,结束于:%s,", t.Name, t.ID, utils.Time2Str(t.CreatedAt), utils.Time2Str(t.TerminatedAt))
|
||||
content = fmt.Sprintf("%s执行%s", taskDesc, TaskStatusName[t.Status])
|
||||
if t.Error() == "" {
|
||||
noticeMsg := t.GetNoticeMsg()
|
||||
if noticeMsg != "" {
|
||||
content += ",通知消息:" + noticeMsg
|
||||
}
|
||||
} else {
|
||||
content += ",\n" + t.Error()
|
||||
}
|
||||
ddmsg.SendUserMessage(dingdingapi.MsgTyeText, authInfo.UserID, "异步任务完成", content)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
101
business/jxutils/unipush/push.go
Normal file
101
business/jxutils/unipush/push.go
Normal file
@@ -0,0 +1,101 @@
|
||||
package push
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"git.rosy.net.cn/baseapi/platformapi/unipushapi"
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils"
|
||||
"git.rosy.net.cn/jx-callback/business/model"
|
||||
"git.rosy.net.cn/jx-callback/business/model/dao"
|
||||
"git.rosy.net.cn/jx-callback/globals"
|
||||
"git.rosy.net.cn/jx-callback/globals/api"
|
||||
beego "github.com/astaxie/beego/server/web"
|
||||
)
|
||||
|
||||
func pushToSingle(content, title string, storeID int) {
|
||||
var (
|
||||
db = dao.GetDB()
|
||||
)
|
||||
if !globals.IsProductEnv() {
|
||||
return
|
||||
}
|
||||
if storeID == 0 {
|
||||
return
|
||||
}
|
||||
storePushs, err := dao.GetStorePushClient(db, storeID, "")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for _, v := range storePushs {
|
||||
|
||||
status, err2 := api.PushAPI.PushToSingle(v.ClientID, false, &unipushapi.Notification{
|
||||
Title: title,
|
||||
Body: content,
|
||||
})
|
||||
if err = err2; err != nil {
|
||||
globals.SugarLogger.Debugf("NotifyNewOrder push error: [%v]", err)
|
||||
continue
|
||||
}
|
||||
if status == unipushapi.SuccessOffLine {
|
||||
_, err = api.PushAPI.PushToSingle(v.ClientID, true, &unipushapi.Notification{
|
||||
Body: content,
|
||||
})
|
||||
if err != nil {
|
||||
globals.SugarLogger.Debugf("NotifyNewOrder push2 error: [%v]", err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func NotifyNewOrder(order *model.GoodsOrder) {
|
||||
globals.SugarLogger.Debugf("NotifyNewOrder push begin orderID :[%v]", order.VendorOrderID)
|
||||
if order == nil || len(order.Skus) == 0 {
|
||||
return
|
||||
}
|
||||
sb := new(strings.Builder)
|
||||
sb.WriteString("老板,")
|
||||
sb.WriteString(order.ConsigneeName)
|
||||
sb.WriteString("购买了商品")
|
||||
sb.WriteString(getOrderDetailBrief(order))
|
||||
pushToSingle(sb.String(), "京西菜市新订单推送", jxutils.GetSaleStoreIDFromOrder(order))
|
||||
}
|
||||
|
||||
func getOrderDetailBrief(order *model.GoodsOrder) (brief string) {
|
||||
sb := new(strings.Builder)
|
||||
sb.WriteString(order.Skus[0].SkuName)
|
||||
sb.WriteString("等共")
|
||||
sb.WriteString(utils.Int2Str(order.Skus[0].Count))
|
||||
sb.WriteString("份(")
|
||||
sb.WriteString(jxutils.IntPrice2StandardString(order.Skus[0].SalePrice))
|
||||
sb.WriteString("元/份)等,预计收入")
|
||||
//TODO 2020-07-20 果园和菜市不同
|
||||
var price int64
|
||||
if beego.BConfig.RunMode == "jxgy" {
|
||||
price = order.EarningPrice
|
||||
} else {
|
||||
if order.EarningType == model.EarningTypePoints {
|
||||
price = order.ActualPayPrice
|
||||
} else {
|
||||
price = order.EarningPrice
|
||||
}
|
||||
}
|
||||
sb.WriteString(jxutils.IntPrice2StandardString(price))
|
||||
sb.WriteString("元")
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
func NotifyAfsOrder(afsOrder *model.AfsOrder) (err error) {
|
||||
globals.SugarLogger.Debugf("NotifyAfsOrder push begin orderID :[%v]", afsOrder.VendorOrderID)
|
||||
pushToSingle("老板,您有新的售后单,请尽快处理!", "京西菜市售后单推送", jxutils.GetSaleStoreIDFromAfsOrder(afsOrder))
|
||||
return err
|
||||
}
|
||||
|
||||
func NotifyOrderCanceled(order *model.GoodsOrder) (err error) {
|
||||
title := fmt.Sprintf("老板,您的订单%s第%d号订单, %s被取消了!", model.VendorChineseNames[order.VendorID], order.OrderSeq, order.VendorOrderID)
|
||||
pushToSingle(title, "京西菜市取消单推送", jxutils.GetSaleStoreIDFromOrder(order))
|
||||
return err
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user