Files
junhong_cmp_fiber/openspec/changes/archive/2026-09-18-close-august-iteration-gaps/tasks.md
break 5ed6b39deb
Some checks failed
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Has been cancelled
feat(收口): 补齐 8 月迭代缺口并同步 Spec 与证据链
- 新增六对成对迁移 000232–000237:H5 弹窗类型、退款结算标识与申请人备注、优先轮询事实字段与两个新终态、通道阈值命中留痕、手机号最近解绑人、提现资格校验留痕
- 退款:原因必填与申请人备注、来源支付与渠道流水冻结、线下处理流水号补录审计、按订单查询可选退款方式、企微审批材料补齐且新增字段缺失映射即明确失败
- 优先轮询:人工关闭、有效期到期独立周期任务、失败与过期人工重触发、事实字段与异常重试查询、资产解析端点只读投影
- 通道阈值:命中事实同事务留痕与命中记录查询;员工账单:列表筛选与详情投影;商户池:列表投影与统计周期语义;H5:弹窗类型与类别排序
- 手机号:有效关联数量与最近解绑人、短信验证码失败次数限制;导出:佣金明细十五列与报表序号列
- 时间筛选:三处新增筛选纳入统一严格解析契约,员工账单产生时间参数改名
- 同步 12 份主 Spec 需求、两端点与异步任务证据链,门禁 context-health 与 OpenSpec 校验通过
2026-09-18 15:34:29 +08:00

142 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 实施任务close-august-iteration-gaps
自动化测试按项目决策为 N/A验证在维护者指定的测试环境`junhong_cmp_test` 与测试 RedisENG-TEST-001进行仅创建与清理本 Change 的 fixture禁止重置整库。
## 1. 迁移与数据基线
- [x] 1.1 新增成对迁移 `000232_add_h5_popup_type``tb_h5_popup_configuration``popup_type`(枚举 `promotion`/`announcement`,非空,默认 `announcement`+ 取值 CHECKup 以默认值回填既有行并核对行数down 删列与约束
- [x] 1.2 新增成对迁移 `000233_extend_refund_settlement_fields`:退款主表加 `source_payment_no``original_channel_trade_no``offline_settlement_no``offline_settled_at``offline_settled_by`;退款审批尝试表加 `remark`申请人备注逐次冻结列注释写明冻结来源与补录语义MUST NOT 复用或改写主表既有 `remark`(审批备注)
- [x] 1.3 新增成对迁移 `000234_extend_polling_priority_item`:加 `asset_type``asset_id``device_no_snapshot``agent_shop_id_snapshot``attempt_limit`(默认 3`started_at``finished_at``next_run_at``dequeued_at``integration_log_id``priority_effective_from`;重建状态 CHECK 容纳 `closed``expired`;活动项部分唯一索引谓词保持 `status IN ('pending','processing')` 不变;为 `next_run_at` 建部分索引;既有活动项 `priority_effective_from` 回填为迁移时刻;资产列按既有设备—卡绑定回填,独立卡回填为卡自身
- [x] 1.4 新增成对迁移 `000235_extend_carrier_threshold_hit`:锁表加命中累计流量、阈值数值、阈值单位、判定时间、触发来源、解锁时间、复机结果;判定时间回填既有行创建时间,命中数值与阈值留空;新增按通道、卡与判定时间的查询索引
- [x] 1.5 新增成对迁移 `000236_add_phone_association_invalidator_name`:手机号—资产关联加最近解绑人名称快照列,注释写明审计侧仍只写脱敏值
- [x] 1.6 新增成对迁移 `000237_extend_withdrawal_attempt_qualification`:提现审批尝试表加资料资格版本标识、校验时间、是否通过、未通过稳定原因列
- [x] 1.7 在测试环境以 `scripts/migrate.sh` 与显式 `DB_*` 参数逐对执行 up / down / up核对加列、默认值、回填结果与既有行可读性
- [x] 1.8 核对既有优先轮询活动项不会被上线后首轮到期扫描批量过期;核对读侧对空列与默认值兼容(`next_run_at` 为空不判过期、命中数值为空按「无」展示)
## 2. 员工账单列表与详情
- [x] 2.1 扩展 `EmployeeCollectionBillListRequest`:新增账单编号(映射账单主键标识)精确、核销通过时间 `decided_start_time`/`decided_end_time`、欠款人姓名或账号模糊、客户名称模糊;既有「产生时间」参数由 `created_from`/`created_to` 改名为 `start_time`/`end_time` 并改用严格解析;补 OpenAPI 描述
- [x] 2.2 在 `internal/query/employeecollection/bill.go` 实现新增筛选:编号精确;核销通过时间取「状态为已通过的分摊」关联到的申请表审批终态时间的相关子查询;姓名与客户名称子串匹配(保持输入长度上限);保持既有可见性与分页口径
- [x] 2.3 明确并注释核销通过时间的列语义MUST NOT 使用分摊表的释放时间(该列同时承担通过与释放两种语义)
- [x] 2.4 列表响应并列返回未核销金额与剩余可核销金额,已关闭账单两者为零,保留既有剩余可核销校验逻辑
- [x] 2.5 账单详情新增操作日志投影(动作、操作账号名称、时间、前后值摘要,不含凭证内容与完整收款原文)
- [x] 2.6 账单详情新增来源订单快照投影(来源单号、业务类型、来源金额、来源创建时间、资产标识),来源不可读或字段缺失时返回空值且不阻断
- [x] 2.7 三个时间筛选字段复用共享严格解析器与闭区间;旧参数名与旧格式(含 date-only一律以参数非法拒绝
- [x] 2.8 在测试环境 smoke按编号、核销通过时间、姓名子串、客户名称子串各查询一次并核对预占场景下两个金额字段之差等于预占额详情含操作日志与来源快照来源缺失不阻断
## 3. 商户池列表与轮询边界
- [x] 3.1 `PaymentMerchantPoolResponse` 新增启用成员数、成员总数、当前命中成员、最近更新时间字段
- [x] 3.2 抽取只读取路函数:金额/笔数取首个未达标成员、每轮累计全达标取首位、时间方式按当前时段;调用后 MUST NOT 推进统计世代、MUST NOT 创建支付单;在 `ListPools` 中按页批量计算成员计数与命中成员
- [x] 3.3 调整 `chooseMerchant`:每轮累计全达标视为当轮结束并以条件更新递增统计世代(更新冲突按并发冲突拒绝创建);自然日累计与自然月累计全达标拒绝创建;单启用成员固定被选中且优先于自然周期拒绝
- [x] 3.4 调整 `SavePool`:修改时间周期数值/单位、起始时间或成员顺序并保存成功时,由服务端以保存成功时间重算起点,忽略请求携带的旧起点
- [x] 3.5 确认列表投影只读:查询前后统计世代不变、不新增支付单、不计入成功累计
- [x] 3.6 在测试环境 smoke单成员池达标后仍可下单自然日/自然月全达标拒付并提示暂无可用商户;每轮累计全达标开新轮且世代递增;改时间配置后起点等于保存时刻;列表查询前后世代与支付单数不变
## 4. H5 运营弹窗类型
- [x] 4.1 配置实体与 DTO 新增 `popup_type` 必填字段(创建与更新均校验取值域),更新 OpenAPI 描述
- [x] 4.2 候选排序改为先按类别(风险换卡 > 推广 > 公告再按显式优先级缺省优先级取类型默认值类别顺序使用显式排序表达式MUST NOT 依赖类型取值的字典序
- [x] 4.3 迁移回填后的既有配置 smoke确认同类内相对顺序与显式优先级不变且推广配置优先于显式优先级更高的公告配置
- [x] 4.4 `popup_type` 仅在创建时必填;更新不传保持原值(一经传入仍 MUST 按同一取值域校验);类型变更且请求未显式传优先级时保持原优先级(类型缺省优先级只在创建路径生效)——措辞已按 Spec 口径收窄,见 h5-popup-notification 主 Spec 与 delta
## 5. 退款申请、展示与审批材料
- [x] 5.1 退款创建与重提用例强制退款原因必填去空白后非空拒绝时不落库申请与审批尝试MUST NOT 把该校验加入审批尝试构造与补发历史审批路径(历史申请与补发不追溯)
- [x] 5.2 退款创建与重提新增申请人备注,冻结进当次审批尝试快照(尝试表新列),历史快照不被改写;主表既有备注语义不变
- [x] 5.3 退款单写入冻结的来源支付单号与原支付渠道交易流水号;无线上支付记录的订单以空值保存
- [x] 5.4 新增线下退款处理流水号补录与更正用例与接口,写审计(操作者、时间、前后值),不改变退款状态、实收金额、套餐失效与佣金回溯规则
- [x] 5.5 列表、详情与导出返回来源支付单号、原支付渠道交易流水号、线下退款处理流水号
- [x] 5.6 新增按订单查询可选退款方式接口:返回方式集合、可用性与不可用原因、原收款商户标识与名称、原渠道交易流水号、退款能力校验结果;复用既有方式判定与凭证判定实现;字面量路径先于同组参数路由注册
- [x] 5.7 退款审批材料新增资产类型、设备类型、设备型号、套餐已用量与总量、原支付渠道交易流水号;用量复用既有展示解析规则与真流量口径;设备维度取退款请求关联设备;冻结进审批尝试快照
- [x] 5.8 控件强制范围仅限本次新增字段:场景白名单标注必须映射,场景映射校验缺映射即明确失败,表单构建缺快照值即明确失败并提示缺失控件;既有未映射的可选控件保持静默跳过
- [x] 5.9 提交与企微通过后执行前不新增第二套方式判定(沿用既有方式判定与凭证判定实现)
- [x] 5.10 在测试环境 smoke原因为空被拒备注冻结且历史尝试不变线下订单两个流水号为空可选方式接口返回原路不可用原因企微材料含套餐用量与设备维度模板缺控件时提交明确失败
## 6. 优先轮询出口、留痕与详情投影
- [x] 6.1 入队与融合写入时补齐新列:资产类型与资产 ID、设备号快照、代理归属、尝试上限、有效期起算时间
- [x] 6.2 执行路径写执行开始/结束时间与集成交互日志标识;调度路径写下次计划执行时间;出队时写出队时间
- [x] 6.3 新增人工关闭用例与接口:原因必填、写审计、未完成项转已关闭终态出队、不再发起上游调用;权限限定超级管理员与平台账号
- [x] 6.4 新增独立周期任务类型承载有效期到期处理:固定常量有效期,超期未完成项转已过期终态出队并保留触发类型、尝试次数与最近失败原因;按既有周期任务注册形态注册到任务处理器与 Worker 调度;不新增可维护配置项
- [x] 6.5 新增失败或已过期项的人工重触发:复用人工入队入口建立新活动项、尝试次数归零、写操作者与原因;不绕过并发上限、停复机持锁拒绝与任务类型范围
- [x] 6.6 查询接口补齐返回字段(资产标识、设备号、店铺与代理归属、任务类型、触发与尝试次数、最大尝试次数、最近与下一次执行时间、执行起止与耗时、入队与出队时间、结果与失败原因、集成交互日志标识),并新增状态/任务类型/触发类型/时间范围与失败或超限筛选;时间范围走统一参数与闭区间
- [x] 6.7 既有资产解析端点的卡与设备两个分支返回只读优先轮询状态投影(是否在优先轮询中、最近触发场景、最近轮询结果、最近轮询时间、失败原因),读取不改变队列且受数据范围约束
- [x] 6.8 确认新终态(已关闭、已过期)在状态约束、活动项唯一键与读侧投影下均可读且不占活动项键位
- [x] 6.9 更新路由说明文案:移除「不提供有效期与人工重触发入口」等与本次相反的描述
- [x] 6.10 在测试环境 smoke人工关闭、超期出队、失败项重触发、异常与重试筛选视图、资产详情投影读取前后队列不变
## 7. 通道阈值命中留痕
- [x] 7.1 达量判定事务内写入命中事实(累计流量读数、阈值、单位、周期起点、判定时间、触发来源);留痕写入失败 MUST NOT 放行或延迟停机
- [x] 7.2 周期恢复路径写入解锁时间与复机结果或跳过原因,且不覆盖停机命中事实
- [x] 7.3 新增顶层路径的命中记录查询端点(自带超级管理员与平台用户门禁、按卡数据范围下推、越权与不存在不可区分),支持按通道、卡、计费周期与判定时间范围筛选;时间范围走统一参数与闭区间
- [x] 7.4 历史行兼容:判定时间为既有行创建时间,命中数值与阈值为空时按「无」展示
- [x] 7.5 在测试环境 smoke造一条达量命中并核对返回的用量与阈值数字改阈值后历史命中仍返回命中时数值新周期恢复留痕不覆盖命中事实
## 8. 代理注册审批与提现留痕
- [x] 8.1 代理注册审批材料新增「业务员」字段(上级店铺当前业务员名称快照,为空时以无标记提交),控件缺失时明确失败(仅新增字段强制映射)
- [x] 8.2 提现提交与重提记录本次依据的资料资格版本标识与校验结果(时间、是否通过、未通过稳定原因),冻结进审批尝试快照并在提现详情返回;不改写历史尝试
- [x] 8.3 店铺列表与详情返回直接下级代理数量,按页批量聚合、受数据范围约束、无下级返回零
- [x] 8.4 落地迁移 000237 的列语义与稳定原因取值(资格不存在、已失效、已停用、资料未通过审批)
- [x] 8.5 在测试环境 smoke上级无业务员时的材料字段、资格被替换后历史尝试仍返回原校验结果、下级数量统计
## 9. 手机号关联与验证码限制
- [x] 9.1 关联查询返回手机号当前有效关联数量,与既有「最多十项」上限判定共用同一计数口径并按页批量聚合
- [x] 9.2 关联查询返回最近解绑人名称快照、账号标识与解绑时间;有效关系该三字段为空且不以创建人或更新时间填充
- [x] 9.3 短信验证码校验在 Redis 以手机号维度原子累加失败计数,达到阈值后短时锁定并返回既有请求过多错误码;锁定检查先于验证码比对,锁定期内即使验证码正确也拒绝,窗口内校验成功清零;阈值与窗口为固定常量,提示不泄露验证码正确性、剩余次数与内部键名
- [x] 9.4 计数写入失败时放行并记录MUST NOT 因限流计数故障阻断注册、绑定、换绑、换证与登录成功路径;校验失败不消费验证码的既有语义保持不变
- [x] 9.5 在测试环境 smoke连续错误达阈值后被拒含提交正确验证码、锁定到期恢复、成功后清零、失败不消费验证码
## 10. 导出列补齐
- [x] 10.1 佣金明细导出按固定列序补齐十五列(店铺、业务员、用户组、资产类型、设备类型、设备型号、资产标识、订单号、下单时间、佣金金额、佣金来源、佣金状态、关联佣金明细、入账后金额、创建时间);归属与设备列按导出执行时当前事实补充,缺值以空值导出;回溯行金额为负、可提现为不可提现
- [x] 10.2 报表导出首列新增序号:分组行按展示顺序连续递增,合计行序号列留空且不改合计行其他列的既有表示
- [x] 10.3 确认两类导出不改既有导出粒度、创建期冻结与执行期只读语义
- [x] 10.4 在测试环境或本地导出 smoke核对列名、列序、负数与空值处理 —— CSV 产物级已验证(表头 20 列、前 15 列为规定列序、缺值空、回溯行负值、报表序号与合计行空HTTP 异步导出链路POST /api/admin/export-tasks → asynq 分片 → 对象存储下载)未覆盖
## 11. 时间筛选契约
- [x] 11.1 本变更已新增 `export-time-filter` delta受影响端点表纳入员工账单列表含既有产生时间参数改名、优先轮询项查询、通道阈值命中查询并写明各端点筛选的时间字段实现须与该 delta 一致
- [x] 11.2 三处新增筛选一律复用既有共享严格解析器与闭区间语义,不得复制既有账单日期解析或客户端可选时间解析
- [x] 11.3 员工账单产生时间使用统一参数名,旧参数名与旧格式一律拒绝;核销通过时间使用 `decided_start_time`/`decided_end_time`
- [x] 11.4 优先轮询查询按入队时间、阈值命中查询按判定时间使用统一参数名
- [x] 11.5 在测试环境 smoke三处筛选的拒绝集小数秒、无时区、date-only、空格分隔、`±hhmm`、未补零、非法日历日期)与边界语义(含两端、开始晚于结束拒绝)与既有契约一致
## 12. 端点、路由、文档与生成物
- [x] 12.1 按下列清单落地新增端点(方法、路径、权限主体);变更既有端点不新增路由
| 方法 | 路径 | 权限主体 | 来源任务 |
| --- | --- | --- | --- |
| GET | `/api/admin/refunds/order-options` | 退款组门禁(超级管理员/平台/代理,企业账号拒绝)+ 订单数据范围 | 5.6 |
| POST | `/api/admin/refunds/:id/offline-settlement` | 同退款组门禁 + 订单数据范围 | 5.4 |
| POST | `/api/admin/polling-priority-items/:id/close` | 超级管理员与平台账号 | 6.3 |
| POST | `/api/admin/polling-priority-items/:id/retrigger` | 与人工入队一致(含数据范围内的代理账号) | 6.5 |
| GET | `/api/admin/carrier-traffic-threshold-hits` | 超级管理员与平台账号 + 卡数据范围 | 7.3 |
- [x] 12.2 新端点经 `internal/routes.Register` 注册 RouteSpec需要子路径的新端点必须注册在同段参数路由之前字面量优先并在验证项中核对路由顺序实际生效
- [x] 12.3 通道阈值命中查询使用顶层路径,避免与通道详情的参数路由冲突
- [x] 12.4 本变更复用既有 Handler因此不修改 `cmd/api/docs.go``cmd/gendocs/main.go``pkg/openapi/handlers.go`;若实现中确需新增 Handler 类型,必须同步这三处与 `internal/bootstrap` 装配ENG-ROUTE-001
- [x] 12.5 更新既有路由说明文案(含优先轮询详情中与本次相反的描述)与受影响 DTO 的 OpenAPI 描述
- [x] 12.6 在 Spec 同步阶段把新端点以反引号形式补入对应主 Spec 的「可达操作索引」,与证据矩阵双向一致
- [x] 12.7 运行 `gofmt -w``go build ./cmd/api ./cmd/worker``go run cmd/gendocs/main.go`,并确认连续两次生成结果一致
## 13. 证据链同步与验证
- [x] 13.1 同步 `docs/verification/context-reset/requirement-evidence.json`:为本次新增需求逐条补证据行(键名与需求名严格一致、路径与验证命令真实可复现)
- [x] 13.2 同步 `docs/verification/context-reset/entry-capability-requirement-matrix.json`:为新端点补 http 行、为新增周期任务类型补异步行,并把新需求名追加进相应既有行的 `requirements`
- [x] 13.3 复核既有被修改需求的证据行仍可复现(键不变,仅路径与验证输出可能需更新)
- [x] 13.4 运行 `openspec doctor --json``openspec validate --all``./scripts/context-health.sh`要求输出「Context 健康检查通过」并核对本变更与主 Spec 一致 —— 主 Spec 同步(需求名 169→180后记录员在真实仓库重跑`./scripts/context-health.sh``Context 健康检查通过`、exit=0`openspec validate --all` → 36 passed / 0 failed、exit=0`openspec doctor --json` → healthy=true、exit=0见 verification.md 第 4 节)
- [ ] 13.5 在测试环境完成端到端 smoke逐组核对①员工账单筛选、两金额口径与详情投影②商户池四类语义与列表只读③H5 弹窗类型与类别排序;④退款原因、备注、渠道标识、补录、可选方式与企微材料;⑤优先轮询关闭、超期、重触发、异常视图与详情投影;⑥阈值命中留痕与查询;⑦代理业务员、资格校验留痕与下级数量;⑧手机号关联数量与最近解绑人;⑨验证码失败限制;⑩佣金明细导出十五列;⑪报表导出序号列;⑫迁移 up/down/up、时间筛选拒绝集与路由顺序 —— 未勾:①②③④⑤⑥⑦⑧⑨⑩⑪各域均已有 smoke 证据(见 verification.md未覆盖项为 HTTP 异步导出链路POST /api/admin/export-tasks → asynq 分片 → 对象存储下载)、真实企微提交(退款/注册审批模板控件未在生产配置)、跨域一次总冒烟;上述未覆盖项补齐后再勾
- [x] 13.6 记录验证证据(命令与原文输出)到本变更的验证说明,供归档核对
## 14. 上线前置与回滚
- [x] 14.1 整理上线说明:六对迁移清单与执行顺序、新增端点与参数变更、失效字段默认值
- [x] 14.2 整理回滚步骤:代码回退后新增列可空或带默认值、读侧对空值按「无」处理、既有优先轮询行不因空的下次执行时间被判过期
- [ ] 14.3 **待维护者执行**:在生产配置企业微信退款审批(套餐已用量与总量、资产类型、设备类型与型号、原支付渠道交易流水号)与代理注册审批(业务员)新增控件的映射,并以 `GET /api/admin/wecom/scenes` 返回的启用记录与 `control_mapping` 作为验收证据 —— 待维护者在生产配置企微控件映射并以 `GET /api/admin/wecom/scenes``control_mapping` 作为验收证据
- [x] 14.4 说明员工账单时间参数替换的调用方同步要求(旧参数名不再被接受,需同步前端)