Files
junhong_cmp_fiber/internal/model/dto/notification_dto.go
break 333ba4b647
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 9m23s
feat(H5弹窗): AUG26-007 风险换卡与运营弹窗投放通知
新增 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)与参数校验中文提示共用实现。
2026-09-15 15:23:52 +08:00

107 lines
9.3 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 dto
import "time"
// NotificationUnreadCountResponse 是后台账号未读数投影。
type NotificationUnreadCountResponse struct {
Count int64 `json:"count" description:"未读通知数量"`
DisplayCount string `json:"display_count" description:"徽标显示文本,超过 99 时为 99+"`
}
// NotificationListRequest 是后台通知基础分页参数。
type NotificationListRequest struct {
Category string `json:"category" query:"category" validate:"omitempty,oneof=approval expiry sync system" enums:"approval,expiry,sync,system" description:"通知类别 (approval:审批, expiry:临期, sync:同步, system:系统)"`
Type string `json:"type" query:"type" validate:"omitempty,oneof=system.notice package.expiring agent.recharge.completed refund.completed exchange.shipping.created agent.main_wallet.low_balance h5.popup.risk_exchange h5.popup.operation" enums:"system.notice,package.expiring,agent.recharge.completed,refund.completed,exchange.shipping.created,agent.main_wallet.low_balance,h5.popup.risk_exchange,h5.popup.operation" description:"稳定通知类型 (system.notice:系统通知, package.expiring:套餐临期, agent.recharge.completed:店铺充值入账, refund.completed:店铺退款完成, exchange.shipping.created:换货申请待处理, agent.main_wallet.low_balance:主钱包低余额, h5.popup.risk_exchange:风险换卡弹窗, h5.popup.operation:运营弹窗)"`
Severity string `json:"severity" query:"severity" validate:"omitempty,oneof=info warning error critical" enums:"info,warning,error,critical" description:"通知级别 (info:提示, warning:警告, error:错误, critical:严重)"`
IsRead *bool `json:"is_read" query:"is_read" description:"已读状态;不传时查询全部"`
Page int `json:"page" query:"page" validate:"omitempty,min=1,max=10000" minimum:"1" maximum:"10000" description:"页码,默认 1最大 10000"`
PageSize int `json:"page_size" query:"page_size" validate:"omitempty,min=1,max=50" minimum:"1" maximum:"50" description:"每页数量,默认 20最大 50"`
}
// NotificationItem 是后台账号可见的站内通知投影。
type NotificationItem struct {
ID uint `json:"id" description:"通知ID"`
Category string `json:"category" enums:"approval,expiry,sync,system" description:"通知类别 (approval:审批, expiry:临期, sync:同步, system:系统)"`
Type string `json:"type" enums:"system.notice,package.expiring,agent.recharge.completed,refund.completed,exchange.shipping.created,agent.main_wallet.low_balance,h5.popup.risk_exchange,h5.popup.operation" description:"稳定通知类型 (system.notice:系统通知, package.expiring:套餐临期, agent.recharge.completed:店铺充值入账, refund.completed:店铺退款完成, exchange.shipping.created:换货申请待处理, agent.main_wallet.low_balance:主钱包低余额, h5.popup.risk_exchange:风险换卡弹窗, h5.popup.operation:运营弹窗)"`
Severity string `json:"severity" enums:"info,warning,error,critical" description:"通知级别 (info:提示, warning:警告, error:错误, critical:严重)"`
Title string `json:"title" description:"纯文本标题"`
Body string `json:"body" description:"纯文本正文"`
RefType string `json:"ref_type" description:"受控资源类型可能为空。可选值及含义system_config:系统配置, integration_log:外部集成日志, package:套餐, asset:C端资产, refund:退款, agent_recharge:代理充值, wecom_approval:企微审批, iot_card:物联网卡, device:设备, expiring_asset:临期资产列表, shop_fund:店铺资金概况, card_sync:卡同步记录。后台点击通知应调用目标解析接口,不得直接拼接路由"`
RefID string `json:"ref_id" description:"受控资源数字ID的十进制字符串可能为空。refund、agent_recharge、wecom_approval、iot_card、device、expiring_asset、shop_fund、asset 等类型使用仅用于资源定位不是前端URL"`
RefKey string `json:"ref_key" description:"受控资源稳定Key或展示快照可能为空。system_config 为配置Keyintegration_log/card_sync 为集成标识asset 为资产标识快照仅用于定位或展示不是前端URL"`
IsRead bool `json:"is_read" description:"是否已读"`
ReadAt *time.Time `json:"read_at,omitempty" description:"首次已读时间ISO 8601"`
CreatedAt time.Time `json:"created_at" description:"创建时间ISO 8601"`
// PopupSnapshot 仅弹窗投放类型返回;其他通知类型为空。只包含配置标识、资产关联与受控动作,不含任何 URL 或前端路由。
PopupSnapshot *NotificationPopupSnapshotItem `json:"popup_snapshot,omitempty" description:"弹窗投放快照;仅 h5.popup.risk_exchange 与 h5.popup.operation 返回,其他通知类型为空"`
}
// NotificationPopupSnapshotItem 是弹窗投放通知冻结的快照投影,不含任何 URL 或前端路由。
type NotificationPopupSnapshotItem struct {
ConfigID uint `json:"config_id" description:"运营弹窗配置ID风险换卡弹窗没有配置固定为 0"`
ConfigVersion int64 `json:"config_version" description:"投放时的配置版本;风险换卡弹窗固定为 0。旧版本通知保留原快照不被改写"`
AssetType string `json:"asset_type" enums:"iot_card,device" description:"弹窗关联资产类型 (iot_card:物联网卡, device:设备)"`
AssetID uint `json:"asset_id" description:"弹窗关联资产数字ID"`
ActionType string `json:"action_type,omitempty" enums:"package_purchase,asset_wallet_recharge" description:"受控动作 (package_purchase:套餐购买, asset_wallet_recharge:资产钱包充值);为空表示无受控动作。前端按白名单映射页面,不得由后端下发 URL 或前端路由"`
}
// NotificationListResponse 是后台通知基础分页结果。
type NotificationListResponse struct {
Items []NotificationItem `json:"items" description:"通知列表"`
Total int64 `json:"total" description:"总数量"`
Page int `json:"page" description:"页码"`
Size int `json:"size" description:"每页数量"`
}
// NotificationIDParams 是单条通知路径参数。
type NotificationIDParams struct {
ID uint `json:"id" path:"id" required:"true" description:"通知ID"`
}
// NotificationReadResponse 是单条通知幂等已读结果。
type NotificationReadResponse struct {
Success bool `json:"success" description:"请求是否成功;通知不存在、属于别人或已经已读也返回 true"`
}
// NotificationTargetResponse 是通知受控目标解析结果,不包含任意 URL。
type NotificationTargetResponse struct {
TargetType string `json:"target_type" description:"前端白名单目标类型空表示不支持跳转。可选值refund_detail、agent_recharge_detail、wecom_approval_detail、iot_card_detail、device_detail、expiring_asset_list、shop_fund_summary、integration_log、system_config"`
TargetID *uint `json:"target_id,omitempty" description:"ID型目标的业务主键前端按 target_type 映射受控页面不得自行拼接任意URL"`
TargetKey string `json:"target_key,omitempty" description:"Key型目标的稳定定位值仅用于 integration_log 或 system_config 等白名单目标"`
Available bool `json:"available" description:"当前账号是否仍可访问目标false 时只展示通知正文,不执行跳转"`
}
// NotificationUnreadSummaryResponse 是后台账号未读通知的固定分类汇总。
type NotificationUnreadSummaryResponse struct {
Total int64 `json:"total" description:"未读通知总数"`
Approval int64 `json:"approval" description:"审批类未读数量"`
Expiry int64 `json:"expiry" description:"临期类未读数量"`
Sync int64 `json:"sync" description:"同步类未读数量"`
System int64 `json:"system" description:"系统类未读数量"`
}
// NotificationReadAllRequest 是后台批量已读请求。
type NotificationReadAllRequest struct {
Category string `json:"category" validate:"omitempty,oneof=approval expiry sync system" enums:"approval,expiry,sync,system" description:"可选通知类别 (approval:审批, expiry:临期, sync:同步, system:系统)"`
}
// NotificationReadAllResponse 是后台批量已读结果。
type NotificationReadAllResponse struct {
UpdatedCount int64 `json:"updated_count" description:"本次实际更新的通知数量"`
}
// PersonalNotificationListRequest 是个人客户通知的简化分页参数。
type PersonalNotificationListRequest struct {
IsRead *bool `json:"is_read" query:"is_read" description:"已读状态false 仅查询未读true 仅查询已读,不传时查询全部"`
Page int `json:"page" query:"page" validate:"omitempty,min=1,max=10000" minimum:"1" maximum:"10000" description:"页码,默认 1最大 10000"`
PageSize int `json:"page_size" query:"page_size" validate:"omitempty,min=1,max=50" minimum:"1" maximum:"50" description:"每页数量,默认 20最大 50"`
}
// PersonalNotificationListResponse 是个人客户通知的简化分页结果。
type PersonalNotificationListResponse struct {
Items []NotificationItem `json:"items" description:"当前个人客户可见的业务通知列表"`
Total int64 `json:"total" description:"总数量"`
Page int `json:"page" description:"页码"`
Size int `json:"size" description:"每页数量"`
}