1989 Commits
rsm ... master

Author SHA1 Message Date
807875765@qq.com
cde7d11563 同步EXCEL 第一版 2022-03-14 09:05:28 +08:00
suyl
cc437c32c2 Merge remote-tracking branch 'origin/mark' 2021-08-17 16:14:45 +08:00
suyl
dfa7cf159e aa 2021-08-17 16:02:57 +08:00
suyl
b1e0bf5cb5 aa 2021-08-17 15:59:11 +08:00
suyl
738c35a25a aa 2021-08-17 15:09:55 +08:00
suyl
1b87bde859 aa 2021-08-17 14:41:32 +08:00
suyl
936813003b aa 2021-08-17 13:36:56 +08:00
suyl
411dbaa068 aa 2021-08-17 13:35:15 +08:00
suyl
634e7c8a6b aa 2021-08-17 11:51:11 +08:00
suyl
fe15525b8e aa 2021-08-17 11:25:20 +08:00
suyl
ea3098ec66 aa 2021-08-17 11:22:24 +08:00
suyl
55a2d4b80f aa 2021-08-17 11:21:47 +08:00
suyl
863c2c44d4 a 2021-08-17 11:16:29 +08:00
suyl
c40ee75121 aa 2021-08-17 11:14:18 +08:00
suyl
3a3d1d2f4f aa 2021-08-17 11:00:04 +08:00
suyl
9170b072d7 aa 2021-08-17 10:46:44 +08:00
suyl
d38b283460 aa 2021-08-17 10:38:27 +08:00
suyl
140fda8927 aa 2021-08-16 09:41:38 +08:00
suyl
b067647050 aa 2021-08-14 16:47:23 +08:00
suyl
e0fe502f49 aa 2021-08-14 16:41:15 +08:00
suyl
a0fa918322 aa 2021-08-14 16:39:05 +08:00
suyl
8698b920a9 aa 2021-08-14 15:10:58 +08:00
suyl
e8d7abe61b aa 2021-08-14 15:09:11 +08:00
suyl
0ee1912fae aa 2021-08-14 14:34:29 +08:00
suyl
068c25356b aa 2021-08-14 14:03:32 +08:00
suyl
cdc91763d1 aa 2021-08-14 14:01:20 +08:00
suyl
b26842caf8 aa 2021-08-14 13:58:09 +08:00
suyl
7b4fecc3ec a 2021-08-14 11:34:12 +08:00
suyl
691e65f369 aa 2021-08-14 11:29:39 +08:00
suyl
9d85e74fb0 aa 2021-08-14 10:37:58 +08:00
suyl
77888cc17c aa 2021-08-14 10:25:45 +08:00
suyl
aaa6cd8178 aa 2021-08-14 10:21:16 +08:00
suyl
5ef56867a0 aa 2021-08-14 10:17:56 +08:00
suyl
cc429e785d aa 2021-08-14 10:10:57 +08:00
suyl
2068ea6bf6 aa 2021-08-14 09:53:39 +08:00
suyl
78e9c177ea aa 2021-08-14 09:52:11 +08:00
suyl
59af1a146c aa 2021-08-13 18:53:38 +08:00
suyl
6cffb31d83 aa 2021-08-13 18:51:34 +08:00
suyl
f45fa869a1 aa 2021-08-13 18:43:44 +08:00
suyl
8754f1949e aa 2021-08-13 18:23:19 +08:00
suyl
5b7ac4700e aa 2021-08-13 17:53:54 +08:00
suyl
09e4414d0e aa 2021-08-13 17:22:12 +08:00
suyl
07635aa5ba aa 2021-08-13 17:20:00 +08:00
suyl
70ac32e833 aa 2021-08-13 17:18:14 +08:00
suyl
adec1bf126 aa 2021-08-13 17:10:55 +08:00
suyl
66fdea2164 aa 2021-08-13 17:07:42 +08:00
suyl
f4e77ec21e aa 2021-08-13 16:27:57 +08:00
suyl
fb73d38d31 aa 2021-08-13 15:21:26 +08:00
suyl
72960b6ced aa 2021-08-13 14:56:20 +08:00
suyl
885a5e885b aa 2021-08-13 14:52:06 +08:00
suyl
4f58c18c52 aa 2021-08-13 14:51:13 +08:00
suyl
c0d0669e21 aa 2021-08-13 14:49:00 +08:00
suyl
49b9213c52 a 2021-08-13 14:05:04 +08:00
suyl
d23b51252e a 2021-08-13 13:57:43 +08:00
suyl
9c385b3198 aa 2021-08-13 11:05:48 +08:00
suyl
e659cf3f7f aa 2021-08-12 09:28:17 +08:00
suyl
89a1ec23b1 aa 2021-08-10 11:05:33 +08:00
suyl
e30bd47ea6 aa 2021-08-10 11:03:23 +08:00
suyl
f57995ddb5 aa 2021-08-10 10:35:20 +08:00
suyl
1947db34c0 aa 2021-08-10 10:32:02 +08:00
suyl
4ad32dc2b7 t 2021-08-10 10:30:52 +08:00
suyl
39271aa7c7 aa 2021-08-10 10:24:18 +08:00
suyl
cc77ae075d aa 2021-08-10 10:17:57 +08:00
suyl
bc710e48b5 预订单打印 2021-08-09 15:16:46 +08:00
suyl
b74879f91a 2021-08-09 10:49:37 +08:00
suyl
e294beba0d 投诉提示 2021-08-09 10:48:53 +08:00
suyl
d9f04a9842 aa 2021-08-09 10:23:11 +08:00
suyl
ee34c419bb aa 2021-08-06 15:04:59 +08:00
suyl
e61fcb74b7 aa 2021-08-06 14:31:51 +08:00
suyl
82a4bf47de aa 2021-08-06 14:27:31 +08:00
suyl
f9b7b1d32e aa 2021-08-06 10:50:53 +08:00
suyl
86f58bd376 aa 2021-08-06 10:47:33 +08:00
suyl
8a179a496a aa 2021-08-06 10:04:20 +08:00
suyl
e5b00c0a79 a 2021-08-05 15:19:46 +08:00
suyl
d12902d678 aa 2021-08-05 11:59:14 +08:00
suyl
db16f2950f aa 2021-08-05 11:55:54 +08:00
suyl
8274bc46db test 2021-08-05 11:47:10 +08:00
suyl
ff5debfe44 aa 2021-08-05 11:24:08 +08:00
suyl
bc38e21668 a 2021-08-05 11:20:12 +08:00
suyl
fb3ac9051d aa 2021-08-05 10:59:58 +08:00
suyl
1b1301fc0e aa 2021-08-05 10:34:18 +08:00
suyl
901171fa6e aa 2021-08-05 10:26:49 +08:00
suyl
3f4ae785b0 aa 2021-08-05 10:22:18 +08:00
suyl
8a81ab9c3b aa 2021-08-05 10:16:06 +08:00
suyl
04b41d6e44 aa 2021-08-05 10:12:46 +08:00
suyl
517c443332 aa 2021-08-04 17:50:30 +08:00
suyl
3e47450078 a 2021-08-04 17:33:26 +08:00
suyl
e5467de15e aa 2021-08-04 17:12:18 +08:00
suyl
c40e026dc5 aa 2021-08-04 16:40:20 +08:00
suyl
4119ae3eb6 a 2021-08-04 13:45:34 +08:00
suyl
d1fcacc441 aa 2021-08-04 10:38:45 +08:00
suyl
9cfeafb01c aa 2021-08-04 09:28:15 +08:00
suyl
8bd114f281 aa 2021-08-03 16:59:38 +08:00
suyl
7397b232d9 aa 2021-08-03 16:46:09 +08:00
suyl
230328c38a aa 2021-08-03 16:45:06 +08:00
suyl
6f92cd6f0d aa 2021-08-03 16:31:20 +08:00
suyl
a09223b091 aa 2021-08-03 16:15:18 +08:00
suyl
a41fe05e63 aa 2021-08-03 16:04:26 +08:00
suyl
d81855d486 aa 2021-08-03 16:01:43 +08:00
suyl
68f8294c20 a 2021-08-03 15:30:37 +08:00
suyl
d9e565a5c4 aa 2021-08-03 09:24:44 +08:00
suyl
763ab8569c aa 2021-08-02 18:51:35 +08:00
suyl
9f5438ce92 aa 2021-08-02 18:09:32 +08:00
suyl
e12bb0a2dd aa 2021-08-02 16:06:27 +08:00
suyl
e58d009928 aa 2021-08-02 13:47:37 +08:00
suyl
2853d14e60 aa 2021-08-02 13:43:40 +08:00
suyl
473c3291b8 a 2021-08-02 11:01:05 +08:00
suyl
16b0f7ef33 a 2021-08-02 10:54:05 +08:00
suyl
39a7164d4a aa 2021-08-02 10:50:09 +08:00
suyl
19546f82f6 aa 2021-08-02 10:25:11 +08:00
suyl
2232af6d1c a 2021-08-02 10:21:29 +08:00
suyl
ede44f40f7 aa 2021-08-02 09:43:13 +08:00
suyl
00647ebaf4 aa 2021-08-02 09:16:35 +08:00
suyl
3ceef530d4 aa 2021-07-30 16:28:05 +08:00
suyl
d57beb9a4a aa 2021-07-30 16:19:50 +08:00
suyl
fb1cffd140 aa 2021-07-30 16:08:19 +08:00
suyl
ac7075fab4 aa 2021-07-30 15:24:22 +08:00
suyl
143a0f38dc ebai store 2021-07-30 15:16:42 +08:00
suyl
eacb7e0ccb aa 2021-07-30 11:07:19 +08:00
suyl
f060920812 aa 2021-07-30 10:14:47 +08:00
suyl
f350ed7044 .. 2021-07-29 16:54:51 +08:00
suyl
8260d2e2dd aa 2021-07-29 11:53:42 +08:00
suyl
8acd6030f4 aa 2021-07-29 11:28:58 +08:00
suyl
c15a96fcf5 aa 2021-07-29 11:14:43 +08:00
suyl
f0718ef83c aa 2021-07-29 10:57:40 +08:00
suyl
7b645cc5da aa 2021-07-28 14:21:22 +08:00
suyl
10ca5606c2 aa 2021-07-27 18:45:54 +08:00
suyl
9c8704f252 aa 2021-07-27 17:58:07 +08:00
suyl
2176d6ea6f aa 2021-07-27 17:37:38 +08:00
suyl
a913ce904f aa 2021-07-27 11:11:42 +08:00
suyl
4b7ff1aed3 aa 2021-07-26 14:48:00 +08:00
suyl
1190b8910a 美团售后 2021-07-26 14:41:19 +08:00
suyl
d48eff03b8 aa 2021-07-26 09:55:59 +08:00
suyl
71bee4fef2 aa 2021-07-23 17:49:46 +08:00
suyl
4301c9a90a aa' 2021-07-23 17:47:10 +08:00
suyl
cec46e4654 aa 2021-07-22 17:11:34 +08:00
suyl
4728178c0b aa 2021-07-22 17:10:46 +08:00
suyl
7352ecc7fa aa 2021-07-22 16:58:18 +08:00
suyl
71cb042b46 aa 2021-07-22 16:33:02 +08:00
suyl
a2cf621fb7 a 2021-07-22 16:23:48 +08:00
suyl
778f17f3f6 aa 2021-07-22 15:49:18 +08:00
suyl
64106dcd64 aa 2021-07-22 11:20:46 +08:00
suyl
33e6ca5104 aa 2021-07-22 09:43:18 +08:00
suyl
0ab78607e1 aa 2021-07-21 15:38:15 +08:00
suyl
8b133e6b6a aa 2021-07-21 11:40:42 +08:00
suyl
3401ad1086 aa 2021-07-21 11:02:43 +08:00
suyl
66ff82e0be aa 2021-07-21 10:43:46 +08:00
suyl
677d73e180 aa 2021-07-21 10:38:43 +08:00
suyl
b6c9982390 aa 2021-07-21 09:09:27 +08:00
suyl
263b1daa96 aa 2021-07-20 11:01:54 +08:00
suyl
6c687b9431 aa 2021-07-20 11:01:04 +08:00
suyl
6889d62b08 aa 2021-07-20 10:58:29 +08:00
suyl
9a08503a2f aa 2021-07-20 10:56:25 +08:00
suyl
36602a9cea aa 2021-07-20 10:46:07 +08:00
suyl
d7df047549 aa 2021-07-20 10:41:50 +08:00
suyl
20af1c2fe1 aa 2021-07-20 10:24:38 +08:00
suyl
40611d9418 aa 2021-07-20 10:18:15 +08:00
suyl
d00d1cd16d aa 2021-07-20 10:15:19 +08:00
suyl
2608d1ed2f aa 2021-07-20 10:12:07 +08:00
suyl
1bb6c86b4a aa 2021-07-20 09:53:37 +08:00
suyl
d5d9f509d7 aa 2021-07-19 17:09:32 +08:00
suyl
2d8838c3d7 aa 2021-07-19 16:53:30 +08:00
suyl
38891c676a aa 2021-07-19 16:48:44 +08:00
suyl
145211bbe2 aa 2021-07-19 16:33:02 +08:00
suyl
e19d03ebba aa 2021-07-19 15:44:29 +08:00
suyl
10fb5c7cb1 aa 2021-07-19 11:05:29 +08:00
suyl
91abfc8835 aa 2021-07-19 10:55:03 +08:00
suyl
d946c5018a aa 2021-07-19 10:47:38 +08:00
suyl
7d8b48bb36 aa 2021-07-19 10:24:16 +08:00
suyl
cd1c03c834 aa 2021-07-19 10:21:23 +08:00
suyl
0c41bf7c42 aa 2021-07-19 09:27:02 +08:00
suyl
a7f28b59de aa 2021-07-16 17:05:49 +08:00
suyl
10811a669b aa 2021-07-16 17:05:13 +08:00
suyl
14ec99a8be 投诉 2021-07-16 10:34:18 +08:00
suyl
89f9dd5b6e aa 2021-07-16 10:01:40 +08:00
suyl
828264a51b initjxprint 2021-07-16 09:50:47 +08:00
suyl
e83f7b7a17 aa 2021-07-16 09:46:53 +08:00
suyl
5ac2885861 aa 2021-07-16 09:15:17 +08:00
suyl
2780df1dbd aa 2021-07-15 16:15:13 +08:00
suyl
9dd291b132 aa 2021-07-15 16:05:53 +08:00
suyl
ffe72bcdbd jxprint 2021-07-15 16:01:36 +08:00
suyl
b7bd0015ef aa 2021-07-15 15:40:27 +08:00
suyl
71cb0a62a4 jxprint 2021-07-15 15:35:28 +08:00
suyl
3172f67e20 aa 2021-07-15 14:40:46 +08:00
suyl
91b95bb2cc aa 2021-07-15 14:32:03 +08:00
suyl
aad04d5043 aa 2021-07-15 14:22:42 +08:00
suyl
06dca66631 aa 2021-07-15 14:18:25 +08:00
suyl
c1fbee7d22 aa 2021-07-15 14:16:26 +08:00
suyl
5f2ce8e415 a 2021-07-15 14:13:05 +08:00
suyl
c4ac39eea8 aa 2021-07-15 14:06:51 +08:00
suyl
71419171c8 aa 2021-07-15 13:56:00 +08:00
suyl
25fea4d861 aa 2021-07-15 13:44:41 +08:00
suyl
b3901e6a7c aa 2021-07-15 11:44:18 +08:00
suyl
3c67bc1b65 test 2021-07-15 11:37:53 +08:00
suyl
a7827d893d 禁止调度 2021-07-14 18:37:21 +08:00
suyl
e1d865e42a aa 2021-07-14 14:19:27 +08:00
suyl
2b82822fb4 aa 2021-07-14 14:13:31 +08:00
suyl
9210794b35 aa 2021-07-14 14:00:04 +08:00
suyl
f6ce015ef5 aa 2021-07-14 13:52:21 +08:00
suyl
511528848a aa 2021-07-14 10:50:20 +08:00
suyl
c0024d2421 删除订单商品 2021-07-14 10:44:01 +08:00
suyl
3cf5025ff2 aa 2021-07-13 11:08:02 +08:00
suyl
10f7928e3a aa 2021-07-13 11:05:03 +08:00
suyl
a0e179819e aa 2021-07-13 09:42:52 +08:00
suyl
8919115a58 京西订单origin不算 2021-07-13 09:42:10 +08:00
suyl
29bf5da9e5 a 2021-07-13 09:32:43 +08:00
suyl
e1241c7af1 aa 2021-07-12 14:23:42 +08:00
suyl
a7f116a3ac 把日常跑的拆开看 2021-07-12 13:58:03 +08:00
suyl
30deedf220 美团售前同意条件 2021-07-12 12:03:18 +08:00
suyl
cbf8818d0d aa 2021-07-12 11:26:48 +08:00
suyl
d2b5df3393 aa 2021-07-12 09:18:24 +08:00
suyl
d8a2b91ebf aa 2021-07-09 18:03:37 +08:00
suyl
a19207eccc aa 2021-07-09 15:46:40 +08:00
suyl
c390178dc6 aa 2021-07-09 10:17:46 +08:00
suyl
e3c7ae9181 aa 2021-07-08 17:57:26 +08:00
suyl
896273f2c2 aa 2021-07-08 17:41:23 +08:00
suyl
2b143c0fd2 aa 2021-07-08 17:34:02 +08:00
suyl
6193d17b8b aa 2021-07-08 17:23:24 +08:00
suyl
9b2f3bb49a aa 2021-07-08 11:25:43 +08:00
suyl
2026d07e47 aa 2021-07-07 17:54:11 +08:00
suyl
106caf9666 aa 2021-07-07 17:39:43 +08:00
suyl
d49b104930 aa 2021-07-07 11:58:59 +08:00
suyl
5b5a132fe6 美团订单售前取消 2021-07-07 10:42:51 +08:00
suyl
43638334fc 补订单 2021-07-07 09:34:45 +08:00
suyl
88c1aa2e5e aa 2021-07-07 09:29:41 +08:00
suyl
46287cbb46 aa 2021-07-06 18:44:39 +08:00
suyl
048a84a026 aa 2021-07-06 14:55:34 +08:00
suyl
0bf5783a78 aa 2021-07-06 13:35:15 +08:00
suyl
dcc1b509fa aa 2021-07-06 10:16:55 +08:00
suyl
cc07e1c177 aa 2021-07-06 09:28:16 +08:00
suyl
f8727f9604 aa 2021-07-06 09:22:20 +08:00
suyl
fbf4932724 aa 2021-07-06 09:22:07 +08:00
suyl
a593d57a33 aa 2021-07-05 18:48:41 +08:00
suyl
273072882c aa 2021-07-05 16:53:08 +08:00
suyl
d16d91bf00 aa 2021-07-05 16:45:24 +08:00
suyl
41f919deca a 2021-07-05 11:57:24 +08:00
suyl
4e29eea5ed aa 2021-07-02 13:33:47 +08:00
suyl
e2edd85b73 aa 2021-07-02 10:33:37 +08:00
suyl
48d7bfc8d5 aa 2021-07-02 09:40:42 +08:00
suyl
f6c3442ef5 aa 2021-07-01 15:17:49 +08:00
suyl
b60f2e2d78 aa 2021-07-01 15:12:19 +08:00
suyl
d2b841b688 aa 2021-07-01 15:01:57 +08:00
suyl
06615102ca aa 2021-06-30 14:20:46 +08:00
suyl
96428eecc3 aa 2021-06-30 14:19:07 +08:00
suyl
323fe01518 aa 2021-06-30 14:07:56 +08:00
suyl
6fae4fd46b aa 2021-06-30 13:56:32 +08:00
suyl
184c35bc9b aa 2021-06-30 09:59:11 +08:00
suyl
32eb8cefe7 aa 2021-06-30 09:54:24 +08:00
suyl
b79603caf5 aa' 2021-06-30 09:52:44 +08:00
suyl
ebabe353c8 aa 2021-06-30 09:13:32 +08:00
suyl
e60f57b9db aa 2021-06-29 18:57:29 +08:00
suyl
7ff36b93b7 aa 2021-06-29 18:35:47 +08:00
suyl
9dbaaeda71 aa 2021-06-29 18:32:34 +08:00
suyl
2f23954966 aa 2021-06-29 18:28:55 +08:00
suyl
58f646d930 aa 2021-06-29 18:20:30 +08:00
suyl
c5ba6bf317 aa 2021-06-29 18:12:54 +08:00
suyl
4e2a0919e6 aa 2021-06-29 18:06:35 +08:00
suyl
36ec8f5036 aa 2021-06-29 17:54:32 +08:00
suyl
725d842374 aa 2021-06-29 17:22:01 +08:00
suyl
3d082a954a aa 2021-06-29 17:00:23 +08:00
suyl
4cedb56314 aa 2021-06-29 16:56:37 +08:00
suyl
3a7d377b91 aa 2021-06-29 16:25:37 +08:00
suyl
fd0ab123fc ueditor 2021-06-29 16:22:22 +08:00
suyl
f4bb820b56 aa 2021-06-29 15:48:02 +08:00
suyl
40d9677a26 aa 2021-06-29 15:46:14 +08:00
suyl
c51fc666b1 aa 2021-06-29 15:14:15 +08:00
suyl
87cf0e98d7 aa 2021-06-29 15:11:33 +08:00
suyl
1859e57aa3 aa 2021-06-29 15:09:48 +08:00
suyl
29e11663fc aa 2021-06-29 15:07:06 +08:00
suyl
3f4b979c67 aa 2021-06-29 14:57:05 +08:00
suyl
1276210d49 aa 2021-06-29 14:44:46 +08:00
suyl
3a2dfb1534 aa 2021-06-29 14:38:02 +08:00
suyl
c2749ba5d1 上传图片到微信post 2021-06-29 14:11:06 +08:00
suyl
dcb5377bd5 aa 2021-06-25 15:04:14 +08:00
suyl
06671a4e9b aa 2021-06-25 10:51:40 +08:00
suyl
c06e60cc8e aa 2021-06-25 10:23:45 +08:00
suyl
a52fcd06b6 aa 2021-06-25 09:55:53 +08:00
suyl
51e9cb4304 aa 2021-06-25 09:55:12 +08:00
suyl
469b99849c aa 2021-06-25 09:54:27 +08:00
suyl
6323b4fdc8 aa 2021-06-24 15:55:39 +08:00
suyl
45f0cddfd6 aa 2021-06-24 15:45:50 +08:00
suyl
63b5e1aa54 Merge branch 'master' of e.coding.net:rosydev/jx-callback 2021-06-24 14:27:45 +08:00
suyl
3c2978bcf5 Merge remote-tracking branch 'origin/jdshop' 2021-06-24 14:27:33 +08:00
suyl
0252ab0f53 aa 2021-06-24 14:22:40 +08:00
suyl
38a162969b aa 2021-06-24 14:20:00 +08:00
suyl
998ebe942f aa 2021-06-24 13:50:38 +08:00
suyl
56fb0eb6ad aa 2021-06-24 10:54:22 +08:00
suyl
1062523b2f aa 2021-06-24 10:49:43 +08:00
suyl
ecf155e9a9 aa 2021-06-23 17:39:23 +08:00
suyl
f7a9f3c033 aa 2021-06-23 17:16:11 +08:00
suyl
f8a8907e75 aa 2021-06-23 17:15:19 +08:00
suyl
2586ba37db aa 2021-06-23 17:04:42 +08:00
suyl
a2025bf705 aa 2021-06-23 17:02:03 +08:00
suyl
256dc9a102 aa 2021-06-23 16:59:15 +08:00
suyl
05604201cb aa 2021-06-23 16:51:15 +08:00
suyl
794a31a74e aa 2021-06-23 16:32:33 +08:00
suyl
2df3e2d6c8 aa 2021-06-23 16:31:11 +08:00
suyl
4f07f32834 aa 2021-06-23 16:27:54 +08:00
suyl
cf23588f4c aa 2021-06-23 16:23:46 +08:00
suyl
4282e226b0 aa 2021-06-23 15:37:21 +08:00
suyl
b2e420c0cc aa 2021-06-23 14:33:12 +08:00
suyl
1dbe19eb4f aa 2021-06-23 13:48:43 +08:00
suyl
396a02a40d aa 2021-06-23 11:56:04 +08:00
suyl
22c0a41f35 aa 2021-06-23 10:38:28 +08:00
suyl
f5ce85306c aa 2021-06-23 10:10:41 +08:00
suyl
2daf502fa4 aa 2021-06-23 10:09:50 +08:00
suyl
e2140a3723 aa 2021-06-23 10:07:32 +08:00
suyl
e684b3c15c aa 2021-06-23 10:05:42 +08:00
suyl
2a349c0f44 aa 2021-06-23 10:01:34 +08:00
suyl
b7b34bd6d2 aa 2021-06-23 09:59:07 +08:00
suyl
0999414560 aa 2021-06-23 09:41:35 +08:00
suyl
050da7cc59 aa 2021-06-22 17:48:53 +08:00
suyl
4df658305a aa 2021-06-22 17:24:18 +08:00
suyl
5f230d68d9 aa 2021-06-22 17:20:20 +08:00
suyl
7a0bc2ec02 aa 2021-06-22 16:55:01 +08:00
suyl
b2bd2facfe aa 2021-06-22 16:48:15 +08:00
suyl
f89fd3adab aa 2021-06-22 16:40:13 +08:00
suyl
565e3b66d6 aa 2021-06-22 16:22:36 +08:00
suyl
676c7bc46d aa 2021-06-22 15:29:53 +08:00
suyl
fb445b6e2f aa 2021-06-22 15:26:59 +08:00
suyl
ee1e9f6427 aa 2021-06-22 15:14:09 +08:00
suyl
38dbdcbdc9 aa 2021-06-22 14:59:34 +08:00
suyl
ea0426a88e aa 2021-06-22 14:42:24 +08:00
suyl
9e75e768e3 aa 2021-06-22 14:41:31 +08:00
suyl
27336a8da6 aa 2021-06-22 14:18:45 +08:00
suyl
e0c802ad71 aa 2021-06-22 14:15:47 +08:00
suyl
cb76826acb aa 2021-06-22 14:12:13 +08:00
suyl
71d5990552 aa 2021-06-22 14:04:32 +08:00
suyl
391a5a74a3 aa 2021-06-22 14:01:31 +08:00
suyl
f06350751e aa 2021-06-22 13:53:40 +08:00
suyl
dc257c74c1 aa 2021-06-22 13:47:51 +08:00
suyl
184339d848 aa 2021-06-22 11:46:04 +08:00
suyl
c187a3418d aa 2021-06-22 11:26:13 +08:00
suyl
ee33b5d3a7 aa 2021-06-22 11:15:17 +08:00
suyl
c634c56114 aa 2021-06-22 10:38:59 +08:00
suyl
4e90f3e6d8 aa 2021-06-22 10:12:14 +08:00
suyl
9cd5285598 aa 2021-06-22 10:11:42 +08:00
suyl
db08ac99a5 aa 2021-06-22 10:02:58 +08:00
suyl
5aaf6c1d2c aa 2021-06-22 09:46:49 +08:00
suyl
e885b0e055 aa 2021-06-22 09:33:47 +08:00
suyl
bfd2f930c1 aa 2021-06-21 18:35:36 +08:00
suyl
5aeeb4a941 aa 2021-06-21 18:32:14 +08:00
suyl
7feb689ed3 aa 2021-06-21 17:58:03 +08:00
suyl
eea15cf7c0 aa 2021-06-21 17:40:08 +08:00
suyl
734b37c609 aa 2021-06-21 17:36:47 +08:00
suyl
6f72310df7 aa 2021-06-21 17:10:33 +08:00
suyl
5396545a7c aa 2021-06-21 17:04:30 +08:00
suyl
6d1023deea aa 2021-06-21 16:20:23 +08:00
suyl
be99f08a31 aa 2021-06-21 16:17:45 +08:00
suyl
48fe15173a aa 2021-06-21 15:59:19 +08:00
suyl
e86f6e3d4c aa 2021-06-21 15:56:34 +08:00
suyl
c827df8e74 aa 2021-06-21 15:49:23 +08:00
suyl
1926e3b312 aa 2021-06-21 15:43:22 +08:00
suyl
80a967622c aa 2021-06-21 15:28:24 +08:00
suyl
e63c7a0286 aa 2021-06-21 14:57:48 +08:00
suyl
559c5b7f2b aa 2021-06-21 13:31:47 +08:00
suyl
4910288c94 aa 2021-06-21 11:45:10 +08:00
suyl
00ee8aba4f aa 2021-06-21 11:28:08 +08:00
suyl
cee47b50b9 aa 2021-06-21 11:07:19 +08:00
suyl
6c5310f64c aa 2021-06-21 10:56:10 +08:00
suyl
aac18a21dc aa 2021-06-21 10:46:32 +08:00
suyl
ad88235942 aa 2021-06-21 10:40:16 +08:00
suyl
f388a43480 aa 2021-06-21 10:18:45 +08:00
suyl
faa10e4069 aa 2021-06-21 09:55:36 +08:00
suyl
90a3720de6 aa 2021-06-21 09:47:31 +08:00
suyl
0de165f792 aa 2021-06-21 09:08:13 +08:00
suyl
cf0a41ff80 aa 2021-06-18 18:47:41 +08:00
suyl
1b32594473 aa 2021-06-18 18:25:58 +08:00
suyl
9f78cd10c2 aa 2021-06-18 18:03:34 +08:00
suyl
02f0e297e2 aa 2021-06-18 17:41:44 +08:00
suyl
cff1aed9e9 aa 2021-06-18 16:52:34 +08:00
suyl
5b1e37ffd7 aa 2021-06-18 16:11:39 +08:00
suyl
b1f65097d4 aa 2021-06-18 14:19:57 +08:00
suyl
cd4eac9e34 aa 2021-06-18 14:04:03 +08:00
suyl
863c062a33 aa 2021-06-18 14:01:09 +08:00
suyl
abac0b5576 aa 2021-06-18 13:51:05 +08:00
suyl
8424c617da aa 2021-06-18 11:31:39 +08:00
suyl
03ef52656c aa 2021-06-18 10:53:33 +08:00
suyl
921255c4e8 aa 2021-06-18 10:48:54 +08:00
suyl
eefcf2ec1e aa 2021-06-18 10:29:15 +08:00
suyl
5a311c8e34 aa 2021-06-18 10:26:20 +08:00
suyl
4579a52315 aa 2021-06-18 10:08:49 +08:00
suyl
a63ac58140 aa 2021-06-18 09:16:33 +08:00
suyl
e9e8859406 aa 2021-06-18 09:04:13 +08:00
suyl
4a779a08a5 aa 2021-06-17 19:00:54 +08:00
suyl
921a3bfffd aa 2021-06-17 18:34:13 +08:00
suyl
177156dcba aa 2021-06-17 18:09:06 +08:00
suyl
8229f06272 aa 2021-06-17 17:40:18 +08:00
suyl
761bd82a96 aa 2021-06-17 17:29:31 +08:00
suyl
ab855c884f aa 2021-06-17 15:12:46 +08:00
suyl
521039d001 aa 2021-06-17 13:52:34 +08:00
suyl
3fa4dd77ef aa 2021-06-17 11:19:30 +08:00
suyl
f47d85d691 aa 2021-06-17 11:18:40 +08:00
suyl
30d521fc44 aa 2021-06-17 11:17:20 +08:00
suyl
4022b8eab9 aa 2021-06-17 10:38:37 +08:00
suyl
df0f832242 aa 2021-06-17 10:14:21 +08:00
suyl
c037b8df1f aa 2021-06-17 10:10:34 +08:00
suyl
ee614ea369 aa 2021-06-17 10:06:21 +08:00
suyl
63f8ec3cea aa 2021-06-17 09:10:59 +08:00
suyl
36c3b3cc78 aa 2021-06-16 17:28:31 +08:00
suyl
cfeb6f8858 aa 2021-06-16 14:47:18 +08:00
suyl
4a907607e3 aa 2021-06-15 14:11:28 +08:00
suyl
31e615d3de aa 2021-06-15 11:51:56 +08:00
suyl
9d5e9b10c0 aa 2021-06-15 10:38:29 +08:00
suyl
c57d3dabb5 aa 2021-06-15 10:15:47 +08:00
suyl
b4688c4a77 aa 2021-06-11 09:58:48 +08:00
suyl
2c25f3bfa3 aa 2021-06-10 18:41:58 +08:00
suyl
8f6f185a30 aa 2021-06-10 18:02:04 +08:00
suyl
02f40a608a aa 2021-06-10 17:46:33 +08:00
suyl
09c3db51c9 aa 2021-06-10 17:26:22 +08:00
suyl
897ecdcca0 aa 2021-06-10 17:12:54 +08:00
suyl
afff3d43c1 aa 2021-06-10 15:26:41 +08:00
suyl
4f2ef2ddda aa 2021-06-10 15:17:14 +08:00
suyl
e32b46635a aa 2021-06-10 14:37:08 +08:00
suyl
7a6622724f aa 2021-06-10 11:01:01 +08:00
suyl
a3078bc1b6 aa 2021-06-10 10:58:42 +08:00
suyl
992642d3f8 aa 2021-06-10 10:48:38 +08:00
suyl
01084ffc8a aa 2021-06-10 10:32:35 +08:00
suyl
f39e49d72f aa 2021-06-10 10:11:57 +08:00
suyl
c63e52d3ec aa 2021-06-10 09:46:44 +08:00
suyl
75693625c0 aa 2021-06-10 09:45:03 +08:00
suyl
5cec9e6f3c aa 2021-06-09 18:53:57 +08:00
suyl
edd20e84ce aa 2021-06-09 18:50:22 +08:00
suyl
a04caf8675 aa 2021-06-09 18:45:48 +08:00
suyl
624182a528 aa 2021-06-09 18:34:24 +08:00
suyl
1bce2e36af aa 2021-06-09 18:17:09 +08:00
suyl
fd783476e4 aa 2021-06-09 18:10:27 +08:00
suyl
76b04adb5d aa 2021-06-09 17:19:24 +08:00
suyl
a76b6c5aaa aa 2021-06-09 16:50:02 +08:00
suyl
b2d5395c29 aa 2021-06-09 16:44:22 +08:00
suyl
79af943453 aa 2021-06-09 16:37:56 +08:00
suyl
41549bb2d4 aa 2021-06-09 16:29:40 +08:00
suyl
c099780258 aa 2021-06-09 16:25:02 +08:00
suyl
1b2b61b5d8 aa 2021-06-09 16:10:26 +08:00
suyl
db38799bf8 aa 2021-06-09 15:14:04 +08:00
suyl
959563bc88 aa 2021-06-09 14:08:21 +08:00
suyl
f9bb1d3dd2 aa 2021-06-09 13:44:30 +08:00
suyl
ba9db17339 aa 2021-06-09 11:32:39 +08:00
suyl
2cefd8e2cd aa 2021-06-09 10:43:31 +08:00
suyl
d77bffabf2 aa 2021-06-09 10:06:28 +08:00
suyl
bfa22217fa aa 2021-06-09 09:56:18 +08:00
suyl
5f1f3f744d aa 2021-06-09 09:53:47 +08:00
suyl
545c625a5b aa 2021-06-09 09:46:16 +08:00
suyl
1ef746957f aa 2021-06-08 17:08:45 +08:00
suyl
8c766e815f aa 2021-06-08 15:37:38 +08:00
suyl
73d51012d6 aa 2021-06-08 14:01:33 +08:00
suyl
74c0565ad0 aa 2021-06-08 13:57:47 +08:00
suyl
e1a26c1444 aa 2021-06-08 11:19:31 +08:00
suyl
cf8a9d9504 aa 2021-06-08 10:47:43 +08:00
suyl
7065dc8cd4 aa 2021-06-08 10:30:09 +08:00
suyl
2299750c68 test 2021-06-08 09:59:31 +08:00
suyl
9e21b8ed11 aa 2021-06-07 18:19:52 +08:00
suyl
89a036f0ac aa 2021-06-07 18:08:32 +08:00
suyl
4279c55757 aa 2021-06-07 17:58:09 +08:00
suyl
dcaa6ee324 aa 2021-06-07 17:49:36 +08:00
suyl
68783034bc aa 2021-06-07 16:25:27 +08:00
suyl
1844055a4a aa 2021-06-07 15:43:38 +08:00
suyl
c4eecaffef aa 2021-06-07 14:31:43 +08:00
suyl
a5e63f1484 aa 2021-06-07 13:40:44 +08:00
suyl
9f37c3de71 aa 2021-06-07 11:33:26 +08:00
suyl
bd72b79987 aa 2021-06-07 09:17:12 +08:00
suyl
d70bba284f aa 2021-06-04 18:25:36 +08:00
suyl
bc5b5caa99 aa 2021-06-04 17:27:07 +08:00
suyl
a912bbf2d2 aa 2021-06-04 17:24:32 +08:00
suyl
b53c570a39 aa 2021-06-04 17:15:45 +08:00
suyl
a103a0cf56 aa 2021-06-04 17:12:43 +08:00
suyl
bd428e9a0d aa 2021-06-04 17:07:25 +08:00
suyl
6e6a16b7f1 aa 2021-06-04 17:05:15 +08:00
suyl
a2226b77bd aa 2021-06-04 17:03:35 +08:00
suyl
875fffeed3 aa 2021-06-04 16:29:55 +08:00
suyl
2688ec472c aa 2021-06-04 16:04:26 +08:00
suyl
4fa827cbc3 aa 2021-06-04 15:21:31 +08:00
suyl
bc3071788f aa 2021-06-04 15:18:41 +08:00
suyl
17c4e54468 aa 2021-06-04 15:14:59 +08:00
suyl
58b4ea5856 aa 2021-06-04 14:59:37 +08:00
suyl
8c222ece76 aa 2021-06-04 14:50:42 +08:00
suyl
2ac889968e aa 2021-06-04 11:18:46 +08:00
suyl
508d81474c aa 2021-06-04 11:10:39 +08:00
suyl
621a40e399 aa 2021-06-04 10:14:15 +08:00
suyl
0043d5abc6 aa 2021-06-03 18:19:18 +08:00
suyl
320aae9b18 aa 2021-06-03 18:15:00 +08:00
suyl
6a0e97f555 aa 2021-06-03 18:11:27 +08:00
suyl
e0bba30696 aa 2021-06-03 17:24:36 +08:00
suyl
f4aab63317 aa 2021-06-03 16:37:31 +08:00
suyl
384aa5500a aa 2021-06-03 16:19:54 +08:00
suyl
588f71abcf aa 2021-06-03 15:29:36 +08:00
suyl
48be414a4f aa 2021-06-03 15:29:18 +08:00
suyl
d57530a6b1 aa 2021-06-03 15:02:42 +08:00
suyl
71da34111c aa 2021-06-03 14:34:23 +08:00
suyl
86eb5f6951 aa 2021-06-03 09:44:30 +08:00
suyl
3c39e645ff aa 2021-06-03 09:13:00 +08:00
suyl
34e3dc9910 aa 2021-06-02 18:28:38 +08:00
suyl
c219481ad5 aa 2021-06-02 16:28:41 +08:00
suyl
f36caf0a90 aa 2021-06-02 11:46:53 +08:00
suyl
866fe39ce5 aa 2021-06-02 11:18:35 +08:00
suyl
15c4ea1456 aa 2021-06-02 09:35:52 +08:00
suyl
7fc33acb6d aa 2021-06-01 17:26:18 +08:00
suyl
4df9db3ece aa 2021-06-01 16:07:59 +08:00
suyl
3297672751 aa 2021-06-01 16:05:24 +08:00
suyl
588a2a1f72 aa 2021-06-01 15:54:26 +08:00
suyl
92d8144065 aa 2021-06-01 13:40:44 +08:00
suyl
23d8bed1f4 aa 2021-06-01 11:44:35 +08:00
suyl
279b6028cd aa 2021-06-01 11:34:33 +08:00
suyl
94cde0f6d7 aa 2021-06-01 10:52:01 +08:00
suyl
b8f0d43a0d aa 2021-06-01 10:28:31 +08:00
suyl
e9495a4db2 aa 2021-05-31 15:56:12 +08:00
suyl
567687efad aa 2021-05-31 15:32:53 +08:00
suyl
3212452527 aa 2021-05-31 15:15:11 +08:00
suyl
8281dc3136 aa 2021-05-31 15:09:13 +08:00
suyl
213136e6fa aa 2021-05-31 15:08:25 +08:00
suyl
5acc738576 aa 2021-05-31 14:56:38 +08:00
suyl
fbcc7ac754 aa 2021-05-31 14:27:26 +08:00
suyl
ce5eb03e12 aa 2021-05-31 11:30:20 +08:00
suyl
607194d611 aa 2021-05-31 11:07:15 +08:00
suyl
6ab8265cfa aa 2021-05-31 10:50:29 +08:00
suyl
2e567bb2f4 aa 2021-05-31 10:32:42 +08:00
suyl
77a0571a60 aa 2021-05-31 10:17:42 +08:00
suyl
28040a7b8b aa 2021-05-31 09:57:12 +08:00
suyl
16aab5156c aa 2021-05-27 14:47:08 +08:00
suyl
3edfaadbfa aa 2021-05-27 14:31:43 +08:00
suyl
4dd8e861fe aa 2021-05-27 14:09:16 +08:00
suyl
f1c26fceec aa 2021-05-27 14:01:23 +08:00
suyl
c0929371cc aa 2021-05-27 13:56:43 +08:00
suyl
9eac3c98b4 aa 2021-05-27 11:52:06 +08:00
suyl
35d5a5cdc6 aa 2021-05-27 11:48:21 +08:00
suyl
8e57df5a8d aa 2021-05-27 11:41:50 +08:00
suyl
0dd82e1d7b aa 2021-05-27 10:55:14 +08:00
suyl
3dc1c1b4c9 aa 2021-05-26 16:41:35 +08:00
suyl
a3bf097bc0 aa 2021-05-26 16:31:08 +08:00
suyl
badb8ae4ca aa 2021-05-26 15:17:43 +08:00
suyl
f7e824b88c aa 2021-05-26 15:13:04 +08:00
suyl
99d0f32a7e aa 2021-05-26 14:56:31 +08:00
suyl
166a8ca76b aa 2021-05-26 10:44:10 +08:00
suyl
304fac1dd8 aa 2021-05-26 10:37:48 +08:00
suyl
21990fa123 aa 2021-05-26 10:28:59 +08:00
suyl
469209fe82 aa 2021-05-26 10:12:39 +08:00
suyl
a8f0a39204 a 2021-05-26 10:07:15 +08:00
suyl
e477f80a24 aa 2021-05-26 09:58:37 +08:00
suyl
d8ccfa3234 aa 2021-05-26 09:56:01 +08:00
suyl
77f43f2aee aa 2021-05-26 09:41:05 +08:00
suyl
ed7a761ab6 aa 2021-05-26 09:16:12 +08:00
suyl
d43f03fdda aa 2021-05-26 09:02:08 +08:00
suyl
b2efe6b5b3 aa 2021-05-25 18:28:43 +08:00
suyl
6d366d902f aa 2021-05-25 18:20:50 +08:00
suyl
4e6f7ae859 aa 2021-05-25 18:03:49 +08:00
suyl
e4e959a9f8 aa 2021-05-25 17:57:31 +08:00
suyl
d0752a0912 aa 2021-05-25 17:56:39 +08:00
suyl
e4cc9d3dd9 a 2021-05-25 16:14:51 +08:00
suyl
ad4deafa87 a 2021-05-25 16:02:00 +08:00
suyl
bb525bf3e3 aa 2021-05-25 15:53:24 +08:00
suyl
5dee48bde2 qywx 2021-05-25 14:48:41 +08:00
suyl
ab678fcd7b aa 2021-05-24 17:09:32 +08:00
suyl
3ed482c6f9 aa 2021-05-24 17:08:55 +08:00
suyl
eb5231d847 aa 2021-05-24 15:48:00 +08:00
suyl
c1908dcd8f aa 2021-05-24 15:25:08 +08:00
suyl
d986be7cad aa 2021-05-24 14:42:20 +08:00
suyl
5fe8987522 aa 2021-05-24 14:05:01 +08:00
suyl
f5673437d3 aa 2021-05-22 18:05:29 +08:00
suyl
7ed59caeb1 aa 2021-05-22 18:01:27 +08:00
suyl
1485e5431e aa 2021-05-22 17:56:06 +08:00
suyl
7f365354c3 aa 2021-05-22 17:46:00 +08:00
suyl
b889e5a50e aa 2021-05-22 16:39:16 +08:00
suyl
ff62d8c472 aa 2021-05-22 15:59:52 +08:00
suyl
2d45a39017 aa 2021-05-22 15:48:23 +08:00
suyl
194d79293d aa 2021-05-22 15:41:48 +08:00
suyl
a83d409423 aa 2021-05-22 15:39:10 +08:00
suyl
fc7c2238b1 aa 2021-05-22 14:03:21 +08:00
suyl
1fefc30644 aa 2021-05-22 11:50:20 +08:00
suyl
0084921d13 aa 2021-05-22 11:45:59 +08:00
suyl
1026f491f4 aa 2021-05-22 11:17:45 +08:00
suyl
6540dfde8a aa 2021-05-22 11:16:32 +08:00
suyl
f3bdc35c35 aa 2021-05-22 10:52:36 +08:00
suyl
4223d912d1 aa 2021-05-22 09:12:13 +08:00
suyl
eaddb49b26 aa 2021-05-21 18:13:08 +08:00
suyl
654ae03bf1 aa 2021-05-21 18:08:50 +08:00
suyl
0b5701f2bb aa 2021-05-21 17:58:20 +08:00
suyl
51e559a032 a 2021-05-21 16:41:53 +08:00
suyl
cb6a2ef8f7 aa 2021-05-21 15:47:39 +08:00
suyl
0fd76bd77f aa 2021-05-21 14:59:10 +08:00
suyl
3517ab6a33 aa 2021-05-21 10:38:13 +08:00
suyl
3271c472b3 aa 2021-05-21 10:09:50 +08:00
suyl
fa09361fe5 aa 2021-05-21 10:09:25 +08:00
suyl
0bfc44beb5 aa 2021-05-21 10:04:52 +08:00
suyl
83183f29bc aa 2021-05-21 09:59:07 +08:00
suyl
e04494342f aa 2021-05-21 09:43:32 +08:00
suyl
d639dd788d aa 2021-05-21 08:40:22 +08:00
suyl
20e61f1317 aa 2021-05-20 18:10:46 +08:00
suyl
80e08d5b18 aa 2021-05-20 16:48:40 +08:00
suyl
037e6af401 aa 2021-05-20 16:44:53 +08:00
suyl
68b74c6d68 aa 2021-05-20 16:39:44 +08:00
suyl
6455a5fc26 aa 2021-05-20 16:37:36 +08:00
suyl
b20449bdfd aa 2021-05-20 15:48:51 +08:00
suyl
01b0b01187 aa 2021-05-20 15:46:18 +08:00
suyl
b08499204e aa 2021-05-20 13:50:22 +08:00
suyl
87ace3d7c4 aa 2021-05-20 13:47:18 +08:00
suyl
7884e7ba1a aa 2021-05-20 13:40:11 +08:00
suyl
2d0b50568e aa 2021-05-20 11:56:19 +08:00
suyl
c056a682d7 aa 2021-05-20 11:49:27 +08:00
suyl
24c264e39e aa 2021-05-20 11:42:54 +08:00
suyl
ce6edfe6a5 aa 2021-05-20 11:33:13 +08:00
suyl
48a2fb0a8f aa 2021-05-20 11:32:59 +08:00
suyl
32906db9e1 aa 2021-05-20 11:16:11 +08:00
suyl
cfbf7baf3b aa 2021-05-20 10:12:00 +08:00
suyl
1f89f20354 aa 2021-05-20 10:07:56 +08:00
suyl
4b2af44a88 aa 2021-05-20 10:04:42 +08:00
suyl
145195d338 aa 2021-05-20 09:33:49 +08:00
suyl
1cad95ef99 aa 2021-05-20 09:24:28 +08:00
suyl
3ee892c2d0 aa 2021-05-20 09:07:22 +08:00
suyl
c2c5242b9b aa 2021-05-19 17:38:26 +08:00
suyl
ac60f2d8ed aa 2021-05-19 17:23:16 +08:00
suyl
e313c61083 aa 2021-05-19 17:11:35 +08:00
suyl
f98b1dfa2e aa 2021-05-19 17:05:17 +08:00
suyl
07ebec3775 aa 2021-05-19 16:56:46 +08:00
suyl
5d44dfc76c aa 2021-05-19 16:25:01 +08:00
suyl
16956c2f08 aa 2021-05-19 16:21:20 +08:00
suyl
0bd761d7d7 aa 2021-05-19 16:18:31 +08:00
suyl
be9c90488f aa 2021-05-19 16:17:09 +08:00
suyl
ad74e90c19 aa 2021-05-19 15:27:27 +08:00
suyl
20004df8c5 aa 2021-05-19 14:23:19 +08:00
suyl
6d52187f19 短信模板 2021-05-19 13:42:41 +08:00
suyl
83bd9726ac aa 2021-05-19 10:25:12 +08:00
suyl
ec0df984ab aa 2021-05-19 08:51:37 +08:00
suyl
86513089ae aa 2021-05-17 15:50:48 +08:00
suyl
d74eed1b69 aa 2021-05-17 14:27:06 +08:00
suyl
1b4a20183c aa 2021-05-17 11:45:21 +08:00
suyl
506bf03829 aa 2021-05-17 11:03:06 +08:00
suyl
a5b28a9f66 aa 2021-05-17 09:33:56 +08:00
suyl
0bc820b3e5 aa 2021-05-14 17:45:47 +08:00
suyl
842d27f8b6 aa 2021-05-14 15:23:21 +08:00
suyl
4741634fdf aa 2021-05-14 13:55:33 +08:00
suyl
b00a60966b aa 2021-05-14 11:31:47 +08:00
suyl
a353963998 aa 2021-05-14 11:17:42 +08:00
suyl
6e485be7bb aa 2021-05-14 10:17:24 +08:00
suyl
4a495cb077 aa 2021-05-14 10:04:49 +08:00
suyl
87f5b06c52 aa 2021-05-14 09:50:42 +08:00
suyl
050c66a28b aa 2021-05-14 09:00:05 +08:00
suyl
80e6fc77c9 aa 2021-05-13 18:24:05 +08:00
suyl
861bffc87b aa 2021-05-13 18:14:30 +08:00
suyl
38d0f56e8e aa 2021-05-13 18:06:50 +08:00
suyl
7ddad6a679 aa 2021-05-13 18:02:58 +08:00
suyl
b4941c1548 aa 2021-05-13 17:50:34 +08:00
suyl
0ddd3e15d0 aa 2021-05-13 17:45:39 +08:00
suyl
721957baf1 aa 2021-05-13 17:40:17 +08:00
suyl
f9bdd78e2d aa 2021-05-13 17:34:24 +08:00
suyl
0390cfbf67 aa 2021-05-13 16:37:51 +08:00
suyl
8d3dfe406a aa 2021-05-13 16:20:38 +08:00
suyl
56515fc628 aa 2021-05-13 15:53:24 +08:00
suyl
1652f1c5b1 aa 2021-05-13 14:58:53 +08:00
suyl
b43d885436 aa 2021-05-13 13:47:38 +08:00
suyl
9293116e03 aa 2021-05-13 11:49:30 +08:00
suyl
a1801d13ac aa 2021-05-13 10:06:45 +08:00
suyl
44c7ac3fd2 aa 2021-05-13 09:14:48 +08:00
suyl
5b928a3e39 aa 2021-05-12 17:59:35 +08:00
suyl
287b9c6f8e aa 2021-05-12 17:44:13 +08:00
suyl
02c4ad310e aa 2021-05-12 17:22:44 +08:00
suyl
baffc6750f aa 2021-05-12 17:19:48 +08:00
suyl
54e2a0ffb9 aa 2021-05-12 16:31:53 +08:00
suyl
49fa0f3514 aa 2021-05-12 16:21:08 +08:00
suyl
89ff19ae6d aa 2021-05-12 16:18:11 +08:00
suyl
b00882859f aa 2021-05-12 16:05:16 +08:00
suyl
0bc913e993 aa 2021-05-12 11:56:05 +08:00
suyl
066d9c16e3 aa 2021-05-12 11:39:23 +08:00
suyl
85a5cb0226 aa 2021-05-12 11:36:31 +08:00
suyl
3d97d7d13d aa 2021-05-12 11:23:42 +08:00
suyl
9e550a16aa aa 2021-05-12 11:08:32 +08:00
suyl
76715b1dc6 aa 2021-05-12 10:57:59 +08:00
suyl
b8909a6bde aa 2021-05-12 09:00:04 +08:00
suyl
fa8f4f2f01 aa 2021-05-11 17:31:21 +08:00
suyl
6c16ce1652 aa 2021-05-11 17:25:31 +08:00
suyl
9ba84219b8 aa 2021-05-11 17:18:54 +08:00
suyl
46b20853ef aa 2021-05-11 17:15:42 +08:00
suyl
5bc0a91365 aa 2021-05-11 17:04:56 +08:00
suyl
46cdde3d39 aa 2021-05-11 16:55:26 +08:00
suyl
33dab7754a aa 2021-05-11 16:52:14 +08:00
suyl
f4cde621a2 aa 2021-05-11 16:45:39 +08:00
suyl
1f8bf71865 aa 2021-05-11 16:39:14 +08:00
suyl
f581387aa8 aa 2021-05-11 16:36:27 +08:00
suyl
deece80b16 aa 2021-05-11 16:26:59 +08:00
suyl
20718f4e9d aa 2021-05-11 16:14:07 +08:00
suyl
93d66d1c6a aa 2021-05-11 16:04:24 +08:00
suyl
c84d75e161 aa 2021-05-11 15:41:24 +08:00
suyl
1e0f5a0513 aa 2021-05-11 15:25:48 +08:00
suyl
851c581e2d aa 2021-05-11 15:17:26 +08:00
suyl
7654e279a3 aa 2021-05-11 15:10:52 +08:00
suyl
687e88d49b aa 2021-05-11 14:06:16 +08:00
suyl
26940b3a12 aa 2021-05-11 11:08:06 +08:00
suyl
1dd5b592a8 aa 2021-05-10 17:42:17 +08:00
suyl
a2a50f639b aa 2021-05-07 18:31:33 +08:00
suyl
5b6eec3d96 aa 2021-05-07 17:38:28 +08:00
suyl
9ba7cdb7db aa 2021-05-07 17:12:31 +08:00
suyl
78fd668f8b aa 2021-05-06 18:06:23 +08:00
suyl
52630908f6 aa 2021-05-06 17:27:37 +08:00
suyl
820abd00d6 aa 2021-05-06 16:17:44 +08:00
suyl
85d96a9e91 aa 2021-05-06 14:43:29 +08:00
suyl
abf6587cc0 aa 2021-05-06 13:36:53 +08:00
suyl
e8901253dd aa 2021-05-06 11:51:20 +08:00
suyl
ff5a74c39e aa 2021-05-06 11:28:01 +08:00
suyl
ba2914c1d9 aa 2021-05-06 11:17:57 +08:00
suyl
0a395ad67a aa 2021-05-06 11:07:01 +08:00
suyl
cb563936c0 aa 2021-05-06 09:28:50 +08:00
suyl
6c11ac29be aa 2021-04-29 18:17:58 +08:00
suyl
dbada6a3a8 aa 2021-04-29 17:36:52 +08:00
suyl
88d600d00c aa 2021-04-28 17:45:26 +08:00
suyl
64a251da34 aa 2021-04-28 17:27:43 +08:00
suyl
199ac9e495 aa 2021-04-28 17:22:40 +08:00
suyl
d9f3294893 aa 2021-04-28 17:08:02 +08:00
suyl
63e293ac74 aa 2021-04-28 15:04:23 +08:00
suyl
18987c669e aa 2021-04-28 14:48:50 +08:00
suyl
3ab5515ee6 aa 2021-04-28 14:43:21 +08:00
suyl
d4dec7cf2a aa 2021-04-28 14:33:11 +08:00
suyl
db4ee9ae55 aa 2021-04-27 09:44:17 +08:00
suyl
5590c170b9 aa 2021-04-27 09:30:11 +08:00
suyl
e5d02223b7 aa 2021-04-27 09:29:08 +08:00
suyl
4095161df5 aa 2021-04-27 09:19:29 +08:00
suyl
d0bd92a49f aa 2021-04-27 09:07:49 +08:00
suyl
e178ddc87a aa 2021-04-26 18:20:45 +08:00
suyl
e41a9a64d6 aa 2021-04-26 18:07:15 +08:00
suyl
bedf8f26b1 aa 2021-04-26 18:03:48 +08:00
suyl
334c174f67 aa 2021-04-26 17:59:20 +08:00
suyl
c5383d74ff 微信扫码建商品,重量13.5 转 int报错,round一下 2021-04-25 14:10:34 +08:00
suyl
bcf7cf6137 aa 2021-04-25 09:57:48 +08:00
suyl
f7725bcfc4 aa 2021-04-23 10:21:55 +08:00
suyl
58585baea9 aa 2021-04-23 09:54:04 +08:00
suyl
48df8aac52 aa 2021-04-22 17:28:31 +08:00
suyl
2b7ca32373 aa 2021-04-22 15:34:22 +08:00
suyl
0e9358131f aa 2021-04-22 14:26:24 +08:00
suyl
566c822533 aa 2021-04-22 14:23:32 +08:00
苏尹岚
87b0ebaef8 aa 2021-04-21 15:53:39 +08:00
苏尹岚
ab746f2af2 aa 2021-04-21 15:05:13 +08:00
苏尹岚
9a3c42b7a5 aa 2021-04-21 14:14:33 +08:00
苏尹岚
ae03393043 aa 2021-04-20 16:28:01 +08:00
苏尹岚
e38f3db539 aa 2021-04-20 16:15:11 +08:00
苏尹岚
627963b856 aa 2021-04-20 15:40:38 +08:00
苏尹岚
d302e881e7 aa 2021-04-20 15:24:55 +08:00
苏尹岚
2fc88aeec5 aa 2021-04-20 10:38:08 +08:00
苏尹岚
51c7cf1932 aa 2021-04-20 10:30:16 +08:00
苏尹岚
c6cbc1035c aa 2021-04-19 17:16:23 +08:00
苏尹岚
53c032e710 aa 2021-04-19 12:00:37 +08:00
苏尹岚
b4b806531a aa 2021-04-19 11:20:42 +08:00
苏尹岚
af2862bed8 aa 2021-04-19 10:09:05 +08:00
苏尹岚
20241e1728 aa 2021-04-19 10:00:18 +08:00
苏尹岚
7644a79884 aa 2021-04-19 09:59:34 +08:00
suyl
ccaab14b99 aa 2021-04-17 13:27:16 +08:00
苏尹岚
81ed9d41be aa 2021-04-16 18:03:05 +08:00
苏尹岚
19e5f5c140 aa 2021-04-16 17:58:21 +08:00
苏尹岚
e5115da909 aa 2021-04-16 17:43:21 +08:00
苏尹岚
f4ea8f5c73 aa 2021-04-16 16:40:50 +08:00
苏尹岚
8588dbb843 aa 2021-04-16 16:25:47 +08:00
苏尹岚
72388ffeae aa 2021-04-16 16:23:06 +08:00
苏尹岚
884f63cace aa 2021-04-16 16:04:45 +08:00
苏尹岚
be0f151537 aa 2021-04-16 15:58:45 +08:00
苏尹岚
dfcabde3c2 aa 2021-04-16 15:48:51 +08:00
苏尹岚
ae50d7f2b7 aa 2021-04-16 15:00:04 +08:00
苏尹岚
1aa365993c aa 2021-04-16 14:53:36 +08:00
苏尹岚
f22645a54b aa 2021-04-16 14:51:03 +08:00
苏尹岚
809c046994 aa 2021-04-16 14:42:54 +08:00
苏尹岚
1b3c71f470 aa 2021-04-16 13:55:04 +08:00
苏尹岚
6caf80bfab aa 2021-04-16 09:22:45 +08:00
苏尹岚
fb10b933f1 aa 2021-04-15 16:58:09 +08:00
苏尹岚
2c14e19f7a aa 2021-04-15 16:29:44 +08:00
苏尹岚
e4d95199b6 aa 2021-04-15 16:28:20 +08:00
苏尹岚
9e26d3c97f aa 2021-04-15 14:05:12 +08:00
苏尹岚
661c6b477e aa 2021-04-15 10:35:23 +08:00
苏尹岚
93271b13cc aa 2021-04-15 10:22:45 +08:00
苏尹岚
c8edb512fd aa 2021-04-15 10:03:15 +08:00
苏尹岚
abbd00ce49 aa 2021-04-15 09:45:52 +08:00
苏尹岚
b309641e5c aa 2021-04-14 17:21:27 +08:00
苏尹岚
2101a5282e aa 2021-04-14 13:41:07 +08:00
苏尹岚
25ca1abdf7 aa 2021-04-14 09:18:22 +08:00
苏尹岚
5efc05444f aa 2021-04-13 18:13:05 +08:00
苏尹岚
b074de535b aa 2021-04-13 17:40:30 +08:00
苏尹岚
806789f756 aa 2021-04-13 17:39:14 +08:00
苏尹岚
4f7fd9f1e2 aa 2021-04-13 14:03:11 +08:00
苏尹岚
d56f4ab906 aa 2021-04-13 13:48:38 +08:00
苏尹岚
8f75cbe3e6 aa 2021-04-13 11:35:16 +08:00
苏尹岚
9f98a6bda0 aa 2021-04-13 11:30:24 +08:00
苏尹岚
c07095187b aa 2021-04-13 10:51:04 +08:00
苏尹岚
42ddb8f4b3 aa 2021-04-13 10:46:50 +08:00
苏尹岚
82325109ef aa 2021-04-13 10:15:26 +08:00
苏尹岚
6c313cfe2b aa 2021-04-13 09:41:02 +08:00
苏尹岚
6369593549 aa 2021-04-13 09:29:17 +08:00
苏尹岚
ed9eb3946e aa 2021-04-13 09:27:06 +08:00
苏尹岚
9dbca78044 aa 2021-04-13 09:02:36 +08:00
苏尹岚
812432837d aa 2021-04-12 17:43:55 +08:00
苏尹岚
accfdcb4ab aa 2021-04-12 16:55:33 +08:00
苏尹岚
050eaa21f3 aa 2021-04-12 15:54:46 +08:00
苏尹岚
23f3fc0373 aa 2021-04-12 14:55:48 +08:00
苏尹岚
2d81f1d452 aa 2021-04-12 14:36:16 +08:00
苏尹岚
a679116c83 aa 2021-04-12 14:13:09 +08:00
苏尹岚
7c1d855ccf aa 2021-04-12 11:57:40 +08:00
苏尹岚
777ce0a036 aa 2021-04-12 11:52:51 +08:00
苏尹岚
4ab7a3cb34 aa 2021-04-12 11:40:49 +08:00
苏尹岚
e802d32dc0 aa 2021-04-12 11:30:48 +08:00
苏尹岚
fd4caa1a73 aa 2021-04-12 10:59:43 +08:00
苏尹岚
9839e1ed91 aa 2021-04-12 10:41:26 +08:00
苏尹岚
6cdc6038da aa 2021-04-12 10:34:31 +08:00
苏尹岚
47bc850dca aa 2021-04-12 10:24:33 +08:00
苏尹岚
52e2ef46d8 aa 2021-04-12 09:41:12 +08:00
苏尹岚
5368642312 aa 2021-04-12 09:32:53 +08:00
苏尹岚
fdf8022695 aa 2021-04-09 18:21:35 +08:00
苏尹岚
77fa1137ac aa 2021-04-09 18:03:13 +08:00
苏尹岚
ba144fe63b aa 2021-04-09 17:54:06 +08:00
苏尹岚
927d1b82b7 aa 2021-04-09 17:19:02 +08:00
苏尹岚
dc8ab361b7 aa 2021-04-09 15:29:25 +08:00
苏尹岚
d8b757cbc2 aa 2021-04-09 14:45:43 +08:00
苏尹岚
101b0e5c24 aa 2021-04-09 11:47:39 +08:00
苏尹岚
dc1f8df8f6 aa 2021-04-09 11:44:57 +08:00
苏尹岚
4078d6f8e3 aa 2021-04-09 11:31:23 +08:00
苏尹岚
f062cb371c aa 2021-04-09 11:20:24 +08:00
苏尹岚
b1868591d0 aa 2021-04-09 11:16:05 +08:00
苏尹岚
c670855e11 aa 2021-04-09 11:11:50 +08:00
苏尹岚
71c79f62d4 aa 2021-04-09 10:32:51 +08:00
苏尹岚
038f60595d aa 2021-04-09 10:15:28 +08:00
苏尹岚
a160037979 aa 2021-04-09 10:03:31 +08:00
苏尹岚
4722561a6e aa 2021-04-09 09:44:44 +08:00
苏尹岚
a71f903e00 aa 2021-04-08 18:01:52 +08:00
苏尹岚
855ad3a265 aa 2021-04-08 16:37:46 +08:00
苏尹岚
07f9e1a5f3 aa 2021-04-08 15:24:07 +08:00
苏尹岚
3cf833dac7 aa 2021-04-08 14:49:42 +08:00
苏尹岚
d8e891a30e aa 2021-04-08 14:46:13 +08:00
苏尹岚
51898baf86 aa 2021-04-08 14:43:39 +08:00
苏尹岚
0dfd67c7a2 aa 2021-04-08 14:36:05 +08:00
苏尹岚
0857bf1048 aa 2021-04-08 14:28:19 +08:00
苏尹岚
4a0eda6233 aa 2021-04-08 14:23:45 +08:00
苏尹岚
69cead93dc aa 2021-04-08 14:06:47 +08:00
苏尹岚
1f63dc8158 aa 2021-04-08 13:51:49 +08:00
苏尹岚
a11d5312be aa 2021-04-07 17:44:50 +08:00
苏尹岚
1e76399ef4 aa 2021-04-07 15:22:12 +08:00
苏尹岚
6dc8f9d451 aa 2021-04-07 15:11:13 +08:00
苏尹岚
95c60f62af aa 2021-04-07 15:03:24 +08:00
苏尹岚
868488acae aa 2021-04-07 14:52:19 +08:00
苏尹岚
66a56c2a61 aa 2021-04-07 14:48:12 +08:00
苏尹岚
07a6fd04fd aa 2021-04-07 14:45:04 +08:00
苏尹岚
7c6513e2b4 aa 2021-04-07 14:25:42 +08:00
苏尹岚
98f443379d aa 2021-04-07 14:00:43 +08:00
苏尹岚
b5aa8748ec aa 2021-04-07 11:37:50 +08:00
苏尹岚
4b25b4dbf7 aa 2021-04-07 11:32:04 +08:00
苏尹岚
809abf0795 aa 2021-04-07 11:18:58 +08:00
苏尹岚
d477b90ea2 aa 2021-04-07 10:58:11 +08:00
苏尹岚
553aa5c8fb aa 2021-04-07 10:56:08 +08:00
苏尹岚
d72afc0cfa aa 2021-04-07 09:32:38 +08:00
苏尹岚
8e1419f495 aa 2021-04-07 09:06:00 +08:00
苏尹岚
d030002bf6 aa 2021-04-07 09:00:38 +08:00
苏尹岚
d1bf1cc6b3 aa 2021-04-06 18:25:44 +08:00
苏尹岚
a66e7bc13d aa 2021-04-06 16:31:38 +08:00
苏尹岚
b84ff3e0f7 aa 2021-04-06 16:26:45 +08:00
苏尹岚
24c4c27efd aa 2021-04-06 15:00:47 +08:00
苏尹岚
3f895f0dc4 aa 2021-04-06 14:32:17 +08:00
苏尹岚
6fa79a7a0a aa 2021-04-06 13:44:46 +08:00
苏尹岚
2952285378 aa 2021-04-06 13:41:57 +08:00
苏尹岚
c4bd9f6c76 aa 2021-04-06 11:59:48 +08:00
苏尹岚
d302095877 aa 2021-04-06 11:52:09 +08:00
苏尹岚
ffc647b832 aa 2021-04-06 11:23:01 +08:00
苏尹岚
8c5d4abe7e aa 2021-04-06 11:18:42 +08:00
苏尹岚
d5a5a259e7 aa 2021-04-06 10:50:36 +08:00
苏尹岚
20b14bcb86 aa 2021-04-06 10:48:35 +08:00
苏尹岚
41dc1bfb0f aa 2021-04-06 10:22:09 +08:00
苏尹岚
00b540d313 aa 2021-04-06 10:00:19 +08:00
苏尹岚
115c5ea945 Merge branch 'jdshop' of e.coding.net:rosydev/jx-callback into jdshop 2021-04-06 08:51:21 +08:00
苏尹岚
73653d654a aa 2021-04-06 08:51:10 +08:00
suyl
51267259c7 aa 2021-04-04 19:57:35 +08:00
苏尹岚
e066132005 aa 2021-04-02 17:53:09 +08:00
苏尹岚
6f3fb65b57 aa 2021-04-02 17:30:54 +08:00
苏尹岚
789c101429 aa 2021-04-02 17:26:54 +08:00
苏尹岚
65cfe8e369 aa 2021-04-02 17:19:51 +08:00
苏尹岚
ad13f8eb57 aa 2021-04-02 17:13:59 +08:00
苏尹岚
be626824e4 aa 2021-04-02 16:52:35 +08:00
苏尹岚
d4a861e8af a 2021-04-02 15:59:40 +08:00
苏尹岚
e66e0d68d7 aa 2021-04-02 15:56:59 +08:00
苏尹岚
91930ee307 aa 2021-04-02 11:44:40 +08:00
苏尹岚
8429a5a27c aa 2021-04-02 11:03:01 +08:00
苏尹岚
0a92b20480 aa 2021-04-02 10:32:47 +08:00
苏尹岚
0bfaefe163 aa 2021-04-02 10:30:22 +08:00
苏尹岚
f1b842d90c aa 2021-04-02 10:23:25 +08:00
苏尹岚
aeb4b1b4b0 aa 2021-04-02 10:18:16 +08:00
苏尹岚
4ba5545c7d aa 2021-04-02 09:57:02 +08:00
苏尹岚
04ae92b33c aa 2021-04-02 09:55:36 +08:00
苏尹岚
f46afb51ed aa 2021-04-01 15:05:04 +08:00
苏尹岚
f33267afab aa 2021-04-01 14:50:34 +08:00
苏尹岚
78a077d30e aa 2021-04-01 14:07:08 +08:00
苏尹岚
721b9a7357 aa 2021-04-01 13:50:19 +08:00
苏尹岚
51c282c62b 果园京东token 2021-04-01 11:31:40 +08:00
苏尹岚
02965fa230 aa 2021-04-01 11:08:54 +08:00
苏尹岚
9ebee52e94 aa 2021-04-01 10:46:37 +08:00
苏尹岚
c5671965ac aa 2021-04-01 10:12:56 +08:00
苏尹岚
e4f9c547a9 aa 2021-04-01 09:51:47 +08:00
苏尹岚
cc12d82076 aa 2021-04-01 09:45:39 +08:00
苏尹岚
11f607481f aa 2021-04-01 09:35:06 +08:00
苏尹岚
999765eb08 aa 2021-03-31 17:00:41 +08:00
苏尹岚
b784fa92ca aa 2021-03-31 16:42:26 +08:00
苏尹岚
e3d995c85d aa 2021-03-31 16:37:04 +08:00
苏尹岚
6bf4ce45a5 aa 2021-03-31 16:20:11 +08:00
苏尹岚
9863f02757 aa 2021-03-31 16:01:30 +08:00
苏尹岚
3cb180a48d aa 2021-03-31 15:57:50 +08:00
苏尹岚
1208b4357e aa 2021-03-31 15:53:06 +08:00
苏尹岚
423c1f8845 aa 2021-03-31 15:37:59 +08:00
苏尹岚
ebb7424282 aa 2021-03-31 14:52:00 +08:00
苏尹岚
90f16f87f2 aa 2021-03-31 13:45:56 +08:00
苏尹岚
d6d2688545 aa 2021-03-31 11:51:45 +08:00
苏尹岚
9564d04538 aa 2021-03-31 11:38:05 +08:00
苏尹岚
6378fa103d a 2021-03-31 11:24:28 +08:00
苏尹岚
bc951c1a59 aa 2021-03-31 10:00:28 +08:00
苏尹岚
125c335c80 aa 2021-03-30 17:42:41 +08:00
苏尹岚
0dd7235485 aa 2021-03-30 17:39:07 +08:00
苏尹岚
b9f7d7f53a 饿百订单自提核销 2021-03-30 15:57:37 +08:00
苏尹岚
02bdeb7b9d 美团饿百流量活动通知 2021-03-30 14:35:15 +08:00
苏尹岚
06685fa638 aa 2021-03-30 14:05:22 +08:00
苏尹岚
5bad08f487 aa 2021-03-30 11:00:32 +08:00
苏尹岚
eaa5d71a82 aa 2021-03-30 10:53:47 +08:00
苏尹岚
ac2bb2bcfe aa 2021-03-30 10:19:36 +08:00
苏尹岚
c56500548c aa 2021-03-29 18:30:07 +08:00
苏尹岚
f7b3a4df3f aa 2021-03-29 16:29:55 +08:00
苏尹岚
37c1599279 aa 2021-03-29 15:32:20 +08:00
苏尹岚
34841c3bfd aa 2021-03-29 11:08:10 +08:00
苏尹岚
69bcb8a6aa aa 2021-03-29 10:56:39 +08:00
苏尹岚
f15d8654a3 aa 2021-03-29 10:54:39 +08:00
苏尹岚
90ec474f19 aa 2021-03-29 10:28:20 +08:00
苏尹岚
f0fcf84ddb aa 2021-03-29 10:19:53 +08:00
苏尹岚
1e663b5f57 aa 2021-03-29 10:02:51 +08:00
苏尹岚
213c1b902f aa 2021-03-29 09:51:17 +08:00
苏尹岚
fcc482a36a aa 2021-03-29 09:39:45 +08:00
苏尹岚
103a8d14a5 aa 2021-03-26 18:04:13 +08:00
苏尹岚
e213e20df2 aa 2021-03-26 17:53:59 +08:00
苏尹岚
c05a3e14ec aa 2021-03-26 17:34:00 +08:00
苏尹岚
8cb61ef62d aa 2021-03-26 17:31:10 +08:00
苏尹岚
d56beac926 aa 2021-03-26 16:30:44 +08:00
苏尹岚
e45d85e999 aa 2021-03-26 16:29:27 +08:00
苏尹岚
559908276d aa 2021-03-26 16:13:17 +08:00
苏尹岚
8e561af698 aa 2021-03-26 16:11:37 +08:00
苏尹岚
9aa44c38d1 aa 2021-03-26 15:33:19 +08:00
苏尹岚
14b364724d aa 2021-03-26 15:12:19 +08:00
苏尹岚
d85011dd0a aa 2021-03-26 15:02:52 +08:00
苏尹岚
5afc312f8d aa 2021-03-26 14:57:41 +08:00
苏尹岚
e552c8453e aa 2021-03-26 14:48:20 +08:00
苏尹岚
b3c7d41958 aa 2021-03-26 13:52:10 +08:00
苏尹岚
11740281ca aa 2021-03-26 11:13:07 +08:00
苏尹岚
1100724061 aa 2021-03-26 10:51:28 +08:00
苏尹岚
7da263c887 aa 2021-03-26 10:46:25 +08:00
苏尹岚
6551ec2d93 aa 2021-03-26 10:15:44 +08:00
苏尹岚
b0986a3da8 aa 2021-03-26 10:12:02 +08:00
苏尹岚
44b9677c3c aa 2021-03-26 09:59:42 +08:00
苏尹岚
0be1343529 aa 2021-03-26 09:54:20 +08:00
苏尹岚
4adacb7cec aa 2021-03-26 09:51:46 +08:00
苏尹岚
597b78f1f0 aa 2021-03-26 09:49:34 +08:00
苏尹岚
5c13c89954 aa 2021-03-26 09:32:31 +08:00
苏尹岚
c269768e5b aa 2021-03-26 09:29:15 +08:00
苏尹岚
ba8e3cb957 aa 2021-03-26 09:27:04 +08:00
苏尹岚
bf71bcd0e4 aa 2021-03-26 09:21:42 +08:00
苏尹岚
7dd8c32cab aa 2021-03-26 08:58:49 +08:00
苏尹岚
d6380f1a56 Merge remote-tracking branch 'origin/mark' into jdshop 2021-03-25 18:23:44 +08:00
苏尹岚
a6062d17af aa 2021-03-25 18:09:07 +08:00
苏尹岚
ded9520700 aa 2021-03-25 18:04:55 +08:00
苏尹岚
86712582f2 aa 2021-03-25 17:55:03 +08:00
苏尹岚
51d70c03d1 aa 2021-03-25 17:50:24 +08:00
苏尹岚
42f4c89771 aa 2021-03-25 17:47:14 +08:00
苏尹岚
dd038c8de2 aa 2021-03-25 17:41:12 +08:00
苏尹岚
e61db3ce10 aa 2021-03-25 17:38:41 +08:00
苏尹岚
964f13e2f1 aa 2021-03-25 17:30:24 +08:00
苏尹岚
735a35e97e aa 2021-03-25 17:29:40 +08:00
苏尹岚
8f0e1ecb2f aa 2021-03-25 17:25:17 +08:00
苏尹岚
f2b8089536 aa 2021-03-25 17:20:21 +08:00
苏尹岚
604317bb78 aa 2021-03-25 16:52:40 +08:00
苏尹岚
6da9b98dc9 aa 2021-03-25 16:46:46 +08:00
苏尹岚
fba334121e aa 2021-03-25 16:33:24 +08:00
苏尹岚
6ec18af939 aa 2021-03-25 16:28:15 +08:00
苏尹岚
9450b30d49 aa 2021-03-25 16:22:55 +08:00
苏尹岚
304b1dfd56 aa 2021-03-25 16:21:59 +08:00
苏尹岚
b540ba28f0 aa 2021-03-25 16:20:35 +08:00
苏尹岚
185d51dd49 a 2021-03-25 16:17:48 +08:00
苏尹岚
fe8ee55d90 aa 2021-03-25 16:14:53 +08:00
苏尹岚
ab52769654 aa 2021-03-25 16:12:49 +08:00
苏尹岚
27aceae1d3 aa 2021-03-25 16:10:43 +08:00
苏尹岚
c647fdb17e aa 2021-03-25 15:51:15 +08:00
苏尹岚
0bf471365c aa 2021-03-25 11:59:02 +08:00
苏尹岚
989277c929 aa 2021-03-25 11:51:35 +08:00
苏尹岚
e35d761fea aa 2021-03-25 11:48:52 +08:00
苏尹岚
d54c9a8d07 aa 2021-03-25 11:43:44 +08:00
苏尹岚
ca8f2c8f9d aa 2021-03-25 11:38:32 +08:00
苏尹岚
4c19bd32a0 aa 2021-03-25 11:30:23 +08:00
苏尹岚
baae588ec4 aa 2021-03-25 11:20:49 +08:00
苏尹岚
e5495ef351 aa 2021-03-25 10:23:31 +08:00
苏尹岚
1f74c01397 aa 2021-03-25 10:04:35 +08:00
苏尹岚
17d31987b6 aa 2021-03-24 17:55:05 +08:00
苏尹岚
9647764894 aa 2021-03-24 17:06:02 +08:00
苏尹岚
08e2bc5dea aa 2021-03-24 17:02:14 +08:00
苏尹岚
67a11f61dd aa 2021-03-24 16:47:21 +08:00
苏尹岚
03f4ea45ad aa 2021-03-24 16:46:09 +08:00
苏尹岚
ec111a543a aa 2021-03-24 16:06:31 +08:00
苏尹岚
dda5e519bb aa 2021-03-24 15:29:31 +08:00
苏尹岚
bba3e273f6 aa 2021-03-24 10:17:11 +08:00
苏尹岚
08ddc2308d aa 2021-03-23 16:55:41 +08:00
苏尹岚
97a9605712 aa 2021-03-23 16:50:29 +08:00
苏尹岚
b40e508ac2 aa 2021-03-23 14:20:07 +08:00
苏尹岚
58cf995091 aa 2021-03-23 14:17:55 +08:00
苏尹岚
2211b15f4c aa 2021-03-23 14:13:57 +08:00
苏尹岚
9cba4e483c aa 2021-03-23 14:02:19 +08:00
苏尹岚
bbd2ca3487 aa 2021-03-23 08:55:06 +08:00
苏尹岚
34cab3adf9 aa 2021-03-22 16:03:14 +08:00
苏尹岚
be3476e351 aa 2021-03-22 14:06:46 +08:00
苏尹岚
c4fe124508 aa 2021-03-22 11:57:54 +08:00
苏尹岚
cc9f5d77b9 aa 2021-03-22 10:46:22 +08:00
苏尹岚
7c97ebc3d8 aa 2021-03-19 18:43:32 +08:00
苏尹岚
51837e2f15 aa 2021-03-19 18:35:10 +08:00
苏尹岚
2d3cecd1da aa 2021-03-19 18:09:23 +08:00
苏尹岚
3a8c83434c aa 2021-03-19 17:39:32 +08:00
苏尹岚
1cc13e5811 aa 2021-03-19 16:02:09 +08:00
苏尹岚
6dd3b55876 aa 2021-03-19 15:53:21 +08:00
苏尹岚
7e7cdce60e aa 2021-03-19 15:51:10 +08:00
苏尹岚
08bec358e7 aa 2021-03-19 15:23:56 +08:00
苏尹岚
2539799443 aa 2021-03-19 15:19:48 +08:00
苏尹岚
b076524fa0 aa 2021-03-19 15:17:42 +08:00
苏尹岚
531aee3f71 aa 2021-03-19 15:14:27 +08:00
苏尹岚
e184f7a856 aa 2021-03-19 15:11:08 +08:00
苏尹岚
bd2c116afe aa 2021-03-19 14:56:55 +08:00
苏尹岚
452fef7336 aa 2021-03-19 14:44:14 +08:00
苏尹岚
0f4806c178 aa 2021-03-19 14:43:25 +08:00
苏尹岚
6446b02cdc aa 2021-03-19 14:26:42 +08:00
苏尹岚
ee63b713de aa 2021-03-19 14:23:27 +08:00
苏尹岚
119c569618 aa 2021-03-19 14:06:00 +08:00
苏尹岚
b1f87f4334 aa 2021-03-19 14:01:38 +08:00
苏尹岚
89306f76b6 aa 2021-03-19 13:57:05 +08:00
苏尹岚
9ae3335206 aa 2021-03-19 11:40:54 +08:00
苏尹岚
b4efae54a1 aa 2021-03-19 11:35:37 +08:00
苏尹岚
dd34822b86 aa 2021-03-19 10:56:10 +08:00
苏尹岚
2ba2ff5994 aa 2021-03-19 10:45:27 +08:00
苏尹岚
8dba6fe19f aa 2021-03-19 10:01:54 +08:00
苏尹岚
c2954f52d2 aa 2021-03-19 10:00:49 +08:00
苏尹岚
e6b7893243 aa 2021-03-19 09:51:49 +08:00
苏尹岚
1fc7e9f5a0 aa 2021-03-19 09:48:59 +08:00
苏尹岚
ef53db347a aa 2021-03-19 09:41:26 +08:00
苏尹岚
7e20fb35fe aa 2021-03-19 09:31:45 +08:00
苏尹岚
3c6d727423 aa 2021-03-19 09:18:34 +08:00
苏尹岚
875c29f655 aa 2021-03-19 09:11:57 +08:00
苏尹岚
1f5f972402 aa 2021-03-18 18:20:58 +08:00
苏尹岚
4d4c5d6b9d aa 2021-03-18 17:50:53 +08:00
苏尹岚
ab9d270aa9 aa 2021-03-18 14:33:35 +08:00
苏尹岚
e1fced0a71 aa 2021-03-18 14:09:43 +08:00
苏尹岚
0f21a0df0b aa 2021-03-18 11:24:34 +08:00
苏尹岚
7bb3c81f00 aa 2021-03-18 10:53:37 +08:00
苏尹岚
fe489660b8 aa 2021-03-18 10:46:00 +08:00
苏尹岚
86ef0528a2 aa 2021-03-18 10:40:14 +08:00
苏尹岚
c87c32ead1 aa 2021-03-18 10:04:10 +08:00
苏尹岚
a17211c679 aa 2021-03-18 09:55:11 +08:00
苏尹岚
79291abfbc aa 2021-03-18 09:41:03 +08:00
苏尹岚
ab74e6517e aa 2021-03-18 09:36:20 +08:00
苏尹岚
bb59d1e341 aa 2021-03-18 09:29:15 +08:00
苏尹岚
dddd9fd117 aa 2021-03-18 09:22:00 +08:00
苏尹岚
2ab1018498 aa 2021-03-18 09:16:53 +08:00
苏尹岚
6c767e160a aa 2021-03-17 18:27:25 +08:00
苏尹岚
757983c756 aa 2021-03-17 18:22:32 +08:00
苏尹岚
e3ab368dda aa 2021-03-17 18:21:30 +08:00
苏尹岚
d690f8cfb7 aa 2021-03-17 18:04:44 +08:00
苏尹岚
c4bcc120a4 aa 2021-03-17 17:55:37 +08:00
苏尹岚
f0d41bc043 aa 2021-03-17 17:35:40 +08:00
苏尹岚
e0a1393eee aa 2021-03-17 17:14:57 +08:00
苏尹岚
f73783b2c9 aa 2021-03-17 17:10:03 +08:00
苏尹岚
371800dcf7 aa 2021-03-17 17:07:32 +08:00
苏尹岚
f0b659c276 aa 2021-03-17 16:57:20 +08:00
苏尹岚
9edf96c9d4 aa 2021-03-17 16:27:43 +08:00
苏尹岚
556fa0913d aa 2021-03-17 16:22:03 +08:00
苏尹岚
2c92e8ec94 aa 2021-03-17 16:02:05 +08:00
苏尹岚
1990f986f5 aa 2021-03-17 15:30:33 +08:00
苏尹岚
1ae86dcdf5 aa 2021-03-17 15:08:48 +08:00
苏尹岚
d40a381552 aa 2021-03-17 15:03:05 +08:00
苏尹岚
9dc520daa3 aa 2021-03-17 14:52:46 +08:00
苏尹岚
bb24236869 aa 2021-03-17 14:41:27 +08:00
苏尹岚
313667604a aa 2021-03-17 14:39:16 +08:00
苏尹岚
385d15fd52 aa 2021-03-17 14:35:05 +08:00
苏尹岚
ada4d6e023 aa 2021-03-17 14:31:03 +08:00
苏尹岚
c4e876ea2f aa 2021-03-17 14:13:59 +08:00
苏尹岚
67dc00e495 aa 2021-03-17 14:08:13 +08:00
苏尹岚
1441e31bae aa 2021-03-17 13:58:14 +08:00
苏尹岚
30e4b26ebf aa 2021-03-17 13:40:55 +08:00
苏尹岚
435a3a1a04 aa 2021-03-17 11:32:17 +08:00
苏尹岚
f02062d9f4 aa 2021-03-17 11:14:08 +08:00
苏尹岚
90860121bd aa 2021-03-17 10:44:38 +08:00
苏尹岚
3edd03b8e0 aa 2021-03-17 10:29:44 +08:00
苏尹岚
4a77161781 aa 2021-03-17 10:23:18 +08:00
苏尹岚
6f3577c92b aa 2021-03-17 10:20:55 +08:00
苏尹岚
9d370d88a8 aa 2021-03-17 09:54:45 +08:00
苏尹岚
2b53dbb708 aa 2021-03-17 09:21:57 +08:00
苏尹岚
71a11a333e aa 2021-03-17 09:04:23 +08:00
苏尹岚
4c52700e6a aa 2021-03-16 18:10:01 +08:00
苏尹岚
11f4c44da2 aa 2021-03-16 10:08:10 +08:00
苏尹岚
0c2ecff553 aa 2021-03-16 09:54:25 +08:00
苏尹岚
4f6001e413 aa 2021-03-16 09:25:24 +08:00
苏尹岚
8a17e0584b aa 2021-03-15 17:49:04 +08:00
苏尹岚
4f26f319ce aa 2021-03-15 17:44:01 +08:00
苏尹岚
aedb4e7649 aa 2021-03-15 17:36:17 +08:00
苏尹岚
a1b173fdd9 aa 2021-03-15 17:19:08 +08:00
苏尹岚
0672c4bdcb aa 2021-03-15 17:15:24 +08:00
苏尹岚
d9dd5859e0 aa 2021-03-15 17:08:18 +08:00
苏尹岚
fbe40b226c aa 2021-03-15 17:07:29 +08:00
苏尹岚
e136f2f2cd aa 2021-03-15 17:01:14 +08:00
苏尹岚
6b080b576d aa 2021-03-15 16:15:56 +08:00
苏尹岚
da6196275d aa 2021-03-15 16:05:11 +08:00
苏尹岚
d312acaf42 aa 2021-03-15 15:40:47 +08:00
苏尹岚
3a417753b7 aa 2021-03-15 14:18:32 +08:00
苏尹岚
26de1f03de aa 2021-03-15 13:50:36 +08:00
苏尹岚
8c526bc31f aa 2021-03-15 11:28:44 +08:00
苏尹岚
0d402ec65f aa 2021-03-15 11:17:31 +08:00
苏尹岚
09f5bf4edc aa 2021-03-15 09:58:32 +08:00
苏尹岚
341db8e564 aa 2021-03-15 09:53:58 +08:00
苏尹岚
375801ad27 aa 2021-03-15 09:21:59 +08:00
苏尹岚
99d4b4dd04 aa 2021-03-12 17:03:40 +08:00
苏尹岚
cd87619ac6 aa 2021-03-12 16:53:22 +08:00
苏尹岚
f47e36d04e aa 2021-03-12 16:22:57 +08:00
苏尹岚
47ce54c9af aa 2021-03-12 16:17:40 +08:00
苏尹岚
a5e37278aa aa 2021-03-12 16:14:22 +08:00
苏尹岚
e97baae8d1 aa 2021-03-12 15:57:29 +08:00
苏尹岚
3d39ce347e aa 2021-03-12 15:52:43 +08:00
苏尹岚
3cb0904e6f aa 2021-03-12 15:43:06 +08:00
苏尹岚
b93fba4bb5 aa 2021-03-12 15:37:42 +08:00
苏尹岚
303ec79cce aa 2021-03-12 14:45:50 +08:00
苏尹岚
a63206756f aa 2021-03-12 14:37:18 +08:00
苏尹岚
3016590f89 aa 2021-03-12 14:27:34 +08:00
苏尹岚
848848df8f aa 2021-03-12 14:19:54 +08:00
苏尹岚
66da206735 aa 2021-03-12 14:16:03 +08:00
苏尹岚
acb75f1e1f aa 2021-03-12 10:50:38 +08:00
苏尹岚
795893424d aa 2021-03-12 09:25:54 +08:00
苏尹岚
656e679140 aa 2021-03-12 09:15:47 +08:00
苏尹岚
52efee5f96 aa 2021-03-12 09:11:40 +08:00
苏尹岚
4b14277f29 aa 2021-03-11 18:21:48 +08:00
苏尹岚
ce5adfaafe aa 2021-03-11 17:51:53 +08:00
苏尹岚
452b61f9c9 aa 2021-03-11 17:41:18 +08:00
苏尹岚
91ea354d63 aa 2021-03-11 17:36:25 +08:00
苏尹岚
344ecb0d13 aa 2021-03-11 16:02:28 +08:00
苏尹岚
41c27faab7 aa 2021-03-11 15:10:54 +08:00
苏尹岚
a2978a94e5 aa 2021-03-11 14:42:12 +08:00
苏尹岚
a391a7b86d aa 2021-03-11 13:39:18 +08:00
苏尹岚
3fb52de653 aa 2021-03-11 11:39:36 +08:00
苏尹岚
f79e750ace aa 2021-03-11 10:53:13 +08:00
苏尹岚
33f6a0f304 aa 2021-03-11 10:14:55 +08:00
苏尹岚
136f54f7fe aa 2021-03-11 10:11:29 +08:00
苏尹岚
cd5356da62 aa 2021-03-11 10:08:38 +08:00
苏尹岚
132d2780f2 aa 2021-03-11 10:02:32 +08:00
苏尹岚
7469a2d11f aa 2021-03-11 09:54:53 +08:00
苏尹岚
7917274c26 aa 2021-03-11 09:52:16 +08:00
苏尹岚
dc7e916471 aa 2021-03-11 09:47:47 +08:00
苏尹岚
f25e490867 aa 2021-03-11 09:39:32 +08:00
苏尹岚
acc063bd09 aa 2021-03-11 09:31:53 +08:00
苏尹岚
c7737f1686 aa 2021-03-10 18:24:25 +08:00
苏尹岚
3df18ef931 aa 2021-03-10 18:16:35 +08:00
苏尹岚
1224e340c0 aa 2021-03-10 18:04:40 +08:00
苏尹岚
2364d4eae2 aa 2021-03-10 17:49:08 +08:00
苏尹岚
acacc3b2de aa 2021-03-10 17:42:43 +08:00
苏尹岚
a76e30bc35 aa 2021-03-10 17:25:59 +08:00
苏尹岚
8859a41bdf aa 2021-03-10 16:44:05 +08:00
苏尹岚
f52c1ad837 aa 2021-03-10 16:19:11 +08:00
苏尹岚
2f59fd1d51 aa 2021-03-10 16:16:22 +08:00
苏尹岚
9e17d72597 aa 2021-03-10 16:05:33 +08:00
苏尹岚
882fa65ea5 aa 2021-03-10 16:00:21 +08:00
苏尹岚
4d41b0dfcd aa 2021-03-10 15:55:40 +08:00
苏尹岚
e5e68f83f5 aa 2021-03-10 15:48:27 +08:00
苏尹岚
154a938979 aa 2021-03-10 15:46:52 +08:00
苏尹岚
176d39b7ed aa 2021-03-10 15:21:37 +08:00
苏尹岚
99e53d869e aa 2021-03-10 15:18:27 +08:00
苏尹岚
3080a707d2 aa 2021-03-10 14:58:40 +08:00
苏尹岚
527596becc aa 2021-03-10 14:48:00 +08:00
苏尹岚
6316344b94 aa 2021-03-10 14:44:33 +08:00
苏尹岚
dc523889f3 aa 2021-03-10 14:40:38 +08:00
苏尹岚
c7dbd7405d aa 2021-03-10 14:08:32 +08:00
苏尹岚
88ff46c0ee aa 2021-03-10 14:01:04 +08:00
苏尹岚
050cd4db15 aa 2021-03-10 13:51:15 +08:00
苏尹岚
a09bc75d82 aa 2021-03-10 12:01:29 +08:00
苏尹岚
54167eade1 aa 2021-03-10 11:51:06 +08:00
苏尹岚
9243b4b5f9 aa 2021-03-10 11:49:23 +08:00
苏尹岚
d66dfa7d44 aa 2021-03-10 11:44:20 +08:00
苏尹岚
d520dae549 aa 2021-03-10 11:25:20 +08:00
苏尹岚
55828db155 aa 2021-03-10 11:23:48 +08:00
苏尹岚
5cdade9b04 aa 2021-03-10 11:18:52 +08:00
苏尹岚
f409f00ef9 aa 2021-03-10 10:30:22 +08:00
苏尹岚
493f2ba97b aa 2021-03-10 10:10:55 +08:00
苏尹岚
89d722c729 aa 2021-03-09 18:01:11 +08:00
苏尹岚
f298706e06 aa 2021-03-09 17:19:56 +08:00
苏尹岚
660de753d4 aa 2021-03-09 13:54:01 +08:00
苏尹岚
3c355a16c5 aa 2021-03-09 13:42:14 +08:00
苏尹岚
83cbb071b0 aa 2021-03-09 11:59:23 +08:00
苏尹岚
244a874040 aa 2021-03-09 11:44:10 +08:00
苏尹岚
38c0ef8fa9 aa 2021-03-09 11:40:39 +08:00
苏尹岚
e0f6b09ef5 aa 2021-03-09 11:36:07 +08:00
苏尹岚
d5c90df9b7 aa 2021-03-09 11:30:22 +08:00
苏尹岚
9e48e96001 aa 2021-03-09 11:21:15 +08:00
苏尹岚
9d8377d453 aa 2021-03-09 11:17:09 +08:00
苏尹岚
767e3c1441 aa 2021-03-09 11:06:40 +08:00
苏尹岚
f2f46cb867 aa 2021-03-09 11:03:23 +08:00
苏尹岚
defbdb0462 aa 2021-03-09 09:40:35 +08:00
苏尹岚
952fc7183e aa 2021-03-09 09:33:44 +08:00
苏尹岚
c4c5ab3bfd aa 2021-03-08 18:30:23 +08:00
苏尹岚
eb0bc0b7c8 aa 2021-03-08 18:14:19 +08:00
苏尹岚
b968e844ac aa 2021-03-08 16:28:12 +08:00
苏尹岚
891286ea51 aa 2021-03-08 16:22:30 +08:00
苏尹岚
981ccb0e46 aa 2021-03-08 16:07:42 +08:00
苏尹岚
4c06b4a8c6 aa 2021-03-08 16:00:20 +08:00
苏尹岚
74db22c236 aa 2021-03-08 14:54:25 +08:00
苏尹岚
bc1ba8c524 aa 2021-03-08 09:17:05 +08:00
苏尹岚
8fd0057740 aa 2021-03-08 09:16:44 +08:00
苏尹岚
f9f37fa2b7 aa 2021-03-05 17:30:43 +08:00
苏尹岚
71f4d08d68 aa 2021-03-05 17:02:14 +08:00
苏尹岚
4f36ffc6fa aa 2021-03-05 16:07:06 +08:00
苏尹岚
ad40d2e0b2 aa 2021-03-05 14:45:43 +08:00
苏尹岚
1a0083fffd aa 2021-03-05 14:18:48 +08:00
苏尹岚
b2e83a782e aa 2021-03-05 14:18:26 +08:00
苏尹岚
5cc1cbe805 aa 2021-03-05 14:18:12 +08:00
苏尹岚
af38c5993d 美团菜市建店加上环境图 2021-03-05 11:27:08 +08:00
苏尹岚
37a678bad9 aa 2021-03-05 11:10:07 +08:00
苏尹岚
bfdd404d68 aa 2021-03-05 10:28:12 +08:00
苏尹岚
05fb4dbb25 aa 2021-03-05 10:03:14 +08:00
苏尹岚
d5401d902e aa 2021-03-04 17:36:14 +08:00
苏尹岚
2c035f55fa aa 2021-03-04 17:29:30 +08:00
苏尹岚
490ea5cdb8 aa 2021-03-04 17:19:04 +08:00
苏尹岚
a2af241bdc aa 2021-03-04 16:58:05 +08:00
苏尹岚
67e33a0be0 aa 2021-03-04 16:54:51 +08:00
苏尹岚
40cfa42170 aa 2021-03-04 16:52:18 +08:00
苏尹岚
b5bf9ade80 aa 2021-03-04 16:51:41 +08:00
苏尹岚
3735a0fcaa aa 2021-03-04 16:50:03 +08:00
苏尹岚
dda3d11583 aa 2021-03-04 16:49:45 +08:00
苏尹岚
f65c9f8355 aa 2021-03-04 16:48:47 +08:00
苏尹岚
a58ee4a597 aa 2021-03-04 16:47:26 +08:00
苏尹岚
8b9207608b aa 2021-03-04 15:30:10 +08:00
苏尹岚
cbdfdff5fd aa 2021-03-04 14:51:29 +08:00
苏尹岚
caebd3921f aa 2021-03-04 14:16:58 +08:00
苏尹岚
2a3b838dca aa 2021-03-04 14:07:41 +08:00
苏尹岚
8c3706ce86 Merge remote-tracking branch 'origin/mark' 2021-03-04 11:34:50 +08:00
苏尹岚
54937a1e7b aa 2021-03-04 11:34:12 +08:00
苏尹岚
c4650f5181 aa 2021-03-04 10:34:59 +08:00
苏尹岚
72d839e049 aa 2021-03-04 09:57:51 +08:00
苏尹岚
262db00540 aa 2021-03-04 09:55:36 +08:00
苏尹岚
399ddf968a aa 2021-03-04 09:35:24 +08:00
苏尹岚
61b9933717 aa 2021-03-04 09:10:55 +08:00
苏尹岚
a1b17255d2 aa 2021-03-03 18:01:01 +08:00
苏尹岚
d43b57660a aa 2021-03-03 17:59:36 +08:00
苏尹岚
8547d2d3b0 aa 2021-03-03 17:31:17 +08:00
苏尹岚
59a4c6c59c aa 2021-03-03 14:42:48 +08:00
苏尹岚
7fa0849e17 aa 2021-03-03 14:11:22 +08:00
苏尹岚
f37545de19 Merge remote-tracking branch 'origin/mark' into jdshop 2021-03-02 16:57:32 +08:00
苏尹岚
cfb02afc84 aa 2021-03-02 16:42:59 +08:00
苏尹岚
67f1333ef3 aa 2021-03-02 16:18:58 +08:00
苏尹岚
52a3d02eac aa 2021-03-02 13:52:59 +08:00
苏尹岚
5cb65919c2 aa 2021-03-02 11:37:07 +08:00
苏尹岚
1f82ec3f0d aa 2021-03-02 11:31:29 +08:00
苏尹岚
0ce761b01e aa 2021-03-02 11:22:58 +08:00
苏尹岚
c68e02b409 aa 2021-03-02 11:09:19 +08:00
苏尹岚
b959fd60dc aa 2021-03-02 10:55:36 +08:00
苏尹岚
dba862d6cd mtwm 定期刷token 2021-03-02 10:54:01 +08:00
苏尹岚
9ed0b9e3b3 aa 2021-03-02 10:43:47 +08:00
苏尹岚
29ab696bfd aa 2021-03-02 10:38:09 +08:00
苏尹岚
dd1e4d9c23 aa 2021-03-02 09:24:40 +08:00
苏尹岚
603b27eb38 aa 2021-03-02 09:12:04 +08:00
苏尹岚
3243044438 aa 2021-03-01 17:18:45 +08:00
苏尹岚
38f5f58644 aa 2021-03-01 17:15:45 +08:00
苏尹岚
af45ed3350 aa 2021-03-01 17:10:38 +08:00
苏尹岚
ada5d570b8 aa 2021-03-01 16:16:40 +08:00
苏尹岚
78898fb39f aa 2021-03-01 16:09:44 +08:00
苏尹岚
5c856aaf12 aa 2021-03-01 15:59:26 +08:00
苏尹岚
c6ad936d08 aa 2021-03-01 15:55:53 +08:00
苏尹岚
b9c6eced38 aa 2021-03-01 15:54:42 +08:00
苏尹岚
0409aed138 aa 2021-03-01 15:53:53 +08:00
苏尹岚
c55e09e208 aa 2021-03-01 15:51:44 +08:00
苏尹岚
b4141f448e aa 2021-03-01 15:32:01 +08:00
苏尹岚
fb342d813b aa 2021-03-01 15:27:45 +08:00
苏尹岚
96dfa7ad5a aa 2021-03-01 15:10:18 +08:00
苏尹岚
423e0de993 aa 2021-03-01 14:44:48 +08:00
苏尹岚
30c5645eef aa 2021-03-01 14:31:53 +08:00
苏尹岚
5ff8a1b7c3 aa 2021-03-01 14:11:11 +08:00
苏尹岚
774a265c69 aa 2021-03-01 14:07:50 +08:00
苏尹岚
92d2509b3c aa 2021-03-01 13:57:00 +08:00
苏尹岚
fab9303d60 aa 2021-03-01 13:55:59 +08:00
苏尹岚
6c004f8c87 aa 2021-03-01 13:53:02 +08:00
苏尹岚
161238482b aa 2021-03-01 13:36:20 +08:00
苏尹岚
9a9edbd117 aa 2021-03-01 11:47:57 +08:00
苏尹岚
7996e34d5d aa 2021-03-01 11:32:36 +08:00
苏尹岚
46278ea30f aa 2021-03-01 09:22:34 +08:00
苏尹岚
63ad2f9f07 Merge branch 'mark' of e.coding.net:rosydev/jx-callback into mark 2021-03-01 09:12:18 +08:00
苏尹岚
7d2c15935b aa 2021-03-01 09:12:05 +08:00
suyl
2060998750 aa 2021-02-26 21:26:28 +08:00
suyl
5ce6668185 aa 2021-02-26 21:08:48 +08:00
苏尹岚
e57beb588a aa 2021-02-26 15:10:30 +08:00
苏尹岚
8f1d5a72d4 aa 2021-02-25 17:18:09 +08:00
苏尹岚
966a11f394 aa 2021-02-25 15:12:08 +08:00
苏尹岚
8d3d64e381 aa 2021-02-25 10:52:22 +08:00
苏尹岚
7f55d919f0 aa 2021-02-25 10:51:23 +08:00
苏尹岚
578ba51f9a aa 2021-02-25 10:21:13 +08:00
苏尹岚
3653a85af2 aa 2021-02-24 18:11:25 +08:00
苏尹岚
5ae8814ef4 aa 2021-02-24 18:10:11 +08:00
苏尹岚
1ab1992917 a 2021-02-24 17:08:01 +08:00
苏尹岚
6b77f9f7c9 aa 2021-02-24 17:01:19 +08:00
苏尹岚
70a95b1de0 aa 2021-02-24 16:52:30 +08:00
苏尹岚
605a5b1d48 a 2021-02-24 16:50:51 +08:00
苏尹岚
655300e414 aa 2021-02-24 16:39:23 +08:00
苏尹岚
aeeb13cd0b aa 2021-02-24 15:20:50 +08:00
苏尹岚
04839ce472 aa 2021-02-24 11:53:26 +08:00
苏尹岚
3d240a86a1 aa 2021-02-24 11:19:31 +08:00
苏尹岚
d123824cab aa 2021-02-24 11:03:34 +08:00
苏尹岚
47d4a11d1d aa 2021-02-24 10:06:55 +08:00
苏尹岚
3858c45274 aa 2021-02-24 09:48:33 +08:00
苏尹岚
7d7ed04f75 aa 2021-02-23 16:20:40 +08:00
苏尹岚
d2c1998dc8 aa 2021-02-23 16:06:15 +08:00
苏尹岚
cdbb242778 aa 2021-02-23 14:22:22 +08:00
苏尹岚
58f4d1ab56 aa 2021-02-23 10:37:16 +08:00
苏尹岚
78ae4f073d aa 2021-02-23 10:28:20 +08:00
苏尹岚
7f94d2b48f aa 2021-02-23 10:16:36 +08:00
苏尹岚
eb93724cbb aa 2021-02-23 09:07:41 +08:00
苏尹岚
24262256e5 aa 2021-02-22 15:07:06 +08:00
苏尹岚
2773d5c9ca aa 2021-02-22 14:52:10 +08:00
苏尹岚
a0f9e96fcb aa 2021-02-22 14:17:58 +08:00
苏尹岚
38f703c482 aa 2021-02-22 11:09:43 +08:00
苏尹岚
c05c826cd9 aa 2021-02-22 11:03:13 +08:00
苏尹岚
4b7ed6ead5 aa 2021-02-22 10:48:42 +08:00
苏尹岚
2b32adefcb aa 2021-02-22 10:27:51 +08:00
苏尹岚
933f9ab820 aa 2021-02-22 09:53:04 +08:00
苏尹岚
dc420cdf3a aa 2021-02-20 16:24:47 +08:00
苏尹岚
a0de3392bc aa 2021-02-20 11:08:43 +08:00
苏尹岚
e465c58262 aa 2021-02-20 10:39:16 +08:00
苏尹岚
833df033c5 aa 2021-02-20 10:35:58 +08:00
苏尹岚
47f26f8f97 aa 2021-02-20 10:17:04 +08:00
苏尹岚
e3397e581a aa 2021-02-20 10:16:44 +08:00
苏尹岚
6f98ae9300 aa 2021-02-20 10:15:47 +08:00
苏尹岚
6833df3548 aa 2021-02-19 17:04:34 +08:00
苏尹岚
63fea2e47b aa 2021-02-19 16:59:13 +08:00
苏尹岚
1b3bcb8b6a aa 2021-02-19 16:50:40 +08:00
苏尹岚
ea374f228a aa 2021-02-19 11:58:43 +08:00
苏尹岚
a2c7eecdca aa 2021-02-19 11:32:20 +08:00
苏尹岚
9c6e6e6c65 aa 2021-02-19 09:35:19 +08:00
苏尹岚
20015b8463 aa 2021-02-19 09:34:08 +08:00
苏尹岚
f4ef3b2433 aa 2021-02-19 08:55:58 +08:00
苏尹岚
f69aca7c08 aa 2021-02-18 18:40:32 +08:00
苏尹岚
86f6319077 aa 2021-02-18 16:05:51 +08:00
苏尹岚
e5bd36ded0 aa 2021-02-18 15:45:06 +08:00
苏尹岚
85f9ac8dd4 aa 2021-02-18 14:04:00 +08:00
苏尹岚
66194c55f9 aa 2021-02-18 11:55:47 +08:00
苏尹岚
0ca51ca7fc 分类名重复不允许创建 2021-02-18 09:57:47 +08:00
苏尹岚
a4fae9be65 aa 2021-02-08 17:45:16 +08:00
苏尹岚
fd2967faae aa 2021-02-08 17:44:58 +08:00
苏尹岚
feeb4ee6e9 aa 2021-02-08 17:34:55 +08:00
苏尹岚
e2842c75be aa 2021-02-08 17:26:54 +08:00
苏尹岚
0aabc8ad2c aa 2021-02-08 17:22:47 +08:00
苏尹岚
b7852f2f65 aa 2021-02-08 17:21:08 +08:00
苏尹岚
fdfc3ab5d1 aa 2021-02-08 17:14:54 +08:00
苏尹岚
1f521cf6bc aa 2021-02-08 17:08:36 +08:00
苏尹岚
a8529b69d9 aa 2021-02-08 16:58:26 +08:00
苏尹岚
c96dd64fd8 aa 2021-02-08 16:47:12 +08:00
苏尹岚
0373cebca9 aa 2021-02-08 16:23:20 +08:00
苏尹岚
3afec3e583 aa 2021-02-08 16:21:25 +08:00
苏尹岚
80eea88ccb aa 2021-02-08 16:15:44 +08:00
苏尹岚
c62d18d03b aa 2021-02-08 16:11:24 +08:00
苏尹岚
dd0189c1df aa 2021-02-08 15:59:12 +08:00
苏尹岚
0242da3d45 aa 2021-02-08 15:48:10 +08:00
苏尹岚
1f3de84373 aa 2021-02-08 15:36:22 +08:00
苏尹岚
03dd2c45d0 aa 2021-02-08 15:24:30 +08:00
苏尹岚
6004f8937b aa 2021-02-08 15:08:41 +08:00
苏尹岚
3c93e0b692 aa 2021-02-08 14:47:43 +08:00
苏尹岚
180ddce248 aa 2021-02-08 14:41:10 +08:00
苏尹岚
a768a893eb aa 2021-02-08 14:36:35 +08:00
苏尹岚
d115bf7faf aa 2021-02-08 14:33:50 +08:00
苏尹岚
3bf0a49837 aa 2021-02-08 14:28:37 +08:00
苏尹岚
530ea46779 aa 2021-02-08 14:14:35 +08:00
苏尹岚
48e4f5962a aa 2021-02-08 14:04:50 +08:00
苏尹岚
0c90169369 aa 2021-02-08 13:58:38 +08:00
苏尹岚
f118613de8 aa 2021-02-08 13:56:12 +08:00
苏尹岚
61d7a50ca6 aa 2021-02-08 13:39:29 +08:00
苏尹岚
35103bd225 aa 2021-02-08 10:56:59 +08:00
苏尹岚
05f2b04969 aa 2021-02-08 10:50:04 +08:00
苏尹岚
a1c5ded380 aa 2021-02-08 10:49:38 +08:00
苏尹岚
9e92a9a0e0 aa 2021-02-08 10:47:40 +08:00
苏尹岚
fd6efea36e aa 2021-02-08 09:27:53 +08:00
苏尹岚
e6d3190691 aa 2021-02-08 09:16:14 +08:00
苏尹岚
8ae64f3f18 aa 2021-02-08 09:02:19 +08:00
苏尹岚
725458dfe3 aa 2021-02-08 08:58:44 +08:00
苏尹岚
ba41fe49a7 aa 2021-02-07 16:32:45 +08:00
苏尹岚
a614ae4e1a aa 2021-02-07 16:25:15 +08:00
苏尹岚
d87bc9da58 aa 2021-02-07 16:09:06 +08:00
苏尹岚
f4af57cb08 美团商超 2021-02-07 15:44:59 +08:00
苏尹岚
306c0eefff aa 2021-02-07 14:17:09 +08:00
苏尹岚
5fd18dea64 aa 2021-02-07 13:48:17 +08:00
苏尹岚
473ba9bf5e aa 2021-02-07 13:40:11 +08:00
苏尹岚
813e8c6e0c aa 2021-02-07 12:12:28 +08:00
苏尹岚
c7a751933b aa 2021-02-07 11:42:32 +08:00
苏尹岚
7280e99863 aa 2021-02-07 11:18:49 +08:00
苏尹岚
0a5d3dc32c a 2021-02-07 11:15:19 +08:00
苏尹岚
feebefb10d aa 2021-02-07 10:45:46 +08:00
苏尹岚
d7a04e164e aa 2021-02-07 09:53:47 +08:00
苏尹岚
2a5a123447 aa 2021-02-05 17:16:03 +08:00
苏尹岚
0e7ad07e19 aa 2021-02-05 16:43:38 +08:00
苏尹岚
fb7ad6b750 a 2021-02-05 16:33:46 +08:00
苏尹岚
a388b89225 aa 2021-02-05 16:03:34 +08:00
苏尹岚
c566c4b16e aa 2021-02-05 16:01:14 +08:00
苏尹岚
1c918447d2 aa 2021-02-05 15:54:18 +08:00
苏尹岚
921c50ac4d aa 2021-02-05 15:32:18 +08:00
苏尹岚
45c879a7ac aa 2021-02-05 15:23:01 +08:00
苏尹岚
42a264f1f0 aa 2021-02-05 15:18:42 +08:00
苏尹岚
90162a6c4f aa 2021-02-05 15:11:35 +08:00
苏尹岚
2709883f7f aa 2021-02-05 14:18:03 +08:00
苏尹岚
8c4fb7871f aa 2021-02-05 14:13:11 +08:00
苏尹岚
87d43927bc aa 2021-02-05 14:03:05 +08:00
苏尹岚
485330a942 aa 2021-02-05 10:58:21 +08:00
苏尹岚
9b1ccb8ec8 aa 2021-02-05 10:28:50 +08:00
苏尹岚
5a45cf3c9a aa 2021-02-05 09:24:52 +08:00
苏尹岚
c8ab2b2310 aa 2021-02-04 17:57:05 +08:00
苏尹岚
5b45811b8d aa 2021-02-04 17:17:57 +08:00
苏尹岚
10a1a1ba5c aa 2021-02-04 15:55:55 +08:00
苏尹岚
e7a0c3cd27 aa 2021-02-04 15:47:26 +08:00
苏尹岚
09e751b83a aa 2021-02-04 15:22:31 +08:00
苏尹岚
6856ef2f8f aa 2021-02-04 10:08:02 +08:00
苏尹岚
fd3b8a2141 aa 2021-02-04 08:56:03 +08:00
苏尹岚
9cad16bbaf aa 2021-02-03 18:45:01 +08:00
苏尹岚
f14fc933dd aa 2021-02-03 16:14:15 +08:00
苏尹岚
58411e0d85 aa 2021-02-03 16:09:31 +08:00
苏尹岚
5129eb9c64 aa 2021-02-03 15:26:37 +08:00
苏尹岚
e540736c59 aa 2021-02-03 15:21:15 +08:00
苏尹岚
a45d4a7464 aa 2021-02-03 15:14:16 +08:00
苏尹岚
b61d7f56c9 aa 2021-02-03 14:56:23 +08:00
苏尹岚
689e7b4bc7 aa 2021-02-03 14:36:11 +08:00
苏尹岚
fb55dd6a85 aa 2021-02-03 11:18:30 +08:00
苏尹岚
1c9f710b45 aa 2021-02-03 10:30:50 +08:00
苏尹岚
aaadd0ed32 aa 2021-02-03 10:15:53 +08:00
苏尹岚
bde302b557 aa 2021-02-02 16:51:40 +08:00
苏尹岚
32d08027bf aa 2021-02-02 16:23:19 +08:00
苏尹岚
f992f6a0e2 aa 2021-02-02 16:11:49 +08:00
苏尹岚
51c096dae4 aa 2021-02-02 16:04:05 +08:00
苏尹岚
5972883978 aa 2021-02-02 16:01:22 +08:00
苏尹岚
3260643e63 aa 2021-02-02 14:05:40 +08:00
苏尹岚
00766183c7 aa 2021-02-02 11:41:17 +08:00
苏尹岚
faef2c35a8 aa 2021-02-01 17:26:06 +08:00
苏尹岚
b222dfe962 aa 2021-02-01 15:37:58 +08:00
苏尹岚
e37bc83bbb aa 2021-02-01 15:12:08 +08:00
苏尹岚
8cc0b3f0da aa 2021-02-01 15:05:30 +08:00
苏尹岚
70ecd6910b aa 2021-02-01 15:02:18 +08:00
苏尹岚
555eb6670f aa 2021-02-01 14:57:05 +08:00
苏尹岚
f2be9f0248 aa 2021-02-01 14:44:32 +08:00
苏尹岚
786a0cadf4 aa 2021-02-01 14:40:33 +08:00
苏尹岚
2e59fe535b Merge branch 'jdshop' of e.coding.net:rosydev/jx-callback into jdshop 2021-02-01 13:39:47 +08:00
苏尹岚
21c7f69ac8 aa 2021-02-01 13:39:05 +08:00
suyl
b3d6017cf1 aa 2021-01-29 19:39:22 +08:00
苏尹岚
353670289a aa 2021-01-29 14:42:41 +08:00
苏尹岚
0e5c9e80bb aa 2021-01-29 14:38:11 +08:00
苏尹岚
b345f1a245 aa 2021-01-29 14:25:29 +08:00
苏尹岚
dffdbd2818 aa 2021-01-29 14:13:05 +08:00
苏尹岚
7f25da11a1 aa 2021-01-28 17:28:01 +08:00
苏尹岚
252a5d1b33 aa 2021-01-28 16:49:23 +08:00
苏尹岚
4724d57094 aa 2021-01-28 16:45:56 +08:00
苏尹岚
61f32cb7a6 aa 2021-01-28 15:33:13 +08:00
苏尹岚
d46ced3522 aa 2021-01-28 15:32:08 +08:00
苏尹岚
418bcbca29 aa 2021-01-28 14:47:35 +08:00
苏尹岚
be029c8f2d aa 2021-01-28 14:44:16 +08:00
苏尹岚
8418420928 aa 2021-01-28 14:32:59 +08:00
苏尹岚
a236f0b14c aa 2021-01-28 14:27:37 +08:00
苏尹岚
de0cc168f3 aa 2021-01-28 14:16:53 +08:00
苏尹岚
8f2970c504 aa 2021-01-28 13:58:45 +08:00
苏尹岚
e2760bb7a4 aa 2021-01-28 11:16:42 +08:00
苏尹岚
8f32bb232e aa 2021-01-28 10:53:24 +08:00
苏尹岚
5d6ff365c1 aa 2021-01-28 10:28:38 +08:00
苏尹岚
a32c7f7983 aa 2021-01-28 10:01:13 +08:00
苏尹岚
599749191b aa 2021-01-28 09:52:18 +08:00
苏尹岚
7deeb4fbd3 aa 2021-01-28 09:41:37 +08:00
苏尹岚
90ac1df1cf aa 2021-01-28 09:17:10 +08:00
苏尹岚
a24754705a aa 2021-01-28 09:16:54 +08:00
苏尹岚
61c44e257d aa 2021-01-27 17:58:50 +08:00
苏尹岚
812ad592f2 aa 2021-01-27 17:57:20 +08:00
苏尹岚
e38ab5104d aa 2021-01-27 15:26:01 +08:00
苏尹岚
7772f9bfaa aa 2021-01-27 15:19:08 +08:00
苏尹岚
60172d7c6b aa 2021-01-27 15:00:53 +08:00
苏尹岚
d2955b2a2f aa 2021-01-27 14:15:35 +08:00
苏尹岚
8ec5e4f3c6 aa 2021-01-27 13:36:59 +08:00
苏尹岚
24bdadb1db aa 2021-01-27 10:24:55 +08:00
苏尹岚
e6a2040027 aa 2021-01-27 09:40:40 +08:00
苏尹岚
0226aab316 aa 2021-01-27 09:40:00 +08:00
苏尹岚
720c23a67e aa 2021-01-26 18:10:48 +08:00
苏尹岚
9ab6b39845 aa 2021-01-26 17:45:23 +08:00
苏尹岚
616f0f6466 aa 2021-01-26 17:38:21 +08:00
苏尹岚
00770ef83f aa 2021-01-26 17:35:28 +08:00
苏尹岚
c8ab4ad146 aa 2021-01-26 16:30:07 +08:00
苏尹岚
fb6aae1930 aa 2021-01-26 16:15:49 +08:00
苏尹岚
30435d4ae4 aa 2021-01-26 15:55:00 +08:00
苏尹岚
3e0707e6a7 aa 2021-01-26 15:51:01 +08:00
苏尹岚
f9b2959e58 aa 2021-01-26 15:34:42 +08:00
苏尹岚
f423d0944e aa 2021-01-26 15:24:20 +08:00
苏尹岚
f6d1d0a272 aa 2021-01-26 15:11:14 +08:00
苏尹岚
5f4d0bcad3 aa 2021-01-26 15:05:52 +08:00
苏尹岚
6238b359d7 aa 2021-01-26 15:03:33 +08:00
苏尹岚
ffbc982804 aa 2021-01-26 14:23:08 +08:00
苏尹岚
52846991ba aa 2021-01-26 14:18:19 +08:00
苏尹岚
799bcb0aa3 aa 2021-01-26 11:36:01 +08:00
苏尹岚
c9c803cf45 aa 2021-01-26 11:19:09 +08:00
苏尹岚
dacd55f239 aa 2021-01-26 11:14:10 +08:00
苏尹岚
af15ad0e84 aa 2021-01-26 11:05:56 +08:00
苏尹岚
7aace1bf49 aa 2021-01-26 10:39:01 +08:00
苏尹岚
a54b831eec aa 2021-01-26 10:30:17 +08:00
苏尹岚
44e9eea0f1 aa 2021-01-26 10:26:04 +08:00
苏尹岚
a1fce2b48c aa 2021-01-26 10:12:56 +08:00
苏尹岚
df6b94ac39 aa 2021-01-26 09:04:35 +08:00
苏尹岚
575b80d890 aa 2021-01-26 08:59:14 +08:00
苏尹岚
7788b79993 aa 2021-01-25 15:19:45 +08:00
苏尹岚
e169216f08 aa 2021-01-25 10:42:04 +08:00
苏尹岚
a7f43036c7 aa 2021-01-22 18:25:06 +08:00
苏尹岚
ed8368c565 aa 2021-01-22 17:03:08 +08:00
苏尹岚
b12669e584 aa 2021-01-22 17:00:14 +08:00
苏尹岚
61d01e7cbf aa 2021-01-22 16:59:13 +08:00
苏尹岚
6462231c86 aa 2021-01-22 16:50:08 +08:00
苏尹岚
c793fc5a04 aa 2021-01-22 14:59:12 +08:00
苏尹岚
7109baf227 aa 2021-01-22 11:25:52 +08:00
苏尹岚
ac880a4387 aa 2021-01-22 11:17:32 +08:00
苏尹岚
6d9a3d2313 aa 2021-01-22 11:10:58 +08:00
苏尹岚
2dcbd1f8fb aa 2021-01-22 11:02:01 +08:00
苏尹岚
e758d5d8ed aa 2021-01-22 10:59:01 +08:00
苏尹岚
7bc90281ff aa 2021-01-22 10:56:01 +08:00
苏尹岚
a7ea8301b1 aa 2021-01-22 10:43:44 +08:00
苏尹岚
370b606575 aa 2021-01-22 10:36:18 +08:00
苏尹岚
618ae4c251 aa 2021-01-22 10:31:41 +08:00
苏尹岚
a1ddba2c78 aa 2021-01-22 10:28:35 +08:00
苏尹岚
1dc91f6f9a aa 2021-01-22 10:23:19 +08:00
苏尹岚
f9f636499d aa 2021-01-22 10:17:38 +08:00
苏尹岚
80d11533c1 aa 2021-01-22 10:14:16 +08:00
苏尹岚
56d9e1b643 aa 2021-01-22 10:07:54 +08:00
苏尹岚
519592bc31 aa 2021-01-22 10:05:43 +08:00
苏尹岚
f85fa9e12a aa 2021-01-21 17:49:59 +08:00
苏尹岚
ada312f753 aa 2021-01-21 17:37:12 +08:00
苏尹岚
5eeb60c12d aa 2021-01-21 17:13:33 +08:00
苏尹岚
091e46b74d aa 2021-01-21 17:01:21 +08:00
苏尹岚
1ed3146ed4 aa 2021-01-21 16:57:54 +08:00
苏尹岚
0eb5c39953 aa 2021-01-21 16:53:51 +08:00
苏尹岚
3f1045530d aa 2021-01-21 10:14:25 +08:00
苏尹岚
f3f091f6ab aa 2021-01-21 09:09:13 +08:00
苏尹岚
a32f1d098d aa 2021-01-20 18:18:39 +08:00
苏尹岚
17dc583f86 aa 2021-01-20 14:46:56 +08:00
苏尹岚
142990b2a6 aaa 2021-01-20 14:25:46 +08:00
苏尹岚
15068e28f9 aaa 2021-01-20 14:23:41 +08:00
苏尹岚
cb8a31a1da aaa 2021-01-20 14:21:17 +08:00
苏尹岚
c9f9c435d5 aaa 2021-01-20 14:18:15 +08:00
苏尹岚
4f61f1f35d aaa 2021-01-20 14:06:40 +08:00
苏尹岚
ca415b259f aa 2021-01-20 13:51:03 +08:00
苏尹岚
5859c589da aa 2021-01-20 13:47:56 +08:00
苏尹岚
729cce6695 aa 2021-01-20 13:46:33 +08:00
苏尹岚
724123aff7 aa 2021-01-20 11:13:37 +08:00
苏尹岚
d27aa2c7ef aa 2021-01-20 10:32:53 +08:00
苏尹岚
b87c390946 aa 2021-01-20 10:04:09 +08:00
苏尹岚
84c98fa1a8 aa 2021-01-20 09:18:34 +08:00
苏尹岚
3ea7abddfa aa 2021-01-19 16:55:24 +08:00
苏尹岚
e149095088 aa 2021-01-19 14:50:25 +08:00
苏尹岚
c2417f70aa aa 2021-01-19 14:21:20 +08:00
苏尹岚
bb2fab1ef3 aa 2021-01-19 09:12:38 +08:00
苏尹岚
e902e02fc3 aa 2021-01-18 11:06:30 +08:00
苏尹岚
bf5aaf2a1c 邮件太多了 2021-01-18 09:44:10 +08:00
苏尹岚
c3319e4201 aa 2021-01-15 14:37:23 +08:00
苏尹岚
74b902e4e5 aa 2021-01-15 09:38:58 +08:00
苏尹岚
f3dd1b3e1e aa 2021-01-14 10:31:27 +08:00
苏尹岚
af4b21ebb7 aa 2021-01-14 10:19:56 +08:00
苏尹岚
d3d3fe0a7c aa 2021-01-14 09:20:29 +08:00
苏尹岚
e488701115 aa 2021-01-14 09:13:23 +08:00
苏尹岚
28c5f80d86 aa 2021-01-13 11:42:47 +08:00
苏尹岚
40c7d367aa aa 2021-01-13 09:54:59 +08:00
苏尹岚
d407d2a35d aa 2021-01-13 09:52:31 +08:00
苏尹岚
1e30bd93f7 aa 2021-01-13 09:32:00 +08:00
苏尹岚
ebeb53a92b aa 2021-01-12 16:52:19 +08:00
苏尹岚
e7e372ea72 aa 2021-01-12 15:39:35 +08:00
苏尹岚
389864e413 aa 2021-01-12 15:35:23 +08:00
苏尹岚
e48d74601f refreshcoupon 2021-01-12 15:23:19 +08:00
苏尹岚
78d54c54ad shangchao 2021-01-12 14:05:42 +08:00
苏尹岚
b769147e2c aa 2021-01-12 11:37:29 +08:00
苏尹岚
6ae3ee1246 aa 2021-01-11 18:08:29 +08:00
苏尹岚
a3c9346792 aa 2021-01-11 18:08:09 +08:00
苏尹岚
e5133e7006 aa 2021-01-11 18:06:50 +08:00
苏尹岚
0c6ff86deb aa 2021-01-11 16:44:31 +08:00
苏尹岚
56caf8380f update 2021-01-11 15:48:30 +08:00
苏尹岚
b206fa3cc7 aa 2021-01-11 15:45:11 +08:00
苏尹岚
c5b66cfd3b aa 2021-01-11 15:36:41 +08:00
苏尹岚
c8c1746e83 aa 2021-01-11 15:23:56 +08:00
苏尹岚
b1a3820fb1 aa 2021-01-11 15:20:51 +08:00
苏尹岚
154791b487 aa 2021-01-11 15:12:49 +08:00
苏尹岚
6bbc45ad59 aa 2021-01-11 15:10:44 +08:00
苏尹岚
1050f5501f aa 2021-01-11 15:07:54 +08:00
苏尹岚
ccd8ed553f aa 2021-01-11 14:52:53 +08:00
苏尹岚
7ce41f81d3 aa 2021-01-11 14:48:31 +08:00
苏尹岚
431cde11bd aa 2021-01-11 14:25:32 +08:00
苏尹岚
bee46dd1d5 aa 2021-01-11 11:35:54 +08:00
苏尹岚
b161e4d55a aa 2021-01-08 18:09:01 +08:00
苏尹岚
9f73470bc2 aa 2021-01-08 16:58:04 +08:00
苏尹岚
6cd1be12b8 aa 2021-01-08 14:48:08 +08:00
苏尹岚
32fd6ac88a aa 2021-01-08 11:47:27 +08:00
苏尹岚
607e2f4396 aa 2021-01-08 11:45:30 +08:00
苏尹岚
16bb40b4a7 aa 2021-01-07 17:51:55 +08:00
苏尹岚
25bb55f92d aa 2021-01-07 17:38:47 +08:00
苏尹岚
5f801aa354 aa 2021-01-07 17:35:46 +08:00
苏尹岚
60e3f1ad91 aa 2021-01-07 16:39:57 +08:00
苏尹岚
f161adef8c aa 2021-01-07 16:29:11 +08:00
苏尹岚
f20b816f56 aa 2021-01-07 14:28:56 +08:00
苏尹岚
ccf6902020 aa 2021-01-07 13:59:16 +08:00
苏尹岚
a90fb644e2 aa 2021-01-07 13:55:09 +08:00
苏尹岚
3745e741c4 aa 2021-01-07 13:44:56 +08:00
苏尹岚
2c2645d4b2 aa 2021-01-07 11:34:40 +08:00
苏尹岚
e3266af9ae aa 2021-01-07 10:36:34 +08:00
苏尹岚
3f331702fd a 2021-01-07 10:33:16 +08:00
苏尹岚
abd0fcdf4a aa 2021-01-06 18:18:17 +08:00
苏尹岚
e8cfe85be5 aaa 2021-01-06 17:37:54 +08:00
苏尹岚
e3145683c7 aa 2021-01-06 16:15:03 +08:00
苏尹岚
820c1c3043 aa 2021-01-06 16:12:04 +08:00
苏尹岚
0f177a14f7 aa 2021-01-06 16:02:29 +08:00
苏尹岚
c8827012b0 aa 2021-01-06 15:58:06 +08:00
苏尹岚
453c6a2e9c aa 2021-01-06 15:38:29 +08:00
苏尹岚
acdb733286 aa 2021-01-06 15:35:11 +08:00
苏尹岚
d32a52c36e aa 2021-01-06 15:32:32 +08:00
苏尹岚
57cb826087 aa 2021-01-06 15:23:08 +08:00
苏尹岚
dbe86ab33b aa 2021-01-06 11:36:11 +08:00
苏尹岚
3b095fef4f aa 2021-01-06 10:08:55 +08:00
苏尹岚
64fb854ab7 aa 2021-01-06 09:57:32 +08:00
苏尹岚
5753470c8b aa 2021-01-06 09:46:54 +08:00
苏尹岚
756b39711d aa 2021-01-05 17:44:19 +08:00
苏尹岚
dd9306927d aa 2021-01-05 16:56:19 +08:00
苏尹岚
baef83d986 aa 2021-01-05 16:43:04 +08:00
苏尹岚
ac6e76a4a6 aa 2021-01-05 16:09:23 +08:00
苏尹岚
41d3c9cdd2 aa 2021-01-04 09:23:56 +08:00
苏尹岚
c8af8bd800 aa 2020-12-31 09:02:40 +08:00
苏尹岚
d8c8a36063 aa 2020-12-31 08:59:20 +08:00
苏尹岚
8720663f07 aa 2020-12-30 19:05:16 +08:00
苏尹岚
91bcf6a09b Merge branch 'jdshop' of e.coding.net:rosydev/jx-callback into jdshop 2020-12-30 19:02:05 +08:00
苏尹岚
3ba96cfe70 aa 2020-12-30 19:01:42 +08:00
suyl
d33a2cddae a 2020-12-29 19:42:01 +08:00
苏尹岚
290dc66f8f aa 2020-12-29 17:51:37 +08:00
苏尹岚
05140d0baf aa 2020-12-29 16:45:20 +08:00
苏尹岚
036e25cae1 aa 2020-12-29 14:20:36 +08:00
苏尹岚
c3c45852b4 aa 2020-12-29 11:16:50 +08:00
苏尹岚
2b275a7b03 aa 2020-12-29 10:05:23 +08:00
苏尹岚
b36f3623f2 a 2020-12-28 17:33:26 +08:00
苏尹岚
ee0bc23493 a 2020-12-28 17:30:02 +08:00
苏尹岚
0f96fb73d0 aa 2020-12-28 14:55:21 +08:00
苏尹岚
dda1a1fc5d a 2020-12-28 10:39:00 +08:00
苏尹岚
15601e9d58 aa 2020-12-28 10:23:32 +08:00
苏尹岚
3458c82cea aa 2020-12-24 16:44:15 +08:00
苏尹岚
e639e0e666 aa 2020-12-24 10:08:39 +08:00
苏尹岚
ada4e33bbe mt 2020-12-24 09:39:57 +08:00
苏尹岚
bdfc2db33b aa 2020-12-24 09:32:45 +08:00
苏尹岚
b96804792c aa 2020-12-23 18:21:39 +08:00
苏尹岚
3b277d8753 test 2020-12-23 18:14:59 +08:00
苏尹岚
01c87b3415 aa 2020-12-23 09:31:11 +08:00
苏尹岚
09494dc4e5 aa 2020-12-22 17:58:58 +08:00
苏尹岚
e73225be50 err 2020-12-22 15:57:04 +08:00
苏尹岚
57a9d70c74 aa 2020-12-22 11:16:43 +08:00
苏尹岚
341004583d aa 2020-12-22 09:41:56 +08:00
苏尹岚
289134241b aa 2020-12-22 09:13:30 +08:00
苏尹岚
540d18acbc aa 2020-12-21 18:19:35 +08:00
苏尹岚
10a6db8ce6 aa 2020-12-21 16:43:59 +08:00
苏尹岚
0e70ef5c83 aa 2020-12-21 14:58:22 +08:00
苏尹岚
292c490bd8 aa 2020-12-21 14:50:37 +08:00
苏尹岚
bc8baccc47 aa 2020-12-21 14:46:53 +08:00
苏尹岚
87b691f8ec aa 2020-12-21 14:43:38 +08:00
苏尹岚
3ab74d5a65 aa 2020-12-21 14:34:03 +08:00
苏尹岚
bb9a7b45d1 aa 2020-12-21 14:28:27 +08:00
苏尹岚
dfe1e719ef a 2020-12-21 13:39:30 +08:00
苏尹岚
753cefe861 fnps 2020-12-21 11:55:12 +08:00
苏尹岚
61deea342c fnps 2020-12-21 11:54:49 +08:00
苏尹岚
1865f1d3fc a 2020-12-21 11:43:50 +08:00
苏尹岚
48defb7af3 aa 2020-12-21 10:54:57 +08:00
苏尹岚
bf6c68930c fnps 2020-12-21 10:42:37 +08:00
苏尹岚
0b5b3884cf a 2020-12-18 17:04:30 +08:00
苏尹岚
a798baac79 jiandian 2020-12-18 11:48:53 +08:00
苏尹岚
699c1a358b jiandian 2020-12-18 11:46:44 +08:00
苏尹岚
944cc6f562 jiandian 2020-12-18 11:43:34 +08:00
苏尹岚
7b27513b76 jiandian 2020-12-18 11:40:27 +08:00
苏尹岚
f4debfde7b a 2020-12-18 10:40:38 +08:00
苏尹岚
e515a2a446 a 2020-12-18 10:00:06 +08:00
苏尹岚
482c2271f0 a 2020-12-18 09:58:39 +08:00
苏尹岚
2ea40c3025 a 2020-12-18 09:57:34 +08:00
苏尹岚
9cae5e65ad aa 2020-12-14 17:59:13 +08:00
苏尹岚
a597d160ee fnps 2020-12-14 16:58:28 +08:00
苏尹岚
6148eb0cae a 2020-12-14 15:42:14 +08:00
苏尹岚
1c21251eee a 2020-12-14 15:32:48 +08:00
苏尹岚
21fbb8e075 aa 2020-12-14 15:28:00 +08:00
苏尹岚
0601ba4473 aa 2020-12-14 15:26:10 +08:00
苏尹岚
3a098b1a42 ss 2020-12-14 14:57:42 +08:00
苏尹岚
e9f7afa492 aa 2020-12-14 14:50:00 +08:00
苏尹岚
267186da39 aa 2020-12-14 13:40:43 +08:00
苏尹岚
b37e288834 aa 2020-12-14 10:13:17 +08:00
苏尹岚
770a773ebe fnps 2020-12-11 14:15:02 +08:00
苏尹岚
3240ece070 a 2020-12-11 10:54:04 +08:00
苏尹岚
b38b644543 aa 2020-12-11 10:39:13 +08:00
苏尹岚
e2b49ea137 aa 2020-12-11 10:35:44 +08:00
苏尹岚
d74f8c926c aa 2020-12-11 10:31:42 +08:00
苏尹岚
8bdb84d514 a 2020-12-11 10:12:07 +08:00
苏尹岚
16aa6cd3f6 a 2020-12-11 10:09:34 +08:00
苏尹岚
dbe5427946 aa 2020-12-11 09:33:54 +08:00
苏尹岚
2454ea7388 jd token 2020-12-11 09:05:11 +08:00
苏尹岚
2324534e2b jd token 2020-12-11 09:01:32 +08:00
苏尹岚
4d6332996e aa 2020-12-10 18:28:10 +08:00
苏尹岚
1756f1acb0 aa 2020-12-10 18:00:25 +08:00
苏尹岚
0e1142038b aa 2020-12-09 16:59:44 +08:00
苏尹岚
17938ffaa8 aa 2020-12-09 16:56:23 +08:00
苏尹岚
be59c52814 a 2020-12-09 16:44:04 +08:00
苏尹岚
047ab618e1 a 2020-12-09 13:40:37 +08:00
苏尹岚
76da2af43e a 2020-12-09 11:50:19 +08:00
苏尹岚
649d07dd87 fnps 2020-12-09 10:24:53 +08:00
苏尹岚
4c3d8d6dc7 a 2020-12-08 14:11:13 +08:00
苏尹岚
6b6d2d6b06 aa 2020-12-08 10:44:52 +08:00
苏尹岚
9ed6bb4974 fnps 2020-12-07 18:23:12 +08:00
苏尹岚
fe202ad41e test 2020-12-07 17:22:48 +08:00
苏尹岚
6d98bdd210 err 2020-12-07 11:57:24 +08:00
苏尹岚
836aa2f9df err 2020-12-07 11:51:33 +08:00
苏尹岚
b4f6d79665 err 2020-12-07 11:38:23 +08:00
苏尹岚
d29d94926c err 2020-12-07 11:26:11 +08:00
苏尹岚
8c30565ae8 err 2020-12-07 11:22:34 +08:00
苏尹岚
30b8c25003 aa 2020-12-07 09:56:34 +08:00
苏尹岚
82cc3572b3 延后1xiaoshi 2020-12-07 09:50:04 +08:00
苏尹岚
e939fa5ab2 aa 2020-12-04 16:28:17 +08:00
苏尹岚
c5587d0073 aa 2020-12-04 16:17:31 +08:00
苏尹岚
ea6a871246 aa 2020-12-04 14:25:22 +08:00
苏尹岚
dd5f3c38ac aa 2020-12-04 10:42:37 +08:00
苏尹岚
d986178f8d a 2020-12-04 10:19:10 +08:00
苏尹岚
fa9bc1fd16 jdshop 2020-12-04 10:07:07 +08:00
苏尹岚
7e92f3cf1a tt 2020-12-04 09:49:18 +08:00
苏尹岚
219d0fb9cb tt 2020-12-04 09:44:07 +08:00
苏尹岚
8a481f8225 tt 2020-12-04 09:37:14 +08:00
苏尹岚
6ae6d067b4 tt 2020-12-04 09:29:13 +08:00
苏尹岚
503d91b3fa store 2020-12-03 16:59:23 +08:00
苏尹岚
a73cba5328 a 2020-12-03 16:39:53 +08:00
苏尹岚
e99739d3ef a 2020-12-03 16:28:46 +08:00
苏尹岚
546cfd774c a 2020-12-03 16:25:29 +08:00
苏尹岚
99d712b5de a 2020-12-03 16:20:11 +08:00
苏尹岚
77183df91d a 2020-12-03 16:12:36 +08:00
苏尹岚
001dcc006c aa 2020-12-03 15:55:22 +08:00
苏尹岚
5a9a0ffb41 aa 2020-12-03 15:25:10 +08:00
苏尹岚
f9834f82f1 a 2020-12-03 15:10:42 +08:00
苏尹岚
a44c12e83b aa 2020-12-03 14:31:30 +08:00
苏尹岚
113d6458dc aa 2020-12-03 14:24:56 +08:00
苏尹岚
61fbdca849 aa 2020-12-03 14:20:25 +08:00
苏尹岚
ebe859cebf aa 2020-12-03 14:15:54 +08:00
苏尹岚
068ede19e7 aa 2020-12-03 14:07:30 +08:00
苏尹岚
c3bcc39877 a 2020-12-03 12:01:49 +08:00
苏尹岚
2937ad574c storename 2020-12-03 11:51:18 +08:00
苏尹岚
7d6bc7e68a aa 2020-12-03 11:22:10 +08:00
苏尹岚
f2e52b14b4 aa 2020-12-03 11:14:19 +08:00
苏尹岚
3af9c45fdb a 2020-12-03 08:48:31 +08:00
苏尹岚
b0f753c238 a 2020-12-02 15:23:20 +08:00
苏尹岚
f50321dcc8 aa 2020-12-02 14:52:29 +08:00
苏尹岚
08c5c00192 a 2020-12-02 14:14:23 +08:00
苏尹岚
9eb51d0748 haocaixians 2020-12-02 10:01:52 +08:00
苏尹岚
9404bb53b7 jds 2020-12-02 09:41:41 +08:00
苏尹岚
542a6b1ecc a 2020-12-02 09:15:37 +08:00
苏尹岚
845068b29a a 2020-12-02 08:56:12 +08:00
苏尹岚
53f7119f71 aa 2020-12-01 18:11:56 +08:00
苏尹岚
de86bf0b91 aa 2020-12-01 18:08:39 +08:00
苏尹岚
06cf6bec1e aa 2020-12-01 17:48:25 +08:00
苏尹岚
303a638aca a 2020-12-01 17:22:03 +08:00
苏尹岚
43186f5b12 test 2020-12-01 16:52:53 +08:00
苏尹岚
017ba5966a test 2020-12-01 14:19:21 +08:00
苏尹岚
89d0da0d3f test 2020-12-01 14:16:14 +08:00
苏尹岚
9b4a39fe6b test 2020-12-01 14:10:02 +08:00
苏尹岚
b357fe0f94 aa 2020-12-01 14:03:37 +08:00
苏尹岚
b8de0d25fa aa 2020-12-01 11:59:36 +08:00
苏尹岚
0bb932bcb4 aa 2020-12-01 11:22:02 +08:00
苏尹岚
ad896f8d3a aa 2020-11-30 10:07:29 +08:00
苏尹岚
4103fd356b a 2020-11-30 09:05:52 +08:00
苏尹岚
a6604db886 aa 2020-11-27 18:05:08 +08:00
苏尹岚
4dacdc71d3 aa 2020-11-27 17:52:46 +08:00
苏尹岚
0a8fadd843 test 2020-11-27 17:22:04 +08:00
苏尹岚
ad4d6fad08 test 2020-11-27 17:08:26 +08:00
苏尹岚
21efbad32b aa 2020-11-27 16:33:23 +08:00
苏尹岚
de502031aa aa 2020-11-27 16:30:46 +08:00
苏尹岚
934dc75ee5 aa 2020-11-27 16:27:45 +08:00
苏尹岚
6016df9311 aa 2020-11-27 15:59:05 +08:00
苏尹岚
e620f9f74e aa 2020-11-27 15:58:48 +08:00
苏尹岚
1a91a66b83 aa 2020-11-27 15:58:26 +08:00
苏尹岚
de9c3173e7 aa 2020-11-27 15:51:15 +08:00
苏尹岚
d031a0a265 a 2020-11-27 14:31:43 +08:00
苏尹岚
bae7d7f2ee jds2 2020-11-27 11:09:18 +08:00
苏尹岚
6fa46bebb6 aa 2020-11-27 09:18:05 +08:00
苏尹岚
2b1dde2931 try 2020-11-26 18:22:29 +08:00
苏尹岚
90efd04463 try 2020-11-26 18:15:28 +08:00
苏尹岚
b94492b9ce try 2020-11-26 18:02:47 +08:00
苏尹岚
462938dce0 try 2020-11-26 17:58:35 +08:00
苏尹岚
777fcf52f7 try 2020-11-26 17:44:24 +08:00
苏尹岚
7688fe50c2 try 2020-11-26 17:23:55 +08:00
苏尹岚
9e23a344ee try 2020-11-26 17:17:34 +08:00
苏尹岚
3d41d6d340 try 2020-11-26 17:13:18 +08:00
苏尹岚
ae65790f08 try 2020-11-26 17:07:53 +08:00
苏尹岚
728c907ac1 try 2020-11-26 17:01:29 +08:00
苏尹岚
566c6d454f try 2020-11-26 15:55:04 +08:00
苏尹岚
7c65775a25 chuangjian 2020-11-26 15:35:31 +08:00
苏尹岚
cf6c376757 chuangjian 2020-11-26 15:33:49 +08:00
苏尹岚
a7109fc2dc chuangjian 2020-11-26 15:01:24 +08:00
苏尹岚
ae9f838b3f chuangjian 2020-11-26 14:51:32 +08:00
苏尹岚
2ca9e25744 chuangjian 2020-11-26 14:44:55 +08:00
苏尹岚
6a1cd0f578 chuangjian 2020-11-26 14:29:15 +08:00
苏尹岚
38e6925011 chuangjian 2020-11-26 14:12:38 +08:00
苏尹岚
4d83a0a577 chuangjian 2020-11-26 14:05:04 +08:00
苏尹岚
007fc99f3a chuangjian 2020-11-26 14:04:38 +08:00
苏尹岚
b8179e66b0 chuangjian 2020-11-26 13:53:57 +08:00
苏尹岚
237f3a85e0 chuangjian 2020-11-26 13:50:02 +08:00
苏尹岚
6fa5863809 chuangjian 2020-11-26 11:52:02 +08:00
苏尹岚
f2ab362045 chuangjian 2020-11-26 11:43:42 +08:00
苏尹岚
8e4e1e8a4c chuangjian 2020-11-26 11:41:15 +08:00
苏尹岚
6835d01ee1 jds 2020-11-26 11:24:55 +08:00
苏尹岚
eef37bf4ad jds 2020-11-26 10:19:58 +08:00
苏尹岚
fc40813a4a jds 2020-11-26 10:10:58 +08:00
苏尹岚
45053920af jds 2020-11-26 10:07:26 +08:00
苏尹岚
743c966034 jds 2020-11-26 09:59:11 +08:00
苏尹岚
0a96b9f403 jds 2020-11-26 09:51:23 +08:00
苏尹岚
49a636922d jds 2020-11-26 09:47:34 +08:00
苏尹岚
b5575b363e jds 2020-11-26 09:46:12 +08:00
苏尹岚
eb577573ef aa 2020-11-26 09:40:01 +08:00
苏尹岚
58e3e3dfb2 heihei 2020-11-26 08:58:45 +08:00
苏尹岚
f4f76fd6bb tmp 2020-11-25 10:52:12 +08:00
苏尹岚
8a51c158a4 jdshop2 2020-11-25 10:04:04 +08:00
苏尹岚
aa19958d42 a 2020-11-20 18:03:53 +08:00
苏尹岚
351b69efde vendori 2020-11-20 17:12:56 +08:00
苏尹岚
b519864234 刷价格 2020-11-20 11:16:48 +08:00
苏尹岚
6fd6a85b84 vendororgcode 2020-11-20 09:15:09 +08:00
苏尹岚
09b5536cc1 京东库存同步修改,jds2 2020-11-20 08:58:08 +08:00
苏尹岚
681277d355 test 2020-11-19 17:15:51 +08:00
苏尹岚
c241149092 test 2020-11-19 17:09:44 +08:00
苏尹岚
ebec62c922 test 2020-11-19 17:05:40 +08:00
苏尹岚
fe6681b95d test 2020-11-19 17:01:05 +08:00
苏尹岚
ab977da6e2 test 2020-11-19 16:53:45 +08:00
苏尹岚
47bba8385e sync jds2 2020-11-19 16:39:39 +08:00
苏尹岚
e25a6cf680 复制京东价格 2020-11-19 15:52:52 +08:00
苏尹岚
f3bbb33c75 jds2 cat 2020-11-19 15:36:11 +08:00
苏尹岚
ee3cc67527 cookie 2020-11-19 15:22:21 +08:00
苏尹岚
6f3409b852 test 2020-11-19 15:02:32 +08:00
苏尹岚
e58b124056 tyetst 2020-11-19 13:37:28 +08:00
苏尹岚
ce7e2f619d test 2020-11-19 11:39:34 +08:00
苏尹岚
728c779002 aa 2020-11-19 11:18:30 +08:00
苏尹岚
c8ceeb6453 cookie 2020-11-19 11:10:49 +08:00
苏尹岚
66d917aea0 cookie 2020-11-19 10:48:10 +08:00
苏尹岚
6b91919789 订单发三方 2020-11-19 09:22:53 +08:00
苏尹岚
f1cb1cfd67 没商品不打印 2020-11-18 16:33:26 +08:00
苏尹岚
f9b2917c99 vendororgcode cat 2020-11-18 15:41:37 +08:00
苏尹岚
d6634b5844 jds2 cat 2020-11-18 15:22:39 +08:00
苏尹岚
67a9c517f2 jds 2020-11-18 14:57:45 +08:00
苏尹岚
694bc68ef9 test 2020-11-18 11:41:47 +08:00
苏尹岚
2fbdab5501 test 2020-11-18 11:37:46 +08:00
苏尹岚
7bcef2fa97 Merge remote-tracking branch 'origin/mark' into jdshop 2020-11-18 11:19:20 +08:00
苏尹岚
2df28fccfd temp 2020-11-18 11:15:03 +08:00
苏尹岚
b202891cae a 2020-11-18 11:05:02 +08:00
苏尹岚
a14edae2a2 aa 2020-11-18 11:04:02 +08:00
苏尹岚
a3cfcab631 jdshop2 create store 2020-11-18 10:46:07 +08:00
苏尹岚
d14bb10be7 storedetail 2020-11-18 10:22:12 +08:00
苏尹岚
5a6dae8d53 jdshop2 2020-11-18 09:48:34 +08:00
苏尹岚
81b7fae762 jds2 2020-11-18 09:30:59 +08:00
苏尹岚
ab98c19187 cao 2020-11-17 17:37:03 +08:00
苏尹岚
67cdb501a7 喜树街上调5% 2020-11-17 14:20:29 +08:00
苏尹岚
7968b7bbc2 test 2020-11-17 14:16:25 +08:00
苏尹岚
00665fd1f9 post 2020-11-16 15:10:20 +08:00
苏尹岚
9c6ea62a7b 任务分成 2020-11-16 15:04:23 +08:00
苏尹岚
7c2256d12f shauxin 0jiesuanjia 2020-11-16 14:22:54 +08:00
苏尹岚
d2e6cdc9c3 三方配送新规则 2020-11-16 09:31:13 +08:00
苏尹岚
2ef49a83d0 三方配送新规则 2020-11-16 09:28:23 +08:00
苏尹岚
878b5d85de chengdu 2020-11-13 15:34:48 +08:00
苏尹岚
83e3c9c9d3 jingdongshouhou 2020-11-13 13:56:59 +08:00
苏尹岚
f3219dda82 美团名字不要有约 2020-11-13 13:49:07 +08:00
苏尹岚
5f42850da8 美团名字不要有约 2020-11-13 13:48:36 +08:00
苏尹岚
2b74307ce6 jdprice 2020-11-12 18:28:15 +08:00
苏尹岚
abdaa91181 act 2020-11-12 18:18:32 +08:00
苏尹岚
cc8705e929 act 2020-11-12 18:12:24 +08:00
苏尹岚
796477af03 act 2020-11-12 18:01:37 +08:00
苏尹岚
ab89f3efa7 qq 2020-11-12 17:11:51 +08:00
苏尹岚
d3e8f56453 heihie 2020-11-12 16:57:02 +08:00
苏尹岚
559aef5a7c 刷新京东价 2020-11-12 15:37:24 +08:00
苏尹岚
7ad2c7508f beta 2020-11-12 15:30:31 +08:00
苏尹岚
75cba5dfb4 beta 2020-11-12 15:29:12 +08:00
苏尹岚
0498bd3b77 beta 2020-11-12 15:24:14 +08:00
苏尹岚
b41d28ff30 beta 2020-11-12 15:16:55 +08:00
苏尹岚
20dbfa5c29 beta 2020-11-12 15:10:07 +08:00
苏尹岚
f7e49363ac beta 2020-11-12 15:03:45 +08:00
苏尹岚
ea3ad9e0ec beta 2020-11-12 14:48:17 +08:00
苏尹岚
1ed629c921 beta 2020-11-12 14:38:47 +08:00
苏尹岚
216a95f230 beta 2020-11-12 14:34:43 +08:00
苏尹岚
6b6f4275b5 beta 2020-11-12 14:24:53 +08:00
苏尹岚
aba0dc681c beta 2020-11-12 14:15:02 +08:00
苏尹岚
e1c673497e beta 2020-11-12 14:09:12 +08:00
苏尹岚
682cf5d233 beta 2020-11-12 14:06:21 +08:00
苏尹岚
d28224a1f3 beta 2020-11-12 13:58:30 +08:00
苏尹岚
e44f7ad94d beta 2020-11-12 13:58:01 +08:00
苏尹岚
12bca0fa70 beta 2020-11-12 13:53:05 +08:00
苏尹岚
61d417c5d0 beta 2020-11-12 13:40:10 +08:00
苏尹岚
b63a12d9d7 beta 2020-11-12 13:35:41 +08:00
苏尹岚
59c66f71fc beta 2020-11-12 12:02:48 +08:00
苏尹岚
02fb1a9873 beta 2020-11-12 11:42:31 +08:00
苏尹岚
fe4acaac27 beta 2020-11-12 11:37:50 +08:00
苏尹岚
11f148cea9 beta 2020-11-12 11:34:14 +08:00
苏尹岚
16ec045108 beta 2020-11-12 11:27:18 +08:00
苏尹岚
f6b3e99a32 beta 2020-11-12 11:26:13 +08:00
苏尹岚
7085da94f3 beta 2020-11-12 11:22:37 +08:00
苏尹岚
33abf02a8f beta 2020-11-12 11:22:12 +08:00
苏尹岚
7633cffee1 beta 2020-11-12 11:19:34 +08:00
苏尹岚
a350224fc3 beta 2020-11-12 11:13:17 +08:00
苏尹岚
fa7200f3c6 beta 2020-11-12 11:05:02 +08:00
苏尹岚
1e77a962e7 beta 2020-11-12 10:57:45 +08:00
苏尹岚
866a7ed173 beta 2020-11-12 10:54:01 +08:00
苏尹岚
56d1785e19 beta 2020-11-12 10:47:32 +08:00
苏尹岚
6eb1ffa810 beta 2020-11-12 10:42:05 +08:00
苏尹岚
4b2e4dc8e8 beta 2020-11-12 10:36:40 +08:00
苏尹岚
f13a5fb3a2 beta 2020-11-12 08:53:17 +08:00
苏尹岚
5f0a9dd2bd beta 2020-11-12 08:46:43 +08:00
苏尹岚
734e7b3d5f ordersale 2020-11-12 08:41:20 +08:00
suyl
9428021532 xiaoxi 2020-11-12 00:16:34 +08:00
suyl
93549d78c8 Merge branch 'jdshop' of e.coding.net:rosydev/jx-callback into jdshop 2020-11-11 21:10:39 +08:00
suyl
2441a1a7d8 xiaoxi 2020-11-11 21:10:29 +08:00
苏尹岚
5a3e88fd25 heihei 2020-11-11 19:06:27 +08:00
苏尹岚
0e1067ad96 ceshi 2020-11-11 18:50:29 +08:00
苏尹岚
788c73a734 heihei 2020-11-11 18:39:35 +08:00
苏尹岚
19c58e4b4a ceshi 2020-11-11 18:08:55 +08:00
苏尹岚
8749f1d4be dingshi 2020-11-11 08:46:34 +08:00
苏尹岚
efd55100d7 meituan shuang 111 2020-11-10 18:44:16 +08:00
苏尹岚
0f46f03493 mendianbeizhu 2020-11-10 09:01:55 +08:00
苏尹岚
f3eae584a2 ebaishoudongjiedan 2020-11-10 08:57:13 +08:00
苏尹岚
99bf4c4d7c dd 2020-11-09 17:46:29 +08:00
苏尹岚
74f536584c jdsc 2020-11-09 17:07:52 +08:00
苏尹岚
172b5650b6 ebaiorder 2020-11-09 16:39:22 +08:00
苏尹岚
ea29ab48dd ebai order cuoxu 2020-11-09 16:25:39 +08:00
苏尹岚
216ca31b35 gy 2020-11-09 15:52:27 +08:00
苏尹岚
7e51b093e5 gy 2020-11-09 15:36:45 +08:00
苏尹岚
adb3c1b6dd gy 2020-11-09 15:32:17 +08:00
苏尹岚
32ed7d2f11 gy 2020-11-09 15:29:05 +08:00
苏尹岚
96d364fac4 gy 2020-11-09 15:27:22 +08:00
苏尹岚
0e2b692adc ebai 2020-11-09 10:28:40 +08:00
suyl
7600f6768f jdshp order 2020-11-08 11:53:19 +08:00
suyl
77663fa9b9 ebai 2020-11-07 17:53:36 +08:00
suyl
70977349be ebai 2020-11-07 12:42:22 +08:00
苏尹岚
9c36dc1417 新老包装 2020-11-06 15:01:56 +08:00
苏尹岚
7363cac26f test 2020-11-06 11:04:55 +08:00
苏尹岚
239c9fa0e0 test 2020-11-06 10:57:09 +08:00
苏尹岚
c888fa59ca quanxian level 2020-11-06 09:56:18 +08:00
苏尹岚
4c53a868c6 youhua 2020-11-04 14:35:47 +08:00
苏尹岚
5dd90ba32c testover 2020-11-04 14:29:31 +08:00
苏尹岚
d2d6ee0bc0 test 2020-11-04 13:58:51 +08:00
苏尹岚
81143786f3 test 2020-11-03 15:21:05 +08:00
苏尹岚
ddd1fd3924 test 2020-11-03 14:33:19 +08:00
苏尹岚
06f764ee94 test 2020-11-03 14:30:26 +08:00
苏尹岚
929595069f test 2020-11-03 14:23:27 +08:00
苏尹岚
ddc832936a gzh queryorder 2020-11-03 13:38:40 +08:00
苏尹岚
48ad86b6f7 ebai售后单 2020-11-03 11:37:37 +08:00
苏尹岚
057f49072d ceshi 2020-11-02 13:41:28 +08:00
苏尹岚
386467e1c7 shanghu 2020-11-02 11:40:28 +08:00
苏尹岚
b69f6701dc 售前删除 2020-11-02 09:41:07 +08:00
苏尹岚
8c42f34725 test 2020-10-30 13:58:16 +08:00
苏尹岚
6232cec620 profit 2020-10-29 18:27:37 +08:00
苏尹岚
6962940384 gy baozhuangf 2020-10-29 16:48:24 +08:00
苏尹岚
791a41cb83 果园可以批量 2020-10-29 10:45:50 +08:00
苏尹岚
ebdead42cb 赵哥改平台负责人 2020-10-29 09:52:32 +08:00
苏尹岚
6276cf09ff 公众号提醒加上城市和门店 2020-10-27 09:29:47 +08:00
苏尹岚
e00088ca93 果园饿百拣货完成 2020-10-26 15:38:41 +08:00
苏尹岚
7ec4495706 新疆西藏海南内蒙古运费修改 2020-10-26 10:50:56 +08:00
苏尹岚
a8421b7f12 订单利润修改 2020-10-23 17:44:56 +08:00
苏尹岚
9a0daa6b22 jds订单转移 2020-10-23 13:36:47 +08:00
苏尹岚
ce8a5517f0 ceshi over 2020-10-22 16:39:06 +08:00
苏尹岚
f81a78a0bd testuploadimg 2020-10-22 16:35:23 +08:00
苏尹岚
367a949fb8 testuploadimg 2020-10-22 16:31:08 +08:00
苏尹岚
56144b8e1b testuploadimg 2020-10-22 16:24:22 +08:00
苏尹岚
a05a04fd6c 权限加 2020-10-22 15:51:04 +08:00
苏尹岚
e2114ff395 jdsorder 大大运费 2020-10-22 10:51:32 +08:00
苏尹岚
50dd0d0844 weixin 2020-10-19 15:12:07 +08:00
苏尹岚
f6feb80971 level, jdsorder 30minu 2020-10-16 18:14:54 +08:00
苏尹岚
30433d0ebf del 2020-10-16 17:51:14 +08:00
苏尹岚
3c6fe84d2f a 2020-10-16 17:29:37 +08:00
苏尹岚
5908e49da8 storelevel 2020-10-15 15:32:56 +08:00
苏尹岚
d202109225 地址老user 2020-10-14 16:19:30 +08:00
苏尹岚
d0abf3f7ff 失败重发修改 2020-10-14 16:09:51 +08:00
苏尹岚
ee8f342617 刷新物料哦订单状态判断 2020-10-14 16:05:01 +08:00
苏尹岚
9890a1c3cf minayun 2020-10-13 15:17:13 +08:00
苏尹岚
a293c48428 totalcount 2020-10-12 17:36:23 +08:00
苏尹岚
f589df1c7f sql 2020-10-12 14:44:15 +08:00
苏尹岚
6fe58c9e88 串行了? 2020-10-12 14:43:32 +08:00
苏尹岚
34bfbda252 亏损消息推市场负责人 2020-10-12 14:12:21 +08:00
苏尹岚
b86e8b8413 刷新报价订单商品价格 2020-10-12 14:04:53 +08:00
苏尹岚
c27ffb51eb 赵哥所有门店 2020-10-12 13:49:55 +08:00
苏尹岚
14ae1bfe12 不发短信 2020-10-12 13:30:53 +08:00
苏尹岚
e504547190 用户统计 2020-10-12 11:51:31 +08:00
苏尹岚
31088be645 用户统计 2020-10-12 11:49:56 +08:00
苏尹岚
adf23c0831 饿百接单发消息 2020-10-12 10:12:47 +08:00
苏尹岚
25f97e8d6e 手动接单发消息 2020-10-12 09:31:03 +08:00
苏尹岚
79ff77f17c 手动接单发消息 2020-10-12 09:26:27 +08:00
苏尹岚
63e38075d9 excel 2020-10-10 17:57:40 +08:00
苏尹岚
5c12ff9c91 excel 2020-10-10 17:54:29 +08:00
苏尹岚
14a0254ae6 excel 2020-10-10 17:48:52 +08:00
苏尹岚
05c8f2343c excel 2020-10-10 17:45:23 +08:00
苏尹岚
dde30ae1ad excel 2020-10-10 17:42:30 +08:00
苏尹岚
4e9d0965b0 excel 2020-10-10 17:38:24 +08:00
苏尹岚
dd3d1e9d49 excel 2020-10-10 17:38:08 +08:00
苏尹岚
556fbf8908 excel 2020-10-10 17:31:14 +08:00
苏尹岚
a928837c60 excel 2020-10-10 17:19:10 +08:00
苏尹岚
b3dd78d37c excel 2020-10-10 17:14:42 +08:00
苏尹岚
602f6e3537 excel 2020-10-10 16:45:51 +08:00
苏尹岚
e1184a1195 Merge remote-tracking branch 'origin/mark' 2020-05-28 09:40:00 +08:00
苏尹岚
bd83ec9b18 Merge branch 'master' of e.coding.net:rosydev/jx-callback 2020-05-19 17:38:02 +08:00
苏尹岚
c6375e437e Merge remote-tracking branch 'origin/mark' 2020-05-19 17:37:49 +08:00
苏尹岚
c422b5e3f3 Accept Merge Request #199: (mark -> master)
Merge Request: 更新master
Created By: @苏尹岚
Accepted By: @苏尹岚
URL: https://rosydev.coding.net/p/jx-callback/d/jx-callback/git/merge/199
2020-04-01 16:32:23 +08:00
苏尹岚
903bcdf3dc Merge remote-tracking branch 'origin/mark' 2020-03-03 09:50:01 +08:00
苏尹岚
d2aa139c37 Merge branch 'master' of e.coding.net:rosydev/jx-callback 2020-03-03 09:48:50 +08:00
苏尹岚
690c776d13 Merge branch 'su' 2019-10-31 17:45:31 +08:00
苏尹岚
f58ec8a9b7 Merge branch 'su' 2019-10-31 13:34:52 +08:00
苏尹岚
6f50903ef4 Merge branch 'su'
统计订单信息接口
2019-10-30 17:57:23 +08:00
419 changed files with 94589 additions and 45702 deletions

1
.gitignore vendored
View File

@@ -2,6 +2,7 @@
jx-callback
*.tmp
swagger/
debug
.DS_Store
*.log

View File

@@ -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
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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 {

View File

@@ -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
View 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
}

View 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))
}

View 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
}

View 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)
}

View 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
}

File diff suppressed because it is too large Load Diff

View 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
}

View 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)
}

View 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
}
}
}

File diff suppressed because it is too large Load Diff

View 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()
}

View 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, "")
}

View 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
}

View 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
}

File diff suppressed because it is too large Load Diff

View 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
}

View 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
}

View 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

File diff suppressed because it is too large Load Diff

View 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)
}

View File

@@ -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-5print_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-5print_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
}
}

View File

@@ -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
}

View 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)
}
}

View File

@@ -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
}

View 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

View File

@@ -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,
}
}

View File

@@ -0,0 +1 @@
package cms

View File

@@ -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)
}

View File

@@ -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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View 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)
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View 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)
}

View 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

View 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
}

View 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
}

File diff suppressed because it is too large Load Diff

View 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
}

View 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)
}

View 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)
}

View File

@@ -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
}

View 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

View 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)
}
}

View 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...)
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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)
}

View File

@@ -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
}
}

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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()
}

View File

@@ -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
}

View File

@@ -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("微信购买会员失败")
//}
}

View File

@@ -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
// }

View File

@@ -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
}

View File

@@ -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,

View 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
}

View File

@@ -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
}
// 按时间序列循环

View 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()
}
}

View 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)
}

View 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
}

View 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
}

View 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{})
}

View 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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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]
}

View File

@@ -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]
}

View File

@@ -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
}

View File

@@ -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]
}

View File

@@ -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
}

View File

@@ -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]
}

View File

@@ -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
}

View 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
}

File diff suppressed because it is too large Load Diff

View 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))
}

File diff suppressed because it is too large Load Diff

View File

@@ -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()

View File

@@ -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
}

View File

@@ -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")

View File

@@ -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 {

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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())
}

View File

@@ -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 {

View File

@@ -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
}

View 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