507 lines
13 KiB
Markdown
507 lines
13 KiB
Markdown
# Bug 报告
|
||
|
||
## 严重程度说明
|
||
- 🔴 **严重**: 可能导致程序崩溃或数据丢失
|
||
- 🟡 **中等**: 可能导致功能异常或错误处理不当
|
||
- 🟢 **轻微**: 代码质量问题,不影响功能但需要改进
|
||
|
||
---
|
||
|
||
## 1. api.py (根目录) - 错误日志键名错误
|
||
|
||
### 位置
|
||
- 第540行:`entry_data_batch_delete` 方法
|
||
- 第654行:`workflow_task_hand_over` 方法
|
||
- 第701行:`get_upload_token` 方法
|
||
- 第745行:`upload_file` 方法
|
||
|
||
### 问题描述
|
||
🟡 **中等** - 在错误日志中使用了错误的键名 `data['data_list']`,但这些方法中可能不存在该键。
|
||
|
||
### 代码示例
|
||
```python
|
||
# 第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,需要确保它能被正确调用。
|
||
|
||
### 代码示例
|
||
```python
|
||
# 第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()` 会无限期阻塞,如果任务处理线程出现问题,可能导致请求永远挂起。
|
||
|
||
### 代码示例
|
||
```python
|
||
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。
|
||
|
||
### 代码示例
|
||
```python
|
||
res_j = res.json()
|
||
upload_url = res_j['token_and_url_list'][0]['url']
|
||
upload_token = res_j['token_and_url_list'][0]['token']
|
||
```
|
||
|
||
### 修复建议
|
||
添加检查:
|
||
```python
|
||
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。
|
||
|
||
### 代码示例
|
||
```python
|
||
url = data['data']['附件'][0]['url']
|
||
```
|
||
|
||
### 修复建议
|
||
添加检查:
|
||
```python
|
||
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。
|
||
|
||
### 代码示例
|
||
```python
|
||
action = data1['data']['Action(隐藏)']
|
||
```
|
||
|
||
### 修复建议
|
||
使用安全访问:
|
||
```python
|
||
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`,但没有清理。
|
||
|
||
### 代码示例
|
||
```python
|
||
with open('captcha.png', 'wb') as f:
|
||
f.write(response.content)
|
||
# ... 处理文件 ...
|
||
image.save('preprocessed_captcha.png')
|
||
```
|
||
|
||
### 修复建议
|
||
使用临时文件或处理完后删除:
|
||
```python
|
||
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` 为空字符串,后续请求可能失败。
|
||
|
||
### 代码示例
|
||
```python
|
||
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=')
|
||
```
|
||
|
||
### 修复建议
|
||
添加检查:
|
||
```python
|
||
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)。
|
||
|
||
### 代码示例
|
||
```python
|
||
now = datetime.now()
|
||
if 20 <= now.hour <= 8:
|
||
time.sleep(1)
|
||
else:
|
||
time.sleep(3)
|
||
```
|
||
|
||
### 修复建议
|
||
应该是:
|
||
```python
|
||
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。
|
||
|
||
### 代码示例
|
||
```python
|
||
for task in json['tasks']:
|
||
if task['status'] == 0:
|
||
username = task['assignee']['username']
|
||
instance_id = task['instance_id']
|
||
task_id = task['task_id']
|
||
```
|
||
|
||
### 修复建议
|
||
添加检查:
|
||
```python
|
||
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。
|
||
|
||
### 代码示例
|
||
```python
|
||
operate_org_id = org_res.json().get("data").get("list")[0].get("orgId")
|
||
```
|
||
|
||
### 修复建议
|
||
添加检查:
|
||
```python
|
||
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` 在循环外部初始化,导致每次页面请求都使用相同的重试计数,而不是每页独立重试。
|
||
|
||
### 代码示例
|
||
```python
|
||
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` 移到循环内部:
|
||
```python
|
||
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):` 这个条件判断逻辑有问题,应该检查是否还有未处理的项。
|
||
|
||
### 代码示例
|
||
```python
|
||
if success + fail < len(json_data):
|
||
continue
|
||
```
|
||
|
||
### 修复建议
|
||
这个条件判断似乎是想检查是否处理完所有项,但逻辑不正确。应该移除或修正。
|
||
|
||
---
|
||
|
||
## 14. app/tasks/delete_tasks.py - 变量作用域问题
|
||
|
||
### 位置
|
||
第215行:`delete_car_background` 方法
|
||
|
||
### 问题描述
|
||
🟡 **中等** - 在循环外部使用了 `page` 变量,但 `page` 只在循环内部定义。
|
||
|
||
### 代码示例
|
||
```python
|
||
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` 方法
|
||
|
||
### 问题描述
|
||
🔴 **严重** - 文件在循环外部打开,如果循环中发生异常或提前返回,文件可能不会被关闭,导致资源泄漏。
|
||
|
||
### 代码示例
|
||
```python
|
||
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` 语句确保文件总是被关闭:
|
||
```python
|
||
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` 方法
|
||
|
||
### 问题描述
|
||
🟡 **中等** - 文件在循环外部打开,重试时文件指针已经移动到文件末尾,后续重试会读取空内容。
|
||
|
||
### 代码示例
|
||
```python
|
||
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` 语句在每次循环中打开文件。
|
||
|
||
---
|
||
|
||
## 总结
|
||
|
||
### 按严重程度分类
|
||
|
||
**严重 (🔴)** - 需要立即修复:
|
||
1. app/api.py - 数组访问未检查(get_upload_token)
|
||
2. app/module/F6_Plugin_module.py - 数组访问未检查(accept_file)
|
||
3. app/module/F6_Plugin_module.py - 键访问未检查(check_file)
|
||
4. app/tasks/common.py - 数组访问未检查(approve_workflow)
|
||
5. app/tasks/delete_tasks.py - 数组访问未检查(两处)
|
||
6. api.py (根目录) - 文件资源泄漏(upload_file)
|
||
|
||
**中等 (🟡)** - 建议尽快修复:
|
||
1. api.py - 错误日志键名错误(多处)
|
||
2. api.py - 重试时文件指针问题(upload_file)
|
||
3. main.py - Handler 调用和队列阻塞问题
|
||
4. app/module/module.py - group_id 可能为空
|
||
5. app/module/module.py - 时间判断逻辑错误(两处)
|
||
6. app/tasks/customer_tasks.py - 重试逻辑错误
|
||
7. app/tasks/delete_tasks.py - 条件判断错误
|
||
|
||
**轻微 (🟢)** - 代码质量改进:
|
||
1. app/module/module.py - 临时文件未清理
|
||
|
||
### 建议的修复优先级
|
||
|
||
1. **立即修复**:所有严重级别的bug,特别是可能导致程序崩溃的数组访问问题
|
||
2. **尽快修复**:中等级别的bug,特别是逻辑错误和错误处理问题
|
||
3. **代码改进**:轻微级别的bug,在时间允许时进行改进
|
||
|