[根目录](../CLAUDE.md) > **app** # app - FastAPI 后端模块 ## 模块职责 提供 JobData 平台的 REST API 服务,包含:用户/角色/权限/菜单/部门管理(RBAC),招聘数据入库、查询、清洗与分析,Token 与代理 IP 管理,定时任务调度,以及审计日志记录。 --- ## 入口与启动 | 文件 | 说明 | |------|------| | `run.py`(根目录) | `uvicorn` 启动入口,读取 `APP_HOST`/`APP_PORT`/`UVICORN_WORKERS` 环境变量 | | `app/__init__.py` | FastAPI 应用工厂 `create_app()`,注册中间件、异常处理器、路由,以及 lifespan 钩子 | | `app/core/init_app.py` | lifespan 内部逻辑:DB 迁移、种子数据、ClickHouse 初始化 | | `app/core/scheduler.py` | APScheduler 启动与任务注册 | ### 启动顺序 1. Tortoise-ORM 连接 MySQL,生成 schema 2. 按环境变量执行数据库迁移(Aerich) 3. 初始化种子数据(超级管理员、菜单、API、角色) 4. 初始化 ClickHouse 表/视图(可选) 5. APScheduler 启动定时任务 6. FastAPI 开始接受请求 --- ## 对外接口 API 前缀:`/api/v1`,完整路由注册见 `app/api/v1/__init__.py`。 | 路由前缀 | 标签 | 权限 | 说明 | |----------|------|------|------| | `/base` | 基础模块 | 无 | 登录、获取用户信息、菜单树 | | `/user` | 用户管理 | DependPermission | 用户 CRUD | | `/role` | 角色管理 | DependPermission | 角色 CRUD、菜单/API 分配 | | `/menu` | 菜单管理 | DependPermission | 菜单树 CRUD | | `/api` | API 管理 | DependPermission | 接口注册与权限管理 | | `/dept` | 部门管理 | DependPermission | 部门树 CRUD | | `/auditlog` | 审计日志 | DependPermission | 操作日志查询 | | `/job` & `/universal` | 数据入库/通用数据接口 | 无鉴权(内部调用) | 职位/公司数据批量入库 | | `/token` | Token 管理 | 无鉴权 | Boss Token CRUD | | `/proxy` | 代理 IP 管理 | DependPermission | 代理池管理 | | `/stats` | 数据统计 | 无 | 各平台数据量统计 | | `/pipeline` | 流水线 | 无 | 触发 ECS pipeline | | `/keyword` | 关键词管理 | 无 | 爬虫关键词(城市+职位)管理 | | `/cleaning` | 数据清理 | DependPermission | 定向清洗操作 | | `/analytics` | 数据分析 | 无 | 趋势、来源分布统计 | | `/company` | 公司搜索 | 无 | 公司信息查询 | **认证机制**:JWT(HS256,有效期 7 天),通过 `DependPermission` 依赖注入检查路由级别权限。 --- ## 关键依赖与配置 配置集中在 `app/settings/config.py`(`pydantic-settings.BaseSettings`,支持环境变量覆盖): ```python # 关键字段(需通过环境变量覆盖) SECRET_KEY = "CHANGE_ME_DEV_ONLY" # JWT 签名密钥 TORTOISE_ORM.connections.default # MySQL 连接串(含密码) CLICKHOUSE_HOST / USER / PASS # ClickHouse 连接 SMTP_USER / SMTP_PASS # 邮件凭据 ``` **中间件链**(从外到内): 1. `CORSMiddleware` - 跨域(默认允许 `http://localhost:5173`) 2. `BackGroundTaskMiddleware` - 后台任务支持 3. `HttpAuditLogMiddleware` - HTTP 审计日志(排除登录接口) 4. `IpTrackingMiddleware` - IP 请求追踪 --- ## 数据模型 ### MySQL(Tortoise-ORM) | 表 | 模型文件 | 说明 | |----|----------|------| | `user` | `app/models/admin.py` | 用户(含角色多对多) | | `role` | `app/models/admin.py` | 角色(含菜单、API 多对多) | | `api` | `app/models/admin.py` | 接口注册表 | | `menu` | `app/models/admin.py` | 菜单树(parent_id 自引用) | | `dept` | `app/models/admin.py` | 部门树 + 闭包表 | | `auditlog` | `app/models/admin.py` | HTTP 操作审计 | | `boss_token` | `app/models/token.py` | Boss 直聘登录 Token | | `cleaning_*` | `app/models/cleaning.py` | 数据清洗任务状态 | | `scheduled_task_run` / `stats_total` | `app/models/metrics.py` | 定时任务运行记录与统计汇总 | ### ClickHouse(原始数据存储) | 表/视图 | 引擎 | 说明 | |---------|------|------| | `boss_job` | MergeTree | Boss 职位原始 JSON,`job_id` 去重 | | `boss_company` | MergeTree | Boss 公司原始 JSON,`company_name` 去重 | | `qcwy_job` | MergeTree | 前程无忧职位,`job_id + update_date_time` 去重 | | `qcwy_company` | MergeTree | 前程无忧公司 | | `zhilian_job` | MergeTree | 智联招聘职位,`number + first_publish_time` 去重 | | `zhilian_company` | MergeTree | 智联招聘公司 | | `pending_company` | ReplacingMergeTree | 待处理公司队列,`(source, company_id)` 去重 | | `job_analytics` | VIEW | 三平台统一分析视图(UNION ALL) | ClickHouse 表结构在 `app/core/clickhouse_init.py` 中通过 `CREATE TABLE IF NOT EXISTS` 管理。 --- ## 核心服务 | 服务文件 | 职责 | |----------|------| | `app/services/cleaning.py` | `CleaningService`:多平台定向清洗(URL/ID/公司名/公司ID),自动识别平台 | | `app/services/company_cleaner.py` | 公司数据自动清洗:collect 待处理 → process → 入库 | | `app/services/analytics_service.py` | `AnalyticsService`:封装 ClickHouse 分析查询 | | `app/services/job.py` | `DataRouterService`:数据路由入库(去重逻辑) | | `app/services/ingest_service.py` | 批量数据摄入 | | `app/services/crawler/boss.py` | Boss 爬虫 Service 封装(HTTP 层) | | `app/services/crawler/qcwy.py` | 前程无忧爬虫 Service | | `app/services/crawler/zhilian.py` | 智联招聘爬虫 Service | | `app/repositories/clickhouse_repo.py` | ClickHouse Repository(`ClickHouseBaseRepo` + `JobAnalyticsRepo`) | | `app/core/scheduler.py` | 定时任务:stats、ip_alert、ecs_pipeline、company_cleaning、daily_cleanup | | `app/core/locks.py` | `DistributedLock`:基于文件/Redis 的分布式锁,防多 Worker 重复执行 | | `app/core/algorithms/antispider.py` | 反爬虫算法(签名生成等) | --- ## 测试与质量 - 当前无测试文件,属于主要缺口。 - 代码质量工具:`ruff`(lint)、`black`(格式)、`isort`(导入排序)。 - 建议优先补充的测试: - `CleaningService.clean_target_auto()` 的平台识别逻辑 - `DataRouterService.store_data()` 的去重逻辑 - `app/api/v1/analytics.py` 接口集成测试 --- ## 常见问题 (FAQ) **Q: 启动报 ClickHouse 连接失败?** A: 检查 `CLICKHOUSE_HOST` 环境变量,或在 `config.py` 中将 `CLICKHOUSE_HOST` 置为空字符串跳过初始化。 **Q: 多 Worker 下任务重复执行?** A: 通过文件锁(`.startup_lock` 目录)和 `DistributedLock` 保护,若 Worker 异常退出可能导致锁残留,手动删除 `.startup_lock` 目录即可。 **Q: 新增 API 接口后权限不生效?** A: 在路由文件中注册路由后,重启应用会触发 `api_controller.refresh_api()` 自动扫描 FastAPI 路由表并更新 `api` 表,然后在角色管理中分配权限。 --- ## 相关文件清单 ``` app/ ├── __init__.py # 应用工厂 create_app() ├── settings/config.py # 全局配置(Settings) ├── api/v1/__init__.py # 路由聚合 ├── api/v1/analytics.py # 数据分析接口 ├── api/v1/cleaning/ # 数据清理接口 ├── api/v1/job/ # 数据入库接口 ├── api/v1/keyword/ # 关键词管理接口 ├── api/v1/company/ # 公司搜索接口 ├── controllers/ # 业务控制器(CRUD 封装) ├── core/ │ ├── init_app.py # lifespan 初始化 │ ├── scheduler.py # APScheduler 定时任务 │ ├── clickhouse.py # ClickHouse 连接管理 │ ├── clickhouse_init.py # ClickHouse 表/视图 DDL │ ├── locks.py # 分布式锁 │ ├── middlewares.py # 中间件 │ └── algorithms/ # 签名/反爬虫算法 ├── models/ │ ├── admin.py # User, Role, Api, Menu, Dept, AuditLog │ ├── token.py # BossToken │ ├── metrics.py # ScheduledTaskRun, StatsTotal │ └── cleaning.py # 清洗任务状态 ├── repositories/ │ └── clickhouse_repo.py # ClickHouse 查询仓库 ├── services/ │ ├── cleaning.py # CleaningService │ ├── company_cleaner.py # 公司自动清洗 │ ├── analytics_service.py # 数据分析 Service │ ├── job.py # DataRouterService(数据入库路由) │ └── crawler/ # 各平台爬虫 Service 封装 └── schemas/ # Pydantic 请求/响应 Schema ``` --- ## 变更记录 (Changelog) | 日期 | 说明 | |------|------| | 2026-03-20 | 初始化模块文档 |