## Why 上游 IoT 平台返回的 ICCID 格式不固定(部分运营商 19 位为唯一标识,部分运营商需要完整 20 位才能唯一标识一张卡),而当前 `tb_iot_card.iccid` 仅支持精确匹配查询。当上游传入的位数与数据库存储的位数不一致时,查询静默失败(返回空结果但不报错),导致设备当前使用卡的 `is_current` 状态更新失效,产生不可观测的数据错乱。 ## What Changes - 在 `tb_iot_card` 表新增 `iccid_19`(varchar(19))和 `iccid_20`(varchar(20))两列,分别存储 ICCID 的前 19 位和完整 20 位(19 位卡的 `iccid_20` 为 NULL) - 在 `tb_personal_customer_iccid` 表新增 `iccid_19` 列,采用相同策略 - 所有 ICCID 精确查询改为**按上游传入长度路由**:19 位查 `iccid_19`,20 位查 `iccid_20`,Miss 时记录告警而非降级 - 不降级、不跨列匹配——上游数据质量问题在日志层暴露,不在查询层消化 ## Capabilities ### New Capabilities (无新增业务能力) ### Modified Capabilities - `iot-card`:`tb_iot_card` 存储结构变更,新增两列及对应索引;IotCardStore 的所有精确查询方法(GetByICCID、GetByICCIDs、ExistsByICCID、ExistsByICCIDBatch)改用新列路由 - `iot-card-import-task`:导入任务在写入 IotCard 时同步填充 `iccid_19` / `iccid_20` 两列 ## Impact **数据库** - `tb_iot_card`:新增 `iccid_19`、`iccid_20` 两列 + Partial Index(过滤软删除) - `tb_personal_customer_iccid`:新增 `iccid_19` 列 + 索引 - 存量数据一次性回填脚本(迁移文件中执行) **Store 层**(共 3 个文件,~10 个方法) - `iot_card_store.go`:GetByICCID、GetByICCIDs、ExistsByICCID、ExistsByICCIDBatch - `personal_customer_iccid_store.go`:GetByICCID、GetByCustomerAndICCID - `device_sim_binding_store.go`:UpdateIsCurrentByDeviceID(内部子查询)、GetBoundICCIDs **Service 层**(1 处内联 SQL) - `enterprise_card/service.go`:`WHERE iccid IN ?` 直接 SQL 改为调用 Store 方法或适配新列 **Task 层**(1 处写入路径) - `iot_card_import.go`:processBatch() 中 IotCard 初始化时赋值新字段