Files
iotlcm/internal/logic/common/traffic_cutoff_config.go
T
MiFaZhan 88dfe5c640 chore: 初始开源发布
iotlcm-openapi:对外 OpenAPI 网关(V3 接口 + 旧版兼容接口)。

- Go module:iotlcm-openapi → git.5iot.com/iotlcm-public/iotlcm-openapi(178 文件)
- 凭据与地址脱敏:JWT signingKey、MySQL DSN、Redis 口令、
  RabbitMQ DSN 全部改为 ${*} 占位;私网 IP → 文档地址段
- 7 处真实手机号 → 合法 11 位编造号
- 启动 banner 统一 IOTLCM 方块字 + 社区惯例署名
- 移除内部资产:CI 触发器
- Dockerfile 改用公共基础镜像,移除 GOPRIVATE 内网设置
- README 重写(原为 GoFrame 模板占位文案),补 Apache-2.0 LICENSE,
  LF 行尾 + .gitattributes
2026-10-23 14:15:47 +08:00

210 lines
7.4 KiB
Go

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)
}