All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 8m3s
6.1 KiB
6.1 KiB
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.ScopeShopIDs和task.CreatorUserType填充权限字段 - 1.3 删除
internal/exporter/device_scene.go和internal/exporter/iot_card_scene.go中的全部旧SceneStrategy实现代码(文件内容清空,后续第 5 组重写为DataSource实现) - 1.4 重写
internal/exporter/registry.go:Registry只存储DataSource,删除SceneStrategy相关类型和方法;Get直接返回DataSource;NewDefaultRegistry暂时为空(第 5 组补充) - 1.5 删除
internal/exporter/scope.go中applyShopScopeForTask函数(该逻辑后续由各 DataSource 实现内部通过ExportParams处理,不再作为公共函数) - 1.6 用
go build ./internal/exporter/...确认编译无错误
2. 修改 dispatch:改为 offset/limit 切分分片
- 2.1 修改
internal/model/export_task.go中ExportShardTask:将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 0和shard_limit int not null default 0列(保留原cursor_start / cursor_end列兼容旧任务) - 2.3 修改
internal/task/export_dispatch.go:buildShards方法改为调用DataSource.Count获取总行数,按ExportDefaultShardSize计算 offset/limit 对,生成ExportShardTask列表(填ShardOffset / ShardLimit);删除原 Keyset 游标逻辑 - 2.4 修改
internal/task/export_dispatch.go:dispatch 阶段调用DataSource.Headers一次,将结果序列化后存入task.QueryJSON的resolved_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.go:shard 阶段从task.QueryJSON的resolved_headers读取表头;调用DataSource.Fetch(ctx, params, shard.ShardOffset, shard.ShardLimit)获取数据行,删除原strategy.QueryRows调用 - 3.2 修改
internal/task/export_common.go的writeExportFile:新增withoutHeader bool参数,当为true时跳过写表头行;shard 阶段调用时传true,finalize 写入表头 - 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.go:XLSX 格式走降级路径——合并 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.go的NewDefaultRegistry:改为注册新的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=处理中),验证取消流程正常,任务最终状态为已取消