package service import ( "context" "testing" "cmautobuy/admin/model" "cmautobuy/admin/syb" ) type batchingInnerCodeReader struct { batches [][]int64 } func (r *batchingInnerCodeReader) DetailListByStock(_ context.Context, ids []int64) ([]syb.StockDetail, error) { r.batches = append(r.batches, append([]int64(nil), ids...)) result := make([]syb.StockDetail, 0, len(ids)) for _, id := range ids { result = append(result, syb.StockDetail{ID: id}) } return result, nil } func TestReadInnerCodeDetails_总量不限且每批最多100(t *testing.T) { ids := make([]int64, 205) for index := range ids { ids[index] = int64(index + 1) } reader := &batchingInnerCodeReader{} details, err := readInnerCodeDetails(context.Background(), reader, ids) if err != nil { t.Fatal(err) } if len(details) != len(ids) || len(reader.batches) != 3 { t.Fatalf("只读匹配应处理全部选择并分 3 批,details=%d batches=%d", len(details), len(reader.batches)) } for index, want := range []int{100, 100, 5} { if len(reader.batches[index]) != want { t.Errorf("第 %d 批数量=%d,期望 %d", index+1, len(reader.batches[index]), want) } } } func TestInnerCodeStockIDFromJSON(t *testing.T) { for _, raw := range []string{ `{"stock":{"id":75104587},"detail":{"id":1}}`, `{"stock":{"id":"75104587"},"detail":{"id":1}}`, } { if got, ok := innerCodeStockIDFromJSON(raw); !ok || got != 75104587 { t.Fatalf("解析 stock id 失败 got=%d ok=%v", got, ok) } } if _, ok := innerCodeStockIDFromJSON(`{"stock":{}}`); ok { t.Fatal("缺少 stock.id 时不应成功") } } func TestPlanInnerCodeRows_精确与标准化规格均受档口约束(t *testing.T) { records := []model.InnerCodeRecord{ {ID: 1, OrderNumber: "ORDER-1", Stall: "A档#027", SpecRaw: "A-027 黑色,M碼 建議45-55公斤", InnerCode: "DK1", SourceDuplicateCount: 1}, {ID: 2, OrderNumber: "ORDER-1", Stall: "B档#102", SpecRaw: "杏色,XL", InnerCode: "DK2", SourceDuplicateCount: 1}, } detail := syb.StockDetail{ID: 100, Details: []syb.DetailItem{ innerCodeTestItem(11, "A-027 黑色,M", map[string]any{"sku": "A档#027"}), innerCodeTestItem(12, "杏色,XL", map[string]any{"variationSku": "B档-102"}), }} plans := planInnerCodeRows(records, map[string][]int64{"ORDER-1": {100}}, map[int64]syb.StockDetail{100: detail}) if len(plans) != 2 || plans[0].Status != model.InnerCodeReady || plans[0].DetailID != 11 || plans[1].DetailID != 12 { t.Fatalf("规划结果不正确: %+v", plans) } } func TestPlanInnerCodeRows_聚合码放行且其他异常继续阻断(t *testing.T) { records := []model.InnerCodeRecord{ {ID: 1, OrderNumber: "DUP", SpecRaw: "黑色,M", InnerCode: "DK1", SourceDuplicateCount: 2}, {ID: 2, OrderNumber: "EMPTY", SpecRaw: "", InnerCode: "DK2", SourceDuplicateCount: 1}, {ID: 3, OrderNumber: "PURCHASED", SpecRaw: "黑色,M", InnerCode: "DK3", SourceDuplicateCount: 1}, {ID: 4, OrderNumber: "AMBIGUOUS", SpecRaw: "黑色,M", InnerCode: "DK4", SourceDuplicateCount: 1}, {ID: 5, OrderNumber: "MISSING", SpecRaw: "黑色,M", InnerCode: "DK5", SourceDuplicateCount: 1}, } stocks := map[string][]int64{"DUP": {1}, "EMPTY": {2}, "PURCHASED": {3}, "AMBIGUOUS": {4}} details := map[int64]syb.StockDetail{ 1: {ID: 1, Details: []syb.DetailItem{innerCodeTestItem(1, "黑色,M", nil)}}, 2: {ID: 2, Details: []syb.DetailItem{innerCodeTestItem(2, "黑色,M", nil)}}, 3: {ID: 3, Details: []syb.DetailItem{innerCodeTestItem(3, "黑色,M", map[string]any{"purchasePlatform": "PDD"})}}, 4: {ID: 4, Details: []syb.DetailItem{innerCodeTestItem(4, "黑色,M", nil), innerCodeTestItem(5, "黑色,M", nil)}}, } plans := planInnerCodeRows(records, stocks, details) wants := []model.InnerCodeStatus{model.InnerCodeReady, model.InnerCodeSkipped, model.InnerCodeSkipped, model.InnerCodeSkipped, model.InnerCodeFailed} for index, want := range wants { if plans[index].Status != want { t.Errorf("第 %d 条 status=%s message=%s,期望 %s", index, plans[index].Status, plans[index].ResultMessage, want) } } } func TestPlanInnerCodeRows_同一远端明细不会占用两次(t *testing.T) { records := []model.InnerCodeRecord{ {ID: 1, OrderNumber: "ORDER", SpecRaw: "黑色,M", InnerCode: "DK1", SourceDuplicateCount: 1}, {ID: 2, OrderNumber: "ORDER", SpecRaw: "黑色,M", InnerCode: "DK2", SourceDuplicateCount: 1}, } detail := syb.StockDetail{ID: 10, Details: []syb.DetailItem{innerCodeTestItem(99, "黑色,M", nil)}} plans := planInnerCodeRows(records, map[string][]int64{"ORDER": {10}}, map[int64]syb.StockDetail{10: detail}) if plans[0].Status != model.InnerCodeReady || plans[1].Status != model.InnerCodeSkipped { t.Fatalf("占用门禁失败: %+v", plans) } } func TestPlanInnerCodeRows_部分匹配保留未选记录的明细占用(t *testing.T) { selected := model.InnerCodeRecord{ ID: 1, BusinessDate: "2026-08-15", OrderNumber: "ORDER", SpecRaw: "黑色,M", InnerCode: "DK1", SourceDuplicateCount: 1, } contextRecords := []model.InnerCodeRecord{ selected, {ID: 2, BusinessDate: "2026-08-15", OrderNumber: "ORDER", DetailID: 99, Status: model.InnerCodeReady}, } reserved := innerCodeReservedDetails(contextRecords, map[int64]bool{selected.ID: true}) detail := syb.StockDetail{ID: 10, Details: []syb.DetailItem{innerCodeTestItem(99, "黑色,M", nil)}} plans := planInnerCodeRowsWithReserved( []model.InnerCodeRecord{selected}, map[string][]int64{"ORDER": {10}}, map[int64]syb.StockDetail{10: detail}, reserved, ) if len(plans) != 1 || plans[0].Status != model.InnerCodeSkipped || plans[0].ResultMessage != "符合条件的顺运宝商品已被同订单其他记录占用" { t.Fatalf("未选记录的明细占用没有生效: %+v", plans) } } func TestInnerCodeReservedDetails_选中记录释放自身旧占用(t *testing.T) { records := []model.InnerCodeRecord{ {ID: 1, OrderNumber: "ORDER", DetailID: 99, Status: model.InnerCodeReady}, {ID: 2, OrderNumber: "ORDER", DetailID: 100, Status: model.InnerCodeNeedsCheck}, {ID: 3, OrderNumber: "ORDER", DetailID: 101, Status: model.InnerCodeSkipped}, } reserved := innerCodeReservedDetails(records, map[int64]bool{1: true}) if reserved["ORDER"][99] || !reserved["ORDER"][100] || reserved["ORDER"][101] { t.Fatalf("明细占用集合不正确: %+v", reserved) } } func TestUniquePositiveInnerCodeIDs_过滤无效和重复编号(t *testing.T) { got := uniquePositiveInnerCodeIDs([]int64{3, 0, -1, 3, 2}) if len(got) != 2 || got[0] != 3 || got[1] != 2 { t.Fatalf("选中编号清理结果不正确: %v", got) } } func TestPlanInnerCodeRows_远端已是目标码(t *testing.T) { record := model.InnerCodeRecord{ID: 1, OrderNumber: "ORDER", SpecRaw: "黑色,M", InnerCode: "DK1", SourceDuplicateCount: 1} item := innerCodeTestItem(1, "黑色,M", map[string]any{"innerExpCode": "DK1"}) plans := planInnerCodeRows([]model.InnerCodeRecord{record}, map[string][]int64{"ORDER": {10}}, map[int64]syb.StockDetail{10: {ID: 10, Details: []syb.DetailItem{item}}}) if plans[0].Status != model.InnerCodeAlreadyFilled || plans[0].RemoteInnerCode != "DK1" { t.Fatalf("已存在判断失败: %+v", plans[0]) } } func innerCodeTestItem(id int64, spec string, raw map[string]any) syb.DetailItem { if raw == nil { raw = make(map[string]any) } raw["id"] = id raw["productSpec"] = spec return syb.DetailItem{ID: id, ProductSpec: spec, Raw: raw} }