Compare commits

1989 Commits

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
393 changed files with 99272 additions and 4207 deletions

View File

@@ -4,14 +4,13 @@ import (
"bytes"
"encoding/base64"
"errors"
"fmt"
"regexp"
"strings"
"time"
"git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/business/dao"
"git.rosy.net.cn/jx-callback/business/model"
"git.rosy.net.cn/jx-callback/business/model/dao"
"git.rosy.net.cn/jx-callback/globals"
"git.rosy.net.cn/jx-callback/globals/api"
"github.com/dchest/captcha"
@@ -213,6 +212,9 @@ func SendVerifyCode(authToken, captchaID, captchaValue, authID string) (verfifyC
if handler := authers[authType]; handler == nil {
err = ErrIllegalAuthType
} else {
//if user, _ := dao.GetUserByID(dao.GetDB(), "mobile", authID); user != nil {
// return "", authInfo, fmt.Errorf("该用户已存在,请勿重复注册!")
//}
verfifyCode, err = handler.SendVerifyCode(authID)
}
}
@@ -244,6 +246,7 @@ func LoginInternal(ctx *Context, authType, authID, authIDType, authSecret string
realAuthID = user.GetID()
}
if authBindEx, err = handler.VerifySecret(realAuthID, authSecret); err == nil {
globals.SugarLogger.Debugf("Login authBindEx", utils.Format4Output(authBindEx, false))
if authBindEx == nil { // mobile, email会返回nil表示不会新建AuthBind实体
user = userProvider.GetUser(authID, authIDType)
authBindEx = &AuthBindEx{
@@ -266,6 +269,10 @@ func LoginInternal(ctx *Context, authType, authID, authIDType, authSecret string
// if user != nil {
// authBindEx.UserID = user.GetID()
// }
if user2 := userProvider.GetUser(authBindEx.UserHint.Email, UserIDMobile); user2 != nil {
user = user2
}
} else if authBindEx.UserID != "" {
user = userProvider.GetUser(authBindEx.UserID, UserIDID)
}
@@ -277,9 +284,9 @@ func LoginInternal(ctx *Context, authType, authID, authIDType, authSecret string
//如果是小程序
if authType == "weixinmini" || authType == "weixinapp" {
appID := strings.Split(authSecret, ",")[0]
if appID == "wxa4a76d7b4c88604e" || appID == "wx2d6949f724b2541d" || appID == "wx18111a41fd17f24f" { //菜市或者果园
if user != nil {
binds, err := dao.GetUserBindAuthInfo(dao.GetDB(), user.GetID(), 0, nil, "", "", "wx2bb99eb5d2c9b82c")
if appID == "wx08a5c2a8581414ff" || appID == "wx2d6949f724b2541d" || appID == "wx18111a41fd17f24f" || appID == "wx4b5930c13f8b1170" { //菜市或者果园
if authInfo.AuthBindInfo.UserID != "" {
binds, err := dao.GetUserBindAuthInfo(dao.GetDB(), authInfo.AuthBindInfo.UserID, 0, nil, "", "", []string{"wx2bb99eb5d2c9b82c", "wx4b5930c13f8b1170"})
if err != nil {
return authInfo, err
}
@@ -340,6 +347,18 @@ func AddAuthBind(user IUser, newAuthInfo *AuthInfo) (err error) {
return err
}
func AddAuthBindWithMobile(authInfo *AuthInfo, mobile string) (err error) {
if handler := authers[authInfo.AuthBindInfo.Type]; handler != nil {
user, _ := dao.GetUserByID(dao.GetDB(), "mobile", mobile)
authInfo.AuthBindInfo.UserID = user.UserID
//handler.UnbindAuth(user.GetID(), newAuthInfo.GetAuthType(), newAuthInfo.GetAuthTypeID(), user.GetName())
err = handler.AddAuthBind(authInfo.AuthBindInfo, user.GetName())
} else {
err = ErrIllegalAuthType
}
return err
}
func UnbindAuth(userID, authType, authTypeID, userName string) (err error) {
globals.SugarLogger.Debugf("UnbindAuth userID:%s, authType:%s, authTypeID:%s, userName:%s", userID, authType, authTypeID, userName)
if handler := authers[authType]; handler != nil {
@@ -463,7 +482,7 @@ func DisableUser(userID, operatorUserName string) (err error) {
}
func GetUserBindAuthInfo(userID string) (authList []*model.AuthBind, err error) {
return dao.GetUserBindAuthInfo(dao.GetDB(), userID, model.AuthBindTypeAuth, nil, "", "", "")
return dao.GetUserBindAuthInfo(dao.GetDB(), userID, model.AuthBindTypeAuth, nil, "", "", nil)
}
func DeletedTokenInfoWithoutParam(authInfo *AuthInfo) (err error) {
@@ -476,16 +495,3 @@ func DeletedTokenInfoWithoutParam(authInfo *AuthInfo) (err error) {
}
return err
}
func CheckWeixinminiAuthBind(userID string) (err error) {
var (
db = dao.GetDB()
)
authBinds, err := dao.GetUserBindAuthInfo(db, userID, model.AuthBindTypeAuth, []string{"weixinmini", "weixinapp"}, "", "", "")
if len(authBinds) == 0 {
return fmt.Errorf("请绑定微信认证方式!")
} else {
return nil
}
return err
}

View File

@@ -0,0 +1,42 @@
package alipay
import (
"git.rosy.net.cn/baseapi/platformapi/alipayapi"
"git.rosy.net.cn/jx-callback/business/auth2"
"git.rosy.net.cn/jx-callback/business/auth2/authprovider"
"git.rosy.net.cn/jx-callback/globals"
"git.rosy.net.cn/jx-callback/globals/api"
)
const (
AuthType = "alipaycode"
AuthKey = "GHp3ojlVYRRu2XID4FX2ew=="
)
type Auther struct {
authprovider.DefAuther
}
var (
AutherObj *Auther
)
func init() {
AutherObj = new(Auther)
auth2.RegisterAuther(AuthType, AutherObj)
}
func (a *Auther) VerifySecret(dummy, code string) (authBindEx *auth2.AuthBindEx, err error) {
globals.SugarLogger.Debugf("VerifySecret dummy:%s, code:%s", dummy, code)
tokenInfo, err := api.AliPayAPI.SystemAuthToken(alipayapi.GrantTypeCode, code, "")
if err == nil {
//userInfo, err2 := api.AliPayAPI.UserInfoShare(tokenInfo.AccessToken)
//if err = err2; err == nil {
if authBindEx, err = a.UnionFindAuthBind(AuthType, api.AliPayAPI.GetAppID(), nil, tokenInfo.UserID, tokenInfo.AlipayUserID, tokenInfo); err == nil {
authBindEx.AuthSecret = tokenInfo.AccessToken
authBindEx.AuthSecret2 = tokenInfo.RefreshToken
}
}
return authBindEx, err
}

View File

@@ -9,8 +9,8 @@ import (
"git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/business/auth2"
"git.rosy.net.cn/jx-callback/business/dao"
"git.rosy.net.cn/jx-callback/business/model"
"git.rosy.net.cn/jx-callback/business/model/dao"
"git.rosy.net.cn/jx-callback/globals"
"git.rosy.net.cn/jx-callback/globals/api"
)
@@ -82,7 +82,7 @@ func (a *DefAuther) UnionFindAuthBind(curAuthType, curAuthTypeID string, unionAu
} else if dao.IsNoRowsError(err) { // 直接找不到尝试unionID
if unionID != "" { // 且有unionID
var authBindList []*model.AuthBind
if authBindList, err = dao.GetUserBindAuthInfo(db, "", model.AuthBindTypeAuth, unionAuthTypeList, "", unionID, ""); err == nil && len(authBindList) > 0 { // 通过unionID找到至少一个认证方式
if authBindList, err = dao.GetUserBindAuthInfo(db, "", model.AuthBindTypeAuth, unionAuthTypeList, "", unionID, nil); err == nil && len(authBindList) > 0 { // 通过unionID找到至少一个认证方式
authBind = authBindList[0]
authBind.Type = curAuthType
authBind.TypeID = curAuthTypeID

View File

@@ -0,0 +1,6 @@
package dingding
const (
AuthTypeStaff = "ddstaff" // 钉钉企业登录
AuthTypeQRCode = "ddqrcode"
)

View File

@@ -0,0 +1,37 @@
package dingding
import (
"git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/business/auth2"
"git.rosy.net.cn/jx-callback/business/auth2/authprovider"
"git.rosy.net.cn/jx-callback/globals"
"git.rosy.net.cn/jx-callback/globals/api"
)
type QRCodeAuther struct {
authprovider.DefAuther
}
var (
AutherObjQRCode *QRCodeAuther
)
func init() {
AutherObjQRCode = new(QRCodeAuther)
auth2.RegisterAuther(AuthTypeQRCode, AutherObjQRCode)
}
func (a *QRCodeAuther) VerifySecret(dummy, code string) (authBindEx *auth2.AuthBindEx, err error) {
globals.SugarLogger.Debugf("dingding qrcode VerifySecret code:%s", code)
userQRInfo, err := api.DingDingQRCodeAPI.GetUserInfoByCode(code)
if err == nil {
globals.SugarLogger.Debugf("dingding qrcode VerifySecret code:%s, userQRInfo:%s", code, utils.Format4Output(userQRInfo, false))
if authBindEx, err = a.UnionFindAuthBind(AuthTypeQRCode, api.DingDingQRCodeAPI.GetAppID(), []string{AuthTypeStaff, AuthTypeQRCode}, userQRInfo.OpenID, userQRInfo.UnionID, userQRInfo); err == nil {
authBindEx.UserHint = &auth2.UserBasic{
Name: userQRInfo.Nickname,
}
}
}
return authBindEx, err
}

View File

@@ -0,0 +1,47 @@
package dingding
import (
"git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/business/auth2"
"git.rosy.net.cn/jx-callback/business/auth2/authprovider"
"git.rosy.net.cn/jx-callback/business/model"
"git.rosy.net.cn/jx-callback/globals"
"git.rosy.net.cn/jx-callback/globals/api"
)
type StaffAuther struct {
authprovider.DefAuther
}
var (
AutherObjStaff *StaffAuther
)
func init() {
AutherObjStaff = new(StaffAuther)
auth2.RegisterAuther(AuthTypeStaff, AutherObjStaff)
}
func (a *StaffAuther) VerifySecret(dummy, code string) (authBindEx *auth2.AuthBindEx, err error) {
globals.SugarLogger.Debugf("dingding staff VerifySecret code:%s", code)
userID, err := api.DingDingAPI.GetUserID(code)
if err == nil {
userDetail, err2 := api.DingDingAPI.GetUserDetail(userID.UserID)
if err = err2; err == nil {
if authBindEx, err = a.UnionFindAuthBind(AuthTypeStaff, api.DingDingQRCodeAPI.GetAppID(), []string{AuthTypeStaff, AuthTypeQRCode}, userID.UserID, utils.Interface2String(userDetail["unionid"]), userDetail); err == nil {
authBindEx.UserHint = &auth2.UserBasic{
UserID2: userID.UserID,
Mobile: utils.Interface2String(userDetail["mobile"]),
Email: utils.Interface2String(userDetail["email"]),
Name: utils.Interface2String(userDetail["name"]),
}
}
}
}
return authBindEx, err
}
func (a *StaffAuther) GetUserType() (userType int8) {
return model.UserTypeOperator
}

View File

@@ -0,0 +1,90 @@
package mobile
import (
"errors"
"fmt"
"git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/business/auth2"
"git.rosy.net.cn/jx-callback/business/auth2/authprovider"
"git.rosy.net.cn/jx-callback/globals"
aliyunsmsclient "github.com/KenmyZhang/aliyun-communicate"
)
const (
AuthType = auth2.AuthTypeMobile
TestVerifyCode = "123456"
)
var (
warningMap = map[string]int{
"isv.AMOUNT_NOT_ENOUGH": 1,
"isv.ACCOUNT_ABNORMAL": 1,
"isv.OUT_OF_SERVICE": 1,
"isv.DAY_LIMIT_CONTROL": 1,
}
)
var (
ErrVerifyCodeIsWrong = errors.New("验证码错")
)
type Auther struct {
authprovider.DefAuther
}
var (
AutherObj *Auther
)
func init() {
AutherObj = new(Auther)
auth2.RegisterAuther(AuthType, AutherObj)
}
// 特殊接口
func (a *Auther) SendVerifyCode(mobileNumber string) (verifyCode string, err error) {
verifyCode = a.GenerateVerifyCode(mobileNumber)
smsClient := aliyunsmsclient.New("http://dysmsapi.aliyuncs.com/")
response, err := smsClient.Execute(globals.AliKey, globals.AliSecret, mobileNumber, globals.SMSSignName, globals.SMSMobileVerifyTemplate, string(utils.MustMarshal(map[string]interface{}{
"code": verifyCode,
})))
a.SaveVerifyCode(mobileNumber, verifyCode)
if err == nil && response.Code == aliyunsmsclient.ResponseCodeOk {
// a.SaveVerifyCode(mobileNumber, verifyCode)
} else {
if err == nil {
if warningMap[response.Code] == 1 {
globals.SugarLogger.Warnf("SendVerifyCode mobileNumber:%s failed with response:%s", mobileNumber, utils.Format4Output(response, false))
} else {
globals.SugarLogger.Infof("SendVerifyCode mobileNumber:%s failed with response:%s", mobileNumber, utils.Format4Output(response, false))
}
err = fmt.Errorf("发送短信出错:%s", response.Message)
} else {
globals.SugarLogger.Warnf("SendVerifyCode mobileNumber:%s failed with error:%v", mobileNumber, err)
}
}
return verifyCode, err
}
func (a *Auther) VerifySecret(mobileNumber, code string) (authBindEx *auth2.AuthBindEx, err error) {
globals.SugarLogger.Debugf("VerifySecret mobileNumber:%s, code:%s", mobileNumber, code)
err = ErrVerifyCodeIsWrong
if (code == auth2.InternalAuthSecret ||
auth2.TestMobileMap[mobileNumber] == 1 && code == TestVerifyCode) ||
a.VerifyCode(mobileNumber, code) {
err = nil
}
return nil, err
}
// 此函数为空
func (a *Auther) AddAuthBind(authBindEx *auth2.AuthBindEx, userName string) (err error) {
return err
}
// 此函数为空
func (a *Auther) UnbindAuth(userID, authType, authTypeID, userName string) (err error) {
return err
}

View File

@@ -0,0 +1,88 @@
package password
import (
"crypto/sha1"
"errors"
"fmt"
"git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/business/auth2"
"git.rosy.net.cn/jx-callback/business/auth2/authprovider"
"git.rosy.net.cn/jx-callback/business/model"
"git.rosy.net.cn/jx-callback/business/model/dao"
"git.rosy.net.cn/jx-callback/globals"
)
const (
AuthType = auth2.AuthTypePassword
)
type Auther struct {
authprovider.DefAuther
}
var (
AutherObj *Auther
)
var (
ErrUserAndPassNotMatch = errors.New("用户名密码不匹配")
)
func init() {
AutherObj = new(Auther)
auth2.RegisterAuther(AuthType, AutherObj)
}
func (a *Auther) VerifySecret(userID, passMD5 string) (authBindEx *auth2.AuthBindEx, err error) {
globals.SugarLogger.Debugf("localpass VerifySecret userID:%s", userID)
var authBind *model.AuthBind
if authBind, err = dao.GetAuthBind(dao.GetDB(), model.AuthBindTypeAuth, AuthType, userID); err == nil {
if err = a.checkPassword(authBind, passMD5); err == nil {
authBindEx = &auth2.AuthBindEx{
AuthBind: *authBind,
}
}
} else if dao.IsNoRowsError(err) {
err = auth2.ErrUserAuthTypeNotExist
}
return authBindEx, err
}
// 特殊接口
func (a *Auther) ChangePassword(userID, userName, oldPassMD5, newPassMD5 string) (err error) {
var authBind *model.AuthBind
db := dao.GetDB()
salt := utils.GetUUID()
encryptPwd := a.encryptPassword(newPassMD5, salt)
if authBind, err = dao.GetAuthBind(db, model.AuthBindTypeAuth, AuthType, userID); err == nil {
if err = a.checkPassword(authBind, oldPassMD5); err == nil || authBind.AuthSecret == "" { // 如果原密码为空,不判断原密码,代表重置密码
_, err = dao.UpdateEntityLogically(db, authBind, map[string]interface{}{
"AuthSecret": encryptPwd,
"AuthSecret2": salt,
}, userName, nil)
}
} else if dao.IsNoRowsError(err) {
err = a.AddAuthBind(&auth2.AuthBindEx{
AuthBind: model.AuthBind{
UserID: userID,
Type: AuthType,
AuthID: userID,
AuthSecret: encryptPwd,
AuthSecret2: salt,
},
}, userName)
}
return err
}
func (a *Auther) encryptPassword(password, salt string) string {
return fmt.Sprintf("%x", sha1.Sum([]byte(password+salt)))
}
func (a *Auther) checkPassword(authBind *model.AuthBind, passMD5 string) (err error) {
if authBind.AuthSecret != a.encryptPassword(passMD5, authBind.AuthSecret2) {
return ErrUserAndPassNotMatch
}
return nil
}

View File

@@ -0,0 +1,105 @@
package weixin
import (
"errors"
"git.rosy.net.cn/baseapi/platformapi/weixinapi"
"git.rosy.net.cn/jx-callback/business/auth2"
"git.rosy.net.cn/jx-callback/business/auth2/authprovider"
"git.rosy.net.cn/jx-callback/business/model"
"git.rosy.net.cn/jx-callback/globals"
"git.rosy.net.cn/jx-callback/globals/api"
)
const (
AuthTypeWeixin = "wxqrcode" // 微信扫码
AuthTypeMP = "weixinsns" // 公众号
AuthTypeWXNative = "wxnative" // 微信APP
AuthTypeWxApp = "weixinapp" //app微信登录
)
type Auther struct {
authprovider.DefAuther
authType string
}
var (
AutherObjWX *Auther
AutherObjMP *Auther
AutherObjNative *Auther
AutherObjApp *Auther
)
var (
ErrStateIsWrong = errors.New("登录state非法")
)
func init() {
AutherObjWX = &Auther{
authType: AuthTypeWeixin,
}
auth2.RegisterAuther(AuthTypeWeixin, AutherObjWX)
AutherObjMP = &Auther{
authType: AuthTypeMP,
}
auth2.RegisterAuther(AuthTypeMP, AutherObjMP)
AutherObjNative = &Auther{
authType: AuthTypeWXNative,
}
auth2.RegisterAuther(AuthTypeWXNative, AutherObjNative)
AutherObjApp = &Auther{
authType: AuthTypeWxApp,
}
auth2.RegisterAuther(AuthTypeWxApp, AutherObjApp)
}
func (a *Auther) VerifySecret(id, secret string) (authBindEx *auth2.AuthBindEx, err error) {
globals.SugarLogger.Debugf("weixin VerifySecret id:%s secret:%s", id, secret)
var openID, accessToken string
_, jsCode := SplitJsCode(secret)
if a.authType != AuthTypeWXNative {
state := id
code := jsCode
if state == "" {
token, err2 := a.getAPI().SNSRetrieveToken(code)
if err = err2; err == nil {
openID = token.OpenID
accessToken = token.AccessToken
}
} else {
err = ErrStateIsWrong
}
} else {
openID = id
accessToken = secret
}
if err == nil {
wxUserinfo, err2 := a.getAPI().SNSGetUserInfo(accessToken, openID)
if err = err2; err == nil {
if authBindEx, err = a.UnionFindAuthBind(a.authType, a.getAPI().GetAppID(), []string{AuthTypeWeixin, AuthTypeMP, AuthTypeMini, AuthTypeWXNative, AuthTypeWxApp}, wxUserinfo.OpenID, wxUserinfo.UnionID, wxUserinfo); err == nil {
authBindEx.UserHint = &auth2.UserBasic{
Name: wxUserinfo.NickName,
Avatar: wxUserinfo.HeadImgURL,
}
}
}
}
return authBindEx, err
}
func (a *Auther) getAPI() *weixinapi.API {
if a.authType == AuthTypeWeixin {
return api.WeixinPageAPI
}
if a.authType == AuthTypeWxApp {
return api.WeixinApp
}
return api.WeixinAPI
}
func (a *Auther) GetUserType() (userType int8) {
return model.UserTypeStoreBoss
}

View File

@@ -0,0 +1,139 @@
package weixin
import (
"errors"
"strings"
"git.rosy.net.cn/baseapi/platformapi/weixinapi"
"git.rosy.net.cn/jx-callback/business/auth2"
"git.rosy.net.cn/jx-callback/business/auth2/authprovider"
"git.rosy.net.cn/jx-callback/business/model"
"git.rosy.net.cn/jx-callback/globals"
"git.rosy.net.cn/jx-callback/globals/api"
)
const (
AuthTypeMini = "weixinmini" // 小程序
)
type MiniAuther struct {
authprovider.DefAuther
}
var (
ErrAuthTypeShouldBeMini = errors.New("当前操作要求是小程序登录方式")
)
var (
AutherObjMini *MiniAuther
)
func init() {
AutherObjMini = new(MiniAuther)
auth2.RegisterAuther(AuthTypeMini, AutherObjMini)
}
func (a *MiniAuther) VerifySecret(dummy, jsCode string) (authBindEx *auth2.AuthBindEx, err error) {
globals.SugarLogger.Debugf("weixin mini VerifySecret jsCode:%s", jsCode)
appID, jsCode := SplitJsCode(jsCode)
sessionInfo, err := getWxApp(appID).SNSCode2Session(jsCode)
if err == nil {
sessionKey := sessionInfo.SessionKey
sessionInfo.SessionKey = ""
if authBindEx, err = a.UnionFindAuthBind(AuthTypeMini, getWxApp(appID).GetAppID(), []string{AuthTypeWeixin, AuthTypeMP, AuthTypeMini, AuthTypeWXNative}, sessionInfo.OpenID, sessionInfo.UnionID, sessionInfo); err == nil {
authBindEx.UserData = sessionKey
}
}
return authBindEx, err
}
// 特殊接口
func (a *MiniAuther) DecryptData(authInfo *auth2.AuthInfo, jsCode, encryptedData, iv string) (decryptedDataBase64 string, err error) {
globals.SugarLogger.Debugf("weixin mini DecryptData jsCode:%s, encryptedData:%s, iv:%s", jsCode, encryptedData, iv)
var sessionKey string
appID, jsCode := SplitJsCode(jsCode)
if jsCode != "" {
sessionInfo, err := getWxApp(appID).SNSCode2Session(jsCode)
if err == nil {
// if authBindEx, err := a.UnionFindAuthBind(AuthTypeMini, getWxApp(appID).GetAppID(), []string{AuthTypeMini}, sessionInfo.OpenID, "", nil); err == nil {
// if authBindEx.UserID != authInfo.GetID() {
// return "", fmt.Errorf("jsCode与token不匹配")
// }
// } else {
// return "", err
// }
sessionKey = sessionInfo.SessionKey
} else {
return "", err
}
} else {
if authInfo.AuthBindInfo.Type != AuthTypeMini {
// return "", ErrAuthTypeShouldBeMini
}
sessionKey = authInfo.AuthBindInfo.UserData.(string)
}
// decryptedData, err := ProxySNSDecodeMiniProgramData(encryptedData, sessionKey, iv)
decryptedData, err := weixinapi.SNSDecodeMiniProgramData(encryptedData, sessionKey, iv)
if err != nil {
return "", err
}
// authInfo.AuthBindInfo.UserData = sessionKey
return string(decryptedData), nil
}
func (a *MiniAuther) GetUserType() (userType int8) {
return model.UserTypeStoreBoss
}
func getWxApp(appID string) (miniApi *weixinapi.API) {
miniApi = api.WeixinMiniAPI
if len(appID) > 0 && appID == api.WeixinMiniAppID2 {
miniApi = api.WeixinMiniAPI2
}
if len(appID) > 0 && appID == api.WeixinMiniAppIDsc {
miniApi = api.WeixinMiniAPIsc
}
return miniApi
}
func SplitJsCode(jsCode string) (appID, realJsCode string) {
list := strings.Split(jsCode, ",")
if len(list) == 2 {
appID = list[0]
realJsCode = list[1]
} else if len(list) == 1 {
realJsCode = jsCode
} else {
globals.SugarLogger.Warnf("SplitJsCode abnormal jsCode:%s", jsCode)
}
return appID, realJsCode
}
func ComposeJsCode(appID, jsCode string) (composedCode string) {
composedCode = strings.Join([]string{
appID,
jsCode,
}, ",")
return composedCode
}
// func ProxySNSCode2Session(jsCode string) (sessionInfo *weixinapi.SessionInfo, err error) {
// miniApi := api.WeixinMiniAPI
// list := strings.Split(jsCode, ",")
// if len(list) >= 2 && len(list[0]) == len("wx4b5930c13f8b1170") {
// if list[0] == api.WeixinMiniAppID2 {
// miniApi = api.WeixinMiniAPI2
// }
// miniApi = getWxApp(list[0])
// jsCode = strings.Join(list[1:], ",")
// }
// sessionInfo, err = miniApi.SNSCode2Session(jsCode)
// return sessionInfo, err
// }
// func ProxySNSDecodeMiniProgramData(encryptedData, sessionKey, iv string) (decryptedData []byte, err error) {
// globals.SugarLogger.Debugf("ProxySNSDecodeMiniProgramData, encryptedData:%s, sessionKey:%s, iv:%s", encryptedData, sessionKey, iv)
// decryptedData, err = api.WeixinMiniAPI.SNSDecodeMiniProgramData(encryptedData, sessionKey, iv)
// return decryptedData, err
// }

View File

@@ -6,8 +6,8 @@ import (
"git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/business/authz"
"git.rosy.net.cn/jx-callback/business/dao"
"git.rosy.net.cn/jx-callback/business/model"
"git.rosy.net.cn/jx-callback/business/model/dao"
)
func GetRoleDescription(name string, storeID int) (description string) {

View File

@@ -0,0 +1,171 @@
package casbinauth
import (
jxmodel "git.rosy.net.cn/jx-callback/business/model"
"git.rosy.net.cn/jx-callback/globals"
"github.com/astaxie/beego/client/orm"
"github.com/casbin/casbin/model"
"github.com/casbin/casbin/persist"
)
type Adapter struct {
}
// finalizer is the destructor for Adapter.
func finalizer(a *Adapter) {
}
func NewAdapter() *Adapter {
return &Adapter{}
}
func loadPolicyLine(line jxmodel.CasbinRule, model model.Model) {
lineText := line.PType
if line.V0 != "" {
lineText += ", " + line.V0
}
if line.V1 != "" {
lineText += ", " + line.V1
}
if line.V2 != "" {
lineText += ", " + line.V2
}
if line.V3 != "" {
lineText += ", " + line.V3
}
if line.V4 != "" {
lineText += ", " + line.V4
}
if line.V5 != "" {
lineText += ", " + line.V5
}
persist.LoadPolicyLine(lineText, model)
}
func (a *Adapter) LoadPolicy(model model.Model) error {
var lines []jxmodel.CasbinRule
o := orm.NewOrm()
_, err := o.QueryTable("casbin_rule").Limit(-1).All(&lines)
if err != nil {
return err
}
for _, line := range lines {
loadPolicyLine(line, model)
}
return nil
}
func savePolicyLine(ptype string, rule []string) jxmodel.CasbinRule {
line := jxmodel.CasbinRule{}
line.PType = ptype
if len(rule) > 0 {
line.V0 = rule[0]
}
if len(rule) > 1 {
line.V1 = rule[1]
}
if len(rule) > 2 {
line.V2 = rule[2]
}
if len(rule) > 3 {
line.V3 = rule[3]
}
if len(rule) > 4 {
line.V4 = rule[4]
}
if len(rule) > 5 {
line.V5 = rule[5]
}
return line
}
func (a *Adapter) clearAll(o orm.Ormer) (err error) {
_, err = o.Raw(`
DELETE t1
FROM casbin_rule t1
`).Exec()
return err
}
// SavePolicy saves policy to database.
func (a *Adapter) SavePolicy(model model.Model) error {
globals.SugarLogger.Debugf("SavePolicy")
o := orm.NewOrm()
a.clearAll(o)
var lines []jxmodel.CasbinRule
for ptype, ast := range model["p"] {
for _, rule := range ast.Policy {
line := savePolicyLine(ptype, rule)
lines = append(lines, line)
}
}
for ptype, ast := range model["g"] {
for _, rule := range ast.Policy {
line := savePolicyLine(ptype, rule)
lines = append(lines, line)
}
}
_, err := o.InsertMulti(len(lines), lines)
return err
}
// AddPolicy adds a policy rule to the storage.
func (a *Adapter) AddPolicy(sec string, ptype string, rule []string) error {
o := orm.NewOrm()
line := savePolicyLine(ptype, rule)
_, err := o.Insert(&line)
return err
}
// RemovePolicy removes a policy rule from the storage.
func (a *Adapter) RemovePolicy(sec string, ptype string, rule []string) error {
o := orm.NewOrm()
line := savePolicyLine(ptype, rule)
_, err := o.Delete(&line, "p_type", "v0", "v1", "v2", "v3", "v4", "v5")
return err
}
// RemoveFilteredPolicy removes policy rules that match the filter from the storage.
func (a *Adapter) RemoveFilteredPolicy(sec string, ptype string, fieldIndex int, fieldValues ...string) error {
line := jxmodel.CasbinRule{}
line.PType = ptype
filter := []string{}
filter = append(filter, "p_type")
if fieldIndex <= 0 && 0 < fieldIndex+len(fieldValues) {
line.V0 = fieldValues[0-fieldIndex]
filter = append(filter, "v0")
}
if fieldIndex <= 1 && 1 < fieldIndex+len(fieldValues) {
line.V1 = fieldValues[1-fieldIndex]
filter = append(filter, "v1")
}
if fieldIndex <= 2 && 2 < fieldIndex+len(fieldValues) {
line.V2 = fieldValues[2-fieldIndex]
filter = append(filter, "v2")
}
if fieldIndex <= 3 && 3 < fieldIndex+len(fieldValues) {
line.V3 = fieldValues[3-fieldIndex]
filter = append(filter, "v3")
}
if fieldIndex <= 4 && 4 < fieldIndex+len(fieldValues) {
line.V4 = fieldValues[4-fieldIndex]
filter = append(filter, "v4")
}
if fieldIndex <= 5 && 5 < fieldIndex+len(fieldValues) {
line.V5 = fieldValues[5-fieldIndex]
filter = append(filter, "v5")
}
o := orm.NewOrm()
_, err := o.Delete(&line, filter...)
return err
}

View File

@@ -0,0 +1,49 @@
package casbinauth
import (
"git.rosy.net.cn/jx-callback/business/authz"
"git.rosy.net.cn/jx-callback/business/authz/autils"
"github.com/casbin/casbin"
"github.com/casbin/casbin/errors"
)
type CasbinAuthz struct {
enforcer *casbin.SyncedEnforcer
}
func New(modelFile string) (authObj authz.IAuthz, err error) {
obj := &CasbinAuthz{}
obj.enforcer, err = casbin.NewSyncedEnforcer(modelFile, NewAdapter())
return obj, err
}
func (c *CasbinAuthz) AddRole4User(userID string, r *authz.RoleInfo) (err error) {
_, err = c.enforcer.AddRoleForUser(userID, r.GetFullName())
return err
}
func (c *CasbinAuthz) DeleteRole4User(userID string, r *authz.RoleInfo) (err error) {
_, err = c.enforcer.DeleteRoleForUser(userID, r.GetFullName())
return err
}
func (c *CasbinAuthz) GetUserRoleList(userID string) (roleList []*authz.RoleInfo, err error) {
roleNameList, err := c.enforcer.GetRolesForUser(userID)
if err == nil && len(roleNameList) > 0 {
roleList = autils.FullRoleName2RoleList(roleNameList)
}
return roleList, err
}
func (c *CasbinAuthz) GetRoleUserList(r *authz.RoleInfo) (userIDList []string, err error) {
// globals.SugarLogger.Debug(roleFullName)
userIDList, err = c.enforcer.GetUsersForRole(r.GetFullName())
if err == errors.ERR_NAME_NOT_FOUND {
err = nil
}
return userIDList, err
}
// func (c *CasbinAuthz) GetAllRoleList() (roleList []*authz.RoleInfo) {
// return authz.FullRoleName2RoleList(c.enforcer.GetAllRoles())
// }

128
business/cs/weimob_order.go Normal file
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

@@ -1,32 +0,0 @@
package dao
import (
"git.rosy.net.cn/jx-callback/business/model"
)
func GetDataResource(db *DaoDB, hashCode, fullURL string) (dataRes *model.DataResource, err error) {
sql := `
SELECT t1.*
FROM data_resource t1
WHERE 0 = 1`
sqlParams := []interface{}{}
if hashCode != "" {
sql += " OR t1.hash_code = ?"
sqlParams = append(sqlParams, hashCode)
}
if fullURL != "" {
sql += " OR t1.main_url = ? OR t1.qiniu_url = ? OR t1.ebai_url = ? OR t1.mtwm_url = ?"
sqlParams = append(sqlParams, fullURL, fullURL, fullURL, fullURL)
}
err = GetRow(db, &dataRes, sql, sqlParams...)
return dataRes, err
}
func GetNeedUploadDataResource(db *DaoDB) (dataResList []*model.DataResource, err error) {
sql := `
SELECT t1.*
FROM data_resource t1
WHERE t1.use_type <> 0 AND (t1.ebai_url = '' OR t1.mtwm_url = '')`
err = GetRows(db, &dataResList, sql)
return dataResList, err
}

View File

@@ -1,91 +0,0 @@
package dao
import (
"git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/business/model"
"git.rosy.net.cn/jx-callback/globals"
)
// QueryUserPrinter 查询用户打印机
func QueryUserPrinter(userId, printNo string) (*model.Printer, error) {
sql := `
SELECT *
FROM printer
WHERE 1 = 1 AND deleted_at = ?
`
sqlParams := []interface{}{
utils.DefaultTimeValue,
}
if printNo != "" {
sql += " AND print_no = ?"
sqlParams = append(sqlParams, printNo)
}
if userId != "" {
sql += " AND user_id = ?"
sqlParams = append(sqlParams, userId)
}
var printer = &model.Printer{}
err := GetRow(GetDB(), &printer, sql, sqlParams)
return printer, err
}
func GetPrinters(db *DaoDB, appID int, printNo string, status, statusNeq int) (printers []*model.Printer, err error) {
sql := `
SELECT *
FROM printer
WHERE 1 = 1 AND deleted_at = ?
`
sqlParams := []interface{}{
utils.DefaultTimeValue,
}
if appID != 0 {
sql += " AND app_id = ?"
sqlParams = append(sqlParams, appID)
}
if printNo != "" {
sql += " AND print_no = ?"
sqlParams = append(sqlParams, printNo)
}
if status != 0 {
sql += " AND status = ?"
sqlParams = append(sqlParams, status)
}
if statusNeq != 0 {
sql += " AND status <> ?"
sqlParams = append(sqlParams, statusNeq)
}
err = GetRows(db, &printers, sql, sqlParams)
globals.SugarLogger.Debugf("======printers==== %s", utils.Format4Output(printers, false))
return printers, err
}
func GetPrintMsgs(db *DaoDB, printNo, msgID string, status, statusNeq int) (printMsgs []*model.PrintMsg, err error) {
sql := `
SELECT *
FROM print_msg
WHERE 1 = 1 AND deleted_at = ?
`
sqlParams := []interface{}{
utils.DefaultTimeValue,
}
if status != model.PrintMsgAll {
sql += " AND status = ?"
sqlParams = append(sqlParams, status)
}
if statusNeq != model.PrintMsgAll {
sql += " AND status <> ?"
sqlParams = append(sqlParams, statusNeq)
}
if printNo != "" {
sql += " AND print_no = ?"
sqlParams = append(sqlParams, printNo)
}
if msgID != "" {
sql += " AND msg_id = ?"
sqlParams = append(sqlParams, msgID)
}
err = GetRows(db, &printMsgs, sql, sqlParams)
return printMsgs, err
}

View File

@@ -1,25 +0,0 @@
package dao
import (
"errors"
"git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/business/model"
)
func GetPrintSetting(printNo string) (*model.PrintSettingObj, error) {
sql := ` SELECT * FROM print_setting WHERE print_no = ? and deleted_at = ? `
sqlParams := []interface{}{
printNo,
utils.DefaultTimeValue,
}
var printSetting *model.PrintSetting
if err := GetRow(GetDB(), &printSetting, sql, sqlParams...); err != nil {
return nil, err
}
if printSetting == nil {
return nil, errors.New("数据查询异常")
}
return model.UnMarshalString2Json(printSetting)
}

View File

@@ -1,34 +0,0 @@
package dao
import (
"errors"
"git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/business/model"
)
// SelectUserDefaultTemp 查询用户默认模板
func SelectUserDefaultTemp(userId string, tempType string) (*model.SystemTemp, bool, error) {
var result *model.SystemTemp
if err := GetRow(GetDB(), &result, `SELECT * FROM system_temp WHERE user_id = ? AND temp_type = ? AND is_use = ? AND deleted_at = ? ORDER BY created_at desc `, []interface{}{userId, tempType, 1, utils.DefaultTimeValue}...); err != nil {
return nil, false, err
}
return result, true, nil
}
// QuerySystemTemp 查询系统模板
func QuerySystemTemp() (*model.SystemTemp, error) {
var result []*model.SystemTemp
if err := GetRows(GetDB(), &result, `SELECT * FROM system_temp WHERE user_id = ? AND temp_type = ? AND is_use = ? AND deleted_at = ? ORDER BY created_at desc `, []interface{}{"system_user", "user_store", 1, utils.DefaultTimeValue}...); err != nil {
return nil, err
}
if len(result) > 0 {
return result[0], nil
}
return nil, errors.New("模板获取异常")
}
// AddTemp 添加模板数据
func AddTemp(param *model.SystemTemp) error {
return CreateEntity(GetDB(), param)
}

View File

@@ -1,19 +0,0 @@
package dao
import (
"encoding/json"
"git.rosy.net.cn/baseapi/utils"
"testing"
)
func TestGetUsers(t *testing.T) {
aa := "{\"title\":\"<center><b>京西菜市</b></center><br><center>手机买菜上京西</center><br><center>极速到家送惊喜</center><br>--------------------------------<br>\",\"divider\":\"--------------------------------<br>\",\"payOrderTime\":\"<left>下单时间:%s</left>\",\"trySendTime\":\"<left>预计送达时间:%s</left>\",\"consigneeName\":\"<left>客户名称:%s</left>\",\"consigneeMobile\":\"<left>客户电话:%s</left>\",\"consigneeAddress\":\"<left>客户地址:%s</left>\",\"buyerComment\":\"<left>客户备注:%s</left>\",\"qrcOrder\":\"<qrl>%s</qrl>\",\"goodsListDetail\":\"商品列表:<br>品名 数量 单价 小计<br>--------------------------------<br>\",\"skuName\":\"<b>%s</b><br>\",\"skuNumber\":\"<b>x%s</b>\",\"skuPrice\":\"<b>¥%s</b>\",\"skuAllPrice\":\"<b>¥%s</b>\",\"skuUpc\":\"upc码 %s<br>\",\"allSkuTypeCount\":\"<br><br><b>共 %s 种\",\"allSkuCount\":\" %s 件商品</b><br>\",\"storeName\":\"<center><b>商品质量问题请联系:</b></center><br><center><b>%s:\",\"storeTel\":\"%s</b></center><br><br>更多信息请关注官方微信:\",\"officialName\":\"<b>%s</b><br><br><br>--------------------------------<br>--------------------------------<br><br>\"}"
conte := make(map[string]string, 0)
if err := json.Unmarshal([]byte(aa), &conte); err != nil {
t.Fatal(err)
}
t.Log(utils.Format4Output(conte, false))
}

View File

@@ -1,19 +0,0 @@
package dao
import (
"git.rosy.net.cn/jx-callback/business/model"
)
func CheckHeard(printNo string) (bool, error) {
var data *model.PrintActivation
err := GetRow(GetDB(), &data, `SELECT * FROM print_activation WHERE print_no = ?`, []interface{}{printNo}...)
if err != nil {
return false, err
}
if data == nil {
return false, nil
}
return true, nil
}

View File

@@ -1,42 +0,0 @@
package dao
import (
"git.rosy.net.cn/jx-callback/business/model"
"time"
)
// QueryPrintBindStore 查询绑定门店
func QueryPrintBindStore(printNo string) ([]*model.PrintBindStore, error) {
var data []*model.PrintBindStore
if err := GetRows(GetDB(), &data, `SELECT * FROM print_bind_store WHERE print_no = ?`, []interface{}{printNo}...); err != nil {
return nil, err
}
return data, nil
}
// BindStoreList 绑定门店信息
func BindStoreList(req *model.AddPrinterParam, userId string) error {
param := &model.PrintBindStore{
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
StoreID: req.StoreId,
StoreName: req.Name,
StoreVendor: 9, // 绑定平台,全平台
PrintNo: req.PrintNo,
StoreStatus: 1, // 门店开启
BindStatus: 1, // 绑定状态
}
if userId != "" {
param.UserId = userId
} else {
param.UserId = "system"
}
return CreateEntity(GetDB(), param)
}
// DeleteStoreList 删除绑定门店
func DeleteStoreList(printNo string, storeId string) error {
sql := ` DELETE FROM print_bind_store WHERE print_no = ? AND store_id = ? `
_, err := ExecuteSQL(GetDB(), sql, []interface{}{printNo, storeId}...)
return err
}

View File

@@ -1,539 +0,0 @@
package dao
import (
"encoding/json"
"fmt"
"git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/business/model"
"git.rosy.net.cn/jx-print/globals"
"strings"
"time"
)
var SystemTempObj map[string]*model.SystemTemp
func Init() {
SystemTempObj = make(map[string]*model.SystemTemp, 0)
sysTempList, err := QuerySystemTemp()
if err != nil {
globals.SugarLogger.Debug("query system temp err :", err)
return
}
SystemTempObj[sysTempList.TempSize] = sysTempList
//now := time.Now()
//param := &model.SystemTemp{
// CreatedAt: &now,
// UpdatedAt: &now,
// LastOperator: "system",
// DeletedAt: &utils.DefaultTimeValue,
// TempName: "",
// TempRank: model.SystemTempKey,
// Temp: "",
// UserId: "system_user",
// TempType: model.TempTypeMerchant,
// TempSize: model.SystemTempSizeBig,
// PrintSn: "system",
// IsUse: 1,
//}
// 初始化大字体模板
//if _, v := temp[model.SystemTempSizeBig]; !v {
// param.TempName = "system" + model.SystemTempSizeBig
// param.Temp = model.SystemTempValue
// if err := AddTemp(param); err != nil {
// globals.SugarLogger.Debug("init system temp err :", err)
// }
// SystemTempObj[model.SystemTempSizeBig] = param
//}
// 初始化中字体模板
//if _, v := temp[model.SystemTempSizeMedium]; !v {
// param.TempName = "system" + model.SystemTempSizeMedium
// medium := strings.Replace(model.SystemTempValue, "<b>", "<hb>", -1)
// medium2 := strings.Replace(medium, "</b>", "</hb>", -1)
// param.Temp = medium2
// param.TempSize = model.SystemTempSizeMedium
// param.IsUse = 2
// if err := AddTemp(param); err != nil {
// globals.SugarLogger.Debug("init system temp err :", err)
// }
// SystemTempObj[model.SystemTempSizeMedium] = param
//}
// 初始化小字体模板
//if _, v := temp[model.SystemTempSizeSmall]; !v {
// param.TempName = "system" + model.SystemTempSizeSmall
// medium := strings.Replace(model.SystemTempValue, "<b>", " ", -1)
// medium2 := strings.Replace(medium, "</b>", " ", -1)
// param.Temp = medium2
// param.TempSize = model.SystemTempSizeSmall
// param.IsUse = 2
// if err := AddTemp(param); err != nil {
// globals.SugarLogger.Debug("init system temp err :", err)
// }
// SystemTempObj[model.SystemTempSizeSmall] = param
//}
}
// MakePrintMsgOnTemp 将打印数据渲染到模板当中
func MakePrintMsgOnTemp(param map[string]string, userId string) (string, error) {
// 查询用户默认模板,不存在则使用系统默认模板
userTemp := &model.SystemTemp{}
userTemp, isHave, err := SelectUserDefaultTemp(userId, model.TempTypeMerchantUser)
if err != nil || !isHave {
userTemp, err = QuerySystemTemp()
}
if userTemp == nil || !isHave || err != nil {
if userTemp.TempType != "" {
userTemp = SystemTempObj[userTemp.TempSize]
} else {
userTemp = SystemTempObj[model.SystemTempSizeBig]
}
}
// 需要打印数据
printMsg := ""
printValue := make([]interface{}, 0, 0)
userTempMap := make(map[string]string, 0)
if err := json.Unmarshal([]byte(userTemp.Temp), &userTempMap); err != nil {
return "", err
}
for _, v := range strings.Split(userTemp.TempRank, ",") {
switch v {
case "skuName", "skuNumber", "skuPrice", "skuAllPrice", "allSkuTypeCount", "allSkuCount", "skuUpc", "userPayMoney":
continue
case "goodsListDetail":
printMsg += userTempMap[v]
skuList := make([]*model.SkuListPrintOrder, 0, 0)
if err := json.Unmarshal([]byte(param["skuList"]), &skuList); err != nil {
return "", err
}
for i := 0; i < len(skuList); i++ {
printMsg += userTempMap["skuName"]
printMsg += userTempMap["skuNumber"]
printMsg += userTempMap["skuPrice"]
printMsg += userTempMap["skuAllPrice"]
printValue = append(printValue, skuList[i].SkuName, skuList[i].SkuCount, skuList[i].SalePrice, skuList[i].TotalCountPrice)
if skuList[i].Upc != "" {
printMsg += userTempMap["skuUpc"]
printValue = append(printValue, skuList[i].Upc)
}
}
printMsg += userTempMap["allSkuTypeCount"]
printValue = append(printValue, param["allSkuTypeCount"])
printMsg += userTempMap["allSkuCount"]
printValue = append(printValue, param["allSkuCount"])
printMsg += userTempMap["userPayMoney"]
printValue = append(printValue, param["userPayMoney"])
case "businessType":
if param[v] == "2" { // 是预订单
printMsg += userTempMap[v]
printValue = append(printValue, param[v])
}
case "divider":
printMsg += userTempMap[v]
case "title":
printMsg += userTempMap[v]
case "qrcOrder": // 老版打印机展示不要
//printMsg += userTempMap[v]
//printValue = append(printValue, param[v])
printMsg += `<b>%s #%s</b>`
printValue = append(printValue, param["vendorName"], param["vendorOrderNo"])
default:
printMsg += userTempMap[v]
printValue = append(printValue, param[v])
}
}
return strings.Replace(fmt.Sprintf(strings.Replace(printMsg, "\n", "", -1), printValue...), "\\n", "\r\n", -1), nil
//}
}
// MakePrintMsgOnTempVoice 制作平台语音
func MakePrintMsgOnTempVoice(param map[string]string, setting *model.PrintSettingObj, userId string) (string, error) {
// 打订单
if param[model.OrderStatusPrint] != "" {
// 订单提示设置
printMsg, err := PrinterOrderVoice(param, setting, userId)
if err != nil {
globals.SugarLogger.Debug("err Unmarshal userTemp.printMsg", err)
return "", err
}
return printMsg, nil
}
// 打运单
if param[model.WayBillStatusPrint] != "" {
switch param[model.WayBillStatusPrint] {
case utils.Int2Str(model.WaybillStatusCourierAssigned): // 分配骑手
return PrintWayBillOrderStatus(param, setting), nil
case utils.Int2Str(model.WaybillStatusDeliverReminder): // 催单
printVoiceMsg := ``
printVoiceValue := make([]interface{}, 0, 0)
//printVoiceMsg, printVoiceValue = SyntheticSpeech(printVoiceMsg, printVoiceValue, param)
printVoiceMsg += `<sound>%d</sound>`
printVoiceValue = append(printVoiceValue, model.ReminderOrderVoice)
return strings.Replace(fmt.Sprintf(strings.Replace(printVoiceMsg, "\n", "", -1), printVoiceValue...), "\\n", "\r\n", -1), nil
case utils.Int2Str(model.WaybillStatusDelivered): // 送达
printVoiceMsg := ``
printVoiceValue := make([]interface{}, 0, 0)
//printVoiceMsg, printVoiceValue = SyntheticSpeech(printVoiceMsg, printVoiceValue, param)
printVoiceMsg += `<sound>%d</sound>`
printVoiceValue = append(printVoiceValue, model.FinialsOrderVoice)
return strings.Replace(fmt.Sprintf(strings.Replace(printVoiceMsg, "\n", "", -1), printVoiceValue...), "\\n", "\r\n", -1), nil
}
}
// 门店状态
if param[model.StoreStatusPrint] != "" {
return PrintStoreStatus(param, setting), nil
}
// 进店咨询
if param[model.EnterTheStorePrint] != "" && setting.VoiceSetting.ConsultingPrint == model.SettingOpen {
return `<sound>19</sound>`, nil
}
return "", nil
}
// PrinterOrderVoice 打印机订单提示设置
func PrinterOrderVoice(param map[string]string, setting *model.PrintSettingObj, userId string) (string, error) {
var (
printVoiceMsg string //语音信息
printVoiceValue = make([]interface{}, 0, 0)
textMsg string // 文本信息
err error
)
// 订单状态
switch param[model.OrderStatusPrint] {
// 新订单(待接单)
case utils.Int2Str(model.OrderStatusNew): // utils.Int2Str(model.OrderStatusFinishedPickup), utils.Int2Str(model.OrderStatusAccepted)
//if param[model.OrderStatusPrint] != utils.Int2Str(model.OrderStatusNew) && param[model.VendorIDPrint] == utils.Int64ToStr(model.VendorIDMTWM) {
// return "", err
//}
//if param[model.OrderStatusPrint] == utils.Int2Str(model.OrderStatusFinishedPickup) && param[model.VendorIDPrint] == utils.Int64ToStr(model.VendorIDEBAI) {
// return "", err
//}
// 称谓设置/平台语音设置
if setting.CallNameSetting == 64 || setting.CallNameSetting == 65 || setting.CallNameSetting == 66 {
// 老板
printVoiceMsg += `<sound>%d</sound>`
printVoiceValue = append(printVoiceValue, setting.CallNameSetting)
}
// 订单设置
if setting.PrintSetting.WaitOrderPrint == model.SettingOpen { // 打印订单
textMsg, err = MakePrintMsgOnTemp(param, userId)
if err != nil {
return "", err
}
}
if setting.VoiceSetting.WaitOrderVoice == model.SettingOpen { // 订单通知
printVoiceMsg += `<sound>%d</sound>` // 你来新订单了
printVoiceValue = append(printVoiceValue, model.NewOrderVoice)
}
// 申请取消
case utils.Int2Str(model.ApplyOrderCancel):
// 称谓设置/平台语音设置
if setting.CallNameSetting == 64 || setting.CallNameSetting == 65 || setting.CallNameSetting == 66 {
// 老板
printVoiceMsg += `<sound>%d</sound>`
printVoiceValue = append(printVoiceValue, setting.CallNameSetting)
}
// 订单设置
if setting.PrintSetting.ApplyUserCancelOrder == model.SettingOpen { // 申请取消打印
rejection := ``
rejectionValue := make([]interface{}, 0, 0)
rejection += `<center><b>客户申请取消订单:</b></center><br>`
rejection += `<center><b>订单号: %s</b></center><br>`
rejection += `<center><b>订单来源: %s # %s</b></center><br>`
rejection += `<center><b>取消时间: %s</b></center><br>`
rejectionValue = append(rejectionValue, param[model.OrderNoPrint], param[model.VendorNamePrint], param[model.VendorOrderNoPrint], utils.Time2DateStr(time.Now()))
textMsg = strings.Replace(fmt.Sprintf(strings.Replace(rejection, "\n", "", -1), rejectionValue...), "\\n", "\r\n", -1)
}
if setting.VoiceSetting.ApplyUserOrderCancelVoice == model.SettingOpen { // 申请取消语音
printVoiceMsg += `<sound>%d</sound>`
printVoiceValue = append(printVoiceValue, model.ApplyCancelVoice)
//printVoiceMsg, printVoiceValue = SyntheticSpeech(printVoiceMsg, printVoiceValue, param)
}
// 申请退货
case utils.Int2Str(model.ApplyOrderRefundGoods):
// 称谓设置/平台语音设置
if setting.CallNameSetting == 64 || setting.CallNameSetting == 65 || setting.CallNameSetting == 66 {
// 老板
printVoiceMsg += `<sound>%d</sound>`
printVoiceValue = append(printVoiceValue, setting.CallNameSetting)
}
rejection := ``
rejectionValue := make([]interface{}, 0, 0)
rejection += `<center><b>客户申请退货:</b></center><br>`
rejection += `<center><b>订单号: %s</b></center><br>`
rejection += `<center><b>订单来源: %s # %s</b></center><br>`
rejection += `<center><b>原因: %s</b></center><br>`
rejectionValue = append(rejectionValue, param[model.OrderNoPrint], param[model.VendorNamePrint], param[model.VendorOrderNoPrint], param[model.RejectionReasonPrint])
textMsg = strings.Replace(fmt.Sprintf(strings.Replace(rejection, "\n", "", -1), rejectionValue...), "\\n", "\r\n", -1)
if setting.VoiceSetting.ApplyRefundGoodsVoice == model.SettingOpen { // 申请退货语音
printVoiceMsg += `<sound>%d</sound>`
printVoiceValue = append(printVoiceValue, model.ApplyReturnGoodsVoice)
// printVoiceMsg, printVoiceValue = SyntheticSpeech(printVoiceMsg, printVoiceValue, param)
}
// 申请退款
case utils.Int2Str(model.ApplyOrderRefund):
// 称谓设置/平台语音设置
if setting.CallNameSetting == 64 || setting.CallNameSetting == 65 || setting.CallNameSetting == 66 {
// 老板
printVoiceMsg += `<sound>%d</sound>`
printVoiceValue = append(printVoiceValue, setting.CallNameSetting)
}
// 订单设置
if setting.PrintSetting.ApplyUserRefund == model.SettingOpen { // 取消退款订单
textMsg, err = MakePrintMsgOnTemp(param, userId)
if err != nil {
return "", err
}
}
if setting.VoiceSetting.ApplyRefundOrderVoice == model.SettingOpen {
//printVoiceMsg, printVoiceValue = SyntheticSpeech(printVoiceMsg, printVoiceValue, param)
printVoiceMsg += `<sound>%d</sound>`
printVoiceValue = append(printVoiceValue, model.ApplyRefundVoice)
}
// 取消打印
case utils.Int2Str(model.OrderStatusRejection): // 拒收
// 称谓设置/平台语音设置
if setting.CallNameSetting == 64 || setting.CallNameSetting == 65 || setting.CallNameSetting == 66 {
// 老板
printVoiceMsg += `<sound>%d</sound>`
printVoiceValue = append(printVoiceValue, setting.CallNameSetting)
}
//if setting.VoiceSetting.CustomerRejectionVoice == model.SettingOpen && setting.SystemVoice == model.SettingOpen {
// // 暂无语音打印
//}
// 拒收暂无语音设置,使用文本提示
if setting.PrintSetting.CustomerRejectionPrint == model.SettingOpen { // 客户拒收打印
rejection := ``
rejectionValue := make([]interface{}, 0, 0)
rejection += `<center><b>客户拒收信息:</b></center><br>`
rejection += `<center><b>订单号: %s</b></center><br>`
rejection += `<center><b>订单来源: %s # %s</b></center><br>`
rejection += `<center><b>拒收原因: %s</b></center><br>`
rejectionValue = append(rejectionValue, param[model.OrderNoPrint], param[model.VendorNamePrint], param[model.VendorOrderNoPrint], param[model.RejectionReasonPrint])
textMsg = strings.Replace(fmt.Sprintf(strings.Replace(rejection, "\n", "", -1), rejectionValue...), "\\n", "\r\n", -1)
}
case utils.Int2Str(model.OrderStatusCustomerService): // 客服退款
// 称谓设置/平台语音设置
if setting.CallNameSetting == 64 || setting.CallNameSetting == 65 || setting.CallNameSetting == 66 {
// 老板
printVoiceMsg += `<sound>%d</sound>`
printVoiceValue = append(printVoiceValue, setting.CallNameSetting)
}
if setting.VoiceSetting.CusterRefundVoice == model.SettingOpen {
printVoiceMsg += `<sound>%d</sound>`
printVoiceValue = append(printVoiceValue, model.BusinessResponsibilityVoice)
}
if setting.PrintSetting.CusterRefundPrint == model.SettingOpen {
rejection := ``
rejectionValue := make([]interface{}, 0, 0)
rejection += `<center><b>客服退款详情:</b></center><br>`
rejection += `<center><b>订单号: %s</b></center><br>`
rejection += `<center><b>订单来源: %s # %s</b></center><br>`
rejection += `<center><b>退款原因: %s</b></center><br>`
rejection += `<center><b>退款时间: %s</b></center><br>`
rejectionValue = append(rejectionValue, param[model.OrderNoPrint], param[model.VendorNamePrint], param[model.VendorOrderNoPrint], param[model.CustcareRefundReasonPrint], utils.Time2DateStr(time.Now()))
textMsg = strings.Replace(fmt.Sprintf(strings.Replace(rejection, "\n", "", -1), rejectionValue...), "\\n", "\r\n", -1)
}
case utils.Int2Str(model.OrderStatusCanceled), utils.Int2Str(model.CancelOrderSuccess): // 取消订单成功
// 称谓设置/平台语音设置
if setting.CallNameSetting == 64 || setting.CallNameSetting == 65 || setting.CallNameSetting == 66 {
// 老板
printVoiceMsg += `<sound>%d</sound>`
printVoiceValue = append(printVoiceValue, setting.CallNameSetting)
if setting.VoiceSetting.RefundGoodsVoice == model.SettingOpen {
printVoiceMsg += `<sound>%d</sound>`
printVoiceValue = append(printVoiceValue, model.CancelOrderVoice)
}
}
if setting.PrintSetting.CusterRefundPrint == model.SettingOpen {
rejection := ``
rejectionValue := make([]interface{}, 0, 0)
rejection += `<center><b>订单取消成功:</b></center><br>`
rejection += `<center><b>订单号: %s</b></center><br>`
rejection += `<center><b>订单来源: %s # %s</b></center><br>`
rejection += `<center><b>取消原因: %s</b></center><br>`
rejection += `<center><b>取消成功时间: %s</b></center><br>`
rejectionValue = append(rejectionValue, param[model.OrderNoPrint], param[model.VendorNamePrint], param[model.VendorOrderNoPrint], param[model.CustcareRefundReasonPrint], utils.Time2DateStr(time.Now()))
textMsg = strings.Replace(fmt.Sprintf(strings.Replace(rejection, "\n", "", -1), rejectionValue...), "\\n", "\r\n", -1)
}
case utils.Int2Str(model.BusinessCancelOrder): // 商家取消打印
// 称谓设置/平台语音设置
if setting.CallNameSetting == 64 || setting.CallNameSetting == 65 || setting.CallNameSetting == 66 {
// 老板
printVoiceMsg += `<sound>%d</sound>`
printVoiceValue = append(printVoiceValue, setting.CallNameSetting)
}
if setting.PrintSetting.BusinessOrderCancel == model.SettingOpen {
rejection := ``
rejectionValue := make([]interface{}, 0, 0)
rejection += `<center><b>商家侧取消订单:</b></center><br>`
rejection += `<center><b>订单号: %s</b></center><br>`
rejection += `<center><b>订单来源: %s # %s</b></center><br>`
rejection += `<center><b>取消原因: %s</b></center><br>`
rejection += `<center><b>取消成功时间: %s</b></center><br>`
rejectionValue = append(rejectionValue, param[model.OrderNoPrint], param[model.VendorNamePrint], param[model.VendorOrderNoPrint], param[model.CustcareRefundReasonPrint], utils.Time2DateStr(time.Now()))
textMsg = strings.Replace(fmt.Sprintf(strings.Replace(rejection, "\n", "", -1), rejectionValue...), "\\n", "\r\n", -1)
}
case utils.Int2Str(model.OrderRefundMoneySuccess): // 订单退款成功打印
// 称谓设置/平台语音设置
if setting.CallNameSetting == 64 || setting.CallNameSetting == 65 || setting.CallNameSetting == 66 {
// 老板
printVoiceMsg += `<sound>%d</sound>`
printVoiceValue = append(printVoiceValue, setting.CallNameSetting)
}
if setting.PrintSetting.OrderCancelSuccess == model.SettingOpen {
rejection := ``
rejectionValue := make([]interface{}, 0, 0)
rejection += `<center><b>退款成功:</b></center><br>`
rejection += `<center><b>订单号: %s</b></center><br>`
rejection += `<center><b>订单来源: %s # %s</b></center><br>`
rejectionValue = append(rejectionValue, param[model.OrderNoPrint], param[model.VendorNamePrint], param[model.VendorOrderNoPrint])
textMsg = strings.Replace(fmt.Sprintf(strings.Replace(rejection, "\n", "", -1), rejectionValue...), "\\n", "\r\n", -1)
}
}
voice := strings.Replace(fmt.Sprintf(strings.Replace(printVoiceMsg, "\n", "", -1), printVoiceValue...), "\\n", "\r\n", -1)
return voice + textMsg, nil
}
// PrintWayBillOrderStatus 打印运单类通知消息
func PrintWayBillOrderStatus(param map[string]string, setting *model.PrintSettingObj) string {
var (
printVoiceMsg string //语音信息
printVoiceValue = make([]interface{}, 0, 0)
textMsg string // 文本信息
textMsgValue = make([]interface{}, 0, 0) // 文本信息
)
switch param[model.WayBillStatusPrint] {
case utils.Int2Str(model.WaybillStatusAccepted), utils.Int2Str(model.WaybillStatusCourierAssigned): // 分配骑手
if setting.PrintSetting.RiderTakeOrder == model.SettingOpen { // 打印订单
textMsg += `<center><b>接单骑手信息:</b></center><br>`
textMsg += `<center><b>骑手姓名: %s</b></center><br>`
textMsg += `<center><b>骑手电话: %s</b></center><br>`
textMsg += `<center><b>接单时间: %s</b></center><br>`
textMsg += `<center><b>订单来源: %s</b></center><br>`
textMsg += `<center><b>单号: #%s</b></center><br>`
textMsgValue = append(textMsgValue, param[model.RiderNamePrint], param[model.RiderPhonePrint], utils.Time2DateStr(time.Now()), param[model.VendorNamePrint], param[model.VendorOrderNoPrint])
}
}
if setting.VoiceSetting.RiderTakeOrderVoice == model.SettingOpen { // 骑手接单语音通知
printVoiceMsg += `<sound>%d</sound><sound>%d</sound>` // 骑手已经接单了
printVoiceValue = append(printVoiceValue, model.RiderGetOrderVoice)
}
msg := strings.Replace(fmt.Sprintf(strings.Replace(textMsg, "\n", "", -1), textMsgValue...), "\\n", "\r\n", -1)
voice := strings.Replace(fmt.Sprintf(strings.Replace(printVoiceMsg, "\n", "", -1), printVoiceValue...), "\\n", "\r\n", -1)
return voice + msg
}
// PrintStoreStatus 打印门店状态
func PrintStoreStatus(param map[string]string, setting *model.PrintSettingObj) string {
var (
printVoiceMsg string //语音信息
printVoiceValue = make([]interface{}, 0, 0)
textMsg string // 文本信息
textMsgValue = make([]interface{}, 0, 0)
)
switch utils.Str2Int(param[model.StoreStatusPrint]) {
case -9: // 丢失授权
var voice string
if setting.VoiceSetting.LoseAuthorization == model.SettingOpen {
printVoiceMsg += `<sound>%d</sound>`
printVoiceValue = append(printVoiceValue, model.LoseTokenVoice)
voice = strings.Replace(fmt.Sprintf(strings.Replace(printVoiceMsg, "\n", "", -1), printVoiceValue...), "\\n", "\r\n", -1)
}
textMsg += `<left><b>门店丢失授权通知</b></left><br>`
textMsg += `<left><b>门店:%s</b></left><br>`
textMsg += `<left><b>平台:%s</b></left><br>`
textMsg += `<left><b>下线时间:%s</b></left><br>`
textMsg += `<left><b>授权丢失,将无法继续打压订单!!!!</b></left><br>`
textMsgValue = append(textMsgValue, param[model.StoreNamePrint], param[model.VendorNamePrint], utils.Time2DateStr(time.Now()))
msg := strings.Replace(fmt.Sprintf(strings.Replace(textMsg, "\n", "", -1), textMsgValue...), "\\n", "\r\n", -1)
return voice + msg
default:
// 离线打印文本开启
textMsg += `<center><b>门店下线通知</b></center><br>`
textMsg += `<center><b>门店:%s</b></center><br>`
textMsg += `<center><b>平台:%s</b></center><br>`
textMsg += `<center><b>下线时间:%s</b></center><br>`
textMsgValue = append(textMsgValue, param[model.StoreNamePrint], param[model.VendorNamePrint], utils.Time2DateStr(time.Now()))
// 离线打印语音开启
printVoiceMsg += `<sound>%d</sound>`
printVoiceValue = append(printVoiceValue, model.StoreOfflineVoice)
voice := strings.Replace(fmt.Sprintf(strings.Replace(printVoiceMsg, "\n", "", -1), printVoiceValue...), "\\n", "\r\n", -1)
msg := strings.Replace(fmt.Sprintf(strings.Replace(textMsg, "\n", "", -1), textMsgValue...), "\\n", "\r\n", -1)
return voice + msg
}
}
// SyntheticSpeech 合成语音 (美团xxx号订单)
func SyntheticSpeech(printVoiceMsg string, printVoiceValue []interface{}, param map[string]string) (string, []interface{}) {
printVoiceMsg += `<sound>%d</sound>` // 美团
switch param[model.VendorIDPrint] {
case utils.Int2Str(model.VendorIDJD): // 京东
printVoiceValue = append(printVoiceValue, model.JdVoice)
case utils.Int2Str(model.VendorIDMTWM): // 美团
printVoiceValue = append(printVoiceValue, model.MtVoice)
case utils.Int2Str(model.VendorIDELM): // 饿了么
printVoiceValue = append(printVoiceValue, model.ElmVoice)
case utils.Int2Str(model.VendorIDEBAI): // 饿百
printVoiceValue = append(printVoiceValue, model.ElmVoice)
case utils.Int2Str(model.VendorIDJDShop): // 京东商城
printVoiceValue = append(printVoiceValue, model.JdToHose)
case utils.Int2Str(model.VendorIDTT): // 抖音
// 暂无
}
if param[model.VendorOrderNoPrint] != "" {
switch len(param[model.VendorOrderNoPrint]) {
case 1:
printVoiceMsg += `<sound>%d</sound>`
printVoiceValue = append(printVoiceValue, model.NumberVoiceMap[param[model.VendorOrderNoPrint]])
case 2:
printVoiceMsg += `<sound>%d</sound>`
printVoiceValue = append(printVoiceValue, model.NumberVoiceMap[param[model.VendorOrderNoPrint][:1]+"0"])
if param[model.VendorOrderNoPrint][1:] != "0" {
printVoiceMsg += `<sound>%d</sound>`
printVoiceValue = append(printVoiceValue, model.NumberVoiceMap[param[model.VendorOrderNoPrint][1:]])
}
case 3:
printVoiceMsg += `<sound>%d</sound>`
printVoiceValue = append(printVoiceValue, model.NumberVoiceMap[param[model.VendorOrderNoPrint][0:1]+"00"])
if param[model.VendorOrderNoPrint][1:2] == "0" && param[model.VendorOrderNoPrint][2:] == "0" {
} else if param[model.VendorOrderNoPrint][1:2] == "0" && param[model.VendorOrderNoPrint][2:] != "0" {
printVoiceMsg += `<sound>%d</sound>`
printVoiceValue = append(printVoiceValue, model.NumberVoiceMap[param[model.VendorOrderNoPrint][1:2]])
printVoiceMsg += `<sound>%d</sound>`
printVoiceValue = append(printVoiceValue, model.NumberVoiceMap[param[model.VendorOrderNoPrint][2:]])
} else if param[model.VendorOrderNoPrint][1:2] != "0" && param[model.VendorOrderNoPrint][2:] == "0" {
printVoiceMsg += `<sound>%d</sound>`
printVoiceValue = append(printVoiceValue, model.NumberVoiceMap[param[model.VendorOrderNoPrint][1:2]+"0"])
} else if param[model.VendorOrderNoPrint][1:2] != "0" && param[model.VendorOrderNoPrint][2:] != "0" {
printVoiceMsg += `<sound>%d</sound>`
printVoiceValue = append(printVoiceValue, model.NumberVoiceMap[param[model.VendorOrderNoPrint][1:2]+"0"])
printVoiceMsg += `<sound>%d</sound>`
printVoiceValue = append(printVoiceValue, model.NumberVoiceMap[param[model.VendorOrderNoPrint][2:]])
}
}
}
printVoiceMsg += `<sound>%d</sound>`
printVoiceValue = append(printVoiceValue, model.OrderNoVoice)
return printVoiceMsg, printVoiceValue
}

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

@@ -0,0 +1,130 @@
package cms
import (
"git.rosy.net.cn/jx-callback/business/auth2"
"git.rosy.net.cn/jx-callback/business/auth2/authprovider/weixin"
"git.rosy.net.cn/jx-callback/business/authz/autils"
"git.rosy.net.cn/jx-callback/business/model"
"git.rosy.net.cn/jx-callback/business/model/dao"
"git.rosy.net.cn/jx-callback/business/model/legacymodel"
"git.rosy.net.cn/jx-callback/globals"
"git.rosy.net.cn/jx-callback/globals/api2"
)
// todo 是否需要将Store.MarketManPhone与OperatorPhone成角色
func TransferLegacyWeixins(mobile string) (err error) {
globals.SugarLogger.Debugf("TransferLegacyWeixins mobile:%s", mobile)
if !globals.EnableWXAuth2 || globals.DisableWXAuth1 {
return nil
}
remark4Transfer := "transfer"
// DELETE t1
// FROM auth_bind t1
// WHERE t1.remark = 'transfer';
// DELETE t1
// FROM user t1
// WHERE t1.remark = 'transfer';
// TRUNCATE TABLE casbin_rule;
sql := `
SELECT t1.*
FROM weixins t1
LEFT JOIN user t2 ON t2.mobile = t1.tel
LEFT JOIN auth_bind t3 ON t3.auth_id = t1.openid AND t3.type = 'weixinsns'
LEFT JOIN auth_bind t4 ON t4.auth_id = t1.openid_mini AND t4.type = 'weixinmini'
WHERE`
sqlParams := []interface{}{}
if mobile != "" {
remark4Transfer = "transfer2"
sql += " t1.tel = ?"
sqlParams = append(sqlParams, mobile)
} else {
sql += " t2.id IS NULL OR (t1.openid <> '' AND t3.id IS NULL) OR (t1.openid_mini <> '' AND t4.id IS NULL)"
}
sql += " ORDER BY t1.parentid;"
var weixinList []*legacymodel.WeiXins
db := dao.GetDB()
err = dao.GetRows(db, &weixinList, sql, sqlParams...)
if err != nil {
return err
}
parentMap := make(map[int]*legacymodel.WeiXins)
for _, v := range weixinList {
if v.ParentID == -1 {
parentMap[v.ID] = v
} else {
if parentMap[v.ParentID] != nil {
v.JxStoreID = parentMap[v.ParentID].JxStoreID
}
}
if v.Tel != "" {
user := &model.User{
UserID2: v.Tel,
Name: v.NickName,
Mobile: &v.Tel,
Type: model.UserTypeStoreBoss,
Remark: remark4Transfer,
}
if user.Name == "" {
user.Name = user.GetMobile()
}
userList, _, err2 := dao.GetUsers(db, 0, "", nil, nil, []string{v.Tel}, 0, -1)
if err = err2; err != nil {
return err
}
// globals.SugarLogger.Debug(utils.Format4Output(user, false))
if len(userList) == 0 {
err = CreateUser(user, v.LastOperator)
} else {
user = userList[0]
}
if err != nil {
return err
}
if v.OpenID != "" {
auth2.AddAuthBind(user, &auth2.AuthInfo{
AuthBindInfo: &auth2.AuthBindEx{
AuthBind: model.AuthBind{
Type: weixin.AuthTypeMP,
AuthID: v.OpenID,
AuthID2: v.OpenIDUnion,
Remark: remark4Transfer,
},
},
})
}
if v.OpenIDMini != "" {
auth2.AddAuthBind(user, &auth2.AuthInfo{
AuthBindInfo: &auth2.AuthBindEx{
AuthBind: model.AuthBind{
Type: weixin.AuthTypeMini,
AuthID: v.OpenIDMini,
AuthID2: v.OpenIDUnion,
Remark: remark4Transfer,
},
},
})
}
if v.JxStoreID > 0 { // 运营就不加到门店老板组里了
if user.Type&model.UserTypeOperator == 0 {
api2.RoleMan.AddRole4User(user.GetID(), autils.NewStoreBossRole(v.JxStoreID))
}
} else {
if mobile != "" {
rList, err2 := api2.RoleMan.GetUserRoleList(user.GetID())
if err = err2; err == nil {
for _, role := range rList {
if role.StoreID > 0 {
api2.RoleMan.DeleteRole4User(user.GetID(), autils.NewStoreBossRole(role.StoreID))
}
}
}
}
}
}
}
return err
}

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

@@ -2,17 +2,32 @@ package cms
import (
"fmt"
"git.rosy.net.cn/baseapi/platformapi/mtunionapi"
"reflect"
"regexp"
"strconv"
"strings"
"time"
"git.rosy.net.cn/jx-callback/business/partner/purchase/ebai"
"git.rosy.net.cn/baseapi/utils/errlist"
"git.rosy.net.cn/jx-callback/globals/api2"
"git.rosy.net.cn/baseapi/platformapi/dingdingapi"
"git.rosy.net.cn/jx-callback/business/auth2/authprovider/mobile"
"git.rosy.net.cn/jx-callback/business/authz/autils"
"git.rosy.net.cn/jx-callback/business/jxutils"
"git.rosy.net.cn/jx-callback/business/partner"
"git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/business/dao"
"git.rosy.net.cn/jx-callback/business/jxutils/ddmsg"
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
"git.rosy.net.cn/jx-callback/business/jxutils/tasksch"
"git.rosy.net.cn/jx-callback/business/model"
"git.rosy.net.cn/jx-callback/business/model/dao"
"git.rosy.net.cn/jx-callback/globals/api"
)
const (
@@ -28,8 +43,61 @@ type SysConfigLimit struct {
}
var (
serviceInfo map[string]interface{}
serviceInfo map[string]interface{}
allowUpdatePlaceFieldsMap = map[string]bool{
"name": true,
"enabled": true,
"mtpsPrice": true,
}
regexpMsgContentOpID = regexp.MustCompile(`"openid":"(.*?)"`)
receiveMsgUsersMap = map[string][]string{
SendMsgTypeOpenStoreRequest: []string{
"石锋",
// "x",
// "周扬",
},
SendMsgTypeSuggestRequest: []string{
"石锋",
// "x",
// "周扬",
// "苏尹岚",
},
}
needConfirmRequestMap = map[string]int{
SendMsgTypeOpenStoreRequest: 1,
}
SysConfigLimitMap = map[string]*SysConfigLimit{
model.ConfigSysEbaiBoxFee: &SysConfigLimit{
ValueType: reflect.Int,
MinValue: 0,
MaxValue: 500,
AfterChanged: func() (err error) {
_, err = dao.SetStoreMapSyncStatus(dao.GetDB(), []int{model.VendorIDEBAI}, nil, model.SyncFlagModifiedMask)
return err
},
},
model.ConfigSysMtwmBoxFee: &SysConfigLimit{
ValueType: reflect.Int,
MinValue: 0,
MaxValue: 500,
AfterChanged: func() (err error) {
_, err = dao.SetStoreMapSyncStatus(dao.GetDB(), []int{model.VendorIDMTWM}, nil, model.SyncFlagModifiedMask)
return err
},
},
model.ConfigSysMtwmSkuBoxFee: &SysConfigLimit{
ValueType: reflect.Int,
MinValue: 0,
MaxValue: 50,
AfterChanged: func() (err error) {
_, err = dao.SetStoreSkuSyncStatus(dao.GetDB(), model.VendorIDMTWM, nil, nil, model.SyncFlagModifiedMask)
return err
},
},
}
)
func InitServiceInfo(version string, buildTime time.Time, gitCommit string) {
@@ -43,41 +111,49 @@ func InitServiceInfo(version string, buildTime time.Time, gitCommit string) {
"buildTime": buildTimeStr,
"gitCommit": gitCommit,
"metaData": map[string]interface{}{
"vendorTypeName": model.VendorTypeName,
"vendorName": model.VendorChineseNames,
//"vendorImg": model.VendorImg,
//"vendorColors": model.VendorColors,
"skuNamePrefix": model.SkuNamePrefixNames,
"skuNameUnit": model.UnitNames,
"skuSpecUnit": model.SpecUnitNames,
"skuStatus": model.SkuStatusName,
"storeDeliveryRangeType": model.DeliveryRangeTypeName,
"storeDeliveryType": model.DeliveryTypeName,
"storeStatus": model.StoreStatusName,
"categoryType": model.CategoryTypeName,
"vendorTypeName": model.VendorTypeName,
"vendorName": model.VendorChineseNames,
"orderStatus": model.OrderStatusName,
"waybillStatus": model.WaybillStatusName,
"orderTypeName": model.OrderTypeName,
"taskStatusName": tasksch.TaskStatusName,
"opRequestTypeName": model.RequestTypeName,
"opRequestStatusName": model.RequestStatusName,
"storeMsgSendStatusName": model.StoreMsgSendStatusName,
"shopChineseNames": model.ShopChineseNames,
"printerVendorInfo": model.PrinterVendorInfo,
"printerStatusName": partner.PrinterStatusName,
"purchaseVendorInfo": model.PurchaseVendorInfo,
"afsReasonTypeName": model.AfsReasonTypeName,
"afsAppealTypeName": model.AfsAppealTypeName,
"actTypeName": model.ActTypeName,
"actStatusName": model.ActStatusName,
"actCreateTypeName": model.ActCreateTypeName,
"storeAuditStatusName": model.StoreAuditStatusName,
"configTypeName": model.ConfigTypeName,
"autoSaleAt": AutoSaleAtStr,
"userTypeName": model.UserTypeName,
"storePriceTypeName": model.StorePriceTypeName,
"payStatusName": model.PayStatusName,
"refundStatusName": model.RefundStatusName,
"autoReplyTypeName": model.AutoReplyTypeName,
"complaintReasons": model.ComplaintReasons,
"supplementType": model.SupplementTypeName,
"operateType": model.OperateTypeName,
"thingType": model.ThingTypeName,
"apiFunctionName": model.ApiFunctionName,
"vendorStatus": model.VendorStatus,
"unionActTypeNames": map[int]map[int]interface{}{
model.VendorIDMTWM: map[int]interface{}{
mtunionapi.ActTypeQB: "券包推广",
},
//model.VendorIDTB: map[int]interface{}{
// tbunionapi.TbElmActTypeBDH: "本地化",
//},
//model.VendorIDPDD: map[int]interface{}{
// 1: "进行中的活动",
//},
model.VendorIDJDShop: map[int]interface{}{
2: "进行中",
},
},
"unionOrderStatusName": model.UnionOrderStatusName,
"couponsStatus": model.CouponStatusName,
"ebaiSupplierID": ebai.EbaiSupplierIDMap,
"ebaiSupplierInfo": ebai.EbaiSupplierInfo,
},
}
}
@@ -119,15 +195,213 @@ func GetPlaces(ctx *jxcontext.Context, keyword string, includeDisabled bool, par
return places, dao.GetRows(nil, &places, sql, sqlParams)
}
func SendMsg2Somebody(ctx *jxcontext.Context, mobileNum, verifyCode, msgType, msgContent string) (err error) {
func UpdatePlaces(ctx *jxcontext.Context, places []map[string]interface{}, userName string) (num int64, err error) {
if len(places) == 0 {
return 0, ErrMissingInput
}
updateFields := []string{}
for k := range places[0] {
if allowUpdatePlaceFieldsMap[k] {
updateFields = append(updateFields, k)
}
}
for _, place := range places {
if place["code"] == nil {
return 0, ErrMissingInput
}
placeid := &model.Place{}
valid := dao.NormalMakeMapByFieldList(place, updateFields, userName)
if num, err = dao.UpdateEntityLogically(nil, placeid, valid, userName, utils.Params2Map("Code", place["code"])); err != nil {
return num, err
}
}
return num, err
}
func UpdatePlace(ctx *jxcontext.Context, placeCode int, payload map[string]interface{}, userName string) (num int64, err error) {
payload["code"] = placeCode
return UpdatePlaces(ctx, []map[string]interface{}{payload}, userName)
}
func GetCoordinateDistrictCode(ctx *jxcontext.Context, lng, lat float64) (code int, err error) {
return api.AutonaviAPI.GetCoordinateDistrictCode(lng, lat), nil
}
func GetCoordinateCityInfo(ctx *jxcontext.Context, lng, lat float64) (name string, err error) {
name, _ = api.AutonaviAPI.GetCoordinateCityInfo(lng, lat)
return name, err
}
func SendMsg2Somebody(ctx *jxcontext.Context, mobileNum, verifyCode, msgType, msgContent string) (err error) {
if needConfirmRequestMap[msgType] == 1 {
if _, err = mobile.AutherObj.VerifySecret(mobileNum, verifyCode); err != nil {
return err
}
}
db := dao.GetDB()
//获取门店信息
var (
stores []*model.Store
authBinds []*model.AuthBind
order *model.GoodsOrder
storeName string
storeID int
vendorOrderID string
)
if mobileNum != "" {
sql := `
SELECT * FROM store WHERE (tel1 = ? OR tel2 = ?) AND deleted_at = ?
`
sqlParams := []interface{}{mobileNum, mobileNum, utils.DefaultTimeValue}
err = dao.GetRows(db, &stores, sql, sqlParams)
if len(stores) > 0 {
storeName = stores[0].Name
storeID = stores[0].ID
}
if storeID == 0 {
results := regexpMsgContentOpID.FindStringSubmatch(msgContent)
if len(results) > 0 {
sql3 := `
SELECT * FROM auth_bind WHERE auth_id = ? OR auth_id2 = ?
`
sqlParams3 := []interface{}{results[1], results[1]}
err = dao.GetRows(db, &authBinds, sql3, sqlParams3)
if len(authBinds) > 0 {
user, _ := dao.GetUserByID(db, "user_id", authBinds[0].UserID)
mobileNum = *user.Mobile
sqlParams4 := []interface{}{mobileNum, mobileNum, utils.DefaultTimeValue}
err = dao.GetRows(db, &stores, sql, sqlParams4)
if len(stores) > 0 {
storeName = stores[0].Name
storeID = stores[0].ID
}
}
}
}
} else {
results := regexpMsgContentOpID.FindStringSubmatch(msgContent)
if len(results) > 0 {
sql3 := `
SELECT * FROM auth_bind WHERE auth_id = ? OR auth_id2 = ?
`
sqlParams3 := []interface{}{results[1], results[1]}
err = dao.GetRows(db, &authBinds, sql3, sqlParams3)
if len(authBinds) > 0 {
user, _ := dao.GetUserByID(db, "user_id", authBinds[0].UserID)
mobileNum = *user.Mobile
sql4 := `
SELECT * FROM store WHERE (tel1 = ? OR tel2 = ?) AND deleted_at = ?
`
sqlParams4 := []interface{}{mobileNum, mobileNum, utils.DefaultTimeValue}
err = dao.GetRows(db, &stores, sql4, sqlParams4)
if len(stores) > 0 {
storeName = stores[0].Name
storeID = stores[0].ID
}
}
}
}
sql2 := `
SELECT *
FROM goods_order
WHERE IF(store_id <> '', store_id, jx_store_id) = ?
ORDER BY order_created_at DESC
LIMIT 1
`
sqlParams2 := []interface{}{storeID}
err = dao.GetRow(db, &order, sql2, sqlParams2)
if order != nil {
vendorOrderID = order.VendorOrderID
}
if storeID == 0 {
vendorOrderID = ""
}
msgContent = msgContent + " 门店名称:" + storeName + " 门店ID" + utils.Int2Str(storeID) + " 最新订单号:" + vendorOrderID
for _, v := range receiveMsgUsersMap[msgType] {
user, err2 := dao.GetUserByID(db, "name", v)
if err2 == nil {
ddmsg.SendUserMessage(dingdingapi.MsgTyeText, user.GetID(), msgType, msgContent)
} else if err == nil {
err = err2
}
}
return err
}
func checkSysConfig(key, value string) (err error) {
if limit := SysConfigLimitMap[key]; limit != nil {
if limit.ValueType == reflect.Int {
int64Value, err2 := strconv.ParseInt(value, 10, 64)
if err = err2; err == nil {
if int64Value < limit.MinValue || int64Value > limit.MaxValue {
err = fmt.Errorf("配置%s,值%s超范围[%d,%d]", key, value, limit.MinValue, limit.MaxValue)
}
}
}
}
return err
}
func onSysConfigChanged(key, value string) (err error) {
if limit := SysConfigLimitMap[key]; limit != nil && limit.AfterChanged != nil {
err = limit.AfterChanged()
}
return err
}
func checkConfig(opFlag int, configType, key, value string) (err error) {
switch configType {
case model.ConfigTypePricePack:
if value != "" {
pricePack := dao.PricePercentagePack2Obj(value)
if pricePack == nil {
err = fmt.Errorf("配置:%s不合法", value)
}
}
case model.ConfigTypeFreightPack:
if value != "" {
freightPack := dao.FreightDeductionPack2Obj(value)
if freightPack == nil {
err = fmt.Errorf("配置:%s不合法", value)
} else {
var lastStage *model.FreightDeductionItem
for _, v := range freightPack.FreightDeductionList {
if lastStage != nil && lastStage.DeductFreight > v.DeductFreight {
err = fmt.Errorf("免运设置不合理:门槛:%s,免运金额:%s,门槛:%s,免运金额:%s",
jxutils.IntPrice2StandardString(int64(lastStage.BeginPrice)), jxutils.IntPrice2StandardString(int64(lastStage.DeductFreight)),
jxutils.IntPrice2StandardString(int64(v.BeginPrice)), jxutils.IntPrice2StandardString(int64(v.DeductFreight)))
return err
}
lastStage = v
}
}
}
case model.ConfigTypeBank:
if value != "" {
if model.BankName[key] == "" {
err = fmt.Errorf("此银行代码:%s不支持请联系开发", value)
}
}
case model.ConfigTypeRole:
case model.ConfigTypeSys:
if opFlag&( /*model.SyncFlagNewMask|*/ model.SyncFlagDeletedMask) != 0 {
err = fmt.Errorf("系统参数只支持修改或添加,不支持删除")
} else {
err = checkSysConfig(key, value)
}
case model.ConfigTypeJxStore:
case model.ConfigTypeCookie:
case model.ConfigTypeDiscountCard:
default:
err = fmt.Errorf("当前只支持配置:%s, 传入的配置类型:%s", utils.Format4Output(model.ConfigTypeName, true), configType)
}
return err
}
func AddConfig(ctx *jxcontext.Context, key, configType, value string) (err error) {
// if err = checkConfig(model.SyncFlagNewMask, configType, key, value); err != nil {
// return err
// }
if err = checkConfig(model.SyncFlagNewMask, configType, key, value); err != nil {
return err
}
db := dao.GetDB()
conf := &model.NewConfig{
@@ -138,7 +412,75 @@ func AddConfig(ctx *jxcontext.Context, key, configType, value string) (err error
dao.WrapAddIDCULDEntity(conf, ctx.GetUserName())
err = dao.CreateEntity(db, conf)
if configType == model.ConfigTypeSys && err == nil {
// err = onSysConfigChanged(key, value)
err = onSysConfigChanged(key, value)
}
return err
}
func DeleteConfig(ctx *jxcontext.Context, key, configType string) (err error) {
if err = checkConfig(model.SyncFlagDeletedMask, configType, key, ""); err != nil {
return err
}
db := dao.GetDB()
switch configType {
case model.ConfigTypePricePack:
storeMapList, err2 := dao.GetStoresMapList(db, nil, nil, nil, model.StoreStatusAll, model.StoreIsSyncYes, key, "", "")
if err = err2; err == nil {
var storeInfo []string
for _, v := range storeMapList {
storeInfo = append(storeInfo, fmt.Sprintf("门店:%d, 平台:%s", v.StoreID, model.VendorChineseNames[v.VendorID]))
}
if len(storeInfo) > 0 {
err = fmt.Errorf("还有门店在使用价格包:%s门店信息:%s", key, strings.Join(storeInfo, ","))
}
}
case model.ConfigTypeFreightPack:
storeMapList, err2 := dao.GetStoresMapList(db, nil, nil, nil, model.StoreStatusAll, model.StoreIsSyncYes, "", "", "")
if err = err2; err == nil {
var storeInfo []string
for _, v := range storeMapList {
if v.FreightDeductionPack == key {
storeInfo = append(storeInfo, fmt.Sprintf("门店:%d, 平台:%s", v.StoreID, model.VendorChineseNames[v.VendorID]))
}
}
if len(storeInfo) > 0 {
err = fmt.Errorf("还有门店在使用价格包:%s门店信息:%s", key, strings.Join(storeInfo, ","))
}
}
case model.ConfigTypeBank:
//todo
return fmt.Errorf("暂不支持删除银行")
case model.ConfigTypeRole:
errList := errlist.New()
userIDs, err2 := api2.RoleMan.GetRoleUserList(autils.NewRole(key, 0))
if err = err2; err == nil && len(userIDs) > 0 {
userList, totalCount, err2 := dao.GetUsers(dao.GetDB(), 0, "", userIDs, nil, nil, 0, -1)
if err = err2; err == nil && totalCount > 0 {
// todo
// err = fmt.Errorf("还有人员在使用角色:%s人员信息:%s", key, utils.MustMarshal(utils.Struct2Map(userList, "compact")))
err = fmt.Errorf("还有人员在使用角色:%s人员信息:%s", key, utils.Format4Output(userList, false))
}
}
errList.AddErr(err)
storeList, err2 := dao.GetStoreList(db, nil, nil, nil, nil, nil, key)
if err = err2; err == nil && len(storeList) > 0 {
storeIDs := make([]int, len(storeList))
for k, v := range storeList {
storeIDs[k] = v.ID
}
err = fmt.Errorf("还有门店在使用角色:%s门店信息:%s", key, utils.MustMarshal(storeIDs))
}
errList.AddErr(err)
err = errList.GetErrListAsOne()
}
if err == nil {
_, err = dao.DeleteEntityLogically(db, &model.NewConfig{}, nil, ctx.GetUserName(), map[string]interface{}{
"Key": key,
"Type": configType,
})
}
if configType == model.ConfigTypeSys && err == nil {
err = onSysConfigChanged(key, "")
}
return err
}
@@ -147,10 +489,13 @@ func UpdateConfig(ctx *jxcontext.Context, key, configType, value string) (hint s
if key == "" {
return "", fmt.Errorf("修改配置必须给定key")
}
if err = checkConfig(model.SyncFlagModifiedMask, configType, key, value); err != nil {
return "", err
}
hint = "1"
db := dao.GetDB()
txDB, _ := dao.Begin(db)
txDB , _ := dao.Begin(db)
defer func() {
if r := recover(); r != nil {
dao.Rollback(db, txDB)
@@ -170,12 +515,54 @@ func UpdateConfig(ctx *jxcontext.Context, key, configType, value string) (hint s
}
switch configType {
case model.ConfigTypePricePack:
storeMapList, err := dao.GetStoresMapList(db, nil, nil, nil, model.StoreStatusAll, model.StoreIsSyncYes, key, "", "")
if err != nil {
dao.Rollback(db, txDB)
return "", err
}
dao.Commit(db, txDB)
vendorStoreMap := make(map[int][]int)
for _, v := range storeMapList {
vendorStoreMap[v.VendorID] = append(vendorStoreMap[v.VendorID], v.StoreID)
}
for vendorID, storeIDs := range vendorStoreMap {
if vendorID != model.VendorIDJX {
dao.SetStoreSkuSyncStatus(db, vendorID, storeIDs, nil, model.SyncFlagPriceMask)
} else {
hint, err = ReCalculateJxPrice(db, ctx, storeIDs)
}
}
case model.ConfigTypeFreightPack:
dao.Commit(db, txDB)
storeMapList, err := dao.GetStoresMapList(db, nil, nil, nil, model.StoreStatusAll, model.StoreIsSyncYes, "", "", "")
if err != nil {
return "", err
}
for _, v := range storeMapList {
var storeMapList2 []*model.StoreMap
if v.FreightDeductionPack == key {
storeMapList2 = append(storeMapList2, v)
}
if len(storeMapList2) > 0 {
task := tasksch.NewParallelTask("同步门店配送免运", tasksch.NewParallelConfig().SetIsContinueWhenError(true), ctx,
func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
storeMap := batchItemList[0].(*model.StoreMap)
_, err = CurVendorSync.SyncStore(ctx, db, storeMap.VendorID, storeMap.StoreID, false, ctx.GetUserName())
return retVal, err
}, storeMapList2)
tasksch.HandleTask(task, nil, true).Run()
if len(storeMapList2) < 5 {
_, err = task.GetResult(0)
} else {
hint = task.GetID()
}
}
}
default:
dao.Commit(db, txDB)
}
if configType == model.ConfigTypeSys && err == nil {
// err = onSysConfigChanged(key, value)
err = onSysConfigChanged(key, value)
}
return hint, err
}
@@ -183,3 +570,17 @@ func UpdateConfig(ctx *jxcontext.Context, key, configType, value string) (hint s
func QueryConfigs(key, configType, keyword string) (configList []*model.NewConfig, err error) {
return dao.QueryConfigs(dao.GetDB(), key, configType, keyword)
}
func GetCityBankBranches(ctx *jxcontext.Context, cityCode int, bankCode string) (info map[int]map[string][]string, err error) {
list, err := dao.GetCityBankBranches(dao.GetDB(), cityCode, bankCode)
if err == nil && len(list) > 0 {
info = make(map[int]map[string][]string)
for _, v := range list {
if info[v.CityCode] == nil {
info[v.CityCode] = make(map[string][]string)
}
info[v.CityCode][v.PayeeBankCode] = append(info[v.CityCode][v.PayeeBankCode], v.PayeeBankBranchName)
}
}
return info, err
}

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

View File

@@ -1,11 +0,0 @@
package cms
import (
"fmt"
"testing"
)
func TestConn(t *testing.T) {
err := DelPrinterSeq(1000, "120220915001012")
fmt.Println(err)
}

View File

@@ -4,10 +4,10 @@ import (
"time"
"git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/business/dao"
"git.rosy.net.cn/jx-callback/business/jxutils"
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
"git.rosy.net.cn/jx-callback/business/model"
"git.rosy.net.cn/jx-callback/business/model/dao"
)
type MessageStatusExt struct {
@@ -65,20 +65,20 @@ func GetStoreMessages(ctx *jxcontext.Context, msgIDs, storeIDs, types []int, fro
pageSize = jxutils.FormalizePageSize(pageSize)
sqlParams = append(sqlParams, pageSize, offset)
db := dao.GetDB()
txDB, _ := dao.Begin(db)
txDB , _ := dao.Begin(db)
defer dao.Commit(db, txDB)
var msgList []*model.Message
// globals.SugarLogger.Debug(sql)
if err = dao.GetRowsTx(txDB, &msgList, sql, sqlParams...); err == nil {
pagedInfo = &model.PagedInfo{
TotalCount: dao.GetLastTotalRowCountTx(txDB),
TotalCount: dao.GetLastTotalRowCount2(db, txDB),
Data: msgList,
}
}
return pagedInfo, err
}
func GetStoreMessageStatuses(ctx *jxcontext.Context, msgIDs, storeIDs []int, fromReadCount, toReadCount int, fromTime, toTime time.Time, keyword string, offset, pageSize int) (pagedInfo *model.PagedInfo, err error) {
func GetStoreMessageStatuses(ctx *jxcontext.Context, msgIDs, storeIDs, confirmStatuss []int, fromReadCount, toReadCount int, fromTime, toTime time.Time, keyword string, offset, pageSize int) (pagedInfo *model.PagedInfo, err error) {
sql := `
SELECT SQL_CALC_FOUND_ROWS
t1.*,
@@ -92,6 +92,10 @@ func GetStoreMessageStatuses(ctx *jxcontext.Context, msgIDs, storeIDs []int, fro
sql += " AND t1.store_id IN (" + dao.GenQuestionMarks(len(storeIDs)) + ")"
sqlParams = append(sqlParams, storeIDs)
}
if len(confirmStatuss) > 0 {
sql += " AND t1.confirm_status IN (" + dao.GenQuestionMarks(len(confirmStatuss)) + ")"
sqlParams = append(sqlParams, confirmStatuss)
}
if len(msgIDs) > 0 {
sql += " AND t1.message_id IN (" + dao.GenQuestionMarks(len(msgIDs)) + ")"
sqlParams = append(sqlParams, msgIDs)
@@ -120,14 +124,14 @@ func GetStoreMessageStatuses(ctx *jxcontext.Context, msgIDs, storeIDs []int, fro
sql += " LIMIT ? OFFSET ?"
sqlParams = append(sqlParams, jxutils.FormalizePageSize(pageSize), offset)
db := dao.GetDB()
txDB, _ := dao.Begin(db)
txDB , _ := dao.Begin(db)
defer dao.Commit(db, txDB)
var msgStatusList []*MessageStatusExt
// globals.SugarLogger.Debug(sql)
// globals.SugarLogger.Debug(utils.Format4Output(sqlParams, false))
if err = dao.GetRowsTx(txDB, &msgStatusList, sql, sqlParams...); err == nil {
pagedInfo = &model.PagedInfo{
TotalCount: dao.GetLastTotalRowCountTx(txDB),
TotalCount: dao.GetLastTotalRowCount2(db, txDB),
Data: msgStatusList,
}
}

View File

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

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/dao"
"git.rosy.net.cn/jx-callback/business/jxutils"
"git.rosy.net.cn/jx-callback/business/model"
"net"
"regexp"
"time"
)
const (
sounda = "sounda" //提示音a
soundb = "soundb" //提示音b
soundc = "soundc" //提示音c
soundd = "soundd" //提示音d
sounde = "sounde" //报警音e
soundf = "soundf" //报警音f
soundg = "soundg" //报警音g
)
type PrintInfo struct {
PrintNo string
AppID int
}
var (
regexpMobile = regexp.MustCompile("^1(3[0-9]|4[01456879]|5[0-35-9]|6[2567]|7[0-8]|8[0-9]|9[0-35-9])\\d{8}$")
soundMap = map[string]string{
"sounda": "sounda",
"soundb": "soundb",
"soundc": "soundc",
"soundd": "soundd",
"sounde": "sounde",
"soundf": "soundf",
"soundg": "soundg",
}
)
func AddPrinter(appID int, printers []*model.AddPrinterParam) (err error) {
var (
db = dao.GetDB()
errs []error
)
if len(printers) > 50 {
return fmt.Errorf("一次最多只能绑定50台")
}
for _, v := range printers {
if printers2, _ := dao.GetPrinters(db, appID, v.PrintNo, 0, 0); len(printers2) > 0 {
// 代表打印机已经在小程序注册了,查询打印机授权门店
bindStoreList, err := dao.QueryPrintBindStore(v.PrintNo)
if err != nil {
errs = append(errs, fmt.Errorf("QueryPrintBindStore err : %v ", err))
continue
}
if len(bindStoreList) >= 5 {
errs = append(errs, fmt.Errorf("当前打印机绑定门店数据超过五个,无法继续绑定"))
continue
}
have := false
userId := ""
for _, bsl := range bindStoreList {
if bsl.StoreID == v.StoreId {
have = true
userId = bsl.UserId
}
}
if !have {
if err := dao.BindStoreList(printers[0], userId); err != nil {
errs = append(errs, fmt.Errorf("BindStoreList err : %v ", err))
continue
}
}
continue
}
//验证
if err = checkPrinterInfo(v.PrintNo, v.Name, "", "", 0); err != nil {
errs = append(errs, err)
continue
}
// 检查心跳
exits, err := dao.CheckHeard(v.PrintNo)
if err != nil {
errs = append(errs, fmt.Errorf("CheckHeard err : %v ", err))
continue
}
if !exits {
errs = append(errs, fmt.Errorf("打印机未激活,请激活后在绑定"))
continue
}
printer := &model.Printer{
AppID: appID,
PrintNo: v.PrintNo,
Name: v.Name,
IccID: "",
Status: model.PrinterStatusOffline,
Sound: "sounda",
PrintKey: v.SIM,
IsOnline: 0,
Volume: 1,
FlowFlag: 0,
OfflineCount: 0,
UserId: "system_user",
}
// 创建打印机
if err := InitPrint(printer, v); err != nil {
return err
}
}
if len(errs) > 0 {
err = jxutils.BuildErr(errs)
}
return err
}
func InitPrint(printer *model.Printer, printParam *model.AddPrinterParam) error {
txDb, _ := dao.Begin(dao.GetDB())
// 创建打印机
dao.WrapAddIDCULDEntity(printer, "")
if err := dao.CreateEntityTx(txDb, printer); err != nil {
txDb.Rollback()
return err
}
// 赋予打印机默认配置
day := time.Now()
param, err := MarshalJson2String(&model.PrintSetting{
CreatedAt: day,
UpdatedAt: day,
DeletedAt: utils.DefaultTimeValue,
PrintNo: printer.PrintNo,
CallNameSetting: 64, // 老板
BusinessOffLineVoice: 1, // 离线开关
BalanceNotEnoughVoice: 1, // 余额不足
EveryDayGreetVoice: 1, // 问好
BusinessPrintNum: 1, // 商户侧打印次数
CustomerPrintNum: 1, // 用户侧打印次数
})
if err != nil {
return err
}
if err := dao.CreateEntityTx(txDb, param); err != nil {
txDb.Rollback()
return err
}
// 初始化打印机账户
if err := dao.CreateEntityTx(txDb, &model.PrintBill{
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
PrintNo: param.PrintNo,
PrintBalance: 20000,
UserId: "system",
}); err != nil {
txDb.Rollback()
return err
}
// 初始化绑定信息
if err := dao.BindStoreList(printParam, ""); err != nil {
txDb.Rollback()
return err
}
defer txDb.Commit()
return err
}
// MarshalJson2String 工具类
func MarshalJson2String(param *model.PrintSetting) (*model.PrintSetting, error) {
// 语音设置
voiceSetting := &model.VoiceSettingDetail{
WaitOrderVoice: model.SettingOpen,
RiderTakeOrderVoice: model.SettingOpen,
ApplyUserOrderCancelVoice: model.SettingOpen,
ApplyRefundOrderVoice: model.SettingOpen,
ApplyRefundGoodsVoice: model.SettingOpen,
RefundGoodsVoice: model.SettingOpen,
ConfirmGoodsVoice: model.SettingOpen,
SuccessGoodsVoice: model.SettingOpen,
ConsultingPrint: model.SettingOpen,
ReminderVoice: model.SettingOpen,
CustomerRejectionVoice: model.SettingOpen,
CusterRefundVoice: model.SettingOpen,
LoseAuthorization: model.SettingOpen,
}
customerVoiceSettingByte, err := json.Marshal(voiceSetting)
if err != nil {
return nil, err
}
param.VoiceSetting = string(customerVoiceSettingByte)
// 打印设置
printSetting := &model.PrintSettingDetail{
UserOrderCancel: model.SettingOpen,
RefundOrder: model.SettingOpen,
BusinessOrderCancel: model.SettingOpen,
RiderTakeOrder: model.SettingOpen,
CusterRefundPrint: model.SettingOpen,
WaitOrderPrint: model.SettingOpen,
ApplyUserCancelOrder: model.SettingOpen,
ApplyUserRefund: model.SettingOpen,
OrderCancelSuccess: model.SettingOpen,
}
pickingSettingByte, err := json.Marshal(printSetting)
if err != nil {
return nil, err
}
param.PrintSetting = string(pickingSettingByte)
return param, nil
}
func checkPrinterInfo(printNo, name, sound, sim string, volume int) (err error) {
if printNo != "" {
}
if sim != "" {
//if regexpMobile.FindString(sim) == "" {
return fmt.Errorf("暂不支持修改sim卡号码print_no : %v ", printNo)
//}
}
if volume != 0 {
if volume <= 0 || volume > 5 {
return fmt.Errorf("请输入正确的音量1-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 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.GetPrintMsgs(db, printNo, "", model.PrintMsgAll, model.PrintMsgSuccess)
for _, v := range printMsgs {
v.DeletedAt = time.Now()
if _, err = dao.UpdateEntity(db, v, "DeletedAt"); err != nil {
return err
}
}
}
// 简历连接
conn, err := net.Dial("tcp", "www.jxcs.net:8000")
if err != nil {
return err
}
clearPrint := fmt.Sprintf(`{"print_no_clear":%s}`, printNo)
// 发送数据
if _, err := conn.Write([]byte(clearPrint)); err != nil {
return err
}
// 等待数据
buf := make([]byte, 1024)
n, err := conn.Read(buf)
if err != nil {
return err
}
if string(buf[:n]) != "ok" {
return errors.New("缓存清理失败")
}
defer conn.Close()
return err
}
func DoPrintMsg(appID int, msgID, printNo, content string, orderNo string) (err error) {
var (
db = dao.GetDB()
)
//打印机必须绑定在该应用下才能打印
if printers, _ := dao.GetPrinters(db, appID, printNo, 0, 0); len(printers) == 0 {
return fmt.Errorf("未在该应用下获取到此打印机print_no %v", printNo)
}
printMsg := &model.PrintMsg{
PrintNo: printNo,
Content: content,
OrderNo: orderNo,
MsgID: msgID,
}
dao.WrapAddIDCULDEntity(printMsg, "")
if err = dao.CreateEntity(db, printMsg); err != nil {
return err
}
return err
}
type GetPrintMsgResult struct {
MsgID string `json:"msg_id"` //消息ID
PrintNo string `json:"print_no"` //打印机编号
OrderNo string `json:"order_no"` //订单序号
Content string `json:"content"` //订单内容
Status int `json:"status"` //打印状态
Comment string `json:"comment"` //失败原因
}
func GetPrintMsg(appID int, msgID string) (printMsg *GetPrintMsgResult, err error) {
var (
db = dao.GetDB()
)
if printMsgs, _ := dao.GetPrintMsgs(db, "", msgID, model.PrintMsgAll, model.PrintMsgAll); len(printMsgs) > 0 {
result := printMsgs[0]
printMsg = &GetPrintMsgResult{
MsgID: result.MsgID,
PrintNo: result.PrintNo,
OrderNo: result.OrderNo,
Content: result.Content,
Status: result.Status,
Comment: result.Comment,
}
return printMsg, err
} else {
return printMsg, fmt.Errorf("未找到该消息msg_id :%v", msgID)
}
}
func GetPrinterStatus(appID int, printNo string) (status int, err error) {
var (
db = dao.GetDB()
)
//看有没有
if printers, _ := dao.GetPrinters(db, appID, printNo, 0, 0); len(printers) == 0 {
return status, fmt.Errorf("该应用下未找到该打印机print_no : %v", printNo)
} else {
return printers[0].Status + printers[0].IsOnline, nil // 当两个值都唯一时->在线正常
server := "print.jxcs.net:8000"
tcpAddr, err := net.ResolveTCPAddr("tcp4", server)
if err != nil {
//os.Exit(1)
return status, err
}
conn, err := net.DialTCP("tcp", nil, tcpAddr)
if err != nil {
return status, err
}
status = connHandler(conn, &PrintInfo{
PrintNo: printNo,
AppID: appID,
})
return status, nil
}
}
func connHandler(c net.Conn, printInfo *PrintInfo) (status int) {
defer c.Close()
//缓冲
buf := make([]byte, 1024)
data, _ := json.Marshal(printInfo)
//写入数据
c.Write(data)
//服务器端返回的数据写入buf
n, _ := c.Read(buf)
status = utils.Str2Int(string(buf[:n]))
//服务器端回传的信息
fmt.Println("server response:", string(buf[:n]))
return status
}
//#region 打印机拼装模板
// QueryPrinterSetting 查询用户设置
func QueryPrinterSetting() {
}
//#endregion 打印机

3301
business/jxstore/cms/sku.go Normal file

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

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

@@ -6,10 +6,10 @@ import (
"git.rosy.net.cn/baseapi/platformapi/autonavi"
"git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/business/dao"
"git.rosy.net.cn/jx-callback/business/jxutils"
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
"git.rosy.net.cn/jx-callback/business/model"
"git.rosy.net.cn/jx-callback/business/model/dao"
"git.rosy.net.cn/jx-callback/globals/api"
)
@@ -67,7 +67,7 @@ func GetNearSupplyGoodsStoreByStoreID(ctx *jxcontext.Context, storeID int) (stor
stores []*model.Store
db = dao.GetDB()
)
store2, _ := dao.GetStoreDetail(db, storeID, model.VendorIDJX)
store2, _ := dao.GetStoreDetail(db, storeID, model.VendorIDJX, "")
if store2 == nil {
return nil, fmt.Errorf("该门店未绑定京西平台storeID: %v", storeID)
}
@@ -109,7 +109,7 @@ func GetNearSupplyGoodsStoreByStoreID(ctx *jxcontext.Context, storeID int) (stor
return store, err
}
func GetStoreListByLocation(ctx *jxcontext.Context, lng, lat float64, maxRadius int, needWalkDistance, isJds bool) (storeList []*Store4User, err error) {
func GetStoreListByLocation(ctx *jxcontext.Context, lng, lat float64, maxRadius int, needWalkDistance, isJds bool, brandID int) (storeList []*Store4User, err error) {
const (
maxStoreCount4User = 5
)
@@ -134,14 +134,20 @@ func GetStoreListByLocation(ctx *jxcontext.Context, lng, lat float64, maxRadius
WHERE t1.deleted_at = ? AND t1.status <> ? AND t1.lng > ? AND t1.lng < ? AND t1.lat > ? AND t1.lat < ?
AND sm.is_order <> ?
AND t1.id <> ?
ORDER BY t1.id
`
sqlParams = []interface{}{
sqlParams = append(sqlParams,
model.VendorIDJX, utils.DefaultTimeValue, model.StoreStatusDisabled,
utils.DefaultTimeValue, model.StoreStatusDisabled, jxutils.StandardCoordinate2Int(lng1), jxutils.StandardCoordinate2Int(lng2), jxutils.StandardCoordinate2Int(lat1), jxutils.StandardCoordinate2Int(lat2),
model.YES,
model.MatterStoreID,
)
if brandID != 0 {
sql += " AND t1.brand_id = ?"
sqlParams = append(sqlParams, brandID)
}
sql += `
ORDER BY t1.id
`
} else {
sql = `
SELECT t1.*,
@@ -179,12 +185,12 @@ func GetStoreListByLocation(ctx *jxcontext.Context, lng, lat float64, maxRadius
city.name city_name
FROM store t1
JOIN place city ON city.code = t1.city_code
WHERE t1.deleted_at = ? AND t1.status <> ? AND t1.id = ?
WHERE t1.deleted_at = ? AND t1.id = ?
`
sqlParams2 := []interface{}{
// model.VendorIDJX, utils.DefaultTimeValue, model.StoreStatusDisabled,
utils.DefaultTimeValue,
model.StoreStatusDisabled,
// model.StoreStatusDisabled,
// jxutils.StandardCoordinate2Int(0),
// jxutils.StandardCoordinate2Int(10000),
// jxutils.StandardCoordinate2Int(0),
@@ -224,3 +230,60 @@ func GetStoreListByLocation(ctx *jxcontext.Context, lng, lat float64, maxRadius
}
return storeList, err
}
func GetVendorOrgCode(ctx *jxcontext.Context, vendorID int, vendorOrgCode, vendorType string) (vendorOrgs []*model.VendorOrgCode, err error) {
return dao.GetVendorOrgCode(dao.GetDB(), vendorID, vendorOrgCode, vendorType)
}
func UpdateVendorOrgCode(ctx *jxcontext.Context, ID int, payload map[string]interface{}) (err error) {
var (
db = dao.GetDB()
vendorOrgCode = &model.VendorOrgCode{}
)
vendorOrgCode.ID = ID
err = dao.GetEntity(db, vendorOrgCode)
valid := dao.StrictMakeMapByStructObject(payload, vendorOrgCode, ctx.GetUserName())
if len(valid) > 0 {
if valid["key"] != nil {
valid["vendorOrgCode"] = valid["key"]
delete(valid, "key")
}
if valid["code"] != nil {
valid["vendorID"] = valid["code"]
delete(valid, "code")
}
if valid["name"] != nil {
valid["comment"] = valid["name"]
delete(valid, "name")
}
txDB, _ := dao.Begin(db)
defer func() {
if r := recover(); r != nil {
dao.Rollback(db, txDB)
panic(r)
}
}()
if _, err = dao.UpdateEntityLogically(db, vendorOrgCode, valid, ctx.GetUserName(), nil); err != nil {
dao.Rollback(db, txDB)
return err
}
dao.Commit(db, txDB)
}
return err
}
func AddVendorOrgCode(ctx *jxcontext.Context, vendorOrgCode *model.VendorOrgCode) (err error) {
var (
db = dao.GetDB()
)
list, err := dao.GetVendorOrgCode(db, vendorOrgCode.VendorID, vendorOrgCode.VendorOrgCode, model.VendorOrgTypePlatform)
if err != nil {
return err
}
if len(list) > 0 {
return fmt.Errorf("库里有这个账号了,[%v]", vendorOrgCode.VendorOrgCode)
}
dao.WrapAddIDCULDEntity(vendorOrgCode, ctx.GetUserName())
dao.CreateEntity(db, vendorOrgCode)
return err
}

View File

@@ -1,29 +1,33 @@
package event
import (
"fmt"
"regexp"
"strings"
"time"
"git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/business/dao"
"git.rosy.net.cn/jx-callback/business/jxutils"
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
"git.rosy.net.cn/jx-callback/business/model"
"git.rosy.net.cn/jx-callback/business/model/dao"
"git.rosy.net.cn/jx-callback/globals"
"git.rosy.net.cn/jx-callback/globals/api"
)
var (
NoUseEventMap = map[string]string{
"CreateQrOrBarCode": "CreateQrOrBarCode",
"StatisticsReportForOrders": "StatisticsReportForOrders",
"UpdateUser": "UpdateUser",
}
regexpToken = regexp.MustCompile(`,"token":".*"`)
)
const (
sysMessageTitle = ""
)
type CheckCookie struct {
VendorID int `json:"vendorID"`
VendorOrgCode string `json:"vendorOrgCode"`
Status string `json:"status"`
}
func AddOperateEvent(ctx *jxcontext.Context, accessUUID, jsonData string, errCode, errMsg string, useTime int, apiFunctionSpec string) (err error) {
var (
@@ -63,7 +67,7 @@ func AddOperateEvent(ctx *jxcontext.Context, accessUUID, jsonData string, errCod
ErrMsg: errMsg,
UseTime: useTime,
}
txDB, _ := dao.Begin(db)
txDB , _ := dao.Begin(db)
defer func() {
if r := recover(); r != nil {
dao.Rollback(db, txDB)
@@ -76,7 +80,7 @@ func AddOperateEvent(ctx *jxcontext.Context, accessUUID, jsonData string, errCod
}
func AddOperateEventDetail(db *dao.DaoDB, operateEventDetail *model.OperateEventDetail) (err error) {
txDB, _ := dao.Begin(db)
txDB , _ := dao.Begin(db)
defer func() {
if r := recover(); r != nil {
dao.Rollback(db, txDB)
@@ -92,7 +96,7 @@ func AddOperateEventDetail(db *dao.DaoDB, operateEventDetail *model.OperateEvent
func DeleteOperateEventAndDetail(ctx *jxcontext.Context, deleteTime time.Time) (err error) {
db := dao.GetDB()
txDB, _ := dao.Begin(db)
txDB , _ := dao.Begin(db)
defer func() {
if r := recover(); r != nil {
dao.Rollback(db, txDB)
@@ -129,3 +133,69 @@ func GetOperateEvents(ctx *jxcontext.Context, name string, apiFunctions []string
}
return pageInfo, err
}
func GetCheckVendorCookie(ctx *jxcontext.Context, vendorIDs []int, isAuto bool) (ccList []*CheckCookie, err error) {
var (
ebaiOrderID = "1577329467196263592"
ebaiErr = "return not json"
ebaiErr2 = "系统错误"
mtStoreID = "7388603"
mtErr = "返回结果格式不正常"
jdUpcCode = "6952395700895"
jdErr = "请输入用户名"
// mtpsErr = "用户未登录"
errMsg = ""
)
for _, v := range vendorIDs {
cc := &CheckCookie{}
var flag = false
switch v {
case model.VendorIDEBAI:
resultMap, err := api.EbaiAPI.GetStoreOrderInfo(ebaiOrderID)
if len(resultMap) < 1 && err != nil {
if strings.Contains(err.Error(), ebaiErr) || strings.Contains(err.Error(), ebaiErr2) {
errMsg += fmt.Sprintf(" 饿百账号:[%v]的Cookie无效了")
flag = true
}
}
case model.VendorIDMTWM:
_, err := api.MtwmAPI.PackagePriceGet(mtStoreID)
if err != nil {
if strings.Contains(err.Error(), mtErr) {
errMsg += fmt.Sprintf(" 美团账号:[%v]的Cookie无效了")
flag = true
}
globals.SugarLogger.Debugf("cookieCheck", err)
}
case model.VendorIDJD:
result, err := api.JdAPI.GetJdUpcCodeByName("", jdUpcCode, 1, 5)
if len(result) < 1 && err != nil {
if strings.Contains(err.Error(), jdErr) {
errMsg += fmt.Sprintf(" 京东账号:[%v]的Cookie无效了")
flag = true
}
}
case model.VendorIDJDShop:
_, err := api.JdShopAPI.OrderDetail("124350112427")
if err != nil {
if strings.Contains(err.Error(), "登录") {
errMsg += fmt.Sprintf("京东商城:[%v]的Cookie无效了")
flag = true
}
globals.SugarLogger.Debugf("cookieCheck", err)
}
}
cc.VendorID = v
cc.VendorOrgCode = ""
if flag {
cc.Status = "无效"
} else {
cc.Status = "有效"
}
ccList = append(ccList, cc)
}
if isAuto && errMsg != "" {
globals.SugarLogger.Warnf("GetCheckVendorCookie[%v]", errMsg)
}
return ccList, err
}

View File

@@ -0,0 +1,119 @@
package financial
import (
"context"
"errors"
"fmt"
"mime/multipart"
"path"
"strings"
"time"
"git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/business/jxutils"
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
"git.rosy.net.cn/jx-callback/business/jxutils/tasksch"
"git.rosy.net.cn/jx-callback/business/jxutils/weixinmsg"
"git.rosy.net.cn/jx-callback/business/model/dao"
"git.rosy.net.cn/jx-callback/business/model/legacymodel"
"git.rosy.net.cn/jx-callback/globals"
"git.rosy.net.cn/jx-callback/globals/api"
"github.com/qiniu/api.v7/storage"
)
type tUploadFileInfo struct {
FileHeader *multipart.FileHeader
StoreID int
}
func SendFilesToStores(ctx *jxcontext.Context, files []*multipart.FileHeader, title, shopName string, isAsync bool, userName string) (hint string, err error) {
globals.SugarLogger.Debugf("SendFilesToStores, fileCount:%d isAsync:%t, userName:%s", len(files), isAsync, userName)
if len(files) == 0 {
return "", errors.New("没有文件上传!")
}
fileList := make([]*tUploadFileInfo, len(files))
for k, fileHeader := range files {
fileList[k] = &tUploadFileInfo{
FileHeader: fileHeader,
}
fileNameParts := strings.Split(fileHeader.Filename, "_")
if len(fileNameParts) < 3 {
return "", fmt.Errorf("文件名:%s不规范没有包含三个必要的部分", fileHeader.Filename)
}
fileList[k].StoreID = int(utils.Str2Int64WithDefault(fileNameParts[0], 0))
if fileList[k].StoreID < 100000 || fileList[k].StoreID > 1000000 {
return "", fmt.Errorf("文件名:%s不规范不以合法的京西门店ID开始", fileHeader.Filename)
}
}
putPolicy := storage.PutPolicy{
Scope: globals.QiniuBucket,
Expires: 10 * 60,
}
upToken := putPolicy.UploadToken(api.QiniuAPI)
cfg := &storage.Config{}
task := tasksch.NewParallelTask("SendFilesToStores", tasksch.NewParallelConfig().SetIsContinueWhenError(true), ctx,
func(t *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
fileInfo := batchItemList[0].(*tUploadFileInfo)
fileHeader := fileInfo.FileHeader
db := dao.GetDB()
storeID, _ := dao.GetRealLinkStoreID(db, fileInfo.StoreID)
file, err := fileHeader.Open()
globals.SugarLogger.Debugf("SendFilesToStores upload file:%s", fileHeader.Filename)
if err == nil {
ret := storage.PutRet{}
key := "storeBill/" + utils.Int2Str(storeID) + "_" + strings.ToLower(utils.GetUUID()) + path.Ext(fileHeader.Filename)
formUploader := storage.NewFormUploader(cfg)
for i := 0; i < 3; i++ {
if err = formUploader.Put(context.Background(), &ret, upToken, key, file, fileHeader.Size, &storage.PutExtra{}); err == nil {
break
}
}
file.Close()
if err == nil {
billRec := &legacymodel.StoreBill{
Date: time.Now(),
Url: strings.Replace(jxutils.ComposeQiniuResURL(ret.Key), "http://", "https://", -1),
StoreId: storeID,
BillName: fileHeader.Filename,
ShopName: shopName,
BillTitle: title,
}
if err = dao.CreateEntity(db, billRec); err == nil {
err = weixinmsg.NotifySaleBill(storeID, title, shopName, fmt.Sprintf("%s/billshow/?path=%s", globals.BackstageHost, billRec.Url))
if err != nil {
globals.SugarLogger.Infof("SendFilesToStores NotifySaleBill file:%s error:%v", fileHeader.Filename, err)
}
err = nil // 忽略微信发送错误
} else {
globals.SugarLogger.Warnf("SendFilesToStores CreateEntity file:%s error:%v", fileHeader.Filename, err)
}
} else {
globals.SugarLogger.Warnf("SendFilesToStores file:%s failed with error:%v", fileHeader.Filename, err)
}
} else {
globals.SugarLogger.Warnf("SendFilesToStores open file:%s failed with error:%v", fileHeader.Filename, err)
}
return retVal, err
}, fileList)
tasksch.HandleTask(task, nil, true).Run()
hint = task.ID
if !isAsync {
_, err = task.GetResult(0)
}
return task.ID, err
}
func GetStoreBills(ctx *jxcontext.Context, storeID int) (bills []*legacymodel.StoreBill, err error) {
db := dao.GetDB()
if err = dao.GetRows(db, &bills, `
SELECT *
FROM store_bill
WHERE store_id = ?
ORDER BY date DESC
LIMIT 10
`, storeID); err != nil {
return nil, err
}
return bills, err
}

View File

@@ -0,0 +1,278 @@
package initdata
import (
"fmt"
"git.rosy.net.cn/baseapi/platformapi/autonavi"
"git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
"git.rosy.net.cn/jx-callback/business/jxutils/tasksch"
"git.rosy.net.cn/jx-callback/business/model"
"git.rosy.net.cn/jx-callback/business/model/dao"
"git.rosy.net.cn/jx-callback/business/partner"
"git.rosy.net.cn/jx-callback/globals/api"
)
func TruncateTable(db *dao.DaoDB, tableName string) (err error) {
_, err = dao.ExecuteSQL(db, "TRUNCATE TABLE "+tableName)
return err
}
func insertPlace(ctx *jxcontext.Context, db *dao.DaoDB, parent *autonavi.District, placeList []*autonavi.District) (err error) {
for _, v := range placeList {
if v.Level <= autonavi.DistrictLevelDistrict {
place := &model.Place{
Code: int(utils.Str2Int64(v.Adcode)),
Name: v.Name,
Level: int8(v.Level),
TelCode: v.CityCode,
Enabled: 1,
}
if parent != nil {
place.ParentCode = int(utils.Str2Int64(parent.Adcode))
}
dao.WrapAddIDCULEntity(place, ctx.GetUserName())
if err = dao.CreateEntity(db, place); err != nil {
return err
}
if err = insertPlace(ctx, db, v, v.Districts); err != nil {
return err
}
}
}
return nil
}
func InitPlace(ctx *jxcontext.Context) (err error) {
placeList, err2 := api.AutonaviAPI.GetDistricts(autonavi.DistrictLevelDistrict, "")
if err = err2; err != nil {
return err
}
placeList = placeList[0].Districts
db := dao.GetDB()
txDB , _ := dao.Begin(db)
defer func() {
if r := recover(); r != nil || err != nil {
dao.Rollback(db, txDB)
if r != nil {
panic(r)
}
}
}()
if _, err = dao.ExecuteSQL(db, `
DELETE t1
FROM place t1
WHERE code < 9000000;
`); err != nil {
return err
}
if err = insertPlace(ctx, db, nil, placeList); err != nil {
return err
}
updateSqls := []string{
`
UPDATE place t1
JOIN jde_city t2 ON t1.code = t2.col_tencentAddressCode
SET t1.jd_code = t2.col_areaCode;
`,
`
UPDATE place t1
JOIN place t2 ON t1.parent_code = t2.code AND t2.jd_code != 0
JOIN jde_district t3 ON t1.name = t3.col_areaName AND t2.jd_code = t3.col_cityCode
SET t1.jd_code = t3.col_areaCode
WHERE t1.level = 3;
`,
`
UPDATE
place t1
JOIN ebde_places t2 ON t1.name = t2.col_city_name
SET t1.ebai_code = t2.col_city_id
WHERE t1.level = 1 OR t1.level = 2;
`,
`
UPDATE
place t1
JOIN place t1p ON t1.parent_code = t1p.code
JOIN ebde_places t2 ON t1.name = t2.col_city_name
JOIN ebde_places t2p ON t2.col_parent_id = t2p.col_city_id AND t1p.ebai_code = t2p.col_city_id
SET t1.ebai_code = t2.col_city_id
WHERE t1.level = 3;
`,
`
UPDATE
place t1
JOIN mtpsdeliveryprice t2 ON t1.code = t2.citycode
SET t1.mtps_price = t2.price;
`,
`
UPDATE
place t1
JOIN mtpsdeliveryprice t2 ON t1.name LIKE CONCAT(t2.cityname, '%')
SET t1.mtps_price = t2.price
WHERE t1.level = 2 AND t1.mtps_price = 0;
`,
`
UPDATE place t1
LEFT JOIN (
SELECT DISTINCT city_code
FROM store
UNION DISTINCT
SELECT DISTINCT place_code city_code
FROM sku_name_place_bind
) t2 ON t1.code = t2.city_code
SET t1.enabled = 0
WHERE t1.level = 2 AND t2.city_code IS NULL;
`,
}
for _, v := range updateSqls {
if _, err = dao.ExecuteSQL(db, v); err != nil {
return err
}
}
dao.Commit(db, txDB)
return err
}
func InitVendorCategory(ctx *jxcontext.Context, vendorID int, isAsync bool) (hint string, err error) {
if handler := partner.GetPurchasePlatformFromVendorID(vendorID); handler != nil {
var cats []*model.SkuVendorCategory
rootTask := tasksch.NewSeqTask(fmt.Sprintf("创建%s的平台分类", model.VendorChineseNames[vendorID]), ctx,
func(task *tasksch.SeqTask, step int, params ...interface{}) (result interface{}, err error) {
switch step {
case 0:
cats, err = handler.GetVendorCategories(ctx)
if err != nil {
return nil, err
}
case 1:
db := dao.GetDB()
txDB , _ := dao.Begin(db)
defer func() {
if r := recover(); r != nil || err != nil {
dao.Rollback(db, txDB)
if r != nil {
panic(r)
}
}
}()
sql := `
DELETE
FROM sku_vendor_category
WHERE vendor_id = ?
`
if _, err = dao.ExecuteSQL(db, sql, vendorID); err != nil {
return nil, err
}
for _, cat := range cats {
dao.WrapAddIDCULEntity(cat, ctx.GetUserName())
}
if err = dao.CreateMultiEntities(db, cats); err != nil {
return nil, err
}
dao.Commit(db, txDB)
}
return nil, err
}, 2)
tasksch.HandleTask(rootTask, nil, true).Run()
if !isAsync {
if _, err = rootTask.GetResult(0); err == nil {
hint = utils.Int2Str(len(cats))
}
} else {
hint = rootTask.ID
}
} else {
err = fmt.Errorf("找不到平台:%d", vendorID)
}
return hint, err
}
func UploadImg4Vendors(ctx *jxcontext.Context, isAsync, isContinueWhenError bool) (hint string, err error) {
// db := dao.GetDB()
// rootTask := tasksch.NewSeqTask("合并SkuName图片至DataResource", ctx,
// func(task *tasksch.SeqTask, step int, params ...interface{}) (result interface{}, err error) {
// switch step {
// case 0: // 计算SkuName中缺失的hashCode注意是DescImg不是Img
// var skuNameList []*model.SkuName
// if err = dao.GetRows(db, &skuNameList, `
// SELECT t1.*
// FROM sku_name t1
// WHERE t1.desc_img <> '' AND t1.img_hash_code = ''
// `); err == nil && len(skuNameList) > 0 {
// calcTask := tasksch.NewParallelTask("UploadImg4Vendors calc hashCode",
// tasksch.NewParallelConfig().SetIsContinueWhenError(isContinueWhenError).SetParallelCount(5), ctx,
// func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
// skuName := batchItemList[0].(*model.SkuName)
// _, skuName.ImgHashCode, err = jxutils.DownloadFileByURL(skuName.DescImg)
// if err == nil {
// dao.UpdateEntity(db, skuName, "ImgHashCode")
// }
// return retVal, err
// }, skuNameList)
// tasksch.HandleTask(calcTask, task, false).Run()
// _, err = calcTask.GetResult(0)
// }
// case 1: // 从SkuName添加缺失的图片至DataResource
// _, err = dao.ExecuteSQL(db, `
// INSERT INTO data_resource(created_at, updated_at, last_operator, hash_code,
// resource_type, name, main_url, ebai_url, qiniu_url, use_type)
// SELECT t1.created_at, t1.created_at, t1.last_operator, t1.img_hash_code,
// CASE
// WHEN INSTR(t1.desc_img, ".jpg") > 0 OR INSTR(t1.desc_img, ".jpeg") > 0 THEN
// 'image/jpeg'
// WHEN INSTR(t1.desc_img, ".png") > 0 OR INSTR(t1.desc_img, ".peg") > 0 THEN
// 'image/png'
// WHEN INSTR(t1.desc_img, ".gif") THEN
// 'image/gif'
// ELSE
// ''
// END resource_type,
// CONCAT(t1.name, '_desc'), desc_img main_url, t1.desc_img_ebai ebai_url,
// IF(INSTR(t1.desc_img, "image.jxc4.com") > 0, t1.desc_img, '') qiniu_url, 2
// FROM sku_name t1
// JOIN (
// SELECT img_hash_code, MAX(id) id, COUNT(*) ct
// FROM sku_name
// WHERE img_hash_code <> '' AND desc_img <> ''
// GROUP BY 1
// ) t3 ON t3.id = t1.id
// LEFT JOIN data_resource t2 ON (t2.main_url <> '' AND t2.main_url = t1.desc_img)
// WHERE t1.desc_img <> '' AND t1.img_hash_code <> '' AND t2.id IS NULL;
// `)
// case 2: // 统一SkuName中同hashCode不同图片地址至同一地址
// _, err = dao.ExecuteSQL(db, `
// UPDATE sku_name t1
// JOIN data_resource t2 ON t2.hash_code = t1.img_hash_code AND t2.main_url <> ''
// SET t1.desc_img = t2.main_url
// WHERE t1.img_hash_code <> '' AND t1.desc_img <> t2.main_url;
// `)
// case 3: // 上传DataResource中缺失的平台图片
// dataResList, err2 := dao.GetNeedUploadDataResource(db)
// if err = err2; err == nil && len(dataResList) > 0 {
// uploadTask := tasksch.NewParallelTask("批量上传图片至平台",
// tasksch.NewParallelConfig().SetIsContinueWhenError(isContinueWhenError).SetParallelCount(2), ctx,
// func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
// dataRes := batchItemList[0].(*model.DataResource)
// _, err = datares.UploadImage2Vendors(ctx, task, dataRes, nil, false)
// return nil, err
// }, dataResList)
// tasksch.HandleTask(uploadTask, task, true).Run()
// _, err = uploadTask.GetResult(0)
// }
// }
// return result, err
// }, 4)
// tasksch.HandleTask(rootTask, nil, true).Run()
// if !isAsync {
// if _, err = rootTask.GetResult(0); err == nil {
// hint = "1"
// }
// } else {
// hint = rootTask.ID
// }
return hint, err
}
func getSkuNameKey(prefix, name, comment, specUnit, unit string, specQuality float32) string {
return fmt.Sprintf("%s-%s-%f-%s-%s", prefix, name, specQuality, specUnit, unit)
}

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,15 +1,623 @@
package misc
import (
"fmt"
"sync"
"time"
"git.rosy.net.cn/jx-callback/business/partner/purchase/jdshop"
"git.rosy.net.cn/baseapi/platformapi/jdapi"
"git.rosy.net.cn/jx-callback/business/partner/purchase/jx/localjx"
"git.rosy.net.cn/jx-callback/globals/api"
"git.rosy.net.cn/jx-callback/business/jxstore/report"
"git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/baseapi/utils/errlist"
"git.rosy.net.cn/jx-callback/business/jxcallback/orderman"
"git.rosy.net.cn/jx-callback/business/jxcallback/scheduler/defsch"
"git.rosy.net.cn/jx-callback/business/jxstore/act"
"git.rosy.net.cn/jx-callback/business/jxstore/cms"
"git.rosy.net.cn/jx-callback/business/jxutils"
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
"git.rosy.net.cn/jx-callback/business/jxutils/netprinter"
"git.rosy.net.cn/jx-callback/business/jxutils/tasksch"
"git.rosy.net.cn/jx-callback/business/model"
"git.rosy.net.cn/jx-callback/business/model/dao"
"git.rosy.net.cn/jx-callback/business/netspider"
"git.rosy.net.cn/jx-callback/business/partner"
"git.rosy.net.cn/jx-callback/globals"
beego "github.com/astaxie/beego/server/web"
)
func Init() {
const (
SpecialTaskID = "Running"
TaskNameSyncStoreSku = "SyncStoreSku"
)
var (
dailyHeartbeat = []string{
"09:00:00",
}
dailyWorkTimeList = []string{
"21:30:00",
}
dailyWorkTimeList2 = []string{
"2:00:00",
}
priceReferTimeList = []string{
"03:00:00",
}
checkCookieList = []string{
"08:00:00",
"12:00:00",
"18:00:00",
}
createStorePriceTimeList = []string{
"04:00:00",
}
refreshPageActTimeList = []string{
"7:00:00",
"8:00:00",
"9:00:00",
"10:00:00",
"11:00:00",
"12:00:00",
"13:00:00",
"14:00:00",
"15:00:00",
"16:00:00",
"17:00:00",
"18:00:00",
"19:00:00",
"22:00:00",
}
ChangeStoreSkuSaleStatusList = []string{
"7:00:00",
"8:00:00",
"9:00:00",
"10:00:00",
"11:00:00",
"12:00:00",
"13:00:00",
"14:00:00",
"15:00:00",
"16:00:00",
"17:00:00",
"18:00:00",
"19:00:00",
"20:00:00",
}
openRemoteStoreTimeList = []string{
"04:30:00",
"23:30:00",
}
updateActStatusTimeList = []string{
"00:01:00",
}
sendSecKillWarnList = []string{
"9:00:00",
}
autoPayForPopluarManList = []string{
"10:30:00",
}
autoSaleStoreSkuTimeList = []string{
cms.AutoSaleAtStr,
}
backUpStoreSkuBindList = []string{
"23:30:00",
}
exSyncList = []string{
"11:30:00",
}
importantTaskMap = &sync.Map{}
cancelPayTimeOutOrderList = localjx.GetHalfHoursList()
discountActJxList = localjx.GetDiscountActHoursList()
//ebaiStorePageCookieExdTOKEN string
//ebaiStorePageCookieWMUSS2 string
//ebaiStorePageCookieWMSTOKEN2 string
ebaiStorePageCookie string
//ebaiStorePageCookieWMSTOKEN string
mtwmCookieStr string
mtpsStoreToken string
jd2StorePageCookie string
JdStorePageCookie string
yinbaoCookie string
feiePageCookie string
jdStorePageEarning string
//jdsCookie string
// jdsCookie2 string
)
func GetImportantTaskID(taskName string) string {
if value, ok := importantTaskMap.Load(taskName); ok {
return value.(string)
}
return ""
}
func SaveImportantTaskID(taskName, taskID string) {
importantTaskMap.Store(taskName, taskID)
}
func IsImportantTaskRunning(taskName string) bool {
taskID := GetImportantTaskID(taskName)
if taskID == "" {
return false
} else if taskID == SpecialTaskID {
return true
}
return tasksch.IsTaskRunning(taskID)
}
func Init() {
if globals.IsProductEnv() {
//ScheduleTimerFunc("doDailyWork2", doDailyWork2, dailyWorkTimeList2)
//京东的订单信息解密密钥获取
ScheduleTimerFuncByInterval(func() {
jdshop.InitKey()
}, 10*time.Second, 8*time.Hour)
ScheduleTimerFuncByInterval(func() {
RefreshRealMobile(jxcontext.AdminCtx, model.VendorIDEBAI, time.Now().Add(-24*time.Hour), utils.DefaultTimeValue, false, true)
report.RefreshStoreManageState(jxcontext.AdminCtx, nil, []int{model.VendorIDJD})
}, 5*time.Second, 1*time.Hour)
ScheduleTimerFuncByInterval(func() {
defsch.FixedScheduler.ConfirmSelfTakeOrders(jxcontext.AdminCtx, []int{model.VendorIDJD}, time.Now().Add(-48*time.Hour), time.Now().Add(-30*time.Minute), true, true)
}, 5*time.Second, 10*time.Minute)
ScheduleTimerFuncByInterval(func() {
report.RefreshStoreManageState(jxcontext.AdminCtx, nil, []int{model.VendorIDMTWM, model.VendorIDEBAI})
}, 5*time.Second, 40*time.Minute)
ScheduleTimerFuncByInterval(func() {
curDate := utils.Time2Date(time.Now())
orderman.FixedOrderManager.AmendMissingOrders(jxcontext.AdminCtx, []int{model.VendorIDJD, model.VendorIDMTWM, model.VendorIDEBAI}, 0, curDate, curDate, true, true)
orderman.SaveJdsOrders(jxcontext.AdminCtx, time.Now().Add(-30*time.Minute), time.Now())
}, 5*time.Second, 10*time.Minute)
ScheduleTimerFunc("RefreshJdShopOrdersEarningPrice", func() {
orderman.RefreshJdShopOrdersEarningPrice(jxcontext.AdminCtx, utils.Time2Str(time.Now().AddDate(0, 0, -2)), utils.Time2Str(time.Now().AddDate(0, 0, -2)))
orderman.RefreshJdShopOrdersEarningPrice(jxcontext.AdminCtx, utils.Time2Str(time.Now().AddDate(0, 0, -1)), utils.Time2Str(time.Now().AddDate(0, 0, -1)))
}, []string{
"05:00:00",
})
ScheduleTimerFunc("auto enable remote store", func() {
cms.EnableHaveRestStores(jxcontext.AdminCtx, false, true)
// cms.OpenRemoteStoreByJxStatus(jxcontext.AdminCtx, nil, nil, false, false, true)
}, openRemoteStoreTimeList)
// ScheduleTimerFunc("SaveAndSendAlarmVendorSnapshot", func() {
// cms.SaveAndSendAlarmVendorSnapshot(jxcontext.AdminCtx, nil, nil, false)
// }, cms.WatchVendorStoreTimeList)
ScheduleTimerFunc("RefreshPageActs", func() {
act.RefreshPageActs(jxcontext.AdminCtx, []int{model.VendorIDEBAI}, time.Now().Add(-30*24*time.Hour), false)
}, refreshPageActTimeList)
ScheduleTimerFunc("UpdateActStatusByTime", func() {
dao.UpdateActStatusByTime(dao.GetDB(), time.Now())
}, updateActStatusTimeList)
ScheduleScoreStore()
// ScheduleCheckStoreAlert()
ScheduleTimerFunc("ChangeStoreSkuSaleStatus", func() {
cms.CurVendorSync.ChangeStoreSkuSaleStatus(jxcontext.AdminCtx, 0, true, true)
}, ChangeStoreSkuSaleStatusList)
ScheduleTimerFunc("BeginSavePriceRefer", func() {
report.BeginSavePriceRefer(jxcontext.AdminCtx, nil, nil, true, true)
}, priceReferTimeList)
ScheduleTimerFunc("CreateStorePriceScore", func() {
cms.CreateStorePriceScore(jxcontext.AdminCtx)
}, createStorePriceTimeList)
ScheduleTimerFunc("AutoFocusStoreSkusForTopSkus", func() {
cms.AutoFocusStoreSkusForTopSkus(jxcontext.AdminCtx, true, true)
}, createStorePriceTimeList)
//ScheduleTimerFunc("GetCheckVendorCookie", func() {
// event.GetCheckVendorCookie(jxcontext.AdminCtx, []int{model.VendorIDEBAI, model.VendorIDJD, model.VendorIDMTWM, model.VendorIDMTPS, model.VendorIDJDShop}, true)
//}, checkCookieList)
//ScheduleTimerFunc("SendSeckillSkusCountMsg", func() {
// cms.SendSeckillSkusCountMsg(jxcontext.AdminCtx, []int{model.VendorIDEBAI, model.VendorIDJD, model.VendorIDMTWM}, false, true)
//}, sendSecKillWarnList)
// ScheduleTimerFunc("每日报警心跳", func() {
// globals.SugarLogger.Warnf("每日报警心跳,这不是报警。启动时间:%s", cms.GetServiceInfo(jxcontext.AdminCtx)["startupTime"])
// }, dailyHeartbeat)
ScheduleTimerFunc("AutoPayForPopluarMan", func() {
localjx.AutoPayForPopluarMan(jxcontext.AdminCtx)
}, autoPayForPopluarManList)
ScheduleTimerFunc("CancelPayTimeOutOrder", func() {
localjx.CancelPayTimeOutOrder(jxcontext.AdminCtx)
}, cancelPayTimeOutOrderList)
ScheduleTimerFunc("BackUpStoreSkuBind", func() {
cms.BackUpStoreSkuBind(jxcontext.AdminCtx, true, true)
}, backUpStoreSkuBindList)
// ScheduleTimerFunc("SendNoCatSkusToOperater", func() {
// cms.SendNoCatSkusToOperater(jxcontext.AdminCtx)
// }, autoPayForPopluarManList)
ScheduleTimerFunc("CleanStoreIsBoughtMatter", func() {
cms.CleanStoreIsBoughtMatter(jxcontext.AdminCtx)
}, priceReferTimeList)
ScheduleTimerFunc("CleanUserOrderSMSMark", func() {
cms.CleanUserOrderSMSMark(jxcontext.AdminCtx)
}, priceReferTimeList)
ScheduleTimerFunc("CreateOrderByPriceDefend", func() {
localjx.CreateOrderByPriceDefend(jxcontext.AdminCtx)
}, []string{
"22:00:00",
})
//ScheduleTimerFunc("ChangeJxPriceByDiscountAct", func() {
// act.ChangeJxPriceByDiscountAct(jxcontext.AdminCtx)
//}, discountActJxList)
ScheduleTimerFunc("RefreshUserMemberStatus", func() {
cms.RefreshUserMemberStatus(jxcontext.AdminCtx)
}, updateActStatusTimeList)
//刷优惠券状态
ScheduleTimerFunc("RefreshCouponsStatus", func() {
localjx.RefreshCouponsStatus(jxcontext.AdminCtx)
}, updateActStatusTimeList)
//刷新美团商超门店Token
ScheduleTimerFunc("RefreshMTWMToken", func() {
cms.RefreshMTWMToken(jxcontext.AdminCtx)
}, updateActStatusTimeList)
//获取最新平台流量活动
ScheduleTimerFunc("GetNewVendorPopActs", func() {
act.GetNewVendorPopActs(jxcontext.AdminCtx)
}, dailyHeartbeat)
//企业微信群人数通告
ScheduleTimerFunc("SendQywxPeopleCount", func() {
cms.SendQywxPeopleCount(jxcontext.AdminCtx)
}, dailyHeartbeat)
//刷新京东售后单结算价
ScheduleTimerFunc("RefreshJdAfsOrderTotalShopMoney", func() {
orderman.RefreshJdAfsOrderTotalShopMoney()
}, openRemoteStoreTimeList)
ScheduleTimerFunc("doDailyWork1", func() {
//同步商品额外前缀和水印图(打标记)
cms.SyncSkuExperfixAndWatermark(jxcontext.AdminCtx)
}, dailyWorkTimeList)
ScheduleTimerFunc("BuildFakeMatterOrder", func() {
orderman.BuildFakeMatterOrder()
}, []string{
"09:00:00",
"15:00:00",
"23:00:00",
})
ScheduleTimerFunc("doDailyWork2", func() {
//刷新京东会员
//report.RefreshJDMembers(jxcontext.AdminCtx)
cms.SetSingleStoreSkuSyncModifyStatus(dao.GetDB(), []int{1, 3})
cms.CurVendorSync.SyncStoresSkus2(jxcontext.AdminCtx, nil, 0, dao.GetDB(), []int{1, 3}, nil, false, nil, nil, model.SyncFlagSaleMask, true, true)
}, dailyWorkTimeList)
ScheduleTimerFunc("doDailyWork3", func() {
dao.SetStoresMapSyncStatus(dao.GetDB(), nil, nil, model.SyncFlagStoreStatus)
cms.CurVendorSync.SyncStore2(jxcontext.AdminCtx, dao.GetDB(), []int{model.VendorIDMTWM, model.VendorIDJD, model.VendorIDEBAI}, nil, true, true)
syncStoreSku()
InitEx()
cms.SyncStoresCourierInfo(jxcontext.AdminCtx, nil, false, true)
netprinter.RebindAllPrinters(jxcontext.AdminCtx, false, true)
}, dailyWorkTimeList)
ScheduleTimerFunc("doDailyWork4", func() {
curDate := utils.Time2Date(time.Now())
orderman.FixedOrderManager.AmendMissingOrders(jxcontext.AdminCtx, []int{model.VendorIDJD, model.VendorIDMTWM, model.VendorIDEBAI}, 0, curDate.Add(-72*time.Hour), curDate, true, true)
//只传toDate默认刷新toDate到5天以前的订单
orderman.RefreshOrdersWithoutJxStoreID(jxcontext.AdminCtx, "", "", true, true)
//禁用没有绑定的门店
cms.DisabledStoreWithoutVendor(jxcontext.AdminCtx, true, true)
}, dailyWorkTimeList)
ScheduleTimerFunc("doDailyWork5", func() {
//刷新物料订单状态
localjx.RefreshAllMatterOrderStatus(jxcontext.AdminCtx)
//同步美团配送与否状态及美团门店是否存在
//cms.SetMTPSStatus(jxcontext.AdminCtx, 0, 0)
cms.SetMTPSStatus2(0)
//售后单如果超过12小时没有审核就自动通过
RefreshAfsOrderStatusAccess(jxcontext.AdminCtx)
//刷新门店分组管理
cms.RefreshStoreBind(jxcontext.AdminCtx)
}, dailyWorkTimeList)
ScheduleTimerFunc("RrefreshMtwmVendorAct", func() {
//刷新美团平台活动
act.RrefreshMtwmVendorAct()
//刷新饿百平台活动
act.RrefreshEbaiVendorAct()
}, dailyWorkTimeList2)
}
ScheduleTimerFunc("AutoSaleStoreSku", func() {
cms.AutoSaleStoreSku(jxcontext.AdminCtx, nil, false)
}, autoSaleStoreSkuTimeList)
if beego.BConfig.RunMode == "jxgy" {
ScheduleTimerFunc("SyncMatterC4ToGy", func() {
cms.SyncMatterC4ToGy(jxcontext.AdminCtx, true, true)
}, dailyWorkTimeList)
}
if beego.BConfig.RunMode == "beta" {
//ScheduleTimerFunc("CancelPayTimeOutOrder", func() {
// localjx.CancelPayTimeOutOrder(jxcontext.AdminCtx)
//}, cancelPayTimeOutOrderList)
ScheduleTimerFunc("GetAndStoreCitiesShops", func() {
netspider.GetAndStoreCitiesShops(jxcontext.AdminCtx, nil, nil, 0, 0, false, false)
}, []string{
"04:05:06",
})
//京东的订单信息解密密钥获取
//ScheduleTimerFuncByInterval(func() {
// jdshop.InitKey()
//}, 10*time.Second, 8*time.Hour)
ScheduleTimerFunc("ChangeJxPriceByDiscountAct", func() {
act.ChangeJxPriceByDiscountAct(jxcontext.AdminCtx)
}, discountActJxList)
//ScheduleTimerFunc("CreateOrderByPriceDefend", func() {
// localjx.CreateOrderByPriceDefend(jxcontext.AdminCtx)
//}, []string{
// "22:00:00",
//})
//刷新门店数据坐标等
ScheduleTimerFunc("RefreshPageStore", func() {
cms.RefreshPageStore()
}, []string{
"20:00:00",
})
}
if configs, err := dao.QueryConfigs(dao.GetDB(), "ebaiStorePageCookie", model.ConfigTypeCookie, ""); err == nil {
ebaiStorePageCookie = configs[0].Value
}
if configs, err := dao.QueryConfigs(dao.GetDB(), "mtwmCookieStr", model.ConfigTypeCookie, ""); err == nil {
mtwmCookieStr = configs[0].Value
}
if configs, err := dao.QueryConfigs(dao.GetDB(), "mtpsStoreToken", model.ConfigTypeCookie, ""); err == nil {
mtpsStoreToken = configs[0].Value
}
if configs, err := dao.QueryConfigs(dao.GetDB(), "jdStorePageCookie", model.ConfigTypeCookie, ""); err == nil {
JdStorePageCookie = configs[0].Value
}
if configs, err := dao.QueryConfigs(dao.GetDB(), "jdStorePageEarning", model.ConfigTypeCookie, ""); err == nil {
jdStorePageEarning = configs[0].Value
}
if configs, err := dao.QueryConfigs(dao.GetDB(), "jd2StorePageCookie", model.ConfigTypeCookie, ""); err == nil {
jd2StorePageCookie = configs[0].Value
}
if configs, err := dao.QueryConfigs(dao.GetDB(), "feiePageCookie", model.ConfigTypeCookie, ""); err == nil {
feiePageCookie = configs[0].Value
}
//if configs, err := dao.QueryConfigs(dao.GetDB(), "jdsCookie", model.ConfigTypeCookie, ""); err == nil {
// jdsCookie = configs[0].Value
// api.JdShopAPI.SetCookieWithStr(jdsCookie)
//}
//if configs, err := dao.QueryConfigs(dao.GetDB(), "yinbaoCookie", model.ConfigTypeCookie, ""); err == nil {
// yinbaoCookie := configs[0].Value
// api.YinBaoAPI.SetCookie(".POSPALAUTH30220", yinbaoCookie)
//}
if globals.Jd2OrgCode != "" {
api.Jd2API.SetJdCookie(jd2StorePageCookie)
}
//api.EbaiAPI.SetCookie("PASSPORT_DELIMONT_TOKEN", ebaiStorePageCookieExdTOKEN)
//api.EbaiAPI.SetCookie("WMUSS", ebaiStorePageCookieWMUSS)
//api.EbaiAPI.SetCookie("WMSTOKEN", ebaiStorePageCookieWMSTOKEN)
//api.Ebai2API.SetCookie("PASSPORT_DELIMONT_TOKEN", ebaiStorePageCookieExdTOKEN)
//api.Ebai2API.SetCookie("WMUSS", ebaiStorePageCookieWMUSS2)
//api.Ebai2API.SetCookie("WMSTOKEN", ebaiStorePageCookieWMSTOKEN2)
api.EbaiAPI.SetCookieWithStr(ebaiStorePageCookie)
api.MtwmAPI.SetCookieWithStr(mtwmCookieStr)
api.MtpsAPI.SetCookie("token", mtpsStoreToken)
//api.JdAPI.SetJdCookie(JdStorePageCookie)
api.JdAPI.SetCookieWithStr(JdStorePageCookie)
api.JdAPI.SetCookie("user", jdStorePageEarning)
api.JdPageAPI.SetCookie(jdapi.AccessStorePageCookieName, JdStorePageCookie)
api.JdPageAPI.SetCookie(jdapi.AccessStorePageCookieName2, JdStorePageCookie)
api.FeieAPI.SetCookieWithStr(feiePageCookie)
}
func syncStoreSku() {
syncFlag := 0
if beego.BConfig.RunMode == "jxgy" {
//syncFlag = model.SyncFlagPriceMask
if true {
syncFlag |= model.SyncFlagSaleMask
}
} else {
syncFlag |= model.SyncFlagSaleMask
}
task := tasksch.NewParallelTask("同步京西与平台数据", nil, jxcontext.AdminCtx,
func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
step := batchItemList[0].(int)
errList := errlist.New()
db := dao.GetDB()
switch step {
case 0:
if beego.BConfig.RunMode != "jxgy" {
errList.AddErr(cms.DeleteSkuNameExPrefixOverdue(db))
errList.AddErr(cms.SetMultiStoreSkuSyncModifyStatus(db, partner.GetMultiStoreVendorIDs()))
_, err = cms.CurVendorSync.LoopMultiStoresVendors(jxcontext.AdminCtx, db, "同步多门店平台商品库", false, true,
func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
vendorInfo := batchItemList[0].(*cms.MultiStoreVendorInfo)
_, err = cms.FullSyncVendorStuff(jxcontext.AdminCtx, task, 0, vendorInfo.VendorID, vendorInfo.OrgCode, false, true)
return retVal, err
})
errList.AddErr(err)
_, err = cms.CurVendorSync.SyncStoresSkus2(jxcontext.AdminCtx, nil, 0, db, []int{0}, nil, false, nil, []int{27379}, syncFlag, true, true)
//_, err = cms.CurVendorSync.FullSyncStoresSkus(jxcontext.AdminCtx, db, partner.GetMultiStoreVendorIDs(), nil, false, []int{27379}, true, true)
errList.AddErr(err)
}
case 1:
//TODO 暂时不同步银豹可能要从银豹到京西2020-04-27
//errList.AddErr(cms.SetSingleStoreSkuSyncModifyStatus(db, []int{1, 3}))
// errList.AddErr(cms.SetSingleStoreSkuSyncModifyStatus(db, partner.GetSingleStoreVendorIDs()))
// _, err = cms.CurVendorSync.AmendAndPruneStoreStuff(jxcontext.AdminCtx, []int{1, 3}, nil, false, true, cms.AmendPruneAll, false)
// _, err = cms.CurVendorSync.AmendAndPruneStoreStuff(jxcontext.AdminCtx, partner.GetSingleStoreVendorIDs(), nil, false, true, cms.AmendPruneAll, false)
errList.AddErr(err)
SaveImportantTaskID(TaskNameSyncStoreSku, SpecialTaskID)
//taskID, err2 := cms.CurVendorSync.SyncStoresSkus2(jxcontext.AdminCtx, nil, 0, db, []int{1, 3}, nil, false, nil, nil, syncFlag, true, true)
// taskID, err2 := cms.CurVendorSync.SyncStoresSkus2(jxcontext.AdminCtx, nil, 0, db, partner.GetSingleStoreVendorIDs(), nil, false, nil, nil, syncFlag, true, true)
//errList.AddErr(err2)
//SaveImportantTaskID(TaskNameSyncStoreSku, taskID)
}
err = errList.GetErrListAsOne()
return retVal, err
}, []int{0, 1})
tasksch.HandleTask(task, nil, true).Run()
}
func doDailyWork2() {
globals.SugarLogger.Debug("doDailyWork2")
if beego.BConfig.RunMode == "jxgy" {
syncStoreSku()
refreshPointOrderNewEarningPrice()
}
//同步京东商城门店的商品
// cms.CurVendorSync.SyncJdsStoresSkus(jxcontext.AdminCtx, nil, true, true)
//刷新京东商城的门店库存
// cms.SyncJdsStoreStock(jxcontext.AdminCtx, true, true)
}
func doDailyWork() {
globals.SugarLogger.Debug("doDailyWork")
//同步商品额外前缀和水印图(打标记)
cms.SyncSkuExperfixAndWatermark(jxcontext.AdminCtx)
dao.SetStoresMapSyncStatus(dao.GetDB(), nil, nil, model.SyncFlagStoreStatus)
cms.CurVendorSync.SyncStore2(jxcontext.AdminCtx, dao.GetDB(), []int{model.VendorIDMTWM, model.VendorIDJD, model.VendorIDEBAI}, nil, true, true)
if beego.BConfig.RunMode == "prod" {
syncStoreSku()
}
InitEx()
cms.SyncStoresCourierInfo(jxcontext.AdminCtx, nil, false, true)
netprinter.RebindAllPrinters(jxcontext.AdminCtx, false, true)
// 每天补全前一天与当天的订单
// curDate := utils.Time2Date(time.Now())
// orderman.FixedOrderManager.AmendMissingOrders(jxcontext.AdminCtx, nil, 0, curDate.Add(-72*time.Hour), curDate, true, true)
//订单门店归属补漏
//fromDate, toDate都不传默认刷新当前天5天以前的订单只传fromDate默认刷新fromDate到当天的订单
//只传toDate默认刷新toDate到5天以前的订单
orderman.RefreshOrdersWithoutJxStoreID(jxcontext.AdminCtx, "", "", true, true)
//刷新京东门店的等级
// cms.RefreshJdLevel(jxcontext.AdminCtx)
//删除操作日志
// event.DeleteOperateEventAndDetail(jxcontext.AdminCtx, time.Now().AddDate(0, -3, 0))
//禁用没有绑定的门店
cms.DisabledStoreWithoutVendor(jxcontext.AdminCtx, true, true)
//刷新物料订单状态
localjx.RefreshAllMatterOrderStatus(jxcontext.AdminCtx)
//同步银豹到京西
// cms.CurVendorSync.SyncStoreSkusFromYb(jxcontext.AdminCtx, nil, true, true)
//同步上架京东商城待售商品
// cms.RefreshJdsSkusStatus(jxcontext.AdminCtx)
//同步美团配送与否状态及美团门店是否存在
cms.SetMTPSStatus(jxcontext.AdminCtx, 0, 0)
//售后单如果超过12小时没有审核就自动通过
RefreshAfsOrderStatusAccess(jxcontext.AdminCtx)
//刷新京东商城订单结算价
orderman.RefreshJdShopOrdersEarningPrice(jxcontext.AdminCtx, utils.Time2Str(time.Now().AddDate(0, 0, -2)), utils.Time2Str(time.Now()))
//刷新门店分组管理
cms.RefreshStoreBind(jxcontext.AdminCtx)
//刷新京东会员
report.RefreshJDMembers(jxcontext.AdminCtx)
}
func refreshPointOrderNewEarningPrice() {
var (
db = dao.GetDB()
orders []*model.GoodsOrder
)
sql := `
SELECT * FROM goods_order WHERE earning_type = 2 AND status = 110 AND order_created_at > ?
`
sqlParams := []interface{}{time.Now().AddDate(0, 0, -1)}
dao.GetRows(db, &orders, sql, sqlParams)
if len(orders) > 0 {
for _, v := range orders {
if v.NewEarningPrice == 0 {
v.NewEarningPrice = int64(int(v.TotalShopMoney) * (100 - v.OrderPayPercentage/2) / 100)
dao.UpdateEntity(db, v, "NewEarningPrice")
}
}
}
}
func RefreshAfsOrderStatusAccess(ctx *jxcontext.Context) {
var (
offset = 0
pageSize = 9999
db = dao.GetDB()
)
afsOrderList, _, err := dao.GetAfsOrdersByPage(db, "", "", "", time.Now().AddDate(0, 0, -7), time.Now(), offset, pageSize)
if err != nil {
return
}
for _, v := range afsOrderList {
if v.Status == model.AfsOrderStatusWait4Approve && time.Now().Sub(v.AfsCreatedAt).Hours() > 12 {
defsch.FixedScheduler.AgreeOrRefuseRefund(ctx, v.AfsOrderID, v.VendorID, model.AfsTypePartRefund, "超时系统同意")
}
}
}
func RefreshRealMobile(ctx *jxcontext.Context, vendorID int, fromTime, toTime time.Time, isAsync, isContinueWhenError bool) (hint string, err error) {
globals.SugarLogger.Debug("RefreshRealMobile")
handler := partner.GetPurchasePlatformFromVendorID(vendorID)
if handler == nil {
return "", fmt.Errorf("不合法的vendorID:%d", vendorID)
}
sql := `
SELECT *
FROM goods_order
WHERE vendor_id = ? AND consignee_mobile2 = ''
`
sqlParams := []interface{}{
vendorID,
}
if !utils.IsTimeZero(fromTime) {
sql += " AND order_created_at >= ?"
sqlParams = append(sqlParams, fromTime)
}
if !utils.IsTimeZero(toTime) {
sql += " AND order_created_at <= ?"
sqlParams = append(sqlParams, toTime)
}
var orderList []*model.GoodsOrder
db := dao.GetDB()
if err = dao.GetRows(db, &orderList, sql, sqlParams...); err == nil && len(orderList) > 0 {
task := tasksch.NewParallelTask("misc RefreshRealMobile", tasksch.NewParallelConfig().SetIsContinueWhenError(isContinueWhenError), ctx,
func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
order := batchItemList[0].(*model.GoodsOrder)
mobile, err2 := handler.GetOrderRealMobile(ctx, order)
if err = err2; err == nil {
//mobile = jxutils.FormalizeMobile(mobile)
//if jxutils.IsStringLikeMobile(mobile) && strings.Index(order.ConsigneeMobile, mobile) == -1 {
order.ConsigneeMobile2 = mobile
_, err = dao.UpdateEntity(db, order, "ConsigneeMobile2")
//}
} else {
globals.SugarLogger.Infof("RefreshRealMobile orderID:%s failed with error:%v", order.VendorOrderID, err)
}
return nil, err
}, orderList)
tasksch.HandleTask(task, nil, true).Run()
hint = task.ID
if !isAsync {
_, err = task.GetResult(0)
}
}
return hint, err
}
// 按时间序列循环

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

@@ -0,0 +1,431 @@
package permission
import (
"fmt"
"strings"
"time"
"git.rosy.net.cn/jx-callback/business/jxutils"
"git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
"git.rosy.net.cn/jx-callback/business/model"
"git.rosy.net.cn/jx-callback/business/model/dao"
)
func GetMenu(ctx *jxcontext.Context, userID string) (menus []*model.Menu, err error) {
if userID == "" {
return dao.GetMenu(dao.GetDB(), "", 0, 0, userID)
} else {
if user, err := dao.GetUserByID(dao.GetDB(), "user_id", userID); err == nil {
if user.Type&model.UserTypeRole != 0 {
return dao.GetMenuWithUser(dao.GetDB(), "", 0, 0, userID)
} else {
return dao.GetMenu(dao.GetDB(), "", 0, 0, "")
}
}
}
return dao.GetMenu(dao.GetDB(), "", 0, 0, userID)
}
func AddMenu(ctx *jxcontext.Context, menu *model.Menu) (err error) {
var (
db = dao.GetDB()
)
if menu == nil {
return fmt.Errorf("添加失败menu nil")
}
if menu.Name == "" || menu.Level == 0 {
return fmt.Errorf("添加失败menu 名称和等级必须有值!")
}
menus, err := dao.GetMenu(db, menu.Name, menu.Level, 0, "")
if len(menus) > 0 {
return fmt.Errorf("添加失败!已存在相同名称的 menu name : %v", menu.Name)
}
txDB, _ := dao.Begin(db)
defer func() {
if r := recover(); r != nil {
dao.Rollback(db, txDB)
panic(r)
}
}()
dao.WrapAddIDCULDEntity(menu, ctx.GetUserName())
err = dao.CreateEntity(db, menu)
dao.Commit(db, txDB)
return err
}
func UpdateMenu(ctx *jxcontext.Context, menuID int, payload map[string]interface{}, isDelete bool) (num int64, err error) {
var (
db = dao.GetDB()
)
menu := &model.Menu{}
menu.ID = menuID
err = dao.GetEntity(db, menu)
if err != nil {
return 0, err
}
txDB, _ := dao.Begin(db)
defer func() {
if r := recover(); r != nil {
dao.Rollback(db, txDB)
panic(r)
}
}()
if !isDelete {
valid := dao.StrictMakeMapByStructObject(payload, menu, ctx.GetUserName())
if len(valid) > 0 {
if num, err = dao.UpdateEntityLogically(db, menu, valid, ctx.GetUserName(), nil); err != nil {
dao.Rollback(db, txDB)
return 0, err
}
}
} else {
menu.DeletedAt = time.Now()
num, err = dao.UpdateEntity(db, menu, "DeletedAt")
}
dao.Commit(db, txDB)
return num, err
}
func GetRole(ctx *jxcontext.Context, name string) (roles []*model.Role, err error) {
var (
db = dao.GetDB()
)
roles, err = dao.GetRole(db, name, "")
for _, v := range roles {
if v.CityCodes != "" {
if cityInfos, err := dao.GetPlaces(db, jxutils.StrListToIntList(strings.Split(v.CityCodes, ","))); err == nil {
v.CityInfo = cityInfos
}
}
if v.StoreIDs != "" {
if stores, err := dao.GetStoreList(db, jxutils.StrListToIntList(strings.Split(v.StoreIDs, ",")), nil, nil, nil, nil, ""); err == nil {
v.Stores = stores
}
}
}
return roles, err
}
func AddRole(ctx *jxcontext.Context, name string) (err error) {
var (
db = dao.GetDB()
)
roles, err := dao.GetRole(db, "", name)
if len(roles) > 0 {
return fmt.Errorf("添加失败!已存在相同名称的 role name : %v", name)
}
role := &model.Role{
Name: name,
}
txDB, _ := dao.Begin(db)
defer func() {
if r := recover(); r != nil {
dao.Rollback(db, txDB)
panic(r)
}
}()
dao.WrapAddIDCULDEntity(role, ctx.GetUserName())
err = dao.CreateEntity(db, role)
dao.Commit(db, txDB)
return err
}
func UpdateRole(ctx *jxcontext.Context, roleID int, name string, isDelete bool, brandID int, cityCodes, storeIDs []int) (num int64, err error) {
var (
db = dao.GetDB()
cityCodesStr []string
storeIDsStr []string
)
for _, v := range cityCodes {
cityCodesStr = append(cityCodesStr, utils.Int2Str(v))
}
for _, v := range storeIDs {
storeIDsStr = append(storeIDsStr, utils.Int2Str(v))
}
if roleID == 1 {
return 0, fmt.Errorf("管理员不允许修改!")
}
role := &model.Role{}
role.ID = roleID
err = dao.GetEntity(db, role)
if err != nil {
return 0, err
}
txDB, _ := dao.Begin(db)
defer func() {
if r := recover(); r != nil {
dao.Rollback(db, txDB)
panic(r)
}
}()
if !isDelete {
role.Name = name
role.UpdatedAt = time.Now()
role.LastOperator = ctx.GetUserName()
role.BrandID = brandID
role.CityCodes = strings.Join(cityCodesStr, ",")
role.StoreIDs = strings.Join(storeIDsStr, ",")
num, err = dao.UpdateEntity(db, role, "Name", "UpdatedAt", "LastOperator", "BrandID", "CityCodes", "StoreIDs")
} else {
role.DeletedAt = time.Now()
num, err = dao.UpdateEntity(db, role, "DeletedAt")
}
dao.Commit(db, txDB)
return num, err
}
func GetUserRole(ctx *jxcontext.Context, userID string) (userRoles []*model.UserRole, err error) {
return dao.GetUserRole(dao.GetDB(), []string{userID}, nil)
}
func UpdateUserRole(ctx *jxcontext.Context, userIDs []string, roleIDs []int) (err error) {
var (
db = dao.GetDB()
roleIDMap = make(map[int]int)
nowRoleIDMap = make(map[int]int)
userRoleMap = make(map[string][]int)
addUserRoleMap = make(map[string][]int)
deleteUserRoleMap = make(map[string][]int)
)
for _, v := range roleIDs {
roleIDMap[v] = 1
}
userRoles, err := dao.GetUserRole(db, userIDs, nil)
if err != nil {
return err
}
if len(userRoles) > 0 {
for _, v := range userRoles {
userRoleMap[v.UserID] = append(userRoleMap[v.UserID], v.RoleID)
}
}
for _, userID := range userIDs {
nowRoleIDs := userRoleMap[userID]
for _, nowRoleID := range nowRoleIDs {
if roleIDMap[nowRoleID] == 0 {
deleteUserRoleMap[userID] = append(deleteUserRoleMap[userID], nowRoleID)
}
nowRoleIDMap[nowRoleID] = 1
}
for roleID, _ := range roleIDMap {
if nowRoleIDMap[roleID] == 0 {
addUserRoleMap[userID] = append(addUserRoleMap[userID], roleID)
}
}
}
txDB, _ := dao.Begin(db)
defer func() {
if r := recover(); r != nil {
dao.Rollback(db, txDB)
panic(r)
}
}()
if len(addUserRoleMap) > 0 {
for userID, roleIDs := range addUserRoleMap {
for _, roleID := range roleIDs {
userRole := &model.UserRole{
UserID: userID,
RoleID: roleID,
}
dao.WrapAddIDCULDEntity(userRole, ctx.GetUserName())
err = dao.CreateEntity(db, userRole)
}
}
}
if len(deleteUserRoleMap) > 0 {
for userID, roleIDs := range deleteUserRoleMap {
for _, roleID := range roleIDs {
userRoles, _ := dao.GetUserRole(db, []string{userID}, []int{roleID})
if len(userRoles) > 0 {
userRoles[0].DeletedAt = time.Now()
userRoles[0].LastOperator = ctx.GetUserName()
_, err = dao.UpdateEntity(db, userRoles[0], "DeletedAt", "LastOperator")
}
}
}
}
for _, v := range userIDs {
if user, err := dao.GetUserByID(db, "user_id", v); err == nil {
user.Type = user.Type | model.UserTypeRole
dao.UpdateEntity(db, user, "Type")
}
}
dao.Commit(db, txDB)
return err
}
func GetRoleMenu(ctx *jxcontext.Context, roleID int) (roleMenus []*model.RoleMenu, err error) {
return dao.GetRoleMenu(dao.GetDB(), []int{roleID}, nil)
}
func UpdateRoleMenu(ctx *jxcontext.Context, roleIDs, menuIDs []int) (err error) {
var (
db = dao.GetDB()
menuIDMap = make(map[int]int)
nowMenuIDMap = make(map[int]int)
roleMenuMap = make(map[int][]int)
addRoleMenuMap = make(map[int][]int)
deleteRoleMenuMap = make(map[int][]int)
)
for _, v := range menuIDs {
menuIDMap[v] = 1
}
roleMenus, err := dao.GetRoleMenu(db, roleIDs, nil)
if err != nil {
return err
}
if len(roleMenus) > 0 {
for _, v := range roleMenus {
roleMenuMap[v.RoleID] = append(roleMenuMap[v.RoleID], v.MenuID)
}
}
for _, roleID := range roleIDs {
nowMenuIDs := roleMenuMap[roleID]
for _, nowMenuID := range nowMenuIDs {
if menuIDMap[nowMenuID] == 0 {
deleteRoleMenuMap[roleID] = append(deleteRoleMenuMap[roleID], nowMenuID)
}
nowMenuIDMap[nowMenuID] = 1
}
for menuID, _ := range menuIDMap {
if nowMenuIDMap[menuID] == 0 {
addRoleMenuMap[roleID] = append(addRoleMenuMap[roleID], menuID)
}
}
}
txDB, _ := dao.Begin(db)
defer func() {
if r := recover(); r != nil {
dao.Rollback(db, txDB)
panic(r)
}
}()
if len(addRoleMenuMap) > 0 {
for roleID, menuIDs := range addRoleMenuMap {
for _, menuID := range menuIDs {
roleMenu := &model.RoleMenu{
RoleID: roleID,
MenuID: menuID,
}
dao.WrapAddIDCULDEntity(roleMenu, ctx.GetUserName())
err = dao.CreateEntity(db, roleMenu)
}
}
}
if len(deleteRoleMenuMap) > 0 {
for roleID, menuIDs := range deleteRoleMenuMap {
for _, menuID := range menuIDs {
roleMenus, _ := dao.GetRoleMenu(db, []int{roleID}, []int{menuID})
if len(roleMenus) > 0 {
roleMenus[0].DeletedAt = time.Now()
roleMenus[0].LastOperator = ctx.GetUserName()
_, err = dao.UpdateEntity(db, roleMenus[0], "DeletedAt", "LastOperator")
}
}
}
}
dao.Commit(db, txDB)
return err
}
func GetUserStoresResultMap(userID string) (resultMap map[int]int, err error) {
var (
db = dao.GetDB()
// brandIDMap = make(map[int]int)
// cityCodeMap = make(map[int]int)
// storeIDMap = make(map[int]int)
)
resultMap = make(map[int]int)
user, _ := dao.GetUserByID(db, "user_id", userID)
if user != nil {
if user.BindStoreID != "" {
for _, v := range strings.Split(user.BindStoreID, ",") {
resultMap[utils.Str2Int(v)] = utils.Str2Int(v)
}
return resultMap, err
}
}
userRoles, err2 := dao.GetUserRole2(db, []string{userID}, nil)
err = err2
for _, v := range userRoles {
var (
brandIDs, cityCodes, storeIDs []int
)
// if _, ok := brandIDMap[v.BrandID]; !ok {
// brandIDMap[v.BrandID] = 1
// }
// if v.CityCodes != "" {
// for _, cityCode := range jxutils.StrListToIntList(strings.Split(v.CityCodes, ",")) {
// if _, ok := cityCodeMap[cityCode]; !ok {
// cityCodeMap[cityCode] = 1
// }
// }
// }
// if v.StoreIDs != "" {
// for _, storeID := range jxutils.StrListToIntList(strings.Split(v.StoreIDs, ",")) {
// if _, ok := storeIDMap[storeID]; !ok {
// storeIDMap[storeID] = 1
// }
// }
// }
if v.CityCodes == "" && v.StoreIDs == "" {
continue
}
if v.BrandID != 0 {
brandIDs = append(brandIDs, v.BrandID)
}
if v.CityCodes != "0" && v.CityCodes != "" {
cityCodes = append(cityCodes, jxutils.StrListToIntList(strings.Split(v.CityCodes, ","))...)
}
if v.StoreIDs != "" {
storeIDs = append(storeIDs, jxutils.StrListToIntList(strings.Split(v.StoreIDs, ","))...)
}
if stores, err := dao.GetStoreList(db, storeIDs, cityCodes, nil, brandIDs, nil, ""); len(stores) > 0 && err == nil {
for _, v := range stores {
resultMap[v.ID] = v.ID
}
}
}
// for k, _ := range brandIDMap {
// brandIDs = append(brandIDs, k)
// }
// for k, _ := range cityCodeMap {
// cityCodes = append(cityCodes, k)
// }
// for k, _ := range storeIDMap {
// storeIDs = append(storeIDs, k)
// }
// if stores1, err := dao.GetStoreList(db, nil, nil, brandIDs, nil, nil, ""); len(stores1) > 0 && err == nil {
// stores = append(stores, stores1...)
// }
// if stores2, err := dao.GetStoreList(db, nil, cityCodes, nil, nil, nil, ""); len(stores2) > 0 && err == nil {
// stores = append(stores, stores2...)
// }
// if stores3, err := dao.GetStoreList(db, storeIDs, nil, nil, nil, nil, ""); len(stores3) > 0 && err == nil {
// stores = append(stores, stores3...)
// }
return resultMap, err
}
func IsRoled(ctx *jxcontext.Context) bool {
if ctx.GetUserName() != "jxadmin" {
if user, err := dao.GetUserByID(dao.GetDB(), "user_id", ctx.GetUserID()); err == nil {
if user.Type&model.UserTypeRole != 0 {
return true
}
}
}
return false
}
func IsRoledByUserID(userID string) bool {
if user, err := dao.GetUserByID(dao.GetDB(), "user_id", userID); err == nil {
if user.Type&model.UserTypeRole != 0 {
return true
}
}
return false
}

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

@@ -4,8 +4,8 @@ import (
"fmt"
"sync"
"git.rosy.net.cn/jx-callback/business/dao"
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
"git.rosy.net.cn/jx-callback/business/model/dao"
"git.rosy.net.cn/jx-callback/business/model/legacymodel"
)

View File

@@ -9,19 +9,24 @@ import (
"image/png"
"net/http"
"git.rosy.net.cn/jx-callback/business/dao"
"git.rosy.net.cn/jx-callback/business/jxutils"
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
"git.rosy.net.cn/jx-callback/business/jxutils/tasksch"
"git.rosy.net.cn/jx-callback/business/model"
"git.rosy.net.cn/jx-callback/business/model/dao"
"git.rosy.net.cn/jx-callback/business/partner"
"git.rosy.net.cn/jx-callback/globals"
"git.rosy.net.cn/jx-callback/globals/api"
"github.com/qiniu/api.v7/storage"
)
const (
qiniuTokenExpires = 300 // 七牛TOKEN有效时间5分钟
MainImgWidth = 800
MainImgHeight = 800
MainImgWidth = 800
MainImgHeight = 800
MainImgWidth2 = 500
MainImgHeight2 = 500
)
type UploadResTokenInfo struct {
@@ -48,24 +53,23 @@ func Binary2Image(binaryData []byte, mimeType string) (img image.Image, outMimeT
}
func GetQiniuUploadToken(ctx *jxcontext.Context, suffix, hashCode string) (upTokenInfo *UploadResTokenInfo, err error) {
//imgURL := ""
//if hashCode != "" {
// imgURL, _ = GetDataResource(ctx, hashCode)
//}
//
//putPolicy := storage.PutPolicy{
// Scope: globals.QiniuBucket,
// Expires: qiniuTokenExpires,
//}
//upTokenInfo = &UploadResTokenInfo{
// Token: putPolicy.UploadToken(api.QiniuAPI),
// Expires: putPolicy.Expires,
// FileName: jxutils.GenPicFileName(suffix),
// Hit: imgURL != "",
// Img: imgURL,
//}
//return upTokenInfo, err
return nil, err
imgURL := ""
if hashCode != "" {
imgURL, _ = GetDataResource(ctx, hashCode)
}
putPolicy := storage.PutPolicy{
Scope: globals.QiniuBucket,
Expires: qiniuTokenExpires,
}
upTokenInfo = &UploadResTokenInfo{
Token: putPolicy.UploadToken(api.QiniuAPI),
Expires: putPolicy.Expires,
FileName: jxutils.GenPicFileName(suffix),
Hit: imgURL != "",
Img: imgURL,
}
return upTokenInfo, err
}
// 此函数要求resBinary不能空mimeType与hashCode必须是正确的
@@ -79,7 +83,7 @@ func RegisterDataResource(ctx *jxcontext.Context, name, resourceURL, mimeType, h
return nil, err
}
if imgType == model.ImgTypeMain {
if img.Bounds().Dx() != MainImgWidth || img.Bounds().Dy() != MainImgHeight {
if (img.Bounds().Dx() != MainImgWidth || img.Bounds().Dy() != MainImgHeight) && (img.Bounds().Dx() != MainImgWidth2 || img.Bounds().Dy() != MainImgHeight2) {
return nil, fmt.Errorf("图片大小:%dx%d非法要求必须:%dx%d", img.Bounds().Dx(), img.Bounds().Dy(), MainImgWidth, MainImgHeight)
}
}
@@ -107,10 +111,10 @@ func RegisterDataResource(ctx *jxcontext.Context, name, resourceURL, mimeType, h
return dataRes, err
}
if imgType > 0 {
//if globals.EnableStoreWrite {
// 忽略上传错误
UploadImage2Vendors(ctx, nil, dataRes, resBinary, isAsyncUpload2Vendor)
//}
if globals.EnableStoreWrite {
// 忽略上传错误
UploadImage2Vendors(ctx, nil, dataRes, resBinary, isAsyncUpload2Vendor)
}
}
return dataRes, err
}
@@ -150,82 +154,82 @@ func GetDataResource(ctx *jxcontext.Context, hashCode string) (resourceURL strin
// 这个函数,可能部分平台成功,部分失败
func UploadImage2Vendors(ctx *jxcontext.Context, parentTask tasksch.ITask, dataRes *model.DataResource, imgData []byte, isAsync bool) (hint string, err error) {
// var vendorIDs []int
// if dataRes.EbaiURL == "" {
// vendorIDs = append(vendorIDs, model.VendorIDEBAI)
// }
// if dataRes.MtwmURL == "" {
// vendorIDs = append(vendorIDs, model.VendorIDMTWM)
// }
var vendorIDs []int
if dataRes.EbaiURL == "" {
vendorIDs = append(vendorIDs, model.VendorIDEBAI)
}
if dataRes.MtwmURL == "" {
vendorIDs = append(vendorIDs, model.VendorIDMTWM)
}
// if dataRes.JdsURL == "" {
// vendorIDs = append(vendorIDs, model.VendorIDJDShop)
// }
// if len(vendorIDs) > 0 {
// imgName := jxutils.GetShortNameFromURL(dataRes.MainURL)
// task := tasksch.NewSeqTask(fmt.Sprintf("上传图片至平台1:%s,%s", dataRes.Name, dataRes.MainURL), ctx,
// func(task *tasksch.SeqTask, step int, params ...interface{}) (result interface{}, err error) {
// switch step {
// case 0:
// if imgData == nil {
// if imgData, _, err = jxutils.DownloadFileByURL(dataRes.MainURL); err != nil {
// return nil, err
// }
// }
// case 1:
// uploadTask := tasksch.NewParallelTask(fmt.Sprintf("上传图片至平台2:%s,%s", dataRes.Name, imgName),
// tasksch.NewParallelConfig().SetIsContinueWhenError(true), ctx,
// func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
// vendorID := batchItemList[0].(int)
// if handler := partner.GetPurchasePlatformFromVendorID(vendorID); handler != nil {
// // TODO vendorOrgCode
// imgHint, err2 := handler.UploadImg(ctx, "", dataRes.MainURL, imgData, imgName, int(dataRes.UseType))
// if err = err2; err == nil {
// retVal = [][]interface{}{
// []interface{}{
// vendorID,
// imgHint,
// },
// }
// }
// }
// return retVal, err
// }, vendorIDs)
// tasksch.HandleTask(uploadTask, task, false).Run()
// resultList, err2 := uploadTask.GetResult(0)
// err = err2
// if len(resultList) > 0 {
// db := dao.GetDB()
// for _, v := range resultList {
// result := v.([]interface{})
// vendorID := result[0].(int)
// imgHint := result[1].(string)
// updateField := ""
// if vendorID == model.VendorIDEBAI {
// dataRes.EbaiURL = imgHint
// updateField = "EbaiURL"
// } else if vendorID == model.VendorIDMTWM {
// dataRes.MtwmURL = imgHint
// updateField = "MtwmURL"
// }
// // else if vendorID == model.VendorIDJDShop {
// // dataRes.JdsURL = imgHint
// // updateField = "JdsURL"
// // }
// dao.UpdateEntity(db, dataRes, updateField)
// }
// }
// }
// return result, err
// }, 2)
// tasksch.HandleTask(task, parentTask, false).Run()
// if !isAsync {
// if _, err = task.GetResult(0); err == nil {
// hint = "1"
// }
// } else {
// hint = task.GetID()
// }
// }
if len(vendorIDs) > 0 {
imgName := jxutils.GetShortNameFromURL(dataRes.MainURL)
task := tasksch.NewSeqTask(fmt.Sprintf("上传图片至平台1:%s,%s", dataRes.Name, dataRes.MainURL), ctx,
func(task *tasksch.SeqTask, step int, params ...interface{}) (result interface{}, err error) {
switch step {
case 0:
if imgData == nil {
if imgData, _, err = jxutils.DownloadFileByURL(dataRes.MainURL); err != nil {
return nil, err
}
}
case 1:
uploadTask := tasksch.NewParallelTask(fmt.Sprintf("上传图片至平台2:%s,%s", dataRes.Name, imgName),
tasksch.NewParallelConfig().SetIsContinueWhenError(true), ctx,
func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
vendorID := batchItemList[0].(int)
if handler := partner.GetPurchasePlatformFromVendorID(vendorID); handler != nil {
// TODO vendorOrgCode
imgHint, err2 := handler.UploadImg(ctx, "", dataRes.MainURL, imgData, imgName, int(dataRes.UseType))
if err = err2; err == nil {
retVal = [][]interface{}{
[]interface{}{
vendorID,
imgHint,
},
}
}
}
return retVal, err
}, vendorIDs)
tasksch.HandleTask(uploadTask, task, false).Run()
resultList, err2 := uploadTask.GetResult(0)
err = err2
if len(resultList) > 0 {
db := dao.GetDB()
for _, v := range resultList {
result := v.([]interface{})
vendorID := result[0].(int)
imgHint := result[1].(string)
updateField := ""
if vendorID == model.VendorIDEBAI {
dataRes.EbaiURL = imgHint
updateField = "EbaiURL"
} else if vendorID == model.VendorIDMTWM {
dataRes.MtwmURL = imgHint
updateField = "MtwmURL"
}
// else if vendorID == model.VendorIDJDShop {
// dataRes.JdsURL = imgHint
// updateField = "JdsURL"
// }
dao.UpdateEntity(db, dataRes, updateField)
}
}
}
return result, err
}, 2)
tasksch.HandleTask(task, parentTask, false).Run()
if !isAsync {
if _, err = task.GetResult(0); err == nil {
hint = "1"
}
} else {
hint = task.GetID()
}
}
return hint, err
}

View File

@@ -7,11 +7,17 @@ import (
"git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
"git.rosy.net.cn/jx-callback/business/model"
"git.rosy.net.cn/jx-callback/globals/api2"
"git.rosy.net.cn/jx-callback/globals/testinit"
_ "git.rosy.net.cn/jx-callback/business/partner/purchase/ebai"
_ "git.rosy.net.cn/jx-callback/business/partner/purchase/jd"
_ "git.rosy.net.cn/jx-callback/business/partner/purchase/mtwm"
)
func init() {
testinit.Init()
api2.Init()
}
func TestGetQiniuUploadToken(t *testing.T) {

View File

@@ -0,0 +1,58 @@
package ddmsg
import (
"fmt"
"git.rosy.net.cn/baseapi/platformapi/dingdingapi"
"git.rosy.net.cn/baseapi/utils/errlist"
"git.rosy.net.cn/jx-callback/business/auth2"
"git.rosy.net.cn/jx-callback/business/auth2/authprovider/dingding"
"git.rosy.net.cn/jx-callback/globals"
"git.rosy.net.cn/jx-callback/globals/api"
)
func SendDDUserMessage(msgType, ddUserID, title, content string) (err error) {
globals.SugarLogger.Debugf("SendDDUserMessage ddUserID:%s, title:%s", ddUserID, title)
if globals.IsProductEnv() {
if msgType == dingdingapi.MsgTyeText {
err = api.DingDingAPI.CorpAsyncSendSimple(ddUserID, content)
} else if msgType == dingdingapi.MsgTypeMarkdown {
err = api.DingDingAPI.CorpAsyncSendMarkdown([]string{ddUserID}, nil, false, title, content)
}
}
return err
}
func SendUserMessage(msgType, userID, title, content string) (err error) {
globals.SugarLogger.Debugf("SendUserMessage userID:%s, title:%s", userID, title)
authList, err := auth2.GetUserBindAuthInfo(userID)
findOneMethod := false
if err == nil {
for _, auth := range authList {
if auth.Type == dingding.AuthTypeStaff /*|| auth.Type == weixin.AuthTypeMP*/ {
findOneMethod = true
if len(content) > dingdingapi.MaxWorkContentLen {
content = content[:dingdingapi.MaxWorkContentLen-4] + "..."
}
err = SendDDUserMessage(msgType, auth.AuthID, title, content)
break
}
}
}
if !findOneMethod {
err = fmt.Errorf("用户[%s]找不到至少一个有效的通讯方式", userID)
}
if err != nil {
globals.SugarLogger.Infof("SendUserMessage userID:%s, title:%s, content:%s failed with error:%v", userID, title, content, err)
}
return err
}
func SendUsersMessage(msgType string, userIDs []string, title, content string) (err error) {
errList := errlist.New()
for _, userID := range userIDs {
errList.AddErr(SendUserMessage(msgType, userID, title, content))
}
err = errList.GetErrListAsOne()
return err
}

View File

@@ -7,8 +7,8 @@ import (
"time"
"git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/business/dao"
"git.rosy.net.cn/jx-callback/business/model"
"git.rosy.net.cn/jx-callback/business/model/dao"
"git.rosy.net.cn/jx-callback/business/partner"
"git.rosy.net.cn/jx-callback/globals/refutil"
)

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()
case EventTypeWX2Token:
token = api.WeixinMiniAPI2.CBGetToken()
}
if token != oldToken {

View File

@@ -7,6 +7,7 @@ import (
"git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/business/auth2"
"git.rosy.net.cn/jx-callback/business/model"
"git.rosy.net.cn/jx-callback/business/model/dao"
"git.rosy.net.cn/jx-callback/globals"
)
@@ -24,8 +25,6 @@ type Context struct {
const (
MaxUserNameLen = 30
RsmDefultToken = "rushSkyMonkeyToken_20201203"
)
var (
@@ -73,10 +72,7 @@ func New(notUsed interface{}, token string, w http.ResponseWriter, r *http.Reque
// }
}
if err == model.ErrTokenIsInvalid {
//if !globals.IsProductEnv() {
// err = nil
//} else
if token == RsmDefultToken {
if !globals.IsProductEnv() {
err = nil
} else {
errCode = model.ErrCodeTokenIsInvalid
@@ -162,3 +158,14 @@ func (ctx *Context) GetUserID() (userID string) {
}
return userID
}
func (ctx *Context) GetFullUser() (user *model.User) {
token := ctx.GetToken()
authInfo, err2 := auth2.GetTokenInfo(token)
if err2 == nil {
if authInfo.TokenType == auth2.TokenTypeNormal {
user, _ = dao.GetUserByID(dao.GetDB(), "user_id", authInfo.GetID())
}
}
return user
}

View File

@@ -1,6 +1,8 @@
package jxutils
import (
"bytes"
"context"
"crypto/aes"
"crypto/md5"
"encoding/base64"
@@ -9,12 +11,19 @@ import (
"math"
"math/rand"
"regexp"
"sort"
"strings"
"time"
"git.rosy.net.cn/baseapi"
"git.rosy.net.cn/baseapi/platformapi/autonavi"
"git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/baseapi/utils/routinepool"
"git.rosy.net.cn/jx-callback/business/jxutils/excel"
"git.rosy.net.cn/jx-callback/business/model"
"git.rosy.net.cn/jx-callback/globals"
"git.rosy.net.cn/jx-callback/globals/api"
"github.com/qiniu/api.v7/storage"
)
var (
@@ -33,22 +42,36 @@ var (
model.VendorIDEBAI: []string{
"image-star.elemecdn.com",
},
}
letterBytes = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
flowUnitMap = map[string]string{
"KB": "KB",
"MB": "MB",
"GB": "GB",
model.VendorIDYB: []string{
"pospalstoreimg.area27.pospal.cn",
},
}
)
const fileExt = ".xlsx"
type OrderSkuList []*model.OrderSku
func (l OrderSkuList) Len() int {
return len(l)
}
// Less reports whether the element with
// index i should sort before the element with index j.
func (l OrderSkuList) Less(i, j int) bool {
return l[i].SalePrice < l[j].SalePrice
}
// Swap swaps the elements with indexes i and j.
func (l OrderSkuList) Swap(i, j int) {
tmp := l[i]
l[i] = l[j]
l[j] = tmp
}
func init() {
rand.Seed(time.Now().Unix())
routinePool = routinepool.New(1000, 1000)
routinePool = routinepool.New(2000, 2000)
// Go regex does not support lookarounds.
// https://stackoverflow.com/questions/38933898/error-parsing-regexp-invalid-or-unsupported-perl-syntax
@@ -57,20 +80,102 @@ func init() {
orderNoBeginTimestamp = utils.Str2Time("2010-01-01 00:00:00").Unix()
}
func RandStringBytes(n int) string {
b := make([]byte, n)
for i := range b {
b[i] = letterBytes[rand.Intn(len(letterBytes))]
func getJxStoreIDFromOrder(order *model.GoodsOrder) (retVal int) {
if order.JxStoreID != 0 {
return order.JxStoreID
}
return string(b)
return order.StoreID
}
func GenRand6() (num int) {
return utils.Str2Int(fmt.Sprintf("%06v", rand.New(rand.NewSource(time.Now().UnixNano())).Int31n(1000000)))
// 此函数得到的是order的销售门店京西ID与GetJxStoreIDFromOrder的区别是order.StoreID的解释不同参考其它相关资料
func GetSaleStoreIDFromOrder(order *model.GoodsOrder) (retVal int) {
return getJxStoreIDFromOrder(order)
}
// 此函数得到的是order的商品的展示门店京西ID与GetJxStoreIDFromOrder的区别是order.StoreID的解释不同参考其它相关资料
func GetShowStoreIDFromOrder(order *model.GoodsOrder) (retVal int) {
return getJxStoreIDFromOrder(order)
}
func GetSkuIDFromOrderSku(sku *model.OrderSku) (skuID int) {
if sku.JxSkuID > 0 {
return sku.JxSkuID
}
return sku.SkuID
}
func GetSaleStoreIDFromAfsOrder(order *model.AfsOrder) (retVal int) {
if order.JxStoreID > 0 {
return order.JxStoreID
}
return order.StoreID
}
func GetSkuIDFromOrderSkuFinancial(sku *model.OrderSkuFinancial) (skuID int) {
if sku.JxSkuID > 0 {
return sku.JxSkuID
}
return sku.SkuID
}
func SplitUniversalOrderID(universalOrderID string) (orderID string, vendorID int) {
index := strings.Index(universalOrderID, "|")
if index != -1 {
orderID = universalOrderID[:index]
vendorID = int(utils.Str2Int64(universalOrderID[index+1:]))
} else {
if vendorID = GetPossibleVendorIDFromVendorOrderID(universalOrderID); vendorID == model.VendorIDUnknown {
// globals.SugarLogger.Errorf("unkown order type:%v", universalOrderID)
panic(fmt.Sprintf("unkown order type, orderID:%s", universalOrderID))
}
orderID = universalOrderID
}
return orderID, vendorID
}
func GetPossibleVendorIDFromVendorOrderID(vendorOrderID string) (vendorID int) {
vendorID = model.VendorIDUnknown
//if vendorOrderIDInt64 := utils.Str2Int64WithDefault(vendorOrderID, 0); vendorOrderIDInt64 > 0 {
orderIDLen := len(vendorOrderID)
globals.SugarLogger.Debugf("GetPossibleVendorIDFromVendorOrderID, orderIDLen: %v", orderIDLen)
// 5287873015048 13 wsc
// 15380342248732 14 old ebai order
// 800402581000221 15,16 jd order
// 33437032333978492 17 mtwm order
// 3022716176275221584 19 elm order, new ebai order
// 京东到家从2020年开始订单号的长度都会在现有基础上加一位订单号的前两位取的是当年的最后两位数2020取的20以适应业务的发展。
// 改造点:
// 1、订单号位数变化由原有15位数增加1位数调整为16位数对接商家需检查是否有对订单号位数做长度校验。
// 2、第一位数字发生变化由原来9开头调整为当年年份后两位数如2020年订单开头为20
if orderIDLen == len("925265130002541") || orderIDLen == len("1925265130002541") {
vendorID = model.VendorIDJD
} else if orderIDLen == len("3022716176275221584") {
// vendorID = model.VendorIDELM
vendorID = model.VendorIDEBAI // 饿百零售开放平台订单接口中订单ID“order_id”字段长度将调整为19位和饿了么订单ID“eleme_order_id”字段格式保持一致。
} else if orderIDLen == len("15380342248732") {
if vendorOrderID[:2] == "88" {
vendorID = model.VendorIDJX
} else {
vendorID = model.VendorIDEBAI
}
} else if orderIDLen == len("33437032333978492") || orderIDLen == len("116379390766579767") {
vendorID = model.VendorIDMTWM
} else if orderIDLen == len("5287873015048") {
vendorID = model.VendorIDWSC
} else if orderIDLen == len("1000004390") {
vendorID = model.VendorIDJX
} else if orderIDLen == len("18100216009800000001") || orderIDLen == len("191075245758000000039") {
vendorID = model.VendorIDJDShop
}
//} else {
// globals.SugarLogger.Debugf("GetPossibleVendorIDFromVendorOrderID, 2: %v", vendorOrderID)
//}
return vendorID
}
func GenOrderNo() (orderNo int64) {
var prefix = utils.Str2Int64(time.Now().Format("20060102"))
const prefix = 88
const randPartNum = 1000
orderNo = time.Now().Unix() - orderNoBeginTimestamp
orderNo = orderNo * randPartNum
@@ -84,6 +189,54 @@ func GenOrderNo() (orderNo int64) {
return orderNo
}
func GenAfsOrderNo() (orderNo int64) {
const prefix = 80
const randPartNum = 100
orderNo = time.Now().Unix() - orderNoBeginTimestamp
orderNo = orderNo * randPartNum
md5Bytes := md5.Sum([]byte(utils.GetUUID()))
randPart := 0
for k, v := range md5Bytes {
randPart += int(v) << ((k % 3) * 8)
}
orderNo += int64(randPart % randPartNum)
orderNo += int64(math.Pow10(int(math.Log10(float64(orderNo)))+1)) * prefix
return orderNo
}
func GetPossibleVendorIDFromAfsOrderID(afsOrderID string) (vendorID int) {
vendorID = model.VendorIDUnknown
if afsOrderIDInt64 := utils.Str2Int64WithDefault(afsOrderID, 0); afsOrderIDInt64 > 0 {
orderIDLen := len(afsOrderID)
if orderIDLen == len("22586438") { // 8
vendorID = model.VendorIDJD
} else if orderIDLen == len("1413138834") { // 10
vendorID = model.VendorIDEBAI
} else if orderIDLen == len("29488498752") { // 11
vendorID = model.VendorIDMTWM
}
}
return vendorID
}
func ComposeUniversalOrderID(orderID string, vendorID int) string {
// return fmt.Sprintf("%s|%d", orderID, vendorID)
return orderID // 当前用长度就能区分先不加上vendorID
// return orderID + utils.Int64ToStr(time.Now().Unix())
}
func GetUniversalOrderIDFromWaybill(bill *model.Waybill) string {
return ComposeUniversalOrderID(bill.VendorOrderID, bill.OrderVendorID)
}
func GetUniversalOrderIDFromOrder(order *model.GoodsOrder) string {
return ComposeUniversalOrderID(order.VendorOrderID, order.VendorID)
}
func GetUniversalOrderIDFromOrderStatus(status *model.OrderStatus) string {
return ComposeUniversalOrderID(status.VendorOrderID, status.VendorID)
}
// distance单位为米
func ConvertDistanceToLogLat(lng, lat, distance, angle float64) (newLng, newLat float64) {
oneDu := 111319.55 // 单位为米
@@ -108,6 +261,36 @@ func EarthDistance(lng1, lat1, lng2, lat2 float64) float64 {
return dist * radius
}
// 返回结果单元为公里
func WalkingDistance(lng1, lat1, lng2, lat2 float64) (distance float64) {
if distance = api.AutonaviAPI.WalkingDistance(lng1, lat1, lng2, lat2); distance == 0 {
distance = EarthDistance(lng1, lat1, lng2, lat2) * 1.4
} else {
distance /= 1000
}
return distance
}
//经纬度坐标转换到平面坐标
func MillierConvertion(lat float64, lon float64) [2]float64 {
var L, H, W, temp, mill, x, y float64
L = 6371.393 * math.Pi * 2 //地球周长
W = L // 平面展开后x轴等于周长
H = L / 2 // y轴约等于周长一半
mill = 2.3 // 米勒投影中的一个常数范围大约在正负2.3之间
temp = math.Pi
x = lon * temp / 180 // 将经度从度数转换为弧度
y = lat * temp / 180 // 将纬度从度数转换为弧度
y = 1.25 * math.Log(math.Tan(0.25*temp+0.4*y)) // 米勒投影的转换
// 弧度转为实际距离
x = (W / 2) + (W/(2*math.Pi))*x
y = (H / 2) - (H/(2*mill))*y
x = utils.Str2Float64(fmt.Sprintf("%.2f", x))
y = utils.Str2Float64(fmt.Sprintf("%.2f", y))
var result = [2]float64{x, y}
return result
}
func StandardCoordinate2Int(value float64) int {
return int(math.Round(value * 1000000))
}
@@ -116,6 +299,25 @@ func IntCoordinate2Standard(value int) float64 {
return float64(value) / 1000000
}
func IntCoordinate2MarsStandard(gpsLng, gpsLat int, coordinateType int) (marsLng, marsLat float64, err error) {
marsLng = IntCoordinate2Standard(gpsLng)
marsLat = IntCoordinate2Standard(gpsLat)
coordSys := ""
switch coordinateType {
case model.CoordinateTypeGPS:
coordSys = autonavi.CoordSysGPS
case model.CoordinateTypeMars:
return marsLng, marsLat, nil
case model.CoordinateTypeBaiDu:
coordSys = autonavi.CoordSysBaidu
case model.CoordinateTypeMapbar:
coordSys = autonavi.CoordSysMapbar
default:
panic(fmt.Sprintf("known coordinate type:%d", coordinateType))
}
return api.AutonaviAPI.CoordinateConvert(marsLng, marsLat, coordSys)
}
func IntPrice2Standard(value int64) float64 {
return float64(value) / 100
}
@@ -218,7 +420,7 @@ func ComposeSkuNameOriginal(prefix, name, comment, unit string, spec_quality flo
return skuName
}
func ComposeSkuNameForJds(prefix, name, comment, unit string, spec_quality float32, spec_unit string, maxLen int) (skuName string) {
func ComposeSkuNameForMTWM(prefix, name, comment, unit string, spec_quality float32, spec_unit string, maxLen int) (skuName string) {
strBuilder := &strings.Builder{}
if prefix != "" {
strBuilder.WriteString("[")
@@ -227,15 +429,16 @@ func ComposeSkuNameForJds(prefix, name, comment, unit string, spec_quality float
}
skuName += name
strBuilder.WriteString(name)
if comment != "" {
strBuilder.WriteString(" ")
strBuilder.WriteString(comment)
}
if unit == "份" {
strBuilder.WriteString("约")
}
strBuilder.WriteString("")
if unit != "" {
strBuilder.WriteString(ComposeSkuSpec(spec_quality, spec_unit))
strBuilder.WriteString("/")
strBuilder.WriteString(unit)
}
if comment != "" {
strBuilder.WriteString("(")
strBuilder.WriteString(comment)
strBuilder.WriteString(")")
}
skuName = strBuilder.String()
if maxLen > 0 {
@@ -254,10 +457,12 @@ func ComposeSkuName(prefix, name, comment, unit string, spec_quality float32, sp
return skuName
}
func ComposeSkuNameSync(prefix, name, comment, unit string, spec_quality float32, spec_unit string, maxLen int, exPrefix string, exPrefixBegin, exPrefixEnd *time.Time) (skuName string) {
func ComposeSkuNameSync(prefix, name, comment, unit string, spec_quality float32, spec_unit string, maxLen int, exPrefix string, exPrefixBegin, exPrefixEnd *time.Time, isEx bool) (skuName string) {
if exPrefix != "" && exPrefixBegin != nil && exPrefixEnd != nil {
if utils.Time2Date(time.Now().Add(6*time.Hour)).Sub(*exPrefixBegin) >= 0 && utils.Time2Date(time.Now()).Sub(*exPrefixEnd) <= 0 {
skuName = exPrefix
if isEx {
skuName = exPrefix
}
}
if utils.Time2Date(time.Now().Add(6*time.Hour)).Sub(*exPrefixEnd) > 0 {
skuName = ""
@@ -267,16 +472,18 @@ func ComposeSkuNameSync(prefix, name, comment, unit string, spec_quality float32
return skuName
}
func ComposeSkuNameSync2(prefix, name, comment, unit string, spec_quality float32, spec_unit string, maxLen int, exPrefix string, exPrefixBegin, exPrefixEnd *time.Time) (skuName string) {
func ComposeSkuNameSync2(prefix, name, comment, unit string, spec_quality float32, spec_unit string, maxLen int, exPrefix string, exPrefixBegin, exPrefixEnd *time.Time, isEx bool) (skuName string) {
if exPrefix != "" && exPrefixBegin != nil && exPrefixEnd != nil {
if utils.Time2Date(time.Now().Add(6*time.Hour)).Sub(*exPrefixBegin) >= 0 && utils.Time2Date(time.Now()).Sub(*exPrefixEnd) <= 0 {
skuName = exPrefix
if isEx {
skuName = exPrefix
}
}
if utils.Time2Date(time.Now().Add(6*time.Hour)).Sub(*exPrefixEnd) > 0 {
skuName = ""
}
}
skuName += ComposeSkuNameForJds(prefix, name, comment, unit, spec_quality, spec_unit, maxLen)
skuName += ComposeSkuNameForMTWM(prefix, name, comment, unit, spec_quality, spec_unit, maxLen)
return skuName
}
@@ -377,7 +584,7 @@ func MakeValidationMapFromSlice(validValues []string, flag int) map[string]int {
}
func ComposeQiniuResURL(key string) string {
return "https://image.jxc4.com/" + key
return "http://image.jxc4.com/" + key
}
func IsLegalMobileNumber(num int64) bool {
@@ -417,6 +624,151 @@ func Strings2Objs(strAndObjAddPairs ...interface{}) (err error) {
return nil
}
func RefreshOrderSkuRelated(order *model.GoodsOrder) *model.GoodsOrder {
order.SkuCount = 0
order.GoodsCount = 0
order.SalePrice = 0
order.VendorPrice = 0
order.ShopPrice = 0
order.Weight = 0
order.EarningPrice = 0
for _, sku := range order.Skus {
if sku.SkuID > math.MaxInt32 {
sku.SkuID = sku.JxSkuID
}
sku.OrderCreatedAt = order.OrderCreatedAt
sku.VendorID = order.VendorID
sku.VendorOrderID = order.VendorOrderID
order.SkuCount++
order.GoodsCount += sku.Count
order.SalePrice += sku.SalePrice * int64(sku.Count)
order.VendorPrice += sku.VendorPrice * int64(sku.Count)
order.ShopPrice += sku.ShopPrice * int64(sku.Count)
order.EarningPrice += sku.EarningPrice * int64(sku.Count)
order.Weight += sku.Weight * sku.Count
}
return order
}
func RefreshOrderEarningPrice2(order *model.GoodsOrder, payPercentage int) *model.GoodsOrder {
if order.EarningType == model.EarningTypePoints {
// if order.VendorID == model.VendorIDJDShop || order.VendorID == model.VendorIDJX {
// order.NewEarningPrice = order.TotalShopMoney * int64((100 - payPercentage)) / 100
// } else {
order.NewEarningPrice = order.TotalShopMoney * int64((100 - payPercentage/2)) / 100
// }
} else {
order.NewEarningPrice = order.EarningPrice
}
return order
}
func RefreshOrderEarningPrice3(order *model.GoodsOrder, payPercentage int, bill *model.Waybill) *model.GoodsOrder {
if order.EarningType == model.EarningTypePoints {
// if order.VendorID == model.VendorIDJDShop || order.VendorID == model.VendorIDJX {
// order.NewEarningPrice = (order.TotalShopMoney - bill.DesiredFee) * int64((100 - payPercentage)) / 100
// } else {
order.NewEarningPrice = order.TotalShopMoney*int64((100-payPercentage/2))/100 - bill.DesiredFee
// }
} else {
order.NewEarningPrice = order.EarningPrice
}
return order
}
func RefreshAfsOrderSkuRelated(afsOrder *model.AfsOrder) *model.AfsOrder {
afsOrder.SkuUserMoney = 0
afsOrder.PmSkuSubsidyMoney = 0
for _, orderSku := range afsOrder.Skus {
if orderSku.SkuID > math.MaxInt32 {
orderSku.SkuID = orderSku.JxSkuID
}
afsOrder.SkuUserMoney += orderSku.UserMoney
afsOrder.PmSkuSubsidyMoney += orderSku.PmSkuSubsidyMoney
}
return afsOrder
}
func RemoveSkuFromOrder(order *model.GoodsOrder, removedSkuList []*model.OrderSku) *model.GoodsOrder {
removedSkuMap := make(map[int]*model.OrderSku)
removedSkuMap2 := make(map[string]*model.OrderSku)
for _, sku := range removedSkuList {
if skuID := GetSkuIDFromOrderSku(sku); skuID > 0 {
if removedSkuMap[skuID] == nil {
removedSkuMap[skuID] = sku
} else {
removedSkuMap[skuID].Count += sku.Count
}
}
if vendorSkuID := sku.VendorSkuID; vendorSkuID != "" {
if removedSkuMap2[vendorSkuID] == nil {
removedSkuMap2[vendorSkuID] = sku
} else {
removedSkuMap2[vendorSkuID].Count += sku.Count
}
}
}
var skuList []*model.OrderSku
sort.Sort(sort.Reverse(OrderSkuList(order.Skus)))
for _, sku := range order.Skus {
var removedSku *model.OrderSku
if skuID := GetSkuIDFromOrderSku(sku); skuID > 0 {
removedSku = removedSkuMap[skuID]
}
if removedSku == nil {
if vendorSkuID := sku.VendorSkuID; vendorSkuID != "" {
removedSku = removedSkuMap2[vendorSkuID]
}
}
copiedSku := *sku
tmp := &copiedSku
if removedSku != nil {
if removedSku.Count >= sku.Count {
tmp = nil
removedSku.Count -= sku.Count
} else {
tmp.Count -= removedSku.Count
removedSku.Count = 0
}
}
if tmp != nil {
skuList = append(skuList, tmp)
}
}
order.Skus = skuList
return RefreshOrderSkuRelated(order)
}
func UploadExportContent(content []byte, key string) (downloadURL string, err error) {
putPolicy := storage.PutPolicy{
Scope: globals.QiniuBucket,
Expires: 10 * 60,
DeleteAfterDays: 1,
}
upToken := putPolicy.UploadToken(api.QiniuAPI)
cfg := &storage.Config{}
formUploader := storage.NewFormUploader(cfg)
ret := storage.PutRet{}
for i := 0; i < 3; i++ {
if err = formUploader.Put(context.Background(), &ret, upToken, key, bytes.NewReader(content), int64(len(content)), &storage.PutExtra{}); err == nil {
break
}
}
if err == nil {
downloadURL = ComposeQiniuResURL(key)
}
return downloadURL, err
}
func UploadExeclAndPushMsg(sheetList []*excel.Obj2ExcelSheetConfig, name string) (downloadURL, fileName string, err error) {
excelBin := excel.Obj2Excel(sheetList)
timeStr := utils.Int64ToStr(time.Now().Unix())
fileName = name + timeStr + fileExt
baseapi.SugarLogger.Debugf("WriteToExcel:save %s success", fileName)
downloadURL, err = UploadExportContent(excelBin, fileName)
return downloadURL, fileName, err
}
func TaskResult2Hint(resultList []interface{}) (hint string) {
strList := make([]string, len(resultList))
for k, v := range resultList {
@@ -474,15 +826,42 @@ func OperationTime2Str2(openTime, closeTime int16) (str string) {
return str
}
func OperationTimeStr4VendorStore(v *model.VendorStoreSnapshot) (str string) {
str = fmt.Sprintf("%s", OperationTime2Str2(v.OpenTime1, v.CloseTime1))
if v.OpenTime2 > 0 && v.CloseTime2 > 0 {
str += fmt.Sprintf(",%s", OperationTime2Str2(v.OpenTime2, v.CloseTime2))
}
return str
}
func OperationTime2HourMinuteFormat(time time.Time) (i int16) {
return int16(time.Hour()*100 + time.Minute())
}
// 得到饿百订单的取货码
func GetEbaiOrderGetCode(order *model.GoodsOrder) (getCode string) {
if order.VendorID == model.VendorIDEBAI && len(order.VendorOrderID2) >= 4 {
getCode = order.VendorOrderID2[len(order.VendorOrderID2)-4:]
}
return getCode
}
func WriteFile(fileName string, binData []byte) error {
err := ioutil.WriteFile(fileName, binData, 0666)
return err
}
func GetRealMobile4Order(order *model.GoodsOrder) (mobileNumber string) {
mobileNumber = order.ConsigneeMobile2
if mobileNumber == "" {
mobileNumber = order.ConsigneeMobile
}
if !IsStringLikeMobile(mobileNumber) {
mobileNumber = ""
}
return mobileNumber
}
func GuessDataResourceVendor(resourceURL string) (vendorID int) {
vendorID = -1
for tmpVendorID, urlList := range resourceTypeMap {
@@ -545,23 +924,30 @@ func GetOneEmailFromStr(str string) (email string) {
// 计算一个坐标点距离一个门店的距离单位为米如果不在有效范围内则返回0
func Point2StoreDistance(lng, lat float64, intStoreLng, intStoreLat int, deliveryRangeType int8, deliveryRange string) (distance int) {
// storeLng := IntCoordinate2Standard(intStoreLng)
// storeLat := IntCoordinate2Standard(intStoreLat)
// if deliveryRangeType == model.DeliveryRangeTypeRadius {
// maxDistance := int(utils.Str2Int64WithDefault(deliveryRange, 0))
// distance = int(EarthDistance(lng, lat, storeLng, storeLat) * 1000)
// if distance > maxDistance {
// distance = 0
// }
// } else {
// points := CoordinateStr2Points(deliveryRange)
// if utils.IsPointInPolygon(lng, lat, points) {
// distance = int(EarthDistance(lng, lat, storeLng, storeLat) * 1000)
// }
// }
storeLng := IntCoordinate2Standard(intStoreLng)
storeLat := IntCoordinate2Standard(intStoreLat)
if deliveryRangeType == model.DeliveryRangeTypeRadius {
maxDistance := int(utils.Str2Int64WithDefault(deliveryRange, 0))
distance = int(EarthDistance(lng, lat, storeLng, storeLat) * 1000)
if distance > maxDistance {
distance = 0
}
} else {
points := CoordinateStr2Points(deliveryRange)
if utils.IsPointInPolygon(lng, lat, points) {
distance = int(EarthDistance(lng, lat, storeLng, storeLat) * 1000)
}
}
return distance
}
func TranslateStorePriceType(storePriceType int8) int8 {
if storePriceType == model.StoreChangePriceTypeManagedStore {
storePriceType = model.StoreChangePriceTypeBossDisabled
}
return storePriceType
}
func TranslateSoundSize(vendorID, soundPercentage int) (soundSize string) {
if vendorID == model.VendorIDYiLianYun || vendorID == model.VendorIDFeiE {
if soundPercentage == 0 {
@@ -576,6 +962,25 @@ func TranslateSoundSize(vendorID, soundPercentage int) (soundSize string) {
if soundPercentage > 66 && soundPercentage <= 100 {
soundSize = "3"
}
} else if vendorID == model.VendorIDJxprint {
if soundPercentage == 0 {
soundSize = "0"
}
if soundPercentage > 0 && soundPercentage <= 20 {
soundSize = "1"
}
if soundPercentage > 20 && soundPercentage <= 40 {
soundSize = "2"
}
if soundPercentage > 40 && soundPercentage <= 60 {
soundSize = "3"
}
if soundPercentage > 60 && soundPercentage <= 80 {
soundSize = "4"
}
if soundPercentage > 80 && soundPercentage <= 100 {
soundSize = "5"
}
}
return soundSize
}
@@ -612,9 +1017,43 @@ func PKCS5UnPadding(origData []byte) []byte {
return origData[:(length - unpadding)]
}
func GetIssue() (issue int) {
year, month, _ := time.Now().Date()
return year*100 + int(month)
//合成水印图
func MixWatermarkImg(imgWatermark, img string, exPrefixBegin, exPrefixEnd *time.Time) (imgMix string) {
if exPrefixBegin != nil && exPrefixEnd != nil {
if utils.Time2Date(time.Now().Add(6*time.Hour)).Sub(*exPrefixBegin) >= 0 && utils.Time2Date(time.Now()).Sub(*exPrefixEnd) <= 0 {
baseURL := base64.URLEncoding.EncodeToString([]byte(imgWatermark))
var imgUrl string
if strings.Contains(img, "?") {
imgUrl = img + "/imageView2/0/q/75|watermark/1/image/" + baseURL + "/dissolve/100/gravity/Center/dx/0/dy/0"
} else {
imgUrl = img + "?imageView2/0/q/75|watermark/1/image/" + baseURL + "/dissolve/100/gravity/Center/dx/0/dy/0"
}
if resBinary, _, err := DownloadFileByURL(imgUrl); err == nil {
if downloadURL, err := UploadExportContent(resBinary, utils.Int64ToStr(time.Now().Unix())+img[strings.LastIndex(img, "/")+1:len(img)]); err == nil {
if err == nil {
return downloadURL
}
}
}
}
if utils.Time2Date(time.Now().Add(6*time.Hour)).Sub(*exPrefixEnd) > 0 {
return imgMix
}
}
return imgMix
}
func GetDefendPriceIssue() (issue int) {
if time.Now().Hour() >= 22 {
issue = utils.Str2Int(time.Now().AddDate(0, 0, 1).Format("20060102"))
} else {
issue = utils.Str2Int(time.Now().Format("20060102"))
}
return issue
}
func GetLastDefendPriceIssue() (issue int) {
return utils.Str2Int(time.Now().AddDate(0, 0, 1).Format("20060102"))
}
//根据一堆坐标求面积
@@ -647,74 +1086,16 @@ func polarTriangleArea(tan1, lng1, tan2, lng2 float64) (s float64) {
return 2 * math.Atan2(t*math.Sin(deltaLng), 1+t*math.Cos(deltaLng))
}
func GenRandomString(l int) string {
str := "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
bytes := []byte(str)
result := []byte{}
r := rand.New(rand.NewSource(time.Now().UnixNano()))
for i := 0; i < l; i++ {
result = append(result, bytes[r.Intn(len(bytes))])
func IntListToStrList(i []int) (s []string) {
for _, v := range i {
s = append(s, utils.Int2Str(v))
}
return string(result)
return s
}
func GetDayTime() (dayTimeBegin, dayTimeEnd time.Time) {
dayTimeBegin = utils.Str2Time(utils.Time2Str(utils.Time2Date(time.Now())) + " 00:00:00")
dayTimeEnd = dayTimeBegin.AddDate(0, 0, 1)
return dayTimeBegin, dayTimeEnd
}
func GetWeekTime() (weekTimeBegin, weekTimeEnd time.Time) {
offset := int(time.Now().Weekday() - 1)
if offset == -1 {
offset = -6
func StrListToIntList(s []string) (i []int) {
for _, v := range s {
i = append(i, utils.Str2Int(v))
}
weekTimeBegin = time.Now().AddDate(0, 0, offset)
weekTimeEnd = weekTimeBegin.AddDate(0, 0, 7)
return weekTimeBegin, weekTimeEnd
}
func BuildErr(errs []error) (err error) {
var errStr = strings.Builder{}
for _, v := range errs {
errStr.WriteString(v.Error())
}
return fmt.Errorf(errStr.String())
}
func SplitFlowAndUnit(flowStr string) (flow float64, unit string) {
for _, v := range flowUnitMap {
if strings.Contains(flowStr, v) {
return utils.Str2Float64WithDefault(flowStr[:len(flowStr)-2], 0), flowStr[len(flowStr)-2:]
}
}
return flow, unit
}
func Flow2KB(flow float64, unit string) (flowKB float64) {
if unit == "KB" {
return flow
} else if unit == "MB" {
return flow * 1024
} else if unit == "GB" {
return flow * 1024 * 1024
}
return flowKB
}
func FlowKB2Other(flowKB float64) (flow float64, unit string) {
if flowKB < 1024 {
return flowKB, "KB"
} else {
flowMB := math.Round(flowKB / float64(1024))
if flowMB < 1024 {
return flowMB, "MB"
} else {
flowGB := math.Round(flowMB / float64(1024))
if flowGB < 1024 {
return flowGB, "GB"
}
}
}
return flow, unit
return i
}

View File

@@ -0,0 +1,41 @@
package jxutils
import (
"git.rosy.net.cn/jx-callback/business/model"
)
type ActStoreSkuMap struct {
actStoreSkuMap map[int64]map[int]*model.ActStoreSku2
}
// isActPrice为true表示是活动false表示是结算
func NewActStoreSkuMap(actStoreSkuList []*model.ActStoreSku2, isActPrice bool) (actMap *ActStoreSkuMap) {
actMap = &ActStoreSkuMap{}
actStoreSkuMap := make(map[int64]map[int]*model.ActStoreSku2)
for _, v := range actStoreSkuList {
index := Combine2Int(v.StoreID, v.SkuID)
if actStoreSkuMap[index] == nil {
actStoreSkuMap[index] = make(map[int]*model.ActStoreSku2)
}
if (isActPrice && v.ActualActPrice > 0 && (actStoreSkuMap[index][v.VendorID] == nil || actStoreSkuMap[index][v.VendorID].ActualActPrice > v.ActualActPrice)) ||
(!isActPrice && v.EarningPrice > 0 && (actStoreSkuMap[index][v.VendorID] == nil || actStoreSkuMap[index][v.VendorID].EarningPrice > v.EarningPrice)) {
actStoreSkuMap[index][v.VendorID] = v
}
}
actMap.actStoreSkuMap = actStoreSkuMap
return actMap
}
func (a *ActStoreSkuMap) GetActStoreSku(storeID, skuID, vendorID int) (storeSku *model.ActStoreSku2) {
index := Combine2Int(storeID, skuID)
if a.actStoreSkuMap[index] != nil {
if vendorID < 0 {
for k := range a.actStoreSkuMap[index] {
vendorID = k
break
}
}
storeSku = a.actStoreSkuMap[index][vendorID]
}
return storeSku
}

View File

@@ -6,6 +6,7 @@ import (
"encoding/base64"
"errors"
"fmt"
beego "github.com/astaxie/beego/server/web"
"image"
"image/png"
"io/ioutil"
@@ -20,6 +21,7 @@ import (
"git.rosy.net.cn/baseapi/platformapi"
"git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/business/model"
"git.rosy.net.cn/jx-callback/globals"
"github.com/boombuler/barcode"
"github.com/boombuler/barcode/code128"
"github.com/boombuler/barcode/qr"
@@ -96,6 +98,20 @@ func SplitStoreName(fullName, separator, defaultPrefix string) (prefix, bareName
return TrimDecorationChar(prefix), TrimDecorationChar(bareName)
}
func ComposeStoreName(bareName string, vendorID int) (fullName string) {
bareName = TrimDecorationChar(strings.Trim(bareName, "-"))
storeName := globals.StoreName
if vendorID == model.VendorIDJD {
fullName = storeName + "-" + bareName
} else {
if globals.IsMainProductEnv() && model.ShopChineseNames[vendorID] != "" {
storeName = model.ShopChineseNames[vendorID]
}
fullName = storeName + "(" + bareName + ")"
}
return fullName
}
func StrTime2JxOperationTime(strTime string, defValue int16) int16 {
if timeValue, err := time.Parse("15:04:05", strTime); err == nil {
return int16(timeValue.Hour()*100 + timeValue.Minute())
@@ -201,6 +217,42 @@ func RegularizeSkuQuality(specQuality float32, specUnit string) (g int) {
return int(specQuality)
}
// 计算SKU价格unitPrice为一斤的单价specQuality为质量单位为克
func CaculateSkuPrice(unitPrice int, specQuality float32, specUnit string, skuNameUnit string) int {
if skuNameUnit != model.SpecialUnit {
return unitPrice
}
specQuality2 := RegularizeSkuQuality(specQuality, specUnit)
floatPrice := float64(unitPrice) * float64(specQuality2) / float64(model.SpecialSpecQuality)
// if specQuality2 < 250 {
// floatPrice = floatPrice * 110 / 100
// } else if specQuality2 < 500 {
// floatPrice = floatPrice * 105 / 100
// }
if floatPrice <= 1 {
floatPrice = 1
}
return int(math.Round(floatPrice))
}
// 计算SKU标准价格CaculateSkuPrice的逆过程
func CaculateUnitPrice(skuPrice int, specQuality float32, specUnit string, skuNameUnit string) (unitPrice int) {
if skuNameUnit != model.SpecialUnit {
return skuPrice
}
specQuality2 := RegularizeSkuQuality(specQuality, specUnit)
unitPrice = skuPrice * model.SpecialSpecQuality / specQuality2
// if specQuality2 < 250 {
// unitPrice = unitPrice * 100 / 110
// } else if specQuality2 < 500 {
// unitPrice = unitPrice * 100 / 105
// }
if unitPrice <= 1 {
unitPrice = 1
}
return unitPrice
}
func ConstrainPricePercentage(percentage int) int {
if percentage < model.MinVendorPricePercentage || percentage > model.MaxVendorPricePercentage {
percentage = model.DefVendorPricePercentage
@@ -236,6 +288,65 @@ func CaculateSkuPriceFromVendor(vendorPrice, percentage, priceAdd int) (price in
return price
}
func GetPricePercentage(l model.PricePercentagePack, price int, defPricePercentage int) (pricePercentage, priceAdd int) {
pricePercentage = defPricePercentage
itemLen := len(l)
if itemLen > 0 {
low := 0
high := itemLen - 1
mid := 0
for low <= high {
mid = low + (high-low)/2
if mid < 0 || mid >= itemLen-1 {
break
}
if price >= l[mid].BeginPrice {
if price < l[mid+1].BeginPrice {
break
} else {
low = mid + 1
}
} else {
high = mid - 1
}
}
if mid >= 0 && mid <= itemLen-1 && low <= high {
pricePercentage = l[mid].PricePercentage
priceAdd = l[mid].PriceAdd
}
}
return pricePercentage, priceAdd
}
func GetPricePercentageByVendorPrice(l model.PricePercentagePack, vendorPrice int, defPricePercentage int) (pricePercentage, priceAdd int) {
pricePercentage = defPricePercentage
if len(l) > 0 {
var lastItem *model.PricePercentageItem
for _, v := range l {
if CaculateSkuVendorPrice(v.BeginPrice, v.PricePercentage, v.PriceAdd) > vendorPrice {
break
}
lastItem = v
}
if lastItem != nil {
pricePercentage = lastItem.PricePercentage
priceAdd = lastItem.PriceAdd
}
}
return pricePercentage, priceAdd
}
func CaculatePriceByPricePack(l model.PricePercentagePack, defPricePercentage, price int) (outPrice int) {
pricePercentage, priceAdd := GetPricePercentage(l, price, defPricePercentage)
return CaculateSkuVendorPrice(price, pricePercentage, priceAdd)
}
func CaculateJxPriceByPricePack(l model.PricePercentagePack, defPricePercentage, vendorPrice int) (jxPrice int) {
pricePercentage, priceAdd := GetPricePercentageByVendorPrice(l, vendorPrice, defPricePercentage)
jxPrice = CaculateSkuPriceFromVendor(vendorPrice, pricePercentage, priceAdd)
return jxPrice
}
func ConstrainPayPercentage(payPerCentage int) int {
if payPerCentage <= 50 {
payPerCentage = 70
@@ -243,6 +354,10 @@ func ConstrainPayPercentage(payPerCentage int) int {
return payPerCentage
}
func IsSkuSpecial(specQuality float32, specUnit string) bool {
return int(specQuality) == model.SpecialSpecQuality && (specUnit == model.SpecialSpecUnit || specUnit == model.SpecialSpecUnit2)
}
var lastFakeID int64
var lastFakeIDMutex sync.RWMutex
@@ -326,6 +441,28 @@ func FormatSkuWeight(specQuality float32, specUnit string) int {
return RegularizeSkuQuality(specQuality, specUnit)
}
type SkuList []*model.Sku
func (s SkuList) Len() int {
return len(s)
}
func (s SkuList) Less(i, j int) bool {
if s[i].NameID == s[j].NameID {
if s[i].SpecUnit == s[j].SpecUnit {
return s[i].SpecQuality < s[j].SpecQuality
}
return s[i].SpecUnit < s[j].SpecUnit
}
return s[i].NameID < s[j].NameID
}
func (s SkuList) Swap(i, j int) {
tmp := s[i]
s[i] = s[j]
s[j] = tmp
}
func DownloadFileByURL(fileURL string) (bodyData []byte, fileMD5 string, err error) {
response, err := http.Get(fileURL)
if err == nil {
@@ -367,9 +504,19 @@ func GetVendorName(vendorID int) (vendorName string) {
}
func CaculateSkuEarningPrice(shopPrice, salePrice int64, storePayPercentage int) (earningPrice int64) {
earningPrice = salePrice
if salePrice != 0 {
if shopPrice > 0 && shopPrice < earningPrice {
//TODO 2021-07-05 16:50菜市和果园一样取低的
//TODO 2021-07-05 18:48 还是改回来
if beego.BConfig.RunMode == "jxgy" {
earningPrice = salePrice
if salePrice != 0 {
if shopPrice > 0 && shopPrice < earningPrice {
earningPrice = shopPrice
}
}
} else {
if shopPrice == 0 {
earningPrice = salePrice * 70 / 100
} else {
earningPrice = shopPrice
}
}

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
}

View File

@@ -8,6 +8,8 @@ import (
"git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/baseapi/utils/errlist"
"git.rosy.net.cn/jx-callback/business/jxutils"
"git.rosy.net.cn/jx-callback/business/model"
"git.rosy.net.cn/jx-callback/business/model/dao"
"git.rosy.net.cn/jx-callback/globals"
"git.rosy.net.cn/jx-callback/globals/api"
)
@@ -21,7 +23,7 @@ var (
}
)
func SendSMSMsg(mobileList []string, signName, templateCode string, templateParam map[string]interface{}) (err error) {
func SendSMSMsg(mobileList []string, signName, templateCode string, templateParam map[string]interface{}, order *model.GoodsOrder) (err error) {
if len(mobileList) > 0 {
errList := errlist.New()
mobileList = jxutils.StringMap2List(jxutils.StringList2Map(mobileList))
@@ -41,6 +43,11 @@ func SendSMSMsg(mobileList []string, signName, templateCode string, templatePara
globals.SugarLogger.Infof(errMsg)
}
}
// else {
// if order != nil {
// err = updateStoreSMSNotifyMark(order)
// }
// }
}
}
}
@@ -48,3 +55,174 @@ func SendSMSMsg(mobileList []string, signName, templateCode string, templatePara
}
return err
}
func getOrderNotifyPhone(order *model.GoodsOrder) (phoneList []string) {
return dao.GetOrderNotifyPhones(dao.GetDB(), jxutils.GetSaleStoreIDFromOrder(order))
}
func NotifyNewOrder(order *model.GoodsOrder) (err error) {
if isPushSMS(order) {
temp := ""
var price int64
store, _ := dao.GetStoreDetail(dao.GetDB(), jxutils.GetSaleStoreIDFromOrder(order), order.VendorID, order.VendorOrgCode)
if store.VendorPayPercentage < 50 && store.VendorPayPercentage != 0 {
temp = globals.SMSNewOrderTemplate
price = order.ActualPayPrice
} else if store.VendorPayPercentage > 50 {
temp = globals.SMSNewOrderTemplateQ
price = order.ShopPrice
} else if store.VendorPayPercentage == 0 {
if store.PayPercentage > 50 {
temp = globals.SMSNewOrderTemplateQ
price = order.ShopPrice
} else {
temp = globals.SMSNewOrderTemplate
price = order.ActualPayPrice
}
}
err = SendSMSMsg(getOrderNotifyPhone(order), globals.SMSSignName, temp, map[string]interface{}{
"daySeq": order.OrderSeq,
"consigneeName": order.ConsigneeName,
"payMoney": jxutils.IntPrice2StandardString(price),
}, order)
}
return err
}
func NotifyOrderCanceled(order *model.GoodsOrder) (err error) {
err = SendSMSMsg(getOrderNotifyPhone(order), globals.SMSSignName, globals.SMSOrderCanceledTemplate, map[string]interface{}{
"vendorName": model.VendorChineseNames[order.VendorID],
"seq": order.OrderSeq,
"orderID": order.VendorOrderID,
}, order)
return err
}
func isPushSMS(order *model.GoodsOrder) bool {
storeID := 0
if order.StoreID == 0 {
storeID = order.JxStoreID
} else {
storeID = order.StoreID
}
stores, _ := dao.GetStoresMapList(dao.GetDB(), []int{order.VendorID}, []int{storeID}, nil, model.StoreStatusAll, model.StoreIsSyncAll, "", "", "")
if len(stores) > 0 {
if stores[0].IsOrder == model.NO {
if storeID == model.MatterStoreID || storeID == model.JdShopMainStoreID {
return false
} else {
return true
}
} else {
return false
}
} else {
return false
}
}
func updateStoreSMSNotifyMark(order *model.GoodsOrder) (err error) {
var db = dao.GetDB()
stores, _ := dao.GetStoreList(db, []int{order.StoreID}, nil, nil, nil, nil, "")
if len(stores) > 0 {
stores[0].SMSNotifyMark = model.YES
_, err = dao.UpdateEntity(db, stores[0], "SMSNotifyMark")
}
return err
}
//每月向用户发送
func NotifyNewUserOrder(order *model.GoodsOrder) (err error) {
var (
db = dao.GetDB()
storeTel string
storeID int
mobile string
)
if order.StoreID == 0 {
storeID = order.JxStoreID
} else {
storeID = order.StoreID
}
if order.ConsigneeMobile2 != "" {
mobile = order.ConsigneeMobile2
uoSMS, err := dao.GetUserOrderSMS(db, mobile, "")
stores, _ := dao.GetStoreList(db, []int{storeID}, nil, nil, nil, nil, "")
if len(stores) > 0 {
if stores[0].Tel1 == "" {
storeTel = stores[0].Tel2
} else {
storeTel = stores[0].Tel1
}
}
if uoSMS == nil {
uoSMSc := &model.UserOrderSms{
Mobile: mobile,
Name: order.ConsigneeName,
VendorUserID: order.VendorUserID,
TotalCount: 0,
SMSMark: model.NO,
}
err = dao.CreateEntity(db, uoSMSc)
err = SendSMSMsg([]string{uoSMSc.Mobile}, globals.SMSSignName, globals.SMSNewUserOrderTemplate, map[string]interface{}{
"tel": storeTel,
}, nil)
if err == nil {
uoSMS2, _ := dao.GetUserOrderSMS(db, mobile, "")
uoSMS2.SMSMark = model.YES
uoSMS2.TotalCount++
_, err = dao.UpdateEntity(db, uoSMS2, "SMSMark", "TotalCount")
}
} else {
if uoSMS.SMSMark != model.YES {
err = SendSMSMsg([]string{uoSMS.Mobile}, globals.SMSSignName, globals.SMSNewUserOrderTemplate, map[string]interface{}{
"tel": storeTel,
}, nil)
if err == nil {
uoSMS.SMSMark = model.YES
uoSMS.TotalCount++
_, err = dao.UpdateEntity(db, uoSMS, "SMSMark", "TotalCount")
}
}
}
}
return err
}
//给配送员发短信
func NotifyNewCourierOrder(bill *model.Waybill) (err error) {
var (
db = dao.GetDB()
storeTel string
storeID int
)
order := &model.GoodsOrder{}
order.VendorOrderID = bill.VendorOrderID
err = dao.GetEntity(db, order, "VendorOrderID")
if order.StoreID == 0 {
storeID = order.JxStoreID
} else {
storeID = order.StoreID
}
stores, _ := dao.GetStoreList(db, []int{storeID}, nil, nil, nil, nil, "")
if len(stores) > 0 {
if stores[0].Tel1 == "" {
storeTel = stores[0].Tel2
} else {
storeTel = stores[0].Tel1
}
}
err = SendSMSMsg([]string{bill.CourierMobile}, globals.SMSSignName, globals.SMSNewOrderTemplate, map[string]interface{}{
"tel": storeTel,
}, nil)
return err
}
//京西订单配送员取货后,给用户发短信提醒
func NotifyJxOrder(order *model.GoodsOrder, bill *model.Waybill) (err error) {
err = SendSMSMsg([]string{order.ConsigneeMobile}, globals.SMSSignName, globals.SMSJxOrderDelivering, map[string]interface{}{
"phone": bill.CourierMobile,
}, order)
return err
}

View File

@@ -0,0 +1,39 @@
package storeskulock
import (
"fmt"
"time"
"git.rosy.net.cn/jx-callback/globals"
"git.rosy.net.cn/jx-callback/globals/api"
)
const (
cacheKeyPrefix = "jdpromotion"
)
func LockJdStoreSku(jdStoreID string, jdSkuID int64, expire time.Time) {
return
globals.SugarLogger.Debug(expire, " ", time.Now())
duration := expire.Sub(time.Now())
if duration > 0 {
api.Cacher.Set(genCacheKey(jdStoreID, jdSkuID), 1, duration)
}
}
func UnlockJdStoreSku(jdStoreID string, jdSkuID int64) {
api.Cacher.Del(genCacheKey(jdStoreID, jdSkuID))
}
func IsJdStoreSkuLocked(jdStoreID string, jdSkuID int64) bool {
return false
return api.Cacher.Get(genCacheKey(jdStoreID, jdSkuID)) != nil
}
func ClearJdStoreSkuLock() {
api.Cacher.FlushKeys(cacheKeyPrefix)
}
func genCacheKey(jdStoreID string, jdSkuID int64) string {
return fmt.Sprintf("%s.%s.%d", cacheKeyPrefix, jdStoreID, jdSkuID)
}

View File

@@ -8,18 +8,19 @@ import (
"git.rosy.net.cn/baseapi/platformapi"
"git.rosy.net.cn/jx-callback/business/dao"
"git.rosy.net.cn/jx-callback/business/jxutils/eventhub/syseventhub"
"git.rosy.net.cn/jx-callback/business/model"
"git.rosy.net.cn/jx-callback/business/model/dao"
"git.rosy.net.cn/jx-callback/business/model/legacymodel"
"git.rosy.net.cn/jx-callback/globals/api"
"git.rosy.net.cn/baseapi/platformapi/weimobapi"
"git.rosy.net.cn/baseapi/platformapi/yilianyunapi"
"git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/globals"
beego "github.com/astaxie/beego/adapter"
"github.com/astaxie/beego/client/orm"
beego "github.com/astaxie/beego/server/web"
)
const (
@@ -28,6 +29,7 @@ const (
weimobTokenExpires = 7200 * time.Second
yilianyunTokenExpires = 30 * 24 * 3600 * time.Second
pushTokenExpires = 7200 * time.Second
fnTokenExpires = 1 * time.Hour
maxRefreshGap = 5 * 60 * time.Second
errRefreshGap = 10 * time.Second
@@ -138,6 +140,92 @@ func RefreshWeixinToken() (err error) {
return err
}
func RefreshFnToken() (err error) {
if api.FnAPI != nil {
err = RefreshConfig("fn", fnTokenExpires, func() (token string, expireTimeStr string) {
globals.SugarLogger.Debugf("RefreshFnToken RunMode:%s", beego.BConfig.RunMode)
if globals.IsMainProductEnv() {
if tokenInfo, err := api.FnAPI.GetAccessToken(); err == nil {
globals.SugarLogger.Debugf("RefreshFnToken tokenInfo:%s", utils.Format4Output(tokenInfo, true))
token = tokenInfo.AccessToken
} else {
globals.SugarLogger.Errorf("RefreshFnToken RefreshToken failed with error:%v", err)
}
sql := `
UPDATE jxgy.config SET token = ? WHERE thirdparty = ?
`
sqlParams := []interface{}{
token, "fn",
}
dao.ExecuteSQL(dao.GetDB(), sql, sqlParams)
}
if beego.BConfig.RunMode == "jxgy" {
config := &legacymodel.Config{}
sql := `
SELECT * FROM config WHERE thirdparty = 'fn'
`
if err := dao.GetRow(dao.GetDB(), &config, sql, nil); err == nil {
token = config.Token
}
}
return token, expireTimeStr
}, func(value string) {
globals.SugarLogger.Debugf("RefreshFnToken setter value:%s", value)
// syseventhub.SysEventHub.OnNewWXToken(value)
api.FnAPI.SetToken(value)
})
}
return err
}
func RefreshQywxToken() (err error) {
if api.QywxAPI != nil {
err = RefreshConfig("qywx", weixinTokenExpires, func() (token string, expireTimeStr string) {
globals.SugarLogger.Debugf("RefreshQywxToken RunMode:%s", beego.BConfig.RunMode)
if globals.IsMainProductEnv() {
if tokenInfo, err := api.QywxAPI.GetToken(); err == nil {
globals.SugarLogger.Debugf("RefreshQywxToken tokenInfo:%s", utils.Format4Output(tokenInfo, true))
token = tokenInfo
} else {
globals.SugarLogger.Errorf("RefreshQywxToken RefreshToken failed with error:%v", err)
}
}
return token, expireTimeStr
}, func(value string) {
globals.SugarLogger.Debugf("RefreshQywxToken setter value:%s", value)
api.QywxAPI.SetToken(value)
})
}
return err
}
func RefreshWeixin2Token() (err error) {
if api.WeixinMiniAPI2 != nil {
err = RefreshConfig("wechat2", weixinTokenExpires, func() (token string, expireTimeStr string) {
globals.SugarLogger.Debugf("RefreshWeixin2Token RunMode:%s", beego.BConfig.RunMode)
if globals.IsMainProductEnv() {
if tokenInfo, err := api.WeixinMiniAPI2.CBRetrieveToken(); err == nil {
globals.SugarLogger.Debugf("RefreshWeixin2Token tokenInfo:%s", utils.Format4Output(tokenInfo, true))
token = tokenInfo.AccessToken
} else {
globals.SugarLogger.Errorf("RefreshWeixin2Token RefreshToken failed with error:%v", err)
}
} else {
if tokenInfo := getWX2TokenFromRemote(api.WeixinMiniAPI2.CBGetToken()); tokenInfo != nil {
expireTimeStr = utils.Time2Str(time.Now().Add(-weixinTokenExpires))
token = tokenInfo.Token
}
}
return token, expireTimeStr
}, func(value string) {
globals.SugarLogger.Debugf("RefreshWeixinToken setter value:%s", value)
syseventhub.SysEventHub.OnNewWX2Token(value)
api.WeixinMiniAPI2.CBSetToken(value)
})
}
return err
}
func RefreshPushToken() (err error) {
if api.PushAPI != nil {
err = RefreshConfig("push", pushTokenExpires, func() (token string, expireTimeStr string) {
@@ -254,6 +342,33 @@ func SaveWeimobToken(token *weimobapi.TokenInfo) (err error) {
return dao.CreateOrUpdate(db, config)
}
func RefreshYilianyunToken() (err error) {
return RefreshConfig("yilianyun", yilianyunTokenExpires, func() (token string, expireTimeStr string) {
globals.SugarLogger.Debugf("RefreshYilianyunToken RunMode:%s", beego.BConfig.RunMode)
if globals.IsMainProductEnv() { // 只有京西菜市刷新易联云key
if tokenInfo, err := api.YilianyunAPI.RetrieveToken(); err == nil {
token = string(utils.MustMarshal(tokenInfo))
} else {
globals.SugarLogger.Errorf("RefreshYilianyunToken RefreshToken failed with error:%v", err)
}
} else {
if tokenInfo := getYLYTokenFromRemote(api.YilianyunAPI.GetToken()); tokenInfo != nil {
expireTimeStr = utils.Time2Str(time.Now().Add(-yilianyunTokenExpires))
token = tokenInfo.Token
}
}
return token, expireTimeStr
}, func(value string) {
token := value
var tokenInfo *yilianyunapi.TokenInfo
if err := utils.TryUnmarshalUseNumber([]byte(value), &tokenInfo); err == nil {
token = tokenInfo.AccessToken
}
syseventhub.SysEventHub.OnNewYLYToken(token)
api.YilianyunAPI.SetToken(token)
})
}
func PollingRemotEvent(remoteURL string, waitSecond int, params map[string]interface{}) (tokenInfo *syseventhub.TokenInfo) {
if waitSecond == 0 {
waitSecond = 5 * 60

View File

@@ -7,7 +7,9 @@ import (
"sync"
"time"
"git.rosy.net.cn/baseapi/platformapi/dingdingapi"
"git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/business/jxutils/ddmsg"
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
"git.rosy.net.cn/jx-callback/globals"
)
@@ -484,20 +486,20 @@ func SendMessage(t *BaseTask) {
time.Sleep(10 * time.Millisecond) // 等待GetResult中的isGetResultCalled赋值
globals.SugarLogger.Debugf("BaseTask task ID:%s, name:%s finished, isGetResultCalled:%t", t.ID, t.Name, t.isGetResultCalled)
if !t.isGetResultCalled && t.parent == nil && len(GetTasks(t.ID, TaskStatusBegin, TaskStatusEnd, 24, "")) > 0 {
//if authInfo, err := t.ctx.GetV2AuthInfo(); err == nil { // 这里应该是不管登录类型,直接以可能的方式发消息
// var content string
// taskDesc := fmt.Sprintf("你的异步任务[%s],ID[%s],开始于:%s,结束于:%s,", t.Name, t.ID, utils.Time2Str(t.CreatedAt), utils.Time2Str(t.TerminatedAt))
// content = fmt.Sprintf("%s执行%s", taskDesc, TaskStatusName[t.Status])
// if t.Error() == "" {
// noticeMsg := t.GetNoticeMsg()
// if noticeMsg != "" {
// content += ",通知消息:" + noticeMsg
// }
// } else {
// content += ",\n" + t.Error()
// }
// ddmsg.SendUserMessage(dingdingapi.MsgTyeText, authInfo.UserID, "异步任务完成", content)
//}
if authInfo, err := t.ctx.GetV2AuthInfo(); err == nil { // 这里应该是不管登录类型,直接以可能的方式发消息
var content string
taskDesc := fmt.Sprintf("你的异步任务[%s],ID[%s],开始于:%s,结束于:%s,", t.Name, t.ID, utils.Time2Str(t.CreatedAt), utils.Time2Str(t.TerminatedAt))
content = fmt.Sprintf("%s执行%s", taskDesc, TaskStatusName[t.Status])
if t.Error() == "" {
noticeMsg := t.GetNoticeMsg()
if noticeMsg != "" {
content += ",通知消息:" + noticeMsg
}
} else {
content += ",\n" + t.Error()
}
ddmsg.SendUserMessage(dingdingapi.MsgTyeText, authInfo.UserID, "异步任务完成", content)
}
}
}

View File

@@ -0,0 +1,101 @@
package push
import (
"fmt"
"strings"
"git.rosy.net.cn/baseapi/platformapi/unipushapi"
"git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/business/jxutils"
"git.rosy.net.cn/jx-callback/business/model"
"git.rosy.net.cn/jx-callback/business/model/dao"
"git.rosy.net.cn/jx-callback/globals"
"git.rosy.net.cn/jx-callback/globals/api"
beego "github.com/astaxie/beego/server/web"
)
func pushToSingle(content, title string, storeID int) {
var (
db = dao.GetDB()
)
if !globals.IsProductEnv() {
return
}
if storeID == 0 {
return
}
storePushs, err := dao.GetStorePushClient(db, storeID, "")
if err != nil {
return
}
for _, v := range storePushs {
status, err2 := api.PushAPI.PushToSingle(v.ClientID, false, &unipushapi.Notification{
Title: title,
Body: content,
})
if err = err2; err != nil {
globals.SugarLogger.Debugf("NotifyNewOrder push error: [%v]", err)
continue
}
if status == unipushapi.SuccessOffLine {
_, err = api.PushAPI.PushToSingle(v.ClientID, true, &unipushapi.Notification{
Body: content,
})
if err != nil {
globals.SugarLogger.Debugf("NotifyNewOrder push2 error: [%v]", err)
continue
}
}
}
}
func NotifyNewOrder(order *model.GoodsOrder) {
globals.SugarLogger.Debugf("NotifyNewOrder push begin orderID :[%v]", order.VendorOrderID)
if order == nil || len(order.Skus) == 0 {
return
}
sb := new(strings.Builder)
sb.WriteString("老板,")
sb.WriteString(order.ConsigneeName)
sb.WriteString("购买了商品")
sb.WriteString(getOrderDetailBrief(order))
pushToSingle(sb.String(), "京西菜市新订单推送", jxutils.GetSaleStoreIDFromOrder(order))
}
func getOrderDetailBrief(order *model.GoodsOrder) (brief string) {
sb := new(strings.Builder)
sb.WriteString(order.Skus[0].SkuName)
sb.WriteString("等共")
sb.WriteString(utils.Int2Str(order.Skus[0].Count))
sb.WriteString("份(")
sb.WriteString(jxutils.IntPrice2StandardString(order.Skus[0].SalePrice))
sb.WriteString("元/份)等,预计收入")
//TODO 2020-07-20 果园和菜市不同
var price int64
if beego.BConfig.RunMode == "jxgy" {
price = order.EarningPrice
} else {
if order.EarningType == model.EarningTypePoints {
price = order.ActualPayPrice
} else {
price = order.EarningPrice
}
}
sb.WriteString(jxutils.IntPrice2StandardString(price))
sb.WriteString("元")
return sb.String()
}
func NotifyAfsOrder(afsOrder *model.AfsOrder) (err error) {
globals.SugarLogger.Debugf("NotifyAfsOrder push begin orderID :[%v]", afsOrder.VendorOrderID)
pushToSingle("老板,您有新的售后单,请尽快处理!", "京西菜市售后单推送", jxutils.GetSaleStoreIDFromAfsOrder(afsOrder))
return err
}
func NotifyOrderCanceled(order *model.GoodsOrder) (err error) {
title := fmt.Sprintf("老板,您的订单%s第%d号订单, %s被取消了", model.VendorChineseNames[order.VendorID], order.OrderSeq, order.VendorOrderID)
pushToSingle(title, "京西菜市取消单推送", jxutils.GetSaleStoreIDFromOrder(order))
return err
}

Some files were not shown because too many files have changed in this diff Show More