重构数据引擎
This commit is contained in:
363
docs/USAGE.md
Normal file
363
docs/USAGE.md
Normal file
@@ -0,0 +1,363 @@
|
||||
# Data Engine 通用数据同步引擎 - 使用文档
|
||||
|
||||
## 一、概述
|
||||
|
||||
本系统是一个**配置驱动的通用数据同步引擎**,核心思想是:**平台管理 + 接口管理**。
|
||||
|
||||
您只需要通过 API 维护好平台和接口的配置信息(认证方式、请求参数、响应解析、目标表结构),系统就会自动:
|
||||
|
||||
1. **创建目标表**(根据 `table_definition` 自动建表)
|
||||
2. **拉取数据**(分页请求、多步骤请求、并发处理)
|
||||
3. **写入数据库**(批量 upsert)
|
||||
4. **增量同步**(通过 `filtering` 按最后修改时间过滤)
|
||||
5. **自动调度**(按配置的时间间隔循环执行)
|
||||
6. **补偿重试**(失败的同步任务自动重试,退避递增)
|
||||
|
||||
**不需要为每个平台写一行业务代码。**
|
||||
|
||||
---
|
||||
|
||||
## 二、数据库初始化
|
||||
|
||||
### 2.1 核心表
|
||||
|
||||
首次使用需执行 `sql/init_core_tables.sql`,创建以下 4 张表:
|
||||
|
||||
| 表名 | 说明 |
|
||||
|------|------|
|
||||
| `api_datasource_platform` | 数据源平台配置(认证信息、限流等) |
|
||||
| `api_interface` | 接口配置(URL、请求参数、表结构等) |
|
||||
| `sync_task_log` | 同步任务日志(记录状态,用于补偿) |
|
||||
| `sync_tracker` | 同步跟踪(记录每个接口的最后同步时间) |
|
||||
|
||||
### 2.2 初始化数据
|
||||
|
||||
执行 `sql/seed_data.sql` 创建腾讯广告平台+接口配置。如需清空重来:
|
||||
|
||||
```sql
|
||||
ALTER SEQUENCE api_datasource_platform_id_seq RESTART WITH 1;
|
||||
ALTER SEQUENCE api_interface_id_seq RESTART WITH 1;
|
||||
DELETE FROM api_interface;
|
||||
DELETE FROM api_datasource_platform;
|
||||
\i sql/seed_data.sql
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 三、配置说明
|
||||
|
||||
### 3.1 平台管理 (`api_datasource_platform`)
|
||||
|
||||
| 字段 | 说明 | 示例 |
|
||||
|------|------|------|
|
||||
| `platform_code` | 平台编码(唯一) | `tencent` |
|
||||
| `platform_name` | 平台名称 | `腾讯广告` |
|
||||
| `api_base_url` | API 基础地址 | `https://api.e.qq.com/v3.0` |
|
||||
| `auth_type` | 认证类型 | `OAUTH2` / `TOKEN` / `API_KEY` / `BASIC` |
|
||||
| `token` | access_token | `xxxxx` |
|
||||
| `client_id` / `client_secret` | OAuth2 凭证 | `xxxxx` |
|
||||
| `auth_config` | 自定义认证配置(JSONB) | 详见 3.1.1 |
|
||||
|
||||
#### 3.1.1 `auth_config` 字段详解
|
||||
|
||||
```json
|
||||
{
|
||||
"token_in_query": true, // token 放在 URL 查询参数
|
||||
"query_key": "access_token", // 参数名,默认 "access_token"
|
||||
"header_name": "Authorization", // token 放请求头时的头名
|
||||
"header_format": "Bearer {token}",
|
||||
"extra_query_params": { // 额外查询参数
|
||||
"timestamp": "{timestamp}", // {timestamp} 自动替换为当前时间戳
|
||||
"nonce": "{nonce}" // {nonce} 自动替换为随机字符串
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2 接口管理 (`api_interface`)
|
||||
|
||||
| 字段 | 说明 | 示例 |
|
||||
|------|------|------|
|
||||
| `platform_id` | 所属平台 ID | `1` |
|
||||
| `name` / `code` | 接口名称 / 唯一编码 | `图片素材` / `image` |
|
||||
| `url` | 接口地址(相对路径) | `/images/get` |
|
||||
| `method` | 请求方法 | `GET` / `POST` |
|
||||
| `request_config` | 请求配置(JSONB) | 详见 3.2.1 |
|
||||
| `response_config` | 响应配置(JSONB) | 详见 3.2.2 |
|
||||
| `table_definition` | 表结构定义(JSONB) | 详见 3.3 |
|
||||
|
||||
#### 3.2.1 `request_config` 字段详解
|
||||
|
||||
```json
|
||||
{
|
||||
"parameters_location": "query", // 参数位置: "query"(URL) / "body"(默认)
|
||||
"page": 1,
|
||||
"page_size": 100,
|
||||
"page_param": "page", // 分页参数名(自定义)
|
||||
"page_size_param": "page_size",
|
||||
"time_field": "last_modified_time", // 增量时间字段
|
||||
"fields": ["field1", "field2"], // 请求字段(如音频的 fields)
|
||||
"prefetch": { ... } // 预取配置(见下文)
|
||||
}
|
||||
```
|
||||
|
||||
**`parameters_location` 说明**:
|
||||
- 未设置或 `"body"` → 参数放在 JSON body 中(POST 请求)
|
||||
- `"query"` → 参数放在 URL 查询字符串中(GET 请求用)
|
||||
- 当 `method=GET` 时,即使不设置也默认走 query
|
||||
|
||||
**预取(prefetch)**:某些接口需要"先拿列表→遍历每个元素查数据"(如先拉账户列表,再遍历拉图片)。
|
||||
|
||||
```json
|
||||
"prefetch": {
|
||||
"url": "/advertiser/get", // 预取接口地址
|
||||
"method": "GET",
|
||||
"response_path": "data.list", // 从响应中取值路径
|
||||
"target_param": "account_id", // 注入主请求的参数名
|
||||
"value_field": "account_id" // 从预取结果取哪个字段
|
||||
}
|
||||
```
|
||||
|
||||
**并发处理**:有 prefetch 的接口会并发处理每个实体,并发数由 `config.yml` 的 `sync.concurrency` 控制。
|
||||
|
||||
**增量同步**:配置了 `time_field` 的接口,增量同步时会自动生成 `filtering` 参数:
|
||||
```json
|
||||
{"field": "last_modified_time", "operator": "GREATER_EQUALS", "values": ["<timestamp>"]}
|
||||
```
|
||||
|
||||
#### 3.2.2 `response_config` 字段详解
|
||||
|
||||
系统默认解析的响应格式:
|
||||
|
||||
```json
|
||||
{ "code": 0, "message": "success", "data": { "list": [...], "page_info": { "total_page": N } } }
|
||||
```
|
||||
|
||||
可通过 `response_config` 自定义数据路径:
|
||||
```json
|
||||
{ "list_path": "data.list" }
|
||||
```
|
||||
|
||||
### 3.3 `table_definition` 字段详解
|
||||
|
||||
```json
|
||||
{
|
||||
"table_name": "tencent_image",
|
||||
"columns": [
|
||||
{ "name": "image_id", "type": "VARCHAR(100)", "comment": "图片ID" },
|
||||
{ "name": "account_id", "type": "BIGINT", "comment": "账户ID" }
|
||||
],
|
||||
"conflict_keys": ["image_id", "account_id"]
|
||||
}
|
||||
```
|
||||
|
||||
**自动添加的列**(无需声明):
|
||||
|
||||
| 列名 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `id` | BIGSERIAL PRIMARY KEY | 自增主键 |
|
||||
| `tenant_id` | BIGINT | 租户 ID |
|
||||
| `creator` | VARCHAR(64) | 创建人 |
|
||||
| `created_at` | TIMESTAMPTZ | 创建时间 |
|
||||
| `updater` | VARCHAR(64) | 更新人 |
|
||||
| `updated_at` | TIMESTAMPTZ | 更新时间 |
|
||||
| `deleted_at` | TIMESTAMPTZ | 软删除 |
|
||||
| `raw_data` | JSONB | 原始响应数据 |
|
||||
|
||||
---
|
||||
|
||||
## 四、API 接口
|
||||
|
||||
基础地址:`http://localhost:3002`
|
||||
|
||||
### 4.1 平台管理
|
||||
|
||||
| 方法 | 路径 | 说明 |
|
||||
|------|------|------|
|
||||
| POST | `/api/datasourcePlatform/createDatasourcePlatform` | 创建平台 |
|
||||
| GET | `/api/datasourcePlatform/listDatasourcePlatforms` | 列表 |
|
||||
| GET | `/api/datasourcePlatform/getDatasourcePlatform` | 详情 |
|
||||
| GET | `/api/datasourcePlatform/getPlatformByCode` | 按编码查 |
|
||||
| PUT | `/api/datasourcePlatform/updateDatasourcePlatform` | 更新 |
|
||||
| PUT | `/api/datasourcePlatform/updateDatasourcePlatformStatus` | 更新状态 |
|
||||
| DELETE | `/api/datasourcePlatform/deleteDatasourcePlatform` | 删除 |
|
||||
| GET | `/api/datasourcePlatform/getPlatformStatistics` | 统计 |
|
||||
|
||||
### 4.2 接口管理
|
||||
|
||||
| 方法 | 路径 | 说明 |
|
||||
|------|------|------|
|
||||
| POST | `/api/apiInterface/createApiInterface` | 创建接口 |
|
||||
| GET | `/api/apiInterface/listApiInterfaces` | 列表 |
|
||||
| GET | `/api/apiInterface/getApiInterface` | 详情 |
|
||||
| PUT | `/api/apiInterface/updateApiInterface` | 更新 |
|
||||
| PUT | `/api/apiInterface/updateApiInterfaceStatus` | 更新状态 |
|
||||
| DELETE | `/api/apiInterface/deleteApiInterface` | 删除 |
|
||||
|
||||
### 4.3 同步控制
|
||||
|
||||
| 方法 | 路径 | 说明 |
|
||||
|------|------|------|
|
||||
| POST | `/api/sync/ctrl/trigger` | 触发同步 |
|
||||
| GET | `/api/sync/ctrl/config` | 查询配置 |
|
||||
|
||||
**触发同步示例:**
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:3002/api/sync/ctrl/trigger \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"platformCode":"tencent","interfaceCode":"image","fullSync":true}'
|
||||
```
|
||||
|
||||
响应:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"tableName": "tencent_image",
|
||||
"totalRows": 1500,
|
||||
"insertedRows": 1450,
|
||||
"duration": "12.3s"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 五、配置文件 (`config.yml`)
|
||||
|
||||
```yaml
|
||||
sync:
|
||||
page_size: 100 # 每次分页请求条数
|
||||
concurrency: 5 # 并发处理数(prefetch 遍历实体时)
|
||||
retry_count: 3 # 最大重试次数
|
||||
sync_interval_minutes: 60 # 自动同步间隔(分钟)
|
||||
compensation_interval_seconds: 300 # 补偿扫描间隔(秒)
|
||||
auto_sync_enabled: true # 是否启用自动同步
|
||||
|
||||
tencent:
|
||||
oauth:
|
||||
client_id: "1112038234"
|
||||
client_secret: "GxyjXFbZAs5dnsNQ"
|
||||
access_token: "4bacfc7c9b0a31f70ec0eb4771f8b542"
|
||||
refresh_token: "d15b37363a42449026d337708516e95e"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 六、自动同步机制
|
||||
|
||||
### 6.1 启动流程
|
||||
|
||||
1. 服务启动 → `InitAndStartAutoSync` 在 goroutine 中启动调度器
|
||||
2. 自动扫描所有 ACTIVE 平台下有 `table_definition` 的接口
|
||||
3. 无 `sync_tracker` 记录 → 全量拉取;有记录 → 增量拉取
|
||||
|
||||
### 6.2 增量同步原理
|
||||
|
||||
- `sync_tracker` 表记录每个接口的最后同步时间
|
||||
- 配置了 `time_field` 的接口,增量时生成 `filtering=[{"field":"last_modified_time","operator":"GREATER_EQUALS","values":["<时间戳>"]}]`
|
||||
- 不支持时间过滤的接口(如 audio/advertiser)每次全量,`ON CONFLICT` 去重
|
||||
|
||||
### 6.3 异常中断恢复
|
||||
|
||||
- 同步开始前写 `sync_tracker.sync_status='running'`
|
||||
- 同步完成后写 `'success'`
|
||||
- 重启检测到 `'running'` → 日志告警 → 重新全量
|
||||
|
||||
---
|
||||
|
||||
## 七、补偿机制
|
||||
|
||||
### 7.1 工作原理
|
||||
|
||||
1. 同步失败 → 自动写入 `sync_task_log`(status=failed)
|
||||
2. 补偿调度器(随主服务自动启动)每 N 秒扫描 failed 记录
|
||||
3. 对未达最大重试次数的任务,调用 `SyncByConfig` 重试
|
||||
4. 重试间隔按退避策略递增:5min → 15min → 30min → 60min → 120min
|
||||
5. 达最大次数 → 标记 `manual_review`,等待人工介入
|
||||
|
||||
### 7.2 配置
|
||||
|
||||
```yaml
|
||||
sync:
|
||||
compensation_interval_seconds: 300 # 扫描间隔
|
||||
retry_count: 3 # 最大重试次数
|
||||
```
|
||||
|
||||
补偿调度器随主服务自动启动,无需手动运行。
|
||||
|
||||
---
|
||||
|
||||
## 八、当前已配置接口(腾讯广告)
|
||||
|
||||
| 接口编码 | 名称 | 方法+路径 | 类型 | 增量 |
|
||||
|------|------|------|------|------|
|
||||
| `account_relation` | 账户列表 | `GET /advertiser/get` | 单接口分页 | 不支持 |
|
||||
| `image` | 图片素材 | `GET /images/get` | prefetch 遍历账户 | ✅ `last_modified_time` |
|
||||
| `video` | 视频素材 | `GET /videos/get` | prefetch 遍历账户 | ✅ `last_modified_time` |
|
||||
| `audio` | 音频素材 | `POST /muse_audios/get` | 单接口 POST | 不支持 |
|
||||
|
||||
三个素材表自动包含 `verify_status DEFAULT 'PENDING'`、`verified_at`、`verified_by` 校验字段。
|
||||
|
||||
### 图片/视频同步流程
|
||||
|
||||
```
|
||||
SyncByConfig("tencent", "image")
|
||||
├── ① 预取: 分页拉取 /advertiser/get → 774 个 account_id
|
||||
│ 数据同时存入 tencent_account_relation 表
|
||||
├── ② 并发遍历账户(config.yml concurrency=5)
|
||||
│ 每个 account_id → GET /images/get?account_id=xxx&page=1&page_size=100
|
||||
│ 自动补充 filtering(增量时)
|
||||
│ 结果 upsert 到 tencent_image
|
||||
└── ③ 更新 sync_tracker 记录同步时间
|
||||
```
|
||||
|
||||
### 音频同步流程
|
||||
|
||||
```
|
||||
单次 POST /muse_audios/get → 分页拉全量 → upsert 到 tencent_audio
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 九、快速开始
|
||||
|
||||
```bash
|
||||
# 1. 建表
|
||||
psql -h localhost -U postgres -d data-engine -f sql/init_core_tables.sql
|
||||
|
||||
# 2. 初始化数据
|
||||
psql -h localhost -U postgres -d data-engine -f sql/seed_data.sql
|
||||
|
||||
# 3. 启动
|
||||
go run main.go
|
||||
```
|
||||
|
||||
启动后自动同步和补偿调度器自动运行。也可手动触发:
|
||||
|
||||
```bash
|
||||
# 触发图片全量同步
|
||||
curl -X POST http://localhost:3002/api/sync/ctrl/trigger \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"platformCode":"tencent","interfaceCode":"image","fullSync":true}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 十、常见问题
|
||||
|
||||
**Q: 响应格式不符合 `{code: 0, data: {list: [...]}}` 怎么办?**
|
||||
|
||||
修改 `dynamic_sync.go` 的 `parseResp` 函数。
|
||||
|
||||
**Q: 如何添加新平台?**
|
||||
|
||||
调用平台管理 API 创建平台 → 调用接口管理 API 创建接口(带 `table_definition`)→ 系统自动建表并同步。
|
||||
|
||||
**Q: prefetch 的响应格式要求?**
|
||||
|
||||
必须是 JSON,`response_path` 指向一个数组。如 `response_path: "data.list"` 从 `data.list` 取值。
|
||||
|
||||
**Q: 如何排查同步失败?**
|
||||
|
||||
1. `GET /api/sync/ctrl/config?platformCode=xxx` 查看配置
|
||||
2. 查询 `sync_task_log` 表看失败记录
|
||||
3. 补偿调度器会自动重试,日志会打印重试过程
|
||||
Reference in New Issue
Block a user