214 lines
7.6 KiB
Markdown
214 lines
7.6 KiB
Markdown
# 📋 实施计划:Ruff 代码优化 + 项目质量提升
|
||
|
||
> 生成时间:2026-03-20
|
||
> 工作目录:/Users/win/2025/AICoding/JobData
|
||
|
||
---
|
||
|
||
## 一、现状问题总览
|
||
|
||
### 1.1 Ruff 实际扫描结果(34 个错误,22 个可自动修复)
|
||
|
||
| 规则 | 数量 | 说明 | 可自动修复 |
|
||
|------|------|------|:---:|
|
||
| `F401` | 20 | 未使用的 import | ✅ |
|
||
| `E402` | 5 | import 不在文件顶部 | ❌ |
|
||
| `F541` | 3 | f-string 没有占位符 | ✅ |
|
||
| `F821` | 3 | 引用了未定义的变量名 | ❌ |
|
||
| `F811` | 2 | 重复定义(导入后又被覆盖) | ✅ |
|
||
| `E722` | 1 | 裸 `except:`(不捕获具体异常) | ❌ |
|
||
| **合计** | **34** | | **22 可自动修复** |
|
||
|
||
### 1.2 受影响文件清单
|
||
|
||
| 文件 | 问题数 | 最严重问题 |
|
||
|------|--------|-----------|
|
||
| `app/api/v1/token/token.py` | 8 | E402 + F811(import 顺序混乱,重复定义) |
|
||
| `app/services/job.py` | 3 | **F821 未定义变量** `udt`、`fpt`;**E722 裸 except** |
|
||
| `app/services/crawler/zhilian.py` | 1 | **F821 未定义变量** `json` |
|
||
| `app/services/company_cleaner.py` | 3 | F541 空 f-string |
|
||
| `app/services/crawler/__init__.py` | 3 | F401 无效的服务导出 |
|
||
| `app/repositories/clickhouse_repo.py` | 2 | F401 `math`, `Generator` |
|
||
| `app/schemas/token.py` | 2 | F401 `Dict`, `Any` |
|
||
| `app/controllers/job.py` | 1 | F401 `Optional` |
|
||
| `app/controllers/keyword.py` | 1 | F401 `CRUDBase` |
|
||
| `app/core/algorithms/antispider.py` | 1 | F401 `os` |
|
||
| `app/core/ip_tracking.py` | 1 | F401 `Any` |
|
||
| `app/core/locks.py` | 1 | F401 `time` |
|
||
| `app/api/v1/analytics.py` | 1 | F401 `List` |
|
||
| `app/api/v1/ingest/ingest.py` | 1 | F401 `Optional` |
|
||
| `app/schemas/analytics.py` | 1 | F401 `Any` |
|
||
| `app/services/crawler/boss.py` | 1 | F401 `os` |
|
||
|
||
---
|
||
|
||
### 1.3 Ruff 扫描之外的深层问题(代码审查发现)
|
||
|
||
#### 🔴 CRITICAL — 安全问题(硬编码凭据)
|
||
|
||
| 文件 | 行号 | 问题 |
|
||
|------|------|------|
|
||
| `app/settings/config.py` | ~23 | `SECRET_KEY = "CHANGE_ME_DEV_ONLY"` JWT 密钥 |
|
||
| `app/settings/config.py` | ~27-30 | ClickHouse 主机 IP、用户名、密码明文 |
|
||
| `app/settings/config.py` | ~44-45 | SMTP 真实邮箱账号 + 授权码明文 |
|
||
| `app/settings/config.py` | ~52 | MySQL root 密码 + 生产 IP 硬编码在连接串 |
|
||
| `app/services/job.py` | ~533-535 | 外部 API salt 硬编码 |
|
||
|
||
#### 🔴 HIGH — 性能问题(事件循环阻塞)
|
||
|
||
| 文件 | 行号 | 问题 |
|
||
|------|------|------|
|
||
| `app/services/job.py` | ~547 | `async def` 中调用同步 `requests.post` 阻塞事件循环 |
|
||
| `app/services/job.py` | ~926-933 | 串行逐条发送远程推送,N 条数据 = N 次串行阻塞 |
|
||
| `app/core/locks.py` | ~38 | 同步 `redis.Redis` 在 `async` 方法中调用,阻塞事件循环 |
|
||
|
||
#### 🟡 MEDIUM — 代码质量
|
||
|
||
| 文件 | 问题 |
|
||
|------|------|
|
||
| `app/services/job.py` | 7 个 `_check_*_duplicate` 方法几乎完全重复,仅 SQL 参数不同 |
|
||
| `app/services/job.py` | 1 个死代码方法:`_check_qcwy_company_duplicate_by_name` 从未被调用 |
|
||
| `app/repositories/clickhouse_repo.py` | `group_by_column` 直接拼入 SQL(潜在 SQL 注入) |
|
||
| `app/api/v1/__init__.py` | 同一 router 注册两次(`/job` 和 `/universal`),OpenAPI 文档重复 |
|
||
| 全项目 | 零测试文件,关键业务逻辑(去重、路由分发)无任何测试保护 |
|
||
|
||
---
|
||
|
||
## 二、实施步骤
|
||
|
||
### Phase 1:Ruff 自动修复(低风险,5 分钟)
|
||
|
||
```bash
|
||
# 自动修复 22 个可自动修复的问题
|
||
pipenv run ruff check app/ --fix
|
||
|
||
# 验证修复结果
|
||
pipenv run ruff check app/ --statistics
|
||
```
|
||
|
||
自动修复覆盖:F401(未使用 import)、F541(空 f-string)、F811(重复定义)
|
||
|
||
### Phase 2:手动修复 Ruff 报告的无法自动修复问题(12 个)
|
||
|
||
#### 2.1 F821 未定义变量(CRITICAL,会导致运行时崩溃)
|
||
|
||
**`app/services/job.py:348`** — 变量 `udt` 未定义
|
||
|
||
需要读取上下文,确认 `udt` 应该是什么(可能是 `update_date_time` 的缩写或某个局部变量)。
|
||
|
||
**`app/services/job.py:374`** — 变量 `fpt` 未定义
|
||
|
||
需要读取上下文,确认 `fpt` 应该是什么(可能是 `first_publish_time` 缩写)。
|
||
|
||
**`app/services/crawler/zhilian.py:60`** — `json` 模块未导入但被使用
|
||
|
||
修复:在文件顶部添加 `import json`。
|
||
|
||
#### 2.2 E722 裸 except(`app/services/job.py:302`)
|
||
|
||
```python
|
||
# 修改前
|
||
except:
|
||
pass
|
||
|
||
# 修改后
|
||
except Exception as e:
|
||
logger.error(f"处理失败: {e}")
|
||
```
|
||
|
||
#### 2.3 E402 import 不在顶部(`app/api/v1/token/token.py:92-96`)
|
||
|
||
将条件式 import 移至文件顶部,或使用 `TYPE_CHECKING` 保护块。
|
||
|
||
### Phase 3:凭据安全(CRITICAL,建议本次一并完成)
|
||
|
||
**目标**:将所有硬编码凭据移入环境变量
|
||
|
||
1. 在项目根目录创建 `.env.example`(安全模板)
|
||
2. 修改 `app/settings/config.py`,用 `pydantic-settings` 从环境变量读取所有敏感值
|
||
3. 启动时校验必填环境变量,缺失则报错退出(Fail Fast)
|
||
4. 将 `.env` 加入 `.gitignore`(已有则确认)
|
||
|
||
```python
|
||
# config.py 改造后示例
|
||
from pydantic_settings import BaseSettings
|
||
|
||
class Settings(BaseSettings):
|
||
SECRET_KEY: str # 必填,无默认值
|
||
CLICKHOUSE_HOST: str = "localhost"
|
||
CLICKHOUSE_USER: str = "default"
|
||
CLICKHOUSE_PASS: str # 必填
|
||
SMTP_USER: str = ""
|
||
SMTP_PASS: str = ""
|
||
DB_URL: str # 必填
|
||
|
||
class Config:
|
||
env_file = ".env"
|
||
```
|
||
|
||
### Phase 4:性能修复(async 阻塞问题)
|
||
|
||
1. 将 `app/services/job.py` 中的 `requests.post` 替换为 `httpx.AsyncClient`(已在依赖中)
|
||
2. 将 `_batch_send_to_remote_server` 改为 `asyncio.gather` 并发执行
|
||
3. 将 `app/core/locks.py` 中的同步 `redis.Redis` 替换为 `aioredis`(或 `redis.asyncio`)
|
||
|
||
### Phase 5:代码去重(可维护性)
|
||
|
||
合并 7 个重复的 `_check_*_duplicate` 方法为 1 个通用方法:
|
||
|
||
```python
|
||
async def _check_duplicate(
|
||
self,
|
||
table: str,
|
||
conditions: dict[str, str], # {"column_name": "value"}
|
||
days: int = 90
|
||
) -> bool:
|
||
...
|
||
```
|
||
|
||
删除死代码:`_check_qcwy_company_duplicate_by_name`
|
||
|
||
---
|
||
|
||
## 三、关键文件索引
|
||
|
||
| 文件 | 操作 | 说明 |
|
||
|------|------|------|
|
||
| `app/settings/config.py` | 重构 | 凭据移入环境变量 |
|
||
| `app/services/job.py` | 修复 | F821、E722、async 阻塞、方法去重 |
|
||
| `app/services/crawler/zhilian.py` | 修复 | 添加 `import json` |
|
||
| `app/api/v1/token/token.py` | 整理 | 修复 E402 import 顺序 |
|
||
| `app/services/company_cleaner.py` | 自动修复 | F541 空 f-string |
|
||
| `app/core/locks.py` | 修复 | 同步 redis → 异步 |
|
||
| `app/repositories/clickhouse_repo.py` | 修复 | 删除未用 import |
|
||
| `.env.example` | 新建 | 环境变量模板 |
|
||
|
||
---
|
||
|
||
## 四、风险与缓解
|
||
|
||
| 风险 | 缓解措施 |
|
||
|------|----------|
|
||
| 修复 F821 时误判变量用途 | 先读原函数完整逻辑再修复 |
|
||
| 凭据迁移后服务无法启动 | 先创建 `.env` 再重启服务 |
|
||
| async 改造引入新 bug | 修改后在本地运行完整功能测试 |
|
||
| 方法合并破坏去重逻辑 | 保持原有 SQL 逻辑不变,只提取公共参数 |
|
||
|
||
---
|
||
|
||
## 五、执行顺序建议
|
||
|
||
```
|
||
Phase 1(5min) → pipenv run ruff check app/ --fix
|
||
Phase 2(30min) → 手动修复 F821 × 3、E722 × 1、E402 × 5
|
||
Phase 3(60min) → 凭据安全迁移(需配合运维创建 .env)
|
||
Phase 4(90min) → async 阻塞修复(requests → httpx)
|
||
Phase 5(60min) → 去重方法合并(可选,不影响功能)
|
||
```
|
||
|
||
---
|
||
|
||
## SESSION_ID(供 /ccg:execute 使用)
|
||
- CODEX_SESSION: N/A(本次分析由 Claude 本地执行)
|
||
- GEMINI_SESSION: N/A
|