docs(H5弹窗): AUG26-007 归档变更并同步 h5-popup-notification 主 Spec 与证据链
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 1m31s

This commit is contained in:
2026-09-15 16:29:56 +08:00
parent 333ba4b647
commit 41722760b1
8 changed files with 314 additions and 3 deletions

View File

@@ -113,6 +113,7 @@
- 请求携带页面与当前资产标识;资产参数统一沿用既有 `identifier` 形态,**不采用路径参数**。
- 资产归属使用 `customer_binding.OwnsAsset`;资产不存在与其归属校验失败**统一按资源不可见返回**,不形成可枚举差异。
- 风险命中时只返回风险候选;未命中风险再按时间、启停、页面、范围与频率匹配运营配置。
- 风险资格成立即只处理风险分支:当天已投放风险候选(无论客户是否已读)时,当天不再返回运营弹窗候选,次日恢复按各自频率与优先级匹配;「风险优先」只用于两者同时命中的判定。理由:避免客户关闭风险提示后当天立即看到营销弹窗,与「稍后处理仅抑制当天」的口径一致。
## 后台与 H5 动作契约
@@ -124,7 +125,7 @@
### H5 候选查询与风险换卡
- `GET /api/c/v1/popup-candidates`:当前个人客户必须提交 `page` 和当前资产 `identifier`;首页也必须先由客户选定当前资产。服务校验该资产属于当前客户,否则按资源不可见返回。
- 查询先判断广电卡、运营商扩展状态风险停机、无活动物流换货单(`shipping` 且状态含已完成)、未提交风险地址和「客户+资产+上海自然日」未展示;命中时创建/复用风险通知并只返回风险换卡候选。关闭或稍后处理只调用既有通知已读,不修改风险资格,次日允许再次投放。
- 查询先判断广电卡、运营商扩展状态风险停机、无活动物流换货单(`shipping` 且状态含已完成)、未提交风险地址和「客户+资产+上海自然日」未展示;命中时创建/复用风险通知并只返回风险换卡候选。关闭或稍后处理只调用既有通知已读,不修改风险资格,次日允许再次投放。当天已投放风险候选(无论客户是否已读)后,当天不再返回运营弹窗候选,次日恢复运营匹配。
- 未命中风险时,按当前时间、启用状态、页面、店铺/设备类型/卡类型范围和频率匹配运营配置;同维度多值取任一命中,无配置即全量。只返回优先级最高一条,同优先级取最近更新时间;以客户、配置版本、资产、日期/一次性键创建或复用通知。
### 风险地址提交与通知读取
@@ -141,6 +142,13 @@
- **两个白名单是重复实现**:本次按最小改动同时扩两处;漏改一处即静默失效,验证必须覆盖列表可见与已读生效两条。
- **`Creator/Updater` 置 0**:保留「客户自助发起」语义,但会让换货单缺少后台操作者;后续如需追溯,应另行定义客户来源标识而非复用账号 ID。
- **`flow_type` 必须参与资格判定**:若实现遗漏该条件,直接换货单会永久压制风险候选,且不会报错,属静默错误。
- **当天不投放运营候选**:风险资格成立当天不再做运营匹配,代价是当天放弃一次运营投放机会,次日恢复。这是产品口径,已固化在 spec 的风险换卡 Requirement 中。
### 后续候选(本 Change 不实施)
- **风险换卡的资产状态与退款守卫**:当前按 PRD 只校验广电卡、运营商扩展状态严格等于风险停机、无活动物流换货单三项;是否还需排除「已换货/已停用」资产与「存在未终结退款」的资产尚未定论。属需求外增量,需独立 Change。
- **设备类型取值归一化**:设备类型沿用 `tb_device.device_type` 自由文本,存在大小写与写法变体(如 MiFi/MiFI归一化方案待定。属需求外增量需独立 Change。
- **H5 资产参数形态与错误码边界、投递侧类型约束**:候选用 query `identifier` 而地址提交用路径 `asset_id` 的形态统一、「资产不可见」在既有 H5 资产入口为 403 而本接口为 400 的边界口径、弹窗类型通知经 Outbox 通道投递时的载荷约束,三者的统一方案待定。属需求外增量,需独立 Change。
## Migration Plan

View File

@@ -6,7 +6,7 @@
### Requirement: 风险换卡候选与地址提交
系统 SHALL 仅在当前 H5 客户访问的资产为广电卡、运营商扩展状态为风险停机、且不存在待填写收货信息、待发货、已发货待确认或已完成的物流换货单时返回风险换卡弹窗。物流换货单只统计物流换货流程;直接换货单不参与该判定。同一客户同一资产每天至多展示一次;稍后处理仅抑制当天,次日仍可命中。风险换卡优先级固定高于运营弹窗。
系统 SHALL 仅在当前 H5 客户访问的资产为广电卡、运营商扩展状态为风险停机、且不存在待填写收货信息、待发货、已发货待确认或已完成的物流换货单时返回风险换卡弹窗。物流换货单只统计物流换货流程;直接换货单不参与该判定。同一客户同一资产每天至多展示一次;稍后处理仅抑制当天,次日仍可命中。当天已投放风险换卡候选后,无论客户是否已读,当天不再返回运营弹窗候选;次日风险与运营按各自的频率与优先级规则恢复匹配。风险换卡优先级固定高于运营弹窗仅适用于两者同时命中的场景
客户提交收货人姓名、收货手机号和完整地址文本后,系统 MUST 幂等创建关联旧资产的物流换货单;首次地址锁定,客户不得修改,重复提交返回首次创建的换货单与首次地址且不覆盖。并发提交同一资产时只创建一张换货单。自动换货单不预设业务数据迁移,发货选择新资产时仍由后台按既有换货流程决定。风险条件不再成立或地址已提交后停止新投放,已投放通知在通知中心展示 90 天。客户请求不属于自己的资产时,系统 MUST 返回与资产不存在相同的不可见结果。

View File

@@ -83,11 +83,16 @@
### 与 design 的取舍(已确认保留)
- **风险资格命中时不下落到运营匹配**design §9 把「客户+资产+上海自然日未展示」列入「风险命中」集合,字面执行会得出「客户当天关闭风险弹窗后,再请求可返回运营弹窗」。本实现改为:风险资格(广电卡 + 严格等于风险停机的运营商扩展状态 + 无活动物流换货单)成立即只处理风险分支——当日通知存在且未读则返回同一通知(满足 3.3),已读则返回空候选(满足 3.4既不下落运营匹配也不在当天重复投放。理由spec 场景「风险与运营候选同时命中 → 仅返回风险换卡候选」的前置条件只是「同时满足风险换卡与运营条件」,并未排除「当天已关闭」;且「稍后处理仅抑制当天」若同时放开运营投放,会与「风险换卡优先级固定高于运营弹窗」相冲突。取舍点注释在 `internal/application/h5popup/candidate.go``GetCandidate`
- **风险资格命中时不下落到运营匹配**design §9 把「客户+资产+上海自然日未展示」列入「风险命中」集合,字面执行会得出「客户当天关闭风险弹窗后,再请求可返回运营弹窗」。本实现改为:风险资格(广电卡 + 严格等于风险停机的运营商扩展状态 + 无活动物流换货单)成立即只处理风险分支——当日通知存在且未读则返回同一通知(满足 3.3),已读则返回空候选(满足 3.4既不下落运营匹配也不在当天重复投放。理由spec 场景「风险与运营候选同时命中 → 仅返回风险换卡候选」的前置条件只是「同时满足风险换卡与运营条件」,并未排除「当天已关闭」;且「稍后处理仅抑制当天」若同时放开运营投放,会与「风险换卡优先级固定高于运营弹窗」相冲突。取舍点注释在 `internal/application/h5popup/candidate.go``GetCandidate`该口径已在 spec 的风险换卡 Requirement 固化(当天不再返回运营弹窗候选,次日恢复)。
- **运营弹窗频率去重键不含资产**:按 design §7 与 spec「每客户每配置支持仅一次或每天一次」键为「客户+配置+版本」(`daily` 再追加上海自然日);「后台与 H5 动作契约」中出现的「资产」只落在通知 `ref_type/ref_id/ref_key`。注释在 `operationEventKey`
- **未实测项**个人客户令牌访问后台配置接口由后台认证中间件先行拒绝HTTP 401 / `code=1003`),因此该用例证明的是「个人令牌无法进入后台配置边界」,平台范围校验由代理与企业两条 403 证据覆盖。
- **`up`/`down` 破坏性说明**`down` 会删除 `popup_snapshot` 与配置表,恢复方式与不可逆范围已写在 `000225_add_h5_popup_configuration.down.sql` 头部注释。
### 验证说明(口径与限制)
- **跨自然日重投**:以昨日 `event_id` 的已读通知作为夹具,证明「上海自然日已进入去重键、昨日已读不抑制今天」,未做真实跨日等待(未修改服务器时钟)。
- **Redis 验证面**:本轮以本地 Redis DB 6 提供会话存储(仓库 `.env` 未提供测试 Redis 地址)。本 Change 的通知投放、幂等与匹配语义全部落在 PostgreSQL不依赖 Redis故不影响验证结论后续涉及 Redis 语义的 Change 应使用测试环境地址。
### 实现落点
- 迁移:`migrations/000225_add_h5_popup_configuration.{up,down}.sql`