All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 8m3s
6.5 KiB
6.5 KiB
ADDED Requirements
Requirement: 批量归属决策
奇成迁移脚本 SHALL 使用 mapping.yaml 的批量归属规则决定卡和设备的目标店铺。奇成 agent_id 只能作为可选参考信息输出到审核文件,MUST NOT 作为默认归属决策来源。
Scenario: 默认店铺分配设备
- WHEN
ownership_rules.device.mode = default_shop且default_target_shop_code = KWTX - THEN 所有未被
overrides.devices覆盖的设备迁移 SQL 使用KWTX对应的tb_shop.id作为shop_id - AND
ownership_resolution.csv记录每台设备的决策来源为default_shop
Scenario: 默认店铺分配独立卡
- WHEN
ownership_rules.standalone_card.mode = default_shop且default_target_shop_code = KWTX - THEN 所有未绑定设备且未被
overrides.cards覆盖的卡迁移 SQL 使用KWTX对应的tb_shop.id作为shop_id
Scenario: 覆盖项优先于批量规则
- WHEN 某设备在
overrides.devices中配置了target_shop_code = OTHER - THEN 该设备使用
OTHER作为目标店铺 - AND 审核文件记录决策来源为
override
Scenario: 目标店铺缺失阻断
- WHEN 规则要求资产分配到某个
target_shop_code,但新库不存在该店铺 - THEN 脚本 MUST 在
errors.csv写入该资产的错误 - AND 脚本 MUST NOT 为该资产生成店铺分配 SQL
Requirement: 显式设备主卡和套餐来源槽位
奇成迁移脚本 SHALL 支持在输入数据或 mapping.yaml 中显式指定设备 current_slot 和 package_source_slot。未逐行指定时,脚本 SHALL 使用 ownership_rules.device.current_slot 和 ownership_rules.device.package_source_slot 作为批量默认值。
Scenario: 使用批量默认槽位
- WHEN
devices.csv某设备未填写current_slot和package_source_slot - AND
ownership_rules.device.current_slot = 2 - AND
ownership_rules.device.package_source_slot = 2 - THEN 该设备的当前绑定卡和套餐来源卡均使用
sim_iccid_2
Scenario: 使用设备行覆盖槽位
- WHEN
devices.csv某设备填写current_slot = 1和package_source_slot = 1 - THEN 该设备使用第 1 槽作为当前槽位和套餐来源槽位
- AND 该行配置优先于批量默认槽位
Scenario: 槽位无卡阻断设备套餐
- WHEN 某设备的
package_source_slot = 2,但sim_iccid_2为空 - THEN 脚本 MUST 在
errors.csv写入该设备的错误 - AND 脚本 MUST NOT 为该设备生成套餐使用记录
Requirement: 单卡与设备迁移分支分离
奇成迁移脚本 SHALL 分离独立卡套餐迁移和设备套餐迁移。独立卡套餐 MUST 写入 usage_type = single_card 并关联 iot_card_id;设备套餐 MUST 写入 usage_type = device 并关联 device_id。
Scenario: 独立卡生成单卡套餐
- WHEN 某卡未绑定设备且命中可迁移套餐
- THEN 脚本为该卡生成
tb_order.order_type = single_card - AND 脚本为该卡生成
tb_package_usage.usage_type = single_card
Scenario: 设备绑定卡生成设备套餐
- WHEN 某卡位于设备
package_source_slot - THEN 脚本为该设备生成
tb_order.order_type = device - AND 脚本为该设备生成
tb_package_usage.usage_type = device
Scenario: 设备非来源槽位不重复生成套餐
- WHEN 某卡绑定在设备非
package_source_slot的槽位 - THEN 脚本 MUST NOT 为该卡单独生成主套餐使用记录
- AND 该卡仍 SHALL 生成基础卡资产和设备绑定 SQL
Requirement: 当前生效和待生效套餐迁移
奇成迁移脚本 SHALL 迁移正式套餐中的当前生效套餐和未生效待生效套餐。当前生效套餐写入 tb_package_usage.status = 1;待生效套餐写入 tb_package_usage.status = 0 并设置稳定 priority。
Scenario: 当前生效套餐迁移为 active
- WHEN 奇成某正式套餐处于当前生效状态
- THEN 脚本生成的
tb_package_usage.status = 1 - AND
activated_at和expires_at来源于奇成套餐生命周期时间
Scenario: 未生效套餐迁移为 pending
- WHEN 奇成某正式套餐属于未生效待生效套餐
- THEN 脚本生成的
tb_package_usage.status = 0 - AND
data_usage_mb = 0 - AND
priority按待生效顺序稳定递增
Scenario: 已过期套餐不迁移
- WHEN 奇成某正式套餐已过期且不属于当前生效或待生效范围
- THEN 脚本 MUST NOT 为该套餐生成
tb_package_usage - AND
package_resolution.csv记录跳过原因
Scenario: 目标套餐映射缺失阻断
- WHEN 某可迁移 legacy 套餐没有配置
target_package_id - THEN 脚本 MUST 在
errors.csv或package_resolution.csv中记录缺失映射 - AND 脚本 MUST NOT 为该套餐生成套餐使用记录
Requirement: 迁移审核产物
奇成迁移脚本 SHALL 在生成 SQL 时同步输出可审计 CSV,说明每个资产和套餐的最终决策。
Scenario: 输出归属审核文件
- WHEN 执行
migrate_assets.py - THEN 脚本输出
ownership_resolution.csv - AND 文件至少包含资产类型、资产标识、来源卡、目标店铺、决策来源、是否生成分配、原因
Scenario: 输出套餐审核文件
- WHEN 执行
migrate_runtime.py - THEN 脚本输出
package_resolution.csv - AND 文件至少包含资产类型、资产标识、来源卡、legacy 套餐 ID、legacy 套餐名称、目标套餐 ID、目标状态、优先级、决策、原因
Scenario: 摘要显示关键数量
- WHEN 脚本生成完成
- THEN
summary.txtMUST 展示卡数、设备数、分配资产数、active 套餐数、pending 套餐数、错误数和跳过数
Requirement: 迁移脚本只读和幂等边界
奇成迁移脚本 SHALL 保持对奇成库只读,并生成可重复执行的 PostgreSQL SQL 文件。脚本 MUST NOT 直接写新库业务数据。
Scenario: 奇成连接只读
- WHEN 脚本连接奇成库查询元数据和套餐生命周期
- THEN 连接 MUST 使用只读事务
Scenario: 生成 SQL 幂等
- WHEN 同一批次 SQL 被重复执行
- THEN 插入类语句 SHALL 使用唯一键或条件确保不会重复创建相同资产、订单、套餐使用记录和分配记录
Scenario: 不直接写新库
- WHEN 执行 Python 迁移脚本
- THEN 脚本只生成 SQL 和审核文件
- AND 脚本 MUST NOT 直接向新库写入业务表