Files

500 lines
22 KiB
Go

package common
import (
"context"
"encoding/json"
"testing"
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/consts"
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/model"
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/model/request"
_ "github.com/gogf/gf/contrib/drivers/sqlite/v2"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g"
)
func TestBatchExecutionLogDetailListPreservesUnknownStatusAndReason(t *testing.T) {
ctx := setupBatchExecutionLogListDB(t)
insertBatchExecutionLogDetail(t, ctx, "DATA-REVIEW", consts.BatchTypeDataStart, consts.BatchSourceTypeBatchInput, 1, "test-review-card")
const reason = "上游已受理,补查超时,执行结果待核查"
if _, err := g.DB().Exec(ctx, "UPDATE batch_detail SET detail_status = ?, error_reason = ? WHERE batch_no = ?", consts.BatchDetailStatusUnknown, reason, "DATA-REVIEW"); err != nil {
t.Fatal(err)
}
status := consts.BatchDetailStatusUnknown
list, total, err := NewCommonBatchExecutionLog().DetailList(ctx, &model.BatchDetailListParam{
BatchNo: "DATA-REVIEW", DetailStatus: &status,
})
if err != nil || total != 1 || len(list) != 1 {
t.Fatalf("读取待核查明细失败:total=%d,明细数=%d,错误:%v", total, len(list), err)
}
if list[0].DetailStatus != consts.BatchDetailStatusUnknown || list[0].Error == nil || list[0].Error.Reason != reason {
t.Fatalf("待核查状态或具体原因未保留:%+v", list[0])
}
status = consts.BatchDetailStatusFailed
list, total, err = NewCommonBatchExecutionLog().DetailList(ctx, &model.BatchDetailListParam{
BatchNo: "DATA-REVIEW", DetailStatus: &status,
})
if err != nil || total != 0 || len(list) != 0 {
t.Fatalf("待核查明细被计入失败筛选:total=%d,明细数=%d,错误:%v", total, len(list), err)
}
}
func TestBatchExecutionLogListPreservesReviewStatusAndResult(t *testing.T) {
ctx := setupBatchExecutionLogListDB(t)
if _, err := g.DB().Exec(ctx, "ALTER TABLE batch_main ADD batch_async_status INTEGER NOT NULL DEFAULT 0"); err != nil {
t.Fatal(err)
}
if _, err := g.DB().Exec(ctx, "INSERT INTO batch_main(batch_no, batch_type, batch_async_status, run_result) VALUES (?, ?, ?, ?), (?, ?, ?, ?)",
"DATA-REVIEW", consts.BatchTypeDataStart, consts.BatchAsyncStatusReview, consts.BatchRunResultReview,
"DATA-FAILED", consts.BatchTypeDataStop, consts.BatchAsyncStatusFailed, consts.BatchRunResultFailed,
); err != nil {
t.Fatal(err)
}
status := consts.BatchAsyncStatusReview
list, total, err := NewCommonBatchExecutionLog().List(ctx, &model.BatchExecutionLogListParam{
BatchAsyncStatus: &status, PageInfo: request.PageInfo{Page: 1, PageSize: 20},
})
if err != nil || total != 1 || len(list) != 1 {
t.Fatalf("读取待核查批次失败:total=%d,批次数=%d,错误:%v", total, len(list), err)
}
if list[0].BatchNo != "DATA-REVIEW" || list[0].BatchAsyncStatus != consts.BatchAsyncStatusReview || list[0].RunResult != consts.BatchRunResultReview {
t.Fatalf("待核查父状态或运行结果未保留:%+v", list[0])
}
}
func TestBatchExecutionLogUnknownCountFromBatchInfo(t *testing.T) {
ctx := setupBatchExecutionLogListDB(t)
for _, statement := range []string{
`ALTER TABLE batch_main ADD COLUMN target_dept_id INTEGER NOT NULL DEFAULT 0`,
`ALTER TABLE batch_main ADD COLUMN batch_async_status INTEGER NOT NULL DEFAULT 0`,
`ALTER TABLE batch_main ADD COLUMN fail_reason TEXT NOT NULL DEFAULT ''`,
`ALTER TABLE batch_main ADD COLUMN source_oss_id INTEGER NOT NULL DEFAULT 0`,
`ALTER TABLE batch_main ADD COLUMN result_oss_id INTEGER NOT NULL DEFAULT 0`,
`CREATE TABLE t_resource_batch_download (id INTEGER PRIMARY KEY, batch_no TEXT)`,
`INSERT INTO t_resource_batch_download(id, batch_no) VALUES (1, 'DATA-REVIEW'), (2, 'DATA-LEGACY')`,
`INSERT INTO batch_main(batch_no, batch_type, batch_info, total_count) VALUES ('DATA-REVIEW', 109, '{"apnUnknownCount":2}', 3), ('DATA-LEGACY', 109, '{}', 1)`,
} {
if _, err := g.DB().Exec(ctx, statement); err != nil {
t.Fatal(err)
}
}
list, total, err := NewCommonBatchExecutionLog().List(ctx, &model.BatchExecutionLogListParam{PageInfo: request.PageInfo{Page: 1, PageSize: 20}})
if err != nil || total != 2 || len(list) != 2 {
t.Fatalf("读取未知计数列表失败: total=%d len=%d err=%v", total, len(list), err)
}
counts := map[string]int{}
for _, item := range list {
counts[item.BatchNo] = item.UnknownCount
}
if counts["DATA-REVIEW"] != 2 || counts["DATA-LEGACY"] != 0 {
t.Fatalf("列表待核查数错误: %+v", counts)
}
for batchNo, want := range map[string]int{"DATA-REVIEW": 2, "DATA-LEGACY": 0} {
summary, err := NewCommonBatchExecutionLog().Summary(ctx, &model.BatchExecutionLogDetailParam{BatchNo: batchNo})
if err != nil || summary == nil || summary.UnknownCount != want {
t.Fatalf("概要 %s 待核查数错误: summary=%+v err=%v", batchNo, summary, err)
}
}
}
func TestBatchExecutionLogResultFilePreservesOriginalName(t *testing.T) {
ctx := setupBatchExecutionLogListDB(t)
for _, statement := range []string{
`CREATE TABLE t_resource_batch_download (id INTEGER PRIMARY KEY, batch_no TEXT, oss_id INTEGER, file_url TEXT, file_path TEXT)`,
`CREATE TABLE sys_oss (oss_id INTEGER PRIMARY KEY, original_name TEXT, url TEXT, path TEXT)`,
`INSERT INTO t_resource_batch_download(id, batch_no, oss_id, file_url, file_path) VALUES (1, 'DATA-REVIEW', 1, '/upload/DATA-REVIEW-result-nonce.xlsx', ''), (2, 'DATA-LEGACY', 2, '/upload/legacy.xlsx', '')`,
`INSERT INTO sys_oss(oss_id, original_name, url, path) VALUES (1, 'DATA-REVIEW-result.xlsx', '/upload/DATA-REVIEW-result-nonce.xlsx', ''), (2, '', '/upload/legacy.xlsx', '')`,
} {
if _, err := g.DB().Exec(ctx, statement); err != nil {
t.Fatal(err)
}
}
for batchNo, want := range map[string]string{"DATA-REVIEW": "DATA-REVIEW-result.xlsx", "DATA-LEGACY": ""} {
result, err := NewCommonBatchExecutionLog().getResultFile(ctx, batchNo, 0, nil)
if err != nil || result == nil || result.OriginalName != want {
t.Fatalf("原始下载名未透传: batch=%s result=%+v err=%v", batchNo, result, err)
}
encoded, err := json.Marshal(result)
if err != nil {
t.Fatal(err)
}
var response map[string]any
if err := json.Unmarshal(encoded, &response); err != nil || response["originalName"] != want {
t.Fatalf("响应JSON丢失原始下载名: %s err=%v", encoded, err)
}
if batchNo == "DATA-REVIEW" && result.Url != "/upload/DATA-REVIEW-result-nonce.xlsx" {
t.Fatalf("原始下载名影响了物理文件定位: %s", result.Url)
}
}
}
func TestBatchExecutionLogListReturnsTotalCount(t *testing.T) {
ctx := setupBatchExecutionLogListDB(t)
if _, err := g.DB().Exec(ctx, `
INSERT INTO batch_main(
batch_no, batch_type, source_type, total_count, run_result,
created_dept, created_by, tenant_id
) VALUES(?, ?, ?, ?, ?, ?, ?, ?)
`, "OUT-1", 2, 0, 7, 2, 0, 0, ""); err != nil {
t.Fatalf("insert batch: %v", err)
}
list, total, err := NewCommonBatchExecutionLog().List(ctx, &model.BatchExecutionLogListParam{
PageInfo: request.PageInfo{Page: 1, PageSize: 20},
})
if err != nil {
t.Fatalf("list execution logs: %v", err)
}
if total != 1 {
t.Fatalf("total rows = %d, want 1", total)
}
if len(list) != 1 {
t.Fatalf("list length = %d, want 1", len(list))
}
if list[0].TotalCount != 7 {
t.Fatalf("totalCount = %d, want 7", list[0].TotalCount)
}
}
func TestAllocateDetailInheritsHistoricalUnits(t *testing.T) {
ctx := setupBatchExecutionLogListDB(t)
for _, statement := range []string{
`ALTER TABLE batch_main ADD target_dept_id INTEGER DEFAULT 0`,
`INSERT INTO batch_main(batch_no,batch_type,batch_info,created_dept,target_dept_id) VALUES('ALC-HISTORY',3,'{"sourceDeptId":10,"targetDeptId":20,"sourceDeptName":"历史划拨单位","targetDeptName":"历史接收单位"}',10,20)`,
`INSERT INTO batch_detail(batch_no,batch_type,item_id,snapshot) VALUES('ALC-HISTORY',3,'card','{}')`,
} {
if _, err := g.DB().Exec(ctx, statement); err != nil {
t.Fatal(err)
}
}
rows, total, err := NewCommonBatchExecutionLog().DetailList(ctx, &model.BatchDetailListParam{BatchNo: "ALC-HISTORY"})
if err != nil || total != 1 || len(rows) != 1 {
t.Fatal("读取历史明细失败", total, err)
}
if rows[0].SourceDeptName != "历史划拨单位" || rows[0].TargetDeptName != "历史接收单位" {
t.Fatal("历史明细未优先继承批次快照", rows[0])
}
}
func TestBatchExecutionLogDetailListNormalizesFileRowNo(t *testing.T) {
ctx := setupBatchExecutionLogListDB(t)
insertBatchExecutionLogDetail(t, ctx, "INB-1", consts.BatchTypeInbound, consts.BatchSourceTypeFile, 2, "89860000000000000001")
list, total, err := NewCommonBatchExecutionLog().DetailList(ctx, &model.BatchDetailListParam{
BatchNo: "INB-1",
})
if err != nil {
t.Fatalf("detail list: %v", err)
}
if total != 1 {
t.Fatalf("total rows = %d, want 1", total)
}
if len(list) != 1 {
t.Fatalf("list length = %d, want 1", len(list))
}
if list[0].RowNo != 1 {
t.Fatalf("file batch rowNo = %d, want 1", list[0].RowNo)
}
}
func TestBatchExecutionLogDetailListNormalizesManualRowNo(t *testing.T) {
ctx := setupBatchExecutionLogListDB(t)
insertBatchExecutionLogDetail(t, ctx, "INB-2", consts.BatchTypeInbound, consts.BatchSourceTypeBatchInput, 2, "89860000000000000002")
list, total, err := NewCommonBatchExecutionLog().DetailList(ctx, &model.BatchDetailListParam{
BatchNo: "INB-2",
})
if err != nil {
t.Fatalf("detail list: %v", err)
}
if total != 1 {
t.Fatalf("total rows = %d, want 1", total)
}
if len(list) != 1 {
t.Fatalf("list length = %d, want 1", len(list))
}
if list[0].RowNo != 1 {
t.Fatalf("manual batch rowNo = %d, want 1", list[0].RowNo)
}
}
func TestBatchExecutionLogDetailListNumbersPages(t *testing.T) {
ctx := setupBatchExecutionLogListDB(t)
insertBatchExecutionLogDetail(t, ctx, "ROWS", consts.BatchTypeInbound, consts.BatchSourceTypeBatchSelect, 2, "first")
_, err := g.DB().Exec(ctx, "INSERT INTO batch_detail(batch_no, batch_type, detail_status, row_no, item_id) VALUES (?, ?, ?, ?, ?)", "ROWS", consts.BatchTypeInbound, consts.BatchDetailStatusFailed, 9, "second")
if err != nil {
t.Fatal(err)
}
list, total, err := NewCommonBatchExecutionLog().DetailList(ctx, &model.BatchDetailListParam{BatchNo: "ROWS", PageInfo: request.PageInfo{Page: 2, PageSize: 1}})
if err != nil || total != 2 || len(list) != 1 || list[0].RowNo != 2 || list[0].Iccid != "first" {
t.Fatalf("paged rows: list=%+v total=%d err=%v", list, total, err)
}
list, total, err = NewCommonBatchExecutionLog().DetailList(ctx, &model.BatchDetailListParam{BatchNo: "ROWS", PageInfo: request.PageInfo{Page: 1, PageSize: 1}})
if err != nil || total != 2 || len(list) != 1 || list[0].Iccid != "second" {
t.Fatalf("failed card not first: list=%+v total=%d err=%v", list, total, err)
}
failed := consts.BatchDetailStatusFailed
list, total, err = NewCommonBatchExecutionLog().DetailList(ctx, &model.BatchDetailListParam{BatchNo: "ROWS", DetailStatus: &failed, PageInfo: request.PageInfo{Page: 1, PageSize: 1}})
if err != nil || total != 1 || len(list) != 1 || list[0].RowNo != 1 {
t.Fatalf("filtered rows: list=%+v total=%d err=%v", list, total, err)
}
}
func TestBatchExecutionLogDetailListPrioritizesRealFailureOverCascade(t *testing.T) {
ctx := setupBatchExecutionLogListDB(t)
insertBatchExecutionLogDetail(t, ctx, "PRIORITY", consts.BatchTypeAllocate, consts.BatchSourceTypeBatchInput, 1, "cascade")
if _, err := g.DB().Exec(ctx, "UPDATE batch_detail SET detail_status = ?, error_reason = ? WHERE batch_no = ?", consts.BatchDetailStatusFailed, "本批次存在失败明细,整批未执行", "PRIORITY"); err != nil {
t.Fatal(err)
}
if _, err := g.DB().Exec(ctx, "INSERT INTO batch_detail(batch_no,batch_type,detail_status,row_no,item_id,error_reason) VALUES(?,?,?,?,?,?)", "PRIORITY", consts.BatchTypeAllocate, consts.BatchDetailStatusFailed, 2, "actual-error", "ICCID不在当前操作员授权范围内"); err != nil {
t.Fatal(err)
}
for page, want := range []string{"actual-error", "cascade"} {
rows, total, err := NewCommonBatchExecutionLog().DetailList(ctx, &model.BatchDetailListParam{BatchNo: "PRIORITY", PageInfo: request.PageInfo{Page: page + 1, PageSize: 1}})
if err != nil || total != 2 || len(rows) != 1 || rows[0].Iccid != want {
t.Fatalf("page=%d want=%s rows=%+v total=%d err=%v", page+1, want, rows, total, err)
}
}
}
func setupBatchExecutionLogListDB(t *testing.T) context.Context {
t.Helper()
ctx := context.Background()
if err := gdb.SetConfig(gdb.Config{
"default": gdb.ConfigGroup{{
Type: "sqlite",
Link: "sqlite::@file(:memory:)",
MaxOpenConnCount: 1,
}},
}); err != nil {
t.Fatal(err)
}
t.Cleanup(func() {
_ = g.DB().Close(ctx)
})
if err := g.DB().GetCore().ClearTableFieldsAll(ctx); err != nil {
t.Fatal(err)
}
if _, err := g.DB().Exec(ctx, `
CREATE TABLE batch_main (
id INTEGER PRIMARY KEY AUTOINCREMENT,
batch_no TEXT NOT NULL,
batch_type INTEGER NOT NULL,
source_batch_no TEXT NOT NULL DEFAULT '',
source_type INTEGER,
source_channel TEXT NOT NULL DEFAULT 'legacy', execution_mode INTEGER NOT NULL DEFAULT 0,
batch_info TEXT,
total_count INTEGER NOT NULL DEFAULT 0,
run_result INTEGER NOT NULL DEFAULT 0,
start_time TEXT,
end_time TEXT,
created_dept INTEGER NOT NULL DEFAULT 0,
created_by INTEGER NOT NULL DEFAULT 0,
created_at TEXT,
tenant_id TEXT NOT NULL DEFAULT ''
)
`); err != nil {
t.Fatal(err)
}
if _, err := g.DB().Exec(ctx, `
CREATE TABLE batch_detail (
id INTEGER PRIMARY KEY AUTOINCREMENT,
batch_no TEXT NOT NULL,
batch_type INTEGER NOT NULL,
detail_status INTEGER NOT NULL DEFAULT 0,
row_no INTEGER NOT NULL DEFAULT 0,
item_id TEXT NOT NULL DEFAULT '',
error_reason TEXT NOT NULL DEFAULT '',
snapshot TEXT,
tenant_id TEXT NOT NULL DEFAULT '',
created_dept INTEGER NOT NULL DEFAULT 0,
created_by INTEGER NOT NULL DEFAULT 0
)
`); err != nil {
t.Fatal(err)
}
return ctx
}
func TestBatchExecutionLogListHidesAllocateChildBatches(t *testing.T) {
ctx := setupBatchExecutionLogListDB(t)
if _, err := g.DB().Exec(ctx, `
INSERT INTO batch_main(
batch_no, batch_type, source_batch_no, source_type, batch_info, total_count,
run_result, created_dept, created_by, tenant_id
) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`,
"ALC-PARENT", consts.BatchTypeAllocate, "", consts.BatchSourceTypeBatchInput, `{"jobRole":"allocate_parent"}`, 2, consts.BatchRunResultRunning, 0, 0, "",
"ALC-CHILD", consts.BatchTypeAllocate, "ALC-PARENT", consts.BatchSourceTypeBatchInput, `{"jobRole":"allocate_chunk"}`, 2, consts.BatchRunResultRunning, 0, 0, "",
); err != nil {
t.Fatalf("insert allocate batches: %v", err)
}
list, total, err := NewCommonBatchExecutionLog().List(ctx, &model.BatchExecutionLogListParam{
PageInfo: request.PageInfo{Page: 1, PageSize: 20},
})
if err != nil {
t.Fatalf("list execution logs: %v", err)
}
if total != 1 || len(list) != 1 {
t.Fatalf("allocate child batch was not hidden: total=%d list=%d", total, len(list))
}
if list[0].BatchNo != "ALC-PARENT" {
t.Fatalf("visible batch = %q, want parent", list[0].BatchNo)
}
if _, err := NewCommonBatchExecutionLog().Summary(ctx, &model.BatchExecutionLogDetailParam{BatchNo: "ALC-CHILD"}); err == nil {
t.Fatal("allocate child was exposed by summary")
}
if _, _, err := NewCommonBatchExecutionLog().DetailList(ctx, &model.BatchDetailListParam{BatchNo: "ALC-CHILD"}); err == nil {
t.Fatal("allocate child was exposed by detail list")
}
}
func TestBatchExecutionLogDetailListShowsAllocateChildFailureReason(t *testing.T) {
ctx := setupBatchExecutionLogListDB(t)
if _, err := g.DB().Exec(ctx, `
INSERT INTO batch_main(batch_no, batch_type, source_batch_no, source_type, batch_info, total_count, run_result, created_dept, created_by, tenant_id)
VALUES (?, ?, '', ?, ?, 2, ?, 0, 0, ''), (?, ?, ?, ?, ?, 2, ?, 0, 0, '')
`, "ALC-PARENT", consts.BatchTypeAllocate, consts.BatchSourceTypeBatchInput, `{"jobRole":"allocate_parent"}`, consts.BatchRunResultFailed,
"ALC-CHILD", consts.BatchTypeAllocate, "ALC-PARENT", consts.BatchSourceTypeBatchInput, `{"jobRole":"allocate_chunk"}`, consts.BatchRunResultFailed); err != nil {
t.Fatalf("insert allocate batches: %v", err)
}
if _, err := g.DB().Exec(ctx, `
INSERT INTO batch_detail(batch_no, batch_type, detail_status, row_no, item_id, error_reason, snapshot, tenant_id, created_dept, created_by)
VALUES (?, ?, ?, 1, '89860000000000000001', ?, '{}', '', 0, 0), (?, ?, ?, 2, '89860000000000000002', '', '{}', '', 0, 0)
`, "ALC-CHILD", consts.BatchTypeAllocate, consts.BatchDetailStatusFailed, "套餐已过期,不能划拨",
"ALC-CHILD", consts.BatchTypeAllocate, consts.BatchDetailStatusSuccess); err != nil {
t.Fatalf("insert allocate details: %v", err)
}
failed := consts.BatchDetailStatusFailed
list, total, err := NewCommonBatchExecutionLog().DetailList(ctx, &model.BatchDetailListParam{
PageInfo: request.PageInfo{Page: 1, PageSize: 20},
BatchNo: "ALC-PARENT",
DetailStatus: &failed,
})
if err != nil {
t.Fatalf("list parent failure details: %v", err)
}
if total != 1 || len(list) != 1 {
t.Fatalf("failed detail count = %d, list length = %d, want 1", total, len(list))
}
if list[0].Iccid != "89860000000000000001" || list[0].Error == nil || list[0].Error.Reason != "套餐已过期,不能划拨" {
t.Fatalf("unexpected failure detail: %+v", list[0])
}
}
func TestBatchExecutionLogDetailListAggregatesAllAllocateChildren(t *testing.T) {
ctx := setupBatchExecutionLogListDB(t)
if _, err := g.DB().Exec(ctx, `
INSERT INTO batch_main(batch_no, batch_type, source_batch_no, source_type, batch_info, total_count, run_result, created_dept, created_by, tenant_id)
VALUES (?, ?, '', ?, ?, 1, ?, 0, 0, ''), (?, ?, ?, ?, ?, 1, ?, 0, 0, ''), (?, ?, ?, ?, ?, 1, ?, 0, 0, '')
`, "ALC-PARENT", consts.BatchTypeAllocate, consts.BatchSourceTypeBatchInput, `{"jobRole":"allocate_parent"}`, consts.BatchRunResultFailed,
"ALC-CHILD", consts.BatchTypeAllocate, "ALC-PARENT", consts.BatchSourceTypeBatchInput, `{"jobRole":"allocate_chunk"}`, consts.BatchRunResultFailed,
"ALC-RELATED", consts.BatchTypeAllocate, "ALC-PARENT", consts.BatchSourceTypeBatchInput, `{}`, consts.BatchRunResultFailed); err != nil {
t.Fatalf("insert allocate batches: %v", err)
}
if _, err := g.DB().Exec(ctx, `
INSERT INTO batch_detail(batch_no, batch_type, detail_status, row_no, item_id, error_reason, snapshot, tenant_id, created_dept, created_by)
VALUES (?, ?, ?, 1, 'parent-card', '', '{}', '', 0, 0), (?, ?, ?, 1, 'chunk-card', '', '{}', '', 0, 0), (?, ?, ?, 1, 'related-card', '', '{}', '', 0, 0)
`, "ALC-PARENT", consts.BatchTypeAllocate, consts.BatchDetailStatusSuccess,
"ALC-CHILD", consts.BatchTypeAllocate, consts.BatchDetailStatusSuccess,
"ALC-RELATED", consts.BatchTypeAllocate, consts.BatchDetailStatusSuccess); err != nil {
t.Fatalf("insert batch details: %v", err)
}
list, total, err := NewCommonBatchExecutionLog().DetailList(ctx, &model.BatchDetailListParam{BatchNo: "ALC-PARENT"})
if err != nil || total != 3 || len(list) != 3 {
t.Fatalf("unexpected parent details: list=%+v total=%d err=%v", list, total, err)
}
seen := map[string]bool{}
for _, item := range list {
seen[item.Iccid] = true
}
for _, iccid := range []string{"parent-card", "chunk-card", "related-card"} {
if !seen[iccid] {
t.Fatalf("parent details missing %s: %+v", iccid, list)
}
}
}
func TestBatchExecutionLogSelfReferencedParentAggregatesChildren(t *testing.T) {
ctx := setupBatchExecutionLogListDB(t)
if _, err := g.DB().Exec(ctx, `
INSERT INTO batch_main(batch_no, batch_type, source_batch_no, batch_info)
VALUES ('SELF-PARENT', 3, 'SELF-PARENT', '{}'), ('SELF-CHILD', 3, 'SELF-PARENT', '{}')
`); err != nil {
t.Fatal(err)
}
if _, err := g.DB().Exec(ctx, `
INSERT INTO batch_detail(batch_no, batch_type, detail_status, row_no, item_id, snapshot)
VALUES ('SELF-PARENT', 3, 2, 1, 'parent-card', '{}'), ('SELF-CHILD', 3, 2, 2, 'child-card', '{}')
`); err != nil {
t.Fatal(err)
}
list, total, err := NewCommonBatchExecutionLog().DetailList(ctx, &model.BatchDetailListParam{BatchNo: "SELF-PARENT"})
if err != nil || total != 2 || len(list) != 2 || list[0].Iccid != "parent-card" || list[1].Iccid != "child-card" {
t.Fatalf("self-referenced parent aggregation: list=%+v total=%d err=%v", list, total, err)
}
}
func insertBatchExecutionLogDetail(t *testing.T, ctx context.Context, batchNo string, batchType, sourceType, rowNo int, itemID string) {
t.Helper()
if _, err := g.DB().Exec(ctx, `
INSERT INTO batch_main(
batch_no, batch_type, source_type, total_count, run_result,
created_dept, created_by, tenant_id
) VALUES(?, ?, ?, ?, ?, ?, ?, ?)
`, batchNo, batchType, sourceType, 1, 2, 0, 0, ""); err != nil {
t.Fatalf("insert batch main: %v", err)
}
if _, err := g.DB().Exec(ctx, `
INSERT INTO batch_detail(
batch_no, batch_type, detail_status, row_no, item_id, snapshot,
tenant_id, created_dept, created_by
) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?)
`, batchNo, batchType, consts.BatchDetailStatusSuccess, rowNo, itemID, `{"ICCID":"`+itemID+`"}`, "", 0, 0); err != nil {
t.Fatalf("insert batch detail: %v", err)
}
}
// 出库等批次不写 t_resource_batch_download,详情查询查不到记录时
// 必须按「无结果文件」返回,而不是把 sql: no rows in result set 抛给页面。
func TestBatchExecutionLogSummaryTreatsMissingDownloadAsNoResultFile(t *testing.T) {
ctx := setupBatchExecutionLogListDB(t)
if _, err := g.DB().Exec(ctx, `
CREATE TABLE t_resource_batch_download (
id INTEGER PRIMARY KEY AUTOINCREMENT,
batch_no TEXT NOT NULL DEFAULT '',
oss_id INTEGER NOT NULL DEFAULT 0,
file_url TEXT NOT NULL DEFAULT '',
file_path TEXT NOT NULL DEFAULT '',
created_at TEXT,
tenant_id TEXT NOT NULL DEFAULT '',
created_dept INTEGER NOT NULL DEFAULT 0,
created_by INTEGER NOT NULL DEFAULT 0
)
`); err != nil {
t.Fatalf("create download table: %v", err)
}
if _, err := g.DB().Exec(ctx, `
INSERT INTO batch_main(
batch_no, batch_type, source_type, batch_info, total_count,
run_result, created_dept, created_by, tenant_id
) VALUES(?, ?, ?, ?, 0, ?, 0, 0, '')
`, "OUT-NOFILE", 2, 0, `{}`, consts.BatchRunResultSuccess); err != nil {
t.Fatalf("insert outbound batch: %v", err)
}
res, err := NewCommonBatchExecutionLog().Summary(ctx, &model.BatchExecutionLogDetailParam{BatchNo: "OUT-NOFILE"})
if err != nil {
t.Fatalf("summary should not fail when result file is absent: %v", err)
}
if res == nil {
t.Fatal("summary returned nil result")
}
if res.ResultFile != nil {
t.Fatalf("resultFile = %v, want nil when no download record exists", res.ResultFile)
}
}