sync: 同步 v1.0.4 并脱敏配置与镜像

This commit is contained in:
MiFaZhan
2026-10-29 14:36:49 +08:00
parent 743e8e6e6e
commit 2206a393f7
516 changed files with 18119 additions and 2253 deletions
@@ -2,6 +2,8 @@ package common
import (
"context"
"database/sql"
"errors"
"fmt"
"path/filepath"
"strconv"
@@ -11,6 +13,7 @@ import (
"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"
@@ -20,6 +23,7 @@ import (
"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"
)
@@ -94,6 +98,17 @@ func (s *sCommonBatchExecutionLog) ossModel(ctx context.Context, option ...*hand
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{})
@@ -142,6 +157,8 @@ func (s *sCommonBatchExecutionLog) List(ctx context.Context, in *model.BatchExec
}
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)
@@ -187,9 +204,11 @@ func (s *sCommonBatchExecutionLog) Summary(ctx context.Context, in *model.BatchE
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)
@@ -204,11 +223,17 @@ func (s *sCommonBatchExecutionLog) Summary(ctx context.Context, in *model.BatchE
res.SourceFile = sourceFile
}
if resultFile, err := s.getResultFile(ctx, batch.BatchNo, batch.ResultOssId, batch.BatchInfo); err != nil {
return nil, err
// 导出任务的成品文件落在 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 {
res.ResultFile = resultFile
resultFile, err = s.getResultFile(ctx, batch.BatchNo, batch.ResultOssId, batch.BatchInfo)
}
if err != nil {
return nil, err
}
res.ResultFile = resultFile
return res, nil
}
@@ -263,7 +288,7 @@ func (s *sCommonBatchExecutionLog) DetailList(ctx context.Context, in *model.Bat
}
var details []*entity.BatchDetail
mod = mod.Page(in.Page, in.PageSize).OrderAsc(columns.RowNo)
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, "获取批次执行明细列表失败,请稍后重试")
}
@@ -328,6 +353,65 @@ func (s *sCommonBatchExecutionLog) DetailList(ctx context.Context, in *model.Bat
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("批次号不能为空")
@@ -471,11 +555,13 @@ func (s *sCommonBatchExecutionLog) getSourceFile(ctx context.Context, sourceOssI
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 {
Scan(download); err != nil && !errors.Is(err, sql.ErrNoRows) {
return nil, gerror.Wrap(err, "获取结果文件信息失败,请稍后重试!")
}
ossId := resultOssId
@@ -490,14 +576,16 @@ func (s *sCommonBatchExecutionLog) getResultFile(ctx context.Context, batchNo st
}
url := batchExecutionLogFileURL(ctx, download.FileUrl, download.FilePath)
originalName := ""
if ossId > 0 {
oss := &entity.SysOss{}
if err := s.ossModel(ctx).
if scanErr := s.ossModel(ctx).
Where(dao.SysOss.Columns().OssId, ossId).
Scan(oss); err != nil {
return nil, gerror.Wrap(err, "获取结果文件OSS信息失败,请稍后重试!")
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
}
@@ -508,10 +596,49 @@ func (s *sCommonBatchExecutionLog) getResultFile(ctx context.Context, batchNo st
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