qrpa 1.0.34__py3-none-any.whl → 1.1.50__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.
- qrpa/RateLimitedSender.py +45 -45
- qrpa/__init__.py +31 -26
- qrpa/db_migrator.py +600 -600
- qrpa/feishu_bot_app.py +267 -267
- qrpa/feishu_client.py +410 -0
- qrpa/feishu_logic.py +1443 -0
- qrpa/fun_base.py +339 -337
- qrpa/fun_excel.py +529 -61
- qrpa/fun_file.py +318 -318
- qrpa/fun_web.py +258 -148
- qrpa/fun_win.py +198 -198
- qrpa/mysql_module/__init__.py +0 -0
- qrpa/mysql_module/new_product_analysis_model.py +556 -0
- qrpa/mysql_module/shein_ledger_model.py +468 -0
- qrpa/mysql_module/shein_product_model.py +495 -0
- qrpa/mysql_module/shein_return_order_model.py +569 -0
- qrpa/mysql_module/shein_store_model.py +595 -0
- qrpa/shein_daily_report_model.py +375 -375
- qrpa/shein_excel.py +1248 -109
- qrpa/shein_lib.py +2333 -143
- qrpa/shein_mysql.py +92 -0
- qrpa/shein_sqlite.py +153 -153
- qrpa/shein_ziniao.py +529 -450
- qrpa/temu_chrome.py +56 -56
- qrpa/temu_excel.py +139 -139
- qrpa/temu_lib.py +156 -154
- qrpa/time_utils.py +87 -50
- qrpa/time_utils_example.py +243 -243
- qrpa/wxwork.py +318 -318
- {qrpa-1.0.34.dist-info → qrpa-1.1.50.dist-info}/METADATA +1 -1
- qrpa-1.1.50.dist-info/RECORD +33 -0
- qrpa-1.0.34.dist-info/RECORD +0 -24
- {qrpa-1.0.34.dist-info → qrpa-1.1.50.dist-info}/WHEEL +0 -0
- {qrpa-1.0.34.dist-info → qrpa-1.1.50.dist-info}/top_level.txt +0 -0
qrpa/feishu_logic.py
ADDED
|
@@ -0,0 +1,1443 @@
|
|
|
1
|
+
# -*- coding: UTF-8 -*-
|
|
2
|
+
import json
|
|
3
|
+
import time
|
|
4
|
+
from .feishu_client import FeishuClient
|
|
5
|
+
|
|
6
|
+
from .fun_base import log
|
|
7
|
+
|
|
8
|
+
class FeishuBusinessLogic:
|
|
9
|
+
def __init__(self, client: FeishuClient):
|
|
10
|
+
self.client = client
|
|
11
|
+
|
|
12
|
+
def get_all_folder_files(self, folder_token: str):
|
|
13
|
+
# 获取文件夹中所有文件(自动处理分页)
|
|
14
|
+
all_files = []
|
|
15
|
+
page_token = None
|
|
16
|
+
|
|
17
|
+
while True:
|
|
18
|
+
data = self.client.list_folder_files(folder_token, page_token=page_token)
|
|
19
|
+
files = data.get("files", [])
|
|
20
|
+
all_files.extend(files)
|
|
21
|
+
|
|
22
|
+
page_token = data.get("next_page_token")
|
|
23
|
+
if not page_token:
|
|
24
|
+
break
|
|
25
|
+
|
|
26
|
+
return all_files
|
|
27
|
+
|
|
28
|
+
def get_all_permission_members(self, token: str, type: str):
|
|
29
|
+
# 获取文档所有权限成员(自动处理分页)
|
|
30
|
+
all_members = []
|
|
31
|
+
page_token = None
|
|
32
|
+
|
|
33
|
+
while True:
|
|
34
|
+
data = self.client.list_permission_members(token, type, page_token=page_token)
|
|
35
|
+
members = data.get("members", [])
|
|
36
|
+
all_members.extend(members)
|
|
37
|
+
|
|
38
|
+
page_token = data.get("next_page_token")
|
|
39
|
+
if not page_token:
|
|
40
|
+
break
|
|
41
|
+
|
|
42
|
+
return all_members
|
|
43
|
+
|
|
44
|
+
def number_to_excel_column(self, num: int):
|
|
45
|
+
# 将数字转换为Excel列名(1->A, 2->B, 26->Z, 27->AA)
|
|
46
|
+
result = ""
|
|
47
|
+
while num > 0:
|
|
48
|
+
num -= 1
|
|
49
|
+
result = chr(65 + num % 26) + result
|
|
50
|
+
num //= 26
|
|
51
|
+
return result
|
|
52
|
+
|
|
53
|
+
def get_existing_column_values(self, spreadsheet_token: str, sheet_id: str, column_name: str, header_row: int = 1):
|
|
54
|
+
# 获取指定列中已存在的所有值
|
|
55
|
+
all_data = self.client.read_sheet_data(spreadsheet_token, sheet_id)
|
|
56
|
+
if not all_data or len(all_data.get("valueRange", {}).get("values", [])) < header_row:
|
|
57
|
+
return set()
|
|
58
|
+
|
|
59
|
+
values = all_data["valueRange"]["values"]
|
|
60
|
+
headers = values[header_row - 1] if len(values) >= header_row else []
|
|
61
|
+
|
|
62
|
+
column_index = -1
|
|
63
|
+
for i, header in enumerate(headers):
|
|
64
|
+
if str(header).strip() == column_name:
|
|
65
|
+
column_index = i
|
|
66
|
+
break
|
|
67
|
+
|
|
68
|
+
if column_index == -1:
|
|
69
|
+
return set()
|
|
70
|
+
|
|
71
|
+
existing_values = set()
|
|
72
|
+
for row_index in range(header_row, len(values)):
|
|
73
|
+
row = values[row_index]
|
|
74
|
+
if len(row) > column_index and row[column_index]:
|
|
75
|
+
value = str(row[column_index]).strip()
|
|
76
|
+
if value:
|
|
77
|
+
existing_values.add(value)
|
|
78
|
+
|
|
79
|
+
return existing_values
|
|
80
|
+
|
|
81
|
+
def get_existing_row_combinations(self, spreadsheet_token: str, sheet_id: str, key_columns: list, header_row: int = 1):
|
|
82
|
+
# 获取表格中已存在的行数据组合(基于关键字段)
|
|
83
|
+
all_data = self.client.read_sheet_data(spreadsheet_token, sheet_id)
|
|
84
|
+
if not all_data or len(all_data.get("valueRange", {}).get("values", [])) < header_row:
|
|
85
|
+
return set()
|
|
86
|
+
|
|
87
|
+
values = all_data["valueRange"]["values"]
|
|
88
|
+
headers = values[header_row - 1] if len(values) >= header_row else []
|
|
89
|
+
|
|
90
|
+
key_indices = []
|
|
91
|
+
for col_name in key_columns:
|
|
92
|
+
column_index = -1
|
|
93
|
+
for i, header in enumerate(headers):
|
|
94
|
+
if str(header).strip() == col_name:
|
|
95
|
+
column_index = i
|
|
96
|
+
break
|
|
97
|
+
if column_index != -1:
|
|
98
|
+
key_indices.append(column_index)
|
|
99
|
+
|
|
100
|
+
if not key_indices:
|
|
101
|
+
return set()
|
|
102
|
+
|
|
103
|
+
existing_combinations = set()
|
|
104
|
+
for row_index in range(header_row, len(values)):
|
|
105
|
+
row = values[row_index]
|
|
106
|
+
key_values = []
|
|
107
|
+
for idx in key_indices:
|
|
108
|
+
if len(row) > idx and row[idx] is not None:
|
|
109
|
+
key_values.append(str(row[idx]).strip())
|
|
110
|
+
else:
|
|
111
|
+
key_values.append("")
|
|
112
|
+
|
|
113
|
+
if all(value for value in key_values):
|
|
114
|
+
combination_key = "|".join(key_values)
|
|
115
|
+
existing_combinations.add(combination_key)
|
|
116
|
+
|
|
117
|
+
return existing_combinations
|
|
118
|
+
|
|
119
|
+
def append_data_with_deduplication(self, spreadsheet_token: str, sheet_id: str, values: list,
|
|
120
|
+
dedup_columns: list, insert_data_option: str = "INSERT_ROWS", header_row: int = 1,
|
|
121
|
+
reference_column: str = None):
|
|
122
|
+
# 向工作表追加数据,根据指定字段组合去重
|
|
123
|
+
if not values or len(values) < 2:
|
|
124
|
+
return {
|
|
125
|
+
"status" : "success",
|
|
126
|
+
"message" : "无数据需要插入",
|
|
127
|
+
"total_rows" : 0,
|
|
128
|
+
"inserted_rows": 0,
|
|
129
|
+
"skipped_rows" : 0
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
# 获取现有数据中的行组合
|
|
133
|
+
existing_combinations = self.get_existing_row_combinations(spreadsheet_token, sheet_id, dedup_columns, header_row)
|
|
134
|
+
|
|
135
|
+
headers = values[0]
|
|
136
|
+
data_rows = values[1:]
|
|
137
|
+
|
|
138
|
+
# 找到去重字段的索引
|
|
139
|
+
dedup_indices = []
|
|
140
|
+
for col_name in dedup_columns:
|
|
141
|
+
try:
|
|
142
|
+
column_index = headers.index(col_name)
|
|
143
|
+
dedup_indices.append(column_index)
|
|
144
|
+
except ValueError:
|
|
145
|
+
pass
|
|
146
|
+
|
|
147
|
+
if not dedup_indices:
|
|
148
|
+
raise Exception(f"在数据表头中未找到任何去重字段: {dedup_columns}")
|
|
149
|
+
|
|
150
|
+
# 过滤重复数据 - 只剔除与表格已存在数据重复的记录
|
|
151
|
+
filtered_rows = []
|
|
152
|
+
skipped_count = 0
|
|
153
|
+
|
|
154
|
+
for row in data_rows:
|
|
155
|
+
key_values = []
|
|
156
|
+
for idx in dedup_indices:
|
|
157
|
+
if len(row) > idx and row[idx] is not None:
|
|
158
|
+
key_values.append(str(row[idx]).strip())
|
|
159
|
+
else:
|
|
160
|
+
key_values.append("")
|
|
161
|
+
|
|
162
|
+
if all(value for value in key_values):
|
|
163
|
+
combination_key = "|".join(key_values)
|
|
164
|
+
if combination_key in existing_combinations:
|
|
165
|
+
skipped_count += 1
|
|
166
|
+
continue
|
|
167
|
+
|
|
168
|
+
filtered_rows.append(row)
|
|
169
|
+
|
|
170
|
+
if not filtered_rows:
|
|
171
|
+
return {
|
|
172
|
+
"status" : "success",
|
|
173
|
+
"message" : f"所有数据都已存在,共跳过 {skipped_count} 行重复数据",
|
|
174
|
+
"total_rows" : len(data_rows),
|
|
175
|
+
"inserted_rows": 0,
|
|
176
|
+
"skipped_rows" : skipped_count
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
# 插入过滤后的数据
|
|
180
|
+
if reference_column:
|
|
181
|
+
# 使用精确位置插入避免覆盖原有数据
|
|
182
|
+
append_result = self._insert_data_at_precise_position_with_reference_column(
|
|
183
|
+
spreadsheet_token, sheet_id, filtered_rows, headers, reference_column, insert_data_option
|
|
184
|
+
)
|
|
185
|
+
else:
|
|
186
|
+
# 回退到普通追加
|
|
187
|
+
append_result = self.client.write_data_to_range(spreadsheet_token, sheet_id, "A:A", filtered_rows, insert_data_option)
|
|
188
|
+
|
|
189
|
+
return {
|
|
190
|
+
"status" : "success",
|
|
191
|
+
"message" : f"成功插入 {len(filtered_rows)} 行新数据,跳过 {skipped_count} 行重复数据",
|
|
192
|
+
"total_rows" : len(data_rows),
|
|
193
|
+
"inserted_rows": len(filtered_rows),
|
|
194
|
+
"skipped_rows" : skipped_count,
|
|
195
|
+
"append_result": append_result
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
def analyze_merge_ranges(self, data_rows: list, merge_column_index: int):
|
|
199
|
+
# 分析数据,找出需要合并的行范围
|
|
200
|
+
merge_ranges = []
|
|
201
|
+
current_value = None
|
|
202
|
+
start_row = 0
|
|
203
|
+
|
|
204
|
+
for i, row in enumerate(data_rows):
|
|
205
|
+
if len(row) <= merge_column_index:
|
|
206
|
+
continue
|
|
207
|
+
|
|
208
|
+
row_value = row[merge_column_index]
|
|
209
|
+
|
|
210
|
+
if current_value is None:
|
|
211
|
+
current_value = row_value
|
|
212
|
+
start_row = i
|
|
213
|
+
elif row_value != current_value:
|
|
214
|
+
if i - 1 > start_row:
|
|
215
|
+
merge_ranges.append({
|
|
216
|
+
'start_row': start_row,
|
|
217
|
+
'end_row' : i - 1,
|
|
218
|
+
'value' : current_value
|
|
219
|
+
})
|
|
220
|
+
|
|
221
|
+
current_value = row_value
|
|
222
|
+
start_row = i
|
|
223
|
+
|
|
224
|
+
# 处理最后一个合并范围
|
|
225
|
+
if len(data_rows) - 1 > start_row:
|
|
226
|
+
merge_ranges.append({
|
|
227
|
+
'start_row': start_row,
|
|
228
|
+
'end_row' : len(data_rows) - 1,
|
|
229
|
+
'value' : current_value
|
|
230
|
+
})
|
|
231
|
+
|
|
232
|
+
return merge_ranges
|
|
233
|
+
|
|
234
|
+
def _insert_data_at_precise_position_with_reference_column(self, spreadsheet_token: str, sheet_id: str,
|
|
235
|
+
data_rows: list, headers: list, reference_column: str,
|
|
236
|
+
insert_data_option: str = "INSERT_ROWS"):
|
|
237
|
+
# 使用参考列确定准确插入位置的数据插入方法
|
|
238
|
+
log(f"[DEBUG] 使用参考列 '{reference_column}' 进行精确位置插入")
|
|
239
|
+
|
|
240
|
+
try:
|
|
241
|
+
# 找到参考列的索引
|
|
242
|
+
try:
|
|
243
|
+
ref_column_index = headers.index(reference_column)
|
|
244
|
+
ref_column_letter = self.number_to_excel_column(ref_column_index + 1)
|
|
245
|
+
log(f"[DEBUG] 参考列索引: {ref_column_index}, 列字母: {ref_column_letter}")
|
|
246
|
+
except ValueError:
|
|
247
|
+
log(f"[DEBUG] 未找到参考列 '{reference_column}',回退到普通追加")
|
|
248
|
+
return self.client.write_data_to_range(spreadsheet_token, sheet_id, "A:A", data_rows, insert_data_option)
|
|
249
|
+
|
|
250
|
+
# 读取参考列的数据来确定当前总行数
|
|
251
|
+
ref_column_range = f"{ref_column_letter}:{ref_column_letter}"
|
|
252
|
+
data = self.client.read_multiple_ranges(spreadsheet_token, sheet_id, [ref_column_range])
|
|
253
|
+
|
|
254
|
+
value_ranges = data.get("valueRanges", [])
|
|
255
|
+
if not value_ranges:
|
|
256
|
+
log(f"[DEBUG] 无法读取参考列数据,回退到普通追加")
|
|
257
|
+
return self.client.write_data_to_range(spreadsheet_token, sheet_id, "A:A", data_rows, insert_data_option)
|
|
258
|
+
|
|
259
|
+
# 获取参考列所有数据
|
|
260
|
+
first_range = value_ranges[0]
|
|
261
|
+
all_column_values = first_range.get("values", [])
|
|
262
|
+
|
|
263
|
+
# 计算当前总行数(包括表头)
|
|
264
|
+
total_rows = len(all_column_values)
|
|
265
|
+
|
|
266
|
+
# 新数据的起始行号(表格行号,从1开始)
|
|
267
|
+
insert_start_row = total_rows + 1
|
|
268
|
+
|
|
269
|
+
log(f"[DEBUG] 当前总行数: {total_rows}, 新数据起始行: {insert_start_row}")
|
|
270
|
+
|
|
271
|
+
# 计算数据范围
|
|
272
|
+
rows_count = len(data_rows)
|
|
273
|
+
cols_count = len(data_rows[0]) if data_rows else 0
|
|
274
|
+
|
|
275
|
+
# 构建插入范围
|
|
276
|
+
if cols_count > 0:
|
|
277
|
+
end_col = self.number_to_excel_column(cols_count)
|
|
278
|
+
else:
|
|
279
|
+
end_col = 'A'
|
|
280
|
+
range_str = f"A{insert_start_row}:{end_col}{insert_start_row + rows_count - 1}"
|
|
281
|
+
|
|
282
|
+
log(f"[DEBUG] 精确插入位置: {range_str},插入 {rows_count} 行数据")
|
|
283
|
+
|
|
284
|
+
# 使用精确范围写入数据
|
|
285
|
+
result = self.client.write_data_to_range(spreadsheet_token, sheet_id, range_str, data_rows)
|
|
286
|
+
log(f"[DEBUG] 成功在精确位置插入数据")
|
|
287
|
+
return result
|
|
288
|
+
|
|
289
|
+
except Exception as e:
|
|
290
|
+
log(f"[DEBUG] 精确位置插入失败,回退到普通追加: {str(e)}")
|
|
291
|
+
return self.client.write_data_to_range(spreadsheet_token, sheet_id, "A:A", data_rows, insert_data_option)
|
|
292
|
+
|
|
293
|
+
def _perform_merge_for_empty_sheet(self, spreadsheet_token: str, sheet_id: str, data_list: list,
|
|
294
|
+
merge_column: str, merge_columns: list, dedup_result: dict):
|
|
295
|
+
# 对空表格插入的数据进行合并操作
|
|
296
|
+
log(f"[DEBUG] _perform_merge_for_empty_sheet 开始执行")
|
|
297
|
+
log(f"[DEBUG] data_list 长度: {len(data_list)}")
|
|
298
|
+
|
|
299
|
+
try:
|
|
300
|
+
headers = data_list[0] # 表头
|
|
301
|
+
data_rows = data_list[1:] # 数据行
|
|
302
|
+
|
|
303
|
+
log(f"[DEBUG] 表头: {headers[:5]}...") # 只显示前5列
|
|
304
|
+
log(f"[DEBUG] 数据行数: {len(data_rows)}")
|
|
305
|
+
log(f"[DEBUG] 第一行数据: {data_rows[0][:5] if data_rows else '无数据'}...")
|
|
306
|
+
|
|
307
|
+
# 找到基准列和需要合并的列的索引
|
|
308
|
+
try:
|
|
309
|
+
merge_column_index = headers.index(merge_column)
|
|
310
|
+
log(f"[DEBUG] 找到基准列 '{merge_column}' 的索引: {merge_column_index}")
|
|
311
|
+
except ValueError:
|
|
312
|
+
log(f"[DEBUG] 未找到基准列 '{merge_column}' 在表头中")
|
|
313
|
+
return {
|
|
314
|
+
"status" : "success",
|
|
315
|
+
"message" : dedup_result.get("message", "") + f",未找到基准列 '{merge_column}'",
|
|
316
|
+
"dedup_result": dedup_result,
|
|
317
|
+
"merge_result": {"merge_count": 0, "message": f"未找到基准列 '{merge_column}'"}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
merge_column_indices = []
|
|
321
|
+
for col_name in merge_columns:
|
|
322
|
+
try:
|
|
323
|
+
idx = headers.index(col_name)
|
|
324
|
+
merge_column_indices.append(idx)
|
|
325
|
+
log(f"[DEBUG] 找到需要合并的列 '{col_name}' 的索引: {idx}")
|
|
326
|
+
except ValueError:
|
|
327
|
+
log(f"[DEBUG] 未找到需要合并的列 '{col_name}' 在表头中")
|
|
328
|
+
|
|
329
|
+
log(f"[DEBUG] 总共找到 {len(merge_column_indices)} 个需要合并的列")
|
|
330
|
+
|
|
331
|
+
if not merge_column_indices:
|
|
332
|
+
log(f"[DEBUG] 没有找到任何需要合并的列,退出合并操作")
|
|
333
|
+
return {
|
|
334
|
+
"status" : "success",
|
|
335
|
+
"message" : dedup_result.get("message", "") + ",未找到需要合并的列",
|
|
336
|
+
"dedup_result": dedup_result,
|
|
337
|
+
"merge_result": {"merge_count": 0, "message": "未找到需要合并的列"}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
# 分析合并范围
|
|
341
|
+
log(f"[DEBUG] 开始分析合并范围...")
|
|
342
|
+
merge_ranges = self.analyze_merge_ranges(data_rows, merge_column_index)
|
|
343
|
+
log(f"[DEBUG] 分析完成,找到 {len(merge_ranges)} 个合并范围")
|
|
344
|
+
|
|
345
|
+
for i, merge_range in enumerate(merge_ranges):
|
|
346
|
+
log(f"[DEBUG] 合并范围 {i + 1}: 第{merge_range['start_row'] + 1}行到第{merge_range['end_row'] + 1}行,值: {merge_range['value']}")
|
|
347
|
+
|
|
348
|
+
# 执行合并操作
|
|
349
|
+
merge_count = 0
|
|
350
|
+
log(f"[DEBUG] 开始执行合并操作...")
|
|
351
|
+
|
|
352
|
+
for merge_group in merge_ranges:
|
|
353
|
+
start_row = merge_group['start_row']
|
|
354
|
+
end_row = merge_group['end_row']
|
|
355
|
+
|
|
356
|
+
log(f"[DEBUG] 处理合并组: 第{start_row}行到第{end_row}行")
|
|
357
|
+
|
|
358
|
+
if end_row > start_row:
|
|
359
|
+
for col_index in merge_column_indices:
|
|
360
|
+
col_letter = self.number_to_excel_column(col_index + 1)
|
|
361
|
+
# 注意:数据从第2行开始(第1行是表头),所以要+2
|
|
362
|
+
range_str = f"{col_letter}{start_row + 2}:{col_letter}{end_row + 2}"
|
|
363
|
+
|
|
364
|
+
log(f"[DEBUG] 尝试合并单元格范围: {range_str}")
|
|
365
|
+
|
|
366
|
+
try:
|
|
367
|
+
self.client.merge_cells(spreadsheet_token, sheet_id, "MERGE_ALL", range_str)
|
|
368
|
+
merge_count += 1
|
|
369
|
+
log(f"[DEBUG] 成功合并单元格: {range_str}")
|
|
370
|
+
except Exception as e:
|
|
371
|
+
log(f"[DEBUG] 合并单元格失败: {range_str}, 错误: {str(e)}")
|
|
372
|
+
else:
|
|
373
|
+
log(f"[DEBUG] 跳过合并(只有一行): 第{start_row}行")
|
|
374
|
+
|
|
375
|
+
return {
|
|
376
|
+
"status" : "success",
|
|
377
|
+
"message" : f"{dedup_result.get('message', '')},成功合并 {merge_count} 个单元格范围",
|
|
378
|
+
"dedup_result": dedup_result,
|
|
379
|
+
"merge_result": {
|
|
380
|
+
"merge_count": merge_count,
|
|
381
|
+
"message" : f"成功合并 {merge_count} 个单元格范围"
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
except Exception as e:
|
|
386
|
+
return {
|
|
387
|
+
"status" : "success",
|
|
388
|
+
"message" : dedup_result.get("message", "") + f",合并操作失败: {str(e)}",
|
|
389
|
+
"dedup_result": dedup_result,
|
|
390
|
+
"merge_result": {"merge_count": 0, "message": f"合并操作失败: {str(e)}"}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
def write_data_and_merge_by_column_with_deduplication(self, spreadsheet_token: str, sheet_id: str,
|
|
394
|
+
data_list: list, dedup_column: str, merge_column: str,
|
|
395
|
+
merge_columns: list, reference_column: str, header_row: int = 1, image_column: str = None):
|
|
396
|
+
# 写入数据并根据指定列的值合并相关列的单元格(带去重功能)
|
|
397
|
+
log(f"[DEBUG] write_data_and_merge_by_column_with_deduplication 开始执行")
|
|
398
|
+
log(f"[DEBUG] 参数: dedup_column={dedup_column}, merge_column={merge_column}")
|
|
399
|
+
log(f"[DEBUG] merge_columns={merge_columns}")
|
|
400
|
+
log(f"[DEBUG] reference_column={reference_column}")
|
|
401
|
+
log(f"[DEBUG] data_list 长度: {len(data_list) if data_list else 0}")
|
|
402
|
+
|
|
403
|
+
# 检查表格是否为空
|
|
404
|
+
try:
|
|
405
|
+
log(f"[DEBUG] 检查表格是否为空...")
|
|
406
|
+
first_row_data = self.client.read_multiple_ranges(spreadsheet_token, sheet_id, ["A1:ZZ1"])
|
|
407
|
+
value_ranges = first_row_data.get("valueRanges", [])
|
|
408
|
+
|
|
409
|
+
# 更严格的空表格检查:不仅要有数据,还要有非空的内容
|
|
410
|
+
if not value_ranges or not value_ranges[0].get("values"):
|
|
411
|
+
is_empty_sheet = True
|
|
412
|
+
log(f"[DEBUG] 表格完全为空")
|
|
413
|
+
else:
|
|
414
|
+
first_row = value_ranges[0]["values"][0]
|
|
415
|
+
# 检查第一行是否有任何非空值
|
|
416
|
+
has_non_empty_values = any(cell and str(cell).strip() for cell in first_row)
|
|
417
|
+
is_empty_sheet = not has_non_empty_values
|
|
418
|
+
log(f"[DEBUG] 第一行数据: {first_row[:5]}...")
|
|
419
|
+
log(f"[DEBUG] 第一行是否有非空值: {has_non_empty_values}")
|
|
420
|
+
|
|
421
|
+
log(f"[DEBUG] 表格空状态检查结果: is_empty_sheet={is_empty_sheet}")
|
|
422
|
+
except Exception as e:
|
|
423
|
+
log(f"[DEBUG] 检查表格空状态时出错: {str(e)},默认认为是空表格")
|
|
424
|
+
is_empty_sheet = True
|
|
425
|
+
|
|
426
|
+
if is_empty_sheet:
|
|
427
|
+
log(f"[DEBUG] 检测到空表格,开始插入数据...")
|
|
428
|
+
log(f"[DEBUG] 数据行数: {len(data_list)} 行")
|
|
429
|
+
|
|
430
|
+
# 空表格直接插入所有数据
|
|
431
|
+
self.client.write_data_to_range(spreadsheet_token, sheet_id, "A:A", data_list, "INSERT_ROWS")
|
|
432
|
+
inserted_rows = len(data_list) - 1 if data_list else 0
|
|
433
|
+
|
|
434
|
+
log(f"[DEBUG] 数据插入完成,插入了 {inserted_rows} 行数据")
|
|
435
|
+
|
|
436
|
+
dedup_result = {
|
|
437
|
+
"total_rows" : inserted_rows,
|
|
438
|
+
"inserted_rows": inserted_rows,
|
|
439
|
+
"skipped_rows" : 0,
|
|
440
|
+
"message" : f"成功插入 {inserted_rows} 行新数据(空表格直接插入)"
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
# 空表格插入数据后也需要进行合并操作
|
|
444
|
+
if not data_list or len(data_list) < 2:
|
|
445
|
+
log(f"[DEBUG] 数据不足,无需合并。data_list长度: {len(data_list) if data_list else 0}")
|
|
446
|
+
return {
|
|
447
|
+
"status" : "success",
|
|
448
|
+
"message" : "数据插入完成,但数据不足无需合并",
|
|
449
|
+
"dedup_result": dedup_result,
|
|
450
|
+
"merge_result": {"merge_count": 0, "message": "数据不足无需合并"}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
log(f"[DEBUG] 开始对空表格插入的数据进行合并操作...")
|
|
454
|
+
log(f"[DEBUG] 基准列: {merge_column}, 需要合并的列: {merge_columns}")
|
|
455
|
+
|
|
456
|
+
# 对空表格插入的数据进行合并操作
|
|
457
|
+
merge_result = self._perform_merge_for_empty_sheet(
|
|
458
|
+
spreadsheet_token, sheet_id, data_list, merge_column, merge_columns, dedup_result
|
|
459
|
+
)
|
|
460
|
+
|
|
461
|
+
# 处理图片插入
|
|
462
|
+
if image_column:
|
|
463
|
+
try:
|
|
464
|
+
image_result = self._insert_images_for_empty_sheet(
|
|
465
|
+
spreadsheet_token, sheet_id, data_list, image_column
|
|
466
|
+
)
|
|
467
|
+
merge_result["image_result"] = image_result
|
|
468
|
+
merge_result["message"] += f",{image_result.get('message', '')}"
|
|
469
|
+
except Exception as e:
|
|
470
|
+
log(f"[DEBUG] 空表格图片插入失败: {str(e)}")
|
|
471
|
+
merge_result["image_result"] = {"image_count": 0, "message": f"图片插入失败: {str(e)}"}
|
|
472
|
+
else:
|
|
473
|
+
merge_result["image_result"] = {"image_count": 0, "message": "未指定图片列"}
|
|
474
|
+
|
|
475
|
+
return merge_result
|
|
476
|
+
else:
|
|
477
|
+
log(f"[DEBUG] 检测到非空表格,进行去重插入...")
|
|
478
|
+
# 非空表格进行去重插入,传递参考列参数
|
|
479
|
+
dedup_result = self.append_data_with_deduplication(
|
|
480
|
+
spreadsheet_token, sheet_id, data_list, [dedup_column], "INSERT_ROWS", header_row, reference_column
|
|
481
|
+
)
|
|
482
|
+
|
|
483
|
+
if dedup_result.get("inserted_rows", 0) == 0:
|
|
484
|
+
return {
|
|
485
|
+
"status" : "success",
|
|
486
|
+
"message" : dedup_result.get("message", ""),
|
|
487
|
+
"dedup_result": dedup_result,
|
|
488
|
+
"merge_result": {"merge_count": 0, "message": "无需合并"}
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
# 执行合并操作
|
|
492
|
+
merge_count = 0
|
|
493
|
+
try:
|
|
494
|
+
# 读取表头
|
|
495
|
+
header_data = self.client.read_multiple_ranges(spreadsheet_token, sheet_id, [f"A{header_row}:ZZ{header_row}"])
|
|
496
|
+
headers = header_data.get("valueRanges", [{}])[0].get("values", [[]])[0] if header_data.get("valueRanges") else []
|
|
497
|
+
|
|
498
|
+
if not headers:
|
|
499
|
+
return {
|
|
500
|
+
"status" : "success",
|
|
501
|
+
"message" : dedup_result.get("message", "") + ",无法读取表头跳过合并",
|
|
502
|
+
"dedup_result": dedup_result,
|
|
503
|
+
"merge_result": {"merge_count": 0, "message": "无法读取表头"}
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
# 找到基准列和需要合并的列的索引
|
|
507
|
+
merge_column_index = headers.index(merge_column)
|
|
508
|
+
merge_column_indices = []
|
|
509
|
+
for col_name in merge_columns:
|
|
510
|
+
try:
|
|
511
|
+
merge_column_indices.append(headers.index(col_name))
|
|
512
|
+
except ValueError:
|
|
513
|
+
pass
|
|
514
|
+
|
|
515
|
+
if not merge_column_indices:
|
|
516
|
+
return {
|
|
517
|
+
"status" : "success",
|
|
518
|
+
"message" : dedup_result.get("message", "") + ",未找到需要合并的列",
|
|
519
|
+
"dedup_result": dedup_result,
|
|
520
|
+
"merge_result": {"merge_count": 0, "message": "未找到需要合并的列"}
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
# 使用参考列确定总行数
|
|
524
|
+
ref_column_index = headers.index(reference_column)
|
|
525
|
+
ref_column_letter = self.number_to_excel_column(ref_column_index + 1)
|
|
526
|
+
|
|
527
|
+
ref_data = self.client.read_multiple_ranges(spreadsheet_token, sheet_id, [f"{ref_column_letter}:{ref_column_letter}"])
|
|
528
|
+
all_column_values = ref_data.get("valueRanges", [{}])[0].get("values", [])
|
|
529
|
+
total_rows = len(all_column_values)
|
|
530
|
+
|
|
531
|
+
# 计算新插入数据的范围
|
|
532
|
+
inserted_rows = dedup_result.get("inserted_rows", 0)
|
|
533
|
+
new_data_start_row = total_rows - inserted_rows + 1
|
|
534
|
+
new_data_end_row = total_rows
|
|
535
|
+
|
|
536
|
+
# 读取基准列的新插入数据部分
|
|
537
|
+
merge_column_letter = self.number_to_excel_column(merge_column_index + 1)
|
|
538
|
+
base_column_range = f"{merge_column_letter}{new_data_start_row}:{merge_column_letter}{new_data_end_row}"
|
|
539
|
+
base_data = self.client.read_multiple_ranges(spreadsheet_token, sheet_id, [base_column_range])
|
|
540
|
+
|
|
541
|
+
base_values = base_data.get("valueRanges", [{}])[0].get("values", [])
|
|
542
|
+
new_data_column_values = [row[0] if row else "" for row in base_values]
|
|
543
|
+
|
|
544
|
+
# 分析合并范围 - 需要将列值转换为行数据格式
|
|
545
|
+
data_rows_for_merge = [[value] for value in new_data_column_values]
|
|
546
|
+
merge_ranges = self.analyze_merge_ranges(data_rows_for_merge, 0)
|
|
547
|
+
log(f"[DEBUG] 新插入数据的合并范围: {merge_ranges}")
|
|
548
|
+
|
|
549
|
+
# 执行合并操作
|
|
550
|
+
for merge_group in merge_ranges:
|
|
551
|
+
start_row = merge_group['start_row']
|
|
552
|
+
end_row = merge_group['end_row']
|
|
553
|
+
|
|
554
|
+
if end_row > start_row:
|
|
555
|
+
abs_start_row = new_data_start_row + start_row
|
|
556
|
+
abs_end_row = new_data_start_row + end_row
|
|
557
|
+
|
|
558
|
+
for col_index in merge_column_indices:
|
|
559
|
+
col_letter = self.number_to_excel_column(col_index + 1)
|
|
560
|
+
range_str = f"{col_letter}{abs_start_row}:{col_letter}{abs_end_row}"
|
|
561
|
+
|
|
562
|
+
try:
|
|
563
|
+
self.client.merge_cells(spreadsheet_token, sheet_id, "MERGE_ALL", range_str)
|
|
564
|
+
merge_count += 1
|
|
565
|
+
except Exception:
|
|
566
|
+
pass
|
|
567
|
+
|
|
568
|
+
except Exception:
|
|
569
|
+
pass
|
|
570
|
+
|
|
571
|
+
# 处理图片插入
|
|
572
|
+
log(f"[DEBUG] 开始处理图片插入,image_column: {image_column}")
|
|
573
|
+
image_result = {"image_count": 0, "message": "未指定图片列"}
|
|
574
|
+
if image_column:
|
|
575
|
+
log(f"[DEBUG] 图片列已指定: {image_column},开始调用 _insert_images_for_column_with_merge_info")
|
|
576
|
+
try:
|
|
577
|
+
# 传递合并范围信息,避免重新分析
|
|
578
|
+
image_result = self._insert_images_for_column_with_merge_info(
|
|
579
|
+
spreadsheet_token, sheet_id, data_list, image_column,
|
|
580
|
+
dedup_result, header_row, reference_column, locals().get('merge_ranges', []), locals().get('new_data_start_row', 0)
|
|
581
|
+
)
|
|
582
|
+
log(f"[DEBUG] _insert_images_for_column_with_merge_info 执行完成,结果: {image_result}")
|
|
583
|
+
except Exception as e:
|
|
584
|
+
log(f"[DEBUG] 图片插入失败: {str(e)}")
|
|
585
|
+
import traceback
|
|
586
|
+
log(f"[DEBUG] 错误堆栈: {traceback.format_exc()}")
|
|
587
|
+
image_result = {"image_count": 0, "message": f"图片插入失败: {str(e)}"}
|
|
588
|
+
else:
|
|
589
|
+
log(f"[DEBUG] 未指定图片列,跳过图片插入")
|
|
590
|
+
|
|
591
|
+
log(f"[DEBUG] 最终图片插入结果: {image_result}")
|
|
592
|
+
|
|
593
|
+
return {
|
|
594
|
+
"status" : "success",
|
|
595
|
+
"message" : f"{dedup_result.get('message', '')},成功合并 {merge_count} 个单元格范围,{image_result.get('message', '')}",
|
|
596
|
+
"dedup_result": dedup_result,
|
|
597
|
+
"merge_result": {
|
|
598
|
+
"merge_count": merge_count,
|
|
599
|
+
"message" : f"成功合并 {merge_count} 个单元格范围"
|
|
600
|
+
},
|
|
601
|
+
"image_result": image_result
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
def _get_merge_ranges_for_image_insertion(self, data_list: list, headers: list):
|
|
605
|
+
"""
|
|
606
|
+
获取合并范围信息,用于图片插入时的判断
|
|
607
|
+
"""
|
|
608
|
+
try:
|
|
609
|
+
# 这里使用现有的合并逻辑,但主要是为了获取合并信息
|
|
610
|
+
# 假设我们使用setup_shein_return_spreadsheet中定义的合并列信息
|
|
611
|
+
merge_columns = [
|
|
612
|
+
'退货单号', '签收状态', '店铺信息', '店长', '退货类型', '退货原因',
|
|
613
|
+
'SKC图片', 'SKC信息', '包裹名', '包裹号', '退货计划单号',
|
|
614
|
+
'订单号', '发货单', '退货出库时间', '退回方式', '快递名称',
|
|
615
|
+
'运单号', '退货地址', '商家联系人', '商家手机号', '入库问题图片地址'
|
|
616
|
+
]
|
|
617
|
+
|
|
618
|
+
# 使用退货单号作为基准列来分析合并范围
|
|
619
|
+
merge_column = '退货单号'
|
|
620
|
+
|
|
621
|
+
try:
|
|
622
|
+
merge_column_index = headers.index(merge_column)
|
|
623
|
+
data_rows = data_list[1:] # 排除表头
|
|
624
|
+
return self.analyze_merge_ranges(data_rows, merge_column_index)
|
|
625
|
+
except ValueError:
|
|
626
|
+
log(f"[DEBUG] 未找到基准列 '{merge_column}',返回空合并范围")
|
|
627
|
+
return []
|
|
628
|
+
except Exception as e:
|
|
629
|
+
log(f"[DEBUG] 获取合并范围信息失败: {str(e)}")
|
|
630
|
+
return []
|
|
631
|
+
|
|
632
|
+
def _insert_images_for_empty_sheet(self, spreadsheet_token: str, sheet_id: str, data_list: list, image_column: str):
|
|
633
|
+
"""
|
|
634
|
+
为空表格插入的数据处理图片插入,考虑合并单元格情况
|
|
635
|
+
"""
|
|
636
|
+
log(f"[DEBUG] _insert_images_for_empty_sheet 开始执行")
|
|
637
|
+
|
|
638
|
+
if not data_list or len(data_list) < 2:
|
|
639
|
+
return {"image_count": 0, "message": "数据不足,无法插入图片"}
|
|
640
|
+
|
|
641
|
+
headers = data_list[0]
|
|
642
|
+
data_rows = data_list[1:]
|
|
643
|
+
|
|
644
|
+
# 找到图片列的索引
|
|
645
|
+
try:
|
|
646
|
+
image_column_index = headers.index(image_column)
|
|
647
|
+
image_column_letter = self.number_to_excel_column(image_column_index + 1)
|
|
648
|
+
log(f"[DEBUG] 图片列 '{image_column}' 索引: {image_column_index}, 列字母: {image_column_letter}")
|
|
649
|
+
except ValueError:
|
|
650
|
+
log(f"[DEBUG] 未找到图片列 '{image_column}'")
|
|
651
|
+
return {"image_count": 0, "message": f"未找到图片列 '{image_column}'"}
|
|
652
|
+
|
|
653
|
+
# 分析需要插入图片的行(只在合并组的第一行插入)
|
|
654
|
+
merge_ranges = self._get_merge_ranges_for_image_insertion(data_list, headers)
|
|
655
|
+
|
|
656
|
+
image_count = 0
|
|
657
|
+
processed_merge_groups = set() # 记录已处理的合并组
|
|
658
|
+
|
|
659
|
+
for row_index, row in enumerate(data_rows):
|
|
660
|
+
if len(row) > image_column_index and row[image_column_index]:
|
|
661
|
+
image_url = str(row[image_column_index]).strip()
|
|
662
|
+
if image_url and image_url.startswith('http'):
|
|
663
|
+
# 检查当前行是否在合并组中,且是否为合并组的第一行
|
|
664
|
+
should_insert = True
|
|
665
|
+
current_row_number = row_index + 2 # 表格行号(第1行是表头)
|
|
666
|
+
|
|
667
|
+
for merge_group in merge_ranges:
|
|
668
|
+
merge_start = merge_group['start_row'] + 2 # 转换为表格行号
|
|
669
|
+
merge_end = merge_group['end_row'] + 2
|
|
670
|
+
|
|
671
|
+
if merge_start <= current_row_number <= merge_end:
|
|
672
|
+
# 当前行在合并组中
|
|
673
|
+
if current_row_number == merge_start:
|
|
674
|
+
# 是合并组的第一行,可以插入
|
|
675
|
+
if merge_group['value'] not in processed_merge_groups:
|
|
676
|
+
processed_merge_groups.add(merge_group['value'])
|
|
677
|
+
else:
|
|
678
|
+
should_insert = False # 同一个值的合并组已处理过
|
|
679
|
+
else:
|
|
680
|
+
# 不是合并组的第一行,跳过
|
|
681
|
+
should_insert = False
|
|
682
|
+
break
|
|
683
|
+
|
|
684
|
+
if should_insert:
|
|
685
|
+
try:
|
|
686
|
+
# 使用正确的范围格式:A3:A3
|
|
687
|
+
cell_range = f"{sheet_id}!{image_column_letter}{current_row_number}:{image_column_letter}{current_row_number}"
|
|
688
|
+
log(f"[DEBUG] 插入图片到单元格: {cell_range}, URL: {image_url}")
|
|
689
|
+
self.client.write_image(spreadsheet_token, cell_range, image_url)
|
|
690
|
+
image_count += 1
|
|
691
|
+
except Exception as e:
|
|
692
|
+
log(f"[DEBUG] 插入图片失败: {cell_range}, 错误: {str(e)}")
|
|
693
|
+
else:
|
|
694
|
+
log(f"[DEBUG] 跳过图片插入(合并单元格非首行): 第{current_row_number}行")
|
|
695
|
+
|
|
696
|
+
return {
|
|
697
|
+
"image_count": image_count,
|
|
698
|
+
"message" : f"成功插入 {image_count} 张图片"
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
def _insert_images_for_column(self, spreadsheet_token: str, sheet_id: str, data_list: list,
|
|
702
|
+
image_column: str, dedup_result: dict, header_row: int, reference_column: str):
|
|
703
|
+
"""
|
|
704
|
+
为非空表格新插入的数据处理图片插入,考虑合并单元格情况
|
|
705
|
+
"""
|
|
706
|
+
log(f"[DEBUG] _insert_images_for_column 开始执行")
|
|
707
|
+
log(f"[DEBUG] 输入参数检查:")
|
|
708
|
+
log(f"[DEBUG] - spreadsheet_token: {spreadsheet_token}")
|
|
709
|
+
log(f"[DEBUG] - sheet_id: {sheet_id}")
|
|
710
|
+
log(f"[DEBUG] - image_column: {image_column}")
|
|
711
|
+
log(f"[DEBUG] - dedup_result: {dedup_result}")
|
|
712
|
+
log(f"[DEBUG] - header_row: {header_row}")
|
|
713
|
+
log(f"[DEBUG] - reference_column: {reference_column}")
|
|
714
|
+
log(f"[DEBUG] - data_list 长度: {len(data_list) if data_list else 0}")
|
|
715
|
+
|
|
716
|
+
# 如果没有插入新数据,直接返回
|
|
717
|
+
inserted_rows = dedup_result.get("inserted_rows", 0)
|
|
718
|
+
log(f"[DEBUG] 检查插入行数: {inserted_rows}")
|
|
719
|
+
if inserted_rows == 0:
|
|
720
|
+
log(f"[DEBUG] 无新数据插入,返回")
|
|
721
|
+
return {"image_count": 0, "message": "无新数据插入,无需处理图片"}
|
|
722
|
+
|
|
723
|
+
if not data_list or len(data_list) < 2:
|
|
724
|
+
log(f"[DEBUG] 数据不足,返回")
|
|
725
|
+
return {"image_count": 0, "message": "数据不足,无法插入图片"}
|
|
726
|
+
|
|
727
|
+
headers = data_list[0]
|
|
728
|
+
log(f"[DEBUG] 表头: {headers[:10]}...") # 显示前10列
|
|
729
|
+
|
|
730
|
+
# 找到图片列的索引
|
|
731
|
+
try:
|
|
732
|
+
image_column_index = headers.index(image_column)
|
|
733
|
+
image_column_letter = self.number_to_excel_column(image_column_index + 1)
|
|
734
|
+
log(f"[DEBUG] 图片列 '{image_column}' 索引: {image_column_index}, 列字母: {image_column_letter}")
|
|
735
|
+
except ValueError:
|
|
736
|
+
log(f"[DEBUG] 未找到图片列 '{image_column}',可用列: {headers}")
|
|
737
|
+
return {"image_count": 0, "message": f"未找到图片列 '{image_column}'"}
|
|
738
|
+
|
|
739
|
+
try:
|
|
740
|
+
# 使用参考列确定总行数和新插入数据的位置
|
|
741
|
+
log(f"[DEBUG] 查找参考列 '{reference_column}'")
|
|
742
|
+
ref_column_index = headers.index(reference_column)
|
|
743
|
+
ref_column_letter = self.number_to_excel_column(ref_column_index + 1)
|
|
744
|
+
log(f"[DEBUG] 参考列 '{reference_column}' 索引: {ref_column_index}, 列字母: {ref_column_letter}")
|
|
745
|
+
|
|
746
|
+
log(f"[DEBUG] 读取参考列数据...")
|
|
747
|
+
ref_data = self.client.read_multiple_ranges(spreadsheet_token, sheet_id, [f"{ref_column_letter}:{ref_column_letter}"])
|
|
748
|
+
log(f"[DEBUG] 参考列原始数据: {ref_data}")
|
|
749
|
+
all_column_values = ref_data.get("valueRanges", [{}])[0].get("values", [])
|
|
750
|
+
total_rows = len(all_column_values)
|
|
751
|
+
log(f"[DEBUG] 表格总行数: {total_rows}")
|
|
752
|
+
|
|
753
|
+
# 计算新插入数据的范围
|
|
754
|
+
new_data_start_row = total_rows - inserted_rows + 1
|
|
755
|
+
new_data_end_row = total_rows
|
|
756
|
+
|
|
757
|
+
log(f"[DEBUG] 计算得出新插入数据范围: 第{new_data_start_row}行到第{new_data_end_row}行")
|
|
758
|
+
log(f"[DEBUG] 新插入行数: {inserted_rows},总行数: {total_rows}")
|
|
759
|
+
|
|
760
|
+
# 读取新插入数据的所有信息(包括基准列)用于分析合并情况
|
|
761
|
+
merge_column = '退货单号' # 基准列
|
|
762
|
+
try:
|
|
763
|
+
log(f"[DEBUG] 查找基准列 '{merge_column}'")
|
|
764
|
+
merge_column_index = headers.index(merge_column)
|
|
765
|
+
merge_column_letter = self.number_to_excel_column(merge_column_index + 1)
|
|
766
|
+
log(f"[DEBUG] 基准列 '{merge_column}' 索引: {merge_column_index}, 列字母: {merge_column_letter}")
|
|
767
|
+
|
|
768
|
+
# 读取基准列的新插入数据
|
|
769
|
+
merge_column_range = f"{merge_column_letter}{new_data_start_row}:{merge_column_letter}{new_data_end_row}"
|
|
770
|
+
log(f"[DEBUG] 读取基准列范围: {merge_column_range}")
|
|
771
|
+
merge_data = self.client.read_multiple_ranges(spreadsheet_token, sheet_id, [merge_column_range])
|
|
772
|
+
log(f"[DEBUG] 基准列原始数据: {merge_data}")
|
|
773
|
+
merge_values = merge_data.get("valueRanges", [{}])[0].get("values", [])
|
|
774
|
+
log(f"[DEBUG] 基准列值: {merge_values}")
|
|
775
|
+
|
|
776
|
+
# 读取图片列的新插入数据部分
|
|
777
|
+
image_column_range = f"{image_column_letter}{new_data_start_row}:{image_column_letter}{new_data_end_row}"
|
|
778
|
+
log(f"[DEBUG] 读取图片列范围: {image_column_range}")
|
|
779
|
+
image_data = self.client.read_multiple_ranges(spreadsheet_token, sheet_id, [image_column_range])
|
|
780
|
+
log(f"[DEBUG] 图片列原始数据: {image_data}")
|
|
781
|
+
image_values = image_data.get("valueRanges", [{}])[0].get("values", [])
|
|
782
|
+
log(f"[DEBUG] 图片列值: {image_values}")
|
|
783
|
+
|
|
784
|
+
# 分析合并范围
|
|
785
|
+
log(f"[DEBUG] 开始分析合并范围...")
|
|
786
|
+
merge_input_data = [[row[0]] if row else [""] for row in merge_values]
|
|
787
|
+
log(f"[DEBUG] 合并分析输入数据: {merge_input_data}")
|
|
788
|
+
merge_ranges = self.analyze_merge_ranges(merge_input_data, 0)
|
|
789
|
+
log(f"[DEBUG] 分析得到的合并范围: {merge_ranges}")
|
|
790
|
+
|
|
791
|
+
image_count = 0
|
|
792
|
+
processed_merge_groups = set() # 记录已处理的合并组值
|
|
793
|
+
|
|
794
|
+
for row_index, image_row in enumerate(image_values):
|
|
795
|
+
if image_row and len(image_row) > 0:
|
|
796
|
+
image_url = str(image_row[0]).strip()
|
|
797
|
+
if image_url and image_url.startswith('http'):
|
|
798
|
+
# 检查当前行是否在合并组中,且是否为合并组的第一行
|
|
799
|
+
should_insert = True
|
|
800
|
+
current_absolute_row = new_data_start_row + row_index
|
|
801
|
+
|
|
802
|
+
# 获取当前行对应的基准列值
|
|
803
|
+
if row_index < len(merge_values) and merge_values[row_index]:
|
|
804
|
+
current_merge_value = str(merge_values[row_index][0]).strip()
|
|
805
|
+
else:
|
|
806
|
+
current_merge_value = ""
|
|
807
|
+
|
|
808
|
+
for merge_group in merge_ranges:
|
|
809
|
+
merge_start_in_new_data = merge_group['start_row']
|
|
810
|
+
merge_end_in_new_data = merge_group['end_row']
|
|
811
|
+
|
|
812
|
+
if merge_start_in_new_data <= row_index <= merge_end_in_new_data:
|
|
813
|
+
# 当前行在合并组中
|
|
814
|
+
merge_group_key = f"{merge_group['value']}_{merge_start_in_new_data}"
|
|
815
|
+
|
|
816
|
+
if row_index == merge_start_in_new_data:
|
|
817
|
+
# 是合并组的第一行,可以插入
|
|
818
|
+
if merge_group_key not in processed_merge_groups:
|
|
819
|
+
processed_merge_groups.add(merge_group_key)
|
|
820
|
+
else:
|
|
821
|
+
should_insert = False # 同一个合并组已处理过
|
|
822
|
+
else:
|
|
823
|
+
# 不是合并组的第一行,跳过
|
|
824
|
+
should_insert = False
|
|
825
|
+
break
|
|
826
|
+
|
|
827
|
+
if should_insert:
|
|
828
|
+
try:
|
|
829
|
+
# 使用正确的范围格式:A3:A3
|
|
830
|
+
cell_range = f"{sheet_id}!{image_column_letter}{current_absolute_row}:{image_column_letter}{current_absolute_row}"
|
|
831
|
+
log(f"[DEBUG] 插入图片到单元格: {cell_range}, URL: {image_url}")
|
|
832
|
+
self.client.write_image(spreadsheet_token, cell_range, image_url)
|
|
833
|
+
image_count += 1
|
|
834
|
+
except Exception as e:
|
|
835
|
+
log(f"[DEBUG] 插入图片失败: {cell_range}, 错误: {str(e)}")
|
|
836
|
+
else:
|
|
837
|
+
log(f"[DEBUG] 跳过图片插入(合并单元格非首行): 第{current_absolute_row}行")
|
|
838
|
+
|
|
839
|
+
return {
|
|
840
|
+
"image_count": image_count,
|
|
841
|
+
"message" : f"成功插入 {image_count} 张图片"
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
except ValueError:
|
|
845
|
+
log(f"[DEBUG] 未找到基准列 '{merge_column}',按普通方式处理图片插入")
|
|
846
|
+
# 回退到简单处理方式
|
|
847
|
+
image_column_range = f"{image_column_letter}{new_data_start_row}:{image_column_letter}{new_data_end_row}"
|
|
848
|
+
image_data = self.client.read_multiple_ranges(spreadsheet_token, sheet_id, [image_column_range])
|
|
849
|
+
image_values = image_data.get("valueRanges", [{}])[0].get("values", [])
|
|
850
|
+
|
|
851
|
+
image_count = 0
|
|
852
|
+
for row_index, row in enumerate(image_values):
|
|
853
|
+
if row and len(row) > 0:
|
|
854
|
+
image_url = str(row[0]).strip()
|
|
855
|
+
if image_url and image_url.startswith('http'):
|
|
856
|
+
try:
|
|
857
|
+
current_absolute_row = new_data_start_row + row_index
|
|
858
|
+
cell_range = f"{sheet_id}!{image_column_letter}{current_absolute_row}:{image_column_letter}{current_absolute_row}"
|
|
859
|
+
log(f"[DEBUG] 插入图片到单元格: {cell_range}, URL: {image_url}")
|
|
860
|
+
self.client.write_image(spreadsheet_token, cell_range, image_url)
|
|
861
|
+
image_count += 1
|
|
862
|
+
except Exception as e:
|
|
863
|
+
log(f"[DEBUG] 插入图片失败: {cell_range}, 错误: {str(e)}")
|
|
864
|
+
|
|
865
|
+
return {
|
|
866
|
+
"image_count": image_count,
|
|
867
|
+
"message" : f"成功插入 {image_count} 张图片"
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
except Exception as e:
|
|
871
|
+
log(f"[DEBUG] 处理图片插入时出错: {str(e)}")
|
|
872
|
+
return {"image_count": 0, "message": f"处理图片插入时出错: {str(e)}"}
|
|
873
|
+
|
|
874
|
+
def _insert_images_for_column_with_merge_info(self, spreadsheet_token: str, sheet_id: str, data_list: list,
|
|
875
|
+
image_column: str, dedup_result: dict, header_row: int,
|
|
876
|
+
reference_column: str, merge_ranges: list, new_data_start_row: int):
|
|
877
|
+
"""
|
|
878
|
+
使用已有的合并范围信息为新插入的数据处理图片插入
|
|
879
|
+
"""
|
|
880
|
+
log(f"[DEBUG] _insert_images_for_column_with_merge_info 开始执行")
|
|
881
|
+
log(f"[DEBUG] 接收到的合并范围: {merge_ranges}")
|
|
882
|
+
log(f"[DEBUG] 新数据起始行: {new_data_start_row}")
|
|
883
|
+
|
|
884
|
+
# 如果没有插入新数据,直接返回
|
|
885
|
+
inserted_rows = dedup_result.get("inserted_rows", 0)
|
|
886
|
+
if inserted_rows == 0:
|
|
887
|
+
log(f"[DEBUG] 无新数据插入,返回")
|
|
888
|
+
return {"image_count": 0, "message": "无新数据插入,无需处理图片"}
|
|
889
|
+
|
|
890
|
+
if not data_list or len(data_list) < 2:
|
|
891
|
+
log(f"[DEBUG] 数据不足,返回")
|
|
892
|
+
return {"image_count": 0, "message": "数据不足,无法插入图片"}
|
|
893
|
+
|
|
894
|
+
headers = data_list[0]
|
|
895
|
+
data_rows = data_list[1:]
|
|
896
|
+
|
|
897
|
+
# 找到图片列的索引
|
|
898
|
+
try:
|
|
899
|
+
image_column_index = headers.index(image_column)
|
|
900
|
+
image_column_letter = self.number_to_excel_column(image_column_index + 1)
|
|
901
|
+
log(f"[DEBUG] 图片列 '{image_column}' 索引: {image_column_index}, 列字母: {image_column_letter}")
|
|
902
|
+
except ValueError:
|
|
903
|
+
log(f"[DEBUG] 未找到图片列 '{image_column}'")
|
|
904
|
+
return {"image_count": 0, "message": f"未找到图片列 '{image_column}'"}
|
|
905
|
+
|
|
906
|
+
# 获取过滤后的数据(新插入的数据)
|
|
907
|
+
# 不能重新执行去重逻辑,因为数据已经插入到表格中了
|
|
908
|
+
# 应该直接从表格中读取新插入的数据
|
|
909
|
+
log(f"[DEBUG] 开始处理图片插入,新插入行数: {inserted_rows}")
|
|
910
|
+
|
|
911
|
+
# 直接从表格中读取新插入的图片列数据
|
|
912
|
+
try:
|
|
913
|
+
# 使用参考列确定总行数和新插入数据的位置
|
|
914
|
+
ref_column_index = headers.index(reference_column)
|
|
915
|
+
ref_column_letter = self.number_to_excel_column(ref_column_index + 1)
|
|
916
|
+
|
|
917
|
+
ref_data = self.client.read_multiple_ranges(spreadsheet_token, sheet_id, [f"{ref_column_letter}:{ref_column_letter}"])
|
|
918
|
+
all_column_values = ref_data.get("valueRanges", [{}])[0].get("values", [])
|
|
919
|
+
total_rows = len(all_column_values)
|
|
920
|
+
|
|
921
|
+
# 计算新插入数据的范围
|
|
922
|
+
actual_new_data_start_row = total_rows - inserted_rows + 1
|
|
923
|
+
actual_new_data_end_row = total_rows
|
|
924
|
+
|
|
925
|
+
log(f"[DEBUG] 从表格读取新插入数据范围: 第{actual_new_data_start_row}行到第{actual_new_data_end_row}行")
|
|
926
|
+
|
|
927
|
+
# 读取图片列的新插入数据部分
|
|
928
|
+
image_column_range = f"{image_column_letter}{actual_new_data_start_row}:{image_column_letter}{actual_new_data_end_row}"
|
|
929
|
+
image_data = self.client.read_multiple_ranges(spreadsheet_token, sheet_id, [image_column_range])
|
|
930
|
+
image_values = image_data.get("valueRanges", [{}])[0].get("values", [])
|
|
931
|
+
|
|
932
|
+
log(f"[DEBUG] 从表格读取的图片列数据: {image_values}")
|
|
933
|
+
|
|
934
|
+
# 构建图片URL映射,解析JSON格式的URL数据
|
|
935
|
+
image_urls_for_new_rows = []
|
|
936
|
+
for row in image_values:
|
|
937
|
+
if row and len(row) > 0:
|
|
938
|
+
raw_url_data = str(row[0]).strip()
|
|
939
|
+
# 解析飞书表格中的URL数据格式
|
|
940
|
+
parsed_url = self._extract_url_from_feishu_data(raw_url_data)
|
|
941
|
+
image_urls_for_new_rows.append(parsed_url)
|
|
942
|
+
log(f"[DEBUG] 解析URL: 原始='{raw_url_data[:100]}...' -> 解析后='{parsed_url}'")
|
|
943
|
+
else:
|
|
944
|
+
image_urls_for_new_rows.append("")
|
|
945
|
+
|
|
946
|
+
log(f"[DEBUG] 实际插入行的图片URL: {image_urls_for_new_rows}")
|
|
947
|
+
|
|
948
|
+
# 使用传入的合并范围信息(它们已经是正确的)
|
|
949
|
+
log(f"[DEBUG] 使用传入的合并范围: {merge_ranges}")
|
|
950
|
+
|
|
951
|
+
except Exception as e:
|
|
952
|
+
log(f"[DEBUG] 从表格读取图片数据失败: {str(e)}")
|
|
953
|
+
return {"image_count": 0, "message": f"读取图片数据失败: {str(e)}"}
|
|
954
|
+
|
|
955
|
+
# 使用合并范围信息决定在哪些行插入图片
|
|
956
|
+
image_count = 0
|
|
957
|
+
processed_merge_groups = set()
|
|
958
|
+
|
|
959
|
+
for row_index in range(len(image_urls_for_new_rows)):
|
|
960
|
+
image_url = image_urls_for_new_rows[row_index]
|
|
961
|
+
if not image_url or not image_url.startswith('http'):
|
|
962
|
+
log(f"[DEBUG] 第{row_index}行无有效图片URL: '{image_url}'")
|
|
963
|
+
continue
|
|
964
|
+
|
|
965
|
+
should_insert = True
|
|
966
|
+
current_absolute_row = actual_new_data_start_row + row_index # 使用实际的起始行号
|
|
967
|
+
|
|
968
|
+
log(f"[DEBUG] 处理第{row_index}行,绝对行号: {current_absolute_row}, URL: {image_url}")
|
|
969
|
+
|
|
970
|
+
# 检查当前行是否在合并组中
|
|
971
|
+
for merge_group in merge_ranges:
|
|
972
|
+
merge_start_in_new_data = merge_group['start_row']
|
|
973
|
+
merge_end_in_new_data = merge_group['end_row']
|
|
974
|
+
|
|
975
|
+
log(f"[DEBUG] 检查合并组: {merge_group}, 当前行索引: {row_index}")
|
|
976
|
+
|
|
977
|
+
if merge_start_in_new_data <= row_index <= merge_end_in_new_data:
|
|
978
|
+
# 当前行在合并组中
|
|
979
|
+
merge_group_key = f"{merge_group['value']}_{merge_start_in_new_data}"
|
|
980
|
+
|
|
981
|
+
if row_index == merge_start_in_new_data:
|
|
982
|
+
# 是合并组的第一行,可以插入
|
|
983
|
+
if merge_group_key not in processed_merge_groups:
|
|
984
|
+
processed_merge_groups.add(merge_group_key)
|
|
985
|
+
log(f"[DEBUG] 合并组第一行,允许插入: 行{current_absolute_row}, 组{merge_group_key}")
|
|
986
|
+
else:
|
|
987
|
+
should_insert = False
|
|
988
|
+
log(f"[DEBUG] 合并组已处理过,跳过: 行{current_absolute_row}, 组{merge_group_key}")
|
|
989
|
+
else:
|
|
990
|
+
# 不是合并组的第一行,跳过
|
|
991
|
+
should_insert = False
|
|
992
|
+
log(f"[DEBUG] 合并组非首行,跳过: 行{current_absolute_row}")
|
|
993
|
+
break
|
|
994
|
+
else:
|
|
995
|
+
# 没有在任何合并组中,可以插入
|
|
996
|
+
log(f"[DEBUG] 独立行,允许插入: 行{current_absolute_row}")
|
|
997
|
+
|
|
998
|
+
if should_insert:
|
|
999
|
+
try:
|
|
1000
|
+
# 使用正确的范围格式:A3:A3
|
|
1001
|
+
cell_range = f"{sheet_id}!{image_column_letter}{current_absolute_row}:{image_column_letter}{current_absolute_row}"
|
|
1002
|
+
log(f"[DEBUG] 插入图片到单元格: {cell_range}, URL: {image_url}")
|
|
1003
|
+
self.client.write_image(spreadsheet_token, cell_range, image_url)
|
|
1004
|
+
image_count += 1
|
|
1005
|
+
except Exception as e:
|
|
1006
|
+
log(f"[DEBUG] 插入图片失败: {cell_range}, 错误: {str(e)}")
|
|
1007
|
+
else:
|
|
1008
|
+
log(f"[DEBUG] 跳过图片插入: 行{current_absolute_row}")
|
|
1009
|
+
|
|
1010
|
+
log(f"[DEBUG] 图片插入完成,成功插入 {image_count} 张图片")
|
|
1011
|
+
return {
|
|
1012
|
+
"image_count": image_count,
|
|
1013
|
+
"message" : f"成功插入 {image_count} 张图片"
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
def _extract_url_from_feishu_data(self, raw_data: str):
|
|
1017
|
+
"""
|
|
1018
|
+
从飞书表格的URL数据格式中提取真正的URL
|
|
1019
|
+
"""
|
|
1020
|
+
try:
|
|
1021
|
+
# 处理None和空字符串
|
|
1022
|
+
if not raw_data or raw_data == 'None':
|
|
1023
|
+
return ""
|
|
1024
|
+
|
|
1025
|
+
# 尝试解析JSON格式的数据
|
|
1026
|
+
import json
|
|
1027
|
+
if raw_data.startswith('[') and raw_data.endswith(']'):
|
|
1028
|
+
parsed_data = json.loads(raw_data)
|
|
1029
|
+
if isinstance(parsed_data, list) and len(parsed_data) > 0:
|
|
1030
|
+
first_item = parsed_data[0]
|
|
1031
|
+
if isinstance(first_item, dict):
|
|
1032
|
+
# 优先使用link字段,其次使用text字段
|
|
1033
|
+
url = first_item.get('link') or first_item.get('text', '')
|
|
1034
|
+
if url and url.startswith('http'):
|
|
1035
|
+
return url
|
|
1036
|
+
|
|
1037
|
+
# 如果不是JSON格式,检查是否直接是URL
|
|
1038
|
+
if raw_data.startswith('http'):
|
|
1039
|
+
return raw_data
|
|
1040
|
+
|
|
1041
|
+
return ""
|
|
1042
|
+
except Exception as e:
|
|
1043
|
+
log(f"[DEBUG] 解析URL数据失败: {str(e)}, 原始数据: {raw_data}")
|
|
1044
|
+
# 如果解析失败,尝试直接检查是否包含URL
|
|
1045
|
+
if 'http' in raw_data:
|
|
1046
|
+
import re
|
|
1047
|
+
# 使用正则表达式提取URL
|
|
1048
|
+
url_pattern = r'https?://[^\s\'"]*'
|
|
1049
|
+
matches = re.findall(url_pattern, raw_data)
|
|
1050
|
+
if matches:
|
|
1051
|
+
return matches[0]
|
|
1052
|
+
return ""
|
|
1053
|
+
|
|
1054
|
+
def setup_shein_return_spreadsheet(self, monthly_data: list, sheet_title: str):
|
|
1055
|
+
"""
|
|
1056
|
+
希音退货列表业务逻辑 - 按月份分组存储数据
|
|
1057
|
+
|
|
1058
|
+
Args:
|
|
1059
|
+
monthly_data: 指定月份的数据列表,包含表头和数据行
|
|
1060
|
+
sheet_title: 工作表标题(如:202507, 202508)
|
|
1061
|
+
"""
|
|
1062
|
+
log(f"[DEBUG] setup_shein_return_spreadsheet 开始执行")
|
|
1063
|
+
log(f"[DEBUG] sheet_title: {sheet_title}")
|
|
1064
|
+
log(f"[DEBUG] monthly_data 长度: {len(monthly_data) if monthly_data else 0}")
|
|
1065
|
+
|
|
1066
|
+
# 获取根目录
|
|
1067
|
+
root_folder_token = self.client.get_root_folder_meta().get('token')
|
|
1068
|
+
all_files = self.get_all_folder_files(folder_token=root_folder_token)
|
|
1069
|
+
|
|
1070
|
+
# 检查是否存在"希音退货列表"表格
|
|
1071
|
+
shein_return_spreadsheet_token = None
|
|
1072
|
+
shein_return_spreadsheet_url = None
|
|
1073
|
+
for file in all_files:
|
|
1074
|
+
if file.get("name") == "希音退货列表" and file.get("type") == "sheet":
|
|
1075
|
+
shein_return_spreadsheet_token = file.get("token")
|
|
1076
|
+
shein_return_spreadsheet_url = file.get("url")
|
|
1077
|
+
break
|
|
1078
|
+
|
|
1079
|
+
# 如果不存在则创建
|
|
1080
|
+
if not shein_return_spreadsheet_token:
|
|
1081
|
+
create_result = self.client.create_spreadsheet(title="希音退货列表", folder_token=root_folder_token)
|
|
1082
|
+
shein_return_spreadsheet_token = create_result.get('spreadsheet', {}).get('spreadsheet_token')
|
|
1083
|
+
shein_return_spreadsheet_url = create_result.get('spreadsheet', {}).get('url')
|
|
1084
|
+
|
|
1085
|
+
# 获取表格中的所有工作表
|
|
1086
|
+
sheets_result = self.client.query_sheets(shein_return_spreadsheet_token)
|
|
1087
|
+
sheets = sheets_result.get('sheets', [])
|
|
1088
|
+
|
|
1089
|
+
# 检查是否存在指定的工作表
|
|
1090
|
+
target_sheet_title = sheet_title
|
|
1091
|
+
target_sheet_id = None
|
|
1092
|
+
|
|
1093
|
+
for sheet in sheets:
|
|
1094
|
+
if sheet.get('title') == target_sheet_title:
|
|
1095
|
+
target_sheet_id = sheet.get('sheet_id')
|
|
1096
|
+
break
|
|
1097
|
+
|
|
1098
|
+
# 如果不存在则创建
|
|
1099
|
+
if not target_sheet_id:
|
|
1100
|
+
add_sheet_result = self.client.add_sheet(
|
|
1101
|
+
spreadsheet_token=shein_return_spreadsheet_token,
|
|
1102
|
+
title=target_sheet_title,
|
|
1103
|
+
index=0
|
|
1104
|
+
)
|
|
1105
|
+
replies = add_sheet_result.get('replies', [])
|
|
1106
|
+
if replies:
|
|
1107
|
+
target_sheet_id = replies[0].get('addSheet', {}).get('properties', {}).get('sheetId')
|
|
1108
|
+
|
|
1109
|
+
# 处理月份数据并写入表格
|
|
1110
|
+
try:
|
|
1111
|
+
if not monthly_data or len(monthly_data) < 2:
|
|
1112
|
+
return {
|
|
1113
|
+
"error" : "数据不足",
|
|
1114
|
+
"message": f"月份 {sheet_title} 的数据不足,无法处理"
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
merge_columns = [
|
|
1118
|
+
'退货单号', '签收状态', '店铺信息', '店长', '退货类型', '退货原因',
|
|
1119
|
+
'SKC图片', 'SKC信息', '包裹名', '包裹号', '退货计划单号',
|
|
1120
|
+
'订单号', '发货单', '退货出库时间', '退回方式', '快递名称',
|
|
1121
|
+
'运单号', '退货地址', '商家联系人', '商家手机号', '入库问题图片地址'
|
|
1122
|
+
]
|
|
1123
|
+
|
|
1124
|
+
merge_result = self.write_data_and_merge_by_column_with_deduplication(
|
|
1125
|
+
spreadsheet_token=shein_return_spreadsheet_token,
|
|
1126
|
+
sheet_id=target_sheet_id,
|
|
1127
|
+
data_list=monthly_data,
|
|
1128
|
+
dedup_column='退货单号',
|
|
1129
|
+
merge_column='退货单号',
|
|
1130
|
+
merge_columns=merge_columns,
|
|
1131
|
+
reference_column='平台SKU',
|
|
1132
|
+
image_column='SKC图片'
|
|
1133
|
+
)
|
|
1134
|
+
|
|
1135
|
+
# 设置表格权限
|
|
1136
|
+
self.client.batch_create_permission_members(
|
|
1137
|
+
token=shein_return_spreadsheet_token,
|
|
1138
|
+
type="sheet",
|
|
1139
|
+
members=[
|
|
1140
|
+
{
|
|
1141
|
+
"member_type": "openchat",
|
|
1142
|
+
"member_id" : "oc_c27bcdef75057de2ab720189d34da477",
|
|
1143
|
+
"perm" : "edit",
|
|
1144
|
+
},
|
|
1145
|
+
{
|
|
1146
|
+
"member_type": "openchat",
|
|
1147
|
+
"member_id" : "oc_679fabef0a4ac753a4aad58a3777d42f",
|
|
1148
|
+
"perm" : "edit",
|
|
1149
|
+
},
|
|
1150
|
+
{
|
|
1151
|
+
"member_type": "openchat",
|
|
1152
|
+
"member_id" : "oc_05f89743bce14feb5b2cdc76401b3066",
|
|
1153
|
+
"perm" : "edit",
|
|
1154
|
+
},
|
|
1155
|
+
]
|
|
1156
|
+
)
|
|
1157
|
+
|
|
1158
|
+
# 设置标题
|
|
1159
|
+
self.set_header_row_style(
|
|
1160
|
+
spreadsheet_token=shein_return_spreadsheet_token,
|
|
1161
|
+
sheet_id=target_sheet_id,
|
|
1162
|
+
header_row=1,
|
|
1163
|
+
column_count=27
|
|
1164
|
+
)
|
|
1165
|
+
|
|
1166
|
+
# 设置对齐
|
|
1167
|
+
self.set_column_to_align(
|
|
1168
|
+
spreadsheet_token=shein_return_spreadsheet_token,
|
|
1169
|
+
sheet_id=target_sheet_id,
|
|
1170
|
+
column_names_to_left=['D', 'G', 'I', 'J', 'K', 'M', 'N', 'X', 'Y', 'AA'],
|
|
1171
|
+
column_names_to_center=['B', 'C', 'E', 'F', 'L', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'Z'],
|
|
1172
|
+
column_names_to_right=['O'],
|
|
1173
|
+
)
|
|
1174
|
+
|
|
1175
|
+
# 冻结首行
|
|
1176
|
+
self.client.update_sheet_properties(spreadsheet_token=shein_return_spreadsheet_token, properties={
|
|
1177
|
+
"sheetId" : target_sheet_id,
|
|
1178
|
+
"frozenRowCount": 1
|
|
1179
|
+
})
|
|
1180
|
+
|
|
1181
|
+
log(f"[DEBUG] 月份 {sheet_title} 数据处理完成")
|
|
1182
|
+
|
|
1183
|
+
return {
|
|
1184
|
+
'spreadsheet_token': shein_return_spreadsheet_token,
|
|
1185
|
+
'sheet_id' : target_sheet_id,
|
|
1186
|
+
'sheet_title' : target_sheet_title,
|
|
1187
|
+
'spreadsheet_url' : shein_return_spreadsheet_url,
|
|
1188
|
+
'merge_result' : merge_result
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1191
|
+
except Exception as e:
|
|
1192
|
+
log(f"[DEBUG] 处理月份 {sheet_title} 数据失败: {str(e)}")
|
|
1193
|
+
return {"error": f"处理失败: {str(e)}"}
|
|
1194
|
+
|
|
1195
|
+
def process_shein_return_data_by_month(self, json_file_path: str = 'data4.json'):
|
|
1196
|
+
"""
|
|
1197
|
+
按月份分组处理希音退货数据
|
|
1198
|
+
|
|
1199
|
+
Args:
|
|
1200
|
+
json_file_path: JSON数据文件路径
|
|
1201
|
+
|
|
1202
|
+
Returns:
|
|
1203
|
+
dict: 处理结果,包含各月份的处理状态
|
|
1204
|
+
"""
|
|
1205
|
+
log(f"[DEBUG] process_shein_return_data_by_month 开始执行")
|
|
1206
|
+
log(f"[DEBUG] 读取文件: {json_file_path}")
|
|
1207
|
+
|
|
1208
|
+
try:
|
|
1209
|
+
# 读取JSON数据
|
|
1210
|
+
with open(json_file_path, 'r', encoding='utf-8') as f:
|
|
1211
|
+
json_data = json.load(f)
|
|
1212
|
+
|
|
1213
|
+
if not json_data or len(json_data) < 2:
|
|
1214
|
+
return {"error": "数据文件为空或数据不足"}
|
|
1215
|
+
|
|
1216
|
+
headers = json_data[0]
|
|
1217
|
+
data_rows = json_data[1:]
|
|
1218
|
+
|
|
1219
|
+
# 找到退货出库时间列的索引
|
|
1220
|
+
try:
|
|
1221
|
+
time_column_index = headers.index('退货出库时间')
|
|
1222
|
+
log(f"[DEBUG] 找到退货出库时间列索引: {time_column_index}")
|
|
1223
|
+
except ValueError:
|
|
1224
|
+
return {"error": "未找到'退货出库时间'列"}
|
|
1225
|
+
|
|
1226
|
+
# 按月份分组数据
|
|
1227
|
+
monthly_data = {}
|
|
1228
|
+
|
|
1229
|
+
for row in data_rows:
|
|
1230
|
+
if len(row) > time_column_index and row[time_column_index]:
|
|
1231
|
+
# 解析日期时间,格式:2025-08-14 10:50:37
|
|
1232
|
+
date_str = str(row[time_column_index]).strip()
|
|
1233
|
+
try:
|
|
1234
|
+
# 提取年月部分
|
|
1235
|
+
date_part = date_str.split(' ')[0] # 获取日期部分
|
|
1236
|
+
year_month = date_part.replace('-', '')[:6] # 转换为YYYYMM格式
|
|
1237
|
+
|
|
1238
|
+
if year_month not in monthly_data:
|
|
1239
|
+
monthly_data[year_month] = []
|
|
1240
|
+
|
|
1241
|
+
monthly_data[year_month].append(row)
|
|
1242
|
+
|
|
1243
|
+
except Exception as e:
|
|
1244
|
+
log(f"[DEBUG] 解析日期失败: {date_str}, 错误: {str(e)}")
|
|
1245
|
+
continue
|
|
1246
|
+
|
|
1247
|
+
log(f"[DEBUG] 数据按月份分组完成,共 {len(monthly_data)} 个月份")
|
|
1248
|
+
for month, rows in monthly_data.items():
|
|
1249
|
+
log(f"[DEBUG] 月份 {month}: {len(rows)} 行数据")
|
|
1250
|
+
|
|
1251
|
+
# 为每个月份处理数据
|
|
1252
|
+
results = {}
|
|
1253
|
+
|
|
1254
|
+
for month, rows in monthly_data.items():
|
|
1255
|
+
log(f"[DEBUG] 开始处理月份: {month}")
|
|
1256
|
+
|
|
1257
|
+
# 构建该月份的完整数据(包含表头)
|
|
1258
|
+
month_data = [headers] + rows
|
|
1259
|
+
|
|
1260
|
+
log(month, json.dumps(month_data, ensure_ascii=False))
|
|
1261
|
+
# 调用原有的函数处理该月份数据
|
|
1262
|
+
result = self.setup_shein_return_spreadsheet(month_data, month)
|
|
1263
|
+
results[month] = result
|
|
1264
|
+
|
|
1265
|
+
log(f"[DEBUG] 月份 {month} 处理完成")
|
|
1266
|
+
|
|
1267
|
+
result = {
|
|
1268
|
+
"status" : "success",
|
|
1269
|
+
"message" : f"成功处理 {len(results)} 个月份的数据",
|
|
1270
|
+
"results" : results,
|
|
1271
|
+
"monthly_summary": {month: len(rows) for month, rows in monthly_data.items()}
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
if result.get('status') == 'success':
|
|
1275
|
+
print(f"✅ {result['message']}")
|
|
1276
|
+
print("\n📊 月份数据统计:")
|
|
1277
|
+
for month, count in result['monthly_summary'].items():
|
|
1278
|
+
print(f" - {month[:4]}年{month[4:]}月: {count} 行数据")
|
|
1279
|
+
|
|
1280
|
+
print("\n📋 处理结果详情:")
|
|
1281
|
+
for month, month_result in result['results'].items():
|
|
1282
|
+
if 'error' in month_result:
|
|
1283
|
+
print(f" ❌ {month}: {month_result['error']}")
|
|
1284
|
+
else:
|
|
1285
|
+
merge_info = month_result.get('merge_result', {})
|
|
1286
|
+
print(f" ✅ {month}: {merge_info.get('message', '处理完成')}")
|
|
1287
|
+
else:
|
|
1288
|
+
print(f"❌ 处理失败: {result.get('error')}")
|
|
1289
|
+
|
|
1290
|
+
print("\n" + "=" * 50 + "\n")
|
|
1291
|
+
|
|
1292
|
+
except Exception as e:
|
|
1293
|
+
log(f"[DEBUG] process_shein_return_data_by_month 执行失败: {str(e)}")
|
|
1294
|
+
return {"error": f"处理失败: {str(e)}"}
|
|
1295
|
+
|
|
1296
|
+
def set_header_row_style(self, spreadsheet_token: str, sheet_id: str, header_row: int = 1,
|
|
1297
|
+
end_column: str = None, column_count: int = None):
|
|
1298
|
+
"""
|
|
1299
|
+
设置表格标题行样式
|
|
1300
|
+
类似xlwings的样式设置:背景色、字体颜色、加粗、居中对齐
|
|
1301
|
+
|
|
1302
|
+
Args:
|
|
1303
|
+
spreadsheet_token: 表格token
|
|
1304
|
+
sheet_id: 工作表ID
|
|
1305
|
+
header_row: 标题行号,默认第1行
|
|
1306
|
+
end_column: 结束列名(如'Z'),与column_count二选一
|
|
1307
|
+
column_count: 列数,与end_column二选一
|
|
1308
|
+
"""
|
|
1309
|
+
try:
|
|
1310
|
+
# 确定范围
|
|
1311
|
+
if end_column:
|
|
1312
|
+
range_str = f"A{header_row}:{end_column}{header_row}"
|
|
1313
|
+
elif column_count:
|
|
1314
|
+
end_col = self.number_to_excel_column(column_count)
|
|
1315
|
+
range_str = f"A{header_row}:{end_col}{header_row}"
|
|
1316
|
+
else:
|
|
1317
|
+
# 默认设置前26列(A-Z)
|
|
1318
|
+
range_str = f"A{header_row}:Z{header_row}"
|
|
1319
|
+
|
|
1320
|
+
# 定义样式 - 根据飞书官方文档格式
|
|
1321
|
+
style_data = {
|
|
1322
|
+
"ranges": [f"{sheet_id}!{range_str}"], # 使用ranges数组
|
|
1323
|
+
"style" : {
|
|
1324
|
+
# 背景色:RGB(68, 114, 196) 转换为十六进制
|
|
1325
|
+
"backColor": "#4472C4",
|
|
1326
|
+
# 字体颜色:白色
|
|
1327
|
+
"foreColor": "#FFFFFF",
|
|
1328
|
+
# 字体设置
|
|
1329
|
+
"font" : {
|
|
1330
|
+
"bold" : True,
|
|
1331
|
+
"fontSize": "11pt/1.5" # 字体大小格式
|
|
1332
|
+
},
|
|
1333
|
+
"hAlign" : 1,
|
|
1334
|
+
"vAlign" : 1
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1337
|
+
|
|
1338
|
+
# 调用批量设置样式API - 修正参数传递
|
|
1339
|
+
result = self.client.batch_set_cell_style(spreadsheet_token, [style_data])
|
|
1340
|
+
log(f"[DEBUG] 成功设置标题行样式: {range_str}")
|
|
1341
|
+
return {
|
|
1342
|
+
"status" : "success",
|
|
1343
|
+
"message": f"成功设置标题行样式: {range_str}",
|
|
1344
|
+
"result" : result
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
except Exception as e:
|
|
1348
|
+
log(f"[DEBUG] 设置标题行样式失败: {str(e)}")
|
|
1349
|
+
return {
|
|
1350
|
+
"status" : "error",
|
|
1351
|
+
"message": f"设置标题行样式失败: {str(e)}"
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1354
|
+
def set_column_style(self, spreadsheet_token: str, sheet_id: str, column_name: str,
|
|
1355
|
+
start_row: int = 1, end_row: int = None, style_config: dict = None):
|
|
1356
|
+
"""
|
|
1357
|
+
设置指定列的样式(支持整列或指定行范围)
|
|
1358
|
+
|
|
1359
|
+
Args:
|
|
1360
|
+
spreadsheet_token: 表格token
|
|
1361
|
+
sheet_id: 工作表ID
|
|
1362
|
+
column_name: 列名(如'A', 'B'等)
|
|
1363
|
+
start_row: 开始行号,默认第1行
|
|
1364
|
+
end_row: 结束行号,None表示整列
|
|
1365
|
+
style_config: 自定义样式配置
|
|
1366
|
+
"""
|
|
1367
|
+
try:
|
|
1368
|
+
# 构建范围
|
|
1369
|
+
if end_row:
|
|
1370
|
+
range_str = f"{column_name}{start_row}:{column_name}{end_row}"
|
|
1371
|
+
else:
|
|
1372
|
+
range_str = f"{column_name}:{column_name}"
|
|
1373
|
+
|
|
1374
|
+
# 默认样式配置(水平居中和垂直居中)
|
|
1375
|
+
default_style = {
|
|
1376
|
+
"hAlign": 1, # 水平居中
|
|
1377
|
+
"vAlign": 1 # 垂直居中
|
|
1378
|
+
}
|
|
1379
|
+
|
|
1380
|
+
# 合并自定义样式
|
|
1381
|
+
final_style = style_config if style_config else default_style
|
|
1382
|
+
|
|
1383
|
+
style_data = {
|
|
1384
|
+
"ranges": [f"{sheet_id}!{range_str}"], # 使用ranges数组
|
|
1385
|
+
"style" : final_style
|
|
1386
|
+
}
|
|
1387
|
+
|
|
1388
|
+
result = self.client.batch_set_cell_style(spreadsheet_token, [style_data])
|
|
1389
|
+
log(f"[DEBUG] 成功设置列样式: {range_str}")
|
|
1390
|
+
return {
|
|
1391
|
+
"status" : "success",
|
|
1392
|
+
"message": f"成功设置列样式: {range_str}",
|
|
1393
|
+
"result" : result
|
|
1394
|
+
}
|
|
1395
|
+
|
|
1396
|
+
except Exception as e:
|
|
1397
|
+
log(f"[DEBUG] 设置列样式失败: {str(e)}")
|
|
1398
|
+
return {
|
|
1399
|
+
"status" : "error",
|
|
1400
|
+
"message": f"设置列样式失败: {str(e)}"
|
|
1401
|
+
}
|
|
1402
|
+
|
|
1403
|
+
def set_column_to_align(self, spreadsheet_token: str, sheet_id: str, column_names_to_left: list, column_names_to_center: list, column_names_to_right: list):
|
|
1404
|
+
try:
|
|
1405
|
+
style_data_left = {
|
|
1406
|
+
"ranges": [f"{sheet_id}!{column_name}:{column_name}" for column_name in column_names_to_left],
|
|
1407
|
+
"style" : {
|
|
1408
|
+
# 字体设置
|
|
1409
|
+
"font" : {
|
|
1410
|
+
"bold" : False,
|
|
1411
|
+
"fontSize": "10pt/1.5"
|
|
1412
|
+
},
|
|
1413
|
+
"hAlign": 0,
|
|
1414
|
+
"vAlign": 1
|
|
1415
|
+
}
|
|
1416
|
+
}
|
|
1417
|
+
style_data_center = {
|
|
1418
|
+
"ranges": [f"{sheet_id}!{column_name}:{column_name}" for column_name in column_names_to_center],
|
|
1419
|
+
"style" : {
|
|
1420
|
+
# 字体设置
|
|
1421
|
+
"font" : {
|
|
1422
|
+
"bold" : False,
|
|
1423
|
+
"fontSize": "10pt/1.5"
|
|
1424
|
+
},
|
|
1425
|
+
"hAlign": 1,
|
|
1426
|
+
"vAlign": 1
|
|
1427
|
+
}
|
|
1428
|
+
}
|
|
1429
|
+
style_data_right = {
|
|
1430
|
+
"ranges": [f"{sheet_id}!{column_name}:{column_name}" for column_name in column_names_to_right],
|
|
1431
|
+
"style" : {
|
|
1432
|
+
# 字体设置
|
|
1433
|
+
"font" : {
|
|
1434
|
+
"bold" : False,
|
|
1435
|
+
"fontSize": "10pt/1.5"
|
|
1436
|
+
},
|
|
1437
|
+
"hAlign": 2,
|
|
1438
|
+
"vAlign": 1
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1441
|
+
result = self.client.batch_set_cell_style(spreadsheet_token, [style_data_left, style_data_center, style_data_right])
|
|
1442
|
+
except Exception as e:
|
|
1443
|
+
log(e)
|