qrpa 1.0.26__py3-none-any.whl → 1.0.27__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of qrpa might be problematic. Click here for more details.

qrpa/shein_lib.py CHANGED
@@ -1,2192 +1,2228 @@
1
- from .fun_file import read_dict_from_file, write_dict_to_file, read_dict_from_file_ex, write_dict_to_file_ex
2
- from .fun_base import log, send_exception, md5_string, get_safe_value
3
- from .fun_web import fetch
4
- from .time_utils import TimeUtils
5
-
6
- from .shein_sqlite import insert_sales, get_last_week_sales, get_near_week_sales, get_near_month_sales, get_last_month_sales
7
-
8
- import math
9
- import time
10
- import json
11
- from datetime import datetime
12
- from playwright.sync_api import Page
13
-
14
- from .shein_daily_report_model import SheinStoreSalesDetailManager
15
-
16
- class SheinLib:
17
-
18
- def __init__(self, config, bridge, web_page: Page, store_username, store_name):
19
- self.config = config
20
- self.bridge = bridge
21
- self.store_username = store_username
22
- self.store_name = store_name
23
- self.web_page = web_page
24
- self.dt = None
25
- self.DictQueryTime = {}
26
-
27
- self.deal_auth()
28
-
29
- # 处理鉴权
30
- def deal_auth(self):
31
- web_page = self.web_page
32
- while not web_page.locator('//div[contains(text(),"商家后台")]').nth(1).is_visible():
33
- if web_page.locator('xpath=//div[@id="container" and @alita-name="gmpsso"]//button[@type="button" and @id]').nth(0).is_visible():
34
- web_page.locator('xpath=//div[@id="container" and @alita-name="gmpsso"]//button[@type="button" and @id]').nth(0).click()
35
- log("鉴权确定按钮可见 点击'确定'按钮")
36
- web_page.wait_for_load_state("load")
37
- web_page.wait_for_timeout(1000)
38
- # time.sleep(1)
39
- if web_page.locator('//input[@name="username"]').is_visible():
40
- log("用户名输入框可见 等待5秒点击'登录'按钮")
41
- web_page.wait_for_timeout(5000)
42
- log('点击"登录"')
43
- web_page.locator('//button[contains(@class,"login_btn")]').click()
44
- web_page.wait_for_load_state("load")
45
- log('再延时5秒')
46
- web_page.wait_for_timeout(5000)
47
- if web_page.locator('//span[contains(text(),"商品管理")]').nth(1).is_visible():
48
- log('商品管理菜单可见 退出鉴权处理')
49
- return
50
- log('商家后台不可见', web_page.title(), web_page.url)
51
- web_page.wait_for_load_state("load")
52
- # time.sleep(1)
53
- web_page.wait_for_timeout(1000)
54
- if 'SHEIN全球商家中心' in web_page.title() and 'https://sso.geiwohuo.com/#/home' in web_page.url:
55
- log('SHEIN全球商家中心 中断循环')
56
- break
57
- if '后台首页' in web_page.title() and 'https://sso.geiwohuo.com/#/home' in web_page.url:
58
- log('后台首页 中断循环')
59
- break
60
- if '商家后台' in web_page.title() and 'https://sso.geiwohuo.com/#/home' in web_page.url:
61
- log('后台首页 中断循环')
62
- break
63
- if 'mrs.biz.sheincorp.cn' in web_page.url and '商家后台' in web_page.title():
64
- web_page.goto('https://sso.geiwohuo.com/#/home')
65
- web_page.wait_for_load_state("load")
66
- web_page.wait_for_timeout(3000)
67
- # time.sleep(3)
68
- if web_page.locator('//h1[contains(text(),"鉴权")]').is_visible():
69
- log('检测到鉴权 刷新页面')
70
- web_page.reload()
71
- web_page.wait_for_load_state('load')
72
- # time.sleep(3)
73
- web_page.wait_for_timeout(3000)
74
- web_page.reload()
75
- # time.sleep(3)
76
- web_page.wait_for_timeout(3000)
77
- if web_page.title() == 'SHEIN':
78
- web_page.goto('https://sso.geiwohuo.com/#/home')
79
- web_page.wait_for_load_state("load")
80
- # time.sleep(3)
81
- web_page.wait_for_timeout(3000)
82
-
83
- # web_page.goto('https://sso.geiwohuo.com')
84
- log('鉴权处理结束')
85
-
86
- # 获取希音退供明细 台账明细一个接口
87
- def get_back_list(self, source='mb'):
88
- page_num = 1
89
- page_size = 200 # 列表最多返回200条数据 大了没有用
90
-
91
- first_day, last_day = TimeUtils.get_last_month_range()
92
-
93
- cache_file = f'{self.config.auto_dir}/shein/cache/return_detail_{self.store_username}_{first_day}_{last_day}.json'
94
- list_item = read_dict_from_file(cache_file, 3600 * 24 * 20)
95
- if len(list_item) > 0:
96
- return list_item
97
-
98
- url = f"https://sso.geiwohuo.com/mils/changeDetail/page"
99
- payload = {
100
- "displayChangeTypeList": ["10"],
101
- "addTimeStart" : f"{first_day} 00:00:00",
102
- "addTimeEnd" : f"{last_day} 23:59:59",
103
- "pageNumber" : page_num,
104
- "pageSize" : page_size,
105
- "changeTypeIndex" : "2"
106
- }
107
- response_text = fetch(self.web_page, url, payload)
108
- error_code = response_text.get('code')
109
- if str(error_code) != '0':
110
- raise send_exception(json.dumps(response_text, ensure_ascii=False))
111
- list_item = response_text['info']['data']['list']
112
- total = response_text['info']['data']['count']
113
- totalPage = math.ceil(total / page_size)
114
-
115
- for page in range(2, totalPage + 1):
116
- log(f'获取台账明细列表 第{page}/{totalPage}页')
117
- payload['pageNumber'] = page
118
- response_text = fetch(self.web_page, url, payload)
119
- spu_list_new = response_text['info']['data']['list']
120
- list_item += spu_list_new
121
- time.sleep(0.1)
122
-
123
- # cost_price =
124
- for item in list_item:
125
- supplierSku = item['supplierSku']
126
- item['cost_price'] = self.bridge.get_sku_cost(supplierSku, source)
127
- item['sku_img'] = self.bridge.get_sku_img(supplierSku, source)
128
-
129
- write_dict_to_file(cache_file, list_item)
130
-
131
- return list_item
132
-
133
- # 不结算列表
134
- def get_no_settlement_list(self, source='mb'):
135
- page_num = 1
136
- page_size = 200 # 列表最多返回200条数据 大了没有用
137
-
138
- first_day, last_day = TimeUtils.get_last_month_range()
139
-
140
- cache_file = f'{self.config.auto_dir}/shein/cache/no_settlement_{self.store_username}_{first_day}_{last_day}.json'
141
- list_item = read_dict_from_file(cache_file, 3600 * 24 * 20)
142
- if len(list_item) > 0:
143
- return list_item
144
-
145
- url = f"https://sso.geiwohuo.com/mils/changeDetail/page"
146
- payload = {
147
- "addTimeStart" : f"{first_day} 00:00:00",
148
- "addTimeEnd" : f"{last_day} 23:59:59",
149
- "pageNumber" : page_num,
150
- "pageSize" : page_size,
151
- "changeTypeIndex" : "2",
152
- "settleTypeList" : ["1"], # 不结算
153
- "displayChangeTypeList": ["6", "7", "9", "10", "11", "12", "13", "16", "18", "19"] # 出库
154
- }
155
- response_text = fetch(self.web_page, url, payload)
156
- error_code = response_text.get('code')
157
- if str(error_code) != '0':
158
- raise send_exception(json.dumps(response_text, ensure_ascii=False))
159
- list_item = response_text['info']['data']['list']
160
- total = response_text['info']['data']['count']
161
- totalPage = math.ceil(total / page_size)
162
-
163
- for page in range(2, totalPage + 1):
164
- log(f'获取台账明细列表 第{page}/{totalPage}页')
165
- payload['pageNumber'] = page
166
- response_text = fetch(self.web_page, url, payload)
167
- spu_list_new = response_text['info']['data']['list']
168
- list_item += spu_list_new
169
- time.sleep(0.1)
170
-
171
- # cost_price =
172
- for item in list_item:
173
- supplierSku = item['supplierSku']
174
- item['cost_price'] = self.bridge.get_sku_cost(supplierSku, source)
175
- item['sku_img'] = self.bridge.get_sku_img(supplierSku, source)
176
-
177
- write_dict_to_file(cache_file, list_item)
178
-
179
- return list_item
180
-
181
- def get_ledger_list(self, source='mb'):
182
- page_num = 1
183
- page_size = 200 # 列表最多返回200条数据 大了没有用
184
-
185
- first_day, last_day = TimeUtils.get_last_month_range()
186
-
187
- cache_file = f'{self.config.auto_dir}/shein/cache/sales_detail_{self.store_username}_{first_day}_{last_day}.json'
188
- list_item = read_dict_from_file(cache_file, 3600 * 24 * 20)
189
- if len(list_item) > 0:
190
- return list_item
191
-
192
- url = f"https://sso.geiwohuo.com/mils/changeDetail/page"
193
- payload = {
194
- "displayChangeTypeList": ["6", "7", "9", "10", "11", "12", "13", "16", "18", "19"], # 出库
195
- "addTimeStart" : f"{first_day} 00:00:00",
196
- "addTimeEnd" : f"{last_day} 23:59:59",
197
- "pageNumber" : page_num,
198
- "pageSize" : page_size,
199
- "changeTypeIndex" : "2"
200
- }
201
- response_text = fetch(self.web_page, url, payload)
202
- error_code = response_text.get('code')
203
- if str(error_code) != '0':
204
- raise send_exception(json.dumps(response_text, ensure_ascii=False))
205
- list_item = response_text['info']['data']['list']
206
- total = response_text['info']['data']['count']
207
- totalPage = math.ceil(total / page_size)
208
-
209
- for page in range(2, totalPage + 1):
210
- log(f'获取台账明细列表 第{page}/{totalPage}页')
211
- payload['pageNumber'] = page
212
- response_text = fetch(self.web_page, url, payload)
213
- spu_list_new = response_text['info']['data']['list']
214
- list_item += spu_list_new
215
- time.sleep(0.1)
216
-
217
- # cost_price =
218
- for item in list_item:
219
- supplierSku = item['supplierSku']
220
- item['cost_price'] = self.bridge.get_sku_cost(supplierSku, source)
221
- item['sku_img'] = self.bridge.get_sku_img(supplierSku, source)
222
-
223
- write_dict_to_file(cache_file, list_item)
224
-
225
- return list_item
226
-
227
- def get_shein_stock_list(self, source='mb'):
228
- page_num = 1
229
- page_size = 200 # 列表最多返回200条数据 大了没有用
230
-
231
- first_day, last_day = TimeUtils.get_last_month_range()
232
-
233
- cache_file = f'{self.config.auto_dir}/shein/cache/stock_detail_{self.store_username}_{first_day}_{last_day}.json'
234
- list_item = read_dict_from_file(cache_file, 3600 * 24 * 20)
235
- if len(list_item) > 0:
236
- return list_item
237
-
238
- url = f"https://sso.geiwohuo.com/mils/report/month/detail/list"
239
- payload = {
240
- "reportDateStart": first_day,
241
- "reportDateEnd" : last_day,
242
- "pageNumber" : page_num,
243
- "pageSize" : page_size,
244
- }
245
- response_text = fetch(self.web_page, url, payload)
246
- error_code = response_text.get('code')
247
- if str(error_code) != '0':
248
- raise send_exception(json.dumps(response_text, ensure_ascii=False))
249
- list_item = response_text['info']['data']['list']
250
- total = response_text['info']['data']['count']
251
- totalPage = math.ceil(total / page_size)
252
-
253
- for page in range(2, totalPage + 1):
254
- log(f'获取库存结余明细列表 第{page}/{totalPage}页')
255
- page_num = page
256
- payload = {
257
- "reportDateStart": first_day,
258
- "reportDateEnd" : last_day,
259
- "pageNumber" : page_num,
260
- "pageSize" : page_size,
261
- }
262
- response_text = fetch(self.web_page, url, payload)
263
- spu_list_new = response_text['info']['data']['list']
264
- list_item += spu_list_new
265
- time.sleep(0.1)
266
-
267
- for item in list_item:
268
- supplierSku = item['supplierSku']
269
- item['cost_price'] = self.bridge.get_sku_cost(supplierSku, source)
270
- item['sku_img'] = self.bridge.get_sku_img(supplierSku, source)
271
-
272
- write_dict_to_file(cache_file, list_item)
273
-
274
- return list_item
275
-
276
- def get_replenish_list(self):
277
- page_num = 1
278
- page_size = 50
279
- first_day, last_day = TimeUtils.get_last_month_range()
280
-
281
- cache_file = f'{self.config.auto_dir}/cache/replenish_list_{self.store_username}_{first_day}_{last_day}.json'
282
- list_item = read_dict_from_file(cache_file, 3600 * 24 * 20)
283
- if len(list_item) > 0:
284
- return list_item
285
-
286
- url = f"https://sso.geiwohuo.com/gsfs/finance/selfReplenish/list"
287
- payload = {
288
- "page" : page_num,
289
- "perPage" : page_size,
290
- "tabType" : 2,
291
- "addTimeStart": f"{first_day} 00:00:00",
292
- "addTimeEnd" : f"{last_day} 23:59:59"
293
- }
294
- response_text = fetch(self.web_page, url, payload)
295
- error_code = response_text.get('code')
296
- if str(error_code) != '0':
297
- raise send_exception(json.dumps(response_text, ensure_ascii=False))
298
- list_item = response_text['info']['data']
299
- total = response_text['info']['meta']['count']
300
- totalPage = math.ceil(total / page_size)
301
-
302
- for page in range(2, totalPage + 1):
303
- log(f'获取不扣款列表 第{page}/{totalPage}页')
304
- page_num = page
305
- payload = {
306
- "page" : page_num,
307
- "perPage" : page_size,
308
- "tabType" : 2,
309
- "addTimeStart": f"{first_day} 00:00:00",
310
- "addTimeEnd" : f"{last_day} 23:59:59"
311
- }
312
- response_text = fetch(self.web_page, url, payload)
313
- spu_list_new = response_text['info']['data']
314
- list_item += spu_list_new
315
- time.sleep(0.1)
316
-
317
- write_dict_to_file(cache_file, list_item)
318
-
319
- return list_item
320
-
321
- def get_return_list(self):
322
- page_num = 1
323
- page_size = 200
324
- first_day, last_day = TimeUtils.get_last_month_range()
325
-
326
- cache_file = f'{self.config.auto_dir}/cache/return_list_{self.store_username}_{first_day}_{last_day}.json'
327
- list_item = read_dict_from_file(cache_file, 3600 * 24 * 20)
328
- if len(list_item) > 0:
329
- return list_item
330
-
331
- url = f"https://sso.geiwohuo.com/pfmp/returnOrder/page"
332
- payload = {
333
- "addTimeStart" : f"{first_day} 00:00:00",
334
- "addTimeEnd" : f"{last_day} 23:59:59",
335
- "returnOrderStatusList": [4],
336
- "page" : page_num,
337
- "perPage" : page_size
338
- }
339
- response_text = fetch(self.web_page, url, payload)
340
- error_code = response_text.get('code')
341
- if str(error_code) != '0':
342
- raise send_exception(json.dumps(response_text, ensure_ascii=False))
343
-
344
- list_item = response_text['info']['data']
345
- total = response_text['info']['meta']['count']
346
- totalPage = math.ceil(total / page_size)
347
-
348
- for page in range(2, totalPage + 1):
349
- log(f'获取不扣款列表 第{page}/{totalPage}页')
350
- page_num = page
351
- payload = {
352
- "addTimeStart" : f"{first_day} 00:00:00",
353
- "addTimeEnd" : f"{last_day} 23:59:59",
354
- "returnOrderStatusList": [4],
355
- "page" : page_num,
356
- "perPage" : page_size
357
- }
358
- response_text = fetch(self.web_page, url, payload)
359
- spu_list_new = response_text['info']['data']
360
- list_item += spu_list_new
361
- time.sleep(0.1)
362
-
363
- write_dict_to_file(cache_file, list_item)
364
-
365
- return list_item
366
-
367
- def get_comment_list(self):
368
- cache_file = f'{self.config.auto_dir}/shein/dict/comment_list_{TimeUtils.today_date()}.json'
369
- comment_list = read_dict_from_file_ex(cache_file, self.store_username, 3600)
370
- if len(comment_list) > 0:
371
- return comment_list
372
-
373
- page_num = 1
374
- page_size = 50
375
-
376
- yesterday = TimeUtils.get_yesterday()
377
-
378
- url = f"https://sso.geiwohuo.com/gsp/goods/comment/list"
379
- payload = {
380
- "page" : page_num,
381
- "perPage" : page_size,
382
- "startCommentTime": f"{yesterday} 00:00:00",
383
- "commentEndTime" : f"{yesterday} 23:59:59",
384
- "commentStarList" : ["3", "2", "1"]
385
- }
386
- response_text = fetch(self.web_page, url, payload)
387
- error_code = response_text.get('code')
388
- if str(error_code) != '0':
389
- raise send_exception(json.dumps(response_text, ensure_ascii=False))
390
-
391
- comment_list = response_text['info']['data']
392
- total = response_text['info']['meta']['count']
393
- totalPage = math.ceil(total / page_size)
394
-
395
- for page in range(2, totalPage + 1):
396
- log(f'获取评价列表 第{page}/{totalPage}页')
397
- page_num = page
398
- payload['page'] = page_num
399
- response_text = fetch(self.web_page, url, payload)
400
- comment_list = response_text['info']['data']
401
- time.sleep(0.1)
402
-
403
- write_dict_to_file_ex(cache_file, {self.store_username: comment_list}, [self.store_username])
404
- return comment_list
405
-
406
- def get_last_month_outbound_amount(self):
407
- url = "https://sso.geiwohuo.com/mils/report/month/list"
408
- start, end = TimeUtils.get_current_year_range()
409
- payload = {
410
- "reportDateStart": start, "reportDateEnd": end, "pageNumber": 1, "pageSize": 50
411
- }
412
- response_text = fetch(self.web_page, url, payload)
413
- error_code = response_text.get('code')
414
- if str(error_code) != '0':
415
- raise send_exception(json.dumps(response_text, ensure_ascii=False))
416
- info = response_text.get('info')
417
- lst = info.get('data', {}).get('list', [])
418
- if not lst:
419
- log(f'⚠️ {self.store_name} 最近一个月无出库记录,金额为0')
420
- return 0
421
-
422
- last_item = lst[-1]
423
- log(f'正在获取 {self.store_name} 最近一个月出库金额: {last_item["totalCustomerAmount"]}')
424
- return last_item['totalCustomerAmount']
425
-
426
- def get_funds_data(self):
427
- log(f'正在获取 {self.store_name} 财务数据')
428
- url = "https://sso.geiwohuo.com/sso/homePage/dataOverview/v2/detail"
429
- payload = {
430
- "metaIndexIds": [
431
- 298,
432
- 67,
433
- 70,
434
- 72
435
- ],
436
- }
437
- response_text = fetch(self.web_page, url, payload)
438
- error_code = response_text.get('code')
439
- if str(error_code) != '0':
440
- raise send_exception(json.dumps(response_text, ensure_ascii=False))
441
- info = response_text.get('info')
442
- num298 = 0 # 在途商品金额
443
- num67 = 0 # 在仓商品金额
444
- num70 = 0 # 待结算金额
445
- num72 = 0 # 可提现金额
446
- for item in info['list']:
447
- if item['metaIndexId'] == 298:
448
- num298 = item['count']
449
- if item['metaIndexId'] == 67:
450
- num67 = item['count']
451
- if item['metaIndexId'] == 70:
452
- num70 = item['count']
453
- if item['metaIndexId'] == 72:
454
- num72 = item['count']
455
-
456
- outAmount = self.get_last_month_outbound_amount()
457
- dict_store = read_dict_from_file(self.config.shein_store_alias)
458
- store_manager = dict_store.get(str(self.store_username).lower())
459
- NotifyItem = [f'{self.store_name}', self.store_username, store_manager, num298, num67, num70, num72, outAmount, '',
460
- TimeUtils.current_datetime()]
461
-
462
- cache_file = f'{self.config.auto_dir}/shein/cache/stat_fund_{TimeUtils.today_date()}.json'
463
- write_dict_to_file_ex(cache_file, {self.store_username: NotifyItem}, [self.store_username])
464
- return NotifyItem
465
-
466
- def getQueryDate(self):
467
- query_time = self.DictQueryTime.get(self.store_username, None)
468
- if query_time is not None:
469
- log(f'从字典获取query_time: {query_time}')
470
- return query_time
471
- log('获取日期范围')
472
- url = "https://sso.geiwohuo.com/mgs-api-prefix/estimate/queryDateRange"
473
- payload = {}
474
- response_text = fetch(self.web_page, url, payload)
475
- error_code = response_text.get('code')
476
- if str(error_code) != '0':
477
- raise send_exception(json.dumps(response_text, ensure_ascii=False))
478
- query_time = response_text.get('info').get('quality_goods_query_time')
479
- self.DictQueryTime.update({self.store_username: query_time})
480
- log(f'query_time: {query_time}')
481
- return query_time
482
-
483
- def get_goods_quality_estimate_list(self, query_date):
484
- cache_file = f'{self.config.auto_dir}/shein/dict/googs_estimate_{query_date}.json'
485
- estimate_list = read_dict_from_file_ex(cache_file, self.store_username, 3600 * 8)
486
- if len(estimate_list) > 0:
487
- return estimate_list
488
-
489
- page_num = 1
490
- page_size = 100
491
-
492
- url = f"https://sso.geiwohuo.com/mgs-api-prefix/estimate/queryNewQualityGoodsList"
493
- payload = {
494
- "page_no" : page_num,
495
- "page_size" : page_size,
496
- "start_date": query_date,
497
- "end_date" : query_date,
498
- "order_col" : "skc_sale_cnt_14d",
499
- "order_type": "desc"
500
- }
501
- response_text = fetch(self.web_page, url, payload, {'lan': 'CN'})
502
- error_code = response_text.get('code')
503
- if str(error_code) != '0':
504
- raise send_exception(json.dumps(response_text, ensure_ascii=False))
505
-
506
- estimate_list = response_text['info']['data']
507
- total = response_text['info']['meta']['count']
508
- totalPage = math.ceil(total / page_size)
509
-
510
- for page in range(2, totalPage + 1):
511
- log(f'获取质量评估列表 第{page}/{totalPage}页')
512
- page_num = page
513
- payload['page'] = page_num
514
- response_text = fetch(self.web_page, url, payload)
515
- estimate_list = response_text['info']['data']
516
- time.sleep(0.1)
517
-
518
- write_dict_to_file_ex(cache_file, {self.store_username: estimate_list}, [self.store_username])
519
- return estimate_list
520
-
521
- # 已上架备货款A数量
522
- def get_product_bak_A_count(self):
523
- url = "https://sso.geiwohuo.com/idms/goods-skc/list"
524
- payload = {
525
- "pageNumber" : 1,
526
- "pageSize" : 10,
527
- "supplierCodes" : "",
528
- "skcs" : "",
529
- "spu" : "",
530
- "c7dSaleCntBegin" : "",
531
- "c7dSaleCntEnd" : "",
532
- "goodsLevelIdList" : [
533
- 61,
534
- 90
535
- ],
536
- "supplyStatus" : "",
537
- "shelfStatus" : 1,
538
- "categoryIdList" : [],
539
- "skcStockBegin" : "",
540
- "skcStockEnd" : "",
541
- "skuStockBegin" : "",
542
- "skuStockEnd" : "",
543
- "skcSaleDaysBegin" : "",
544
- "skcSaleDaysEnd" : "",
545
- "skuSaleDaysBegin" : "",
546
- "skuSaleDaysEnd" : "",
547
- "planUrgentCountBegin" : "",
548
- "planUrgentCountEnd" : "",
549
- "skcAvailableOrderBegin": "",
550
- "skcAvailableOrderEnd" : "",
551
- "skuAvailableOrderBegin": "",
552
- "skuAvailableOrderEnd" : "",
553
- "shelfDateBegin" : "",
554
- "shelfDateEnd" : "",
555
- "stockWarnStatusList" : [],
556
- "labelFakeIdList" : [],
557
- "sheinSaleByInventory" : "",
558
- "tspIdList" : [],
559
- "adviceStatus" : [],
560
- "sortBy7dSaleCnt" : 2,
561
- "goodsLevelFakeIdList" : [
562
- 3
563
- ]
564
- }
565
- response_text = fetch(self.web_page, url, payload)
566
- error_code = response_text.get('code')
567
- if str(error_code) != '0':
568
- raise send_exception(json.dumps(response_text, ensure_ascii=False))
569
- info = response_text.get('info')
570
- count = info.get('count', 0)
571
- log('获取已上架备货款A数量', count)
572
- return count
573
-
574
- # 已上架备货款B数量
575
- def get_product_bak_B_count(self):
576
- url = "https://sso.geiwohuo.com/idms/goods-skc/list"
577
- payload = {
578
- "pageNumber" : 1,
579
- "pageSize" : 10,
580
- "supplierCodes" : "",
581
- "skcs" : "",
582
- "spu" : "",
583
- "c7dSaleCntBegin" : "",
584
- "c7dSaleCntEnd" : "",
585
- "goodsLevelIdList" : [
586
- 62,
587
- 227,
588
- 12,
589
- 230,
590
- 80,
591
- 58,
592
- 224
593
- ],
594
- "supplyStatus" : "",
595
- "shelfStatus" : 1,
596
- "categoryIdList" : [],
597
- "skcStockBegin" : "",
598
- "skcStockEnd" : "",
599
- "skuStockBegin" : "",
600
- "skuStockEnd" : "",
601
- "skcSaleDaysBegin" : None,
602
- "skcSaleDaysEnd" : "",
603
- "skuSaleDaysBegin" : "",
604
- "skuSaleDaysEnd" : "",
605
- "planUrgentCountBegin" : "",
606
- "planUrgentCountEnd" : "",
607
- "skcAvailableOrderBegin": "",
608
- "skcAvailableOrderEnd" : "",
609
- "skuAvailableOrderBegin": None,
610
- "skuAvailableOrderEnd" : "",
611
- "shelfDateBegin" : "",
612
- "shelfDateEnd" : "",
613
- "stockWarnStatusList" : [],
614
- "labelFakeIdList" : [],
615
- "sheinSaleByInventory" : "",
616
- "tspIdList" : [],
617
- "adviceStatus" : [],
618
- "sortBy7dSaleCnt" : 2,
619
- "goodsLevelFakeIdList" : [
620
- 4
621
- ]
622
- }
623
- response_text = fetch(self.web_page, url, payload)
624
- error_code = response_text.get('code')
625
- if str(error_code) != '0':
626
- raise send_exception(json.dumps(response_text, ensure_ascii=False))
627
- info = response_text.get('info')
628
- count = info.get('count', 0)
629
- log('获取已上架备货款B数量', count)
630
- return count
631
-
632
- # 已上架新款A数量
633
- def get_product_A_count(self):
634
- url = "https://sso.geiwohuo.com/idms/goods-skc/list"
635
- payload = {
636
- "pageNumber" : 1,
637
- "pageSize" : 10,
638
- "supplierCodes" : "",
639
- "skcs" : "",
640
- "spu" : "",
641
- "c7dSaleCntBegin" : "",
642
- "c7dSaleCntEnd" : "",
643
- "goodsLevelIdList" : [
644
- 107
645
- ],
646
- "supplyStatus" : "",
647
- "shelfStatus" : 1,
648
- "categoryIdList" : [],
649
- "skcStockBegin" : "",
650
- "skcStockEnd" : "",
651
- "skuStockBegin" : "",
652
- "skuStockEnd" : "",
653
- "skcSaleDaysBegin" : None,
654
- "skcSaleDaysEnd" : "",
655
- "skuSaleDaysBegin" : "",
656
- "skuSaleDaysEnd" : "",
657
- "planUrgentCountBegin" : "",
658
- "planUrgentCountEnd" : "",
659
- "skcAvailableOrderBegin": "",
660
- "skcAvailableOrderEnd" : "",
661
- "skuAvailableOrderBegin": None,
662
- "skuAvailableOrderEnd" : "",
663
- "shelfDateBegin" : "",
664
- "shelfDateEnd" : "",
665
- "stockWarnStatusList" : [],
666
- "labelFakeIdList" : [],
667
- "sheinSaleByInventory" : "",
668
- "tspIdList" : [],
669
- "adviceStatus" : [],
670
- "sortBy7dSaleCnt" : 2,
671
- "goodsLevelFakeIdList" : [
672
- 2
673
- ]
674
- }
675
- response_text = fetch(self.web_page, url, payload)
676
- error_code = response_text.get('code')
677
- if str(error_code) != '0':
678
- raise send_exception(json.dumps(response_text, ensure_ascii=False))
679
- info = response_text.get('info')
680
- count = info.get('count', 0)
681
- log('获取已上架新款A数量', count)
682
- return count
683
-
684
- # 本周已上架数量
685
- def get_week_shelf_product_count(self, start_date, end_date):
686
- url = "https://sso.geiwohuo.com/idms/goods-skc/list"
687
- payload = {
688
- "pageNumber" : 1,
689
- "pageSize" : 10,
690
- "supplierCodes" : "",
691
- "skcs" : "",
692
- "spu" : "",
693
- "c7dSaleCntBegin" : "",
694
- "c7dSaleCntEnd" : "",
695
- "goodsLevelIdList" : [],
696
- "supplyStatus" : "",
697
- "shelfStatus" : 1,
698
- "categoryIdList" : [],
699
- "skcStockBegin" : "",
700
- "skcStockEnd" : "",
701
- "skuStockBegin" : "",
702
- "skuStockEnd" : "",
703
- "skcSaleDaysBegin" : "",
704
- "skcSaleDaysEnd" : "",
705
- "skuSaleDaysBegin" : "",
706
- "skuSaleDaysEnd" : "",
707
- "planUrgentCountBegin" : "",
708
- "planUrgentCountEnd" : "",
709
- "skcAvailableOrderBegin": "",
710
- "skcAvailableOrderEnd" : "",
711
- "skuAvailableOrderBegin": "",
712
- "skuAvailableOrderEnd" : "",
713
- "shelfDateBegin" : start_date,
714
- "shelfDateEnd" : end_date,
715
- "stockWarnStatusList" : [],
716
- "labelFakeIdList" : [],
717
- "sheinSaleByInventory" : "",
718
- "tspIdList" : [],
719
- "adviceStatus" : [],
720
- "sortBy7dSaleCnt" : 2
721
- }
722
- response_text = fetch(self.web_page, url, payload)
723
- error_code = response_text.get('code')
724
- if str(error_code) != '0':
725
- raise send_exception(json.dumps(response_text, ensure_ascii=False))
726
- info = response_text.get('info')
727
- count = info.get('count', 0)
728
- log('获取本周上架数量')
729
- return count
730
-
731
- # 已上架数量
732
- def get_shelf_product_count(self):
733
- log('获取所有已上架数量')
734
- url = "https://sso.geiwohuo.com/spmp-api-prefix/spmp/product/list?page_num=1&page_size=10"
735
- payload = {
736
- "language" : "zh-cn",
737
- "only_recommend_resell" : False,
738
- "only_spmb_copy_product": False,
739
- "search_abandon_product": False,
740
- "shelf_type" : "ON_SHELF",
741
- "sort_type" : 1
742
- }
743
- response_text = fetch(self.web_page, url, payload)
744
- error_code = response_text.get('code')
745
- if str(error_code) != '0':
746
- raise send_exception(json.dumps(response_text, ensure_ascii=False))
747
-
748
- info = response_text.get('info')
749
- customObj = info.get('meta').get('customObj')
750
- # 将数据转换成字典
751
- result = {item["shelf_status"]: item["count"] for item in customObj}
752
- return result
753
-
754
- def get_yesterday_upload_product_count(self, dt=None):
755
- url = "https://sso.geiwohuo.com/spmp-api-prefix/spmp/product/publish/record/page_list?page_num=1&page_size=100"
756
- payload = {
757
- "edit_type" : 0,
758
- "language" : "zh-cn",
759
- "only_current_month_recommend": False,
760
- "only_spmb_copy_product" : False,
761
- "query_time_out" : False,
762
- "search_diy_custom" : False
763
- }
764
- response_text = fetch(self.web_page, url, payload)
765
- error_code = response_text.get('code')
766
- if str(error_code) != '0':
767
- raise send_exception(json.dumps(response_text, ensure_ascii=False))
768
- info = response_text.get('info')
769
- count = 0
770
- for item in info.get('data', {}):
771
- if TimeUtils.is_yesterday(item['create_time'], dt):
772
- count += 1
773
- log('获取昨日已上传数量', count)
774
- return count
775
-
776
- def get_week_sales_stat_detail(self):
777
- global ListNotify, NotifyItem
778
- dt = self.get_dt_time()
779
- yesterday = TimeUtils.get_yesterday(dt)
780
- date_7_days_ago = TimeUtils.get_past_nth_day(6, None, '%Y-%m-%d')
781
- log('-7', date_7_days_ago)
782
- date_1_days_ago = TimeUtils.get_past_nth_day(1, None, '%Y-%m-%d')
783
- log('-1', date_1_days_ago)
784
-
785
- url = "https://sso.geiwohuo.com/sbn/index/get_critical_indicator_curve_chart"
786
- payload = {
787
- "areaCd" : "cn",
788
- "dt" : dt,
789
- "countrySite": [
790
- "shein-all"
791
- ],
792
- "startDate" : date_7_days_ago,
793
- "endDate" : date_1_days_ago,
794
- "queryType" : 1,
795
- "pageNum" : 1,
796
- "pageSize" : 100
797
- }
798
- response_text = fetch(self.web_page, url, payload)
799
- error_code = response_text.get('code')
800
- if str(error_code) != '0':
801
- raise send_exception(json.dumps(response_text, ensure_ascii=False))
802
- info = response_text.get('info', {})
803
-
804
- last_item = SheinStoreSalesDetailManager(self.config.database_url).get_records_as_dict(self.store_username, yesterday)
805
- log('last_item', last_item)
806
- day_item = info[-1]
807
- item = {}
808
- item["store_username"] = self.store_username
809
- item["store_name"] = self.store_name
810
- item["day"] = day_item["dataDate"]
811
- item["sales_num"] = day_item["saleCnt1d"]
812
- item['sales_num_inc'] = item['sales_num'] - last_item.get('sales_num', 0)
813
- item['sales_amount'] = day_item['dealAmt1d']
814
- item['sales_amount_inc'] = item['sales_amount'] - float(last_item.get('sales_amount', 0))
815
- item['visitor_num'] = day_item['idxShopGoodsUv1d']
816
- item['visitor_num_inc'] = item['visitor_num'] - last_item.get('visitor_num', 0)
817
- item['bak_A_num'] = self.get_product_bak_A_count()
818
- item['bak_A_num_inc'] = item['bak_A_num'] - last_item.get('bak_A_num', 0)
819
- item['new_A_num'] = self.get_product_A_count()
820
- item['new_A_num_inc'] = item['new_A_num'] - last_item.get('new_A_num', 0)
821
- dictProduct = self.get_shelf_product_count()
822
- item['on_sales_product_num'] = dictProduct.get('ON_SHELF')
823
- item['on_sales_product_num_inc'] = item['on_sales_product_num'] - last_item.get('on_sales_product_num', 0)
824
- item['wait_shelf_product_num'] = dictProduct.get('WAIT_SHELF')
825
- item['wait_shelf_product_num_inc'] = item['wait_shelf_product_num'] - last_item.get('wait_shelf_product_num', 0)
826
- item['upload_product_num'] = self.get_yesterday_upload_product_count()
827
- item['upload_product_num_inc'] = item['upload_product_num'] - last_item.get('upload_product_num', 0)
828
- item['sold_out_product_num'] = dictProduct.get('SOLD_OUT')
829
- item['shelf_off_product_num'] = dictProduct.get('OUT_SHELF')
830
-
831
- SheinStoreSalesDetailManager(self.config.database_url).insert_data([item])
832
-
833
- def get_delivery_order_list(self, orderType=2):
834
- page_num = 1
835
- page_size = 200
836
-
837
- url = f"https://sso.geiwohuo.com/pfmp/order/list"
838
- payload = {}
839
- if orderType == 1:
840
- payload = {
841
- "orderType": orderType,
842
- "page" : page_num,
843
- "perPage" : page_size,
844
- "status" : [2],
845
- }
846
- elif orderType == 2:
847
- payload = {
848
- "orderType" : orderType,
849
- "page" : page_num,
850
- "perPage" : page_size,
851
- "status" : [2],
852
- "isJitOrder": 2
853
- }
854
- response_text = fetch(self.web_page, url, payload)
855
- error_code = response_text.get('code')
856
- if str(error_code) != '0':
857
- raise send_exception(json.dumps(response_text, ensure_ascii=False))
858
-
859
- spu_list = response_text['info']['data']
860
- total = response_text['info']['meta']['count']
861
- totalPage = math.ceil(total / page_size)
862
-
863
- skc_list = [item['goods']['skcName'] for item in spu_list]
864
- self.get_activity_label(skc_list)
865
-
866
- for page in range(2, totalPage + 1):
867
- log(f'获取订单列表 第{page}/{totalPage}页')
868
- page_num = page
869
- if orderType == 1:
870
- payload = {
871
- "orderType": orderType,
872
- "page" : page_num,
873
- "perPage" : page_size,
874
- "status" : [2],
875
- }
876
- elif orderType == 2:
877
- payload = {
878
- "orderType" : orderType,
879
- "page" : page_num,
880
- "perPage" : page_size,
881
- "status" : [2],
882
- "isJitOrder": 2
883
- }
884
- response_text = fetch(self.web_page, url, payload)
885
- spu_list_new = response_text['info']['data']
886
- skc_list = [item['goods']['skcName'] for item in spu_list_new]
887
- self.get_activity_label(skc_list)
888
- spu_list += spu_list_new
889
- time.sleep(0.3)
890
-
891
- if len(spu_list) == 0:
892
- log(f'无{["", "急采", "备货"][orderType]}发货单')
893
- return None
894
-
895
- write_to_excel = [
896
- # 0 1 2 3 4 5 6 7
897
- ['店铺名称', 'SKC图片', 'SKU图片', '商品信息', '下单/需求数量', '库存(模式/本地/在途/希音)', '成本价', '核价',
898
- '近7天SKU销量/SKC销量/SKC曝光', 'SKC点击率/SKC转化率', '自主参与活动', '最晚预约上门取件', '要求实际完成取件',
899
- 'SKC', 'SKU']
900
- ]
901
- cache_file2 = f'{self.config.auto_dir}/shein/dict/skc_shelf_{self.store_username}.json'
902
- DictSkcShelf = read_dict_from_file(cache_file2)
903
- cache_file3 = f'{self.config.auto_dir}/shein/dict/skc_product_{self.store_username}.json'
904
- DictSkcProduct = read_dict_from_file(cache_file3)
905
- cache_file = f'{self.config.auto_dir}/shein/dict/activity_price_{self.store_name}.json'
906
- dictActivityPrice = read_dict_from_file(cache_file)
907
- cache_file4 = f'{self.config.auto_dir}/shein/dict/dict_sku_info_{self.store_username}.json'
908
- DictSkuInfo = read_dict_from_file(cache_file4)
909
- for spu_item in spu_list:
910
- skc = spu_item['goods']['skcName']
911
- skcCode = spu_item['goods']['supplierCode']
912
- skc_img = str(spu_item['goods']['imgPath'])
913
- orderNum = spu_item['sellerOrderNo']
914
- orderTime = spu_item['allocateTime']
915
- requestTakeParcelTime = spu_item['requestTakeParcelTime']
916
- suggestedReserveTime = spu_item['suggestedReserveTime']
917
- good_level = spu_item['goods']['goodsLevelName']
918
-
919
- self.get_skc_week_actual_sales(skc)
920
-
921
- spu = DictSkcProduct[skc]['spu_name']
922
- log('spu', spu)
923
- for sku_item in spu_item['detail']:
924
- needQuantity = sku_item['needQuantity']
925
- orderQuantity = sku_item['orderQuantity']
926
- # sku_img = sku_item['skuThumb']
927
- skuCode = sku_item['supplierSku']
928
- stock = self.bridge.get_sku_stock(skuCode, 'mb')
929
- cost_price = self.bridge.get_sku_cost(skuCode, 'mb')
930
- suffixZh = sku_item['suffixZh']
931
- sku = sku_item['skuCode']
932
- supplyPrice = sku_item['supplyPrice']
933
- sku_img = self.bridge.get_sku_img(skuCode, 'mb')
934
- sale_model = DictSkuInfo[skuCode][0]
935
- shein_stock = DictSkuInfo[skuCode][1]
936
- shelf_days = DictSkuInfo[skuCode][2]
937
- real_transit = DictSkuInfo[skuCode][3]
938
- stock_str = f'{sale_model}\n{stock}/{real_transit}/{shein_stock}'
939
-
940
- item = []
941
- item.append(f'{self.store_name}\n{good_level}')
942
- item.append(skc_img)
943
- item.append(sku_img)
944
- if cost_price == '-':
945
- profit = '-'
946
- else:
947
- profit = f'{float(supplyPrice) - float(cost_price):.2f}'
948
- # item.append(f'订单号: {orderNum}\n下单时间: {orderTime}\n最晚预约上门取件: {suggestedReserveTime}\nSKC: {skc}\nSKC货号: {skcCode}\nSKU货号: {skuCode}\n属性集: {suffixZh}\n上架时间: {DictSkcShelf[skc]}\n上架天数: {shelf_days}\n成本/核价/利润: ¥{cost_price}/¥{supplyPrice}/¥{profit}\n下单/需求数量: {orderQuantity}/{needQuantity}\n库存模式/本地/在途/希音: {sale_model}/{stock}/{real_transit}/{shein_stock}\n')
949
- item.append(f'订单号: {orderNum}\n下单时间: {orderTime}\n最晚预约上门取件: {suggestedReserveTime}\nSKC: {skc}\nSKC货号: {skcCode}\nSKU货号: {skuCode}\n属性集: {suffixZh}\n上架时间: {DictSkcShelf[skc]}\n上架天数: {shelf_days}\n成本/核价/利润: ¥{cost_price}/¥{supplyPrice}/¥{profit}')
950
- item.append(f'[{orderQuantity}/{needQuantity}]')
951
- item.append(stock_str)
952
- item.append(cost_price)
953
- item.append(supplyPrice)
954
- sale_num_list, sale_data_list = self.get_skc_week_sale_list(spu, skc, sku)
955
- item.append("\n".join(sale_num_list))
956
- item.append("\n".join(sale_data_list))
957
- item.append(self.get_skc_activity_label(skc, sku, dictActivityPrice))
958
- item.append(suggestedReserveTime)
959
- item.append(requestTakeParcelTime)
960
- item.append(skc)
961
- item.append(sku)
962
- write_to_excel.append(item)
963
-
964
- cache_file = f'{self.config.auto_dir}/shein/cache/jit_{TimeUtils.today_date()}_{orderType}_{TimeUtils.get_period()}.json'
965
- write_dict_to_file_ex(cache_file, {self.store_username: write_to_excel}, {self.store_username})
966
-
967
- return write_to_excel
968
-
969
- # 获取商品包含sku销量的列表
970
- def get_dict_sku_stock_detail(self):
971
- log(f'获取备货信息商品列表 做成字典')
972
- url = "https://sso.geiwohuo.com/idms/goods-skc/list"
973
- pageNumber = 1
974
- pageSize = 100
975
- dictPayload = {
976
- "pageNumber" : pageNumber,
977
- "pageSize" : pageSize,
978
- "supplierCodes" : "",
979
- "skcs" : "",
980
- "spu" : "",
981
- "c7dSaleCntBegin" : "",
982
- "c7dSaleCntEnd" : "",
983
- "goodsLevelIdList" : [],
984
- "supplyStatus" : "",
985
- "shelfStatus" : "",
986
- "categoryIdList" : [],
987
- "skcStockBegin" : "",
988
- "skcStockEnd" : "",
989
- "skuStockBegin" : "",
990
- "skuStockEnd" : "",
991
- "skcSaleDaysBegin" : "",
992
- "skcSaleDaysEnd" : "",
993
- "skuSaleDaysBegin" : "",
994
- "skuSaleDaysEnd" : "",
995
- "planUrgentCountBegin" : "",
996
- "planUrgentCountEnd" : "",
997
- "skcAvailableOrderBegin": "",
998
- "skcAvailableOrderEnd" : "",
999
- "skuAvailableOrderBegin": "",
1000
- "skuAvailableOrderEnd" : "",
1001
- "shelfDateBegin" : "",
1002
- "shelfDateEnd" : "",
1003
- "stockWarnStatusList" : [],
1004
- "labelFakeIdList" : [],
1005
- "sheinSaleByInventory" : "",
1006
- "tspIdList" : [],
1007
- "adviceStatus" : [],
1008
- "sortBy7dSaleCnt" : 2
1009
- }
1010
- payload = dictPayload
1011
- response_text = fetch(self.web_page, url, payload)
1012
- error_code = response_text.get('code')
1013
- if str(error_code) != '0':
1014
- raise send_exception(json.dumps(response_text, ensure_ascii=False))
1015
-
1016
- spu_list = response_text['info']['list']
1017
-
1018
- total = response_text['info']['count']
1019
- totalPage = math.ceil(total / pageSize)
1020
- for page in range(2, totalPage + 1):
1021
- log(f'获取备货信息商品列表 第{page}/{totalPage}页')
1022
- dictPayload['pageNumber'] = page
1023
- payload = dictPayload
1024
- response_text = fetch(self.web_page, url, payload)
1025
- spu_list_new = response_text['info']['list']
1026
- spu_list += spu_list_new
1027
- time.sleep(0.3)
1028
-
1029
- DictSkuInfo = {}
1030
- for spu_info in spu_list:
1031
- sale_model = spu_info.get('saleModel', {}).get('name') if spu_info.get('saleModel') else '-'
1032
- shelfDays = spu_info['shelfDays']
1033
- for sku_info in spu_info['skuList']:
1034
- attr = sku_info['attr']
1035
- if attr == '合计':
1036
- continue
1037
- skuExtCode = str(sku_info['supplierSku'])
1038
- shein_stock = sku_info['stock']
1039
-
1040
- transit = sku_info['transit'] # 在途
1041
- real_transit = transit + sku_info['stayShelf'] - sku_info['transitSale']
1042
-
1043
- DictSkuInfo[skuExtCode] = [sale_model, shein_stock, shelfDays, real_transit]
1044
-
1045
- write_dict_to_file(f'{self.config.auto_dir}/shein/dict/dict_sku_info_{self.store_username}.json', DictSkuInfo)
1046
-
1047
- return DictSkuInfo
1048
-
1049
- def get_shop_notify_num(self):
1050
- log(f'正在获取 {self.store_name} 通知数据')
1051
- url = "https://sso.geiwohuo.com/sso/homePage/v4/detail"
1052
- payload = {
1053
- "metaIndexIds": [
1054
- 246, # 急采-待发货
1055
- 245 # 备货-待发货
1056
- ],
1057
- "templateType": 0
1058
- }
1059
- response_text = fetch(self.web_page, url, payload)
1060
- error_code = response_text.get('code')
1061
- if str(error_code) != '0':
1062
- raise send_exception(json.dumps(response_text, ensure_ascii=False))
1063
- info = response_text.get('info')
1064
-
1065
- cache_file = f'{self.config.auto_dir}/shein/notify/{self.store_name}_{TimeUtils.get_current_datetime()}.json'
1066
- write_dict_to_file(cache_file, info)
1067
-
1068
- num245 = 0
1069
- num246 = 0
1070
- for item in info['list']:
1071
- if item['metaIndexId'] == 245:
1072
- num245 = item['count']
1073
- if item['metaIndexId'] == 246:
1074
- num246 = item['count']
1075
-
1076
- NotifyItem = [self.store_name, num246, num245]
1077
-
1078
- cache_file = f'{self.config.auto_dir}/shein/cache/jit_notify_{TimeUtils.today_date()}.json'
1079
- write_dict_to_file_ex(cache_file, {self.store_username: NotifyItem}, {self.store_username})
1080
-
1081
- return info
1082
-
1083
- def get_activity_list(self):
1084
- url = "https://sso.geiwohuo.com/mrs-api-prefix/mbrs/activity/get_activity_list?page_num=1&page_size=100"
1085
- payload = {}
1086
- response_text = fetch(self.web_page, url, payload)
1087
- error_code = response_text.get('code')
1088
- if str(error_code) != '0':
1089
- raise send_exception(json.dumps(response_text, ensure_ascii=False))
1090
- total = response_text.get('info', {}).get('total_count')
1091
- excel_data = [[
1092
- '店铺名称', '活动信息', '已报数量', '可报数量'
1093
- ]]
1094
- if total > 0:
1095
- for item in response_text.get('info', {}).get('activity_detail_list'):
1096
- activity_tag = item.get('text_tag_content')
1097
- activity_name = item['activity_name']
1098
- start_time = item['activity_start_zone_time']
1099
- end_time = item['activity_end_zone_time']
1100
- start_time2 = item['start_zone_time']
1101
- end_time2 = item['end_zone_time']
1102
- allow_goods_num = item.get('allow_goods_num')
1103
- apply_goods_num = item.get('apply_goods_num')
1104
- row_item = [
1105
- self.store_name,
1106
- f"活动名称: 【{activity_tag}】{activity_name}\n报名时间: {start_time}~{end_time}\n活动时间: {start_time2}~{end_time2}\n已报数量: {apply_goods_num}/{allow_goods_num}",
1107
- apply_goods_num,
1108
- allow_goods_num,
1109
- ]
1110
- excel_data.append(row_item)
1111
- cache_file = f'{self.config.auto_dir}/shein/activity_list/activity_list_{TimeUtils.today_date()}.json'
1112
- write_dict_to_file_ex(cache_file, {self.store_username: excel_data}, [self.store_username])
1113
-
1114
- def get_product_list(self):
1115
- # self.web_page.goto('https://sso.geiwohuo.com/#/spmp/commdities/list')
1116
- # self.web_page.wait_for_load_state("load")
1117
-
1118
- cache_file = f'{self.config.auto_dir}/shein/dict/product_list_{self.store_username}.json'
1119
- DictSpuInfo = read_dict_from_file(cache_file, 3600)
1120
- if len(DictSpuInfo) > 0:
1121
- return DictSpuInfo
1122
-
1123
- page_num = 1
1124
- page_size = 100
1125
-
1126
- url = f"https://sso.geiwohuo.com/spmp-api-prefix/spmp/product/list?page_num={page_num}&page_size={page_size}"
1127
- payload = {
1128
- "language" : "zh-cn",
1129
- "only_recommend_resell" : False,
1130
- "only_spmb_copy_product": False,
1131
- "search_abandon_product": False,
1132
- "sort_type" : 1
1133
- }
1134
- response_text = fetch(self.web_page, url, payload)
1135
- error_code = response_text.get('code')
1136
- if str(error_code) != '0':
1137
- raise send_exception(json.dumps(response_text, ensure_ascii=False))
1138
-
1139
- spu_list = response_text['info']['data']
1140
- total = response_text['info']['meta']['count']
1141
- totalPage = math.ceil(total / page_size)
1142
-
1143
- for page_num in range(2, totalPage + 1):
1144
- log(f'获取商品列表 第{page_num}/{totalPage}页')
1145
- url = f"https://sso.geiwohuo.com/spmp-api-prefix/spmp/product/list?page_num={page_num}&page_size={page_size}"
1146
- response_text = fetch(self.web_page, url, payload)
1147
- spu_list_new = response_text['info']['data']
1148
- spu_list += spu_list_new
1149
- time.sleep(0.3)
1150
-
1151
- DictSkcShelf = {}
1152
- DictSkcProduct = {}
1153
- DictSpuInfo = {}
1154
- for spu_item in spu_list:
1155
- spu = spu_item['spu_name']
1156
- first_shelf_time = spu_item['first_shelf_time']
1157
- for skc_item in spu_item['skc_info_list']:
1158
- skc_name = skc_item['skc_name']
1159
- DictSkcShelf[skc_name] = first_shelf_time
1160
- DictSkcProduct[skc_name] = spu_item
1161
- DictSpuInfo[spu] = spu_item
1162
-
1163
- cache_file2 = f'{self.config.auto_dir}/shein/dict/skc_shelf_{self.store_username}.json'
1164
- write_dict_to_file(cache_file2, DictSkcShelf)
1165
- cache_file3 = f'{self.config.auto_dir}/dict/skc_product_{self.store_username}.json'
1166
- write_dict_to_file(cache_file3, DictSkcProduct)
1167
-
1168
- write_dict_to_file(cache_file, DictSpuInfo)
1169
- return DictSpuInfo
1170
-
1171
- def query_obm_activity_list(self):
1172
- page_num = 1
1173
- page_size = 100
1174
- date_60_days_ago = TimeUtils.get_past_nth_day(59)
1175
- cache_file = f'{self.config.auto_dir}/shein/cache/obm_activity_{self.store_name}_{date_60_days_ago}_{TimeUtils.today_date()}.json'
1176
- list_item = read_dict_from_file(cache_file, 3600 * 8)
1177
- if len(list_item) > 0:
1178
- return list_item
1179
-
1180
- url = f"https://sso.geiwohuo.com/mrs-api-prefix/promotion/obm/query_obm_activity_list"
1181
- payload = {
1182
- "insert_end_time" : f"{TimeUtils.today_date()} 23:59:59",
1183
- "insert_start_time": f"{date_60_days_ago} 00:00:00",
1184
- "page_num" : page_num,
1185
- "page_size" : page_size,
1186
- "system" : "mrs",
1187
- "time_zone" : "Asia/Shanghai",
1188
- # "state": 3, # 活动开启中 不能用这个条件
1189
- "type_id" : 31 # 限时折扣
1190
- }
1191
-
1192
- response_text = fetch(self.web_page, url, payload)
1193
- error_code = response_text.get('code')
1194
- if str(error_code) != '0':
1195
- raise send_exception(json.dumps(response_text, ensure_ascii=False))
1196
-
1197
- list_item = response_text['info']['data']
1198
- total = response_text['info']['meta']['count']
1199
- totalPage = math.ceil(total / page_size)
1200
-
1201
- for page in range(2, totalPage + 1):
1202
- log(f'获取营销工具列表 第{page}/{totalPage}页')
1203
- payload["page_num"] = page
1204
- response_text = fetch(self.web_page, url, payload)
1205
- list_item += response_text['info']['data']
1206
- time.sleep(0.1)
1207
-
1208
- write_dict_to_file(cache_file, list_item)
1209
- return list_item
1210
-
1211
- def query_goods_detail(self, activity_id):
1212
- # web_page.goto(f'https://sso.geiwohuo.com/#/mrs/tools/activity/obm-time-limit-info/{activity_id}')
1213
- # web_page.wait_for_load_state('load')
1214
- log(f'正在获取 {self.store_name} {activity_id} 营销工具商品详情')
1215
-
1216
- cache_file = f'{self.config.auto_dir}/shein/cache/query_goods_detail_{activity_id}.json'
1217
- list_item = read_dict_from_file(cache_file, 3600 * 8)
1218
- if len(list_item) > 0:
1219
- return list_item
1220
-
1221
- page_num = 1
1222
- page_size = 100
1223
- url = "https://sso.geiwohuo.com/mrs-api-prefix/promotion/simple_platform/query_goods_detail"
1224
- payload = {
1225
- "activity_id": activity_id,
1226
- "page_num" : page_num,
1227
- "page_size" : page_size
1228
- }
1229
- response_text = fetch(self.web_page, url, payload)
1230
- error_code = response_text.get('code')
1231
- if str(error_code) != '0':
1232
- raise send_exception(json.dumps(response_text, ensure_ascii=False))
1233
- list_item = response_text['info']['data']
1234
- total = response_text['info']['meta']['count']
1235
- totalPage = math.ceil(total / page_size)
1236
-
1237
- for page in range(2, totalPage + 1):
1238
- log(f'获取营销工具商品列表 第{page}/{totalPage}页')
1239
- payload["page_num"] = page
1240
- response_text = fetch(self.web_page, url, payload)
1241
- list_item += response_text['info']['data']
1242
- time.sleep(0.1)
1243
-
1244
- write_dict_to_file(cache_file, list_item)
1245
- return list_item
1246
-
1247
- def get_partake_activity_goods_list(self):
1248
- # self.web_page.goto(f'https://sso.geiwohuo.com/#/mbrs/marketing/list/1')
1249
- # self.web_page.wait_for_load_state('load')
1250
- log(f'正在获取 {self.store_name} 活动列表')
1251
- page_num = 1
1252
- page_size = 100
1253
- date_60_days_ago = TimeUtils.get_past_nth_day(59)
1254
- cache_file = f'{self.config.auto_dir}/shein/cache/platform_activity_{self.store_name}_{date_60_days_ago}_{TimeUtils.today_date()}.json'
1255
- list_item = read_dict_from_file(cache_file, 3600 * 8)
1256
- if len(list_item) > 0:
1257
- return list_item
1258
-
1259
- url = f"https://sso.geiwohuo.com/mrs-api-prefix/mbrs/activity/get_partake_activity_goods_list?page_num={page_num}&page_size={page_size}"
1260
- payload = {
1261
- "goods_audit_status" : 1,
1262
- "insert_zone_time_end" : f"{TimeUtils.today_date()} 23:59:59",
1263
- "insert_zone_time_start": f"{date_60_days_ago} 00:00:00"
1264
- }
1265
-
1266
- response_text = fetch(self.web_page, url, payload)
1267
- error_code = response_text.get('code')
1268
- if str(error_code) != '0':
1269
- raise send_exception(json.dumps(response_text, ensure_ascii=False))
1270
- list_item = response_text['info']['data']
1271
- total = response_text['info']['meta']['count']
1272
- totalPage = math.ceil(total / page_size)
1273
-
1274
- for page in range(2, totalPage + 1):
1275
- log(f'获取活动列表 第{page}/{totalPage}页')
1276
- payload["page_num"] = page
1277
- response_text = fetch(self.web_page, url, payload)
1278
- list_item += response_text['info']['data']
1279
- time.sleep(0.1)
1280
-
1281
- write_dict_to_file(cache_file, list_item)
1282
- return list_item
1283
-
1284
- def generate_activity_price_dict(self):
1285
- cache_file = f'{self.config.auto_dir}/shein/dict/activity_price_{self.store_name}.json'
1286
- dict_activity_price = {}
1287
- activity_list = self.query_obm_activity_list()
1288
- for activity in activity_list:
1289
- activity_id = activity['activity_id']
1290
- activity_name = activity['act_name']
1291
- sub_type_id = activity['sub_type_id'] # 1.不限量 2.限量
1292
- dateBegin = TimeUtils.convert_datetime_to_date(activity['start_time'])
1293
- dateEnd = TimeUtils.convert_datetime_to_date(activity['end_time'])
1294
- skc_list = self.query_goods_detail(activity_id)
1295
- for skc_item in skc_list:
1296
- attend_num_sum = skc_item['attend_num_sum']
1297
- product_act_price = skc_item['product_act_price'] # 活动价
1298
- if sub_type_id == 1:
1299
- attend_num_sum = '不限量'
1300
- for sku_item in skc_item['sku_info_list']:
1301
- sku = sku_item['sku'] # 平台sku
1302
- product_act_price = sku_item['product_act_price'] if sku_item[
1303
- 'product_act_price'] else product_act_price # 活动价
1304
- key = f'{sku}_{dateBegin}_{dateEnd}_{activity_name}'
1305
- dict_activity_price[key] = [product_act_price, attend_num_sum]
1306
-
1307
- platform_activity_list = self.get_partake_activity_goods_list()
1308
- for platform_activity in platform_activity_list:
1309
- activity_name = platform_activity['activity_name']
1310
- text_tag_content = platform_activity['text_tag_content']
1311
- attend_num = platform_activity['attend_num']
1312
- dateBegin = TimeUtils.convert_timestamp_to_date(platform_activity['start_time'])
1313
- dateEnd = TimeUtils.convert_timestamp_to_date(platform_activity['end_time'])
1314
- if text_tag_content != '新品':
1315
- attend_num = '-'
1316
- for sku_item in platform_activity['activity_sku_list']:
1317
- sku = sku_item['sku_code']
1318
- enroll_price = sku_item['enroll_display_str'][:-3]
1319
- key = f'{sku}_{dateBegin}_{dateEnd}_{activity_name}'
1320
- dict_activity_price[key] = [enroll_price, attend_num]
1321
-
1322
- write_dict_to_file(cache_file, dict_activity_price)
1323
-
1324
- def get_skc_week_actual_sales(self, skc):
1325
- first_day, last_day = TimeUtils.get_past_7_days_range()
1326
- cache_file = f'{self.config.auto_dir}/shein/cache/{skc}_{first_day}_{last_day}.json'
1327
- if datetime.now().hour >= 9:
1328
- DictSkuSalesDate = read_dict_from_file(cache_file)
1329
- else:
1330
- DictSkuSalesDate = read_dict_from_file(cache_file, 1800)
1331
- if len(DictSkuSalesDate) > 0:
1332
- return DictSkuSalesDate
1333
-
1334
- url = f"https://sso.geiwohuo.com/idms/sale-trend/detail"
1335
- payload = {
1336
- "skc" : skc,
1337
- "startDate": first_day,
1338
- "endDate" : last_day,
1339
- "daysToAdd": 0
1340
- }
1341
- response_text = fetch(self.web_page, url, payload)
1342
- error_code = response_text.get('code')
1343
- if str(error_code) != '0':
1344
- log(response_text)
1345
- return {}
1346
- list_item = response_text['info']['salesVolumeDateVoList']
1347
- for item in list_item:
1348
- key = item['date']
1349
- DictSkuSalesDate[key] = item['salesVolumeMap']
1350
- list_item2 = response_text['info']['actualSalesVolumeMap']
1351
- for item in list_item2:
1352
- sku = item['skuCode']
1353
- if sku is not None:
1354
- DictSkuSalesDate[sku] = item['actualSalesVolume']
1355
-
1356
- write_dict_to_file(cache_file, DictSkuSalesDate)
1357
- return DictSkuSalesDate
1358
-
1359
- def get_preemption_list(self, skc_list):
1360
- url = f"https://sso.geiwohuo.com/idms/goods-skc/preemption-num"
1361
- payload = skc_list
1362
- response_text = fetch(self.web_page, url, payload)
1363
- error_code = response_text.get('code')
1364
- if str(error_code) != '0':
1365
- raise send_exception(json.dumps(response_text, ensure_ascii=False))
1366
-
1367
- dict = response_text['info']
1368
-
1369
- cache_file = f'{self.config.auto_dir}/shein/preemption_num/preemption_num_{self.store_username}.json'
1370
- dict_preemption_num = read_dict_from_file(cache_file)
1371
- dict_preemption_num.update(dict)
1372
- write_dict_to_file(cache_file, dict_preemption_num)
1373
-
1374
- return dict
1375
-
1376
- def get_activity_label(self, skc_list):
1377
- url = f"https://sso.geiwohuo.com/idms/goods-skc/activity-label"
1378
- payload = skc_list
1379
- response_text = fetch(self.web_page, url, payload)
1380
- error_code = response_text.get('code')
1381
- if str(error_code) != '0':
1382
- raise send_exception(json.dumps(response_text, ensure_ascii=False))
1383
- dict = response_text['info']
1384
-
1385
- cache_file = f'{self.config.auto_dir}/shein/activity_label/activity_label_{self.store_username}.json'
1386
- dict_label = read_dict_from_file(cache_file)
1387
- dict_label.update(dict)
1388
- write_dict_to_file(cache_file, dict_label)
1389
-
1390
- return dict
1391
-
1392
- def get_sku_price_v2(self, skc_list):
1393
- log(f'获取sku价格列表', skc_list)
1394
- url = "https://sso.geiwohuo.com/idms/goods-skc/price"
1395
- response_text = fetch(self.web_page, url, skc_list)
1396
- error_code = response_text.get('code')
1397
- if str(error_code) != '0':
1398
- raise send_exception(json.dumps(response_text, ensure_ascii=False))
1399
- dict = response_text['info']
1400
-
1401
- cache_file = f'{self.config.auto_dir}/shein/sku_price/sku_price_{self.store_username}.json'
1402
- dict_sku_price = read_dict_from_file(cache_file)
1403
- dict_sku_price.update(dict)
1404
- write_dict_to_file(cache_file, dict_sku_price)
1405
-
1406
- return dict
1407
-
1408
- def get_stock_advice(self, skc_list):
1409
- log(f'获取sku库存建议列表', skc_list)
1410
- url = f"https://sso.geiwohuo.com/idms/goods-skc/get-vmi-spot-advice"
1411
- payload = skc_list
1412
- response_text = fetch(self.web_page, url, payload)
1413
- error_code = response_text.get('code')
1414
- if str(error_code) != '0':
1415
- raise send_exception(json.dumps(response_text, ensure_ascii=False))
1416
- dict = response_text['info']
1417
-
1418
- cache_file = f'{self.config.auto_dir}/shein/vmi_spot_advice/spot_advice_{self.store_username}.json'
1419
- dict_advice = read_dict_from_file(cache_file)
1420
- dict_advice.update(dict)
1421
- write_dict_to_file(cache_file, dict_advice)
1422
-
1423
- return dict
1424
-
1425
- def get_dt_time(self):
1426
- if self.dt is not None:
1427
- log(f'字典dt: {self.dt}')
1428
- return self.dt
1429
- log('获取非实时更新时间')
1430
- url = "https://sso.geiwohuo.com/sbn/common/get_update_time"
1431
- payload = {
1432
- "pageCode": "Index",
1433
- "areaCd" : "cn"
1434
- }
1435
- response_text = fetch(self.web_page, url, payload)
1436
- error_code = response_text.get('code')
1437
- if str(error_code) != '0':
1438
- raise send_exception(json.dumps(response_text, ensure_ascii=False))
1439
- self.dt = response_text.get('info').get('dt')
1440
- log(f'dt: {self.dt}')
1441
- return self.dt
1442
-
1443
- def get_dict_skc_week_trend(self):
1444
- page_num = 1
1445
- page_size = 100
1446
-
1447
- date_7_days_ago = TimeUtils.get_past_nth_day(7, None, '%Y%m%d')
1448
- log('-7', date_7_days_ago)
1449
- date_1_days_ago = TimeUtils.get_past_nth_day(1, None, '%Y%m%d')
1450
- log('-1', date_1_days_ago)
1451
-
1452
- url = f"https://sso.geiwohuo.com/sbn/new_goods/get_skc_diagnose_list"
1453
- payload = {
1454
- "areaCd" : "cn",
1455
- "countrySite": [
1456
- "shein-all"
1457
- ],
1458
- "startDate" : date_7_days_ago,
1459
- "endDate" : date_1_days_ago,
1460
- "pageNum" : page_num,
1461
- "pageSize" : page_size
1462
- }
1463
- response_text = fetch(self.web_page, url, payload)
1464
- error_code = response_text.get('code')
1465
- if str(error_code) != '0':
1466
- raise send_exception(json.dumps(response_text, ensure_ascii=False))
1467
- spu_list = response_text['info']['data']
1468
- total = response_text['info']['meta']['count']
1469
- totalPage = math.ceil(total / page_size)
1470
-
1471
- for page in range(2, totalPage + 1):
1472
- log(f'获取商品列表 第{page}/{totalPage}页')
1473
- page_num = page
1474
- payload = {
1475
- "areaCd" : "cn",
1476
- "countrySite": [
1477
- "shein-all"
1478
- ],
1479
- "startDate" : date_7_days_ago,
1480
- "endDate" : date_1_days_ago,
1481
- "pageNum" : page_num,
1482
- "pageSize" : page_size
1483
- }
1484
- response_text = fetch(self.web_page, url, payload)
1485
- spu_list_new = response_text['info']['data']
1486
- spu_list += spu_list_new
1487
- time.sleep(0.3)
1488
-
1489
- DictSkcWeekTrend = {}
1490
- for spu_item in spu_list:
1491
- skc = str(spu_item['skc'])
1492
- DictSkcWeekTrend[skc] = spu_item
1493
-
1494
- log('len(DictSkcWeekTrend)', len(DictSkcWeekTrend))
1495
- write_dict_to_file(f'{self.config.auto_dir}/shein/dict/dict_skc_week_trend_{self.store_username}.json', DictSkcWeekTrend)
1496
- return DictSkcWeekTrend
1497
-
1498
- def get_skc_sales(self, skc, start_date, end_date):
1499
- url = "https://sso.geiwohuo.com/idms/stockadvice/saleTrendDetail"
1500
- payload = {
1501
- "skc" : skc,
1502
- "startDate": start_date,
1503
- "endDate" : end_date
1504
- }
1505
- response_text = fetch(self.web_page, url, payload)
1506
- error_code = response_text.get('code')
1507
- error_msg = response_text.get('msg')
1508
- if str(error_code) != '0':
1509
- raise send_exception(json.dumps(response_text, ensure_ascii=False))
1510
-
1511
- sales_list = response_text['info']['saleTrendDetailList']
1512
- if sales_list:
1513
- for skc_list in sales_list:
1514
- date = skc_list['date']
1515
- if date == '合计':
1516
- log('无销量skc: ', skc)
1517
- continue
1518
- skc_sale = skc_list['skcSale']
1519
- skc_order = skc_list['skcOrder']
1520
- for sku_list in skc_list['skuSaleTrendDetailList']:
1521
- sku = sku_list['skuCode']
1522
- attr_name = sku_list['attributeName']
1523
- sku_sale = sku_list['skuSale']
1524
- sku_order = sku_list['skuOrder']
1525
- if sku_sale > 0:
1526
- insert_sales(skc, date, skc_sale, skc_order, sku, attr_name, sku_sale, sku_order)
1527
- return sales_list
1528
-
1529
- # 获取商品包含sku销量的列表
1530
- def get_product_sku_sales_list(self, source='mb'):
1531
- log(f'获取销量列表')
1532
- url = "https://sso.geiwohuo.com/idms/goods-skc/list"
1533
- pageNumber = 1
1534
- pageSize = 100
1535
- dictPayload = {
1536
- "pageNumber" : pageNumber,
1537
- "pageSize" : pageSize,
1538
- "supplierCodes" : "",
1539
- "skcs" : "",
1540
- "spu" : "",
1541
- "c7dSaleCntBegin" : "",
1542
- "c7dSaleCntEnd" : "",
1543
- "goodsLevelIdList" : [],
1544
- "supplyStatus" : "",
1545
- "shelfStatus" : 1,
1546
- "categoryIdList" : [],
1547
- "skcStockBegin" : "",
1548
- "skcStockEnd" : "",
1549
- "skuStockBegin" : "",
1550
- "skuStockEnd" : "",
1551
- "skcSaleDaysBegin" : "",
1552
- "skcSaleDaysEnd" : "",
1553
- "skuSaleDaysBegin" : "",
1554
- "skuSaleDaysEnd" : "",
1555
- "planUrgentCountBegin" : "",
1556
- "planUrgentCountEnd" : "",
1557
- "skcAvailableOrderBegin": "",
1558
- "skcAvailableOrderEnd" : "",
1559
- "skuAvailableOrderBegin": "",
1560
- "skuAvailableOrderEnd" : "",
1561
- "shelfDateBegin" : "",
1562
- "shelfDateEnd" : "",
1563
- "stockWarnStatusList" : [],
1564
- "labelFakeIdList" : [],
1565
- "sheinSaleByInventory" : "",
1566
- "tspIdList" : [],
1567
- "adviceStatus" : [],
1568
- "sortBy7dSaleCnt" : 2
1569
- }
1570
- payload = dictPayload
1571
- response_text = fetch(self.web_page, url, payload)
1572
- error_code = response_text.get('code')
1573
- if str(error_code) != '0':
1574
- raise send_exception(json.dumps(response_text, ensure_ascii=False))
1575
-
1576
- spu_list = response_text['info']['list']
1577
-
1578
- total = response_text['info']['count']
1579
- totalPage = math.ceil(total / pageSize)
1580
- for page in range(2, totalPage + 1):
1581
- log(f'获取SKU销量列表 第{page}/{totalPage}页')
1582
- dictPayload['pageNumber'] = page
1583
- payload = dictPayload
1584
- response_text = fetch(self.web_page, url, payload)
1585
- spu_list_new = response_text['info']['list']
1586
- spu_list += spu_list_new
1587
- time.sleep(0.3)
1588
-
1589
- cache_file = f'{self.config.auto_dir}/shein/sku_price/sku_price_{self.store_username}.json'
1590
- dict_sku_price = read_dict_from_file(cache_file)
1591
-
1592
- cache_file2 = f'{self.config.auto_dir}/shein/dict/dict_skc_week_trend_{self.store_username}.json'
1593
- DictSkcWeekTrend = read_dict_from_file(cache_file2)
1594
-
1595
- cache_file3 = f'{self.config.auto_dir}/shein/dict/product_list_{self.store_username}.json'
1596
- DictSpuInfo = read_dict_from_file(cache_file3)
1597
-
1598
- cache_file = f'{self.config.auto_dir}/shein/dict/activity_price_{self.store_name}.json'
1599
- dictActivityPrice = read_dict_from_file(cache_file)
1600
-
1601
- product_sku_list = [
1602
- [
1603
- '店铺名称', '商品信息', 'SKC图片', 'SKU图片', 'SKU', 'SKU货号', '在售天数', '库存(模式/本地/在途/希音)',
1604
- '今天销量', '今日订单数', # 9
1605
- '远7天销量', '远7天订单数', '近7天销量', '近7天订单数', '周销增量', '远30天销量', '远30天订单数',
1606
- '近30天销量', '近30天订单数', '月销增量', '总销量', # 11
1607
- '申报价', '成本价', '毛利润', '毛利率', '近7天利润', '近30天利润', # 6
1608
- 'SPU', 'SKC', 'SKC货号', '近7天SKU销量/SKC销量/SKC曝光', 'SKC点击率/SKC转化率', '自主参与活动', '商品标题', '叶子类目', # 5
1609
- 'SKC近7天销量', 'SKC近7天曝光人数', 'SKC近7天商详访客', 'SKC近7天点击率', 'SKC近7天支付人数',
1610
- 'SKC近7天支付率', 'SKC近7天评论数'
1611
- ]
1612
- ]
1613
-
1614
- date_60_days_ago = TimeUtils.get_past_nth_day(60, None, '%Y-%m-%d')
1615
- log('-60', date_60_days_ago)
1616
- date_7_days_ago = TimeUtils.get_past_nth_day(7, None, '%Y-%m-%d')
1617
- log('-7', date_7_days_ago)
1618
- date_1_days_ago = TimeUtils.get_past_nth_day(1, None, '%Y-%m-%d')
1619
- log('-1', date_1_days_ago)
1620
-
1621
- count = 0
1622
- for spu_info in spu_list:
1623
- count += 1
1624
- # if count > 10:
1625
- # break
1626
- spu = spu_info['spu']
1627
- skc = str(spu_info['skc'])
1628
- # if not shein_db.exists_sales_1_days_ago(skc):
1629
- # log(f'未查到昨天销量: {skc}')
1630
- self.get_skc_sales(skc, date_60_days_ago, date_1_days_ago)
1631
- skcCode = spu_info['supplierCode']
1632
- product_name = DictSpuInfo[spu]['product_name_en']
1633
- category_name = spu_info['categoryName']
1634
- shelfDays = spu_info['shelfDays']
1635
- shelf_status = DictSpuInfo[spu]['shelf_status']
1636
- dictStatus = {
1637
- 'WAIT_SHELF': "待上架",
1638
- 'ON_SHELF' : "已上架",
1639
- 'SOLD_OUT' : "已售罄",
1640
- 'OUT_SHELF' : "已下架"
1641
- }
1642
- status_cn = dictStatus[shelf_status]
1643
- good_level = spu_info['goodsLevel']['name']
1644
- sale_model = spu_info['saleModel']['name']
1645
-
1646
- for sku_info in spu_info['skuList']:
1647
- sku = sku_info['skuCode']
1648
- skuExtCode = str(sku_info['supplierSku'])
1649
- if sku == '合计':
1650
- continue
1651
-
1652
- # 获取基础数据
1653
- stock = self.bridge.get_sku_stock(skuExtCode, source)
1654
- cost_price = self.bridge.get_sku_cost(skuExtCode, source)
1655
- sku_img = self.bridge.get_sku_img(skuExtCode, source)
1656
-
1657
- # 计算库存相关数据
1658
- shein_stock = sku_info['stock']
1659
- transit = sku_info['transit'] # 在途
1660
- real_transit = transit + sku_info['stayShelf'] - sku_info['transitSale']
1661
-
1662
- # 获取销量数据
1663
- week_sales = get_last_week_sales(sku)
1664
- week_sales2 = get_near_week_sales(sku)
1665
- month_sales = get_last_month_sales(sku)
1666
- month_sales2 = get_near_month_sales(sku)
1667
-
1668
- # 获取SKC趋势数据
1669
- key = str(skc)
1670
- skc_trend = DictSkcWeekTrend.get(key, {})
1671
-
1672
- # 使用append组装数据
1673
- sku_item = []
1674
-
1675
- # 店铺名称
1676
- sku_item.append(f'{self.store_name}\n({status_cn})\n{good_level}\n{date_7_days_ago}\n{date_1_days_ago}')
1677
-
1678
- # 商品信息
1679
- product_info = f"SPU: {spu}\nSKC: {skc}\nSKC货号: {skcCode}\n类目: {category_name}\n在售天数: {shelfDays}"
1680
- sku_item.append(product_info)
1681
-
1682
- # SKC图片
1683
- sku_item.append(spu_info['picUrl'])
1684
-
1685
- # SKU图片
1686
- sku_item.append(sku_img)
1687
-
1688
- # SKU基本信息
1689
- sku_item.append(sku) # SKU
1690
- sku_item.append(f"{sku_info['supplierSku']}") # SKU货号
1691
- sku_item.append(shelfDays) # 在售天数
1692
-
1693
- # 库存信息
1694
- sku_item.append(f'{sale_model}\n{stock}/{real_transit}/{shein_stock}')
1695
-
1696
- # 今日销量数据
1697
- sku_item.append(sku_info['totalSaleVolume']) # 今日销量
1698
- sku_item.append(sku_info['orderCnt']) # 今日订单数
1699
-
1700
- # 远7天销量数据
1701
- sku_item.append(week_sales[0]) # 远7日销量
1702
- sku_item.append(week_sales[1]) # 远7日订单数
1703
-
1704
- # 近7天销量数据
1705
- sku_item.append(week_sales2[0]) # 近7日销量
1706
- sku_item.append(week_sales2[1]) # 近7日订单数
1707
- sku_item.append(week_sales2[1] - week_sales2[0]) # 周增销量
1708
-
1709
- # 远30天销量数据
1710
- sku_item.append(month_sales[0]) # 远30日销量
1711
- sku_item.append(month_sales[1]) # 远30日订单数
1712
-
1713
- # 近30天销量数据
1714
- sku_item.append(month_sales2[0]) # 近30日销量
1715
- sku_item.append(month_sales2[1]) # 近30日订单数
1716
- sku_item.append(month_sales2[1] - month_sales2[0]) # 月增销量
1717
-
1718
- # 总销量
1719
- sku_item.append('-')
1720
-
1721
- # 价格相关
1722
- sku_item.append(dict_sku_price[sku]) # 申报价
1723
- sku_item.append(cost_price) # 成本价
1724
- sku_item.append('') # 毛利润
1725
- sku_item.append('') # 毛利率
1726
- sku_item.append('') # 近7天利润
1727
- sku_item.append('') # 近30天利润
1728
-
1729
- # 商品标识
1730
- sku_item.append(spu) # SPU
1731
- sku_item.append(skc) # SKC
1732
- sku_item.append(spu_info['supplierCode']) # SKC货号
1733
-
1734
- sale_num_list, sale_data_list = self.get_skc_week_sale_list(spu, skc, sku)
1735
- sku_item.append("\n".join(sale_num_list))
1736
- sku_item.append("\n".join(sale_data_list))
1737
- sku_item.append(self.get_skc_activity_label(skc, sku, dictActivityPrice))
1738
-
1739
- sku_item.append(product_name) # 商品标题
1740
- sku_item.append(category_name) # 叶子类目
1741
-
1742
- # SKC趋势数据
1743
- sku_item.append(skc_trend.get('saleCnt', 0)) # SKC近7天销量
1744
- sku_item.append(skc_trend.get('epsUvIdx', 0)) # SKC近7天曝光人数
1745
- sku_item.append(skc_trend.get('goodsUvIdx', 0)) # SKC近7天商详访客
1746
- sku_item.append(skc_trend.get('epsGdsCtrIdx', 0)) # SKC近7天点击率
1747
- sku_item.append(skc_trend.get('payUvIdx', 0)) # SKC近7天支付人数
1748
- sku_item.append(skc_trend.get('gdsPayCtrIdx', 0)) # SKC近7天支付率
1749
- sku_item.append(skc_trend.get('totalCommentCnt', 0)) # 评论数
1750
-
1751
- product_sku_list.append(sku_item)
1752
-
1753
- cache_file = f'{self.config.auto_dir}/shein/cache/week_sales_{TimeUtils.today_date()}.json'
1754
- write_dict_to_file_ex(cache_file, {self.store_name: product_sku_list}, [self.store_name])
1755
-
1756
- return product_sku_list
1757
-
1758
- # 获取一个skc一周内的销售趋势(商品明细中的)
1759
- def get_dict_skc_week_trend_v2(self, spu, skc):
1760
- dt = self.get_dt_time()
1761
-
1762
- date_7_days_ago = TimeUtils.get_past_nth_day(7, None, '%Y%m%d')
1763
- log('-7', date_7_days_ago)
1764
- date_1_days_ago = TimeUtils.get_past_nth_day(1, None, '%Y%m%d')
1765
- log('-1', date_1_days_ago)
1766
-
1767
- cache_file = f'{self.config.auto_dir}/shein/dict/dict_skc_week_trend_{skc}_{date_7_days_ago}_{date_1_days_ago}.json'
1768
- if datetime.now().hour >= 9:
1769
- DictSkc = read_dict_from_file(cache_file)
1770
- else:
1771
- DictSkc = read_dict_from_file(cache_file, 1800)
1772
- if len(DictSkc) > 0:
1773
- return DictSkc
1774
-
1775
- url = f"https://sso.geiwohuo.com/sbn/new_goods/get_skc_diagnose_trend"
1776
- payload = {
1777
- "areaCd" : "cn",
1778
- "countrySite": [
1779
- "shein-all"
1780
- ],
1781
- "dt" : dt,
1782
- "endDate" : date_1_days_ago,
1783
- "spu" : [spu],
1784
- "skc" : [skc],
1785
- "startDate" : date_7_days_ago,
1786
- }
1787
- response_text = fetch(self.web_page, url, payload)
1788
- error_code = response_text.get('code')
1789
- if str(error_code) != '0':
1790
- raise send_exception(json.dumps(response_text, ensure_ascii=False))
1791
-
1792
- data_list = response_text['info']
1793
- DictSkc = {}
1794
- for date_item in data_list:
1795
- dataDate = date_item['dataDate']
1796
- # epsUvIdx = date_item['epsUvIdx']
1797
- # saleCnt = date_item['saleCnt']
1798
- DictSkc[dataDate] = date_item
1799
-
1800
- log('len(DictSkc)', len(DictSkc))
1801
- write_dict_to_file(cache_file, DictSkc)
1802
- return DictSkc
1803
-
1804
- def get_skc_week_sale_list(self, spu, skc, sku):
1805
- dict_skc = self.get_dict_skc_week_trend_v2(spu, skc)
1806
- date_list = TimeUtils.get_past_7_days_list()
1807
- first_day, last_day = TimeUtils.get_past_7_days_range()
1808
- cache_file = f'{self.config.auto_dir}/shein/cache/{skc}_{first_day}_{last_day}.json'
1809
- DictSkuSalesDate = read_dict_from_file(cache_file)
1810
- sales_detail = []
1811
- for date in date_list:
1812
- sales_num = DictSkuSalesDate.get(date, {}).get(sku, {}).get("hisActualValue", 0)
1813
- sales_num = sales_num if sales_num is not None else 0
1814
-
1815
- saleCnt = get_safe_value(dict_skc.get(date, {}), 'saleCnt', 0)
1816
- epsUvIdx = get_safe_value(dict_skc.get(date, {}), 'epsUvIdx', 0)
1817
-
1818
- sales_detail.append(f'{date}({TimeUtils.get_weekday_name(date)}): {sales_num}/{saleCnt}/{epsUvIdx}')
1819
-
1820
- sales_data = []
1821
- for date in date_list:
1822
- goodsUvIdx = get_safe_value(dict_skc.get(date, {}), 'goodsUvIdx', 0) # 商详访客
1823
- epsGdsCtrIdx = get_safe_value(dict_skc.get(date, {}), 'epsGdsCtrIdx', 0) # 点击率
1824
-
1825
- payUvIdx = get_safe_value(dict_skc.get(date, {}), 'payUvIdx', 0) # 支付人数
1826
- gdsPayCtrIdx = get_safe_value(dict_skc.get(date, {}), 'gdsPayCtrIdx', 0) # 转化率
1827
-
1828
- sales_data.append(f'{date}({TimeUtils.get_weekday_name(date)}): {epsGdsCtrIdx:.2%}({goodsUvIdx})/{gdsPayCtrIdx:.2%}({payUvIdx})')
1829
-
1830
- return sales_detail, sales_data
1831
-
1832
- def get_activity_price(self, activity_dict, sku, activity_name, dateBegin, dateEnd):
1833
- key = f'{sku}_{dateBegin}_{dateEnd}_{activity_name}'
1834
- price_info = activity_dict.get(key, ['-', '-'])
1835
- return f'活动价:¥{price_info[0]}, 活动库存:{price_info[1]}'
1836
-
1837
- def get_skc_activity_label(self, skc, sku, dict_activity_price=None):
1838
- cache_file = f'{self.config.auto_dir}/shein/activity_label/activity_label_{self.store_username}.json'
1839
- dict_label = read_dict_from_file(cache_file)
1840
- operateLabelList = dict_label[skc]['operateLabelList']
1841
- activityList = []
1842
- activityList2 = []
1843
- for item in operateLabelList:
1844
- if item['name'] == '活动中':
1845
- activityList.extend(item.get('activityList', []))
1846
- if item['name'] == '即将开始':
1847
- activityList2.extend(item.get('activityList', []))
1848
-
1849
- if activityList:
1850
- activityLabel = '\n'.join([
1851
- f' [{act["date"]}]\n【{self.get_activity_price(dict_activity_price, sku, act["name"], act["dateBegin"], act["dateEnd"])}】{act["name"]}\n'
1852
- for act in activityList])
1853
- else:
1854
- activityLabel = '无'
1855
- if activityList2:
1856
- activityLabel2 = '\n'.join([
1857
- f' [{act["date"]}]\n【{self.get_activity_price(dict_activity_price, sku, act["name"], act["dateBegin"], act["dateEnd"])}】{act["name"]}\n'
1858
- for act in activityList2])
1859
- else:
1860
- activityLabel2 = '无'
1861
- return f'活动中:\n{activityLabel}\n即将开始:\n{activityLabel2}'
1862
-
1863
- # 获取商品包含sku销量的列表
1864
- # mode = 1.备货建议 2.已上架 3.昨日上架 4.昨日出单
1865
- # 5.采购-缺货要补货 (有现货建议 建议采购为正 有销量)
1866
- # 6.运营采购-滞销清库存 (无现货建议 建议采购为负 30天外 无销量)
1867
- # 7.运营-新品上架需要优化 (无现货建议 建议采购为负 上架15天内)
1868
- # 8.运营-潜在滞销款 (无现货建议 30天外 有销量)
1869
- # 9.运营-潜力热销款 (有现货建议 30天内 有销量)
1870
- # 10.运营-热销款 (有现货建议 30天外 有销量)
1871
- def get_bak_advice(self, mode=1, skcs=None, source='mb'):
1872
- log(f'获取备货信息商品列表 做成字典')
1873
- if skcs == None or len(skcs) == 0:
1874
- # if mode == 3:
1875
- # skcs = "sh2405133614611175" # 这是一个不存在的skc
1876
- # else:
1877
- skcs = ""
1878
- else:
1879
- skcs = ",".join(skcs)
1880
-
1881
- url = "https://sso.geiwohuo.com/idms/goods-skc/list"
1882
- pageNumber = 1
1883
- pageSize = 100
1884
- dictPayload = {
1885
- "pageNumber" : pageNumber,
1886
- "pageSize" : pageSize,
1887
- "supplierCodes" : "",
1888
- "skcs" : skcs,
1889
- "spu" : "",
1890
- "c7dSaleCntBegin" : "",
1891
- "c7dSaleCntEnd" : "",
1892
- "goodsLevelIdList" : [10, 107, 61, 90, 87, 237, 220, 219, 88, 75, 62, 227, 12, 230, 80, 58, 224, 97],
1893
- "supplyStatus" : "",
1894
- "shelfStatus" : "",
1895
- "categoryIdList" : [],
1896
- "skcStockBegin" : "",
1897
- "skcStockEnd" : "",
1898
- "skuStockBegin" : "",
1899
- "skuStockEnd" : "",
1900
- "skcSaleDaysBegin" : "",
1901
- "skcSaleDaysEnd" : "",
1902
- "skuSaleDaysBegin" : "",
1903
- "skuSaleDaysEnd" : "",
1904
- "planUrgentCountBegin" : "",
1905
- "planUrgentCountEnd" : "",
1906
- "skcAvailableOrderBegin": "",
1907
- "skcAvailableOrderEnd" : "",
1908
- "skuAvailableOrderBegin": "",
1909
- "skuAvailableOrderEnd" : "",
1910
- "shelfDateBegin" : "",
1911
- "shelfDateEnd" : "",
1912
- "stockWarnStatusList" : [],
1913
- "labelFakeIdList" : [],
1914
- "sheinSaleByInventory" : "",
1915
- "tspIdList" : [],
1916
- "adviceStatus" : [],
1917
- "sortBy7dSaleCnt" : 2,
1918
- "goodsLevelFakeIdList" : [1, 2, 3, 8, 14, 15, 4, 11]
1919
- }
1920
- payload = dictPayload
1921
- response_text = fetch(self.web_page, url, payload)
1922
- error_code = response_text.get('code')
1923
- if str(error_code) != '0':
1924
- raise send_exception(json.dumps(response_text, ensure_ascii=False))
1925
-
1926
- spu_list = response_text['info']['list']
1927
-
1928
- skc_list = [item['skc'] for item in spu_list]
1929
- self.get_activity_label(skc_list)
1930
- self.get_preemption_list(skc_list)
1931
- self.get_sku_price_v2(skc_list)
1932
- self.get_stock_advice(skc_list)
1933
-
1934
- total = response_text['info']['count']
1935
- totalPage = math.ceil(total / pageSize)
1936
- for page in range(2, totalPage + 1):
1937
- log(f'获取备货信息商品列表 第{page}/{totalPage}页')
1938
- dictPayload['pageNumber'] = page
1939
- payload = dictPayload
1940
- response_text = fetch(self.web_page, url, payload)
1941
- spu_list_new = response_text['info']['list']
1942
-
1943
- skc_list = [item['skc'] for item in spu_list_new]
1944
- self.get_activity_label(skc_list)
1945
- self.get_preemption_list(skc_list)
1946
- self.get_sku_price_v2(skc_list)
1947
- self.get_stock_advice(skc_list)
1948
-
1949
- spu_list += spu_list_new
1950
- time.sleep(0.3)
1951
-
1952
- cache_file = f'{self.config.auto_dir}/shein/dict/activity_price_{self.store_name}.json'
1953
- dictActivityPrice = read_dict_from_file(cache_file)
1954
- # cache_file = f'{self.config.auto_dir}/shein/dict/product_list_{self.store_username}.json'
1955
- # DictSpuInfo = read_dict_from_file(cache_file, 5)
1956
- cache_file = f'{self.config.auto_dir}/shein/preemption_num/preemption_num_{self.store_username}.json'
1957
- dict_preemption_num = read_dict_from_file(cache_file)
1958
- cache_file = f'{self.config.auto_dir}/shein/vmi_spot_advice/spot_advice_{self.store_username}.json'
1959
- dict_advice = read_dict_from_file(cache_file)
1960
- cache_file = f'{self.config.auto_dir}/shein/sku_price/sku_price_{self.store_username}.json'
1961
- dict_sku_price = read_dict_from_file(cache_file)
1962
- date_list = TimeUtils.get_past_7_days_list()
1963
- if mode in [2, 5, 6, 7, 8, 9, 10]:
1964
- excel_data = [[
1965
- '店铺名称', 'SKC图片', 'SKU图片', '商品信息', '建议现货数量', '现有库存数量', '已采购数量', '预测日销',
1966
- '本地和采购可售天数', '生产天数', '建议采购', '产品起定量',
1967
- '备货周期()', '备货建议', '近7天SKU销量/SKC销量/SKC曝光', 'SKC点击率/SKC转化率', '自主参与活动',
1968
- 'SKC',
1969
- "SKU"
1970
- ]]
1971
- else:
1972
- excel_data = [[
1973
- '店铺名称', 'SKC图片', 'SKU图片', '商品信息', '备货建议', '近7天SKU销量/SKC销量/SKC曝光',
1974
- 'SKC点击率/SKC转化率', '自主参与活动', 'SKC', "SKU"
1975
- ]]
1976
- for spu_info in spu_list:
1977
- spu = str(spu_info['spu'])
1978
- skc = str(spu_info['skc'])
1979
-
1980
- status_cn = spu_info['shelfStatus']['name']
1981
- if status_cn != '已上架':
1982
- continue
1983
-
1984
- # shelf_status = DictSpuInfo.get(spu, {}).get('shelf_status', '')
1985
- # if mode != 1:
1986
- # if shelf_status != 'ON_SHELF' and shelf_status != 'SOLD_OUT':
1987
- # log('跳过', skc, shelf_status)
1988
- # continue
1989
-
1990
- # if mode in [5, 6, 7, 8, 9, 10] and shelf_status == 'SOLD_OUT':
1991
- # continue
1992
- #
1993
- # dictStatus = {
1994
- # 'WAIT_SHELF': "待上架",
1995
- # 'ON_SHELF': "已上架",
1996
- # 'SOLD_OUT': "已售罄",
1997
- # 'OUT_SHELF': "已下架"
1998
- # }
1999
- # status_cn = dictStatus.get(shelf_status, '-')
2000
-
2001
- sale_model = spu_info['saleModel']['name']
2002
- goods_level = spu_info['goodsLevel']['name']
2003
- goods_label = [label["name"] for label in spu_info['goodsLabelList']]
2004
- skc_img = spu_info['picUrl']
2005
- shelfDate = spu_info['shelfDate']
2006
- shelfDays = spu_info['shelfDays']
2007
- categoryName = spu_info['categoryName']
2008
-
2009
- if mode in [3] and shelfDays != 1:
2010
- continue
2011
-
2012
- DictSkuSalesDate = self.get_skc_week_actual_sales(skc)
2013
-
2014
- for sku_info in spu_info['skuList']:
2015
- row_item = []
2016
- attr = sku_info['attr']
2017
- if attr == '合计':
2018
- continue
2019
- predictDaySales = sku_info['predictDaySales']
2020
- availableOrderCount = sku_info['availableOrderCount']
2021
- if mode == 1:
2022
- if availableOrderCount is None or availableOrderCount <= 0:
2023
- log('跳过', skc, availableOrderCount)
2024
- continue
2025
-
2026
- row_item.append(f'{self.store_name}\n({status_cn})\n{goods_level}\n{",".join(goods_label)}')
2027
- row_item.append(skc_img)
2028
- sku = sku_info['skuCode']
2029
- skuExtCode = str(sku_info['supplierSku'])
2030
- sku_img = self.bridge.get_sku_img(skuExtCode, source)
2031
- row_item.append(sku_img)
2032
-
2033
- transit = sku_info['transit'] # 在途
2034
-
2035
- stock = self.bridge.get_sku_stock(skuExtCode, source)
2036
- cost_price = self.bridge.get_sku_cost(skuExtCode, source)
2037
-
2038
- supplyPrice = dict_sku_price[sku]
2039
- shein_stock = sku_info['stock']
2040
- if cost_price == '-':
2041
- profit = '-'
2042
- else:
2043
- profit = f'{float(supplyPrice) - float(cost_price):.2f}'
2044
-
2045
- min_spot_advice = dict_advice.get(skc, {}).get(sku, {}).get('minSpotAdvice', 0)
2046
- max_spot_advice = dict_advice.get(skc, {}).get(sku, {}).get('maxSpotAdvice', 0)
2047
- stock_advice = f'{min_spot_advice}~{max_spot_advice}'
2048
- log('stock_advice', stock_advice)
2049
- # 建议现货数量
2050
- advice_stock_number = round((min_spot_advice + max_spot_advice) / 4)
2051
-
2052
- # 有现货建议
2053
- if mode in [5, 9, 10] and advice_stock_number == 0:
2054
- continue
2055
-
2056
- # 无现货建议
2057
- if mode in [6, 7, 8] and advice_stock_number > 0:
2058
- continue
2059
-
2060
- stockSaleDays = sku_info['stockSaleDays']
2061
-
2062
- product_info = (
2063
- f'SPU: {spu}\n'
2064
- f'SKC: {skc}\n'
2065
- f'SKU货号: {skuExtCode}\n'
2066
- f'属性集: {attr}\n'
2067
- f'商品分类: {categoryName}\n'
2068
- f'上架日期: {shelfDate}\n'
2069
- f'上架天数: {shelfDays}\n'
2070
- f'库存可售天数/现货建议: {stockSaleDays}/{stock_advice}\n'
2071
- )
2072
- row_item.append(product_info)
2073
-
2074
- # 建议采购数量逻辑
2075
- try:
2076
- # 尝试将字符串数字转换为 float,再转为 int(如有必要)
2077
- current_stock = float(stock)
2078
- advice_purchase_number = advice_stock_number - int(current_stock)
2079
-
2080
- # 建议采购为正
2081
- if (mode == 5 and advice_purchase_number <= 0):
2082
- continue
2083
-
2084
- except (ValueError, TypeError):
2085
- # 无法转换为数值时
2086
- advice_purchase_number = '-'
2087
-
2088
- if mode in [2, 5, 6, 7, 8, 9, 10]:
2089
- row_item.append(advice_stock_number)
2090
- row_item.append(stock)
2091
-
2092
- row_item.append(0)
2093
- row_item.append(predictDaySales)
2094
- row_item.append(0)
2095
- row_item.append(7)
2096
-
2097
- row_item.append(advice_purchase_number)
2098
- row_item.append(0) # 产品起定量
2099
- row_item.append(0) # 备货周期(天)
2100
-
2101
- adviceOrderCount = sku_info['adviceOrderCount'] if sku_info['adviceOrderCount'] is not None else '-'
2102
- if sku_info['autoOrderStatus'] is not None:
2103
- autoOrderStatus = ['-', '是', '否'][sku_info['autoOrderStatus']] if sku_info[
2104
- 'adviceOrderCount'] is not None else '-'
2105
- else:
2106
- autoOrderStatus = '-'
2107
- orderCount = sku_info['orderCount'] # 已下单数
2108
- c7dSaleCnt = sku_info['c7dSaleCnt']
2109
- c30dSaleCnt = sku_info['c30dSaleCnt']
2110
- orderCnt = sku_info['orderCnt']
2111
- totalSaleVolume = sku_info['totalSaleVolume']
2112
- planUrgentCount = sku_info['planUrgentCount']
2113
- preemptionCount = dict_preemption_num[skc][sku]
2114
- predictDaySales = sku_info['predictDaySales']
2115
- goodsDate = sku_info['goodsDate']
2116
- stockDays = sku_info['stockDays']
2117
-
2118
- real_transit = transit + sku_info['stayShelf'] - sku_info['transitSale']
2119
-
2120
- sales_info = (
2121
- f'近7天/30天销量: {c7dSaleCnt}/{c30dSaleCnt}\n'
2122
- f'当天销量/购买单数: {totalSaleVolume}/{orderCnt}\n'
2123
- f'预测日销/下单参数: {predictDaySales}/{goodsDate}+{stockDays}\n'
2124
- f'预占数/预计急采数: {preemptionCount}/{planUrgentCount}\n'
2125
- f'建议下单/已下单数: {adviceOrderCount}/{orderCount}\n'
2126
- f'拟下单数/自动下单: {availableOrderCount}/{autoOrderStatus}\n'
2127
- f'模式/本地/在途/希音: {sale_model[:2]}/{stock}/{real_transit}/{shein_stock}\n'
2128
- f'成本/核价/利润: ¥{cost_price}/¥{supplyPrice}/¥{profit}\n'
2129
- )
2130
-
2131
- row_item.append(sales_info)
2132
-
2133
- flag_yesterday = 0
2134
- sales7cn = 0
2135
- for date in date_list:
2136
- sales_num = DictSkuSalesDate.get(date, {}).get(sku, {}).get("hisActualValue", 0)
2137
- sales_num = sales_num if sales_num is not None else 0
2138
- sales7cn += sales_num
2139
- if TimeUtils.is_yesterday_date(date) and sales_num == 0:
2140
- flag_yesterday = 1
2141
-
2142
- if mode == 4 and flag_yesterday:
2143
- continue
2144
-
2145
- # 过滤掉未建立马帮信息的
2146
- if mode in [5, 6, 7, 8, 9, 10] and advice_purchase_number == '-':
2147
- continue
2148
-
2149
- # 建议采购为正
2150
- if mode in [5] and advice_purchase_number < 0:
2151
- continue
2152
-
2153
- # 建议采购为负
2154
- if mode in [6, 7] and advice_purchase_number >= 0:
2155
- continue
2156
-
2157
- # 30内
2158
- if mode in [9] and shelfDays > 31:
2159
- continue
2160
-
2161
- # 15天内
2162
- if mode in [7] and shelfDays > 15:
2163
- continue
2164
-
2165
- # 30外
2166
- if mode in [6, 8, 10] and shelfDays < 31:
2167
- continue
2168
-
2169
- # 有销量
2170
- if mode in [5, 8, 9, 10] and sales7cn == 0:
2171
- continue
2172
-
2173
- # 无销量
2174
- if mode in [6] and sales7cn > 0:
2175
- continue
2176
-
2177
- sale_num_list, sale_data_list = self.get_skc_week_sale_list(spu, skc, sku)
2178
- row_item.append("\n".join(sale_num_list))
2179
- row_item.append("\n".join(sale_data_list))
2180
- row_item.append(self.get_skc_activity_label(skc, sku, dictActivityPrice))
2181
- row_item.append(skc)
2182
- row_item.append(sku)
2183
- excel_data.append(row_item)
2184
-
2185
- cache_file = f'{self.config.auto_dir}/shein/cache/bak_advice_{mode}_{TimeUtils.today_date()}.json'
2186
- write_dict_to_file_ex(cache_file, {self.store_name: excel_data}, {self.store_name})
2187
-
2188
- cache_file = f'{self.config.auto_dir}/shein/cache/bak_advice_notify_{mode}_{TimeUtils.today_date()}.json'
2189
- NotifyItem = [self.store_name, len(excel_data[1:])]
2190
- write_dict_to_file_ex(cache_file, {self.store_name: NotifyItem}, {self.store_name})
2191
-
2192
- return excel_data
1
+ from .fun_file import read_dict_from_file, write_dict_to_file, read_dict_from_file_ex, write_dict_to_file_ex
2
+ from .fun_base import log, send_exception, md5_string, get_safe_value
3
+ from .fun_web import fetch, full_screen_shot
4
+ from .time_utils import TimeUtils
5
+ from .wxwork import WxWorkBot
6
+
7
+ from .shein_sqlite import insert_sales, get_last_week_sales, get_near_week_sales, get_near_month_sales, get_last_month_sales
8
+
9
+ import math
10
+ import time
11
+ import json
12
+ from datetime import datetime
13
+ from playwright.sync_api import Page
14
+
15
+ from .shein_daily_report_model import SheinStoreSalesDetailManager
16
+
17
+ class SheinLib:
18
+
19
+ def __init__(self, config, bridge, web_page: Page, store_username, store_name):
20
+ self.config = config
21
+ self.bridge = bridge
22
+ self.store_username = store_username
23
+ self.store_name = store_name
24
+ self.web_page = web_page
25
+ self.dt = None
26
+ self.DictQueryTime = {}
27
+
28
+ self.deal_auth()
29
+
30
+ # 处理鉴权
31
+ def deal_auth(self):
32
+ web_page = self.web_page
33
+
34
+ # 定义最大重试次数
35
+ MAX_RETRIES = 5
36
+ retries = 0
37
+ wait_count = 0
38
+ is_send = False
39
+
40
+ # close_modal(web_page) # 不能开启 需要勾选协议弹窗
41
+
42
+ while retries < MAX_RETRIES:
43
+ try:
44
+
45
+ retries += 1
46
+
47
+ while not web_page.locator('//div[contains(text(),"商家后台")]').nth(1).is_visible():
48
+
49
+ if web_page.locator('xpath=//div[@id="container" and @alita-name="gmpsso"]//button[@type="button" and @id]').nth(0).is_visible():
50
+ log("鉴权确定按钮可见 点击'确定'按钮", self.store_username, self.store_name)
51
+ web_page.locator('xpath=//div[@id="container" and @alita-name="gmpsso"]//button[@type="button" and @id]').nth(0).click()
52
+ web_page.wait_for_load_state("load")
53
+ web_page.wait_for_timeout(1000)
54
+
55
+ while web_page.locator('//div[text()="验证码"]').is_visible():
56
+ log(f'等待输入验证码: {wait_count}', self.store_username, self.store_name)
57
+ if not is_send:
58
+ is_send = True
59
+ img_path = full_screen_shot(web_page, self.config)
60
+ WxWorkBot(self.config.wxwork_bot_exception).send_img(img_path)
61
+ WxWorkBot(self.config.wxwork_bot_exception).send_text(f'{self.store_username},{self.store_name} 需要登录验证码')
62
+ time.sleep(5)
63
+ wait_count += 1
64
+
65
+ if web_page.locator('//input[@name="username"]').is_visible():
66
+ log("用户名输入框可见 等待5秒点击'登录'按钮", self.store_username, self.store_name)
67
+ web_page.wait_for_timeout(5000)
68
+ log('点击"登录"', self.store_username, self.store_name)
69
+ web_page.locator('//button[contains(@class,"login_btn")]').click()
70
+ web_page.wait_for_load_state("load")
71
+ log('再延时5秒', self.store_username, self.store_name)
72
+ web_page.wait_for_timeout(5000)
73
+
74
+ if web_page.locator('//span[contains(text(),"商品管理")]').nth(1).is_visible():
75
+ log('商品管理菜单可见 退出鉴权处理', self.store_username, self.store_name)
76
+ return
77
+
78
+ log('商家后台不可见', web_page.title(), web_page.url, self.store_username, self.store_name)
79
+ web_page.wait_for_load_state("load")
80
+ web_page.wait_for_timeout(1000)
81
+
82
+ if 'SHEIN全球商家中心' in web_page.title() and 'https://sso.geiwohuo.com/#/home' in web_page.url:
83
+ log('SHEIN全球商家中心 中断循环', self.store_username, self.store_name)
84
+ break
85
+
86
+ if '后台首页' in web_page.title() and 'https://sso.geiwohuo.com/#/home' in web_page.url:
87
+ log('后台首页 中断循环', self.store_username, self.store_name)
88
+ break
89
+
90
+ if '商家后台' in web_page.title() and 'https://sso.geiwohuo.com/#/home' in web_page.url:
91
+ log('后台首页 中断循环', self.store_username, self.store_name)
92
+ break
93
+
94
+ if 'mrs.biz.sheincorp.cn' in web_page.url and '商家后台' in web_page.title():
95
+ web_page.goto('https://sso.geiwohuo.com/#/home')
96
+ web_page.wait_for_load_state("load")
97
+ web_page.wait_for_timeout(3000)
98
+
99
+ if web_page.locator('//h1[contains(text(),"鉴权")]').is_visible():
100
+ log('检测到鉴权 刷新页面', self.store_username, self.store_name)
101
+ web_page.reload()
102
+ web_page.wait_for_load_state('load')
103
+ web_page.wait_for_timeout(3000)
104
+ web_page.reload()
105
+ web_page.wait_for_timeout(3000)
106
+
107
+ if web_page.title() == 'SHEIN':
108
+ web_page.goto('https://sso.geiwohuo.com/#/home')
109
+ web_page.wait_for_load_state("load")
110
+ web_page.wait_for_timeout(3000)
111
+
112
+ except Exception as e:
113
+ log(f"错误发生: {e}, 重试中...({self.store_username}, {self.store_name})")
114
+ retries += 1
115
+ if retries >= MAX_RETRIES:
116
+ log(f"达到最大重试次数,停止尝试({self.store_username}, {self.store_name})")
117
+ break
118
+ time.sleep(2) # 错误时等待2秒后重试
119
+
120
+ log('鉴权处理结束')
121
+
122
+ # 获取希音退供明细 和 台账明细一个接口
123
+ def get_back_list(self, source='mb'):
124
+ page_num = 1
125
+ page_size = 200 # 列表最多返回200条数据 大了没有用
126
+
127
+ first_day, last_day = TimeUtils.get_last_month_range()
128
+
129
+ cache_file = f'{self.config.auto_dir}/shein/cache/return_detail_{self.store_username}_{first_day}_{last_day}.json'
130
+ list_item = read_dict_from_file(cache_file, 3600 * 24 * 20)
131
+ if len(list_item) > 0:
132
+ return list_item
133
+
134
+ url = f"https://sso.geiwohuo.com/mils/changeDetail/page"
135
+ payload = {
136
+ "displayChangeTypeList": ["10"],
137
+ "addTimeStart" : f"{first_day} 00:00:00",
138
+ "addTimeEnd" : f"{last_day} 23:59:59",
139
+ "pageNumber" : page_num,
140
+ "pageSize" : page_size,
141
+ "changeTypeIndex" : "2"
142
+ }
143
+ response_text = fetch(self.web_page, url, payload)
144
+ error_code = response_text.get('code')
145
+ if str(error_code) != '0':
146
+ raise send_exception(json.dumps(response_text, ensure_ascii=False))
147
+ list_item = response_text['info']['data']['list']
148
+ total = response_text['info']['data']['count']
149
+ totalPage = math.ceil(total / page_size)
150
+
151
+ for page in range(2, totalPage + 1):
152
+ log(f'获取台账明细列表 第{page}/{totalPage}页')
153
+ payload['pageNumber'] = page
154
+ response_text = fetch(self.web_page, url, payload)
155
+ spu_list_new = response_text['info']['data']['list']
156
+ list_item += spu_list_new
157
+ time.sleep(0.1)
158
+
159
+ # cost_price =
160
+ for item in list_item:
161
+ supplierSku = item['supplierSku']
162
+ item['cost_price'] = self.bridge.get_sku_cost(supplierSku, source)
163
+ item['sku_img'] = self.bridge.get_sku_img(supplierSku, source)
164
+
165
+ write_dict_to_file(cache_file, list_item)
166
+
167
+ return list_item
168
+
169
+ # 不结算列表
170
+ def get_no_settlement_list(self, source='mb'):
171
+ page_num = 1
172
+ page_size = 200 # 列表最多返回200条数据 大了没有用
173
+
174
+ first_day, last_day = TimeUtils.get_last_month_range()
175
+
176
+ cache_file = f'{self.config.auto_dir}/shein/cache/no_settlement_{self.store_username}_{first_day}_{last_day}.json'
177
+ list_item = read_dict_from_file(cache_file, 3600 * 24 * 20)
178
+ if len(list_item) > 0:
179
+ return list_item
180
+
181
+ url = f"https://sso.geiwohuo.com/mils/changeDetail/page"
182
+ payload = {
183
+ "addTimeStart" : f"{first_day} 00:00:00",
184
+ "addTimeEnd" : f"{last_day} 23:59:59",
185
+ "pageNumber" : page_num,
186
+ "pageSize" : page_size,
187
+ "changeTypeIndex" : "2",
188
+ "settleTypeList" : ["1"], # 不结算
189
+ "displayChangeTypeList": ["6", "7", "9", "10", "11", "12", "13", "16", "18", "19"] # 出库
190
+ }
191
+ response_text = fetch(self.web_page, url, payload)
192
+ error_code = response_text.get('code')
193
+ if str(error_code) != '0':
194
+ raise send_exception(json.dumps(response_text, ensure_ascii=False))
195
+ list_item = response_text['info']['data']['list']
196
+ total = response_text['info']['data']['count']
197
+ totalPage = math.ceil(total / page_size)
198
+
199
+ for page in range(2, totalPage + 1):
200
+ log(f'获取台账明细列表 第{page}/{totalPage}页')
201
+ payload['pageNumber'] = page
202
+ response_text = fetch(self.web_page, url, payload)
203
+ spu_list_new = response_text['info']['data']['list']
204
+ list_item += spu_list_new
205
+ time.sleep(0.1)
206
+
207
+ # cost_price =
208
+ for item in list_item:
209
+ supplierSku = item['supplierSku']
210
+ item['cost_price'] = self.bridge.get_sku_cost(supplierSku, source)
211
+ item['sku_img'] = self.bridge.get_sku_img(supplierSku, source)
212
+
213
+ write_dict_to_file(cache_file, list_item)
214
+
215
+ return list_item
216
+
217
+ def get_ledger_list(self, source='mb'):
218
+ page_num = 1
219
+ page_size = 200 # 列表最多返回200条数据 大了没有用
220
+
221
+ first_day, last_day = TimeUtils.get_last_month_range()
222
+
223
+ cache_file = f'{self.config.auto_dir}/shein/cache/sales_detail_{self.store_username}_{first_day}_{last_day}.json'
224
+ list_item = read_dict_from_file(cache_file, 3600 * 24 * 20)
225
+ if len(list_item) > 0:
226
+ return list_item
227
+
228
+ url = f"https://sso.geiwohuo.com/mils/changeDetail/page"
229
+ payload = {
230
+ "displayChangeTypeList": ["6", "7", "9", "10", "11", "12", "13", "16", "18", "19"], # 出库
231
+ "addTimeStart" : f"{first_day} 00:00:00",
232
+ "addTimeEnd" : f"{last_day} 23:59:59",
233
+ "pageNumber" : page_num,
234
+ "pageSize" : page_size,
235
+ "changeTypeIndex" : "2"
236
+ }
237
+ response_text = fetch(self.web_page, url, payload)
238
+ error_code = response_text.get('code')
239
+ if str(error_code) != '0':
240
+ raise send_exception(json.dumps(response_text, ensure_ascii=False))
241
+ list_item = response_text['info']['data']['list']
242
+ total = response_text['info']['data']['count']
243
+ totalPage = math.ceil(total / page_size)
244
+
245
+ for page in range(2, totalPage + 1):
246
+ log(f'获取台账明细列表 第{page}/{totalPage}页')
247
+ payload['pageNumber'] = page
248
+ response_text = fetch(self.web_page, url, payload)
249
+ spu_list_new = response_text['info']['data']['list']
250
+ list_item += spu_list_new
251
+ time.sleep(0.1)
252
+
253
+ # cost_price =
254
+ for item in list_item:
255
+ supplierSku = item['supplierSku']
256
+ item['cost_price'] = self.bridge.get_sku_cost(supplierSku, source)
257
+ item['sku_img'] = self.bridge.get_sku_img(supplierSku, source)
258
+
259
+ write_dict_to_file(cache_file, list_item)
260
+
261
+ return list_item
262
+
263
+ def get_shein_stock_list(self, source='mb'):
264
+ page_num = 1
265
+ page_size = 200 # 列表最多返回200条数据 大了没有用
266
+
267
+ first_day, last_day = TimeUtils.get_last_month_range()
268
+
269
+ cache_file = f'{self.config.auto_dir}/shein/cache/stock_detail_{self.store_username}_{first_day}_{last_day}.json'
270
+ list_item = read_dict_from_file(cache_file, 3600 * 24 * 20)
271
+ if len(list_item) > 0:
272
+ return list_item
273
+
274
+ url = f"https://sso.geiwohuo.com/mils/report/month/detail/list"
275
+ payload = {
276
+ "reportDateStart": first_day,
277
+ "reportDateEnd" : last_day,
278
+ "pageNumber" : page_num,
279
+ "pageSize" : page_size,
280
+ }
281
+ response_text = fetch(self.web_page, url, payload)
282
+ error_code = response_text.get('code')
283
+ if str(error_code) != '0':
284
+ raise send_exception(json.dumps(response_text, ensure_ascii=False))
285
+ list_item = response_text['info']['data']['list']
286
+ total = response_text['info']['data']['count']
287
+ totalPage = math.ceil(total / page_size)
288
+
289
+ for page in range(2, totalPage + 1):
290
+ log(f'获取库存结余明细列表 第{page}/{totalPage}页')
291
+ page_num = page
292
+ payload = {
293
+ "reportDateStart": first_day,
294
+ "reportDateEnd" : last_day,
295
+ "pageNumber" : page_num,
296
+ "pageSize" : page_size,
297
+ }
298
+ response_text = fetch(self.web_page, url, payload)
299
+ spu_list_new = response_text['info']['data']['list']
300
+ list_item += spu_list_new
301
+ time.sleep(0.1)
302
+
303
+ for item in list_item:
304
+ supplierSku = item['supplierSku']
305
+ item['cost_price'] = self.bridge.get_sku_cost(supplierSku, source)
306
+ item['sku_img'] = self.bridge.get_sku_img(supplierSku, source)
307
+
308
+ write_dict_to_file(cache_file, list_item)
309
+
310
+ return list_item
311
+
312
+ def get_replenish_list(self):
313
+ page_num = 1
314
+ page_size = 50
315
+ first_day, last_day = TimeUtils.get_last_month_range()
316
+
317
+ cache_file = f'{self.config.auto_dir}/cache/replenish_list_{self.store_username}_{first_day}_{last_day}.json'
318
+ list_item = read_dict_from_file(cache_file, 3600 * 24 * 20)
319
+ if len(list_item) > 0:
320
+ return list_item
321
+
322
+ url = f"https://sso.geiwohuo.com/gsfs/finance/selfReplenish/list"
323
+ payload = {
324
+ "page" : page_num,
325
+ "perPage" : page_size,
326
+ "tabType" : 2,
327
+ "addTimeStart": f"{first_day} 00:00:00",
328
+ "addTimeEnd" : f"{last_day} 23:59:59"
329
+ }
330
+ response_text = fetch(self.web_page, url, payload)
331
+ error_code = response_text.get('code')
332
+ if str(error_code) != '0':
333
+ raise send_exception(json.dumps(response_text, ensure_ascii=False))
334
+ list_item = response_text['info']['data']
335
+ total = response_text['info']['meta']['count']
336
+ totalPage = math.ceil(total / page_size)
337
+
338
+ for page in range(2, totalPage + 1):
339
+ log(f'获取不扣款列表 第{page}/{totalPage}页')
340
+ page_num = page
341
+ payload = {
342
+ "page" : page_num,
343
+ "perPage" : page_size,
344
+ "tabType" : 2,
345
+ "addTimeStart": f"{first_day} 00:00:00",
346
+ "addTimeEnd" : f"{last_day} 23:59:59"
347
+ }
348
+ response_text = fetch(self.web_page, url, payload)
349
+ spu_list_new = response_text['info']['data']
350
+ list_item += spu_list_new
351
+ time.sleep(0.1)
352
+
353
+ write_dict_to_file(cache_file, list_item)
354
+
355
+ return list_item
356
+
357
+ def get_return_list(self):
358
+ page_num = 1
359
+ page_size = 200
360
+ first_day, last_day = TimeUtils.get_last_month_range()
361
+
362
+ cache_file = f'{self.config.auto_dir}/cache/return_list_{self.store_username}_{first_day}_{last_day}.json'
363
+ list_item = read_dict_from_file(cache_file, 3600 * 24 * 20)
364
+ if len(list_item) > 0:
365
+ return list_item
366
+
367
+ url = f"https://sso.geiwohuo.com/pfmp/returnOrder/page"
368
+ payload = {
369
+ "addTimeStart" : f"{first_day} 00:00:00",
370
+ "addTimeEnd" : f"{last_day} 23:59:59",
371
+ "returnOrderStatusList": [4],
372
+ "page" : page_num,
373
+ "perPage" : page_size
374
+ }
375
+ response_text = fetch(self.web_page, url, payload)
376
+ error_code = response_text.get('code')
377
+ if str(error_code) != '0':
378
+ raise send_exception(json.dumps(response_text, ensure_ascii=False))
379
+
380
+ list_item = response_text['info']['data']
381
+ total = response_text['info']['meta']['count']
382
+ totalPage = math.ceil(total / page_size)
383
+
384
+ for page in range(2, totalPage + 1):
385
+ log(f'获取不扣款列表 第{page}/{totalPage}页')
386
+ page_num = page
387
+ payload = {
388
+ "addTimeStart" : f"{first_day} 00:00:00",
389
+ "addTimeEnd" : f"{last_day} 23:59:59",
390
+ "returnOrderStatusList": [4],
391
+ "page" : page_num,
392
+ "perPage" : page_size
393
+ }
394
+ response_text = fetch(self.web_page, url, payload)
395
+ spu_list_new = response_text['info']['data']
396
+ list_item += spu_list_new
397
+ time.sleep(0.1)
398
+
399
+ write_dict_to_file(cache_file, list_item)
400
+
401
+ return list_item
402
+
403
+ def get_comment_list(self):
404
+ cache_file = f'{self.config.auto_dir}/shein/dict/comment_list_{TimeUtils.today_date()}.json'
405
+ comment_list = read_dict_from_file_ex(cache_file, self.store_username, 3600)
406
+ if len(comment_list) > 0:
407
+ return comment_list
408
+
409
+ page_num = 1
410
+ page_size = 50
411
+
412
+ yesterday = TimeUtils.get_yesterday()
413
+
414
+ url = f"https://sso.geiwohuo.com/gsp/goods/comment/list"
415
+ payload = {
416
+ "page" : page_num,
417
+ "perPage" : page_size,
418
+ "startCommentTime": f"{yesterday} 00:00:00",
419
+ "commentEndTime" : f"{yesterday} 23:59:59",
420
+ "commentStarList" : ["3", "2", "1"]
421
+ }
422
+ response_text = fetch(self.web_page, url, payload)
423
+ error_code = response_text.get('code')
424
+ if str(error_code) != '0':
425
+ raise send_exception(json.dumps(response_text, ensure_ascii=False))
426
+
427
+ comment_list = response_text['info']['data']
428
+ total = response_text['info']['meta']['count']
429
+ totalPage = math.ceil(total / page_size)
430
+
431
+ for page in range(2, totalPage + 1):
432
+ log(f'获取评价列表 第{page}/{totalPage}页')
433
+ page_num = page
434
+ payload['page'] = page_num
435
+ response_text = fetch(self.web_page, url, payload)
436
+ comment_list = response_text['info']['data']
437
+ time.sleep(0.1)
438
+
439
+ write_dict_to_file_ex(cache_file, {self.store_username: comment_list}, [self.store_username])
440
+ return comment_list
441
+
442
+ def get_last_month_outbound_amount(self):
443
+ url = "https://sso.geiwohuo.com/mils/report/month/list"
444
+ start, end = TimeUtils.get_current_year_range()
445
+ payload = {
446
+ "reportDateStart": start, "reportDateEnd": end, "pageNumber": 1, "pageSize": 50
447
+ }
448
+ response_text = fetch(self.web_page, url, payload)
449
+ error_code = response_text.get('code')
450
+ if str(error_code) != '0':
451
+ raise send_exception(json.dumps(response_text, ensure_ascii=False))
452
+ info = response_text.get('info')
453
+ lst = info.get('data', {}).get('list', [])
454
+ if not lst:
455
+ log(f'⚠️ {self.store_name} 最近一个月无出库记录,金额为0')
456
+ return 0
457
+
458
+ last_item = lst[-1]
459
+ log(f'正在获取 {self.store_name} 最近一个月出库金额: {last_item["totalCustomerAmount"]}')
460
+ return last_item['totalCustomerAmount']
461
+
462
+ def get_funds_data(self):
463
+ log(f'正在获取 {self.store_name} 财务数据')
464
+ url = "https://sso.geiwohuo.com/sso/homePage/dataOverview/v2/detail"
465
+ payload = {
466
+ "metaIndexIds": [
467
+ 298,
468
+ 67,
469
+ 70,
470
+ 72
471
+ ],
472
+ }
473
+ response_text = fetch(self.web_page, url, payload)
474
+ error_code = response_text.get('code')
475
+ if str(error_code) != '0':
476
+ raise send_exception(json.dumps(response_text, ensure_ascii=False))
477
+ info = response_text.get('info')
478
+ num298 = 0 # 在途商品金额
479
+ num67 = 0 # 在仓商品金额
480
+ num70 = 0 # 待结算金额
481
+ num72 = 0 # 可提现金额
482
+ for item in info['list']:
483
+ if item['metaIndexId'] == 298:
484
+ num298 = item['count']
485
+ if item['metaIndexId'] == 67:
486
+ num67 = item['count']
487
+ if item['metaIndexId'] == 70:
488
+ num70 = item['count']
489
+ if item['metaIndexId'] == 72:
490
+ num72 = item['count']
491
+
492
+ outAmount = self.get_last_month_outbound_amount()
493
+ dict_store = read_dict_from_file(self.config.shein_store_alias)
494
+ store_manager = dict_store.get(str(self.store_username).lower())
495
+ NotifyItem = [f'{self.store_name}', self.store_username, store_manager, num298, num67, num70, num72, outAmount, '',
496
+ TimeUtils.current_datetime()]
497
+
498
+ cache_file = f'{self.config.auto_dir}/shein/cache/stat_fund_{TimeUtils.today_date()}.json'
499
+ write_dict_to_file_ex(cache_file, {self.store_username: NotifyItem}, [self.store_username])
500
+ return NotifyItem
501
+
502
+ def getQueryDate(self):
503
+ query_time = self.DictQueryTime.get(self.store_username, None)
504
+ if query_time is not None:
505
+ log(f'从字典获取query_time: {query_time}')
506
+ return query_time
507
+ log('获取日期范围')
508
+ url = "https://sso.geiwohuo.com/mgs-api-prefix/estimate/queryDateRange"
509
+ payload = {}
510
+ response_text = fetch(self.web_page, url, payload)
511
+ error_code = response_text.get('code')
512
+ if str(error_code) != '0':
513
+ raise send_exception(json.dumps(response_text, ensure_ascii=False))
514
+ query_time = response_text.get('info').get('quality_goods_query_time')
515
+ self.DictQueryTime.update({self.store_username: query_time})
516
+ log(f'query_time: {query_time}')
517
+ return query_time
518
+
519
+ def get_goods_quality_estimate_list(self, query_date):
520
+ cache_file = f'{self.config.auto_dir}/shein/dict/googs_estimate_{query_date}.json'
521
+ estimate_list = read_dict_from_file_ex(cache_file, self.store_username, 3600 * 8)
522
+ if len(estimate_list) > 0:
523
+ return estimate_list
524
+
525
+ page_num = 1
526
+ page_size = 100
527
+
528
+ url = f"https://sso.geiwohuo.com/mgs-api-prefix/estimate/queryNewQualityGoodsList"
529
+ payload = {
530
+ "page_no" : page_num,
531
+ "page_size" : page_size,
532
+ "start_date": query_date,
533
+ "end_date" : query_date,
534
+ "order_col" : "skc_sale_cnt_14d",
535
+ "order_type": "desc"
536
+ }
537
+ response_text = fetch(self.web_page, url, payload, {'lan': 'CN'})
538
+ error_code = response_text.get('code')
539
+ if str(error_code) != '0':
540
+ raise send_exception(json.dumps(response_text, ensure_ascii=False))
541
+
542
+ estimate_list = response_text['info']['data']
543
+ total = response_text['info']['meta']['count']
544
+ totalPage = math.ceil(total / page_size)
545
+
546
+ for page in range(2, totalPage + 1):
547
+ log(f'获取质量评估列表 第{page}/{totalPage}页')
548
+ page_num = page
549
+ payload['page'] = page_num
550
+ response_text = fetch(self.web_page, url, payload)
551
+ estimate_list = response_text['info']['data']
552
+ time.sleep(0.1)
553
+
554
+ write_dict_to_file_ex(cache_file, {self.store_username: estimate_list}, [self.store_username])
555
+ return estimate_list
556
+
557
+ # 已上架备货款A数量
558
+ def get_product_bak_A_count(self):
559
+ url = "https://sso.geiwohuo.com/idms/goods-skc/list"
560
+ payload = {
561
+ "pageNumber" : 1,
562
+ "pageSize" : 10,
563
+ "supplierCodes" : "",
564
+ "skcs" : "",
565
+ "spu" : "",
566
+ "c7dSaleCntBegin" : "",
567
+ "c7dSaleCntEnd" : "",
568
+ "goodsLevelIdList" : [
569
+ 61,
570
+ 90
571
+ ],
572
+ "supplyStatus" : "",
573
+ "shelfStatus" : 1,
574
+ "categoryIdList" : [],
575
+ "skcStockBegin" : "",
576
+ "skcStockEnd" : "",
577
+ "skuStockBegin" : "",
578
+ "skuStockEnd" : "",
579
+ "skcSaleDaysBegin" : "",
580
+ "skcSaleDaysEnd" : "",
581
+ "skuSaleDaysBegin" : "",
582
+ "skuSaleDaysEnd" : "",
583
+ "planUrgentCountBegin" : "",
584
+ "planUrgentCountEnd" : "",
585
+ "skcAvailableOrderBegin": "",
586
+ "skcAvailableOrderEnd" : "",
587
+ "skuAvailableOrderBegin": "",
588
+ "skuAvailableOrderEnd" : "",
589
+ "shelfDateBegin" : "",
590
+ "shelfDateEnd" : "",
591
+ "stockWarnStatusList" : [],
592
+ "labelFakeIdList" : [],
593
+ "sheinSaleByInventory" : "",
594
+ "tspIdList" : [],
595
+ "adviceStatus" : [],
596
+ "sortBy7dSaleCnt" : 2,
597
+ "goodsLevelFakeIdList" : [
598
+ 3
599
+ ]
600
+ }
601
+ response_text = fetch(self.web_page, url, payload)
602
+ error_code = response_text.get('code')
603
+ if str(error_code) != '0':
604
+ raise send_exception(json.dumps(response_text, ensure_ascii=False))
605
+ info = response_text.get('info')
606
+ count = info.get('count', 0)
607
+ log('获取已上架备货款A数量', count)
608
+ return count
609
+
610
+ # 已上架备货款B数量
611
+ def get_product_bak_B_count(self):
612
+ url = "https://sso.geiwohuo.com/idms/goods-skc/list"
613
+ payload = {
614
+ "pageNumber" : 1,
615
+ "pageSize" : 10,
616
+ "supplierCodes" : "",
617
+ "skcs" : "",
618
+ "spu" : "",
619
+ "c7dSaleCntBegin" : "",
620
+ "c7dSaleCntEnd" : "",
621
+ "goodsLevelIdList" : [
622
+ 62,
623
+ 227,
624
+ 12,
625
+ 230,
626
+ 80,
627
+ 58,
628
+ 224
629
+ ],
630
+ "supplyStatus" : "",
631
+ "shelfStatus" : 1,
632
+ "categoryIdList" : [],
633
+ "skcStockBegin" : "",
634
+ "skcStockEnd" : "",
635
+ "skuStockBegin" : "",
636
+ "skuStockEnd" : "",
637
+ "skcSaleDaysBegin" : None,
638
+ "skcSaleDaysEnd" : "",
639
+ "skuSaleDaysBegin" : "",
640
+ "skuSaleDaysEnd" : "",
641
+ "planUrgentCountBegin" : "",
642
+ "planUrgentCountEnd" : "",
643
+ "skcAvailableOrderBegin": "",
644
+ "skcAvailableOrderEnd" : "",
645
+ "skuAvailableOrderBegin": None,
646
+ "skuAvailableOrderEnd" : "",
647
+ "shelfDateBegin" : "",
648
+ "shelfDateEnd" : "",
649
+ "stockWarnStatusList" : [],
650
+ "labelFakeIdList" : [],
651
+ "sheinSaleByInventory" : "",
652
+ "tspIdList" : [],
653
+ "adviceStatus" : [],
654
+ "sortBy7dSaleCnt" : 2,
655
+ "goodsLevelFakeIdList" : [
656
+ 4
657
+ ]
658
+ }
659
+ response_text = fetch(self.web_page, url, payload)
660
+ error_code = response_text.get('code')
661
+ if str(error_code) != '0':
662
+ raise send_exception(json.dumps(response_text, ensure_ascii=False))
663
+ info = response_text.get('info')
664
+ count = info.get('count', 0)
665
+ log('获取已上架备货款B数量', count)
666
+ return count
667
+
668
+ # 已上架新款A数量
669
+ def get_product_A_count(self):
670
+ url = "https://sso.geiwohuo.com/idms/goods-skc/list"
671
+ payload = {
672
+ "pageNumber" : 1,
673
+ "pageSize" : 10,
674
+ "supplierCodes" : "",
675
+ "skcs" : "",
676
+ "spu" : "",
677
+ "c7dSaleCntBegin" : "",
678
+ "c7dSaleCntEnd" : "",
679
+ "goodsLevelIdList" : [
680
+ 107
681
+ ],
682
+ "supplyStatus" : "",
683
+ "shelfStatus" : 1,
684
+ "categoryIdList" : [],
685
+ "skcStockBegin" : "",
686
+ "skcStockEnd" : "",
687
+ "skuStockBegin" : "",
688
+ "skuStockEnd" : "",
689
+ "skcSaleDaysBegin" : None,
690
+ "skcSaleDaysEnd" : "",
691
+ "skuSaleDaysBegin" : "",
692
+ "skuSaleDaysEnd" : "",
693
+ "planUrgentCountBegin" : "",
694
+ "planUrgentCountEnd" : "",
695
+ "skcAvailableOrderBegin": "",
696
+ "skcAvailableOrderEnd" : "",
697
+ "skuAvailableOrderBegin": None,
698
+ "skuAvailableOrderEnd" : "",
699
+ "shelfDateBegin" : "",
700
+ "shelfDateEnd" : "",
701
+ "stockWarnStatusList" : [],
702
+ "labelFakeIdList" : [],
703
+ "sheinSaleByInventory" : "",
704
+ "tspIdList" : [],
705
+ "adviceStatus" : [],
706
+ "sortBy7dSaleCnt" : 2,
707
+ "goodsLevelFakeIdList" : [
708
+ 2
709
+ ]
710
+ }
711
+ response_text = fetch(self.web_page, url, payload)
712
+ error_code = response_text.get('code')
713
+ if str(error_code) != '0':
714
+ raise send_exception(json.dumps(response_text, ensure_ascii=False))
715
+ info = response_text.get('info')
716
+ count = info.get('count', 0)
717
+ log('获取已上架新款A数量', count)
718
+ return count
719
+
720
+ # 本周已上架数量
721
+ def get_week_shelf_product_count(self, start_date, end_date):
722
+ url = "https://sso.geiwohuo.com/idms/goods-skc/list"
723
+ payload = {
724
+ "pageNumber" : 1,
725
+ "pageSize" : 10,
726
+ "supplierCodes" : "",
727
+ "skcs" : "",
728
+ "spu" : "",
729
+ "c7dSaleCntBegin" : "",
730
+ "c7dSaleCntEnd" : "",
731
+ "goodsLevelIdList" : [],
732
+ "supplyStatus" : "",
733
+ "shelfStatus" : 1,
734
+ "categoryIdList" : [],
735
+ "skcStockBegin" : "",
736
+ "skcStockEnd" : "",
737
+ "skuStockBegin" : "",
738
+ "skuStockEnd" : "",
739
+ "skcSaleDaysBegin" : "",
740
+ "skcSaleDaysEnd" : "",
741
+ "skuSaleDaysBegin" : "",
742
+ "skuSaleDaysEnd" : "",
743
+ "planUrgentCountBegin" : "",
744
+ "planUrgentCountEnd" : "",
745
+ "skcAvailableOrderBegin": "",
746
+ "skcAvailableOrderEnd" : "",
747
+ "skuAvailableOrderBegin": "",
748
+ "skuAvailableOrderEnd" : "",
749
+ "shelfDateBegin" : start_date,
750
+ "shelfDateEnd" : end_date,
751
+ "stockWarnStatusList" : [],
752
+ "labelFakeIdList" : [],
753
+ "sheinSaleByInventory" : "",
754
+ "tspIdList" : [],
755
+ "adviceStatus" : [],
756
+ "sortBy7dSaleCnt" : 2
757
+ }
758
+ response_text = fetch(self.web_page, url, payload)
759
+ error_code = response_text.get('code')
760
+ if str(error_code) != '0':
761
+ raise send_exception(json.dumps(response_text, ensure_ascii=False))
762
+ info = response_text.get('info')
763
+ count = info.get('count', 0)
764
+ log('获取本周上架数量')
765
+ return count
766
+
767
+ # 已上架数量
768
+ def get_shelf_product_count(self):
769
+ log('获取所有已上架数量')
770
+ url = "https://sso.geiwohuo.com/spmp-api-prefix/spmp/product/list?page_num=1&page_size=10"
771
+ payload = {
772
+ "language" : "zh-cn",
773
+ "only_recommend_resell" : False,
774
+ "only_spmb_copy_product": False,
775
+ "search_abandon_product": False,
776
+ "shelf_type" : "ON_SHELF",
777
+ "sort_type" : 1
778
+ }
779
+ response_text = fetch(self.web_page, url, payload)
780
+ error_code = response_text.get('code')
781
+ if str(error_code) != '0':
782
+ raise send_exception(json.dumps(response_text, ensure_ascii=False))
783
+
784
+ info = response_text.get('info')
785
+ customObj = info.get('meta').get('customObj')
786
+ # 将数据转换成字典
787
+ result = {item["shelf_status"]: item["count"] for item in customObj}
788
+ return result
789
+
790
+ def get_yesterday_upload_product_count(self, dt=None):
791
+ url = "https://sso.geiwohuo.com/spmp-api-prefix/spmp/product/publish/record/page_list?page_num=1&page_size=100"
792
+ payload = {
793
+ "edit_type" : 0,
794
+ "language" : "zh-cn",
795
+ "only_current_month_recommend": False,
796
+ "only_spmb_copy_product" : False,
797
+ "query_time_out" : False,
798
+ "search_diy_custom" : False
799
+ }
800
+ response_text = fetch(self.web_page, url, payload)
801
+ error_code = response_text.get('code')
802
+ if str(error_code) != '0':
803
+ raise send_exception(json.dumps(response_text, ensure_ascii=False))
804
+ info = response_text.get('info')
805
+ count = 0
806
+ for item in info.get('data', {}):
807
+ if TimeUtils.is_yesterday(item['create_time'], dt):
808
+ count += 1
809
+ log('获取昨日已上传数量', count)
810
+ return count
811
+
812
+ def get_week_sales_stat_detail(self):
813
+ global ListNotify, NotifyItem
814
+ dt = self.get_dt_time()
815
+ yesterday = TimeUtils.get_yesterday(dt)
816
+ date_7_days_ago = TimeUtils.get_past_nth_day(6, None, '%Y-%m-%d')
817
+ log('-7', date_7_days_ago)
818
+ date_1_days_ago = TimeUtils.get_past_nth_day(1, None, '%Y-%m-%d')
819
+ log('-1', date_1_days_ago)
820
+
821
+ url = "https://sso.geiwohuo.com/sbn/index/get_critical_indicator_curve_chart"
822
+ payload = {
823
+ "areaCd" : "cn",
824
+ "dt" : dt,
825
+ "countrySite": [
826
+ "shein-all"
827
+ ],
828
+ "startDate" : date_7_days_ago,
829
+ "endDate" : date_1_days_ago,
830
+ "queryType" : 1,
831
+ "pageNum" : 1,
832
+ "pageSize" : 100
833
+ }
834
+ response_text = fetch(self.web_page, url, payload)
835
+ error_code = response_text.get('code')
836
+ if str(error_code) != '0':
837
+ raise send_exception(json.dumps(response_text, ensure_ascii=False))
838
+ info = response_text.get('info', {})
839
+
840
+ last_item = SheinStoreSalesDetailManager(self.config.database_url).get_records_as_dict(self.store_username, yesterday)
841
+ log('last_item', last_item)
842
+ day_item = info[-1]
843
+ item = {}
844
+ item["store_username"] = self.store_username
845
+ item["store_name"] = self.store_name
846
+ item["day"] = day_item["dataDate"]
847
+ item["sales_num"] = day_item["saleCnt1d"]
848
+ item['sales_num_inc'] = item['sales_num'] - last_item.get('sales_num', 0)
849
+ item['sales_amount'] = day_item['dealAmt1d']
850
+ item['sales_amount_inc'] = item['sales_amount'] - float(last_item.get('sales_amount', 0))
851
+ item['visitor_num'] = day_item['idxShopGoodsUv1d']
852
+ item['visitor_num_inc'] = item['visitor_num'] - last_item.get('visitor_num', 0)
853
+ item['bak_A_num'] = self.get_product_bak_A_count()
854
+ item['bak_A_num_inc'] = item['bak_A_num'] - last_item.get('bak_A_num', 0)
855
+ item['new_A_num'] = self.get_product_A_count()
856
+ item['new_A_num_inc'] = item['new_A_num'] - last_item.get('new_A_num', 0)
857
+ dictProduct = self.get_shelf_product_count()
858
+ item['on_sales_product_num'] = dictProduct.get('ON_SHELF')
859
+ item['on_sales_product_num_inc'] = item['on_sales_product_num'] - last_item.get('on_sales_product_num', 0)
860
+ item['wait_shelf_product_num'] = dictProduct.get('WAIT_SHELF')
861
+ item['wait_shelf_product_num_inc'] = item['wait_shelf_product_num'] - last_item.get('wait_shelf_product_num', 0)
862
+ item['upload_product_num'] = self.get_yesterday_upload_product_count()
863
+ item['upload_product_num_inc'] = item['upload_product_num'] - last_item.get('upload_product_num', 0)
864
+ item['sold_out_product_num'] = dictProduct.get('SOLD_OUT')
865
+ item['shelf_off_product_num'] = dictProduct.get('OUT_SHELF')
866
+
867
+ SheinStoreSalesDetailManager(self.config.database_url).insert_data([item])
868
+
869
+ def get_delivery_order_list(self, orderType=2):
870
+ page_num = 1
871
+ page_size = 200
872
+
873
+ url = f"https://sso.geiwohuo.com/pfmp/order/list"
874
+ payload = {}
875
+ if orderType == 1:
876
+ payload = {
877
+ "orderType": orderType,
878
+ "page" : page_num,
879
+ "perPage" : page_size,
880
+ "status" : [2],
881
+ }
882
+ elif orderType == 2:
883
+ payload = {
884
+ "orderType" : orderType,
885
+ "page" : page_num,
886
+ "perPage" : page_size,
887
+ "status" : [2],
888
+ "isJitOrder": 2
889
+ }
890
+ response_text = fetch(self.web_page, url, payload)
891
+ error_code = response_text.get('code')
892
+ if str(error_code) != '0':
893
+ raise send_exception(json.dumps(response_text, ensure_ascii=False))
894
+
895
+ spu_list = response_text['info']['data']
896
+ total = response_text['info']['meta']['count']
897
+ totalPage = math.ceil(total / page_size)
898
+
899
+ skc_list = [item['goods']['skcName'] for item in spu_list]
900
+ self.get_activity_label(skc_list)
901
+
902
+ for page in range(2, totalPage + 1):
903
+ log(f'获取订单列表 第{page}/{totalPage}')
904
+ page_num = page
905
+ if orderType == 1:
906
+ payload = {
907
+ "orderType": orderType,
908
+ "page" : page_num,
909
+ "perPage" : page_size,
910
+ "status" : [2],
911
+ }
912
+ elif orderType == 2:
913
+ payload = {
914
+ "orderType" : orderType,
915
+ "page" : page_num,
916
+ "perPage" : page_size,
917
+ "status" : [2],
918
+ "isJitOrder": 2
919
+ }
920
+ response_text = fetch(self.web_page, url, payload)
921
+ spu_list_new = response_text['info']['data']
922
+ skc_list = [item['goods']['skcName'] for item in spu_list_new]
923
+ self.get_activity_label(skc_list)
924
+ spu_list += spu_list_new
925
+ time.sleep(0.3)
926
+
927
+ if len(spu_list) == 0:
928
+ log(f'无{["", "急采", "备货"][orderType]}发货单')
929
+ return None
930
+
931
+ write_to_excel = [
932
+ # 0 1 2 3 4 5 6 7
933
+ ['店铺名称', 'SKC图片', 'SKU图片', '商品信息', '下单/需求数量', '库存(模式/本地/在途/希音)', '成本价', '核价',
934
+ '近7天SKU销量/SKC销量/SKC曝光', 'SKC点击率/SKC转化率', '自主参与活动', '最晚预约上门取件', '要求实际完成取件',
935
+ 'SKC', 'SKU']
936
+ ]
937
+ cache_file2 = f'{self.config.auto_dir}/shein/dict/skc_shelf_{self.store_username}.json'
938
+ DictSkcShelf = read_dict_from_file(cache_file2)
939
+ cache_file3 = f'{self.config.auto_dir}/shein/dict/skc_product_{self.store_username}.json'
940
+ DictSkcProduct = read_dict_from_file(cache_file3)
941
+ cache_file = f'{self.config.auto_dir}/shein/dict/activity_price_{self.store_name}.json'
942
+ dictActivityPrice = read_dict_from_file(cache_file)
943
+ cache_file4 = f'{self.config.auto_dir}/shein/dict/dict_sku_info_{self.store_username}.json'
944
+ DictSkuInfo = read_dict_from_file(cache_file4)
945
+ for spu_item in spu_list:
946
+ skc = spu_item['goods']['skcName']
947
+ skcCode = spu_item['goods']['supplierCode']
948
+ skc_img = str(spu_item['goods']['imgPath'])
949
+ orderNum = spu_item['sellerOrderNo']
950
+ orderTime = spu_item['allocateTime']
951
+ requestTakeParcelTime = spu_item['requestTakeParcelTime']
952
+ suggestedReserveTime = spu_item['suggestedReserveTime']
953
+ good_level = spu_item['goods']['goodsLevelName']
954
+
955
+ self.get_skc_week_actual_sales(skc)
956
+
957
+ spu = DictSkcProduct[skc]['spu_name']
958
+ log('spu', spu)
959
+ for sku_item in spu_item['detail']:
960
+ needQuantity = sku_item['needQuantity']
961
+ orderQuantity = sku_item['orderQuantity']
962
+ # sku_img = sku_item['skuThumb']
963
+ skuCode = sku_item['supplierSku']
964
+ stock = self.bridge.get_sku_stock(skuCode, 'mb')
965
+ cost_price = self.bridge.get_sku_cost(skuCode, 'mb')
966
+ suffixZh = sku_item['suffixZh']
967
+ sku = sku_item['skuCode']
968
+ supplyPrice = sku_item['supplyPrice']
969
+ sku_img = self.bridge.get_sku_img(skuCode, 'mb')
970
+ sale_model = DictSkuInfo[skuCode][0]
971
+ shein_stock = DictSkuInfo[skuCode][1]
972
+ shelf_days = DictSkuInfo[skuCode][2]
973
+ real_transit = DictSkuInfo[skuCode][3]
974
+ stock_str = f'{sale_model}\n{stock}/{real_transit}/{shein_stock}'
975
+
976
+ item = []
977
+ item.append(f'{self.store_name}\n{good_level}')
978
+ item.append(skc_img)
979
+ item.append(sku_img)
980
+ if cost_price == '-':
981
+ profit = '-'
982
+ else:
983
+ profit = f'{float(supplyPrice) - float(cost_price):.2f}'
984
+ # item.append(f'订单号: {orderNum}\n下单时间: {orderTime}\n最晚预约上门取件: {suggestedReserveTime}\nSKC: {skc}\nSKC货号: {skcCode}\nSKU货号: {skuCode}\n属性集: {suffixZh}\n上架时间: {DictSkcShelf[skc]}\n上架天数: {shelf_days}\n成本/核价/利润: ¥{cost_price}/¥{supplyPrice}/¥{profit}\n下单/需求数量: {orderQuantity}/{needQuantity}\n库存模式/本地/在途/希音: {sale_model}/{stock}/{real_transit}/{shein_stock}\n')
985
+ item.append(f'订单号: {orderNum}\n下单时间: {orderTime}\n最晚预约上门取件: {suggestedReserveTime}\nSKC: {skc}\nSKC货号: {skcCode}\nSKU货号: {skuCode}\n属性集: {suffixZh}\n上架时间: {DictSkcShelf[skc]}\n上架天数: {shelf_days}\n成本/核价/利润: ¥{cost_price}/¥{supplyPrice}/¥{profit}')
986
+ item.append(f'[{orderQuantity}/{needQuantity}]')
987
+ item.append(stock_str)
988
+ item.append(cost_price)
989
+ item.append(supplyPrice)
990
+ sale_num_list, sale_data_list = self.get_skc_week_sale_list(spu, skc, sku)
991
+ item.append("\n".join(sale_num_list))
992
+ item.append("\n".join(sale_data_list))
993
+ item.append(self.get_skc_activity_label(skc, sku, dictActivityPrice))
994
+ item.append(suggestedReserveTime)
995
+ item.append(requestTakeParcelTime)
996
+ item.append(skc)
997
+ item.append(sku)
998
+ write_to_excel.append(item)
999
+
1000
+ cache_file = f'{self.config.auto_dir}/shein/cache/jit_{TimeUtils.today_date()}_{orderType}_{TimeUtils.get_period()}.json'
1001
+ write_dict_to_file_ex(cache_file, {self.store_username: write_to_excel}, {self.store_username})
1002
+
1003
+ return write_to_excel
1004
+
1005
+ # 获取商品包含sku销量的列表
1006
+ def get_dict_sku_stock_detail(self):
1007
+ log(f'获取备货信息商品列表 做成字典')
1008
+ url = "https://sso.geiwohuo.com/idms/goods-skc/list"
1009
+ pageNumber = 1
1010
+ pageSize = 100
1011
+ dictPayload = {
1012
+ "pageNumber" : pageNumber,
1013
+ "pageSize" : pageSize,
1014
+ "supplierCodes" : "",
1015
+ "skcs" : "",
1016
+ "spu" : "",
1017
+ "c7dSaleCntBegin" : "",
1018
+ "c7dSaleCntEnd" : "",
1019
+ "goodsLevelIdList" : [],
1020
+ "supplyStatus" : "",
1021
+ "shelfStatus" : "",
1022
+ "categoryIdList" : [],
1023
+ "skcStockBegin" : "",
1024
+ "skcStockEnd" : "",
1025
+ "skuStockBegin" : "",
1026
+ "skuStockEnd" : "",
1027
+ "skcSaleDaysBegin" : "",
1028
+ "skcSaleDaysEnd" : "",
1029
+ "skuSaleDaysBegin" : "",
1030
+ "skuSaleDaysEnd" : "",
1031
+ "planUrgentCountBegin" : "",
1032
+ "planUrgentCountEnd" : "",
1033
+ "skcAvailableOrderBegin": "",
1034
+ "skcAvailableOrderEnd" : "",
1035
+ "skuAvailableOrderBegin": "",
1036
+ "skuAvailableOrderEnd" : "",
1037
+ "shelfDateBegin" : "",
1038
+ "shelfDateEnd" : "",
1039
+ "stockWarnStatusList" : [],
1040
+ "labelFakeIdList" : [],
1041
+ "sheinSaleByInventory" : "",
1042
+ "tspIdList" : [],
1043
+ "adviceStatus" : [],
1044
+ "sortBy7dSaleCnt" : 2
1045
+ }
1046
+ payload = dictPayload
1047
+ response_text = fetch(self.web_page, url, payload)
1048
+ error_code = response_text.get('code')
1049
+ if str(error_code) != '0':
1050
+ raise send_exception(json.dumps(response_text, ensure_ascii=False))
1051
+
1052
+ spu_list = response_text['info']['list']
1053
+
1054
+ total = response_text['info']['count']
1055
+ totalPage = math.ceil(total / pageSize)
1056
+ for page in range(2, totalPage + 1):
1057
+ log(f'获取备货信息商品列表 第{page}/{totalPage}页')
1058
+ dictPayload['pageNumber'] = page
1059
+ payload = dictPayload
1060
+ response_text = fetch(self.web_page, url, payload)
1061
+ spu_list_new = response_text['info']['list']
1062
+ spu_list += spu_list_new
1063
+ time.sleep(0.3)
1064
+
1065
+ DictSkuInfo = {}
1066
+ for spu_info in spu_list:
1067
+ sale_model = spu_info.get('saleModel', {}).get('name') if spu_info.get('saleModel') else '-'
1068
+ shelfDays = spu_info['shelfDays']
1069
+ for sku_info in spu_info['skuList']:
1070
+ attr = sku_info['attr']
1071
+ if attr == '合计':
1072
+ continue
1073
+ skuExtCode = str(sku_info['supplierSku'])
1074
+ shein_stock = sku_info['stock']
1075
+
1076
+ transit = sku_info['transit'] # 在途
1077
+ real_transit = transit + sku_info['stayShelf'] - sku_info['transitSale']
1078
+
1079
+ DictSkuInfo[skuExtCode] = [sale_model, shein_stock, shelfDays, real_transit]
1080
+
1081
+ write_dict_to_file(f'{self.config.auto_dir}/shein/dict/dict_sku_info_{self.store_username}.json', DictSkuInfo)
1082
+
1083
+ return DictSkuInfo
1084
+
1085
+ def get_shop_notify_num(self):
1086
+ log(f'正在获取 {self.store_name} 通知数据')
1087
+ url = "https://sso.geiwohuo.com/sso/homePage/v4/detail"
1088
+ payload = {
1089
+ "metaIndexIds": [
1090
+ 246, # 急采-待发货
1091
+ 245 # 备货-待发货
1092
+ ],
1093
+ "templateType": 0
1094
+ }
1095
+ response_text = fetch(self.web_page, url, payload)
1096
+ error_code = response_text.get('code')
1097
+ if str(error_code) != '0':
1098
+ raise send_exception(json.dumps(response_text, ensure_ascii=False))
1099
+ info = response_text.get('info')
1100
+
1101
+ cache_file = f'{self.config.auto_dir}/shein/notify/{self.store_name}_{TimeUtils.get_current_datetime()}.json'
1102
+ write_dict_to_file(cache_file, info)
1103
+
1104
+ num245 = 0
1105
+ num246 = 0
1106
+ for item in info['list']:
1107
+ if item['metaIndexId'] == 245:
1108
+ num245 = item['count']
1109
+ if item['metaIndexId'] == 246:
1110
+ num246 = item['count']
1111
+
1112
+ NotifyItem = [self.store_name, num246, num245]
1113
+
1114
+ cache_file = f'{self.config.auto_dir}/shein/cache/jit_notify_{TimeUtils.today_date()}.json'
1115
+ write_dict_to_file_ex(cache_file, {self.store_username: NotifyItem}, {self.store_username})
1116
+
1117
+ return info
1118
+
1119
+ def get_activity_list(self):
1120
+ url = "https://sso.geiwohuo.com/mrs-api-prefix/mbrs/activity/get_activity_list?page_num=1&page_size=100"
1121
+ payload = {}
1122
+ response_text = fetch(self.web_page, url, payload)
1123
+ error_code = response_text.get('code')
1124
+ if str(error_code) != '0':
1125
+ raise send_exception(json.dumps(response_text, ensure_ascii=False))
1126
+ total = response_text.get('info', {}).get('total_count')
1127
+ excel_data = [[
1128
+ '店铺名称', '活动信息', '已报数量', '可报数量'
1129
+ ]]
1130
+ if total > 0:
1131
+ for item in response_text.get('info', {}).get('activity_detail_list'):
1132
+ activity_tag = item.get('text_tag_content')
1133
+ activity_name = item['activity_name']
1134
+ start_time = item['activity_start_zone_time']
1135
+ end_time = item['activity_end_zone_time']
1136
+ start_time2 = item['start_zone_time']
1137
+ end_time2 = item['end_zone_time']
1138
+ allow_goods_num = item.get('allow_goods_num')
1139
+ apply_goods_num = item.get('apply_goods_num')
1140
+ row_item = [
1141
+ self.store_name,
1142
+ f"活动名称: 【{activity_tag}】{activity_name}\n报名时间: {start_time}~{end_time}\n活动时间: {start_time2}~{end_time2}\n已报数量: {apply_goods_num}/{allow_goods_num}",
1143
+ apply_goods_num,
1144
+ allow_goods_num,
1145
+ ]
1146
+ excel_data.append(row_item)
1147
+ cache_file = f'{self.config.auto_dir}/shein/activity_list/activity_list_{TimeUtils.today_date()}.json'
1148
+ write_dict_to_file_ex(cache_file, {self.store_username: excel_data}, [self.store_username])
1149
+
1150
+ def get_product_list(self):
1151
+ # self.web_page.goto('https://sso.geiwohuo.com/#/spmp/commdities/list')
1152
+ # self.web_page.wait_for_load_state("load")
1153
+
1154
+ cache_file = f'{self.config.auto_dir}/shein/dict/product_list_{self.store_username}.json'
1155
+ DictSpuInfo = read_dict_from_file(cache_file, 3600)
1156
+ if len(DictSpuInfo) > 0:
1157
+ return DictSpuInfo
1158
+
1159
+ page_num = 1
1160
+ page_size = 100
1161
+
1162
+ url = f"https://sso.geiwohuo.com/spmp-api-prefix/spmp/product/list?page_num={page_num}&page_size={page_size}"
1163
+ payload = {
1164
+ "language" : "zh-cn",
1165
+ "only_recommend_resell" : False,
1166
+ "only_spmb_copy_product": False,
1167
+ "search_abandon_product": False,
1168
+ "sort_type" : 1
1169
+ }
1170
+ response_text = fetch(self.web_page, url, payload)
1171
+ error_code = response_text.get('code')
1172
+ if str(error_code) != '0':
1173
+ raise send_exception(json.dumps(response_text, ensure_ascii=False))
1174
+
1175
+ spu_list = response_text['info']['data']
1176
+ total = response_text['info']['meta']['count']
1177
+ totalPage = math.ceil(total / page_size)
1178
+
1179
+ for page_num in range(2, totalPage + 1):
1180
+ log(f'获取商品列表 第{page_num}/{totalPage}页')
1181
+ url = f"https://sso.geiwohuo.com/spmp-api-prefix/spmp/product/list?page_num={page_num}&page_size={page_size}"
1182
+ response_text = fetch(self.web_page, url, payload)
1183
+ spu_list_new = response_text['info']['data']
1184
+ spu_list += spu_list_new
1185
+ time.sleep(0.3)
1186
+
1187
+ DictSkcShelf = {}
1188
+ DictSkcProduct = {}
1189
+ DictSpuInfo = {}
1190
+ for spu_item in spu_list:
1191
+ spu = spu_item['spu_name']
1192
+ first_shelf_time = spu_item['first_shelf_time']
1193
+ for skc_item in spu_item['skc_info_list']:
1194
+ skc_name = skc_item['skc_name']
1195
+ DictSkcShelf[skc_name] = first_shelf_time
1196
+ DictSkcProduct[skc_name] = spu_item
1197
+ DictSpuInfo[spu] = spu_item
1198
+
1199
+ cache_file2 = f'{self.config.auto_dir}/shein/dict/skc_shelf_{self.store_username}.json'
1200
+ write_dict_to_file(cache_file2, DictSkcShelf)
1201
+ cache_file3 = f'{self.config.auto_dir}/dict/skc_product_{self.store_username}.json'
1202
+ write_dict_to_file(cache_file3, DictSkcProduct)
1203
+
1204
+ write_dict_to_file(cache_file, DictSpuInfo)
1205
+ return DictSpuInfo
1206
+
1207
+ def query_obm_activity_list(self):
1208
+ page_num = 1
1209
+ page_size = 100
1210
+ date_60_days_ago = TimeUtils.get_past_nth_day(59)
1211
+ cache_file = f'{self.config.auto_dir}/shein/cache/obm_activity_{self.store_name}_{date_60_days_ago}_{TimeUtils.today_date()}.json'
1212
+ list_item = read_dict_from_file(cache_file, 3600 * 8)
1213
+ if len(list_item) > 0:
1214
+ return list_item
1215
+
1216
+ url = f"https://sso.geiwohuo.com/mrs-api-prefix/promotion/obm/query_obm_activity_list"
1217
+ payload = {
1218
+ "insert_end_time" : f"{TimeUtils.today_date()} 23:59:59",
1219
+ "insert_start_time": f"{date_60_days_ago} 00:00:00",
1220
+ "page_num" : page_num,
1221
+ "page_size" : page_size,
1222
+ "system" : "mrs",
1223
+ "time_zone" : "Asia/Shanghai",
1224
+ # "state": 3, # 活动开启中 不能用这个条件
1225
+ "type_id" : 31 # 限时折扣
1226
+ }
1227
+
1228
+ response_text = fetch(self.web_page, url, payload)
1229
+ error_code = response_text.get('code')
1230
+ if str(error_code) != '0':
1231
+ raise send_exception(json.dumps(response_text, ensure_ascii=False))
1232
+
1233
+ list_item = response_text['info']['data']
1234
+ total = response_text['info']['meta']['count']
1235
+ totalPage = math.ceil(total / page_size)
1236
+
1237
+ for page in range(2, totalPage + 1):
1238
+ log(f'获取营销工具列表 第{page}/{totalPage}页')
1239
+ payload["page_num"] = page
1240
+ response_text = fetch(self.web_page, url, payload)
1241
+ list_item += response_text['info']['data']
1242
+ time.sleep(0.1)
1243
+
1244
+ write_dict_to_file(cache_file, list_item)
1245
+ return list_item
1246
+
1247
+ def query_goods_detail(self, activity_id):
1248
+ # web_page.goto(f'https://sso.geiwohuo.com/#/mrs/tools/activity/obm-time-limit-info/{activity_id}')
1249
+ # web_page.wait_for_load_state('load')
1250
+ log(f'正在获取 {self.store_name} {activity_id} 营销工具商品详情')
1251
+
1252
+ cache_file = f'{self.config.auto_dir}/shein/cache/query_goods_detail_{activity_id}.json'
1253
+ list_item = read_dict_from_file(cache_file, 3600 * 8)
1254
+ if len(list_item) > 0:
1255
+ return list_item
1256
+
1257
+ page_num = 1
1258
+ page_size = 100
1259
+ url = "https://sso.geiwohuo.com/mrs-api-prefix/promotion/simple_platform/query_goods_detail"
1260
+ payload = {
1261
+ "activity_id": activity_id,
1262
+ "page_num" : page_num,
1263
+ "page_size" : page_size
1264
+ }
1265
+ response_text = fetch(self.web_page, url, payload)
1266
+ error_code = response_text.get('code')
1267
+ if str(error_code) != '0':
1268
+ raise send_exception(json.dumps(response_text, ensure_ascii=False))
1269
+ list_item = response_text['info']['data']
1270
+ total = response_text['info']['meta']['count']
1271
+ totalPage = math.ceil(total / page_size)
1272
+
1273
+ for page in range(2, totalPage + 1):
1274
+ log(f'获取营销工具商品列表 第{page}/{totalPage}页')
1275
+ payload["page_num"] = page
1276
+ response_text = fetch(self.web_page, url, payload)
1277
+ list_item += response_text['info']['data']
1278
+ time.sleep(0.1)
1279
+
1280
+ write_dict_to_file(cache_file, list_item)
1281
+ return list_item
1282
+
1283
+ def get_partake_activity_goods_list(self):
1284
+ # self.web_page.goto(f'https://sso.geiwohuo.com/#/mbrs/marketing/list/1')
1285
+ # self.web_page.wait_for_load_state('load')
1286
+ log(f'正在获取 {self.store_name} 活动列表')
1287
+ page_num = 1
1288
+ page_size = 100
1289
+ date_60_days_ago = TimeUtils.get_past_nth_day(59)
1290
+ cache_file = f'{self.config.auto_dir}/shein/cache/platform_activity_{self.store_name}_{date_60_days_ago}_{TimeUtils.today_date()}.json'
1291
+ list_item = read_dict_from_file(cache_file, 3600 * 8)
1292
+ if len(list_item) > 0:
1293
+ return list_item
1294
+
1295
+ url = f"https://sso.geiwohuo.com/mrs-api-prefix/mbrs/activity/get_partake_activity_goods_list?page_num={page_num}&page_size={page_size}"
1296
+ payload = {
1297
+ "goods_audit_status" : 1,
1298
+ "insert_zone_time_end" : f"{TimeUtils.today_date()} 23:59:59",
1299
+ "insert_zone_time_start": f"{date_60_days_ago} 00:00:00"
1300
+ }
1301
+
1302
+ response_text = fetch(self.web_page, url, payload)
1303
+ error_code = response_text.get('code')
1304
+ if str(error_code) != '0':
1305
+ raise send_exception(json.dumps(response_text, ensure_ascii=False))
1306
+ list_item = response_text['info']['data']
1307
+ total = response_text['info']['meta']['count']
1308
+ totalPage = math.ceil(total / page_size)
1309
+
1310
+ for page in range(2, totalPage + 1):
1311
+ log(f'获取活动列表 第{page}/{totalPage}页')
1312
+ payload["page_num"] = page
1313
+ response_text = fetch(self.web_page, url, payload)
1314
+ list_item += response_text['info']['data']
1315
+ time.sleep(0.1)
1316
+
1317
+ write_dict_to_file(cache_file, list_item)
1318
+ return list_item
1319
+
1320
+ def generate_activity_price_dict(self):
1321
+ cache_file = f'{self.config.auto_dir}/shein/dict/activity_price_{self.store_name}.json'
1322
+ dict_activity_price = {}
1323
+ activity_list = self.query_obm_activity_list()
1324
+ for activity in activity_list:
1325
+ activity_id = activity['activity_id']
1326
+ activity_name = activity['act_name']
1327
+ sub_type_id = activity['sub_type_id'] # 1.不限量 2.限量
1328
+ dateBegin = TimeUtils.convert_datetime_to_date(activity['start_time'])
1329
+ dateEnd = TimeUtils.convert_datetime_to_date(activity['end_time'])
1330
+ skc_list = self.query_goods_detail(activity_id)
1331
+ for skc_item in skc_list:
1332
+ attend_num_sum = skc_item['attend_num_sum']
1333
+ product_act_price = skc_item['product_act_price'] # 活动价
1334
+ if sub_type_id == 1:
1335
+ attend_num_sum = '不限量'
1336
+ for sku_item in skc_item['sku_info_list']:
1337
+ sku = sku_item['sku'] # 平台sku
1338
+ product_act_price = sku_item['product_act_price'] if sku_item[
1339
+ 'product_act_price'] else product_act_price # 活动价
1340
+ key = f'{sku}_{dateBegin}_{dateEnd}_{activity_name}'
1341
+ dict_activity_price[key] = [product_act_price, attend_num_sum]
1342
+
1343
+ platform_activity_list = self.get_partake_activity_goods_list()
1344
+ for platform_activity in platform_activity_list:
1345
+ activity_name = platform_activity['activity_name']
1346
+ text_tag_content = platform_activity['text_tag_content']
1347
+ attend_num = platform_activity['attend_num']
1348
+ dateBegin = TimeUtils.convert_timestamp_to_date(platform_activity['start_time'])
1349
+ dateEnd = TimeUtils.convert_timestamp_to_date(platform_activity['end_time'])
1350
+ if text_tag_content != '新品':
1351
+ attend_num = '-'
1352
+ for sku_item in platform_activity['activity_sku_list']:
1353
+ sku = sku_item['sku_code']
1354
+ enroll_price = sku_item['enroll_display_str'][:-3]
1355
+ key = f'{sku}_{dateBegin}_{dateEnd}_{activity_name}'
1356
+ dict_activity_price[key] = [enroll_price, attend_num]
1357
+
1358
+ write_dict_to_file(cache_file, dict_activity_price)
1359
+
1360
+ def get_skc_week_actual_sales(self, skc):
1361
+ first_day, last_day = TimeUtils.get_past_7_days_range()
1362
+ cache_file = f'{self.config.auto_dir}/shein/cache/{skc}_{first_day}_{last_day}.json'
1363
+ if datetime.now().hour >= 9:
1364
+ DictSkuSalesDate = read_dict_from_file(cache_file)
1365
+ else:
1366
+ DictSkuSalesDate = read_dict_from_file(cache_file, 1800)
1367
+ if len(DictSkuSalesDate) > 0:
1368
+ return DictSkuSalesDate
1369
+
1370
+ url = f"https://sso.geiwohuo.com/idms/sale-trend/detail"
1371
+ payload = {
1372
+ "skc" : skc,
1373
+ "startDate": first_day,
1374
+ "endDate" : last_day,
1375
+ "daysToAdd": 0
1376
+ }
1377
+ response_text = fetch(self.web_page, url, payload)
1378
+ error_code = response_text.get('code')
1379
+ if str(error_code) != '0':
1380
+ log(response_text)
1381
+ return {}
1382
+ list_item = response_text['info']['salesVolumeDateVoList']
1383
+ for item in list_item:
1384
+ key = item['date']
1385
+ DictSkuSalesDate[key] = item['salesVolumeMap']
1386
+ list_item2 = response_text['info']['actualSalesVolumeMap']
1387
+ for item in list_item2:
1388
+ sku = item['skuCode']
1389
+ if sku is not None:
1390
+ DictSkuSalesDate[sku] = item['actualSalesVolume']
1391
+
1392
+ write_dict_to_file(cache_file, DictSkuSalesDate)
1393
+ return DictSkuSalesDate
1394
+
1395
+ def get_preemption_list(self, skc_list):
1396
+ url = f"https://sso.geiwohuo.com/idms/goods-skc/preemption-num"
1397
+ payload = skc_list
1398
+ response_text = fetch(self.web_page, url, payload)
1399
+ error_code = response_text.get('code')
1400
+ if str(error_code) != '0':
1401
+ raise send_exception(json.dumps(response_text, ensure_ascii=False))
1402
+
1403
+ dict = response_text['info']
1404
+
1405
+ cache_file = f'{self.config.auto_dir}/shein/preemption_num/preemption_num_{self.store_username}.json'
1406
+ dict_preemption_num = read_dict_from_file(cache_file)
1407
+ dict_preemption_num.update(dict)
1408
+ write_dict_to_file(cache_file, dict_preemption_num)
1409
+
1410
+ return dict
1411
+
1412
+ def get_activity_label(self, skc_list):
1413
+ url = f"https://sso.geiwohuo.com/idms/goods-skc/activity-label"
1414
+ payload = skc_list
1415
+ response_text = fetch(self.web_page, url, payload)
1416
+ error_code = response_text.get('code')
1417
+ if str(error_code) != '0':
1418
+ raise send_exception(json.dumps(response_text, ensure_ascii=False))
1419
+ dict = response_text['info']
1420
+
1421
+ cache_file = f'{self.config.auto_dir}/shein/activity_label/activity_label_{self.store_username}.json'
1422
+ dict_label = read_dict_from_file(cache_file)
1423
+ dict_label.update(dict)
1424
+ write_dict_to_file(cache_file, dict_label)
1425
+
1426
+ return dict
1427
+
1428
+ def get_sku_price_v2(self, skc_list):
1429
+ log(f'获取sku价格列表', skc_list)
1430
+ url = "https://sso.geiwohuo.com/idms/goods-skc/price"
1431
+ response_text = fetch(self.web_page, url, skc_list)
1432
+ error_code = response_text.get('code')
1433
+ if str(error_code) != '0':
1434
+ raise send_exception(json.dumps(response_text, ensure_ascii=False))
1435
+ dict = response_text['info']
1436
+
1437
+ cache_file = f'{self.config.auto_dir}/shein/sku_price/sku_price_{self.store_username}.json'
1438
+ dict_sku_price = read_dict_from_file(cache_file)
1439
+ dict_sku_price.update(dict)
1440
+ write_dict_to_file(cache_file, dict_sku_price)
1441
+
1442
+ return dict
1443
+
1444
+ def get_stock_advice(self, skc_list):
1445
+ log(f'获取sku库存建议列表', skc_list)
1446
+ url = f"https://sso.geiwohuo.com/idms/goods-skc/get-vmi-spot-advice"
1447
+ payload = skc_list
1448
+ response_text = fetch(self.web_page, url, payload)
1449
+ error_code = response_text.get('code')
1450
+ if str(error_code) != '0':
1451
+ raise send_exception(json.dumps(response_text, ensure_ascii=False))
1452
+ dict = response_text['info']
1453
+
1454
+ cache_file = f'{self.config.auto_dir}/shein/vmi_spot_advice/spot_advice_{self.store_username}.json'
1455
+ dict_advice = read_dict_from_file(cache_file)
1456
+ dict_advice.update(dict)
1457
+ write_dict_to_file(cache_file, dict_advice)
1458
+
1459
+ return dict
1460
+
1461
+ def get_dt_time(self):
1462
+ if self.dt is not None:
1463
+ log(f'字典dt: {self.dt}')
1464
+ return self.dt
1465
+ log('获取非实时更新时间')
1466
+ url = "https://sso.geiwohuo.com/sbn/common/get_update_time"
1467
+ payload = {
1468
+ "pageCode": "Index",
1469
+ "areaCd" : "cn"
1470
+ }
1471
+ response_text = fetch(self.web_page, url, payload)
1472
+ error_code = response_text.get('code')
1473
+ if str(error_code) != '0':
1474
+ raise send_exception(json.dumps(response_text, ensure_ascii=False))
1475
+ self.dt = response_text.get('info').get('dt')
1476
+ log(f'dt: {self.dt}')
1477
+ return self.dt
1478
+
1479
+ def get_dict_skc_week_trend(self):
1480
+ page_num = 1
1481
+ page_size = 100
1482
+
1483
+ date_7_days_ago = TimeUtils.get_past_nth_day(7, None, '%Y%m%d')
1484
+ log('-7', date_7_days_ago)
1485
+ date_1_days_ago = TimeUtils.get_past_nth_day(1, None, '%Y%m%d')
1486
+ log('-1', date_1_days_ago)
1487
+
1488
+ url = f"https://sso.geiwohuo.com/sbn/new_goods/get_skc_diagnose_list"
1489
+ payload = {
1490
+ "areaCd" : "cn",
1491
+ "countrySite": [
1492
+ "shein-all"
1493
+ ],
1494
+ "startDate" : date_7_days_ago,
1495
+ "endDate" : date_1_days_ago,
1496
+ "pageNum" : page_num,
1497
+ "pageSize" : page_size
1498
+ }
1499
+ response_text = fetch(self.web_page, url, payload)
1500
+ error_code = response_text.get('code')
1501
+ if str(error_code) != '0':
1502
+ raise send_exception(json.dumps(response_text, ensure_ascii=False))
1503
+ spu_list = response_text['info']['data']
1504
+ total = response_text['info']['meta']['count']
1505
+ totalPage = math.ceil(total / page_size)
1506
+
1507
+ for page in range(2, totalPage + 1):
1508
+ log(f'获取商品列表 第{page}/{totalPage}页')
1509
+ page_num = page
1510
+ payload = {
1511
+ "areaCd" : "cn",
1512
+ "countrySite": [
1513
+ "shein-all"
1514
+ ],
1515
+ "startDate" : date_7_days_ago,
1516
+ "endDate" : date_1_days_ago,
1517
+ "pageNum" : page_num,
1518
+ "pageSize" : page_size
1519
+ }
1520
+ response_text = fetch(self.web_page, url, payload)
1521
+ spu_list_new = response_text['info']['data']
1522
+ spu_list += spu_list_new
1523
+ time.sleep(0.3)
1524
+
1525
+ DictSkcWeekTrend = {}
1526
+ for spu_item in spu_list:
1527
+ skc = str(spu_item['skc'])
1528
+ DictSkcWeekTrend[skc] = spu_item
1529
+
1530
+ log('len(DictSkcWeekTrend)', len(DictSkcWeekTrend))
1531
+ write_dict_to_file(f'{self.config.auto_dir}/shein/dict/dict_skc_week_trend_{self.store_username}.json', DictSkcWeekTrend)
1532
+ return DictSkcWeekTrend
1533
+
1534
+ def get_skc_sales(self, skc, start_date, end_date):
1535
+ url = "https://sso.geiwohuo.com/idms/stockadvice/saleTrendDetail"
1536
+ payload = {
1537
+ "skc" : skc,
1538
+ "startDate": start_date,
1539
+ "endDate" : end_date
1540
+ }
1541
+ response_text = fetch(self.web_page, url, payload)
1542
+ error_code = response_text.get('code')
1543
+ error_msg = response_text.get('msg')
1544
+ if str(error_code) != '0':
1545
+ raise send_exception(json.dumps(response_text, ensure_ascii=False))
1546
+
1547
+ sales_list = response_text['info']['saleTrendDetailList']
1548
+ if sales_list:
1549
+ for skc_list in sales_list:
1550
+ date = skc_list['date']
1551
+ if date == '合计':
1552
+ log('无销量skc: ', skc)
1553
+ continue
1554
+ skc_sale = skc_list['skcSale']
1555
+ skc_order = skc_list['skcOrder']
1556
+ for sku_list in skc_list['skuSaleTrendDetailList']:
1557
+ sku = sku_list['skuCode']
1558
+ attr_name = sku_list['attributeName']
1559
+ sku_sale = sku_list['skuSale']
1560
+ sku_order = sku_list['skuOrder']
1561
+ if sku_sale > 0:
1562
+ insert_sales(skc, date, skc_sale, skc_order, sku, attr_name, sku_sale, sku_order)
1563
+ return sales_list
1564
+
1565
+ # 获取商品包含sku销量的列表
1566
+ def get_product_sku_sales_list(self, source='mb'):
1567
+ log(f'获取销量列表')
1568
+ url = "https://sso.geiwohuo.com/idms/goods-skc/list"
1569
+ pageNumber = 1
1570
+ pageSize = 100
1571
+ dictPayload = {
1572
+ "pageNumber" : pageNumber,
1573
+ "pageSize" : pageSize,
1574
+ "supplierCodes" : "",
1575
+ "skcs" : "",
1576
+ "spu" : "",
1577
+ "c7dSaleCntBegin" : "",
1578
+ "c7dSaleCntEnd" : "",
1579
+ "goodsLevelIdList" : [],
1580
+ "supplyStatus" : "",
1581
+ "shelfStatus" : 1,
1582
+ "categoryIdList" : [],
1583
+ "skcStockBegin" : "",
1584
+ "skcStockEnd" : "",
1585
+ "skuStockBegin" : "",
1586
+ "skuStockEnd" : "",
1587
+ "skcSaleDaysBegin" : "",
1588
+ "skcSaleDaysEnd" : "",
1589
+ "skuSaleDaysBegin" : "",
1590
+ "skuSaleDaysEnd" : "",
1591
+ "planUrgentCountBegin" : "",
1592
+ "planUrgentCountEnd" : "",
1593
+ "skcAvailableOrderBegin": "",
1594
+ "skcAvailableOrderEnd" : "",
1595
+ "skuAvailableOrderBegin": "",
1596
+ "skuAvailableOrderEnd" : "",
1597
+ "shelfDateBegin" : "",
1598
+ "shelfDateEnd" : "",
1599
+ "stockWarnStatusList" : [],
1600
+ "labelFakeIdList" : [],
1601
+ "sheinSaleByInventory" : "",
1602
+ "tspIdList" : [],
1603
+ "adviceStatus" : [],
1604
+ "sortBy7dSaleCnt" : 2
1605
+ }
1606
+ payload = dictPayload
1607
+ response_text = fetch(self.web_page, url, payload)
1608
+ error_code = response_text.get('code')
1609
+ if str(error_code) != '0':
1610
+ raise send_exception(json.dumps(response_text, ensure_ascii=False))
1611
+
1612
+ spu_list = response_text['info']['list']
1613
+
1614
+ total = response_text['info']['count']
1615
+ totalPage = math.ceil(total / pageSize)
1616
+ for page in range(2, totalPage + 1):
1617
+ log(f'获取SKU销量列表 第{page}/{totalPage}页')
1618
+ dictPayload['pageNumber'] = page
1619
+ payload = dictPayload
1620
+ response_text = fetch(self.web_page, url, payload)
1621
+ spu_list_new = response_text['info']['list']
1622
+ spu_list += spu_list_new
1623
+ time.sleep(0.3)
1624
+
1625
+ cache_file = f'{self.config.auto_dir}/shein/sku_price/sku_price_{self.store_username}.json'
1626
+ dict_sku_price = read_dict_from_file(cache_file)
1627
+
1628
+ cache_file2 = f'{self.config.auto_dir}/shein/dict/dict_skc_week_trend_{self.store_username}.json'
1629
+ DictSkcWeekTrend = read_dict_from_file(cache_file2)
1630
+
1631
+ cache_file3 = f'{self.config.auto_dir}/shein/dict/product_list_{self.store_username}.json'
1632
+ DictSpuInfo = read_dict_from_file(cache_file3)
1633
+
1634
+ cache_file = f'{self.config.auto_dir}/shein/dict/activity_price_{self.store_name}.json'
1635
+ dictActivityPrice = read_dict_from_file(cache_file)
1636
+
1637
+ product_sku_list = [
1638
+ [
1639
+ '店铺名称', '商品信息', 'SKC图片', 'SKU图片', 'SKU', 'SKU货号', '在售天数', '库存(模式/本地/在途/希音)',
1640
+ '今天销量', '今日订单数', # 9
1641
+ '远7天销量', '远7天订单数', '近7天销量', '近7天订单数', '周销增量', '远30天销量', '远30天订单数',
1642
+ '近30天销量', '近30天订单数', '月销增量', '总销量', # 11
1643
+ '申报价', '成本价', '毛利润', '毛利率', '近7天利润', '近30天利润', # 6
1644
+ 'SPU', 'SKC', 'SKC货号', '近7天SKU销量/SKC销量/SKC曝光', 'SKC点击率/SKC转化率', '自主参与活动', '商品标题', '叶子类目', # 5
1645
+ 'SKC近7天销量', 'SKC近7天曝光人数', 'SKC近7天商详访客', 'SKC近7天点击率', 'SKC近7天支付人数',
1646
+ 'SKC近7天支付率', 'SKC近7天评论数'
1647
+ ]
1648
+ ]
1649
+
1650
+ date_60_days_ago = TimeUtils.get_past_nth_day(60, None, '%Y-%m-%d')
1651
+ log('-60', date_60_days_ago)
1652
+ date_7_days_ago = TimeUtils.get_past_nth_day(7, None, '%Y-%m-%d')
1653
+ log('-7', date_7_days_ago)
1654
+ date_1_days_ago = TimeUtils.get_past_nth_day(1, None, '%Y-%m-%d')
1655
+ log('-1', date_1_days_ago)
1656
+
1657
+ count = 0
1658
+ for spu_info in spu_list:
1659
+ count += 1
1660
+ # if count > 10:
1661
+ # break
1662
+ spu = spu_info['spu']
1663
+ skc = str(spu_info['skc'])
1664
+ # if not shein_db.exists_sales_1_days_ago(skc):
1665
+ # log(f'未查到昨天销量: {skc}')
1666
+ self.get_skc_sales(skc, date_60_days_ago, date_1_days_ago)
1667
+ skcCode = spu_info['supplierCode']
1668
+ product_name = DictSpuInfo[spu]['product_name_en']
1669
+ category_name = spu_info['categoryName']
1670
+ shelfDays = spu_info['shelfDays']
1671
+ shelf_status = DictSpuInfo[spu]['shelf_status']
1672
+ dictStatus = {
1673
+ 'WAIT_SHELF': "待上架",
1674
+ 'ON_SHELF' : "已上架",
1675
+ 'SOLD_OUT' : "已售罄",
1676
+ 'OUT_SHELF' : "已下架"
1677
+ }
1678
+ status_cn = dictStatus[shelf_status]
1679
+ good_level = spu_info['goodsLevel']['name']
1680
+ sale_model = spu_info['saleModel']['name']
1681
+
1682
+ for sku_info in spu_info['skuList']:
1683
+ sku = sku_info['skuCode']
1684
+ skuExtCode = str(sku_info['supplierSku'])
1685
+ if sku == '合计':
1686
+ continue
1687
+
1688
+ # 获取基础数据
1689
+ stock = self.bridge.get_sku_stock(skuExtCode, source)
1690
+ cost_price = self.bridge.get_sku_cost(skuExtCode, source)
1691
+ sku_img = self.bridge.get_sku_img(skuExtCode, source)
1692
+
1693
+ # 计算库存相关数据
1694
+ shein_stock = sku_info['stock']
1695
+ transit = sku_info['transit'] # 在途
1696
+ real_transit = transit + sku_info['stayShelf'] - sku_info['transitSale']
1697
+
1698
+ # 获取销量数据
1699
+ week_sales = get_last_week_sales(sku)
1700
+ week_sales2 = get_near_week_sales(sku)
1701
+ month_sales = get_last_month_sales(sku)
1702
+ month_sales2 = get_near_month_sales(sku)
1703
+
1704
+ # 获取SKC趋势数据
1705
+ key = str(skc)
1706
+ skc_trend = DictSkcWeekTrend.get(key, {})
1707
+
1708
+ # 使用append组装数据
1709
+ sku_item = []
1710
+
1711
+ # 店铺名称
1712
+ sku_item.append(f'{self.store_name}\n({status_cn})\n{good_level}\n{date_7_days_ago}\n{date_1_days_ago}')
1713
+
1714
+ # 商品信息
1715
+ product_info = f"SPU: {spu}\nSKC: {skc}\nSKC货号: {skcCode}\n类目: {category_name}\n在售天数: {shelfDays}"
1716
+ sku_item.append(product_info)
1717
+
1718
+ # SKC图片
1719
+ sku_item.append(spu_info['picUrl'])
1720
+
1721
+ # SKU图片
1722
+ sku_item.append(sku_img)
1723
+
1724
+ # SKU基本信息
1725
+ sku_item.append(sku) # SKU
1726
+ sku_item.append(f"{sku_info['supplierSku']}") # SKU货号
1727
+ sku_item.append(shelfDays) # 在售天数
1728
+
1729
+ # 库存信息
1730
+ sku_item.append(f'{sale_model}\n{stock}/{real_transit}/{shein_stock}')
1731
+
1732
+ # 今日销量数据
1733
+ sku_item.append(sku_info['totalSaleVolume']) # 今日销量
1734
+ sku_item.append(sku_info['orderCnt']) # 今日订单数
1735
+
1736
+ # 远7天销量数据
1737
+ sku_item.append(week_sales[0]) # 远7日销量
1738
+ sku_item.append(week_sales[1]) # 远7日订单数
1739
+
1740
+ # 近7天销量数据
1741
+ sku_item.append(week_sales2[0]) # 近7日销量
1742
+ sku_item.append(week_sales2[1]) # 近7日订单数
1743
+ sku_item.append(week_sales2[1] - week_sales2[0]) # 周增销量
1744
+
1745
+ # 远30天销量数据
1746
+ sku_item.append(month_sales[0]) # 远30日销量
1747
+ sku_item.append(month_sales[1]) # 远30日订单数
1748
+
1749
+ # 近30天销量数据
1750
+ sku_item.append(month_sales2[0]) # 近30日销量
1751
+ sku_item.append(month_sales2[1]) # 近30日订单数
1752
+ sku_item.append(month_sales2[1] - month_sales2[0]) # 月增销量
1753
+
1754
+ # 总销量
1755
+ sku_item.append('-')
1756
+
1757
+ # 价格相关
1758
+ sku_item.append(dict_sku_price[sku]) # 申报价
1759
+ sku_item.append(cost_price) # 成本价
1760
+ sku_item.append('') # 毛利润
1761
+ sku_item.append('') # 毛利率
1762
+ sku_item.append('') # 近7天利润
1763
+ sku_item.append('') # 近30天利润
1764
+
1765
+ # 商品标识
1766
+ sku_item.append(spu) # SPU
1767
+ sku_item.append(skc) # SKC
1768
+ sku_item.append(spu_info['supplierCode']) # SKC货号
1769
+
1770
+ sale_num_list, sale_data_list = self.get_skc_week_sale_list(spu, skc, sku)
1771
+ sku_item.append("\n".join(sale_num_list))
1772
+ sku_item.append("\n".join(sale_data_list))
1773
+ sku_item.append(self.get_skc_activity_label(skc, sku, dictActivityPrice))
1774
+
1775
+ sku_item.append(product_name) # 商品标题
1776
+ sku_item.append(category_name) # 叶子类目
1777
+
1778
+ # SKC趋势数据
1779
+ sku_item.append(skc_trend.get('saleCnt', 0)) # SKC近7天销量
1780
+ sku_item.append(skc_trend.get('epsUvIdx', 0)) # SKC近7天曝光人数
1781
+ sku_item.append(skc_trend.get('goodsUvIdx', 0)) # SKC近7天商详访客
1782
+ sku_item.append(skc_trend.get('epsGdsCtrIdx', 0)) # SKC近7天点击率
1783
+ sku_item.append(skc_trend.get('payUvIdx', 0)) # SKC近7天支付人数
1784
+ sku_item.append(skc_trend.get('gdsPayCtrIdx', 0)) # SKC近7天支付率
1785
+ sku_item.append(skc_trend.get('totalCommentCnt', 0)) # 评论数
1786
+
1787
+ product_sku_list.append(sku_item)
1788
+
1789
+ cache_file = f'{self.config.auto_dir}/shein/cache/week_sales_{TimeUtils.today_date()}.json'
1790
+ write_dict_to_file_ex(cache_file, {self.store_name: product_sku_list}, [self.store_name])
1791
+
1792
+ return product_sku_list
1793
+
1794
+ # 获取一个skc一周内的销售趋势(商品明细中的)
1795
+ def get_dict_skc_week_trend_v2(self, spu, skc):
1796
+ dt = self.get_dt_time()
1797
+
1798
+ date_7_days_ago = TimeUtils.get_past_nth_day(7, None, '%Y%m%d')
1799
+ log('-7', date_7_days_ago)
1800
+ date_1_days_ago = TimeUtils.get_past_nth_day(1, None, '%Y%m%d')
1801
+ log('-1', date_1_days_ago)
1802
+
1803
+ cache_file = f'{self.config.auto_dir}/shein/dict/dict_skc_week_trend_{skc}_{date_7_days_ago}_{date_1_days_ago}.json'
1804
+ if datetime.now().hour >= 9:
1805
+ DictSkc = read_dict_from_file(cache_file)
1806
+ else:
1807
+ DictSkc = read_dict_from_file(cache_file, 1800)
1808
+ if len(DictSkc) > 0:
1809
+ return DictSkc
1810
+
1811
+ url = f"https://sso.geiwohuo.com/sbn/new_goods/get_skc_diagnose_trend"
1812
+ payload = {
1813
+ "areaCd" : "cn",
1814
+ "countrySite": [
1815
+ "shein-all"
1816
+ ],
1817
+ "dt" : dt,
1818
+ "endDate" : date_1_days_ago,
1819
+ "spu" : [spu],
1820
+ "skc" : [skc],
1821
+ "startDate" : date_7_days_ago,
1822
+ }
1823
+ response_text = fetch(self.web_page, url, payload)
1824
+ error_code = response_text.get('code')
1825
+ if str(error_code) != '0':
1826
+ raise send_exception(json.dumps(response_text, ensure_ascii=False))
1827
+
1828
+ data_list = response_text['info']
1829
+ DictSkc = {}
1830
+ for date_item in data_list:
1831
+ dataDate = date_item['dataDate']
1832
+ # epsUvIdx = date_item['epsUvIdx']
1833
+ # saleCnt = date_item['saleCnt']
1834
+ DictSkc[dataDate] = date_item
1835
+
1836
+ log('len(DictSkc)', len(DictSkc))
1837
+ write_dict_to_file(cache_file, DictSkc)
1838
+ return DictSkc
1839
+
1840
+ def get_skc_week_sale_list(self, spu, skc, sku):
1841
+ dict_skc = self.get_dict_skc_week_trend_v2(spu, skc)
1842
+ date_list = TimeUtils.get_past_7_days_list()
1843
+ first_day, last_day = TimeUtils.get_past_7_days_range()
1844
+ cache_file = f'{self.config.auto_dir}/shein/cache/{skc}_{first_day}_{last_day}.json'
1845
+ DictSkuSalesDate = read_dict_from_file(cache_file)
1846
+ sales_detail = []
1847
+ for date in date_list:
1848
+ sales_num = DictSkuSalesDate.get(date, {}).get(sku, {}).get("hisActualValue", 0)
1849
+ sales_num = sales_num if sales_num is not None else 0
1850
+
1851
+ saleCnt = get_safe_value(dict_skc.get(date, {}), 'saleCnt', 0)
1852
+ epsUvIdx = get_safe_value(dict_skc.get(date, {}), 'epsUvIdx', 0)
1853
+
1854
+ sales_detail.append(f'{date}({TimeUtils.get_weekday_name(date)}): {sales_num}/{saleCnt}/{epsUvIdx}')
1855
+
1856
+ sales_data = []
1857
+ for date in date_list:
1858
+ goodsUvIdx = get_safe_value(dict_skc.get(date, {}), 'goodsUvIdx', 0) # 商详访客
1859
+ epsGdsCtrIdx = get_safe_value(dict_skc.get(date, {}), 'epsGdsCtrIdx', 0) # 点击率
1860
+
1861
+ payUvIdx = get_safe_value(dict_skc.get(date, {}), 'payUvIdx', 0) # 支付人数
1862
+ gdsPayCtrIdx = get_safe_value(dict_skc.get(date, {}), 'gdsPayCtrIdx', 0) # 转化率
1863
+
1864
+ sales_data.append(f'{date}({TimeUtils.get_weekday_name(date)}): {epsGdsCtrIdx:.2%}({goodsUvIdx})/{gdsPayCtrIdx:.2%}({payUvIdx})')
1865
+
1866
+ return sales_detail, sales_data
1867
+
1868
+ def get_activity_price(self, activity_dict, sku, activity_name, dateBegin, dateEnd):
1869
+ key = f'{sku}_{dateBegin}_{dateEnd}_{activity_name}'
1870
+ price_info = activity_dict.get(key, ['-', '-'])
1871
+ return f'活动价:¥{price_info[0]}, 活动库存:{price_info[1]}'
1872
+
1873
+ def get_skc_activity_label(self, skc, sku, dict_activity_price=None):
1874
+ cache_file = f'{self.config.auto_dir}/shein/activity_label/activity_label_{self.store_username}.json'
1875
+ dict_label = read_dict_from_file(cache_file)
1876
+ operateLabelList = dict_label[skc]['operateLabelList']
1877
+ activityList = []
1878
+ activityList2 = []
1879
+ for item in operateLabelList:
1880
+ if item['name'] == '活动中':
1881
+ activityList.extend(item.get('activityList', []))
1882
+ if item['name'] == '即将开始':
1883
+ activityList2.extend(item.get('activityList', []))
1884
+
1885
+ if activityList:
1886
+ activityLabel = '\n'.join([
1887
+ f' [{act["date"]}]\n【{self.get_activity_price(dict_activity_price, sku, act["name"], act["dateBegin"], act["dateEnd"])}】{act["name"]}\n'
1888
+ for act in activityList])
1889
+ else:
1890
+ activityLabel = '无'
1891
+ if activityList2:
1892
+ activityLabel2 = '\n'.join([
1893
+ f' [{act["date"]}]\n【{self.get_activity_price(dict_activity_price, sku, act["name"], act["dateBegin"], act["dateEnd"])}】{act["name"]}\n'
1894
+ for act in activityList2])
1895
+ else:
1896
+ activityLabel2 = '无'
1897
+ return f'活动中:\n{activityLabel}\n即将开始:\n{activityLabel2}'
1898
+
1899
+ # 获取商品包含sku销量的列表
1900
+ # mode = 1.备货建议 2.已上架 3.昨日上架 4.昨日出单
1901
+ # 5.采购-缺货要补货 (有现货建议 建议采购为正 有销量)
1902
+ # 6.运营采购-滞销清库存 (无现货建议 建议采购为负 30天外 无销量)
1903
+ # 7.运营-新品上架需要优化 (无现货建议 建议采购为负 上架15天内)
1904
+ # 8.运营-潜在滞销款 (无现货建议 30天外 有销量)
1905
+ # 9.运营-潜力热销款 (有现货建议 30天内 有销量)
1906
+ # 10.运营-热销款 (有现货建议 30天外 有销量)
1907
+ def get_bak_advice(self, mode=1, skcs=None, source='mb'):
1908
+ log(f'获取备货信息商品列表 做成字典')
1909
+ if skcs == None or len(skcs) == 0:
1910
+ # if mode == 3:
1911
+ # skcs = "sh2405133614611175" # 这是一个不存在的skc
1912
+ # else:
1913
+ skcs = ""
1914
+ else:
1915
+ skcs = ",".join(skcs)
1916
+
1917
+ url = "https://sso.geiwohuo.com/idms/goods-skc/list"
1918
+ pageNumber = 1
1919
+ pageSize = 100
1920
+ dictPayload = {
1921
+ "pageNumber" : pageNumber,
1922
+ "pageSize" : pageSize,
1923
+ "supplierCodes" : "",
1924
+ "skcs" : skcs,
1925
+ "spu" : "",
1926
+ "c7dSaleCntBegin" : "",
1927
+ "c7dSaleCntEnd" : "",
1928
+ "goodsLevelIdList" : [10, 107, 61, 90, 87, 237, 220, 219, 88, 75, 62, 227, 12, 230, 80, 58, 224, 97],
1929
+ "supplyStatus" : "",
1930
+ "shelfStatus" : "",
1931
+ "categoryIdList" : [],
1932
+ "skcStockBegin" : "",
1933
+ "skcStockEnd" : "",
1934
+ "skuStockBegin" : "",
1935
+ "skuStockEnd" : "",
1936
+ "skcSaleDaysBegin" : "",
1937
+ "skcSaleDaysEnd" : "",
1938
+ "skuSaleDaysBegin" : "",
1939
+ "skuSaleDaysEnd" : "",
1940
+ "planUrgentCountBegin" : "",
1941
+ "planUrgentCountEnd" : "",
1942
+ "skcAvailableOrderBegin": "",
1943
+ "skcAvailableOrderEnd" : "",
1944
+ "skuAvailableOrderBegin": "",
1945
+ "skuAvailableOrderEnd" : "",
1946
+ "shelfDateBegin" : "",
1947
+ "shelfDateEnd" : "",
1948
+ "stockWarnStatusList" : [],
1949
+ "labelFakeIdList" : [],
1950
+ "sheinSaleByInventory" : "",
1951
+ "tspIdList" : [],
1952
+ "adviceStatus" : [],
1953
+ "sortBy7dSaleCnt" : 2,
1954
+ "goodsLevelFakeIdList" : [1, 2, 3, 8, 14, 15, 4, 11]
1955
+ }
1956
+ payload = dictPayload
1957
+ response_text = fetch(self.web_page, url, payload)
1958
+ error_code = response_text.get('code')
1959
+ if str(error_code) != '0':
1960
+ raise send_exception(json.dumps(response_text, ensure_ascii=False))
1961
+
1962
+ spu_list = response_text['info']['list']
1963
+
1964
+ skc_list = [item['skc'] for item in spu_list]
1965
+ self.get_activity_label(skc_list)
1966
+ self.get_preemption_list(skc_list)
1967
+ self.get_sku_price_v2(skc_list)
1968
+ self.get_stock_advice(skc_list)
1969
+
1970
+ total = response_text['info']['count']
1971
+ totalPage = math.ceil(total / pageSize)
1972
+ for page in range(2, totalPage + 1):
1973
+ log(f'获取备货信息商品列表 第{page}/{totalPage}页')
1974
+ dictPayload['pageNumber'] = page
1975
+ payload = dictPayload
1976
+ response_text = fetch(self.web_page, url, payload)
1977
+ spu_list_new = response_text['info']['list']
1978
+
1979
+ skc_list = [item['skc'] for item in spu_list_new]
1980
+ self.get_activity_label(skc_list)
1981
+ self.get_preemption_list(skc_list)
1982
+ self.get_sku_price_v2(skc_list)
1983
+ self.get_stock_advice(skc_list)
1984
+
1985
+ spu_list += spu_list_new
1986
+ time.sleep(0.3)
1987
+
1988
+ cache_file = f'{self.config.auto_dir}/shein/dict/activity_price_{self.store_name}.json'
1989
+ dictActivityPrice = read_dict_from_file(cache_file)
1990
+ # cache_file = f'{self.config.auto_dir}/shein/dict/product_list_{self.store_username}.json'
1991
+ # DictSpuInfo = read_dict_from_file(cache_file, 5)
1992
+ cache_file = f'{self.config.auto_dir}/shein/preemption_num/preemption_num_{self.store_username}.json'
1993
+ dict_preemption_num = read_dict_from_file(cache_file)
1994
+ cache_file = f'{self.config.auto_dir}/shein/vmi_spot_advice/spot_advice_{self.store_username}.json'
1995
+ dict_advice = read_dict_from_file(cache_file)
1996
+ cache_file = f'{self.config.auto_dir}/shein/sku_price/sku_price_{self.store_username}.json'
1997
+ dict_sku_price = read_dict_from_file(cache_file)
1998
+ date_list = TimeUtils.get_past_7_days_list()
1999
+ if mode in [2, 5, 6, 7, 8, 9, 10]:
2000
+ excel_data = [[
2001
+ '店铺名称', 'SKC图片', 'SKU图片', '商品信息', '建议现货数量', '现有库存数量', '已采购数量', '预测日销',
2002
+ '本地和采购可售天数', '生产天数', '建议采购', '产品起定量',
2003
+ '备货周期(天)', '备货建议', '近7天SKU销量/SKC销量/SKC曝光', 'SKC点击率/SKC转化率', '自主参与活动',
2004
+ 'SKC',
2005
+ "SKU"
2006
+ ]]
2007
+ else:
2008
+ excel_data = [[
2009
+ '店铺名称', 'SKC图片', 'SKU图片', '商品信息', '备货建议', '近7天SKU销量/SKC销量/SKC曝光',
2010
+ 'SKC点击率/SKC转化率', '自主参与活动', 'SKC', "SKU"
2011
+ ]]
2012
+ for spu_info in spu_list:
2013
+ spu = str(spu_info['spu'])
2014
+ skc = str(spu_info['skc'])
2015
+
2016
+ status_cn = spu_info['shelfStatus']['name']
2017
+ if status_cn != '已上架':
2018
+ continue
2019
+
2020
+ # shelf_status = DictSpuInfo.get(spu, {}).get('shelf_status', '')
2021
+ # if mode != 1:
2022
+ # if shelf_status != 'ON_SHELF' and shelf_status != 'SOLD_OUT':
2023
+ # log('跳过', skc, shelf_status)
2024
+ # continue
2025
+
2026
+ # if mode in [5, 6, 7, 8, 9, 10] and shelf_status == 'SOLD_OUT':
2027
+ # continue
2028
+ #
2029
+ # dictStatus = {
2030
+ # 'WAIT_SHELF': "待上架",
2031
+ # 'ON_SHELF': "已上架",
2032
+ # 'SOLD_OUT': "已售罄",
2033
+ # 'OUT_SHELF': "已下架"
2034
+ # }
2035
+ # status_cn = dictStatus.get(shelf_status, '-')
2036
+
2037
+ sale_model = spu_info['saleModel']['name']
2038
+ goods_level = spu_info['goodsLevel']['name']
2039
+ goods_label = [label["name"] for label in spu_info['goodsLabelList']]
2040
+ skc_img = spu_info['picUrl']
2041
+ shelfDate = spu_info['shelfDate']
2042
+ shelfDays = spu_info['shelfDays']
2043
+ categoryName = spu_info['categoryName']
2044
+
2045
+ if mode in [3] and shelfDays != 1:
2046
+ continue
2047
+
2048
+ DictSkuSalesDate = self.get_skc_week_actual_sales(skc)
2049
+
2050
+ for sku_info in spu_info['skuList']:
2051
+ row_item = []
2052
+ attr = sku_info['attr']
2053
+ if attr == '合计':
2054
+ continue
2055
+ predictDaySales = sku_info['predictDaySales']
2056
+ availableOrderCount = sku_info['availableOrderCount']
2057
+ if mode == 1:
2058
+ if availableOrderCount is None or availableOrderCount <= 0:
2059
+ log('跳过', skc, availableOrderCount)
2060
+ continue
2061
+
2062
+ row_item.append(f'{self.store_name}\n({status_cn})\n{goods_level}\n{",".join(goods_label)}')
2063
+ row_item.append(skc_img)
2064
+ sku = sku_info['skuCode']
2065
+ skuExtCode = str(sku_info['supplierSku'])
2066
+ sku_img = self.bridge.get_sku_img(skuExtCode, source)
2067
+ row_item.append(sku_img)
2068
+
2069
+ transit = sku_info['transit'] # 在途
2070
+
2071
+ stock = self.bridge.get_sku_stock(skuExtCode, source)
2072
+ cost_price = self.bridge.get_sku_cost(skuExtCode, source)
2073
+
2074
+ supplyPrice = dict_sku_price[sku]
2075
+ shein_stock = sku_info['stock']
2076
+ if cost_price == '-':
2077
+ profit = '-'
2078
+ else:
2079
+ profit = f'{float(supplyPrice) - float(cost_price):.2f}'
2080
+
2081
+ min_spot_advice = dict_advice.get(skc, {}).get(sku, {}).get('minSpotAdvice', 0)
2082
+ max_spot_advice = dict_advice.get(skc, {}).get(sku, {}).get('maxSpotAdvice', 0)
2083
+ stock_advice = f'{min_spot_advice}~{max_spot_advice}'
2084
+ log('stock_advice', stock_advice)
2085
+ # 建议现货数量
2086
+ advice_stock_number = round((min_spot_advice + max_spot_advice) / 4)
2087
+
2088
+ # 有现货建议
2089
+ if mode in [5, 9, 10] and advice_stock_number == 0:
2090
+ continue
2091
+
2092
+ # 无现货建议
2093
+ if mode in [6, 7, 8] and advice_stock_number > 0:
2094
+ continue
2095
+
2096
+ stockSaleDays = sku_info['stockSaleDays']
2097
+
2098
+ product_info = (
2099
+ f'SPU: {spu}\n'
2100
+ f'SKC: {skc}\n'
2101
+ f'SKU货号: {skuExtCode}\n'
2102
+ f'属性集: {attr}\n'
2103
+ f'商品分类: {categoryName}\n'
2104
+ f'上架日期: {shelfDate}\n'
2105
+ f'上架天数: {shelfDays}\n'
2106
+ f'库存可售天数/现货建议: {stockSaleDays}/{stock_advice}\n'
2107
+ )
2108
+ row_item.append(product_info)
2109
+
2110
+ # 建议采购数量逻辑
2111
+ try:
2112
+ # 尝试将字符串数字转换为 float,再转为 int(如有必要)
2113
+ current_stock = float(stock)
2114
+ advice_purchase_number = advice_stock_number - int(current_stock)
2115
+
2116
+ # 建议采购为正
2117
+ if (mode == 5 and advice_purchase_number <= 0):
2118
+ continue
2119
+
2120
+ except (ValueError, TypeError):
2121
+ # 无法转换为数值时
2122
+ advice_purchase_number = '-'
2123
+
2124
+ if mode in [2, 5, 6, 7, 8, 9, 10]:
2125
+ row_item.append(advice_stock_number)
2126
+ row_item.append(stock)
2127
+
2128
+ row_item.append(0)
2129
+ row_item.append(predictDaySales)
2130
+ row_item.append(0)
2131
+ row_item.append(7)
2132
+
2133
+ row_item.append(advice_purchase_number)
2134
+ row_item.append(0) # 产品起定量
2135
+ row_item.append(0) # 备货周期(天)
2136
+
2137
+ adviceOrderCount = sku_info['adviceOrderCount'] if sku_info['adviceOrderCount'] is not None else '-'
2138
+ if sku_info['autoOrderStatus'] is not None:
2139
+ autoOrderStatus = ['-', '是', '否'][sku_info['autoOrderStatus']] if sku_info[
2140
+ 'adviceOrderCount'] is not None else '-'
2141
+ else:
2142
+ autoOrderStatus = '-'
2143
+ orderCount = sku_info['orderCount'] # 已下单数
2144
+ c7dSaleCnt = sku_info['c7dSaleCnt']
2145
+ c30dSaleCnt = sku_info['c30dSaleCnt']
2146
+ orderCnt = sku_info['orderCnt']
2147
+ totalSaleVolume = sku_info['totalSaleVolume']
2148
+ planUrgentCount = sku_info['planUrgentCount']
2149
+ preemptionCount = dict_preemption_num[skc][sku]
2150
+ predictDaySales = sku_info['predictDaySales']
2151
+ goodsDate = sku_info['goodsDate']
2152
+ stockDays = sku_info['stockDays']
2153
+
2154
+ real_transit = transit + sku_info['stayShelf'] - sku_info['transitSale']
2155
+
2156
+ sales_info = (
2157
+ f'近7天/30天销量: {c7dSaleCnt}/{c30dSaleCnt}\n'
2158
+ f'当天销量/购买单数: {totalSaleVolume}/{orderCnt}\n'
2159
+ f'预测日销/下单参数: {predictDaySales}/{goodsDate}+{stockDays}\n'
2160
+ f'预占数/预计急采数: {preemptionCount}/{planUrgentCount}\n'
2161
+ f'建议下单/已下单数: {adviceOrderCount}/{orderCount}\n'
2162
+ f'拟下单数/自动下单: {availableOrderCount}/{autoOrderStatus}\n'
2163
+ f'模式/本地/在途/希音: {sale_model[:2]}/{stock}/{real_transit}/{shein_stock}\n'
2164
+ f'成本/核价/利润: ¥{cost_price}/¥{supplyPrice}/¥{profit}\n'
2165
+ )
2166
+
2167
+ row_item.append(sales_info)
2168
+
2169
+ flag_yesterday = 0
2170
+ sales7cn = 0
2171
+ for date in date_list:
2172
+ sales_num = DictSkuSalesDate.get(date, {}).get(sku, {}).get("hisActualValue", 0)
2173
+ sales_num = sales_num if sales_num is not None else 0
2174
+ sales7cn += sales_num
2175
+ if TimeUtils.is_yesterday_date(date) and sales_num == 0:
2176
+ flag_yesterday = 1
2177
+
2178
+ if mode == 4 and flag_yesterday:
2179
+ continue
2180
+
2181
+ # 过滤掉未建立马帮信息的
2182
+ if mode in [5, 6, 7, 8, 9, 10] and advice_purchase_number == '-':
2183
+ continue
2184
+
2185
+ # 建议采购为正
2186
+ if mode in [5] and advice_purchase_number < 0:
2187
+ continue
2188
+
2189
+ # 建议采购为负
2190
+ if mode in [6, 7] and advice_purchase_number >= 0:
2191
+ continue
2192
+
2193
+ # 30内
2194
+ if mode in [9] and shelfDays > 31:
2195
+ continue
2196
+
2197
+ # 15天内
2198
+ if mode in [7] and shelfDays > 15:
2199
+ continue
2200
+
2201
+ # 30外
2202
+ if mode in [6, 8, 10] and shelfDays < 31:
2203
+ continue
2204
+
2205
+ # 有销量
2206
+ if mode in [5, 8, 9, 10] and sales7cn == 0:
2207
+ continue
2208
+
2209
+ # 无销量
2210
+ if mode in [6] and sales7cn > 0:
2211
+ continue
2212
+
2213
+ sale_num_list, sale_data_list = self.get_skc_week_sale_list(spu, skc, sku)
2214
+ row_item.append("\n".join(sale_num_list))
2215
+ row_item.append("\n".join(sale_data_list))
2216
+ row_item.append(self.get_skc_activity_label(skc, sku, dictActivityPrice))
2217
+ row_item.append(skc)
2218
+ row_item.append(sku)
2219
+ excel_data.append(row_item)
2220
+
2221
+ cache_file = f'{self.config.auto_dir}/shein/cache/bak_advice_{mode}_{TimeUtils.today_date()}.json'
2222
+ write_dict_to_file_ex(cache_file, {self.store_name: excel_data}, {self.store_name})
2223
+
2224
+ cache_file = f'{self.config.auto_dir}/shein/cache/bak_advice_notify_{mode}_{TimeUtils.today_date()}.json'
2225
+ NotifyItem = [self.store_name, len(excel_data[1:])]
2226
+ write_dict_to_file_ex(cache_file, {self.store_name: NotifyItem}, {self.store_name})
2227
+
2228
+ return excel_data