Files
ShiErFeng/docs/superpowers/specs/2026-09-22-test-data-batch-cleanup-design.md
T

17 KiB
Raw Blame History

测试数据批次隔离与级联清理设计

1. 背景

当前菌种、客户、客户现场、产线、仓库和托盘等主数据在被业务单据引用后不允许删除。该约束能保护正式业务的外键完整性、库存状态、报表口径和托盘追溯链,但也导致开发、联调和验收期间产生的测试数据无法安全清理。

本设计引入“测试数据批次”,将正式数据与测试数据从创建、查询、业务操作到报表统计全程隔离。超级管理员可在预览完整影响范围后整批清理测试数据;正式数据在任何情况下都不能通过该能力物理删除。

2. 目标与非目标

2.1 目标

  • 以测试批次为边界隔离正式数据和测试数据。
  • 支持授权的普通用户在指定测试批次内完成入库、装填、发货、使用和退回流程。
  • 支持超级管理员将升级前已有的旧测试数据完整纳入测试批次。
  • 支持关闭批次后预览影响范围,并在单个数据库事务中整批清理。
  • 保留批次元数据和操作审计,但不保留已清理的测试业务明细。
  • 防止前端绕过、权限误配、并发请求或部分失败导致正式数据被删或测试链路残缺。

2.2 非目标

  • 不允许按单条主数据或单据强制级联删除。
  • 不允许将正在进行的批次直接清理。
  • 不用测试清理代替正式业务的停用、作废、冲销或审计流程。
  • 不根据名称、编码、创建人或时间自动猜测旧数据是否为测试数据。
  • 不在本次实施中直接运行生产数据库迁移或清理。

3. 角色与权限边界

能力 超级管理员 已授权测试用户 其他用户
查看测试批次管理 允许 不允许 不允许
创建、关闭、重新开启批次 允许 不允许 不允许
授权批次测试用户 允许 不允许 不允许
创建批次内测试主数据 允许 不允许 不允许
进入已授权批次执行业务 允许 允许 不允许
将旧数据纳入批次 允许 不允许 不允许
预览并整批清理 允许 不允许 不允许
物理删除正式数据 不允许 不允许 不允许

超级管理员以后端 current_user.user.admin 为最终判断,不仅依赖前端角色或菜单权限。测试批次接口可同时使用独立权限字符控制菜单展示,但权限字符被误分配给普通角色时,后端仍必须返回 403。

4. 数据模型

4.1 测试批次

新增 biz_test_batch,至少包含:

  • batch_id:主键。
  • batch_code:系统生成的唯一编码。
  • batch_name:用于界面识别与二次确认。
  • status:ACTIVE、CLOSED、PURGED。
  • create_by、create_time、update_by、update_time。
  • closed_by、closed_at、purged_by、purged_at。
  • purge_summary:保留按数据类型汇总的清理数量,不保留敏感业务内容。
  • remark:批次用途说明。

新增 biz_test_batch_user,保存批次与参与测试用户的关联。该关联只是批次的外层访问权;现有用户-仓库、用户-现场数据范围仍然生效。

4.2 业务数据标记

以下表新增可空 test_batch_id 和索引,并外键引用 biz_test_batch.batch_id:

  • 主数据:biz_strain、biz_customer、biz_customer_site、biz_production_line、biz_warehouse、biz_pallet。
  • 入库:biz_inbound_order、biz_inbound_item。
  • 装填:biz_filling_record。
  • 发货:biz_shipment_order、biz_shipment_item。
  • 使用:biz_usage_record。
  • 退回:biz_return_order、biz_return_item。
  • 库存与追溯:biz_pallet_inventory、biz_pallet_event。

test_batch_id IS NULL 表示正式数据;非空表示所属测试批次。该字段不允许普通业务请求直接写入,由后端从已验证的当前模式或关联记录中派生。

biz_user_warehouse 和 biz_user_site 不重复保存 test_batch_id,其数据归属由所关联的仓库或现场派生。批次清理时,必须先删除指向该批次仓库和现场的用户关联。

4.3 一致性约束

  • 正式数据只能引用正式数据。
  • 测试数据只能引用同一批次的测试数据。
  • 不同测试批次之间不能互相引用。
  • 批次标记一旦创建不能通过通用编辑接口修改。
  • 只有“纳入测试批次”专用流程可将旧的无标记数据一次性改为测试数据。
  • 关联对象的批次一致性由服务层在写入前校验,数据库外键作为存在性和删除顺序的最后保护。
  • 现有编码、单号和二维码唯一约束继续全局生效;测试数据使用独立编码,清理后可重新使用。

5. 数据模式与查询隔离

5.1 模式选择

Web 管理端和 PDA 操作端都需支持:

  • 正式模式:不传测试批次上下文,后端只允许 test_batch_id IS NULL 的数据。
  • 测试模式:携带当前 batch_id。已授权用户只能进入 ACTIVE 批次;超级管理员可在批次管理页以只读方式检查 CLOSED 批次。任何写入都要求批次为 ACTIVE。

前端可通过统一请求头传递当前批次,但该值只是请求上下文,不是信任根。后端必须每次验证用户授权、批次状态和数据归属。

5.2 隔离范围

批次条件必须应用于:

  • 六类基础资料的列表、详情和选项。编码、单号和二维码的唯一性校验仍按全局数据执行,与现有数据库唯一约束保持一致。
  • 入库、装填、发货、使用和退回的列表、详情、扫码、创建、修改和确认。
  • 仓库、现场、产线、菌种和托盘等业务选项接口。
  • 托盘库存、装填、发货、消耗、退回和追溯六类报表。
  • 首页仓储运营指标、待处理数量、趋势图和状态分布。

任何缺失批次条件的查询都可能造成测试数据污染正式报表,实施时必须对所有业务 DAO 做完整盘点。

6. 批次生命周期

ACTIVE --关闭--> CLOSED --重新开启--> ACTIVE
                       |
                       +--预览并确认清理--> PURGED
  • ACTIVE:允许超级管理员创建测试主数据,允许已授权用户执行业务。
  • CLOSED:所有测试数据只读;可查看、预览清理范围、重新开启或清理。
  • PURGED:批次业务数据已删除;仅保留批次元数据和审计汇总,不能重新开启。

关闭、重新开启和清理都需锁定批次记录。每个测试写操作在业务数据加锁前必须重新校验批次仍为 ACTIVE,保证“关闭成功后无新写入”。

7. 新测试数据创建与业务继承

  1. 超级管理员创建批次,分配参与测试的用户。
  2. 超级管理员切换到该批次,创建菌种、客户、现场、产线、仓库和托盘。
  3. 后端根据当前批次自动写入 test_batch_id,忽略或拒绝请求体中伪造的批次字段。
  4. 已授权用户进入该批次,使用批次内的仓库、现场、产线、菌种和托盘执行业务。
  5. 入库单、装填记录、发货单、使用记录、退回单、库存和事件由后端自动继承同一 test_batch_id。
  6. 任一关联数据的批次不一致时,整个业务操作失败并回滚。

8. 旧数据纳入测试批次

8.1 入口

超级管理员在正式模式的基础资料和业务管理页选择旧数据后,可执行“纳入测试批次”。报表页不提供该操作,因为报表行不是独立业务实体;需从其来源主数据或单据发起。

8.2 关联闭包

后端必须沿实际外键与业务关联双向计算连通闭包,直到没有新关联记录。闭包至少覆盖:

  • 菌种与装填记录。
  • 客户、现场与产线层级。
  • 仓库、入库、装填、发货和退回。
  • 托盘、单据明细、库存、使用记录和追溯事件。
  • 与装填周期相关的发货、使用和退回记录。

由于旧数据没有可靠的测试标记,系统无法自动识别闭包中哪些数据被用户视为“正式”。预览必须按表展示数量和关键编号;如果闭包包含需保留的正式数据,管理员必须取消,系统不允许只标记半条关联链。

8.3 确认与写入

  • 目标批次必须为 ACTIVE。
  • 闭包中所有数据必须尚未归属任何测试批次。
  • 管理员需输入目标批次名称二次确认。
  • 服务端使用不透明预览校验标识;确认时重新计算闭包,范围变化则拒绝旧确认。
  • 事务内锁定批次和闭包中的根记录,然后一次性写入所有 test_batch_id。
  • 任何一步失败都回滚,不得出现部分数据已纳入、部分数据仍为正式的状态。

9. 整批清理

9.1 前置条件

  • 操作人必须是超级管理员。
  • 批次必须为 CLOSED。
  • 清理预览需展示每类主数据、单据、明细、库存和事件的数量。
  • 管理员必须输入完整批次名称并通过最终确认。
  • 确认请求必须携带服务端生成的一次性预览标识。

9.2 事务与删除顺序

清理服务在单个数据库事务内锁定批次,重新计算影响范围并校验预览标识。删除顺序必须由实际外键图生成并以回归测试固化,原则为:

  1. 先删除 biz_pallet_inventory,解除其对托盘事件和活动装填记录的外键引用。
  2. 删除 biz_usage_record、biz_return_item、biz_shipment_item 和 biz_inbound_item。
  3. 删除 biz_return_order、biz_shipment_order 和 biz_inbound_order。
  4. 删除 biz_pallet_event,再删除 biz_filling_record。
  5. 删除指向测试仓库、现场的 biz_user_warehouse 和 biz_user_site 关联。
  6. 先删除产线,再删除现场和客户;随后删除仓库、菌种和托盘等其他主数据。
  7. 删除批次用户授权,更新批次为 PURGED,写入清理汇总。

实施时以 SQLAlchemy 模型和 MySQL/PostgreSQL 外键的实际编译结果为准,不依赖上述文字顺序代替自动化测试。

不对业务外键配置 ON DELETE CASCADE。如果任何外键、并发状态或数据库错误导致删除失败,整个事务回滚,批次保持 CLOSED。

9.3 幂等与审计

  • 同一批次的并发清理只有一个可以执行,其他请求等待后读取最新状态。
  • 对 PURGED 批次重复请求返回稳定的“已清理”结果,不重复执行删除。
  • 批次记录保留清理人、清理时间和分类数量。
  • 系统操作日志保留,作为审计证据;日志不记录密码、Token、Cookie 或大量业务明细。

10. 界面与交互

10.1 测试数据批次管理

在“系统管理”下新增“测试数据批次”页面,仅超级管理员可见。页面支持:

  • 查看批次编码、名称、状态、创建人、参与用户和时间。
  • 创建批次、编辑备注、授权或移除测试用户。
  • 关闭或重新开启未清理批次。
  • 查看数据汇总、清理预览和最终清理确认。
  • 查看已清理批次的审计汇总。

10.2 模式提示

  • Web 顶部和 PDA 工作台提供“正式数据 / 已授权测试批次”切换。
  • 测试模式始终展示高可见的批次名称和“测试数据”标识,不得只在隐蔽的下拉框中表示。
  • 切换模式后刷新当前列表、选项、首页指标和报表,并清空未提交的跨模式表单状态。
  • 批次被关闭、授权被移除或会话失效时,普通测试用户退回正式模式并得到明确提示;超级管理员仍可从批次管理页查看关闭批次的只读汇总和清理预览。

10.3 现有删除入口

  • 基础资料、业务管理和报表页不再提供物理删除入口。
  • 正式主数据通过停用保留历史引用;正式业务单据按现有业务状态处理,不使用测试批次能力物理删除。
  • 旧的删除 API 保留路由兼容,但统一拒绝物理删除并提示“请通过测试数据批次清理”。
  • “纳入测试批次”只在正式模式下向超级管理员展示。

11. 后端组件与接口边界

建议新增独立的批次控制器、服务、DAO 和 VO,不将级联清理逻辑分散到六个主数据服务中。职责划分如下:

  • 批次管理:创建、列表、详情、用户授权、关闭和重新开启。
  • 批次上下文:解析请求模式,校验用户与批次,为查询和写入提供受信任的 batch_id | None。
  • 批次一致性校验:根据业务输入校验所有关联数据处于同一模式和批次。
  • 旧数据纳入:计算关联闭包、生成预览、校验确认并原子写入批次标记。
  • 整批清理:统计影响数据、生成预览、校验确认并原子删除。

预览与执行必须分为不同接口。执行接口不接受前端传入的“要删哪些表或 ID”,只接受批次 ID、批次名称确认和一次性预览标识;实际范围始终由后端重算。

12. 错误处理

需向用户明确区分以下失败:

  • 非超级管理员访问管理或清理接口。
  • 用户未被授权访问所选测试批次。
  • 批次已关闭、已清理或在操作期间状态发生变化。
  • 请求将正式数据与测试数据或不同批次数据混用。
  • 旧数据关联闭包包含已属于其他批次的数据。
  • 预览后数据范围发生变化,一次性校验标识失效。
  • 批次名称确认不匹配。
  • 外键、并发或数据库错误触发整体回滚。

前端不将未知错误表示为成功,也不在失败后乐观移除列表记录。所有成功状态均以服务端提交后重新查询的结果为准。

13. 数据库迁移与发布

  • 同步更新 MySQL 和 PostgreSQL 全量建库脚本。
  • 为已有环境提供 MySQL 和 PostgreSQL 增量补丁。
  • 先创建批次与授权表,再以可空字段方式增加 test_batch_id、索引和外键。
  • 既有记录的 test_batch_id 保持 NULL,迁移不自动更新业务数据。
  • 发布前必须完成数据库备份和恢复演练;应用迁移和首次旧数据纳入属于独立高风险操作,需单独确认后执行。
  • 先发布后端与前端代码,确认正式模式查询不回归,再创建新测试批次或纳入旧数据。

14. 测试与验证

14.1 后端

  • 批次生命周期和不合法状态转换。
  • 超级管理员硬校验,包括普通用户持有误分配权限字符的情况。
  • 批次用户授权与现有仓库/现场数据范围的叠加效果。
  • 正式、同批次和跨批次关联校验。
  • 创建主数据和五类业务时的批次自动继承。
  • 主数据、业务列表、选项、扫码、首页和六类报表的模式隔离。
  • 旧数据闭包的完整性、循环去重、跨批次拒绝和事务回滚。
  • 关闭批次后所有写入被拒绝,重新开启后恢复。
  • 清理预览计数、预览过期、确认文本、外键删除顺序、失败回滚和重复清理幂等性。
  • MySQL 和 PostgreSQL 的 SQL 编译与迁移契约。

14.2 Web 与 PDA

  • 超级管理员、已授权测试用户和未授权用户的菜单与按钮可见性。
  • 正式/测试模式切换、批次标识、请求上下文和授权失效回退。
  • 旧数据纳入预览、批次名称二次确认和预览过期提示。
  • 批次关闭后的只读界面和清理确认。
  • 现有物理删除按钮移除,直接调用旧删除 API 时的稳定拒绝提示。
  • Web 生产构建、PDA H5 构建、最相关契约测试和后端业务单元测试。

15. 验收标准

  1. 正式模式的页面、选项、首页和报表不展示任何测试数据。
  2. 测试模式只展示当前批次数据,不显示正式数据或其他批次数据。
  3. 授权的普通用户可完成测试业务链,但无法管理、纳入、关闭或清理批次。
  4. 正式与测试数据、不同测试批次之间不能混用。
  5. 超级管理员可预览并完整纳入旧测试数据,范围变化时旧确认失效。
  6. ACTIVE 批次不能清理;CLOSED 批次不能新增或修改业务数据。
  7. 整批清理成功后,所有相关主数据、单据、明细、库存和追溯记录消失,报表不再返回该批次数据。
  8. 清理任一步失败时不删除任何数据,批次仍为 CLOSED。
  9. 超级管理员也无法通过批次清理或旧删除 API 物理删除正式数据。
  10. 已清理批次保留操作人、时间和分类数量,且日志不包含凭据或敏感信息。

16. 已知风险与控制

  • 旧数据闭包可能过大:旧测试数据如果复用了正式仓库、现场或托盘,关联闭包可能扩展到大量正式数据。系统必须展示完整预览并允许取消,不提供忽略关联的强制选项。
  • 大批量事务时间较长:在目标数据库上通过压力测试确定在线清理上限,并将上限写入部署配置和运维文档。在线接口必须阻止超过上限的批次,且不得通过分段提交规避原子性;超大批次由 DBA 在备份后使用受审计脚本处理。数值上限依赖目标环境,本设计不在缺少实测数据时猜测固定值。
  • 请求上下文被伪造:后端每次验证授权、批次状态和数据归属,不相信前端显示或请求头本身。
  • 漏加查询条件导致统计污染:使用统一批次上下文和 DAO 查询辅助方法,并用契约测试盘点全部业务查询。
  • 仅前端隐藏不能提供安全性:批次管理、纳入和清理均在后端强制超级管理员检查。
  • 迁移与实际数据存在差异:应用前在目标数据库执行只读外键和表结构盘点,与代码模型和迁移脚本三方对照,不假设生产结构与仓库 SQL 完全一致。