Files
junhong_cmp_fiber/pkg/constants/package_traffic_alert.go
break d5bcda94fe 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。
2026-09-16 17:05:55 +08:00

87 lines
3.7 KiB
Go
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.
package constants
// 套餐真流量达量预警的通知投递结果常量。
//
// 该结果不落库为独立列,而是由「预警行的可靠通知事件 + tb_outbox_event 状态 + tb_notification 事实」
// 交叉推导因此枚举值一旦发布不得调整含义。1~4 表示已写入通知事件后的四种结果,
// 5 表示触发时店铺没有有效业务员,预警只保存事实不产生通知,也不在未来补发。
const (
// PackageTrafficAlertNotifyNotified 表示通知事件已生成站内通知,接收人可读。
PackageTrafficAlertNotifyNotified = 1
// PackageTrafficAlertNotifyPending 表示通知事件已写入但尚未完成投递。
PackageTrafficAlertNotifyPending = 2
// PackageTrafficAlertNotifyFailed 表示通知事件投递达到最终失败或等待人工重放。
PackageTrafficAlertNotifyFailed = 3
// PackageTrafficAlertNotifyRecipientGone 表示事件已投递但接收人已失效,未生成站内通知。
PackageTrafficAlertNotifyRecipientGone = 4
// PackageTrafficAlertNotifyNoBusinessOwner 表示触发时店铺无有效业务员,只保存预警。
PackageTrafficAlertNotifyNoBusinessOwner = 5
)
// GetPackageTrafficAlertNotifyStatusName 返回通知投递结果的中文名称。
func GetPackageTrafficAlertNotifyStatusName(status int) string {
switch status {
case PackageTrafficAlertNotifyNotified:
return "已通知"
case PackageTrafficAlertNotifyPending:
return "待投递"
case PackageTrafficAlertNotifyFailed:
return "投递失败"
case PackageTrafficAlertNotifyRecipientGone:
return "未通知(接收人已失效)"
case PackageTrafficAlertNotifyNoBusinessOwner:
return "未通知(无有效业务员)"
default:
return "未知"
}
}
// IsValidPackageTrafficAlertNotifyStatus 判断通知投递结果枚举是否受支持。
func IsValidPackageTrafficAlertNotifyStatus(status int) bool {
switch status {
case PackageTrafficAlertNotifyNotified,
PackageTrafficAlertNotifyPending,
PackageTrafficAlertNotifyFailed,
PackageTrafficAlertNotifyRecipientGone,
PackageTrafficAlertNotifyNoBusinessOwner:
return true
default:
return false
}
}
// ResolvePackageTrafficAlertNotifyStatus 由通知事件、Outbox 状态与站内通知事实推导投递结果。
// 判断顺序必须与读侧列表、详情与导出保持完全一致,因此该口径只在此处实现一次。
// - hasEvent=false触发时无有效业务员只保存预警
// - hasNotification=true通知已生成接收人可读
// - Outbox 为最终失败/已投递但无通知/其余未终结状态,分别对应失败、接收人已失效与待投递。
func ResolvePackageTrafficAlertNotifyStatus(hasEvent bool, outboxStatus *int, hasNotification bool) int {
if !hasEvent {
return PackageTrafficAlertNotifyNoBusinessOwner
}
if hasNotification {
return PackageTrafficAlertNotifyNotified
}
if outboxStatus == nil {
return PackageTrafficAlertNotifyPending
}
switch *outboxStatus {
case OutboxStatusFailed:
return PackageTrafficAlertNotifyFailed
case OutboxStatusDelivered:
// 事件已投递却没有生成通知:接收人在投递期已失效;本能力不允许改投他人。
return PackageTrafficAlertNotifyRecipientGone
default:
return PackageTrafficAlertNotifyPending
}
}
// PackageTrafficAlertRuleMaxRemarkLength 是预警规则备注的最大长度。
const PackageTrafficAlertRuleMaxRemarkLength = 500
// PackageTrafficAlertScanAggregateType 与 Outbox 事件的聚合类型,标识事件由达量扫描产生。
const PackageTrafficAlertScanAggregateType = "package_traffic_alert"
// PackageTrafficAlertEventIDPrefix 是可靠通知事件ID前缀pta:<package_usage_id>:<阈值万分比>。
const PackageTrafficAlertEventIDPrefix = "pta"