Files
Break 1f634eb465
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 8m3s
init
2026-06-16 15:15:50 +08:00

6.5 KiB
Raw Blame History

ADDED Requirements

Requirement: 批量归属决策

奇成迁移脚本 SHALL 使用 mapping.yaml 的批量归属规则决定卡和设备的目标店铺。奇成 agent_id 只能作为可选参考信息输出到审核文件MUST NOT 作为默认归属决策来源。

Scenario: 默认店铺分配设备

  • WHEN ownership_rules.device.mode = default_shopdefault_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_shopdefault_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_slotpackage_source_slot。未逐行指定时,脚本 SHALL 使用 ownership_rules.device.current_slotownership_rules.device.package_source_slot 作为批量默认值。

Scenario: 使用批量默认槽位

  • WHEN devices.csv 某设备未填写 current_slotpackage_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 = 1package_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_atexpires_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.csvpackage_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.txt MUST 展示卡数、设备数、分配资产数、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 直接向新库写入业务表