Files

434 lines
13 KiB
Go

package warningconfig
import (
"context"
"strings"
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/consts"
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/batchimport"
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/model"
"github.com/gogf/gf/v2/encoding/gjson"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
)
const (
warnItemTypeCard = 1
warnItemTypeFlowPool = 2
warnUnitMb = 1
warnUnitPercent = 2
warnUnitDay = 3
warnUnitMonth = 4
warnTypeCardTraffic = 1
warnTypeExpireDay = 2
warnTypeExpireMonth = 3
warnTypeFlowPoolPercent = 4
warnAutoActionDataCut = 3
warnMessageDefaultType = warnMethodPlatform
)
type Processor struct {
config batchimport.ProcessorConfig
itemType int
}
func NewProcessor(itemTypeInput ...int) *Processor {
itemType := warnItemTypeCard
if len(itemTypeInput) > 0 {
itemType = normalizeItemType(itemTypeInput[0])
}
return &Processor{
itemType: itemType,
config: batchimport.ProcessorConfig{
Code: "sim_warning_config",
BatchType: consts.BatchTypeSimWarningConfig,
BatchNoPrefix: consts.BatchNoPrefixSimWarningConfig,
TaskName: consts.ResourceBatchDownloadTaskSimWarningConfig,
ExportScene: consts.ResourceExportSceneSimWarningConfig,
ImportDir: consts.SimWarningConfigSubDirImportExcel,
ResultDir: consts.SimWarningConfigSubDirResultExcel,
MaxFileSize: consts.SimWarningConfigMaxFileSize,
TemplateHeaders: templateHeaders(itemType),
RequiredHeaders: requiredHeaders(itemType),
},
}
}
func (p *Processor) Config() batchimport.ProcessorConfig {
return p.config
}
func (p *Processor) ParseRows(ctx context.Context, filePath string) ([]batchimport.Row, error) {
return ParseExcelFileRows(ctx, filePath, p.itemType)
}
func (p *Processor) ValidateRows(ctx context.Context, rows []batchimport.Row) error {
if p.itemType == warnItemTypeFlowPool {
return p.validateFlowPoolRows(rows)
}
return p.validateCardRows(rows)
}
func (p *Processor) validateCardRows(rows []batchimport.Row) error {
firstByIccid := make(map[string]*Row, len(rows))
for _, item := range rows {
if item == nil {
continue
}
row, ok := item.(*Row)
if !ok || row == nil {
item.AddErrorReason("预警配置行类型错误")
continue
}
row.ItemType = warnItemTypeCard
row.Iccid = strings.TrimSpace(row.Iccid)
row.Msisdn = strings.TrimSpace(row.Msisdn)
row.Imsi = strings.TrimSpace(row.Imsi)
if row.Iccid == "" {
row.AddErrorReason("ICCID不能为空")
continue
}
if first, exists := firstByIccid[row.Iccid]; exists {
row.AddErrorReason("ICCID重复")
first.AddErrorReason("ICCID重复")
continue
}
firstByIccid[row.Iccid] = row
if row.ConfigSet && row.Reason == "" {
if err := validateOptions(&row.Config); err != nil {
row.AddErrorReason(err.Error())
}
}
}
return nil
}
func (p *Processor) validateFlowPoolRows(rows []batchimport.Row) error {
firstByCode := make(map[string]*Row, len(rows))
for _, item := range rows {
if item == nil {
continue
}
row, ok := item.(*Row)
if !ok || row == nil {
item.AddErrorReason("预警配置行类型错误")
continue
}
row.ItemType = warnItemTypeFlowPool
row.FlowPoolCode = strings.TrimSpace(row.FlowPoolCode)
if row.FlowPoolCode == "" {
row.AddErrorReason("流量池编号不能为空")
continue
}
if first, exists := firstByCode[row.FlowPoolCode]; exists {
row.AddErrorReason("流量池编号重复")
first.AddErrorReason("流量池编号重复")
continue
}
firstByCode[row.FlowPoolCode] = row
if row.ConfigSet && row.Reason == "" {
if err := validateOptions(&row.Config); err != nil {
row.AddErrorReason(err.Error())
}
}
}
return nil
}
func (p *Processor) BuildBatchInfo(ctx context.Context, req batchimport.SubmitRequest, result *batchimport.Result, importOss *model.SysOssSaveContentModel, resultOss *model.SysOssSaveContentModel, downloadId int64) *gjson.Json {
options, _ := batchInfoOptions(req.BizParams)
scene := submitScene(req.BizParams)
info := g.Map{
"operation": "sim_warning_config",
"configName": warningConfigName(options.ItemType, options.WarnUnit),
"submitScene": scene,
"itemType": options.ItemType,
"warnUnit": options.WarnUnit,
"firstWarn": options.FirstWarn,
"secondWarn": options.SecondWarn,
"thirdWarn": options.ThirdWarn,
"warnMethods": options.WarnMethods,
"warnPhones": options.WarnPhones,
"warnEmails": options.WarnEmails,
"autoAction": options.AutoAction,
"isEnabled": options.IsEnabled,
"remark": options.Remark,
"rowConfig": scene == "excel" || scene == "input",
"resultFile": g.Map{"status": "pending"},
}
if result != nil && result.ImportFileOssId > 0 {
info["importFileOssId"] = result.ImportFileOssId
} else if importOss != nil && importOss.OssId > 0 {
info["importFileOssId"] = importOss.OssId
}
if resultOss != nil && resultOss.OssId > 0 {
info["resultFileOssId"] = resultOss.OssId
}
if downloadId > 0 {
info["resultDownloadId"] = downloadId
}
return gjson.New(info)
}
func OptionsFromParams(params any) (Options, error) {
switch in := params.(type) {
case *model.TSimWarningConfigExcelSubmitParam:
if in == nil {
return Options{}, gerror.New("预警配置参数不能为空")
}
return NormalizeContextOptions(in.ItemType, in.WarnUnit)
case *model.TSimWarningConfigInputSubmitParam:
if in == nil {
return Options{}, gerror.New("预警配置参数不能为空")
}
return NormalizeOptions(&in.TSimWarningConfigApplyOptions)
case *model.TSimWarningConfigRangeSubmitParam:
if in == nil {
return Options{}, gerror.New("预警配置参数不能为空")
}
return NormalizeOptions(&in.TSimWarningConfigApplyOptions)
default:
return Options{}, gerror.New("预警配置参数不能为空")
}
}
func batchInfoOptions(params any) (Options, error) {
if in, ok := params.(*model.TSimWarningConfigInputSubmitParam); ok && in != nil {
contextOptions, err := NormalizeContextOptions(in.ItemType, in.WarnUnit)
if err != nil {
return Options{}, err
}
if in.FirstWarn <= 0 {
return contextOptions, nil
}
}
return OptionsFromParams(params)
}
func ValidateSubmitOptions(params any) error {
_, err := OptionsFromParams(params)
return err
}
func NormalizeOptions(in *model.TSimWarningConfigApplyOptions) (Options, error) {
if in == nil {
return Options{}, gerror.New("预警配置参数不能为空")
}
itemType := normalizeItemType(in.ItemType)
options := Options{
ItemType: itemType,
WarnType: deriveWarnType(itemType, in.WarnUnit),
WarnUnit: in.WarnUnit,
FirstWarn: in.FirstWarn,
SecondWarn: in.SecondWarn,
ThirdWarn: in.ThirdWarn,
WarnMethods: normalizeWarnMethods(in.WarnMethods),
WarnPhones: strings.TrimSpace(in.WarnPhones),
WarnEmails: strings.TrimSpace(in.WarnEmails),
WarnEmailCode: strings.TrimSpace(in.WarnEmailCode),
AutoAction: in.AutoAction,
IsEnabled: 1,
Remark: strings.TrimSpace(in.Remark),
}
if in.IsEnabled != nil {
options.IsEnabled = *in.IsEnabled
}
if err := validateOptions(&options); err != nil {
return Options{}, err
}
return options, nil
}
func validateOptions(options *Options) error {
if options == nil {
return gerror.New("预警配置参数不能为空")
}
if options.ItemType != warnItemTypeCard && options.ItemType != warnItemTypeFlowPool {
return gerror.New("预警对象类型无效")
}
if options.WarnUnit == warnUnitDay || options.WarnUnit == warnUnitMonth {
return gerror.New("日期预警由系统默认规则处理,不支持手动配置")
}
if options.IsEnabled != 0 && options.IsEnabled != 1 {
return gerror.New("启停状态无效")
}
if options.AutoAction != 0 && options.AutoAction != warnAutoActionDataCut {
return gerror.New("第三次是否断网配置无效")
}
if err := validateNotificationOptions(options); err != nil {
return err
}
if err := validateWarnTypeUnit(options.ItemType, options.WarnType, options.WarnUnit); err != nil {
return err
}
if options.ItemType == warnItemTypeFlowPool && options.AutoAction != 0 {
return gerror.New("流量池预警不支持第三次断网")
}
if options.AutoAction == warnAutoActionDataCut && options.ThirdWarn <= 0 {
return gerror.New("开启第三次断网时必须配置第三次提醒")
}
switch options.WarnType {
case warnTypeCardTraffic:
if options.WarnUnit == warnUnitPercent {
options.FirstWarn = normalizePercentThreshold(options.FirstWarn)
options.SecondWarn = normalizePercentThreshold(options.SecondWarn)
options.ThirdWarn = normalizePercentThreshold(options.ThirdWarn)
if options.FirstWarn > 100 || options.SecondWarn > 100 || options.ThirdWarn > 100 {
return gerror.New("百分比阈值不能大于100")
}
}
return validateTrafficThresholds(options.FirstWarn, options.SecondWarn, options.ThirdWarn)
case warnTypeFlowPoolPercent:
options.FirstWarn = normalizePercentThreshold(options.FirstWarn)
options.SecondWarn = normalizePercentThreshold(options.SecondWarn)
options.ThirdWarn = normalizePercentThreshold(options.ThirdWarn)
if options.FirstWarn > 100 || options.SecondWarn > 100 || options.ThirdWarn > 100 {
return gerror.New("百分比阈值不能大于100")
}
return validateTrafficThresholds(options.FirstWarn, options.SecondWarn, options.ThirdWarn)
default:
return gerror.New("预警类型无效")
}
}
func validateTrafficThresholds(first, second, third float64) error {
if first <= 0 {
return gerror.New("第一档阈值必须大于0")
}
if second < 0 || third < 0 {
return gerror.New("预警阈值不能小于0")
}
if second > 0 && second <= first {
return gerror.New("流量预警第二档阈值必须大于第一档")
}
if third > 0 {
if second <= 0 {
return gerror.New("配置第三档时必须先配置第二档")
}
if third <= second {
return gerror.New("流量预警第三档阈值必须大于第二档")
}
}
return nil
}
func normalizePercentThreshold(value float64) float64 {
if value > 0 && value <= 1 {
return value * 100
}
return value
}
func normalizeWarnMethods(methods string) string {
selected := map[string]struct{}{
warnMethodPlatform: {},
}
for _, method := range splitWarnList(methods) {
switch strings.ToLower(method) {
case warnMethodPlatform, "平台消息", "站内提醒", "站内消息":
selected[warnMethodPlatform] = struct{}{}
case warnMethodEmail, "邮箱":
selected[warnMethodEmail] = struct{}{}
case warnMethodSMS, "短信":
selected[warnMethodSMS] = struct{}{}
case warnMethodPhone, "电话":
selected[warnMethodPhone] = struct{}{}
}
}
ordered := []string{
warnMethodPlatform,
warnMethodEmail,
warnMethodSMS,
warnMethodPhone,
}
result := make([]string, 0, len(ordered))
for _, method := range ordered {
if _, ok := selected[method]; ok {
result = append(result, method)
}
}
return strings.Join(result, ",")
}
func submitScene(params any) string {
switch params.(type) {
case *model.TSimWarningConfigExcelSubmitParam:
return "excel"
case *model.TSimWarningConfigInputSubmitParam:
return "input"
case *model.TSimWarningConfigRangeSubmitParam:
return "range"
default:
return ""
}
}
func warningConfigName(itemType, warnUnit int) string {
warnType := deriveWarnType(itemType, warnUnit)
switch warnType {
case warnTypeFlowPoolPercent:
return "流量池预警配置"
case warnTypeCardTraffic:
return "流量预警配置"
case warnTypeExpireDay:
return "日期预警(天)配置"
case warnTypeExpireMonth:
return "日期预警(月)配置"
default:
return "SIM卡预警配置"
}
}
func normalizeItemType(itemType int) int {
if itemType == 0 {
return warnItemTypeCard
}
return itemType
}
func deriveWarnType(itemType, warnUnit int) int {
if itemType == warnItemTypeFlowPool {
return warnTypeFlowPoolPercent
}
switch warnUnit {
case warnUnitMb, warnUnitPercent:
return warnTypeCardTraffic
case warnUnitDay:
return warnTypeExpireDay
case warnUnitMonth:
return warnTypeExpireMonth
default:
return 0
}
}
func validateWarnTypeUnit(itemType, warnType, warnUnit int) error {
switch warnType {
case warnTypeCardTraffic:
if itemType != warnItemTypeCard {
return gerror.New("单卡流量预警只能配置单卡对象")
}
if warnUnit != warnUnitMb && warnUnit != warnUnitPercent {
return gerror.New("单卡流量预警仅支持MB或百分比单位")
}
case warnTypeExpireDay:
return gerror.New("日期预警由系统默认规则处理,不支持手动配置")
case warnTypeExpireMonth:
return gerror.New("日期预警由系统默认规则处理,不支持手动配置")
case warnTypeFlowPoolPercent:
if itemType != warnItemTypeFlowPool {
return gerror.New("流量池预警只能配置流量池对象")
}
if warnUnit != warnUnitPercent {
return gerror.New("流量池预警仅支持百分比单位")
}
default:
return gerror.New("预警类型无效")
}
return nil
}