Files
iotlcm/admin-svr/server/internal/logic/iot/simcardbatch/inbound/pending.go
T

317 lines
9.9 KiB
Go

package inbound
import (
"context"
"io"
"strings"
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/consts"
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/dao"
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/batchimport"
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/batchmetadata"
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/contexts"
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/rabbitmq"
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/model"
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/model/do"
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/service"
"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/net/ghttp"
)
const inputInlineMaxRowsConfigKey = "batch.inbound.inputInlineMaxRows"
type BatchExecuteMessage struct {
BatchNo string `json:"batchNo"`
BatchType int `json:"batchType"`
}
type PendingResult struct {
BatchNo string
BatchType int
}
func CreatePendingExcel(ctx context.Context, file *ghttp.UploadFile, in *model.TSimCardInboundExcelSubmitParam) (*PendingResult, error) {
if in == nil {
in = &model.TSimCardInboundExcelSubmitParam{}
}
if file == nil {
return nil, gerror.New("请选择上传文件")
}
if err := service.SysOss().CheckType(ctx, consts.SysUploadFileTypeFile, file); err != nil {
return nil, err
}
if file.Size > consts.SimCardInboundMaxFileSize {
return nil, gerror.Newf("上传文件大小超过限制,最大为%dM", consts.SimCardInboundMaxFileSize>>20)
}
batchNo, err := batchimport.NextBatchNoContext(ctx, consts.BatchNoPrefixInbound)
if err != nil {
return nil, err
}
content, err := readUploadContent(file)
if err != nil {
return nil, err
}
importOss, err := service.SysOss().SaveContent(ctx, &model.SysOssSaveContentParam{
Content: content,
FileName: batchNo + "-import.xlsx",
NewFileType: consts.SysUploadNewFileTypeUserFile,
SubDirName: consts.SimCardInboundSubDirImportExcel,
})
if err != nil {
return nil, gerror.Wrap(err, "保存入库原始Excel失败")
}
info := newPendingBatchInfo()
info["source"] = g.Map{"importFileOssId": importOss.OssId}
info["inboundOptions"] = inboundOptions(in.InboundCommon())
return createPendingAndPublish(ctx, pendingInput{
BatchNo: batchNo,
SourceType: consts.BatchSourceTypeFile,
SourceOssId: importOss.OssId,
BatchInfo: gjson.New(info),
})
}
func CreatePendingRange(ctx context.Context, in *model.TSimCardInboundRangeSubmitParam) (*PendingResult, error) {
if in == nil {
in = &model.TSimCardInboundRangeSubmitParam{}
}
if err := validateInboundCommon(in.InboundCommon()); err != nil {
return nil, err
}
rangeInfo, err := pendingRangeInfo(in)
if err != nil {
return nil, err
}
batchNo, err := batchimport.NextBatchNoContext(ctx, consts.BatchNoPrefixInbound)
if err != nil {
return nil, err
}
info := newPendingBatchInfo()
info["source"] = g.Map{"range": rangeInfo}
info["inboundOptions"] = inboundOptions(in.InboundCommon())
return createPendingAndPublish(ctx, pendingInput{
BatchNo: batchNo,
SourceType: consts.BatchSourceTypeRange,
BatchInfo: gjson.New(info),
})
}
func CreatePendingInput(ctx context.Context, in *model.TSimCardInboundInputSubmitParam) (*PendingResult, error) {
if in == nil {
in = &model.TSimCardInboundInputSubmitParam{}
}
if err := validateInboundCommon(in.InboundCommon()); err != nil {
return nil, err
}
inputText := in.Input
if strings.TrimSpace(inputText) == "" {
return nil, gerror.New("批量输入入库数据为空")
}
batchNo, err := batchimport.NextBatchNoContext(ctx, consts.BatchNoPrefixInbound)
if err != nil {
return nil, err
}
var sourceOssId int64
source := g.Map{}
if countNonEmptyLines(inputText) <= inputInlineMaxRows(ctx) {
source["inputText"] = inputText
} else {
inputOss, saveErr := service.SysOss().SaveContent(ctx, &model.SysOssSaveContentParam{
Content: []byte(inputText),
FileName: batchNo + "-input.txt",
NewFileType: consts.SysUploadNewFileTypeUserFile,
SubDirName: consts.SimCardInboundSubDirImportInput,
})
if saveErr != nil {
return nil, gerror.Wrap(saveErr, "保存入库批量输入原文失败")
}
source["inputFileOssId"] = inputOss.OssId
sourceOssId = inputOss.OssId
}
info := newPendingBatchInfo()
info["source"] = source
info["inboundOptions"] = inboundOptions(in.InboundCommon())
return createPendingAndPublish(ctx, pendingInput{
BatchNo: batchNo,
SourceType: consts.BatchSourceTypeBatchInput,
SourceOssId: sourceOssId,
BatchInfo: gjson.New(info),
})
}
type pendingInput struct {
BatchNo string
SourceType int
SourceOssId int64
BatchInfo *gjson.Json
}
func createPendingAndPublish(ctx context.Context, input pendingInput) (*PendingResult, error) {
if strings.TrimSpace(input.BatchNo) == "" {
return nil, gerror.New("批次号为空")
}
operator := batchimport.OperatorContext{
TenantId: contexts.GetTenantId(ctx),
DeptId: contexts.GetDeptId(ctx),
UserId: contexts.GetUserId(ctx),
}
err := g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
if _, insertErr := tx.Ctx(ctx).Model(dao.BatchMain.Table()).Data(do.BatchMain{
BatchNo: input.BatchNo,
OrderSn: "",
BatchType: consts.BatchTypeInbound,
BatchStatus: consts.BatchStatusNormal,
BatchAsyncStatus: consts.BatchAsyncStatusPending,
SourceType: input.SourceType,
SourceChannel: batchmetadata.Console,
ExecutionMode: batchmetadata.Bulk,
SourceBatchNo: input.BatchNo,
SourceOssId: inboundOssIDValue(input.SourceOssId),
IsCrossDept: 0,
DeptId: operator.DeptId,
TotalCount: 0,
ValidCount: 0,
SuccessCount: 0,
FailCount: 0,
RunResult: consts.BatchRunResultPending,
FailReason: "",
BatchInfo: input.BatchInfo,
TenantId: operator.TenantId,
CreatedDept: operator.DeptId,
CreatedBy: operator.UserId,
UpdatedBy: operator.UserId,
}).Insert(); insertErr != nil {
return gerror.Wrap(insertErr, "写入待处理入库批次失败")
}
return nil
})
if err != nil {
return nil, err
}
publishBatch(ctx, input.BatchNo)
return &PendingResult{BatchNo: input.BatchNo, BatchType: consts.BatchTypeInbound}, nil
}
func publishBatch(ctx context.Context, batchNo string) {
if err := rabbitmq.Publish(ctx, consts.QueueBatchExecute, BatchExecuteMessage{
BatchNo: batchNo,
BatchType: consts.BatchTypeInbound,
}); err != nil {
g.Log().Errorf(ctx, "inbound batch async publish failed, batchNo=%s, batchType=%d, tenantId=%s, error=%+v",
batchNo, consts.BatchTypeInbound, contexts.GetTenantId(ctx), err)
}
}
func inboundOssIDValue(ossID int64) any {
if ossID <= 0 {
return nil
}
return ossID
}
func newPendingBatchInfo() g.Map {
return g.Map{
"resultFile": g.Map{
"status": "pending",
},
}
}
func inboundOptions(common model.TSimCardInboundCommonFields) g.Map {
return g.Map{
"chanKey": strings.TrimSpace(common.ChanKey),
"validateUpstreamBasicInfo": common.ValidateUpstreamBasicInfo,
"cardType": strings.TrimSpace(common.CardType),
"upPkgCode": strings.TrimSpace(common.UpPkgCode),
"suppliersCode": strings.TrimSpace(common.SuppliersCode),
"upstreamActivationTime": common.UpstreamActivationTime,
"insertToPlugEnabled": common.InsertToPlugEnabled,
"carrierTrafficCutoffEnabled": common.CarrierTrafficCutoffEnabled,
"agreementExpiryDate": common.AgreementExpiryDate,
"hasTestPeriod": common.HasTestPeriod,
"testPeriodDays": common.TestPeriodDays,
"testPeriodLatestExpiryDate": common.TestPeriodLatestExpiryDate,
"hasSilentPeriod": common.HasSilentPeriod,
"silentPeriodDays": common.SilentPeriodDays,
"silentPeriodLatestExpiryDate": common.SilentPeriodLatestExpiryDate,
}
}
func validateInboundCommon(common model.TSimCardInboundCommonFields) error {
if strings.TrimSpace(common.ChanKey) == "" {
return gerror.New("通道编号不能为空")
}
return nil
}
func pendingRangeInfo(in *model.TSimCardInboundRangeSubmitParam) (g.Map, error) {
rangeType := strings.ToLower(strings.TrimSpace(in.RangeType))
if rangeType == "" {
rangeType = "iccid"
}
if rangeType != "iccid" && rangeType != "msisdn" && rangeType != "imsi" {
return nil, gerror.New("连号类型必须是 iccid、msisdn 或 imsi")
}
values := g.Map{
"rangeType": rangeType,
"startIccid": strings.TrimSpace(in.StartIccid),
"endIccid": strings.TrimSpace(in.EndIccid),
"startMsisdn": strings.TrimSpace(in.StartMsisdn),
"endMsisdn": strings.TrimSpace(in.EndMsisdn),
"startImsi": strings.TrimSpace(in.StartImsi),
"endImsi": strings.TrimSpace(in.EndImsi),
}
fieldNames := map[string][2]string{
"iccid": {"startIccid", "endIccid"},
"msisdn": {"startMsisdn", "endMsisdn"},
"imsi": {"startImsi", "endImsi"},
}
for _, key := range fieldNames[rangeType] {
if strings.TrimSpace(values[key].(string)) == "" {
return nil, gerror.Newf("%s不能为空", key)
}
}
return values, nil
}
func countNonEmptyLines(input string) int {
count := 0
for _, line := range strings.Split(input, "\n") {
if strings.TrimSpace(line) != "" {
count++
}
}
return count
}
func inputInlineMaxRows(ctx context.Context) int {
value := g.Cfg().MustGet(ctx, inputInlineMaxRowsConfigKey, consts.SimCardInboundDefaultInputInlineMaxRows).Int()
if value <= 0 {
return consts.SimCardInboundDefaultInputInlineMaxRows
}
return value
}
func readUploadContent(file *ghttp.UploadFile) ([]byte, error) {
reader, err := file.Open()
if err != nil {
return nil, gerror.Wrap(err, "打开上传文件失败")
}
defer reader.Close()
content, err := io.ReadAll(reader)
if err != nil {
return nil, gerror.Wrap(err, "读取上传文件失败")
}
return content, nil
}