# 📋 实施计划: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