chore: 并入 iotlcm-admin-svr 历史到 admin-svr/(原提交 49818c6)
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
package trafficcutoffconfig
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/batchimport"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/batchinput/excel"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/batchinput/text"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/logic/iot/simcardbatch/common/cardsource"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
)
|
||||
|
||||
const maxRangeRows = 100000
|
||||
|
||||
func ParseExcelFileRows(ctx context.Context, filePath string) ([]batchimport.Row, error) {
|
||||
parsedRows, err := excel.ParseFile(ctx, filePath, excel.ParseOptions{
|
||||
RequiredHeaders: []string{"ICCID"},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rows := make([]batchimport.Row, 0, len(parsedRows))
|
||||
for _, parsed := range parsedRows {
|
||||
rows = append(rows, &Row{
|
||||
RowNo: parsed.RowNo,
|
||||
Iccid: excel.Field(parsed, "ICCID"),
|
||||
Msisdn: excel.Field(parsed, "MSISDN"),
|
||||
Imsi: excel.Field(parsed, "IMSI"),
|
||||
})
|
||||
}
|
||||
return rows, nil
|
||||
}
|
||||
|
||||
func ParseInputRows(ctx context.Context, input string) ([]batchimport.Row, error) {
|
||||
lines, err := text.ParseWhitespaceFields(ctx, input)
|
||||
if err != nil {
|
||||
if errors.Is(err, text.ErrEmptyInput) {
|
||||
return nil, gerror.New("批量输入达量断网配置数据为空")
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
rows := make([]batchimport.Row, 0, len(lines))
|
||||
for _, line := range lines {
|
||||
row := &Row{RowNo: line.RowNo}
|
||||
switch len(line.Fields) {
|
||||
case 1:
|
||||
row.Iccid = line.Fields[0]
|
||||
case 3:
|
||||
row.Iccid = line.Fields[0]
|
||||
row.Msisdn = line.Fields[1]
|
||||
row.Imsi = line.Fields[2]
|
||||
default:
|
||||
row.AddErrorReason("格式错误,应为 ICCID 或 ICCID MSISDN IMSI")
|
||||
}
|
||||
rows = append(rows, row)
|
||||
}
|
||||
return rows, nil
|
||||
}
|
||||
|
||||
func BuildRangeRows(ctx context.Context, startIccid, endIccid, startMsisdn, endMsisdn, startImsi, endImsi string) ([]batchimport.Row, error) {
|
||||
return cardsource.BuildRangeRows(ctx, cardsource.RangeConfig{
|
||||
StartIccid: startIccid,
|
||||
EndIccid: endIccid,
|
||||
StartMsisdn: startMsisdn,
|
||||
EndMsisdn: endMsisdn,
|
||||
StartImsi: startImsi,
|
||||
EndImsi: endImsi,
|
||||
MaxRows: maxRangeRows,
|
||||
EmptyMessage: "连号达量断网配置数据为空",
|
||||
OverLimitMessage: "连号达量断网配置数量不能超过%d条",
|
||||
CountMismatchMessage: "ICCID号段数量(%d)、MSISDN号段数量(%d)、IMSI号段数量(%d)必须一致",
|
||||
}, func(rowNo int, iccid, msisdn, imsi string) *Row {
|
||||
return &Row{
|
||||
RowNo: rowNo,
|
||||
Iccid: iccid,
|
||||
Msisdn: msisdn,
|
||||
Imsi: imsi,
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
package trafficcutoffconfig
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math"
|
||||
"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"
|
||||
)
|
||||
|
||||
type Processor struct{}
|
||||
|
||||
func NewProcessor() *Processor {
|
||||
return &Processor{}
|
||||
}
|
||||
|
||||
func (p *Processor) Config() batchimport.ProcessorConfig {
|
||||
return batchimport.ProcessorConfig{
|
||||
Code: "sim_card_traffic_cutoff_config",
|
||||
BatchType: consts.BatchTypeTrafficCutoffConfig,
|
||||
BatchNoPrefix: consts.BatchNoPrefixTrafficCutoffConfig,
|
||||
TaskName: consts.ResourceBatchDownloadTaskTrafficCutoffConfig,
|
||||
ExportScene: consts.ResourceExportSceneTrafficCutoffConfig,
|
||||
ImportDir: consts.SimCardTrafficCutoffConfigSubDirImportExcel,
|
||||
ResultDir: consts.SimCardTrafficCutoffConfigSubDirResultExcel,
|
||||
MaxFileSize: consts.SimCardTrafficCutoffConfigMaxFileSize,
|
||||
TemplateHeaders: []string{"ICCID", "MSISDN", "IMSI"},
|
||||
RequiredHeaders: []string{"ICCID"},
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Processor) ParseRows(ctx context.Context, filePath string) ([]batchimport.Row, error) {
|
||||
return ParseExcelFileRows(ctx, filePath)
|
||||
}
|
||||
|
||||
func (p *Processor) ValidateRows(_ context.Context, rows []batchimport.Row) error {
|
||||
firstByIccid := make(map[string]*Row, len(rows))
|
||||
for _, item := range rows {
|
||||
row, ok := item.(*Row)
|
||||
if !ok || row == nil {
|
||||
item.AddErrorReason("达量断网配置行类型错误")
|
||||
continue
|
||||
}
|
||||
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(fmt.Sprintf("ICCID与第%d行重复", first.RowNo))
|
||||
first.AddErrorReason("ICCID重复")
|
||||
continue
|
||||
}
|
||||
firstByIccid[row.Iccid] = row
|
||||
}
|
||||
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, _ := OptionsFromParams(req.BizParams)
|
||||
info := g.Map{
|
||||
"operation": "traffic_cutoff_config",
|
||||
"configName": "达量断网配置",
|
||||
"submitScene": submitScene(req.BizParams),
|
||||
"trafficCutoffStatus": options.TrafficCutoffStatus,
|
||||
"trafficCutoffThreshold": options.TrafficCutoffThreshold,
|
||||
"trafficCutoffCrossMonthHold": options.TrafficCutoffCrossMonthHold,
|
||||
"trafficCutoffThresholdUnit": "MB",
|
||||
"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.TSimCardTrafficCutoffExcelSubmitParam:
|
||||
if in == nil {
|
||||
return Options{}, gerror.New("达量断网配置参数不能为空")
|
||||
}
|
||||
return NormalizeOptions(&in.TSimCardTrafficCutoffOptions)
|
||||
case *model.TSimCardTrafficCutoffInputSubmitParam:
|
||||
if in == nil {
|
||||
return Options{}, gerror.New("达量断网配置参数不能为空")
|
||||
}
|
||||
return NormalizeOptions(&in.TSimCardTrafficCutoffOptions)
|
||||
case *model.TSimCardTrafficCutoffRangeSubmitParam:
|
||||
if in == nil {
|
||||
return Options{}, gerror.New("达量断网配置参数不能为空")
|
||||
}
|
||||
return NormalizeOptions(&in.TSimCardTrafficCutoffOptions)
|
||||
default:
|
||||
return Options{}, gerror.New("达量断网配置参数不能为空")
|
||||
}
|
||||
}
|
||||
|
||||
func NormalizeOptions(in *model.TSimCardTrafficCutoffOptions) (Options, error) {
|
||||
if in == nil || in.TrafficCutoffStatus == nil {
|
||||
return Options{}, gerror.New("达量断网配置状态不能为空")
|
||||
}
|
||||
crossMonthHold := 0
|
||||
if in.TrafficCutoffCrossMonthHold != nil {
|
||||
crossMonthHold = *in.TrafficCutoffCrossMonthHold
|
||||
}
|
||||
options := Options{
|
||||
TrafficCutoffCrossMonthHold: crossMonthHold,
|
||||
TrafficCutoffStatus: *in.TrafficCutoffStatus,
|
||||
TrafficCutoffThreshold: in.TrafficCutoffThreshold,
|
||||
}
|
||||
if err := ValidateConfig(options.TrafficCutoffStatus, options.TrafficCutoffThreshold, options.TrafficCutoffCrossMonthHold); err != nil {
|
||||
return Options{}, err
|
||||
}
|
||||
return options, nil
|
||||
}
|
||||
|
||||
func ValidateConfig(status int, threshold float64, crossMonthHoldValues ...int) error {
|
||||
crossMonthHold := 0
|
||||
if len(crossMonthHoldValues) > 0 {
|
||||
crossMonthHold = crossMonthHoldValues[0]
|
||||
}
|
||||
if status != 0 && status != 1 && status != 2 {
|
||||
return gerror.New("达量断网启停状态无效")
|
||||
}
|
||||
if threshold < 0 {
|
||||
return gerror.New("达量断网阈值不能小于0")
|
||||
}
|
||||
if status == 0 && threshold != 0 {
|
||||
return gerror.New("未配置达量断网时阈值必须为0")
|
||||
}
|
||||
if status == 1 && threshold <= 0 {
|
||||
return gerror.New("开启达量断网时阈值必须大于0")
|
||||
}
|
||||
if crossMonthHold != 0 && crossMonthHold != 1 {
|
||||
return gerror.New("invalid cross-month traffic cutoff hold value")
|
||||
}
|
||||
if status != 1 && crossMonthHold != 0 {
|
||||
return gerror.New("cross-month hold requires traffic cutoff to be enabled")
|
||||
}
|
||||
rounded := math.Round(threshold*1000) / 1000
|
||||
if math.Abs(threshold-rounded) > 0.0000001 {
|
||||
return gerror.New("达量断网阈值最多保留3位小数")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func submitScene(params any) string {
|
||||
switch params.(type) {
|
||||
case *model.TSimCardTrafficCutoffExcelSubmitParam:
|
||||
return "excel"
|
||||
case *model.TSimCardTrafficCutoffInputSubmitParam:
|
||||
return "input"
|
||||
case *model.TSimCardTrafficCutoffRangeSubmitParam:
|
||||
return "range"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
package trafficcutoffconfig
|
||||
|
||||
import (
|
||||
"context"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/batchmetadata"
|
||||
|
||||
"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/logic/iot/simcardbatch/common"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/model"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/net/ghttp"
|
||||
)
|
||||
|
||||
func CreatePendingExcel(ctx context.Context, file *ghttp.UploadFile, in *model.TSimCardTrafficCutoffExcelSubmitParam) (*PendingResult, error) {
|
||||
if in == nil {
|
||||
in = &model.TSimCardTrafficCutoffExcelSubmitParam{}
|
||||
}
|
||||
options, err := OptionsFromParams(in)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
processor := NewProcessor()
|
||||
req := batchimport.SubmitRequest{
|
||||
SourceChannel: batchmetadata.Console,
|
||||
ExecutionMode: batchmetadata.Bulk,
|
||||
File: file,
|
||||
BizParams: in,
|
||||
SourceType: consts.BatchSourceTypeFile,
|
||||
Operator: common.CurrentOperator(ctx),
|
||||
}
|
||||
result, err := batchimport.CreatePendingUpload(ctx, req, processor, prepareOptions(options))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
common.PublishBatchMessage(ctx, result, consts.QueueBatchExecute, "traffic_cutoff_config")
|
||||
return pendingResultFromBatch(result), nil
|
||||
}
|
||||
|
||||
func CreatePendingInput(ctx context.Context, in *model.TSimCardTrafficCutoffInputSubmitParam) (*PendingResult, error) {
|
||||
if in == nil {
|
||||
in = &model.TSimCardTrafficCutoffInputSubmitParam{}
|
||||
}
|
||||
options, err := OptionsFromParams(in)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rows, err := ParseInputRows(ctx, in.Input)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
processor := NewProcessor()
|
||||
req := batchimport.SubmitRequest{
|
||||
SourceChannel: batchmetadata.Console,
|
||||
ExecutionMode: batchmetadata.Bulk,
|
||||
BizParams: in,
|
||||
SourceType: consts.BatchSourceTypeBatchInput,
|
||||
Operator: common.CurrentOperator(ctx),
|
||||
}
|
||||
result, err := batchimport.CreatePendingRows(ctx, req, processor, rows, prepareOptions(options))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
common.PublishBatchMessage(ctx, result, consts.QueueBatchExecute, "traffic_cutoff_config")
|
||||
return pendingResultFromBatch(result), nil
|
||||
}
|
||||
|
||||
func CreatePendingRange(ctx context.Context, in *model.TSimCardTrafficCutoffRangeSubmitParam) (*PendingResult, error) {
|
||||
if in == nil {
|
||||
in = &model.TSimCardTrafficCutoffRangeSubmitParam{}
|
||||
}
|
||||
options, err := OptionsFromParams(in)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rows, err := BuildRangeRows(ctx, in.StartIccid, in.EndIccid, in.StartMsisdn, in.EndMsisdn, in.StartImsi, in.EndImsi)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
processor := NewProcessor()
|
||||
req := batchimport.SubmitRequest{
|
||||
SourceChannel: batchmetadata.Console,
|
||||
ExecutionMode: batchmetadata.Bulk,
|
||||
BizParams: in,
|
||||
SourceType: consts.BatchSourceTypeRange,
|
||||
Operator: common.CurrentOperator(ctx),
|
||||
}
|
||||
result, err := batchimport.CreatePendingRows(ctx, req, processor, rows, prepareOptions(options))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
common.PublishBatchMessage(ctx, result, consts.QueueBatchExecute, "traffic_cutoff_config")
|
||||
return pendingResultFromBatch(result), nil
|
||||
}
|
||||
|
||||
func prepareOptions(options Options) batchimport.PendingOptions {
|
||||
return batchimport.PendingOptions{
|
||||
PrepareRows: func(ctx context.Context, rows []batchimport.Row) error {
|
||||
if err := common.PrepareAuthorizedRows(ctx, rows, func(item batchimport.Row) (*Row, error) {
|
||||
row, ok := item.(*Row)
|
||||
if !ok || row == nil {
|
||||
return nil, gerror.New("达量断网配置行类型错误")
|
||||
}
|
||||
return row, nil
|
||||
}, "达量断网配置行类型错误"); err != nil {
|
||||
return err
|
||||
}
|
||||
applyConfigSnapshot(rows, options)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func applyConfigSnapshot(rows []batchimport.Row, options Options) {
|
||||
for _, item := range rows {
|
||||
row, ok := item.(*Row)
|
||||
if !ok || row == nil {
|
||||
continue
|
||||
}
|
||||
row.Config = options
|
||||
}
|
||||
}
|
||||
|
||||
func pendingResultFromBatch(result *batchimport.Result) *PendingResult {
|
||||
res := &PendingResult{
|
||||
BatchType: consts.BatchTypeTrafficCutoffConfig,
|
||||
BatchAsyncStatus: consts.BatchAsyncStatusPending,
|
||||
}
|
||||
if result == nil {
|
||||
return res
|
||||
}
|
||||
valid := result.ValidRows
|
||||
if result.TotalRows > 0 && valid == 0 {
|
||||
valid = result.TotalRows - result.ErrorRows
|
||||
if valid < 0 {
|
||||
valid = 0
|
||||
}
|
||||
}
|
||||
res.BatchNo = result.BatchNo
|
||||
res.BatchType = result.BatchType
|
||||
res.Total = result.TotalRows
|
||||
res.ValidCount = valid
|
||||
res.FailCount = result.ErrorRows
|
||||
return res
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
package trafficcutoffconfig
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/v2/encoding/gjson"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
type Row struct {
|
||||
RowNo int
|
||||
Iccid string
|
||||
Msisdn string
|
||||
Imsi string
|
||||
Authorized bool
|
||||
Reason string
|
||||
Config Options
|
||||
}
|
||||
|
||||
func (r *Row) GetRowNo() int {
|
||||
if r == nil {
|
||||
return 0
|
||||
}
|
||||
return r.RowNo
|
||||
}
|
||||
|
||||
func (r *Row) GetItemID() string {
|
||||
if r == nil {
|
||||
return ""
|
||||
}
|
||||
return r.Iccid
|
||||
}
|
||||
|
||||
func (r *Row) GetErrorReasons() []string {
|
||||
if r == nil || r.Reason == "" {
|
||||
return nil
|
||||
}
|
||||
return []string{r.Reason}
|
||||
}
|
||||
|
||||
func (r *Row) AddErrorReason(reason string) {
|
||||
if r == nil || r.Reason != "" || strings.TrimSpace(reason) == "" {
|
||||
return
|
||||
}
|
||||
r.Reason = strings.TrimSpace(reason)
|
||||
}
|
||||
|
||||
func (r *Row) GetIccid() string {
|
||||
if r == nil {
|
||||
return ""
|
||||
}
|
||||
return r.Iccid
|
||||
}
|
||||
|
||||
func (r *Row) SetAuthorized(v bool) {
|
||||
if r == nil {
|
||||
return
|
||||
}
|
||||
r.Authorized = v
|
||||
}
|
||||
|
||||
func (r *Row) Snapshot() *gjson.Json {
|
||||
if r == nil {
|
||||
return gjson.New(g.Map{})
|
||||
}
|
||||
return gjson.New(g.Map{
|
||||
"ICCID": r.Iccid,
|
||||
"MSISDN": r.Msisdn,
|
||||
"IMSI": r.Imsi,
|
||||
"authorized": r.Authorized,
|
||||
"trafficCutoffStatus": r.Config.TrafficCutoffStatus,
|
||||
"trafficCutoffThreshold": r.Config.TrafficCutoffThreshold,
|
||||
"trafficCutoffCrossMonthHold": r.Config.TrafficCutoffCrossMonthHold,
|
||||
})
|
||||
}
|
||||
|
||||
type Options struct {
|
||||
TrafficCutoffCrossMonthHold int
|
||||
TrafficCutoffStatus int
|
||||
TrafficCutoffThreshold float64
|
||||
}
|
||||
|
||||
type PendingResult struct {
|
||||
BatchNo string
|
||||
BatchType int
|
||||
BatchAsyncStatus int
|
||||
Total int
|
||||
ValidCount int
|
||||
FailCount int
|
||||
}
|
||||
Reference in New Issue
Block a user