fao/docs/架构设计.md
2026-04-23 19:24:46 +08:00

226 lines
12 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.

# 蔬菜价格指数平台 — 架构设计
> 版本1.2
> 范围多源价格采集、数据清洗、指数计算、MySQL 存储、接口服务、ECharts 可视化。
> 目标:轻量落地、成本可控、模块解耦、可迭代扩容。
**语言与职责(已定稿)**
- **Python仅负责采集层**多源适配器、调度、反爬、raw/队列表落库),**不承担**管理后台、对外 API、治理批处理、指计算。
- **Java全栈后台****Spring Boot** 为主治理与标准化、指数字典与批算、REST API、鉴权/审计、**管理端后端**、数据库迁移(**Flyway / Liquibase** 等,推荐与 Java 工程同仓)。
- **集成面****MySQL 为事实源**Python 写 raw及约定内字段Java 服务消费 raw 后写 clean、指标与维表。跨语言不共享运行时**只共享表结构契约与可选OpenAPI/内部 HTTP**。
---
## 1. 设计目标与原则
| 目标 | 说明 |
|------|------|
| 轻量优先 | 单机或少量节点即可闭环 MVP避免过早引入重中间件 |
| 可演进 | 原始层、治理规则、指算法、接口契约版本化,支持重算与追溯 |
| 解耦 | 按数据源隔离适配器,采集/治理/计算/服务异步与边界清晰 |
| 风险可控 | 反爬、去重、异常、合规与审计可落实在流程与表结构上 |
**原则**:原始数据以追加为主;指数计算与抓取失败解耦;对外服务读「指标/维表」而非直扫原始大表(必要时走缓存/汇总表)。
---
## 2. 总体分层架构
| 层级 | 职责 | 轻量实现形态 |
|------|------|----------------|
| 采集层 | 多源页面/API 抓取、任务调度、反爬与频控 | Python 爬虫 + APScheduler 或 Cron |
| 接入/缓冲层 | 削峰、解耦、失败重试 | Redis List/Stream或队列表 + 多 Worker |
| 治理层 | 去重、单位/品种归一、异常与质量打标 | **Java 定时任务**(如 Spring `@Scheduled` / XXL-Job 等)+ 可配置规则/维表 |
| 计算层 | 指数据模型、基期、权重、链式/环比等 | **Java 批算**,输入输出可幂等、可版本化 |
| 存储层 | 原始明细、清洗数据、指数结果、元数据 | **MySQL 8.0+(主库,推荐 InnoDB + utf8mb4** |
| 服务层 | REST API、鉴权、限流、健康检查、**管理后台 API** | **Spring Boot**(可 Undertow/Tomcat网关按组织规范 |
| 展示层 | 图表、看板、运营侧配置界面 | 前端 + ECharts**与 Java API 联调** |
**存储说明**:优先复用**现成 MySQL 实例**;大对象/审计快照可接 MinIO/对象存储。热查询与去重可叠加 **Redis**(可选)。
---
## 3. 技术选型
| 领域 | 建议 | 备注 |
|------|------|------|
| 爬虫 | **Python**`httpx` / `aiohttp` + `selectolax` / `parsel`;强渲染页用 Playwright | 仅 `ingest` + `adapters/`**禁止**在爬虫工程实现指算法/管理端 |
| 爬虫调度 | Cron 或 Python 内 APScheduler、独立 worker 进程 | 与 **Java 治理/指计算** 在时间上解耦(可 raw 落库后由 Java 轮询或消息触发) |
| 主库 | **MySQL 8.0+** | Java 与 Python **共用**(凭 DSN/账号分离读写权限更佳InnoDB、utf8mb4、分区同前 |
| 后台与批算 | **Java 17+****Spring Boot 3.x** | 治理/指数/REST/管理端一体;批处理可用 Spring 自带任务或接调度平台 |
| 表结构迁移 | **Flyway****Liquibase****放在 `server/` 工程** | Python 不持有权威 DDL按迁移脚本对齐 |
| 缓存/去重 | Redis | Java 与可选Python 爬虫**共用**时,**键命名空间**须约定,避免互踩 |
| API 契约 | 对外/前后端以 **OpenAPI** 为单一描述(可 `server` 生成 spec | Python 侧不暴露业务 API 时,可不建 OpenAPI |
| 前端 | Vue 或 React + ECharts | 请求 **Java 后端** |
| 可观测 | 统一标准:双栈**结构化日志** + trace id可 OpenTelemetry | Java/Python 用不同 app name 区分 |
| 部署 | Docker Compose`java` 服务 + `crawl-worker` 镜像**分离**;扩容再上编排 | |
---
## 4. 模块解耦
```
各数据源适配器 (Source A/B/…N)
统一采集契约RawRecord + 来源元数据 (source_id、抓取时间、指纹等)
原始层raw只增不改或软删策略
治理与标准化(品种/市场/单位/别名映射、质量 flag
指数计算引擎 ←── 基期/权重/算法版本配置
指标结果表 + 维表
对外/管理 APIJava/ ECharts
```
- **源适配器Python**:一源一包;**仅** 输出统一 raw 契约;选择器、登录、限频不混入 Java 代码。
- **治理/指数/服务Java**:消费 raw 表,**写** clean 与 `index_series` 等,对外提供 REST 与**运营配置能力**。
- **指数字典与算法版本**`index_code`、`config_version` 等字段在 **Java 域模型与 Flyway** 中一以贯之。
- **计算幂等**:由 Java 批任务保证,按「业务日期 + 指数版本」可重算。
---
## 5. 数据流(端到端)
1. 调度触发各源任务,拉取列表/详情。
2. 写入 **raw**(可含 `LONGTEXT`/`JSON` 与关键检索列),记录 HTTP 状态与内容指纹。
3. **去重Java 治理)**DB 唯一约束在 Flyway 中统一定义Redis 去重与 Java/爬虫约定 key 前缀。
4. **标准化Java**:单位、市场、品种别名 → 维表由 **管理端/导入** 维护。
5. **异常与质量Java**:写 clean 与 `quality_flag`
6. **指数批算Java**:读清洗 + 当版本配置 → 写 `index_series`
7. **APIJava**:读指标与维表;**管理端** 对规则、维表、重算任务 **发令**;缓存键与索引策略在 Java 侧实现。
---
## 6. MySQL 实践要点
- 版本与字符集MySQL **8.0+**`utf8mb4`InnoDB。
- 半结构化JSON 存扩展字段,**高筛选字段尽量落普通列+索引**8.0 可配合生成列建索引。
- 大表:按**交易日期/入库日期** RANGE 分区,便于归档与冷数据迁出。
- 多 Worker 队列表:使用 `SELECT … FOR UPDATE SKIP LOCKED`8.0)实现任务抢占。
- 备份:定期全量/增量raw 与指标层策略与 RPO 对齐业务要求。
与 PostgreSQL 相比:若未来有极重 JSON 分析或强 GIS 需求,可再评估从库/分析库;当前以结构化为主的指数平台,**单 MySQL 可覆盖 MVP 至中期**。
---
## 7. 反爬、质量与合规
| 主题 | 建议 |
|------|------|
| 反爬 | 频控、退避、UA/协议合法范围内轮换;**优先**对接官方或开放 API。 |
| 去重 | DB 唯一约束 + 指纹/哈希;同日多价按产品规则(如中位、最新)在治理层定稿。 |
| 异常 | 硬规则(负值、超阈)→ 统计(分位/IQR→ 业务产季/地域规则。 |
| 指算法 | 固定文档:公式、基期=100、缺权/缺价处理、品种替代;多版本可并存。 |
| 合规 | robots/条款与用途评估;**最小化**存个人类信息;对公示指数附方法论与免责声明。 |
**生产环境长期自动化采集**:除上表原则外,须满足《[数据源上线清单](./数据源上线清单.md)》中的 **授权归档、SLA、观测告警、降级与对外话术** 等上线门禁;与《产品设计-蔬菜价格指数平台》中 `source_code`、来源透传约定一致。
---
## 8. 主要风险与缓解
| 风险 | 缓解 |
|------|------|
| 源站改版 | 监控解析失败率;适配器独立;**原始 HTML/快照** 可选入对象存储 |
| 数据损坏/误删 | raw 追加策略、备份、指数可重算 |
| 单库压力 | 分区、读写分离(从库只读 API、热数据 Redis |
| 指争议与口径变更 | 配置版本化、**可重放**历史计算、对账报表 |
---
## 9. 分阶段迭代
| 阶段 | 内容 | 验收参考 |
|------|------|----------|
| 阶段 1MVP | 12 个稳定源、Python 写 raw、Java 最小治理 + 1 套指数、**Spring Boot** + ECharts | 日任务成功率、指口径可说明、API P95 |
| 阶段 2 | 多源、Java 管理维表/规则、多指数、Redis、监控、指数重算入口 | 维表覆盖率、重算耗时、跨语言可观测 |
| 阶段 3 | 消息驱动、抓取/计算**水平扩展**、复杂地理层级、SLA | 扩展与容灾指标 |
---
## 10. 扩展预留
- 表设计早期包含:`source_id`、`config_version`、**时间维度** 便于分区。
- **表结构以 Java 侧迁移为权威**;若 Python 需 Pydantic 模型,**由 DDL/OpenAPI/手动同步**,避免两真相。
- Java **API 无状态**,配置与密钥走 Spring Profile / 环境变量/中心配置。
- Python 爬虫**无状态**、可水平扩展,仅依赖 DSN 与 `PYTHONPATH`/`pip install -e .`。
---
## 11. 文档维护
- 表结构、指数口径与**对外方法论文档**应与本文件同步更新(可拆子文档:`docs/指数字典与口径.md` 等)。
- **生产采集门禁**与运维基线见《[数据源上线清单](./数据源上线清单.md)》;变更授权或 SLA 时同步评审本文件第 7 节是否需修订。
- 重大架构变更时递增本文「版本」并记录变更摘要。
**版本摘要1.2**:增补对《数据源上线清单》的引用,明确上线环境持续采集的文档门禁。
---
## 12. 项目目录结构(与实现对齐)
**双栈单仓****Python 仅爬取****Java 为后台主工程**
```text
fao/
├── docs/ # 架构、口径、运维说明
├── server/ # **Java 主工程**Spring Boot 3 + Maven`pom.xml` 在根下)
│ # `mvn -f server/pom.xml spring-boot:run`;迁移:`src/main/resources/db/migration/`
├── src/
│ └── fao/ # **仅** Python 采集包pyproject 可 `pip install -e .`
│ ├── common/ # 爬虫子进程:配置、日志
│ ├── db/ # 仅向 raw/队列写入所需的最小数据访问
│ └── ingest/
│ └── adapters/ # 各数据源一模块
├── scripts/ # 爬虫/批抓入口(`python -m` 等),**不含**指计算
├── config/ # 爬虫用 `.env.example` 等
├── db/
│ └── migrations/ # **可选**:与 `server` 中 Flyway **二选一为权威**;建议以 `server/.../db/migration` 为准后此处留空或删
├── tests/
│ ├── unit/ # 建议拆server 内 surefire + Python pytest 各管各
│ └── integration/
├── web/ # 前端,对接 **Java API**
├── deploy/ # 含 `docker-compose`:服务 `app`(Java) + `crawl`(Python) 等
├── pyproject.toml # 仅 fao 爬虫包
├── setup.py
└── .gitignore
```
| 路径 | 放什么、不放什么 |
|------|------------------|
| `server/` | 全部 **HTTP、安全、域模型、批治理、指计算、迁移****不** 写爬虫。 |
| `src/fao/ingest` | 抓取、限频、**仅 raw/队列** 模型与写入;**禁止** 指算法与维表管理。 |
| `src/fao/db` | 爬虫用 DAO表结构**消费** Java Flyway 产出。 |
| `scripts` | 只调度 **Python 爬取****指数重算/治理** 入口放在 **Java** 或企业调度调 Java 接口。 |
| `config` | 以爬虫侧为主Java 用 `server``application-*.yml`。 |
| `web` | 管理端/大屏BFF 为 **Java**。 |
**可选**`e2e/` 联调;`ops/` 运维脚本。前端可拆独立仓库,**`server` 为后台边界**。
---
## 13. 语言边界与数据所有权
| 项目 | 约定 |
|------|------|
| **表结构** | **Java 工程迁移脚本** 为唯一权威Python 升级前 **拉取最新** DDL 或从文档生成模型。 |
| **raw 写入** | 仅 **Python**(或经审查的 ETL写到约定列**不** 写 clean/指数字段,除非团队明确开例外并文档化。 |
| **clean / 指标** | 仅 **Java**(及经同一鉴权的运维脚本)。 |
| **同一 Redis** | key 加前缀:`crawl:` / `app:` 等。 |
| **重算指数** | 管理端/定时器调 **Java** 内部服务或 `POST /internal/...`**内网 + 鉴权**)。 |
| **故障** | 爬虫挂 → raw 空;**Java 报表仍可展示**历史指数据;**治理/批算**在 Java 侧可告警「无新 raw」。 |
---
*本文档为蔬菜价格指数平台的技术架构说明,实现细节以各模块设计与数据库 DDL 为准。*