档口入库码:异步逐件写入、检查点与只读复核 #121

Closed
opened 2026-08-27 23:30:17 +08:00 by ila · 3 comments
Owner

所属与来源

  • 父 MVP:#117。
  • 来源:用户于 2026-08-27 要求先按已确认的全栈分析方案建单并制作总原型,审核通过后才能实施。
  • 前置依赖:#118、#119、#120。
  • 类型:高风险外部写入与并发状态机。

目标

安全地把已确认计划异步逐件写入 SYB,并对超时、崩溃和未知结果提供不可误重试的恢复闭环。

方案

  • 复用现有 sybclient.DeleteInnerCode/CreateInnerCodeDetail/UpdateDetailCode 和 ErrWriteResultUnknown。
  • 提交时事务创建执行批次、锁定记录并进入 queued;使用 UUID requestId 幂等。
  • 数据库活动槽/worker 租约保证跨进程全局串行;记录领取采用原子状态转换,不能只靠 sync.Mutex。
  • 每个动作前重读完整 stock 并核对规划身份;远端发生漂移时停止,不执行旧计划。
  • 旧码只在与规划快照完全一致时删除;多件时复用可用明细,不足部分创建数量 1、价格 0 的稳定命名占位明细。
  • 每项远端修改前后保存单件级结构化检查点;写后重读整个 stock,目标码必须只出现在预期 detail。
  • 写请求禁止自动重试。网络超时、连接中断、响应无法确认或崩溃窗口统一进入 needs_check。
  • needs_check 只允许调用只读 stock 查询复核;不提供“一键重试写入”。
  • 服务启动恢复:未开始远端动作的队列恢复为待再次确认;处于 applying 或检查点不完整的记录进入 needs_check。
  • 批量物理删除与回写状态互斥:queued、applying、needs_check 记录必须拒绝删除,防止丢失活动或不明确的远端证据;其余状态按 #119 删除契约处理。

非目标

  • 不创建采购订单、不付款。
  • 不自动修复歧义、不自动重跑未知写结果。
  • 不在本工单执行真实 SYB 写入或生产发布,除非用户再次明确授权。

验收标准

  • 跨两个服务实例仍只有一个远端写执行者。
  • 旧码删除、单件更新、多件占位、已存在目标码和重复空明细行为正确。
  • 所有远端修改前后都有可恢复检查点。
  • 响应前失败、响应后断线、写后核验失败、进程重启等故障注入均不会自动重试。
  • needs_check 复核只读,能够判定已成功、未写入或仍不明确。
  • 批量删除无法移除 queued、applying、needs_check,并发提交时由数据库状态条件保证。
  • 批次进度、部分成功、错误摘要和操作者审计完整。

验证

  • fake SYB 状态机与调用次数断言。
  • 数据库并发、租约竞争、幂等重放和重启恢复测试。
  • 在远端调用前/后注入失败,验证状态和检查点。
  • 首次真 SYB 写入保持“未验证”,直至用户单独授权。

风险与文档

外部写入、并发和恢复均属高风险。完成后必须更新架构、状态机、业务规则、安全边界、API、运行排错和 SYB 接口 Wiki。

## 所属与来源 - 父 MVP:#117。 - 来源:用户于 2026-08-27 要求先按已确认的全栈分析方案建单并制作总原型,审核通过后才能实施。 - 前置依赖:#118、#119、#120。 - 类型:高风险外部写入与并发状态机。 ## 目标 安全地把已确认计划异步逐件写入 SYB,并对超时、崩溃和未知结果提供不可误重试的恢复闭环。 ## 方案 - 复用现有 `sybclient.DeleteInnerCode/CreateInnerCodeDetail/UpdateDetailCode` 和 `ErrWriteResultUnknown`。 - 提交时事务创建执行批次、锁定记录并进入 `queued`;使用 UUID `requestId` 幂等。 - 数据库活动槽/worker 租约保证跨进程全局串行;记录领取采用原子状态转换,不能只靠 `sync.Mutex`。 - 每个动作前重读完整 stock 并核对规划身份;远端发生漂移时停止,不执行旧计划。 - 旧码只在与规划快照完全一致时删除;多件时复用可用明细,不足部分创建数量 1、价格 0 的稳定命名占位明细。 - 每项远端修改前后保存单件级结构化检查点;写后重读整个 stock,目标码必须只出现在预期 detail。 - 写请求禁止自动重试。网络超时、连接中断、响应无法确认或崩溃窗口统一进入 `needs_check`。 - `needs_check` 只允许调用只读 stock 查询复核;不提供“一键重试写入”。 - 服务启动恢复:未开始远端动作的队列恢复为待再次确认;处于 applying 或检查点不完整的记录进入 `needs_check`。 - 批量物理删除与回写状态互斥:`queued`、`applying`、`needs_check` 记录必须拒绝删除,防止丢失活动或不明确的远端证据;其余状态按 #119 删除契约处理。 ## 非目标 - 不创建采购订单、不付款。 - 不自动修复歧义、不自动重跑未知写结果。 - 不在本工单执行真实 SYB 写入或生产发布,除非用户再次明确授权。 ## 验收标准 - [ ] 跨两个服务实例仍只有一个远端写执行者。 - [ ] 旧码删除、单件更新、多件占位、已存在目标码和重复空明细行为正确。 - [ ] 所有远端修改前后都有可恢复检查点。 - [ ] 响应前失败、响应后断线、写后核验失败、进程重启等故障注入均不会自动重试。 - [ ] `needs_check` 复核只读,能够判定已成功、未写入或仍不明确。 - [ ] 批量删除无法移除 `queued`、`applying`、`needs_check`,并发提交时由数据库状态条件保证。 - [ ] 批次进度、部分成功、错误摘要和操作者审计完整。 ## 验证 - fake SYB 状态机与调用次数断言。 - 数据库并发、租约竞争、幂等重放和重启恢复测试。 - 在远端调用前/后注入失败,验证状态和检查点。 - 首次真 SYB 写入保持“未验证”,直至用户单独授权。 ## 风险与文档 外部写入、并发和恢复均属高风险。完成后必须更新架构、状态机、业务规则、安全边界、API、运行排错和 SYB 接口 Wiki。
Author
Owner

开始实施。#119 数据模型/导入和 #120 自动只读规划基础已经落地并通过首轮测试。本工单实现确认预检、幂等排队、数据库全局租约、原子领取、逐件检查点、写后整单回读、needs_check 只读复核与启动恢复;所有写入测试只使用 fake SYB,禁止连接真实写接口。

开始实施。#119 数据模型/导入和 #120 自动只读规划基础已经落地并通过首轮测试。本工单实现确认预检、幂等排队、数据库全局租约、原子领取、逐件检查点、写后整单回读、`needs_check` 只读复核与启动恢复;所有写入测试只使用 fake SYB,禁止连接真实写接口。
Author
Owner

实施完成,待用户验收

实现提交:5c6655f;文档提交:fd15ce0、68395ca、2f54155。

  • 回写预览和 UUID 幂等排队已实现;数据库租约保证跨实例全局串行,记录通过条件更新原子领取。
  • 执行前重读完整 stock,并核对主明细、所有既有计划明细、采购字段和旧码未漂移;旧码删除、占位明细创建、逐件写码都保存前后检查点并逐件整单回读。
  • 远端写动作不自动重试;结果不明确转 needs_check。只读复核只能读取 stock,可判定已成功、确认未写入或继续需复核。
  • 启动恢复将未开始的 queued 释放回可回写,将 applying 转为 needs_check;批次进度与执行项保留。
  • fake SYB 覆盖多件/占位/检查点、未知结果、只读复核、请求幂等、租约竞争和重启恢复。未连接真实 SYB、未执行真实写入、未发布生产环境;首次真实写入仍需用户单独授权。

共同验证与文档证据

  • .\scripts\verify.ps1 -Component all:通过(服务端 go test ./... 与构建、Web lint/生产构建、Android 单测与 debug APK 构建);Web lint 仅有 30 条存量 warning,0 error。
  • 专项浏览器验收:npx playwright test tests/e2e/syb-inner-codes.spec.ts,1 passed。
  • 线上 Wiki revision:架构 6ed5b970878ff0211946fbba02c64eaccfe8d8bb;业务规则 4236dd1d219cc6235537adfd8d0411308c449971;产品索引 0d9924b4566e567edf167ba678fedde28a41cfd3;SYB 契约 f596931468caf524cbd9d90b3c299b32a8cf8228。
  • harness.py sync 已完整导出。独立 sync --check 因 Gitea 直连读取超时未返回成功码;随后通过 Gitea MCP 对上述四页逐字比较正文并核对镜像 revision,四页均 content_equal=true、revision_equal=true。
  • 保留无关未跟踪文件 gitea.zip,未纳入提交。
## 实施完成,待用户验收 实现提交:`5c6655f`;文档提交:`fd15ce0`、`68395ca`、`2f54155`。 - 回写预览和 UUID 幂等排队已实现;数据库租约保证跨实例全局串行,记录通过条件更新原子领取。 - 执行前重读完整 stock,并核对主明细、所有既有计划明细、采购字段和旧码未漂移;旧码删除、占位明细创建、逐件写码都保存前后检查点并逐件整单回读。 - 远端写动作不自动重试;结果不明确转 `needs_check`。只读复核只能读取 stock,可判定已成功、确认未写入或继续需复核。 - 启动恢复将未开始的 queued 释放回可回写,将 applying 转为 needs_check;批次进度与执行项保留。 - fake SYB 覆盖多件/占位/检查点、未知结果、只读复核、请求幂等、租约竞争和重启恢复。**未连接真实 SYB、未执行真实写入、未发布生产环境**;首次真实写入仍需用户单独授权。 ### 共同验证与文档证据 - `.\scripts\verify.ps1 -Component all`:通过(服务端 `go test ./...` 与构建、Web lint/生产构建、Android 单测与 debug APK 构建);Web lint 仅有 30 条存量 warning,0 error。 - 专项浏览器验收:`npx playwright test tests/e2e/syb-inner-codes.spec.ts`,1 passed。 - 线上 Wiki revision:架构 `6ed5b970878ff0211946fbba02c64eaccfe8d8bb`;业务规则 `4236dd1d219cc6235537adfd8d0411308c449971`;产品索引 `0d9924b4566e567edf167ba678fedde28a41cfd3`;SYB 契约 `f596931468caf524cbd9d90b3c299b32a8cf8228`。 - `harness.py sync` 已完整导出。独立 `sync --check` 因 Gitea 直连读取超时未返回成功码;随后通过 Gitea MCP 对上述四页逐字比较正文并核对镜像 revision,四页均 `content_equal=true`、`revision_equal=true`。 - 保留无关未跟踪文件 `gitea.zip`,未纳入提交。
Author
Owner

用户于 2026-08-28 明确确认本工单验收通过。按项目流程记录验收结论并关闭工单;本次仅更新工单状态,无新增长期文档事实,不重复同步 Wiki。

用户于 2026-08-28 明确确认本工单验收通过。按项目流程记录验收结论并关闭工单;本次仅更新工单状态,无新增长期文档事实,不重复同步 Wiki。
ila closed this issue 2026-08-28 15:07:24 +08:00
Sign in to join this conversation.
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: OPC/goauto#121