Files
junhong_cmp_fiber/internal/model/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

95 lines
7.6 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 model
import (
"time"
)
// PackageTrafficAlertRule 套餐真流量预警规则模型。
// 每个套餐商品至多一条当前规则package_id 唯一),只提供创建、修改阈值与启停,不提供删除;
// 规则变更只影响后续扫描,已产生的预警快照不被改写。
// 表内不设软删除列:停用由 enabled 表达,唯一约束因此可用非部分索引,规避部分索引与 OnConflict 的谓词问题。
type PackageTrafficAlertRule struct {
// ID 主键。
ID uint `gorm:"column:id;primaryKey;autoIncrement" json:"id"`
BaseModel `gorm:"embedded"`
// PackageID 套餐商品ID唯一约束保证每个商品至多一条当前规则。
PackageID uint `gorm:"column:package_id;type:bigint;not null;uniqueIndex:uq_package_traffic_alert_rule_package;comment:套餐商品ID" json:"package_id"`
// ThresholdPercent 真流量预警阈值百分比,取值 1100允许两位小数。
ThresholdPercent float64 `gorm:"column:threshold_percent;type:numeric(5,2);not null;comment:真流量预警阈值百分比 1100" json:"threshold_percent"`
// Enabled 状态 0-禁用 1-启用;停用后扫描不再创建新预警。
Enabled int `gorm:"column:enabled;type:smallint;not null;default:0;comment:状态 0-禁用 1-启用" json:"enabled"`
// Remark 备注,最多 500 字符。
Remark string `gorm:"column:remark;type:varchar(500);not null;default:'';comment:备注" json:"remark"`
// CreatedAt 创建时间。
CreatedAt time.Time `gorm:"column:created_at;type:timestamptz;not null;autoCreateTime" json:"created_at"`
// UpdatedAt 最近更新时间,阈值修改与启停均刷新。
UpdatedAt time.Time `gorm:"column:updated_at;type:timestamptz;not null;autoUpdateTime" json:"updated_at"`
}
// TableName 返回套餐真流量预警规则表名。
func (PackageTrafficAlertRule) TableName() string {
return "tb_package_traffic_alert_rule"
}
// PackageTrafficAlert 套餐真流量达量预警事实模型。
// 唯一键为「主套餐使用记录 + 阈值快照」;触发时的阈值、汇总用量、资产与归属快照一律冻结,
// 后续归属或绑定变化不得改写本行,导出与列表只读这些快照列。
type PackageTrafficAlert struct {
// ID 主键。
ID uint `gorm:"column:id;primaryKey;autoIncrement" json:"id"`
BaseModel `gorm:"embedded"`
// PackageUsageID 主套餐使用记录IDmaster_usage_id 为空且按优先级/生效时间/编号取第一条)。
PackageUsageID uint `gorm:"column:package_usage_id;type:bigint;not null;uniqueIndex:uq_package_traffic_alert_usage_threshold,priority:1;comment:主套餐使用记录ID" json:"package_usage_id"`
// PackageID 阈值来源的套餐商品ID快照。
PackageID uint `gorm:"column:package_id;type:bigint;not null;comment:阈值来源套餐商品ID" json:"package_id"`
// RuleID 触发时的规则ID快照。
RuleID uint `gorm:"column:rule_id;type:bigint;not null;comment:触发时规则ID" json:"rule_id"`
// AssetType 资产类型 iot_card/device。
AssetType string `gorm:"column:asset_type;type:varchar(16);not null;comment:资产类型 iot_card-物联网卡 device-设备" json:"asset_type"`
// AssetID 资产ID取自使用记录的绑定资产。
AssetID uint `gorm:"column:asset_id;type:bigint;not null;comment:资产ID" json:"asset_id"`
// AssetIdentifierSnapshot 资产标识快照。
AssetIdentifierSnapshot string `gorm:"column:asset_identifier_snapshot;type:varchar(100);not null;default:'';comment:资产标识快照" json:"asset_identifier_snapshot"`
// CardIdentifierSnapshot 卡标识快照。
CardIdentifierSnapshot string `gorm:"column:card_identifier_snapshot;type:varchar(100);not null;default:'';comment:卡标识快照" json:"card_identifier_snapshot"`
// CounterpartIdentifierSnapshot 对应标识符快照(卡→当前设备标识,设备→当前卡标识)。
CounterpartIdentifierSnapshot string `gorm:"column:counterpart_identifier_snapshot;type:varchar(100);not null;default:'';comment:对应标识符快照" json:"counterpart_identifier_snapshot"`
// DeviceTypeSnapshot 设备类型快照。
DeviceTypeSnapshot string `gorm:"column:device_type_snapshot;type:varchar(50);not null;default:'';comment:设备类型快照" json:"device_type_snapshot"`
// DeviceModelSnapshot 设备型号快照。
DeviceModelSnapshot string `gorm:"column:device_model_snapshot;type:varchar(100);not null;default:'';comment:设备型号快照" json:"device_model_snapshot"`
// PackageNameSnapshot 套餐名称快照。
PackageNameSnapshot string `gorm:"column:package_name_snapshot;type:varchar(255);not null;default:'';comment:套餐名称快照" json:"package_name_snapshot"`
// UsedMBSnapshot 触发时该资产全部当前有效套餐的真已用量汇总(MB)。
UsedMBSnapshot int64 `gorm:"column:used_mb_snapshot;type:bigint;not null;default:0;comment:真已用量汇总快照(MB)" json:"used_mb_snapshot"`
// LimitMBSnapshot 触发时该资产全部当前有效套餐的真总量快照汇总(MB)。
LimitMBSnapshot int64 `gorm:"column:limit_mb_snapshot;type:bigint;not null;comment:真总量快照汇总(MB)" json:"limit_mb_snapshot"`
// UsagePercentSnapshot 汇总比例快照,单位百分比。
UsagePercentSnapshot float64 `gorm:"column:usage_percent_snapshot;type:numeric(9,2);not null;default:0;comment:汇总比例快照(%)" json:"usage_percent_snapshot"`
// ThresholdPercentSnapshot 触发阈值快照,与使用记录组成唯一键。
ThresholdPercentSnapshot float64 `gorm:"column:threshold_percent_snapshot;type:numeric(5,2);not null;uniqueIndex:uq_package_traffic_alert_usage_threshold,priority:2;comment:触发阈值快照(%)" json:"threshold_percent_snapshot"`
// ExpiresAtSnapshot 主套餐使用记录到期时间快照;为空表示无法推算剩余天数。
ExpiresAtSnapshot *time.Time `gorm:"column:expires_at_snapshot;type:timestamptz;comment:主套餐到期时间快照" json:"expires_at_snapshot,omitempty"`
// TriggeredAt 触发时间。
TriggeredAt time.Time `gorm:"column:triggered_at;type:timestamptz;not null;comment:触发时间" json:"triggered_at"`
// ShopIDSnapshot 触发时资产所属店铺ID快照。
ShopIDSnapshot uint `gorm:"column:shop_id_snapshot;type:bigint;not null;default:0;comment:触发时所属店铺ID快照" json:"shop_id_snapshot"`
// ShopNameSnapshot 触发时店铺名称快照。
ShopNameSnapshot string `gorm:"column:shop_name_snapshot;type:varchar(100);not null;default:'';comment:触发时店铺名称快照" json:"shop_name_snapshot"`
// BusinessOwnerAccountIDSnapshot 触发时店铺业务员账号ID快照。
BusinessOwnerAccountIDSnapshot *uint `gorm:"column:business_owner_account_id_snapshot;type:bigint;comment:触发时业务员账号ID快照" json:"business_owner_account_id_snapshot,omitempty"`
// BusinessOwnerNameSnapshot 触发时业务员账号名快照。
BusinessOwnerNameSnapshot string `gorm:"column:business_owner_name_snapshot;type:varchar(64);not null;default:'';comment:触发时业务员名称快照" json:"business_owner_name_snapshot"`
// NotificationEventID 可靠通知事件ID无有效业务员或写入失败前保持为空。
NotificationEventID string `gorm:"column:notification_event_id;type:varchar(64);not null;default:'';comment:可靠通知事件ID" json:"notification_event_id"`
// CreatedAt 创建时间。
CreatedAt time.Time `gorm:"column:created_at;type:timestamptz;not null;autoCreateTime" json:"created_at"`
// UpdatedAt 最近更新时间;预警事实创建后不再改写。
UpdatedAt time.Time `gorm:"column:updated_at;type:timestamptz;not null;autoUpdateTime" json:"updated_at"`
}
// TableName 返回套餐真流量达量预警表名。
func (PackageTrafficAlert) TableName() string {
return "tb_package_traffic_alert"
}