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:"弹窗标题,1~100 字符;投放时冻结写入通知标题"` Content string `json:"content" validate:"required,min=1,max=2000" required:"true" minLength:"1" maxLength:"2000" description:"弹窗正文,1~2000 字符;投放时冻结写入通知正文,不接受 HTML、URL 或前端路由"` 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:"弹窗标题,1~100 字符;不传保持原值"` Content *string `json:"content" validate:"omitempty,min=1,max=2000" minLength:"1" maxLength:"2000" description:"弹窗正文,1~2000 字符;不传保持原值,不接受 HTML、URL 或前端路由"` 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:"弹窗正文"` 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)"` }