docs: 轮询配置接口文档与实际行为对齐
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 9m57s

- 匹配预览 GET 只收 query iccid,POST 只收请求体,去掉 POST 文档中多余的 query 参数
- 预览草稿 overrides 改为独立结构,字段全部可选,最多 20 条
- clear_fields 用具名类型生成数组元素枚举;ordered_ids 说明须为正整数且不重复
This commit is contained in:
邹进
2026-10-24 17:41:52 +09:00
parent 8fc3485061
commit 2ddaffe373
5 changed files with 67 additions and 31 deletions

View File

@@ -217,8 +217,15 @@ func (h *PollingConfigHandler) MatchPreview(c *fiber.Ctx) error {
if err := c.BodyParser(&req); err != nil {
return errors.New(errors.CodeInvalidParam, "请求参数解析失败")
}
} else if err := c.QueryParser(&req); err != nil {
return errors.New(errors.CodeInvalidParam, "请求参数解析失败")
} else {
var query dto.PollingConfigMatchPreviewQuery
if err := c.QueryParser(&query); err != nil {
return errors.New(errors.CodeInvalidParam, "请求参数解析失败")
}
req.ICCID = query.ICCID
}
if len(req.Overrides) > 20 {
return errors.New(errors.CodeInvalidParam, "匹配预览参数不合法:未保存修改最多 20 条")
}
if req.ICCID == "" {
return errors.New(errors.CodeInvalidParam, "匹配预览参数不合法:ICCID 不能为空")

View File

@@ -21,22 +21,22 @@ type CreatePollingConfigRequest struct {
// UpdatePollingConfigRequest 更新轮询配置请求
type UpdatePollingConfigRequest struct {
ConfigName *string `json:"config_name" validate:"omitempty,min=1,max=100" minLength:"1" maxLength:"100" description:"配置名称"`
CardCondition *string `json:"card_condition" validate:"omitempty,oneof=not_real_name real_name activated suspended" description:"卡状态条件 (not_real_name:未实名, real_name:已实名, activated:已激活, suspended:已停用)"`
CardCategory *string `json:"card_category" validate:"omitempty,oneof=normal industry" description:"卡业务类型 (normal:普通卡, industry:行业卡)"`
CarrierID *uint `json:"carrier_id" validate:"omitempty" description:"运营商ID(可选,精确匹配)"`
Priority *int `json:"priority" validate:"omitempty,min=1,max=1000" minimum:"1" maximum:"1000" description:"优先级(数字越小优先级越高)"`
RealnameCheckInterval *int `json:"realname_check_interval" validate:"omitempty,min=0" minimum:"0" description:"实名检查间隔(秒):正数按间隔(最小30秒),0 不检查且不再继承,不传或为空继承优先级更低的配置"`
CarddataCheckInterval *int `json:"carddata_check_interval" validate:"omitempty,min=0" minimum:"0" description:"流量检查间隔(秒):正数按间隔(最小60秒),0 不检查且不再继承,不传或为空继承优先级更低的配置"`
PackageCheckInterval *int `json:"package_check_interval" validate:"omitempty,min=0" minimum:"0" description:"套餐检查间隔(秒):正数按间隔(最小60秒),0 不检查且不再继承,不传或为空继承优先级更低的配置"`
ProtectCheckInterval *int `json:"protect_check_interval" validate:"omitempty,min=30" minimum:"30" description:"保护期一致性检查间隔(秒),NULL表示不检查,最小30秒"`
CardStatusCheckInterval *int `json:"card_status_check_interval" validate:"omitempty,min=0" minimum:"0" description:"卡状态检查间隔(秒):正数按间隔(最小30秒),0 不检查且不再继承,不传或为空继承优先级更低的配置"`
PackageState *string `json:"package_state" validate:"omitempty,oneof=active depleted pending none" enum:"active,depleted,pending,none" description:"套餐状态匹配 (active:有生效套餐, depleted:套餐已用完, pending:只有待生效套餐, none:无套餐),为空不限"`
AssetStatus *int `json:"asset_status" validate:"omitempty,oneof=1 2 3 4" enum:"1,2,3,4" description:"资产状态匹配 (1:在库, 2:已销售, 3:已换货, 4:已停用),为空不限"`
PreResetWindow *int `json:"pre_reset_window" validate:"omitempty,min=60" minimum:"60" description:"重置日前加密窗口长度(秒),与窗口内流量间隔同时配置才生效"`
PreResetCarddataInterval *int `json:"pre_reset_carddata_interval" validate:"omitempty,min=60" minimum:"60" description:"重置日前窗口内的流量检查间隔(秒),与窗口长度同时配置才生效"`
ClearFields []string `json:"clear_fields" validate:"omitempty,dive,oneof=realname_check_interval carddata_check_interval package_check_interval card_status_check_interval carrier_id package_state asset_status pre_reset_window pre_reset_carddata_interval" description:"要清空为空值的字段(间隔清空即改为继承,匹配条件清空即不限):realname_check_interval/carddata_check_interval/package_check_interval/card_status_check_interval/carrier_id/package_state/asset_status/pre_reset_window/pre_reset_carddata_interval"`
Description *string `json:"description" validate:"omitempty,max=500" maxLength:"500" description:"配置说明"`
ConfigName *string `json:"config_name" validate:"omitempty,min=1,max=100" minLength:"1" maxLength:"100" description:"配置名称"`
CardCondition *string `json:"card_condition" validate:"omitempty,oneof=not_real_name real_name activated suspended" description:"卡状态条件 (not_real_name:未实名, real_name:已实名, activated:已激活, suspended:已停用)"`
CardCategory *string `json:"card_category" validate:"omitempty,oneof=normal industry" description:"卡业务类型 (normal:普通卡, industry:行业卡)"`
CarrierID *uint `json:"carrier_id" validate:"omitempty" description:"运营商ID(可选,精确匹配)"`
Priority *int `json:"priority" validate:"omitempty,min=1,max=1000" minimum:"1" maximum:"1000" description:"优先级(数字越小优先级越高)"`
RealnameCheckInterval *int `json:"realname_check_interval" validate:"omitempty,min=0" minimum:"0" description:"实名检查间隔(秒):正数按间隔(最小30秒),0 不检查且不再继承,不传或为空继承优先级更低的配置"`
CarddataCheckInterval *int `json:"carddata_check_interval" validate:"omitempty,min=0" minimum:"0" description:"流量检查间隔(秒):正数按间隔(最小60秒),0 不检查且不再继承,不传或为空继承优先级更低的配置"`
PackageCheckInterval *int `json:"package_check_interval" validate:"omitempty,min=0" minimum:"0" description:"套餐检查间隔(秒):正数按间隔(最小60秒),0 不检查且不再继承,不传或为空继承优先级更低的配置"`
ProtectCheckInterval *int `json:"protect_check_interval" validate:"omitempty,min=30" minimum:"30" description:"保护期一致性检查间隔(秒),NULL表示不检查,最小30秒"`
CardStatusCheckInterval *int `json:"card_status_check_interval" validate:"omitempty,min=0" minimum:"0" description:"卡状态检查间隔(秒):正数按间隔(最小30秒),0 不检查且不再继承,不传或为空继承优先级更低的配置"`
PackageState *string `json:"package_state" validate:"omitempty,oneof=active depleted pending none" enum:"active,depleted,pending,none" description:"套餐状态匹配 (active:有生效套餐, depleted:套餐已用完, pending:只有待生效套餐, none:无套餐),为空不限"`
AssetStatus *int `json:"asset_status" validate:"omitempty,oneof=1 2 3 4" enum:"1,2,3,4" description:"资产状态匹配 (1:在库, 2:已销售, 3:已换货, 4:已停用),为空不限"`
PreResetWindow *int `json:"pre_reset_window" validate:"omitempty,min=60" minimum:"60" description:"重置日前加密窗口长度(秒),与窗口内流量间隔同时配置才生效"`
PreResetCarddataInterval *int `json:"pre_reset_carddata_interval" validate:"omitempty,min=60" minimum:"60" description:"重置日前窗口内的流量检查间隔(秒),与窗口长度同时配置才生效"`
ClearFields []PollingConfigClearField `json:"clear_fields" validate:"omitempty,dive,oneof=realname_check_interval carddata_check_interval package_check_interval card_status_check_interval carrier_id package_state asset_status pre_reset_window pre_reset_carddata_interval" description:"要清空为空值的字段(间隔清空即改为继承,匹配条件清空即不限):realname_check_interval/carddata_check_interval/package_check_interval/card_status_check_interval/carrier_id/package_state/asset_status/pre_reset_window/pre_reset_carddata_interval"`
Description *string `json:"description" validate:"omitempty,max=500" maxLength:"500" description:"配置说明"`
}
// PollingConfigListRequest 轮询配置列表请求
@@ -101,20 +101,39 @@ type PollingConfigPageResult struct {
// UpdatePollingConfigPrioritiesRequest 整体调整轮询配置优先级请求
type UpdatePollingConfigPrioritiesRequest struct {
OrderedIDs []uint `json:"ordered_ids" validate:"required,min=1,max=1000" required:"true" minItems:"1" description:"按期望生效顺序排列的配置 ID 列表(排在前面的优先级更高);系统按顺序把优先级重排为 1、2、3…,未列出的配置保持原优先级"`
OrderedIDs []uint `json:"ordered_ids" validate:"required,min=1,max=1000,dive,min=1" required:"true" minItems:"1" maxItems:"1000" description:"按期望生效顺序排列的配置 ID 列表(排在前面的优先级更高);ID 须为正整数且不重复;系统按顺序把优先级重排为 1、2、3…,未列出的配置保持原优先级"`
}
// PollingConfigOverride 匹配预览中的未保存修改;ID 为 0 表示新增草稿,Disabled 为 true 表示假设该配置停用
// PollingConfigOverride 匹配预览中的未保存修改;ID 为 0 表示新增草稿,Disabled 为 true 表示假设该配置停用。
// 字段含义与创建轮询配置相同,均为可选;草稿名称为空时显示"未保存的配置"。
type PollingConfigOverride struct {
ID uint `json:"id" description:"要覆盖的配置 ID,0 表示新增一条草稿配置"`
Disabled bool `json:"disabled" description:"假设该配置停用(不参与匹配)"`
CreatePollingConfigRequest
ID uint `json:"id" description:"要覆盖的配置 ID,0 表示新增一条草稿配置"`
Disabled bool `json:"disabled" description:"假设该配置停用(不参与匹配),为 true 时其余字段忽略"`
ConfigName string `json:"config_name" maxLength:"100" description:"草稿配置名称"`
CardCondition string `json:"card_condition" enum:"not_real_name,activated,suspended" description:"卡状态条件 (not_real_name:未实名, activated:已激活, suspended:已停用),为空不限"`
CardCategory string `json:"card_category" enum:"normal,industry" description:"卡业务类型 (normal:普通卡, industry:行业卡),为空不限"`
CarrierID *uint `json:"carrier_id" description:"运营商账户ID,为空不限"`
Priority int `json:"priority" minimum:"0" description:"优先级(数字越小越优先;与已有配置相同时排在已有配置之后)"`
RealnameCheckInterval *int `json:"realname_check_interval" minimum:"0" description:"实名检查间隔(秒),正数按间隔、0 不检查、空继承"`
CarddataCheckInterval *int `json:"carddata_check_interval" minimum:"0" description:"流量检查间隔(秒),正数按间隔、0 不检查、空继承"`
PackageCheckInterval *int `json:"package_check_interval" minimum:"0" description:"套餐检查间隔(秒),正数按间隔、0 不检查、空继承"`
ProtectCheckInterval *int `json:"protect_check_interval" minimum:"0" description:"保护期检查间隔(秒),正数按间隔、0 不检查、空继承"`
CardStatusCheckInterval *int `json:"card_status_check_interval" minimum:"0" description:"卡状态检查间隔(秒),正数按间隔、0 不检查、空继承"`
PackageState *string `json:"package_state" enum:"active,depleted,pending,none" description:"套餐状态匹配 (active:有生效套餐, depleted:套餐已用完, pending:只有待生效套餐, none:无套餐),为空不限"`
AssetStatus *int `json:"asset_status" enum:"1,2,3,4" description:"资产状态匹配 (1:在库, 2:已销售, 3:已换货, 4:已停用),为空不限"`
PreResetWindow *int `json:"pre_reset_window" minimum:"60" description:"重置日前加密窗口长度(秒),与窗口内流量间隔同时配置才生效"`
PreResetCarddataInterval *int `json:"pre_reset_carddata_interval" minimum:"60" description:"重置日前窗口内的流量检查间隔(秒),与窗口长度同时配置才生效"`
}
// PollingConfigMatchPreviewRequest 匹配预览请求
// PollingConfigMatchPreviewQuery 按当前配置预览的查询参数
type PollingConfigMatchPreviewQuery struct {
ICCID string `json:"-" query:"iccid" validate:"required,max=30" required:"true" maxLength:"30" description:"要预览的卡 ICCID"`
}
// PollingConfigMatchPreviewRequest 带未保存修改的匹配预览请求(请求体)
type PollingConfigMatchPreviewRequest struct {
ICCID string `json:"iccid" query:"iccid" validate:"required,max=30" required:"true" maxLength:"30" description:"要预览的卡 ICCID"`
Overrides []PollingConfigOverride `json:"overrides" description:"未保存的修改(仅 POST 支持),不写任何数据"`
ICCID string `json:"iccid" validate:"required,max=30" required:"true" maxLength:"30" description:"要预览的卡 ICCID"`
Overrides []PollingConfigOverride `json:"overrides" maxItems:"20" description:"未保存的修改,不写任何数据"`
}
// PollingCardMatchFacts 卡的匹配维度取值
@@ -218,3 +237,12 @@ type PollingPreResetCarrierStat struct {
type PollingPreResetStatsResponse struct {
Carriers []PollingPreResetCarrierStat `json:"carriers" description:"有开启轮询卡的运营商账户"`
}
// PollingConfigClearField 是更新轮询配置时可清空为空值的字段名。
type PollingConfigClearField string
// Enum 返回可清空字段的全部取值,供接口文档生成数组元素枚举。
func (PollingConfigClearField) Enum() []interface{} {
return []interface{}{"realname_check_interval", "carddata_check_interval", "package_check_interval", "card_status_check_interval",
"carrier_id", "package_state", "asset_status", "pre_reset_window", "pre_reset_carddata_interval"}
}

View File

@@ -48,7 +48,7 @@ func registerPollingConfigRoutes(router fiber.Router, handler *admin.PollingConf
Register(configs, doc, groupPath, "GET", "/match-preview", handler.MatchPreview, RouteSpec{
Summary: "预览卡的轮询配置匹配结果(按当前配置)",
Tags: []string{"轮询配置管理"},
Input: new(dto.PollingConfigMatchPreviewRequest),
Input: new(dto.PollingConfigMatchPreviewQuery),
Output: new(dto.PollingConfigMatchPreviewResponse),
Auth: true,
})

View File

@@ -3,6 +3,7 @@ package polling
import (
"context"
"fmt"
"github.com/break/junhong_cmp_fiber/internal/model/dto"
"github.com/break/junhong_cmp_fiber/internal/model"
"github.com/break/junhong_cmp_fiber/pkg/constants"
@@ -74,9 +75,9 @@ func validPackageState(state string) bool {
}
// applyPollingConfigClearFields 把指定字段清空为 NULL:间隔清空即改为继承,匹配条件清空即不限。
func applyPollingConfigClearFields(cfg *model.PollingConfig, fields []string) error {
func applyPollingConfigClearFields(cfg *model.PollingConfig, fields []dto.PollingConfigClearField) error {
for _, field := range fields {
switch field {
switch string(field) {
case "realname_check_interval":
cfg.RealnameCheckInterval = nil
case "carddata_check_interval":
@@ -96,7 +97,7 @@ func applyPollingConfigClearFields(cfg *model.PollingConfig, fields []string) er
case "pre_reset_carddata_interval":
cfg.PreResetCarddataInterval = nil
default:
return errors.New(errors.CodeInvalidParam, "轮询配置参数不合法:不支持清空字段 "+field)
return errors.New(errors.CodeInvalidParam, "轮询配置参数不合法:不支持清空字段 "+string(field))
}
}
return nil