242 lines
11 KiB
Go
242 lines
11 KiB
Go
package app
|
|
|
|
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/os/gtime"
|
|
"git.5iot.com/iotlcm-public/iotlcm-app/internal/consts"
|
|
"git.5iot.com/iotlcm-public/iotlcm-app/internal/dao"
|
|
"git.5iot.com/iotlcm-public/iotlcm-app/internal/library/allocationunits"
|
|
"git.5iot.com/iotlcm-public/iotlcm-app/internal/library/batchimport"
|
|
"git.5iot.com/iotlcm-public/iotlcm-app/internal/library/batchmetadata"
|
|
"git.5iot.com/iotlcm-public/iotlcm-app/internal/model"
|
|
"git.5iot.com/iotlcm-public/iotlcm-app/internal/model/do"
|
|
"git.5iot.com/iotlcm-public/iotlcm-app/internal/model/entity"
|
|
)
|
|
|
|
func appAllocateParentInfo(batch *appAllocateBatchContext, remark string) *gjson.Json {
|
|
return gjson.New(map[string]any{
|
|
"operation": "sim_card_allocate", "targetBatchType": consts.BatchTypeAllocate,
|
|
"jobRole": "allocate_parent", "autoSplit": true, "splitCompleted": true,
|
|
"chunkCount": 1, "chunkSize": 100, "parsedCount": len(batch.Cards),
|
|
"targetDeptId": batch.Target.DeptId, "targetDeptName": batch.Target.DeptName,
|
|
"cards": appAllocateCardSnapshotList(batch), "remark": remark, "resultFile": map[string]any{"status": "pending"},
|
|
})
|
|
}
|
|
|
|
func (s *sAppCard) queueAppAllocateBatch(ctx context.Context, tx gdb.TX, user *entity.SysUser, prepare *entity.BatchMain, batch *appAllocateBatchContext, remark string) error {
|
|
units, err := allocationunits.Snapshot(ctx, user.TenantId, user.DeptId, batch.Target.DeptId, tx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
childNo, err := batchimport.NextBatchNoContext(ctx, consts.BatchNoPrefixAllocate)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
mode := batchmetadata.Bulk
|
|
if prepare.ExecutionMode == batchmetadata.Single {
|
|
mode = batchmetadata.Single
|
|
}
|
|
parentInfo := allocationunits.Merge(appAllocateParentInfo(batch, remark), units)
|
|
if prepare.BatchInfo != nil {
|
|
for _, key := range []string{"requestId", "requestHash"} {
|
|
if value := prepare.BatchInfo.Get(key).String(); value != "" {
|
|
parentInfo.Set(key, value)
|
|
}
|
|
}
|
|
}
|
|
childInfo := gjson.New(map[string]any{
|
|
"operation": "sim_card_allocate", "jobRole": "allocate_chunk", "autoSplit": true,
|
|
"targetBatchType": consts.BatchTypeAllocate, "targetDeptId": batch.Target.DeptId,
|
|
"parentBatchNo": prepare.BatchNo, "chunkNo": 1, "remark": remark,
|
|
})
|
|
if _, err := dao.BatchMain.Ctx(ctx).TX(tx).Data(do.BatchMain{
|
|
BatchNo: childNo, BatchType: consts.BatchTypeAllocate,
|
|
BatchStatus: consts.BatchAsyncStatusPending, BatchAsyncStatus: consts.BatchAsyncStatusPending,
|
|
SourceType: consts.BatchSourceTypeBatchSelect, SourceBatchNo: prepare.BatchNo,
|
|
SourceChannel: batchmetadata.MiniProgram, ExecutionMode: mode,
|
|
TargetDeptId: batch.Target.DeptId, DeptId: user.DeptId,
|
|
TotalCount: len(batch.Cards), ValidCount: 0, SuccessCount: 0, FailCount: 0,
|
|
RunResult: consts.BatchRunResultPending, BatchInfo: allocationunits.Merge(childInfo, units),
|
|
TenantId: user.TenantId, CreatedDept: user.DeptId, CreatedBy: user.UserId, UpdatedBy: user.UserId,
|
|
}).Insert(); err != nil {
|
|
return gerror.Wrap(err, "创建划拨执行子批次失败")
|
|
}
|
|
details := make([]do.BatchDetail, 0, len(batch.Cards))
|
|
for index, item := range batch.Cards {
|
|
details = append(details, do.BatchDetail{
|
|
BatchNo: childNo, BatchType: consts.BatchTypeAllocate,
|
|
DetailStatus: consts.BatchDetailStatusPending, BlankOutStatus: consts.BlankOutStatusNo,
|
|
RowNo: index + 1, ItemId: item.Card.Iccid,
|
|
Snapshot: gjson.New(map[string]any{
|
|
"iccid": item.Card.Iccid, "msisdn": item.Card.Msisdn, "imsi": item.Card.Imsi,
|
|
"targetDeptId": batch.Target.DeptId, "targetDeptName": units.Get("targetDeptName").String(), "targetDeptKey": units.Get("targetDeptKey").String(),
|
|
"sourceDeptId": item.Card.DeptId, "sourceDeptName": units.Get("sourceDeptName").String(), "sourceDeptKey": units.Get("sourceDeptKey").String(), "remark": remark,
|
|
}),
|
|
TenantId: user.TenantId, CreatedDept: user.DeptId, CreatedBy: user.UserId, UpdatedBy: user.UserId,
|
|
})
|
|
}
|
|
if _, err := dao.BatchDetail.Ctx(ctx).TX(tx).Data(details).Insert(); err != nil {
|
|
return gerror.Wrap(err, "创建划拨执行明细失败")
|
|
}
|
|
if _, err := dao.BatchDetail.Ctx(ctx).TX(tx).
|
|
Where(dao.BatchDetail.Columns().BatchNo, prepare.BatchNo).
|
|
Where(dao.BatchDetail.Columns().BatchType, consts.BatchTypePrepare).
|
|
Where(dao.BatchDetail.Columns().TenantId, user.TenantId).
|
|
Where(dao.BatchDetail.Columns().CreatedBy, user.UserId).Delete(); err != nil {
|
|
return gerror.Wrap(err, "清理划拨预处理明细失败")
|
|
}
|
|
_, err = dao.BatchMain.Ctx(ctx).TX(tx).Where(dao.BatchMain.Columns().Id, prepare.Id).Data(do.BatchMain{
|
|
BatchType: consts.BatchTypeAllocate, SourceBatchNo: "", ResultOssId: 0,
|
|
BatchStatus: consts.BatchAsyncStatusPending, BatchAsyncStatus: consts.BatchAsyncStatusPending,
|
|
RunResult: consts.BatchRunResultPending, ValidCount: 0, SuccessCount: 0, FailCount: 0, FailReason: "",
|
|
SourceChannel: batchmetadata.MiniProgram, ExecutionMode: mode,
|
|
BatchInfo: parentInfo, UpdatedBy: user.UserId,
|
|
StartTime: new(gtime.Time), EndTime: new(gtime.Time),
|
|
}).Update()
|
|
if err != nil {
|
|
return gerror.Wrap(err, "创建划拨父批次失败")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *sAppCard) createAppAllocatePending(ctx context.Context, user *entity.SysUser, param *model.AppCardAllocateBatchSubmitParam) (*model.AppCardAllocateBatchModel, error) {
|
|
if !appWorkerBatchAsyncEnabled(ctx) {
|
|
return nil, gerror.New("划拨执行服务未启用,请联系管理员")
|
|
}
|
|
if user.DeptId <= 0 || s.isTerminalUser(ctx, user) {
|
|
return nil, gerror.New("当前账号不能使用卡划拨功能")
|
|
}
|
|
values := appNormalizeAllocateCardValues(param.CardValues)
|
|
mode := appAllocateExecutionMode(param.ExecutionMode)
|
|
if len(values) == 0 || len(values) > 100 || (mode == batchmetadata.Single && len(values) != 1) {
|
|
return nil, gerror.New("请选择1至100张卡,单卡划拨只能选择一张卡")
|
|
}
|
|
target, err := s.loadAllocateTarget(ctx, user, param.TargetDeptId)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
batch := &appAllocateBatchContext{Target: target, TotalCount: len(values)}
|
|
for index, value := range values {
|
|
card, err := s.findVisibleCard(ctx, user, value)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if card == nil || card.DeptId != user.DeptId || strings.TrimSpace(card.Iccid) == "" {
|
|
return nil, gerror.Newf("第%d张卡不在当前企业可划拨范围内", index+1)
|
|
}
|
|
batch.Cards = append(batch.Cards, &appAllocateCardContext{Card: card, ItemId: card.Iccid, RowNo: index + 1, SourceDeptId: card.DeptId})
|
|
}
|
|
parentNo, err := batchimport.NextBatchNoContext(ctx, consts.BatchNoPrefixAllocate)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
parent, err := s.persistAppAllocatePending(ctx, user, parentNo, mode, batch, strings.TrimSpace(param.Remark))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
response := appAllocateQueuedResponse(parent)
|
|
if err := s.publishAppAllocateBatch(ctx, user, parentNo); err != nil {
|
|
response.FailReason = "任务已受理,投递尚未确认,请保留批次号并稍后刷新"
|
|
}
|
|
return response, nil
|
|
}
|
|
|
|
func (s *sAppCard) persistAppAllocatePending(ctx context.Context, user *entity.SysUser, parentNo string, mode int, batch *appAllocateBatchContext, remark string) (*entity.BatchMain, error) {
|
|
parent := &entity.BatchMain{
|
|
BatchNo: parentNo, BatchType: consts.BatchTypeAllocate, ExecutionMode: uint(mode),
|
|
BatchStatus: consts.BatchAsyncStatusPending, BatchAsyncStatus: consts.BatchAsyncStatusPending,
|
|
TotalCount: len(batch.Cards), TargetDeptId: batch.Target.DeptId, BatchInfo: appAllocateParentInfo(batch, remark),
|
|
}
|
|
err := dao.BatchMain.Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
|
|
id, err := dao.BatchMain.Ctx(ctx).TX(tx).Data(do.BatchMain{
|
|
BatchNo: parentNo, BatchType: consts.BatchTypeAllocate, SourceBatchNo: "",
|
|
BatchStatus: consts.BatchAsyncStatusPending, BatchAsyncStatus: consts.BatchAsyncStatusPending,
|
|
RunResult: consts.BatchRunResultPending, SourceType: consts.BatchSourceTypeBatchSelect,
|
|
SourceChannel: batchmetadata.MiniProgram, ExecutionMode: mode,
|
|
TotalCount: len(batch.Cards), TargetDeptId: batch.Target.DeptId, DeptId: user.DeptId,
|
|
TenantId: user.TenantId, CreatedDept: user.DeptId, CreatedBy: user.UserId, UpdatedBy: user.UserId,
|
|
}).InsertAndGetId()
|
|
if err != nil {
|
|
return gerror.Wrap(err, "创建划拨待处理批次失败")
|
|
}
|
|
parent.Id = id
|
|
return s.queueAppAllocateBatch(ctx, tx, user, parent, batch, remark)
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return parent, nil
|
|
}
|
|
|
|
func (s *sAppCard) publishAppAllocateBatch(ctx context.Context, user *entity.SysUser, batchNo string) error {
|
|
var children []*entity.BatchMain
|
|
columns := dao.BatchMain.Columns()
|
|
if err := dao.BatchMain.Ctx(ctx).Where(columns.SourceBatchNo, batchNo).
|
|
Where(columns.BatchType, consts.BatchTypeAllocate).Where(columns.TenantId, user.TenantId).
|
|
Where(columns.CreatedBy, user.UserId).Scan(&children); err != nil {
|
|
return err
|
|
}
|
|
for _, child := range children {
|
|
if child.BatchAsyncStatus == consts.BatchAsyncStatusPending || child.BatchAsyncStatus == consts.BatchAsyncStatusProcessing {
|
|
if err := s.publishWorkerBatchExecute(ctx, child.BatchNo, consts.BatchTypeAllocate, true); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
return s.publishWorkerBatchExecute(ctx, batchNo, consts.BatchTypeAllocate, true)
|
|
}
|
|
|
|
func appAllocateQueuedResponse(batch *entity.BatchMain) *model.AppCardAllocateBatchModel {
|
|
status := appLifecycleBatchStatus(batch.BatchStatus, batch.BatchAsyncStatus)
|
|
target := &model.AppCardAllocateTargetModel{DeptId: batch.TargetDeptId}
|
|
if batch.BatchInfo != nil {
|
|
target.DeptName = strings.TrimSpace(batch.BatchInfo.Get("targetDeptName").String())
|
|
}
|
|
return &model.AppCardAllocateBatchModel{
|
|
BatchNo: batch.BatchNo, BatchType: consts.BatchTypeAllocate,
|
|
Quantity: batch.TotalCount, TotalCount: batch.TotalCount,
|
|
ValidCount: batch.ValidCount, FailCount: batch.FailCount,
|
|
FailReason: appLifecycleFailReasonForResponse(status, batch.FailReason),
|
|
Status: status, Target: target, Cards: []*model.AppCardModel{}, Details: []*model.AppCardAllocateBatchDetailModel{},
|
|
}
|
|
}
|
|
|
|
func appAllocateExecutionMode(requested int) int {
|
|
if requested == batchmetadata.Single {
|
|
return batchmetadata.Single
|
|
}
|
|
return batchmetadata.Bulk
|
|
}
|
|
|
|
func appValidateAllocateRetry(batch *entity.BatchMain, param *model.AppCardAllocateBatchSubmitParam) error {
|
|
if param.ExecutionMode > 0 && uint(param.ExecutionMode) != batch.ExecutionMode {
|
|
return gerror.New("提交规模与原划拨批次不一致")
|
|
}
|
|
if strings.TrimSpace(param.Remark) != "" && strings.TrimSpace(param.Remark) != batch.BatchInfo.Get("remark").String() {
|
|
return gerror.New("提交备注与原划拨批次不一致")
|
|
}
|
|
if strings.TrimSpace(param.RequestId) != "" && strings.TrimSpace(param.RequestId) != batch.BatchInfo.Get("requestId").String() {
|
|
return gerror.New("请求标识与原划拨批次不一致")
|
|
}
|
|
|
|
if param.TargetDeptId > 0 && param.TargetDeptId != batch.TargetDeptId {
|
|
return gerror.New("提交目标客户与原划拨批次不一致")
|
|
}
|
|
if len(param.CardValues) > 0 {
|
|
var expected []string
|
|
for _, card := range batch.BatchInfo.Get("cards").Array() {
|
|
expected = append(expected, gjson.New(card).Get("iccid").String())
|
|
}
|
|
if !appSameStringSet(appNormalizeAllocateCardValues(param.CardValues), expected) {
|
|
return gerror.New("提交卡片与原划拨批次不一致")
|
|
}
|
|
}
|
|
return nil
|
|
}
|