727 lines
24 KiB
Go
727 lines
24 KiB
Go
// Package gen
|
|
// @Link https://github.com/xiujiecn/xiu-admin
|
|
// @Copyright Copyright (c) 2026 LiXiujie
|
|
// @Author Lxj <xxx@example.com>
|
|
// @License https://github.com/xiujiecn/xiu-admin/blob/master/LICENSE
|
|
// @AutoGenerate Version
|
|
package common
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"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/allocationunits"
|
|
"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/exportcenter"
|
|
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/rabbitmq"
|
|
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/xgorm"
|
|
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/xgorm/handler"
|
|
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/xgorm/hook"
|
|
"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/model/entity"
|
|
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/service"
|
|
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/utility/convert"
|
|
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/utility/excel"
|
|
|
|
"github.com/gogf/gf/v2/database/gdb"
|
|
"github.com/gogf/gf/v2/errors/gerror"
|
|
"github.com/gogf/gf/v2/frame/g"
|
|
"github.com/gogf/gf/v2/os/gctx"
|
|
"github.com/gogf/gf/v2/os/gtime"
|
|
"github.com/gogf/gf/v2/util/gconv"
|
|
)
|
|
|
|
type sCommonBatchMain struct{}
|
|
|
|
const restartPhaseWaitingResume = "waiting_resume"
|
|
|
|
type batchExecuteMessage struct {
|
|
BatchNo string `json:"batchNo"`
|
|
BatchType int `json:"batchType"`
|
|
}
|
|
|
|
var publishBatchExecuteMessage = rabbitmq.Publish
|
|
|
|
func NewCommonBatchMain() *sCommonBatchMain {
|
|
return &sCommonBatchMain{}
|
|
}
|
|
|
|
func (s *sCommonBatchMain) getRequeueBatch(ctx context.Context, batchNo string) (*entity.BatchMain, error) {
|
|
columns := dao.BatchMain.Columns()
|
|
batch := &entity.BatchMain{}
|
|
err := s.Model(ctx).
|
|
Fields(
|
|
columns.Id,
|
|
columns.BatchNo,
|
|
columns.BatchType,
|
|
columns.BatchAsyncStatus,
|
|
columns.RunResult,
|
|
columns.BatchInfo,
|
|
columns.TenantId,
|
|
columns.DeptId,
|
|
columns.CreatedDept,
|
|
columns.CreatedBy,
|
|
).
|
|
Where(columns.BatchNo, batchNo).
|
|
Where(columns.BatchType+" <> ?", consts.BatchTypePrepare).
|
|
Scan(batch)
|
|
if err != nil {
|
|
return nil, gerror.Wrap(err, "查询批次信息失败")
|
|
}
|
|
if batch.Id == 0 {
|
|
return nil, gerror.New("批次不存在或无权限")
|
|
}
|
|
return batch, nil
|
|
}
|
|
|
|
func validateRequeueBatch(batch *entity.BatchMain) error {
|
|
if batch == nil {
|
|
return gerror.New("批次不存在")
|
|
}
|
|
switch batch.BatchAsyncStatus {
|
|
case consts.BatchAsyncStatusPending:
|
|
return nil
|
|
case consts.BatchAsyncStatusProcessing:
|
|
return validateProcessingRequeueBatch(batch)
|
|
case consts.BatchAsyncStatusReview:
|
|
return gerror.New("待核查批次不支持安全重投,请先核对上游执行结果")
|
|
case consts.BatchAsyncStatusFailed, consts.BatchAsyncStatusSuccess, consts.BatchAsyncStatusPartialSuccess:
|
|
return gerror.New("终态批次不支持安全重投")
|
|
default:
|
|
return gerror.Newf("当前批次状态不支持安全重投,status=%d", batch.BatchAsyncStatus)
|
|
}
|
|
}
|
|
|
|
func validateProcessingRequeueBatch(batch *entity.BatchMain) error {
|
|
if batch.BatchType != consts.BatchTypeRestart {
|
|
return gerror.New("处理中批次不支持安全重投,避免重复调用运营商")
|
|
}
|
|
if restartPhase(batch) != restartPhaseWaitingResume {
|
|
return gerror.New("RST 批次当前不在等待复机阶段,不能安全重投")
|
|
}
|
|
resumeAt := restartResumeAt(batch)
|
|
if resumeAt == nil {
|
|
return gerror.New("RST 批次缺少计划复机时间,不能安全重投")
|
|
}
|
|
if resumeAt.After(gtime.Now()) {
|
|
return gerror.Newf("未到计划复机时间,resumeAt=%s", resumeAt.String())
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func restartPhase(batch *entity.BatchMain) string {
|
|
if batch == nil || batch.BatchInfo == nil {
|
|
return ""
|
|
}
|
|
return strings.TrimSpace(batch.BatchInfo.Get("restart.phase").String())
|
|
}
|
|
|
|
func restartResumeAt(batch *entity.BatchMain) *gtime.Time {
|
|
if batch == nil || batch.BatchInfo == nil {
|
|
return nil
|
|
}
|
|
value := strings.TrimSpace(batch.BatchInfo.Get("restart.resumeAt").String())
|
|
if value == "" {
|
|
return nil
|
|
}
|
|
parsed := gtime.NewFromStr(value)
|
|
if parsed == nil || parsed.IsZero() {
|
|
return nil
|
|
}
|
|
return parsed
|
|
}
|
|
|
|
func init() {
|
|
service.RegisterCommonBatchMain(NewCommonBatchMain())
|
|
}
|
|
|
|
// Model 批次主表ORM模型
|
|
func (s *sCommonBatchMain) Model(ctx context.Context, option ...*handler.Option) *gdb.Model {
|
|
if len(option) == 0 {
|
|
option = append(option, &handler.Option{
|
|
FilterTenant: true,
|
|
FilterAuth: true,
|
|
})
|
|
}
|
|
return handler.Model(dao.BatchMain.Ctx(ctx), option...)
|
|
}
|
|
|
|
func (s *sCommonBatchMain) detailModel(ctx context.Context, option ...*handler.Option) *gdb.Model {
|
|
if len(option) == 0 {
|
|
option = append(option, &handler.Option{
|
|
FilterTenant: true,
|
|
FilterAuth: true,
|
|
})
|
|
}
|
|
return handler.Model(dao.BatchDetail.Ctx(ctx), option...)
|
|
}
|
|
|
|
// List 获取批次主表列表
|
|
func (s *sCommonBatchMain) List(ctx context.Context, in *model.BatchMainListParam) (list []*model.BatchMainListModel, totalCount int, err error) {
|
|
mod := s.Model(ctx)
|
|
|
|
// 字段过滤
|
|
mod = mod.Fields(model.BatchMainListModel{})
|
|
|
|
// 查询主键ID
|
|
if in.Id > 0 {
|
|
mod = mod.Where(dao.BatchMain.Columns().Id, in.Id)
|
|
}
|
|
// 查询批次编号
|
|
if in.BatchNo != "" {
|
|
mod = mod.Where(dao.BatchMain.Columns().BatchNo+" LIKE ?", "%"+in.BatchNo+"%")
|
|
}
|
|
// 查询订单编号
|
|
if in.OrderSn != "" {
|
|
mod = mod.Where(dao.BatchMain.Columns().OrderSn+" LIKE ?", "%"+in.OrderSn+"%")
|
|
}
|
|
|
|
// 查询批次类型 枚举
|
|
mod = visibleBatchTypeModel(mod, dao.BatchMain.Columns().BatchType, in.BatchType)
|
|
|
|
// 查询创建时间
|
|
if len(in.CreatedAt) == 2 {
|
|
in.CreatedAt[1] = in.CreatedAt[1].EndOfDay()
|
|
}
|
|
if len(in.CreatedAt) == 2 {
|
|
mod = mod.WhereBetween(dao.BatchMain.Columns().CreatedAt, in.CreatedAt[0], in.CreatedAt[1])
|
|
}
|
|
|
|
// 分页
|
|
mod = mod.Page(in.Page, in.PageSize)
|
|
|
|
// 排序
|
|
mod = mod.OrderDesc(dao.BatchMain.Columns().Id)
|
|
|
|
// 操作人摘要信息
|
|
mod = mod.Hook(hook.MemberSummary)
|
|
|
|
// 查询数据
|
|
if err = mod.ScanAndCount(&list, &totalCount, false); err != nil {
|
|
err = gerror.Wrap(err, "获取批次主表列表失败,请稍后重试!")
|
|
return
|
|
}
|
|
units := make([]*allocationunits.Fields, 0, len(list))
|
|
for _, item := range list {
|
|
if item.BatchType == consts.BatchTypeAllocate {
|
|
fields := allocationunits.Resolve(item.BatchInfo, item.CreatedDept, item.TargetDeptId)
|
|
units = append(units, &fields)
|
|
}
|
|
}
|
|
if err = allocationunits.Fill(ctx, contexts.GetTenantId(ctx), units); err != nil {
|
|
return nil, 0, err
|
|
}
|
|
unitIndex := 0
|
|
for _, item := range list {
|
|
if item.BatchType == consts.BatchTypeAllocate {
|
|
fields := units[unitIndex]
|
|
item.SourceDeptId, item.SourceDeptName = fields.SourceDeptId, fields.SourceDeptName
|
|
item.TargetDeptId, item.TargetDeptName, item.TargetDeptMode = fields.TargetDeptId, fields.TargetDeptName, fields.TargetDeptMode
|
|
unitIndex++
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func (s *sCommonBatchMain) SuccessList(ctx context.Context, in *model.BatchMainSuccessListParam) (list []*model.BatchMainSuccessListModel, totalCount int, err error) {
|
|
if in == nil {
|
|
in = &model.BatchMainSuccessListParam{}
|
|
}
|
|
|
|
columns := dao.BatchMain.Columns()
|
|
mod := s.Model(ctx).
|
|
Fields(
|
|
columns.BatchInfo,
|
|
columns.BatchNo,
|
|
columns.BatchType,
|
|
columns.StartTime+" AS batch_time",
|
|
columns.TotalCount,
|
|
columns.CreatedDept,
|
|
columns.TargetDeptId,
|
|
).
|
|
Where(columns.RunResult, consts.BatchRunResultSuccess).
|
|
WhereGT(columns.TotalCount, 0)
|
|
|
|
var requestedBatchType *int
|
|
if in.BatchType != 0 {
|
|
requestedBatchType = &in.BatchType
|
|
}
|
|
mod = successListBatchTypeModel(mod, columns.BatchType, requestedBatchType)
|
|
|
|
if chanKey := strings.TrimSpace(in.ChanKey); chanKey != "" {
|
|
extract := "JSON_UNQUOTE(JSON_EXTRACT(%s, '%s'))"
|
|
if strings.EqualFold(dao.BatchMain.DB().GetConfig().Type, "sqlite") {
|
|
extract = "JSON_EXTRACT(%s, '%s')"
|
|
}
|
|
nested := fmt.Sprintf(extract, columns.BatchInfo, "$.inboundOptions.chanKey")
|
|
legacy := fmt.Sprintf(extract, columns.BatchInfo, "$.chanKey")
|
|
mod = mod.Where(fmt.Sprintf("CASE WHEN JSON_VALID(%s) THEN COALESCE(NULLIF(TRIM(%s), ''), TRIM(%s)) END = ?", columns.BatchInfo, nested, legacy), chanKey)
|
|
}
|
|
|
|
if in.BatchNo != "" {
|
|
mod = mod.Where(columns.BatchNo+" LIKE ?", "%"+in.BatchNo+"%")
|
|
}
|
|
if len(in.Time) == 2 {
|
|
in.Time[1] = in.Time[1].EndOfDay()
|
|
mod = mod.WhereBetween(columns.StartTime, in.Time[0], in.Time[1])
|
|
}
|
|
|
|
mod = mod.Page(in.Page, in.PageSize).OrderDesc(columns.Id)
|
|
if err = mod.ScanAndCount(&list, &totalCount, false); err != nil {
|
|
return nil, 0, gerror.Wrap(err, "获取成功批次列表失败,请稍后重试")
|
|
}
|
|
if err = fillInboundBatchStockCounts(ctx, list); err != nil {
|
|
return nil, 0, err
|
|
}
|
|
units := make([]*allocationunits.Fields, 0, len(list))
|
|
for _, item := range list {
|
|
if item.BatchType == consts.BatchTypeAllocate {
|
|
item.Fields = allocationunits.Resolve(item.BatchInfo, item.StoredSourceDeptId, item.StoredTargetDeptId)
|
|
units = append(units, &item.Fields)
|
|
}
|
|
}
|
|
if err = allocationunits.Fill(ctx, contexts.GetTenantId(ctx), units); err != nil {
|
|
return nil, 0, err
|
|
}
|
|
return
|
|
}
|
|
|
|
func fillInboundBatchStockCounts(ctx context.Context, list []*model.BatchMainSuccessListModel) error {
|
|
if len(list) == 0 {
|
|
return nil
|
|
}
|
|
batchNos := make([]string, 0, len(list))
|
|
for _, item := range list {
|
|
if item == nil || item.BatchType != consts.BatchTypeInbound || strings.TrimSpace(item.BatchNo) == "" {
|
|
continue
|
|
}
|
|
batchNos = append(batchNos, item.BatchNo)
|
|
}
|
|
if len(batchNos) == 0 {
|
|
return nil
|
|
}
|
|
|
|
type stockCountRow struct {
|
|
BatchNo string `orm:"batch_no"`
|
|
StockCount int `orm:"stock_count"`
|
|
}
|
|
var rows []*stockCountRow
|
|
stockColumns := dao.TSimCardStock.Columns()
|
|
if err := dao.TSimCardStock.Ctx(ctx).
|
|
Fields(stockColumns.BatchNo, "COUNT(1) AS stock_count").
|
|
Where(stockColumns.TenantId, contexts.GetTenantId(ctx)).
|
|
WhereIn(stockColumns.BatchNo, batchNos).
|
|
Group(stockColumns.BatchNo).
|
|
Scan(&rows); err != nil {
|
|
return gerror.Wrap(err, "统计入库批次在库数量失败")
|
|
}
|
|
|
|
stockCountMap := make(map[string]int, len(rows))
|
|
for _, row := range rows {
|
|
if row != nil {
|
|
stockCountMap[row.BatchNo] = row.StockCount
|
|
}
|
|
}
|
|
for _, item := range list {
|
|
if item != nil && item.BatchType == consts.BatchTypeInbound {
|
|
item.StockCount = stockCountMap[item.BatchNo]
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type batchDetailQuery struct {
|
|
BatchNo string
|
|
BatchType *int
|
|
DetailStatus *int
|
|
Iccid string
|
|
Msisdn string
|
|
CurrentStockOnly bool
|
|
Page int
|
|
PageSize int
|
|
}
|
|
|
|
func (s *sCommonBatchMain) ListCards(ctx context.Context, in *model.BatchCardListParam) (list []*model.BatchCardListModel, totalCount int, err error) {
|
|
if in == nil || in.BatchNo == "" {
|
|
return nil, 0, gerror.New("批次号不能为空")
|
|
}
|
|
batch, err := s.getVisibleBatch(ctx, in.BatchNo, in.BatchType)
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
|
|
details, totalCount, err := s.listBatchDetails(ctx, batch, batchDetailQuery{
|
|
DetailStatus: in.DetailStatus,
|
|
BatchNo: in.BatchNo,
|
|
BatchType: in.BatchType,
|
|
Iccid: in.Iccid,
|
|
Msisdn: in.Msisdn,
|
|
CurrentStockOnly: in.CurrentStockOnly,
|
|
Page: in.Page,
|
|
PageSize: in.PageSize,
|
|
})
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
|
|
list = make([]*model.BatchCardListModel, 0, len(details))
|
|
displayRowStart := 0
|
|
if in.Page > 0 && in.PageSize > 0 {
|
|
displayRowStart = (in.Page - 1) * in.PageSize
|
|
}
|
|
units := make([]*allocationunits.Fields, 0, len(details))
|
|
parentUnits := allocationunits.Resolve(batch.BatchInfo, batch.CreatedDept, batch.TargetDeptId)
|
|
for index, detail := range details {
|
|
iccid, msisdn, imsi := batchDetailSnapshotCards(detail)
|
|
item := &model.BatchCardListModel{
|
|
RowNo: displayRowStart + index + 1,
|
|
Iccid: iccid,
|
|
Msisdn: msisdn,
|
|
Imsi: imsi,
|
|
DetailStatus: detail.DetailStatus,
|
|
ErrorReason: detail.ErrorReason,
|
|
}
|
|
if batch.BatchType == consts.BatchTypeAllocate {
|
|
item.Fields = allocationunits.Resolve(detail.Snapshot, parentUnits.SourceDeptId, parentUnits.TargetDeptId)
|
|
if item.SourceDeptName == "" && item.SourceDeptId == parentUnits.SourceDeptId {
|
|
item.SourceDeptName = parentUnits.SourceDeptName
|
|
}
|
|
if item.TargetDeptName == "" && item.TargetDeptId == parentUnits.TargetDeptId {
|
|
item.TargetDeptName = parentUnits.TargetDeptName
|
|
}
|
|
units = append(units, &item.Fields)
|
|
}
|
|
list = append(list, item)
|
|
}
|
|
if err = allocationunits.Fill(ctx, contexts.GetTenantId(ctx), units); err != nil {
|
|
return nil, 0, err
|
|
}
|
|
return
|
|
}
|
|
|
|
// listBatchDetails 获取批次卡清单明细。
|
|
func (s *sCommonBatchMain) listBatchDetails(ctx context.Context, batch *entity.BatchMain, query batchDetailQuery) (list []*entity.BatchDetail, totalCount int, err error) {
|
|
columns := dao.BatchDetail.Columns()
|
|
mod := s.detailModel(ctx).
|
|
Fields(
|
|
columns.Id,
|
|
columns.BatchNo,
|
|
columns.BatchType,
|
|
columns.DetailStatus,
|
|
columns.RowNo,
|
|
columns.ItemId,
|
|
columns.ErrorReason,
|
|
columns.Snapshot,
|
|
columns.TenantId,
|
|
columns.CreatedDept,
|
|
columns.CreatedBy,
|
|
).
|
|
Where(columns.BatchType, batch.BatchType)
|
|
if batch.BatchType == consts.BatchTypeAllocate && (batch.SourceBatchNo == "" || batch.SourceBatchNo == batch.BatchNo) {
|
|
mainColumns := dao.BatchMain.Columns()
|
|
childBatches := s.Model(ctx).Fields(mainColumns.BatchNo).
|
|
Where(mainColumns.BatchType, consts.BatchTypeAllocate).
|
|
Where(mainColumns.SourceBatchNo, batch.BatchNo).
|
|
Where(mainColumns.SourceBatchNo + " <> " + mainColumns.BatchNo)
|
|
mod = mod.Where("("+columns.BatchNo+" = ? OR "+columns.BatchNo+" IN (?))", batch.BatchNo, childBatches)
|
|
} else {
|
|
mod = mod.Where(columns.BatchNo, batch.BatchNo)
|
|
}
|
|
|
|
if query.DetailStatus != nil {
|
|
mod = mod.Where(columns.DetailStatus, *query.DetailStatus)
|
|
}
|
|
if query.Iccid != "" {
|
|
like := "%" + query.Iccid + "%"
|
|
mod = mod.Where(
|
|
"("+columns.ItemId+" LIKE ? OR JSON_UNQUOTE(JSON_EXTRACT("+columns.Snapshot+", '$.ICCID')) LIKE ?)",
|
|
like,
|
|
like,
|
|
)
|
|
}
|
|
if query.Msisdn != "" {
|
|
mod = mod.Where(
|
|
"JSON_UNQUOTE(JSON_EXTRACT("+columns.Snapshot+", '$.MSISDN')) LIKE ?",
|
|
"%"+query.Msisdn+"%",
|
|
)
|
|
}
|
|
if query.CurrentStockOnly {
|
|
mod = onlyCurrentStockDetails(mod)
|
|
}
|
|
|
|
mod = orderBatchCards(mod.Page(query.Page, query.PageSize), batch.BatchType)
|
|
if err = mod.ScanAndCount(&list, &totalCount, false); err != nil {
|
|
return nil, 0, gerror.Wrap(err, "获取批次明细列表失败,请稍后重试")
|
|
}
|
|
return
|
|
}
|
|
|
|
func orderBatchCards(mod *gdb.Model, batchType int) *gdb.Model {
|
|
columns := dao.BatchDetail.Columns()
|
|
if batchType == consts.BatchTypeInbound || batchType == consts.BatchTypeOutbound || batchType == consts.BatchTypeAllocate {
|
|
order := fmt.Sprintf("CASE WHEN %s = %d AND COALESCE(%s, '') NOT LIKE '本批%%整批未执行%%' AND COALESCE(%s, '') NOT LIKE '同批%%本行未执行%%' AND COALESCE(%s, '') NOT LIKE '同批%%整批未入库%%' THEN 0 ELSE 1 END, %s ASC, %s ASC", columns.DetailStatus, consts.BatchDetailStatusFailed, columns.ErrorReason, columns.ErrorReason, columns.ErrorReason, columns.RowNo, columns.Id)
|
|
return mod.Order(gdb.Raw(order))
|
|
}
|
|
return mod.OrderAsc(columns.RowNo).OrderAsc(columns.Id)
|
|
}
|
|
|
|
func onlyCurrentStockDetails(mod *gdb.Model) *gdb.Model {
|
|
detailTable := dao.BatchDetail.Table()
|
|
stockTable := dao.TSimCardStock.Table()
|
|
return mod.Where(fmt.Sprintf(
|
|
"EXISTS (SELECT 1 FROM `%s` stock WHERE stock.`tenant_id` = `%s`.`tenant_id` AND stock.`batch_no` = `%s`.`batch_no` AND stock.`iccid` = COALESCE(NULLIF(JSON_UNQUOTE(JSON_EXTRACT(`%s`.`snapshot`, '$.ICCID')), ''), `%s`.`item_id`))",
|
|
stockTable,
|
|
detailTable,
|
|
detailTable,
|
|
detailTable,
|
|
detailTable,
|
|
))
|
|
}
|
|
|
|
func (s *sCommonBatchMain) getVisibleBatch(ctx context.Context, batchNo string, batchType *int) (*entity.BatchMain, error) {
|
|
if batchNo == "" {
|
|
return nil, gerror.New("批次号不能为空")
|
|
}
|
|
|
|
columns := dao.BatchMain.Columns()
|
|
batch := &entity.BatchMain{}
|
|
mod := s.Model(ctx).
|
|
Fields(
|
|
columns.Id,
|
|
columns.BatchNo,
|
|
columns.BatchType,
|
|
columns.TenantId,
|
|
columns.DeptId,
|
|
columns.BatchInfo,
|
|
columns.TargetDeptId,
|
|
columns.SourceBatchNo,
|
|
columns.CreatedDept,
|
|
columns.CreatedBy,
|
|
).
|
|
Where(columns.BatchNo, batchNo)
|
|
mod = visibleBatchTypeModel(mod, columns.BatchType, batchType)
|
|
if err := mod.Scan(batch); err != nil {
|
|
return nil, gerror.Wrap(err, "获取批次信息失败,请稍后重试")
|
|
}
|
|
if batch.Id == 0 {
|
|
return nil, gerror.New("批次不存在")
|
|
}
|
|
if !visibleBatchTypeMatches(batch.BatchType, batchType) {
|
|
return nil, gerror.New("批次类型不匹配")
|
|
}
|
|
return batch, nil
|
|
}
|
|
|
|
func batchDetailSnapshotCards(detail *entity.BatchDetail) (iccid, msisdn, imsi string) {
|
|
if detail == nil {
|
|
return
|
|
}
|
|
if detail.Snapshot != nil {
|
|
iccid = strings.TrimSpace(detail.Snapshot.Get("ICCID").String())
|
|
msisdn = strings.TrimSpace(detail.Snapshot.Get("MSISDN").String())
|
|
imsi = strings.TrimSpace(detail.Snapshot.Get("IMSI").String())
|
|
}
|
|
if iccid == "" {
|
|
iccid = detail.ItemId
|
|
}
|
|
return
|
|
}
|
|
|
|
// ExportWarehousing 导出入库批次列表(异步:提交任务后到下载中心取文件)。
|
|
// 数据来源与入库管理页保持一致:成功批次列表(batch_type=入库)。
|
|
func (s *sCommonBatchMain) ExportWarehousing(ctx context.Context, in *model.BatchMainSuccessListParam) (err error) {
|
|
if in == nil {
|
|
in = &model.BatchMainSuccessListParam{}
|
|
}
|
|
// 与页面 WarehousingList 一致:固定只导入库批次,不接受调用方传其它类型
|
|
in.BatchType = consts.BatchTypeInbound
|
|
|
|
_, err = exportcenter.Submit(ctx, &exportcenter.Request{
|
|
BizKey: "batchMainWarehousing",
|
|
BizName: "入库管理",
|
|
PermCode: "cpm:iot:tSimCardStock:export",
|
|
QueryParam: in,
|
|
Tags: func() ([]string, error) {
|
|
return convert.GetEntityDescTags(model.BatchMainSuccessListModel{})
|
|
},
|
|
Fetch: func(ctx context.Context, page, pageSize int) (rows []interface{}, total int, err error) {
|
|
query := *in
|
|
query.Page = page
|
|
query.PageSize = pageSize
|
|
list, count, err := s.SuccessList(ctx, &query)
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
rows = make([]interface{}, 0, len(list))
|
|
for _, item := range list {
|
|
rows = append(rows, item)
|
|
}
|
|
return rows, count, nil
|
|
},
|
|
})
|
|
return err
|
|
}
|
|
|
|
// Export 导出批次主表
|
|
func (s *sCommonBatchMain) Export(ctx context.Context, in *model.BatchMainListParam) (err error) {
|
|
list, totalCount, err := s.List(ctx, in)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
// 字段的排序是依据tags的字段顺序,如果你不想使用默认的排序方式,可以直接定义 tags = []string{"字段名称", "字段名称2", ...}
|
|
tags, err := convert.GetEntityDescTags(model.BatchMainExportModel{})
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
var (
|
|
fileName = "导出批次主表-" + gctx.CtxId(ctx)
|
|
sheetName = fmt.Sprintf("索引条件共%v行,共%v页,当前导出是第%v页,本页共%v行", totalCount, (totalCount+in.PageSize-1)/in.PageSize, in.Page, len(list))
|
|
exports []model.BatchMainExportModel
|
|
)
|
|
|
|
if err = gconv.Scan(list, &exports); err != nil {
|
|
return
|
|
}
|
|
|
|
err = excel.ExportByStructs(ctx, tags, exports, fileName, sheetName)
|
|
return
|
|
}
|
|
|
|
// Edit 修改/新增批次主表
|
|
func (s *sCommonBatchMain) Edit(ctx context.Context, in *model.BatchMainEditParam) (err error) {
|
|
if in == nil {
|
|
return gerror.New("批次参数不能为空")
|
|
}
|
|
if in.Id <= 0 {
|
|
if in.ExecutionMode == nil {
|
|
return gerror.New("新建批次必须指定单卡或批量操作规模")
|
|
}
|
|
if err = batchmetadata.ValidateCreation(batchmetadata.Console, *in.ExecutionMode); err != nil {
|
|
return err
|
|
}
|
|
if *in.ExecutionMode == batchmetadata.Single && (in.TotalCount == nil || *in.TotalCount != 1) {
|
|
return gerror.New("单卡批次总数量必须为1")
|
|
}
|
|
}
|
|
data := do.BatchMain{}
|
|
if err = gconv.Struct(in, &data); err != nil {
|
|
return gerror.Wrap(err, "转换批次参数失败")
|
|
}
|
|
data.CreatedAt = nil
|
|
data.UpdatedAt = nil
|
|
// 验证'BatchNo'唯一
|
|
if err = xgorm.IsUnique(ctx, &dao.BatchMain, g.Map{dao.BatchMain.Columns().BatchNo: in.BatchNo}, "批次编号已存在", in.Id); err != nil {
|
|
return
|
|
}
|
|
return g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) (err error) {
|
|
|
|
// 修改
|
|
if in.Id > 0 {
|
|
data.UpdatedBy = contexts.GetUserId(ctx)
|
|
data.SourceChannel = nil
|
|
data.ExecutionMode = nil
|
|
if _, err = s.Model(ctx).TX(tx).
|
|
Fields(model.BatchMainUpdateFields{}).
|
|
WherePri(in.Id).Data(data).Update(); err != nil {
|
|
err = gerror.Wrap(err, "修改批次主表失败,请稍后重试!")
|
|
}
|
|
return
|
|
}
|
|
|
|
// 新增
|
|
data.Id = nil
|
|
data.TenantId = contexts.GetTenantId(ctx)
|
|
data.CreatedDept = contexts.GetDeptId(ctx)
|
|
data.CreatedBy = contexts.GetUserId(ctx)
|
|
data.SourceChannel = batchmetadata.Console
|
|
data.ExecutionMode = *in.ExecutionMode
|
|
if _, err = s.Model(ctx, &handler.Option{FilterAuth: false}).TX(tx).
|
|
Fields(model.BatchMainInsertFields{}).
|
|
Data(data).Insert(); err != nil {
|
|
err = gerror.Wrap(err, "新增批次主表失败,请稍后重试!")
|
|
}
|
|
return
|
|
})
|
|
}
|
|
|
|
// Delete 删除批次主表
|
|
func (s *sCommonBatchMain) Delete(ctx context.Context, in *model.BatchMainDeleteParam) (err error) {
|
|
|
|
if _, err = s.Model(ctx).WherePri(in.Id).Unscoped().Delete(); err != nil {
|
|
err = gerror.Wrap(err, "删除批次主表失败,请稍后重试!")
|
|
return
|
|
}
|
|
return
|
|
}
|
|
|
|
// View 获取批次主表指定信息
|
|
func (s *sCommonBatchMain) Requeue(ctx context.Context, in *model.BatchMainRequeueParam) (res *model.BatchMainRequeueModel, err error) {
|
|
if in == nil {
|
|
in = &model.BatchMainRequeueParam{}
|
|
}
|
|
batchNo := strings.TrimSpace(in.BatchNo)
|
|
reason := strings.TrimSpace(in.Reason)
|
|
if batchNo == "" {
|
|
return nil, gerror.New("批次编号不能为空")
|
|
}
|
|
if reason == "" {
|
|
return nil, gerror.New("重投原因不能为空")
|
|
}
|
|
|
|
batch, err := s.getRequeueBatch(ctx, batchNo)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err = validateRequeueBatch(batch); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
message := batchExecuteMessage{BatchNo: batch.BatchNo, BatchType: batch.BatchType}
|
|
if err = publishBatchExecuteMessage(ctx, consts.QueueBatchExecute, message); err != nil {
|
|
g.Log().Errorf(ctx, "batch requeue publish failed, batchNo=%s, batchType=%d, tenantId=%s, userId=%d, reason=%s, err=%+v",
|
|
batch.BatchNo, batch.BatchType, contexts.GetTenantId(ctx), contexts.GetUserId(ctx), reason, err)
|
|
return nil, gerror.Wrap(err, "发布批次重投消息失败")
|
|
}
|
|
g.Log().Infof(ctx, "batch requeue published, batchNo=%s, batchType=%d, status=%d, runResult=%d, tenantId=%s, userId=%d, reason=%s",
|
|
batch.BatchNo, batch.BatchType, batch.BatchAsyncStatus, batch.RunResult, contexts.GetTenantId(ctx), contexts.GetUserId(ctx), reason)
|
|
|
|
return &model.BatchMainRequeueModel{
|
|
BatchNo: batch.BatchNo,
|
|
BatchType: batch.BatchType,
|
|
BatchAsyncStatus: batch.BatchAsyncStatus,
|
|
RunResult: batch.RunResult,
|
|
Requeued: true,
|
|
}, nil
|
|
}
|
|
|
|
func (s *sCommonBatchMain) View(ctx context.Context, in *model.BatchMainViewParam) (res *model.BatchMainViewModel, err error) {
|
|
mod := visibleBatchTypeModel(
|
|
s.Model(ctx).WherePri(in.Id),
|
|
dao.BatchMain.Columns().BatchType,
|
|
nil,
|
|
)
|
|
if err = mod.Hook(hook.MemberSummary).Scan(&res); err != nil {
|
|
err = gerror.Wrap(err, "获取批次主表信息,请稍后重试!")
|
|
return
|
|
}
|
|
if res == nil || res.Id == 0 {
|
|
return nil, gerror.New("批次不存在")
|
|
}
|
|
if res.BatchType == consts.BatchTypeAllocate {
|
|
fields := allocationunits.Resolve(res.BatchInfo, res.CreatedDept, res.TargetDeptId)
|
|
if err = allocationunits.Fill(ctx, contexts.GetTenantId(ctx), []*allocationunits.Fields{&fields}); err != nil {
|
|
return nil, err
|
|
}
|
|
res.SourceDeptId, res.SourceDeptName = fields.SourceDeptId, fields.SourceDeptName
|
|
res.TargetDeptId, res.TargetDeptName, res.TargetDeptMode = fields.TargetDeptId, fields.TargetDeptName, fields.TargetDeptMode
|
|
}
|
|
return
|
|
}
|