fix: 第1批 M2、M3、M16、M23 导入与展示类缺陷
Some checks failed
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Failing after 8h37m36s

- M2 批量换绑表头改为“平台登录账号”,只认手机号,细分格式、不存在、多匹配、非平台、已禁用失败原因
- M3 后台订单列表与详情返回 C 端在线购买的交易流水号、支付渠道、商户名称,详情新增全部支付单 payments
- M16 预警规则不再返回 real_data_mb,预警列表/详情新增 threshold_mb,导出末列追加“阈值流量(MB)”
- M23 设备导入 IMEI 必填,空 IMEI 行记为失败
- OpenSpec Change fix-august-iteration-follow-up-gaps:13/18,导入任务、导出任务的 Worker 端到端验证与前端/运营通知待部署后完成
This commit is contained in:
邹进
2026-10-11 23:49:29 +09:00
parent 260fec7114
commit 25c081fe60
24 changed files with 553 additions and 42 deletions

View File

@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-09-29

View File

@@ -0,0 +1,48 @@
## Context
本 Change 承接《第1批-8-9月迭代缺陷修复技术方案》(下称“方案”)第 1、2、9、10 节,四项都是导入与展示类,无迁移、互不依赖。旧版本中的 M1、M4~M6 已移到同属第 1 批的 `fix-august-iteration-export-and-password-hints`。
| 项 | 现状(已验证) |
|---|---|
| M2 | 导入按 `username = ? AND user_type = 2 AND status = 1` 查业务员(`internal/task/shop_business_owner_import.go:321`),运营填的是手机号,全部失败;查不到、非平台、已禁用合并为一个笼统原因 |
| M3 | 订单响应已有 `payment_method`、`paid_at`;第三方流水号只在导出里有;`tb_payment` 存渠道类型与商户名称快照,历史记录只有 `payment_config_id`;`tb_payment` 同时存充值类支付,ID 空间与套餐订单不同 |
| M16 | 规则项返回 `real_data_mb`(只是主套餐额度),预警列表的 `limit_mb` 是含加油包的汇总快照,两者对不上;列表没有阈值对应流量 |
| M23 | 设备唯一创建入口是导入(`internal/task/device_import.go`),`pkg/utils/excel.go` 只校验虚拟号非空 |
验证面只有维护者指定的测试环境;自动化测试为 N/A。
## Goals / Non-Goals
**Goals:**
- 批量换绑按手机号定位平台账号,失败原因可定位到具体情形。
- 后台能看到 C 端在线购买的交易流水号、渠道、商户,详情能看到全部支付单。
- 预警规则页与列表口径一致、可读。
- 不再产生缺 IMEI 的新设备。
**Non-Goals:**
- 不改 C 端订单接口、支付写入、渠道补查、订单导出现有列。
- 不改预警判定、唯一键、通知,不处理旧预警记录。
- 不校验 IMEI 格式与唯一性,不处理存量缺 IMEI 设备。
- 不做 M1、M4~M6(在 `fix-august-iteration-export-and-password-hints`)。
## Decisions
1. **M2 只认手机号,表头叫“平台登录账号”。** 2026-10-11 确认。项目没有公共手机号校验函数,各处只检查“长度 11、全为数字”(如 DTO `len=11,numeric`),本项格式规则同样定为“去首尾空白后为 11 位数字”。行级失败原因中原“业务员登录账号”文案统一改为“平台登录账号”。按手机号查全部未删除账号、不在 SQL 里过滤类型与状态,再在代码里依次判定,才能把“不存在 / 多匹配 / 非平台 / 已禁用”区分开。表头唯一来源是 `pkg/constants/shop_business_owner_import.go` 的 `ShopBusinessOwnerImportColumns`,失败原因文案也放常量。不做新旧表头并存:并存会让运营继续用旧模板填用户名,问题不收敛。
2. **M3 支付记录按订单 ID 批量读取,并强制带 `order_type = 'package_order'`。** 不带会把同 ID 的充值支付串进来。已支付取一条时按 `paid_at DESC, id DESC`,保证异常多条时结果确定。`payments` 用专用响应 DTO,不直接序列化 `model.Payment`,避免以后模型加字段被带出。已退款订单仍给值,因为对账需要原交易信息。
3. **M16 删除 `real_data_mb` 而不是改名或标注。** 规则按百分比配置,任何具体额度都会被误读为分母;服务端 `RealDataMB > 0` 校验保留。`threshold_mb` 由 `limit_mb × threshold_percent ÷ 100` 计算,口径天然与 `limit_mb` 一致。导出列追加在末尾:分片按位置把行截齐到表头(`internal/task/export_common.go` 的 `alignRowsToHeaders`,由 `export_shard.go` 调用),追加在末尾时旧任务只是少一列,插在中间会整体错位。
4. **M23 在解析层校验。** 与虚拟号为空写法一致,进 `ParseErrors`,由 `device_import.go` 并入失败明细,任务汇总方式不变。
## Risks / Trade-offs
- [旧模板整批失败] → 交付测试环境前通知运营换新模板;失败原因明确提示“请填写手机号”。
- [前端仍读取 `real_data_mb`] → 测试环境交付时前端同步去掉该字段,任务里单列核对项。
- [异常的多条已支付支付记录] → 稳定排序取一条,不在读接口修数据。
- [IMEI 非空但格式错误或重复] → 本批不处理,后续对存量做统计后另行评审。
## Migration Plan
无数据库迁移。部署测试环境的 API 与 Worker 后立即生效;已冻结表头的预警导出任务按旧表头输出。
## Open Questions
无。

View File

@@ -0,0 +1,32 @@
## Why
8-9 月迭代第 1 批中有四项导入与展示类缺陷,行为变化小、互不依赖:批量换绑业务员因只认用户名而整批失败;后台订单看不到 C 端在线购买的原始支付信息;真流量预警规则页显示的套餐额度与预警列表汇总口径对不上,且列表缺阈值对应流量;设备导入允许 IMEI 为空,产生的设备在 H5 设备卡列表、实名页报错。依据为《第1批-8-9月迭代缺陷修复技术方案》第 1、2、9、10 节。
本 Change 改写自 2026-09-29 的旧内容:原 M1(修改密码提示)、M4~M6(订单、IoT 卡、设备导出列)移到同属第 1 批的 `fix-august-iteration-export-and-password-hints`;原“业务员登录账号支持用户名或手机号”的方案作废,改为只认手机号。
## What Changes
- **BREAKING** 店铺负责人 CSV 导入的固定表头改为「店铺编码、操作类型、平台登录账号、备注」,旧表头整批失败。“平台登录账号”只接受手机号:格式不合法(须为 11 位数字)、不存在、匹配多个账号、不是平台用户、已禁用分别给出行级失败原因(M2)。
- 后台订单列表与详情始终返回 `third_party_trade_no`、`payment_channel`、`payment_merchant_name` 三个字符串字段,只对个人客户用微信或支付宝支付、状态为已支付或已退款的套餐订单给值,其余为空串;订单详情新增 `payments` 数组,列出该订单全部支付单(任何状态),不含二维码、商户号、路由等敏感或内部字段(M3)。
- **BREAKING** 真流量预警规则列表与详情不再返回 `real_data_mb`;预警列表、详情、导出新增 `threshold_mb`(阈值对应流量,按触发快照计算,口径含加油包),导出新列追加在最后一列;判定口径不变(M16)。
- 设备导入中 IMEI 为空(含仅空白)的行记为失败,原因“设备IMEI(imei)不能为空”;只校验非空,不校验格式与唯一性(M23)。
## Capabilities
### New Capabilities
无。
### Modified Capabilities
- `business-user-group`:店铺负责人 CSV 导入表头改为“平台登录账号”、只认手机号,并细分行级失败原因。
- `order-refund-exchange`:后台订单列表与详情返回 C 端在线购买的交易流水号、支付渠道、商户名称,详情返回全部支付单。
- `package-traffic-alert`:规则不再返回套餐真流量额度;预警查询与导出增加阈值流量。
- `asset-device`:设备导入 IMEI 必填。
## Impact
- 代码:`internal/task/shop_business_owner_import.go`、`pkg/constants/shop_business_owner_import.go`、`internal/routes/business_user_group.go`;`internal/service/order/service.go` 及订单 DTO、支付记录批量读取;`internal/query/packagetrafficalert/`、`internal/model/dto/package_traffic_alert_dto.go`、预警导出数据源;`pkg/utils/excel.go`、`internal/routes/device.go`。
- 接口:后台订单列表与详情新增字段;预警规则接口删除 `real_data_mb`,测试环境前端须在同一次交付中去掉对该字段的使用;导入接口说明更新;重新生成接口文档。
- 运营:批量换绑须改用新模板;设备导入文件须填 IMEI。
- 不涉及数据库迁移、Redis 结构、第三方渠道调用,不改变预警判定与支付写入。

View File

@@ -0,0 +1,17 @@
## ADDED Requirements
### Requirement: 设备导入 IMEI 必填
设备导入 SHALL 要求每个数据行填写 IMEI。IMEI 去除首尾空白后为空的行 MUST 记为失败,失败原因为“设备IMEI(imei)不能为空”,计入任务失败数并出现在任务详情的逐行失败明细中,MUST NOT 静默跳过;同一文件中 IMEI 齐全的行照常导入。虚拟号与 IMEI 同时为空时只记虚拟号为空一条原因。全部行失败时任务状态为失败。系统 MUST 只校验非空,MUST NOT 校验 IMEI 长度、格式或唯一性。导入模板列名、列数、列序不变;存量缺 IMEI 的设备不在本要求处理范围。
#### Scenario: 文件混有空 IMEI 行与正常行
- **WHEN** 导入文件同时包含 IMEI 为空的行与 IMEI 齐全的行
- **THEN** 空 IMEI 行以“设备IMEI(imei)不能为空”和正确行号出现在失败明细中,正常行导入成功,任务状态为已完成
#### Scenario: 整份文件 IMEI 都为空
- **WHEN** 导入文件所有行 IMEI 为空或只有空格
- **THEN** 任务状态为失败,失败明细逐行列出原因
#### Scenario: 虚拟号与 IMEI 都为空
- **WHEN** 某行虚拟号与 IMEI 都为空
- **THEN** 该行只记录“设备虚拟号(virtual_no)不能为空”一条原因

View File

@@ -0,0 +1,36 @@
## MODIFIED Requirements
### Requirement: 店铺负责人 CSV 导入
超级管理员和平台用户 SHALL 通过 CSV 导入批量设置或清空店铺负责人;导入 MUST 异步执行并返回成功数、失败数与逐行明细。导入入口仅限超级管理员与平台用户,故导入失败原因不含数据范围无权。
导入文件 MUST 为 CSV,编码 MUST 为 UTF-8 且 MAY 带 BOM;非 UTF-8 时系统 SHALL 按既有 GBK 转换能力尝试解码,仍失败时按任务级失败并给出明确原因。文件首行表头 MUST 与固定列序「店铺编码、操作类型、平台登录账号、备注」完全一致,不一致时(含旧表头「业务员登录账号」)为任务级失败,系统 MUST NOT 同时兼容新旧表头。操作类型 MUST 为「换绑」或「清空」;换绑 MUST 填写平台登录账号,清空 MUST NOT 填写平台登录账号。备注可选,填写时 MUST 写入该行审计。店铺 MUST 以店铺编码唯一定位。
平台登录账号 MUST 填写平台用户的登录手机号。系统 MUST 去除首尾空白后校验为 11 位数字,不合法时该行失败且 MUST NOT 查询账号;填写用户名按格式不合法处理。格式合法时系统 MUST 按手机号精确查询全部未删除账号(不按用户类型与状态过滤),再依次判定:零条失败为“平台登录账号(手机号)不存在”;多于一条失败为“平台登录账号(手机号)匹配多个账号”;唯一账号不是平台用户失败为“该手机号对应账号不是平台用户”;唯一账号已禁用失败为“该手机号对应账号已禁用”。只有唯一命中启用平台账号时方可执行换绑。
每行 MUST 独立校验与执行:有效行成功更新,失败行 MUST 保留原值且不影响其他已成功行;结果 MUST 保存行号(自数据首行起计,表头不计入)、成功或失败状态与失败原因。任务级失败与行级失败 MUST 分开记录,任务级失败不产生行明细。导入 MUST NOT 设置行数硬上限。所有实际变更 MUST 记录店铺负责人前后值与行备注审计。
行级失败原因 MUST 限定为:店铺编码不存在或已删除、操作类型非法、换绑必须填写平台登录账号、清空不得填写平台登录账号、平台登录账号请填写手机号且格式不正确、平台登录账号(手机号)不存在、平台登录账号(手机号)匹配多个账号、该手机号对应账号不是平台用户、该手机号对应账号已禁用、行格式错误。任务级失败原因 MUST 覆盖:文件格式或表头不符、编码无法解码、文件无数据行。
#### Scenario: 导入包含有效和无效行
- **WHEN** 店铺负责人 CSV 导入同时包含可成功换绑或清空的行与校验失败的行
- **THEN** 系统更新每个有效行、保留失败行原值,并返回逐行行号、失败原因及成功与失败汇总
#### Scenario: 表头或编码不符整批失败
- **WHEN** 上传文件的表头与固定列序不一致(含仍使用旧表头「业务员登录账号」),或文件既非合法 UTF-8 又无法按既有 GBK 转换能力解码
- **THEN** 系统不处理任何行、不产生行明细,并返回明确的任务级失败原因
#### Scenario: 换绑与清空字段互斥
- **WHEN** 某行操作类型为换绑但平台登录账号为空,或操作类型为清空但填写了平台登录账号
- **THEN** 该行失败并保留店铺原负责人,其他行照常处理
#### Scenario: 手机号唯一命中启用平台账号
- **WHEN** 某换绑行填写的手机号唯一命中一个未删除且启用的平台账号
- **THEN** 系统将目标店铺负责人更新为该账号,并按既有规则记录逐行结果与审计前后值
#### Scenario: 填写用户名或格式不合法的号码
- **WHEN** 某换绑行的平台登录账号填写用户名或格式不合法的号码
- **THEN** 该行以“平台登录账号请填写手机号,格式不正确”失败、不查询账号且保留原负责人
#### Scenario: 手机号不存在、多匹配或账号类型状态不符
- **WHEN** 某换绑行的手机号没有命中账号、命中多个账号、唯一账号不是平台用户或唯一账号已禁用
- **THEN** 该行分别返回“平台登录账号(手机号)不存在”“平台登录账号(手机号)匹配多个账号”“该手机号对应账号不是平台用户”或“该手机号对应账号已禁用”,并保留原负责人

View File

@@ -0,0 +1,35 @@
## ADDED Requirements
### Requirement: 后台订单返回 C 端在线购买的原始支付信息
系统 SHALL 在后台订单列表与 `GET /api/admin/orders/{id}` 的每条订单中始终返回字符串字段 `third_party_trade_no`(交易流水号)、`payment_channel`(支付渠道类型)、`payment_merchant_name`(商户名称)。仅当订单买家为个人客户、支付方式为微信或支付宝、且订单支付状态为已支付或已退款时,系统 MUST 从该订单自身的套餐订单支付记录(`order_type = 'package_order'`)中状态为已支付的一条取值;存在多条已支付记录时 MUST 按支付时间倒序、记录 ID 倒序取第一条。`payment_channel` 取支付记录的渠道类型快照,快照为空时按支付记录关联的支付配置推断,推断不出为空串;`payment_merchant_name` 取商户名称快照,没有为空串。其他订单(代理购买、钱包支付、线下支付、未支付)三个字段 MUST 为空串。交易时间与支付方式沿用既有 `paid_at`、`payment_method`。
列表 MUST 按当页订单集合批量读取支付记录,MUST NOT 逐行查询;数据库错误 MUST 按既有数据库错误返回,MUST NOT 当作空值。系统 MUST NOT 调用第三方渠道补查,MUST NOT 改变 C 端订单接口与订单导出现有列。
#### Scenario: C 端微信或支付宝已支付订单
- **WHEN** 后台调用方查询个人客户以微信或支付宝支付成功的套餐订单
- **THEN** 列表与详情返回的三个字段与该订单已支付支付记录的流水号、渠道类型快照、商户名称快照一致
#### Scenario: 已退款订单仍返回原交易信息
- **WHEN** 上述订单退款成功、订单支付状态变为已退款后再次查询
- **THEN** 三个字段仍返回原已支付支付记录的值
#### Scenario: 非 C 端在线购买订单
- **WHEN** 后台调用方查询代理购买、钱包支付、线下支付或未支付的订单
- **THEN** 响应仍包含三个字段且值均为空串
#### Scenario: 套餐订单与同 ID 充值单不串
- **WHEN** 存在一笔客户充值或代理充值支付记录,其业务单 ID 与某套餐订单 ID 相同
- **THEN** 该套餐订单的三个字段只来自 `order_type = 'package_order'` 的支付记录
### Requirement: 后台订单详情返回全部支付单
系统 SHALL 在 `GET /api/admin/orders/{id}` 响应中返回 `payments` 数组,列出该订单的全部支付单,用于排查付款后订单未成功、重复支付、更换支付方式或重付。适用范围与“后台订单返回 C 端在线购买的原始支付信息”相同;不适用的订单返回空数组 `[]`。取数 MUST 按该订单且 `order_type = 'package_order'`,包含所有状态,按创建时间倒序、记录 ID 倒序排序。每条 MUST 返回支付单号、支付方式、支付渠道类型(取值规则同上)、商户名称、交易流水号、金额(分)、状态与状态名称、创建时间、支付时间、过期时间;MUST NOT 返回二维码内容、商户号、商户池与路由信息、支付配置 ID、付款凭证存储键。订单列表 MUST NOT 返回 `payments`。
#### Scenario: 先微信未付再支付宝已付
- **WHEN** 个人客户对同一订单先发起微信支付未完成、再用支付宝支付成功,后台查询该订单详情
- **THEN** `payments` 含两条支付单,按创建时间倒序,状态分别为已支付与待支付或已失败,且不含二维码、商户号与路由字段
#### Scenario: 不适用订单返回空数组
- **WHEN** 后台查询代理购买、钱包支付或线下支付订单的详情
- **THEN** `payments` 为空数组

View File

@@ -0,0 +1,44 @@
## ADDED Requirements
### Requirement: 规则页不返回套餐流量额度
真流量预警规则按套餐配置百分比阈值,判定时按资产当前有效的主套餐与加油包汇总计算,规则创建时无法得知资产最终的汇总额度。规则列表与规则详情 MUST NOT 返回套餐商品真流量额度字段 `real_data_mb`,只返回套餐、百分比阈值、启用状态与备注;规则接口说明 MUST 写明上述判定口径。创建或修改规则时对套餐商品真流量额度大于零的服务端校验 MUST 保持不变。
#### Scenario: 规则响应不含套餐额度
- **WHEN** 调用方查询真流量预警规则列表或详情
- **THEN** 响应不包含 `real_data_mb`
#### Scenario: 无真流量额度的套餐仍不可配置
- **WHEN** 管理员为真流量商品额度不大于零的套餐创建或启用规则
- **THEN** 系统仍拒绝并保持该套餐无有效规则
## MODIFIED Requirements
### Requirement: 预警查询与导出
超级管理员和平台用户 SHALL 在既有资产数据范围内查询和导出预警;先应用既有资产数据范围,该范围当前对超级管理员与平台无实际过滤,保留为冻结语义与未来放开的前置。越权与不存在 MUST 统一按既有的资源不可见处理。导出 MUST 复用既有异步任务并在创建时冻结操作者、筛选、时间范围与可见资产范围,执行期间归属变化 MUST NOT 扩大范围,且 MUST 按触发时间筛选。列表、详情与导出 MUST 返回资产类型、资产标识、对应标识符、卡标识、设备类型、设备型号、套餐名称、真流量已用量、真流量额度、比例、阈值快照、阈值对应流量、到期时间、剩余天数、触发时间、店铺、业务员、用户组与通知投递结果,且 MUST NOT 包含运营商通道列。真流量已用量与真流量额度的接口说明 MUST 写明为“主套餐与加油包汇总”。阈值对应流量 `threshold_mb` MUST 等于触发快照的真流量额度乘以阈值百分比除以 100,保留两位小数,口径与真流量额度一致(含加油包)。导出中阈值对应流量列(“阈值流量(MB)”)MUST 追加在最后一列;任务创建时已冻结表头的历史任务 MUST 按冻结表头输出,不插入该列。导出中套餐、用量、总量、阈值、阈值对应流量与到期时间 MUST 使用触发快照,店铺、业务员与用户组 MUST 按执行时当前归属补充;预警行仍冻结店铺与业务员快照供列表、详情与追溯,两种口径 MUST NOT 混用;用户组 MUST NOT 写入店铺表,按既有实时推导。
#### Scenario: 受限导出
- **WHEN** 平台用户在其资产数据范围内创建预警导出
- **THEN** 导出仅包含创建时可见预警,即使任务执行期间店铺归属发生变化
#### Scenario: 归属变更后导出
- **WHEN** 预警记录创建后资产所属店铺或业务员变更,再执行已创建导出任务
- **THEN** 套餐、用量、总量、阈值、阈值对应流量与到期时间仍使用触发快照,店铺、业务员与用户组使用执行时当前归属,且不超出任务创建时冻结的可见范围
#### Scenario: 越权或不存在
- **WHEN** 调用者读取不在其可见范围内的预警详情
- **THEN** 系统按资源不可见处理,不区分越权、不存在与已删除
#### Scenario: 阈值对应流量
- **WHEN** 某预警触发快照的真流量额度为 103424 MB、阈值为 80%
- **THEN** 列表与详情返回 `threshold_mb` 为 82739.20,新建导出任务的最后一列“阈值流量(MB)”为同一值
#### Scenario: 历史导出任务列序不变
- **WHEN** Worker 执行本要求生效前已冻结表头的预警导出任务
- **THEN** 输出仍使用冻结的旧表头与旧列数,不插入阈值流量列

View File

@@ -0,0 +1,32 @@
## 1. M2 批量换绑按平台登录账号(手机号)
- [x] 1.1 `pkg/constants/shop_business_owner_import.go`:固定表头改为「店铺编码、操作类型、平台登录账号、备注」;行级失败原因常量按 delta spec 改名与拆分(换绑必须填写平台登录账号、清空不得填写平台登录账号、格式不正确、不存在、匹配多个、不是平台用户、已禁用)。
- [x] 1.2 `internal/task/shop_business_owner_import.go`:值去首尾空白后校验为 11 位数字,不合法直接失败不查库;按手机号查全部未删除账号,代码中依次判定零条、多条、非平台、已禁用。
- [x] 1.3 同步 `internal/routes/business_user_group.go` 导入说明与模板说明(“填写平台用户的登录手机号”)。
## 2. M3 后台订单支付信息
- [x] 2.1 新增按订单 ID 集合批量读取 `order_type = 'package_order'` 支付记录的只读查询;已支付取一条按 `paid_at DESC, id DESC`;渠道类型快照为空时按 `payment_config_id` 推断。
- [x] 2.2 `internal/service/order/service.go` 列表与详情组装三个字段,仅个人客户、微信或支付宝、订单已支付或已退款时给值,其余为空串;数据库错误照常返回。
- [x] 2.3 详情新增 `payments` 数组与专用响应 DTO(字段与排除项按 delta spec),列表不返回。
## 3. M16 预警口径展示
- [x] 3.1 从 `PackageTrafficAlertRuleItem` 删除 `real_data_mb`,`internal/query/packagetrafficalert/query.go` 同步去掉取值;保留 `rule.go` 的 `RealDataMB > 0` 校验。
- [x] 3.2 预警列表、详情 DTO 增加 `threshold_mb`(两位小数);导出数据源追加“阈值流量(MB)”为最后一列,按冻结表头兼容历史任务。
- [x] 3.3 更新规则与预警接口字段说明(汇总口径)。
## 4. M23 设备导入 IMEI 必填
- [x] 4.1 `pkg/utils/excel.go` 在虚拟号校验之后增加 IMEI 非空校验,原因文案放常量。
- [x] 4.2 `internal/routes/device.go` 导入说明中 `imei` 改为必填。
## 5. 文档与测试环境验证
- [x] 5.1 对变更 Go 文件执行 `gofmt`,运行 `go build ./cmd/api ./cmd/worker` 与 `go run cmd/gendocs/main.go`,核对新字段与说明。
- [ ] 5.2 按 ENG-TEST-001 在 `junhong_cmp_test` 创建并清理专用账号与店铺:用新表头 CSV 验证启用平台账号手机号换绑成功且审计前后值正确;格式不合法、用户名、不存在、非平台、已禁用分别按 delta spec 失败且原负责人不变;旧表头整批失败。
- [x] 5.3 订单:C 端微信、支付宝已支付及已退款订单三个字段与 `tb_payment` 一致;代理、钱包、线下、未支付为空串;构造“先微信未付、再支付宝已付”订单核对 `payments`;套餐订单与同 ID 充值单不串;列表无逐行查询。
- [ ] 5.4 预警:规则列表与详情不含 `real_data_mb`,无额度套餐仍被拒;列表与详情 `threshold_mb` 计算正确;新导出任务最后一列为阈值流量,旧任务列序不变。
- [ ] 5.5 设备导入:混合文件、整份空 IMEI、仅空格、虚拟号与 IMEI 都为空四种情形按 delta spec 核对。
- [ ] 5.6 通知测试环境前端去掉 `real_data_mb` 的使用、接入订单新字段;通知运营换新换绑模板、导入须填 IMEI。
- [ ] 5.7 运行 `openspec validate --all` 与 `./scripts/context-health.sh`。