All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 9m2s
- 新增成对迁移 000223(tb_phone_asset_association,含有效关系部分唯一索引与 down 守卫)与 000224(解绑导入任务表),不回填历史 - H5:need_bind_phone 三支判定(开关关闭完全短路);已有主号幂等建联;十项上限按手机号 advisory 串行化(含换绑到全新号的并发场景);换绑原子迁移与冲突整单回滚;不写遗留列 - 后台:关联列表、单项/批量解绑、CSV 导入解绑(B1–B16),超管/平台 gate + 资产数据范围复核,三态统一文案 - 读侧:卡/设备列表与详情按页一次 IN 聚合;两类导出补「关联手机号」列并保留历史表头反解兼容 - 脱敏:关联审计走独立动作/资源只写脱敏手机号;访问日志手机号类字段脱敏 - 同步主 Spec openspec/specs/phone-asset-association 并归档 AUG26-009,补齐 requirement-evidence 与入口矩阵,context-health 通过
6.8 KiB
6.8 KiB
1. 关联事实与 H5 建联
- 1.1 新增成对迁移
migrations/000223_*(编号按实施时目录最大编号复核,当前最大为000222)、模型与常量:关联表含asset_type、asset_id、phone、status、建立时间、建立来源(固定h5_sms_verification)与失效信息;配有效关系部分唯一索引(phone, asset_type, asset_id)、按phone计数索引、按(asset_type, asset_id)集合查询索引;down带有效性守卫,不回填历史。 - 1.2 新增关联 store 的四个方法:
ExistsValid(登录判定)、CountValidByPhone(十项上限)、ListValidByAssets(列表/导出一次批量聚合)、CreateIfAbsent(幂等建联)。 - 1.3 改造
issueLoginToken为三支判定:无主号 →true;有主号但未与当前访问资产存在有效关系 →true;已关联 →false;全局开关关闭 → 恒false且不查询、不创建、不删除关系。关联查询使用 JWT 的asset_type/asset_id,不使用phoneclaim。保持登录响应形状不变。 - 1.4 扩展
BindPhone:已有主号且提交号码等于主号、验证码有效时幂等建联且不改账号手机号;号码不等于主号仍拒绝;无当前访问资产身份时只完成账号手机号绑定;建联前在事务内锁定手机号行计数,达十项返回上限文案且不写关联。 - 1.5 改造
ChangePhone:事务内先算「新号现有有效关系数 + 旧号待迁移数」,超十项整次失败;通过时把旧号全部有效关系原子迁移至新号;与「新号已存在的同资产有效关系」冲突时整次失败回滚;不写遗留列tb_personal_customer.phone;审计只写脱敏手机号。加锁顺序统一为按手机号行id ASC。
2. 读侧与后台解除
- 2.1 资产列表与详情补关联手机号投影:按当页资产集合一次
IN批量查询后装配,禁止逐资产查询;卡与设备两条 Query 路径都要覆盖。 - 2.2 IoT 卡与设备两个导出场景补「关联手机号」列:设备导出新列并入尾部并让
deviceExportTailHeaderCount递增,buildDeviceExportRow同步;cardGroupCountFromHeaders保留旧表头反解兼容分支,保证历史任务可用ResolvedHeaders重导出。 - 2.3 实现单项解绑
DELETE /phone-asset-associations/:id:路径主键即指定一条关系;必须二次确认并填写原因(1~500 字符);锁关系后复核资产数据范围,标记失效并逐条审计(脱敏)。 - 2.4 实现勾选批量解绑
POST /phone-asset-associations/batch-unbind:资产集合去重、二次确认、原因必填;每项资产独立解除全部有效关系,返回成功数、失败数与逐项结果;部分成功不回滚成功项。 - 2.5 实现 CSV 解绑导入场景,按 B1–B16 全清单(不含 B17):B1 任务类型常量;B2 队列常量、
QueueForTaskType分支与队列权重;B3 新任务表成对迁移(含down守卫);B4 任务模型(含逐行结果明细 jsonb);B5 任务 store(UpdateStatus/UpdateProgress/UpdateResult);B6internal/bootstrap各装配文件注入;B7 worker 处理器(逐行独立事务、BOM 去除、UTF-8 校验失败按 GBK 回退、固定表头列序、行号自数据首行起计、任务级失败不产生行明细、不设行数硬上限);B8pkg/queue/handler.go处理器注册;B9cmd/worker/main.go启动补偿;B10internal/infrastructure/releasegate/checker.go:243未完成异步任务表清单追加新表;B11 应用 service(建任务、入队失败必落库);B12 Handler 与 DTO(入口限超级管理员与平台用户);B13 路由注册;B14 占位装配;B15 上传用途常量、存储映射、用途枚举与上传接口文档表四处同步;B16 审计动作码与资源注册。列:资产标识(必填,复用既有资产标识解析)+ 备注(可选),解绑原因由任务级必填字段与二次确认提供;任务明细持久化解绑当时的完整手机号快照(B13、B14 与 2.6 合并执行)。 - 2.6 注册本 Change 全部新增接口的路由(沿用超管/平台路由组级 gate 先例)、
cmd/api/docs.go与cmd/gendocs/main.go占位装配及 OpenAPI 文档(ENG-ROUTE-001),并补充三态统一失败文案常量供新用例使用;既有内联字面量按 As-Is 保留。
3. 验证与交付
- 3.1 按 ENG-TEST-001 在维护者指定测试面验证正向行为:开关开启首次访问未关联资产返回
need_bind_phone=true且完成验证后建立关系;已关联资产不再要求验证;开关关闭后登录不要求验证且不新建、不删除关系;第 11 项资产验证被拒绝且原十项不变;重复验证同一资产不产生第二条关系;换绑超限整次失败且新旧关系均不变;换绑迁移与新号既有同资产关系冲突时整次回滚。 - 3.2 验证反向断言:换货完成后关联记录数不变且新资产不继承旧资产关联;换货新资产首次登录返回
need_bind_phone=true;资产导入后关联记录数不变;后台资产编辑后关联记录数不变。- 说明:判定为完成(维护者裁定)。证据:全仓
tb_phone_asset_association写入点仅本 Change 的 5 处(H5 建联、换绑迁移、后台单项解除、后台批量解除、CSV 导入解除),exchange/customer_binding/ 资产导入 / 后台资产编辑模块对本表零引用且零改动;等价谓词「有主号但未与当前访问资产存在有效关系 →need_bind_phone=true」已实测。完整换货 / 资产导入端到端(需共享队列与对象存储)未执行,作为残留范围记录。
- 说明:判定为完成(维护者裁定)。证据:全仓
- 3.3 验证读侧与脱敏:资产列表按页一次聚合(以查询次数为准,禁止 N+1);卡与设备两类导出均含关联手机号列且历史任务表头仍可反解列组;批量任务结果在关系失效后仍返回完整手机号快照;审计、日志与错误中只出现脱敏手机号;越权、资产不存在、已无有效关系三态返回同一文案。
- 3.4 验证权限与批量语义:代理、企业、个人客户访问后台解绑入口返回 403;数据范围外资产不可见也不可解除;单项解绑未指定关系被拒绝;缺二次确认或原因被拒绝;批量与导入按资产解除全部有效关系,逐资产独立结果,失败行保留原状且不影响其他行,无行数硬上限。
- 3.5 验证迁移与交付命令:隔离库
up/down/up(有数据时down被守卫拒绝);gofmt -w <changed-go-files>、go build ./cmd/api ./cmd/worker、go run cmd/gendocs/main.go、openspec validate add-phone-asset-associations --strict、openspec doctor --json;自动化测试按项目决策为 N/A。fixture 只增删本 Change 自己的记录,禁止重置整库。