qrpa 1.0.15__py3-none-any.whl → 1.0.16__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/__init__.py CHANGED
@@ -9,6 +9,7 @@ from .time_utils import TimeUtils
9
9
 
10
10
  from .fun_file import read_dict_from_file, read_dict_from_file_ex, write_dict_to_file, write_dict_to_file_ex
11
11
  from .fun_file import get_progress_json_ex, check_progress_json_ex, done_progress_json_ex
12
+ from .fun_file import delete_file, delete_file_simple
12
13
 
13
14
  from .fun_web import fetch, fetch_via_iframe, find_all_iframe, full_screen_shot
14
15
  from .fun_win import *
qrpa/fun_file.py CHANGED
@@ -8,6 +8,152 @@ from datetime import date, datetime, timedelta, timezone
8
8
 
9
9
  from .fun_base import log
10
10
 
11
+ import os
12
+ import gc
13
+ import time
14
+ import psutil
15
+ from pathlib import Path
16
+
17
+ def delete_file(file_path):
18
+ """
19
+ 删除文件的优化函数,自动处理已打开的文件
20
+
21
+ Args:
22
+ file_path (str): 要删除的文件路径
23
+
24
+ Returns:
25
+ bool: 删除成功返回True,失败返回False
26
+ """
27
+ try:
28
+ file_path = Path(file_path).resolve() # 规范化路径
29
+
30
+ if not file_path.exists():
31
+ log(f"文件 {file_path} 不存在。")
32
+ return False
33
+
34
+ # 强制垃圾回收,关闭可能的文件句柄
35
+ gc.collect()
36
+
37
+ # 第一次尝试直接删除
38
+ try:
39
+ os.remove(file_path)
40
+ log(f"文件 {file_path} 已成功删除。")
41
+ return True
42
+ except PermissionError:
43
+ log(f"文件 {file_path} 可能被占用,尝试关闭相关进程...")
44
+
45
+ # 尝试找到并关闭占用该文件的进程
46
+ if close_file_handles(file_path):
47
+ # 等待一小段时间让系统释放句柄
48
+ time.sleep(0.1)
49
+
50
+ # 再次尝试删除
51
+ try:
52
+ os.remove(file_path)
53
+ log(f"文件 {file_path} 已成功删除。")
54
+ return True
55
+ except PermissionError:
56
+ log(f"错误:即使尝试关闭文件句柄后,仍无法删除文件 {file_path}。")
57
+ return False
58
+ else:
59
+ log(f"错误:无法关闭文件 {file_path} 的句柄,删除失败。")
60
+ return False
61
+
62
+ except FileNotFoundError:
63
+ log(f"错误:文件 {file_path} 未找到。")
64
+ return False
65
+ except Exception as e:
66
+ log(f"错误:删除文件时发生未知错误:{e}")
67
+ return False
68
+
69
+ def close_file_handles(file_path):
70
+ """
71
+ 尝试关闭指定文件的所有句柄
72
+
73
+ Args:
74
+ file_path (Path): 文件路径
75
+
76
+ Returns:
77
+ bool: 成功关闭返回True,失败返回False
78
+ """
79
+ try:
80
+ file_path_str = str(file_path)
81
+ closed_any = False
82
+
83
+ # 遍历所有进程,查找打开该文件的进程
84
+ for proc in psutil.process_iter(['pid', 'name']):
85
+ try:
86
+ # 获取进程打开的文件列表
87
+ open_files = proc.open_files()
88
+ for open_file in open_files:
89
+ if os.path.samefile(open_file.path, file_path_str):
90
+ log(f"发现进程 {proc.info['name']} (PID: {proc.info['pid']}) 正在使用文件")
91
+
92
+ # 如果是当前Python进程,尝试强制关闭文件句柄
93
+ if proc.info['pid'] == os.getpid():
94
+ closed_any = True
95
+ else:
96
+ # 对于其他进程,可以选择终止(谨慎使用)
97
+ log(f"警告:文件被其他进程占用,请手动关闭应用程序")
98
+
99
+ except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
100
+ continue
101
+ except Exception as e:
102
+ continue
103
+
104
+ return closed_any
105
+
106
+ except Exception as e:
107
+ log(f"关闭文件句柄时出错:{e}")
108
+ return False
109
+
110
+ def delete_file_simple(file_path):
111
+ """
112
+ 简化版本的删除文件函数(不需要额外依赖)
113
+
114
+ Args:
115
+ file_path (str): 要删除的文件路径
116
+
117
+ Returns:
118
+ bool: 删除成功返回True,失败返回False
119
+ """
120
+ try:
121
+ file_path = os.path.abspath(file_path) # 获取绝对路径
122
+
123
+ if not os.path.exists(file_path):
124
+ log(f"文件 {file_path} 不存在。")
125
+ return False
126
+
127
+ # 强制垃圾回收
128
+ gc.collect()
129
+
130
+ # 尝试删除
131
+ try:
132
+ os.remove(file_path)
133
+ log(f"文件 {file_path} 已成功删除。")
134
+ return True
135
+ except PermissionError:
136
+ # 在Windows上,尝试修改文件权限
137
+ if os.name == 'nt': # Windows
138
+ try:
139
+ os.chmod(file_path, 0o777)
140
+ time.sleep(0.1) # 短暂等待
141
+ os.remove(file_path)
142
+ log(f"文件 {file_path} 已成功删除。")
143
+ return True
144
+ except Exception:
145
+ pass
146
+
147
+ log(f"错误:没有权限删除文件 {file_path},可能文件正在被使用。")
148
+ return False
149
+
150
+ except FileNotFoundError:
151
+ log(f"错误:文件 {file_path} 未找到。")
152
+ return False
153
+ except Exception as e:
154
+ log(f"错误:删除文件时发生未知错误:{e}")
155
+ return False
156
+
11
157
  def read_dict_from_file(file_path, cache_interval=3600 * 24 * 365 * 10):
12
158
  """
13
159
  从文件中读取字典。
@@ -170,4 +316,4 @@ def check_progress_json_ex(config, task_key, just_store_username=None):
170
316
  return False
171
317
  else:
172
318
  log(f"进度文件不存在或为空: {progress_file}")
173
- return True
319
+ return True
qrpa/shein_excel.py CHANGED
@@ -79,3 +79,83 @@ class SheinExcel:
79
79
  self.format_bak_advice(new_excel_path, sheet_name, mode)
80
80
 
81
81
  return new_excel_path_list
82
+
83
+ def write_activity_list(self):
84
+ cache_file = f'{self.config.auto_dir}/shein/activity_list/activity_list_{TimeUtils.today_date()}.json'
85
+ dict_activity = read_dict_from_file(cache_file)
86
+ all_data = []
87
+ header = []
88
+ for store_username, excel_data in dict_activity.items():
89
+ header = excel_data[:1]
90
+ all_data += excel_data[1:]
91
+
92
+ all_data = header + all_data
93
+
94
+ excel_path = create_file_path(self.config.excel_activity_list)
95
+ sheet_name = 'Sheet1'
96
+ write_data(excel_path, sheet_name, all_data)
97
+ self.format_activity_list(excel_path, sheet_name)
98
+
99
+ def format_activity_list(self, excel_path, sheet_name):
100
+ app, wb, sheet = open_excel(excel_path, sheet_name)
101
+ beautify_title(sheet)
102
+ add_borders(sheet)
103
+ column_to_left(sheet, ['活动信息'])
104
+ colorize_by_field(app, wb, sheet, '店铺名称')
105
+ autofit_column(sheet, ['店铺名称', '活动信息'])
106
+ wb.save()
107
+ close_excel(app, wb)
108
+
109
+ def write_jit_data(self):
110
+ excel_path_1 = create_file_path(self.config.Excel_Order_Type_1)
111
+ summary_excel_data_1 = []
112
+
113
+ cache_file_1 = f'{self.config.auto_dir}/shein/cache/jit_{TimeUtils.today_date()}_1_{TimeUtils.get_period()}.json'
114
+ dict_1 = read_dict_from_file(cache_file_1)
115
+ dict_store = read_dict_from_file(f'{self.config.auto_dir}/shein_store_alias.json')
116
+
117
+ header = []
118
+ for store_username, excel_data in dict_1.items():
119
+ store_name = dict_store.get(store_username)
120
+ sheet_name = store_name
121
+ write_data(excel_path_1, sheet_name, excel_data)
122
+ self.format_jit(excel_path_1, sheet_name)
123
+ header = excel_data[0]
124
+ summary_excel_data_1 += excel_data[1:]
125
+
126
+ if len(summary_excel_data_1) > 0:
127
+ sheet_name = 'Sheet1'
128
+ write_data(excel_path_1, sheet_name, [header] + summary_excel_data_1)
129
+ self.format_jit(excel_path_1, sheet_name)
130
+
131
+ excel_path_2 = create_file_path(self.config.Excel_Order_Type_2)
132
+ summary_excel_data_2 = []
133
+
134
+ cache_file_2 = f'{self.config.auto_dir}/shein/cache/jit_{TimeUtils.today_date()}_2_{TimeUtils.get_period()}.json'
135
+ dict_2 = read_dict_from_file(cache_file_2)
136
+
137
+ header = []
138
+ for store_username, excel_data in dict_2.items():
139
+ store_name = dict_store.get(store_username)
140
+ sheet_name = store_name
141
+ write_data(excel_path_2, sheet_name, excel_data)
142
+ self.format_jit(excel_path_2, sheet_name)
143
+ header = excel_data[0]
144
+ summary_excel_data_2 += excel_data[1:]
145
+
146
+ if len(summary_excel_data_2) > 0:
147
+ sheet_name = 'Sheet1'
148
+ write_data(excel_path_2, sheet_name, [header] + summary_excel_data_2)
149
+ self.format_jit(excel_path_2, sheet_name)
150
+
151
+ def format_jit(self, excel_path, sheet_name):
152
+ app, wb, sheet = open_excel(excel_path, sheet_name)
153
+ beautify_title(sheet)
154
+ add_borders(sheet)
155
+ colorize_by_field(app, wb, sheet, 'SKC')
156
+ column_to_left(sheet, ["商品信息", "近7天SKU销量/SKC销量/SKC曝光", "SKC点击率/SKC转化率", "自主参与活动"])
157
+ autofit_column(sheet,
158
+ ['店铺名称', '商品信息', "近7天SKU销量/SKC销量/SKC曝光", "SKC点击率/SKC转化率", "自主参与活动"])
159
+ InsertImageV2(app, wb, sheet, ['SKC图片', 'SKU图片'])
160
+ wb.save()
161
+ close_excel(app, wb)
qrpa/shein_lib.py CHANGED
@@ -1,6 +1,6 @@
1
1
  from qrpa import read_dict_from_file, write_dict_to_file, read_dict_from_file_ex, write_dict_to_file_ex
2
2
  from qrpa import log, fetch, send_exception, md5_string
3
- from qrpa import time_utils, get_safe_value
3
+ from qrpa import time_utils, TimeUtils, get_safe_value
4
4
 
5
5
  import math
6
6
  import time
@@ -77,6 +77,287 @@ class SheinLib:
77
77
  # web_page.goto('https://sso.geiwohuo.com')
78
78
  log('鉴权处理结束')
79
79
 
80
+ def get_delivery_order_list(self, orderType=2):
81
+ page_num = 1
82
+ page_size = 200
83
+
84
+ url = f"https://sso.geiwohuo.com/pfmp/order/list"
85
+ payload = {}
86
+ if orderType == 1:
87
+ payload = {
88
+ "orderType": orderType,
89
+ "page" : page_num,
90
+ "perPage" : page_size,
91
+ "status" : [2],
92
+ }
93
+ elif orderType == 2:
94
+ payload = {
95
+ "orderType" : orderType,
96
+ "page" : page_num,
97
+ "perPage" : page_size,
98
+ "status" : [2],
99
+ "isJitOrder": 2
100
+ }
101
+ response_text = fetch(self.web_page, url, payload)
102
+ error_code = response_text.get('code')
103
+ if str(error_code) != '0':
104
+ raise send_exception(json.dumps(response_text, ensure_ascii=False))
105
+
106
+ spu_list = response_text['info']['data']
107
+ total = response_text['info']['meta']['count']
108
+ totalPage = math.ceil(total / page_size)
109
+
110
+ skc_list = [item['goods']['skcName'] for item in spu_list]
111
+ self.get_activity_label(skc_list)
112
+
113
+ for page in range(2, totalPage + 1):
114
+ log(f'获取订单列表 第{page}/{totalPage}页')
115
+ page_num = page
116
+ if orderType == 1:
117
+ payload = {
118
+ "orderType": orderType,
119
+ "page" : page_num,
120
+ "perPage" : page_size,
121
+ "status" : [2],
122
+ }
123
+ elif orderType == 2:
124
+ payload = {
125
+ "orderType" : orderType,
126
+ "page" : page_num,
127
+ "perPage" : page_size,
128
+ "status" : [2],
129
+ "isJitOrder": 2
130
+ }
131
+ response_text = fetch(self.web_page, url, payload)
132
+ spu_list_new = response_text['info']['data']
133
+ skc_list = [item['goods']['skcName'] for item in spu_list_new]
134
+ self.get_activity_label(skc_list)
135
+ spu_list += spu_list_new
136
+ time.sleep(0.3)
137
+
138
+ if len(spu_list) == 0:
139
+ log(f'无{["", "急采", "备货"][orderType]}发货单')
140
+ return None
141
+
142
+ write_to_excel = [
143
+ # 0 1 2 3 4 5 6 7
144
+ ['店铺名称', 'SKC图片', 'SKU图片', '商品信息', '下单/需求数量', '库存(模式/本地/在途/希音)', '成本价', '核价',
145
+ '近7天SKU销量/SKC销量/SKC曝光', 'SKC点击率/SKC转化率', '自主参与活动', '最晚预约上门取件', '要求实际完成取件',
146
+ 'SKC', 'SKU']
147
+ ]
148
+ cache_file2 = f'{self.config.auto_dir}/shein/dict/skc_shelf_{self.store_username}.json'
149
+ DictSkcShelf = read_dict_from_file(cache_file2)
150
+ cache_file3 = f'{self.config.auto_dir}/shein/dict/skc_product_{self.store_username}.json'
151
+ DictSkcProduct = read_dict_from_file(cache_file3)
152
+ cache_file = f'{self.config.auto_dir}/shein/dict/activity_price_{self.store_name}.json'
153
+ dictActivityPrice = read_dict_from_file(cache_file)
154
+ cache_file4 = f'{self.config.auto_dir}/shein/dict/dict_sku_info_{self.store_username}.json'
155
+ DictSkuInfo = read_dict_from_file(cache_file4)
156
+ for spu_item in spu_list:
157
+ skc = spu_item['goods']['skcName']
158
+ skcCode = spu_item['goods']['supplierCode']
159
+ skc_img = str(spu_item['goods']['imgPath'])
160
+ orderNum = spu_item['sellerOrderNo']
161
+ orderTime = spu_item['allocateTime']
162
+ requestTakeParcelTime = spu_item['requestTakeParcelTime']
163
+ suggestedReserveTime = spu_item['suggestedReserveTime']
164
+ good_level = spu_item['goods']['goodsLevelName']
165
+
166
+ self.get_skc_week_actual_sales(skc)
167
+
168
+ spu = DictSkcProduct[skc]['spu_name']
169
+ log('spu', spu)
170
+ for sku_item in spu_item['detail']:
171
+ needQuantity = sku_item['needQuantity']
172
+ orderQuantity = sku_item['orderQuantity']
173
+ # sku_img = sku_item['skuThumb']
174
+ skuCode = sku_item['supplierSku']
175
+ stock = self.bridge.get_sku_stock(skuCode, 'mb')
176
+ cost_price = self.bridge.get_sku_cost(skuCode, 'mb')
177
+ suffixZh = sku_item['suffixZh']
178
+ sku = sku_item['skuCode']
179
+ supplyPrice = sku_item['supplyPrice']
180
+ sku_img = self.bridge.get_sku_img(skuCode, 'mb')
181
+ sale_model = DictSkuInfo[skuCode][0]
182
+ shein_stock = DictSkuInfo[skuCode][1]
183
+ shelf_days = DictSkuInfo[skuCode][2]
184
+ real_transit = DictSkuInfo[skuCode][3]
185
+ stock_str = f'{sale_model}\n{stock}/{real_transit}/{shein_stock}'
186
+
187
+ item = []
188
+ item.append(f'{self.store_name}\n{good_level}')
189
+ item.append(skc_img)
190
+ item.append(sku_img)
191
+ if cost_price == '-':
192
+ profit = '-'
193
+ else:
194
+ profit = f'{float(supplyPrice) - float(cost_price):.2f}'
195
+ # 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')
196
+ 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}')
197
+ item.append(f'[{orderQuantity}/{needQuantity}]')
198
+ item.append(stock_str)
199
+ item.append(cost_price)
200
+ item.append(supplyPrice)
201
+ sale_num_list, sale_data_list = self.get_skc_week_sale_list(spu, skc, sku)
202
+ item.append("\n".join(sale_num_list))
203
+ item.append("\n".join(sale_data_list))
204
+ item.append(self.get_skc_activity_label(skc, sku, dictActivityPrice))
205
+ item.append(suggestedReserveTime)
206
+ item.append(requestTakeParcelTime)
207
+ item.append(skc)
208
+ item.append(sku)
209
+ write_to_excel.append(item)
210
+
211
+ cache_file = f'{self.config.auto_dir}/shein/cache/jit_{TimeUtils.today_date()}_{orderType}_{TimeUtils.get_period()}.json'
212
+ write_dict_to_file_ex(cache_file, {self.store_username: write_to_excel}, {self.store_username})
213
+
214
+ return write_to_excel
215
+
216
+ # 获取商品包含sku销量的列表
217
+ def get_dict_sku_stock_detail(self):
218
+ log(f'获取备货信息商品列表 做成字典')
219
+ url = "https://sso.geiwohuo.com/idms/goods-skc/list"
220
+ pageNumber = 1
221
+ pageSize = 100
222
+ dictPayload = {
223
+ "pageNumber" : pageNumber,
224
+ "pageSize" : pageSize,
225
+ "supplierCodes" : "",
226
+ "skcs" : "",
227
+ "spu" : "",
228
+ "c7dSaleCntBegin" : "",
229
+ "c7dSaleCntEnd" : "",
230
+ "goodsLevelIdList" : [],
231
+ "supplyStatus" : "",
232
+ "shelfStatus" : "",
233
+ "categoryIdList" : [],
234
+ "skcStockBegin" : "",
235
+ "skcStockEnd" : "",
236
+ "skuStockBegin" : "",
237
+ "skuStockEnd" : "",
238
+ "skcSaleDaysBegin" : "",
239
+ "skcSaleDaysEnd" : "",
240
+ "skuSaleDaysBegin" : "",
241
+ "skuSaleDaysEnd" : "",
242
+ "planUrgentCountBegin" : "",
243
+ "planUrgentCountEnd" : "",
244
+ "skcAvailableOrderBegin": "",
245
+ "skcAvailableOrderEnd" : "",
246
+ "skuAvailableOrderBegin": "",
247
+ "skuAvailableOrderEnd" : "",
248
+ "shelfDateBegin" : "",
249
+ "shelfDateEnd" : "",
250
+ "stockWarnStatusList" : [],
251
+ "labelFakeIdList" : [],
252
+ "sheinSaleByInventory" : "",
253
+ "tspIdList" : [],
254
+ "adviceStatus" : [],
255
+ "sortBy7dSaleCnt" : 2
256
+ }
257
+ payload = dictPayload
258
+ response_text = fetch(self.web_page, url, payload)
259
+ error_code = response_text.get('code')
260
+ if str(error_code) != '0':
261
+ raise send_exception(json.dumps(response_text, ensure_ascii=False))
262
+
263
+ spu_list = response_text['info']['list']
264
+
265
+ total = response_text['info']['count']
266
+ totalPage = math.ceil(total / pageSize)
267
+ for page in range(2, totalPage + 1):
268
+ log(f'获取备货信息商品列表 第{page}/{totalPage}页')
269
+ dictPayload['pageNumber'] = page
270
+ payload = dictPayload
271
+ response_text = fetch(self.web_page, url, payload)
272
+ spu_list_new = response_text['info']['list']
273
+ spu_list += spu_list_new
274
+ time.sleep(0.3)
275
+
276
+ DictSkuInfo = {}
277
+ for spu_info in spu_list:
278
+ sale_model = spu_info.get('saleModel', {}).get('name') if spu_info.get('saleModel') else '-'
279
+ shelfDays = spu_info['shelfDays']
280
+ for sku_info in spu_info['skuList']:
281
+ attr = sku_info['attr']
282
+ if attr == '合计':
283
+ continue
284
+ skuExtCode = str(sku_info['supplierSku'])
285
+ shein_stock = sku_info['stock']
286
+
287
+ transit = sku_info['transit'] # 在途
288
+ real_transit = transit + sku_info['stayShelf'] - sku_info['transitSale']
289
+
290
+ DictSkuInfo[skuExtCode] = [sale_model, shein_stock, shelfDays, real_transit]
291
+
292
+ write_dict_to_file(f'{self.config.auto_dir}/shein/dict/dict_sku_info_{self.store_username}.json', DictSkuInfo)
293
+
294
+ return DictSkuInfo
295
+
296
+ def get_shop_notify_num(self):
297
+ log(f'正在获取 {self.store_name} 通知数据')
298
+ url = "https://sso.geiwohuo.com/sso/homePage/v4/detail"
299
+ payload = {
300
+ "metaIndexIds": [
301
+ 246, # 急采-待发货
302
+ 245 # 备货-待发货
303
+ ],
304
+ "templateType": 0
305
+ }
306
+ response_text = fetch(self.web_page, url, payload)
307
+ error_code = response_text.get('code')
308
+ if str(error_code) != '0':
309
+ raise send_exception(json.dumps(response_text, ensure_ascii=False))
310
+ info = response_text.get('info')
311
+
312
+ cache_file = f'{self.config.auto_dir}/shein/notify/{self.store_name}_{TimeUtils.get_current_datetime()}.json'
313
+ write_dict_to_file(cache_file, info)
314
+
315
+ num245 = 0
316
+ num246 = 0
317
+ for item in info['list']:
318
+ if item['metaIndexId'] == 245:
319
+ num245 = item['count']
320
+ if item['metaIndexId'] == 246:
321
+ num246 = item['count']
322
+
323
+ NotifyItem = [self.store_name, num246, num245]
324
+
325
+ cache_file = f'{self.config.auto_dir}/shein/cache/jit_notify_{TimeUtils.today_date()}.json'
326
+ write_dict_to_file_ex(cache_file, {self.store_username: NotifyItem}, {self.store_username})
327
+
328
+ return info
329
+
330
+ def get_activity_list(self):
331
+ url = "https://sso.geiwohuo.com/mrs-api-prefix/mbrs/activity/get_activity_list?page_num=1&page_size=100"
332
+ payload = {}
333
+ response_text = fetch(self.web_page, url, payload)
334
+ error_code = response_text.get('code')
335
+ if str(error_code) != '0':
336
+ raise send_exception(json.dumps(response_text, ensure_ascii=False))
337
+ total = response_text.get('info', {}).get('total_count')
338
+ excel_data = [[
339
+ '店铺名称', '活动信息', '已报数量', '可报数量'
340
+ ]]
341
+ if total > 0:
342
+ for item in response_text.get('info', {}).get('activity_detail_list'):
343
+ activity_tag = item.get('text_tag_content')
344
+ activity_name = item['activity_name']
345
+ start_time = item['activity_start_zone_time']
346
+ end_time = item['activity_end_zone_time']
347
+ start_time2 = item['start_zone_time']
348
+ end_time2 = item['end_zone_time']
349
+ allow_goods_num = item.get('allow_goods_num')
350
+ apply_goods_num = item.get('apply_goods_num')
351
+ row_item = [
352
+ self.store_name,
353
+ f"活动名称: 【{activity_tag}】{activity_name}\n报名时间: {start_time}~{end_time}\n活动时间: {start_time2}~{end_time2}\n已报数量: {apply_goods_num}/{allow_goods_num}",
354
+ apply_goods_num,
355
+ allow_goods_num,
356
+ ]
357
+ excel_data.append(row_item)
358
+ cache_file = f'{self.config.auto_dir}/shein/activity_list/activity_list_{TimeUtils.today_date()}.json'
359
+ write_dict_to_file_ex(cache_file, {self.store_username: excel_data}, [self.store_username])
360
+
80
361
  def get_product_list(self):
81
362
  self.web_page.goto('https://sso.geiwohuo.com/#/spmp/commdities/list')
82
363
  self.web_page.wait_for_load_state("load")
@@ -521,7 +802,6 @@ class SheinLib:
521
802
  # 10.运营-热销款 (有现货建议 30天外 有销量)
522
803
  def get_bak_advice(self, mode=1, skcs=None, source='mb'):
523
804
  log(f'获取备货信息商品列表 做成字典')
524
- global DictSkuInfo
525
805
  if skcs == None or len(skcs) == 0:
526
806
  # if mode == 3:
527
807
  # skcs = "sh2405133614611175" # 这是一个不存在的skc
qrpa/wxwork.py CHANGED
@@ -17,6 +17,7 @@ import hashlib
17
17
  import base64
18
18
  import requests
19
19
  from requests_toolbelt import MultipartEncoder
20
+ from datetime import datetime
20
21
 
21
22
  # 通过企微群机器人发送消息
22
23
  class WxWorkBot:
@@ -99,6 +100,42 @@ class WxWorkBot:
99
100
  }
100
101
  self.send_msg(data)
101
102
 
103
+ def send_notify(self, title, sub_title_list, data_list):
104
+ """
105
+ 发送Markdown消息
106
+ :param content:
107
+ :return:
108
+ """
109
+
110
+ current_date = datetime.now().strftime("%Y-%m-%d")
111
+ header = f"{current_date} {title}\n\n"
112
+
113
+ arr_color = ['warning', 'info', 'warning']
114
+ arr_sub_header = [f"<font color='{arr_color[index]}'>{title}</font>" for index, title in enumerate(sub_title_list)]
115
+ sub_header = "\t".join(arr_sub_header) + "\n\n"
116
+
117
+ # 获取每个元素的行索引和列索引
118
+ arr_content = [
119
+ [
120
+ f'{value}' if col_idx == 0 else f"<font color='{arr_color[col_idx - 1]}'>{value}</font>"
121
+ for col_idx, value in enumerate(row)
122
+ ] # 每行的元素组成一个子列表
123
+ for row_idx, row in enumerate(data_list) # 外层循环控制行
124
+ ]
125
+ # 将二维数组转换为字符串
126
+ content = "\n".join(
127
+ # 对每行的元素列表使用 join(),用 \t 连接
128
+ "\t".join(row) for row in arr_content
129
+ )
130
+
131
+ data = {
132
+ "msgtype" : "markdown",
133
+ "markdown": {
134
+ "content": header + sub_header + content
135
+ }
136
+ }
137
+ self.send_msg(data)
138
+
102
139
  def send_img(self, img_path):
103
140
  """
104
141
  发送图片消息
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: qrpa
3
- Version: 1.0.15
3
+ Version: 1.0.16
4
4
  Summary: qsir's rpa library
5
5
  Author: QSir
6
6
  Author-email: QSir <1171725650@qq.com>
@@ -1,18 +1,18 @@
1
1
  qrpa/RateLimitedSender.py,sha256=hqvb1qspDFaW4RsLuVufylOrefkMgixANKeBaGEqYb4,1421
2
- qrpa/__init__.py,sha256=NNE1S-k2R-5Ed_ssVVu-R6_DJ00zBWuPnOvyJAU6DOI,706
2
+ qrpa/__init__.py,sha256=0EV-ZBTPzRP4rjVqFU2FYe1gxrtzdomd84a3dzNGLpM,761
3
3
  qrpa/db_migrator.py,sha256=2VmhzcMsU0MKpl-mNCwKyV8tLTqyEysSpP27-S_rQZ8,21862
4
4
  qrpa/fun_base.py,sha256=W_owEa8-yuGG18n9kX3remm9YTzym69ztQjtYCNMTw4,3308
5
5
  qrpa/fun_excel.py,sha256=S-A-kTZ2e3dJ8NqdhiOFqTlcr0d6XLMP6imagztRel0,103605
6
- qrpa/fun_file.py,sha256=BInN-Iuxi8sYiJ031gXI_DzO_n170RsOf7fnpkl_etM,7100
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/shein_excel.py,sha256=6DjK5p1S6y4oElcxmLpvJtQPSf76Vu99Pdsmo2d-Po0,3818
10
- qrpa/shein_lib.py,sha256=aEN0Z9hOjF0XLIoPAHtbIleVmG9NlPsf1XPseOgFBiM,39099
9
+ qrpa/shein_excel.py,sha256=zP_WRZigbbDXXSeNei1j0sKO_-_gMBTd6wz05TwDfM0,7429
10
+ qrpa/shein_lib.py,sha256=xOyXgyS1a6GMjho5RFrfPo0kp56Hjj0IquIuF64-1jo,52689
11
11
  qrpa/shein_ziniao.py,sha256=nSqqcEPh4nVQtUxUnIRzeZfTLyXywGPjPZn5pP-w57U,18309
12
12
  qrpa/time_utils.py,sha256=ef0hhbN_6b-gcnz5ETIVOoxemIMvcxGVGGIRnHnGaBo,29564
13
13
  qrpa/time_utils_example.py,sha256=shHOXKKF3QSzb0SHsNc34M61wEkkLuM30U9X1THKNS8,8053
14
- qrpa/wxwork.py,sha256=IeJq-1LwKCsCt_AdMSLvYrQJWNoQp_wqVtk6iqwJqf4,9200
15
- qrpa-1.0.15.dist-info/METADATA,sha256=OnrU6Dk4AtMPz-WSJsjaAIBeb52W300aLT8U0cFC_5g,231
16
- qrpa-1.0.15.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
17
- qrpa-1.0.15.dist-info/top_level.txt,sha256=F6T5igi0fhXDucPPUbmmSC0qFCDEsH5eVijfVF48OFU,5
18
- qrpa-1.0.15.dist-info/RECORD,,
14
+ qrpa/wxwork.py,sha256=8LzRmoHYo0KBfZ1cmkWPj824Zp52NAUYm3RXYzmIIi0,10507
15
+ qrpa-1.0.16.dist-info/METADATA,sha256=Z7iNE0BPDJucPDz6Gg8ptn1ZHTil51WyTeylRcdud94,231
16
+ qrpa-1.0.16.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
17
+ qrpa-1.0.16.dist-info/top_level.txt,sha256=F6T5igi0fhXDucPPUbmmSC0qFCDEsH5eVijfVF48OFU,5
18
+ qrpa-1.0.16.dist-info/RECORD,,
File without changes