跳到正文

大型后台管理系统如何把业务操作做可靠(5):可恢复的导入导出任务

大型后台管理系统如何把业务操作做可靠

  1. 把业务规则写进订单和退款模型
  2. 并发编辑、草稿合并与搜索乱序
  3. 让报表说清统计口径与数据进度
  4. 退款超时后的幂等与对账
  5. 可恢复的导入导出任务(当前)
  6. 批量操作的预览、执行与恢复
  7. 多租户授权、撤权与审计
  8. 旧页面、接口和审批模板如何一起演进
  9. 沿真实操作验收可靠性与性能
  10. 设计规范和组件库如何落到日常开发
  11. 脚手架、接口契约与前后端联调
  12. 构建产物、持续交付与灰度回退
  13. 低代码表单与审批流程的执行边界
  14. 功能投放、权限与灰度人群
  15. 桌面、移动端与宿主能力适配
  16. 多个前端应用如何集成与独立交付
  17. 复杂编辑器的状态、命令与协作

一万行商品导入到第 2,500 行时,执行进程退出了。运营关掉过页面,也不知道最后一行是否成功。系统需要回答已经完成了哪些行、哪些还可以处理,以及取消会停在哪里。

先设计可查询的任务资源

上传、提交与执行可以分开。上传完成后,服务端记录文件归属、大小、摘要和版本。提交导入时检查该文件属于当前租户且尚在保留期内,并保存列映射及校验规则版本。任务受理成功后返回稳定地址。

HTTP/1.1 202 Accepted
Location: /jobs/J42
Retry-After: 5
Content-Type: application/json

{"id":"J42","state":"queued"}

在这个示例契约里,状态接口用 200 返回任务资源。执行失败写进任务状态和错误字段。网络错误或查不到任务由 HTTP 错误表达。这样客户端能区分「这次查询失败」和「任务已经失败」。浏览器重新打开时可以从任务列表进入,轮询遵循服务端提示并在隐藏或离开页面时降低频率或暂停。微软异步请求应答模式

分开任务状态和明细状态

任务有排队、运行、请求取消以及终态。终态可按业务分为全部成功、部分完成、失败和取消。逐项处理时,每一行还需要单独的终态。错误结果提供稳定错误码、行号和必要信息,长错误明细分页或下载。

观察到的状态 可做的动作
待处理 等待执行或请求取消
已写入 查看结果
明确失败且可重试 修正输入或创建重试任务
提交下游后结果未知 查下游结果
已取消且从未执行 根据业务决定是否另建任务

「请求取消」可以先落到任务记录,执行端在开始下一批之前检查。并行执行时,还需等待已经取得资格的工作单元完成或确认停下。最后返回成功、失败和取消的实际数量。若任务已经写入 2,500 行,这些行仍保留成功结果。需要撤销的场景另行定义反向操作和受影响对象。

让每行副作用和检查点一起落地

同库导入可以用任务 ID 与稳定行号作为处理键,文件版本固定后,同一行总能再次定位。业务写入和该行结果在一个事务提交,进度从已提交的明细聚合。下面是伪代码。执行端取得任务租约后才进入循环,租约应有期限,接手者更新代次,过期执行端不得继续提交。

processItem(jobId, itemId, workerGeneration):
  transaction:
    requireCurrentGeneration(jobId, workerGeneration)
    item = lockItem(jobId, itemId)
    if item.hasFinalResult:
      return item.result

    if cancellationRequested(jobId):
      return saveCancelled(item)

    outcome = validateAndApply(item.input)
    return saveItemResult(item, outcome)

租约代次校验须与写入同处受保护边界。跨系统写入则按明细操作 ID 查询和去重,不能用本地检查点推断外部副作用。失败行重试要保留原任务和行标识,明确是同一业务意图重放,还是改过数据的重新提交。若两个导入任务可能写同一商品,还要靠商品业务键和写入版本防止覆盖。

原执行端提交第 2500 行后退出,接手执行端读取已提交的逐行结果

导出也需要固定读取规则

任务受理时记下租户、字段、筛选条件和权限策略。若导出要复现用户眼前的数据,后台需要一个可用的快照或版本。若合同允许生成时取数,文件记录实际取数时间与条件。大数据量可分块读取和写入临时文件,全部完成后再将产物标记为可下载。下载入口还要校验当前权限与文件期限。

多租户环境可以对重型任务设并发配额。观察任务积压时分别看等待和执行时间。某租户连续提交大文件时,检查其他租户的短任务是否还能及时开始。

从文件校验到失败行重跑

上传时先限制文件体积、行数、编码和格式。解析阶段记录文件中的原始行号,并把空行、重复主键和字段类型错误分别标出。文件能解析与业务数据能写入是两次判断。例如价格格式合法,但商品所属门店已关闭,后者要在执行阶段再次检查。

导入接口需要约定同一商品重复出现时如何处理。可在预检时拒绝重复业务键,也可规定以最后一行为准并明确展示被覆盖行。若业务依赖顺序,例如先创建分类再导入商品,就不要把全表任意拆成并发批次。可以分阶段执行,先处理分类,再将商品按分类依赖调度。

以一万行文件为例,预检发现 20 行格式错误,剩余 9,980 行进入待执行。某次取消后结果可能如下。数字是示例,预检失败是否计入执行总量需要在接口上明确。

分类 数量 操作人员下一步
预检未通过 20 修正源文件
已写入 2,500 查写入结果
明确执行失败 30 下载明细并筛选可重试项
尚未开始,已取消 7,450 确认范围后另发任务

选择失败项重跑时,服务端创建关联原任务的计划,检查这些项确属可重试且没有在别处完成。若用户更改了商品价格或目标,就要让新请求表达新意图,同时靠业务主键和版本条件决定是否更新。仅重新上传原表,会失去原先成功和失败的边界。

恢复时判断最后一行到底做了什么

第 2,500 行提交前崩溃,持久结果不存在,接手端可再次处理。提交后崩溃,结果与商品写入已经一起提交,接手端直接读取结果。若写入发生在另一个服务而本地结果尚未落盘,接手端先用同一明细操作 ID 查远端。单靠「进度停在 2,499」无法确定第 2,500 行是否完成。

文件生成也可以采用类似原则。每次尝试写不同的临时对象,最终通过带代次检查的任务更新发布下载指针。旧执行端即使晚一步完成,也不能替换新执行端已发布的文件。清理临时对象时排除任务当前引用的产物。

验收任务中断与恢复

在一行事务提交前终止进程,接手后该行可以处理一次。在事务提交后、进度展示更新前终止,接手后已写行应从持久结果重建。再验证取消与最后一批同时发生、下载时已撤权、旧文件已过期这三种情况,分别检查任务状态、真实数据和界面下一步。

总结

任务 ID 保存整次工作的归属和状态,明细 ID 决定每个工作单元如何恢复。只有持久记录能与业务写入对得上,进度、部分成功、取消和失败重试才有可解释的含义。