This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-06-12
|
||||
@@ -0,0 +1,137 @@
|
||||
## Context
|
||||
|
||||
当前奇成迁移脚本分为 `scan_legacy.py`、`migrate_assets.py`、`migrate_runtime.py`。脚本可以生成卡、设备、绑定、分配、伪订单、套餐使用记录和系列回填 SQL,但多个关键决策依赖奇成查询结果和脚本默认值:
|
||||
|
||||
- 设备归属由 `sim_iccid_1` 对应卡的奇成 `agent_id` 推导。
|
||||
- 设备套餐来源由 `current_slot` 决定,但 CSV 未提供时默认 `1`。
|
||||
- 套餐迁移只解析一条当前正式套餐,无法覆盖“当前生效 + 未生效待生效”的队列。
|
||||
- 卡和设备共用一套套餐解析路径,导致设备绑定卡、独立卡、卡级系列和设备级系列边界不清。
|
||||
- 缺失代理映射或未命中奇成归属时,脚本会把资产留在平台库存,无法体现人工指定的迁移目标。
|
||||
|
||||
本次变更只涉及 `scripts/migration/` 下的离线迁移脚本和文档,不改变线上 Go API、Handler/Service/Store/Model 分层和业务数据库结构。
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
- 让 `mapping.yaml` 成为迁移决策源:资产给哪个店铺、设备当前槽位、套餐来源槽位、套餐迁移范围都由配置决定。
|
||||
- 支持万级迁移的批量规则:默认店铺、设备/独立卡分支规则、按槽位规则、少量资产级覆盖。
|
||||
- 拆分单卡迁移与设备迁移分支,避免独立卡和设备绑定卡共用不清晰逻辑。
|
||||
- 迁移当前生效和未生效待生效正式套餐,分别写入 `tb_package_usage.status=1` 和 `status=0`。
|
||||
- 为每次生成 SQL 输出可审计 CSV,让业务和开发能在执行前确认归属、套餐映射和跳过原因。
|
||||
- 保持奇成连接只读、SQL 幂等、人工执行和当前脚本目录内的轻量实现方式。
|
||||
|
||||
**Non-Goals:**
|
||||
- 不新增后台 API 或前端页面。
|
||||
- 不新增数据库表或修改线上业务模型。
|
||||
- 不迁移历史已过期套餐、加油包、历史订单明细或历史流量日明细。
|
||||
- 不逐资产要求配置店铺;逐资产配置仅用于异常覆盖。
|
||||
- 不新增自动化测试或 `_test.go`。
|
||||
|
||||
## Decisions
|
||||
|
||||
### 决策 1:使用“批量规则 + 覆盖项”的配置模型
|
||||
|
||||
**选择**:在 `mapping.yaml` 中新增 `ownership_rules`、`package_rules`、`overrides` 三类配置。
|
||||
|
||||
示例:
|
||||
|
||||
```yaml
|
||||
ownership_rules:
|
||||
default_target_shop_code: KWTX
|
||||
device:
|
||||
mode: default_shop
|
||||
current_slot: 2
|
||||
package_source_slot: 2
|
||||
standalone_card:
|
||||
mode: default_shop
|
||||
|
||||
package_rules:
|
||||
migrate_statuses:
|
||||
- active
|
||||
- pending
|
||||
packages:
|
||||
- legacy_meal_id: D6695908379395072
|
||||
target_package_id: 40
|
||||
|
||||
overrides:
|
||||
devices:
|
||||
- virtual_no: "862639073940258"
|
||||
target_shop_code: OTHER
|
||||
current_slot: 1
|
||||
package_source_slot: 1
|
||||
cards:
|
||||
- iccid: "89861590172420360956"
|
||||
target_shop_code: OTHER
|
||||
```
|
||||
|
||||
**理由**:单批上万资产不能逐条配置,默认规则覆盖 99% 场景,覆盖项只处理少量例外。
|
||||
|
||||
**替代方案**:继续使用 `mapping.agents` 从奇成代理推导店铺。否决原因:奇成代理不是本次迁移的权威归属,且无代理或代理未映射时会出现静默漏分配。
|
||||
|
||||
### 决策 2:拆分独立卡与设备套餐分支
|
||||
|
||||
**选择**:
|
||||
- 独立卡:只为未绑定设备的卡生成 `usage_type='single_card'`。
|
||||
- 设备:只按设备的 `package_source_slot` 对应卡生成 `usage_type='device'`,其他槽位卡只做绑定和卡基础信息,不重复生成主套餐。
|
||||
|
||||
**理由**:新系统运行态按设备维度判断设备套餐和复机条件,设备内多卡不能重复生成并列主套餐。独立卡和设备套餐生命周期也需要不同的资产定位字段。
|
||||
|
||||
**替代方案**:继续逐卡生成套餐后再靠 `_runtime_asset_for_card()` 折叠。否决原因:折叠逻辑隐蔽,难以解释某张卡为什么迁或不迁。
|
||||
|
||||
### 决策 3:完整查询正式套餐生命周期并映射为 active/pending 队列
|
||||
|
||||
**选择**:新增查询函数读取每张来源卡在奇成 `tbl_card_life` 中所有正式套餐记录,排除 `type=1` 加油包和已过期记录,按当前时间和状态识别:
|
||||
- 当前生效:写入 `status=1`,保留 `activated_at`、`expires_at` 和已用量。
|
||||
- 未生效待生效:写入 `status=0`,`data_usage_mb=0`,按开始/到期时间和队列顺序设置 `priority`。
|
||||
|
||||
**理由**:新系统已支持 `PackageUsageStatusPending=0` 和队列激活,能承接待生效套餐。当前脚本只取一条最大过期时间套餐,解释不了“20G/100G 只迁一个”这类问题。
|
||||
|
||||
**替代方案**:只迁当前生效套餐。否决原因:业务已确认必须迁移当前生效 + 未生效待生效套餐。
|
||||
|
||||
### 决策 4:生成审核 CSV 作为执行前合同
|
||||
|
||||
**选择**:每次生成 SQL 时输出:
|
||||
- `ownership_resolution.csv`:资产类型、资产标识、来源卡、目标店铺、决策来源、是否分配、原因。
|
||||
- `package_resolution.csv`:资产类型、资产标识、来源卡、legacy 套餐、目标套餐、目标状态、优先级、决策、原因。
|
||||
- `summary.txt`:按资产类型、状态、错误类型汇总。
|
||||
|
||||
**理由**:迁移脚本是一次性高风险操作,执行前必须能从产物直接解释每个资产和套餐的处理结果。
|
||||
|
||||
**替代方案**:只写 `errors.csv` / `warnings.csv`。否决原因:只能看到异常,无法证明正常路径是否符合业务预期。
|
||||
|
||||
### 决策 5:关键缺失项阻断,不再静默降级
|
||||
|
||||
**选择**:以下情况必须写入 `errors.csv` 并跳过对应资产或套餐:
|
||||
- 目标店铺码不存在或为空且规则要求分配。
|
||||
- 设备 `current_slot` / `package_source_slot` 不在 1-4 或对应槽位无卡。
|
||||
- legacy 套餐没有 `target_package_id`。
|
||||
- 目标套餐不存在、已删除或不是正式套餐。
|
||||
- 同一资产产生多个 active 主套餐且无法按规则裁决。
|
||||
|
||||
**理由**:静默进入平台库存或漏迁套餐会让后续人工排查成本远高于生成期阻断。
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- **[风险] 新配置结构与旧 `mapping.yaml` 不兼容** → 提供清晰错误和示例,必要时保留旧字段读取但输出升级提示。
|
||||
- **[风险] 奇成套餐状态字段与真实生效窗口不一致** → 审核 CSV 同时输出奇成原始 `status/type/start_date/expire_date`,便于人工确认。
|
||||
- **[风险] 待生效套餐优先级排序错误会影响自动激活顺序** → 按 `start_date ASC, expire_date ASC, legacy_life_id ASC` 生成稳定排序,并在 `package_resolution.csv` 输出 priority。
|
||||
- **[风险] 批量默认店铺误配会影响大量资产** → `ownership_resolution.csv` 必须在执行 SQL 前人工抽查;summary 输出目标店铺分布。
|
||||
- **[Trade-off] 不做 UI 化配置** → 先保持脚本和 YAML,满足迁移批次的速度和可审查性,后续如迁移频率升高再考虑管理界面。
|
||||
|
||||
## Migration Plan
|
||||
|
||||
1. 扩展 `mapping.yaml.example` 和 `resources/README.md`,明确批量规则、槽位字段和审核流程。
|
||||
2. 增强配置加载器,新增结构化配置对象和校验错误。
|
||||
3. 扩展 CSV 加载器,支持 `current_slot` / `package_source_slot` 列;未填时从 `ownership_rules.device` 取默认值。
|
||||
4. 扩展奇成查询,读取完整正式套餐生命周期。
|
||||
5. 重构 SQL 构造逻辑,拆分独立卡和设备套餐生成。
|
||||
6. 新增审核 CSV 与阻断错误输出。
|
||||
7. 用当前 120 台设备样例和新增小样例手动生成 SQL,核对 120 台归属、4 类套餐迁移问题和待生效队列。
|
||||
|
||||
**回滚**:本变更只影响 SQL 生成脚本。若生成产物不符合预期,不执行输出 SQL 即可;已生成 SQL 可删除后用旧分支重新生成。
|
||||
|
||||
## Open Questions
|
||||
|
||||
- 是否所有迁移批次默认都只有一个目标店铺,还是需要支持按资源文件分组指定多个默认店铺?
|
||||
- 待生效套餐 `activated_at` 是否保留奇成 `start_date`,还是写 `NULL` 等待新系统激活时回填?
|
||||
- 同一来源卡存在多个奇成 `status=1` 正式套餐时,是否按时间窗口裁决一个 active,其余转 pending,还是直接阻断人工处理?
|
||||
@@ -0,0 +1,43 @@
|
||||
## Why
|
||||
|
||||
当前奇成迁移脚本把奇成 `agent_id`、默认 `sim_iccid_1`、单一当前套餐查询结果当成迁移决策来源,导致批量设备店铺归属、设备主卡、套餐系列和套餐队列迁移不可控。随着单批上万到几万张卡迁移成为常态,脚本需要改为由 `mapping.yaml` 的批量规则驱动,奇成只提供历史事实,生成可审核的中间产物后再输出 SQL。
|
||||
|
||||
功能 ID:`feature-qicheng-migration-control`
|
||||
|
||||
## What Changes
|
||||
|
||||
- **重构迁移决策来源**:`mapping.yaml` 成为资产归属、设备当前槽位、套餐来源槽位、套餐迁移范围的唯一决策入口;奇成只用于读取运营商、套餐生命周期、流量和佣金等历史事实。
|
||||
- **新增批量归属规则**:支持默认店铺、按设备/独立卡分支配置、按槽位配置和少量资产级覆盖,避免万级迁移逐卡配置。
|
||||
- **拆分单卡迁移与设备迁移分支**:独立卡生成 `single_card` 套餐使用记录;设备绑定卡按设备维度生成 `device` 套餐使用记录,设备内卡不重复生成主套餐。
|
||||
- **支持当前生效 + 未生效待生效套餐迁移**:完整读取奇成正式套餐生命周期,当前生效套餐写为 `status=1`,后续待生效套餐写为 `status=0` 并按优先级排队。
|
||||
- **新增审核产物**:生成 `package_resolution.csv`、`ownership_resolution.csv` 和摘要文件,明确每个资产/套餐的决策、映射、跳过原因和缺失项。
|
||||
- **增强阻断规则**:关键映射缺失、目标店铺不存在、目标套餐无效、设备主卡槽位缺失等必须进入错误清单并阻断对应资产,不再静默进入平台库存或漏写系列。
|
||||
- **更新脚本文档**:同步更新 `scripts/migration/README.md`、`resources/README.md` 和奇成迁移方案文档,说明新配置模型、执行顺序和人工审核点。
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
- `qicheng-migration-control`:定义奇成迁移脚本的批量归属决策、设备/单卡分支、套餐队列迁移、审核产物和阻断规则。
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
无。
|
||||
|
||||
## Impact
|
||||
|
||||
**脚本与配置**:
|
||||
- `scripts/migration/config/mapping.yaml.example`:新增批量归属、槽位、套餐迁移范围和覆盖配置示例。
|
||||
- `scripts/migration/lib/mapping_loader.py`:加载并校验新的配置结构,保留旧配置兼容或提供明确升级错误。
|
||||
- `scripts/migration/lib/csv_loader.py`:支持设备 `current_slot`、`package_source_slot` 或由批量规则填充默认槽位。
|
||||
- `scripts/migration/lib/legacy_query.py`:新增读取完整正式套餐生命周期的查询。
|
||||
- `scripts/migration/lib/sql_builder.py`:拆分单卡/设备套餐生成逻辑,生成审核 CSV 和阻断错误。
|
||||
- `scripts/migration/scan_legacy.py`、`migrate_assets.py`、`migrate_runtime.py`:按新规则消费配置并输出 SQL。
|
||||
|
||||
**数据库与业务系统**:
|
||||
- 不新增业务表,不修改 API,不引入外部依赖。
|
||||
- 继续输出人工执行 SQL,保持只读访问奇成、幂等插入和线上手动执行模式。
|
||||
|
||||
**验证**:
|
||||
- 不新增自动化测试或 `_test.go`。
|
||||
- 通过样例 CSV、生成的 `errors.csv` / `warnings.csv` / 审核 CSV、SQL 内容核对和测试库手动 SQL 查询验证。
|
||||
@@ -0,0 +1,143 @@
|
||||
## 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.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 直接向新库写入业务表
|
||||
@@ -0,0 +1,55 @@
|
||||
## 1. 配置与文档
|
||||
|
||||
- [x] 1.1 更新 `scripts/migration/config/mapping.yaml.example`,新增 `ownership_rules`、`package_rules`、`overrides` 示例
|
||||
- [x] 1.2 更新 `scripts/migration/resources/README.md`,说明 `current_slot`、`package_source_slot`、批量默认槽位和覆盖优先级
|
||||
- [x] 1.3 更新 `scripts/migration/README.md`,说明新执行流程、审核 CSV、错误阻断规则和手动验证方法
|
||||
- [x] 1.4 更新 `docs/脚本/奇成数据迁移方案.md`,将范围改为当前生效 + 未生效待生效套餐,并说明 mapping.yaml 是决策源
|
||||
|
||||
## 2. 配置加载与输入解析
|
||||
|
||||
- [x] 2.1 扩展 `lib/mapping_loader.py`,新增归属规则、套餐规则、覆盖规则的数据结构
|
||||
- [x] 2.2 在 `mapping_loader` 中实现配置校验:店铺码格式、槽位范围、套餐规则、覆盖项唯一性
|
||||
- [x] 2.3 扩展 `lib/csv_loader.py`,支持读取设备行级 `current_slot` 和 `package_source_slot`
|
||||
- [x] 2.4 实现设备槽位解析优先级:设备行配置 > `overrides.devices` > `ownership_rules.device` 默认值
|
||||
- [x] 2.5 保留或显式拒绝旧配置结构,输出中文错误提示和升级指引
|
||||
|
||||
## 3. 奇成套餐生命周期查询
|
||||
|
||||
- [x] 3.1 在 `lib/legacy_query.py` 新增正式套餐生命周期查询函数,读取当前生效和未生效待生效套餐所需字段
|
||||
- [x] 3.2 查询结果保留 legacy 套餐 ID、套餐名、类型、状态、开始时间、到期时间和稳定排序键
|
||||
- [x] 3.3 排除加油包和已过期套餐,并在审核产物中记录跳过原因
|
||||
- [x] 3.4 处理同一资产多个 active 主套餐的冲突:无法唯一裁决时写入错误并阻断该资产套餐生成
|
||||
|
||||
## 4. 归属决策与资产 SQL
|
||||
|
||||
- [x] 4.1 新增归属解析模块或函数,统一输出资产目标店铺、决策来源和错误
|
||||
- [x] 4.2 修改卡资产生成逻辑:独立卡按 `ownership_rules.standalone_card` 和覆盖项确定店铺
|
||||
- [x] 4.3 修改设备资产生成逻辑:设备按 `ownership_rules.device`、槽位配置和覆盖项确定店铺
|
||||
- [x] 4.4 修改设备绑定逻辑:`is_current` 使用解析后的 `current_slot`,不再固定默认 slot1
|
||||
- [x] 4.5 修改分配记录生成逻辑:只按归属解析结果生成,不再依赖奇成 `agent_id`
|
||||
|
||||
## 5. 套餐队列 SQL
|
||||
|
||||
- [x] 5.1 拆分独立卡套餐生成路径,生成 `single_card` 订单和套餐使用记录
|
||||
- [x] 5.2 拆分设备套餐生成路径,按 `package_source_slot` 生成 `device` 订单和套餐使用记录
|
||||
- [x] 5.3 为 active 套餐写入 `status=1`、生效/到期时间和用量快照
|
||||
- [x] 5.4 为 pending 套餐写入 `status=0`、稳定 `priority`、到期时间和 0 用量
|
||||
- [x] 5.5 设备和卡的 `series_id` 从目标套餐 `tb_package.series_id` 回填,并覆盖已有资产漏写场景
|
||||
- [x] 5.6 目标套餐不存在、已删除或不是 formal 时生成 SQL 前置校验或错误阻断
|
||||
|
||||
## 6. 审核产物与错误输出
|
||||
|
||||
- [x] 6.1 生成 `output/ownership_resolution.csv`
|
||||
- [x] 6.2 生成 `output/package_resolution.csv`
|
||||
- [x] 6.3 更新 `output/summary.txt`,展示分配数、active/pending 套餐数、跳过数和错误数
|
||||
- [x] 6.4 更新 `errors.csv` / `warnings.csv` 结构或内容,覆盖店铺缺失、槽位缺失、套餐映射缺失、目标套餐无效等关键错误
|
||||
- [x] 6.5 确保关键错误阻断对应资产或套餐 SQL,不再静默进入平台库存
|
||||
|
||||
## 7. 手动验证
|
||||
|
||||
- [x] 7.1 使用当前 120 台设备样例生成 SQL,确认 `ownership_resolution.csv` 显示 120 台设备归属到预期店铺
|
||||
- [x] 7.2 使用包含 current + pending 套餐的小样例生成 SQL,确认 `package_resolution.csv` 显示 active 和 pending 队列
|
||||
- [x] 7.3 检查 `step2_02_package_usages.sql`,确认 active 写 `status=1`,pending 写 `status=0` 且 priority 稳定递增
|
||||
- [x] 7.4 检查设备套餐 SQL,确认只为 `package_source_slot` 生成 device 级套餐,非来源槽位不重复生成主套餐
|
||||
- [ ] 7.5 在测试库手动执行生成 SQL,查询 `tb_device.shop_id`、`tb_iot_card.shop_id`、`tb_package_usage.status`、`tb_package_usage.priority` 和资产 `series_id`
|
||||
- [x] 7.6 记录未验证项和已知限制,不新增自动化测试或 `_test.go`
|
||||
Reference in New Issue
Block a user