package service import ( "database/sql" "testing" "cmautobuy/admin/model" "cmautobuy/admin/repository" ) func seedWorkflowOrder(t *testing.T, db *sql.DB, sybID, goodsID, rawSpec string) model.SybOrder { t.Helper() order := model.SybOrder{ SybID: sybID, OrderNo: "ORDER-" + sybID, Title: "测试商品", ProductSpec: rawSpec, ShopeeGoodsID: goodsID, Quantity: 1, SybData: `{}`, } if _, err := repository.UpsertSybOrder(db, order); err != nil { t.Fatalf("准备顺运宝明细失败: %v", err) } return order } func TestUpsertSybOrder_补建骨架并由报表提升来源(t *testing.T) { db := newSyncTestDB(t) seedWorkflowOrder(t, db, "SYB-1", "SP-1", "黑色, M") product, err := repository.GetShopeeProductByGoodsID(db, "SP-1") if err != nil || product == nil || product.Source != "syb" || product.Title != "测试商品" { t.Fatalf("顺运宝骨架不正确: product=%+v err=%v", product, err) } context, err := repository.GetSybOrderContext(db, "SYB-1") if err != nil || context == nil || context.Order.SpecKey != "黑色, M" { t.Fatalf("规格身份键不正确: context=%+v err=%v", context, err) } if err := repository.UpsertShopeeProduct(db, "SP-1", "报表标题", "正常", "MAIN-1"); err != nil { t.Fatal(err) } product, err = repository.GetShopeeProductByGoodsID(db, "SP-1") if err != nil || product.Source != "report" || product.Title != "报表标题" { t.Fatalf("报表导入应提升骨架来源: product=%+v err=%v", product, err) } seedWorkflowOrder(t, db, "SYB-2", "SP-1", "白色,L") product, _ = repository.GetShopeeProductByGoodsID(db, "SP-1") if product.Source != "report" || product.Title != "报表标题" { t.Fatalf("后续同步不得覆盖报表商品: %+v", product) } } func TestListSybOrdersView_空规格阻断且普通规格直接进入PDD阶段(t *testing.T) { db := newSyncTestDB(t) seedWorkflowOrder(t, db, "SYB-EMPTY", "SP-EMPTY", "") seedWorkflowOrder(t, db, "SYB-PDD", "SP-PDD", "黑色,M") cases := []struct{ stage, wantID string }{ {SybStageSpecMissing, "SYB-EMPTY"}, {SybStagePddMissing, "SYB-PDD"}, } for _, tc := range cases { result, err := ListSybOrdersView(db, "", tc.stage, 1) if err != nil { t.Fatal(err) } if result.Total != 1 || len(result.Rows) != 1 || result.Rows[0].SybID != tc.wantID { t.Fatalf("阶段 %s 结果错误: %+v", tc.stage, result) } if result.Rows[0].StageText == "" || result.Rows[0].ActionText == "" { t.Fatalf("阶段 %s 必须有文字和下一步: %+v", tc.stage, result.Rows[0]) } } } func TestSybStageFor_全部筛选阶段与逐行推导一致(t *testing.T) { key, err := OptionKey(map[string]string{"color": "黑色", "size": "M码", "style": "常规"}) if err != nil { t.Fatal(err) } ready := repository.SybOrderContext{ Order: model.SybOrder{SpecKey: "黑色,M", Quantity: 1}, PddGoodsID: "737116531267", PddCollectStatus: string(model.CollectCollected), PddSkusJSON: collectedThreeDimensions, MappingOptionKey: key, } copyWith := func(change func(*repository.SybOrderContext)) repository.SybOrderContext { value := ready change(&value) return value } cases := []struct { want string action string context repository.SybOrderContext }{ {SybStageSpecMissing, "核对数据", copyWith(func(c *repository.SybOrderContext) { c.Order.SpecKey = "" })}, {SybStagePddMissing, "关联 PDD", copyWith(func(c *repository.SybOrderContext) { c.PddGoodsID, c.PddCollectStatus = "", "" })}, {SybStagePddPending, "创建采集任务", copyWith(func(c *repository.SybOrderContext) { c.PddCollectStatus = string(model.CollectPending) })}, {SybStagePddCollecting, "查看采集状态", copyWith(func(c *repository.SybOrderContext) { c.PddCollectStatus = string(model.CollectCollecting) })}, {SybStagePddFailed, "重新采集", copyWith(func(c *repository.SybOrderContext) { c.PddCollectStatus = string(model.CollectFailed) })}, {SybStageMappingPending, "匹配规格", copyWith(func(c *repository.SybOrderContext) { c.MappingOptionKey = "" })}, {SybStageTaskCreated, "查看采购任务", copyWith(func(c *repository.SybOrderContext) { c.HasActiveTask = true })}, {SybStagePurchaseBlocked, "核对数据", copyWith(func(c *repository.SybOrderContext) { c.Order.Quantity = 0 })}, {SybStagePurchaseReady, "创建采购任务", ready}, } if got := len(SybStageOptions()) - 1; got != len(cases) { t.Fatalf("筛选项有 %d 个业务阶段,但测试只覆盖 %d 个", got, len(cases)) } for _, tc := range cases { stage, text, _, action := sybStageFor(tc.context) if stage != tc.want || text != SybStageLabel(tc.want) || action != tc.action { t.Fatalf("阶段 %s 推导错误: stage=%s text=%q action=%q", tc.want, stage, text, action) } } }