13 KiB
Bug 报告
严重程度说明
- 🔴 严重: 可能导致程序崩溃或数据丢失
- 🟡 中等: 可能导致功能异常或错误处理不当
- 🟢 轻微: 代码质量问题,不影响功能但需要改进
1. api.py (根目录) - 错误日志键名错误
位置
- 第540行:
entry_data_batch_delete方法 - 第654行:
workflow_task_hand_over方法 - 第701行:
get_upload_token方法 - 第745行:
upload_file方法
问题描述
🟡 中等 - 在错误日志中使用了错误的键名 data['data_list'],但这些方法中可能不存在该键。
代码示例
# 第540行 - entry_data_batch_delete
error_task_logger.error(
f"任务 {data['data_list'][start_index:end_index]} 连续{max_retries}次请求失败,放弃此次请求。")
# 应该是 data['data_ids']
# 第654行 - workflow_task_hand_over
error_task_logger.error(
f"任务 {data['data_list']} 连续{max_retries}次请求失败,放弃此次请求。")
# 该方法没有 data_list 键
# 第701行 - get_upload_token
error_task_logger.error(
f"任务 {data['data_list']} 连续{max_retries}次请求失败,放弃此次请求。")
# 该方法没有 data_list 键
# 第745行 - upload_file
error_task_logger.error(
f"任务 {data['data_list']} 连续{max_retries}次请求失败,放弃此次请求。")
# 该方法没有 data_list 键
修复建议
使用正确的键名或使用 data.get() 方法安全访问。
2. main.py - Handler 调用问题
位置
第76行和第80行
问题描述
🟡 中等 - 当 action_map.get() 返回默认的 lambda 函数时,handler 是一个函数对象,但代码中直接将其传递给 enqueue_task,这应该是正确的。但如果 handler 是 lambda,需要确保它能被正确调用。
代码示例
# 第76行
handler = action_map.get(sub_action, lambda x: {'msg': '未执行'})
# 第80行
handler = action_map.get(action, lambda x: {'msg': '未知的操作'})
修复建议
确保所有 handler 都能正确处理数据参数。如果 handler 可能返回 None,需要添加检查。
3. main.py - 队列阻塞风险
位置
第86行
问题描述
🟡 中等 - 使用 response_queue.get() 会无限期阻塞,如果任务处理线程出现问题,可能导致请求永远挂起。
代码示例
result = await anyio.to_thread.run_sync(response_queue.get)
修复建议
添加超时机制或使用 response_queue.get(timeout=...)。
4. app/api.py - 数组访问未检查
位置
第712-713行:get_upload_token 方法
问题描述
🔴 严重 - 直接访问 res_j['token_and_url_list'][0] 可能导致 KeyError 或 IndexError。
代码示例
res_j = res.json()
upload_url = res_j['token_and_url_list'][0]['url']
upload_token = res_j['token_and_url_list'][0]['token']
修复建议
添加检查:
token_list = res_j.get('token_and_url_list', [])
if not token_list:
raise ValueError("未获取到上传凭证")
upload_url = token_list[0].get('url')
upload_token = token_list[0].get('token')
5. app/module/F6_Plugin_module.py - 数组访问未检查
位置
第40行:accept_file 方法
问题描述
🔴 严重 - 直接访问 data['data']['附件'][0]['url'] 可能导致 KeyError 或 IndexError。
代码示例
url = data['data']['附件'][0]['url']
修复建议
添加检查:
attachments = data.get('data', {}).get('附件', [])
if not attachments:
return None, data
url = attachments[0].get('url')
6. app/module/F6_Plugin_module.py - 键访问未检查
位置
第96行:check_file 方法
问题描述
🔴 严重 - 直接访问 data1['data']['Action(隐藏)'] 可能导致 KeyError。
代码示例
action = data1['data']['Action(隐藏)']
修复建议
使用安全访问:
action = data1.get('data', {}).get('Action(隐藏)')
if not action:
return {'msg': '缺少Action字段'}
7. app/module/module.py - 临时文件未清理
位置
第21-31行:get_captcha 方法
问题描述
🟢 轻微 - 创建了临时文件 captcha.png 和 preprocessed_captcha.png,但没有清理。
代码示例
with open('captcha.png', 'wb') as f:
f.write(response.content)
# ... 处理文件 ...
image.save('preprocessed_captcha.png')
修复建议
使用临时文件或处理完后删除:
import tempfile
import os
with tempfile.NamedTemporaryFile(suffix='.png', delete=False) as tmp:
tmp.write(response.content)
tmp_path = tmp.name
try:
# 处理文件
...
finally:
if os.path.exists(tmp_path):
os.remove(tmp_path)
8. app/module/module.py - group_id 可能为空
位置
第65-73行:login_in 方法
问题描述
🟡 中等 - 如果找不到匹配的 company_name,group_id 为空字符串,后续请求可能失败。
代码示例
group_id = ''
for group in res_json.get('data', []):
if group["groupName"] == company_name:
group_id = group.get("groupId")
token = quote(res_json['token'])
url = (f'https://yunxiu.f6car.cn/kzf6/user/loginAfterChooseGroup?'
f'token={token}&groupId={group_id}&macAddress=')
修复建议
添加检查:
if not group_id:
logger.error(f"未找到公司名称: {company_name}")
return None
9. app/module/module.py - 时间判断逻辑错误
位置
第119行:delete_customer_background 方法(在 delete_tasks.py 中)
第246行:delete_car_background 方法(在 delete_tasks.py 中)
问题描述
🟡 中等 - 时间判断逻辑错误:if 20 <= now.hour <= 8: 永远不会为 True(20 不可能小于等于 8)。
代码示例
now = datetime.now()
if 20 <= now.hour <= 8:
time.sleep(1)
else:
time.sleep(3)
修复建议
应该是:
now = datetime.now()
if 8 <= now.hour <= 20: # 8点到20点之间
time.sleep(3.5)
else: # 其他时间
time.sleep(1.5)
10. app/tasks/common.py - 数组访问未检查
位置
第56行:approve_workflow 方法
问题描述
🔴 严重 - 直接访问 json['tasks'] 和 json['tasks'][0] 可能导致 KeyError 或 IndexError。
代码示例
for task in json['tasks']:
if task['status'] == 0:
username = task['assignee']['username']
instance_id = task['instance_id']
task_id = task['task_id']
修复建议
添加检查:
tasks = json.get('tasks', [])
if not tasks:
logger.error("未找到待处理任务")
return
for task in tasks:
if task.get('status') == 0:
assignee = task.get('assignee', {})
username = assignee.get('username')
instance_id = task.get('instance_id')
task_id = task.get('task_id')
if username and instance_id and task_id:
break
11. app/tasks/delete_tasks.py - 数组访问未检查
位置
第62行:delete_customer_background 方法
第154行:delete_car_background 方法
问题描述
🔴 严重 - 直接访问 org_res.json().get("data").get("list")[0] 可能导致 IndexError。
代码示例
operate_org_id = org_res.json().get("data").get("list")[0].get("orgId")
修复建议
添加检查:
org_data = org_res.json().get("data", {}).get("list", [])
if not org_data:
logger.error("未获取到门店信息")
return
operate_org_id = org_data[0].get("orgId")
12. app/tasks/customer_tasks.py - 重试逻辑错误
位置
第52-67行:modify_customer_info_background 方法
问题描述
🟡 中等 - retry_count 在循环外部初始化,导致每次页面请求都使用相同的重试计数,而不是每页独立重试。
代码示例
retry_count = 0
for page in range(1, total_pages + 1):
while retry_count < max_retries:
# ...
if response.status_code == 200:
break
else:
retry_count += 1
修复建议
将 retry_count 移到循环内部:
for page in range(1, total_pages + 1):
retry_count = 0
while retry_count < max_retries:
# ...
13. app/tasks/delete_tasks.py - 条件判断错误
位置
第115-116行:delete_customer_background 方法
问题描述
🟡 中等 - if success + fail < len(json_data): 这个条件判断逻辑有问题,应该检查是否还有未处理的项。
代码示例
if success + fail < len(json_data):
continue
修复建议
这个条件判断似乎是想检查是否处理完所有项,但逻辑不正确。应该移除或修正。
14. app/tasks/delete_tasks.py - 变量作用域问题
位置
第215行:delete_car_background 方法
问题描述
🟡 中等 - 在循环外部使用了 page 变量,但 page 只在循环内部定义。
代码示例
for page in range(1, all_page + 1):
# ...
for item in items:
# ...
if not car_id or not customer_id:
logger.info(f"页码 {page} 中缺少必要的ID信息") # page 在这里可用
# ...
# ...
logger.error(f"删除失败: 页码 {page}, ...") # page 在这里可用
实际上这个不是bug,page 在循环内部是可用的。但如果在循环外部使用就会有问题。
15. api.py (根目录) - 文件资源泄漏
位置
第721-747行:upload_file 方法
问题描述
🔴 严重 - 文件在循环外部打开,如果循环中发生异常或提前返回,文件可能不会被关闭,导致资源泄漏。
代码示例
f = open(file_path, 'rb')
files = {"file": f}
retries = 0
while retries <= max_retries:
try:
res = requests.post(url=url, data=payload, headers=headers, files=files, timeout=10)
# ...
if res.status_code == 200:
return data_get # 提前返回,文件未关闭!
# ...
except requests.exceptions.RequestException as e:
# 如果异常发生,文件可能不会被关闭
# ...
f.close() # 只有在循环结束后才会执行
修复建议
使用 with 语句确保文件总是被关闭:
retries = 0
result = None
while retries <= max_retries:
try:
with open(file_path, 'rb') as f:
files = {"file": f}
res = requests.post(url=url, data=payload, headers=headers, files=files, timeout=10)
res.raise_for_status()
data_get = res.json()
if res.status_code == 200:
return data_get
retries += 1
time.sleep(3)
except requests.exceptions.RequestException as e:
logger.warning(f"请求异常: {e}, 将重新请求")
retries += 1
time.sleep(3)
if retries > max_retries:
error_task_logger.error(f"上传文件失败,已重试{max_retries}次")
return None
16. api.py (根目录) - 重试时文件指针问题
位置
第721-747行:upload_file 方法
问题描述
🟡 中等 - 文件在循环外部打开,重试时文件指针已经移动到文件末尾,后续重试会读取空内容。
代码示例
f = open(file_path, 'rb')
files = {"file": f}
retries = 0
while retries <= max_retries:
res = requests.post(url=url, data=payload, headers=headers, files=files, timeout=10)
# 第一次请求后,文件指针已经移动到末尾
# 重试时会读取空内容
修复建议
每次重试时重新打开文件,或使用 with 语句在每次循环中打开文件。
总结
按严重程度分类
严重 (🔴) - 需要立即修复:
- app/api.py - 数组访问未检查(get_upload_token)
- app/module/F6_Plugin_module.py - 数组访问未检查(accept_file)
- app/module/F6_Plugin_module.py - 键访问未检查(check_file)
- app/tasks/common.py - 数组访问未检查(approve_workflow)
- app/tasks/delete_tasks.py - 数组访问未检查(两处)
- api.py (根目录) - 文件资源泄漏(upload_file)
中等 (🟡) - 建议尽快修复:
- api.py - 错误日志键名错误(多处)
- api.py - 重试时文件指针问题(upload_file)
- main.py - Handler 调用和队列阻塞问题
- app/module/module.py - group_id 可能为空
- app/module/module.py - 时间判断逻辑错误(两处)
- app/tasks/customer_tasks.py - 重试逻辑错误
- app/tasks/delete_tasks.py - 条件判断错误
轻微 (🟢) - 代码质量改进:
- app/module/module.py - 临时文件未清理
建议的修复优先级
- 立即修复:所有严重级别的bug,特别是可能导致程序崩溃的数组访问问题
- 尽快修复:中等级别的bug,特别是逻辑错误和错误处理问题
- 代码改进:轻微级别的bug,在时间允许时进行改进