feat: 停复机规则演练与终态自动关闭轮询
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 9m26s
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 9m26s
统一停复机规则转正的第一次发布:总开关仍关闭,现有停复机行为不变。 - 新增停复机规则演练:GET /stop-resume-rule/dry-run 按新规则对开机与停机卡只判断不执行,返回运营商账户 × 停机原因 × 判断结果汇总与分页明细,可假设通道实名管控配置;POST /stop-resume-rule/dry-run/export 导出明细(场景 stop_resume_rule_dry_run);仅超级管理员与平台账号 - 风险停机、已销户关闭轮询不再限于独立卡,只关该卡自身轮询,不级联设备 - 错误分类规则增加 auto_disable_polling,仅终态类别可勾选;停复机(含设备停复机)命中该规则时不重试、关闭该卡轮询、回写网关扩展状态并写审计 iot_card.terminal_disable_polling - 迁移 000250 加列与约束,并为"已销户""仅支持风险复机"置位
This commit is contained in:
@@ -4,7 +4,7 @@ import "time"
|
||||
|
||||
// CreateExportTaskRequest 创建导出任务请求。
|
||||
type CreateExportTaskRequest struct {
|
||||
Scene string `json:"scene" validate:"required,oneof=device iot_card order package agent_wallet_transaction agent_recharge refund exchange commission_record package_traffic_alert expiring_asset operations_activation operations_renewal" required:"true" description:"导出场景 (device:设备, iot_card:IoT卡, order:订单, package:套餐, agent_wallet_transaction:代理主钱包流水, agent_recharge:代理充值, refund:退款, exchange:换货, commission_record:佣金明细, package_traffic_alert:套餐真流量达量预警, expiring_asset:临期资产, operations_activation:设备激活情况报表, operations_renewal:套餐续费情况报表)"`
|
||||
Scene string `json:"scene" validate:"required,oneof=device iot_card order package agent_wallet_transaction agent_recharge refund exchange commission_record package_traffic_alert expiring_asset operations_activation operations_renewal stop_resume_rule_dry_run" required:"true" description:"导出场景 (device:设备, iot_card:IoT卡, order:订单, package:套餐, agent_wallet_transaction:代理主钱包流水, agent_recharge:代理充值, refund:退款, exchange:换货, commission_record:佣金明细, package_traffic_alert:套餐真流量达量预警, expiring_asset:临期资产, operations_activation:设备激活情况报表, operations_renewal:套餐续费情况报表, stop_resume_rule_dry_run:停复机规则演练明细)"`
|
||||
Format string `json:"format" validate:"required,oneof=xlsx csv" required:"true" description:"导出格式 (xlsx:Excel, csv:CSV)"`
|
||||
Query map[string]interface{} `json:"query,omitempty" description:"导出筛选参数(JSON对象,可选);时间筛选固定使用 filters.start_time 与 filters.end_time,取值必须为带显式时区的 RFC3339 秒级时间"`
|
||||
}
|
||||
@@ -22,7 +22,7 @@ type CreateExportTaskResponse struct {
|
||||
type ListExportTaskRequest struct {
|
||||
Page int `json:"page" query:"page" validate:"omitempty,min=1" minimum:"1" description:"页码"`
|
||||
PageSize int `json:"page_size" query:"page_size" validate:"omitempty,min=1,max=100" minimum:"1" maximum:"100" description:"每页数量"`
|
||||
Scene string `json:"scene" query:"scene" validate:"omitempty,oneof=device iot_card order package agent_wallet_transaction agent_recharge refund exchange commission_record package_traffic_alert expiring_asset operations_activation operations_renewal" description:"导出场景 (device:设备, iot_card:IoT卡, order:订单, package:套餐, agent_wallet_transaction:代理主钱包流水, agent_recharge:代理充值, refund:退款, exchange:换货, commission_record:佣金明细, package_traffic_alert:套餐真流量达量预警, expiring_asset:临期资产, operations_activation:设备激活情况报表, operations_renewal:套餐续费情况报表)"`
|
||||
Scene string `json:"scene" query:"scene" validate:"omitempty,oneof=device iot_card order package agent_wallet_transaction agent_recharge refund exchange commission_record package_traffic_alert expiring_asset operations_activation operations_renewal stop_resume_rule_dry_run" description:"导出场景 (device:设备, iot_card:IoT卡, order:订单, package:套餐, agent_wallet_transaction:代理主钱包流水, agent_recharge:代理充值, refund:退款, exchange:换货, commission_record:佣金明细, package_traffic_alert:套餐真流量达量预警, expiring_asset:临期资产, operations_activation:设备激活情况报表, operations_renewal:套餐续费情况报表, stop_resume_rule_dry_run:停复机规则演练明细)"`
|
||||
Status *int `json:"status" query:"status" validate:"omitempty,min=1,max=5" minimum:"1" maximum:"5" description:"任务状态 (1:待处理, 2:处理中, 3:已完成, 4:已失败, 5:已取消)"`
|
||||
StartTime string `json:"start_time" query:"start_time" description:"创建时间起始(带时区的 RFC3339 秒级时间,闭区间含该时刻,如 2026-09-01T00:00:00+08:00)"`
|
||||
EndTime string `json:"end_time" query:"end_time" description:"创建时间结束(带时区的 RFC3339 秒级时间,闭区间含该时刻,如 2026-09-30T23:59:59+08:00)"`
|
||||
@@ -33,7 +33,7 @@ type ExportTaskItem struct {
|
||||
ID uint `json:"id" description:"任务ID"`
|
||||
TaskID uint `json:"task_id" description:"任务ID"`
|
||||
TaskNo string `json:"task_no" description:"任务编号"`
|
||||
Scene string `json:"scene" description:"导出场景 (device:设备, iot_card:IoT卡, order:订单, package:套餐, agent_wallet_transaction:代理主钱包流水, agent_recharge:代理充值, refund:退款, exchange:换货, commission_record:佣金明细, package_traffic_alert:套餐真流量达量预警, operations_activation:设备激活情况报表, operations_renewal:套餐续费情况报表)"`
|
||||
Scene string `json:"scene" description:"导出场景 (device:设备, iot_card:IoT卡, order:订单, package:套餐, agent_wallet_transaction:代理主钱包流水, agent_recharge:代理充值, refund:退款, exchange:换货, commission_record:佣金明细, package_traffic_alert:套餐真流量达量预警, operations_activation:设备激活情况报表, operations_renewal:套餐续费情况报表, stop_resume_rule_dry_run:停复机规则演练明细)"`
|
||||
Format string `json:"format" description:"导出格式 (xlsx:Excel, csv:CSV)"`
|
||||
Status int `json:"status" description:"任务状态 (1:待处理, 2:处理中, 3:已完成, 4:已失败, 5:已取消)"`
|
||||
StatusName string `json:"status_name" description:"任务状态名称(中文)"`
|
||||
|
||||
@@ -74,15 +74,16 @@ type GatewayErrorRuleListRequest struct {
|
||||
|
||||
// SaveGatewayErrorRuleRequest 新建或修改上游错误分类规则请求
|
||||
type SaveGatewayErrorRuleRequest struct {
|
||||
ID uint `json:"-" description:"规则ID"`
|
||||
Keyword string `json:"keyword" validate:"required,max=200" required:"true" maxLength:"200" description:"匹配关键字或正则"`
|
||||
MatchType string `json:"match_type" validate:"required,oneof=contains prefix regex" required:"true" enum:"contains,prefix,regex" description:"匹配方式 (contains:包含, prefix:前缀, regex:正则)"`
|
||||
Category string `json:"category" validate:"required,oneof=rate_limited_upstream terminal already_target transient business account_config" required:"true" enum:"rate_limited_upstream,terminal,already_target,transient,business,account_config" description:"类别 (rate_limited_upstream:上游频率限制, terminal:终态不可操作, already_target:已是目标状态, transient:临时故障, business:业务错误, account_config:账户配置错误)"`
|
||||
Operations []GatewayOperation `json:"operations" validate:"omitempty,dive,oneof=query_flow query_status query_realname stop start" description:"适用操作,为空表示全部"`
|
||||
Priority int `json:"priority" validate:"min=1,max=10000" minimum:"1" maximum:"10000" description:"匹配优先级,越小越先匹配"`
|
||||
MaybeAccepted bool `json:"maybe_accepted" description:"上游可能已受理(如返回含调用成功、操作进行中、请求被合并)"`
|
||||
Status *int `json:"status" validate:"omitempty,oneof=0 1" enum:"0,1" description:"状态 (0:停用, 1:启用),新建默认启用"`
|
||||
Remark string `json:"remark" validate:"omitempty,max=500" maxLength:"500" description:"备注"`
|
||||
ID uint `json:"-" description:"规则ID"`
|
||||
Keyword string `json:"keyword" validate:"required,max=200" required:"true" maxLength:"200" description:"匹配关键字或正则"`
|
||||
MatchType string `json:"match_type" validate:"required,oneof=contains prefix regex" required:"true" enum:"contains,prefix,regex" description:"匹配方式 (contains:包含, prefix:前缀, regex:正则)"`
|
||||
Category string `json:"category" validate:"required,oneof=rate_limited_upstream terminal already_target transient business account_config" required:"true" enum:"rate_limited_upstream,terminal,already_target,transient,business,account_config" description:"类别 (rate_limited_upstream:上游频率限制, terminal:终态不可操作, already_target:已是目标状态, transient:临时故障, business:业务错误, account_config:账户配置错误)"`
|
||||
Operations []GatewayOperation `json:"operations" validate:"omitempty,dive,oneof=query_flow query_status query_realname stop start" description:"适用操作,为空表示全部"`
|
||||
Priority int `json:"priority" validate:"min=1,max=10000" minimum:"1" maximum:"10000" description:"匹配优先级,越小越先匹配"`
|
||||
MaybeAccepted bool `json:"maybe_accepted" description:"上游可能已受理(如返回含调用成功、操作进行中、请求被合并)"`
|
||||
AutoDisablePolling *bool `json:"auto_disable_polling" description:"命中后自动关闭该卡轮询(停复机不重试、回写网关扩展状态),仅类别为 terminal 时可勾选;更新时不传保留原值"`
|
||||
Status *int `json:"status" validate:"omitempty,oneof=0 1" enum:"0,1" description:"状态 (0:停用, 1:启用),新建默认启用"`
|
||||
Remark string `json:"remark" validate:"omitempty,max=500" maxLength:"500" description:"备注"`
|
||||
}
|
||||
|
||||
// UpdateGatewayErrorRuleStatusRequest 启停上游错误分类规则请求
|
||||
@@ -93,18 +94,19 @@ type UpdateGatewayErrorRuleStatusRequest struct {
|
||||
|
||||
// GatewayErrorRuleResponse 上游错误分类规则响应
|
||||
type GatewayErrorRuleResponse struct {
|
||||
ID uint `json:"id" description:"规则ID"`
|
||||
Keyword string `json:"keyword" description:"匹配关键字或正则"`
|
||||
MatchType string `json:"match_type" description:"匹配方式 (contains:包含, prefix:前缀, regex:正则)"`
|
||||
Category string `json:"category" description:"类别"`
|
||||
CategoryName string `json:"category_name" description:"类别名称"`
|
||||
Operations []string `json:"operations" description:"适用操作,为空表示全部"`
|
||||
Priority int `json:"priority" description:"匹配优先级"`
|
||||
MaybeAccepted bool `json:"maybe_accepted" description:"上游可能已受理"`
|
||||
Status int `json:"status" description:"状态 (0:停用, 1:启用)"`
|
||||
Remark string `json:"remark" description:"备注"`
|
||||
CreatedAt time.Time `json:"created_at" description:"创建时间"`
|
||||
UpdatedAt time.Time `json:"updated_at" description:"更新时间"`
|
||||
ID uint `json:"id" description:"规则ID"`
|
||||
Keyword string `json:"keyword" description:"匹配关键字或正则"`
|
||||
MatchType string `json:"match_type" description:"匹配方式 (contains:包含, prefix:前缀, regex:正则)"`
|
||||
Category string `json:"category" description:"类别"`
|
||||
CategoryName string `json:"category_name" description:"类别名称"`
|
||||
Operations []string `json:"operations" description:"适用操作,为空表示全部"`
|
||||
Priority int `json:"priority" description:"匹配优先级"`
|
||||
MaybeAccepted bool `json:"maybe_accepted" description:"上游可能已受理"`
|
||||
AutoDisablePolling bool `json:"auto_disable_polling" description:"命中后自动关闭该卡轮询"`
|
||||
Status int `json:"status" description:"状态 (0:停用, 1:启用)"`
|
||||
Remark string `json:"remark" description:"备注"`
|
||||
CreatedAt time.Time `json:"created_at" description:"创建时间"`
|
||||
UpdatedAt time.Time `json:"updated_at" description:"更新时间"`
|
||||
}
|
||||
|
||||
// GatewayErrorRulePageResult 上游错误分类规则分页结果
|
||||
@@ -124,12 +126,13 @@ type TestGatewayErrorRuleRequest struct {
|
||||
|
||||
// TestGatewayErrorRuleResponse 测试上游错误分类响应
|
||||
type TestGatewayErrorRuleResponse struct {
|
||||
Category string `json:"category" description:"命中类别"`
|
||||
CategoryName string `json:"category_name" description:"命中类别名称"`
|
||||
RuleID uint `json:"rule_id" description:"命中规则ID,未命中或命中草稿为 0"`
|
||||
Keyword string `json:"keyword" description:"命中关键字,未命中为空"`
|
||||
MaybeAccepted bool `json:"maybe_accepted" description:"上游可能已受理"`
|
||||
Matched bool `json:"matched" description:"是否命中规则;未命中时按业务错误处理"`
|
||||
Category string `json:"category" description:"命中类别"`
|
||||
CategoryName string `json:"category_name" description:"命中类别名称"`
|
||||
RuleID uint `json:"rule_id" description:"命中规则ID,未命中或命中草稿为 0"`
|
||||
Keyword string `json:"keyword" description:"命中关键字,未命中为空"`
|
||||
MaybeAccepted bool `json:"maybe_accepted" description:"上游可能已受理"`
|
||||
AutoDisablePolling bool `json:"auto_disable_polling" description:"命中后自动关闭该卡轮询"`
|
||||
Matched bool `json:"matched" description:"是否命中规则;未命中时按业务错误处理"`
|
||||
}
|
||||
|
||||
// StopResumeRecordListRequest 停复机记录列表请求
|
||||
|
||||
70
internal/model/dto/stop_resume_rule_dto.go
Normal file
70
internal/model/dto/stop_resume_rule_dto.go
Normal file
@@ -0,0 +1,70 @@
|
||||
package dto
|
||||
|
||||
// StopResumeRuleDryRunRequest 停复机规则演练请求。
|
||||
type StopResumeRuleDryRunRequest struct {
|
||||
CarrierID *uint `json:"carrier_id" query:"carrier_id" validate:"omitempty,min=1" minimum:"1" description:"运营商账户ID,不传为全部"`
|
||||
StopReason string `json:"stop_reason" query:"stop_reason" validate:"omitempty,max=50" maxLength:"50" description:"停机原因筛选:传停机原因编码(如 not_realname、carrier_stopped、manual),传 none 表示停机原因为空,不传为全部"`
|
||||
AssumeControlOn string `json:"assume_control_on" query:"assume_control_on" validate:"omitempty,max=2000" maxLength:"2000" description:"假设开启实名管控的运营商账户ID,逗号分隔;只用于预演,不改实际配置"`
|
||||
AssumeControlOff string `json:"assume_control_off" query:"assume_control_off" validate:"omitempty,max=2000" maxLength:"2000" description:"假设关闭实名管控的运营商账户ID,逗号分隔;只用于预演,不改实际配置"`
|
||||
Result string `json:"result" query:"result" validate:"omitempty,oneof=will_resume will_stop no_action" enum:"will_resume,will_stop,no_action" description:"明细按判断结果筛选 (will_resume:将复机, will_stop:将停机, no_action:不动作),汇总不受影响"`
|
||||
Page int `json:"page" query:"page" validate:"omitempty,min=1" minimum:"1" description:"明细页码,默认1"`
|
||||
PageSize int `json:"page_size" query:"page_size" validate:"omitempty,min=1,max=500" minimum:"1" maximum:"500" description:"明细每页数量,默认20,最大500"`
|
||||
}
|
||||
|
||||
// StopResumeRuleDryRunExportRequest 停复机规则演练明细导出请求。
|
||||
type StopResumeRuleDryRunExportRequest struct {
|
||||
CarrierID *uint `json:"carrier_id" validate:"omitempty,min=1" minimum:"1" description:"运营商账户ID,不传为全部"`
|
||||
StopReason string `json:"stop_reason" validate:"omitempty,max=50" maxLength:"50" description:"停机原因筛选,none 表示停机原因为空"`
|
||||
AssumeControlOn string `json:"assume_control_on" validate:"omitempty,max=2000" maxLength:"2000" description:"假设开启实名管控的运营商账户ID,逗号分隔"`
|
||||
AssumeControlOff string `json:"assume_control_off" validate:"omitempty,max=2000" maxLength:"2000" description:"假设关闭实名管控的运营商账户ID,逗号分隔"`
|
||||
Format string `json:"format" validate:"omitempty,oneof=xlsx csv" enum:"xlsx,csv" description:"导出格式 (xlsx:Excel, csv:CSV),默认 xlsx"`
|
||||
}
|
||||
|
||||
// StopResumeRuleDryRunSummaryItem 演练汇总项:运营商账户 × 当前停机原因 × 判断结果 × 原因。
|
||||
type StopResumeRuleDryRunSummaryItem struct {
|
||||
CarrierID uint `json:"carrier_id" description:"运营商账户ID"`
|
||||
CarrierName string `json:"carrier_name" description:"运营商账户名称"`
|
||||
StopReason string `json:"stop_reason" description:"当前停机原因,空表示无"`
|
||||
StopReasonName string `json:"stop_reason_name" description:"当前停机原因名称"`
|
||||
Result string `json:"result" description:"判断结果 (will_resume:将复机, will_stop:将停机, no_action:不动作)"`
|
||||
ResultName string `json:"result_name" description:"判断结果名称"`
|
||||
Reason string `json:"reason" description:"将停机时为目标停机原因,不动作时为不动作原因 (polling_suspended:暂停轮询, conditions_met:满足开机条件, no_package:无套餐, traffic_exhausted:流量已用完, not_realname:未实名, threshold_locked:持通道阈值锁, carrier_missing:所属通道不存在)"`
|
||||
ReasonName string `json:"reason_name" description:"原因名称"`
|
||||
Count int64 `json:"count" description:"卡数"`
|
||||
}
|
||||
|
||||
// StopResumeRuleDryRunTotals 演练总计。
|
||||
type StopResumeRuleDryRunTotals struct {
|
||||
Total int64 `json:"total" description:"参与演练的开机与停机卡数"`
|
||||
WillResume int64 `json:"will_resume" description:"将复机卡数"`
|
||||
WillStop int64 `json:"will_stop" description:"将停机卡数"`
|
||||
NoAction int64 `json:"no_action" description:"不动作卡数"`
|
||||
}
|
||||
|
||||
// StopResumeRuleDryRunItem 演练明细。
|
||||
type StopResumeRuleDryRunItem struct {
|
||||
CardID uint `json:"card_id" description:"卡ID"`
|
||||
ICCID string `json:"iccid" description:"ICCID"`
|
||||
CarrierID uint `json:"carrier_id" description:"运营商账户ID"`
|
||||
CarrierName string `json:"carrier_name" description:"运营商账户名称"`
|
||||
DeviceID uint `json:"device_id" description:"有效绑定设备ID,独立卡为 0"`
|
||||
NetworkStatus int `json:"network_status" description:"当前网络状态 (0:停机, 1:开机)"`
|
||||
StopReason string `json:"stop_reason" description:"当前停机原因"`
|
||||
StopReasonName string `json:"stop_reason_name" description:"当前停机原因名称"`
|
||||
CardCategory string `json:"card_category" description:"卡类别"`
|
||||
RealnameOK bool `json:"realname_ok" description:"按(假设的)通道实名管控是否满足实名条件"`
|
||||
Result string `json:"result" description:"判断结果"`
|
||||
ResultName string `json:"result_name" description:"判断结果名称"`
|
||||
Reason string `json:"reason" description:"将停机时为目标停机原因,不动作时为不动作原因"`
|
||||
ReasonName string `json:"reason_name" description:"原因名称"`
|
||||
}
|
||||
|
||||
// StopResumeRuleDryRunResponse 停复机规则演练响应。
|
||||
type StopResumeRuleDryRunResponse struct {
|
||||
Totals StopResumeRuleDryRunTotals `json:"totals" description:"总计"`
|
||||
Summary []StopResumeRuleDryRunSummaryItem `json:"summary" description:"按运营商账户 × 当前停机原因 × 判断结果 × 原因汇总"`
|
||||
Items []StopResumeRuleDryRunItem `json:"items" description:"明细(按卡ID升序分页)"`
|
||||
Total int64 `json:"total" description:"明细总数(按 result 筛选后)"`
|
||||
Page int `json:"page" description:"当前页"`
|
||||
PageSize int `json:"size" description:"每页数量"`
|
||||
}
|
||||
@@ -37,8 +37,10 @@ type GatewayErrorClassification struct {
|
||||
Operations datatypes.JSON `gorm:"column:operations;type:jsonb;not null;comment:适用操作,空数组表示全部" json:"operations"`
|
||||
Priority int `gorm:"column:priority;not null;default:100;comment:匹配优先级" json:"priority"`
|
||||
MaybeAccepted bool `gorm:"column:maybe_accepted;not null;default:false;comment:上游可能已受理" json:"maybe_accepted"`
|
||||
Status int `gorm:"column:status;type:smallint;not null;default:1;comment:状态 0-停用 1-启用" json:"status"`
|
||||
Remark string `gorm:"column:remark;type:varchar(500);not null;default:'';comment:备注" json:"remark"`
|
||||
// AutoDisablePolling 命中后自动关闭该卡轮询,仅终态不可操作类别可置位。
|
||||
AutoDisablePolling bool `gorm:"column:auto_disable_polling;not null;default:false;comment:命中后自动关闭该卡轮询" json:"auto_disable_polling"`
|
||||
Status int `gorm:"column:status;type:smallint;not null;default:1;comment:状态 0-停用 1-启用" json:"status"`
|
||||
Remark string `gorm:"column:remark;type:varchar(500);not null;default:'';comment:备注" json:"remark"`
|
||||
}
|
||||
|
||||
// TableName 返回错误分类规则表名。
|
||||
|
||||
Reference in New Issue
Block a user