init
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 8m3s

This commit is contained in:
Break
2026-06-16 15:15:50 +08:00
parent 84ab3bad99
commit 1f634eb465
21 changed files with 2776 additions and 41 deletions

View File

@@ -0,0 +1,52 @@
## 1. 新增 DataSource 接口与 ExportParams 类型,删除旧 SceneStrategy 实现
- [x] 1.1 在 `internal/exporter/datasource.go` 新建文件,定义 `DataSource` 接口(`Scene / Count / Headers / Fetch`)和 `ExportParams` 结构体(`Filters map[string]any / ScopeShopIDs []uint / UserType int`
- [x] 1.2 在 `internal/exporter/query_params.go` 新建文件,实现 `ParseExportParams(task *model.ExportTask) ExportParams`:从 `task.QueryJSON` 解析 `filters`,从 `task.ScopeShopIDs``task.CreatorUserType` 填充权限字段
- [x] 1.3 删除 `internal/exporter/device_scene.go``internal/exporter/iot_card_scene.go` 中的全部旧 `SceneStrategy` 实现代码(文件内容清空,后续第 5 组重写为 `DataSource` 实现)
- [x] 1.4 重写 `internal/exporter/registry.go``Registry` 只存储 `DataSource`,删除 `SceneStrategy` 相关类型和方法;`Get` 直接返回 `DataSource``NewDefaultRegistry` 暂时为空(第 5 组补充)
- [x] 1.5 删除 `internal/exporter/scope.go``applyShopScopeForTask` 函数(该逻辑后续由各 DataSource 实现内部通过 `ExportParams` 处理,不再作为公共函数)
- [x] 1.6 用 `go build ./internal/exporter/...` 确认编译无错误
## 2. 修改 dispatch改为 offset/limit 切分分片
- [x] 2.1 修改 `internal/model/export_task.go``ExportShardTask`:将 `CursorStart / CursorEnd uint64` 字段语义改为 `Offset / Limit int`(字段名改为 `ShardOffset / ShardLimit`),同步更新 `gorm` 标签注释
- [x] 2.2 新增数据库迁移文件 `migrations/000XXX_alter_export_shard_task_offset.up.sql`:在 `tb_export_shard_task` 中添加 `shard_offset bigint not null default 0``shard_limit int not null default 0` 列(保留原 `cursor_start / cursor_end` 列兼容旧任务)
- [x] 2.3 修改 `internal/task/export_dispatch.go``buildShards` 方法改为调用 `DataSource.Count` 获取总行数,按 `ExportDefaultShardSize` 计算 offset/limit 对,生成 `ExportShardTask` 列表(填 `ShardOffset / ShardLimit`);删除原 Keyset 游标逻辑
- [x] 2.4 修改 `internal/task/export_dispatch.go`dispatch 阶段调用 `DataSource.Headers` 一次,将结果序列化后存入 `task.QueryJSON``resolved_headers` 子键(通过新增 `ExportTaskStore.UpdateResolvedHeaders` 方法写入)
- [x] 2.5 修改 `internal/store/postgres/export_task_store.go`:新增 `UpdateResolvedHeaders(ctx, taskID, headers []string, updater uint) error` 方法
- [x] 2.6 用 `go build ./internal/task/...` 确认编译无错误
## 3. 修改 shard调用 DataSource.Fetch分片文件不含表头
- [x] 3.1 修改 `internal/task/export_shard.go`shard 阶段从 `task.QueryJSON``resolved_headers` 读取表头;调用 `DataSource.Fetch(ctx, params, shard.ShardOffset, shard.ShardLimit)` 获取数据行,删除原 `strategy.QueryRows` 调用
- [x] 3.2 修改 `internal/task/export_common.go``writeExportFile`:新增 `withoutHeader bool` 参数,当为 `true` 时跳过写表头行shard 阶段调用时传 `true`finalize 写入表头
- [x] 3.3 用 `go build ./internal/task/...` 确认编译无错误
## 4. 修改 finalize合并分片 OSS 文件,不重查数据库
- [x] 4.1 在 `pkg/storage/types.go` 确认(或新增)`Provider` 接口有 `Download(ctx, key string) (io.ReadCloser, error)` 方法;若无则添加并在对应实现中实现
- [x] 4.2 修改 `internal/task/export_finalize.go`:所有分片成功后,按 `shard_no` 升序循环从 OSS 下载各分片文件,流式追加写入本地临时 CSV 文件;写完后在文件头部插入 `resolved_headers`
- [x] 4.3 修改 `internal/task/export_finalize.go`XLSX 格式走降级路径——合并 CSV 后用 `excelize` 流式写出 xlsx而非直接合并 xlsx 分片
- [x] 4.4 用 `go build ./internal/task/...` 确认编译无错误
## 5. 迁移 device 和 iot_card 场景到 DataSource 接口
- [x] 5.1 重写 `internal/exporter/device_scene.go`:实现 `DataSource` 接口,`Count` 按权限快照计总数,`Headers` 返回固定列头(非动态),`Fetch` 用 GORM 加 `ORDER BY id ASC LIMIT ? OFFSET ?` 查询并应用 `ExportParams.Filters`(支持 `status / shop_id / created_at_start / created_at_end` 过滤)
- [x] 5.2 重写 `internal/exporter/iot_card_scene.go`:同上,支持 `status / shop_id / carrier_name / created_at_start / created_at_end` 过滤条件;`Fetch` 中用 `LEFT JOIN tb_package_usage` 补充套餐信息列(可选列,通过 `Filters["with_package"]` 开关控制)
- [x] 5.3 修改 `internal/exporter/registry.go``NewDefaultRegistry`:改为注册新的 `DataSource` 实现,删除旧 `SceneStrategy` 注册
- [x] 5.4 用 MCP PostgreSQL 工具分别对 device 和 iot_card 场景各抽取一批数据,对比新 `Fetch` 输出与原 `QueryRows` 输出一致(重点验证权限过滤和筛选条件)
- [x] 5.5 用 `go build ./...` 确认全项目编译无错误
## 6. 执行数据库迁移并验证
- [x] 6.1 执行 `migrate -path migrations -database "..." up` 应用 shard 表字段迁移
- [x] 6.2 用 MCP PostgreSQL 工具确认 `tb_export_shard_task` 表已新增 `shard_offset / shard_limit`
- [x] 6.3 确认存量 shard 记录的 `cursor_start / cursor_end` 列数据未被破坏(迁移为纯新增列,无数据变更)
## 7. 端到端验证
- [x] 7.1 通过 API 创建一个 `scene=iot_card, format=csv` 的导出任务,传入筛选条件 `{"filters": {"shop_id": 5}}`,用 MCP 查询 `tb_export_task` 确认 `query_json` 已记录筛选参数
- [x] 7.2 等待任务完成,用 MCP 查询 `tb_export_shard_task` 确认各分片的 `shard_offset / shard_limit` 已按预期填写,`status = 3`(已成功)
- [x] 7.3 调用任务详情接口获取 `download_url`,下载文件验证:列头与 `Headers` 一致、数据行仅含 `shop_id=5` 的记录、无重复行、无缺失行
- [x] 7.4 创建一个 `scene=device, format=xlsx` 的导出任务不传筛选条件验证全量导出正常xlsx 文件可正常打开
- [x] 7.5 创建导出任务后立即取消(`status=处理中`),验证取消流程正常,任务最终状态为已取消