17 KiB
测试数据批次隔离与级联清理设计
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. 新测试数据创建与业务继承
- 超级管理员创建批次,分配参与测试的用户。
- 超级管理员切换到该批次,创建菌种、客户、现场、产线、仓库和托盘。
- 后端根据当前批次自动写入
test_batch_id,忽略或拒绝请求体中伪造的批次字段。 - 已授权用户进入该批次,使用批次内的仓库、现场、产线、菌种和托盘执行业务。
- 入库单、装填记录、发货单、使用记录、退回单、库存和事件由后端自动继承同一
test_batch_id。 - 任一关联数据的批次不一致时,整个业务操作失败并回滚。
8. 旧数据纳入测试批次
8.1 入口
超级管理员在正式模式的基础资料和业务管理页选择旧数据后,可执行“纳入测试批次”。报表页不提供该操作,因为报表行不是独立业务实体;需从其来源主数据或单据发起。
8.2 关联闭包
后端必须沿实际外键与业务关联双向计算连通闭包,直到没有新关联记录。闭包至少覆盖:
- 菌种与装填记录。
- 客户、现场与产线层级。
- 仓库、入库、装填、发货和退回。
- 托盘、单据明细、库存、使用记录和追溯事件。
- 与装填周期相关的发货、使用和退回记录。
由于旧数据没有可靠的测试标记,系统无法自动识别闭包中哪些数据被用户视为“正式”。预览必须按表展示数量和关键编号;如果闭包包含需保留的正式数据,管理员必须取消,系统不允许只标记半条关联链。
8.3 确认与写入
- 目标批次必须为
ACTIVE。 - 闭包中所有数据必须尚未归属任何测试批次。
- 管理员需输入目标批次名称二次确认。
- 服务端使用不透明预览校验标识;确认时重新计算闭包,范围变化则拒绝旧确认。
- 事务内锁定批次和闭包中的根记录,然后一次性写入所有
test_batch_id。 - 任何一步失败都回滚,不得出现部分数据已纳入、部分数据仍为正式的状态。
9. 整批清理
9.1 前置条件
- 操作人必须是超级管理员。
- 批次必须为
CLOSED。 - 清理预览需展示每类主数据、单据、明细、库存和事件的数量。
- 管理员必须输入完整批次名称并通过最终确认。
- 确认请求必须携带服务端生成的一次性预览标识。
9.2 事务与删除顺序
清理服务在单个数据库事务内锁定批次,重新计算影响范围并校验预览标识。删除顺序必须由实际外键图生成并以回归测试固化,原则为:
- 先删除
biz_pallet_inventory,解除其对托盘事件和活动装填记录的外键引用。 - 删除
biz_usage_record、biz_return_item、biz_shipment_item和biz_inbound_item。 - 删除
biz_return_order、biz_shipment_order和biz_inbound_order。 - 删除
biz_pallet_event,再删除biz_filling_record。 - 删除指向测试仓库、现场的
biz_user_warehouse和biz_user_site关联。 - 先删除产线,再删除现场和客户;随后删除仓库、菌种和托盘等其他主数据。
- 删除批次用户授权,更新批次为
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. 验收标准
- 正式模式的页面、选项、首页和报表不展示任何测试数据。
- 测试模式只展示当前批次数据,不显示正式数据或其他批次数据。
- 授权的普通用户可完成测试业务链,但无法管理、纳入、关闭或清理批次。
- 正式与测试数据、不同测试批次之间不能混用。
- 超级管理员可预览并完整纳入旧测试数据,范围变化时旧确认失效。
ACTIVE批次不能清理;CLOSED批次不能新增或修改业务数据。- 整批清理成功后,所有相关主数据、单据、明细、库存和追溯记录消失,报表不再返回该批次数据。
- 清理任一步失败时不删除任何数据,批次仍为
CLOSED。 - 超级管理员也无法通过批次清理或旧删除 API 物理删除正式数据。
- 已清理批次保留操作人、时间和分类数量,且日志不包含凭据或敏感信息。
16. 已知风险与控制
- 旧数据闭包可能过大:旧测试数据如果复用了正式仓库、现场或托盘,关联闭包可能扩展到大量正式数据。系统必须展示完整预览并允许取消,不提供忽略关联的强制选项。
- 大批量事务时间较长:在目标数据库上通过压力测试确定在线清理上限,并将上限写入部署配置和运维文档。在线接口必须阻止超过上限的批次,且不得通过分段提交规避原子性;超大批次由 DBA 在备份后使用受审计脚本处理。数值上限依赖目标环境,本设计不在缺少实测数据时猜测固定值。
- 请求上下文被伪造:后端每次验证授权、批次状态和数据归属,不相信前端显示或请求头本身。
- 漏加查询条件导致统计污染:使用统一批次上下文和 DAO 查询辅助方法,并用契约测试盘点全部业务查询。
- 仅前端隐藏不能提供安全性:批次管理、纳入和清理均在后端强制超级管理员检查。
- 迁移与实际数据存在差异:应用前在目标数据库执行只读外键和表结构盘点,与代码模型和迁移脚本三方对照,不假设生产结构与仓库 SQL 完全一致。