Compare commits
1989 Commits
| 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 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -2,6 +2,7 @@
|
||||
|
||||
jx-callback
|
||||
*.tmp
|
||||
swagger/
|
||||
debug
|
||||
.DS_Store
|
||||
*.log
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -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
|
||||
}
|
||||
@@ -475,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) {
|
||||
@@ -488,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
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@ import (
|
||||
|
||||
const (
|
||||
AuthType = "alipaycode"
|
||||
|
||||
AuthKey = "GHp3ojlVYRRu2XID4FX2ew=="
|
||||
)
|
||||
|
||||
type Auther struct {
|
||||
@@ -29,14 +31,11 @@ func (a *Auther) VerifySecret(dummy, code string) (authBindEx *auth2.AuthBindEx,
|
||||
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, userInfo.UserID, "", userInfo); err == nil {
|
||||
authBindEx.UserHint = &auth2.UserBasic{
|
||||
Name: userInfo.NickName,
|
||||
Avatar: userInfo.Avatar,
|
||||
}
|
||||
}
|
||||
//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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -3,7 +3,6 @@ package mobile
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
"git.rosy.net.cn/jx-callback/business/auth2"
|
||||
@@ -45,9 +44,7 @@ func init() {
|
||||
|
||||
// 特殊接口
|
||||
func (a *Auther) SendVerifyCode(mobileNumber string) (verifyCode string, err error) {
|
||||
verifyCode = fmt.Sprintf("%06d", rand.Intn(1000000))
|
||||
globals.SugarLogger.Debugf("SendVerifyCode code: %v", verifyCode)
|
||||
// a.GenerateVerifyCode(mobileNumber)
|
||||
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,
|
||||
@@ -72,12 +69,11 @@ func (a *Auther) SendVerifyCode(mobileNumber string) (verifyCode string, err err
|
||||
|
||||
func (a *Auther) VerifySecret(mobileNumber, code string) (authBindEx *auth2.AuthBindEx, err error) {
|
||||
globals.SugarLogger.Debugf("VerifySecret mobileNumber:%s, code:%s", mobileNumber, code)
|
||||
def := &authprovider.DefAuther{}
|
||||
|
||||
err = ErrVerifyCodeIsWrong
|
||||
savedVerifyCode := def.LoadVerifyCode(mobileNumber)
|
||||
if code == auth2.InternalAuthSecret ||
|
||||
auth2.TestMobileMap[mobileNumber] == 1 && code == TestVerifyCode || (code != "" && savedVerifyCode != "" && code == savedVerifyCode) {
|
||||
// || a.VerifyCode(mobileNumber, code)
|
||||
if (code == auth2.InternalAuthSecret ||
|
||||
auth2.TestMobileMap[mobileNumber] == 1 && code == TestVerifyCode) ||
|
||||
a.VerifyCode(mobileNumber, code) {
|
||||
err = nil
|
||||
}
|
||||
return nil, err
|
||||
|
||||
@@ -57,11 +57,12 @@ func init() {
|
||||
}
|
||||
|
||||
func (a *Auther) VerifySecret(id, secret string) (authBindEx *auth2.AuthBindEx, err error) {
|
||||
globals.SugarLogger.Debugf("weixin VerifySecret id:%s secret:%s", secret, secret)
|
||||
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 := secret
|
||||
code := jsCode
|
||||
if state == "" {
|
||||
token, err2 := a.getAPI().SNSRetrieveToken(code)
|
||||
if err = err2; err == nil {
|
||||
|
||||
@@ -57,9 +57,9 @@ func (a *MiniAuther) DecryptData(authInfo *auth2.AuthInfo, jsCode, encryptedData
|
||||
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不匹配")
|
||||
// }
|
||||
// if authBindEx.UserID != authInfo.GetID() {
|
||||
// return "", fmt.Errorf("jsCode与token不匹配")
|
||||
// }
|
||||
// } else {
|
||||
// return "", err
|
||||
// }
|
||||
@@ -69,7 +69,7 @@ func (a *MiniAuther) DecryptData(authInfo *auth2.AuthInfo, jsCode, encryptedData
|
||||
}
|
||||
} else {
|
||||
if authInfo.AuthBindInfo.Type != AuthTypeMini {
|
||||
return "", ErrAuthTypeShouldBeMini
|
||||
// return "", ErrAuthTypeShouldBeMini
|
||||
}
|
||||
sessionKey = authInfo.AuthBindInfo.UserData.(string)
|
||||
}
|
||||
@@ -88,6 +88,12 @@ func (a *MiniAuther) GetUserType() (userType int8) {
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
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))
|
||||
}
|
||||
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)
|
||||
}
|
||||
@@ -1,467 +0,0 @@
|
||||
package cms
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
"git.rosy.net.cn/jx-callback/business/jxstore/event"
|
||||
"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"
|
||||
"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 QueryPrintList(printNo, printKey string, status, isOnline int64, page, size int64) ([]*model.Printer, int, error) {
|
||||
prints, count, err := dao.QueryPrintList(dao.GetDB(), printNo, printKey, status, isOnline, page, size)
|
||||
return prints, count, 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.GetPrintMsgs2(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,
|
||||
Status: event.PrintMsgWait,
|
||||
}
|
||||
|
||||
t, ok := event.PrintObject.GetPrintObj(printNo)
|
||||
if ok {
|
||||
t.Lock()
|
||||
defer t.Unlock()
|
||||
t.MsgMap[printNo] <- printMsg
|
||||
printMsg.Status = event.PrintMsgAlreadyLoad
|
||||
} /*else {
|
||||
t = event.NewTcpClient()
|
||||
event.BuildAllMap(t, printNo)
|
||||
event.PrintObject[printNo] = t
|
||||
|
||||
t.Lock()
|
||||
defer t.Unlock()
|
||||
t.MsgMap[printNo] <- printMsg
|
||||
printMsg.Status = event.PrintMsgAlreadyLoad
|
||||
}*/
|
||||
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.GetPrintMsgs2(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()
|
||||
)
|
||||
//看有没有
|
||||
printers, _ := dao.GetPrinters(db, appID, printNo, 0, 0)
|
||||
if 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
|
||||
}
|
||||
}
|
||||
@@ -71,7 +71,7 @@ func TransferLegacyWeixins(mobile string) (err error) {
|
||||
if user.Name == "" {
|
||||
user.Name = user.GetMobile()
|
||||
}
|
||||
userList, _, err2 := dao.GetUsers(db, 0, "", "", nil, nil, []string{v.Tel}, 0, -1)
|
||||
userList, _, err2 := dao.GetUsers(db, 0, "", nil, nil, []string{v.Tel}, 0, -1)
|
||||
if err = err2; err != nil {
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -1,24 +1,28 @@
|
||||
package cms
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"git.rosy.net.cn/baseapi/platformapi/mtunionapi"
|
||||
"git.rosy.net.cn/baseapi/platformapi/tbunionapi"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.rosy.net.cn/baseapi/platformapi/ejyapi"
|
||||
"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/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"
|
||||
@@ -39,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) {
|
||||
@@ -54,65 +111,49 @@ func InitServiceInfo(version string, buildTime time.Time, gitCommit string) {
|
||||
"buildTime": buildTimeStr,
|
||||
"gitCommit": gitCommit,
|
||||
"metaData": map[string]interface{}{
|
||||
"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,
|
||||
"vendorImg": model.VendorImg,
|
||||
"vendorColors": model.VendorColors,
|
||||
"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,
|
||||
"jobLimitCountType": `[{
|
||||
"id":` + utils.Int2Str(model.JobLimitCountTypePO) +
|
||||
`,"value": "每人一次"
|
||||
},{
|
||||
"id":` + utils.Int2Str(model.JobLimitCountTypePDO) +
|
||||
`,"value": "每人每天一次"
|
||||
},{
|
||||
"id":` + utils.Int2Str(model.JobLimitCountTypePWO) +
|
||||
`,"value": "每人每周一次"
|
||||
},{
|
||||
"id":` + utils.Int2Str(model.JobLimitCountTypeNoLimit) +
|
||||
`,"value": "不限次"
|
||||
}]`,
|
||||
"billTypeNames": model.BillTypeNames,
|
||||
"deliveryStatusName": model.DeliveryStatusName,
|
||||
"cashbackName": model.CashbackName,
|
||||
"consumeName": model.ConsumeName,
|
||||
"txWaybillNames": model.TxWaybillNames,
|
||||
"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,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -154,6 +195,34 @@ func GetPlaces(ctx *jxcontext.Context, keyword string, includeDisabled bool, par
|
||||
return places, dao.GetRows(nil, &places, sql, sqlParams)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
@@ -164,14 +233,175 @@ func GetCoordinateCityInfo(ctx *jxcontext.Context, lng, lat float64) (name strin
|
||||
}
|
||||
|
||||
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{
|
||||
@@ -182,21 +412,41 @@ 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
|
||||
// }
|
||||
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("暂不支持删除银行")
|
||||
@@ -204,7 +454,7 @@ func DeleteConfig(ctx *jxcontext.Context, key, configType string) (err error) {
|
||||
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)
|
||||
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")))
|
||||
@@ -212,7 +462,14 @@ func DeleteConfig(ctx *jxcontext.Context, key, configType string) (err error) {
|
||||
}
|
||||
}
|
||||
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()
|
||||
}
|
||||
@@ -223,7 +480,7 @@ func DeleteConfig(ctx *jxcontext.Context, key, configType string) (err error) {
|
||||
})
|
||||
}
|
||||
if configType == model.ConfigTypeSys && err == nil {
|
||||
// err = onSysConfigChanged(key, "")
|
||||
err = onSysConfigChanged(key, "")
|
||||
}
|
||||
return err
|
||||
}
|
||||
@@ -232,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)
|
||||
@@ -255,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
|
||||
}
|
||||
@@ -269,105 +571,16 @@ func QueryConfigs(key, configType, keyword string) (configList []*model.NewConfi
|
||||
return dao.QueryConfigs(dao.GetDB(), key, configType, keyword)
|
||||
}
|
||||
|
||||
func InitStation(ctx *jxcontext.Context) (err error) {
|
||||
var (
|
||||
db = dao.GetDB()
|
||||
stationMap = make(map[string]*model.StationInfo)
|
||||
stationEjyMap = make(map[string]*ejyapi.GetStationListResult)
|
||||
addList []*model.StationInfo
|
||||
updateList []*model.StationInfo
|
||||
deleteList []*model.StationInfo
|
||||
)
|
||||
api.EjyAPI.SetTimestamp(time.Now().Unix())
|
||||
if stations, err := dao.GetStationList(db); len(stations) > 0 && err == nil {
|
||||
for _, v := range stations {
|
||||
stationMap[v.StationID] = v
|
||||
}
|
||||
}
|
||||
if getStationListResult, err := api.EjyAPI.GetStationList(); len(getStationListResult) > 0 && err == nil {
|
||||
for _, v := range getStationListResult {
|
||||
stationEjyMap[v.StationID] = v
|
||||
if stationMap[v.StationID] == nil {
|
||||
addList = append(addList, EjyStationToStationInfo(v))
|
||||
} else {
|
||||
updateList = append(updateList, stationMap[v.StationID])
|
||||
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)
|
||||
}
|
||||
}
|
||||
for _, v := range stationMap {
|
||||
if stationEjyMap[v.StationID] == nil {
|
||||
deleteList = append(deleteList, v)
|
||||
}
|
||||
}
|
||||
task := tasksch.NewParallelTask("InitStation", tasksch.NewParallelConfig().SetParallelCount(1).SetIsContinueWhenError(true), ctx,
|
||||
func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
|
||||
step := batchItemList[0].(int)
|
||||
switch step {
|
||||
case 0:
|
||||
if len(addList) > 0 {
|
||||
err = dao.CreateMultiEntities(db, addList)
|
||||
}
|
||||
case 1:
|
||||
if len(updateList) > 0 {
|
||||
task := tasksch.NewParallelTask("updateList", tasksch.NewParallelConfig().SetParallelCount(1).SetIsContinueWhenError(true), ctx,
|
||||
func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
|
||||
station := batchItemList[0].(*model.StationInfo)
|
||||
dao.UpdateEntity(db, station)
|
||||
return retVal, err
|
||||
}, updateList)
|
||||
tasksch.HandleTask(task, nil, true).Run()
|
||||
_, err = task.GetResult(0)
|
||||
}
|
||||
case 2:
|
||||
if len(deleteList) > 0 {
|
||||
task := tasksch.NewParallelTask("deleteList", tasksch.NewParallelConfig().SetParallelCount(1).SetIsContinueWhenError(true), ctx,
|
||||
func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
|
||||
station := batchItemList[0].(*model.StationInfo)
|
||||
dao.DeleteEntity(db, station)
|
||||
return retVal, err
|
||||
}, deleteList)
|
||||
tasksch.HandleTask(task, nil, true).Run()
|
||||
_, err = task.GetResult(0)
|
||||
}
|
||||
}
|
||||
return retVal, err
|
||||
}, []int{0, 1, 2})
|
||||
tasksch.HandleTask(task, nil, true).Run()
|
||||
_, err = task.GetResult(0)
|
||||
return err
|
||||
}
|
||||
|
||||
func EjyStationToStationInfo(station *ejyapi.GetStationListResult) (stationInfo *model.StationInfo) {
|
||||
stationInfo = &model.StationInfo{
|
||||
StationID: station.StationID,
|
||||
StationName: station.StationName,
|
||||
ProvinceName: station.ProvinceName,
|
||||
ProvinceID: station.ProvinceID,
|
||||
CityName: station.CityName,
|
||||
Latitude: utils.Str2Float64(station.Latitude),
|
||||
Longitude: utils.Str2Float64(station.Longitude),
|
||||
Location: station.Location,
|
||||
StarNum: station.StarNum,
|
||||
Phone: station.Phone,
|
||||
StationPic: station.StationPic,
|
||||
StationBannerPic: station.StationBannerPic,
|
||||
District: station.District,
|
||||
CityID: station.CityID,
|
||||
StationType: station.StationType,
|
||||
}
|
||||
if station.Prices != nil {
|
||||
if data, err := json.Marshal(station.Prices); err == nil {
|
||||
stationInfo.Prices = string(data)
|
||||
}
|
||||
}
|
||||
if station.Adverts != nil {
|
||||
if data, err := json.Marshal(station.Adverts); err == nil {
|
||||
stationInfo.Adverts = string(data)
|
||||
}
|
||||
}
|
||||
return stationInfo
|
||||
}
|
||||
|
||||
func GetStationList(ctx *jxcontext.Context, stationName string, cityCode int, lat, lng float64, oilCode string, sortType, offset, pageSize int) (pageInfo *model.PagedInfo, err error) {
|
||||
return dao.GetStationInfoList(dao.GetDB(), stationName, cityCode, lat, lng, oilCode, sortType, offset, pageSize)
|
||||
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()
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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,519 +0,0 @@
|
||||
package cms
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"git.rosy.net.cn/baseapi/platformapi/recharge_phone_bill"
|
||||
"git.rosy.net.cn/baseapi/platformapi/tonglianpayapi"
|
||||
"git.rosy.net.cn/jx-callback/business/jxstore/event"
|
||||
"git.rosy.net.cn/jx-callback/business/q_bida"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.rosy.net.cn/jx-callback/business/auth2"
|
||||
|
||||
"git.rosy.net.cn/jx-callback/globals"
|
||||
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
|
||||
"git.rosy.net.cn/jx-callback/business/jxstore/financial"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils"
|
||||
|
||||
"git.rosy.net.cn/jx-callback/business/model/dao"
|
||||
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
|
||||
|
||||
"git.rosy.net.cn/jx-callback/business/model"
|
||||
)
|
||||
|
||||
func CreateOrder(ctx *jxcontext.Context, type1, orderType int, way string, price int, lng, lat float64, mobile, flowCode string) (orderID, errCode string, err error) {
|
||||
var (
|
||||
db = dao.GetDB()
|
||||
order *model.Order
|
||||
DayTimeBegin, DayTimeEnd = jxutils.GetDayTime()
|
||||
)
|
||||
if err = auth2.CheckWeixinminiAuthBind(ctx.GetUserID()); err != nil {
|
||||
return "", errCode, err
|
||||
}
|
||||
txDB, _ := dao.Begin(db)
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
dao.Rollback(db, txDB)
|
||||
panic(r)
|
||||
}
|
||||
}()
|
||||
if type1 == model.OrderTypeCash {
|
||||
//如果用户没有对应账单信息就给他生成一条
|
||||
userBill, err := dao.GetUserBill(db, ctx.GetUserID(), "")
|
||||
if userBill == nil {
|
||||
err = financial.AddUserBill(txDB, jxutils.GenBillID(), ctx.GetUserID())
|
||||
}
|
||||
if userBill.AccountBalance < price {
|
||||
return "", model.ErrCodeAccountBalanceNotEnough, fmt.Errorf("用户余额不足!")
|
||||
}
|
||||
//用户一天只能提现一次
|
||||
billExpends, err := dao.GetBillExpend(db, ctx.GetUserID(), model.BillTypeCash, DayTimeBegin, DayTimeEnd)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
if len(billExpends) > 0 {
|
||||
return "", "", fmt.Errorf("抱歉,一天只能提现一次!")
|
||||
}
|
||||
}
|
||||
address, dCode, cCode, err := getAddressInfoFromCoord(db, lng, lat)
|
||||
order = &model.Order{
|
||||
OrderID: utils.Int64ToStr(jxutils.GenOrderNo()),
|
||||
UserID: ctx.GetUserID(),
|
||||
Type: type1,
|
||||
OrderType: orderType,
|
||||
Way: way,
|
||||
Status: model.OrderStatusWait4Pay,
|
||||
PayPrice: price,
|
||||
Lng: lng,
|
||||
Lat: lat,
|
||||
Address: address,
|
||||
DistrictCode: dCode,
|
||||
CityCode: cCode,
|
||||
}
|
||||
|
||||
// 话费充值
|
||||
if order.OrderType == 7 {
|
||||
// 校验充值编号
|
||||
have, err := CheckMobileAndFlowCode(mobile, flowCode)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
if !have {
|
||||
return "", "", errors.New("充值模板错误")
|
||||
}
|
||||
order.Mobile = mobile
|
||||
order.FlowCode = flowCode
|
||||
order.RechargeStatus = 3 // 当前系统待充值
|
||||
}
|
||||
|
||||
dao.WrapAddIDCULEntity(order, ctx.GetUserName())
|
||||
if err = dao.CreateEntityTx(txDB, order); err != nil {
|
||||
dao.Rollback(db, txDB)
|
||||
}
|
||||
dao.Commit(db, txDB)
|
||||
return order.OrderID, errCode, err
|
||||
}
|
||||
|
||||
func CheckMobileAndFlowCode(mobile, flowCode string) (bool, error) {
|
||||
// 校验业务电话和充值号码是否正确
|
||||
if mobile == "" || flowCode == "" {
|
||||
return false, errors.New("充值电话费用号码/业务代码不能为空")
|
||||
}
|
||||
regRuler := "^1[3456789]{1}\\d{9}$"
|
||||
if !regexp.MustCompile(regRuler).MatchString(mobile) {
|
||||
return false, errors.New("电话号码格式校验错误")
|
||||
}
|
||||
|
||||
switch mobile[0:4] {
|
||||
case "1703", "1705", "1706": // 中国移动
|
||||
if flowCode == recharge_phone_bill.FlowCodeY10Y100 || flowCode == recharge_phone_bill.FlowCodeY10Y200 {
|
||||
return true, nil
|
||||
}
|
||||
case "1704", "1707", "1708", "1709": // 中国联通
|
||||
if flowCode == recharge_phone_bill.FlowCodeL10Y50 || flowCode == recharge_phone_bill.FlowCodeL10Y100 || flowCode == recharge_phone_bill.FlowCodeL10Y200 {
|
||||
return true, nil
|
||||
}
|
||||
case "1700", "1701", "1702 ": // 中国电信
|
||||
if flowCode == recharge_phone_bill.FlowCodeD10Y50 || flowCode == recharge_phone_bill.FlowCodeD10Y100 || flowCode == recharge_phone_bill.FlowCodeD10Y200 {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
switch mobile[0:3] {
|
||||
case "139", "138", "137", "136", "135", "134", "150", "151", "152", "157", "158", "159 182", "183", "184", "187", "188", "147", "198", "178 ", "165": // 中国移动
|
||||
if flowCode == recharge_phone_bill.FlowCodeY10Y100 || flowCode == recharge_phone_bill.FlowCodeY10Y200 {
|
||||
return true, nil
|
||||
}
|
||||
case "130", "131", "132", "155", "156", "185", "186", "175", "176", "166", "171", "167": // 中国联通
|
||||
if flowCode == recharge_phone_bill.FlowCodeL10Y50 || flowCode == recharge_phone_bill.FlowCodeL10Y100 || flowCode == recharge_phone_bill.FlowCodeL10Y200 {
|
||||
return true, nil
|
||||
}
|
||||
case "133", "153", "173", "177", "180", "181", "189", "191", "199": // 中国电信
|
||||
if flowCode == recharge_phone_bill.FlowCodeD10Y50 || flowCode == recharge_phone_bill.FlowCodeD10Y100 || flowCode == recharge_phone_bill.FlowCodeD10Y200 {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, errors.New("运营商查询错误/充值编码错误")
|
||||
}
|
||||
|
||||
func Pay(ctx *jxcontext.Context, orderID string, payType int, vendorPayType, appId string, isChoose int) (result *financial.WxPayParam, err error) {
|
||||
var (
|
||||
tempPayprice int
|
||||
tempPaymethod int
|
||||
db = dao.GetDB()
|
||||
tdb, _ = dao.Begin(db)
|
||||
)
|
||||
orderInfo, err := dao.GetOrderByID(db, orderID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var (
|
||||
payHandler = &financial.PayHandler{
|
||||
PayType: payType,
|
||||
Ctx: ctx,
|
||||
VendorPayType: vendorPayType,
|
||||
}
|
||||
)
|
||||
if orderInfo.OrderType == 6 {
|
||||
return nil, errors.New("不能余额充值余额")
|
||||
}
|
||||
|
||||
// 用户是否使用余额抵消
|
||||
if isChoose == 1 { // 余额抵消
|
||||
// 查询用户余额
|
||||
userBill, err := dao.GetUserBill(db, ctx.GetUserID(), "")
|
||||
if err != nil || userBill == nil { // 出现错误或异常直接使用金钱支付
|
||||
tempPayprice = orderInfo.PayPrice
|
||||
tempPaymethod = model.OrderPayMethodWX
|
||||
} else {
|
||||
if userBill.AccountBalance-orderInfo.PayPrice >= 0 { // 余额大于支付金额,使用余额支付
|
||||
switch orderInfo.OrderType { // 1-发任务,2-会员月卡,3-发快递,4-提现,5-会员年卡,6-使用充值到余额方式的订单,7-话费
|
||||
case model.OrderTypeMember, model.OrderTypeMemberYear:
|
||||
if err := financial.OnWXPayFinished(orderInfo); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case model.OrderTypeDelivery:
|
||||
call := &tonglianpayapi.CallBackResult{}
|
||||
call.TrxID = "ziDingYi_" + utils.Int64ToStr(time.Now().Unix())
|
||||
call.TrxStatus = tonglianpayapi.TrxStatusSuccess
|
||||
if err := financial.OnWxPaySendPage(dao.GetDB(), orderInfo, call, 4); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case model.OrderTypeMobile:
|
||||
if err := financial.OnWxPayTelephone(orderInfo); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
default:
|
||||
return nil, errors.New("其他待处理信息,联系管理员")
|
||||
}
|
||||
//账户支出
|
||||
if err = financial.AddExpendUpdateAccount(tdb, userBill, model.BillTypePayByAccountBalance, orderInfo.PayPrice, 1); err != nil {
|
||||
dao.Rollback(db, tdb)
|
||||
return nil, err
|
||||
}
|
||||
return nil, nil
|
||||
} else {
|
||||
// 混合支付
|
||||
tempPayprice = orderInfo.PayPrice - userBill.AccountBalance
|
||||
tempPaymethod = model.OrderPayMethodMix
|
||||
//账户支出
|
||||
if err = financial.AddExpendUpdateAccount(tdb, userBill, model.BillTypePayByAccountBalance, userBill.AccountBalance, 1); err != nil {
|
||||
dao.Rollback(db, tdb)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
} else { // 原价给
|
||||
tempPayprice = orderInfo.PayPrice
|
||||
tempPaymethod = model.OrderPayMethodWX
|
||||
}
|
||||
|
||||
orderInfo.PayPrice = tempPayprice
|
||||
orderInfo.PayMethod = tempPaymethod
|
||||
dao.UpdateEntity(dao.GetDB(), orderInfo, "PayPrice", "PayMethod")
|
||||
payHandler.Order = orderInfo
|
||||
//如果用户没有对应账单信息就给他生成一条
|
||||
// 给用户创建一个银行卡账户
|
||||
userBill, err := dao.GetUserBill(db, orderInfo.UserID, "")
|
||||
if userBill == nil {
|
||||
err = financial.AddUserBill(tdb, jxutils.GenBillID(), orderInfo.UserID)
|
||||
}
|
||||
err = payHandler.CreatePay(tdb, appId)
|
||||
return payHandler.WxPayParam, err
|
||||
}
|
||||
|
||||
//余额支付 微信补差值
|
||||
func PayByBalance(ctx *jxcontext.Context, orderID string, isChoose, payType int, vendorPayType, appID string) (*financial.WxPayParam, string, error) {
|
||||
var (
|
||||
db = dao.GetDB()
|
||||
)
|
||||
//获取订单信息
|
||||
orderInfo, err := dao.GetOrderByID(db, orderID)
|
||||
if err != nil {
|
||||
return nil, "获取订单信息失败", err
|
||||
}
|
||||
//获取用户 会员账户信息
|
||||
userBill, err := dao.GetUserBill(db, orderInfo.UserID, "")
|
||||
if err != nil {
|
||||
return nil, "获取用户会员账户余额失败", err
|
||||
}
|
||||
if orderInfo.Status == model.OrderNotPay {
|
||||
//需要充值余额支付的方式//todo 后续添加
|
||||
//if orderInfo.OrderType == 6 {
|
||||
// WxPayParam, err := Pay(ctx, orderInfo.OrderID, payType, vendorPayType, appID, orderInfo.PayPrice)
|
||||
// if err != nil {
|
||||
// return nil, "微信支付失败:", err
|
||||
// }
|
||||
// return WxPayParam, "", err
|
||||
//}
|
||||
//快递混合支付
|
||||
if orderInfo.OrderType == model.OrderTypeDelivery {
|
||||
if isChoose == model.PayChooseBalance {
|
||||
// (1)使用余额且 余额大于支付金额
|
||||
if userBill.AccountBalance > orderInfo.PayPrice {
|
||||
globals.SugarLogger.Debug("进入余额支付部分")
|
||||
txDB, _ := dao.Begin(db)
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
panic(r)
|
||||
}
|
||||
}()
|
||||
//增加账单 余额减去相应金额
|
||||
money := userBill.AccountBalance - orderInfo.PayPrice
|
||||
if err = dao.UpdateUserBill(userBill.UserID, money); err != nil {
|
||||
return nil, "余额支付失败", err
|
||||
}
|
||||
//更新订单状态
|
||||
orderInfo.PayMethod = 1
|
||||
orderInfo.Status = 110
|
||||
if _, err := dao.UpdateEntityTx(txDB, orderInfo, "PayMethod"); err != nil {
|
||||
dao.Rollback(db, txDB)
|
||||
return nil, "更新order.PayMethod状态失败", err
|
||||
}
|
||||
if _, err := dao.UpdateEntityTx(txDB, orderInfo, "Status"); err != nil {
|
||||
dao.Rollback(db, txDB)
|
||||
return nil, "更新order.Status状态失败", err
|
||||
}
|
||||
//todo 后续需增加其他订单类型
|
||||
//更新快递 订单状态
|
||||
temp_vendor_status := 4
|
||||
if _, err := dao.SetUserVendorOrderStatus(txDB, orderInfo.OrderID, temp_vendor_status); err != nil {
|
||||
dao.Rollback(db, txDB)
|
||||
return nil, "更新user_vendor_order状态失败", err
|
||||
}
|
||||
dao.Commit(db, txDB)
|
||||
//再次从数据库获取order、userOrder
|
||||
orderNew, err := dao.GetOrderByID(db, orderID)
|
||||
if err != nil {
|
||||
return nil, "获取orderNew失败", err
|
||||
}
|
||||
userOrder, err := dao.GetUserVendorOrder(db, orderNew.UserID, orderNew.OrderID)
|
||||
if err != nil {
|
||||
return nil, "获取userOrder失败", err
|
||||
}
|
||||
//快递单 同步到qbd
|
||||
if orderNew.Status == 110 && userOrder.OrderStatus == 4 {
|
||||
if err := q_bida.CreateOrder2QBiDa(userOrder, orderInfo.OrderID); err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
} else {
|
||||
return nil, "order/user_vendor_order更新状态出错", nil
|
||||
}
|
||||
} else {
|
||||
//(2)用户选中余额 但余额<订单总价 需混合微信支付
|
||||
if userBill.AccountBalance == 0 {
|
||||
WxPayParam, err := Pay(ctx, orderInfo.OrderID, payType, vendorPayType, appID, orderInfo.PayPrice)
|
||||
if err != nil {
|
||||
return nil, "微信支付失败:", err
|
||||
}
|
||||
return WxPayParam, "", err
|
||||
} else if userBill.AccountBalance > 0 {
|
||||
//(2)用户使用余额,剩余微信支付
|
||||
totalPrice := orderInfo.PayPrice //订单原价
|
||||
needPay := totalPrice - userBill.AccountBalance //需支付金额
|
||||
WxPayParam, err := Pay(ctx, orderInfo.OrderID, payType, vendorPayType, appID, needPay)
|
||||
if err != nil {
|
||||
return nil, "微信支付失败:", err
|
||||
}
|
||||
return WxPayParam, "", err
|
||||
}
|
||||
}
|
||||
}
|
||||
if isChoose == model.PayNotChooseBalance {
|
||||
//(3)不选中余额支付 即直接微信支付
|
||||
WxPayParam, err := Pay(ctx, orderInfo.OrderID, payType, vendorPayType, appID, orderInfo.PayPrice)
|
||||
if err != nil {
|
||||
return nil, "微信支付失败:", err
|
||||
}
|
||||
return WxPayParam, "", err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
func Cash(ctx *jxcontext.Context, orderID string, payType int, vendorPayType string) (errCode string, err error) {
|
||||
var (
|
||||
db = dao.GetDB()
|
||||
order = &model.Order{
|
||||
OrderID: orderID,
|
||||
}
|
||||
payHandler = &financial.PayHandler{
|
||||
PayType: payType,
|
||||
Ctx: ctx,
|
||||
VendorPayType: vendorPayType,
|
||||
}
|
||||
)
|
||||
err = dao.GetEntity(db, order, "OrderID")
|
||||
if err != nil {
|
||||
return errCode, err
|
||||
}
|
||||
if order.ID == 0 {
|
||||
return errCode, fmt.Errorf("未找到此订单!")
|
||||
}
|
||||
payHandler.Order = order
|
||||
err = payHandler.CreateRefund()
|
||||
return errCode, err
|
||||
}
|
||||
|
||||
func GetOrders(ctx *jxcontext.Context, orderID, userID string, orderType int, cityCodes []int, fromTime, toTime, keyword string, offset, pageSize int) (pageInfo *model.PagedInfo, err error) {
|
||||
return dao.GetOrders(dao.GetDB(), orderID, userID, orderType, cityCodes, utils.Str2Time(fromTime), utils.Str2Time(toTime), keyword, offset, pageSize)
|
||||
}
|
||||
|
||||
//id获取订单详情(单条查询)
|
||||
//func GetOrderByID(ctx *jxcontext.Context, orderID int) (errMsg string, err error) {
|
||||
// //var db = dao.GetDB()
|
||||
// if _, err := dao.GetOrderByID(dao.GetDB(), orderID); err != nil {
|
||||
// return "id获取订单详情失败", err
|
||||
// }
|
||||
// return "", err
|
||||
//}
|
||||
|
||||
func FinishedCashOrders(ctx *jxcontext.Context, orderIDs []string) (err error) {
|
||||
var (
|
||||
db = dao.GetDB()
|
||||
)
|
||||
for _, orderID := range orderIDs {
|
||||
order := &model.Order{
|
||||
OrderID: orderID,
|
||||
}
|
||||
dao.GetEntity(db, order, "OrderID")
|
||||
if order.ID != 0 && order.Status == model.OrderStatusWait4Pay {
|
||||
order.PayFinishedAt = time.Now()
|
||||
order.Comment = "手动转账"
|
||||
order.Status = model.OrderStatusFinished
|
||||
dao.UpdateEntity(db, order, "PayFinishedAt", "Comment", "Status")
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func GetPayStatistics(ctx *jxcontext.Context, userID string, pop int, cityCodes []int, mobile, fromTime, toTime string, consumeTypes []int) (getPayStatisticsResult *dao.GetPayStatisticsResult, err error) {
|
||||
var (
|
||||
db = dao.GetDB()
|
||||
)
|
||||
return dao.GetPayStatistics(db, userID, pop, cityCodes, mobile, utils.Str2Time(fromTime), utils.Str2Time(toTime), consumeTypes)
|
||||
}
|
||||
|
||||
func GetManageStatisticsImg(ctx *jxcontext.Context, cityCodes []int, fromTime, toTime string, jobIDs []int) (getManageStatistics []*dao.GetManageStatisticsResult, err error) {
|
||||
var (
|
||||
db = dao.GetDB()
|
||||
fromTimeT = utils.Str2Time(fromTime)
|
||||
toTimeT = utils.Str2Time(toTime)
|
||||
)
|
||||
for i := 1; i < utils.Float64TwoInt(toTimeT.Sub(fromTimeT).Hours()/24)+1; i++ {
|
||||
getManageStatisticsResult, _ := dao.GetManageStatistics(db, cityCodes, fromTimeT.AddDate(0, 0, i-1), jobIDs)
|
||||
getManageStatistics = append(getManageStatistics, getManageStatisticsResult)
|
||||
}
|
||||
return getManageStatistics, err
|
||||
}
|
||||
|
||||
func GetManageStatisticsJob(ctx *jxcontext.Context, cityCodes []int, fromTime, toTime string, jobIDs []int, offset, pageSize int) (paged *model.PagedInfo, err error) {
|
||||
var (
|
||||
db = dao.GetDB()
|
||||
)
|
||||
return dao.GetManageStatisticsJob(db, cityCodes, utils.Str2Time(fromTime), utils.Str2Time(toTime), jobIDs, offset, pageSize)
|
||||
}
|
||||
|
||||
func NewUnionOrder(unionOrder *model.UnionOrder, orderStatus *model.UnionOrderStatus) (err error) {
|
||||
var (
|
||||
db = dao.GetDB()
|
||||
userID string
|
||||
)
|
||||
txDB, _ := dao.Begin(db)
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
dao.Rollback(db, txDB)
|
||||
panic(r)
|
||||
}
|
||||
}()
|
||||
userBinds, err := dao.GetUserUnionBind(db, "", unionOrder.VendorID, unionOrder.PID)
|
||||
if err != nil {
|
||||
dao.Rollback(db, txDB)
|
||||
return err
|
||||
}
|
||||
if len(userBinds) == 0 {
|
||||
userID = unionOrder.PID
|
||||
} else {
|
||||
userID = userBinds[0].UserID
|
||||
}
|
||||
unionOrder.UserID = userID
|
||||
dao.WrapAddIDCULEntity(unionOrder, jxcontext.AdminCtx.GetUserName())
|
||||
if err = dao.CreateEntityTx(txDB, unionOrder); err != nil {
|
||||
dao.Rollback(db, txDB)
|
||||
return err
|
||||
}
|
||||
dao.WrapAddIDCULEntity(orderStatus, jxcontext.AdminCtx.GetUserName())
|
||||
if err = dao.CreateEntityTx(txDB, orderStatus); err != nil {
|
||||
dao.Rollback(db, txDB)
|
||||
return err
|
||||
}
|
||||
dao.Commit(db, txDB)
|
||||
//发消息
|
||||
if err == nil {
|
||||
orderMessage(unionOrder)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func ChangeUnionOrder(unionOrder *model.UnionOrder, orderStatus *model.UnionOrderStatus) (err error) {
|
||||
var (
|
||||
db = dao.GetDB()
|
||||
)
|
||||
globals.SugarLogger.Debugf("ChangeUnionOrder1, unionorder: %v", utils.Format4Output(unionOrder, true))
|
||||
txDB, _ := dao.Begin(db)
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
dao.Rollback(db, txDB)
|
||||
panic(r)
|
||||
}
|
||||
}()
|
||||
unionOrder.Status = orderStatus.Status
|
||||
globals.SugarLogger.Debugf("ChangeUnionOrder2, unionorder: %v", utils.Format4Output(unionOrder, true))
|
||||
if _, err = dao.UpdateEntityTx(txDB, unionOrder, "Status"); err != nil {
|
||||
dao.Rollback(db, txDB)
|
||||
fmt.Println("err1", err)
|
||||
return err
|
||||
}
|
||||
dao.WrapAddIDCULEntity(orderStatus, jxcontext.AdminCtx.GetUserName())
|
||||
if err = dao.CreateEntityTx(txDB, orderStatus); err != nil {
|
||||
dao.Rollback(db, txDB)
|
||||
fmt.Println("err2", err)
|
||||
return err
|
||||
}
|
||||
dao.Commit(db, txDB)
|
||||
//发消息
|
||||
if err == nil {
|
||||
orderMessage(unionOrder)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func orderMessage(unionOrder *model.UnionOrder) {
|
||||
content := new(strings.Builder)
|
||||
content.WriteString("您有[")
|
||||
content.WriteString(model.VendorChineseNames[unionOrder.VendorID])
|
||||
content.WriteString("]平台的推广订单:")
|
||||
content.WriteString(unionOrder.VendorOrderID)
|
||||
content.WriteString("。")
|
||||
content.WriteString(model.UnionOrderStatusName[unionOrder.Status])
|
||||
content.WriteString("。预计返佣:")
|
||||
content.WriteString(jxutils.IntPrice2StandardString(int64(unionOrder.PromotionAmount)))
|
||||
content.WriteString("元。")
|
||||
event.SendSysMessageSimple(content.String(), unionOrder.UserID)
|
||||
}
|
||||
|
||||
func GetMyUnionOrders(ctx *jxcontext.Context, statuss []int, vendorID, offset, pageSize int) (page *model.PagedInfo, err error) {
|
||||
return dao.GetMyUnionOrders(dao.GetDB(), ctx.GetUserID(), statuss, vendorID, offset, pageSize)
|
||||
}
|
||||
|
||||
func GetUnionOrders(ctx *jxcontext.Context, vendorIDs, statuss []int, beginTime, endTime, keyword string, offset, pageSize int) (page *model.PagedInfo, err error) {
|
||||
return dao.GetUnionOrdersPage(dao.GetDB(), vendorIDs, statuss, utils.Str2Time(beginTime), utils.Str2Time(endTime), keyword, offset, pageSize)
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
package cms
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"git.rosy.net.cn/baseapi/platformapi/recharge_phone_bill"
|
||||
"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/globals/api"
|
||||
"time"
|
||||
)
|
||||
|
||||
// QueryUserRecharge 用户查询充值列表
|
||||
func QueryUserRecharge(userId []string, mobile, orderId string, page, pageSize int, startTime, endTime string, rechargeStatus int) ([]*model.RechargeUserModelData, int, error) {
|
||||
if page == 0 {
|
||||
page = 1
|
||||
}
|
||||
if pageSize == 0 {
|
||||
pageSize = 10
|
||||
}
|
||||
var start time.Time
|
||||
var end time.Time
|
||||
if startTime != "" {
|
||||
start = utils.Str2Time(startTime)
|
||||
}
|
||||
if endTime != "" {
|
||||
end = utils.Str2Time(endTime)
|
||||
}
|
||||
|
||||
return dao.QueryRechargeRecommend(userId, mobile, orderId, page, pageSize, start, end, rechargeStatus)
|
||||
}
|
||||
|
||||
// QueryUserOrderDetail 用户查询订单详情
|
||||
func QueryUserOrderDetail(orderId, mobile string) ([]recharge_phone_bill.QueryOrderDetailResList, error) {
|
||||
data, err := api.TelephoneAPI.QueryOrderDetail("", orderId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
have := false
|
||||
for _, v := range data {
|
||||
if v.Mobile == mobile {
|
||||
have = true
|
||||
}
|
||||
}
|
||||
|
||||
if have {
|
||||
return data, err
|
||||
}
|
||||
return nil, errors.New("参数电话号码异常")
|
||||
}
|
||||
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)
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
package cms
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
"net"
|
||||
"os"
|
||||
)
|
||||
|
||||
var (
|
||||
textChan chan string
|
||||
)
|
||||
|
||||
func TestTemp2() {
|
||||
server := "127.0.0.1:8000"
|
||||
tcpAddr, err := net.ResolveTCPAddr("tcp4", server)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
conn, err := net.DialTCP("tcp", nil, tcpAddr)
|
||||
if err != nil {
|
||||
fmt.Println("Dial err:", err)
|
||||
return
|
||||
}
|
||||
textChan = make(chan string, 10)
|
||||
connHandler2(conn)
|
||||
}
|
||||
|
||||
func connHandler2(c net.Conn) {
|
||||
//接收终端输入
|
||||
//reader := bufio.NewReader(os.Stdin)
|
||||
//缓冲
|
||||
//buf := make([]byte, 1024)
|
||||
fmt.Println("Please input data...")
|
||||
go func() {
|
||||
for {
|
||||
//读取终端输入直到读取到\n
|
||||
//input, err := reader.ReadString('\n')
|
||||
//if err != nil {
|
||||
// fmt.Println("ReadString err:", err)
|
||||
// return
|
||||
//}
|
||||
s := <-textChan
|
||||
//写入数据
|
||||
n, err := c.Write([]byte(s))
|
||||
if err != nil {
|
||||
fmt.Println("Write err:", err, n)
|
||||
return
|
||||
}
|
||||
//服务器端返回的数据写入buf
|
||||
//cnt, err := c.Read(buf)
|
||||
//if err != nil {
|
||||
// fmt.Println("Read err:", err)
|
||||
// return
|
||||
//}
|
||||
//服务器端回传的信息
|
||||
//fmt.Println("server response:", string(buf[0:cnt]))
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
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...)
|
||||
}
|
||||
@@ -1,7 +1,16 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"git.rosy.net.cn/baseapi/platformapi/autonavi"
|
||||
"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/model"
|
||||
"git.rosy.net.cn/jx-callback/business/model/dao"
|
||||
"git.rosy.net.cn/jx-callback/globals/api"
|
||||
)
|
||||
|
||||
type Store4User struct {
|
||||
@@ -53,166 +62,228 @@ func (x Store4UserList) Swap(i, j int) {
|
||||
x[i], x[j] = x[j], x[i]
|
||||
}
|
||||
|
||||
//
|
||||
//func GetNearSupplyGoodsStoreByStoreID(ctx *jxcontext.Context, storeID int) (store *model.Store, err error) {
|
||||
// var (
|
||||
// stores []*model.Store
|
||||
// db = dao.GetDB()
|
||||
// )
|
||||
// store2, _ := dao.GetStoreDetail(db, storeID, model.VendorIDJX)
|
||||
// if store2 == nil {
|
||||
// return nil, fmt.Errorf("该门店未绑定京西平台!storeID: %v", storeID)
|
||||
// }
|
||||
// if store2.IsSupplyGoods == model.YES {
|
||||
// return nil, fmt.Errorf("该门店已经是货源门店,无法从其他货源门店进货!storeID: %v", storeID)
|
||||
// }
|
||||
// sql := `
|
||||
// SELECT a.*
|
||||
// FROM store a
|
||||
// JOIN store_map b ON b.store_id = a.id
|
||||
// JOIN store c ON c.city_code = a.city_code AND c.id = ?
|
||||
// WHERE a.deleted_at = ?
|
||||
// AND b.deleted_at = ?
|
||||
// AND b.vendor_id = ?
|
||||
// AND b.is_supply_goods = ?
|
||||
// AND a.status = ?
|
||||
// `
|
||||
// sqlParams := []interface{}{
|
||||
// storeID,
|
||||
// utils.DefaultTimeValue, utils.DefaultTimeValue,
|
||||
// model.VendorIDJX, model.YES, model.StoreStatusOpened,
|
||||
// }
|
||||
// err = dao.GetRows(db, &stores, sql, sqlParams)
|
||||
// if len(stores) > 0 {
|
||||
// realDistance := float64(0)
|
||||
// for _, v := range stores {
|
||||
// distance := jxutils.EarthDistance(jxutils.IntCoordinate2Standard(v.Lng), jxutils.IntCoordinate2Standard(v.Lat), jxutils.IntCoordinate2Standard(store2.Lng), jxutils.IntCoordinate2Standard(store2.Lat))
|
||||
// if realDistance == 0 {
|
||||
// realDistance = distance
|
||||
// store = v
|
||||
// } else {
|
||||
// if realDistance > distance {
|
||||
// realDistance = distance
|
||||
// store = v
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// return store, err
|
||||
//}
|
||||
//
|
||||
//func GetStoreListByLocation(ctx *jxcontext.Context, lng, lat float64, maxRadius int, needWalkDistance, isJds bool) (storeList []*Store4User, err error) {
|
||||
// const (
|
||||
// maxStoreCount4User = 5
|
||||
// )
|
||||
//
|
||||
// var (
|
||||
// sql string
|
||||
// sqlParams []interface{}
|
||||
// )
|
||||
//
|
||||
// lng2, _ := jxutils.ConvertDistanceToLogLat(lng, lat, float64(maxRadius), 90)
|
||||
// _, lat2 := jxutils.ConvertDistanceToLogLat(lng, lat, float64(maxRadius), 0)
|
||||
// lng1 := lng - (lng2 - lng)
|
||||
// lat1 := lat - (lat2 - lat)
|
||||
// // globals.SugarLogger.Debugf("%f,%f,%f,%f\n", lng1, lng2, lat1, lat2)
|
||||
// if !isJds {
|
||||
// sql = `
|
||||
// SELECT t1.*,
|
||||
// city.name city_name
|
||||
// FROM store t1
|
||||
// JOIN place city ON city.code = t1.city_code
|
||||
// JOIN store_map sm ON sm.store_id = t1.id AND sm.vendor_id = ? AND sm.deleted_at = ? AND sm.status <> ?
|
||||
// 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{}{
|
||||
// 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,
|
||||
// }
|
||||
// } else {
|
||||
// sql = `
|
||||
// SELECT t1.*,
|
||||
// city.name city_name
|
||||
// FROM store t1
|
||||
// JOIN place city ON city.code = t1.city_code
|
||||
// JOIN store_map sm ON sm.store_id = t1.id AND sm.vendor_id = ? AND sm.deleted_at = ? AND sm.status = ?
|
||||
// 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{}{
|
||||
// model.VendorIDJDShop, utils.DefaultTimeValue, model.StoreStatusOpened,
|
||||
// utils.DefaultTimeValue, model.StoreStatusOpened, jxutils.StandardCoordinate2Int(lng1), jxutils.StandardCoordinate2Int(lng2), jxutils.StandardCoordinate2Int(lat1), jxutils.StandardCoordinate2Int(lat2),
|
||||
// model.YES,
|
||||
// model.MatterStoreID,
|
||||
// }
|
||||
// }
|
||||
// var storeList1 []*Store4User
|
||||
// if err = dao.GetRows(dao.GetDB(), &storeList1, sql, sqlParams...); err == nil {
|
||||
// var storeList2 []*Store4User
|
||||
// for _, v := range storeList1 {
|
||||
// distance := jxutils.Point2StoreDistance(lng, lat, v.Lng, v.Lat, v.DeliveryRangeType, v.DeliveryRange)
|
||||
// if distance > 0 || (lng == jxutils.IntCoordinate2Standard(v.Lng) && lat == jxutils.IntCoordinate2Standard(v.Lat)) {
|
||||
// v.Distance = distance
|
||||
// storeList2 = append(storeList2, v)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // 为了审核用
|
||||
// if len(storeList2) == 0 {
|
||||
// sql2 := `
|
||||
// SELECT t1.*,
|
||||
// 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 = ?
|
||||
// `
|
||||
// sqlParams2 := []interface{}{
|
||||
// // model.VendorIDJX, utils.DefaultTimeValue, model.StoreStatusDisabled,
|
||||
// utils.DefaultTimeValue,
|
||||
// model.StoreStatusDisabled,
|
||||
// // jxutils.StandardCoordinate2Int(0),
|
||||
// // jxutils.StandardCoordinate2Int(10000),
|
||||
// // jxutils.StandardCoordinate2Int(0),
|
||||
// // jxutils.StandardCoordinate2Int(10000),
|
||||
// // model.YES,
|
||||
// 102919, //商城模板店
|
||||
// }
|
||||
// dao.GetRows(dao.GetDB(), &storeList2, sql2, sqlParams2...)
|
||||
// // if len(storeList2) > 1 {
|
||||
// // storeList2 = storeList2[:1]
|
||||
// // }
|
||||
// }
|
||||
//
|
||||
// // 如果要求以步行距离来算
|
||||
// if needWalkDistance {
|
||||
// var coordList []*autonavi.Coordinate
|
||||
// for _, v := range storeList2 {
|
||||
// coordList = append(coordList, &autonavi.Coordinate{
|
||||
// Lng: v.FloatLng,
|
||||
// Lat: v.FloatLat,
|
||||
// })
|
||||
// }
|
||||
// if distanceList, err2 := api.AutonaviAPI.BatchWalkingDistance(lng, lat, coordList); err2 == nil {
|
||||
// for k, v := range storeList2 {
|
||||
// v.WalkDistance = int(distanceList[k])
|
||||
// }
|
||||
// } else {
|
||||
// return nil, err2
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// sort.Sort(Store4UserList(storeList2))
|
||||
// storeList = storeList2
|
||||
// if len(storeList) > maxStoreCount4User {
|
||||
// storeList = storeList[:maxStoreCount4User]
|
||||
// }
|
||||
// }
|
||||
// return storeList, err
|
||||
//}
|
||||
func GetNearSupplyGoodsStoreByStoreID(ctx *jxcontext.Context, storeID int) (store *model.Store, err error) {
|
||||
var (
|
||||
stores []*model.Store
|
||||
db = dao.GetDB()
|
||||
)
|
||||
store2, _ := dao.GetStoreDetail(db, storeID, model.VendorIDJX, "")
|
||||
if store2 == nil {
|
||||
return nil, fmt.Errorf("该门店未绑定京西平台!storeID: %v", storeID)
|
||||
}
|
||||
if store2.IsSupplyGoods == model.YES {
|
||||
return nil, fmt.Errorf("该门店已经是货源门店,无法从其他货源门店进货!storeID: %v", storeID)
|
||||
}
|
||||
sql := `
|
||||
SELECT a.*
|
||||
FROM store a
|
||||
JOIN store_map b ON b.store_id = a.id
|
||||
JOIN store c ON c.city_code = a.city_code AND c.id = ?
|
||||
WHERE a.deleted_at = ?
|
||||
AND b.deleted_at = ?
|
||||
AND b.vendor_id = ?
|
||||
AND b.is_supply_goods = ?
|
||||
AND a.status = ?
|
||||
`
|
||||
sqlParams := []interface{}{
|
||||
storeID,
|
||||
utils.DefaultTimeValue, utils.DefaultTimeValue,
|
||||
model.VendorIDJX, model.YES, model.StoreStatusOpened,
|
||||
}
|
||||
err = dao.GetRows(db, &stores, sql, sqlParams)
|
||||
if len(stores) > 0 {
|
||||
realDistance := float64(0)
|
||||
for _, v := range stores {
|
||||
distance := jxutils.EarthDistance(jxutils.IntCoordinate2Standard(v.Lng), jxutils.IntCoordinate2Standard(v.Lat), jxutils.IntCoordinate2Standard(store2.Lng), jxutils.IntCoordinate2Standard(store2.Lat))
|
||||
if realDistance == 0 {
|
||||
realDistance = distance
|
||||
store = v
|
||||
} else {
|
||||
if realDistance > distance {
|
||||
realDistance = distance
|
||||
store = v
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return store, err
|
||||
}
|
||||
|
||||
func GetStoreListByLocation(ctx *jxcontext.Context, lng, lat float64, maxRadius int, needWalkDistance, isJds bool, brandID int) (storeList []*Store4User, err error) {
|
||||
const (
|
||||
maxStoreCount4User = 5
|
||||
)
|
||||
|
||||
var (
|
||||
sql string
|
||||
sqlParams []interface{}
|
||||
)
|
||||
|
||||
lng2, _ := jxutils.ConvertDistanceToLogLat(lng, lat, float64(maxRadius), 90)
|
||||
_, lat2 := jxutils.ConvertDistanceToLogLat(lng, lat, float64(maxRadius), 0)
|
||||
lng1 := lng - (lng2 - lng)
|
||||
lat1 := lat - (lat2 - lat)
|
||||
// globals.SugarLogger.Debugf("%f,%f,%f,%f\n", lng1, lng2, lat1, lat2)
|
||||
if !isJds {
|
||||
sql = `
|
||||
SELECT t1.*,
|
||||
city.name city_name
|
||||
FROM store t1
|
||||
JOIN place city ON city.code = t1.city_code
|
||||
JOIN store_map sm ON sm.store_id = t1.id AND sm.vendor_id = ? AND sm.deleted_at = ? AND sm.status <> ?
|
||||
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 <> ?
|
||||
`
|
||||
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.*,
|
||||
city.name city_name
|
||||
FROM store t1
|
||||
JOIN place city ON city.code = t1.city_code
|
||||
JOIN store_map sm ON sm.store_id = t1.id AND sm.vendor_id = ? AND sm.deleted_at = ? AND sm.status = ?
|
||||
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{}{
|
||||
model.VendorIDJDShop, utils.DefaultTimeValue, model.StoreStatusOpened,
|
||||
utils.DefaultTimeValue, model.StoreStatusOpened, jxutils.StandardCoordinate2Int(lng1), jxutils.StandardCoordinate2Int(lng2), jxutils.StandardCoordinate2Int(lat1), jxutils.StandardCoordinate2Int(lat2),
|
||||
model.YES,
|
||||
model.MatterStoreID,
|
||||
}
|
||||
}
|
||||
var storeList1 []*Store4User
|
||||
if err = dao.GetRows(dao.GetDB(), &storeList1, sql, sqlParams...); err == nil {
|
||||
var storeList2 []*Store4User
|
||||
for _, v := range storeList1 {
|
||||
distance := jxutils.Point2StoreDistance(lng, lat, v.Lng, v.Lat, v.DeliveryRangeType, v.DeliveryRange)
|
||||
if distance > 0 || (lng == jxutils.IntCoordinate2Standard(v.Lng) && lat == jxutils.IntCoordinate2Standard(v.Lat)) {
|
||||
v.Distance = distance
|
||||
storeList2 = append(storeList2, v)
|
||||
}
|
||||
}
|
||||
|
||||
// 为了审核用
|
||||
if len(storeList2) == 0 {
|
||||
sql2 := `
|
||||
SELECT t1.*,
|
||||
city.name city_name
|
||||
FROM store t1
|
||||
JOIN place city ON city.code = t1.city_code
|
||||
WHERE t1.deleted_at = ? AND t1.id = ?
|
||||
`
|
||||
sqlParams2 := []interface{}{
|
||||
// model.VendorIDJX, utils.DefaultTimeValue, model.StoreStatusDisabled,
|
||||
utils.DefaultTimeValue,
|
||||
// model.StoreStatusDisabled,
|
||||
// jxutils.StandardCoordinate2Int(0),
|
||||
// jxutils.StandardCoordinate2Int(10000),
|
||||
// jxutils.StandardCoordinate2Int(0),
|
||||
// jxutils.StandardCoordinate2Int(10000),
|
||||
// model.YES,
|
||||
102919, //商城模板店
|
||||
}
|
||||
dao.GetRows(dao.GetDB(), &storeList2, sql2, sqlParams2...)
|
||||
// if len(storeList2) > 1 {
|
||||
// storeList2 = storeList2[:1]
|
||||
// }
|
||||
}
|
||||
|
||||
// 如果要求以步行距离来算
|
||||
if needWalkDistance {
|
||||
var coordList []*autonavi.Coordinate
|
||||
for _, v := range storeList2 {
|
||||
coordList = append(coordList, &autonavi.Coordinate{
|
||||
Lng: v.FloatLng,
|
||||
Lat: v.FloatLat,
|
||||
})
|
||||
}
|
||||
if distanceList, err2 := api.AutonaviAPI.BatchWalkingDistance(lng, lat, coordList); err2 == nil {
|
||||
for k, v := range storeList2 {
|
||||
v.WalkDistance = int(distanceList[k])
|
||||
}
|
||||
} else {
|
||||
return nil, err2
|
||||
}
|
||||
}
|
||||
|
||||
sort.Sort(Store4UserList(storeList2))
|
||||
storeList = storeList2
|
||||
if len(storeList) > maxStoreCount4User {
|
||||
storeList = storeList[:maxStoreCount4User]
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
@@ -4,179 +4,29 @@ import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
"git.rosy.net.cn/jx-callback/business/jxstore/financial"
|
||||
"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/gorilla/websocket"
|
||||
"git.rosy.net.cn/jx-callback/globals/api"
|
||||
)
|
||||
|
||||
var (
|
||||
NoUseEventMap = map[string]string{
|
||||
"CreateQrOrBarCode": "CreateQrOrBarCode",
|
||||
"StatisticsReportForOrders": "StatisticsReportForOrders",
|
||||
"UpdateUser": "UpdateUser",
|
||||
}
|
||||
regexpToken = regexp.MustCompile(`,"token":".*"`)
|
||||
wsClient = &WSClient{}
|
||||
//广播频道(通道)
|
||||
broadcast = make(chan *model.ImMessageRecord)
|
||||
)
|
||||
|
||||
const (
|
||||
sysMessageTitle = ""
|
||||
)
|
||||
|
||||
//连接的客户端,吧每个客户端都放进来
|
||||
type WSClient struct {
|
||||
Clients map[int]map[string]*websocket.Conn
|
||||
ClientsHeart map[string]*websocket.Conn
|
||||
s *sync.RWMutex
|
||||
}
|
||||
|
||||
func init() {
|
||||
clients := make(map[int]map[string]*websocket.Conn)
|
||||
clientsHeart := make(map[string]*websocket.Conn)
|
||||
wsClient.Clients = clients
|
||||
wsClient.ClientsHeart = clientsHeart
|
||||
wsClient.s = new(sync.RWMutex)
|
||||
go handleMessages()
|
||||
}
|
||||
|
||||
//广播推送消息
|
||||
func handleMessages() {
|
||||
for {
|
||||
//读取通道中的消息
|
||||
msg := <-broadcast
|
||||
if msg.GroupID == 0 {
|
||||
// globals.SugarLogger.Debugf("heart %v", utils.Format4Output(msg, false))
|
||||
if wsClient.ClientsHeart[msg.UserID] != nil {
|
||||
if err := wsClient.ClientsHeart[msg.UserID].WriteJSON(&model.ImMessageRecord{
|
||||
Key: "pang",
|
||||
}); err != nil {
|
||||
globals.SugarLogger.Debugf("heart client.WriteJSON error: %v", err)
|
||||
wsClient.ClientsHeart[msg.UserID].Close() //关闭
|
||||
delete(wsClient.ClientsHeart, msg.UserID)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
globals.SugarLogger.Debugf("clients len %v", len(wsClient.Clients))
|
||||
//循环map客户端
|
||||
for userID, client := range wsClient.Clients[msg.GroupID] {
|
||||
//把通道中的消息发送给客户端
|
||||
user, err := dao.GetUser(dao.GetDB(), msg.UserID)
|
||||
if err == nil {
|
||||
msg.UserInfo = user
|
||||
}
|
||||
globals.SugarLogger.Debugf("msg %v", utils.Format4Output(msg, false))
|
||||
if msg.CreatedAt == utils.ZeroTimeValue {
|
||||
msg.CreatedAt = time.Now()
|
||||
}
|
||||
err = client.WriteJSON(msg)
|
||||
if err != nil {
|
||||
globals.SugarLogger.Debugf("client.WriteJSON error: %v", err)
|
||||
client.Close() //关闭
|
||||
delete(wsClient.Clients[msg.GroupID], userID)
|
||||
// delete(clients, client) //删除map中的客户端
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func ImMessage(userID string, ws *websocket.Conn) (err error) {
|
||||
var (
|
||||
clientUser = make(map[string]*websocket.Conn)
|
||||
db = dao.GetDB()
|
||||
)
|
||||
//将当前客户端放入map中
|
||||
messageGroups, _ := dao.GetUserMessageGroups(dao.GetDB(), userID)
|
||||
if len(messageGroups) == 0 {
|
||||
return
|
||||
}
|
||||
wsClient.s.Lock()
|
||||
clientUser[userID] = ws
|
||||
wsClient.ClientsHeart[userID] = ws
|
||||
for _, v := range messageGroups {
|
||||
if len(wsClient.Clients[v.GroupID]) > 0 {
|
||||
wsClient.Clients[v.GroupID][userID] = ws
|
||||
} else {
|
||||
wsClient.Clients[v.GroupID] = clientUser
|
||||
}
|
||||
}
|
||||
wsClient.s.Unlock()
|
||||
globals.SugarLogger.Debugf("userID :%v ,clients :%v", userID, utils.Format4Output(wsClient.Clients, false))
|
||||
|
||||
var s *model.ImMessageRecord
|
||||
for {
|
||||
//接收客户端的消息
|
||||
err := ws.ReadJSON(&s)
|
||||
if err != nil {
|
||||
globals.SugarLogger.Debugf("页面可能断开啦 ws.ReadJSON error: %v", err.Error())
|
||||
for k, _ := range wsClient.Clients {
|
||||
delete(wsClient.Clients[k], userID)
|
||||
}
|
||||
delete(wsClient.ClientsHeart, userID)
|
||||
// delete(clients, ws) //删除map中的客户端
|
||||
break //结束循环
|
||||
} else {
|
||||
//接受消息 业务逻辑
|
||||
broadcast <- s
|
||||
if s.GroupID != 0 {
|
||||
if s.GroupID != model.SysGroupID {
|
||||
//发聊天消息时,这个组所有的成员包括创建者都在userIDs里
|
||||
userIDs := []string{}
|
||||
if results, err := dao.GetMessageGroups(db, "", s.GroupID, 0, true, ""); err == nil {
|
||||
for _, v := range results {
|
||||
userIDs = append(userIDs, v.UserID)
|
||||
for _, vv := range v.MessageGroupMembers {
|
||||
userIDs = append(userIDs, vv.UserID)
|
||||
}
|
||||
}
|
||||
}
|
||||
//如果这些人不在这个组的ws池子里就打上未读标记
|
||||
for _, v := range userIDs {
|
||||
if wsClient.ClientsHeart[v] == nil {
|
||||
messageGroupReads, _ := dao.GetMessageGroupRead(db, v, s.GroupID)
|
||||
for _, vv := range messageGroupReads {
|
||||
vv.UnReadCount++
|
||||
dao.UpdateEntity(db, vv, "UnReadCount")
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if wsClient.ClientsHeart[s.ToUserID] == nil {
|
||||
messageGroupReads, _ := dao.GetMessageGroupRead(db, s.ToUserID, s.GroupID)
|
||||
for _, vv := range messageGroupReads {
|
||||
vv.UnReadCount++
|
||||
dao.UpdateEntity(db, vv, "UnReadCount")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
utils.CallFuncAsync(func() {
|
||||
if s.GroupID != 0 {
|
||||
dao.WrapAddIDCULDEntity(s, "")
|
||||
dao.CreateEntity(db, s)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
ws.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
func GetOnlineUserCount() (count int) {
|
||||
wsClient.s.RLock()
|
||||
count = len(wsClient.ClientsHeart)
|
||||
wsClient.s.RUnlock()
|
||||
return count
|
||||
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) {
|
||||
@@ -217,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)
|
||||
@@ -230,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)
|
||||
@@ -246,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)
|
||||
@@ -284,505 +134,68 @@ func GetOperateEvents(ctx *jxcontext.Context, name string, apiFunctions []string
|
||||
return pageInfo, err
|
||||
}
|
||||
|
||||
func CreateMessageGroup(ctx *jxcontext.Context, userID, userID2, groupName string, dividePercentage, quitPrice int) (messageGroupResult *dao.GetMessageGroupsResult, err error) {
|
||||
func GetCheckVendorCookie(ctx *jxcontext.Context, vendorIDs []int, isAuto bool) (ccList []*CheckCookie, err error) {
|
||||
var (
|
||||
db = dao.GetDB()
|
||||
groupID int
|
||||
ebaiOrderID = "1577329467196263592"
|
||||
ebaiErr = "return not json"
|
||||
ebaiErr2 = "系统错误"
|
||||
mtStoreID = "7388603"
|
||||
mtErr = "返回结果格式不正常"
|
||||
jdUpcCode = "6952395700895"
|
||||
jdErr = "请输入用户名"
|
||||
// mtpsErr = "用户未登录"
|
||||
errMsg = ""
|
||||
)
|
||||
for {
|
||||
groupID = jxutils.GenRand6()
|
||||
temp := &model.MessageGroup{
|
||||
GroupID: groupID,
|
||||
}
|
||||
dao.GetEntity(db, temp, "GroupID")
|
||||
if temp.UserID == "" {
|
||||
break
|
||||
}
|
||||
}
|
||||
if userID2 != "" {
|
||||
messageGroups, err := dao.GetMessageGroups(db, userID, 0, model.GroupTypeSingle, true, userID2)
|
||||
if len(messageGroups) > 0 && len(messageGroups[0].MessageGroupMembers) > 0 {
|
||||
return messageGroups[0], err
|
||||
}
|
||||
user, err := dao.GetUserByID(db, "user_id", userID2)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if user == nil {
|
||||
return nil, fmt.Errorf("无法找到要联系的用户!")
|
||||
}
|
||||
messageGroup := &model.MessageGroup{
|
||||
GroupID: groupID,
|
||||
UserID: userID,
|
||||
// Name: user.Name,
|
||||
Type: model.GroupTypeSingle,
|
||||
MaxCount: 2,
|
||||
}
|
||||
messageGroupMember := &model.MessageGroupMember{
|
||||
GroupID: groupID,
|
||||
MemberUserID: userID2,
|
||||
Type: model.GroupMemberTypeNormal,
|
||||
}
|
||||
dao.WrapAddIDCULDEntity(messageGroup, ctx.GetUserName())
|
||||
dao.WrapAddIDCULDEntity(messageGroupMember, ctx.GetUserName())
|
||||
err = dao.CreateEntity(db, messageGroup)
|
||||
err = dao.CreateEntity(db, messageGroupMember)
|
||||
if err == nil {
|
||||
messageGroupRead := &model.MessageGroupRead{
|
||||
GroupID: groupID,
|
||||
UserID: userID,
|
||||
}
|
||||
dao.WrapAddIDCULEntity(messageGroupRead, ctx.GetUserName())
|
||||
messageGroupRead2 := messageGroupRead
|
||||
messageGroupRead2.UserID = userID2
|
||||
dao.CreateEntity(db, messageGroupRead)
|
||||
if err = dao.CreateEntity(db, messageGroupRead2); err == nil {
|
||||
if messageGroupReads, err := dao.GetMessageGroupRead(db, userID2, model.SysGroupID); len(messageGroupReads) == 0 && err == nil {
|
||||
messageGroupRead := &model.MessageGroupRead{
|
||||
GroupID: model.SysGroupID,
|
||||
UserID: userID2,
|
||||
}
|
||||
dao.WrapAddIDCULEntity(messageGroupRead, ctx.GetUserName())
|
||||
dao.CreateEntity(db, messageGroupRead)
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
userMembers, err := dao.GetUserMember(db, userID, model.MemberTypeNormal)
|
||||
messageGroupsResult, err := dao.GetMessageGroups(db, userID, 0, model.GroupTypeMulit, false, "")
|
||||
messageGroupMembers, err := dao.GetMessageGroupMembers(db, 0, model.GroupTypeMulit, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(userMembers) == 0 {
|
||||
return nil, fmt.Errorf("抱歉,只有会员才能创建群聊!")
|
||||
}
|
||||
if len(messageGroupsResult) > 0 {
|
||||
return nil, fmt.Errorf("您已经有群组了,请勿重复创建!群号为:%d", messageGroupsResult[0].GroupID)
|
||||
}
|
||||
if len(messageGroupMembers) > 0 {
|
||||
return nil, fmt.Errorf("您已加入了[%v]群,请先退出后再创建!", messageGroupMembers[0].GroupID)
|
||||
}
|
||||
messageGroup := &model.MessageGroup{
|
||||
GroupID: groupID,
|
||||
UserID: userID,
|
||||
Name: groupName,
|
||||
Type: model.GroupTypeMulit,
|
||||
MaxCount: 2000,
|
||||
DividePercentage: dividePercentage,
|
||||
QuitPrice: quitPrice,
|
||||
}
|
||||
user, err := dao.GetUserByID(db, "user_id", userID)
|
||||
if groupName == "" {
|
||||
messageGroup.Name = user.Name + "的集团"
|
||||
}
|
||||
dao.WrapAddIDCULDEntity(messageGroup, ctx.GetUserName())
|
||||
if err = dao.CreateEntity(db, messageGroup); err == nil {
|
||||
messageGroupRead := &model.MessageGroupRead{
|
||||
GroupID: groupID,
|
||||
UserID: userID,
|
||||
}
|
||||
dao.WrapAddIDCULEntity(messageGroupRead, ctx.GetUserName())
|
||||
dao.CreateEntity(db, messageGroupRead)
|
||||
}
|
||||
}
|
||||
if err == nil {
|
||||
if messageGroupReads, err := dao.GetMessageGroupRead(db, userID, model.SysGroupID); len(messageGroupReads) == 0 && err == nil {
|
||||
messageGroupRead := &model.MessageGroupRead{
|
||||
GroupID: model.SysGroupID,
|
||||
UserID: userID,
|
||||
}
|
||||
dao.WrapAddIDCULEntity(messageGroupRead, ctx.GetUserName())
|
||||
dao.CreateEntity(db, messageGroupRead)
|
||||
}
|
||||
}
|
||||
return messageGroupResult, err
|
||||
}
|
||||
|
||||
func GetMessageGroupByUser(ctx *jxcontext.Context, userID string) (messageGroupResult []*dao.GetMessageGroupsResult, err error) {
|
||||
var (
|
||||
db = dao.GetDB()
|
||||
)
|
||||
messageGroups, err := dao.GetMessageGroups(db, "", model.SysGroupID, 0, false, "")
|
||||
messageGroups2, err := dao.GetMessageGroups(db, userID, 0, 0, true, "")
|
||||
messageGroups = append(messageGroups, messageGroups2...)
|
||||
messageGroupMembers, err := dao.GetMessageGroupMembers(db, 0, 0, userID)
|
||||
for _, v := range messageGroupMembers {
|
||||
if messageGroupList, err := dao.GetMessageGroups(db, "", v.GroupID, 0, false, ""); err == nil {
|
||||
messageGroups = append(messageGroups, messageGroupList...)
|
||||
}
|
||||
}
|
||||
for _, v := range messageGroups {
|
||||
//是这个人创建的群聊,如果是单聊就返回对方的头像
|
||||
if v.Type == model.GroupTypeSingle {
|
||||
var userID2 string
|
||||
if v.UserID == userID {
|
||||
userID2 = v.MessageGroupMembers[0].MemberUserID
|
||||
} else {
|
||||
userID2 = userID
|
||||
}
|
||||
if user, err := dao.GetUserByID(db, "user_id", userID2); err == nil {
|
||||
v.Avatar = user.Avatar
|
||||
v.Name = user.Name
|
||||
}
|
||||
}
|
||||
var (
|
||||
imMessageRecord *model.ImMessageRecord
|
||||
sql string
|
||||
sqlParams = []interface{}{}
|
||||
)
|
||||
if v.GroupID != model.SysGroupID {
|
||||
//最后一条记录和时间和人
|
||||
sql = `
|
||||
SELECT * FROM im_message_record WHERE group_id = ? ORDER BY created_at DESC LIMIT 1
|
||||
`
|
||||
sqlParams = append(sqlParams, v.GroupID)
|
||||
} else {
|
||||
//最后一条记录和时间和人
|
||||
sql = `
|
||||
SELECT * FROM im_message_record WHERE group_id = ? AND to_user_id = ? ORDER BY created_at DESC LIMIT 1
|
||||
`
|
||||
sqlParams = append(sqlParams, v.GroupID, userID)
|
||||
}
|
||||
if err = dao.GetRow(db, &imMessageRecord, sql, sqlParams); err == nil {
|
||||
v.LastTime = imMessageRecord.CreatedAt
|
||||
v.LastContent = imMessageRecord.Content
|
||||
v.LastMessageType = imMessageRecord.MessageType
|
||||
if user3, err := dao.GetUserByID(db, "user_id", imMessageRecord.UserID); err == nil {
|
||||
v.LastUserName = user3.Name
|
||||
}
|
||||
} else {
|
||||
err = nil
|
||||
}
|
||||
//该用户各组的未读消息数
|
||||
var unReadCount int
|
||||
if messageGroupReads, err := dao.GetMessageGroupRead(db, userID, v.GroupID); err == nil && len(messageGroupReads) > 0 {
|
||||
for _, vv := range messageGroupReads {
|
||||
unReadCount += vv.UnReadCount
|
||||
}
|
||||
}
|
||||
v.UnReadMessageCount = unReadCount
|
||||
}
|
||||
return messageGroups, err
|
||||
}
|
||||
|
||||
func AddMessageGroup(ctx *jxcontext.Context, groupID int, userID string) (err error) {
|
||||
var (
|
||||
db = dao.GetDB()
|
||||
)
|
||||
messageGroupMembers, err := dao.GetMessageGroupMembers(db, groupID, 0, userID)
|
||||
messageGroupMembers3, err := dao.GetMessageGroupMembers(db, 0, 0, userID)
|
||||
messageGroupsResult, err := dao.GetMessageGroups(db, userID, groupID, model.GroupTypeMulit, false, "")
|
||||
messageGroupsResult2, err := dao.GetMessageGroups(db, "", groupID, model.GroupTypeMulit, false, "")
|
||||
messageGroupsResult3, err := dao.GetMessageGroups(db, userID, 0, model.GroupTypeMulit, false, "")
|
||||
messageGroupMembers2, err := dao.GetMessageGroupMembers(db, groupID, 0, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(messageGroupMembers) > 0 {
|
||||
return fmt.Errorf("此用户已经在该群组中了!")
|
||||
}
|
||||
if len(messageGroupMembers3) > 0 {
|
||||
return fmt.Errorf("您已经有群组[%v]了,不能申请加入其它群!", messageGroupMembers3[0].GroupID)
|
||||
}
|
||||
if len(messageGroupsResult) > 0 {
|
||||
return fmt.Errorf("请不要加入自己创建的群!")
|
||||
}
|
||||
if len(messageGroupsResult2) > 0 && len(messageGroupMembers2) > 0 {
|
||||
if len(messageGroupMembers2)+1 > messageGroupsResult2[0].MaxCount {
|
||||
return fmt.Errorf("抱歉该群组已经满员了!")
|
||||
}
|
||||
}
|
||||
if len(messageGroupsResult3) > 0 {
|
||||
return fmt.Errorf("您已经拥有[%v]群了,不能再加入其它群!", messageGroupsResult3[0].GroupID)
|
||||
}
|
||||
|
||||
messageGroupMember := &model.MessageGroupMember{
|
||||
GroupID: groupID,
|
||||
MemberUserID: userID,
|
||||
Type: model.GroupMemberTypeNormal,
|
||||
}
|
||||
dao.WrapAddIDCULDEntity(messageGroupMember, ctx.GetUserName())
|
||||
if err = dao.CreateEntity(db, messageGroupMember); err == nil {
|
||||
messageGroupRead := &model.MessageGroupRead{
|
||||
GroupID: groupID,
|
||||
UserID: userID,
|
||||
}
|
||||
dao.WrapAddIDCULEntity(messageGroupRead, ctx.GetUserName())
|
||||
if err = dao.CreateEntity(db, messageGroupRead); err == nil {
|
||||
if messageGroupReads, err := dao.GetMessageGroupRead(db, userID, model.SysGroupID); len(messageGroupReads) == 0 && err == nil {
|
||||
messageGroupRead := &model.MessageGroupRead{
|
||||
GroupID: model.SysGroupID,
|
||||
UserID: userID,
|
||||
}
|
||||
dao.WrapAddIDCULEntity(messageGroupRead, ctx.GetUserName())
|
||||
dao.CreateEntity(db, messageGroupRead)
|
||||
}
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func UpdateMessageGroup(ctx *jxcontext.Context, groupID int, payload map[string]interface{}) (num int64, err error) {
|
||||
var (
|
||||
db = dao.GetDB()
|
||||
messageGroup = &model.MessageGroup{
|
||||
GroupID: groupID,
|
||||
}
|
||||
)
|
||||
if err = dao.GetEntity(db, messageGroup, "GroupID"); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if messageGroup.UserID != ctx.GetUserID() {
|
||||
return 0, fmt.Errorf("只有群主才能修改群信息!")
|
||||
}
|
||||
valid := dao.StrictMakeMapByStructObject(payload, messageGroup, ctx.GetUserName())
|
||||
if len(valid) > 0 {
|
||||
txDB, _ := dao.Begin(db)
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
dao.Rollback(db, txDB)
|
||||
panic(r)
|
||||
}
|
||||
}()
|
||||
if num, err = dao.UpdateEntityLogically(db, messageGroup, valid, ctx.GetUserName(), nil); err != nil {
|
||||
dao.Rollback(db, txDB)
|
||||
return 0, err
|
||||
}
|
||||
dao.Commit(db, txDB)
|
||||
}
|
||||
return num, err
|
||||
}
|
||||
|
||||
func DeleteMessageGroup(ctx *jxcontext.Context, groupID int, userID string, flag bool) (errCode string, err error) {
|
||||
var (
|
||||
db = dao.GetDB()
|
||||
messageGroup = &model.MessageGroup{
|
||||
GroupID: groupID,
|
||||
}
|
||||
)
|
||||
if err = dao.GetEntity(db, messageGroup, "GroupID"); err != nil {
|
||||
return errCode, err
|
||||
}
|
||||
//解散群
|
||||
if flag {
|
||||
if messageGroup.UserID != ctx.GetUserID() {
|
||||
return errCode, fmt.Errorf("只有群主才能解散群!")
|
||||
}
|
||||
messageGroup.DeletedAt = time.Now()
|
||||
messageGroup.LastOperator = ctx.GetUserName()
|
||||
dao.UpdateEntity(db, messageGroup, "DeletedAt", "LastOperator")
|
||||
messageGroupMembers, _ := dao.GetMessageGroupMembers(db, groupID, 0, "")
|
||||
for _, v := range messageGroupMembers {
|
||||
v.DeletedAt = time.Now()
|
||||
v.LastOperator = ctx.GetUserName()
|
||||
dao.UpdateEntity(db, v, "DeletedAt", "LastOperator")
|
||||
}
|
||||
} else {
|
||||
//token中用户与传入user不一致就判定为踢人
|
||||
if ctx.GetUserID() != userID {
|
||||
if messageGroup.UserID != ctx.GetUserID() {
|
||||
messageGroupMembers, err := dao.GetMessageGroupMembers(db, groupID, 0, ctx.GetUserID())
|
||||
if err != nil {
|
||||
return errCode, err
|
||||
}
|
||||
if messageGroupMembers[0].Type == model.GroupMemberTypeNormal {
|
||||
return errCode, fmt.Errorf("只有创建者和管理员才能踢人!")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
quitPrice := messageGroup.QuitPrice
|
||||
userBill, err := dao.GetUserBill(db, userID, "")
|
||||
userBillGroupMaster, err := dao.GetUserBill(db, messageGroup.UserID, "")
|
||||
case model.VendorIDMTWM:
|
||||
_, err := api.MtwmAPI.PackagePriceGet(mtStoreID)
|
||||
if err != nil {
|
||||
return errCode, err
|
||||
}
|
||||
if userBill.AccountBalance < quitPrice {
|
||||
return model.ErrCodeAccountBalanceNotEnough, fmt.Errorf("用户余额不足,请充值!")
|
||||
}
|
||||
txDB, _ := dao.Begin(db)
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
dao.Rollback(db, txDB)
|
||||
panic(r)
|
||||
if strings.Contains(err.Error(), mtErr) {
|
||||
errMsg += fmt.Sprintf(" 美团账号:[%v]的Cookie无效了!")
|
||||
flag = true
|
||||
}
|
||||
}()
|
||||
//账户支出
|
||||
if err = financial.AddExpendUpdateAccount(txDB, userBill, model.BillTypeQuitGroup, quitPrice, 0); err != nil {
|
||||
dao.Rollback(db, txDB)
|
||||
return errCode, err
|
||||
globals.SugarLogger.Debugf("cookieCheck", err)
|
||||
}
|
||||
//群主收到退团金额
|
||||
if err = financial.AddIncomeUpdateAccount(txDB, userBillGroupMaster, model.BillTypeQuitGroup, quitPrice, 0); err != nil {
|
||||
dao.Rollback(db, txDB)
|
||||
return errCode, 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
|
||||
}
|
||||
}
|
||||
dao.Commit(db, txDB)
|
||||
}
|
||||
messageGroupMembers, err := dao.GetMessageGroupMembers(db, groupID, 0, userID)
|
||||
if err != nil {
|
||||
return errCode, err
|
||||
}
|
||||
messageGroupMembers[0].DeletedAt = time.Now()
|
||||
messageGroupMembers[0].LastOperator = ctx.GetUserName()
|
||||
dao.UpdateEntity(db, messageGroupMembers[0], "DeletedAt", "LastOperator")
|
||||
}
|
||||
return errCode, err
|
||||
}
|
||||
|
||||
func TransferMessageGroupMaster(ctx *jxcontext.Context, groupID int, userID string) (err error) {
|
||||
var (
|
||||
db = dao.GetDB()
|
||||
messageGroup = &model.MessageGroup{
|
||||
GroupID: groupID,
|
||||
}
|
||||
)
|
||||
if err = dao.GetEntity(db, messageGroup, "GroupID"); err != nil {
|
||||
return err
|
||||
}
|
||||
if messageGroup.UserID != ctx.GetUserID() {
|
||||
return fmt.Errorf("只有群主才能转让群主!")
|
||||
}
|
||||
//群主换成选的那个人
|
||||
messageGroup.UserID = userID
|
||||
messageGroup.LastOperator = ctx.GetUserName()
|
||||
//之前那个群成员换成之前的群主
|
||||
messageGroupMembers, err := dao.GetMessageGroupMembers(db, groupID, 0, userID)
|
||||
if err != nil && len(messageGroupMembers) == 0 {
|
||||
return err
|
||||
}
|
||||
messageGroupMember := messageGroupMembers[0]
|
||||
messageGroupMember.MemberUserID = ctx.GetUserID()
|
||||
messageGroupMember.LastOperator = ctx.GetUserName()
|
||||
txDB, _ := dao.Begin(db)
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
dao.Rollback(db, txDB)
|
||||
panic(r)
|
||||
}
|
||||
}()
|
||||
if _, err = dao.UpdateEntity(db, messageGroup, "UserID", "LastOperator"); err != nil {
|
||||
dao.Rollback(db, txDB)
|
||||
return err
|
||||
}
|
||||
if _, err = dao.UpdateEntity(db, messageGroupMember, "MemberUserID", "LastOperator"); err != nil {
|
||||
dao.Rollback(db, txDB)
|
||||
return err
|
||||
}
|
||||
dao.Commit(db, txDB)
|
||||
return err
|
||||
}
|
||||
|
||||
func SendSysMessageSimple(content, toUserID string) (err error) {
|
||||
return SendSysMessage(jxcontext.AdminCtx, &model.ImMessageRecord{
|
||||
Content: sysMessageTitle + " " + content,
|
||||
ToUserID: toUserID,
|
||||
GroupID: model.SysGroupID,
|
||||
MessageType: 1, // 普通文字消息
|
||||
})
|
||||
}
|
||||
|
||||
func SendSysMessage(ctx *jxcontext.Context, imMessageRecord *model.ImMessageRecord) (err error) {
|
||||
var (
|
||||
db = dao.GetDB()
|
||||
userID = imMessageRecord.ToUserID
|
||||
groupID = imMessageRecord.GroupID
|
||||
)
|
||||
if groupID != model.SysGroupID {
|
||||
return fmt.Errorf("只能给系统组发消息!")
|
||||
}
|
||||
imMessageRecord.CreatedAt = time.Now()
|
||||
imMessageRecord.LastOperator = ctx.GetUserName()
|
||||
imMessageRecord.DeletedAt = utils.DefaultTimeValue
|
||||
imMessageRecord.UpdatedAt = utils.DefaultTimeValue
|
||||
imMessageRecord.Seq = time.Now().Unix()
|
||||
err = dao.CreateEntity(db, imMessageRecord)
|
||||
if userID == "" {
|
||||
//循环map客户端
|
||||
for _, client := range wsClient.Clients[groupID] {
|
||||
globals.SugarLogger.Debugf("msg %v", utils.Format4Output(imMessageRecord, false))
|
||||
err = client.WriteJSON(imMessageRecord)
|
||||
case model.VendorIDJDShop:
|
||||
_, err := api.JdShopAPI.OrderDetail("124350112427")
|
||||
if err != nil {
|
||||
globals.SugarLogger.Debugf("client.WriteJSON error: %v", err)
|
||||
client.Close() //关闭
|
||||
// delete(wsClient.Clients[msg.GroupID], userID)
|
||||
if strings.Contains(err.Error(), "登录") {
|
||||
errMsg += fmt.Sprintf("京东商城:[%v]的Cookie无效了!")
|
||||
flag = true
|
||||
}
|
||||
globals.SugarLogger.Debugf("cookieCheck", err)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
client := wsClient.Clients[groupID][userID]
|
||||
globals.SugarLogger.Debugf("msg %v", utils.Format4Output(imMessageRecord, false))
|
||||
if client == nil {
|
||||
return
|
||||
}
|
||||
err = client.WriteJSON(imMessageRecord)
|
||||
if err != nil {
|
||||
globals.SugarLogger.Debugf("client.WriteJSON error: %v", err)
|
||||
client.Close() //关闭
|
||||
// delete(wsClient.Clients[msg.GroupID], userID)
|
||||
cc.VendorID = v
|
||||
cc.VendorOrgCode = ""
|
||||
if flag {
|
||||
cc.Status = "无效"
|
||||
} else {
|
||||
cc.Status = "有效"
|
||||
}
|
||||
ccList = append(ccList, cc)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func DeleteMessageRecord(ctx *jxcontext.Context) (err error) {
|
||||
var (
|
||||
db = dao.GetDB()
|
||||
)
|
||||
sql := `
|
||||
DELETE FROM im_message_record WHERE created_at < ? AND group_id <> ?
|
||||
`
|
||||
sqlParams := []interface{}{
|
||||
time.Now().AddDate(0, 0, -3), model.SysGroupID,
|
||||
if isAuto && errMsg != "" {
|
||||
globals.SugarLogger.Warnf("GetCheckVendorCookie:[%v]", errMsg)
|
||||
}
|
||||
_, err = dao.ExecuteSQL(db, sql, sqlParams)
|
||||
return err
|
||||
}
|
||||
|
||||
func UpdateUserMessageGroupRead(ctx *jxcontext.Context, reads []*model.MessageGroupRead) (err error) {
|
||||
var (
|
||||
db = dao.GetDB()
|
||||
userID = ctx.GetUserID()
|
||||
)
|
||||
for _, v := range reads {
|
||||
if messageReads, err := dao.GetMessageGroupRead(db, userID, v.GroupID); err == nil {
|
||||
if len(messageReads) > 0 {
|
||||
messageReads[0].UnReadCount += v.UnReadCount
|
||||
dao.UpdateEntity(db, messageReads[0], "UnReadCount")
|
||||
} else {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
type GetUserStatisticsResult struct {
|
||||
RegisterUserCount int `json:"registerUserCount"` //注册数
|
||||
OnlineUserCount int `json:"onlineUserCount"` //在线用户数
|
||||
ConsumeUserCount int `json:"consumeUserCount"` //消费用户数
|
||||
MemberUserCount int `json:"memberUserCount"` //会员用户数
|
||||
}
|
||||
|
||||
func GetUserStatistics(ctx *jxcontext.Context) (getUserStatisticsResult *GetUserStatisticsResult, err error) {
|
||||
var (
|
||||
db = dao.GetDB()
|
||||
)
|
||||
getUserStatisticsResult = &GetUserStatisticsResult{}
|
||||
sql := `
|
||||
SELECT a.member_user_count, b.consume_user_count FROM
|
||||
( SELECT DISTINCT COUNT(a.user_id) member_user_count
|
||||
FROM user a
|
||||
JOIN user_member b ON a.user_id = b.user_id AND b.deleted_at = ? )a,
|
||||
( SELECT DISTINCT COUNT(a.user_id) consume_user_count
|
||||
FROM user a
|
||||
JOIN ` + "`order`" + `b ON a.user_id = b.user_id AND b.status = ?)b
|
||||
`
|
||||
sqlParams := []interface{}{
|
||||
utils.DefaultTimeValue, model.OrderStatusFinished,
|
||||
}
|
||||
err = dao.GetRow(db, &getUserStatisticsResult, sql, sqlParams)
|
||||
paged, _ := dao.GetUsers2(db, "", "", 0, "", utils.ZeroTimeValue, utils.ZeroTimeValue, 0, nil, nil, 0, -1)
|
||||
getUserStatisticsResult.RegisterUserCount = paged.TotalCount
|
||||
getUserStatisticsResult.OnlineUserCount = GetOnlineUserCount()
|
||||
return getUserStatisticsResult, err
|
||||
return ccList, err
|
||||
}
|
||||
|
||||
@@ -1,97 +0,0 @@
|
||||
package event
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
PrintObject *PrintObjectStruct // 缓存的打印机对象
|
||||
PrintAddrAndIp *PrintAddrAndIpStruct // 缓存打印机地址:[ip:printNo] event 文件包,connect只能获取到addr
|
||||
PrintIpAndAddr *PrintIpAndAddrStruct // 缓存打印机地址:[printNo:ip] api_controller 只能获取到printNo
|
||||
)
|
||||
|
||||
func init() {
|
||||
fmt.Println("初始化打印机对象")
|
||||
PrintObject = &PrintObjectStruct{
|
||||
PrintObject: make(map[string]*TcpClient),
|
||||
RWMutex: new(sync.RWMutex),
|
||||
}
|
||||
PrintAddrAndIp = &PrintAddrAndIpStruct{
|
||||
PrintObject: make(map[string]string),
|
||||
RWMutex: new(sync.RWMutex),
|
||||
}
|
||||
PrintIpAndAddr = &PrintIpAndAddrStruct{
|
||||
PrintObject: make(map[string]string),
|
||||
RWMutex: new(sync.RWMutex),
|
||||
}
|
||||
}
|
||||
|
||||
type PrintObjectStruct struct {
|
||||
PrintObject map[string]*TcpClient
|
||||
*sync.RWMutex
|
||||
}
|
||||
|
||||
func (p *PrintObjectStruct) GetPrintObj(printNo string) (*TcpClient, bool) {
|
||||
p.RLock()
|
||||
defer p.RUnlock()
|
||||
tcpObj, ok := PrintObject.PrintObject[printNo]
|
||||
return tcpObj, ok
|
||||
}
|
||||
func (p *PrintObjectStruct) SetPrintObj(printNo string, tcpObj *TcpClient) {
|
||||
p.RLock()
|
||||
defer p.RUnlock()
|
||||
PrintObject.PrintObject[printNo] = tcpObj
|
||||
}
|
||||
|
||||
func (p *PrintObjectStruct) DelPrintObj(printNo string) {
|
||||
p.RLock()
|
||||
defer p.RUnlock()
|
||||
delete(PrintObject.PrintObject, printNo)
|
||||
}
|
||||
|
||||
type PrintAddrAndIpStruct struct {
|
||||
PrintObject map[string]string
|
||||
*sync.RWMutex
|
||||
}
|
||||
|
||||
func (p *PrintAddrAndIpStruct) GetPrintAddrAndIp(ip string) (string, bool) {
|
||||
p.RLock()
|
||||
defer p.RUnlock()
|
||||
printNo, ok := PrintAddrAndIp.PrintObject[ip]
|
||||
return printNo, ok
|
||||
}
|
||||
func (p *PrintAddrAndIpStruct) SetPrintAddrAndIp(ip string, printNo string) {
|
||||
p.RLock()
|
||||
defer p.RUnlock()
|
||||
PrintAddrAndIp.PrintObject[ip] = printNo
|
||||
}
|
||||
|
||||
func (p *PrintAddrAndIpStruct) DelPrintAddrAndIp(ip string) {
|
||||
p.RLock()
|
||||
defer p.RUnlock()
|
||||
delete(PrintAddrAndIp.PrintObject, ip)
|
||||
}
|
||||
|
||||
type PrintIpAndAddrStruct struct {
|
||||
PrintObject map[string]string
|
||||
*sync.RWMutex
|
||||
}
|
||||
|
||||
func (p *PrintIpAndAddrStruct) GetPrintIpAndAddr(printNo string) (string, bool) {
|
||||
p.RLock()
|
||||
defer p.RUnlock()
|
||||
tcpObj, ok := PrintIpAndAddr.PrintObject[printNo]
|
||||
return tcpObj, ok
|
||||
}
|
||||
func (p *PrintIpAndAddrStruct) SetPrintIpAndAddr(printNo string, ip string) {
|
||||
p.RLock()
|
||||
defer p.RUnlock()
|
||||
PrintIpAndAddr.PrintObject[printNo] = ip
|
||||
}
|
||||
|
||||
func (p *PrintIpAndAddrStruct) DelPrintIpAndAddr(printNo string) {
|
||||
p.RLock()
|
||||
defer p.RUnlock()
|
||||
delete(PrintIpAndAddr.PrintObject, printNo)
|
||||
}
|
||||
@@ -1,373 +0,0 @@
|
||||
package event
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"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/globals"
|
||||
"io"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// ConnRead 获取链接数据
|
||||
func ConnRead(c net.Conn) ([]byte, int, error) {
|
||||
buffer := make([]byte, 1024*2)
|
||||
n, err := c.Read(buffer)
|
||||
return buffer, n, err
|
||||
}
|
||||
|
||||
// ListenTcp 入口
|
||||
func ListenTcp() {
|
||||
l, err := net.Listen("tcp", ":8000")
|
||||
if err != nil {
|
||||
fmt.Println("listen error:", err)
|
||||
return
|
||||
}
|
||||
|
||||
for {
|
||||
c, err := l.Accept()
|
||||
if err != nil || c == nil {
|
||||
fmt.Println("accept error:", err)
|
||||
break
|
||||
}
|
||||
|
||||
fn := func() {
|
||||
// 捕获异常 防止waitGroup阻塞
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
fmt.Println("recover err = ", err)
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
if err := handleConn(c); err != nil {
|
||||
c.Close()
|
||||
Poll.Wait()
|
||||
Poll.Stop()
|
||||
return
|
||||
}
|
||||
}
|
||||
Poll.AddJob(fn)
|
||||
}
|
||||
}
|
||||
|
||||
func handleConn(c net.Conn) error {
|
||||
if c == nil {
|
||||
return errors.New("conn is nil")
|
||||
}
|
||||
for {
|
||||
buffer, n, err := ConnRead(c)
|
||||
printRemoteAddr := c.RemoteAddr().String()
|
||||
printRemoteAddr = strings.Split(printRemoteAddr, ":")[0]
|
||||
printNoByIP, _ := PrintAddrAndIp.GetPrintAddrAndIp(printRemoteAddr)
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
fmt.Println("connection close")
|
||||
} else {
|
||||
fmt.Println("ReadString err:", err)
|
||||
}
|
||||
globals.SugarLogger.Debugf("--------printRemoteAddr := %s,printNo := %s", printRemoteAddr, printNoByIP)
|
||||
if printNo, ok := PrintAddrAndIp.GetPrintAddrAndIp(printRemoteAddr); ok {
|
||||
PrintAddrAndIp.DelPrintAddrAndIp(printRemoteAddr)
|
||||
PrintObject.DelPrintObj(printNo)
|
||||
PrintIpAndAddr.DelPrintIpAndAddr(printRemoteAddr)
|
||||
dao.ExecuteSQL(dao.GetDB(), `UPDATE printer SET status = -1,is_online = -1 WHERE print_no = ? `, []interface{}{printNo}...)
|
||||
} else {
|
||||
printStatusOff := make(map[string]int, 0)
|
||||
for ip, pn := range PrintAddrAndIp.PrintObject {
|
||||
if ip == printRemoteAddr {
|
||||
PrintAddrAndIp.DelPrintAddrAndIp(printRemoteAddr)
|
||||
PrintIpAndAddr.DelPrintIpAndAddr(pn)
|
||||
PrintObject.DelPrintObj(printNo)
|
||||
} else if pn != "" {
|
||||
printStatusOff[pn] = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
//看是心跳还是打印回调
|
||||
data := hex.EncodeToString(buffer[:n])
|
||||
var (
|
||||
printNo string = "" //打印机编号
|
||||
heartbeat bool = false
|
||||
callback bool = false
|
||||
)
|
||||
if strings.Contains(data, heartText) || strings.Contains(data, heartTextNew) {
|
||||
printNoData, _ := hex.DecodeString(data[len(heartText) : len(data)-8])
|
||||
printNo = string(printNoData)
|
||||
heartbeat = true
|
||||
} else if strings.Contains(data, printText) || strings.Contains(data, printTextNew) { //打印回调
|
||||
_, printNo = getCallbackMsgInfo(data)
|
||||
callback = true
|
||||
}
|
||||
|
||||
t, ok := PrintObject.GetPrintObj(printNo)
|
||||
if !ok || t.Clients[printNo] == nil || time.Now().Sub(t.Clients[printNo].StatusTime).Seconds() >= 120 {
|
||||
t = NewTcpClient()
|
||||
}
|
||||
|
||||
if heartbeat {
|
||||
// 证明是心跳
|
||||
Heartbeat(c, t, data, printNo, printRemoteAddr)
|
||||
} else if callback {
|
||||
// 打印回调
|
||||
Callback(c, t, data, printNo)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (t *TcpClient) printFail() (err error) {
|
||||
//新开机的打印失败和错误的
|
||||
var (
|
||||
db = dao.GetDB()
|
||||
)
|
||||
prints, _ := dao.GetPrintMsgs(db, "", []int{printMsgFail, printMsgErr, PrintMsgAlreadyLoad, printMsgAlreadySend}, time.Now().Add(-time.Hour*3), time.Now(), 0, 999)
|
||||
for _, printMsg := range prints {
|
||||
t.addMsgChan(printMsg)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (t *TcpClient) changePrintMsg(data string, orderNo int64, printNo string) (err error) {
|
||||
var (
|
||||
db = dao.GetDB()
|
||||
comment string
|
||||
status int
|
||||
)
|
||||
//1、先找出打印机编号和订单序列号,这两个确定唯一一条消息?
|
||||
//通过参数传进来
|
||||
//2、打印成功改变打印表的状态
|
||||
if strings.Contains(data, printSuccessText) || strings.Contains(data, printSuccessTextNew) {
|
||||
status = printMsgSuccess
|
||||
comment = "回调成功,修改打印状态"
|
||||
} else {
|
||||
//打印失败也改变状态并更新失败原因
|
||||
status = printMsgFail
|
||||
comment = printErrMap[data[12:14]]
|
||||
}
|
||||
//这里序号重复会有问题
|
||||
if printMsgs, err := dao.GetPrintMsgNoPage(db, printNo, orderNo); err != nil {
|
||||
globals.SugarLogger.Debugf("changePrintMsg err :[%v]", err)
|
||||
return err
|
||||
} else if len(printMsgs) == 0 {
|
||||
globals.SugarLogger.Debugf("changePrintMsg err ,not found printMsg printNo:[%v], orderNo :[%v]", printNo, orderNo)
|
||||
} else if len(printMsgs) > 0 {
|
||||
for _, v := range printMsgs {
|
||||
v.Comment = comment
|
||||
v.Status = status
|
||||
dao.UpdateEntity(db, v, "Comment", "Status")
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func HandleTcpMessages(t *TcpClient, printNo string) {
|
||||
var (
|
||||
db = dao.GetDB()
|
||||
offset, pageSize = 0, 10
|
||||
)
|
||||
if !t.isExistMsg(printNo) {
|
||||
return
|
||||
}
|
||||
|
||||
fn := func() {
|
||||
//for {
|
||||
// time.Sleep(2 * time.Second)
|
||||
if t.TimeoutMap[printNo] == true {
|
||||
timeNow := time.Now()
|
||||
timeStart := time.Date(timeNow.Year(), timeNow.Month(), timeNow.Day(), 0, 0, 0, 0, timeNow.Location())
|
||||
timeEnd := time.Date(timeNow.Year(), timeNow.Month(), timeNow.Day(), 23, 59, 59, 0, timeNow.Location())
|
||||
prints, _ := dao.GetPrintMsgs(db, printNo, []int{PrintMsgWait}, timeStart.AddDate(0, 0, -1), timeEnd, offset, pageSize)
|
||||
for _, printMsg := range prints {
|
||||
printMsg.Status = PrintMsgAlreadyLoad
|
||||
//先避免重复读再插到channel?
|
||||
if _, err := dao.UpdateEntity(db, printMsg, "Status"); err == nil {
|
||||
if err = t.addMsgChan(printMsg); err != nil {
|
||||
globals.SugarLogger.Debugf("HandleTcpMessages addMsgChan Err: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
globals.SugarLogger.Debugf("HandleTcpMessages timeout")
|
||||
return
|
||||
}
|
||||
}
|
||||
Poll.AddJob(fn)
|
||||
}
|
||||
|
||||
func (t *TcpClient) readTimeoutMap(key string) bool {
|
||||
t.Lock()
|
||||
defer t.Unlock()
|
||||
return t.TimeoutMap[key]
|
||||
}
|
||||
|
||||
func doPrint(t *TcpClient, key string) (err error) {
|
||||
var (
|
||||
db = dao.GetDB()
|
||||
)
|
||||
|
||||
if !t.isExistMsg(key) {
|
||||
return err
|
||||
}
|
||||
|
||||
fn := func() {
|
||||
for {
|
||||
if t.TimeoutMap[key] == true {
|
||||
select {
|
||||
case printMsg, ok := <-t.MsgMap[key]:
|
||||
if !ok {
|
||||
globals.SugarLogger.Debugf("doPrint err !ok ...")
|
||||
return
|
||||
}
|
||||
var (
|
||||
data []byte
|
||||
c net.Conn
|
||||
)
|
||||
if printMsg == nil {
|
||||
globals.SugarLogger.Debugf("print msg is nil")
|
||||
continue
|
||||
}
|
||||
|
||||
if err = checkPrintMsg(db, printMsg); err == nil {
|
||||
status := t.getPrintStatus(printMsg.PrintNo)
|
||||
switch status {
|
||||
//只有在线才打印内容
|
||||
case printerStatusOnline:
|
||||
if c = t.getPrintConn(printMsg.PrintNo); c != nil {
|
||||
data, err = buildMsg(printMsg)
|
||||
}
|
||||
case printerStatusOffline:
|
||||
err = fmt.Errorf("打印机离线!")
|
||||
case printerStatusOnlineWithoutPaper:
|
||||
err = fmt.Errorf("打印机缺纸!")
|
||||
default:
|
||||
err = fmt.Errorf("打印机状态未知!")
|
||||
}
|
||||
}
|
||||
|
||||
if c == nil {
|
||||
if printRemoteAddrIP, have := PrintIpAndAddr.GetPrintIpAndAddr(key); have {
|
||||
PrintIpAndAddr.DelPrintIpAndAddr(key)
|
||||
PrintAddrAndIp.DelPrintAddrAndIp(printRemoteAddrIP)
|
||||
PrintObject.DelPrintObj(key)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
printMsg.Status = printMsgErr
|
||||
printMsg.Comment = err.Error()
|
||||
dao.UpdateEntity(db, printMsg, "Status", "Comment")
|
||||
|
||||
if printRemoteAddrIP, have := PrintIpAndAddr.GetPrintIpAndAddr(key); have {
|
||||
PrintIpAndAddr.DelPrintIpAndAddr(key)
|
||||
PrintAddrAndIp.DelPrintAddrAndIp(printRemoteAddrIP)
|
||||
PrintObject.DelPrintObj(key)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if _, err = c.Write(data); err != nil {
|
||||
globals.SugarLogger.Debugf("handleTcpMessages err [%v]", err)
|
||||
if printRemoteAddrIP, have := PrintIpAndAddr.GetPrintIpAndAddr(key); have {
|
||||
PrintIpAndAddr.DelPrintIpAndAddr(key)
|
||||
PrintAddrAndIp.DelPrintAddrAndIp(printRemoteAddrIP)
|
||||
PrintObject.DelPrintObj(key)
|
||||
}
|
||||
|
||||
} else {
|
||||
//等待回调
|
||||
dataStr := <-t.CallBackMap[key]
|
||||
if dataStr != "" {
|
||||
a, b := getCallbackMsgInfo(dataStr)
|
||||
t.changePrintMsg(dataStr, a, b)
|
||||
// 查询打印机是否扣费,未扣费就扣费,已经扣费不做处理
|
||||
have, err2 := dao.QueryOrderDeductionRecord(db, b, utils.Int64ToStr(a))
|
||||
if err2 == nil && !have {
|
||||
// 扣除打印机账号金额
|
||||
if err = dao.DeductionPrintBalance(db, b); err != nil {
|
||||
globals.SugarLogger.Debugf("扣除用户打印机金额错误 %s", err)
|
||||
} else {
|
||||
// 添加打印记录(支出记录)
|
||||
if err = dao.AddPrintRecord(db, &model.PrintBillRecord{
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
PrintNo: b,
|
||||
PayType: 2,
|
||||
PayMoney: 1, // 固定支出一分钱
|
||||
OrderId: utils.Int64ToStr(a),
|
||||
UserId: "",
|
||||
}); err != nil {
|
||||
globals.SugarLogger.Debugf("添加打印机订单支付记录错误 %s", err)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
globals.SugarLogger.Debugf("今天已经扣除过了! %v %d %s", err2, a, b)
|
||||
}
|
||||
// 回调重置打印机状态时间
|
||||
t.Clients[b].StatusTime = time.Now()
|
||||
//判断音频暂停?
|
||||
//收到打印成功回调后,如果消息中有音频,需要等待一下,等上一个音频播完
|
||||
//暂停时间就暂时取的sound标签内内容长度/2
|
||||
if sounds := regexpSoundSpan.FindStringSubmatch(printMsg.Content); len(sounds) > 0 {
|
||||
sound := sounds[1]
|
||||
lenTime := time.Duration(utf8.RuneCountInString(sound)) * time.Second
|
||||
time.Sleep(lenTime / 2)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
globals.SugarLogger.Debugf("doPrint timeout")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
Poll.AddJob(fn)
|
||||
return err
|
||||
}
|
||||
|
||||
// HandleCheckTcpHeart 检测心跳
|
||||
func HandleCheckTcpHeart(t *TcpClient, key string) {
|
||||
if t.TimeoutMap[key] == true {
|
||||
statusTime := t.getPrintStatusTime(key)
|
||||
if !utils.IsTimeZero(statusTime) {
|
||||
//1分钟内没心跳判断打印机掉线了
|
||||
if time.Now().Sub(statusTime) > time.Second*75 {
|
||||
globals.SugarLogger.Debugf("超过一分十秒没有心跳的打印机[%s],当前心跳时间: %s ,上一次心跳时间 : %s", key, utils.Time2TimeStr(time.Now()), utils.Time2TimeStr(statusTime))
|
||||
changePrinterStatus(key, printerStatusOffline)
|
||||
|
||||
// 链接出错,彻底删除换成
|
||||
if printRemoteAddrIP, have := PrintIpAndAddr.GetPrintIpAndAddr(key); have {
|
||||
PrintIpAndAddr.DelPrintIpAndAddr(key)
|
||||
PrintAddrAndIp.DelPrintAddrAndIp(printRemoteAddrIP)
|
||||
PrintObject.DelPrintObj(key)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
} else {
|
||||
t.getClients(key).C.Close()
|
||||
close(t.MsgMap[key])
|
||||
close(t.CallBackMap[key])
|
||||
//t.delConn(key)
|
||||
t.clear(key)
|
||||
|
||||
// 链接出错,彻底删除换成
|
||||
if printRemoteAddrIP, have := PrintIpAndAddr.GetPrintIpAndAddr(key); have {
|
||||
PrintIpAndAddr.DelPrintIpAndAddr(key)
|
||||
PrintAddrAndIp.DelPrintAddrAndIp(printRemoteAddrIP)
|
||||
PrintObject.DelPrintObj(key)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -1,687 +0,0 @@
|
||||
package event
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"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"
|
||||
"net"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
heartText = "1e000f02000151" // 老版心跳
|
||||
heartTextNew = "1e001a02000151" // 新版心跳
|
||||
printText = "1e00180200" // 老版打印回调
|
||||
printTextNew = "1e00190200" // 新版打印回调
|
||||
printSuccessText = "1e001802000150" // 老版消息打印
|
||||
printSuccessTextNew = "1e001902000150" // 新版消息打印
|
||||
|
||||
printErrWithoutPaper = "05"
|
||||
|
||||
printMsgAlreadySend = 2 //已经发出打印消息
|
||||
printMsgSuccess = 1 //打印成功
|
||||
PrintMsgWait = 0 //待打印
|
||||
printMsgFail = -1 //打印失败(打印机报出)
|
||||
printMsgErr = -2 //京西报出
|
||||
PrintMsgAlreadyLoad = 3 //已放入队列
|
||||
|
||||
heartErrNormal = "00" //正常
|
||||
heartErrWithoutPaper = "04" //心跳错,缺纸
|
||||
heartErrHot = "08" //过热
|
||||
|
||||
printerStatusOnlineWithoutPaper = 2 //在线缺纸
|
||||
printerStatusOnline = 1 //在线
|
||||
printerStatusOffline = -1 //离线
|
||||
printerStatusOfflineAll = -9 //其他异常状态
|
||||
|
||||
PrintSoundMaxNumber = 16 // 十六进制最大补位
|
||||
PlaceFillingParam = "0" // 补位参数
|
||||
)
|
||||
|
||||
//标签
|
||||
const (
|
||||
signBR = "<br>" //换行
|
||||
signCenter = "<center>" //居中
|
||||
signLeft = "<left>" //居左
|
||||
signRight = "<right>" //居右
|
||||
signBig = "<b>" //字体放大
|
||||
signHighBig = "<hb>" //字体纵向放大
|
||||
signWideBig = "<wb>" //字体横向放大
|
||||
signQrCenter = "<qrc>" //二维码居中
|
||||
signQrLeft = "<qrl>" //二维码居左
|
||||
signQrRight = "<qrr>" //二维码居右
|
||||
signSound, signSoundEnd = "<sound>", "</sound>" // 声音结束标签
|
||||
|
||||
// GPRS通讯说明,打印机识别二进制码
|
||||
hexSignBROrEXE = "0a" // 换行
|
||||
hexSignCenter = "1b6101" // 居中打印
|
||||
hexSignLeft = "1b6100" // 恢复居左打印
|
||||
hexSignRight = "1b6102" // 居右打印
|
||||
hexSignNormal = "1b2100"
|
||||
hexSignBig = "1b2130" // 横向及纵向都放大
|
||||
hexSignHighBig = "1b2110" // 倍高
|
||||
hexSignWideBig = "1b2120" // 倍宽
|
||||
hexSignQrCenter = "1d5802" // 二维码居中
|
||||
hexSignQrLeft = "1d5800" // 二维码居左
|
||||
hexSignQrRight = "1d5804" // 二维码居右
|
||||
hexSignQr = "1b5a000106" // "1b5a000106" 0600 : 后面二维码的字节数
|
||||
hexSignQrEnd = "000a1b40" // 000a0a0a1b40
|
||||
hexSignSound = "1d6b40" // 音频指令(自定义语音指令)
|
||||
hexSignSoundSolidification = "1B594155" // 音频指令(固化指令)
|
||||
|
||||
//起始标签 -- 自定义标签utf8转码为gbk字符集
|
||||
byteSignBR = "3c62723e" // 换行
|
||||
byteSignCenter = "3c63656e7465723e" // 居中
|
||||
byteSignLeft = "3c6c6566743e" // 居左
|
||||
byteSignRight = "3c72696768743e" // 居右
|
||||
byteSignBig = "3c623e" // 字体放大
|
||||
byteSignHighBig = "3c68623e" // 字体纵向放大
|
||||
byteSignWideBig = "3c77623e" // 字体横向放大
|
||||
byteSignQrCenter = "3c7172633e" // 二维码居中
|
||||
byteSignQrLeft = "3c71726c3e" // 二维码居左
|
||||
byteSignQrRight = "3c7172723e" // 二维码居右
|
||||
byteSignSound = "3c736f756e643e" // 声音
|
||||
|
||||
//结束标签
|
||||
byteSignCenterE = "3c2f63656e7465723e" // 居中
|
||||
byteSignLeftE = "3c2f6c6566743e" // 居左
|
||||
byteSignRightE = "3c2f72696768743e" // 居右
|
||||
byteSignBigE = "3c2f623e" // 字体放大
|
||||
byteSignHighBigE = "3c2f68623e" // 字体纵向放大
|
||||
byteSignWideBigE = "3c2f77623e" // 字体横向放大
|
||||
byteSignQrCenterE = "3c2f7172633e" // 二维码居中
|
||||
byteSignQrLeftE = "3c2f71726c3e" // 二维码居左
|
||||
byteSignQrRightE = "3c2f7172723e" // 二维码居右
|
||||
byteSignSoundE = "3c2f736f756e643e" // 声音
|
||||
)
|
||||
|
||||
var (
|
||||
printErrMap = map[string]string{
|
||||
printErrWithoutPaper: "打印机缺纸!",
|
||||
}
|
||||
|
||||
signMap = map[string]string{
|
||||
byteSignBR: hexSignBROrEXE,
|
||||
}
|
||||
|
||||
regexpQrc = regexp.MustCompile(byteSignQrCenter + "(.*?)" + byteSignQrCenterE)
|
||||
regexpQrl = regexp.MustCompile(byteSignQrLeft + "(.*?)" + byteSignQrLeftE)
|
||||
regexpQrr = regexp.MustCompile(byteSignQrRight + "(.*?)" + byteSignQrRightE)
|
||||
regexpSound = regexp.MustCompile(byteSignSound + "(.*?)" + byteSignSoundE)
|
||||
regexpSoundSpan = regexp.MustCompile(signSound + "(.*?)" + signSoundEnd)
|
||||
)
|
||||
|
||||
type PrintInfo struct {
|
||||
C net.Conn
|
||||
Status int // 2 //在线缺纸 1 //在线 -1 //离线
|
||||
StatusTime time.Time
|
||||
}
|
||||
|
||||
//type PrintPoolMap struct {
|
||||
// *sync.RWMutex
|
||||
// PrintObj *TcpClient
|
||||
//}
|
||||
|
||||
//连接的客户端,吧每个客户端都放进来
|
||||
type TcpClient struct {
|
||||
Clients map[string]*PrintInfo //放tcp连接的,printNo 为key
|
||||
MsgMap map[string]chan *model.PrintMsg //放打印信息的,printNo为key
|
||||
CallBackMap map[string]chan string //放打印信息回调信息的,printNo为key
|
||||
TimeoutMap map[string]bool //退出channel
|
||||
*sync.RWMutex
|
||||
}
|
||||
|
||||
type GetPrintStatus struct {
|
||||
PrintNo string //打印机编号
|
||||
AppID int
|
||||
}
|
||||
|
||||
//从连接池删除,并关闭连接
|
||||
func (t *TcpClient) delConn(key string) {
|
||||
t.Lock()
|
||||
defer t.Unlock()
|
||||
if t.Clients[key].C != nil {
|
||||
globals.SugarLogger.Debugf("-------close2 := %s", key)
|
||||
t.Clients[key].C.Close()
|
||||
}
|
||||
delete(t.Clients, key)
|
||||
}
|
||||
|
||||
func (t *TcpClient) clear(key string) {
|
||||
t.Lock()
|
||||
defer t.Unlock()
|
||||
t.Clients[key].C.Close()
|
||||
delete(t.Clients, key)
|
||||
close(t.MsgMap[key])
|
||||
delete(t.MsgMap, key)
|
||||
close(t.CallBackMap[key])
|
||||
delete(t.CallBackMap, key)
|
||||
delete(t.TimeoutMap, key)
|
||||
}
|
||||
|
||||
//添加到连接池中
|
||||
func addConn(c net.Conn, t *TcpClient, key string, status int) {
|
||||
t.Lock()
|
||||
defer t.Unlock()
|
||||
t.Clients[key] = &PrintInfo{
|
||||
C: c,
|
||||
Status: status,
|
||||
StatusTime: time.Now(),
|
||||
}
|
||||
}
|
||||
|
||||
func (t *TcpClient) buildMsgMap(key string) {
|
||||
t.Lock()
|
||||
defer t.Unlock()
|
||||
dataChan := make(chan *model.PrintMsg, 1024)
|
||||
t.MsgMap[key] = dataChan
|
||||
}
|
||||
|
||||
func (t *TcpClient) buildCallBackMap(key string) {
|
||||
t.Lock()
|
||||
defer t.Unlock()
|
||||
dataChan := make(chan string, 1024)
|
||||
t.CallBackMap[key] = dataChan
|
||||
}
|
||||
|
||||
func (t *TcpClient) buildTimeoutMap(key string) {
|
||||
t.Lock()
|
||||
defer t.Unlock()
|
||||
//dataChan := make(chan bool)
|
||||
//t.TimeoutMap[key] = dataChan
|
||||
t.TimeoutMap[key] = true
|
||||
}
|
||||
|
||||
func (t *TcpClient) getTimeOut(key string) bool {
|
||||
t.RLock()
|
||||
defer t.RUnlock()
|
||||
//return <-t.TimeoutMap[key]
|
||||
return t.TimeoutMap[key]
|
||||
}
|
||||
|
||||
func buildAllMap(t *TcpClient, key string) {
|
||||
t.Lock()
|
||||
defer t.Unlock()
|
||||
t.MsgMap[key] = make(chan *model.PrintMsg, 1024)
|
||||
t.CallBackMap[key] = make(chan string, 1024)
|
||||
t.TimeoutMap[key] = true
|
||||
}
|
||||
|
||||
func BuildAllMap(t *TcpClient, key string) {
|
||||
buildAllMap(t, key)
|
||||
}
|
||||
|
||||
func (t *TcpClient) getPrintStatus(key string) int {
|
||||
t.RLock()
|
||||
defer t.RUnlock()
|
||||
if t.Clients[key] != nil {
|
||||
return t.Clients[key].Status
|
||||
} else {
|
||||
return printerStatusOfflineAll
|
||||
}
|
||||
}
|
||||
|
||||
func (t *TcpClient) getPrintConn(key string) net.Conn {
|
||||
t.RLock()
|
||||
defer t.RUnlock()
|
||||
if t.Clients[key] != nil {
|
||||
return t.Clients[key].C
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (t *TcpClient) getPrintStatusTime(key string) time.Time {
|
||||
t.RLock()
|
||||
defer t.RUnlock()
|
||||
if t.Clients[key] != nil {
|
||||
return t.Clients[key].StatusTime
|
||||
} else {
|
||||
return utils.ZeroTimeValue
|
||||
}
|
||||
}
|
||||
|
||||
// 获取连接对象
|
||||
func (t *TcpClient) getClients(key string) *PrintInfo {
|
||||
t.RLock()
|
||||
defer t.RUnlock()
|
||||
return t.Clients[key]
|
||||
}
|
||||
|
||||
func (t *TcpClient) isExistMsg(key string) bool {
|
||||
t.RLock()
|
||||
defer t.RUnlock()
|
||||
if t.MsgMap[key] == nil {
|
||||
return false
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
func (t *TcpClient) isExistCallback(key string) bool {
|
||||
t.RLock()
|
||||
defer t.RUnlock()
|
||||
if t.CallBackMap[key] == nil {
|
||||
return false
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
func (t *TcpClient) isExist(key string) bool {
|
||||
t.RLock()
|
||||
defer t.RUnlock()
|
||||
if t.Clients[key] == nil {
|
||||
return false
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
func (t *TcpClient) setPrintStatus(key string, status int) {
|
||||
t.Lock()
|
||||
defer t.Unlock()
|
||||
if t.Clients[key] != nil {
|
||||
t.Clients[key].Status = status
|
||||
//t.Clients[key].StatusTime = time.Now()
|
||||
}
|
||||
}
|
||||
|
||||
func (t *TcpClient) setPrintStatusTime(key string) {
|
||||
t.Lock()
|
||||
defer t.Unlock()
|
||||
if t.Clients[key] != nil {
|
||||
//t.Clients[key].Status = status
|
||||
t.Clients[key].StatusTime = time.Now()
|
||||
}
|
||||
}
|
||||
|
||||
func (t *TcpClient) addMsgChan(printMsg *model.PrintMsg) (err error) {
|
||||
t.Lock()
|
||||
defer func() {
|
||||
t.Unlock()
|
||||
if r := recover(); r != nil && r.(error).Error() == "send on closed channel" {
|
||||
err = fmt.Errorf("send on closed channel")
|
||||
}
|
||||
}()
|
||||
if t.MsgMap[printMsg.PrintNo] == nil {
|
||||
dataChan := make(chan *model.PrintMsg, 1024)
|
||||
t.MsgMap[printMsg.PrintNo] = dataChan
|
||||
}
|
||||
t.MsgMap[printMsg.PrintNo] <- printMsg
|
||||
return err
|
||||
}
|
||||
|
||||
func (t *TcpClient) addCallbackChan(key, data string) {
|
||||
t.Lock()
|
||||
defer t.Unlock()
|
||||
if t.CallBackMap[key] == nil {
|
||||
dataChan := make(chan string, 1024)
|
||||
t.CallBackMap[key] = dataChan
|
||||
}
|
||||
t.CallBackMap[key] <- data
|
||||
}
|
||||
|
||||
func (t *TcpClient) GetCallbackChan(key string) string {
|
||||
t.RLock()
|
||||
defer t.RUnlock()
|
||||
if t.CallBackMap[key] == nil {
|
||||
return ""
|
||||
}
|
||||
return <-t.CallBackMap[key]
|
||||
}
|
||||
|
||||
func NewTcpClient() *TcpClient {
|
||||
t := &TcpClient{
|
||||
Clients: make(map[string]*PrintInfo),
|
||||
CallBackMap: make(map[string]chan string),
|
||||
MsgMap: make(map[string]chan *model.PrintMsg),
|
||||
TimeoutMap: make(map[string]bool, 0),
|
||||
}
|
||||
t.RWMutex = new(sync.RWMutex)
|
||||
return t
|
||||
}
|
||||
|
||||
func printStatus2JxStatus(printStatus string) (status int) {
|
||||
if printStatus == heartErrWithoutPaper {
|
||||
return printerStatusOnlineWithoutPaper
|
||||
} else if printStatus == heartErrNormal {
|
||||
return printerStatusOnline
|
||||
}
|
||||
return status
|
||||
}
|
||||
|
||||
func getCallbackMsgInfo(data string) (orderNo int64, printNo string) {
|
||||
orderNo = h8l82int(data[len(data)-6:len(data)-4], data[len(data)-4:len(data)-2])
|
||||
printNoData, _ := hex.DecodeString(data[len(printSuccessText) : len(data)-6])
|
||||
printNo = string(printNoData)
|
||||
return orderNo, printNo
|
||||
}
|
||||
|
||||
func changePrinterStatus(printNo string, status int) {
|
||||
var (
|
||||
db = dao.GetDB()
|
||||
)
|
||||
if printer, err := dao.GetPrinter(db, printNo); err == nil && printer != nil {
|
||||
var feilds []string
|
||||
if printer.Status != status {
|
||||
printer.Status = status
|
||||
feilds = append(feilds, "Status")
|
||||
}
|
||||
isOnline := 0
|
||||
if status == printerStatusOnline || status == printerStatusOnlineWithoutPaper {
|
||||
isOnline = model.YES
|
||||
} else {
|
||||
isOnline = model.NO
|
||||
printer.OfflineCount++
|
||||
feilds = append(feilds, "OfflineCount")
|
||||
}
|
||||
if isOnline != printer.IsOnline {
|
||||
printer.IsOnline = isOnline
|
||||
feilds = append(feilds, "IsOnline")
|
||||
}
|
||||
if len(feilds) > 0 {
|
||||
dao.UpdateEntity(db, printer, feilds...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//按打印机方提供的文档来的
|
||||
func buildMsg(printMsg *model.PrintMsg) (data []byte, err error) {
|
||||
var (
|
||||
content = printMsg.Content
|
||||
orderNo = printMsg.OrderNo
|
||||
str = "1e"
|
||||
const1 = "0200ff50"
|
||||
printInit = "1b40" //打印机初始化
|
||||
//voice = "1d6b401dfd001a01015b7631365d736f756e64622cc4fad3d0d0c2b6a9b5a5c0b1" //语音,中国
|
||||
//qr = "1d58021b5a0001061600747470733a2f2f7777772e62616964752e636f6d2f1b000A0A0A1B40"
|
||||
orderNoHexH, orderNoHexL, printData string
|
||||
)
|
||||
//写入数据
|
||||
no, err := strconv.ParseInt(orderNo, 10, 64)
|
||||
if err != nil {
|
||||
globals.SugarLogger.Debug("order_msg Order_no 转换异常")
|
||||
}
|
||||
orderNoHexH, orderNoHexL = int2h8l8(no)
|
||||
// 将数据与模板组装
|
||||
if strings.Contains(content, "•") {
|
||||
content = strings.ReplaceAll(content, "•", "-")
|
||||
}
|
||||
printDataGBK, _ := jxutils.Utf8ToGbk([]byte(utils.FilterEmoji(content)))
|
||||
printData = hex.EncodeToString(printDataGBK)
|
||||
printData = replaceContent(printData, printMsg)
|
||||
lenData := int64(len(str) + len(const1) + len(orderNoHexH) + len(orderNoHexL) + len(printInit) + 2 + 4 + len(printData))
|
||||
x1, x2 := int2h8l8(lenData / 2)
|
||||
dataStr := str + x1 + x2 + const1 + orderNoHexH + orderNoHexL + printInit + printData
|
||||
check := getCheckSum(dataStr)
|
||||
return jxutils.Hextob(dataStr + check), err
|
||||
}
|
||||
|
||||
func getCheckSum(str string) (check string) {
|
||||
var sum int64
|
||||
for i := 0; i < len(str); i = i + 2 {
|
||||
b := string(str[i]) + string(str[i+1])
|
||||
bt, _ := strconv.ParseInt(b, 16, 32)
|
||||
sum += bt
|
||||
}
|
||||
_, check = int2h8l8(sum)
|
||||
return check
|
||||
}
|
||||
|
||||
//内容中的标签替换成指令
|
||||
func replaceContent(content string, printMsg *model.PrintMsg) (result string) {
|
||||
var (
|
||||
lenqr int
|
||||
hexLenqr string
|
||||
)
|
||||
result = content
|
||||
for k, v := range signMap {
|
||||
if strings.Contains(result, k) {
|
||||
result = strings.ReplaceAll(result, k, v)
|
||||
}
|
||||
}
|
||||
// 居中标签
|
||||
if strings.Contains(result, byteSignCenter) && strings.Contains(result, byteSignCenterE) {
|
||||
result = strings.ReplaceAll(result, byteSignCenter, hexSignCenter)
|
||||
result = strings.ReplaceAll(result, byteSignCenterE, hexSignBROrEXE+hexSignLeft)
|
||||
}
|
||||
// 居左标签
|
||||
if strings.Contains(result, byteSignLeft) && strings.Contains(result, byteSignLeftE) {
|
||||
result = strings.ReplaceAll(result, byteSignLeft, hexSignLeft)
|
||||
result = strings.ReplaceAll(result, byteSignLeftE, hexSignBROrEXE+hexSignLeft)
|
||||
}
|
||||
// 居右标签
|
||||
if strings.Contains(result, byteSignRight) && strings.Contains(result, byteSignRightE) {
|
||||
result = strings.ReplaceAll(result, byteSignRight, hexSignRight)
|
||||
result = strings.ReplaceAll(result, byteSignRightE, hexSignBROrEXE+hexSignLeft)
|
||||
}
|
||||
// 字体放大
|
||||
if strings.Contains(result, byteSignBig) && strings.Contains(result, byteSignBigE) {
|
||||
result = strings.ReplaceAll(result, byteSignBig, hexSignBig)
|
||||
result = strings.ReplaceAll(result, byteSignBigE, hexSignBROrEXE+hexSignNormal)
|
||||
}
|
||||
// 字体高大
|
||||
if strings.Contains(result, byteSignHighBig) && strings.Contains(result, byteSignHighBigE) {
|
||||
result = strings.ReplaceAll(result, byteSignHighBig, hexSignHighBig)
|
||||
result = strings.ReplaceAll(result, byteSignHighBigE, hexSignBROrEXE+hexSignNormal)
|
||||
}
|
||||
// 字体宽大
|
||||
if strings.Contains(result, byteSignWideBig) && strings.Contains(result, byteSignWideBigE) {
|
||||
result = strings.ReplaceAll(result, byteSignWideBig, hexSignWideBig)
|
||||
result = strings.ReplaceAll(result, byteSignWideBigE, hexSignBROrEXE+hexSignNormal)
|
||||
}
|
||||
// 二维码居中
|
||||
if strings.Contains(result, byteSignQrCenter) && strings.Contains(result, byteSignQrCenterE) {
|
||||
if qrs := regexpQrc.FindStringSubmatch(result); len(qrs) > 0 {
|
||||
lenqr = len(qrs[1]) / 2
|
||||
hexLenqr = fmt.Sprintf("%x", lenqr)
|
||||
if len(hexLenqr) < 2 {
|
||||
hexLenqr = "0" + hexLenqr
|
||||
}
|
||||
}
|
||||
result = strings.ReplaceAll(result, byteSignQrCenter, hexSignQrCenter+hexSignQr+hexLenqr+"00")
|
||||
result = strings.ReplaceAll(result, byteSignQrCenterE, hexSignQrEnd)
|
||||
}
|
||||
// 二维码局左
|
||||
if strings.Contains(result, byteSignQrLeft) && strings.Contains(result, byteSignQrLeftE) {
|
||||
if qrs := regexpQrl.FindStringSubmatch(result); len(qrs) > 0 {
|
||||
lenqr = len(qrs[1]) / 2
|
||||
hexLenqr = fmt.Sprintf("%x", lenqr)
|
||||
if len(hexLenqr) < 2 {
|
||||
hexLenqr = "0" + hexLenqr
|
||||
}
|
||||
}
|
||||
result = strings.ReplaceAll(result, byteSignQrLeft, hexSignQrLeft+hexSignQr+hexLenqr+"00")
|
||||
result = strings.ReplaceAll(result, byteSignQrLeftE, hexSignQrEnd)
|
||||
}
|
||||
// 二维码居右
|
||||
if strings.Contains(result, byteSignQrRight) && strings.Contains(result, byteSignQrRightE) {
|
||||
if qrs := regexpQrr.FindStringSubmatch(result); len(qrs) > 0 {
|
||||
lenqr = len(qrs[1])
|
||||
hexLenqr = fmt.Sprintf("%x", lenqr)
|
||||
if len(hexLenqr) < 2 {
|
||||
hexLenqr = "0" + hexLenqr
|
||||
}
|
||||
}
|
||||
result = strings.ReplaceAll(result, byteSignQrRight, hexSignQrRight+hexSignQr+hexLenqr+"00")
|
||||
result = strings.ReplaceAll(result, byteSignQrRightE, hexSignQrEnd)
|
||||
}
|
||||
// 固定模板输出语音
|
||||
if strings.Contains(result, byteSignSound) && strings.Contains(result, byteSignSoundE) {
|
||||
var soundStr []string
|
||||
for _, v1 := range strings.Split(result, byteSignSoundE) {
|
||||
v1 += byteSignSoundE
|
||||
if sounds := regexpSound.FindStringSubmatch(v1); len(sounds) > 0 {
|
||||
sound := sounds[1]
|
||||
// 将语音包转换为十六进制
|
||||
voice := ""
|
||||
for _, v := range strings.Split(sound, "2c") {
|
||||
voice += hexSignSoundSolidification
|
||||
soundNum, _ := hex.DecodeString(v) // 十六进制转字符串
|
||||
intSound, _ := strconv.ParseInt(string(soundNum), 10, 64)
|
||||
int16Sound := strconv.FormatInt(intSound, 16)
|
||||
if intSound < PrintSoundMaxNumber { // 小于十六补位
|
||||
voice += PlaceFillingParam + int16Sound
|
||||
} else {
|
||||
voice += int16Sound
|
||||
}
|
||||
}
|
||||
soundStr = append(soundStr, voice)
|
||||
}
|
||||
}
|
||||
|
||||
result = strings.ReplaceAll(result, byteSignSound, "*")
|
||||
result = strings.ReplaceAll(result, byteSignSoundE, "&")
|
||||
for i := 0; i < len(soundStr); i++ {
|
||||
start := strings.Index(result, "*")
|
||||
end := strings.Index(result, "&")
|
||||
result = strings.Replace(result, result[start:end+1], soundStr[i], 1)
|
||||
}
|
||||
}
|
||||
// 自动合成语音功能
|
||||
//if strings.Contains(result, byteSignSound) && strings.Contains(result, byteSignSoundE) {
|
||||
// if sounds := regexpSound.FindStringSubmatch(result); len(sounds) > 0 {
|
||||
// sound := sounds[1]
|
||||
// if printer, _ := dao.GetPrinter(dao.GetDB(), printMsg.PrintNo); printer != nil {
|
||||
// //先把结束标签消了
|
||||
// result = strings.ReplaceAll(result, byteSignSoundE, "")
|
||||
// soundPrefix := ""
|
||||
// if printer.Sound != "" {
|
||||
// soundPrefix = "[v" + utils.Int2Str(printer.Volume*2) + "]" + printer.Sound
|
||||
// } else {
|
||||
// soundPrefix = "[v" + utils.Int2Str(printer.Volume*2) + "]"
|
||||
// }
|
||||
// hexPrefix, _ := jxutils.Utf8ToGbk([]byte(soundPrefix))
|
||||
// hexPrefixStr := hex.EncodeToString(hexPrefix)
|
||||
// realSound := hexPrefixStr + sound
|
||||
// allLen := fmt.Sprintf("%x", (len("fd001a0101")+len(realSound))/2)
|
||||
// if len(allLen) < 2 {
|
||||
// allLen = "0" + allLen
|
||||
// }
|
||||
// soundLenH, soundLenX := int2h8l8(int64((len(realSound) + len("0101")) / 2))
|
||||
// result = strings.ReplaceAll(result, byteSignSound, hexSignSound+allLen+"fd"+soundLenH+soundLenX+"0100"+hexPrefixStr)
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
return result
|
||||
}
|
||||
|
||||
func checkPrintMsg(db *dao.DaoDB, printMsg *model.PrintMsg) (err error) {
|
||||
if printMsg.Content == "" {
|
||||
return fmt.Errorf("此打印信息内容为空!printMsg printNo:[%v], orderNo :[%v] 1", printMsg.PrintNo, printMsg.OrderNo)
|
||||
}
|
||||
if printMsg.PrintNo == "" {
|
||||
return fmt.Errorf("此打印信息打印机编号为空!printMsg printNo:[%v], orderNo :[%v] 2", printMsg.PrintNo, printMsg.OrderNo)
|
||||
}
|
||||
if printMsg.OrderNo == "" {
|
||||
return fmt.Errorf("此打印信息订单序号为空!printMsg printNo:[%v], orderNo :[%v] 3", printMsg.PrintNo, printMsg.OrderNo)
|
||||
}
|
||||
//if printer, err := dao.GetPrinter(db, printMsg.PrintNo); err == nil {
|
||||
// if printer != nil {
|
||||
// if printer.FlowFlag == 1 {
|
||||
// return fmt.Errorf("此打印机当月流量已用完,请及时充值!printNo:[%v]", printMsg.PrintNo)
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
return err
|
||||
}
|
||||
|
||||
func int2h8l8(i int64) (h, l string) {
|
||||
origin2 := fmt.Sprintf("%b", i)
|
||||
flag := 16 - len(origin2)
|
||||
for i := 0; i < flag; i++ {
|
||||
origin2 = "0" + origin2
|
||||
}
|
||||
begin8 := origin2[:8]
|
||||
end8 := origin2[8:]
|
||||
r1, _ := strconv.ParseInt(begin8, 2, 32)
|
||||
r2, _ := strconv.ParseInt(end8, 2, 32)
|
||||
h = fmt.Sprintf("%x", r1)
|
||||
l = fmt.Sprintf("%x", r2)
|
||||
if len(h)%2 != 0 {
|
||||
h = "0" + h
|
||||
}
|
||||
if len(l)%2 != 0 {
|
||||
l = "0" + l
|
||||
}
|
||||
return h, l
|
||||
}
|
||||
|
||||
func h8l82int(h, l string) (i int64) {
|
||||
s1, s2 := xtob(h), xtob(l)
|
||||
flag1 := 8 - len(s1)
|
||||
flag2 := 8 - len(s2)
|
||||
for m := 0; m < flag1; m++ {
|
||||
s1 = "0" + s1
|
||||
}
|
||||
for j := 0; j < flag2; j++ {
|
||||
s2 = "0" + s2
|
||||
}
|
||||
i, _ = strconv.ParseInt(s1+s2, 2, 32)
|
||||
return i
|
||||
}
|
||||
|
||||
func xtob(x string) string {
|
||||
base, _ := strconv.ParseInt(x, 16, 10)
|
||||
return strconv.FormatInt(base, 2)
|
||||
}
|
||||
|
||||
// Heartbeat 心跳回调
|
||||
func Heartbeat(c net.Conn, t *TcpClient, data string, printNo string, printRemoteAddr string) {
|
||||
//printNoData, _ := hex.DecodeString(data[len(heartText) : len(data)-8])
|
||||
//printNo = string(printNoData)
|
||||
status := printStatus2JxStatus(data[len(data)-8 : len(data)-6])
|
||||
//如果没在连接池里
|
||||
//1、加到连接池中,不同的打印机no开不同的goroutine
|
||||
//2、初始化channel,每个打印机一个,放打印消息和打印回调消息
|
||||
//3、读数据库里的待打印信息,放到打印channel中
|
||||
//4、读打印channel并打印,并切等待回调channel中的消息
|
||||
//5、修改数据库中打印机状态(没在连接池中说明是重新连接的)
|
||||
//6、监听心跳时间,超过1分多钟就clear掉
|
||||
if t.getClients(printNo) == nil || t == nil || t.getPrintStatusTime(printNo).IsZero() || time.Now().Sub(t.Clients[printNo].StatusTime).Seconds() >= 120 {
|
||||
addConn(c, t, printNo, status)
|
||||
buildAllMap(t, printNo)
|
||||
//t.TimeoutMap[printNo] <- true
|
||||
HandleTcpMessages(t, printNo)
|
||||
doPrint(t, printNo)
|
||||
if status == printerStatusOnline {
|
||||
//t.printFail()
|
||||
}
|
||||
//changePrinterStatus(printNo, status)
|
||||
// todo 暂时关闭心跳检测
|
||||
HandleCheckTcpHeart(t, printNo)
|
||||
// todo 证明打印机已经被激活,将激活打印机存入数据库,保证用户不能无限制绑定打印机
|
||||
if err := dao.NotExistsCreate(printNo); err != nil {
|
||||
globals.SugarLogger.Debugf("监听打印机心跳,不存在则创建 :[%v],printNo[%s]", err, printNo)
|
||||
}
|
||||
|
||||
PrintObject.SetPrintObj(printNo, t)
|
||||
PrintAddrAndIp.SetPrintAddrAndIp(printRemoteAddr, printNo)
|
||||
PrintIpAndAddr.SetPrintIpAndAddr(printNo, printRemoteAddr)
|
||||
} else {
|
||||
//在加到连接池中已经更新了时间,所以放在else里
|
||||
t.setPrintStatusTime(printNo)
|
||||
}
|
||||
//状态不一致再更新状态(可能缺纸了,过热了等)
|
||||
t.setPrintStatus(printNo, status)
|
||||
changePrinterStatus(printNo, status)
|
||||
}
|
||||
|
||||
// Callback 打印成功回调
|
||||
func Callback(c net.Conn, t *TcpClient, data string, printNo string) {
|
||||
//打印消息发送后,打印机会回调该条打印消息的状态(打印成功or失败,失败原因..)
|
||||
//将回调的信息放到回调channel中,打印成功后再打印下一条消息
|
||||
//_, printNo = getCallbackMsgInfo(data)
|
||||
//更新打印机心跳时间(打印机本身不会在打印的同时,或回调的同时发心跳消息,会导致心跳判断超时,这里更新一下)
|
||||
t.setPrintStatusTime(printNo)
|
||||
t.addCallbackChan(printNo, data)
|
||||
}
|
||||
@@ -1,117 +0,0 @@
|
||||
package event
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils"
|
||||
"git.rosy.net.cn/jx-callback/business/model"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
func TestPrintMsg(t *testing.T) {
|
||||
|
||||
msg := &model.PrintMsg{
|
||||
ModelIDCULD: model.ModelIDCULD{},
|
||||
PrintNo: "120220915001069",
|
||||
Content: "<left><hb>客户地址:瑞小满颐园 (瑞小满•颐园正门 门卫大叔 就可以 拍照反馈 辛苦谢谢)@#四川省成都市金牛区天回镇街道瑞小满•熊猫颐园</hb></left>",
|
||||
OrderNo: "12", // 2147483648111
|
||||
Status: 0,
|
||||
Comment: "",
|
||||
MsgID: "20221216175529_09440117",
|
||||
}
|
||||
|
||||
data, err := buildMsg(msg)
|
||||
|
||||
fmt.Println("data=", string(data))
|
||||
fmt.Println("err=", err)
|
||||
}
|
||||
|
||||
// 十六进制转字符串
|
||||
func TestDC(t *testing.T) {
|
||||
str := "093c736f756e643e323009091b61011b2130bea9cef7b9fbd4b00a1b21000a1b61000a0a09091b6101cad6bbfac2f2b2cbc9cfbea9cef70a1b61000a1b6101bcabcbd9b5bdbcd2cbcdbeaacfb20a1b61000a2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0acfc2b5a5cab1bce43a20323032312d30382d32372032313a35373a32360ad4a4bcc6cbcdb4ef3a20323032312d30382d32372032323a35373a32360ab6a9b5a5b1e0bac53a2039333135343434313733303231313130300a0a1b2130c3c0cdc5cde2c2f42331390a1b21000a1d58021b5a00010611003933313534343431373330323131313030000a1b400abfcdbba73a20c0eeb4f3c3ce28cfc8c9fa290ab5e7bbb03a2031353938373230303334300ab5d8d6b73a20b1b1c6d6ceb0d2b5b9e3b3a12d32bac5c2a52028ceb0d2b5b9e3b3a142c7f8294023d4c6c4cfcaa1b3fed0dbd2cdd7e5d7d4d6ced6ddb3fed0dbcad0c2b9b3c7d5f2ceb0d2b5b9e3b3a10a0abfcdbba7b1b8d7a23a200a1b2130a1bec8e7d3f6c8b1bbf5a1bfa3ba20c8b1bbf5cab1b5e7bbb0d3ebced2b9b5cda80a1b21000a0a0ac9ccc6b7c3f7cfb83a200ac6b7c3fb20202020cafdc1bf20202020b5a5bcdb20202020202020d0a1bcc60a2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0aa1bed7d4d3c9c6b4a1bfcfb4bebbbaeccce1313630672fbad00a20202020202078312020202020a3a4392e32302020202020a3a4392e32300aa1beb9fbc7d0a1bf20c5a3c4ccc4beb9cfd4bc333030672fb7dd0a20202020202078312020202020a3a4392e37302020202020a3a4392e37300aa1bed7d4d3c9c6b4a1bfd1cfd1a1cfe3bdb6b9fbc7d0313830672fbad00a20202020202078312020202020a3a4382e38302020202020a3a4382e38300a5bcdf8baecb9fbc7d05dcedac3b7d0a1b7acc7d13235672fb8f60a20202020207831302020202020a3a4302e38302020202020a3a4382e30300a5bcfd6b0fe5dccf0caafc1f1d7d1d4bc323530672fb7dd2831b8f6290a20202020202078312020202020a3a4332e35302020202020a3a4332e35300aa1beb1acc6b7ccd8bbdda1bfd0c2cfcab4e0ccf0b0ebbdefb9fec3dcb9cf20b9fec3dbb9cfb9fbc7d0d2bbbad0323530672fbad0283233307e32363067290a20202020202078312020202020a3a4302e30312020202020a3a4302e30310aa1beb1acc6b7ccd8bbdda1bfd0c2cfcab4e0ccf0b0ebbdefb9fec3dcb9cf20b9fec3dbb9cfb9fbc7d0d2bbbad0323530672fbad0283233307e32363067290a20202020202078312020202020a3a4382e36302020202020a3a4382\ne36300a0ab9b237d6d63136bcfec9ccc6b70a2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0a1b61011b2110c9ccc6b7d6cac1bfcecacce2c7ebc1aacfb53a0a1b21000a1b61000a1b61011b2110bea9cef7b9fbd4b0a1a4b9fbc7d0a1a4cbaeb9fbc0cca3a8b9fbb9fbceddb5eaa3a93a31353834313031313339370a1b21000a1b61000a0ab8fcb6e0d0c5cfa2c7ebb9d8d7a2b9d9b7bdcea2d0c53a20bea9cef7b2cbcad00a0a0a2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0a2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d0a0a0a"
|
||||
str2, _ := hex.DecodeString(str)
|
||||
fmt.Println(string(str2))
|
||||
}
|
||||
|
||||
// 十进制字符串转十六进制
|
||||
func Test10Two16(t *testing.T) {
|
||||
//fmt.Println(strconv.FormatInt(11, 16)) // 2 to 10
|
||||
//for _, v := range strings.Split("324c", "d") {
|
||||
// fmt.Println(v)
|
||||
//}
|
||||
hexPrefix, _ := jxutils.Utf8ToGbk([]byte("[8]sounda14"))
|
||||
hexPrefixStr := hex.EncodeToString(hexPrefix)
|
||||
fmt.Println(hexPrefixStr)
|
||||
}
|
||||
|
||||
func TestCheckSum(t *testing.T) {
|
||||
soundNum, _ := hex.DecodeString("3130") // 十六进制转字符串
|
||||
intSound, _ := strconv.ParseInt(string(soundNum), 10, 64)
|
||||
int16Sound := strconv.FormatInt(intSound, 16)
|
||||
b := strconv.FormatInt(16, 16)
|
||||
if int16Sound < b {
|
||||
fmt.Println("111111")
|
||||
} else {
|
||||
fmt.Println("22222")
|
||||
}
|
||||
fmt.Println("==========", int16Sound)
|
||||
fmt.Println("======2====", utf8.RuneCountInString("64,65,66"))
|
||||
}
|
||||
|
||||
func TestTen216(t *testing.T) {
|
||||
aa := `
|
||||
3c736f756e643e
|
||||
312c322c33
|
||||
3c2f736f756e643e
|
||||
1b6101bed3d6d0cec4d7d60a1b6100
|
||||
3c736f756e643e
|
||||
342c352c36
|
||||
3c2f736f756e643e
|
||||
1b6101bed3d6d00a1b6100
|
||||
`
|
||||
bb := `1B59415501 1B59415502 1B59415503`
|
||||
kk := `1B59415501 1B59415502 1B59415503 1b6101bed3d6d0cec4d7d60a1b6100 1b6101bed3d6d0cec4d7d60a1b6100 1B59415504 1B59415505 1B59415506 1b6101bed3d6d00a1b6100`
|
||||
// 第一个语音字段
|
||||
index1 := strings.Index(aa, "3c736f756e643e")
|
||||
index2 := strings.Index(aa, "3c2f736f756e643e")
|
||||
str := aa[index1:index2]
|
||||
gg := strings.Replace(aa, str, bb, 1)
|
||||
fmt.Println(gg)
|
||||
fmt.Println(kk)
|
||||
}
|
||||
|
||||
func TestNewPool(t *testing.T) {
|
||||
var pool = NewPool(100)
|
||||
pool.Start()
|
||||
for i := 0; i < 10; i++ {
|
||||
num := i
|
||||
pool.AddJob(func() {
|
||||
fmt.Printf("Worker %d: %d is odd? %v\n", num%5, num, num%2 == 1)
|
||||
})
|
||||
}
|
||||
|
||||
time.Sleep(5 * time.Second)
|
||||
for i := 0; i < 10; i++ {
|
||||
pool.AddJob(func() {
|
||||
fmt.Println("刘磊")
|
||||
})
|
||||
}
|
||||
pool.Wait()
|
||||
pool.Stop()
|
||||
|
||||
}
|
||||
|
||||
func TestCC(t *testing.T) {
|
||||
var aa map[string]string
|
||||
|
||||
aa = make(map[string]string, 10)
|
||||
fmt.Println(aa)
|
||||
aa["1"] = "2"
|
||||
c, d := aa["1"]
|
||||
fmt.Println(c)
|
||||
fmt.Println(d)
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
package event
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
var Poll *Pool
|
||||
|
||||
func init() {
|
||||
Poll = NewPool(500)
|
||||
Poll.Start()
|
||||
}
|
||||
|
||||
type Job func()
|
||||
|
||||
type Worker struct {
|
||||
id int
|
||||
jobChannel chan Job
|
||||
quit chan bool
|
||||
}
|
||||
|
||||
type Pool struct {
|
||||
workers []*Worker
|
||||
jobChannel chan Job
|
||||
wg sync.WaitGroup
|
||||
}
|
||||
|
||||
func NewWorker(id int, jobChannel chan Job) *Worker {
|
||||
return &Worker{
|
||||
id: id,
|
||||
jobChannel: jobChannel,
|
||||
quit: make(chan bool),
|
||||
}
|
||||
}
|
||||
|
||||
func (w *Worker) Start() {
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case job := <-w.jobChannel:
|
||||
job()
|
||||
case <-w.quit:
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (w *Worker) Stop() {
|
||||
w.quit <- true
|
||||
close(w.quit)
|
||||
//go func() {
|
||||
// w.quit <- true
|
||||
//}()
|
||||
}
|
||||
|
||||
func NewPool(numWorkers int) *Pool {
|
||||
jobChannel := make(chan Job)
|
||||
pool := &Pool{
|
||||
workers: make([]*Worker, numWorkers),
|
||||
jobChannel: jobChannel,
|
||||
}
|
||||
|
||||
for i := 0; i < numWorkers; i++ {
|
||||
worker := NewWorker(i, jobChannel)
|
||||
pool.workers[i] = worker
|
||||
}
|
||||
|
||||
return pool
|
||||
}
|
||||
|
||||
func (p *Pool) Start() {
|
||||
for _, worker := range p.workers {
|
||||
worker.Start()
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Pool) Stop() {
|
||||
for _, worker := range p.workers {
|
||||
worker.Stop()
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
//func (p *Pool) AddJob(job Job) {
|
||||
// p.wg.Add(1)
|
||||
// p.jobChannel <- func() {
|
||||
// job()
|
||||
// p.wg.Done()
|
||||
// }
|
||||
//}
|
||||
|
||||
func (p *Pool) AddJob(job Job) {
|
||||
p.wg.Add(1)
|
||||
go func() {
|
||||
p.jobChannel <- func() {
|
||||
defer p.wg.Done()
|
||||
job()
|
||||
}
|
||||
}()
|
||||
}
|
||||
func (p *Pool) Wait() {
|
||||
p.wg.Wait()
|
||||
}
|
||||
@@ -1,243 +0,0 @@
|
||||
package financial
|
||||
|
||||
import (
|
||||
_ "fmt"
|
||||
"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/model"
|
||||
"git.rosy.net.cn/jx-callback/business/model/dao"
|
||||
"github.com/astaxie/beego/client/orm"
|
||||
"time"
|
||||
)
|
||||
|
||||
func AddMixPay(txDB orm.TxOrmer, orderID string, balancePrice, totalPrice, status int) error {
|
||||
mixPayInfo := &model.MixPay{
|
||||
OrderID: orderID,
|
||||
BalancePrice: balancePrice,
|
||||
TotalPrice: totalPrice,
|
||||
Status: status,
|
||||
WxPrice: totalPrice - balancePrice,
|
||||
}
|
||||
dao.WrapAddIDCULEntity(mixPayInfo, jxcontext.AdminCtx.GetUserName())
|
||||
return dao.CreateEntityTx(txDB, mixPayInfo)
|
||||
}
|
||||
|
||||
func AddBillIncome(txDB orm.TxOrmer, billID int64, billType, incomePrice, jobID int) (err error) {
|
||||
billIncome := &model.BillIncome{
|
||||
BillID: billID,
|
||||
Type: billType,
|
||||
IncomePrice: incomePrice,
|
||||
JobID: jobID,
|
||||
}
|
||||
dao.WrapAddIDCULEntity(billIncome, jxcontext.AdminCtx.GetUserName())
|
||||
return dao.CreateEntityTx(txDB, billIncome)
|
||||
}
|
||||
|
||||
func AddBillExpend(txDB orm.TxOrmer, billID int64, billType, expendPrice, jobID int) (err error) {
|
||||
billExpend := &model.BillExpend{
|
||||
BillID: billID,
|
||||
Type: billType,
|
||||
ExpendPrice: expendPrice,
|
||||
JobID: jobID,
|
||||
}
|
||||
dao.WrapAddIDCULEntity(billExpend, jxcontext.AdminCtx.GetUserName())
|
||||
return dao.CreateEntityTx(txDB, billExpend)
|
||||
}
|
||||
func AddUserBill(txDB orm.TxOrmer, billID int64, userID string) (err error) {
|
||||
userBillInsert := &model.UserBill{
|
||||
BillID: billID,
|
||||
UserID: userID,
|
||||
}
|
||||
dao.WrapAddIDCULDEntity(userBillInsert, jxcontext.AdminCtx.GetUserName())
|
||||
return dao.CreateEntityTx(txDB, userBillInsert)
|
||||
}
|
||||
|
||||
func AddUserBillDb(db *dao.DaoDB, billID int64, userID string) (err error) {
|
||||
userBillInsert := &model.UserBill{
|
||||
BillID: billID,
|
||||
UserID: userID,
|
||||
}
|
||||
dao.WrapAddIDCULDEntity(userBillInsert, jxcontext.AdminCtx.GetUserName())
|
||||
return dao.CreateEntity(db, userBillInsert)
|
||||
}
|
||||
|
||||
func GetUserBillDetail(ctx *jxcontext.Context, userID, fromTime, toTime string, pageSize, offset int) (pagedInfo *model.PagedInfo, err error) {
|
||||
return dao.GetUserBillDetail(dao.GetDB(), userID, utils.Str2Time(fromTime), utils.Str2Time(toTime), pageSize, offset)
|
||||
}
|
||||
|
||||
func AddExpendUpdateAccount(txDB orm.TxOrmer, userBill *model.UserBill, billType, price, jobID int) (err error) {
|
||||
//1、账户支出增加一条记录
|
||||
err = AddBillExpend(txDB, userBill.BillID, billType, price, jobID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
//2、账户表余额减少相应值
|
||||
userBill.AccountBalance -= price
|
||||
_, err = dao.UpdateEntityTx(txDB, userBill, "AccountBalance")
|
||||
return err
|
||||
}
|
||||
|
||||
func AddIncomeUpdateAccount(txDB orm.TxOrmer, userBill *model.UserBill, billType, price, jobID int) (err error) {
|
||||
//2、账户收入增加一条记录
|
||||
err = AddBillIncome(txDB, userBill.BillID, billType, price, jobID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
//1、根据任务剩余数量退钱到账户余额中
|
||||
userBill.AccountBalance += price
|
||||
_, err = dao.UpdateEntityTx(txDB, userBill, "AccountBalance")
|
||||
return err
|
||||
}
|
||||
|
||||
func SettleUnionOrders(ctx *jxcontext.Context, vendorIDs []int) (err error) {
|
||||
var (
|
||||
db = dao.GetDB()
|
||||
unionOrderVendorMap map[int][]*model.UnionOrder //key 为平台ID
|
||||
unionOrderMap map[string]map[int][]*model.UnionOrder //key为userID,整个map把每个User的不同平台的订单放一起
|
||||
settleOrders []*model.UnionOrderSettle
|
||||
)
|
||||
unionOrderVendorMap = make(map[int][]*model.UnionOrder)
|
||||
unionOrderMap = make(map[string]map[int][]*model.UnionOrder)
|
||||
unionOrders, err := dao.GetUnionOrders(db, vendorIDs, []int{model.UnionOrderStatusFinish}, utils.ZeroTimeValue, utils.ZeroTimeValue, model.NO)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, v := range unionOrders {
|
||||
if _, ok := unionOrderMap[v.UserID]; ok {
|
||||
if _, ol := unionOrderVendorMap[v.VendorID]; ol {
|
||||
unionOrderMap[v.UserID][v.VendorID] = append(unionOrderMap[v.UserID][v.VendorID], v)
|
||||
} else {
|
||||
unionOrderMap[v.UserID][v.VendorID] = []*model.UnionOrder{v}
|
||||
}
|
||||
} else {
|
||||
unionOrderVendorMap[v.VendorID] = []*model.UnionOrder{v}
|
||||
unionOrderMap[v.UserID] = unionOrderVendorMap
|
||||
}
|
||||
}
|
||||
for userID, v := range unionOrderMap {
|
||||
var (
|
||||
billID int64
|
||||
)
|
||||
userBill, _ := dao.GetUserBill(db, userID, "")
|
||||
if userBill == nil {
|
||||
billID = jxutils.GenBillID()
|
||||
AddUserBillDb(db, billID, userID)
|
||||
} else {
|
||||
billID = userBill.BillID
|
||||
}
|
||||
for vendorID, vv := range v {
|
||||
var (
|
||||
sumPrice, count int
|
||||
)
|
||||
for _, unionOrder := range vv {
|
||||
count++
|
||||
sumPrice += unionOrder.PromotionAmount
|
||||
}
|
||||
unionOrderSettle := &model.UnionOrderSettle{
|
||||
BillID: billID,
|
||||
VendorID: vendorID,
|
||||
Issue: jxutils.GetIssue(),
|
||||
EarningPrice: sumPrice,
|
||||
OrderCount: count,
|
||||
}
|
||||
dao.WrapAddIDCULEntity(unionOrderSettle, ctx.GetUserName())
|
||||
settleOrders = append(settleOrders, unionOrderSettle)
|
||||
//err = dao.CreateEntity(db, unionOrderSettle)
|
||||
}
|
||||
}
|
||||
txDB, _ := dao.Begin(db)
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
dao.Rollback(db, txDB)
|
||||
panic(r)
|
||||
}
|
||||
}()
|
||||
for _, v := range settleOrders {
|
||||
//插入结算表
|
||||
if err = dao.CreateEntityTx(txDB, v); err != nil {
|
||||
dao.Rollback(db, txDB)
|
||||
return err
|
||||
}
|
||||
//更新用户账户
|
||||
userBill, _ := dao.GetUserBill(db, "", utils.Int64ToStr(v.BillID))
|
||||
if err = AddIncomeUpdateAccount(txDB, userBill, model.BillTypeUnionShare, v.EarningPrice, 0); err != nil {
|
||||
dao.Rollback(db, txDB)
|
||||
return err
|
||||
}
|
||||
}
|
||||
//修改订单结算标志
|
||||
for _, v := range unionOrders {
|
||||
v.IsEarning = model.YES
|
||||
v.UpdatedAt = time.Now()
|
||||
if _, err = dao.UpdateEntityTx(txDB, v, "IsEarning", "UpdatedAt"); err != nil {
|
||||
dao.Rollback(db, txDB)
|
||||
return err
|
||||
}
|
||||
}
|
||||
dao.Commit(db, txDB)
|
||||
return err
|
||||
}
|
||||
|
||||
//微信 老会员续费
|
||||
func WXInvestMember(ctx *jxcontext.Context, memberID int, userID string) (errCode string, err error) {
|
||||
var db = dao.GetDB()
|
||||
//获取用户 当前会员信息
|
||||
userMembers, err := dao.GetUserMember(db, userID, model.MemberTypeNormal)
|
||||
if err != nil {
|
||||
return "获取用户会员信息失败", err
|
||||
}
|
||||
//当前状态是否是会员 续费/开通
|
||||
if len(userMembers) > 0 {
|
||||
userMember := userMembers[0]
|
||||
if memberID == model.OrderTypeMember {
|
||||
userMember.EndAt = userMember.EndAt.AddDate(0, 1, 0)
|
||||
} else {
|
||||
userMember.EndAt = userMember.EndAt.AddDate(1, 0, 0)
|
||||
}
|
||||
if _, err = dao.UpdateEntity(db, userMember, "EndAt"); err != nil {
|
||||
return "更新会员到期时间失败", err
|
||||
}
|
||||
} else {
|
||||
userMember2 := &model.UserMember{
|
||||
UserID: userID,
|
||||
MemberType: model.MemberTypeNormal,
|
||||
MemberTypeID: memberID,
|
||||
}
|
||||
if memberID == model.OrderTypeMember {
|
||||
userMember2.EndAt = time.Now().AddDate(0, 1, 0)
|
||||
} else {
|
||||
userMember2.EndAt = time.Now().AddDate(1, 0, 0)
|
||||
}
|
||||
dao.WrapAddIDCULDEntity(userMember2, ctx.GetUserName())
|
||||
if err = dao.CreateEntity(db, userMember2); err != nil {
|
||||
return "开通会员失败", err
|
||||
}
|
||||
}
|
||||
return errCode, err
|
||||
}
|
||||
|
||||
//微信 新充值会员
|
||||
func WXInvestMemberNew(ctx *jxcontext.Context, memberID int, userID string) (errCode string, err error) {
|
||||
var db = dao.GetDB()
|
||||
userMember2 := &model.UserMember{
|
||||
UserID: userID,
|
||||
MemberType: 1, //model.MemberTypeNormal,
|
||||
MemberTypeID: 1,
|
||||
}
|
||||
if memberID == model.OrderTypeMember {
|
||||
userMember2.EndAt = time.Now().AddDate(0, 1, 0)
|
||||
} else {
|
||||
userMember2.EndAt = time.Now().AddDate(1, 0, 0)
|
||||
}
|
||||
userinfo, err := dao.GetUser(db, userMember2.UserID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
userMember2.LastOperator = userinfo.UserID2
|
||||
userMember2.DeletedAt = utils.DefaultTimeValue
|
||||
if err = dao.CreateEntity(db, userMember2); err != nil {
|
||||
return "开通会员失败", err
|
||||
}
|
||||
return errCode, err
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
package financial
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestOnWXPayFinished(t *testing.T) {
|
||||
//var (
|
||||
// ctx *jxcontext.Context
|
||||
//)
|
||||
//if _, err := WXInvestMember(ctx, 1, "1303D7B2096011ED9A4C525400C36BDA", true); err != nil {
|
||||
// t.Logf("微信购买会员失败")
|
||||
//}
|
||||
}
|
||||
@@ -1,410 +1,119 @@
|
||||
package financial
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/md5"
|
||||
"crypto/sha256"
|
||||
"encoding/json"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/astaxie/beego/client/orm"
|
||||
"sort"
|
||||
"mime/multipart"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.rosy.net.cn/baseapi/platformapi/tonglianpayapi"
|
||||
"git.rosy.net.cn/baseapi/platformapi/wxpayapi"
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
"git.rosy.net.cn/jx-callback/business/auth2/authprovider/weixin"
|
||||
"git.rosy.net.cn/jx-callback/business/model"
|
||||
"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"
|
||||
)
|
||||
|
||||
const (
|
||||
sigKey = "sign"
|
||||
sigTypeMd5 = "MD5"
|
||||
sigTypeSha256 = "HMAC-SHA256"
|
||||
)
|
||||
|
||||
var (
|
||||
payMap = map[string]*wxpayapi.API{
|
||||
"weixinapp": api.WxpayAPI,
|
||||
"weixinmini": api.WxpayAPI2,
|
||||
}
|
||||
)
|
||||
|
||||
func (p *PayHandler) CreatePay(txDB orm.TxOrmer, subAppID string) (err error) {
|
||||
switch p.PayType {
|
||||
case model.PayTypeTL:
|
||||
param := &tonglianpayapi.CreateUnitorderOrderParam{
|
||||
Trxamt: p.Order.PayPrice,
|
||||
NotifyUrl: globals.TLPayNotifyURL,
|
||||
Reqsn: p.Order.OrderID,
|
||||
PayType: p.VendorPayType,
|
||||
}
|
||||
//暂时做兼容处理
|
||||
if p.VendorPayType == "JSAPI" {
|
||||
param.PayType = tonglianpayapi.PayTypeWxXcx
|
||||
}
|
||||
if p.VendorPayType == tonglianpayapi.PayTypeWxXcx {
|
||||
param.SubAppID = subAppID
|
||||
authInfo, err := p.Ctx.GetV2AuthInfo()
|
||||
// 微信小程序支付
|
||||
if err == nil && authInfo.GetAuthType() == weixin.AuthTypeMini && authInfo.GetAuthTypeID() == subAppID {
|
||||
param.Acct = authInfo.GetAuthID()
|
||||
}
|
||||
}
|
||||
if p.VendorPayType == tonglianpayapi.PayTypeZfbJS || p.VendorPayType == tonglianpayapi.PayTypeZfbApp {
|
||||
if authInfo, err := p.Ctx.GetV2AuthInfo(); err == nil {
|
||||
param.Acct = authInfo.GetAuthID()
|
||||
}
|
||||
if param.Acct == "" {
|
||||
return fmt.Errorf("未找到用户的认证ID!")
|
||||
}
|
||||
}
|
||||
if p.VendorPayType == tonglianpayapi.PayTypeH5 {
|
||||
param2 := &tonglianpayapi.CreateH5UnitorderOrderParam{
|
||||
Trxamt: p.Order.PayPrice,
|
||||
NotifyUrl: globals.TLPayNotifyURL,
|
||||
Body: "冲天猴",
|
||||
Charset: "UTF-8",
|
||||
}
|
||||
err = api.TLpayAPI.CreateH5UnitorderOrder(param2)
|
||||
} else {
|
||||
result, err := api.TLpayAPI.CreateUnitorderOrder(param)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var result2 tonglianpayapi.PayInfo
|
||||
json.Unmarshal([]byte(result.PayInfo), &result2)
|
||||
p.Order.PrepayID = result2.Package[strings.LastIndex(result2.Package, "=")+1 : len(result2.Package)]
|
||||
p.Order.TransactionID = result.TrxID
|
||||
|
||||
if _, err = dao.UpdateEntityTx(txDB, p.Order, "PrepayID", "TransactionID"); err != nil {
|
||||
return err
|
||||
}
|
||||
wxPay := &WxPayParam{
|
||||
Prepayid: p.Order.PrepayID,
|
||||
Noncestr: result2.NonceStr,
|
||||
Timestamp: utils.Int64ToStr(utils.MustInterface2Int64(result2.TimeStamp)),
|
||||
Package: result2.Package,
|
||||
Partnerid: "", // 商户Id
|
||||
Appid: result2.AppID,
|
||||
Sign: result2.PaySign,
|
||||
}
|
||||
p.WxPayParam = wxPay
|
||||
}
|
||||
// 暂时不支持微信直接支付
|
||||
case model.PayTypeWX:
|
||||
param := &wxpayapi.CreateOrderParam{
|
||||
OutTradeNo: p.Order.OrderID,
|
||||
Body: "冲天猴儿App账户充值",
|
||||
NotifyURL: globals.WxpayNotifyURL,
|
||||
SpbillCreateIP: p.Ctx.GetRealRemoteIP(),
|
||||
TradeType: p.VendorPayType,
|
||||
TotalFee: p.Order.PayPrice,
|
||||
TimeStart: wxpayapi.Time2PayTime(time.Now()),
|
||||
// ProfitSharing: wxpayapi.OptYes,
|
||||
}
|
||||
authBinds, err := dao.GetUserBindAuthInfo(dao.GetDB(), p.Ctx.GetUserID(), model.AuthBindTypeAuth, []string{p.Order.Way}, "", "", "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(authBinds) == 0 {
|
||||
return fmt.Errorf("未绑定微信认证方式!")
|
||||
}
|
||||
param.OpenID = authBinds[0].AuthID
|
||||
result, err2 := payMap[p.Order.Way].CreateUnifiedOrder(param)
|
||||
if err2 == nil {
|
||||
param2 := make(map[string]interface{})
|
||||
param2["prepayid"] = result.PrepayID
|
||||
param2["noncestr"] = utils.GetUUID()
|
||||
param2["timestamp"] = time.Now().Unix()
|
||||
param2["package"] = "Sign=WXPay"
|
||||
param2["partnerid"] = result.MchID
|
||||
param2["appid"] = result.AppID
|
||||
sign := signParam(sigTypeMd5, param2)
|
||||
wxPay := &WxPayParam{
|
||||
Prepayid: param2["prepayid"].(string),
|
||||
Noncestr: param2["noncestr"].(string),
|
||||
Timestamp: utils.Int64ToStr(utils.MustInterface2Int64(param2["timestamp"])),
|
||||
Package: param2["package"].(string),
|
||||
Partnerid: param2["partnerid"].(string),
|
||||
Appid: param2["appid"].(string),
|
||||
Sign: sign,
|
||||
}
|
||||
p.WxPayParam = wxPay
|
||||
p.Order.PrepayID = result.PrepayID
|
||||
p.Order.Comment = result.CodeURL
|
||||
_, err = dao.UpdateEntityTx(txDB, p.Order, "PrepayID", "Comment")
|
||||
} else {
|
||||
return err2
|
||||
}
|
||||
default:
|
||||
err = fmt.Errorf("支付方式:%d当前不支持", p.PayType)
|
||||
}
|
||||
return err
|
||||
type tUploadFileInfo struct {
|
||||
FileHeader *multipart.FileHeader
|
||||
StoreID int
|
||||
}
|
||||
|
||||
func signParam(signType string, params map[string]interface{}) (sig string) {
|
||||
var valueList []string
|
||||
for k, v := range params {
|
||||
if k != sigKey {
|
||||
if str := fmt.Sprint(v); str != "" {
|
||||
valueList = append(valueList, fmt.Sprintf("%s=%s", k, str))
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
sort.Sort(sort.StringSlice(valueList))
|
||||
valueList = append(valueList, fmt.Sprintf("key=%s", globals.WxpayAppKey))
|
||||
sig = strings.Join(valueList, "&")
|
||||
var binSig []byte
|
||||
if signType == sigTypeSha256 {
|
||||
mac := hmac.New(sha256.New, []byte(globals.WxpayAppKey))
|
||||
mac.Write([]byte(sig))
|
||||
binSig = mac.Sum(nil)
|
||||
} else {
|
||||
binSig2 := md5.Sum([]byte(sig))
|
||||
binSig = binSig2[:]
|
||||
putPolicy := storage.PutPolicy{
|
||||
Scope: globals.QiniuBucket,
|
||||
Expires: 10 * 60,
|
||||
}
|
||||
sig = fmt.Sprintf("%X", binSig)
|
||||
// baseapi.SugarLogger.Debug(sig)
|
||||
return sig
|
||||
}
|
||||
|
||||
func (p *PayHandler) CreateRefund() (err error) {
|
||||
switch p.PayType {
|
||||
case model.PayTypeTL: // 收费贵,不适用
|
||||
case model.PayTypeAliPay: // 支付宝支付
|
||||
|
||||
case model.PayTypeWX: // 需要保持账号每天有交易流水,不适用
|
||||
//企业付款(提现)
|
||||
if p.VendorPayType == model.VendorPayTypeCompanyPay {
|
||||
param := &wxpayapi.TransfersParam{
|
||||
PartnerTradeNo: p.Order.OrderID,
|
||||
CheckName: wxpayapi.CheckName,
|
||||
Desc: "冲天猴儿app提现到账",
|
||||
SpbillCreateIP: p.Ctx.GetRealRemoteIP(),
|
||||
}
|
||||
//1元以下免费,以上收取对应城市手续费
|
||||
place, err := dao.GetPlaceByCode(dao.GetDB(), p.Order.CityCode)
|
||||
if err != nil || place == nil {
|
||||
return fmt.Errorf("未找到该城市!code:%v", p.Order.CityCode)
|
||||
}
|
||||
if p.Order.PayPrice < 100 {
|
||||
param.Amount = p.Order.PayPrice
|
||||
} else {
|
||||
param.Amount = p.Order.PayPrice * place.DividePercentage / 100 //手续费
|
||||
}
|
||||
if authInfo, err := p.Ctx.GetV2AuthInfo(); err == nil {
|
||||
param.OpenID = authInfo.GetAuthID()
|
||||
}
|
||||
globals.SugarLogger.Debugf("CreateRefund wx param: %v", utils.Format4Output(param, false))
|
||||
result, err2 := payMap[p.Order.Way].Transfers(param)
|
||||
if err2 == nil {
|
||||
p.Order.PayFinishedAt = utils.Str2Time(result.PaymentTime)
|
||||
p.Order.Comment = result.DeviceInfo
|
||||
p.Order.OriginalData = utils.Format4Output(result, true)
|
||||
if result.ReturnMsg == "" {
|
||||
p.Order.Status = model.OrderStatusFinished
|
||||
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 {
|
||||
p.Order.Status = model.OrderStatusCanceled
|
||||
}
|
||||
dao.UpdateEntity(dao.GetDB(), p.Order)
|
||||
if result.ReturnMsg == "" {
|
||||
err = OnCashFinished(p.Order)
|
||||
globals.SugarLogger.Warnf("SendFilesToStores file:%s failed with error:%v", fileHeader.Filename, err)
|
||||
}
|
||||
} else {
|
||||
return err2
|
||||
globals.SugarLogger.Warnf("SendFilesToStores open file:%s failed with error:%v", fileHeader.Filename, err)
|
||||
}
|
||||
err = OnCashFinished(p.Order)
|
||||
} else if p.VendorPayType == model.VendorPayTypeTransferAccount {
|
||||
p.Order.PayFinishedAt = time.Now()
|
||||
p.Order.Comment = "手动转账"
|
||||
p.Order.Status = model.OrderStatusFinished
|
||||
if _, err := dao.UpdateEntity(dao.GetDB(), p.Order); err == nil {
|
||||
err = OnCashFinished(p.Order)
|
||||
}
|
||||
}
|
||||
return retVal, err
|
||||
}, fileList)
|
||||
tasksch.HandleTask(task, nil, true).Run()
|
||||
hint = task.ID
|
||||
if !isAsync {
|
||||
_, err = task.GetResult(0)
|
||||
}
|
||||
return err
|
||||
return task.ID, err
|
||||
}
|
||||
|
||||
func OnTLPayCallback(call *tonglianpayapi.CallBackResult) (err error) {
|
||||
globals.SugarLogger.Debugf("OnTLPayCallback msg:%s", utils.Format4Output(call, true))
|
||||
switch call.TrxCode {
|
||||
case tonglianpayapi.MsgTypePay:
|
||||
err = onTLpayFinished(call)
|
||||
case tonglianpayapi.MsgTypeRefund:
|
||||
err = onTLpayRefund(call)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func onTLpayFinished(call *tonglianpayapi.CallBackResult) (err error) {
|
||||
order := &model.Order{
|
||||
OrderID: call.CusorderID,
|
||||
TransactionID: call.TrxID,
|
||||
}
|
||||
func GetStoreBills(ctx *jxcontext.Context, storeID int) (bills []*legacymodel.StoreBill, err error) {
|
||||
db := dao.GetDB()
|
||||
if err = dao.GetEntity(db, order, "OrderID"); err == nil {
|
||||
if order.Status != model.OrderStatusWait4Pay {
|
||||
globals.SugarLogger.Debugf("already pay msg:%s, err:%v", utils.Format4Output(call, true), err)
|
||||
return err
|
||||
}
|
||||
loc, _ := time.LoadLocation("Local")
|
||||
t1, _ := time.ParseInLocation("20060102150405", call.PayTime, loc)
|
||||
order.PayFinishedAt = t1
|
||||
order.OriginalData = utils.Format4Output(call, true)
|
||||
payStatus := model.PayStatusNo
|
||||
//order.PayMethod = 2 // 通联微信支付
|
||||
if call.TrxStatus == tonglianpayapi.TrxStatusSuccess {
|
||||
order.Status = model.OrderStatusFinished
|
||||
payStatus = model.OrderStatusSuccessPay
|
||||
} else {
|
||||
order.Status = model.OrderStatusCanceled
|
||||
payStatus = model.OrderStatusFailPay
|
||||
}
|
||||
|
||||
//充值会员 增加微信支付处理业务
|
||||
if (order.OrderType == model.OrderTypeMember || order.OrderType == model.OrderTypeMemberYear) && call.TrxStatus == tonglianpayapi.TrxStatusSuccess {
|
||||
return OnWXPayFinished(order)
|
||||
}
|
||||
// 充值话费
|
||||
if order.OrderType == model.OrderTypeMobile && call.TrxStatus == tonglianpayapi.TrxStatusSuccess {
|
||||
return OnWxPayTelephone(order)
|
||||
}
|
||||
// 发快递
|
||||
if order.OrderType == model.OrderTypeDelivery && call.TrxStatus == tonglianpayapi.TrxStatusSuccess {
|
||||
return OnWxPaySendPage(db, order, call, payStatus)
|
||||
}
|
||||
//需要充值到余额方式 购买的
|
||||
if order.OrderType == model.OrderTypeBalance && call.TrxStatus == tonglianpayapi.TrxStatusSuccess {
|
||||
if err := dao.UpdateUserBill(order.UserID, order.PayPrice); err != nil {
|
||||
return err
|
||||
}
|
||||
order.Status = 110
|
||||
if _, err := dao.UpdateEntity(db, order, "Status"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
globals.SugarLogger.Debugf("onTLpayFinished msg:%s, err:%v", utils.Format4Output(call, true), err)
|
||||
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 err
|
||||
return bills, err
|
||||
}
|
||||
|
||||
func onTLpayRefund(call *tonglianpayapi.CallBackResult) (err error) {
|
||||
orderPayRefund := &model.OrderPayRefund{
|
||||
RefundID: call.CusorderID,
|
||||
}
|
||||
db := dao.GetDB()
|
||||
if err = dao.GetEntity(db, orderPayRefund, "RefundID"); err == nil {
|
||||
if call.TrxStatus == tonglianpayapi.TrxStatusSuccess {
|
||||
orderPayRefund.Status = model.RefundStatusYes
|
||||
} else {
|
||||
orderPayRefund.Status = model.RefundStatusFailed
|
||||
}
|
||||
orderPayRefund.OriginalData = utils.Format4Output(call, true)
|
||||
dao.UpdateEntity(db, orderPayRefund)
|
||||
} else if dao.IsNoRowsError(err) {
|
||||
globals.SugarLogger.Warnf("收到异常的退款事件, call:%s", utils.Format4Output(call, true))
|
||||
}
|
||||
|
||||
orderPay := &model.Order{
|
||||
OrderID: orderPayRefund.VendorOrderID,
|
||||
}
|
||||
if err = dao.GetEntity(db, orderPay, "VendorOrderID"); err == nil {
|
||||
orderPay.Status = model.OrderStatusCancel
|
||||
}
|
||||
if orderPay.OrderType == model.PayType4Express {
|
||||
tx, _ := dao.Begin(db)
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
dao.Rollback(db, tx)
|
||||
panic(r)
|
||||
}
|
||||
}()
|
||||
|
||||
if _, err = dao.UpdateEntityTx(tx, orderPay); err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
if _, err = dao.UpdateEntityTx(tx, &model.UserVendorOrder{LocalWayBill: call.CusorderID, OrderStatus: model.OrderStatusCancel}, "OrderStatus"); err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
tx.Commit()
|
||||
return err
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func OnWxPayCallback(msg *wxpayapi.CallbackMsg) (err error) {
|
||||
globals.SugarLogger.Debugf("OnWxPayCallback msg:%s", utils.Format4Output(msg, true))
|
||||
switch msg.MsgType {
|
||||
case wxpayapi.MsgTypePay:
|
||||
err = onWxpayFinished(msg.Data.(*wxpayapi.PayResultMsg))
|
||||
case wxpayapi.MsgTypeRefund:
|
||||
// err = onWxpayRefund(msg.Data.(*wxpayapi.RefundResultMsg))
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func onWxpayFinished(msg *wxpayapi.PayResultMsg) (err error) {
|
||||
order := &model.Order{
|
||||
OrderID: msg.OutTradeNo,
|
||||
}
|
||||
db := dao.GetDB()
|
||||
if err = dao.GetEntity(db, order, "OrderID"); err == nil {
|
||||
order.PayFinishedAt = wxpayapi.PayTime2Time(msg.TimeEnd)
|
||||
order.TransactionID = msg.TransactionID
|
||||
order.OriginalData = utils.Format4Output(msg, true)
|
||||
if msg.ResultCode == wxpayapi.ResponseCodeSuccess {
|
||||
order.Status = model.OrderStatusFinished
|
||||
} else {
|
||||
order.Status = model.OrderStatusCanceled
|
||||
}
|
||||
dao.UpdateEntity(db, order)
|
||||
if msg.ResultCode == wxpayapi.ResponseCodeSuccess {
|
||||
err = OnPayFinished(order)
|
||||
}
|
||||
} else {
|
||||
globals.SugarLogger.Debugf("onWxpayFinished msg:%s, err:%v", utils.Format4Output(msg, true), err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// func onWxpayRefund(msg *wxpayapi.RefundResultMsg) (err error) {
|
||||
// orderPayRefund := &model.OrderPayRefund{
|
||||
// RefundID: msg.ReqInfoObj.OutRefundNo,
|
||||
// }
|
||||
// db := dao.GetDB()
|
||||
// if err = dao.GetEntity(db, orderPayRefund, "RefundID"); err == nil {
|
||||
// if msg.ResultCode == wxpayapi.ResponseCodeSuccess {
|
||||
// orderPayRefund.Status = model.RefundStatusYes
|
||||
// } else {
|
||||
// orderPayRefund.Status = model.RefundStatusFailed
|
||||
// }
|
||||
// orderPayRefund.OriginalData = utils.Format4Output(msg, true)
|
||||
// dao.UpdateEntity(db, orderPayRefund)
|
||||
// } else if dao.IsNoRowsError(err) {
|
||||
// globals.SugarLogger.Warnf("收到异常的退款事件, msg:%s", utils.Format4Output(msg, true))
|
||||
// }
|
||||
|
||||
// orderPay := &model.OrderPay{
|
||||
// VendorOrderID: orderPayRefund.VendorOrderID,
|
||||
// VendorID: jxutils.GetPossibleVendorIDFromVendorOrderID(orderPayRefund.VendorOrderID),
|
||||
// PayType: model.PayTypeWX,
|
||||
// Status: model.PayStatusYes,
|
||||
// }
|
||||
// orderPay.DeletedAt = utils.DefaultTimeValue
|
||||
// if err = dao.GetEntity(db, orderPay, "VendorOrderID", "VendorID", "PayType", "Status", "DeletedAt"); err == nil {
|
||||
// orderPay.Status = model.PayStatusRefund
|
||||
// dao.UpdateEntity(db, orderPay)
|
||||
// }
|
||||
// return err
|
||||
// }
|
||||
|
||||
@@ -1,224 +0,0 @@
|
||||
package financial
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"git.rosy.net.cn/baseapi/platformapi/recharge_phone_bill"
|
||||
"git.rosy.net.cn/baseapi/platformapi/tonglianpayapi"
|
||||
"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/q_bida"
|
||||
"git.rosy.net.cn/jx-callback/globals"
|
||||
"git.rosy.net.cn/jx-callback/globals/api"
|
||||
)
|
||||
|
||||
type PayHandler struct {
|
||||
PayType int `json:"-"` //支付方式
|
||||
Ctx *jxcontext.Context
|
||||
Order *model.Order
|
||||
VendorPayType string
|
||||
|
||||
WxPayParam *WxPayParam `json:"wxPayParam"`
|
||||
}
|
||||
|
||||
type WxPayParam struct {
|
||||
Prepayid string `json:"prepayid"`
|
||||
Noncestr string `json:"noncestr"`
|
||||
Timestamp string `json:"timestamp"`
|
||||
Package string `json:"package"`
|
||||
Partnerid string `json:"partnerid"`
|
||||
Appid string `json:"appid"`
|
||||
Sign string `json:"sign"`
|
||||
}
|
||||
|
||||
type PayHandlerInterface interface {
|
||||
CreatePay() (err error)
|
||||
CreateRefund() (err error)
|
||||
}
|
||||
|
||||
func OnPayFinished(order *model.Order) (err error) {
|
||||
var db = dao.GetDB()
|
||||
globals.SugarLogger.Debugf("OnPayFinished begin modify account order: %v", utils.Format4Output(order, false))
|
||||
txDB, _ := dao.Begin(db)
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
dao.Rollback(db, txDB)
|
||||
panic(r)
|
||||
}
|
||||
}()
|
||||
//如果用户没有对应账单信息就给他生成一条
|
||||
userBill, err := dao.GetUserBill(db, order.UserID, "")
|
||||
if userBill == nil {
|
||||
globals.SugarLogger.Debugf("OnPayFinished 未找到该用户的账单 order: %v", utils.Format4Output(order, false))
|
||||
return fmt.Errorf("未找到该用户的账单!%v", order.UserID)
|
||||
}
|
||||
//根据订单类型来操作账户
|
||||
switch order.Type {
|
||||
case model.OrderTypePay:
|
||||
//如果是账户充值(发布任务等)
|
||||
//账户收入
|
||||
if err = AddIncomeUpdateAccount(txDB, userBill, model.BillTypeInvest, order.PayPrice, 0); err != nil {
|
||||
dao.Rollback(db, txDB)
|
||||
}
|
||||
default:
|
||||
globals.SugarLogger.Debugf("OnPayFinished 暂不支持此订单类型 order: %v", utils.Format4Output(order, false))
|
||||
return fmt.Errorf("暂不支持此订单类型!")
|
||||
}
|
||||
dao.Commit(db, txDB)
|
||||
globals.SugarLogger.Debugf("OnPayFinished end modify account ...")
|
||||
return err
|
||||
}
|
||||
|
||||
func OnCashFinished(order *model.Order) (err error) {
|
||||
var (
|
||||
db = dao.GetDB()
|
||||
)
|
||||
globals.SugarLogger.Debugf("OnCashFinished begin modify account order: %v", utils.Format4Output(order, false))
|
||||
txDB, _ := dao.Begin(db)
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
dao.Rollback(db, txDB)
|
||||
panic(r)
|
||||
}
|
||||
}()
|
||||
//如果用户没有对应账单信息就给他生成一条
|
||||
userBill, err := dao.GetUserBill(db, order.UserID, "")
|
||||
if userBill == nil {
|
||||
globals.SugarLogger.Debugf("OnCashFinished 未找到该用户的账单 order: %v", utils.Format4Output(order, false))
|
||||
return fmt.Errorf("未找到该用户的账单!%v", order.UserID)
|
||||
}
|
||||
//根据订单类型来操作账户
|
||||
switch order.Type {
|
||||
case model.OrderTypeCash:
|
||||
//如果是账户提现
|
||||
//账户支出
|
||||
if err = AddExpendUpdateAccount(txDB, userBill, model.BillTypeCash, order.PayPrice, 0); err != nil {
|
||||
dao.Rollback(db, txDB)
|
||||
}
|
||||
default:
|
||||
globals.SugarLogger.Debugf("OnPayFinished 暂不支持此订单类型 order: %v", utils.Format4Output(order, false))
|
||||
return fmt.Errorf("暂不支持此订单类型!")
|
||||
}
|
||||
dao.Commit(db, txDB)
|
||||
globals.SugarLogger.Debugf("OnCashFinished end modify account ...")
|
||||
return err
|
||||
}
|
||||
|
||||
//微信支付充值会员
|
||||
func OnWXPayFinished(order *model.Order) (err error) {
|
||||
var (
|
||||
db = dao.GetDB()
|
||||
ctx *jxcontext.Context
|
||||
txDB, _ = dao.Begin(db)
|
||||
)
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
dao.Rollback(db, txDB)
|
||||
panic(r)
|
||||
}
|
||||
}()
|
||||
|
||||
//判断是新会员充值还是老会员续费
|
||||
userMembers, err := dao.GetUserMember(db, order.UserID, model.MemberTypeNormal)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
//根据订单类型来操作账户
|
||||
if order.Type == model.OrderTypePay {
|
||||
//为新会员生成会员信息并充值
|
||||
if len(userMembers) > 0 {
|
||||
if _, err := WXInvestMember(ctx, order.OrderType, order.UserID); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if _, err := WXInvestMemberNew(ctx, order.OrderType, order.UserID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
order.Status = model.OrderStatusFinished
|
||||
//更新order状态
|
||||
if _, err := dao.UpdateEntityTx(txDB, &order, "Status"); err != nil {
|
||||
dao.Rollback(db, txDB)
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf("暂不支持此订单类型!")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// OnWxPayTelephone 微信充值话费
|
||||
func OnWxPayTelephone(order *model.Order) error {
|
||||
orderNumber, err := api.TelephoneAPI.RechargePhoneBill(&recharge_phone_bill.RechargePhoneBillBase{
|
||||
FlowCode: order.FlowCode,
|
||||
Mobile: order.Mobile,
|
||||
OrderNumber: order.OrderID,
|
||||
CallbackURL: "http://callback.rsm.jxc4.com/recharge/msg",
|
||||
ChargeType: "1",
|
||||
})
|
||||
if err != nil {
|
||||
order.Comment = err.Error()
|
||||
order.RechargeStatus = -1 // 充值失败
|
||||
}
|
||||
order.RechargeStatus = 1 // 充值中
|
||||
order.Comment = orderNumber
|
||||
//更新order状态
|
||||
order.Status = model.OrderStatusFinished
|
||||
if _, err := dao.UpdateEntity(dao.GetDB(), &order, "Status", "RechargeStatus", "Comment"); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// OnWxPaySendPage 发快递支付方
|
||||
func OnWxPaySendPage(db *dao.DaoDB, order *model.Order, call *tonglianpayapi.CallBackResult, payStatus int) (err error) {
|
||||
txdb, _ := dao.Begin(db)
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
panic(r)
|
||||
}
|
||||
}()
|
||||
order.TransactionID = call.TrxID
|
||||
order.Status = 110 //支付成功状态
|
||||
if _, err := dao.UpdateEntityTx(txdb, order, "TransactionID", "Status"); err != nil {
|
||||
dao.Rollback(db, txdb)
|
||||
return err
|
||||
}
|
||||
userOrder := model.UserVendorOrder{LocalWayBill: order.OrderID}
|
||||
if err := dao.GetEntity(db, &userOrder, "LocalWayBill"); err != nil {
|
||||
dao.Rollback(db, txdb)
|
||||
return err
|
||||
}
|
||||
userOrder.OrderStatus = payStatus
|
||||
if _, err := dao.UpdateEntityTx(txdb, &userOrder, "OrderStatus"); err != nil {
|
||||
dao.Rollback(db, txdb)
|
||||
return err
|
||||
}
|
||||
//1-余额,2-微信,5-混合
|
||||
if order.PayMethod == model.OrderPayMethodMix {
|
||||
userBill, err := dao.GetUserBill(db, order.UserID, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
//创建混合支付账单
|
||||
totalPrice := order.PayPrice + userBill.AccountBalance
|
||||
if err := AddMixPay(txdb, order.OrderID, userBill.AccountBalance, totalPrice, 1); err != nil {
|
||||
dao.Rollback(db, txdb)
|
||||
return err
|
||||
}
|
||||
//余额清空
|
||||
if err := dao.UpdateUserBill(order.UserID, 0); err != nil {
|
||||
dao.Rollback(db, txdb)
|
||||
return err
|
||||
}
|
||||
}
|
||||
dao.Commit(db, txdb)
|
||||
if call.TrxStatus == tonglianpayapi.TrxStatusSuccess {
|
||||
switch order.OrderType {
|
||||
case model.PayType4Express:
|
||||
err = q_bida.CreateOrder2QBiDa(&userOrder, order.OrderID)
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
@@ -6,8 +6,10 @@ import (
|
||||
"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"
|
||||
)
|
||||
|
||||
@@ -48,7 +50,7 @@ func InitPlace(ctx *jxcontext.Context) (err error) {
|
||||
}
|
||||
placeList = placeList[0].Districts
|
||||
db := dao.GetDB()
|
||||
txDB, _ := dao.Begin(db)
|
||||
txDB , _ := dao.Begin(db)
|
||||
defer func() {
|
||||
if r := recover(); r != nil || err != nil {
|
||||
dao.Rollback(db, txDB)
|
||||
@@ -67,10 +69,124 @@ func InitPlace(ctx *jxcontext.Context) (err error) {
|
||||
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,
|
||||
|
||||
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,76 +1,623 @@
|
||||
package misc
|
||||
|
||||
import (
|
||||
"git.rosy.net.cn/jx-callback/business/jxstore/partner/pdd"
|
||||
"git.rosy.net.cn/jx-callback/business/model/dao"
|
||||
bidaServer "git.rosy.net.cn/jx-callback/business/q_bida"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"git.rosy.net.cn/jx-callback/business/jxstore/event"
|
||||
"git.rosy.net.cn/jx-callback/business/partner/purchase/jdshop"
|
||||
|
||||
"git.rosy.net.cn/jx-callback/business/jxstore/cms"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
|
||||
"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"
|
||||
)
|
||||
|
||||
const (
|
||||
SpecialTaskID = "Running"
|
||||
TaskNameSyncStoreSku = "SyncStoreSku"
|
||||
)
|
||||
|
||||
var (
|
||||
dailyHeartbeat = []string{
|
||||
"09:00:00",
|
||||
}
|
||||
dailyWorkTimeList = []string{
|
||||
"00:18:35",
|
||||
"21:30:00",
|
||||
}
|
||||
stationTimeList = []string{
|
||||
"11:35:00",
|
||||
dailyWorkTimeList2 = []string{
|
||||
"2:00:00",
|
||||
}
|
||||
startDeleteTime = []string{
|
||||
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 Init() {
|
||||
if globals.IsMainProductEnv() {
|
||||
|
||||
//拼多多订单轮询
|
||||
ScheduleTimerFuncByInterval(func() {
|
||||
pdd.GetUnionOrders()
|
||||
}, 5*time.Second, 5*time.Minute)
|
||||
|
||||
ScheduleTimerFunc("doDailyWork", doDailyWork, dailyWorkTimeList)
|
||||
|
||||
ScheduleTimerFunc("InitStation", func() {
|
||||
//同步油站信息
|
||||
cms.InitStation(jxcontext.AdminCtx)
|
||||
}, stationTimeList)
|
||||
|
||||
// 定时删除打印信息
|
||||
ScheduleTimerFunc("DeleteTimeOutPrintMsg", func() {
|
||||
dao.DeletePrintMsg()
|
||||
}, stationTimeList)
|
||||
|
||||
// 每两小时更新一下订单信息UpdateOrderStatus
|
||||
ScheduleTimerFuncByInterval(func() {
|
||||
bidaServer.UpdateOrderStatus()
|
||||
}, 5*time.Second, 2*time.Hour)
|
||||
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.RefreshJobStatus(jxcontext.AdminCtx)
|
||||
//刷新任务标签状态
|
||||
cms.RefreshJobSpan(jxcontext.AdminCtx)
|
||||
//刷新用户会员时间
|
||||
cms.RefreshUserMemberStatus(jxcontext.AdminCtx)
|
||||
//删除聊天记录
|
||||
event.DeleteMessageRecord(jxcontext.AdminCtx)
|
||||
//自动确认收货(刷新状态)
|
||||
cms.RefreshDropShippingJob(jxcontext.AdminCtx)
|
||||
//刷新京东快递
|
||||
cms.RefreshJdDelivery(jxcontext.AdminCtx)
|
||||
//美团联盟活动自动创建
|
||||
cms.MtUnionJobAutoUpdate(jxcontext.AdminCtx)
|
||||
|
||||
//同步商品额外前缀和水印图(打标记)
|
||||
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
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
package jds
|
||||
|
||||
import (
|
||||
"git.rosy.net.cn/baseapi/platformapi/jdunionapi"
|
||||
"git.rosy.net.cn/jx-callback/business/jxstore/partner"
|
||||
"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"
|
||||
)
|
||||
|
||||
type UnionHandler struct {
|
||||
}
|
||||
|
||||
var (
|
||||
unionHandler *UnionHandler
|
||||
)
|
||||
|
||||
func init() {
|
||||
partner.UnionHandlerMap[model.VendorIDJDShop] = unionHandler
|
||||
}
|
||||
|
||||
func getAPI() (apiobj *jdunionapi.API) {
|
||||
if configs, err := dao.QueryConfigs(dao.GetDB(), "jdunionCookie", model.ConfigTypeCookie, ""); err == nil {
|
||||
api.JdUnionAPI.SetCookieWithStr(configs[0].Value)
|
||||
}
|
||||
return api.JdUnionAPI
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
package jds
|
||||
|
||||
import (
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
"git.rosy.net.cn/jx-callback/business/jxstore/partner"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
|
||||
)
|
||||
|
||||
func (s *UnionHandler) ShareUnionLink(ctx *jxcontext.Context, linkType, unionActID int, sID, userID string, resourceType int, goodsID string) (link string, err error) {
|
||||
|
||||
return "jds", err
|
||||
}
|
||||
|
||||
func (s *UnionHandler) GetUnionActList(ctx *jxcontext.Context, actType int) (actList []*partner.ActivityList, err error) {
|
||||
if result, err2 := getAPI().ListActivitys(); err2 == nil {
|
||||
for _, v := range result {
|
||||
act := &partner.ActivityList{
|
||||
ActID: v.Activityid,
|
||||
ActName: v.Activityname,
|
||||
ActDes: v.Remark,
|
||||
Ratio: "11%",
|
||||
Img: v.Imageurl,
|
||||
}
|
||||
act.DateBegin = utils.Str2Time(v.Activitystartdate)
|
||||
act.DateEnd = utils.Str2Time(v.Activityenddate)
|
||||
actList = append(actList, act)
|
||||
}
|
||||
} else {
|
||||
err = err2
|
||||
}
|
||||
return actList, err
|
||||
}
|
||||
|
||||
func (s *UnionHandler) CreateUnionPosition(ctx *jxcontext.Context, userID string) (sID string, err error) {
|
||||
return sID, err
|
||||
}
|
||||
|
||||
func (s *UnionHandler) GetUnionMatterList(ctx *jxcontext.Context, vendorCatID, keyword string, page, pageSize, sortType int, listID string) (list *partner.MatterList, err error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func (s *UnionHandler) GetUnionMatterDetail(ctx *jxcontext.Context, goodsID string) (result *partner.GoodsDetail, err error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func (s *UnionHandler) GetUnionMatterListRcmmd(ctx *jxcontext.Context, goodsID string, rcmmdType, offset, pageSize int) (result *partner.MatterList, err error) {
|
||||
return nil, err
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
package mt
|
||||
|
||||
import (
|
||||
"git.rosy.net.cn/baseapi/platformapi/mtunionapi"
|
||||
"git.rosy.net.cn/jx-callback/business/jxstore/partner"
|
||||
"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"
|
||||
)
|
||||
|
||||
type UnionHandler struct {
|
||||
}
|
||||
|
||||
var (
|
||||
unionHandler *UnionHandler
|
||||
)
|
||||
|
||||
func init() {
|
||||
partner.UnionHandlerMap[model.VendorIDMTWM] = unionHandler
|
||||
}
|
||||
|
||||
func getAPI() (apiobj *mtunionapi.API) {
|
||||
if configs, err := dao.QueryConfigs(dao.GetDB(), "mtunionCookie", model.ConfigTypeCookie, ""); err == nil {
|
||||
api.MtUnionAPI.SetCookieWithStr(configs[0].Value)
|
||||
}
|
||||
return api.MtUnionAPI
|
||||
}
|
||||
|
||||
func GetAPI() (apiobj *mtunionapi.API) {
|
||||
if configs, err := dao.QueryConfigs(dao.GetDB(), "mtunionCookie", model.ConfigTypeCookie, ""); err == nil {
|
||||
api.MtUnionAPI.SetCookieWithStr(configs[0].Value)
|
||||
}
|
||||
return api.MtUnionAPI
|
||||
}
|
||||
@@ -1,136 +0,0 @@
|
||||
package mt
|
||||
|
||||
import (
|
||||
"git.rosy.net.cn/baseapi/platformapi/mtunionapi"
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
"git.rosy.net.cn/jx-callback/business/jxstore/cms"
|
||||
"git.rosy.net.cn/jx-callback/business/jxstore/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/api"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
orderStatusMap = map[int]int{
|
||||
mtunionapi.MtUnionOrderStatusPay: model.UnionOrderStatusPay,
|
||||
mtunionapi.MtUnionOrderStatusFinished: model.UnionOrderStatusFinish,
|
||||
mtunionapi.MtUnionOrderStatusCanceled: model.UnionOrderStatusFail,
|
||||
}
|
||||
)
|
||||
|
||||
func (s *UnionHandler) ShareUnionLink(ctx *jxcontext.Context, linkType, unionActID int, sID, userID string, resourceType int, goodsID string) (link string, err error) {
|
||||
if linkType == partner.LinkTypeWeiXinMini {
|
||||
if qrCode, err := api.MtUnionAPI.MiniCode(unionActID, sID); err == nil {
|
||||
if qrCode != "" {
|
||||
return qrCode, err
|
||||
}
|
||||
}
|
||||
return
|
||||
} else {
|
||||
return api.MtUnionAPI.GenerateLink(unionActID, linkType, sID)
|
||||
}
|
||||
return link, err
|
||||
}
|
||||
|
||||
func (s *UnionHandler) GetUnionActList(ctx *jxcontext.Context, actType int) (actList []*partner.ActivityList, err error) {
|
||||
if result, err2 := getAPI().ActivityList(actType, 20, 0); err2 == nil {
|
||||
for _, v := range result {
|
||||
act := &partner.ActivityList{
|
||||
ActID: v.ID,
|
||||
ActName: v.ActName,
|
||||
ActDes: v.ActDes,
|
||||
Ratio: v.Ratio,
|
||||
ActSrc: v.ActSrc,
|
||||
Img: v.URL,
|
||||
ActRule: v.ActRule,
|
||||
}
|
||||
if v.DateBound != "" {
|
||||
act.DateBegin = utils.Str2Time(v.DateBound[:strings.LastIndex(v.DateBound, "至")-1])
|
||||
act.DateEnd = utils.Str2Time(v.DateBound[strings.LastIndex(v.DateBound, "至")+4:])
|
||||
}
|
||||
actList = append(actList, act)
|
||||
}
|
||||
} else {
|
||||
err = err2
|
||||
}
|
||||
return actList, err
|
||||
}
|
||||
|
||||
func (s *UnionHandler) CreateUnionPosition(ctx *jxcontext.Context, userID string) (sID string, err error) {
|
||||
return sID, err
|
||||
}
|
||||
|
||||
func (s *UnionHandler) GetUnionMatterList(ctx *jxcontext.Context, vendorCatID, keyword string, page, pageSize, sortType int, listID string) (list *partner.MatterList, err error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func (s *UnionHandler) GetUnionMatterDetail(ctx *jxcontext.Context, goodsID string) (result *partner.GoodsDetail, err error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func (s *UnionHandler) GetUnionMatterListRcmmd(ctx *jxcontext.Context, goodsID string, rcmmdType, offset, pageSize int) (result *partner.MatterList, err error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func OnCallback(call *mtunionapi.CallBackResult) (err error) {
|
||||
var (
|
||||
db = dao.GetDB()
|
||||
)
|
||||
if order, err2 := api.MtUnionAPI.Rtnotify(call.Orderid, call.Type); order != nil && err2 == nil {
|
||||
unionOrders, err3 := dao.GetUnionOrdersByIDs(db, []string{order.Order.Orderid}, model.VendorIDPDD)
|
||||
if err2 != nil {
|
||||
return err2
|
||||
}
|
||||
if err3 != nil {
|
||||
return err3
|
||||
}
|
||||
orderStatus := &model.UnionOrderStatus{
|
||||
VendorOrderID: order.Order.Orderid,
|
||||
VendorID: model.VendorIDMTWM,
|
||||
Status: mtunionOrderStatus2Jx(order.Order.Status),
|
||||
VendorStatus: utils.Int2Str(order.Order.Status),
|
||||
OrderStatusAt: time.Unix(utils.Str2Int64(order.Order.Modtime), 0),
|
||||
}
|
||||
if len(order.Refund) > 0 {
|
||||
orderStatus.Comment = "售后完成"
|
||||
}
|
||||
//change
|
||||
jxutils.CallMsgHandler(func() {
|
||||
if len(unionOrders) > 0 {
|
||||
if unionOrders[0].Status != orderStatus.Status {
|
||||
cms.ChangeUnionOrder(unionOrders[0], orderStatus)
|
||||
}
|
||||
} else {
|
||||
//new
|
||||
unionOrder := &model.UnionOrder{
|
||||
VendorOrderID: order.Order.Orderid,
|
||||
VendorID: model.VendorIDMTWM,
|
||||
Status: mtunionOrderStatus2Jx(order.Order.Status),
|
||||
PID: order.Order.Sid,
|
||||
PayPrice: int(jxutils.StandardPrice2Int(utils.Str2Float64(order.Order.Direct))),
|
||||
//PromotionAmount: int(jxutils.StandardPrice2Int(utils.Str2Float64(order.Coupon[0].Profit))),
|
||||
GoodsName: order.Order.Smstitle,
|
||||
GoodsID: order.Order.Dealid,
|
||||
OrderCreateAt: time.Unix(utils.Str2Int64(order.Order.Paytime), 0),
|
||||
OrderPayAt: time.Unix(utils.Str2Int64(order.Order.Paytime), 0),
|
||||
OrderReceiveAt: time.Unix(utils.Str2Int64(order.Order.Modtime), 0),
|
||||
//OrderSettleAt: time.Unix(utils.Str2Int64(order.Coupon[0].Usetime), 0),
|
||||
}
|
||||
if len(order.Coupon) > 0 {
|
||||
unionOrder.PromotionAmount = int(jxutils.StandardPrice2Int(utils.Str2Float64(order.Coupon[0].Profit)))
|
||||
unionOrder.OrderSettleAt = time.Unix(utils.Str2Int64(order.Coupon[0].Usetime), 0)
|
||||
}
|
||||
cms.NewUnionOrder(unionOrder, orderStatus)
|
||||
}
|
||||
}, jxutils.ComposeUniversalOrderID(order.Order.Orderid, model.VendorIDMTWM))
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func mtunionOrderStatus2Jx(status int) (jxstatus int) {
|
||||
return orderStatusMap[status]
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
package partner
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
|
||||
)
|
||||
|
||||
var (
|
||||
UnionHandlerMap map[int]UnionInterface
|
||||
)
|
||||
|
||||
const (
|
||||
LinkTypeWeiXinMini = 5 //微信小程序二维码
|
||||
LinTypeH5 = 1 //h5链接
|
||||
LinTypeDeepLink = 2 //deeplink(唤起)链接
|
||||
LinTypeInto = 3 //中间页唤起链接
|
||||
LinTypeWx = 4 //微信小程序唤起路径
|
||||
|
||||
ShareTypeOther = 1 //分享给别人
|
||||
ShareTypeOwn = 2 //自己领
|
||||
|
||||
MtUnionJxSID = "000000001"
|
||||
|
||||
//排序参数,正数升序,负数倒序
|
||||
JxSortTypeYJ = 1 //佣金比
|
||||
JxSortTypeXL = 2 //销量
|
||||
JxSortTypeJG = 3 //价格
|
||||
)
|
||||
|
||||
type UnionOrderInfo struct {
|
||||
SID string `json:"sid"`
|
||||
Profit int64 `json:"profit"` //订单实际返佣金额
|
||||
VendorID int `json:"vendorID"`
|
||||
}
|
||||
|
||||
type ActivityList struct {
|
||||
ActID int `json:"actID"`
|
||||
ActName string `json:"actName"`
|
||||
ActDes string `json:"actDes"`
|
||||
Ratio string `json:"ratio"` //返现比?
|
||||
DateBegin time.Time `json:"dateBegin"`
|
||||
DateEnd time.Time `json:"dateEnd"`
|
||||
ActSrc string `json:"actSrc"` //物料?
|
||||
Img string `json:"img"` //活动图
|
||||
ActRule string `json:"actRule"`
|
||||
}
|
||||
|
||||
type GoodsDetail struct {
|
||||
GoodsList
|
||||
MainImg string `json:"mainImg"`
|
||||
Imgs string `json:"imgs"`
|
||||
StoreImg string `json:"storeImg"` //店铺图片
|
||||
StoreName string `json:"storeName"` //店铺名
|
||||
LgstTxt string `json:"lgstTxt"` //物流分
|
||||
DescTxt string `json:"descTxt"` //描述分
|
||||
ServTxt string `json:"servTxt"` //服务分
|
||||
}
|
||||
|
||||
type GoodsDetail2 struct {
|
||||
MainImg string `json:"mainImg"`
|
||||
Imgs string `json:"imgs"`
|
||||
StoreImg string `json:"storeImg"` //店铺图片
|
||||
StoreName string `json:"storeName"` //店铺名
|
||||
Tpwd string `json:"tpwd"` //淘口令 非苹果ios14以上版本的设备(即其他ios版本、Android系统等),可以用此淘口令正常在复制到手淘打开
|
||||
//针对苹果ios14及以上版本的苹果设备,手淘将按照示例值信息格式读取淘口令(需包含:数字+羊角符+url,
|
||||
//识别规则可能根据ios情况变更)。如需更改淘口令内文案、url等内容,请务必先验证更改后的淘口令在手淘可被识别打开!
|
||||
TpwdIOS14 string `json:"tpwdIOS14"`
|
||||
UrlL string `json:"urlL"` //推广地址长链
|
||||
UrlS string `json:"urlS"` //推广地址短链
|
||||
}
|
||||
|
||||
type GoodsList struct {
|
||||
GoodsID string `json:"goodsID"`
|
||||
GoodsName string `json:"goodsName"`
|
||||
Img string `json:"img"`
|
||||
CouponDiscount int `json:"couponDiscount"` //优惠券
|
||||
CouponRemainQuantity int `json:"couponRemainQuantity"` //优惠券剩余数量
|
||||
MinNormalPrice int `json:"minNormalPrice"` //最小购买价格
|
||||
SalesCount string `json:"salesCount"` //销量
|
||||
PromotionRate int `json:"promotionRate"` //佣金比例,千分比
|
||||
GoodsDetail *GoodsDetail2 `json:"goodsDetail"` //淘宝用
|
||||
}
|
||||
|
||||
type MatterList struct {
|
||||
GoodsList []*GoodsList
|
||||
VendorID int `json:"vendorID"`
|
||||
ListID string `json:"listID"`
|
||||
TotalCount int `json:"totalCount"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
UnionHandlerMap = make(map[int]UnionInterface)
|
||||
}
|
||||
|
||||
type UnionInterface interface {
|
||||
ShareUnionLink(ctx *jxcontext.Context, linkType, unionActID int, sID, userID string, resourceType int, goodsID string) (link string, err error)
|
||||
GetUnionActList(ctx *jxcontext.Context, actType int) (result []*ActivityList, err error)
|
||||
CreateUnionPosition(ctx *jxcontext.Context, userID string) (sID string, err error)
|
||||
GetUnionMatterList(ctx *jxcontext.Context, vendorCatID, keyword string, page, pageSize, sortType int, listID string) (result *MatterList, err error)
|
||||
GetUnionMatterDetail(ctx *jxcontext.Context, goodsID string) (result *GoodsDetail, err error)
|
||||
GetUnionMatterListRcmmd(ctx *jxcontext.Context, goodsID string, rcmmdType, offset, pageSize int) (result *MatterList, err error)
|
||||
}
|
||||
|
||||
func GetHandler(vendorID int) UnionInterface {
|
||||
return UnionHandlerMap[vendorID]
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
package pdd
|
||||
|
||||
import (
|
||||
"git.rosy.net.cn/baseapi/platformapi/pddapi"
|
||||
"git.rosy.net.cn/jx-callback/business/jxstore/partner"
|
||||
"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"
|
||||
)
|
||||
|
||||
type UnionHandler struct {
|
||||
}
|
||||
|
||||
var (
|
||||
unionHandler *UnionHandler
|
||||
)
|
||||
|
||||
func init() {
|
||||
partner.UnionHandlerMap[model.VendorIDPDD] = unionHandler
|
||||
}
|
||||
|
||||
func getAPI() (apiobj *pddapi.API) {
|
||||
if configs, err := dao.QueryConfigs(dao.GetDB(), "pddCookie", model.ConfigTypeCookie, ""); err == nil {
|
||||
api.PddAPI.SetCookieWithStr(configs[0].Value)
|
||||
}
|
||||
return api.PddAPI
|
||||
}
|
||||
@@ -1,280 +0,0 @@
|
||||
package pdd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"git.rosy.net.cn/baseapi/platformapi/pddapi"
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
"git.rosy.net.cn/jx-callback/business/jxstore/cms"
|
||||
"git.rosy.net.cn/jx-callback/business/jxstore/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/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/api"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
sortTypeMap = map[int]int{
|
||||
0: 0, //综合排序
|
||||
partner.JxSortTypeYJ: 1, //按佣金比率升序;
|
||||
-partner.JxSortTypeYJ: 2, //按佣金比例降序;
|
||||
partner.JxSortTypeJG: 3, //按价格升序;
|
||||
-partner.JxSortTypeJG: 4, //按价格降序;
|
||||
partner.JxSortTypeXL: 5, //按销量升序;
|
||||
-partner.JxSortTypeXL: 6, //按销量降序;
|
||||
}
|
||||
|
||||
orderStatusMap = map[int]int{
|
||||
pddapi.OrderStatusPay: model.UnionOrderStatusPay,
|
||||
pddapi.OrderStatus1: model.UnionOrderStatusPay,
|
||||
pddapi.OrderStatusTakeOver: model.UnionOrderStatusTakeOver,
|
||||
pddapi.OrderStatusAuditOver: model.UnionOrderStatusAuditOver,
|
||||
pddapi.OrderStatusAuditFail: model.UnionOrderStatusFail,
|
||||
pddapi.OrderStatusEaring: model.UnionOrderStatusFinish,
|
||||
}
|
||||
)
|
||||
|
||||
func (s *UnionHandler) ShareUnionLink(ctx *jxcontext.Context, linkType, unionActID int, sID, userID string, resourceType int, goodsID string) (link string, err error) {
|
||||
//传了goodsID表示是商品分享
|
||||
if goodsID != "" {
|
||||
authBindFlag, err := api.PddAPI.MemberAuthorityQuery(sID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if result, err := api.PddAPI.GoodsPromotionURLGen(sID, goodsID, !authBindFlag); err == nil {
|
||||
switch linkType {
|
||||
case partner.LinTypeH5:
|
||||
return result.MobileURL, err
|
||||
case partner.LinTypeWx:
|
||||
return result.WeAppInfo.PagePath, err
|
||||
default:
|
||||
return link, fmt.Errorf("暂不支持此链接类型!")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if result, err := api.PddAPI.ResourceURLGen(sID, resourceType); err == nil {
|
||||
switch linkType {
|
||||
case partner.LinkTypeWeiXinMini:
|
||||
return "", err
|
||||
case partner.LinTypeH5:
|
||||
return result.SingleURLList.URL, err
|
||||
case partner.LinTypeWx:
|
||||
return result.WeAppInfo.PagePath, err
|
||||
default:
|
||||
return link, fmt.Errorf("暂不支持此链接类型!")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return link, err
|
||||
}
|
||||
|
||||
func (s *UnionHandler) GetUnionActList(ctx *jxcontext.Context, actType int) (actList []*partner.ActivityList, err error) {
|
||||
if result, err2 := getAPI().ActivityOperationList(); err2 == nil {
|
||||
for _, v := range result {
|
||||
act := &partner.ActivityList{
|
||||
ActID: v.ID,
|
||||
ActName: v.Name,
|
||||
ActDes: v.Description,
|
||||
Ratio: "未知",
|
||||
ActSrc: v.Fileurl,
|
||||
Img: v.Bannerimage,
|
||||
DateBegin: utils.Str2Time(v.Starttime),
|
||||
DateEnd: utils.Str2Time(v.Endtime),
|
||||
}
|
||||
actList = append(actList, act)
|
||||
}
|
||||
}
|
||||
return actList, err
|
||||
}
|
||||
|
||||
func (s *UnionHandler) CreateUnionPosition(ctx *jxcontext.Context, userID string) (sID string, err error) {
|
||||
if sID, err = api.PddAPI.GoodsOPidGenerate(userID); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return sID, err
|
||||
}
|
||||
|
||||
func jxSortType2PddSortType(jxSortType int) (sortType int) {
|
||||
return sortTypeMap[jxSortType]
|
||||
}
|
||||
|
||||
func (s *UnionHandler) GetUnionMatterList(ctx *jxcontext.Context, vendorCatID, keyword string, page, pageSize, sortType int, listID string) (list *partner.MatterList, err error) {
|
||||
var (
|
||||
goodsList []*partner.GoodsList
|
||||
)
|
||||
list = &partner.MatterList{}
|
||||
params := &pddapi.GoodsSearchParam{
|
||||
CatID: utils.Str2Int(vendorCatID),
|
||||
Keyword: keyword,
|
||||
Page: page,
|
||||
PageSize: pageSize,
|
||||
SortType: jxSortType2PddSortType(sortType),
|
||||
ListID: listID,
|
||||
}
|
||||
//-1表示是拼多多的精选
|
||||
if vendorCatID == "-1" {
|
||||
params.ActivityTags = []int{pddapi.GoodsActTagBYBT, pddapi.GoodsActTagJXBK, pddapi.GoodsActTagMS, pddapi.GoodsActTagPPGY, pddapi.GoodsActTagPPHB, pddapi.GoodsActTagQWBT, pddapi.GoodsActTagTZTJ}
|
||||
}
|
||||
if goods, err2 := api.PddAPI.GoodsSearch(params); err2 != nil {
|
||||
return nil, err2
|
||||
} else {
|
||||
list.ListID = goods.ListID
|
||||
list.TotalCount = goods.TotalCount
|
||||
list.VendorID = model.VendorIDPDD
|
||||
for _, v := range goods.GoodsList {
|
||||
good := &partner.GoodsList{
|
||||
GoodsID: v.GoodsSign,
|
||||
GoodsName: v.GoodsName,
|
||||
Img: v.GoodsThumbnailURL,
|
||||
CouponDiscount: v.CouponDiscount,
|
||||
CouponRemainQuantity: v.CouponRemainQuantity,
|
||||
MinNormalPrice: v.MinNormalPrice,
|
||||
SalesCount: v.SalesTip,
|
||||
PromotionRate: v.PromotionRate,
|
||||
}
|
||||
goodsList = append(goodsList, good)
|
||||
}
|
||||
list.GoodsList = goodsList
|
||||
}
|
||||
return list, err
|
||||
}
|
||||
|
||||
func (s *UnionHandler) GetUnionMatterDetail(ctx *jxcontext.Context, goodsID string) (result *partner.GoodsDetail, err error) {
|
||||
result = &partner.GoodsDetail{}
|
||||
if goods, err2 := api.PddAPI.GoodsDetail(goodsID); err2 != nil {
|
||||
return nil, err2
|
||||
} else {
|
||||
result.GoodsID = goods.GoodsSign
|
||||
result.GoodsName = goods.GoodsName
|
||||
result.Img = goods.GoodsThumbnailURL
|
||||
result.CouponDiscount = goods.CouponDiscount
|
||||
result.MinNormalPrice = goods.MinNormalPrice
|
||||
result.CouponRemainQuantity = goods.CouponRemainQuantity
|
||||
result.SalesCount = goods.SalesTip
|
||||
result.Imgs = strings.Join(goods.GoodsGalleryUrls, ",")
|
||||
result.MainImg = goods.GoodsImageURL
|
||||
result.StoreImg = goods.MallImgURL
|
||||
result.StoreName = goods.MallName
|
||||
result.LgstTxt = goods.LgstTxt
|
||||
result.DescTxt = goods.DescTxt
|
||||
result.ServTxt = goods.ServTxt
|
||||
result.PromotionRate = goods.PromotionRate
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (s *UnionHandler) GetUnionMatterListRcmmd(ctx *jxcontext.Context, goodsID string, rcmmdType, offset, pageSize int) (list *partner.MatterList, err error) {
|
||||
var (
|
||||
goodsList []*partner.GoodsList
|
||||
)
|
||||
list = &partner.MatterList{}
|
||||
if goods, err2 := api.PddAPI.GoodsRecommendGet(goodsID, rcmmdType, offset, pageSize); err2 != nil {
|
||||
return nil, err2
|
||||
} else {
|
||||
list.ListID = goods.ListID
|
||||
list.TotalCount = goods.Total
|
||||
list.VendorID = model.VendorIDPDD
|
||||
for _, v := range goods.List {
|
||||
good := &partner.GoodsList{
|
||||
GoodsID: v.GoodsSign,
|
||||
GoodsName: v.GoodsName,
|
||||
Img: v.GoodsThumbnailURL,
|
||||
CouponDiscount: v.CouponDiscount,
|
||||
CouponRemainQuantity: v.CouponRemainQuantity,
|
||||
MinNormalPrice: v.MinNormalPrice,
|
||||
SalesCount: v.SalesTip,
|
||||
PromotionRate: v.PromotionRate,
|
||||
}
|
||||
goodsList = append(goodsList, good)
|
||||
}
|
||||
list.GoodsList = goodsList
|
||||
}
|
||||
return list, err
|
||||
}
|
||||
|
||||
func GetUnionOrders() {
|
||||
var (
|
||||
page = 1
|
||||
pageSize = 50
|
||||
db = dao.GetDB()
|
||||
orderIDs []string
|
||||
unionOrderIDsMap map[string]*model.UnionOrder
|
||||
)
|
||||
orders, err := api.PddAPI.OrderListIncrementGet(time.Now().Add(-time.Minute*5).Unix(), time.Now().Unix(), page, pageSize)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for _, v := range orders {
|
||||
orderIDs = append(orderIDs, v.OrderSn)
|
||||
}
|
||||
unionOrders, err := dao.GetUnionOrdersByIDs(db, orderIDs, model.VendorIDPDD)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
unionOrderIDsMap = make(map[string]*model.UnionOrder)
|
||||
for _, v := range unionOrders {
|
||||
unionOrderIDsMap[v.VendorOrderID] = v
|
||||
}
|
||||
task := tasksch.NewParallelTask("GetUnionOrders1", tasksch.NewParallelConfig().SetIsContinueWhenError(true), jxcontext.AdminCtx,
|
||||
func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
|
||||
order := batchItemList[0].(*pddapi.OrderListIncrementGetResult)
|
||||
detail, err := api.PddAPI.OrderDetailGet(order.OrderSn)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
orderStatus := &model.UnionOrderStatus{
|
||||
VendorOrderID: detail.OrderSn,
|
||||
VendorID: model.VendorIDPDD,
|
||||
Status: pddOrderStatus2Jx(detail.OrderStatus),
|
||||
VendorStatus: utils.Int2Str(detail.OrderStatus),
|
||||
OrderStatusAt: time.Unix(int64(detail.OrderModifyAt), 0),
|
||||
}
|
||||
if detail.ReturnStatus == 1 {
|
||||
orderStatus.Comment = "售后中"
|
||||
} else if detail.ReturnStatus == 2 {
|
||||
orderStatus.Comment = "售后完成"
|
||||
orderStatus.Status = model.UnionOrderStatusFail
|
||||
}
|
||||
//change
|
||||
jxutils.CallMsgHandler(func() {
|
||||
globals.SugarLogger.Debugf("GetUnionOrders pdd, orderstatus: %v", utils.Format4Output(orderStatus, true))
|
||||
if unionOrderIDsMap[order.OrderSn] != nil {
|
||||
globals.SugarLogger.Debugf("GetUnionOrders pdd, unionorders: %v", utils.Format4Output(unionOrderIDsMap[order.OrderSn], true))
|
||||
if unionOrderIDsMap[order.OrderSn].Status != orderStatus.Status {
|
||||
cms.ChangeUnionOrder(unionOrderIDsMap[order.OrderSn], orderStatus)
|
||||
}
|
||||
} else {
|
||||
//new
|
||||
unionOrder := &model.UnionOrder{
|
||||
VendorOrderID: detail.OrderSn,
|
||||
VendorID: model.VendorIDPDD,
|
||||
Status: pddOrderStatus2Jx(detail.OrderStatus),
|
||||
PID: detail.Pid,
|
||||
PayPrice: detail.OrderAmount,
|
||||
PromotionAmount: detail.PromotionAmount,
|
||||
GoodsName: detail.GoodsName,
|
||||
GoodsID: detail.GoodsSign,
|
||||
GoodsImg: detail.GoodsThumbnailURL,
|
||||
OrderCreateAt: time.Unix(int64(order.OrderCreateTime), 0),
|
||||
OrderPayAt: time.Unix(int64(order.OrderPayTime), 0),
|
||||
OrderReceiveAt: time.Unix(int64(order.OrderReceiveTime), 0),
|
||||
OrderSettleAt: time.Unix(int64(order.OrderSettleTime), 0),
|
||||
Comment: order.FailReason,
|
||||
}
|
||||
cms.NewUnionOrder(unionOrder, orderStatus)
|
||||
}
|
||||
}, jxutils.ComposeUniversalOrderID(order.OrderSn, model.VendorIDPDD))
|
||||
return retVal, err
|
||||
}, orders)
|
||||
tasksch.HandleTask(task, nil, false).Run()
|
||||
task.GetID()
|
||||
}
|
||||
|
||||
func pddOrderStatus2Jx(status int) (jxstatus int) {
|
||||
return orderStatusMap[status]
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
package taobao
|
||||
|
||||
import (
|
||||
"git.rosy.net.cn/baseapi/platformapi/tbunionapi"
|
||||
"git.rosy.net.cn/jx-callback/business/jxstore/partner"
|
||||
"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"
|
||||
)
|
||||
|
||||
type UnionHandler struct {
|
||||
}
|
||||
|
||||
var (
|
||||
unionHandler *UnionHandler
|
||||
)
|
||||
|
||||
func init() {
|
||||
partner.UnionHandlerMap[model.VendorIDTB] = unionHandler
|
||||
}
|
||||
|
||||
func getAPI() (apiobj *tbunionapi.API) {
|
||||
if configs, err := dao.QueryConfigs(dao.GetDB(), "tbunionCookie", model.ConfigTypeCookie, ""); err == nil {
|
||||
api.TbUnionAPI.SetCookieWithStr(configs[0].Value)
|
||||
}
|
||||
return api.TbUnionAPI
|
||||
}
|
||||
@@ -1,189 +0,0 @@
|
||||
package taobao
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"git.rosy.net.cn/baseapi/platformapi/tbunionapi"
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
"git.rosy.net.cn/jx-callback/business/jxstore/partner"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
|
||||
"git.rosy.net.cn/jx-callback/business/model"
|
||||
"git.rosy.net.cn/jx-callback/globals/api"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
sortTypeMap = map[int]string{
|
||||
0: "", //综合排序
|
||||
partner.JxSortTypeYJ: "tk_total_commi_asc", //按佣金比率升序;
|
||||
-partner.JxSortTypeYJ: "tk_total_commi_des", //按佣金比例降序;
|
||||
partner.JxSortTypeJG: "price_asc", //按价格升序;
|
||||
-partner.JxSortTypeJG: "price_des", //按价格降序;
|
||||
partner.JxSortTypeXL: "total_sales_asc", //按销量升序;
|
||||
-partner.JxSortTypeXL: "total_sales_des", //按销量降序;
|
||||
}
|
||||
)
|
||||
|
||||
func (s *UnionHandler) ShareUnionLink(ctx *jxcontext.Context, linkType, unionActID int, sID, userID string, resourceType int, goodsID string) (link string, err error) {
|
||||
if result, err2 := api.TbUnionAPI.ActivityInfoGet(userID, utils.Int2Str(unionActID), utils.Str2Int64(sID)); err2 == nil {
|
||||
switch linkType {
|
||||
case partner.LinkTypeWeiXinMini:
|
||||
return result.Data.WxQrcodeURL, err
|
||||
case partner.LinTypeH5:
|
||||
return result.Data.ClickURL, err
|
||||
case partner.LinTypeWx:
|
||||
return result.Data.WxMiniprogramPath, err
|
||||
default:
|
||||
return link, fmt.Errorf("暂不支持此链接类型!")
|
||||
}
|
||||
}
|
||||
return link, err
|
||||
}
|
||||
|
||||
func (s *UnionHandler) GetUnionActList(ctx *jxcontext.Context, actType int) (actList []*partner.ActivityList, err error) {
|
||||
if result, err2 := getAPI().GatewayUnionpub(); err2 == nil {
|
||||
for _, v := range result {
|
||||
act := &partner.ActivityList{
|
||||
ActID: utils.Str2Int(v.Pageid),
|
||||
ActName: v.Pagename,
|
||||
ActDes: v.Eventenname,
|
||||
Ratio: "6%",
|
||||
ActSrc: v.Pageurl,
|
||||
Img: v.Pagepicturl,
|
||||
DateBegin: utils.Str2Time(v.Pagestarttime),
|
||||
DateEnd: utils.Str2Time(v.Pageendtime),
|
||||
}
|
||||
actList = append(actList, act)
|
||||
}
|
||||
}
|
||||
return actList, err
|
||||
}
|
||||
|
||||
func (s *UnionHandler) CreateUnionPosition(ctx *jxcontext.Context, userID string) (sID string, err error) {
|
||||
return sID, err
|
||||
}
|
||||
|
||||
func (s *UnionHandler) GetUnionMatterList(ctx *jxcontext.Context, vendorCatID, keyword string, page, pageSize, sortType int, listID string) (list *partner.MatterList, err error) {
|
||||
list = &partner.MatterList{
|
||||
VendorID: model.VendorIDTB,
|
||||
}
|
||||
var goodsList []*partner.GoodsList
|
||||
//传了keyword就是物料搜索
|
||||
//没传就是物料精选。。
|
||||
if keyword != "" {
|
||||
params := &tbunionapi.MaterialOptionalParam{
|
||||
Q: keyword,
|
||||
AdzoneID: tbunionapi.JxAdzoneID,
|
||||
PageNo: page,
|
||||
PageSize: pageSize,
|
||||
}
|
||||
if sortType != 0 {
|
||||
params.Sort = jxSortType2TbunionSortType(sortType)
|
||||
}
|
||||
if materResults, err := api.TbUnionAPI.MaterialOptional(params); err == nil {
|
||||
list.TotalCount = materResults.TotalResults
|
||||
for _, v := range materResults.ResultList {
|
||||
good := &partner.GoodsList{
|
||||
GoodsID: utils.Int64ToStr(v.ItemID),
|
||||
GoodsName: v.Title,
|
||||
Img: v.PictURL,
|
||||
CouponDiscount: utils.Str2Int(v.CouponAmount),
|
||||
CouponRemainQuantity: v.CouponRemainCount,
|
||||
MinNormalPrice: utils.Str2Int(v.ReservePrice),
|
||||
SalesCount: utils.Int2Str(v.Volume),
|
||||
PromotionRate: utils.Str2Int(v.CommissionRate),
|
||||
}
|
||||
good.GoodsDetail.MainImg = v.WhiteImage
|
||||
good.GoodsDetail.StoreName = v.ShopTitle
|
||||
good.GoodsDetail.Imgs = strings.Join(v.SmallImages, ",")
|
||||
good.GoodsDetail.UrlL = v.CouponShareURL
|
||||
if urlS, err2 := api.TbUnionAPI.SpreadGet(v.CouponShareURL); err2 == nil {
|
||||
good.GoodsDetail.UrlS = urlS
|
||||
}
|
||||
if tpwdCreateResult, err3 := api.TbUnionAPI.TpwdCreate(good.GoodsDetail.UrlL); err3 == nil {
|
||||
good.GoodsDetail.Tpwd = tpwdCreateResult.PasswordSimple
|
||||
good.GoodsDetail.TpwdIOS14 = tpwdCreateResult.Model
|
||||
}
|
||||
goodsList = append(goodsList, good)
|
||||
}
|
||||
list.GoodsList = goodsList
|
||||
}
|
||||
} else {
|
||||
if materResults, err := api.TbUnionAPI.OptimusMaterial(0, utils.Str2Int(vendorCatID), tbunionapi.JxAdzoneID, page, pageSize); err == nil {
|
||||
for _, v := range materResults {
|
||||
good := &partner.GoodsList{
|
||||
GoodsID: utils.Int64ToStr(v.ItemID),
|
||||
GoodsName: v.Title,
|
||||
Img: v.PictURL,
|
||||
CouponDiscount: v.CouponAmount,
|
||||
CouponRemainQuantity: v.CouponRemainCount,
|
||||
MinNormalPrice: utils.Str2Int(v.ReservePrice),
|
||||
SalesCount: utils.Int2Str(v.Volume),
|
||||
PromotionRate: utils.Str2Int(v.CommissionRate),
|
||||
}
|
||||
goodsDetail := &partner.GoodsDetail2{
|
||||
MainImg: v.WhiteImage,
|
||||
StoreName: v.ShopTitle,
|
||||
Imgs: strings.Join(v.SmallImages, ","),
|
||||
UrlL: v.CouponShareURL,
|
||||
}
|
||||
if urlS, err2 := api.TbUnionAPI.SpreadGet("https:" + v.CouponShareURL); err2 == nil {
|
||||
goodsDetail.UrlS = urlS
|
||||
}
|
||||
if tpwdCreateResult, err3 := api.TbUnionAPI.TpwdCreate("https:" + goodsDetail.UrlL); err3 == nil {
|
||||
goodsDetail.Tpwd = tpwdCreateResult.PasswordSimple
|
||||
goodsDetail.TpwdIOS14 = tpwdCreateResult.Model
|
||||
}
|
||||
good.GoodsDetail = goodsDetail
|
||||
goodsList = append(goodsList, good)
|
||||
}
|
||||
list.GoodsList = goodsList
|
||||
}
|
||||
}
|
||||
return list, err
|
||||
}
|
||||
|
||||
func (s *UnionHandler) GetUnionMatterDetail(ctx *jxcontext.Context, goodsID string) (result *partner.GoodsDetail, err error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func (s *UnionHandler) GetUnionMatterListRcmmd(ctx *jxcontext.Context, goodsID string, rcmmdType, offset, pageSize int) (list *partner.MatterList, err error) {
|
||||
list = &partner.MatterList{
|
||||
VendorID: model.VendorIDTB,
|
||||
}
|
||||
var goodsList []*partner.GoodsList
|
||||
if materResults, err := api.TbUnionAPI.OptimusMaterial(utils.Str2Int(goodsID), 13256, tbunionapi.JxAdzoneID, offset, pageSize); err == nil {
|
||||
for _, v := range materResults {
|
||||
good := &partner.GoodsList{
|
||||
GoodsID: utils.Int64ToStr(v.ItemID),
|
||||
GoodsName: v.Title,
|
||||
Img: v.PictURL,
|
||||
CouponDiscount: v.CouponAmount,
|
||||
CouponRemainQuantity: v.CouponRemainCount,
|
||||
MinNormalPrice: utils.Str2Int(v.ReservePrice),
|
||||
SalesCount: utils.Int2Str(v.Volume),
|
||||
PromotionRate: utils.Str2Int(v.CommissionRate),
|
||||
}
|
||||
goodsDetail := &partner.GoodsDetail2{
|
||||
MainImg: v.WhiteImage,
|
||||
StoreName: v.ShopTitle,
|
||||
Imgs: strings.Join(v.SmallImages, ","),
|
||||
UrlL: v.CouponShareURL,
|
||||
}
|
||||
if urlS, err2 := api.TbUnionAPI.SpreadGet("https:" + v.CouponShareURL); err2 == nil {
|
||||
goodsDetail.UrlS = urlS
|
||||
}
|
||||
if tpwdCreateResult, err3 := api.TbUnionAPI.TpwdCreate("https:" + goodsDetail.UrlL); err3 == nil {
|
||||
goodsDetail.Tpwd = tpwdCreateResult.PasswordSimple
|
||||
goodsDetail.TpwdIOS14 = tpwdCreateResult.Model
|
||||
}
|
||||
good.GoodsDetail = goodsDetail
|
||||
goodsList = append(goodsList, good)
|
||||
}
|
||||
list.GoodsList = goodsList
|
||||
}
|
||||
return list, err
|
||||
}
|
||||
|
||||
func jxSortType2TbunionSortType(sort int) (tbsort string) {
|
||||
return sortTypeMap[sort]
|
||||
}
|
||||
@@ -1,9 +1,14 @@
|
||||
package cms
|
||||
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"
|
||||
@@ -11,10 +16,17 @@ import (
|
||||
|
||||
func GetMenu(ctx *jxcontext.Context, userID string) (menus []*model.Menu, err error) {
|
||||
if userID == "" {
|
||||
return dao.GetMenu(dao.GetDB(), "", 0, userID)
|
||||
return dao.GetMenu(dao.GetDB(), "", 0, 0, userID)
|
||||
} else {
|
||||
return dao.GetMenuWithUser(dao.GetDB(), "", 0, userID)
|
||||
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) {
|
||||
@@ -27,7 +39,7 @@ func AddMenu(ctx *jxcontext.Context, menu *model.Menu) (err error) {
|
||||
if menu.Name == "" || menu.Level == 0 {
|
||||
return fmt.Errorf("添加失败!menu 名称和等级必须有值!")
|
||||
}
|
||||
menus, err := dao.GetMenu(db, menu.Name, menu.Level, "")
|
||||
menus, err := dao.GetMenu(db, menu.Name, menu.Level, 0, "")
|
||||
if len(menus) > 0 {
|
||||
return fmt.Errorf("添加失败!已存在相同名称的 menu name : %v", menu.Name)
|
||||
}
|
||||
@@ -77,15 +89,31 @@ func UpdateMenu(ctx *jxcontext.Context, menuID int, payload map[string]interface
|
||||
return num, err
|
||||
}
|
||||
|
||||
func GetRole(ctx *jxcontext.Context) (roles []*model.Role, err error) {
|
||||
return dao.GetRole(dao.GetDB(), "")
|
||||
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)
|
||||
roles, err := dao.GetRole(db, "", name)
|
||||
if len(roles) > 0 {
|
||||
return fmt.Errorf("添加失败!已存在相同名称的 role name : %v", name)
|
||||
}
|
||||
@@ -105,10 +133,18 @@ func AddRole(ctx *jxcontext.Context, name string) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
func UpdateRole(ctx *jxcontext.Context, roleID int, name string, isDelete bool) (num int64, err error) {
|
||||
func UpdateRole(ctx *jxcontext.Context, roleID int, name string, isDelete bool, brandID int, cityCodes, storeIDs []int) (num int64, err error) {
|
||||
var (
|
||||
db = dao.GetDB()
|
||||
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("管理员不允许修改!")
|
||||
}
|
||||
@@ -129,7 +165,10 @@ func UpdateRole(ctx *jxcontext.Context, roleID int, name string, isDelete bool)
|
||||
role.Name = name
|
||||
role.UpdatedAt = time.Now()
|
||||
role.LastOperator = ctx.GetUserName()
|
||||
num, err = dao.UpdateEntity(db, role, "Name", "UpdatedAt", "LastOperator")
|
||||
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")
|
||||
@@ -171,7 +210,7 @@ func UpdateUserRole(ctx *jxcontext.Context, userIDs []string, roleIDs []int) (er
|
||||
}
|
||||
nowRoleIDMap[nowRoleID] = 1
|
||||
}
|
||||
for _, roleID := range roleIDMap {
|
||||
for roleID, _ := range roleIDMap {
|
||||
if nowRoleIDMap[roleID] == 0 {
|
||||
addUserRoleMap[userID] = append(addUserRoleMap[userID], roleID)
|
||||
}
|
||||
@@ -208,6 +247,12 @@ func UpdateUserRole(ctx *jxcontext.Context, userIDs []string, roleIDs []int) (er
|
||||
}
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
@@ -245,9 +290,9 @@ func UpdateRoleMenu(ctx *jxcontext.Context, roleIDs, menuIDs []int) (err error)
|
||||
}
|
||||
nowMenuIDMap[nowMenuID] = 1
|
||||
}
|
||||
for _, menuID := range menuIDMap {
|
||||
for menuID, _ := range menuIDMap {
|
||||
if nowMenuIDMap[menuID] == 0 {
|
||||
addRoleMenuMap[menuID] = append(addRoleMenuMap[menuID], menuID)
|
||||
addRoleMenuMap[roleID] = append(addRoleMenuMap[roleID], menuID)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -285,3 +330,102 @@ func UpdateRoleMenu(ctx *jxcontext.Context, roleIDs, menuIDs []int) (err error)
|
||||
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
@@ -28,7 +28,7 @@ func ListConfig(key string) (configList []*legacymodel.Config, err error) {
|
||||
`
|
||||
sqlParams := []interface{}{}
|
||||
if key != "" {
|
||||
sql += " WHERE thirdparty = ?"
|
||||
sql += " WHERE thirdparty = ?"
|
||||
sqlParams = append(sqlParams, key)
|
||||
}
|
||||
db := dao.GetDB()
|
||||
|
||||
@@ -3,8 +3,6 @@ package datares
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"git.rosy.net.cn/jx-callback/globals/api"
|
||||
"github.com/qiniu/api.v7/storage"
|
||||
"image"
|
||||
"image/gif"
|
||||
"image/jpeg"
|
||||
@@ -16,16 +14,19 @@ import (
|
||||
"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"
|
||||
"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 {
|
||||
@@ -82,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)
|
||||
}
|
||||
}
|
||||
@@ -110,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
|
||||
}
|
||||
@@ -153,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
|
||||
}
|
||||
|
||||
|
||||
@@ -1,25 +1,32 @@
|
||||
package datares
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"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/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) {
|
||||
// token, err := GetQiniuUploadToken(jxcontext.AdminCtx, "", "")
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// fmt.Print(token)
|
||||
//}
|
||||
func TestGetQiniuUploadToken(t *testing.T) {
|
||||
token, err := GetQiniuUploadToken(jxcontext.AdminCtx, "", "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fmt.Print(token)
|
||||
}
|
||||
|
||||
func TestGetDataResource(t *testing.T) {
|
||||
dataRes, err := GetDataResource(jxcontext.AdminCtx, "1D3E4A8259F359FB4CF47D541843950D")
|
||||
|
||||
@@ -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()
|
||||
token = api.PushAPI.CBGetToken()
|
||||
case EventTypeWX2Token:
|
||||
token = api.WeixinMiniAPI2.CBGetToken()
|
||||
}
|
||||
|
||||
if token != oldToken {
|
||||
|
||||
@@ -25,8 +25,6 @@ type Context struct {
|
||||
|
||||
const (
|
||||
MaxUserNameLen = 30
|
||||
|
||||
RsmDefultToken = "rushSkyMonkeyToken_20201203"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -76,8 +74,6 @@ 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 {
|
||||
err = nil
|
||||
} else {
|
||||
errCode = model.ErrCodeTokenIsInvalid
|
||||
}
|
||||
|
||||
@@ -7,13 +7,11 @@ import (
|
||||
"crypto/md5"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"golang.org/x/text/encoding/simplifiedchinese"
|
||||
"golang.org/x/text/transform"
|
||||
"io/ioutil"
|
||||
"math"
|
||||
"math/rand"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -44,14 +42,36 @@ var (
|
||||
model.VendorIDEBAI: []string{
|
||||
"image-star.elemecdn.com",
|
||||
},
|
||||
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
|
||||
@@ -60,6 +80,30 @@ func init() {
|
||||
orderNoBeginTimestamp = utils.Str2Time("2010-01-01 00:00:00").Unix()
|
||||
}
|
||||
|
||||
func getJxStoreIDFromOrder(order *model.GoodsOrder) (retVal int) {
|
||||
if order.JxStoreID != 0 {
|
||||
return order.JxStoreID
|
||||
}
|
||||
return order.StoreID
|
||||
}
|
||||
|
||||
// 此函数得到的是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
|
||||
@@ -91,60 +135,46 @@ func SplitUniversalOrderID(universalOrderID string) (orderID string, vendorID in
|
||||
|
||||
func GetPossibleVendorIDFromVendorOrderID(vendorOrderID string) (vendorID int) {
|
||||
vendorID = model.VendorIDUnknown
|
||||
if vendorOrderIDInt64 := utils.Str2Int64WithDefault(vendorOrderID, 0); vendorOrderIDInt64 > 0 {
|
||||
orderIDLen := len(vendorOrderID)
|
||||
// 5287873015048 13 wsc
|
||||
// 15380342248732 14 old ebai order
|
||||
// 800402581000221 15,16 jd order
|
||||
// 33437032333978492 17 mtwm order
|
||||
// 3022716176275221584 19 elm order, new ebai order
|
||||
//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") {
|
||||
vendorID = model.VendorIDMTWM
|
||||
} else if orderIDLen == len("1000004390") {
|
||||
// 京东到家从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 if orderIDLen == len("124557362562000001") || orderIDLen == len("13153183146800000100") {
|
||||
vendorID = model.VendorIDJDShop
|
||||
} 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 GenRand6() (num int) {
|
||||
return utils.Str2Int(fmt.Sprintf("%06v", rand.New(rand.NewSource(time.Now().UnixNano())).Int31n(1000000)))
|
||||
}
|
||||
|
||||
func GenOrderNo() (orderNo int64) {
|
||||
var prefix = utils.Str2Int64(time.Now().Format("20060102"))
|
||||
const randPartNum = 1000
|
||||
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 GenJobOrderNo() (orderNo int64) {
|
||||
const prefix = 88
|
||||
const randPartNum = 1000
|
||||
orderNo = time.Now().Unix() - orderNoBeginTimestamp
|
||||
@@ -159,35 +189,6 @@ func GenJobOrderNo() (orderNo int64) {
|
||||
return orderNo
|
||||
}
|
||||
|
||||
func GenBillID() (billID int64) {
|
||||
const prefix = 66
|
||||
const randPartNum = 100
|
||||
billID = time.Now().Unix() - orderNoBeginTimestamp
|
||||
billID = billID * randPartNum
|
||||
md5Bytes := md5.Sum([]byte(utils.GetUUID()))
|
||||
randPart := 0
|
||||
for k, v := range md5Bytes {
|
||||
randPart += int(v) << ((k % 3) * 8)
|
||||
}
|
||||
billID += int64(randPart % randPartNum)
|
||||
billID += int64(math.Pow10(int(math.Log10(float64(billID)))+1)) * prefix
|
||||
return billID
|
||||
}
|
||||
|
||||
func GenGroupID() (groupID int64) {
|
||||
const randPartNum = 100
|
||||
groupID = time.Now().Unix() - orderNoBeginTimestamp
|
||||
groupID = groupID * randPartNum
|
||||
md5Bytes := md5.Sum([]byte(utils.GetUUID()))
|
||||
randPart := 0
|
||||
for k, v := range md5Bytes {
|
||||
randPart += int(v) << ((k % 3) * 8)
|
||||
}
|
||||
groupID += int64(randPart % randPartNum)
|
||||
groupID += int64(math.Pow10(int(math.Log10(float64(groupID))) + 1))
|
||||
return groupID
|
||||
}
|
||||
|
||||
func GenAfsOrderNo() (orderNo int64) {
|
||||
const prefix = 80
|
||||
const randPartNum = 100
|
||||
@@ -221,6 +222,19 @@ func GetPossibleVendorIDFromAfsOrderID(afsOrderID string) (vendorID int) {
|
||||
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单位为米
|
||||
@@ -257,6 +271,26 @@ func WalkingDistance(lng1, lat1, lng2, lat2 float64) (distance float64) {
|
||||
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))
|
||||
}
|
||||
@@ -386,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("[")
|
||||
@@ -395,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 {
|
||||
@@ -422,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 = ""
|
||||
@@ -435,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
|
||||
}
|
||||
|
||||
@@ -545,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 {
|
||||
@@ -585,6 +624,58 @@ 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
|
||||
@@ -598,6 +689,56 @@ func RefreshAfsOrderSkuRelated(afsOrder *model.AfsOrder) *model.AfsOrder {
|
||||
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,
|
||||
@@ -685,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 {
|
||||
@@ -756,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 {
|
||||
@@ -787,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
|
||||
}
|
||||
@@ -824,27 +1018,42 @@ func PKCS5UnPadding(origData []byte) []byte {
|
||||
}
|
||||
|
||||
//合成水印图
|
||||
func MixWatermarkImg(imgWatermark, img, positon string) (imgMix string) {
|
||||
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/" + positon + "/dx/10/dy/10"
|
||||
} else {
|
||||
imgUrl = img + "?imageView2/0/q/75|watermark/1/image/" + baseURL + "/dissolve/100/gravity/" + positon + "/dx/10/dy/10"
|
||||
}
|
||||
if resBinary, _, err := DownloadFileByURL(imgUrl); err == nil {
|
||||
if downloadURL, err := UploadExportContent(resBinary, "image/"+utils.Int64ToStr(time.Now().Unix())+img[strings.LastIndex(img, "/")+1:len(img)]); err == nil {
|
||||
if err == nil {
|
||||
return downloadURL
|
||||
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 GetIssue() (issue int) {
|
||||
year, month, _ := time.Now().Date()
|
||||
return year*100 + int(month)
|
||||
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"))
|
||||
}
|
||||
|
||||
//根据一堆坐标求面积
|
||||
@@ -877,53 +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 Utf8ToGbk(str []byte) (b []byte, err error) {
|
||||
r := transform.NewReader(bytes.NewReader(str), simplifiedchinese.GBK.NewEncoder())
|
||||
b, err = ioutil.ReadAll(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
func Hextob(str string) []byte {
|
||||
slen := len(str)
|
||||
bHex := make([]byte, len(str)/2)
|
||||
ii := 0
|
||||
for i := 0; i < len(str); i = i + 2 {
|
||||
if slen != 1 {
|
||||
ss := string(str[i]) + string(str[i+1])
|
||||
bt, _ := strconv.ParseInt(ss, 16, 32)
|
||||
bHex[ii] = byte(bt)
|
||||
ii = ii + 1
|
||||
slen = slen - 2
|
||||
}
|
||||
}
|
||||
return bHex
|
||||
return i
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
beego "github.com/astaxie/beego/server/web"
|
||||
"image"
|
||||
"image/png"
|
||||
"io/ioutil"
|
||||
@@ -216,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
|
||||
@@ -251,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
|
||||
@@ -258,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
|
||||
|
||||
@@ -341,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 {
|
||||
@@ -382,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
|
||||
}
|
||||
}
|
||||
@@ -461,11 +593,3 @@ func CalcPolygonAreaAutonavi(points [][2]float64) (area float64) {
|
||||
d += eee*point[1]*c - k*g2
|
||||
return 0.5 * math.Abs(d) / float64(1000000)
|
||||
}
|
||||
|
||||
func BuildErr(errs []error) (err error) {
|
||||
var errStr = strings.Builder{}
|
||||
for _, v := range errs {
|
||||
errStr.WriteString(v.Error())
|
||||
}
|
||||
return fmt.Errorf(errStr.String())
|
||||
}
|
||||
|
||||
@@ -90,6 +90,7 @@ func TestSplitUniversalOrderID(t *testing.T) {
|
||||
[]interface{}{
|
||||
"3022716176275221584",
|
||||
"3022716176275221584",
|
||||
model.VendorIDELM,
|
||||
},
|
||||
[]interface{}{
|
||||
"15380342248732",
|
||||
@@ -104,6 +105,7 @@ func TestSplitUniversalOrderID(t *testing.T) {
|
||||
[]interface{}{
|
||||
"5287873015048",
|
||||
"5287873015048",
|
||||
model.VendorIDWSC,
|
||||
},
|
||||
}
|
||||
for _, v := range testData {
|
||||
@@ -149,6 +151,7 @@ func TestGetPossibleVendorIDFromVendorOrderID(t *testing.T) {
|
||||
},
|
||||
[]interface{}{
|
||||
"5287873015048",
|
||||
model.VendorIDWSC,
|
||||
},
|
||||
}
|
||||
for _, v := range testData {
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/ddmsg"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/weixinmsg"
|
||||
"git.rosy.net.cn/jx-callback/business/model"
|
||||
|
||||
"git.rosy.net.cn/baseapi/platformapi/dingdingapi"
|
||||
@@ -32,7 +33,7 @@ func SendUserMessage(msgType string, user *model.User, title, content string) (e
|
||||
}
|
||||
err = ddmsg.SendDDUserMessage(msgType, auth.AuthID, title, content)
|
||||
} else if auth.Type == weixin.AuthTypeMP && msgType != dingdingapi.MsgTypeMarkdown {
|
||||
//err = weixinmsg.NotifyStoreStatusChanged(auth.AuthID, title, content)
|
||||
err = weixinmsg.NotifyStoreStatusChanged(auth.AuthID, title, content)
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user