qrpa 1.1.34__py3-none-any.whl → 1.1.36__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/mysql_module/new_product_analysis_model.py +67 -2
- qrpa/mysql_module/shein_store_model.py +594 -0
- qrpa/shein_lib.py +253 -2
- {qrpa-1.1.34.dist-info → qrpa-1.1.36.dist-info}/METADATA +1 -1
- {qrpa-1.1.34.dist-info → qrpa-1.1.36.dist-info}/RECORD +7 -6
- {qrpa-1.1.34.dist-info → qrpa-1.1.36.dist-info}/WHEEL +0 -0
- {qrpa-1.1.34.dist-info → qrpa-1.1.36.dist-info}/top_level.txt +0 -0
|
@@ -63,6 +63,7 @@ class SheinNewProductAnalysis(Base):
|
|
|
63
63
|
ab_test = Column(Text, nullable=True, comment='AB测试数据(JSON)')
|
|
64
64
|
|
|
65
65
|
# 分析字段(不参与更新)
|
|
66
|
+
front_price = Column(DECIMAL(10, 2), nullable=True, comment='前台价格')
|
|
66
67
|
reason_analysis = Column(Text, nullable=True, comment='原因分析')
|
|
67
68
|
optimization_action = Column(Text, nullable=True, comment='优化动作')
|
|
68
69
|
|
|
@@ -144,7 +145,7 @@ class NewProductAnalysisManager:
|
|
|
144
145
|
|
|
145
146
|
if existing:
|
|
146
147
|
# 更新现有记录(排除不更新的分析字段)
|
|
147
|
-
exclude_fields = {'reason_analysis', 'optimization_action'}
|
|
148
|
+
exclude_fields = {'front_price', 'reason_analysis', 'optimization_action'}
|
|
148
149
|
for key, value in data.items():
|
|
149
150
|
if key not in exclude_fields:
|
|
150
151
|
setattr(existing, key, value)
|
|
@@ -235,7 +236,13 @@ class NewProductAnalysisManager:
|
|
|
235
236
|
prom_detail = prom.get('promDetail', [])
|
|
236
237
|
|
|
237
238
|
# 根据promId长度判断取哪些字段
|
|
238
|
-
if len(str(prom_id))
|
|
239
|
+
if len(str(prom_id)) >= 11 and prom_detail:
|
|
240
|
+
# 营销工具:取第一个detail的数据
|
|
241
|
+
detail = prom_detail if prom_detail else {}
|
|
242
|
+
prom_info['attend_num_sum'] = detail.get('act_stock_num')
|
|
243
|
+
prom_info['supply_price'] = detail.get('sku_price')
|
|
244
|
+
prom_info['product_act_price'] = detail.get('act_sku_price')
|
|
245
|
+
elif len(str(prom_id)) >= 8 and prom_detail:
|
|
239
246
|
# 营销工具:取第一个detail的数据
|
|
240
247
|
detail = prom_detail[0] if prom_detail else {}
|
|
241
248
|
prom_info['attend_num_sum'] = detail.get('attend_num_sum')
|
|
@@ -328,6 +335,41 @@ class NewProductAnalysisManager:
|
|
|
328
335
|
# 调用insert_data方法插入数据
|
|
329
336
|
return self.insert_data(data_list)
|
|
330
337
|
|
|
338
|
+
def update_front_price(self, stat_date, skc, front_price):
|
|
339
|
+
"""
|
|
340
|
+
更新指定SKC的前台价格
|
|
341
|
+
|
|
342
|
+
Args:
|
|
343
|
+
stat_date (str): 统计日期
|
|
344
|
+
skc (str): 平台SKC
|
|
345
|
+
front_price (float): 前台价格
|
|
346
|
+
|
|
347
|
+
Returns:
|
|
348
|
+
bool: 是否成功
|
|
349
|
+
"""
|
|
350
|
+
session = self.Session()
|
|
351
|
+
try:
|
|
352
|
+
record = session.query(SheinNewProductAnalysis).filter(
|
|
353
|
+
SheinNewProductAnalysis.stat_date == stat_date,
|
|
354
|
+
SheinNewProductAnalysis.skc == skc
|
|
355
|
+
).first()
|
|
356
|
+
|
|
357
|
+
if record:
|
|
358
|
+
record.front_price = front_price
|
|
359
|
+
setattr(record, 'updated_at', datetime.now())
|
|
360
|
+
session.commit()
|
|
361
|
+
print(f"成功更新前台价格: {skc} ({stat_date}) -> {front_price}")
|
|
362
|
+
return True
|
|
363
|
+
else:
|
|
364
|
+
print(f"未找到记录: SKC={skc}, 日期={stat_date}")
|
|
365
|
+
return False
|
|
366
|
+
except Exception as e:
|
|
367
|
+
session.rollback()
|
|
368
|
+
print(f"更新前台价格失败: {e}")
|
|
369
|
+
raise
|
|
370
|
+
finally:
|
|
371
|
+
session.close()
|
|
372
|
+
|
|
331
373
|
def delete_records_by_date(self, store_username, stat_date):
|
|
332
374
|
"""
|
|
333
375
|
删除指定日期的记录
|
|
@@ -410,6 +452,23 @@ def get_records_by_skc(store_username, skc):
|
|
|
410
452
|
manager = get_new_product_analysis_manager()
|
|
411
453
|
return manager.get_records_by_skc(store_username, skc)
|
|
412
454
|
|
|
455
|
+
|
|
456
|
+
def update_front_price(stat_date, skc, front_price):
|
|
457
|
+
"""
|
|
458
|
+
更新前台价格的便捷函数
|
|
459
|
+
|
|
460
|
+
Args:
|
|
461
|
+
stat_date (str): 统计日期
|
|
462
|
+
skc (str): 平台SKC
|
|
463
|
+
front_price (float): 前台价格
|
|
464
|
+
|
|
465
|
+
Returns:
|
|
466
|
+
bool: 是否成功
|
|
467
|
+
"""
|
|
468
|
+
manager = get_new_product_analysis_manager()
|
|
469
|
+
return manager.update_front_price(stat_date, skc, front_price)
|
|
470
|
+
|
|
471
|
+
|
|
413
472
|
if __name__ == '__main__':
|
|
414
473
|
# 测试代码
|
|
415
474
|
manager = NewProductAnalysisManager()
|
|
@@ -427,3 +486,9 @@ if __name__ == '__main__':
|
|
|
427
486
|
json_data = json.load(f)
|
|
428
487
|
count = manager.import_from_json(json_data)
|
|
429
488
|
print(f"成功导入 {count} 条记录")
|
|
489
|
+
|
|
490
|
+
# 更新前台价格(手动设置,后续导入不会覆盖)
|
|
491
|
+
# manager.update_front_price('2025-10-15', 'si2409238815318914', 19.99)
|
|
492
|
+
|
|
493
|
+
# 或使用便捷函数
|
|
494
|
+
# update_front_price('2025-10-15', 'si2409238815318914', 19.99)
|
|
@@ -0,0 +1,594 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
店铺信息数据模型
|
|
5
|
+
使用SQLAlchemy定义shein_store表结构
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from sqlalchemy import create_engine, Column, Integer, String, BigInteger, DateTime, Index
|
|
9
|
+
from sqlalchemy.ext.declarative import declarative_base
|
|
10
|
+
from sqlalchemy.orm import sessionmaker
|
|
11
|
+
from datetime import datetime
|
|
12
|
+
import json
|
|
13
|
+
|
|
14
|
+
# 创建基类
|
|
15
|
+
Base = declarative_base()
|
|
16
|
+
|
|
17
|
+
class SheinStore(Base):
|
|
18
|
+
"""
|
|
19
|
+
希音店铺信息表
|
|
20
|
+
存储店铺基础信息
|
|
21
|
+
"""
|
|
22
|
+
__tablename__ = 'shein_store'
|
|
23
|
+
|
|
24
|
+
# 主键ID
|
|
25
|
+
id = Column(BigInteger, primary_key=True, autoincrement=True, comment='主键ID')
|
|
26
|
+
|
|
27
|
+
# 店铺账号信息(唯一)
|
|
28
|
+
user_name = Column(String(100), nullable=False, unique=True, comment='店铺账号')
|
|
29
|
+
store_username = Column(String(100), nullable=True, comment='店铺用户名')
|
|
30
|
+
store_name = Column(String(200), nullable=True, comment='店铺名称')
|
|
31
|
+
|
|
32
|
+
# 用户ID信息
|
|
33
|
+
user_id = Column(BigInteger, nullable=True, comment='用户ID')
|
|
34
|
+
supplier_id = Column(BigInteger, nullable=True, comment='供应商ID')
|
|
35
|
+
main_user_name = Column(String(100), nullable=True, comment='主账号用户名')
|
|
36
|
+
main_user_id = Column(BigInteger, nullable=True, comment='主账号用户ID')
|
|
37
|
+
|
|
38
|
+
# ULP信息
|
|
39
|
+
ulp_name = Column(String(200), nullable=True, comment='ULP名称')
|
|
40
|
+
ulp_en_name = Column(String(200), nullable=True, comment='ULP英文名称')
|
|
41
|
+
ulp_emplid = Column(String(100), nullable=True, comment='ULP员工ID')
|
|
42
|
+
is_ulp_login = Column(Integer, nullable=True, comment='是否ULP登录')
|
|
43
|
+
|
|
44
|
+
# 时区信息
|
|
45
|
+
timezone = Column(String(100), nullable=True, comment='时区')
|
|
46
|
+
timezone_name = Column(String(200), nullable=True, comment='时区名称')
|
|
47
|
+
utc_timezone = Column(String(50), nullable=True, comment='UTC时区')
|
|
48
|
+
area_timezone = Column(String(100), nullable=True, comment='地区时区')
|
|
49
|
+
|
|
50
|
+
# 系统配置
|
|
51
|
+
switch_new_menu = Column(Integer, nullable=True, comment='是否切换新菜单')
|
|
52
|
+
supplier_user_name = Column(String(100), nullable=True, comment='供应商用户名')
|
|
53
|
+
sso_top_nav = Column(Integer, nullable=True, comment='SSO顶部导航')
|
|
54
|
+
sso_host = Column(String(500), nullable=True, comment='SSO主机地址')
|
|
55
|
+
|
|
56
|
+
# 类目信息
|
|
57
|
+
category_id = Column(BigInteger, nullable=True, comment='类目ID')
|
|
58
|
+
category_out_id = Column(BigInteger, nullable=True, comment='外部类目ID')
|
|
59
|
+
lv1_category_id = Column(BigInteger, nullable=True, comment='一级类目ID')
|
|
60
|
+
lv1_category_name = Column(String(200), nullable=True, comment='一级类目名称')
|
|
61
|
+
lv2_category_name = Column(String(200), nullable=True, comment='二级类目名称')
|
|
62
|
+
|
|
63
|
+
# 其他信息
|
|
64
|
+
supplier_source = Column(Integer, nullable=True, comment='供应商来源')
|
|
65
|
+
external_id = Column(BigInteger, nullable=True, comment='外部ID')
|
|
66
|
+
store_code = Column(BigInteger, nullable=True, comment='店铺编码')
|
|
67
|
+
merchant_code = Column(String(100), nullable=True, comment='商户编码')
|
|
68
|
+
schat_id = Column(BigInteger, nullable=True, comment='Schat ID')
|
|
69
|
+
|
|
70
|
+
# 管理字段(不参与更新)
|
|
71
|
+
store_manager_id = Column(BigInteger, default=0, comment='店长ID(用户表ID)')
|
|
72
|
+
is_deleted = Column(Integer, nullable=True, default=0, comment='软删除标志(0-未删除,1-已删除)')
|
|
73
|
+
|
|
74
|
+
# 备注
|
|
75
|
+
remark = Column(String(500), nullable=True, comment='备注')
|
|
76
|
+
|
|
77
|
+
# 时间戳
|
|
78
|
+
created_at = Column(DateTime, nullable=True, default=datetime.now, comment='创建时间')
|
|
79
|
+
updated_at = Column(DateTime, nullable=True, default=datetime.now, onupdate=datetime.now, comment='更新时间')
|
|
80
|
+
|
|
81
|
+
# 创建索引
|
|
82
|
+
__table_args__ = (
|
|
83
|
+
Index('idx_store_username', 'store_username',unique=True),
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
def __repr__(self):
|
|
87
|
+
return f"<SheinStore(id={self.id}, user_name='{self.user_name}', store_name='{self.store_name}')>"
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class SheinStoreManager:
|
|
91
|
+
"""
|
|
92
|
+
店铺信息数据管理器
|
|
93
|
+
提供数据库操作相关方法
|
|
94
|
+
"""
|
|
95
|
+
|
|
96
|
+
@classmethod
|
|
97
|
+
def __init__(self, database_url):
|
|
98
|
+
"""
|
|
99
|
+
初始化数据库连接
|
|
100
|
+
|
|
101
|
+
Args:
|
|
102
|
+
database_url (str): 数据库连接URL
|
|
103
|
+
"""
|
|
104
|
+
print(f"连接数据库: {database_url}")
|
|
105
|
+
self.engine = create_engine(database_url, echo=False)
|
|
106
|
+
self.Session = sessionmaker(bind=self.engine)
|
|
107
|
+
|
|
108
|
+
def create_table(self):
|
|
109
|
+
"""
|
|
110
|
+
创建数据表
|
|
111
|
+
"""
|
|
112
|
+
Base.metadata.create_all(self.engine)
|
|
113
|
+
print("店铺表创建成功")
|
|
114
|
+
|
|
115
|
+
def insert_data(self, data_list):
|
|
116
|
+
"""
|
|
117
|
+
批量插入数据
|
|
118
|
+
|
|
119
|
+
Args:
|
|
120
|
+
data_list (list): 数据字典列表
|
|
121
|
+
|
|
122
|
+
Returns:
|
|
123
|
+
int: 成功插入的记录数
|
|
124
|
+
"""
|
|
125
|
+
session = self.Session()
|
|
126
|
+
try:
|
|
127
|
+
insert_count = 0
|
|
128
|
+
update_count = 0
|
|
129
|
+
|
|
130
|
+
for data in data_list:
|
|
131
|
+
# 检查是否存在(根据唯一索引:user_name)
|
|
132
|
+
existing = session.query(SheinStore).filter(
|
|
133
|
+
SheinStore.user_name == data.get('user_name')
|
|
134
|
+
).first()
|
|
135
|
+
|
|
136
|
+
if existing:
|
|
137
|
+
# 更新现有记录(排除不更新的字段)
|
|
138
|
+
exclude_fields = {'store_manager_id', 'is_deleted'}
|
|
139
|
+
for key, value in data.items():
|
|
140
|
+
if key not in exclude_fields:
|
|
141
|
+
setattr(existing, key, value)
|
|
142
|
+
setattr(existing, 'updated_at', datetime.now())
|
|
143
|
+
update_count += 1
|
|
144
|
+
else:
|
|
145
|
+
# 插入新记录
|
|
146
|
+
new_record = SheinStore(**data)
|
|
147
|
+
session.add(new_record)
|
|
148
|
+
insert_count += 1
|
|
149
|
+
|
|
150
|
+
session.commit()
|
|
151
|
+
print(f"成功插入 {insert_count} 条记录,更新 {update_count} 条记录")
|
|
152
|
+
return insert_count
|
|
153
|
+
except Exception as e:
|
|
154
|
+
session.rollback()
|
|
155
|
+
print(f"插入数据失败: {e}")
|
|
156
|
+
raise
|
|
157
|
+
finally:
|
|
158
|
+
session.close()
|
|
159
|
+
|
|
160
|
+
def import_from_json_dict(self, json_dict):
|
|
161
|
+
"""
|
|
162
|
+
从JSON字典数据导入到数据库(JSON格式:{userName: {店铺信息}})
|
|
163
|
+
|
|
164
|
+
Args:
|
|
165
|
+
json_dict (dict): JSON字典数据,key为userName,value为店铺信息
|
|
166
|
+
|
|
167
|
+
Returns:
|
|
168
|
+
int: 成功导入的记录数
|
|
169
|
+
"""
|
|
170
|
+
print(f"开始处理 {len(json_dict)} 个店铺数据")
|
|
171
|
+
data_list = []
|
|
172
|
+
skipped_count = 0
|
|
173
|
+
|
|
174
|
+
for user_name, item in json_dict.items():
|
|
175
|
+
# 验证必填字段
|
|
176
|
+
if not user_name:
|
|
177
|
+
print(f"⚠️ 警告: 缺少必填字段 user_name,跳过该记录")
|
|
178
|
+
skipped_count += 1
|
|
179
|
+
continue
|
|
180
|
+
|
|
181
|
+
# 构建数据库记录(字段名转换:驼峰转下划线)
|
|
182
|
+
record = {
|
|
183
|
+
'user_name': item.get('userName'),
|
|
184
|
+
'store_username': item.get('store_username'),
|
|
185
|
+
'store_name': item.get('store_name'),
|
|
186
|
+
'user_id': item.get('userId'),
|
|
187
|
+
'supplier_id': item.get('supplierId'),
|
|
188
|
+
'main_user_name': item.get('mainUserName'),
|
|
189
|
+
'main_user_id': item.get('mainUserId'),
|
|
190
|
+
'ulp_name': item.get('ulpName'),
|
|
191
|
+
'ulp_en_name': item.get('ulpEnName'),
|
|
192
|
+
'ulp_emplid': item.get('ulpEmplid'),
|
|
193
|
+
'is_ulp_login': item.get('isUlpLogin'),
|
|
194
|
+
'timezone': item.get('timezone'),
|
|
195
|
+
'timezone_name': item.get('timezoneName'),
|
|
196
|
+
'utc_timezone': item.get('utcTimezone'),
|
|
197
|
+
'area_timezone': item.get('areaTimezone'),
|
|
198
|
+
'switch_new_menu': item.get('switchNewMenu'),
|
|
199
|
+
'supplier_user_name': item.get('supplierUserName'),
|
|
200
|
+
'sso_top_nav': item.get('ssoTopNav'),
|
|
201
|
+
'sso_host': item.get('ssoHost'),
|
|
202
|
+
'category_id': item.get('categoryId'),
|
|
203
|
+
'category_out_id': item.get('categoryOutId'),
|
|
204
|
+
'lv1_category_id': item.get('lv1CategoryId'),
|
|
205
|
+
'lv1_category_name': item.get('lv1CategoryName'),
|
|
206
|
+
'lv2_category_name': item.get('lv2CategoryName'),
|
|
207
|
+
'supplier_source': item.get('supplierSource'),
|
|
208
|
+
'external_id': item.get('externalId'),
|
|
209
|
+
'store_code': item.get('storeCode'),
|
|
210
|
+
'merchant_code': item.get('merchantCode'),
|
|
211
|
+
'schat_id': item.get('schatId'),
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
print(f"✓ 处理店铺: {record['user_name']} - {record['store_name']}")
|
|
215
|
+
data_list.append(record)
|
|
216
|
+
|
|
217
|
+
if skipped_count > 0:
|
|
218
|
+
print(f"⚠️ 共跳过 {skipped_count} 条记录(缺少必填字段)")
|
|
219
|
+
|
|
220
|
+
# 调用insert_data方法插入数据
|
|
221
|
+
return self.insert_data(data_list)
|
|
222
|
+
|
|
223
|
+
def import_from_json_list(self, json_list):
|
|
224
|
+
"""
|
|
225
|
+
从JSON列表数据导入到数据库(JSON格式:[{店铺信息}, {店铺信息}])
|
|
226
|
+
|
|
227
|
+
Args:
|
|
228
|
+
json_list (list): JSON列表数据
|
|
229
|
+
|
|
230
|
+
Returns:
|
|
231
|
+
int: 成功导入的记录数
|
|
232
|
+
"""
|
|
233
|
+
# 转换为字典格式,然后调用 import_from_json_dict
|
|
234
|
+
json_dict = {item.get('userName'): item for item in json_list if item.get('userName')}
|
|
235
|
+
return self.import_from_json_dict(json_dict)
|
|
236
|
+
|
|
237
|
+
def get_store_by_username(self, user_name, include_deleted=False):
|
|
238
|
+
"""
|
|
239
|
+
根据用户名查询店铺信息
|
|
240
|
+
|
|
241
|
+
Args:
|
|
242
|
+
user_name (str): 店铺账号
|
|
243
|
+
include_deleted (bool): 是否包含已删除的店铺,默认False
|
|
244
|
+
|
|
245
|
+
Returns:
|
|
246
|
+
SheinStore: 店铺对象
|
|
247
|
+
"""
|
|
248
|
+
session = self.Session()
|
|
249
|
+
try:
|
|
250
|
+
query = session.query(SheinStore).filter(
|
|
251
|
+
SheinStore.user_name == user_name
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
# 默认只查询未删除的
|
|
255
|
+
if not include_deleted:
|
|
256
|
+
query = query.filter(SheinStore.is_deleted == 0)
|
|
257
|
+
|
|
258
|
+
store = query.first()
|
|
259
|
+
return store
|
|
260
|
+
finally:
|
|
261
|
+
session.close()
|
|
262
|
+
|
|
263
|
+
def get_all_stores(self, include_deleted=False):
|
|
264
|
+
"""
|
|
265
|
+
获取所有店铺列表
|
|
266
|
+
|
|
267
|
+
Args:
|
|
268
|
+
include_deleted (bool): 是否包含已删除的店铺,默认False
|
|
269
|
+
|
|
270
|
+
Returns:
|
|
271
|
+
list: 店铺列表
|
|
272
|
+
"""
|
|
273
|
+
session = self.Session()
|
|
274
|
+
try:
|
|
275
|
+
query = session.query(SheinStore)
|
|
276
|
+
|
|
277
|
+
# 默认只查询未删除的
|
|
278
|
+
if not include_deleted:
|
|
279
|
+
query = query.filter(SheinStore.is_deleted == 0)
|
|
280
|
+
|
|
281
|
+
stores = query.all()
|
|
282
|
+
return stores
|
|
283
|
+
finally:
|
|
284
|
+
session.close()
|
|
285
|
+
|
|
286
|
+
def get_stores_by_category(self, lv1_category_name=None, lv2_category_name=None, include_deleted=False):
|
|
287
|
+
"""
|
|
288
|
+
根据类目查询店铺列表
|
|
289
|
+
|
|
290
|
+
Args:
|
|
291
|
+
lv1_category_name (str): 一级类目名称
|
|
292
|
+
lv2_category_name (str): 二级类目名称
|
|
293
|
+
include_deleted (bool): 是否包含已删除的店铺,默认False
|
|
294
|
+
|
|
295
|
+
Returns:
|
|
296
|
+
list: 店铺列表
|
|
297
|
+
"""
|
|
298
|
+
session = self.Session()
|
|
299
|
+
try:
|
|
300
|
+
query = session.query(SheinStore)
|
|
301
|
+
|
|
302
|
+
# 默认只查询未删除的
|
|
303
|
+
if not include_deleted:
|
|
304
|
+
query = query.filter(SheinStore.is_deleted == 0)
|
|
305
|
+
|
|
306
|
+
if lv1_category_name:
|
|
307
|
+
query = query.filter(SheinStore.lv1_category_name == lv1_category_name)
|
|
308
|
+
|
|
309
|
+
if lv2_category_name:
|
|
310
|
+
query = query.filter(SheinStore.lv2_category_name == lv2_category_name)
|
|
311
|
+
|
|
312
|
+
stores = query.all()
|
|
313
|
+
return stores
|
|
314
|
+
finally:
|
|
315
|
+
session.close()
|
|
316
|
+
|
|
317
|
+
def update_store_manager(self, user_name, manager_id):
|
|
318
|
+
"""
|
|
319
|
+
更新店铺的店长ID
|
|
320
|
+
|
|
321
|
+
Args:
|
|
322
|
+
user_name (str): 店铺账号
|
|
323
|
+
manager_id (int): 店长ID(用户表ID)
|
|
324
|
+
|
|
325
|
+
Returns:
|
|
326
|
+
bool: 是否成功
|
|
327
|
+
"""
|
|
328
|
+
session = self.Session()
|
|
329
|
+
try:
|
|
330
|
+
store = session.query(SheinStore).filter(
|
|
331
|
+
SheinStore.user_name == user_name
|
|
332
|
+
).first()
|
|
333
|
+
|
|
334
|
+
if store:
|
|
335
|
+
store.store_manager_id = manager_id
|
|
336
|
+
store.updated_at = datetime.now()
|
|
337
|
+
session.commit()
|
|
338
|
+
print(f"成功更新店长ID: {user_name} -> {manager_id}")
|
|
339
|
+
return True
|
|
340
|
+
else:
|
|
341
|
+
print(f"未找到店铺: {user_name}")
|
|
342
|
+
return False
|
|
343
|
+
except Exception as e:
|
|
344
|
+
session.rollback()
|
|
345
|
+
print(f"更新店长ID失败: {e}")
|
|
346
|
+
raise
|
|
347
|
+
finally:
|
|
348
|
+
session.close()
|
|
349
|
+
|
|
350
|
+
def soft_delete_store(self, user_name):
|
|
351
|
+
"""
|
|
352
|
+
软删除指定店铺(设置is_deleted=1)
|
|
353
|
+
|
|
354
|
+
Args:
|
|
355
|
+
user_name (str): 店铺账号
|
|
356
|
+
|
|
357
|
+
Returns:
|
|
358
|
+
bool: 是否成功
|
|
359
|
+
"""
|
|
360
|
+
session = self.Session()
|
|
361
|
+
try:
|
|
362
|
+
store = session.query(SheinStore).filter(
|
|
363
|
+
SheinStore.user_name == user_name
|
|
364
|
+
).first()
|
|
365
|
+
|
|
366
|
+
if store:
|
|
367
|
+
store.is_deleted = 1
|
|
368
|
+
store.updated_at = datetime.now()
|
|
369
|
+
session.commit()
|
|
370
|
+
print(f"成功软删除店铺: {user_name}")
|
|
371
|
+
return True
|
|
372
|
+
else:
|
|
373
|
+
print(f"未找到店铺: {user_name}")
|
|
374
|
+
return False
|
|
375
|
+
except Exception as e:
|
|
376
|
+
session.rollback()
|
|
377
|
+
print(f"软删除失败: {e}")
|
|
378
|
+
raise
|
|
379
|
+
finally:
|
|
380
|
+
session.close()
|
|
381
|
+
|
|
382
|
+
def restore_store(self, user_name):
|
|
383
|
+
"""
|
|
384
|
+
恢复软删除的店铺(设置is_deleted=0)
|
|
385
|
+
|
|
386
|
+
Args:
|
|
387
|
+
user_name (str): 店铺账号
|
|
388
|
+
|
|
389
|
+
Returns:
|
|
390
|
+
bool: 是否成功
|
|
391
|
+
"""
|
|
392
|
+
session = self.Session()
|
|
393
|
+
try:
|
|
394
|
+
store = session.query(SheinStore).filter(
|
|
395
|
+
SheinStore.user_name == user_name
|
|
396
|
+
).first()
|
|
397
|
+
|
|
398
|
+
if store:
|
|
399
|
+
store.is_deleted = 0
|
|
400
|
+
store.updated_at = datetime.now()
|
|
401
|
+
session.commit()
|
|
402
|
+
print(f"成功恢复店铺: {user_name}")
|
|
403
|
+
return True
|
|
404
|
+
else:
|
|
405
|
+
print(f"未找到店铺: {user_name}")
|
|
406
|
+
return False
|
|
407
|
+
except Exception as e:
|
|
408
|
+
session.rollback()
|
|
409
|
+
print(f"恢复失败: {e}")
|
|
410
|
+
raise
|
|
411
|
+
finally:
|
|
412
|
+
session.close()
|
|
413
|
+
|
|
414
|
+
def delete_store(self, user_name):
|
|
415
|
+
"""
|
|
416
|
+
物理删除指定店铺(真正从数据库删除,慎用!)
|
|
417
|
+
|
|
418
|
+
Args:
|
|
419
|
+
user_name (str): 店铺账号
|
|
420
|
+
|
|
421
|
+
Returns:
|
|
422
|
+
int: 删除的记录数
|
|
423
|
+
"""
|
|
424
|
+
session = self.Session()
|
|
425
|
+
try:
|
|
426
|
+
delete_count = session.query(SheinStore).filter(
|
|
427
|
+
SheinStore.user_name == user_name
|
|
428
|
+
).delete()
|
|
429
|
+
session.commit()
|
|
430
|
+
print(f"成功物理删除 {delete_count} 条记录")
|
|
431
|
+
return delete_count
|
|
432
|
+
except Exception as e:
|
|
433
|
+
session.rollback()
|
|
434
|
+
print(f"删除数据失败: {e}")
|
|
435
|
+
raise
|
|
436
|
+
finally:
|
|
437
|
+
session.close()
|
|
438
|
+
|
|
439
|
+
|
|
440
|
+
# 创建全局实例,用于快速访问
|
|
441
|
+
_shein_store_manager = None
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
def get_shein_store_manager(database_url):
|
|
445
|
+
"""
|
|
446
|
+
获取店铺管理器实例
|
|
447
|
+
|
|
448
|
+
Args:
|
|
449
|
+
database_url (str): 数据库连接URL
|
|
450
|
+
|
|
451
|
+
Returns:
|
|
452
|
+
SheinStoreManager: 店铺管理器实例
|
|
453
|
+
"""
|
|
454
|
+
global _shein_store_manager
|
|
455
|
+
if _shein_store_manager is None:
|
|
456
|
+
_shein_store_manager = SheinStoreManager(database_url)
|
|
457
|
+
return _shein_store_manager
|
|
458
|
+
|
|
459
|
+
|
|
460
|
+
def insert_store_data(database_url, data_list):
|
|
461
|
+
"""
|
|
462
|
+
插入店铺数据的便捷函数
|
|
463
|
+
|
|
464
|
+
Args:
|
|
465
|
+
database_url (str): 数据库连接URL
|
|
466
|
+
data_list (list): 数据字典列表
|
|
467
|
+
|
|
468
|
+
Returns:
|
|
469
|
+
int: 成功插入的记录数
|
|
470
|
+
"""
|
|
471
|
+
manager = get_shein_store_manager(database_url)
|
|
472
|
+
return manager.insert_data(data_list)
|
|
473
|
+
|
|
474
|
+
|
|
475
|
+
def import_from_json(database_url, json_data):
|
|
476
|
+
"""
|
|
477
|
+
从JSON数据导入的便捷函数(自动识别字典或列表格式)
|
|
478
|
+
|
|
479
|
+
Args:
|
|
480
|
+
database_url (str): 数据库连接URL
|
|
481
|
+
json_data (dict|list): JSON数据(字典或列表)
|
|
482
|
+
|
|
483
|
+
Returns:
|
|
484
|
+
int: 成功导入的记录数
|
|
485
|
+
"""
|
|
486
|
+
manager = get_shein_store_manager(database_url)
|
|
487
|
+
|
|
488
|
+
if isinstance(json_data, dict):
|
|
489
|
+
return manager.import_from_json_dict(json_data)
|
|
490
|
+
elif isinstance(json_data, list):
|
|
491
|
+
return manager.import_from_json_list(json_data)
|
|
492
|
+
else:
|
|
493
|
+
raise ValueError("json_data 必须是字典或列表类型")
|
|
494
|
+
|
|
495
|
+
|
|
496
|
+
def get_store_by_username(database_url, user_name, include_deleted=False):
|
|
497
|
+
"""
|
|
498
|
+
根据用户名查询店铺的便捷函数
|
|
499
|
+
|
|
500
|
+
Args:
|
|
501
|
+
database_url (str): 数据库连接URL
|
|
502
|
+
user_name (str): 店铺账号
|
|
503
|
+
include_deleted (bool): 是否包含已删除的店铺,默认False
|
|
504
|
+
|
|
505
|
+
Returns:
|
|
506
|
+
SheinStore: 店铺对象
|
|
507
|
+
"""
|
|
508
|
+
manager = get_shein_store_manager(database_url)
|
|
509
|
+
return manager.get_store_by_username(user_name, include_deleted)
|
|
510
|
+
|
|
511
|
+
|
|
512
|
+
def update_store_manager(database_url, user_name, manager_id):
|
|
513
|
+
"""
|
|
514
|
+
更新店长ID的便捷函数
|
|
515
|
+
|
|
516
|
+
Args:
|
|
517
|
+
database_url (str): 数据库连接URL
|
|
518
|
+
user_name (str): 店铺账号
|
|
519
|
+
manager_id (int): 店长ID(用户表ID)
|
|
520
|
+
|
|
521
|
+
Returns:
|
|
522
|
+
bool: 是否成功
|
|
523
|
+
"""
|
|
524
|
+
manager = get_shein_store_manager(database_url)
|
|
525
|
+
return manager.update_store_manager(user_name, manager_id)
|
|
526
|
+
|
|
527
|
+
|
|
528
|
+
def soft_delete_store(database_url, user_name):
|
|
529
|
+
"""
|
|
530
|
+
软删除店铺的便捷函数
|
|
531
|
+
|
|
532
|
+
Args:
|
|
533
|
+
database_url (str): 数据库连接URL
|
|
534
|
+
user_name (str): 店铺账号
|
|
535
|
+
|
|
536
|
+
Returns:
|
|
537
|
+
bool: 是否成功
|
|
538
|
+
"""
|
|
539
|
+
manager = get_shein_store_manager(database_url)
|
|
540
|
+
return manager.soft_delete_store(user_name)
|
|
541
|
+
|
|
542
|
+
|
|
543
|
+
def restore_store(database_url, user_name):
|
|
544
|
+
"""
|
|
545
|
+
恢复店铺的便捷函数
|
|
546
|
+
|
|
547
|
+
Args:
|
|
548
|
+
database_url (str): 数据库连接URL
|
|
549
|
+
user_name (str): 店铺账号
|
|
550
|
+
|
|
551
|
+
Returns:
|
|
552
|
+
bool: 是否成功
|
|
553
|
+
"""
|
|
554
|
+
manager = get_shein_store_manager(database_url)
|
|
555
|
+
return manager.restore_store(user_name)
|
|
556
|
+
|
|
557
|
+
|
|
558
|
+
if __name__ == '__main__':
|
|
559
|
+
# 测试代码
|
|
560
|
+
# 注意:需要提供数据库连接URL
|
|
561
|
+
database_url = "mysql+pymysql://root:123wyk@127.0.0.1:3306/lz"
|
|
562
|
+
|
|
563
|
+
manager = SheinStoreManager(database_url)
|
|
564
|
+
|
|
565
|
+
# 创建表
|
|
566
|
+
manager.create_table()
|
|
567
|
+
|
|
568
|
+
# 读取JSON文件
|
|
569
|
+
with open('../../docs/shein_user.json', 'r', encoding='utf-8') as f:
|
|
570
|
+
json_data = json.load(f)
|
|
571
|
+
|
|
572
|
+
count = manager.import_from_json_dict(json_data)
|
|
573
|
+
print(f"成功导入 {count} 条店铺记录")
|
|
574
|
+
|
|
575
|
+
# 更新店长ID(手动设置,后续导入不会覆盖)
|
|
576
|
+
# manager.update_store_manager('GS0628340', 123) # 123是用户表的ID
|
|
577
|
+
|
|
578
|
+
# 软删除测试
|
|
579
|
+
# manager.soft_delete_store('GS0628340')
|
|
580
|
+
|
|
581
|
+
# 查询店铺(默认不包含已删除)
|
|
582
|
+
# store = manager.get_store_by_username('GS0628340')
|
|
583
|
+
# print(f"查询结果: {store}")
|
|
584
|
+
|
|
585
|
+
# 查询店铺(包含已删除)
|
|
586
|
+
# store = manager.get_store_by_username('GS0628340', include_deleted=True)
|
|
587
|
+
# print(f"查询结果(包含已删除): {store}")
|
|
588
|
+
|
|
589
|
+
# 恢复店铺
|
|
590
|
+
# manager.restore_store('GS0628340')
|
|
591
|
+
|
|
592
|
+
# 按类目查询示例
|
|
593
|
+
# stores = manager.get_stores_by_category(lv2_category_name='全托管-备CN-中国团队-内睡服装')
|
|
594
|
+
# print(f"找到 {len(stores)} 个店铺")
|
qrpa/shein_lib.py
CHANGED
|
@@ -1236,7 +1236,10 @@ class SheinLib:
|
|
|
1236
1236
|
for prom_inf_ing in skc_item['promCampaign'].get('promInfIng') or []:
|
|
1237
1237
|
prom_id = prom_inf_ing['promId']
|
|
1238
1238
|
log('prom_id:', prom_id, len(prom_id))
|
|
1239
|
-
if len(prom_id) >=
|
|
1239
|
+
if len(prom_id) >= 11:
|
|
1240
|
+
# 托管活动
|
|
1241
|
+
prom_inf_ing['promDetail'] = self.get_skc_activity_price_info(skc, prom_id)
|
|
1242
|
+
elif len(prom_id) >= 8:
|
|
1240
1243
|
# 营销工具
|
|
1241
1244
|
prom_inf_ing['promDetail'] = self.query_goods_detail(prom_id)
|
|
1242
1245
|
else:
|
|
@@ -1246,7 +1249,9 @@ class SheinLib:
|
|
|
1246
1249
|
for prom_inf_ready in skc_item['promCampaign'].get('promInfReady') or []:
|
|
1247
1250
|
prom_id = prom_inf_ready['promId']
|
|
1248
1251
|
log('prom_id:', prom_id, len(prom_id))
|
|
1249
|
-
if len(prom_id) >=
|
|
1252
|
+
if len(prom_id) >= 11:
|
|
1253
|
+
prom_inf_ready['promDetail'] = self.get_skc_activity_price_info(skc, prom_id)
|
|
1254
|
+
elif len(prom_id) >= 8:
|
|
1250
1255
|
prom_inf_ready['promDetail'] = self.query_goods_detail(prom_id)
|
|
1251
1256
|
else:
|
|
1252
1257
|
prom_inf_ready['promDetail'] = self.get_partake_activity_detail(prom_id, skc)
|
|
@@ -2373,6 +2378,7 @@ class SheinLib:
|
|
|
2373
2378
|
list_item += response_text['info']['data']
|
|
2374
2379
|
time.sleep(0.1)
|
|
2375
2380
|
|
|
2381
|
+
log(list_item)
|
|
2376
2382
|
write_dict_to_file(cache_file, list_item)
|
|
2377
2383
|
return list_item
|
|
2378
2384
|
|
|
@@ -2398,6 +2404,7 @@ class SheinLib:
|
|
|
2398
2404
|
raise send_exception(json.dumps(response_text, ensure_ascii=False))
|
|
2399
2405
|
list_item = response_text['info']['data']
|
|
2400
2406
|
|
|
2407
|
+
log(list_item)
|
|
2401
2408
|
write_dict_to_file(cache_file, list_item)
|
|
2402
2409
|
return list_item
|
|
2403
2410
|
|
|
@@ -3930,3 +3937,247 @@ class SheinLib:
|
|
|
3930
3937
|
log('临时下载文件已清理')
|
|
3931
3938
|
|
|
3932
3939
|
return output_file_path
|
|
3940
|
+
|
|
3941
|
+
def query_hosting_info_list(self):
|
|
3942
|
+
"""
|
|
3943
|
+
查询店铺活动托管规则列表
|
|
3944
|
+
|
|
3945
|
+
Returns:
|
|
3946
|
+
list: 托管规则列表,每个元素包含:
|
|
3947
|
+
- hosting_id: 托管规则ID
|
|
3948
|
+
- scene_type: 场景类型
|
|
3949
|
+
- state: 状态
|
|
3950
|
+
- hosting_name: 托管规则名称
|
|
3951
|
+
- hosting_tools_id: 托管工具ID
|
|
3952
|
+
- hosting_tools_state: 托管工具状态
|
|
3953
|
+
- time_zone: 时区
|
|
3954
|
+
- create_user: 创建用户
|
|
3955
|
+
- last_update_user: 最后更新用户
|
|
3956
|
+
- insert_time: 创建时间
|
|
3957
|
+
- last_update_time: 最后更新时间
|
|
3958
|
+
- exist_act_goods: 是否存在活动商品
|
|
3959
|
+
"""
|
|
3960
|
+
log(f'正在获取 {self.store_name} 店铺活动托管规则列表')
|
|
3961
|
+
|
|
3962
|
+
cache_file = f'{self.config.auto_dir}/shein/cache/hosting_info_list_{self.store_username}_{TimeUtils.today_date()}.json'
|
|
3963
|
+
hosting_list = read_dict_from_file_ex(cache_file, self.store_username, 3600 * 8)
|
|
3964
|
+
if len(hosting_list) > 0:
|
|
3965
|
+
log('返回缓存数据')
|
|
3966
|
+
return hosting_list
|
|
3967
|
+
|
|
3968
|
+
url = "https://sso.geiwohuo.com/mrs-api-prefix/promotion/hosting/query_hosting_info_list"
|
|
3969
|
+
payload = {}
|
|
3970
|
+
|
|
3971
|
+
response_text = fetch(self.web_page, url, payload)
|
|
3972
|
+
error_code = response_text.get('code')
|
|
3973
|
+
if str(error_code) != '0':
|
|
3974
|
+
raise send_exception(json.dumps(response_text, ensure_ascii=False))
|
|
3975
|
+
|
|
3976
|
+
hosting_list = response_text.get('info', [])
|
|
3977
|
+
log(f'获取到 {len(hosting_list)} 条托管规则')
|
|
3978
|
+
|
|
3979
|
+
write_dict_to_file_ex(cache_file, {self.store_username: hosting_list}, [self.store_username])
|
|
3980
|
+
|
|
3981
|
+
return hosting_list
|
|
3982
|
+
|
|
3983
|
+
def query_hosting_activity_goods(self, hosting_id, goods_states=None):
|
|
3984
|
+
"""
|
|
3985
|
+
查询托管活动参与的商品
|
|
3986
|
+
|
|
3987
|
+
Args:
|
|
3988
|
+
hosting_id: 托管规则ID
|
|
3989
|
+
goods_states: 商品状态列表,默认为[1](在售)
|
|
3990
|
+
|
|
3991
|
+
Returns:
|
|
3992
|
+
list: 参与托管活动的商品列表,每个元素包含:
|
|
3993
|
+
- goods_state: 商品状态
|
|
3994
|
+
- skc_info_list: SKC信息列表
|
|
3995
|
+
- skc_id: SKC ID
|
|
3996
|
+
- skc_name: SKC名称
|
|
3997
|
+
- goods_name: 商品名称
|
|
3998
|
+
- image_url: 图片URL
|
|
3999
|
+
- act_stock_num: 活动库存数量
|
|
4000
|
+
- act_sales_num: 活动销量
|
|
4001
|
+
- activity_info: 活动信息
|
|
4002
|
+
- sku_info_list: SKU信息列表
|
|
4003
|
+
"""
|
|
4004
|
+
if goods_states is None:
|
|
4005
|
+
goods_states = [1]
|
|
4006
|
+
|
|
4007
|
+
log(f'正在获取 {self.store_name} 托管活动商品列表 hosting_id={hosting_id}')
|
|
4008
|
+
|
|
4009
|
+
cache_file = f'{self.config.auto_dir}/shein/cache/hosting_activity_goods_{self.store_username}_{hosting_id}_{TimeUtils.today_date()}.json'
|
|
4010
|
+
goods_list = read_dict_from_file_ex(cache_file, self.store_username, 3600 * 8)
|
|
4011
|
+
if len(goods_list) > 0:
|
|
4012
|
+
log('返回缓存数据')
|
|
4013
|
+
return goods_list
|
|
4014
|
+
|
|
4015
|
+
page_num = 1
|
|
4016
|
+
page_size = 100
|
|
4017
|
+
|
|
4018
|
+
url = "https://sso.geiwohuo.com/mrs-api-prefix/promotion/hosting/query_hosting_activity_goods"
|
|
4019
|
+
payload = {
|
|
4020
|
+
"goods_states": goods_states,
|
|
4021
|
+
"hosting_id" : str(hosting_id),
|
|
4022
|
+
"page_num" : page_num,
|
|
4023
|
+
"page_size" : page_size
|
|
4024
|
+
}
|
|
4025
|
+
|
|
4026
|
+
response_text = fetch(self.web_page, url, payload)
|
|
4027
|
+
error_code = response_text.get('code')
|
|
4028
|
+
if str(error_code) != '0':
|
|
4029
|
+
raise send_exception(json.dumps(response_text, ensure_ascii=False))
|
|
4030
|
+
|
|
4031
|
+
goods_list = response_text.get('info', [])
|
|
4032
|
+
if not goods_list:
|
|
4033
|
+
log('未获取到商品数据')
|
|
4034
|
+
return []
|
|
4035
|
+
|
|
4036
|
+
# 获取第一页数据
|
|
4037
|
+
first_item = goods_list[0] if goods_list else {}
|
|
4038
|
+
skc_info_list = first_item.get('skc_info_list', {})
|
|
4039
|
+
all_data = skc_info_list.get('data', [])
|
|
4040
|
+
meta = skc_info_list.get('meta', {})
|
|
4041
|
+
total = meta.get('count', 0)
|
|
4042
|
+
|
|
4043
|
+
log(f'第1页获取到 {len(all_data)} 条商品,总数: {total}')
|
|
4044
|
+
|
|
4045
|
+
# 如果有多页,继续获取
|
|
4046
|
+
if total > page_size:
|
|
4047
|
+
totalPage = math.ceil(total / page_size)
|
|
4048
|
+
for page in range(2, totalPage + 1):
|
|
4049
|
+
log(f'获取托管活动商品列表 第{page}/{totalPage}页')
|
|
4050
|
+
payload['page_num'] = page
|
|
4051
|
+
response_text = fetch(self.web_page, url, payload)
|
|
4052
|
+
error_code = response_text.get('code')
|
|
4053
|
+
if str(error_code) != '0':
|
|
4054
|
+
log(f'获取第{page}页失败: {response_text}')
|
|
4055
|
+
continue
|
|
4056
|
+
|
|
4057
|
+
page_goods_list = response_text.get('info', [])
|
|
4058
|
+
if page_goods_list:
|
|
4059
|
+
page_data = page_goods_list[0].get('skc_info_list', {}).get('data', [])
|
|
4060
|
+
all_data.extend(page_data)
|
|
4061
|
+
log(f'第{page}页获取到 {len(page_data)} 条商品')
|
|
4062
|
+
|
|
4063
|
+
time.sleep(0.1)
|
|
4064
|
+
|
|
4065
|
+
log(f'总共获取到 {len(all_data)} 条商品')
|
|
4066
|
+
|
|
4067
|
+
# 保存缓存
|
|
4068
|
+
write_dict_to_file_ex(cache_file, {self.store_username: all_data}, [self.store_username])
|
|
4069
|
+
|
|
4070
|
+
return all_data
|
|
4071
|
+
|
|
4072
|
+
def get_skc_activity_price_info(self, skc, activity_id):
|
|
4073
|
+
"""
|
|
4074
|
+
根据SKC和活动ID获取供货价、活动价和活动库存
|
|
4075
|
+
|
|
4076
|
+
Args:
|
|
4077
|
+
skc: SKC名称
|
|
4078
|
+
activity_id: 活动ID(可以是字符串或整数)
|
|
4079
|
+
|
|
4080
|
+
Returns:
|
|
4081
|
+
dict: 包含以下键值的字典,如果未找到则返回None:
|
|
4082
|
+
- sku_price: SKU供货价(取第一个SKU的价格)
|
|
4083
|
+
- act_sku_price: SKU活动价(取第一个SKU的活动价)
|
|
4084
|
+
- act_stock_num: 活动库存数量
|
|
4085
|
+
- skc_name: SKC名称
|
|
4086
|
+
- goods_name: 商品名称
|
|
4087
|
+
- activity_id: 活动ID
|
|
4088
|
+
- currency: 币种
|
|
4089
|
+
- image_url: 商品图片
|
|
4090
|
+
"""
|
|
4091
|
+
log(f'获取SKC活动价格信息: skc={skc}, activity_id={activity_id}')
|
|
4092
|
+
|
|
4093
|
+
# 转换activity_id为整数进行比较
|
|
4094
|
+
try:
|
|
4095
|
+
target_activity_id = int(activity_id)
|
|
4096
|
+
except (ValueError, TypeError):
|
|
4097
|
+
log(f'无效的activity_id: {activity_id}')
|
|
4098
|
+
return None
|
|
4099
|
+
|
|
4100
|
+
# 缓存文件,使用skc和activity_id作为缓存key
|
|
4101
|
+
cache_file = f'{self.config.auto_dir}/shein/cache/skc_activity_price_{self.store_username}_{skc}_{activity_id}_{TimeUtils.today_date()}.json'
|
|
4102
|
+
cached_data = read_dict_from_file(cache_file, 3600 * 8)
|
|
4103
|
+
if cached_data:
|
|
4104
|
+
log('返回缓存的价格信息')
|
|
4105
|
+
return cached_data
|
|
4106
|
+
|
|
4107
|
+
# 获取所有托管规则
|
|
4108
|
+
hosting_list = self.query_hosting_info_list()
|
|
4109
|
+
|
|
4110
|
+
if not hosting_list:
|
|
4111
|
+
log('未找到任何托管规则')
|
|
4112
|
+
return None
|
|
4113
|
+
|
|
4114
|
+
# 遍历所有托管规则,查找匹配的SKC和活动
|
|
4115
|
+
for hosting in hosting_list:
|
|
4116
|
+
hosting_id = hosting.get('hosting_id')
|
|
4117
|
+
if not hosting_id:
|
|
4118
|
+
continue
|
|
4119
|
+
|
|
4120
|
+
log(f'查询托管规则: hosting_id={hosting_id}, hosting_name={hosting.get("hosting_name")}')
|
|
4121
|
+
|
|
4122
|
+
# 获取该托管规则下的商品
|
|
4123
|
+
goods_list = self.query_hosting_activity_goods(hosting_id)
|
|
4124
|
+
|
|
4125
|
+
# 在商品列表中查找匹配的SKC
|
|
4126
|
+
for goods_item in goods_list:
|
|
4127
|
+
skc_name = goods_item.get('skc_name', '')
|
|
4128
|
+
|
|
4129
|
+
# 匹配SKC名称
|
|
4130
|
+
if skc_name != skc:
|
|
4131
|
+
continue
|
|
4132
|
+
|
|
4133
|
+
# 检查活动信息
|
|
4134
|
+
activity_info = goods_item.get('activity_info', {})
|
|
4135
|
+
goods_activity_id = activity_info.get('activity_id')
|
|
4136
|
+
|
|
4137
|
+
# 匹配活动ID
|
|
4138
|
+
try:
|
|
4139
|
+
if int(goods_activity_id) != target_activity_id:
|
|
4140
|
+
continue
|
|
4141
|
+
except (ValueError, TypeError):
|
|
4142
|
+
continue
|
|
4143
|
+
|
|
4144
|
+
log(f'找到匹配的SKC: {skc_name}, activity_id={goods_activity_id}')
|
|
4145
|
+
|
|
4146
|
+
# 提取活动库存
|
|
4147
|
+
act_stock_num = goods_item.get('act_stock_num', 0)
|
|
4148
|
+
|
|
4149
|
+
# 获取第一个SKU的价格信息
|
|
4150
|
+
sku_info_list = goods_item.get('sku_info_list', [])
|
|
4151
|
+
if not sku_info_list:
|
|
4152
|
+
log(f'SKC {skc_name} 没有SKU信息')
|
|
4153
|
+
continue
|
|
4154
|
+
|
|
4155
|
+
first_sku = sku_info_list[0]
|
|
4156
|
+
sku_price = first_sku.get('sku_price', 0)
|
|
4157
|
+
act_sku_price = first_sku.get('act_sku_price', 0)
|
|
4158
|
+
currency = first_sku.get('currency', 'CNY')
|
|
4159
|
+
|
|
4160
|
+
# 构建返回结果
|
|
4161
|
+
result = {
|
|
4162
|
+
'skc_name' : skc_name,
|
|
4163
|
+
'goods_name' : goods_item.get('goods_name', ''),
|
|
4164
|
+
'image_url' : goods_item.get('image_url', ''),
|
|
4165
|
+
'activity_id' : goods_activity_id,
|
|
4166
|
+
'act_stock_num': act_stock_num,
|
|
4167
|
+
'sku_price' : sku_price,
|
|
4168
|
+
'act_sku_price': act_sku_price,
|
|
4169
|
+
'currency' : currency,
|
|
4170
|
+
'start_time' : activity_info.get('start_time', ''),
|
|
4171
|
+
'end_time' : activity_info.get('end_time', ''),
|
|
4172
|
+
'time_zone' : activity_info.get('time_zone', ''),
|
|
4173
|
+
}
|
|
4174
|
+
|
|
4175
|
+
log(f'SKC供货价: {sku_price} {currency}, 活动价: {act_sku_price} {currency}, 活动库存: {act_stock_num}')
|
|
4176
|
+
|
|
4177
|
+
# 保存缓存
|
|
4178
|
+
write_dict_to_file(cache_file, result)
|
|
4179
|
+
|
|
4180
|
+
return result
|
|
4181
|
+
|
|
4182
|
+
log(f'未找到匹配的SKC和活动: skc={skc}, activity_id={activity_id}')
|
|
4183
|
+
return None
|
|
@@ -11,7 +11,7 @@ qrpa/fun_web.py,sha256=9YuVy_wps9Ty_FBZ91W2R0iKgC2IViaJjHbyuUgngGs,11599
|
|
|
11
11
|
qrpa/fun_win.py,sha256=vMdVh00dsnVz8Wey4Bq7J3RPZAY8B_bI_IKphOX1cE8,7836
|
|
12
12
|
qrpa/shein_daily_report_model.py,sha256=O8s9qM45WZRoAgxUFRngvmBrc29v7Uf2ye7K8_bcSRg,12214
|
|
13
13
|
qrpa/shein_excel.py,sha256=-5fNetkVMixPQPw00K9F4OeJn8LSknaoyRXNqsQw1EA,158107
|
|
14
|
-
qrpa/shein_lib.py,sha256=
|
|
14
|
+
qrpa/shein_lib.py,sha256=GSHqiCoVgFs3Ri6HT-AxxcJuGTsZ2eRGnGbv2YsF4ho,190827
|
|
15
15
|
qrpa/shein_mysql.py,sha256=MxbiRSH0gaTtW4ET7lVWRNY4NLOrMLGXO_4STptE1pU,4562
|
|
16
16
|
qrpa/shein_sqlite.py,sha256=i4xwNf60eoG6wbWM1R2i5pDdVW1ZMy6uy9nB-c2WKzk,5554
|
|
17
17
|
qrpa/shein_ziniao.py,sha256=YN7g6m84-vyDyePssfR41lqwaROz-km0-rJ8qY-jhy0,21416
|
|
@@ -22,11 +22,12 @@ qrpa/time_utils.py,sha256=bOTSi_ewXPCxwgG_ndFGf8dl7S4fvSGT9sQ-90LESuo,31458
|
|
|
22
22
|
qrpa/time_utils_example.py,sha256=80zzunKw7F1S8MOwNFmmiCnI8MOYoh4PH-25UrEGuF0,7810
|
|
23
23
|
qrpa/wxwork.py,sha256=Vy8PGEtlTWt4-1laVhuqpJUGCFH2JymgbjvH00aaBog,10946
|
|
24
24
|
qrpa/mysql_module/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
25
|
-
qrpa/mysql_module/new_product_analysis_model.py,sha256=
|
|
25
|
+
qrpa/mysql_module/new_product_analysis_model.py,sha256=nLBxgzcBHofiORETEycWMUliG-9mEErGih9TmBClxkM,18615
|
|
26
26
|
qrpa/mysql_module/shein_ledger_model.py,sha256=KGKfGyzS00rbBZhiZhAzypwYPGs7OdfRLnH2ea36Vm8,18161
|
|
27
27
|
qrpa/mysql_module/shein_product_model.py,sha256=KiXMjPT93XkANCM53cCFaISja0sTmAWsionFrRy8DQ4,18773
|
|
28
28
|
qrpa/mysql_module/shein_return_order_model.py,sha256=8xvKhOzpcJS5FHfyA33UednaqRNCyXo3qeXBzwTXeN8,25993
|
|
29
|
-
qrpa
|
|
30
|
-
qrpa-1.1.
|
|
31
|
-
qrpa-1.1.
|
|
32
|
-
qrpa-1.1.
|
|
29
|
+
qrpa/mysql_module/shein_store_model.py,sha256=tSXB7w8gmLXQfvuzk3te4OL1Tok-kEgGPYrCutDrzSw,20469
|
|
30
|
+
qrpa-1.1.36.dist-info/METADATA,sha256=Xk25QUVlFGW6ET2dpzJB8N9UiPu8BXMxAvZ7MH4kqjU,231
|
|
31
|
+
qrpa-1.1.36.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
32
|
+
qrpa-1.1.36.dist-info/top_level.txt,sha256=F6T5igi0fhXDucPPUbmmSC0qFCDEsH5eVijfVF48OFU,5
|
|
33
|
+
qrpa-1.1.36.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|