sync: 同步 v1.0.4 开源代码并脱敏
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user