# dabot_api
**Repository Path**: shawmon/dabot_api
## Basic Information
- **Project Name**: dabot_api
- **Description**: No description available
- **Primary Language**: Unknown
- **License**: GPL-3.0
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2026-08-07
- **Last Updated**: 2026-08-07
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# 数博数据采集平台(DaBOT API)
以**表名作为参数**的统一数据接入网关:外部系统通过 `app_key + app_secret` 认证后,向任意已注册的逻辑表提交**单条或批量**数据;平台对每条数据执行**字段级校验**(动态 Pydantic 模型)与**业务级校验**(插件式规则引擎),全部通过的数据写入对应物理表,未通过的按记录索引返回精确错误信息。
## 1. 技术栈
| 层次 | 选型 |
|------|------|
| Web 框架 | FastAPI |
| 数据校验 | Pydantic v2(运行时动态建模)|
| 数据库 | PostgreSQL(SQLAlchemy 2.0 ORM + Core)|
| 迁移 | Alembic |
| 缓存 / 限流 | Redis |
| 异步队列 | Celery(Redis 作 broker/backend)|
| 表达式求值 | simpleeval(安全沙箱)|
| 密钥存储 | PBKDF2-HMAC-SHA256(全局盐 + 每用户独立盐)|
## 2. 系统架构
```mermaid
flowchart LR
Client[外部系统] -->|app_key + app_secret| GW[FastAPI 网关]
subgraph MW[中间件链]
RL[限流
Redis 固定窗口] --> IPW[IP 白名单] --> LOG[访问日志]
end
GW --> MW
MW --> AUTH[用户认证
sys_user 校验]
AUTH --> VS[ValidationService]
subgraph VS[校验引擎]
FM[动态 Pydantic 模型
字段级校验] -->|字段级通过| RE[规则引擎
业务级校验]
end
META[(meta_table /
meta_field_def)] -.->|Redis 缓存| FM
RULE[(rule_validation)] -.->|Redis 缓存| RE
CODE[(meta_code)] -.->|Redis 缓存| FM
VS -->|通过| DBW[动态 Insert
写入目标物理表]
VS -->|失败| ERR[按索引返回错误
field_errors / business_errors 分离]
VS --> TRACE[(validation_trace
逐条校验轨迹)]
GW -->|批量超阈值 / 强制异步| CELERY[Celery Worker]
CELERY --> VS
CELERY --> TASK[(import_task
任务状态)]
```
### 目录结构
```
data-validation-platform/
├── app/
│ ├── main.py # 应用入口:中间件、异常处理、路由
│ ├── config.py # 配置(环境变量驱动)
│ ├── database.py # SQLAlchemy 引擎/会话/Base
│ ├── redis_client.py # Redis 客户端与缓存读写(故障自动降级)
│ ├── celery_app.py # Celery 应用
│ ├── core/
│ │ ├── errors.py # 6 位分段错误码体系 + AppException
│ │ ├── security.py # 密钥加盐哈希/校验
│ │ └── model_factory.py # 动态 Pydantic 模型工厂(带编译缓存)
│ ├── models/ # sys_user / meta_table / meta_field_def /
│ │ # meta_code / rule_validation / import_task / validation_trace
│ ├── schemas/common.py # 统一响应模型(字段/业务错误分离)
│ ├── validators/
│ │ ├── base.py # BaseValidator + ValidationContext
│ │ ├── registry.py # @register 注册器
│ │ └── builtin.py # 8 个内置业务校验器
│ ├── services/
│ │ ├── meta_service.py # 元数据/规则/代码加载(Redis 缓存 + 热更新)
│ │ └── validation_service.py# 校验主流程 + 动态入库 + 轨迹落库
│ ├── api/
│ │ ├── deps.py # app_key/app_secret 认证依赖
│ │ └── v1/
│ │ ├── submit.py # 单条/批量提交、任务查询
│ │ ├── rules.py # 规则配置 CRUD + 缓存刷新 + 校验器清单
│ │ └── meta.py # 表定义查询、代码表 CRUD
│ ├── middleware/ # 限流 / IP 白名单 / 访问日志
│ └── tasks/import_tasks.py # Celery 批量导入任务
├── alembic/ # 迁移环境 + 0001 初始版本
├── scripts/seed.py # 演示数据(幂等)
├── docker-compose.yml # postgres + redis + api + worker 一键启动
├── Dockerfile / alembic.ini / requirements.txt / .env.example
```
## 3. 数据流(一次批量提交)
1. 请求经过中间件链:限流 → IP 白名单 → 访问日志;
2. `deps.get_current_user` 校验用户:**存在、有效(status=1)、密钥哈希匹配**,任一失败返回 700001/700002/700003;
3. `meta_service` 从 Redis(未命中则查库并回填)加载**表定义**,不存在返回 200002、停用返回 200003;
4. `model_factory` 按字段定义**动态构建 Pydantic 模型**(按定义快照 LRU 缓存),对每条记录做字段级校验;
5. **字段级失败的记录跳过业务级校验**;通过的记录进入规则引擎,按 `priority` 顺序执行该用户(含全局 `*`)的全部启用规则;
6. 每条记录的结果独立:字段错误进 `field_errors`,业务错误进 `business_errors`,两者完全分离;
7. 全部通过的记录由 SQLAlchemy Core **动态构建的 Table 对象**批量 insert 到目标物理表(列名来自元数据,杜绝注入);
8. 每条记录的校验轨迹(耗时、错误明细)写入 `validation_trace`,独立事务,失败不影响主流程;
9. 批量条数超过 `BATCH_SYNC_THRESHOLD` 或 `force_async=true` 时,转入 Celery 任务,返回 `task_id`,通过 `GET /api/v1/tasks/{task_id}` 查询进度与逐条结果。
## 4. 统一错误码(6 位分段)
> 分段规则:第 1 位 = 大类,2-3 位 = 子模块,4-6 位 = 序号。
| 错误码 | 含义 | 错误码 | 含义 |
|--------|------|--------|------|
| **1xxxxx 系统** | | **4xxxxx 业务** | |
| 100001 | 系统内部错误 | 400001 | 条件必填校验失败 |
| 100002 | 缓存服务不可用 | 400002 | 数值范围校验失败 |
| 100003 | 任务队列不可用 | 400003 | 正则匹配校验失败 |
| **2xxxxx 请求** | | 400004 | 跨字段比较校验失败 |
| 200001 | 请求参数错误 | 400005 | 自定义表达式校验失败 |
| 200002 | 表不存在 | 400006 | 允许值列表校验失败 |
| 200003 | 表已停用 | 400007 | 日期范围校验失败 |
| 200004 | 请求体不能为空 | 400008 | 唯一性校验失败 |
| **3xxxxx 字段** | | **5xxxxx 批量** | |
| 300001 | 必填字段缺失 | 500001 | 批量大小超过上限 |
| 300002 | 字段类型错误 | 500002 | 批量数据为空 |
| 300003 | 字段长度超限 | 500003 | 任务不存在 |
| 300004 | 代码值非法 | 500004 | 任务状态异常 |
| 300005 | 字段格式错误 | **6xxxxx 数据库** | |
| **7xxxxx 权限** | | 600001 | 数据库连接失败 |
| 700001 | 用户不存在 | 600002 | 数据唯一约束冲突 |
| 700002 | 用户已禁用 | 600003 | 数据写入失败 |
| 700003 | 密钥校验失败 | | |
| 700004 | IP 不在白名单 | | |
| 700005 | 请求过于频繁,已限流 | | |
## 5. 表设计
| 表 | 用途 | 关键字段 |
|----|------|----------|
| `sys_user` | 接入方身份 | `app_key`、`app_secret_hash`(加盐哈希)、`salt`、`status` |
| `sys_meta_table` | 逻辑表定义 | `table_name`(接口参数)→ `target_table`(物理表)、`status` |
| `sys_meta_field` | 字段定义 | `data_type`、`max_length`、`nullable`、`code_category`、`regex_pattern` |
| `sys_meta_code` | 代码表 | `category`、`code_value`、`code_label` |
| `sys_validation_rule` | 校验规则 | `user_code`(`*`=全局)、`table_name`、`field_name`、`rule_type`、`rule_params`(JSONB)、`priority` |
| `sys_task` | 异步任务 | `task_id`、`status`、`result`(JSONB 逐条结果) |
| `sys_validation_trace` | 校验轨迹 | `request_id`、`record_index`、`field_errors`、`business_errors`、`duration_ms` |
支持的字段类型:`string` / `integer` / `decimal` / `boolean` / `date` / `datetime`。
## 6. 两级校验
### 字段级(动态 Pydantic 模型,自动完成)
| 元数据配置 | 校验行为 | 错误码 |
|-----------|---------|--------|
| `nullable=false` | 必填 | 300001 |
| `data_type` | 类型转换与校验 | 300002 |
| `max_length` | 字符串长度 | 300003 |
| `code_category` | 枚举值域(查代码表) | 300004 |
| `regex_pattern` | 正则格式 | 300005 |
### 业务级(8 个内置插件校验器)
| rule_type | 说明 | 关键参数 | 错误码 |
|-----------|------|----------|--------|
| `conditional_required` | 条件必填 | `when_field`, `when_value(s)` | 400001 |
| `number_range` | 数值范围 | `min`, `max` | 400002 |
| `regex_match` | 正则匹配 | `pattern` | 400003 |
| `cross_field_compare` | 跨字段比较 | `other_field`, `operator`(eq/ne/gt/gte/lt/lte) | 400004 |
| `expression` | 自定义表达式 | `expression`(变量=字段名与 `value`) | 400005 |
| `allowed_values` | 允许值列表 | `values` | 400006 |
| `date_range` | 日期范围 | `min_date`, `max_date`(支持 `today`) | 400007 |
| `unique` | 唯一性(查目标表) | `scope_fields`(联合唯一,可空) | 400008 |
### 扩展新校验器(两步)
```python
# app/validators/my_validator.py
from app.validators import BaseValidator, register
@register
class MyValidator(BaseValidator):
rule_type = "my_rule"
description = "我的自定义校验"
error_code = "400100"
params_schema = {"threshold": "int"}
def validate(self, value, record, params, ctx):
if bad(value):
return "错误提示"
return None
```
在 `app/validators/__init__.py` 中 import 该模块即完成注册,随后即可在规则配置接口中按 `rule_type="my_rule"` 配置使用。
## 7. API 一览
认证方式:所有业务接口需携带请求头 `X-App-Key` / `X-App-Secret`。
| 方法 | 路径 | 说明 |
|------|------|------|
| POST | `/api/v1/submit/{table_name}` | 单条提交 `{"data": {...}}` |
| POST | `/api/v1/submit/{table_name}/batch` | 批量提交 `{"data": [...], "force_async": false}` |
| GET | `/api/v1/tasks/{task_id}` | 异步任务状态与逐条结果 |
| GET | `/api/v1/rules/validators` | 可用校验器清单(含参数说明) |
| GET/POST | `/api/v1/rules` | 规则查询 / 新建 |
| PUT/DELETE | `/api/v1/rules/{id}` | 规则更新 / 删除(自动热更新缓存) |
| POST | `/api/v1/rules/cache/refresh?table_name=xx` | 手动刷新缓存 |
| GET | `/api/v1/meta/tables` | 已注册表及字段定义 |
| GET/POST/DELETE | `/api/v1/meta/codes...` | 代码表查询 / 新增 / 删除 |
### 调用示例
* linux
```shell
# 单条
curl -X POST http://localhost:8000/api/v1/submit/customer_info \
-H "X-App-Key: demo-key" -H "X-App-Secret: demo-secret" \
-H "Content-Type: application/json" \
-d '{"data": {"name": "张三", "gender": "M", "age": 30, "email": "zs@example.com", "birth_date": "1995-01-01", "amount": 100.5}}'
```
```shell
# 批量(逐条独立校验,按索引返回错误)
curl -X POST http://localhost:8000/api/v1/submit/customer_info/batch \
-H "X-App-Key: demo-key" -H "X-App-Secret: demo-secret" \
-H "Content-Type: application/json" \
-d '{"data": [
{"name": "张三", "gender": "M", "age": 30, "email": "a@x.com", "amount": 100},
{"name": "", "gender": "X", "age": 200}
]}'
```
* windows
```
```
批量响应示例(第 2 条同时命中字段级与业务级错误,完全分离):
```json
{
"code": "000000", "message": "success",
"data": {
"table_name": "customer_info", "total": 2, "success_count": 1, "failed_count": 1, "inserted_count": 1,
"results": [
{"index": 0, "success": true, "field_errors": [], "business_errors": []},
{"index": 1, "success": false,
"field_errors": [{"field": "name", "code": "300001", "message": "必填字段缺失"},
{"field": "gender", "code": "300004", "message": "代码值非法,允许值: ['F', 'M', 'U']"}],
"business_errors": []}
]
}
}
```
## 8. 快速开始
```bash
cp .env.example .env
docker compose up --build
# API: http://localhost:8000/docs
# 演示账号:X-App-Key: demo-key / X-App-Secret: demo-secret
```
本地开发:
```bash
pip install -r requirements.txt
alembic upgrade head
# python scripts/seed.py
python -m scripts.seed
uvicorn app.main:app --reload
celery -A app.celery_app.celery_app worker --loglevel=info
```
数据库版本管理:
```bash
alembic revision --autogenerate -m "add xxx" # 基于模型自动生成迁移
alembic upgrade head # 升级
alembic downgrade -1 # 回滚一个版本
```
## 9. 关键设计说明
- **字段/业务错误分离**:`RecordResult` 中 `field_errors` 与 `business_errors` 为两个独立数组;字段级失败的记录不再执行业务规则,避免基于脏数据的误判。
- **缓存热更新**:表元数据、规则、代码字典分别按 `meta:table:*`、`rule:{user}:{table}`、`code:{category}` 缓存;规则/代码的 CRUD 接口写库后主动失效对应 key,等效热更新;Redis 故障时自动降级直查数据库。
- **入库安全**:目标表名与列名仅来源于管理员维护的元数据,通过 SQLAlchemy Core `Table` 对象参数化写入,无 SQL 拼接注入面。
- **批量策略**:`BATCH_SYNC_THRESHOLD`(默认 200)以内同步返回完整结果;超过则转 Celery,结果(含逐条明细,最多保留 1000 条)落 `import_task.result`。
- **表达式安全**:自定义表达式经 simpleeval 沙箱求值,仅可访问当前记录字段与 `value`,无内建函数与 IO 能力。
- **轨迹审计**:每条记录的校验结果、耗时随 `request_id` 落 `validation_trace`,同时输出结构化访问日志。