重构数据引擎和报表引擎

This commit is contained in:
2026-06-11 13:06:54 +08:00
parent 285a0fc632
commit 419473f266
53 changed files with 8434 additions and 375 deletions

View File

@@ -73,11 +73,15 @@ auth_config: { "app_key": "...", "app_secret": "..." }
| `cursor_pagination` | bool | 是否游标分页true=游标false/无=普通分页) | 通用 |
| `time_field` | string | 增量时间字段名 | 通用 |
| `time_field_mode` | string | `"range"`=beginTime/endTime模式快手`"filtering"`=filtering数组模式腾讯默认 | 通用 |
| `full_sync_start_time` | int | 全量同步时的时间起点,毫秒时间戳。`lastSyncTime=0` 时优先读取此值不存在则回退90天前range或不传时间过滤filtering | 通用 |
| `prefetch` | object | 预取配置(见下方) | 需遍历实体的接口 |
| `recursive` | object | 递归遍历配置(见下方) | 树形结构接口(如钉钉部门) |
| `max_recursive_depth` | int | 递归最大深度默认20 | 递归遍历使用 |
| `body_wrapper_field` | string | 业务参数包装字段名,如`"param"` | 快手 |
| `exclude_from_wrapper` | string[] | body_wrapper 时不包装的字段 | 快手 |
| `top_level_params` | string[] | 保留在顶层的字段(备用) | 通用 |
| `fields` | string[] | API 的 fields 参数 | 腾讯 |
| `row_inject` | string[] | 将请求参数值注入到每行数据中(如 salaryGroupName解决响应不含某请求参数但需存储的场景 | 通用prefetch 和非 prefetch 均支持) |
### prefetch 预取配置
```json
@@ -198,6 +202,15 @@ WARN 接口 [kuaishou/order_list] 正在同步中,跳过重复请求
**调度器时序**:使用 `for { runAutoSync(); time.Sleep(interval) }` 模式,而非 `time.NewTicker`。每次同步**完全结束后**才开始计时 interval避免前一次未跑完就启动下一次导致重叠。无论全量跑 30 分钟还是 70 分钟,下一次都在「本次完成时间 + interval」后执行。
## 三种遍历模式对比
| 模式 | 配置 | 适用场景 | 流程 |
|------|------|---------|------|
| **普通分页** | `page_param` + `page_size_param` | 列表接口分页 | 请求第1页 → 读 `page_info.total_page` → 遍历后续页 |
| **游标分页** | `cursor_pagination: true` | 实时滚动列表(快手订单) | 首次 `cursor=""` → 响应返回 `cursor` → 直到 `""``"nomore"` |
| **prefetch 预取** | `prefetch: {url, ...}` | 先取实体列表,再查详情 | 分页拉取实体来源列表 → 提取全部 ID → 并发查详情 |
| **recursive 递归** | `recursive: {key_field, target_param}` | 树形结构(钉钉部门) | BFS 队列:根级调用 → 提取子ID → 逐级递归下级 → 防重复 |
## 新增平台 + 接口的操作步骤
### 操作步骤
@@ -461,6 +474,69 @@ INSERT INTO api_interface (
```
注意POST 方法且无 `parameters_location: "query"` 时,参数以 JSON body 形式发送。
#### 模板 EOAuth2 + POST + 无分页(钉钉风格)
```sql
INSERT INTO api_datasource_platform (
tenant_id, creator, created_at, updater, updated_at,
platform_code, platform_name, description, status,
api_base_url, auth_type,
auth_config,
rate_limit_per_minute, rate_limit_per_hour,
concurrency_limit, request_timeout_ms, max_retries, retry_delay_ms
) VALUES (
1, 'admin', NOW(), 'admin', NOW(),
'dingtalk', '钉钉', '钉钉开放平台数据同步', 'ACTIVE',
'https://oapi.dingtalk.com', 'OAUTH2',
'{
"token_in_query": true,
"query_key": "access_token"
}'::jsonb,
60, 3600, 5, 30000, 3, 1000
);
INSERT INTO api_interface (
tenant_id, creator, created_at, updater, updated_at,
platform_id, name, code, url, method, status, auth_type,
request_config, response_config, table_definition
) VALUES (
1, 'admin', NOW(), 'admin', NOW(),
(SELECT id FROM api_datasource_platform WHERE platform_code = 'dingtalk'),
'{接口名称}', '{接口编码}',
'{相对路径}', 'POST', 'active', 'inherit',
'{
"parameters_location": "query",
"page_param": "cursor",
"page_size_param": "pageSize",
"{业务参数}": {值}
}'::jsonb,
'{
"success_field": "errcode",
"success_value": 0,
"message_field": "errmsg",
"list_path": "result"
}'::jsonb,
'{
"table_name": "{表名}",
"columns": [
{"name": "id", "type": "BIGINT", "comment": "ID"},
{"name": "name", "type": "VARCHAR(300)", "comment": "名称"}
],
"conflict_keys": ["id"]
}'::jsonb
);
```
注意:钉钉使用 `access_token` 作为 URL 查询参数,通过 `token_in_query: true` + `query_key: "access_token"` 配置。POST 请求通过 `parameters_location: "query"` 将所有参数放在 URL 查询字符串中DingTalk 同时支持 query 和 body 传参)。
如需递归遍历树形结构(如部门全量),在 `request_config` 中添加:
```json
"recursive": {
"key_field": "dept_id", // 从响应行中提取哪个字段的值用于递归
"target_param": "dept_id" // 递归参数注入到请求中的参数名
},
"max_recursive_depth": 20 // 可选最大递归深度默认20
```
递归流程BFS 遍历,先查根级空参 → 提取每行的 `key_field` → 逐级注入 `target_param` 查询下级 → 防重复循环保护 → 所有结果合并入库。
## 新增接口步骤总结
1. **确定平台**已有平台直接跳到第3步否则先新增平台参考对应认证类型的模板
@@ -474,6 +550,67 @@ INSERT INTO api_interface (
- 是否需要遍历实体prefetch
- 增量时间字段(字段名 + 值类型)
4. **选择模板**并填入对应值
> ⚠️ **全量同步起始时间**:如果接口数据量很大,首次全量不想从 90 天前拉取,可以在 `request_config` 中加 `"full_sync_start_time": <毫秒时间戳>` 指定起点。例如只同步最近 30 天:
> ```json
> "full_sync_start_time": 1745942400000
> ```
> - **range 模式**(快手 `time_field_mode: "range"`):全量时取此时间作为 beginTime
> - **filtering 模式**(腾讯 `time_field_mode: "filtering"`):全量时生成 `GREATER_EQUALS` 过滤条件
> - **不配此字段**行为不变range 回退 90 天filtering 不传时间过滤)
5. **执行 seed SQL**
6. **在管理后台验证**`http://{host}:3002/admin`
## 代码审计发现的项目规范
### 包命名规范2026-06-03 审计)
- `model/dto/dict/` 下的 `.go` 文件必须用 `package dict`(曾错误写为 `package api_feature`,已修复)
- `model/entity/dict/` 下的 `.go` 文件用 `package dict`
- `consts/api-feature/` 目录下的文件用 `package api_feature`
- `consts/public/``package public`
- **不要**在 `consts/dict/` 中重复定义 `PlatformStatus`/`ApiMethod` 等类型,应使用 `consts/api-feature` 中的定义
### 数据库状态值规范
- `api_datasource_platform.status`:使用大写 `"ACTIVE"` / `"INACTIVE"`
- `api_interface.status`:使用小写 `"active"` / `"inactive"`
- 代码中查平台状态时不要使用 `api_feature.PlatformStatusActive`(其值为小写),应直接用 `"ACTIVE"` 字符串
### Known Bad Practices已知待改进项
- 敏感信息access_token / app_secret仍分散在 SQL seed文件和 config.yml 中,建议迁移到环境变量或 Vault
- `consts/dict/consts.go``scheduler/run_sync_task_log_task.go` 可能随业务扩展需要更新
## 通用报表引擎 (common/report)
2026-06-10: 通用报表公共包,配置驱动的报表子系统,支持前端自由选择维度/指标/筛选/时间查询。
### 架构分层 (9个Go文件)
```
common/report/
├── api.go # ReportService 统一门面(单例)
├── report.go # 5张系统表 DDL
├── example_usage.go # 完整使用示例文档
├── model/model.go # 实体/请求/响应/常量
├── config/loader.go # 配置加载器(读缓存+CRUD
├── builder/sql_builder.go # 动态 SQL 构建
├── executor/executor.go # 查询执行器
├── extract/extract.go # 天级数据抽取DIRECT/AGGREGATE
└── ddlsync/creator.go # 统计宽表自动创建
```
### 核心能力
1. **自动建表**:根据 FieldConfig 自动 CREATE TABLE stat_xxx
2. **数据抽取**DIRECT逐行/ AGGREGATE聚合 SUM/COUNT模式幂等保证
3. **动态查询**:前端选维度+指标+筛选+时间 → 实时构建 SQL → 分页返回
4. **配置 CRUD**2026-06-10 新增BusinessConfig/ReportConfig/FieldConfig/ExtractConfig 全部可前端维护
### 对外接口 (ReportService)
- 查询:`QueryReportByUserSelect` / `GetReportFields` / `GetAllBusinesses` / `GetAllReports`
- 抽取:`ExtractDailyData` / `AutoCreateStatTable`
- CRUD`SaveBusiness/DeleteBusiness/GetBusiness``SaveReport/DeleteReport/GetReport``SaveField/DeleteField/GetField``SaveExtractConfig/DeleteExtractConfig/GetExtractConfig/GetExtractConfigs`
### 设计原则
- 零硬编码:任意平台/接口通过配置接入,不改代码
- 配置驱动5张 PG 表存储全部配置CRUD API 支持前端维护
- 单例模式:`report.GetService()` 可在任意业务服务中直接调用