JobData/app/CLAUDE.md
2026-03-22 23:22:30 +08:00

198 lines
8.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

[根目录](../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` | 公司搜索 | 无 | 公司信息查询 |
**认证机制**JWTHS256有效期 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 请求追踪
---
## 数据模型
### MySQLTortoise-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 | 初始化模块文档 |