This commit is contained in:
103
docs/unified-export-task-system/功能总结.md
Normal file
103
docs/unified-export-task-system/功能总结.md
Normal file
@@ -0,0 +1,103 @@
|
||||
# 统一导出任务功能总结
|
||||
|
||||
## 1. 功能概览
|
||||
|
||||
本次交付新增统一导出任务系统,提供全局任务入口,支持:
|
||||
|
||||
- 创建导出任务(`scene=device/iot_card`,`format=xlsx/csv`)
|
||||
- 导出任务列表与详情
|
||||
- 导出任务取消(待处理/处理中)
|
||||
- 异步三段式执行:`dispatch -> shard -> finalize`
|
||||
- 分片产物与最终产物上传 OSS
|
||||
- 详情接口直出下载链接(固定 24 小时)
|
||||
|
||||
## 2. 新增接口
|
||||
|
||||
- `POST /api/admin/export-tasks`:创建导出任务
|
||||
- `GET /api/admin/export-tasks`:导出任务列表(支持 `scene/status/time` 过滤)
|
||||
- `GET /api/admin/export-tasks/:id`:导出任务详情(已完成时返回 `download_url`)
|
||||
- `POST /api/admin/export-tasks/:id/cancel`:取消导出任务
|
||||
|
||||
## 3. 状态机
|
||||
|
||||
### 3.1 主任务状态
|
||||
|
||||
- `1` 待处理
|
||||
- `2` 处理中
|
||||
- `3` 已完成
|
||||
- `4` 已失败
|
||||
- `5` 已取消
|
||||
|
||||
### 3.2 分片状态
|
||||
|
||||
- `1` 待处理
|
||||
- `2` 处理中
|
||||
- `3` 已成功
|
||||
- `4` 已失败
|
||||
- `5` 已取消
|
||||
|
||||
## 4. 核心设计
|
||||
|
||||
### 4.1 模型与迁移
|
||||
|
||||
新增两张表:
|
||||
|
||||
- `tb_export_task`:主任务
|
||||
- `tb_export_shard_task`:分片任务
|
||||
|
||||
关键点:
|
||||
|
||||
- 无外键,仅使用 `task_id` 做逻辑关联
|
||||
- 主任务保留进度、分片统计、权限快照、产物信息
|
||||
- 分片任务保留游标范围、分片产物、执行状态
|
||||
|
||||
### 4.2 场景策略注册中心
|
||||
|
||||
新增 `internal/exporter` 场景注册中心与策略接口,首批接入:
|
||||
|
||||
- `device` 场景策略
|
||||
- `iot_card` 场景策略
|
||||
|
||||
统一定义:
|
||||
|
||||
- `Headers()`:导出表头
|
||||
- `NextShardBoundary()`:按 keyset 方式计算分片边界
|
||||
- `QueryRows()`:按分片边界查询行数据
|
||||
|
||||
### 4.3 执行链路
|
||||
|
||||
- `export:dispatch`:构建分片并入队 `export:shard`
|
||||
- `export:shard`:按分片查询数据、写文件、上传 OSS、回写分片结果
|
||||
- `export:finalize`:汇总分片结果并生成最终文件,更新主任务状态
|
||||
|
||||
### 4.4 幂等与重试安全
|
||||
|
||||
- dispatch/shard/finalize 增加 Redis 锁,避免并发重复执行
|
||||
- 分片状态条件更新,重复消费不会重复计数
|
||||
- shard/finalize 利用 Asynq 重试上下文,在最终重试失败时才落库失败状态
|
||||
|
||||
### 4.5 取消语义
|
||||
|
||||
- 待处理:直接更新为已取消
|
||||
- 处理中:仅设置 `cancel_requested=true`,由 Worker 检查点协作停止
|
||||
- dispatch/shard/finalize 均增加取消检查点
|
||||
|
||||
### 4.6 下载链接
|
||||
|
||||
- 仅 `status=已完成` 返回 `download_url`
|
||||
- 链接有效期固定 `24h`(常量 `ExportDownloadURLExpire`)
|
||||
- URL 由详情接口实时生成,过期后再次访问可刷新
|
||||
|
||||
## 5. 权限与可见性
|
||||
|
||||
- 平台/超管:可见全部导出任务
|
||||
- 代理:仅可见其店铺范围内创建的导出任务
|
||||
- 详情越权统一返回:`无权限操作该资源或资源不存在`
|
||||
|
||||
## 6. 文档与生成器更新
|
||||
|
||||
已同步更新:
|
||||
|
||||
- 路由文档(新增 `导出任务` Tag 的 4 个接口)
|
||||
- 文档构建 Handler 集合(`pkg/openapi/handlers.go` 新增 `ExportTask`)
|
||||
- OpenAPI 文件重新生成:`docs/admin-openapi.yaml`
|
||||
56
docs/unified-export-task-system/手工验证清单.md
Normal file
56
docs/unified-export-task-system/手工验证清单.md
Normal file
@@ -0,0 +1,56 @@
|
||||
# 统一导出任务手工验证清单
|
||||
|
||||
## 验证工具与原则
|
||||
|
||||
- 数据库结构与数据状态:使用 PostgreSQL MCP(只读)
|
||||
- 接口契约验证:使用 Postman 或 curl
|
||||
- 明确约束:不新增自动化测试文件(`*_test.go`)
|
||||
|
||||
## A. 创建导出任务
|
||||
|
||||
- [ ] `POST /api/admin/export-tasks`,`scene=device`,`format=xlsx`
|
||||
- [ ] `POST /api/admin/export-tasks`,`scene=iot_card`,`format=csv`
|
||||
- [ ] 非法 `scene`(如 `order`)返回参数错误
|
||||
- [ ] 非法 `format`(如 `pdf`)返回参数错误
|
||||
|
||||
## B. 列表与过滤
|
||||
|
||||
- [ ] `GET /api/admin/export-tasks?page=1&page_size=20` 默认分页生效
|
||||
- [ ] `scene` 过滤生效(`device` / `iot_card`)
|
||||
- [ ] `status` 过滤生效(1~5)
|
||||
- [ ] `start_time/end_time` 时间过滤生效
|
||||
- [ ] `page_size > 100` 时被限制到 100
|
||||
|
||||
## C. 详情与下载
|
||||
|
||||
- [ ] 未完成任务详情不返回 `download_url`
|
||||
- [ ] 已完成任务详情返回 `download_url`
|
||||
- [ ] `download_url` 有效期固定 24 小时
|
||||
- [ ] 过期后再次访问详情可刷新新链接
|
||||
|
||||
## D. 取消语义
|
||||
|
||||
- [ ] 待处理任务可取消,状态立即变为已取消
|
||||
- [ ] 处理中任务可提交取消请求(`cancel_requested=true`)
|
||||
- [ ] 已完成/已失败/已取消任务再次取消被拒绝
|
||||
- [ ] 重复取消处理中任务幂等返回
|
||||
|
||||
## E. 异步链路(dispatch -> shard -> finalize)
|
||||
|
||||
- [ ] 创建任务后 `export:dispatch` 被消费
|
||||
- [ ] 分片任务写入 `tb_export_shard_task`
|
||||
- [ ] `export:shard` 执行后分片状态更新,产物写入 OSS
|
||||
- [ ] `export:finalize` 汇总后主任务状态收敛
|
||||
- [ ] 分片失败时主任务最终失败,错误信息可追踪
|
||||
|
||||
## F. 数据权限与可见性
|
||||
|
||||
- [ ] 平台用户可见全量任务
|
||||
- [ ] 代理仅可见其店铺范围任务
|
||||
- [ ] 越权访问详情统一返回:`无权限操作该资源或资源不存在`
|
||||
|
||||
## G. 数据库核查项(PostgreSQL MCP)
|
||||
|
||||
- [ ] `tb_export_task` 表存在,字段与注释完整
|
||||
- [ ] `tb_export_shard_task` 表存在,字段与注释完整
|
||||
- [ ] 关键索引存在并生效(任务号唯一索引、`task_id+status`、`scene+status+created_at`)
|
||||
59
docs/unified-export-task-system/验收记录.md
Normal file
59
docs/unified-export-task-system/验收记录.md
Normal file
@@ -0,0 +1,59 @@
|
||||
# 统一导出任务验收记录
|
||||
|
||||
## 1. 验收范围
|
||||
|
||||
- 代码编译验证
|
||||
- 数据库迁移验证(PostgreSQL MCP)
|
||||
- OpenAPI 生成验证
|
||||
- 手工接口链路验证准备(受环境变量约束)
|
||||
|
||||
## 2. 已完成结果
|
||||
|
||||
### 2.1 编译结果
|
||||
|
||||
- 命令:`go build ./...`
|
||||
- 结果:通过
|
||||
|
||||
### 2.2 OpenAPI 结果
|
||||
|
||||
- 命令:`go run cmd/gendocs/main.go`
|
||||
- 结果:通过
|
||||
- 产物:`docs/admin-openapi.yaml`
|
||||
|
||||
### 2.3 数据库迁移结果
|
||||
|
||||
- 命令:`migrate -path migrations -database <...> up 1`
|
||||
- 结果:`135/u create_export_task_tables`
|
||||
|
||||
### 2.4 PostgreSQL MCP 结构校验
|
||||
|
||||
已确认:
|
||||
|
||||
- 存在 `tb_export_task`
|
||||
- 存在 `tb_export_shard_task`
|
||||
- 表注释和字段注释已生效
|
||||
|
||||
已确认索引:
|
||||
|
||||
- `idx_export_task_no`(唯一,软删条件)
|
||||
- `idx_export_task_scene_status_created_at`
|
||||
- `idx_export_task_creator_user_id`
|
||||
- `idx_export_task_creator_shop_id`
|
||||
- `idx_export_task_creator_enterprise_id`
|
||||
- `idx_export_shard_task_task_shard_no`(唯一,软删条件)
|
||||
- `idx_export_shard_task_task_status`
|
||||
|
||||
## 3. 待手工执行项(环境阻塞)
|
||||
|
||||
以下项已准备 curl/Postman 清单与模板,但当前环境缺少 API 启动所需配置(`JUNHONG_REDIS_ADDRESS`、`JUNHONG_JWT_SECRET_KEY` 等),暂未执行:
|
||||
|
||||
- 接口契约验证(创建/列表/详情/取消)
|
||||
- 异步状态流转观察(dispatch/shard/finalize)
|
||||
- 两个场景(`device` / `iot_card`)各 `xlsx` 与 `csv` 完整导出
|
||||
- 取消场景回归(待处理取消、处理中取消、重复取消)
|
||||
|
||||
可复现命令与模板见:
|
||||
|
||||
- `docs/unified-export-task-system/手工验证清单.md`
|
||||
- `docs/unified-export-task-system/验收记录模板.md`
|
||||
- `docs/unified-export-task-system/验证数据样本.md`
|
||||
64
docs/unified-export-task-system/验收记录模板.md
Normal file
64
docs/unified-export-task-system/验收记录模板.md
Normal file
@@ -0,0 +1,64 @@
|
||||
# 统一导出任务验收记录模板
|
||||
|
||||
## 1. 基本信息
|
||||
|
||||
- 验收日期:
|
||||
- 验收环境:
|
||||
- 验收人:
|
||||
- 代码版本(commit):
|
||||
|
||||
## 2. 请求与响应记录
|
||||
|
||||
### 2.1 创建任务
|
||||
|
||||
- 请求:
|
||||
- 响应:
|
||||
- 期望:
|
||||
- 结果:通过 / 不通过
|
||||
|
||||
### 2.2 列表过滤
|
||||
|
||||
- 请求:
|
||||
- 响应:
|
||||
- 期望:
|
||||
- 结果:通过 / 不通过
|
||||
|
||||
### 2.3 详情下载
|
||||
|
||||
- 请求:
|
||||
- 响应:
|
||||
- 期望:
|
||||
- 结果:通过 / 不通过
|
||||
|
||||
### 2.4 取消任务
|
||||
|
||||
- 请求:
|
||||
- 响应:
|
||||
- 期望:
|
||||
- 结果:通过 / 不通过
|
||||
|
||||
## 3. 数据库快照(PostgreSQL MCP)
|
||||
|
||||
- `tb_export_task` 关键字段快照:
|
||||
- `tb_export_shard_task` 关键字段快照:
|
||||
- 索引核查结果:
|
||||
|
||||
## 4. OSS 产物记录
|
||||
|
||||
- 分片文件 Key 列表:
|
||||
- 最终文件 Key:
|
||||
- 文件大小:
|
||||
|
||||
## 5. 日志关键字段
|
||||
|
||||
- `task_id`:
|
||||
- `task_no`:
|
||||
- `scene`:
|
||||
- `status`:
|
||||
- `error`(如有):
|
||||
|
||||
## 6. 下载 URL 有效期验证
|
||||
|
||||
- 首次获取时间:
|
||||
- 返回 `download_expires_at`:
|
||||
- 过期后刷新结果:
|
||||
84
docs/unified-export-task-system/验证数据样本.md
Normal file
84
docs/unified-export-task-system/验证数据样本.md
Normal file
@@ -0,0 +1,84 @@
|
||||
# 统一导出任务验证数据样本
|
||||
|
||||
## 目标
|
||||
|
||||
为 `device` 与 `iot_card` 场景准备两类样本:
|
||||
|
||||
- 小数据集:便于快速验证状态流转(10~30 条)
|
||||
- 大数据集:便于验证分片执行(>= 5000 条)
|
||||
|
||||
## 1. device 场景
|
||||
|
||||
### 1.1 小数据集(示例)
|
||||
|
||||
```sql
|
||||
-- 建议在测试库执行,按实际字段补齐 creator/updater
|
||||
INSERT INTO tb_device (creator, updater, virtual_no, device_name, device_model, device_type, max_sim_slots, manufacturer, status)
|
||||
VALUES
|
||||
(1, 1, 'EXP-DEV-S-0001', '导出设备样本1', 'M1', 'tracker', 4, 'JH', 1),
|
||||
(1, 1, 'EXP-DEV-S-0002', '导出设备样本2', 'M1', 'tracker', 4, 'JH', 1),
|
||||
(1, 1, 'EXP-DEV-S-0003', '导出设备样本3', 'M2', 'router', 4, 'JH', 1);
|
||||
```
|
||||
|
||||
### 1.2 大数据集(示例)
|
||||
|
||||
```sql
|
||||
-- 生成 5000 条设备样本
|
||||
INSERT INTO tb_device (creator, updater, virtual_no, device_name, device_model, device_type, max_sim_slots, manufacturer, status)
|
||||
SELECT
|
||||
1,
|
||||
1,
|
||||
'EXP-DEV-L-' || LPAD(gs::text, 6, '0'),
|
||||
'导出设备大样本' || gs,
|
||||
'M-L',
|
||||
'tracker',
|
||||
4,
|
||||
'JH',
|
||||
1
|
||||
FROM generate_series(1, 5000) gs;
|
||||
```
|
||||
|
||||
## 2. iot_card 场景
|
||||
|
||||
### 2.1 小数据集(示例)
|
||||
|
||||
```sql
|
||||
INSERT INTO tb_iot_card (
|
||||
creator, updater, iccid, iccid_19, carrier_id, carrier_type, carrier_name,
|
||||
msisdn, status, activation_status, real_name_status, network_status
|
||||
)
|
||||
VALUES
|
||||
(1, 1, '89860000000000000001', '8986000000000000000', 1, 'CMCC', '中国移动', '13900000001', 1, 0, 0, 1),
|
||||
(1, 1, '89860000000000000002', '8986000000000000000', 1, 'CMCC', '中国移动', '13900000002', 1, 0, 0, 1),
|
||||
(1, 1, '89860000000000000003', '8986000000000000000', 1, 'CMCC', '中国移动', '13900000003', 1, 0, 0, 1);
|
||||
```
|
||||
|
||||
### 2.2 大数据集(示例)
|
||||
|
||||
```sql
|
||||
INSERT INTO tb_iot_card (
|
||||
creator, updater, iccid, iccid_19, carrier_id, carrier_type, carrier_name,
|
||||
msisdn, status, activation_status, real_name_status, network_status
|
||||
)
|
||||
SELECT
|
||||
1,
|
||||
1,
|
||||
'8986001' || LPAD(gs::text, 13, '0'),
|
||||
LEFT('8986001' || LPAD(gs::text, 13, '0'), 19),
|
||||
1,
|
||||
'CMCC',
|
||||
'中国移动',
|
||||
'138' || LPAD(gs::text, 8, '0'),
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1
|
||||
FROM generate_series(1, 5000) gs;
|
||||
```
|
||||
|
||||
## 3. 清理建议
|
||||
|
||||
```sql
|
||||
DELETE FROM tb_device WHERE virtual_no LIKE 'EXP-DEV-%';
|
||||
DELETE FROM tb_iot_card WHERE iccid LIKE '8986001%';
|
||||
```
|
||||
Reference in New Issue
Block a user