chore: 并入 iotlcm-admin-svr 历史到 admin-svr/(原提交 49818c6)
This commit is contained in:
@@ -0,0 +1,764 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"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/contexts"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/rabbitmq"
|
||||
"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/entity"
|
||||
"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/os/gtime"
|
||||
)
|
||||
|
||||
type sCommonBatchExecutionLog struct{}
|
||||
|
||||
type batchExecutionLogSummaryBatch struct {
|
||||
CreatedDept int64 `orm:"created_dept"`
|
||||
TargetDeptId int64 `orm:"target_dept_id"`
|
||||
SourceChannel string `orm:"source_channel"`
|
||||
ExecutionMode int `orm:"execution_mode"`
|
||||
Id int64 `orm:"id"`
|
||||
BatchNo string `orm:"batch_no"`
|
||||
BatchType int `orm:"batch_type"`
|
||||
SourceBatchNo string `orm:"source_batch_no"`
|
||||
SourceType *int `orm:"source_type"`
|
||||
BatchAsyncStatus int `orm:"batch_async_status"`
|
||||
RunResult int `orm:"run_result"`
|
||||
FailReason string `orm:"fail_reason"`
|
||||
TotalCount int `orm:"total_count"`
|
||||
SourceOssId int64 `orm:"source_oss_id"`
|
||||
ResultOssId int64 `orm:"result_oss_id"`
|
||||
BatchInfo *gjson.Json `orm:"batch_info"`
|
||||
StartTime *gtime.Time `orm:"start_time"`
|
||||
EndTime *gtime.Time `orm:"end_time"`
|
||||
}
|
||||
|
||||
func NewCommonBatchExecutionLog() *sCommonBatchExecutionLog {
|
||||
return &sCommonBatchExecutionLog{}
|
||||
}
|
||||
|
||||
func init() {
|
||||
service.RegisterCommonBatchExecutionLog(NewCommonBatchExecutionLog())
|
||||
}
|
||||
|
||||
func (s *sCommonBatchExecutionLog) batchModel(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 *sCommonBatchExecutionLog) 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...)
|
||||
}
|
||||
|
||||
func (s *sCommonBatchExecutionLog) downloadModel(ctx context.Context, option ...*handler.Option) *gdb.Model {
|
||||
if len(option) == 0 {
|
||||
option = append(option, &handler.Option{
|
||||
FilterTenant: true,
|
||||
FilterAuth: false,
|
||||
})
|
||||
}
|
||||
return handler.Model(dao.TResourceBatchDownload.Ctx(ctx), option...)
|
||||
}
|
||||
|
||||
func (s *sCommonBatchExecutionLog) ossModel(ctx context.Context, option ...*handler.Option) *gdb.Model {
|
||||
if len(option) == 0 {
|
||||
option = append(option, &handler.Option{
|
||||
FilterTenant: true,
|
||||
FilterAuth: false,
|
||||
})
|
||||
}
|
||||
return handler.Model(dao.SysOss.Ctx(ctx), option...)
|
||||
}
|
||||
|
||||
// exportTaskModel 导出任务表;导出批次的结果文件只登记在这里。
|
||||
func (s *sCommonBatchExecutionLog) exportTaskModel(ctx context.Context, option ...*handler.Option) *gdb.Model {
|
||||
if len(option) == 0 {
|
||||
option = append(option, &handler.Option{
|
||||
FilterTenant: true,
|
||||
FilterAuth: false,
|
||||
})
|
||||
}
|
||||
return handler.Model(dao.TExportTask.Ctx(ctx), option...)
|
||||
}
|
||||
|
||||
func (s *sCommonBatchExecutionLog) List(ctx context.Context, in *model.BatchExecutionLogListParam) (list []*model.BatchExecutionLogListModel, totalCount int, err error) {
|
||||
mod := s.batchModel(ctx).
|
||||
Fields(model.BatchExecutionLogListModel{})
|
||||
|
||||
if in.BatchNo != "" {
|
||||
mod = mod.Where(dao.BatchMain.Columns().BatchNo+" LIKE ?", "%"+in.BatchNo+"%")
|
||||
}
|
||||
if keyword := strings.TrimSpace(in.Keyword); keyword != "" {
|
||||
like := "%" + keyword + "%"
|
||||
mod = mod.Where(
|
||||
"("+dao.BatchMain.Columns().BatchNo+" LIKE ? OR "+dao.BatchMain.Columns().OrderSn+" LIKE ? OR "+dao.BatchMain.Columns().FailReason+" LIKE ?)",
|
||||
like,
|
||||
like,
|
||||
like,
|
||||
)
|
||||
}
|
||||
if in.BatchAsyncStatus != nil {
|
||||
mod = mod.Where(dao.BatchMain.Columns().BatchAsyncStatus, *in.BatchAsyncStatus)
|
||||
}
|
||||
if in.SourceType != nil {
|
||||
mod = mod.Where(dao.BatchMain.Columns().SourceType, *in.SourceType)
|
||||
}
|
||||
if in.SourceChannel != "" {
|
||||
mod = mod.Where(dao.BatchMain.Columns().SourceChannel, in.SourceChannel)
|
||||
}
|
||||
if in.ExecutionMode != nil {
|
||||
mod = mod.Where(dao.BatchMain.Columns().ExecutionMode, *in.ExecutionMode)
|
||||
}
|
||||
mod = mod.Where(
|
||||
fmt.Sprintf("NOT (%s = ? AND COALESCE(%s, '') <> '' AND %s <> %s)", dao.BatchMain.Columns().BatchType, dao.BatchMain.Columns().SourceBatchNo, dao.BatchMain.Columns().SourceBatchNo, dao.BatchMain.Columns().BatchNo),
|
||||
consts.BatchTypeAllocate,
|
||||
)
|
||||
mod = executionLogBatchTypeModel(mod, dao.BatchMain.Columns().BatchType, dao.BatchMain.Columns().BatchInfo, in.BatchType)
|
||||
if len(in.CreatedAt) == 2 {
|
||||
in.CreatedAt[1] = in.CreatedAt[1].EndOfDay()
|
||||
mod = mod.WhereBetween(dao.BatchMain.Columns().CreatedAt, in.CreatedAt[0], in.CreatedAt[1])
|
||||
}
|
||||
|
||||
mod = mod.Page(in.Page, in.PageSize).
|
||||
OrderDesc(dao.BatchMain.Columns().Id).
|
||||
Hook(hook.MemberSummary)
|
||||
|
||||
if err = mod.ScanAndCount(&list, &totalCount, false); err != nil {
|
||||
err = gerror.Wrap(err, "获取执行日志列表失败,请稍后重试!")
|
||||
return
|
||||
}
|
||||
fields := []*allocationunits.Fields{}
|
||||
for _, item := range list {
|
||||
item.UnknownCount = batchExecutionLogUnknownCount(item.UnitInfo)
|
||||
item.RestartPhase, item.RestartResumeAt = batchRestartPhase(item.UnitInfo)
|
||||
if item.BatchType == consts.BatchTypeAllocate {
|
||||
item.Fields = allocationunits.Resolve(item.UnitInfo, item.CreatedDept, item.StoredTargetDeptId)
|
||||
fields = append(fields, &item.Fields)
|
||||
}
|
||||
}
|
||||
if err = allocationunits.Fill(ctx, contexts.GetTenantId(ctx), fields); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (s *sCommonBatchExecutionLog) Summary(ctx context.Context, in *model.BatchExecutionLogDetailParam) (res *model.BatchExecutionLogDetailModel, err error) {
|
||||
if in == nil || in.BatchNo == "" {
|
||||
return nil, gerror.New("批次号不能为空")
|
||||
}
|
||||
|
||||
batch := &batchExecutionLogSummaryBatch{}
|
||||
mod := executionLogBatchTypeModel(
|
||||
s.batchModel(ctx).Where(dao.BatchMain.Columns().BatchNo, in.BatchNo),
|
||||
dao.BatchMain.Columns().BatchType,
|
||||
dao.BatchMain.Columns().BatchInfo,
|
||||
nil,
|
||||
)
|
||||
if err = mod.Scan(batch); err != nil {
|
||||
return nil, gerror.Wrap(err, "获取执行日志详情失败,请稍后重试!")
|
||||
}
|
||||
if batch.Id == 0 {
|
||||
return nil, gerror.New("执行日志不存在")
|
||||
}
|
||||
if isAllocateChildBatch(batch.BatchType, batch.BatchNo, batch.SourceBatchNo) {
|
||||
return nil, gerror.New("执行日志不存在")
|
||||
}
|
||||
|
||||
res = &model.BatchExecutionLogDetailModel{
|
||||
SourceChannel: batch.SourceChannel,
|
||||
ExecutionMode: batch.ExecutionMode,
|
||||
Id: batch.Id,
|
||||
BatchNo: batch.BatchNo,
|
||||
BatchType: batch.BatchType,
|
||||
SourceType: batch.SourceType,
|
||||
BatchAsyncStatus: batch.BatchAsyncStatus,
|
||||
RunResult: batch.RunResult,
|
||||
FailReason: batch.FailReason,
|
||||
TotalCount: batch.TotalCount,
|
||||
UnknownCount: batchExecutionLogUnknownCount(batch.BatchInfo),
|
||||
StartTime: batch.StartTime,
|
||||
EndTime: batch.EndTime,
|
||||
}
|
||||
res.RestartPhase, res.RestartResumeAt = batchRestartPhase(batch.BatchInfo)
|
||||
|
||||
if batch.BatchType == consts.BatchTypeAllocate {
|
||||
res.Fields = allocationunits.Resolve(batch.BatchInfo, batch.CreatedDept, batch.TargetDeptId)
|
||||
if err := allocationunits.Fill(ctx, contexts.GetTenantId(ctx), []*allocationunits.Fields{&res.Fields}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if sourceFile, err := s.getSourceFile(ctx, batch.SourceOssId, batch.BatchInfo); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
res.SourceFile = sourceFile
|
||||
}
|
||||
|
||||
// 导出任务的成品文件落在 t_export_task,不写 t_resource_batch_download,须分开取。
|
||||
var resultFile *model.BatchExecutionLogResultFileModel
|
||||
if batch.BatchType == consts.BatchTypeExport {
|
||||
resultFile, err = s.getExportResultFile(ctx, batch.BatchNo, batch.ResultOssId)
|
||||
} else {
|
||||
resultFile, err = s.getResultFile(ctx, batch.BatchNo, batch.ResultOssId, batch.BatchInfo)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res.ResultFile = resultFile
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (s *sCommonBatchExecutionLog) DetailList(ctx context.Context, in *model.BatchDetailListParam) (list []*model.BatchDetailListModel, totalCount int, err error) {
|
||||
if in == nil || in.BatchNo == "" {
|
||||
return nil, 0, gerror.New("批次号不能为空")
|
||||
}
|
||||
|
||||
batch, err := s.getDetailBatch(ctx, in.BatchNo, in.BatchType)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
columns := dao.BatchDetail.Columns()
|
||||
detailBatchType := batch.BatchType
|
||||
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, detailBatchType)
|
||||
if isAllocateParentBatch(batch) {
|
||||
childBatches := s.batchModel(ctx).
|
||||
Fields(dao.BatchMain.Columns().BatchNo).
|
||||
Where(dao.BatchMain.Columns().BatchType, consts.BatchTypeAllocate).
|
||||
Where(dao.BatchMain.Columns().SourceBatchNo, batch.BatchNo).
|
||||
Where(dao.BatchMain.Columns().SourceBatchNo + " <> " + dao.BatchMain.Columns().BatchNo)
|
||||
mod = mod.Where("("+columns.BatchNo+" = ? OR "+columns.BatchNo+" IN (?))", batch.BatchNo, childBatches)
|
||||
} else {
|
||||
mod = mod.Where(columns.BatchNo, batch.BatchNo)
|
||||
}
|
||||
|
||||
if in.DetailStatus != nil {
|
||||
mod = mod.Where(columns.DetailStatus, *in.DetailStatus)
|
||||
}
|
||||
if in.Iccid != "" {
|
||||
mod = mod.Where(columns.ItemId+" LIKE ?", "%"+in.Iccid+"%")
|
||||
}
|
||||
if keyword := strings.TrimSpace(in.Keyword); keyword != "" {
|
||||
like := "%" + keyword + "%"
|
||||
mod = mod.Where("("+columns.ItemId+" LIKE ? OR "+columns.ErrorReason+" LIKE ?)", like, like)
|
||||
}
|
||||
|
||||
var details []*entity.BatchDetail
|
||||
mod = orderBatchCards(mod.Page(in.Page, in.PageSize), batch.BatchType)
|
||||
if err = mod.ScanAndCount(&details, &totalCount, false); err != nil {
|
||||
return nil, 0, gerror.Wrap(err, "获取批次执行明细列表失败,请稍后重试")
|
||||
}
|
||||
|
||||
list = make([]*model.BatchDetailListModel, 0, len(details))
|
||||
displayRowStart := 0
|
||||
if in.Page > 0 && in.PageSize > 0 {
|
||||
displayRowStart = (in.Page - 1) * in.PageSize
|
||||
}
|
||||
for index, detail := range details {
|
||||
iccid, msisdn, imsi := batchExecutionLogDetailSnapshotCards(detail)
|
||||
item := &model.BatchDetailListModel{
|
||||
RowNo: displayRowStart + index + 1,
|
||||
Iccid: iccid,
|
||||
Msisdn: msisdn,
|
||||
Imsi: imsi,
|
||||
Imei: batchExecutionLogSnapshotValue(detail, "IMEI", "imei", "rawFields.IMEI"),
|
||||
ChanKey: batchExecutionLogSnapshotValue(detail, "chanKey", "chan_key", "rawFields.chanKey", "rawFields.chan_key", "rawFields.通道编号", "rawFields.通道编码", "rawFields.通道编号*"),
|
||||
CardType: batchExecutionLogSnapshotValue(detail, "cardType", "rawFields.cardType", "rawFields.卡类型"),
|
||||
UpPkgCode: batchExecutionLogSnapshotValue(detail, "upPkgCode", "rawFields.upPkgCode", "rawFields.上游套餐编码", "rawFields.上游套餐"),
|
||||
SuppliersCode: batchExecutionLogSnapshotValue(detail, "suppliersCode", "rawFields.suppliersCode", "rawFields.供应商编码", "rawFields.供应商"),
|
||||
UpstreamActivationTime: batchExecutionLogSnapshotValue(detail, "upstreamActivationTime", "rawFields.upstreamActivationTime", "rawFields.上游激活时间", "rawFields.上游给出激活时间"),
|
||||
InsertToPlugEnabled: batchExecutionLogSnapshotValue(detail, "insertToPlugEnabled", "rawFields.insertToPlugEnabled", "rawFields.是否贴片转插拔"),
|
||||
CarrierTrafficCutoffEnabled: batchExecutionLogSnapshotValue(detail, "carrierTrafficCutoffEnabled", "rawFields.carrierTrafficCutoffEnabled", "rawFields.是否运营商达量断网"),
|
||||
AgreementExpiryDate: batchExecutionLogSnapshotValue(detail, "agreementExpiryDate", "rawFields.agreementExpiryDate", "rawFields.协议到期时间", "rawFields.协议期到期时间"),
|
||||
HasTestPeriod: batchExecutionLogSnapshotValue(detail, "hasTestPeriod", "rawFields.hasTestPeriod", "rawFields.是否有测试期"),
|
||||
TestPeriodDays: batchExecutionLogSnapshotValue(detail, "testPeriodDays", "rawFields.testPeriodDays", "rawFields.测试期天数"),
|
||||
TestPeriodLatestExpiryDate: batchExecutionLogSnapshotValue(detail, "testPeriodLatestExpiryDate", "rawFields.testPeriodLatestExpiryDate", "rawFields.测试期最晚过期日期"),
|
||||
HasSilentPeriod: batchExecutionLogSnapshotValue(detail, "hasSilentPeriod", "rawFields.hasSilentPeriod", "rawFields.是否有沉默期"),
|
||||
SilentPeriodDays: batchExecutionLogSnapshotValue(detail, "silentPeriodDays", "rawFields.silentPeriodDays", "rawFields.沉默期天数"),
|
||||
SilentPeriodLatestExpiryDate: batchExecutionLogSnapshotValue(detail, "silentPeriodLatestExpiryDate", "rawFields.silentPeriodLatestExpiryDate", "rawFields.沉默期最晚过期日期"),
|
||||
DetailStatus: detail.DetailStatus,
|
||||
}
|
||||
if batch.BatchType == consts.BatchTypeAllocate {
|
||||
parentUnits := allocationunits.Resolve(batch.BatchInfo, batch.CreatedDept, batch.TargetDeptId)
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
if detail.ErrorReason != "" {
|
||||
item.Error = &model.BatchDetailErrorModel{
|
||||
Reason: detail.ErrorReason,
|
||||
}
|
||||
}
|
||||
list = append(list, item)
|
||||
}
|
||||
fields := []*allocationunits.Fields{}
|
||||
for _, item := range list {
|
||||
if batch.BatchType == consts.BatchTypeAllocate {
|
||||
fields = append(fields, &item.Fields)
|
||||
}
|
||||
}
|
||||
if err = allocationunits.Fill(ctx, contexts.GetTenantId(ctx), fields); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// dataReviewQueryMessage 通知 Worker 对既有 operationId 做一次只读补查。
|
||||
type dataReviewQueryMessage struct {
|
||||
BatchNo string `json:"batchNo"`
|
||||
RequestedBy string `json:"requestedBy,omitempty"`
|
||||
}
|
||||
|
||||
var publishDataReviewQuery = rabbitmq.Publish
|
||||
|
||||
// SyncReview 手动同步待核查批次的上游执行结果。
|
||||
// 只重查既有受理号(jobId),不重发办理请求、不清零自动补查计数;
|
||||
// 上游仍无结论的明细继续保持待核查。
|
||||
func (s *sCommonBatchExecutionLog) SyncReview(ctx context.Context, in *model.BatchExecutionLogSyncReviewParam) (res *model.BatchExecutionLogSyncReviewModel, err error) {
|
||||
if in == nil {
|
||||
in = &model.BatchExecutionLogSyncReviewParam{}
|
||||
}
|
||||
batchNo := strings.TrimSpace(in.BatchNo)
|
||||
reason := strings.TrimSpace(in.Reason)
|
||||
if batchNo == "" {
|
||||
return nil, gerror.New("批次编号不能为空")
|
||||
}
|
||||
|
||||
columns := dao.BatchMain.Columns()
|
||||
batch := &entity.BatchMain{}
|
||||
// 沿用执行日志列表的可见性口径(部门权限过滤)。
|
||||
if err = s.batchModel(ctx).
|
||||
Fields(columns.Id, columns.BatchNo, columns.BatchType, columns.BatchAsyncStatus, columns.RunResult, columns.BatchInfo).
|
||||
Where(columns.BatchNo, batchNo).
|
||||
Scan(batch); err != nil {
|
||||
return nil, gerror.Wrap(err, "查询批次信息失败")
|
||||
}
|
||||
if batch.Id == 0 {
|
||||
return nil, gerror.New("批次不存在或无权限")
|
||||
}
|
||||
if batch.BatchAsyncStatus != consts.BatchAsyncStatusReview {
|
||||
return nil, gerror.New("仅待核查批次支持手动同步上游结果")
|
||||
}
|
||||
unknownCount := batchExecutionLogUnknownCount(batch.BatchInfo)
|
||||
if unknownCount <= 0 {
|
||||
return nil, gerror.New("该批次没有待核查明细")
|
||||
}
|
||||
|
||||
message := dataReviewQueryMessage{BatchNo: batch.BatchNo, RequestedBy: contexts.GetUserName(ctx)}
|
||||
if err = publishDataReviewQuery(ctx, consts.QueueDataReviewQuery, message); err != nil {
|
||||
g.Log().Errorf(ctx, "手动同步上游结果消息发布失败, batchNo=%s, tenantId=%s, userId=%d, reason=%s, err=%+v",
|
||||
batch.BatchNo, contexts.GetTenantId(ctx), contexts.GetUserId(ctx), reason, err)
|
||||
return nil, gerror.Wrap(err, "发布上游结果同步消息失败")
|
||||
}
|
||||
g.Log().Infof(ctx, "手动同步上游结果消息已发布, batchNo=%s, batchType=%d, unknownCount=%d, tenantId=%s, userId=%d, reason=%s",
|
||||
batch.BatchNo, batch.BatchType, unknownCount, contexts.GetTenantId(ctx), contexts.GetUserId(ctx), reason)
|
||||
|
||||
return &model.BatchExecutionLogSyncReviewModel{
|
||||
BatchNo: batch.BatchNo,
|
||||
BatchType: batch.BatchType,
|
||||
BatchAsyncStatus: batch.BatchAsyncStatus,
|
||||
UnknownCount: unknownCount,
|
||||
Queued: true,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *sCommonBatchExecutionLog) getDetailBatch(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.batchModel(ctx).
|
||||
Fields(
|
||||
columns.Id,
|
||||
columns.BatchNo,
|
||||
columns.BatchType,
|
||||
columns.SourceType,
|
||||
columns.SourceBatchNo,
|
||||
columns.BatchInfo,
|
||||
columns.CreatedDept, columns.TargetDeptId,
|
||||
).
|
||||
Where(columns.BatchNo, batchNo)
|
||||
mod = executionLogBatchTypeModel(mod, columns.BatchType, columns.BatchInfo, batchType)
|
||||
if err := mod.Scan(batch); err != nil {
|
||||
return nil, gerror.Wrap(err, "获取批次信息失败,请稍后重试")
|
||||
}
|
||||
if batch.Id == 0 {
|
||||
return nil, gerror.New("批次不存在")
|
||||
}
|
||||
if isAllocateChildBatch(batch.BatchType, batch.BatchNo, batch.SourceBatchNo) {
|
||||
return nil, gerror.New("批次不存在")
|
||||
}
|
||||
if !executionLogBatchTypeMatches(batch.BatchType, batch.BatchInfo, batchType) {
|
||||
return nil, gerror.New("批次类型不匹配")
|
||||
}
|
||||
return batch, nil
|
||||
}
|
||||
|
||||
func executionLogBatchTypeModel(mod *gdb.Model, batchTypeColumn, batchInfoColumn string, requested *int) *gdb.Model {
|
||||
if requested == nil {
|
||||
return mod
|
||||
}
|
||||
if *requested == consts.BatchTypePrepare {
|
||||
return mod.Where(batchTypeColumn, consts.BatchTypePrepare)
|
||||
}
|
||||
if *requested == consts.BatchTypeAllocate {
|
||||
return mod.Where(
|
||||
fmt.Sprintf("(%s = ? OR (%s = ? AND JSON_UNQUOTE(JSON_EXTRACT(%s, '$.targetBatchType')) = ?))", batchTypeColumn, batchTypeColumn, batchInfoColumn),
|
||||
consts.BatchTypeAllocate,
|
||||
consts.BatchTypePrepare,
|
||||
strconv.Itoa(*requested),
|
||||
)
|
||||
}
|
||||
return mod.Where(
|
||||
fmt.Sprintf("(%s = ? OR (%s = ? AND JSON_UNQUOTE(JSON_EXTRACT(%s, '$.targetBatchType')) = ?))", batchTypeColumn, batchTypeColumn, batchInfoColumn),
|
||||
*requested,
|
||||
consts.BatchTypePrepare,
|
||||
strconv.Itoa(*requested),
|
||||
)
|
||||
}
|
||||
|
||||
func executionLogBatchTypeMatches(actual int, info *gjson.Json, requested *int) bool {
|
||||
if requested == nil {
|
||||
return true
|
||||
}
|
||||
if actual == *requested {
|
||||
return true
|
||||
}
|
||||
return actual == consts.BatchTypePrepare && info != nil && info.Get("targetBatchType").Int() == *requested
|
||||
}
|
||||
|
||||
func isAllocateParentBatch(batch *entity.BatchMain) bool {
|
||||
if batch == nil || batch.BatchType != consts.BatchTypeAllocate || isAllocateChildBatch(batch.BatchType, batch.BatchNo, batch.SourceBatchNo) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func isAllocateChildBatch(batchType int, batchNo, sourceBatchNo string) bool {
|
||||
return batchType == consts.BatchTypeAllocate &&
|
||||
strings.TrimSpace(sourceBatchNo) != "" &&
|
||||
strings.TrimSpace(sourceBatchNo) != strings.TrimSpace(batchNo)
|
||||
}
|
||||
|
||||
func batchExecutionLogDetailSnapshotCards(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 = strings.TrimSpace(detail.Snapshot.Get("iccid").String())
|
||||
}
|
||||
if msisdn == "" {
|
||||
msisdn = strings.TrimSpace(detail.Snapshot.Get("msisdn").String())
|
||||
}
|
||||
if imsi == "" {
|
||||
imsi = strings.TrimSpace(detail.Snapshot.Get("imsi").String())
|
||||
}
|
||||
}
|
||||
if iccid == "" {
|
||||
iccid = detail.ItemId
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func batchExecutionLogSnapshotValue(detail *entity.BatchDetail, paths ...string) string {
|
||||
if detail == nil || detail.Snapshot == nil {
|
||||
return ""
|
||||
}
|
||||
for _, path := range paths {
|
||||
value := strings.TrimSpace(detail.Snapshot.Get(path).String())
|
||||
if value != "" {
|
||||
return value
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (s *sCommonBatchExecutionLog) getSourceFile(ctx context.Context, sourceOssId int64, batchInfo *gjson.Json) (*model.BatchExecutionLogFileModel, error) {
|
||||
ossId := sourceOssId
|
||||
if ossId == 0 {
|
||||
ossId = legacySourceOssId(batchInfo)
|
||||
}
|
||||
if ossId == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
oss := &entity.SysOss{}
|
||||
if err := s.ossModel(ctx).
|
||||
Where(dao.SysOss.Columns().OssId, ossId).
|
||||
Scan(oss); err != nil {
|
||||
return nil, gerror.Wrap(err, "获取原始文件信息失败,请稍后重试!")
|
||||
}
|
||||
if oss.OssId == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return &model.BatchExecutionLogFileModel{
|
||||
OssId: oss.OssId,
|
||||
Url: batchExecutionLogFileURL(ctx, oss.Url, oss.Path),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *sCommonBatchExecutionLog) getResultFile(ctx context.Context, batchNo string, resultOssId int64, batchInfo *gjson.Json) (*model.BatchExecutionLogResultFileModel, error) {
|
||||
download := &entity.TResourceBatchDownload{}
|
||||
// 出库/划拨/导出等批次不写 t_resource_batch_download,查不到记录按「无结果文件」处理,
|
||||
// 不能当异常抛给页面(否则详情弹窗整体报 sql: no rows in result set)。
|
||||
if err := s.downloadModel(ctx).
|
||||
Where(dao.TResourceBatchDownload.Columns().BatchNo, batchNo).
|
||||
OrderDesc(dao.TResourceBatchDownload.Columns().Id).
|
||||
Limit(1).
|
||||
Scan(download); err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, gerror.Wrap(err, "获取结果文件信息失败,请稍后重试!")
|
||||
}
|
||||
ossId := resultOssId
|
||||
if ossId == 0 && download.OssId > 0 {
|
||||
ossId = download.OssId
|
||||
}
|
||||
if ossId == 0 {
|
||||
ossId = legacyResultOssId(batchInfo)
|
||||
}
|
||||
if download.Id == 0 && ossId == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
url := batchExecutionLogFileURL(ctx, download.FileUrl, download.FilePath)
|
||||
originalName := ""
|
||||
if ossId > 0 {
|
||||
oss := &entity.SysOss{}
|
||||
if scanErr := s.ossModel(ctx).
|
||||
Where(dao.SysOss.Columns().OssId, ossId).
|
||||
Scan(oss); scanErr != nil && !errors.Is(scanErr, sql.ErrNoRows) {
|
||||
return nil, gerror.Wrap(scanErr, "获取结果文件OSS信息失败,请稍后重试!")
|
||||
}
|
||||
if oss.OssId > 0 {
|
||||
originalName = oss.OriginalName
|
||||
if ossUrl := batchExecutionLogFileURL(ctx, oss.Url, oss.Path); ossUrl != "" {
|
||||
url = ossUrl
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &model.BatchExecutionLogResultFileModel{
|
||||
DownloadId: download.Id,
|
||||
OssId: ossId,
|
||||
Url: url,
|
||||
OriginalName: originalName,
|
||||
DownloadStatus: resultDownloadStatus(download),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// getExportResultFile 只暴露任务 ID;文件必须经下载中心重新校验本人、权限和有效期。
|
||||
func (s *sCommonBatchExecutionLog) getExportResultFile(ctx context.Context, taskNo string, _ int64) (*model.BatchExecutionLogResultFileModel, error) {
|
||||
task := &entity.TExportTask{}
|
||||
if err := s.exportTaskModel(ctx).
|
||||
Where(dao.TExportTask.Columns().TaskNo, taskNo).
|
||||
Where(dao.TExportTask.Columns().OwnerUserId, contexts.GetUserId(ctx)).
|
||||
Scan(task); err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, gerror.Wrap(err, "获取结果文件信息失败,请稍后重试!")
|
||||
}
|
||||
if task.Id <= 0 || task.DeletedTime != nil || task.Status != consts.DownloadStatusGenerated || task.OssId <= 0 ||
|
||||
(task.ExpireTime != nil && task.ExpireTime.Before(gtime.Now())) || !hasExportPermission(ctx, task.PermCode) {
|
||||
return nil, nil
|
||||
}
|
||||
return &model.BatchExecutionLogResultFileModel{
|
||||
ExportTaskId: task.Id,
|
||||
OriginalName: task.FileName,
|
||||
DownloadStatus: consts.DownloadStatusGenerated,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func batchExecutionLogUnknownCount(batchInfo *gjson.Json) int {
|
||||
if batchInfo == nil {
|
||||
return 0
|
||||
}
|
||||
return batchInfo.Get("apnUnknownCount").Int()
|
||||
}
|
||||
|
||||
// batchRestartPhase 从 batch_info 取出重启阶段与计划复机时间。
|
||||
// 重启批次停机完成后要等待若干分钟再复机,这段时间主状态仍是处理中,
|
||||
// 明细却已落终态;前端据此显示「等待复机」,避免误以为卡住或已办结。
|
||||
func batchRestartPhase(batchInfo *gjson.Json) (phase, resumeAt string) {
|
||||
if batchInfo == nil {
|
||||
return "", ""
|
||||
}
|
||||
return strings.TrimSpace(batchInfo.Get("restart.phase").String()),
|
||||
strings.TrimSpace(batchInfo.Get("restart.resumeAt").String())
|
||||
}
|
||||
|
||||
func legacySourceOssId(batchInfo *gjson.Json) int64 {
|
||||
if batchInfo == nil {
|
||||
return 0
|
||||
}
|
||||
for _, path := range []string{"importFileOssId", "source.importFileOssId", "source.inputFileOssId"} {
|
||||
if ossId := batchInfo.Get(path).Int64(); ossId > 0 {
|
||||
return ossId
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func legacyResultOssId(batchInfo *gjson.Json) int64 {
|
||||
if batchInfo == nil {
|
||||
return 0
|
||||
}
|
||||
return batchInfo.Get("resultFileOssId").Int64()
|
||||
}
|
||||
|
||||
func resultDownloadStatus(download *entity.TResourceBatchDownload) int {
|
||||
if download != nil && download.Id > 0 {
|
||||
return download.DownloadStatus
|
||||
}
|
||||
return consts.DownloadStatusGenerated
|
||||
}
|
||||
|
||||
func batchExecutionLogFileURL(ctx context.Context, storedURL string, storedPath string) string {
|
||||
if url := normalizeBatchLocalUploadURL(ctx, storedURL); url != "" {
|
||||
return url
|
||||
}
|
||||
if url := normalizeBatchLocalUploadURL(ctx, storedPath); url != "" {
|
||||
return url
|
||||
}
|
||||
return strings.TrimSpace(storedURL)
|
||||
}
|
||||
|
||||
func normalizeBatchLocalUploadURL(ctx context.Context, value string) string {
|
||||
value = strings.TrimSpace(value)
|
||||
if value == "" {
|
||||
return ""
|
||||
}
|
||||
lower := strings.ToLower(value)
|
||||
if strings.HasPrefix(lower, "http://") || strings.HasPrefix(lower, "https://") {
|
||||
return value
|
||||
}
|
||||
normalized := filepath.ToSlash(filepath.Clean(value))
|
||||
if normalized == "/upload" || strings.HasPrefix(normalized, "/upload/") {
|
||||
return normalized
|
||||
}
|
||||
|
||||
if configType, err := service.SysConfig().GetConfigByKey(ctx, consts.ConfigOssUrlPath); err == nil && configType != nil {
|
||||
configValue := strings.TrimSpace(configType.ConfigValue)
|
||||
if configValue != "" {
|
||||
if strings.Contains(configValue, "|") {
|
||||
split := strings.SplitN(configValue, "|", 2)
|
||||
from := strings.TrimSpace(filepath.ToSlash(split[0]))
|
||||
to := strings.TrimSpace(filepath.ToSlash(split[1]))
|
||||
if suffix, ok := batchLocalUploadMappedSuffix(normalized, from); ok {
|
||||
return batchJoinUploadURL(to, suffix)
|
||||
}
|
||||
} else if suffix := batchLocalUploadRelativePath(normalized); suffix != "" {
|
||||
return batchJoinUploadURL(configValue, suffix)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if suffix := batchLocalUploadRelativePath(normalized); suffix != "" {
|
||||
return batchJoinUploadURL("/upload", suffix)
|
||||
}
|
||||
if strings.HasPrefix(normalized, "/") {
|
||||
return normalized
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func batchLocalUploadMappedSuffix(filePath string, from string) (string, bool) {
|
||||
from = strings.Trim(strings.TrimSpace(filepath.ToSlash(from)), "/")
|
||||
if from == "" {
|
||||
return "", false
|
||||
}
|
||||
normalized := strings.TrimSpace(filepath.ToSlash(filePath))
|
||||
marker := "/" + from + "/"
|
||||
if idx := strings.Index(normalized, marker); idx >= 0 {
|
||||
return strings.TrimPrefix(normalized[idx+len(marker):], "/"), true
|
||||
}
|
||||
prefix := from + "/"
|
||||
if strings.HasPrefix(normalized, prefix) {
|
||||
return strings.TrimPrefix(normalized[len(prefix):], "/"), true
|
||||
}
|
||||
if strings.Trim(normalized, "/") == from {
|
||||
return "", true
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
func batchLocalUploadRelativePath(filePath string) string {
|
||||
normalized := filepath.ToSlash(filepath.Clean(strings.TrimSpace(filePath)))
|
||||
for _, marker := range []string{
|
||||
"resource/upload/",
|
||||
"/resource/upload/",
|
||||
"/app/resource/upload/",
|
||||
"/app/upload/",
|
||||
"upload/",
|
||||
"/upload/",
|
||||
} {
|
||||
if idx := strings.Index(normalized, marker); idx >= 0 {
|
||||
return strings.TrimPrefix(normalized[idx+len(marker):], "/")
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func batchJoinUploadURL(base string, suffix string) string {
|
||||
base = strings.TrimRight(strings.TrimSpace(filepath.ToSlash(base)), "/")
|
||||
suffix = strings.TrimLeft(strings.TrimSpace(filepath.ToSlash(suffix)), "/")
|
||||
if base == "" {
|
||||
base = "/upload"
|
||||
}
|
||||
if suffix == "" {
|
||||
return base
|
||||
}
|
||||
return base + "/" + suffix
|
||||
}
|
||||
Reference in New Issue
Block a user