Files
junhong_cmp_fiber/openspec/changes/scope-expiry-reminder-to-current-asset/design.md
邹进 713045a657
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 12m9s
fix: 第1批 M9 H5 进入资产的临期个人通知写入即已读
H5 进入资产时已同步弹窗展示临期提醒,通知中心再记未读会重复提醒;
通知按剩余天数每天新增一条,保持未读会持续累积红点。
直发通知载荷新增可选 mark_read,仅临期个人通知设置,其他直发通知与店铺临期通知仍为未读。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 10:45:20 +09:00

34 lines
3.9 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.
## Context
承接《第1批-8-9月迭代缺陷修复技术方案》第 4 节。`packageexpiry` 的 `Publish` 在每日扫描中给店铺接收人(`appendShopNotification`)和所有绑定客户(`loadRecipients`)批量写通知;个人通知列表(`PersonalNotificationListRequest`,`internal/model/dto/notification_dto.go:94`)无资产过滤。H5 令牌带当前资产(`middleware.GetCurrentAsset`),资产信息接口为 `GET /api/c/v1/asset/info`。Outbox 写入按 `event_id` 冲突忽略(`internal/infrastructure/messaging/outbox/repository.go:103`)。
## Goals / Non-Goals
**Goals:** 临期提醒只在客户进入该资产时出现,每天一次;通知中心只显示当前资产的临期提醒。
**Non-Goals:** 不改到期推算、提醒窗口与等级、店铺通知;不新增接口入参;无迁移。
## Decisions
1. **进入时判定,复用最终到期推算。** 在资产信息接口内按当前资产调用 `packageexpiry` 的推算,与每日扫描同口径。
2. **去重顺序:先查键 → 写通知 → `SET NX` → 抢到才返回字段。** 方案原顺序是“先写通知再 `SET NX`”,当天每次进入都会写一次 Outbox(虽冲突忽略,仍是无效写)。评审确认先用 Redis `EXISTS`/`GET` 检查,键存在直接返回,只有未提醒时才写通知。写通知成功后才占键,写失败不占键、不返回字段,避免“当天已去重但通知没发出”。两个并发请求都通过检查时,通知由事件号幂等,只有抢到键的一方返回字段。键定义放 `pkg/constants/redis.go`,TTL 到当天上海时间结束再加缓冲。
3. **Redis 不可用按未提醒处理。** 字段可能当天多次返回,通知由事件号兜底不重复;接受该降级,不因 Redis 故障让资产信息接口失败。
4. **只用 `ref_key` 过滤,列表、未读数、全部已读同一口径。** `ref_id` 不带资产类型,卡 5 与设备 5 冲突。过滤只作用于 `type = package.expiring AND ref_type = asset`。“全部已读”(`PUT /notifications/read-all`)也加同样的条件,否则会把客户在其他资产才能看到的临期提醒一并标为已读。
5. **停用个人客户预发布。** 去掉 `Publish` 里给绑定客户批量写通知的循环,店铺通知不变。
6. **临期个人通知写入即已读,用载荷可选标记实现。** 直发 Outbox 载荷(`PersonalCustomerDirectPayload`,和 `AdminDirectPayload` 共用一个结构)新增可选字段 `mark_read`,默认 false。只有 `appendNotification`(进入资产的临期提醒)设置为 true。`deliverOne` 创建通知时,标记为 true 就写 `is_read=true`、`read_at=now`;审计 `after_data` 改为按实际值记录,不再固定写 `false`。不采用“返回通知 ID,让前端关闭弹窗时调已读接口”(运营弹窗的做法),原因是通知由 Outbox 异步投递,接口响应时通知还不存在,拿不到 ID。改成按事件号标记已读又会带来时序问题,而且前后端都要改。
## Risks / Trade-offs
- [GET 接口产生写副作用] → 只在当天首次命中时写一次通知,其余请求只读 Redis。
- [设备补虚拟号后历史提醒匹配不上] → 影响可接受,已在 spec 中写明。
- [资产信息接口延迟增加] → 推算与 Redis 检查各一次;推算结果为非临期时不访问 Redis。
- [接口已返回 `expiry_reminder`,但前端没有展示弹窗(页面关闭、网络中断),通知却已经是已读] → 接受这个风险。第二天剩余天数变化后会生成新通知、再次弹窗;通知中心仍能看到这条历史记录。
## Migration Plan
无迁移。测试环境部署后,每日扫描不再产生个人客户临期通知;已有个人通知按过滤规则展示。已经存在的未读个人临期通知不处理。
## Open Questions
- 资产信息响应中提醒字段的具体命名在实现时确定,并写入接口文档。