Files
junhong_cmp_fiber/openspec/changes/refactor-export-datasource/tasks.md
Break 1f634eb465
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 8m3s
init
2026-06-16 15:15:50 +08:00

6.1 KiB
Raw Blame History

1. 新增 DataSource 接口与 ExportParams 类型,删除旧 SceneStrategy 实现

  • 1.1 在 internal/exporter/datasource.go 新建文件,定义 DataSource 接口(Scene / Count / Headers / Fetch)和 ExportParams 结构体(Filters map[string]any / ScopeShopIDs []uint / UserType int
  • 1.2 在 internal/exporter/query_params.go 新建文件,实现 ParseExportParams(task *model.ExportTask) ExportParams:从 task.QueryJSON 解析 filters,从 task.ScopeShopIDstask.CreatorUserType 填充权限字段
  • 1.3 删除 internal/exporter/device_scene.gointernal/exporter/iot_card_scene.go 中的全部旧 SceneStrategy 实现代码(文件内容清空,后续第 5 组重写为 DataSource 实现)
  • 1.4 重写 internal/exporter/registry.goRegistry 只存储 DataSource,删除 SceneStrategy 相关类型和方法;Get 直接返回 DataSourceNewDefaultRegistry 暂时为空(第 5 组补充)
  • 1.5 删除 internal/exporter/scope.goapplyShopScopeForTask 函数(该逻辑后续由各 DataSource 实现内部通过 ExportParams 处理,不再作为公共函数)
  • 1.6 用 go build ./internal/exporter/... 确认编译无错误

2. 修改 dispatch改为 offset/limit 切分分片

  • 2.1 修改 internal/model/export_task.goExportShardTask:将 CursorStart / CursorEnd uint64 字段语义改为 Offset / Limit int(字段名改为 ShardOffset / ShardLimit),同步更新 gorm 标签注释
  • 2.2 新增数据库迁移文件 migrations/000XXX_alter_export_shard_task_offset.up.sql:在 tb_export_shard_task 中添加 shard_offset bigint not null default 0shard_limit int not null default 0 列(保留原 cursor_start / cursor_end 列兼容旧任务)
  • 2.3 修改 internal/task/export_dispatch.gobuildShards 方法改为调用 DataSource.Count 获取总行数,按 ExportDefaultShardSize 计算 offset/limit 对,生成 ExportShardTask 列表(填 ShardOffset / ShardLimit);删除原 Keyset 游标逻辑
  • 2.4 修改 internal/task/export_dispatch.godispatch 阶段调用 DataSource.Headers 一次,将结果序列化后存入 task.QueryJSONresolved_headers 子键(通过新增 ExportTaskStore.UpdateResolvedHeaders 方法写入)
  • 2.5 修改 internal/store/postgres/export_task_store.go:新增 UpdateResolvedHeaders(ctx, taskID, headers []string, updater uint) error 方法
  • 2.6 用 go build ./internal/task/... 确认编译无错误

3. 修改 shard调用 DataSource.Fetch分片文件不含表头

  • 3.1 修改 internal/task/export_shard.goshard 阶段从 task.QueryJSONresolved_headers 读取表头;调用 DataSource.Fetch(ctx, params, shard.ShardOffset, shard.ShardLimit) 获取数据行,删除原 strategy.QueryRows 调用
  • 3.2 修改 internal/task/export_common.gowriteExportFile:新增 withoutHeader bool 参数,当为 true 时跳过写表头行shard 阶段调用时传 truefinalize 写入表头
  • 3.3 用 go build ./internal/task/... 确认编译无错误

4. 修改 finalize合并分片 OSS 文件,不重查数据库

  • 4.1 在 pkg/storage/types.go 确认(或新增)Provider 接口有 Download(ctx, key string) (io.ReadCloser, error) 方法;若无则添加并在对应实现中实现
  • 4.2 修改 internal/task/export_finalize.go:所有分片成功后,按 shard_no 升序循环从 OSS 下载各分片文件,流式追加写入本地临时 CSV 文件;写完后在文件头部插入 resolved_headers
  • 4.3 修改 internal/task/export_finalize.goXLSX 格式走降级路径——合并 CSV 后用 excelize 流式写出 xlsx而非直接合并 xlsx 分片
  • 4.4 用 go build ./internal/task/... 确认编译无错误

5. 迁移 device 和 iot_card 场景到 DataSource 接口

  • 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 过滤)
  • 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"] 开关控制)
  • 5.3 修改 internal/exporter/registry.goNewDefaultRegistry:改为注册新的 DataSource 实现,删除旧 SceneStrategy 注册
  • 5.4 用 MCP PostgreSQL 工具分别对 device 和 iot_card 场景各抽取一批数据,对比新 Fetch 输出与原 QueryRows 输出一致(重点验证权限过滤和筛选条件)
  • 5.5 用 go build ./... 确认全项目编译无错误

6. 执行数据库迁移并验证

  • 6.1 执行 migrate -path migrations -database "..." up 应用 shard 表字段迁移
  • 6.2 用 MCP PostgreSQL 工具确认 tb_export_shard_task 表已新增 shard_offset / shard_limit
  • 6.3 确认存量 shard 记录的 cursor_start / cursor_end 列数据未被破坏(迁移为纯新增列,无数据变更)

7. 端到端验证

  • 7.1 通过 API 创建一个 scene=iot_card, format=csv 的导出任务,传入筛选条件 {"filters": {"shop_id": 5}},用 MCP 查询 tb_export_task 确认 query_json 已记录筛选参数
  • 7.2 等待任务完成,用 MCP 查询 tb_export_shard_task 确认各分片的 shard_offset / shard_limit 已按预期填写,status = 3(已成功)
  • 7.3 调用任务详情接口获取 download_url,下载文件验证:列头与 Headers 一致、数据行仅含 shop_id=5 的记录、无重复行、无缺失行
  • 7.4 创建一个 scene=device, format=xlsx 的导出任务不传筛选条件验证全量导出正常xlsx 文件可正常打开
  • 7.5 创建导出任务后立即取消(status=处理中),验证取消流程正常,任务最终状态为已取消