qrpa 1.0.19__py3-none-any.whl → 1.0.21__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_daily_report_model.py +375 -0
- qrpa/shein_excel.py +297 -2
- qrpa/shein_lib.py +314 -0
- qrpa/wxwork.py +3 -0
- {qrpa-1.0.19.dist-info → qrpa-1.0.21.dist-info}/METADATA +1 -1
- {qrpa-1.0.19.dist-info → qrpa-1.0.21.dist-info}/RECORD +8 -7
- {qrpa-1.0.19.dist-info → qrpa-1.0.21.dist-info}/WHEEL +0 -0
- {qrpa-1.0.19.dist-info → qrpa-1.0.21.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
店铺销售明细数据模型
|
|
5
|
+
使用SQLAlchemy定义store_sales_detail表结构
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from sqlalchemy import and_
|
|
9
|
+
from sqlalchemy import create_engine, Column, Integer, String, Date, DECIMAL, UniqueConstraint
|
|
10
|
+
# from sqlalchemy.ext.declarative import declarative_base
|
|
11
|
+
from sqlalchemy.orm import declarative_base
|
|
12
|
+
from sqlalchemy.orm import sessionmaker
|
|
13
|
+
|
|
14
|
+
from datetime import datetime, timedelta
|
|
15
|
+
|
|
16
|
+
from sqlalchemy import distinct
|
|
17
|
+
|
|
18
|
+
# 创建基础类
|
|
19
|
+
Base = declarative_base()
|
|
20
|
+
|
|
21
|
+
# 定义模型类
|
|
22
|
+
class SheinStoreSalesDetail(Base):
|
|
23
|
+
__tablename__ = 'store_sales_detail'
|
|
24
|
+
|
|
25
|
+
id = Column(Integer, primary_key=True, autoincrement=True)
|
|
26
|
+
store_username = Column(String(255), nullable=True)
|
|
27
|
+
store_name = Column(String(255), nullable=True)
|
|
28
|
+
day = Column(Date, nullable=True)
|
|
29
|
+
sales_num = Column(Integer, nullable=True)
|
|
30
|
+
sales_num_inc = Column(Integer, nullable=True)
|
|
31
|
+
sales_amount = Column(DECIMAL(10, 2), nullable=True)
|
|
32
|
+
sales_amount_inc = Column(DECIMAL(10, 2), nullable=True)
|
|
33
|
+
visitor_num = Column(Integer, nullable=True)
|
|
34
|
+
visitor_num_inc = Column(Integer, nullable=True)
|
|
35
|
+
bak_A_num = Column(Integer, nullable=True)
|
|
36
|
+
bak_A_num_inc = Column(Integer, nullable=True)
|
|
37
|
+
new_A_num = Column(Integer, nullable=True)
|
|
38
|
+
new_A_num_inc = Column(Integer, nullable=True)
|
|
39
|
+
on_sales_product_num = Column(Integer, nullable=True)
|
|
40
|
+
on_sales_product_num_inc = Column(Integer, nullable=True)
|
|
41
|
+
wait_shelf_product_num = Column(Integer, nullable=True)
|
|
42
|
+
wait_shelf_product_num_inc = Column(Integer, nullable=True)
|
|
43
|
+
upload_product_num = Column(Integer, nullable=True)
|
|
44
|
+
upload_product_num_inc = Column(Integer, nullable=True)
|
|
45
|
+
sold_out_product_num = Column(Integer, nullable=True)
|
|
46
|
+
shelf_off_product_num = Column(Integer, nullable=True)
|
|
47
|
+
remark = Column(String(255), nullable=True)
|
|
48
|
+
|
|
49
|
+
__table_args__ = (
|
|
50
|
+
UniqueConstraint('store_username', 'day', name='uq_store_day'),
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
def __repr__(self):
|
|
54
|
+
return f"<SheinStoreSalesDetail(store_username={self.store_username}, store_name={self.store_name}, day={self.day})>"
|
|
55
|
+
|
|
56
|
+
class SheinStoreSalesDetailManager:
|
|
57
|
+
"""
|
|
58
|
+
店铺销售明细数据管理器
|
|
59
|
+
提供数据库操作相关方法
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
# database_url = f"mysql+pymysql://{config.mysql_username}:{config.mysql_password}@{config.mysql_host}:{config.mysql_port}/{config.mysql_database}"
|
|
63
|
+
|
|
64
|
+
def __init__(self, database_url=None):
|
|
65
|
+
"""
|
|
66
|
+
初始化数据库连接
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
database_url (str): 数据库连接URL,例如:
|
|
70
|
+
mysql+pymysql://username:password@localhost:3306/database_name
|
|
71
|
+
"""
|
|
72
|
+
if database_url is None:
|
|
73
|
+
database_url = self.database_url
|
|
74
|
+
|
|
75
|
+
self.engine = create_engine(database_url, echo=False)
|
|
76
|
+
self.Session = sessionmaker(bind=self.engine)
|
|
77
|
+
|
|
78
|
+
def create_tables(self):
|
|
79
|
+
"""
|
|
80
|
+
创建数据表
|
|
81
|
+
"""
|
|
82
|
+
Base.metadata.create_all(self.engine)
|
|
83
|
+
print("✅ 数据表检查并创建完毕(如不存在则自动创建)")
|
|
84
|
+
|
|
85
|
+
def drop_tables(self):
|
|
86
|
+
"""
|
|
87
|
+
删除数据表
|
|
88
|
+
"""
|
|
89
|
+
Base.metadata.drop_all(self.engine)
|
|
90
|
+
print("数据表删除成功!")
|
|
91
|
+
|
|
92
|
+
def get_session(self):
|
|
93
|
+
"""
|
|
94
|
+
创建会话
|
|
95
|
+
|
|
96
|
+
Returns:
|
|
97
|
+
session: SQLAlchemy会话对象
|
|
98
|
+
"""
|
|
99
|
+
return self.Session()
|
|
100
|
+
|
|
101
|
+
def get_all_records(self):
|
|
102
|
+
"""
|
|
103
|
+
查询所有记录
|
|
104
|
+
|
|
105
|
+
Returns:
|
|
106
|
+
list: 所有销售明细记录列表
|
|
107
|
+
"""
|
|
108
|
+
session = self.get_session()
|
|
109
|
+
try:
|
|
110
|
+
records = session.query(SheinStoreSalesDetail).all()
|
|
111
|
+
return records
|
|
112
|
+
finally:
|
|
113
|
+
session.close()
|
|
114
|
+
|
|
115
|
+
def get_records_by_username_and_day(self, username, query_day):
|
|
116
|
+
"""
|
|
117
|
+
查询特定用户名和日期的记录
|
|
118
|
+
|
|
119
|
+
Args:
|
|
120
|
+
username (str): 店铺用户名
|
|
121
|
+
query_day (date): 查询日期
|
|
122
|
+
|
|
123
|
+
Returns:
|
|
124
|
+
list: 符合条件的记录列表
|
|
125
|
+
"""
|
|
126
|
+
session = self.get_session()
|
|
127
|
+
try:
|
|
128
|
+
records = session.query(SheinStoreSalesDetail).filter(
|
|
129
|
+
SheinStoreSalesDetail.store_username == username,
|
|
130
|
+
SheinStoreSalesDetail.day == query_day
|
|
131
|
+
).all()
|
|
132
|
+
return records
|
|
133
|
+
finally:
|
|
134
|
+
session.close()
|
|
135
|
+
|
|
136
|
+
def get_records_sorted_by_sales_amount(self):
|
|
137
|
+
"""
|
|
138
|
+
查询记录并按销售金额排序
|
|
139
|
+
|
|
140
|
+
Returns:
|
|
141
|
+
list: 按销售金额倒序排列的记录列表
|
|
142
|
+
"""
|
|
143
|
+
session = self.get_session()
|
|
144
|
+
try:
|
|
145
|
+
records = session.query(SheinStoreSalesDetail).order_by(SheinStoreSalesDetail.sales_amount.desc()).all()
|
|
146
|
+
return records
|
|
147
|
+
finally:
|
|
148
|
+
session.close()
|
|
149
|
+
|
|
150
|
+
def get_limited_records(self, limit):
|
|
151
|
+
"""
|
|
152
|
+
查询并限制结果数量
|
|
153
|
+
|
|
154
|
+
Args:
|
|
155
|
+
limit (int): 限制的记录数量
|
|
156
|
+
|
|
157
|
+
Returns:
|
|
158
|
+
list: 限制数量的记录列表
|
|
159
|
+
"""
|
|
160
|
+
session = self.get_session()
|
|
161
|
+
try:
|
|
162
|
+
records = session.query(SheinStoreSalesDetail).limit(limit).all()
|
|
163
|
+
return records
|
|
164
|
+
finally:
|
|
165
|
+
session.close()
|
|
166
|
+
|
|
167
|
+
def get_records_by_usernames(self, usernames):
|
|
168
|
+
"""
|
|
169
|
+
查询多个店铺用户名的记录
|
|
170
|
+
|
|
171
|
+
Args:
|
|
172
|
+
usernames (list): 店铺用户名列表
|
|
173
|
+
|
|
174
|
+
Returns:
|
|
175
|
+
list: 符合条件的记录列表
|
|
176
|
+
"""
|
|
177
|
+
session = self.get_session()
|
|
178
|
+
try:
|
|
179
|
+
records = session.query(SheinStoreSalesDetail).filter(
|
|
180
|
+
SheinStoreSalesDetail.store_username.in_(usernames)
|
|
181
|
+
).all()
|
|
182
|
+
return records
|
|
183
|
+
finally:
|
|
184
|
+
session.close()
|
|
185
|
+
|
|
186
|
+
def record_to_dict(self, record):
|
|
187
|
+
"""
|
|
188
|
+
将记录转换为字典
|
|
189
|
+
|
|
190
|
+
Args:
|
|
191
|
+
record: SQLAlchemy记录对象
|
|
192
|
+
|
|
193
|
+
Returns:
|
|
194
|
+
dict: 记录字典
|
|
195
|
+
"""
|
|
196
|
+
return {column.name: getattr(record, column.name) for column in record.__table__.columns}
|
|
197
|
+
|
|
198
|
+
def get_records_by_condition(self, filter_condition, order_by=None):
|
|
199
|
+
"""
|
|
200
|
+
公共查询函数(包含排序功能)
|
|
201
|
+
|
|
202
|
+
Args:
|
|
203
|
+
filter_condition: SQLAlchemy过滤条件
|
|
204
|
+
order_by: 排序条件
|
|
205
|
+
|
|
206
|
+
Returns:
|
|
207
|
+
list: 符合条件的记录列表
|
|
208
|
+
"""
|
|
209
|
+
session = self.get_session()
|
|
210
|
+
try:
|
|
211
|
+
# 执行查询,添加排序功能
|
|
212
|
+
query = session.query(SheinStoreSalesDetail).filter(filter_condition)
|
|
213
|
+
|
|
214
|
+
# 如果传入了排序条件,则加入排序
|
|
215
|
+
if order_by is not None:
|
|
216
|
+
query = query.order_by(order_by)
|
|
217
|
+
|
|
218
|
+
# 获取查询结果
|
|
219
|
+
records = query.all()
|
|
220
|
+
return records
|
|
221
|
+
|
|
222
|
+
except Exception as e:
|
|
223
|
+
import traceback
|
|
224
|
+
print("数据库错误:", e)
|
|
225
|
+
traceback.print_exc()
|
|
226
|
+
return []
|
|
227
|
+
|
|
228
|
+
finally:
|
|
229
|
+
# 确保会话关闭
|
|
230
|
+
session.close()
|
|
231
|
+
|
|
232
|
+
def get_distinct_store_sales_list(self):
|
|
233
|
+
"""
|
|
234
|
+
获取 distinct 的 store_username 和 store_name
|
|
235
|
+
|
|
236
|
+
Returns:
|
|
237
|
+
list: 唯一店铺用户名列表
|
|
238
|
+
"""
|
|
239
|
+
session = self.get_session()
|
|
240
|
+
try:
|
|
241
|
+
# 执行 distinct 查询,选择唯一的 store_username 和 store_name
|
|
242
|
+
records = session.query(
|
|
243
|
+
distinct(SheinStoreSalesDetail.store_username)
|
|
244
|
+
).all()
|
|
245
|
+
|
|
246
|
+
# 转换查询结果为二维列表格式
|
|
247
|
+
result = [[record[0], ''] for record in records]
|
|
248
|
+
return result
|
|
249
|
+
|
|
250
|
+
finally:
|
|
251
|
+
# 确保会话关闭
|
|
252
|
+
session.close()
|
|
253
|
+
|
|
254
|
+
def get_one_day_records(self, yesterday, order_by=None):
|
|
255
|
+
"""
|
|
256
|
+
获取单日数据
|
|
257
|
+
|
|
258
|
+
Args:
|
|
259
|
+
yesterday (str): 日期字符串,格式:'YYYY-MM-DD'
|
|
260
|
+
order_by: 排序条件,默认按销售数量倒序
|
|
261
|
+
|
|
262
|
+
Returns:
|
|
263
|
+
list: 指定日期的记录列表
|
|
264
|
+
"""
|
|
265
|
+
if order_by is None:
|
|
266
|
+
order_by = SheinStoreSalesDetail.sales_num.desc()
|
|
267
|
+
|
|
268
|
+
# 将字符串日期转换为 datetime.date 对象
|
|
269
|
+
yesterday_date = datetime.strptime(yesterday, '%Y-%m-%d').date()
|
|
270
|
+
|
|
271
|
+
# 定义查询条件
|
|
272
|
+
filter_condition = SheinStoreSalesDetail.day == yesterday_date
|
|
273
|
+
|
|
274
|
+
# 使用公共查询函数,传入排序条件
|
|
275
|
+
return self.get_records_by_condition(filter_condition, order_by)
|
|
276
|
+
|
|
277
|
+
def get_one_month_records(self, year, month, username, order_by=None):
|
|
278
|
+
"""
|
|
279
|
+
获取某年某月的数据
|
|
280
|
+
|
|
281
|
+
Args:
|
|
282
|
+
year (int): 年份
|
|
283
|
+
month (int): 月份
|
|
284
|
+
username (str): 店铺用户名
|
|
285
|
+
order_by: 排序条件,默认按日期升序
|
|
286
|
+
|
|
287
|
+
Returns:
|
|
288
|
+
list: 指定月份的记录列表
|
|
289
|
+
"""
|
|
290
|
+
if order_by is None:
|
|
291
|
+
order_by = SheinStoreSalesDetail.day.asc()
|
|
292
|
+
|
|
293
|
+
# 将年份和月份格式化为日期(构造该月的第一天)
|
|
294
|
+
first_day_of_month = datetime(year, month, 1).date()
|
|
295
|
+
|
|
296
|
+
# 获取该月的最后一天
|
|
297
|
+
last_day_of_month = (first_day_of_month.replace(day=28) + timedelta(days=4)).replace(day=1) - timedelta(days=1)
|
|
298
|
+
|
|
299
|
+
# 定义查询条件:查询指定年份和月份的记录
|
|
300
|
+
filter_condition = and_(
|
|
301
|
+
SheinStoreSalesDetail.day >= first_day_of_month,
|
|
302
|
+
SheinStoreSalesDetail.day <= last_day_of_month,
|
|
303
|
+
SheinStoreSalesDetail.store_username == username,
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
# 使用公共查询函数,传入排序条件
|
|
307
|
+
return self.get_records_by_condition(filter_condition, order_by)
|
|
308
|
+
|
|
309
|
+
def get_records_as_dict(self, username, query_day_str):
|
|
310
|
+
"""
|
|
311
|
+
获取指定用户名和日期的记录并转换为字典
|
|
312
|
+
|
|
313
|
+
Args:
|
|
314
|
+
username (str): 店铺用户名
|
|
315
|
+
query_day_str (str): 查询日期字符串,格式:'YYYY-MM-DD'
|
|
316
|
+
|
|
317
|
+
Returns:
|
|
318
|
+
dict: 记录字典,如果没有记录则返回空字典
|
|
319
|
+
"""
|
|
320
|
+
# 将字符串日期转换为 datetime.date 对象
|
|
321
|
+
query_day = datetime.strptime(query_day_str, '%Y-%m-%d').date()
|
|
322
|
+
# 获取数据库会话
|
|
323
|
+
session = self.get_session()
|
|
324
|
+
try:
|
|
325
|
+
# 查询特定条件的记录
|
|
326
|
+
records = session.query(SheinStoreSalesDetail).filter(
|
|
327
|
+
SheinStoreSalesDetail.store_username == username,
|
|
328
|
+
SheinStoreSalesDetail.day == query_day
|
|
329
|
+
).all()
|
|
330
|
+
# 如果没有记录,返回一个空字典
|
|
331
|
+
if not records:
|
|
332
|
+
return {}
|
|
333
|
+
# 将记录转换为字典
|
|
334
|
+
records_dict = [
|
|
335
|
+
{column.name: getattr(record, column.name) for column in SheinStoreSalesDetail.__table__.columns}
|
|
336
|
+
for record in records
|
|
337
|
+
]
|
|
338
|
+
|
|
339
|
+
return records_dict[0]
|
|
340
|
+
|
|
341
|
+
finally:
|
|
342
|
+
session.close()
|
|
343
|
+
|
|
344
|
+
def insert_data(self, data_list):
|
|
345
|
+
"""
|
|
346
|
+
插入多条数据
|
|
347
|
+
|
|
348
|
+
Args:
|
|
349
|
+
data_list (list): 要插入的数据列表,每个元素为字典格式
|
|
350
|
+
"""
|
|
351
|
+
# 获取会话
|
|
352
|
+
session = self.get_session()
|
|
353
|
+
try:
|
|
354
|
+
# 将字典转换为模型对象并插入
|
|
355
|
+
records = []
|
|
356
|
+
for data in data_list:
|
|
357
|
+
record = SheinStoreSalesDetail(**data) # 将字典解包为关键字参数
|
|
358
|
+
records.append(record)
|
|
359
|
+
# 将所有记录添加到会话
|
|
360
|
+
session.add_all(records)
|
|
361
|
+
# 提交事务
|
|
362
|
+
session.commit()
|
|
363
|
+
print(f"成功插入 {len(records)} 条数据")
|
|
364
|
+
except Exception as e:
|
|
365
|
+
# 如果发生错误,回滚事务
|
|
366
|
+
session.rollback()
|
|
367
|
+
print(f"插入数据时发生错误: {e}")
|
|
368
|
+
raise
|
|
369
|
+
finally:
|
|
370
|
+
# 关闭会话
|
|
371
|
+
session.close()
|
|
372
|
+
|
|
373
|
+
if __name__ == '__main__':
|
|
374
|
+
# 新的管理器方式示例(取消注释以运行测试)
|
|
375
|
+
pass
|
qrpa/shein_excel.py
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
from .fun_excel import *
|
|
2
2
|
from .fun_base import log
|
|
3
|
-
from .fun_file import read_dict_from_file, read_dict_from_file_ex, write_dict_to_file, write_dict_to_file_ex
|
|
3
|
+
from .fun_file import read_dict_from_file, read_dict_from_file_ex, write_dict_to_file, write_dict_to_file_ex, delete_file
|
|
4
4
|
from .time_utils import TimeUtils
|
|
5
|
+
from .wxwork import WxWorkBot
|
|
6
|
+
from .shein_daily_report_model import SheinStoreSalesDetailManager, SheinStoreSalesDetail
|
|
5
7
|
|
|
6
8
|
class SheinExcel:
|
|
7
9
|
|
|
@@ -9,6 +11,296 @@ class SheinExcel:
|
|
|
9
11
|
self.config = config
|
|
10
12
|
pass
|
|
11
13
|
|
|
14
|
+
def write_sales_data(self):
|
|
15
|
+
yesterday = TimeUtils.get_yesterday()
|
|
16
|
+
model = SheinStoreSalesDetailManager(self.config.database_url)
|
|
17
|
+
records = model.get_one_day_records(yesterday, SheinStoreSalesDetail.sales_amount.desc())
|
|
18
|
+
data_day = []
|
|
19
|
+
dict_store_manager_shein = self.config.shein_store_manager
|
|
20
|
+
dict_store_name = read_dict_from_file(self.config.shein_store_alias)
|
|
21
|
+
for record in records:
|
|
22
|
+
store_data = []
|
|
23
|
+
# store_data.append(record.store_name)
|
|
24
|
+
store_data.append(dict_store_name.get(record.store_username))
|
|
25
|
+
store_data.append(dict_store_manager_shein.get(str(record.store_username).lower(), '-'))
|
|
26
|
+
# log(dict_store_manager_shein.get(str(record.store_username).lower(),'-'))
|
|
27
|
+
store_data.append(record.sales_num)
|
|
28
|
+
store_data.append(record.sales_num_inc)
|
|
29
|
+
store_data.append(record.sales_amount)
|
|
30
|
+
store_data.append(record.sales_amount_inc)
|
|
31
|
+
store_data.append(record.visitor_num)
|
|
32
|
+
store_data.append(record.visitor_num_inc)
|
|
33
|
+
store_data.append(record.bak_A_num)
|
|
34
|
+
store_data.append(record.bak_A_num_inc)
|
|
35
|
+
store_data.append(record.new_A_num)
|
|
36
|
+
store_data.append(record.new_A_num_inc)
|
|
37
|
+
store_data.append(record.on_sales_product_num)
|
|
38
|
+
store_data.append(record.on_sales_product_num_inc)
|
|
39
|
+
store_data.append(record.wait_shelf_product_num)
|
|
40
|
+
store_data.append(record.wait_shelf_product_num_inc)
|
|
41
|
+
store_data.append(record.upload_product_num)
|
|
42
|
+
store_data.append(record.upload_product_num_inc)
|
|
43
|
+
store_data.append(record.sold_out_product_num)
|
|
44
|
+
store_data.append(record.shelf_off_product_num)
|
|
45
|
+
# store_data.append(record.remark) # 不要了
|
|
46
|
+
data_day.append(store_data)
|
|
47
|
+
|
|
48
|
+
excel_path = create_file_path(self.config.excel_daily_report)
|
|
49
|
+
delete_file(excel_path)
|
|
50
|
+
sheet_name_first = 'SHEIN销售部每日店铺情况'
|
|
51
|
+
# write_json_to_excel('excel_daily.json',excel_path,sheet_name_first)
|
|
52
|
+
app, wb, sheet = open_excel(excel_path, sheet_name_first)
|
|
53
|
+
delete_sheet_if_exists(wb, 'Sheet1')
|
|
54
|
+
|
|
55
|
+
las_row = len(data_day) + 4
|
|
56
|
+
log('len(las_row)', las_row)
|
|
57
|
+
sheet.range('B5').value = data_day
|
|
58
|
+
# sheet.range('3:3').api.WrapText = True
|
|
59
|
+
sheet.range(f'A5:U{las_row}').api.Font.Color = 0x000000 # 红色
|
|
60
|
+
sheet.range(f'A5:U{las_row}').api.Font.Bold = False
|
|
61
|
+
sheet.range('A1').value = f'销售部SHEIN{TimeUtils.get_current_month()}月店铺数据'
|
|
62
|
+
sheet.range('A4').value = f'{TimeUtils.format_date_cross_platform(yesterday)}\n({TimeUtils.get_chinese_weekday(yesterday)})'
|
|
63
|
+
sheet.range('A4').column_width = 16
|
|
64
|
+
sheet.range('A4').api.VerticalAlignment = -4160 # 垂直顶部对齐
|
|
65
|
+
sheet.range(f'A4:A{las_row}').merge()
|
|
66
|
+
|
|
67
|
+
# 获取列数据范围
|
|
68
|
+
column_range = sheet.range(f'E5:E{las_row}')
|
|
69
|
+
# 遍历列中的每个单元格
|
|
70
|
+
for cell in column_range:
|
|
71
|
+
# 检查单元格是否有值且为负数
|
|
72
|
+
if cell.value is not None and isinstance(cell.value, (int, float)) and cell.value < 0:
|
|
73
|
+
# 设置字体颜色为红色
|
|
74
|
+
cell.font.color = (255, 0, 0) # RGB值表示红色
|
|
75
|
+
|
|
76
|
+
# 获取列数据范围
|
|
77
|
+
column_range = sheet.range(f'G5:G{las_row}')
|
|
78
|
+
# 遍历列中的每个单元格
|
|
79
|
+
for cell in column_range:
|
|
80
|
+
# 检查单元格是否有值且为负数
|
|
81
|
+
if cell.value is not None and cell.value < 0:
|
|
82
|
+
# 设置字体颜色为红色
|
|
83
|
+
cell.font.color = (255, 0, 0) # RGB值表示红色
|
|
84
|
+
|
|
85
|
+
# 获取列数据范围
|
|
86
|
+
column_range = sheet.range(f'I5:I{las_row}')
|
|
87
|
+
# 遍历列中的每个单元格
|
|
88
|
+
for cell in column_range:
|
|
89
|
+
# 检查单元格是否有值且为负数
|
|
90
|
+
if cell.value is not None and isinstance(cell.value, (int, float)) and cell.value < 0:
|
|
91
|
+
# 设置字体颜色为红色
|
|
92
|
+
cell.font.color = (255, 0, 0) # RGB值表示红色
|
|
93
|
+
|
|
94
|
+
# 获取列数据范围
|
|
95
|
+
column_range = sheet.range(f'K5:K{las_row}')
|
|
96
|
+
# 遍历列中的每个单元格
|
|
97
|
+
for cell in column_range:
|
|
98
|
+
# 检查单元格是否有值且为负数
|
|
99
|
+
if cell.value is not None and isinstance(cell.value, (int, float)) and cell.value < 0:
|
|
100
|
+
# 设置字体颜色为红色
|
|
101
|
+
cell.font.color = (255, 0, 0) # RGB值表示红色
|
|
102
|
+
|
|
103
|
+
# 处理表头
|
|
104
|
+
# 处理第一行
|
|
105
|
+
rangeOne = f'A1:U1'
|
|
106
|
+
sheet.range(rangeOne).merge()
|
|
107
|
+
sheet.range(rangeOne).api.Font.Size = 24
|
|
108
|
+
sheet.range(rangeOne).api.Font.Bold = True
|
|
109
|
+
sheet.range(rangeOne).api.HorizontalAlignment = -4108
|
|
110
|
+
sheet.range(rangeOne).api.VerticalAlignment = -4108
|
|
111
|
+
|
|
112
|
+
# 处理第二行
|
|
113
|
+
rangeTwo_part_1 = f'A2:C2'
|
|
114
|
+
rangeTwo_part_2 = f'D2:O2'
|
|
115
|
+
rangeTwo_part_3 = f'P2:U2'
|
|
116
|
+
sheet.range(rangeTwo_part_1).merge()
|
|
117
|
+
sheet.range(rangeTwo_part_2).merge()
|
|
118
|
+
sheet.range(rangeTwo_part_3).merge()
|
|
119
|
+
|
|
120
|
+
sheet.range(f'A2:C3').color = 0x47a100
|
|
121
|
+
|
|
122
|
+
sheet.range('D2').value = f'店铺的结果和稳定性'
|
|
123
|
+
sheet.range(rangeTwo_part_2).api.Font.Size = 16
|
|
124
|
+
sheet.range(rangeTwo_part_2).api.Font.Color = 0xFFFFFF
|
|
125
|
+
sheet.range(rangeTwo_part_2).api.Font.Bold = True
|
|
126
|
+
sheet.range(rangeTwo_part_2).api.HorizontalAlignment = -4108
|
|
127
|
+
sheet.range(rangeTwo_part_2).api.VerticalAlignment = -4108
|
|
128
|
+
sheet.range(f'D2:O3').color = 0x0000FF
|
|
129
|
+
|
|
130
|
+
sheet.range('P2').value = f'上新的质量和数量'
|
|
131
|
+
sheet.range(rangeTwo_part_3).api.Font.Size = 16
|
|
132
|
+
sheet.range(rangeTwo_part_3).api.Font.Color = 0xFFFFFF
|
|
133
|
+
sheet.range(rangeTwo_part_3).api.Font.Bold = True
|
|
134
|
+
sheet.range(rangeTwo_part_3).api.HorizontalAlignment = -4108
|
|
135
|
+
sheet.range(rangeTwo_part_3).api.VerticalAlignment = -4108
|
|
136
|
+
sheet.range(f'P2:U3').color = 0x47a100
|
|
137
|
+
|
|
138
|
+
# 处理第三行
|
|
139
|
+
rangeThree = f'A3:U3'
|
|
140
|
+
sheet.range('A3').value = ['日期', '店铺', '店长', '昨日单量', '对比前日', '昨日销售额', '对比前日', '昨日访客',
|
|
141
|
+
'对比前天', '备货款A', '对比前日', '新款A', '对比前日', '在售商品', '对比前日', '待上架',
|
|
142
|
+
'对比前日', '昨日上传', '对比前日', '已售罄', '已下架']
|
|
143
|
+
sheet.range(rangeThree).api.Font.Size = 11
|
|
144
|
+
sheet.range(rangeThree).api.Font.Color = 0xFFFFFF
|
|
145
|
+
sheet.range(rangeThree).api.Font.Bold = True
|
|
146
|
+
sheet.range(rangeThree).api.HorizontalAlignment = -4108
|
|
147
|
+
sheet.range(rangeThree).api.VerticalAlignment = -4108
|
|
148
|
+
|
|
149
|
+
# 处理第4行
|
|
150
|
+
rangeFour = f'B4:U4'
|
|
151
|
+
sheet.range('B4').value = '汇总'
|
|
152
|
+
sheet.range('C4').value = '-'
|
|
153
|
+
sheet.range(rangeFour).api.Font.Size = 11
|
|
154
|
+
sheet.range(rangeThree).api.HorizontalAlignment = -4108
|
|
155
|
+
sheet.range(rangeThree).api.VerticalAlignment = -4108
|
|
156
|
+
sheet.range(f'B4:U4').color = 0x50d092
|
|
157
|
+
|
|
158
|
+
for col in range(2, 22): # 对应 C 列到 J 列
|
|
159
|
+
col_letter = xw.utils.col_name(col)
|
|
160
|
+
if col_letter not in ['A', 'B', 'C']:
|
|
161
|
+
sheet.range(f'{col_letter}4').formula = f'=SUM({col_letter}5:{col_letter}{las_row})'
|
|
162
|
+
# 所有列水平居中和垂直居中
|
|
163
|
+
sheet.range(f'{col_letter}:{col_letter}').api.HorizontalAlignment = -4108
|
|
164
|
+
sheet.range(f'{col_letter}:{col_letter}').api.VerticalAlignment = -4108
|
|
165
|
+
sheet.autofit()
|
|
166
|
+
|
|
167
|
+
range_to_border = sheet.range(f'A2:U{las_row}') # 定义范围
|
|
168
|
+
# 设置外部边框(所有边都为实线)
|
|
169
|
+
range_to_border.api.Borders(7).LineStyle = 1 # 上边框
|
|
170
|
+
range_to_border.api.Borders(8).LineStyle = 1 # 下边框
|
|
171
|
+
range_to_border.api.Borders(9).LineStyle = 1 # 左边框
|
|
172
|
+
range_to_border.api.Borders(10).LineStyle = 1 # 右边框
|
|
173
|
+
# 设置内部边框
|
|
174
|
+
range_to_border.api.Borders(1).LineStyle = 1 # 内部上边框
|
|
175
|
+
range_to_border.api.Borders(2).LineStyle = 1 # 内部下边框
|
|
176
|
+
range_to_border.api.Borders(3).LineStyle = 1 # 内部左边框
|
|
177
|
+
range_to_border.api.Borders(4).LineStyle = 1 # 内部右边框
|
|
178
|
+
|
|
179
|
+
wb.save()
|
|
180
|
+
close_excel(app, wb)
|
|
181
|
+
|
|
182
|
+
store_list = model.get_distinct_store_sales_list()
|
|
183
|
+
for store in store_list:
|
|
184
|
+
store_username = store[0]
|
|
185
|
+
store_name = dict_store_name.get(store_username)
|
|
186
|
+
records = model.get_one_month_records(TimeUtils.get_current_year(), TimeUtils.get_current_month(), store_username)
|
|
187
|
+
data_month = []
|
|
188
|
+
for record in records:
|
|
189
|
+
store_data = []
|
|
190
|
+
store_data.append(record.day)
|
|
191
|
+
store_data.append(record.sales_num)
|
|
192
|
+
store_data.append(record.sales_num_inc)
|
|
193
|
+
store_data.append(record.sales_amount)
|
|
194
|
+
store_data.append(record.sales_amount_inc)
|
|
195
|
+
store_data.append(record.visitor_num)
|
|
196
|
+
store_data.append(record.visitor_num_inc)
|
|
197
|
+
store_data.append(record.bak_A_num)
|
|
198
|
+
store_data.append(record.bak_A_num_inc)
|
|
199
|
+
store_data.append(record.new_A_num)
|
|
200
|
+
store_data.append(record.new_A_num_inc)
|
|
201
|
+
store_data.append(record.on_sales_product_num)
|
|
202
|
+
store_data.append(record.on_sales_product_num_inc)
|
|
203
|
+
store_data.append(record.wait_shelf_product_num)
|
|
204
|
+
store_data.append(record.wait_shelf_product_num_inc)
|
|
205
|
+
store_data.append(record.upload_product_num)
|
|
206
|
+
store_data.append(record.upload_product_num_inc)
|
|
207
|
+
store_data.append(record.sold_out_product_num)
|
|
208
|
+
store_data.append(record.shelf_off_product_num)
|
|
209
|
+
store_data.append(record.remark)
|
|
210
|
+
data_month.append(store_data)
|
|
211
|
+
|
|
212
|
+
excel_path = create_file_path(self.config.excel_daily_report)
|
|
213
|
+
sheet_name = store_name
|
|
214
|
+
# write_json_to_excel('excel_month.json',excel_path,sheet_name)
|
|
215
|
+
las_row = len(data_month) + 4
|
|
216
|
+
|
|
217
|
+
app, wb, sheet = open_excel(excel_path, sheet_name)
|
|
218
|
+
|
|
219
|
+
# 处理表头
|
|
220
|
+
# 处理第一行
|
|
221
|
+
rangeOne = f'A1:S1'
|
|
222
|
+
sheet.range(rangeOne).merge()
|
|
223
|
+
sheet.range(rangeOne).api.Font.Size = 24
|
|
224
|
+
sheet.range(rangeOne).api.Font.Bold = True
|
|
225
|
+
sheet.range(rangeOne).api.HorizontalAlignment = -4108
|
|
226
|
+
sheet.range(rangeOne).api.VerticalAlignment = -4108
|
|
227
|
+
|
|
228
|
+
# 处理第二行
|
|
229
|
+
rangeTwo_part_1 = f'A2'
|
|
230
|
+
rangeTwo_part_2 = f'B2:M2'
|
|
231
|
+
rangeTwo_part_3 = f'N2:S2'
|
|
232
|
+
sheet.range(rangeTwo_part_2).merge()
|
|
233
|
+
sheet.range(rangeTwo_part_3).merge()
|
|
234
|
+
|
|
235
|
+
sheet.range(f'A2:A3').color = 0x47a100
|
|
236
|
+
|
|
237
|
+
sheet.range('B2').value = f'店铺的结果和稳定性'
|
|
238
|
+
sheet.range(rangeTwo_part_2).api.Font.Size = 16
|
|
239
|
+
sheet.range(rangeTwo_part_2).api.Font.Color = 0xFFFFFF
|
|
240
|
+
sheet.range(rangeTwo_part_2).api.Font.Bold = True
|
|
241
|
+
sheet.range(rangeTwo_part_2).api.HorizontalAlignment = -4108
|
|
242
|
+
sheet.range(rangeTwo_part_2).api.VerticalAlignment = -4108
|
|
243
|
+
sheet.range(f'B2:M3').color = 0x0000FF
|
|
244
|
+
|
|
245
|
+
sheet.range('N2').value = f'上新的质量和数量'
|
|
246
|
+
sheet.range(rangeTwo_part_3).api.Font.Size = 16
|
|
247
|
+
sheet.range(rangeTwo_part_3).api.Font.Color = 0xFFFFFF
|
|
248
|
+
sheet.range(rangeTwo_part_3).api.Font.Bold = True
|
|
249
|
+
sheet.range(rangeTwo_part_3).api.HorizontalAlignment = -4108
|
|
250
|
+
sheet.range(rangeTwo_part_3).api.VerticalAlignment = -4108
|
|
251
|
+
sheet.range(f'N2:S3').color = 0x47a100
|
|
252
|
+
|
|
253
|
+
# 处理第三行
|
|
254
|
+
rangeThree = f'A3:S3'
|
|
255
|
+
sheet.range('A3').value = ['日期', '昨日单量', '对比前日', '昨日销售额', '对比前日', '昨日访客', '对比前天',
|
|
256
|
+
'备货款A', '对比前日', '新款A', '对比前日', '在售商品', '对比前日', '待上架',
|
|
257
|
+
'对比前日', '昨日上传', '对比前日', '已售罄', '已下架']
|
|
258
|
+
sheet.range(rangeThree).api.Font.Size = 11
|
|
259
|
+
sheet.range(rangeThree).api.Font.Color = 0xFFFFFF
|
|
260
|
+
sheet.range(rangeThree).api.Font.Bold = True
|
|
261
|
+
sheet.range(rangeThree).api.HorizontalAlignment = -4108
|
|
262
|
+
sheet.range(rangeThree).api.VerticalAlignment = -4108
|
|
263
|
+
|
|
264
|
+
# 处理第4行
|
|
265
|
+
rangeFour = f'A4:S4'
|
|
266
|
+
sheet.range('A4').value = '汇总'
|
|
267
|
+
sheet.range(rangeFour).api.Font.Size = 11
|
|
268
|
+
sheet.range(rangeThree).api.HorizontalAlignment = -4108
|
|
269
|
+
sheet.range(rangeThree).api.VerticalAlignment = -4108
|
|
270
|
+
sheet.range(f'A4:S4').color = 0x50d092
|
|
271
|
+
|
|
272
|
+
# 原逻辑
|
|
273
|
+
sheet.range('A5').value = data_month
|
|
274
|
+
# sheet.range('3:3').api.WrapText = True
|
|
275
|
+
sheet.range('A4:T35').api.Font.Color = 0x000000
|
|
276
|
+
sheet.range('A4:T35').api.Font.Bold = False
|
|
277
|
+
sheet.range('A1').value = f'{store_name}SHEIN{TimeUtils.get_current_month()}月店铺数据'
|
|
278
|
+
|
|
279
|
+
range_to_border = sheet.range(f'A2:S{las_row}') # 定义范围
|
|
280
|
+
# 设置外部边框(所有边都为实线)
|
|
281
|
+
range_to_border.api.Borders(7).LineStyle = 1 # 上边框
|
|
282
|
+
range_to_border.api.Borders(8).LineStyle = 1 # 下边框
|
|
283
|
+
range_to_border.api.Borders(9).LineStyle = 1 # 左边框
|
|
284
|
+
range_to_border.api.Borders(10).LineStyle = 1 # 右边框
|
|
285
|
+
# 设置内部边框
|
|
286
|
+
range_to_border.api.Borders(1).LineStyle = 1 # 内部上边框
|
|
287
|
+
range_to_border.api.Borders(2).LineStyle = 1 # 内部下边框
|
|
288
|
+
range_to_border.api.Borders(3).LineStyle = 1 # 内部左边框
|
|
289
|
+
range_to_border.api.Borders(4).LineStyle = 1 # 内部右边框
|
|
290
|
+
wb.save()
|
|
291
|
+
|
|
292
|
+
for col in range(2, 20): # 对应 C 列到 J 列
|
|
293
|
+
col_letter = xw.utils.col_name(col)
|
|
294
|
+
# 所有列水平居中和垂直居中
|
|
295
|
+
sheet.range(f'{col_letter}:{col_letter}').api.HorizontalAlignment = -4108
|
|
296
|
+
sheet.range(f'{col_letter}:{col_letter}').api.VerticalAlignment = -4108
|
|
297
|
+
sheet.range(f'{col_letter}4').formula = f'=SUM({col_letter}5:{col_letter}36)'
|
|
298
|
+
|
|
299
|
+
if sheet_name_first in [sheet.name for sheet in wb.sheets]:
|
|
300
|
+
wb.sheets[sheet_name_first].activate()
|
|
301
|
+
wb.save()
|
|
302
|
+
close_excel(app, wb)
|
|
303
|
+
|
|
12
304
|
def format_bak_advice(self, excel_path, sheet_name, mode):
|
|
13
305
|
app, wb, sheet = open_excel(excel_path, sheet_name)
|
|
14
306
|
beautify_title(sheet)
|
|
@@ -30,6 +322,8 @@ class SheinExcel:
|
|
|
30
322
|
InsertImageV2(app, wb, sheet, ['SKC图片', 'SKU图片'])
|
|
31
323
|
wb.save()
|
|
32
324
|
close_excel(app, wb)
|
|
325
|
+
if mode == 4:
|
|
326
|
+
WxWorkBot('b30aaa8d-1a1f-4378-841a-8b0f8295f2d9').send_file(excel_path)
|
|
33
327
|
|
|
34
328
|
def write_bak_advice(self, mode_list):
|
|
35
329
|
excel_path_list = [
|
|
@@ -159,6 +453,7 @@ class SheinExcel:
|
|
|
159
453
|
InsertImageV2(app, wb, sheet, ['SKC图片', 'SKU图片'])
|
|
160
454
|
wb.save()
|
|
161
455
|
close_excel(app, wb)
|
|
456
|
+
WxWorkBot('b30aaa8d-1a1f-4378-841a-8b0f8295f2d9').send_file(excel_path)
|
|
162
457
|
|
|
163
458
|
def write_week_report(self):
|
|
164
459
|
excel_path = create_file_path(self.config.excel_week_sales_report)
|
|
@@ -188,7 +483,7 @@ class SheinExcel:
|
|
|
188
483
|
format_to_percent(sheet, ['支付率', '点击率', '毛利率'])
|
|
189
484
|
self.dealFormula(sheet) # 有空再封装优化
|
|
190
485
|
colorize_by_field(app, wb, sheet, 'SPU')
|
|
191
|
-
autofit_column(sheet, ['商品信息','店铺名称', 'SKC点击率/SKC转化率', '自主参与活动'])
|
|
486
|
+
autofit_column(sheet, ['商品信息', '店铺名称', 'SKC点击率/SKC转化率', '自主参与活动'])
|
|
192
487
|
column_to_left(sheet, ['店铺名称', 'SKC点击率/SKC转化率', '自主参与活动'])
|
|
193
488
|
specify_column_width(sheet, ['商品标题'], 150 / 6)
|
|
194
489
|
add_borders(sheet)
|
qrpa/shein_lib.py
CHANGED
|
@@ -11,6 +11,8 @@ import json
|
|
|
11
11
|
from datetime import datetime
|
|
12
12
|
from playwright.sync_api import Page
|
|
13
13
|
|
|
14
|
+
from .shein_daily_report_model import SheinStoreSalesDetailManager
|
|
15
|
+
|
|
14
16
|
class SheinLib:
|
|
15
17
|
|
|
16
18
|
def __init__(self, config, bridge, web_page: Page, store_username, store_name):
|
|
@@ -80,6 +82,318 @@ class SheinLib:
|
|
|
80
82
|
# web_page.goto('https://sso.geiwohuo.com')
|
|
81
83
|
log('鉴权处理结束')
|
|
82
84
|
|
|
85
|
+
# 已上架备货款A数量
|
|
86
|
+
def get_product_bak_A_count(self):
|
|
87
|
+
url = "https://sso.geiwohuo.com/idms/goods-skc/list"
|
|
88
|
+
payload = {
|
|
89
|
+
"pageNumber" : 1,
|
|
90
|
+
"pageSize" : 10,
|
|
91
|
+
"supplierCodes" : "",
|
|
92
|
+
"skcs" : "",
|
|
93
|
+
"spu" : "",
|
|
94
|
+
"c7dSaleCntBegin" : "",
|
|
95
|
+
"c7dSaleCntEnd" : "",
|
|
96
|
+
"goodsLevelIdList" : [
|
|
97
|
+
61,
|
|
98
|
+
90
|
|
99
|
+
],
|
|
100
|
+
"supplyStatus" : "",
|
|
101
|
+
"shelfStatus" : 1,
|
|
102
|
+
"categoryIdList" : [],
|
|
103
|
+
"skcStockBegin" : "",
|
|
104
|
+
"skcStockEnd" : "",
|
|
105
|
+
"skuStockBegin" : "",
|
|
106
|
+
"skuStockEnd" : "",
|
|
107
|
+
"skcSaleDaysBegin" : "",
|
|
108
|
+
"skcSaleDaysEnd" : "",
|
|
109
|
+
"skuSaleDaysBegin" : "",
|
|
110
|
+
"skuSaleDaysEnd" : "",
|
|
111
|
+
"planUrgentCountBegin" : "",
|
|
112
|
+
"planUrgentCountEnd" : "",
|
|
113
|
+
"skcAvailableOrderBegin": "",
|
|
114
|
+
"skcAvailableOrderEnd" : "",
|
|
115
|
+
"skuAvailableOrderBegin": "",
|
|
116
|
+
"skuAvailableOrderEnd" : "",
|
|
117
|
+
"shelfDateBegin" : "",
|
|
118
|
+
"shelfDateEnd" : "",
|
|
119
|
+
"stockWarnStatusList" : [],
|
|
120
|
+
"labelFakeIdList" : [],
|
|
121
|
+
"sheinSaleByInventory" : "",
|
|
122
|
+
"tspIdList" : [],
|
|
123
|
+
"adviceStatus" : [],
|
|
124
|
+
"sortBy7dSaleCnt" : 2,
|
|
125
|
+
"goodsLevelFakeIdList" : [
|
|
126
|
+
3
|
|
127
|
+
]
|
|
128
|
+
}
|
|
129
|
+
response_text = fetch(self.web_page, url, payload)
|
|
130
|
+
error_code = response_text.get('code')
|
|
131
|
+
if str(error_code) != '0':
|
|
132
|
+
raise send_exception(json.dumps(response_text, ensure_ascii=False))
|
|
133
|
+
info = response_text.get('info')
|
|
134
|
+
count = info.get('count', 0)
|
|
135
|
+
log('获取已上架备货款A数量', count)
|
|
136
|
+
return count
|
|
137
|
+
|
|
138
|
+
# 已上架备货款B数量
|
|
139
|
+
def get_product_bak_B_count(self):
|
|
140
|
+
url = "https://sso.geiwohuo.com/idms/goods-skc/list"
|
|
141
|
+
payload = {
|
|
142
|
+
"pageNumber" : 1,
|
|
143
|
+
"pageSize" : 10,
|
|
144
|
+
"supplierCodes" : "",
|
|
145
|
+
"skcs" : "",
|
|
146
|
+
"spu" : "",
|
|
147
|
+
"c7dSaleCntBegin" : "",
|
|
148
|
+
"c7dSaleCntEnd" : "",
|
|
149
|
+
"goodsLevelIdList" : [
|
|
150
|
+
62,
|
|
151
|
+
227,
|
|
152
|
+
12,
|
|
153
|
+
230,
|
|
154
|
+
80,
|
|
155
|
+
58,
|
|
156
|
+
224
|
|
157
|
+
],
|
|
158
|
+
"supplyStatus" : "",
|
|
159
|
+
"shelfStatus" : 1,
|
|
160
|
+
"categoryIdList" : [],
|
|
161
|
+
"skcStockBegin" : "",
|
|
162
|
+
"skcStockEnd" : "",
|
|
163
|
+
"skuStockBegin" : "",
|
|
164
|
+
"skuStockEnd" : "",
|
|
165
|
+
"skcSaleDaysBegin" : None,
|
|
166
|
+
"skcSaleDaysEnd" : "",
|
|
167
|
+
"skuSaleDaysBegin" : "",
|
|
168
|
+
"skuSaleDaysEnd" : "",
|
|
169
|
+
"planUrgentCountBegin" : "",
|
|
170
|
+
"planUrgentCountEnd" : "",
|
|
171
|
+
"skcAvailableOrderBegin": "",
|
|
172
|
+
"skcAvailableOrderEnd" : "",
|
|
173
|
+
"skuAvailableOrderBegin": None,
|
|
174
|
+
"skuAvailableOrderEnd" : "",
|
|
175
|
+
"shelfDateBegin" : "",
|
|
176
|
+
"shelfDateEnd" : "",
|
|
177
|
+
"stockWarnStatusList" : [],
|
|
178
|
+
"labelFakeIdList" : [],
|
|
179
|
+
"sheinSaleByInventory" : "",
|
|
180
|
+
"tspIdList" : [],
|
|
181
|
+
"adviceStatus" : [],
|
|
182
|
+
"sortBy7dSaleCnt" : 2,
|
|
183
|
+
"goodsLevelFakeIdList" : [
|
|
184
|
+
4
|
|
185
|
+
]
|
|
186
|
+
}
|
|
187
|
+
response_text = fetch(self.web_page, url, payload)
|
|
188
|
+
error_code = response_text.get('code')
|
|
189
|
+
if str(error_code) != '0':
|
|
190
|
+
raise send_exception(json.dumps(response_text, ensure_ascii=False))
|
|
191
|
+
info = response_text.get('info')
|
|
192
|
+
count = info.get('count', 0)
|
|
193
|
+
log('获取已上架备货款B数量', count)
|
|
194
|
+
return count
|
|
195
|
+
|
|
196
|
+
# 已上架新款A数量
|
|
197
|
+
def get_product_A_count(self):
|
|
198
|
+
url = "https://sso.geiwohuo.com/idms/goods-skc/list"
|
|
199
|
+
payload = {
|
|
200
|
+
"pageNumber" : 1,
|
|
201
|
+
"pageSize" : 10,
|
|
202
|
+
"supplierCodes" : "",
|
|
203
|
+
"skcs" : "",
|
|
204
|
+
"spu" : "",
|
|
205
|
+
"c7dSaleCntBegin" : "",
|
|
206
|
+
"c7dSaleCntEnd" : "",
|
|
207
|
+
"goodsLevelIdList" : [
|
|
208
|
+
107
|
|
209
|
+
],
|
|
210
|
+
"supplyStatus" : "",
|
|
211
|
+
"shelfStatus" : 1,
|
|
212
|
+
"categoryIdList" : [],
|
|
213
|
+
"skcStockBegin" : "",
|
|
214
|
+
"skcStockEnd" : "",
|
|
215
|
+
"skuStockBegin" : "",
|
|
216
|
+
"skuStockEnd" : "",
|
|
217
|
+
"skcSaleDaysBegin" : None,
|
|
218
|
+
"skcSaleDaysEnd" : "",
|
|
219
|
+
"skuSaleDaysBegin" : "",
|
|
220
|
+
"skuSaleDaysEnd" : "",
|
|
221
|
+
"planUrgentCountBegin" : "",
|
|
222
|
+
"planUrgentCountEnd" : "",
|
|
223
|
+
"skcAvailableOrderBegin": "",
|
|
224
|
+
"skcAvailableOrderEnd" : "",
|
|
225
|
+
"skuAvailableOrderBegin": None,
|
|
226
|
+
"skuAvailableOrderEnd" : "",
|
|
227
|
+
"shelfDateBegin" : "",
|
|
228
|
+
"shelfDateEnd" : "",
|
|
229
|
+
"stockWarnStatusList" : [],
|
|
230
|
+
"labelFakeIdList" : [],
|
|
231
|
+
"sheinSaleByInventory" : "",
|
|
232
|
+
"tspIdList" : [],
|
|
233
|
+
"adviceStatus" : [],
|
|
234
|
+
"sortBy7dSaleCnt" : 2,
|
|
235
|
+
"goodsLevelFakeIdList" : [
|
|
236
|
+
2
|
|
237
|
+
]
|
|
238
|
+
}
|
|
239
|
+
response_text = fetch(self.web_page, url, payload)
|
|
240
|
+
error_code = response_text.get('code')
|
|
241
|
+
if str(error_code) != '0':
|
|
242
|
+
raise send_exception(json.dumps(response_text, ensure_ascii=False))
|
|
243
|
+
info = response_text.get('info')
|
|
244
|
+
count = info.get('count', 0)
|
|
245
|
+
log('获取已上架新款A数量', count)
|
|
246
|
+
return count
|
|
247
|
+
|
|
248
|
+
# 本周已上架数量
|
|
249
|
+
def get_week_shelf_product_count(self, start_date, end_date):
|
|
250
|
+
url = "https://sso.geiwohuo.com/idms/goods-skc/list"
|
|
251
|
+
payload = {
|
|
252
|
+
"pageNumber" : 1,
|
|
253
|
+
"pageSize" : 10,
|
|
254
|
+
"supplierCodes" : "",
|
|
255
|
+
"skcs" : "",
|
|
256
|
+
"spu" : "",
|
|
257
|
+
"c7dSaleCntBegin" : "",
|
|
258
|
+
"c7dSaleCntEnd" : "",
|
|
259
|
+
"goodsLevelIdList" : [],
|
|
260
|
+
"supplyStatus" : "",
|
|
261
|
+
"shelfStatus" : 1,
|
|
262
|
+
"categoryIdList" : [],
|
|
263
|
+
"skcStockBegin" : "",
|
|
264
|
+
"skcStockEnd" : "",
|
|
265
|
+
"skuStockBegin" : "",
|
|
266
|
+
"skuStockEnd" : "",
|
|
267
|
+
"skcSaleDaysBegin" : "",
|
|
268
|
+
"skcSaleDaysEnd" : "",
|
|
269
|
+
"skuSaleDaysBegin" : "",
|
|
270
|
+
"skuSaleDaysEnd" : "",
|
|
271
|
+
"planUrgentCountBegin" : "",
|
|
272
|
+
"planUrgentCountEnd" : "",
|
|
273
|
+
"skcAvailableOrderBegin": "",
|
|
274
|
+
"skcAvailableOrderEnd" : "",
|
|
275
|
+
"skuAvailableOrderBegin": "",
|
|
276
|
+
"skuAvailableOrderEnd" : "",
|
|
277
|
+
"shelfDateBegin" : start_date,
|
|
278
|
+
"shelfDateEnd" : end_date,
|
|
279
|
+
"stockWarnStatusList" : [],
|
|
280
|
+
"labelFakeIdList" : [],
|
|
281
|
+
"sheinSaleByInventory" : "",
|
|
282
|
+
"tspIdList" : [],
|
|
283
|
+
"adviceStatus" : [],
|
|
284
|
+
"sortBy7dSaleCnt" : 2
|
|
285
|
+
}
|
|
286
|
+
response_text = fetch(self.web_page, url, payload)
|
|
287
|
+
error_code = response_text.get('code')
|
|
288
|
+
if str(error_code) != '0':
|
|
289
|
+
raise send_exception(json.dumps(response_text, ensure_ascii=False))
|
|
290
|
+
info = response_text.get('info')
|
|
291
|
+
count = info.get('count', 0)
|
|
292
|
+
log('获取本周上架数量')
|
|
293
|
+
return count
|
|
294
|
+
|
|
295
|
+
# 已上架数量
|
|
296
|
+
def get_shelf_product_count(self):
|
|
297
|
+
log('获取所有已上架数量')
|
|
298
|
+
url = "https://sso.geiwohuo.com/spmp-api-prefix/spmp/product/list?page_num=1&page_size=10"
|
|
299
|
+
payload = {
|
|
300
|
+
"language" : "zh-cn",
|
|
301
|
+
"only_recommend_resell" : False,
|
|
302
|
+
"only_spmb_copy_product": False,
|
|
303
|
+
"search_abandon_product": False,
|
|
304
|
+
"shelf_type" : "ON_SHELF",
|
|
305
|
+
"sort_type" : 1
|
|
306
|
+
}
|
|
307
|
+
response_text = fetch(self.web_page, url, payload)
|
|
308
|
+
error_code = response_text.get('code')
|
|
309
|
+
if str(error_code) != '0':
|
|
310
|
+
raise send_exception(json.dumps(response_text, ensure_ascii=False))
|
|
311
|
+
|
|
312
|
+
info = response_text.get('info')
|
|
313
|
+
customObj = info.get('meta').get('customObj')
|
|
314
|
+
# 将数据转换成字典
|
|
315
|
+
result = {item["shelf_status"]: item["count"] for item in customObj}
|
|
316
|
+
return result
|
|
317
|
+
|
|
318
|
+
def get_yesterday_upload_product_count(self, dt=None):
|
|
319
|
+
url = "https://sso.geiwohuo.com/spmp-api-prefix/spmp/product/publish/record/page_list?page_num=1&page_size=100"
|
|
320
|
+
payload = {
|
|
321
|
+
"edit_type" : 0,
|
|
322
|
+
"language" : "zh-cn",
|
|
323
|
+
"only_current_month_recommend": False,
|
|
324
|
+
"only_spmb_copy_product" : False,
|
|
325
|
+
"query_time_out" : False,
|
|
326
|
+
"search_diy_custom" : False
|
|
327
|
+
}
|
|
328
|
+
response_text = fetch(self.web_page, url, payload)
|
|
329
|
+
error_code = response_text.get('code')
|
|
330
|
+
if str(error_code) != '0':
|
|
331
|
+
raise send_exception(json.dumps(response_text, ensure_ascii=False))
|
|
332
|
+
info = response_text.get('info')
|
|
333
|
+
count = 0
|
|
334
|
+
for item in info.get('data', {}):
|
|
335
|
+
if TimeUtils.is_yesterday(item['create_time'], dt):
|
|
336
|
+
count += 1
|
|
337
|
+
log('获取昨日已上传数量', count)
|
|
338
|
+
return count
|
|
339
|
+
|
|
340
|
+
def get_week_sales_stat_detail(self):
|
|
341
|
+
global ListNotify, NotifyItem
|
|
342
|
+
dt = self.get_dt_time()
|
|
343
|
+
yesterday = TimeUtils.get_yesterday(dt)
|
|
344
|
+
date_7_days_ago = TimeUtils.get_past_nth_day(6, None, '%Y-%m-%d')
|
|
345
|
+
log('-7', date_7_days_ago)
|
|
346
|
+
date_1_days_ago = TimeUtils.get_past_nth_day(1, None, '%Y-%m-%d')
|
|
347
|
+
log('-1', date_1_days_ago)
|
|
348
|
+
|
|
349
|
+
url = "https://sso.geiwohuo.com/sbn/index/get_critical_indicator_curve_chart"
|
|
350
|
+
payload = {
|
|
351
|
+
"areaCd" : "cn",
|
|
352
|
+
"dt" : dt,
|
|
353
|
+
"countrySite": [
|
|
354
|
+
"shein-all"
|
|
355
|
+
],
|
|
356
|
+
"startDate" : date_7_days_ago,
|
|
357
|
+
"endDate" : date_1_days_ago,
|
|
358
|
+
"queryType" : 1,
|
|
359
|
+
"pageNum" : 1,
|
|
360
|
+
"pageSize" : 100
|
|
361
|
+
}
|
|
362
|
+
response_text = fetch(self.web_page, url, payload)
|
|
363
|
+
error_code = response_text.get('code')
|
|
364
|
+
if str(error_code) != '0':
|
|
365
|
+
raise send_exception(json.dumps(response_text, ensure_ascii=False))
|
|
366
|
+
info = response_text.get('info', {})
|
|
367
|
+
|
|
368
|
+
last_item = SheinStoreSalesDetailManager(self.config.database_url).get_records_as_dict(self.store_username, yesterday)
|
|
369
|
+
log('last_item', last_item)
|
|
370
|
+
day_item = info[-1]
|
|
371
|
+
item = {}
|
|
372
|
+
item["store_username"] = self.store_username
|
|
373
|
+
item["store_name"] = self.store_name
|
|
374
|
+
item["day"] = day_item["dataDate"]
|
|
375
|
+
item["sales_num"] = day_item["saleCnt1d"]
|
|
376
|
+
item['sales_num_inc'] = item['sales_num'] - last_item.get('sales_num', 0)
|
|
377
|
+
item['sales_amount'] = day_item['dealAmt1d']
|
|
378
|
+
item['sales_amount_inc'] = item['sales_amount'] - float(last_item.get('sales_amount', 0))
|
|
379
|
+
item['visitor_num'] = day_item['idxShopGoodsUv1d']
|
|
380
|
+
item['visitor_num_inc'] = item['visitor_num'] - last_item.get('visitor_num', 0)
|
|
381
|
+
item['bak_A_num'] = self.get_product_bak_A_count()
|
|
382
|
+
item['bak_A_num_inc'] = item['bak_A_num'] - last_item.get('bak_A_num', 0)
|
|
383
|
+
item['new_A_num'] = self.get_product_A_count()
|
|
384
|
+
item['new_A_num_inc'] = item['new_A_num'] - last_item.get('new_A_num', 0)
|
|
385
|
+
dictProduct = self.get_shelf_product_count()
|
|
386
|
+
item['on_sales_product_num'] = dictProduct.get('ON_SHELF')
|
|
387
|
+
item['on_sales_product_num_inc'] = item['on_sales_product_num'] - last_item.get('on_sales_product_num', 0)
|
|
388
|
+
item['wait_shelf_product_num'] = dictProduct.get('WAIT_SHELF')
|
|
389
|
+
item['wait_shelf_product_num_inc'] = item['wait_shelf_product_num'] - last_item.get('wait_shelf_product_num', 0)
|
|
390
|
+
item['upload_product_num'] = self.get_yesterday_upload_product_count()
|
|
391
|
+
item['upload_product_num_inc'] = item['upload_product_num'] - last_item.get('upload_product_num', 0)
|
|
392
|
+
item['sold_out_product_num'] = dictProduct.get('SOLD_OUT')
|
|
393
|
+
item['shelf_off_product_num'] = dictProduct.get('OUT_SHELF')
|
|
394
|
+
|
|
395
|
+
SheinStoreSalesDetailManager(self.config.database_url).insert_data([item])
|
|
396
|
+
|
|
83
397
|
def get_delivery_order_list(self, orderType=2):
|
|
84
398
|
page_num = 1
|
|
85
399
|
page_size = 200
|
qrpa/wxwork.py
CHANGED
|
@@ -6,14 +6,15 @@ qrpa/fun_excel.py,sha256=uaSNvJGA0vopBn0N__tVYpdeX4WlliOAQWP4sHanFUw,104030
|
|
|
6
6
|
qrpa/fun_file.py,sha256=yzjDV16WL5vRys7J4uQcNzIFkX4D5MAlSCwxcD-mwQo,11966
|
|
7
7
|
qrpa/fun_web.py,sha256=5QLQorAhRzMOGMRh4eCJ2UH8ZhVHvxkHwobWhmgU5qM,6286
|
|
8
8
|
qrpa/fun_win.py,sha256=-LnTeocdTt72NVH6VgLdpAT9_C5oV9okeudXG6CftMA,8034
|
|
9
|
-
qrpa/
|
|
10
|
-
qrpa/
|
|
9
|
+
qrpa/shein_daily_report_model.py,sha256=C2ZE3XhLEZdV4Imdz7gE6756apSWyVku-hVKy4BOAwg,12588
|
|
10
|
+
qrpa/shein_excel.py,sha256=e0P6OABDEycwwCgpRLRHvsC1M1mh502sC1cArh9bfQc,29106
|
|
11
|
+
qrpa/shein_lib.py,sha256=IurTKa-K5Ay6DCOSv-cxiz1N6uvFKrxpxvREkua78FE,78696
|
|
11
12
|
qrpa/shein_sqlite.py,sha256=ZQwD0Gz81q9WY7tY2HMEYvSF9r3N_G_Aur3bYfST9WY,5707
|
|
12
13
|
qrpa/shein_ziniao.py,sha256=nSqqcEPh4nVQtUxUnIRzeZfTLyXywGPjPZn5pP-w57U,18309
|
|
13
14
|
qrpa/time_utils.py,sha256=ef0hhbN_6b-gcnz5ETIVOoxemIMvcxGVGGIRnHnGaBo,29564
|
|
14
15
|
qrpa/time_utils_example.py,sha256=shHOXKKF3QSzb0SHsNc34M61wEkkLuM30U9X1THKNS8,8053
|
|
15
|
-
qrpa/wxwork.py,sha256=
|
|
16
|
-
qrpa-1.0.
|
|
17
|
-
qrpa-1.0.
|
|
18
|
-
qrpa-1.0.
|
|
19
|
-
qrpa-1.0.
|
|
16
|
+
qrpa/wxwork.py,sha256=zu6eQZHJEpwe4Q6vvwmBJaU31qm0bixII_XC9uJIrJc,10618
|
|
17
|
+
qrpa-1.0.21.dist-info/METADATA,sha256=r-VvSiexCLJNsr-esjT0QeZEJCadEC9lrioY4imRNog,231
|
|
18
|
+
qrpa-1.0.21.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
19
|
+
qrpa-1.0.21.dist-info/top_level.txt,sha256=F6T5igi0fhXDucPPUbmmSC0qFCDEsH5eVijfVF48OFU,5
|
|
20
|
+
qrpa-1.0.21.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|