package common import ( "context" "strings" "github.com/gogf/gf/v2/database/gdb" "github.com/gogf/gf/v2/encoding/gjson" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/os/gtime" "git.5iot.com/iotlcm-public/iotlcm-openapi/internal/consts" "git.5iot.com/iotlcm-public/iotlcm-openapi/internal/library/batchmetadata" "git.5iot.com/iotlcm-public/iotlcm-openapi/internal/library/contexts" "git.5iot.com/iotlcm-public/iotlcm-openapi/internal/model" "git.5iot.com/iotlcm-public/iotlcm-openapi/internal/model/entity" ) const ( trafficCutoffConfigMaxCards = 1000 trafficCutoffRequestIDMaxLen = 64 ) func (s *sCommon) SubmitTrafficCutoffConfig(ctx context.Context, in *model.CommonTrafficCutoffConfigParam) (*model.CommonTrafficCutoffConfigRes, error) { return s.submitTrafficCutoffConfig(ctx, in, false, batchmetadata.Bulk) } func (s *sCommon) submitTrafficCutoffConfig(ctx context.Context, in *model.CommonTrafficCutoffConfigParam, waitForCompletion bool, executionMode int) (*model.CommonTrafficCutoffConfigRes, error) { if in == nil { return nil, gerror.New("达量断网配置参数不能为空") } if strings.TrimSpace(in.AppId) == "" { return nil, gerror.New("appId不能为空") } idempotencyKey := strings.TrimSpace(in.IdempotencyKey) if len(idempotencyKey) > trafficCutoffRequestIDMaxLen { return nil, gerror.Newf("Idempotency-Key长度不能超过%d", trafficCutoffRequestIDMaxLen) } if in.TrafficCutoffStatus == 0 { in.TrafficCutoffThreshold = 0 } if err := validateTrafficCutoffConfig(in.TrafficCutoffStatus, in.TrafficCutoffThreshold); err != nil { return nil, err } if len(in.Cards) == 0 { return nil, gerror.New("配置卡不能为空") } if len(in.Cards) > trafficCutoffConfigMaxCards { return nil, gerror.Newf("单次最多配置%d张卡", trafficCutoffConfigMaxCards) } identifiers := make([]cardIdentifier, 0, len(in.Cards)) seen := make(map[string]struct{}, len(in.Cards)) for _, card := range in.Cards { identifier := cardIdentifier{ Msisdn: strings.TrimSpace(card.Msisdn), Iccid: strings.TrimSpace(card.Iccid), Imsi: strings.TrimSpace(card.Imsi), } if err := validateCardIdentifier(identifier); err != nil { return nil, err } key := cardIdentifierKey(identifier) if _, ok := seen[key]; ok { return nil, gerror.Newf("卡片标识重复:%s", key) } seen[key] = struct{}{} identifiers = append(identifiers, identifier) } tenantID := strings.TrimSpace(contexts.GetTenantId(ctx)) deptID := contexts.GetAgentDeptId(ctx) if tenantID == "" || deptID <= 0 { return nil, gerror.New("当前接口调用方信息不完整") } operatorID := contexts.GetUserId(ctx) if operatorID <= 0 { operatorID = deptID } if idempotencyKey != "" { if existing, err := s.findTrafficCutoffBatch(ctx, tenantID, deptID, idempotencyKey); err != nil { return nil, err } else if existing != nil { return &model.CommonTrafficCutoffConfigRes{BatchNo: existing.BatchNo, BatchType: consts.BatchTypeTrafficCutoffConfig}, nil } } deptIDs, err := s.loadRenewAccessibleDeptIds(ctx, tenantID, deptID) if err != nil { return nil, err } cardMap, err := s.loadCardsByIdentifiers(ctx, tenantID, deptIDs, identifiers) if err != nil { return nil, err } iccids := make([]string, 0, len(identifiers)) for _, identifier := range identifiers { card := cardMap[cardIdentifierKey(identifier)] if !cardMatchesIdentifier(card, identifier) { return nil, gerror.New("卡片不存在或无权限") } iccids = append(iccids, strings.TrimSpace(card.Iccid)) } batchNo, err := s.BuildBatchNo(ctx, consts.BatchNoPrefixTrafficCutoffConfig) if err != nil { return nil, err } now := gtime.Now() batchInfo := gjson.New(map[string]any{ "operation": "traffic_cutoff_config", "configName": "达量断网配置", "submitScene": "openapi", "trafficCutoffStatus": in.TrafficCutoffStatus, "trafficCutoffThreshold": in.TrafficCutoffThreshold, "trafficCutoffThresholdUnit": "MB", "remark": strings.TrimSpace(in.Remark), "quantity": len(iccids), }) if idempotencyKey != "" { batchInfo.Set("idempotencyKey", idempotencyKey) } err = g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error { main := &entity.BatchMain{ BatchNo: batchNo, BatchType: consts.BatchTypeTrafficCutoffConfig, BatchStatus: consts.BatchStatusNormal, BatchAsyncStatus: consts.BatchAsyncStatusPending, SourceType: consts.BatchSourceTypeBatchInput, DeptId: deptID, TotalCount: len(iccids), ValidCount: len(iccids), RunResult: consts.BatchRunResultPending, BatchInfo: batchInfo, Remark: gjson.New(map[string]any{"remark": strings.TrimSpace(in.Remark)}), TenantId: tenantID, CreatedDept: deptID, CreatedBy: operatorID, CreatedAt: now, UpdatedBy: operatorID, UpdatedAt: now, } if err := s.SaveBatchMain(ctx, tx, main, executionMode); err != nil { return err } details := make([]*entity.BatchDetail, 0, len(iccids)) for index, iccid := range iccids { card := cardMap[cardIdentifierKey(identifiers[index])] snapshot := gjson.New(map[string]any{ "iccid": card.Iccid, "msisdn": card.Msisdn, "imsi": card.Imsi, "authorized": true, "trafficCutoffStatus": in.TrafficCutoffStatus, "trafficCutoffThreshold": in.TrafficCutoffThreshold, "trafficCutoffThresholdUnit": "MB", }) details = append(details, &entity.BatchDetail{ BatchNo: batchNo, BatchType: consts.BatchTypeTrafficCutoffConfig, DetailStatus: consts.BatchDetailStatusPending, BlankOutStatus: consts.BlankOutStatusNo, RowNo: index + 1, ItemId: iccid, Snapshot: snapshot, TenantId: tenantID, CreatedDept: deptID, CreatedBy: operatorID, CreatedAt: now, UpdatedBy: operatorID, UpdatedAt: now, }) } return s.SaveBatchDetails(ctx, tx, details) }) if err != nil { return nil, err } message := model.CommonBatchExecuteMessage{BatchNo: batchNo, BatchType: consts.BatchTypeTrafficCutoffConfig} if err := s.Publish(ctx, consts.QueueBatchExecute, message); err != nil { return nil, gerror.Wrap(err, "投递达量断网配置异步消息失败") } if waitForCompletion { if _, err := s.waitBatchCompletion(ctx, tenantID, deptID, batchNo); err != nil { return nil, err } } return &model.CommonTrafficCutoffConfigRes{BatchNo: batchNo, BatchType: consts.BatchTypeTrafficCutoffConfig}, nil } func (s *sCommon) SubmitSingleTrafficCutoffConfig(ctx context.Context, in *model.CommonTrafficCutoffSingleParam) (*model.CommonSingleTrafficCutoffRes, error) { if in == nil { return nil, gerror.New("traffic cutoff configuration parameters are required") } batch, err := s.submitTrafficCutoffConfig(ctx, &model.CommonTrafficCutoffConfigParam{ AppId: in.AppId, Cards: []model.CommonTrafficCutoffCard{{Msisdn: in.Msisdn, Iccid: in.Iccid, Imsi: in.Imsi}}, TrafficCutoffStatus: in.TrafficCutoffStatus, TrafficCutoffThreshold: in.TrafficCutoffThreshold, Remark: in.Remark, }, true, batchmetadata.Single) if err != nil { return nil, err } return s.loadSingleTrafficCutoffResult(ctx, batch.BatchNo) }