[根目录](../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 | 初始化模块文档 |
