feat(H5弹窗): AUG26-007 风险换卡与运营弹窗投放通知
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 9m23s
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 9m23s
新增 000225 迁移:运营弹窗配置表 tb_h5_popup_configuration(页面/范围/优先级/频率/受控动作/启停/有效期/版本) 与 tb_notification 可空 JSONB 列 popup_snapshot。 新增通知直建窄接口 DirectWriter.CreateOrGetPersonal:与 Outbox 消费共用 prepareDelivery 的渲染、 展示期与 CreateIdempotent 规则,冲突时回查返回既有行;同步扩展个人通知查询与已读两处类型白名单, 并按个人客户入口补齐投递审计来源。 新增 H5 候选与风险换卡:GET /api/c/v1/popup-candidates 先判风险资格(广电卡 + 风险停机 + 无活动物流换货单),命中只返回风险候选;未命中再按时间/启停/页面/店铺/设备类型/卡类型范围/频率 匹配运营配置。POST /api/c/v1/risk-exchanges/:asset_id/address 锁资产行后幂等创建待发货物流换货单, 首次地址锁定,不沿用资产级群发通知。 新增后台运营弹窗配置 CRUD 与启停(仅超级管理员与平台账号),更新递增版本并刷新最近更新时间, 标题与正文统一拒绝 URL 与前端路由,全部写操作记录操作者、前后值、版本与时间。 同步 OpenAPI(cmd/gendocs、cmd/api/docs.go、pkg/openapi/handlers.go)与参数校验中文提示共用实现。
This commit is contained in:
69
internal/model/h5_popup_configuration.go
Normal file
69
internal/model/h5_popup_configuration.go
Normal file
@@ -0,0 +1,69 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"time"
|
||||
|
||||
"github.com/bytedance/sonic"
|
||||
)
|
||||
|
||||
// H5PopupConfiguration 是 H5 运营弹窗全局配置的 PostgreSQL 持久化事实。
|
||||
// 配置只决定后续投放:启停与有效期不删除行,停用走 Enabled 置 0;
|
||||
// Version 是配置版本而非乐观锁,更新事务内递增并参与频率去重键,旧版本通知保留原快照不被改写。
|
||||
// 范围四维统一用 JSONB 字符串数组:空数组表示该维度未配置即全量,已配置而资产该维度无值时不命中。
|
||||
type H5PopupConfiguration struct {
|
||||
ID uint `gorm:"column:id;primaryKey;autoIncrement" json:"id"`
|
||||
Title string `gorm:"column:title;type:varchar(100);not null" json:"title"`
|
||||
Content string `gorm:"column:content;type:varchar(2000);not null" json:"content"`
|
||||
Pages StringJSONBArray `gorm:"column:pages;type:jsonb;not null;default:'[]'" json:"pages"`
|
||||
ShopIDs StringJSONBArray `gorm:"column:shop_ids;type:jsonb;not null;default:'[]'" json:"shop_ids"`
|
||||
DeviceTypes StringJSONBArray `gorm:"column:device_types;type:jsonb;not null;default:'[]'" json:"device_types"`
|
||||
CardTypes StringJSONBArray `gorm:"column:card_types;type:jsonb;not null;default:'[]'" json:"card_types"`
|
||||
Priority int `gorm:"column:priority;type:integer;not null;default:0" json:"priority"`
|
||||
Frequency string `gorm:"column:frequency;type:varchar(20);not null;default:'once'" json:"frequency"`
|
||||
ActionType string `gorm:"column:action_type;type:varchar(40);not null;default:''" json:"action_type"`
|
||||
Enabled int `gorm:"column:enabled;type:smallint;not null;default:0" json:"enabled"`
|
||||
StartsAt time.Time `gorm:"column:starts_at;type:timestamptz;not null" json:"starts_at"`
|
||||
EndsAt time.Time `gorm:"column:ends_at;type:timestamptz;not null" json:"ends_at"`
|
||||
Version int64 `gorm:"column:version;type:bigint;not null;default:1" json:"version"`
|
||||
BaseModel `gorm:"embedded"`
|
||||
CreatedAt time.Time `gorm:"column:created_at;type:timestamptz;not null;autoCreateTime" json:"created_at"`
|
||||
UpdatedAt time.Time `gorm:"column:updated_at;type:timestamptz;not null;autoUpdateTime" json:"updated_at"`
|
||||
}
|
||||
|
||||
// TableName 返回 H5 运营弹窗配置表名。
|
||||
func (H5PopupConfiguration) TableName() string {
|
||||
return "tb_h5_popup_configuration"
|
||||
}
|
||||
|
||||
// NotificationPopupSnapshot 是弹窗投放通知冻结的投放快照,作为可空 JSONB 写入 tb_notification。
|
||||
// 只承载配置标识与受控动作,不含任何 URL 或前端路由;风险换卡弹窗没有配置,ConfigID 与 ConfigVersion 为 0。
|
||||
type NotificationPopupSnapshot struct {
|
||||
ConfigID uint `json:"config_id"`
|
||||
ConfigVersion int64 `json:"config_version"`
|
||||
AssetType string `json:"asset_type"`
|
||||
AssetID uint `json:"asset_id"`
|
||||
ActionType string `json:"action_type"`
|
||||
}
|
||||
|
||||
// Value 将弹窗快照序列化为 JSONB;nil 接收者写入 NULL,供非弹窗通知保持该列为空。
|
||||
func (s *NotificationPopupSnapshot) Value() (driver.Value, error) {
|
||||
if s == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return sonic.Marshal(s)
|
||||
}
|
||||
|
||||
// Scan 从 JSONB 读取弹窗快照;数据库 NULL 时清空快照。
|
||||
func (s *NotificationPopupSnapshot) Scan(value any) error {
|
||||
if value == nil {
|
||||
*s = NotificationPopupSnapshot{}
|
||||
return nil
|
||||
}
|
||||
data, ok := value.([]byte)
|
||||
if !ok {
|
||||
*s = NotificationPopupSnapshot{}
|
||||
return nil
|
||||
}
|
||||
return sonic.Unmarshal(data, s)
|
||||
}
|
||||
Reference in New Issue
Block a user