feat(套餐真流量预警): AUG26-004 真流量预警规则、达量扫描通知与导出
新增 000228 迁移:规则表 tb_package_traffic_alert_rule(每套餐商品至多一条,无软删除,package_id 非部分唯一约束)、达量预警快照表 tb_package_traffic_alert(以主套餐使用记录 + 阈值快照为唯一键, 触发时冻结用量、额度、比例、阈值、到期时间、归属与资产快照),并为 tb_package_usage 新增扫描 范围部分索引 idx_package_usage_alert_scope;down 在预警表存在数据时阻断回滚。 新增规则维护接口 GET/POST/PUT /api/admin/package-traffic-alert-rules(仅超级管理员与平台账号): 创建校验套餐存在且真流量额度大于零,阈值为 1%~100% 的两位小数;修改只影响后续扫描,不回填也 不改写既有预警快照;全部写操作记录操作者、前后值与时间。 新增每日 06:00(Asia/Shanghai)扫描任务 package:traffic:alert:scan,与套餐临期扫描共用 data_cleanup 队列:按资产汇总当前有效套餐的真流量,分子取使用记录真已用量、分母取使用记录真总量快照,命中 主套餐规则阈值时在同一事务创建预警与可靠通知事件;重复执行以唯一冲突视为已处理,不重复投递, 不建停机锁、不调用运营商。 新增预警列表、详情与异步导出 GET /api/admin/package-traffic-alerts、GET /api/admin/package-traffic-alerts/:id、 POST /api/admin/package-traffic-alerts/export,列表与详情一律读冻结快照;新增通知类型 package.traffic.alert 与受控目标 package_traffic_alert_detail,目标解析仅对超级管理员与平台账号 返回可跳转,越权与不存在统一按资源不可见处理。 同步 OpenAPI(cmd/gendocs、cmd/api/docs.go、pkg/openapi/handlers.go)、审计动作与资源注册、上下文 健康检查证据;归档变更并同步 package-traffic-alert 主 Spec。
This commit is contained in:
@@ -1,39 +0,0 @@
|
||||
## Context
|
||||
|
||||
套餐商品已有 `real_data_mb`,套餐使用记录承载真实用量;现有通知以事件投递并按接收人隔离。预警必须是套餐级观测,不能复用运营商通道阈值停机锁。
|
||||
|
||||
## Decisions
|
||||
|
||||
- 新增套餐规则和预警表;规则以套餐唯一,预警以套餐使用记录+阈值快照唯一,保存触发时流量、资产/卡/设备、套餐和店铺/业务员快照。
|
||||
- 扫描查询当前有效使用记录,按资产汇总真实用量与额度,并选出主套餐规则;用唯一约束和事务创建预警及通知事件,扫描可安全重跑。
|
||||
- 不向未来业务员补发:接收人仅在预警创建事务中解析。读侧以资产范围过滤,导出复用现有快照任务。
|
||||
|
||||
## 管理、扫描与查询动作契约
|
||||
|
||||
### 规则维护
|
||||
|
||||
- `POST /package-traffic-alert-rules`:仅超级管理员、平台用户;请求 `package_id`、`threshold_percent`(大于等于 1、小于等于 100,允许小数)、`enabled`、`remark`(最多 500 字符)。套餐必须存在且 `real_data_mb > 0`;同套餐已有规则返回“套餐已存在真流量预警规则”。
|
||||
- `PUT /package-traffic-alert-rules/:id`:允许修改阈值、启停、备注;不修改已产生预警快照。停用后扫描不建新预警;启用或降低阈值后不主动回填,仅由下一次扫描按当前有效套餐判断。
|
||||
- `GET /package-traffic-alert-rules` 返回套餐、真流量总额度、阈值、启用状态、备注和更新时间。所有成功写操作记录操作者、前后值和时间。
|
||||
|
||||
### 扫描与预警创建
|
||||
|
||||
- Worker 只读取当前有效套餐使用记录;按同一资产聚合这些记录的真实已用量与套餐 `real_data_mb`,不读取虚流量、展示流量或运营商通道累计值。主套餐不存在有效规则、总额度不大于零或比例未达阈值时跳过。
|
||||
- 对命中主套餐规则的套餐使用记录,在事务中写预警唯一键 `(package_usage_id, threshold_percent_snapshot)`,同时冻结套餐、资产、卡、当前设备、店铺、业务员、真实用量、额度、比例、阈值和触发时间。唯一冲突视为已处理,不重复投递通知。
|
||||
- 创建时仅解析当前资产所属店铺的有效业务员;存在时在同一可靠事件链创建一条站内通知,保存预警 ID 作为幂等键;不存在时只保存预警。通知失败进入既有可靠投递恢复,不能删除预警或重新计算快照。
|
||||
|
||||
### 列表、详情与导出
|
||||
|
||||
- `GET /package-traffic-alerts`:仅超级管理员、平台用户,先应用既有资产数据范围;支持套餐、店铺、业务员、资产/卡标识、阈值、触发时间和通知投递状态筛选、分页。返回冻结快照与通知结果,当前归属变化不得改写预警事实。
|
||||
- `GET /package-traffic-alerts/:id`:同一数据范围校验后返回完整预警快照和通知投递历史;越权与不存在统一按既有资源不可见处理。
|
||||
- `POST /package-traffic-alerts/export`:复用异步导出;创建时冻结操作者、筛选、时间范围和可见资产范围。每行对应一条预警,执行时不得扩大范围或重算已冻结流量字段。
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- 流量数据延迟 → 下次扫描补建,不回写已冻结预警。
|
||||
- 多卡设备 → 以使用记录资产归属聚合,不从当前设备反推流量。
|
||||
- 扫描并发 → 唯一索引处理同一命中重复创建。
|
||||
|
||||
## Migration Plan
|
||||
|
||||
新增成对迁移和索引;隔离环境验证规则启停/降阈值、有效套餐汇总、去重、无业务员、权限导出和 up/down/up。
|
||||
@@ -1,32 +0,0 @@
|
||||
## Purpose
|
||||
|
||||
按套餐真实流量和当前有效套餐事实生成一次性达量预警,向资产所属店铺当时有效业务员投递可追溯通知,而不将通道级停复机控制或虚流量混入套餐预警。
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: 真流量预警规则
|
||||
系统 SHALL 为每个套餐商品维护至多一条当前真流量预警规则,阈值为 1% 至 100% 的小数百分比。规则启用、修改或降低阈值只影响后续扫描;既有预警 MUST 保留触发时的套餐、阈值、流量和资产快照。规则停用后停止创建新预警;重新启用或降低阈值后,下次扫描发现已有有效套餐达量时必须补建符合条件的预警。
|
||||
|
||||
#### Scenario: 降低阈值后补建
|
||||
- **WHEN** 管理员降低一个启用规则的阈值,下一次扫描发现其有效套餐已达到新阈值
|
||||
- **THEN** 系统创建预警并冻结新阈值,不修改既有预警快照
|
||||
|
||||
### Requirement: 汇总口径、去重与通知
|
||||
系统 SHALL 以同一资产全部当前有效套餐的 `真流量使用量 / 真流量总额度` 汇总比例判断达量,并使用该资产主套餐的规则。实际消耗流量的套餐使用记录所关联资产是权威归属;插拔卡时预警同时展示卡和当前关联设备,但 MUST NOT 汇总多张卡。虚流量、已失效/过期/非当前有效套餐不得计入。
|
||||
|
||||
同一套餐使用记录和同一命中阈值 MUST 至多创建一条预警。预警创建时仅向资产所属店铺当时有效业务员创建站内通知;无有效业务员时仍保留预警事实但不补发给未来新增业务员。通知和预警均须幂等,重复扫描不得重复创建。
|
||||
|
||||
#### Scenario: 多个有效套餐共同达量
|
||||
- **WHEN** 某资产的多个当前有效套餐真流量汇总达到其主套餐规则阈值
|
||||
- **THEN** 系统为命中套餐使用记录创建唯一预警,并只向扫描时该资产所属店铺的有效业务员投递通知
|
||||
|
||||
#### Scenario: 重复扫描
|
||||
- **WHEN** 相同套餐使用记录和相同阈值被重复扫描命中
|
||||
- **THEN** 系统保留原预警和通知,不创建重复记录
|
||||
|
||||
### Requirement: 预警查询与导出
|
||||
超级管理员和平台用户 SHALL 在既有资产数据范围内查询和导出预警;列表和导出返回资产、卡、当前设备、套餐使用记录、真流量用量/额度/比例、阈值快照、触发时间及通知投递结果。导出必须使用既有异步任务并冻结创建时操作者、筛选条件和可见范围。
|
||||
|
||||
#### Scenario: 受限导出
|
||||
- **WHEN** 平台用户在其资产数据范围内创建预警导出
|
||||
- **THEN** 导出仅包含创建时可见预警,即使任务执行期间店铺归属发生变化
|
||||
@@ -1,16 +0,0 @@
|
||||
## 1. 数据与规则
|
||||
|
||||
- [ ] 1.1 追踪套餐使用有效态、真流量字段、资产/卡/设备关联、有效业务员、通知事件及异步导出调用链。
|
||||
- [ ] 1.2 新增成对迁移、模型和约束:套餐唯一规则、预警快照、使用记录+阈值唯一去重、查询/导出索引。
|
||||
- [ ] 1.3 实现规则 CRUD、1%~100% 校验、启停和审计;更新套餐管理 OpenAPI。
|
||||
|
||||
## 2. 扫描与读侧
|
||||
|
||||
- [ ] 2.1 实现可重跑扫描:按资产汇总当前有效套餐真流量、选择主套餐规则、排除虚流量/失效记录,并原子创建预警与通知事件。
|
||||
- [ ] 2.2 接入既有任务调度和通知投递,确保无有效业务员仍建预警、重复扫描不重复通知。
|
||||
- [ ] 2.3 实现受资产数据范围保护的预警列表/详情和异步导出,冻结导出筛选与可见范围。
|
||||
|
||||
## 3. 验证
|
||||
|
||||
- [ ] 3.1 在隔离数据库验证迁移 up/down/up、规则变化补建、有效套餐汇总、重复扫描、插拔卡展示、通知接收人和导出权限。
|
||||
- [ ] 3.2 运行 `gofmt -w`、`go build ./cmd/api ./cmd/worker`、`go run cmd/gendocs/main.go`、`openspec validate add-package-real-usage-alerts --strict`、`openspec doctor --json` 和 `./scripts/context-health.sh`;自动化测试按项目决策为 N/A。
|
||||
@@ -0,0 +1,91 @@
|
||||
## Context
|
||||
|
||||
套餐商品 `tb_package.real_data_mb` 是可变配置值(后台可编辑)。套餐使用记录 `tb_package_usage` 以 `data_usage_mb`(真已用)与 `data_limit_mb`(真总量快照,购买时写入、全仓无更新路径)承载真实用量;既有 H5、后台资产详情、套餐历史、客户视图、代理开放接口与卡导出一律以使用记录快照展示真额度。现有通知以事件投递并按接收人隔离,后台通知无类型白名单,但新增通知类型必须先注册模板定义,否则渲染失败、通知写不进库。资产数据范围对超级管理员与平台用户为空操作。预警必须是套餐级观测,不能复用运营商通道阈值停机锁。
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
|
||||
- 建立套餐真流量规则、预警事实、通知与可追溯导出。
|
||||
|
||||
**Non-Goals:**
|
||||
|
||||
- 不建停机锁、不调用运营商、不做停复机、不读取通道计费周期与通道累计流量。
|
||||
|
||||
## Decisions
|
||||
|
||||
### 额度与用量口径
|
||||
|
||||
- 分子取 `tb_package_usage.data_usage_mb`(真已用),由卡网关读数增量经既有扣减链路写入。
|
||||
- 分母取 `tb_package_usage.data_limit_mb`(真总量快照)。该字段与 H5、后台资产详情、套餐历史、客户视图、代理开放接口与卡导出的既有展示一致;使用记录快照不可变,历史可复现。
|
||||
- 商品 `real_data_mb` 仅作为规则启用时的配置合法性校验(必须大于零),不作为汇总分母。
|
||||
- 禁止读取虚流量(`virtual_total_mb_snapshot` / `enable_virtual_data_snapshot`)、展示量(`display_gain_ratio_snapshot` 放大结果)、卡级累计(`tb_iot_card.data_usage_mb`)与运营商通道累计值(`last_gateway_reading_mb`、`current_month_usage_mb`)。
|
||||
|
||||
### 有效套餐集合与主套餐
|
||||
|
||||
- 按资产(`iot_card_id` 或 `device_id`,二者互斥非零)汇总当前有效套餐使用记录,含加油包:`status IN (1,2) AND refund_id IS NULL`。
|
||||
- 排除 0-待生效、3-已过期、4-已失效。
|
||||
- 判定只依据 `status`,不得仅用 `expires_at` 判断过期(过期与失效由异步任务改状态,`expires_at` 在生效中状态下可能已到期)。
|
||||
- 主套餐(阈值来源)为 `master_usage_id IS NULL`;多条时按 `priority ASC, activated_at ASC, id ASC` 取第一条,与既有当前主套餐查询一致。
|
||||
- 主套餐无有效规则、总额度不大于零或比例未达阈值时跳过。
|
||||
|
||||
### 汇总、去重与补建
|
||||
|
||||
- 唯一键为 `(package_usage_id, threshold_percent_snapshot)`。
|
||||
- 降阈值后同一使用记录产生新阈值快照的预警属预期补建,不是重复;升阈值或停用不修改既有预警快照。
|
||||
- 唯一冲突视为已处理,不重复投递通知。
|
||||
- 实际消耗流量的使用记录所关联资产是权威归属;插拔卡同时展示卡与当前关联设备,不汇总多张卡。
|
||||
|
||||
### 规则维护
|
||||
|
||||
- `POST /package-traffic-alert-rules`:仅超级管理员、平台用户;请求 `package_id`、`threshold_percent`(大于等于 1、小于等于 100,允许小数)、`enabled`、`remark`(最多 500 字符)。套餐必须存在且 `real_data_mb > 0`(配置合法性校验);同套餐已有规则返回“套餐已存在真流量预警规则”。
|
||||
- `PUT /package-traffic-alert-rules/:id`:允许修改阈值、启停、备注;不修改已产生预警快照。停用后扫描不建新预警;启用或降低阈值后不主动回填,仅由下一次扫描按当前有效套餐判断。
|
||||
- `GET /package-traffic-alert-rules` 返回套餐、真流量总额度、阈值、启用状态、备注和更新时间。所有成功写操作记录操作者、前后值和时间。
|
||||
|
||||
### 扫描与幂等
|
||||
|
||||
- Worker 只读取当前有效套餐使用记录(见上集合),按资产聚合真已用量与真总量快照。
|
||||
- 调度形态照抄既有 `package_expiry_reminder`:`asynq.Scheduler` 以 `CRON_TZ=Asia/Shanghai` 注册,调度器仅在单例 Worker 角色创建。既有临期扫描不使用 `asynq.Unique`,也无启动补偿;本项同样不新增手工触发扫描入口。
|
||||
- 幂等由事实唯一键(`(package_usage_id, threshold_percent_snapshot)`)与 Outbox 幂等追加(`OnConflict(event_id) DoNothing`)保证。
|
||||
- 事实与事件在同一事务写入:预警、通知事件与审计同事务提交;唯一冲突视为已处理,不重复投递通知。
|
||||
- 通知失败进入既有可靠投递恢复,不能删除预警或重新计算快照。
|
||||
|
||||
### 通知
|
||||
|
||||
- 类别沿用 `expiry`,不新增类别;新增通知类型 `package.traffic.alert`。
|
||||
- 必须注册注册表 `Definition`,否则渲染失败、通知写不进库。
|
||||
- 接收人新建「仅业务员」解析路径:`business_owner_account_id` 指向启用且 `user_type` 为平台的账号;不得复用返回「店铺 agent 账号 ∪ 业务员」的既有 resolver。
|
||||
- 无有效业务员时只保存预警,不补发给未来业务员。
|
||||
- 新增受控 `ref_type` 并在通知目标定义中注册,指向预警详情,不返回 URL。
|
||||
- 幂等键内嵌使用记录与阈值快照。
|
||||
|
||||
### 列表、详情与导出
|
||||
|
||||
- `GET /package-traffic-alerts`:仅超级管理员、平台用户,先应用既有资产数据范围;支持套餐、店铺、业务员、资产/卡标识、阈值、触发时间和通知投递状态筛选、分页。返回冻结快照与通知结果,当前归属变化不得改写预警事实。
|
||||
- `GET /package-traffic-alerts/:id`:同一数据范围校验后返回完整预警快照和通知投递历史;越权与不存在统一按既有资源不可见处理。
|
||||
- `POST /package-traffic-alerts/export`:仅超级管理员、平台用户。复用既有异步导出任务机制(新增导出场景并复用导出任务创建服务),场景需在全部五处注册点落地(场景常量、`DataSource`、注册中心实例化、场景白名单、导出 DTO `oneof` 两处);创建时冻结操作者、筛选、时间范围与可见资产范围。既有通用导出入口不做场景级角色校验,故本项以受控入口暴露该场景。
|
||||
- 数据范围使用导出侧的范围过滤(空范围拒绝),不得使用请求上下文版过滤(空范围语义相反)。
|
||||
- 导出列:资产类型、资产标识、对应标识符、卡标识、设备类型、设备型号、套餐名称、真流量已用量、真流量额度、比例、阈值快照、到期时间、剩余天数、触发时间、店铺、业务员、用户组、通知投递结果;不含运营商通道列。
|
||||
- 归属口径按 PRD §2.16 写死:导出中套餐、用量、总量、阈值与到期时间使用触发快照;店铺、业务员、用户组按导出执行时当前归属补充。预警行仍冻结店铺与业务员快照,供列表、详情与追溯;两者口径不同且不得混用。用户组不得写入店铺表,按既有实时推导。
|
||||
|
||||
### 数据范围与权限
|
||||
|
||||
- 列表、详情与导出仅超级管理员与平台用户。
|
||||
- 先应用既有资产数据范围;当前对超管或平台无实际过滤,该要求保留为冻结语义与未来放开的前置。
|
||||
- 越权与不存在统一按资源不可见处理。
|
||||
- 导出范围在创建时冻结,执行期间归属变化不得扩大范围。
|
||||
|
||||
### 与通道阈值严格分离
|
||||
|
||||
- 不建停机锁、不调用运营商、不做停复机、不读取通道计费周期与通道累计流量,不触碰既有停复机服务与网关停机调用。
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- 流量数据延迟 → 下次扫描补建,不回写已冻结预警。
|
||||
- 多卡设备 → 以使用记录资产归属聚合,不从当前设备反推流量。
|
||||
- 扫描并发 → 唯一索引处理同一命中重复创建。
|
||||
- 分母取快照 → 商品改价后重跑,比例与预警不因商品当前值变化;代价是商品价与预警分母可能不同,属预期。
|
||||
|
||||
## Migration Plan
|
||||
|
||||
新增成对迁移和索引;隔离环境验证规则启停/降阈值、有效套餐汇总、去重、无业务员、权限导出和 up/down/up。
|
||||
@@ -9,8 +9,9 @@
|
||||
## What Changes
|
||||
|
||||
- 为每个套餐商品维护至多一条 1%~100% 真流量预警规则;规则变更只影响后续扫描,既有预警冻结快照。
|
||||
- 扫描同一资产全部当前有效套餐的真流量汇总,以实际消耗流量的套餐记录关联资产和主套餐规则判断达量。
|
||||
- 为同一套餐使用记录和阈值仅建一条预警,通知当时有效业务员,并提供权限受控列表与异步导出。
|
||||
- 扫描同一资产全部当前有效套餐的真流量汇总,分子取使用记录真已用量、分母取使用记录真总量快照,以实际消耗流量的套餐记录关联资产和主套餐规则判断达量;不读取虚流量、展示量、卡级累计或运营商通道累计值。
|
||||
- 为同一套餐使用记录和阈值快照仅建一条预警,事务内同时写可靠通知事件,通知当时有效业务员,并提供权限受控列表与异步导出。
|
||||
- 预警与运营商通道阈值严格分离:不建停机锁、不调用运营商、不做停复机。
|
||||
|
||||
## Capabilities
|
||||
|
||||
@@ -24,4 +25,4 @@
|
||||
|
||||
## Impact
|
||||
|
||||
影响套餐配置、套餐使用/流量扫描任务、资产投影、通知事件、导出、审计和新增 Schema。
|
||||
影响套餐配置、套餐使用/流量扫描任务、资产投影、通知事件、导出、审计和新增 Schema。
|
||||
@@ -0,0 +1,89 @@
|
||||
## Purpose
|
||||
|
||||
按套餐真实流量和当前有效套餐事实生成一次性达量预警,向资产所属店铺当时有效业务员投递可追溯通知,而不将通道级停复机控制或虚流量混入套餐预警。
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: 真流量预警规则
|
||||
|
||||
系统 SHALL 为每个套餐商品维护至多一条当前真流量预警规则,阈值为 1% 至 100% 的小数百分比。规则启用、修改或降低阈值只影响后续扫描;既有预警 MUST 保留触发时的套餐、阈值、流量和资产快照。规则停用后停止创建新预警;重新启用或降低阈值后,下次扫描发现已有有效套餐达量时必须补建符合条件的预警。
|
||||
|
||||
#### Scenario: 降低阈值后补建
|
||||
|
||||
- **WHEN** 管理员降低一个启用规则的阈值,下一次扫描发现其有效套餐已达到新阈值
|
||||
- **THEN** 系统创建预警并冻结新阈值,不修改既有预警快照
|
||||
|
||||
#### Scenario: 套餐无真流量额度不可启用
|
||||
|
||||
- **WHEN** 管理员为真流量商品额度不大于零的套餐启用规则
|
||||
- **THEN** 系统拒绝并保持该套餐无有效规则
|
||||
|
||||
### Requirement: 有效套餐与汇总口径
|
||||
|
||||
系统 SHALL 以同一资产全部当前有效套餐的真流量汇总比例判断达量,比例分子为套餐使用记录真已用量,分母为套餐使用记录真总量快照,并使用该资产主套餐的规则。当前有效套餐 MUST 为 `status IN (1,2) AND refund_id IS NULL` 的使用记录,含加油包;系统 MUST 排除待生效、已过期与已失效记录,且判定过期 MUST 只依据状态,不得仅用到期时间。实际消耗流量的套餐使用记录所关联资产是权威归属;插拔卡时预警同时展示卡与当前关联设备,但 MUST NOT 汇总多张卡。虚流量、展示量、卡级累计与运营商通道累计值 MUST NOT 计入。主套餐 MUST 为 `master_usage_id IS NULL` 的使用记录,存在多条时按优先级、生效时间、编号依次取第一条。
|
||||
|
||||
#### Scenario: 多个有效套餐共同达量
|
||||
|
||||
- **WHEN** 某资产的多个当前有效套餐真流量汇总达到其主套餐规则阈值
|
||||
- **THEN** 系统为命中套餐使用记录创建唯一预警,并只向扫描时该资产所属店铺的有效业务员投递通知
|
||||
|
||||
#### Scenario: 商品改价后重跑
|
||||
|
||||
- **WHEN** 管理员修改套餐商品的当前真流量配置值,同一使用记录被再次扫描
|
||||
- **THEN** 汇总比例与预警结果不因商品当前值变化
|
||||
|
||||
#### Scenario: 已过期或已失效记录不计入
|
||||
|
||||
- **WHEN** 资产的某条套餐使用记录状态为已过期或已失效
|
||||
- **THEN** 系统不将该记录的真已用量与真总量计入汇总
|
||||
|
||||
### Requirement: 去重与补建
|
||||
|
||||
系统 SHALL 以套餐使用记录和命中阈值快照的组合作为预警唯一键。同一组合 MUST 至多创建一条预警;唯一冲突 MUST 视为已处理且不重复投递通知。降低阈值后对同一使用记录产生新阈值快照的预警 MUST 视为预期补建,不受既有预警阻塞。
|
||||
|
||||
#### Scenario: 重复扫描
|
||||
|
||||
- **WHEN** 相同套餐使用记录和相同阈值被重复扫描命中
|
||||
- **THEN** 系统保留原预警和通知,不创建重复记录
|
||||
|
||||
### Requirement: 预警通知
|
||||
|
||||
系统 SHALL 在预警创建事务内向资产所属店铺当时有效业务员创建站内通知。预警类别 MUST 沿用 `expiry`,并 MUST 注册通知类型 `package.traffic.alert` 的模板定义。有效业务员 MUST 为店铺 `business_owner_account_id` 指向的启用平台账号,系统 MUST NOT 向店铺代理账号补发。无有效业务员时系统 MUST 只保留预警,且 MUST NOT 补发给未来业务员。通知 MUST 幂等,幂等键 MUST 内嵌使用记录与阈值快照,且 MUST 注册指向预警详情的受控目标,不返回 URL。
|
||||
|
||||
#### Scenario: 存在有效业务员
|
||||
|
||||
- **WHEN** 资产所属店铺在扫描时存在有效业务员
|
||||
- **THEN** 系统在同一事务创建预警与一条站内通知,通知在账户侧可见、未读数正确并可跳转受控目标
|
||||
|
||||
#### Scenario: 无有效业务员
|
||||
|
||||
- **WHEN** 资产所属店铺在扫描时无有效业务员
|
||||
- **THEN** 系统只保存预警,不创建通知且不向未来业务员补发
|
||||
|
||||
### Requirement: 预警查询与导出
|
||||
|
||||
超级管理员和平台用户 SHALL 在既有资产数据范围内查询和导出预警;先应用既有资产数据范围,该范围当前对超级管理员与平台无实际过滤,保留为冻结语义与未来放开的前置。越权与不存在 MUST 统一按既有的资源不可见处理。导出 MUST 复用既有异步任务并在创建时冻结操作者、筛选、时间范围与可见资产范围,执行期间归属变化 MUST NOT 扩大范围,且 MUST 按触发时间筛选。列表、详情与导出 MUST 返回资产类型、资产标识、对应标识符、卡标识、设备类型、设备型号、套餐名称、真流量已用量、真流量额度、比例、阈值快照、到期时间、剩余天数、触发时间、店铺、业务员、用户组与通知投递结果,且 MUST NOT 包含运营商通道列。导出中套餐、用量、总量、阈值与到期时间 MUST 使用触发快照,店铺、业务员与用户组 MUST 按执行时当前归属补充;预警行仍冻结店铺与业务员快照供列表、详情与追溯,两种口径 MUST NOT 混用;用户组 MUST NOT 写入店铺表,按既有实时推导。
|
||||
|
||||
#### Scenario: 受限导出
|
||||
|
||||
- **WHEN** 平台用户在其资产数据范围内创建预警导出
|
||||
- **THEN** 导出仅包含创建时可见预警,即使任务执行期间店铺归属发生变化
|
||||
|
||||
#### Scenario: 归属变更后导出
|
||||
|
||||
- **WHEN** 预警记录创建后资产所属店铺或业务员变更,再执行已创建导出任务
|
||||
- **THEN** 套餐、用量、总量、阈值与到期时间仍使用触发快照,店铺、业务员与用户组使用执行时当前归属,且不超出任务创建时冻结的可见范围
|
||||
|
||||
#### Scenario: 越权或不存在
|
||||
|
||||
- **WHEN** 调用者读取不在其可见范围内的预警详情
|
||||
- **THEN** 系统按资源不可见处理,不区分越权、不存在与已删除
|
||||
|
||||
### Requirement: 与运营商通道阈值分离
|
||||
|
||||
系统 SHALL 将本能力与运营商通道阈值控制严格分离。达量预警 MUST NOT 创建停机锁、调用运营商、执行停复机,MUST NOT 读取通道计费周期或通道累计流量。
|
||||
|
||||
#### Scenario: 达量预警不触发通道动作
|
||||
|
||||
- **WHEN** 系统为某资产创建真流量达量预警
|
||||
- **THEN** 系统不写入任何停机锁、不调用运营商接口且不改变卡停机状态
|
||||
@@ -0,0 +1,28 @@
|
||||
## 1. 数据与规则
|
||||
|
||||
- [x] 1.1 追踪套餐使用有效态、真流量字段、资产/卡/设备关联、有效业务员、通知事件及异步导出调用链。
|
||||
- [x] 1.2 新增成对迁移、模型和约束:套餐唯一规则、预警快照、使用记录+阈值快照唯一去重、查询/导出索引。
|
||||
- [x] 1.3 实现规则 CRUD、1%~100% 校验(分母为使用记录真总量快照,规则启用校验商品真流量额度大于零)、启停和审计;更新套餐管理 OpenAPI。
|
||||
|
||||
## 2. 扫描与通知
|
||||
|
||||
- [x] 2.1 实现可重跑扫描:按资产汇总当前有效套餐(`status IN (1,2) AND refund_id IS NULL`)真流量,分子取使用记录真已用量、分母取使用记录真总量快照,排除虚流量/展示量/卡级累计/通道累计与失效记录。
|
||||
- [x] 2.2 选择主套餐规则(`master_usage_id IS NULL`,多条按 `priority ASC, activated_at ASC, id ASC` 取第一条),主套餐无规则或总额度不大于零时跳过。
|
||||
- [x] 2.3 原子创建预警(唯一键 `(package_usage_id, threshold_percent_snapshot)`)与通知事件;事实与事件同一事务,唯一冲突视为已处理。
|
||||
- [x] 2.4 接入既有任务调度(照抄临期扫描的 Cron 加单例注册,不使用 `asynq.Unique`、不新增启动补偿、不新增手工触发入口)与既有通知投递。
|
||||
- [x] 2.5 注册通知类型常量 `package.traffic.alert`、注册表 Definition(类别沿用 `expiry`)、仅业务员解析路径(`business_owner_account_id` 指向启用平台账号)与受控目标定义;无有效业务员只建预警、幂等键内嵌使用记录与阈值快照。
|
||||
|
||||
## 3. 读侧与导出
|
||||
|
||||
- [x] 3.1 实现受资产数据范围保护的预警列表/详情(仅超管与平台,越权与不存在统一不可见)。
|
||||
- [x] 3.2 按 PRD §2.16 落地导出列:资产类型、资产标识、对应标识符、卡标识、设备类型、设备型号、套餐名称、真流量已用量、真流量额度、比例、阈值快照、到期时间、剩余天数、触发时间、店铺、业务员、用户组、通知投递结果(不含运营商通道列)。
|
||||
- [x] 3.3 在五处注册点接入导出场景:场景常量、`DataSource` 实现、注册中心实例化、场景白名单、导出 DTO `oneof` 两处;表头在 dispatch 阶段冻结。
|
||||
- [x] 3.4 导出使用导出侧范围过滤(空范围拒绝,不得用请求上下文版);创建时冻结操作者、筛选、时间范围与可见资产范围;流量与阈值用触发快照,店铺/业务员/用户组按执行时当前归属补充,用户组按既有实时推导。
|
||||
|
||||
## 4. 验证
|
||||
|
||||
- [x] 4.1 在隔离数据库验证迁移 up/down/up、规则启停/降阈值补建、有效套餐汇总、重复扫描、插拔卡展示、通知接收人和导出权限。
|
||||
- [x] 4.2 验证商品改价后重跑比例与预警不因商品当前值变化;已过期或已失效记录不计入。
|
||||
- [x] 4.3 验证唯一冲突不重复通知、无有效业务员只建预警、通知在账户侧可见/未读正确/可跳转受控目标。
|
||||
- [x] 4.4 验证导出归属为执行时当前归属、流量与阈值为触发快照且不超创建时范围;越权与不存在统一不可见;数据范围冻结语义成立。
|
||||
- [x] 4.5 运行 `gofmt -w`、`go build ./cmd/api ./cmd/worker`、`go run cmd/gendocs/main.go`、`openspec validate add-package-real-usage-alerts --strict`、`openspec doctor --json` 和 `./scripts/context-health.sh`;自动化测试按项目决策为 N/A。
|
||||
95
openspec/specs/package-traffic-alert/spec.md
Normal file
95
openspec/specs/package-traffic-alert/spec.md
Normal file
@@ -0,0 +1,95 @@
|
||||
# package-traffic-alert Specification
|
||||
|
||||
## Purpose
|
||||
|
||||
按套餐真实流量和当前有效套餐事实生成一次性达量预警,向资产所属店铺当时有效业务员投递可追溯通知,而不将通道级停复机控制或虚流量混入套餐预警。
|
||||
|
||||
## Requirements
|
||||
|
||||
### Requirement: 真流量预警规则
|
||||
|
||||
系统 SHALL 为每个套餐商品维护至多一条当前真流量预警规则,阈值为 1% 至 100% 的小数百分比。规则启用、修改或降低阈值只影响后续扫描;既有预警 MUST 保留触发时的套餐、阈值、流量和资产快照。规则停用后停止创建新预警;重新启用或降低阈值后,下次扫描发现已有有效套餐达量时必须补建符合条件的预警。
|
||||
|
||||
#### Scenario: 降低阈值后补建
|
||||
|
||||
- **WHEN** 管理员降低一个启用规则的阈值,下一次扫描发现其有效套餐已达到新阈值
|
||||
- **THEN** 系统创建预警并冻结新阈值,不修改既有预警快照
|
||||
|
||||
#### Scenario: 套餐无真流量额度不可启用
|
||||
|
||||
- **WHEN** 管理员为真流量商品额度不大于零的套餐启用规则
|
||||
- **THEN** 系统拒绝并保持该套餐无有效规则
|
||||
|
||||
### Requirement: 有效套餐与汇总口径
|
||||
|
||||
系统 SHALL 以同一资产全部当前有效套餐的真流量汇总比例判断达量,比例分子为套餐使用记录真已用量,分母为套餐使用记录真总量快照,并使用该资产主套餐的规则。当前有效套餐 MUST 为 `status IN (1,2) AND refund_id IS NULL` 的使用记录,含加油包;系统 MUST 排除待生效、已过期与已失效记录,且判定过期 MUST 只依据状态,不得仅用到期时间。实际消耗流量的套餐使用记录所关联资产是权威归属;插拔卡时预警同时展示卡与当前关联设备,但 MUST NOT 汇总多张卡。虚流量、展示量、卡级累计与运营商通道累计值 MUST NOT 计入。主套餐 MUST 为 `master_usage_id IS NULL` 的使用记录,存在多条时按优先级、生效时间、编号依次取第一条。
|
||||
|
||||
#### Scenario: 多个有效套餐共同达量
|
||||
|
||||
- **WHEN** 某资产的多个当前有效套餐真流量汇总达到其主套餐规则阈值
|
||||
- **THEN** 系统为命中套餐使用记录创建唯一预警,并只向扫描时该资产所属店铺的有效业务员投递通知
|
||||
|
||||
#### Scenario: 商品改价后重跑
|
||||
|
||||
- **WHEN** 管理员修改套餐商品的当前真流量配置值,同一使用记录被再次扫描
|
||||
- **THEN** 汇总比例与预警结果不因商品当前值变化
|
||||
|
||||
#### Scenario: 已过期或已失效记录不计入
|
||||
|
||||
- **WHEN** 资产的某条套餐使用记录状态为已过期或已失效
|
||||
- **THEN** 系统不将该记录的真已用量与真总量计入汇总
|
||||
|
||||
### Requirement: 去重与补建
|
||||
|
||||
系统 SHALL 以套餐使用记录和命中阈值快照的组合作为预警唯一键。同一组合 MUST 至多创建一条预警;唯一冲突 MUST 视为已处理且不重复投递通知。降低阈值后对同一使用记录产生新阈值快照的预警 MUST 视为预期补建,不受既有预警阻塞。
|
||||
|
||||
#### Scenario: 重复扫描
|
||||
|
||||
- **WHEN** 相同套餐使用记录和相同阈值被重复扫描命中
|
||||
- **THEN** 系统保留原预警和通知,不创建重复记录
|
||||
|
||||
### Requirement: 预警通知
|
||||
|
||||
系统 SHALL 在预警创建事务内向资产所属店铺当时有效业务员创建站内通知。预警类别 MUST 沿用 `expiry`,并 MUST 注册通知类型 `package.traffic.alert` 的模板定义。有效业务员 MUST 为店铺 `business_owner_account_id` 指向的启用平台账号,系统 MUST NOT 向店铺代理账号补发。无有效业务员时系统 MUST 只保留预警,且 MUST NOT 补发给未来业务员。通知 MUST 幂等,幂等键 MUST 内嵌使用记录与阈值快照,且 MUST 注册指向预警详情的受控目标,不返回 URL。
|
||||
|
||||
#### Scenario: 存在有效业务员
|
||||
|
||||
- **WHEN** 资产所属店铺在扫描时存在有效业务员
|
||||
- **THEN** 系统在同一事务创建预警与一条站内通知,通知在账户侧可见、未读数正确并可跳转受控目标
|
||||
|
||||
#### Scenario: 无有效业务员
|
||||
|
||||
- **WHEN** 资产所属店铺在扫描时无有效业务员
|
||||
- **THEN** 系统只保存预警,不创建通知且不向未来业务员补发
|
||||
|
||||
### Requirement: 预警查询与导出
|
||||
|
||||
超级管理员和平台用户 SHALL 在既有资产数据范围内查询和导出预警;先应用既有资产数据范围,该范围当前对超级管理员与平台无实际过滤,保留为冻结语义与未来放开的前置。越权与不存在 MUST 统一按既有的资源不可见处理。导出 MUST 复用既有异步任务并在创建时冻结操作者、筛选、时间范围与可见资产范围,执行期间归属变化 MUST NOT 扩大范围,且 MUST 按触发时间筛选。列表、详情与导出 MUST 返回资产类型、资产标识、对应标识符、卡标识、设备类型、设备型号、套餐名称、真流量已用量、真流量额度、比例、阈值快照、到期时间、剩余天数、触发时间、店铺、业务员、用户组与通知投递结果,且 MUST NOT 包含运营商通道列。导出中套餐、用量、总量、阈值与到期时间 MUST 使用触发快照,店铺、业务员与用户组 MUST 按执行时当前归属补充;预警行仍冻结店铺与业务员快照供列表、详情与追溯,两种口径 MUST NOT 混用;用户组 MUST NOT 写入店铺表,按既有实时推导。
|
||||
|
||||
#### Scenario: 受限导出
|
||||
|
||||
- **WHEN** 平台用户在其资产数据范围内创建预警导出
|
||||
- **THEN** 导出仅包含创建时可见预警,即使任务执行期间店铺归属发生变化
|
||||
|
||||
#### Scenario: 归属变更后导出
|
||||
|
||||
- **WHEN** 预警记录创建后资产所属店铺或业务员变更,再执行已创建导出任务
|
||||
- **THEN** 套餐、用量、总量、阈值与到期时间仍使用触发快照,店铺、业务员与用户组使用执行时当前归属,且不超出任务创建时冻结的可见范围
|
||||
|
||||
#### Scenario: 越权或不存在
|
||||
|
||||
- **WHEN** 调用者读取不在其可见范围内的预警详情
|
||||
- **THEN** 系统按资源不可见处理,不区分越权、不存在与已删除
|
||||
|
||||
### Requirement: 与运营商通道阈值分离
|
||||
|
||||
系统 SHALL 将本能力与运营商通道阈值控制严格分离。达量预警 MUST NOT 创建停机锁、调用运营商、执行停复机,MUST NOT 读取通道计费周期或通道累计流量。
|
||||
|
||||
#### Scenario: 达量预警不触发通道动作
|
||||
|
||||
- **WHEN** 系统为某资产创建真流量达量预警
|
||||
- **THEN** 系统不写入任何停机锁、不调用运营商接口且不改变卡停机状态
|
||||
|
||||
## 接口
|
||||
|
||||
接口:`GET /api/admin/package-traffic-alert-rules`(查询套餐真流量预警规则列表);`POST /api/admin/package-traffic-alert-rules`(创建套餐真流量预警规则);`PUT /api/admin/package-traffic-alert-rules/{id}`(修改套餐真流量预警规则);`GET /api/admin/package-traffic-alerts`(查询套餐真流量达量预警列表);`GET /api/admin/package-traffic-alerts/{id}`(查询套餐真流量达量预警详情);`POST /api/admin/package-traffic-alerts/export`(导出套餐真流量达量预警)。
|
||||
Reference in New Issue
Block a user