sync: 同步 v1.0.4 开源代码并脱敏

This commit is contained in:
MiFaZhan
2026-10-29 14:52:43 +08:00
parent 32e4316ee4
commit 6893e21f07
136 changed files with 9851 additions and 436 deletions
+33 -6
View File
@@ -3,6 +3,7 @@ package batchresult
import (
"context"
"crypto/md5"
"crypto/rand"
"encoding/hex"
"fmt"
"io"
@@ -11,6 +12,8 @@ import (
"strings"
"time"
"git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/consts"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
"github.com/xuri/excelize/v2"
@@ -32,7 +35,9 @@ type Row struct {
TargetStatus int
Operation string
ApnName string
DetailStatus int
Success bool
Status string
Reason string
UpstreamCode int
OrderNum string
@@ -88,13 +93,27 @@ func (gtr *generator) Create(ctx context.Context, request Request) (Artifact, er
if err = os.MkdirAll(directory, 0o755); err != nil {
return Artifact{}, gerror.Wrap(err, "create batch result directory")
}
fileName := request.BatchNo + "-result.xlsx"
originalName := request.BatchNo + "-result.xlsx"
var nonce [16]byte
if _, err = rand.Read(nonce[:]); err != nil {
return Artifact{}, gerror.Wrap(err, "generate batch result file name")
}
fileName := request.BatchNo + "-result-" + hex.EncodeToString(nonce[:]) + ".xlsx"
filePath := filepath.Join(directory, fileName)
file, err := os.OpenFile(filePath, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0o666)
if err != nil {
return Artifact{}, gerror.Wrap(err, "reserve batch result file")
}
if err = file.Close(); err != nil {
_ = os.Remove(filePath)
return Artifact{}, gerror.Wrap(err, "close reserved result file")
}
logicalPath := filepath.Join(uploadPath, objectDir, fileName)
if err = writeWorkbook(filePath, request); err != nil {
_ = os.Remove(filePath)
return Artifact{}, err
}
artifact, err := inspectArtifact(ctx, filePath, logicalPath, fileName, tenantID)
artifact, err := inspectArtifact(ctx, filePath, logicalPath, fileName, originalName, tenantID)
if err != nil {
_ = os.Remove(filePath)
return Artifact{}, err
@@ -262,6 +281,9 @@ func writeAllocateWorkbook(file *excelize.File, sheet, path string, request Requ
if row.Success {
status = "成功"
}
if row.Status != "" {
status = row.Status
}
values := []interface{}{row.RowNo, row.ICCID, row.MSISDN, row.IMSI, row.TargetDeptName, status, row.Reason}
cell, err := excelize.CoordinatesToCellName(1, index+2)
if err != nil {
@@ -387,6 +409,9 @@ func writeInboundWorkbook(file *excelize.File, sheet, path string, request Reque
if row.Success {
status = "成功"
}
if row.Status != "" {
status = row.Status
}
values := []interface{}{row.RowNo, row.ICCID, status, row.Reason}
cell, err := excelize.CoordinatesToCellName(1, index+2)
if err != nil {
@@ -480,13 +505,15 @@ func writeCancelWorkbook(file *excelize.File, sheet, path string, request Reques
}
func writeDataServiceWorkbook(file *excelize.File, sheet, path string, request Request) error {
headers := []interface{}{"行号", "ICCID", "MSISDN", "IMSI", "操作", "APN", "执行结果", "失败原因", "上游状态码", "上游订单号"}
headers := []interface{}{"行号", "ICCID", "MSISDN", "IMSI", "操作", "APN", "执行结果", "失败/待核查原因", "上游状态码", "上游订单号"}
if err := file.SetSheetRow(sheet, "A1", &headers); err != nil {
return gerror.Wrap(err, "write data service result headers")
}
for index, row := range request.Rows {
status := "失败"
if row.Success {
if row.DetailStatus == consts.BatchDetailStatusUnknown {
status = "待核查"
} else if row.Success {
status = "成功"
}
values := []interface{}{row.RowNo, row.ICCID, row.MSISDN, row.IMSI, row.Operation, row.ApnName, status, row.Reason, row.UpstreamCode, row.OrderNum}
@@ -513,7 +540,7 @@ func writeDataServiceWorkbook(file *excelize.File, sheet, path string, request R
return nil
}
func inspectArtifact(ctx context.Context, localPath, logicalPath, fileName string, tenantID string) (Artifact, error) {
func inspectArtifact(ctx context.Context, localPath, logicalPath, fileName, originalName, tenantID string) (Artifact, error) {
file, err := os.Open(localPath)
if err != nil {
return Artifact{}, gerror.Wrap(err, "open result file")
@@ -526,7 +553,7 @@ func inspectArtifact(ctx context.Context, localPath, logicalPath, fileName strin
}
return Artifact{
FileName: fileName,
OriginalName: fileName,
OriginalName: originalName,
FileSuffix: "xlsx",
Path: logicalPath,
LocalPath: localPath,
+177 -1
View File
@@ -4,13 +4,189 @@ import (
"context"
"os"
"path/filepath"
"reflect"
"strconv"
"strings"
"sync"
"testing"
"git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/consts"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gcfg"
"github.com/xuri/excelize/v2"
)
func TestWriteDataServiceWorkbookPreservesReviewStatusAndReason(t *testing.T) {
for _, batchType := range []int{consts.BatchTypeDataCut, consts.BatchTypeDataStart, consts.BatchTypeDataStop} {
t.Run(TaskName(batchType), func(t *testing.T) {
path := filepath.Join(t.TempDir(), "result.xlsx")
rows := []Row{
{RowNo: 1, ICCID: "test-success", DetailStatus: consts.BatchDetailStatusSuccess, Success: true},
{RowNo: 2, ICCID: "test-failed", DetailStatus: consts.BatchDetailStatusFailed, Reason: "上游明确拒绝"},
{RowNo: 3, ICCID: "test-unknown", DetailStatus: consts.BatchDetailStatusUnknown, Reason: "查询超时,尚未确认上游执行结果", UpstreamCode: 202, OrderNum: "test-job-id"},
{RowNo: 4, ICCID: "test-review-priority", DetailStatus: consts.BatchDetailStatusUnknown, Success: true, Reason: "明细仍待核查"},
{RowNo: 5, ICCID: "test-legacy-success", Success: true},
{RowNo: 6, ICCID: "test-legacy-failed", Reason: "未受理"},
}
if err := writeWorkbook(path, Request{BatchType: batchType, Rows: rows}); err != nil {
t.Fatalf("写入数据服务结果失败:%v", err)
}
file, err := excelize.OpenFile(path)
if err != nil {
t.Fatalf("打开数据服务结果失败:%v", err)
}
defer func() { _ = file.Close() }()
for cell, want := range map[string]string{
"G1": "执行结果", "H1": "失败/待核查原因",
"G2": "成功", "G3": "失败", "H3": "上游明确拒绝",
"G4": "待核查", "H4": rows[2].Reason, "I4": "202", "J4": "test-job-id",
"G5": "待核查", "H5": rows[3].Reason,
"G6": "成功", "G7": "失败", "H7": "未受理",
} {
got, err := file.GetCellValue("处理结果", cell)
if err != nil || got != want {
t.Fatalf("单元格 %s = %q,预期 %q,错误:%v", cell, got, want, err)
}
}
})
}
}
func TestCreateSameBatchUsesIndependentResultFiles(t *testing.T) {
// Keep the generated workbook path below Excel's 207-character limit on Windows.
root, rootErr := os.MkdirTemp("", "batch-result-")
if rootErr != nil {
t.Fatal(rootErr)
}
t.Cleanup(func() {
if err := os.RemoveAll(root); err != nil {
t.Errorf("remove test result directory: %v", err)
}
})
t.Setenv("IOTLCM_WORKER_UPLOAD_ROOT", root)
oldAdapter := g.Cfg().GetAdapter()
adapter, err := gcfg.NewAdapterContent(`
system:
upload:
local:
path: "resource/upload"
`)
if err != nil {
t.Fatal(err)
}
g.Cfg().SetAdapter(adapter)
t.Cleanup(func() { g.Cfg().SetAdapter(oldAdapter) })
request := Request{BatchNo: "DATA-REVIEW", BatchType: consts.BatchTypeDataStart, TenantID: "tenant", Rows: []Row{{RowNo: 1, ICCID: "first-card", DetailStatus: consts.BatchDetailStatusUnknown, Reason: "待核查"}}}
requests := []Request{request, request}
requests[1].Rows = []Row{{RowNo: 1, ICCID: "second-card", DetailStatus: consts.BatchDetailStatusUnknown, Reason: "待核查"}}
artifacts := make([]Artifact, 2)
errors := make([]error, 2)
var workers sync.WaitGroup
for index := range requests {
workers.Add(1)
go func(index int) {
defer workers.Done()
artifacts[index], errors[index] = New().Create(context.Background(), requests[index])
}(index)
}
workers.Wait()
for _, err := range errors {
if err != nil {
t.Fatal(err)
}
}
first, second := artifacts[0], artifacts[1]
defer RemoveLocalFile(first)
defer RemoveLocalFile(second)
if first.Path == second.Path || first.LocalPath == second.LocalPath || first.FileName == second.FileName {
t.Fatalf("同批结果物理文件冲突: first=%+v second=%+v", first, second)
}
if first.OriginalName != "DATA-REVIEW-result.xlsx" || second.OriginalName != first.OriginalName {
t.Fatalf("用户下载原始文件名被更改: %q / %q", first.OriginalName, second.OriginalName)
}
RemoveLocalFile(first)
if _, err := os.Stat(first.LocalPath); !os.IsNotExist(err) {
t.Fatalf("首次生成文件未删除: %v", err)
}
file, err := excelize.OpenFile(second.LocalPath)
if err != nil {
t.Fatalf("删除第一份后第二份不可读: %v", err)
}
defer file.Close()
got, err := file.GetCellValue("处理结果", "B2")
if err != nil || got != "second-card" {
t.Fatalf("第二份结果内容损坏: B2=%q err=%v", got, err)
}
}
func TestCreatePreservesWorkbookAndPermissionsForAllBatchTypes(t *testing.T) {
root, err := os.MkdirTemp("", "batchresult-")
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { _ = os.RemoveAll(root) })
t.Setenv("IOTLCM_WORKER_UPLOAD_ROOT", root)
oldAdapter := g.Cfg().GetAdapter()
adapter, err := gcfg.NewAdapterContent(`
system:
upload:
local:
path: "resource/upload"
`)
if err != nil {
t.Fatal(err)
}
g.Cfg().SetAdapter(adapter)
t.Cleanup(func() { g.Cfg().SetAdapter(oldAdapter) })
for _, batchType := range []int{1, 3, 4, 11, 12, 40, 41, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110} {
t.Run(strconv.Itoa(batchType), func(t *testing.T) {
request := Request{BatchNo: "COMPAT-" + strconv.Itoa(batchType), BatchType: batchType, TenantID: "tenant", Rows: []Row{
{RowNo: 1, ICCID: "test-success", ObjectCode: "test-success", Success: true},
{RowNo: 2, ICCID: "test-failed", ObjectCode: "test-failed", Reason: "原有失败原因"},
}}
baseline := filepath.Join(t.TempDir(), "baseline.xlsx")
if err := writeWorkbook(baseline, request); err != nil {
t.Fatal(err)
}
artifact, err := New().Create(context.Background(), request)
if err != nil {
t.Fatal(err)
}
defer RemoveLocalFile(artifact)
if artifact.OriginalName != request.BatchNo+"-result.xlsx" || filepath.Ext(artifact.LocalPath) != ".xlsx" {
t.Fatalf("结果文件名或后缀发生变化: %+v", artifact)
}
baselineInfo, err := os.Stat(baseline)
if err != nil {
t.Fatal(err)
}
actualInfo, err := os.Stat(artifact.LocalPath)
if err != nil || actualInfo.Mode().Perm() != baselineInfo.Mode().Perm() {
t.Fatalf("结果文件权限未保留原SaveAs行为: %v", err)
}
before, err := excelize.OpenFile(baseline)
if err != nil {
t.Fatal(err)
}
defer before.Close()
after, err := excelize.OpenFile(artifact.LocalPath)
if err != nil {
t.Fatal(err)
}
defer after.Close()
want, err := before.GetRows("处理结果")
if err != nil {
t.Fatal(err)
}
got, err := after.GetRows("处理结果")
if err != nil || !reflect.DeepEqual(got, want) {
t.Fatalf("随机物理名改变了批次%d工作簿内容: err=%v", batchType, err)
}
})
}
}
func TestCreateWritesToMountedUploadAndKeepsLogicalPath(t *testing.T) {
wd := t.TempDir()
oldWd, err := os.Getwd()
@@ -44,7 +220,7 @@ system:
BatchNo: "INB202608140001001",
BatchType: 1,
TenantID: "000000",
Rows: []Row{{RowNo: 1, ICCID: "89860401780000567308", Success: true}},
Rows: []Row{{RowNo: 1, ICCID: "89860401786527567308", Success: true}},
})
if err != nil {
t.Fatalf("Create() error = %v", err)