Files
junhong_cmp_fiber/internal/model/dto/h5_popup_dto.go
break 5ed6b39deb
Some checks failed
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Has been cancelled
feat(收口): 补齐 8 月迭代缺口并同步 Spec 与证据链
- 新增六对成对迁移 000232–000237:H5 弹窗类型、退款结算标识与申请人备注、优先轮询事实字段与两个新终态、通道阈值命中留痕、手机号最近解绑人、提现资格校验留痕
- 退款:原因必填与申请人备注、来源支付与渠道流水冻结、线下处理流水号补录审计、按订单查询可选退款方式、企微审批材料补齐且新增字段缺失映射即明确失败
- 优先轮询:人工关闭、有效期到期独立周期任务、失败与过期人工重触发、事实字段与异常重试查询、资产解析端点只读投影
- 通道阈值:命中事实同事务留痕与命中记录查询;员工账单:列表筛选与详情投影;商户池:列表投影与统计周期语义;H5:弹窗类型与类别排序
- 手机号:有效关联数量与最近解绑人、短信验证码失败次数限制;导出:佣金明细十五列与报表序号列
- 时间筛选:三处新增筛选纳入统一严格解析契约,员工账单产生时间参数改名
- 同步 12 份主 Spec 需求、两端点与异步任务证据链,门禁 context-health 与 OpenSpec 校验通过
2026-09-18 15:34:29 +08:00

152 lines
16 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"
// CreateH5PopupConfigurationRequest 是创建 H5 运营弹窗配置的请求。
// 范围四维中只有 pages 必填;店铺、设备类型、卡类型不传或传空数组表示该维度全量。
// action_type 只接受受控动作白名单,不接受 URL、前端路由或任意参数。
type CreateH5PopupConfigurationRequest struct {
Title string `json:"title" validate:"required,min=1,max=100" required:"true" minLength:"1" maxLength:"100" description:"弹窗标题1100 字符;投放时冻结写入通知标题"`
Content string `json:"content" validate:"required,min=1,max=2000" required:"true" minLength:"1" maxLength:"2000" description:"弹窗正文12000 字符;投放时冻结写入通知正文,不接受 HTML、URL 或前端路由"`
PopupType string `json:"popup_type" validate:"required,oneof=promotion announcement" required:"true" enum:"promotion,announcement" description:"弹窗类型,必填 (promotion:套餐政策推广, announcement:通用公告);类别优先级为风险换卡提醒 > 套餐政策推广 > 通用公告,类别优先于显式优先级"`
Pages []string `json:"pages" validate:"required,min=1,max=4,dive,oneof=home asset_detail package_purchase asset_wallet_recharge" required:"true" description:"命中页面集合,至少一项 (home:首页, asset_detail:资产详情, package_purchase:套餐购买, asset_wallet_recharge:资产钱包充值)"`
ShopIDs []uint `json:"shop_ids" validate:"omitempty,max=200,dive,min=1" maxLength:"200" description:"店铺范围;不传或空数组表示全量,同维度多选取任一命中"`
DeviceTypes []string `json:"device_types" validate:"omitempty,max=100,dive,min=1,max=50" maxLength:"100" description:"设备类型范围;不传或空数组表示全量;资产该维度无值(独立卡或未绑定设备)时不命中已配置范围"`
CardTypes []string `json:"card_types" validate:"omitempty,max=4,dive,oneof=CMCC CUCC CTCC CBN" maxLength:"4" description:"卡类型范围 (CMCC:中国移动, CUCC:中国联通, CTCC:中国电信, CBN:中国广电);不传或空数组表示全量;非卡资产该维度无值时不命中已配置范围"`
Priority *int `json:"priority" validate:"omitempty,min=0,max=1000000" minimum:"0" maximum:"1000000" description:"优先级,数值越大越优先,默认 0"`
Frequency string `json:"frequency" validate:"required,oneof=once daily" required:"true" enum:"once,daily" description:"投放频率 (once:每客户每配置版本仅一次, daily:每客户每配置版本每个上海自然日一次)"`
Enabled *bool `json:"enabled" description:"是否启用;不传按停用创建"`
ActionType *string `json:"action_type" validate:"omitempty,oneof=package_purchase asset_wallet_recharge" enum:"package_purchase,asset_wallet_recharge" description:"受控动作 (package_purchase:套餐购买, asset_wallet_recharge:资产钱包充值);不传表示无受控动作"`
StartsAt time.Time `json:"starts_at" validate:"required" required:"true" description:"生效开始时间ISO 8601"`
EndsAt time.Time `json:"ends_at" validate:"required" required:"true" description:"生效结束时间ISO 8601不得早于开始时间"`
}
// UpdateH5PopupConfigurationRequest 是更新 H5 运营弹窗配置的请求。
// 除范围集合外均为指针:字段缺省表示保持原值;范围字段传空数组表示该维度改为全量。
// 更新成功即递增配置版本,旧版本已投放通知的内容与快照不被改写。
type UpdateH5PopupConfigurationRequest struct {
Title *string `json:"title" validate:"omitempty,min=1,max=100" minLength:"1" maxLength:"100" description:"弹窗标题1100 字符;不传保持原值"`
Content *string `json:"content" validate:"omitempty,min=1,max=2000" minLength:"1" maxLength:"2000" description:"弹窗正文12000 字符;不传保持原值,不接受 HTML、URL 或前端路由"`
PopupType *string `json:"popup_type" validate:"omitempty,oneof=promotion announcement" enum:"promotion,announcement" description:"弹窗类型 (promotion:套餐政策推广, announcement:通用公告);不传保持原值。类别优先级为风险换卡提醒 > 套餐政策推广 > 通用公告,类别优先于显式优先级"`
Pages *[]string `json:"pages" validate:"omitempty,min=1,max=4,dive,oneof=home asset_detail package_purchase asset_wallet_recharge" description:"命中页面集合;不传保持原值,传空数组等价于非法(页面必选)"`
ShopIDs *[]uint `json:"shop_ids" validate:"omitempty,max=200,dive,min=1" maxLength:"200" description:"店铺范围;不传保持原值,传空数组表示改为全量"`
DeviceTypes *[]string `json:"device_types" validate:"omitempty,max=100,dive,min=1,max=50" maxLength:"100" description:"设备类型范围;不传保持原值,传空数组表示改为全量"`
CardTypes *[]string `json:"card_types" validate:"omitempty,max=4,dive,oneof=CMCC CUCC CTCC CBN" maxLength:"4" description:"卡类型范围;不传保持原值,传空数组表示改为全量"`
Priority *int `json:"priority" validate:"omitempty,min=0,max=1000000" minimum:"0" maximum:"1000000" description:"优先级,数值越大越优先;不传保持原值"`
Frequency *string `json:"frequency" validate:"omitempty,oneof=once daily" enum:"once,daily" description:"投放频率 (once:每客户每配置版本仅一次, daily:每客户每配置版本每个上海自然日一次);不传保持原值"`
Enabled *bool `json:"enabled" description:"是否启用;不传保持原值。启停会刷新最近更新时间并影响同优先级排序"`
ActionType *string `json:"action_type" validate:"omitempty,oneof=package_purchase asset_wallet_recharge" enum:"package_purchase,asset_wallet_recharge" description:"受控动作;传空字符串表示清除受控动作,不传保持原值"`
StartsAt *time.Time `json:"starts_at" description:"生效开始时间ISO 8601不传保持原值"`
EndsAt *time.Time `json:"ends_at" description:"生效结束时间ISO 8601不传保持原值不得早于开始时间"`
}
// H5PopupConfigurationIDParams 是运营弹窗配置的路径参数。
type H5PopupConfigurationIDParams struct {
ID uint `json:"id" path:"id" required:"true" description:"运营弹窗配置ID"`
}
// UpdateH5PopupConfigurationParams 是更新运营弹窗配置的路径参数与请求体。
// 路径字段必须由 Handler 从 c.Params 回填后再校验,避免 required 恒失败或被请求体覆盖。
type UpdateH5PopupConfigurationParams struct {
ID uint `json:"id" path:"id" required:"true" description:"运营弹窗配置ID"`
UpdateH5PopupConfigurationRequest
}
// H5PopupConfigurationResponse 是运营弹窗配置投影。
type H5PopupConfigurationResponse struct {
ID uint `json:"id" description:"配置ID"`
Title string `json:"title" description:"弹窗标题"`
Content string `json:"content" description:"弹窗正文"`
PopupType string `json:"popup_type" enums:"promotion,announcement" description:"弹窗类型 (promotion:套餐政策推广, announcement:通用公告)"`
PopupTypeText string `json:"popup_type_text" description:"弹窗类型名称(中文)"`
Pages []string `json:"pages" enums:"home,asset_detail,package_purchase,asset_wallet_recharge" description:"命中页面集合 (home:首页, asset_detail:资产详情, package_purchase:套餐购买, asset_wallet_recharge:资产钱包充值)"`
ShopIDs []uint `json:"shop_ids" description:"店铺范围;空数组表示全量"`
DeviceTypes []string `json:"device_types" description:"设备类型范围;空数组表示全量"`
CardTypes []string `json:"card_types" enums:"CMCC,CUCC,CTCC,CBN" description:"卡类型范围 (CMCC:中国移动, CUCC:中国联通, CTCC:中国电信, CBN:中国广电);空数组表示全量"`
Priority int `json:"priority" description:"优先级,数值越大越优先"`
Frequency string `json:"frequency" enums:"once,daily" description:"投放频率 (once:每客户每配置版本仅一次, daily:每客户每配置版本每个上海自然日一次)"`
FrequencyText string `json:"frequency_text" description:"投放频率名称(中文)"`
Enabled bool `json:"enabled" description:"是否启用;停用后停止新投放,历史通知在展示期内仍可见"`
EnabledText string `json:"enabled_text" description:"启停状态名称(中文)"`
ActionType string `json:"action_type" enums:"package_purchase,asset_wallet_recharge" description:"受控动作 (package_purchase:套餐购买, asset_wallet_recharge:资产钱包充值);空值表示无受控动作"`
StartsAt time.Time `json:"starts_at" description:"生效开始时间ISO 8601"`
EndsAt time.Time `json:"ends_at" description:"生效结束时间ISO 8601"`
Version int64 `json:"version" description:"配置版本,每次更新递增;版本参与频率去重,旧版本通知保留原快照"`
CreatedAt time.Time `json:"created_at" description:"创建时间ISO 8601"`
UpdatedAt time.Time `json:"updated_at" description:"最近更新时间ISO 8601启停同样刷新该时间"`
Creator uint `json:"creator" description:"创建人账号ID"`
Updater uint `json:"updater" description:"最近更新人账号ID"`
}
// H5PopupConfigurationListRequest 是运营弹窗配置列表分页参数。
type H5PopupConfigurationListRequest struct {
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=100" minimum:"1" maximum:"100" description:"每页数量,默认 20最大 100"`
Enabled *bool `json:"enabled" query:"enabled" description:"启停筛选;不传时查询全部"`
}
// H5PopupConfigurationListResponse 是运营弹窗配置分页结果。
type H5PopupConfigurationListResponse struct {
Items []H5PopupConfigurationResponse `json:"items" description:"运营弹窗配置列表"`
Total int64 `json:"total" description:"总数量"`
Page int `json:"page" description:"页码"`
Size int `json:"size" description:"每页数量"`
}
// PopupCandidateRequest 是当前客户查询弹窗候选的请求。
// 首页也必须先由客户选定当前资产,因此 identifier 在所有页面都是必填。
type PopupCandidateRequest struct {
Page string `json:"page" query:"page" validate:"required,oneof=home asset_detail package_purchase asset_wallet_recharge" required:"true" enum:"home,asset_detail,package_purchase,asset_wallet_recharge" description:"当前页面 (home:首页, asset_detail:资产详情, package_purchase:套餐购买, asset_wallet_recharge:资产钱包充值)"`
Identifier string `json:"identifier" query:"identifier" validate:"required,min=1,max=100" required:"true" minLength:"1" maxLength:"100" description:"当前资产标识符,卡支持 ICCID、接入号、虚拟号设备支持虚拟号、IMEI、SN"`
}
// PopupCandidateResponse 是弹窗候选结果candidate 为空表示当前页面与资产没有可投放弹窗。
type PopupCandidateResponse struct {
Candidate *PopupCandidateItem `json:"candidate,omitempty" description:"弹窗候选;为空表示当前页面与资产没有可投放弹窗"`
}
// PopupCandidateItem 是当次投放或复用的弹窗候选。
// 只返回类型、资产关联与受控动作,不返回任何 URL 或前端路由;前端按 action_type 白名单映射页面。
type PopupCandidateItem struct {
NotificationID uint `json:"notification_id" description:"投放或复用的站内通知ID关闭或稍后处理时用它调用既有个人通知已读接口"`
PopupType string `json:"popup_type" enums:"risk_exchange,operation" description:"弹窗类型 (risk_exchange:风险换卡, operation:运营弹窗);风险换卡优先级固定高于运营弹窗"`
NotificationType string `json:"notification_type" enums:"h5.popup.risk_exchange,h5.popup.operation" description:"稳定通知类型 (h5.popup.risk_exchange:风险换卡弹窗, h5.popup.operation:运营弹窗)"`
Title string `json:"title" description:"弹窗标题,投放时冻结"`
Body string `json:"body" description:"弹窗正文,投放时冻结"`
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:资产钱包充值);为空表示无受控动作"`
ConfigID uint `json:"config_id" description:"运营弹窗配置ID风险换卡弹窗固定为 0"`
ConfigVersion int64 `json:"config_version" description:"投放时的配置版本;风险换卡弹窗固定为 0"`
ExpiresAt *time.Time `json:"expires_at,omitempty" description:"展示截止时间ISO 8601自投放时间起 90 天"`
CreatedAt time.Time `json:"created_at" description:"投放时间ISO 8601"`
}
// ClientRiskExchangeAddressParams 是风险换卡地址提交的路径参数与请求体。
// 地址字段沿用既有换货地址字段与长度校验,不额外拆分省市区。
type ClientRiskExchangeAddressParams struct {
AssetID uint `json:"asset_id" path:"asset_id" required:"true" description:"待换卡资产ID物联网卡数字ID"`
ClientShippingInfoRequest
}
// ClientRiskExchangeResponse 是风险换卡地址提交结果。
// 重复提交返回首次创建的换货单与首次地址,不覆盖既有地址。
type ClientRiskExchangeResponse struct {
ID uint `json:"id" description:"物流换货单ID"`
ExchangeNo string `json:"exchange_no" description:"换货单号"`
Status int `json:"status" description:"换货状态 (2:待发货)"`
StatusName string `json:"status_name" description:"换货状态名称(中文)"`
FlowType string `json:"flow_type" description:"换货流程类型 (shipping:物流换货)"`
OldAssetType string `json:"old_asset_type" description:"旧资产类型 (iot_card:物联网卡)"`
OldAssetID uint `json:"old_asset_id" description:"旧资产ID"`
OldAssetIdentifier string `json:"old_asset_identifier" description:"旧资产权威快照,卡为完整 ICCID"`
RecipientName string `json:"recipient_name" description:"收件人姓名,首次提交后锁定"`
RecipientPhone string `json:"recipient_phone" description:"收件人电话,首次提交后锁定"`
RecipientAddress string `json:"recipient_address" description:"收货地址,首次提交后锁定,客户不可修改"`
MigrateData bool `json:"migrate_data" description:"是否执行全量迁移;风险换卡固定 false发货选新资产时仍由后台按既有流程决定"`
MigrationStatus string `json:"migration_status" enums:"not_migrated,pending,migrated,failed" description:"业务数据迁移状态 (not_migrated:不迁移, pending:待迁移, migrated:已迁移, failed:迁移失败);迁移结果以本字段为准"`
MigrationStatusName string `json:"migration_status_name" description:"业务数据迁移状态名称(中文)"`
ExchangeReason string `json:"exchange_reason" description:"换货原因"`
CreatedAt time.Time `json:"created_at" description:"创建时间ISO 8601"`
}