diff --git a/admin/main_test.go b/admin/main_test.go index 9cf4eb1..9c32bfb 100644 --- a/admin/main_test.go +++ b/admin/main_test.go @@ -247,6 +247,23 @@ func TestShopeeDetail_区分顺运宝观测与正式SKU(t *testing.T) { } } +func TestSybPage_明确展示蝦皮商品Pdd关联来源(t *testing.T) { + list, err := os.ReadFile("templates/syb/list.html") + if err != nil { + t.Fatal(err) + } + detail, err := os.ReadFile("templates/syb/detail_modal.html") + if err != nil { + t.Fatal(err) + } + content := string(list) + string(detail) + for _, want := range []string{".PddAssociationText", "仅在需要更换采购商品时修改下面的关联"} { + if !strings.Contains(content, want) { + t.Errorf("顺运宝页面缺少关联复用提示 %q", want) + } + } +} + func TestCatalogHistory_展示更新策略和分级统计(t *testing.T) { list, err := os.ReadFile("templates/catalog/list.html") if err != nil { diff --git a/admin/service/purchase_workflow_test.go b/admin/service/purchase_workflow_test.go index f054766..a9be8bc 100644 --- a/admin/service/purchase_workflow_test.go +++ b/admin/service/purchase_workflow_test.go @@ -44,6 +44,44 @@ func TestAssociateSybPdd_兼容商品ID并复用已有采集结果(t *testing.T) } } +func TestUpsertSybOrder_同步后自动复用蝦皮商品Pdd关联(t *testing.T) { + db := newTestDB(t) + if err := repository.UpsertShopeeProduct(db, "SP-REUSE", "目录商品", "正常", "MAIN-1"); err != nil { + t.Fatal(err) + } + if _, err := AssociateShopeePdd(db, "SP-REUSE", pddURLA, false); err != nil { + t.Fatal(err) + } + + seedWorkflowOrder(t, db, "SYB-REUSE", "SP-REUSE", "黑色,M") + detail, err := GetSybProcessingDetail(db, "SYB-REUSE") + if err != nil || detail == nil { + t.Fatalf("读取同步明细失败: detail=%+v err=%v", detail, err) + } + if detail.Stage != SybStagePddPending || detail.PddGoodsID != "737116531267" || detail.PddAssociationText == "" { + t.Fatalf("新明细应直接复用已有 PDD 关联并进入待采集阶段: %+v", detail) + } + list, err := ListSybOrdersView(db, "", "", SybStagePddPending, 1) + if err != nil || list.Total != 1 || list.Rows[0].PddAssociationText == "" { + t.Fatalf("列表应显示关联来源: list=%+v err=%v", list, err) + } + + // 重复同步仍复用同一商品关系,不覆盖主数据上的 PDD 关联。 + seedWorkflowOrder(t, db, "SYB-REUSE", "SP-REUSE", "黑色,M") + product, err := repository.GetShopeeProductByGoodsID(db, "SP-REUSE") + if err != nil || product == nil || product.PddGoodsID != "737116531267" { + t.Fatalf("重复同步覆盖了蝦皮商品的 PDD 关联: product=%+v err=%v", product, err) + } + + if err := repository.SoftDeletePddProduct(db, "737116531267"); err != nil { + t.Fatal(err) + } + detail, err = GetSybProcessingDetail(db, "SYB-REUSE") + if err != nil || detail.Stage != SybStagePddMissing || detail.PddAssociationText != "" { + t.Fatalf("PDD 软删除后必须回到关联失效状态且不显示复用提示: detail=%+v err=%v", detail, err) + } +} + func TestAssociateSybPdd_新商品ID生成标准链接且完整链接保持兼容(t *testing.T) { t.Run("纯商品ID", func(t *testing.T) { db := newTestDB(t) diff --git a/admin/service/syb.go b/admin/service/syb.go index ba5ee1c..fb21b8d 100644 --- a/admin/service/syb.go +++ b/admin/service/syb.go @@ -925,29 +925,30 @@ func EnsureSybSession(db *sql.DB, client *syb.Client, username string, now time. // SybOrderView 是列表页一行要显示的全部内容,已经格式化成字符串, // 模板里不做判断和格式化,和其余四个模块的做法一致。 type SybOrderView struct { - SybID string - OrderNo string - ShopName string - Title string - ProductSpec string - ShopeeGoodsID string - Quantity int - PriceText string // "NT$239.00",和人民币价格一眼分得清 - ImageURL string - Stage string - StageText string - StageHelp string - ActionText string - NeedsAttention bool - CanCollect bool - CanPurchase bool - SelectionHint string - DefaultUnitPrice string - DefaultTotalPrice string - MappedPddChoice string - MappingOptionKey string - ContextVersion string - UpdatedAt string + SybID string + OrderNo string + ShopName string + Title string + ProductSpec string + ShopeeGoodsID string + Quantity int + PriceText string // "NT$239.00",和人民币价格一眼分得清 + ImageURL string + Stage string + StageText string + StageHelp string + PddAssociationText string + ActionText string + NeedsAttention bool + CanCollect bool + CanPurchase bool + SelectionHint string + DefaultUnitPrice string + DefaultTotalPrice string + MappedPddChoice string + MappingOptionKey string + ContextVersion string + UpdatedAt string } const ( @@ -1154,6 +1155,7 @@ func sybOrderViewFor(context repository.SybOrderContext) SybOrderView { Quantity: o.Quantity, ImageURL: o.ImageURL, UpdatedAt: formatLocalTime(o.UpdatedAt)} v.Stage, v.StageText, v.StageHelp, v.ActionText = sybStageFor(context) + v.PddAssociationText = sybPddAssociationText(context) v.CanCollect = v.Stage == SybStagePddPending || v.Stage == SybStagePddFailed || v.Stage == SybStagePddCollectingStale v.CanPurchase = v.Stage == SybStagePurchaseReady @@ -1209,6 +1211,7 @@ type SybProcessingDetail struct { ShopeeExists bool PddGoodsID string PddURL string + PddAssociationText string CollectStatus string CollectMsg string CanCollect bool @@ -1231,7 +1234,8 @@ func GetSybProcessingDetail(db *sql.DB, sybID string) (*SybProcessingDetail, err ShopeeGoodsID: o.ShopeeGoodsID, Quantity: o.Quantity, ShopeeExists: context.ShopeeExists, PddGoodsID: context.PddGoodsID, PddURL: context.PddGoodsURL, - CollectStatus: context.PddCollectStatus, CollectMsg: context.PddCollectMsg, + PddAssociationText: sybPddAssociationText(*context), + CollectStatus: context.PddCollectStatus, CollectMsg: context.PddCollectMsg, } d.Stage, d.StageText, d.StageHelp, _ = sybStageFor(*context) d.CanCollect = context.PddGoodsID != "" && @@ -1258,6 +1262,15 @@ func GetSybProcessingDetail(db *sql.DB, sybID string) (*SybProcessingDetail, err return d, nil } +// sybPddAssociationText 只在当前蝦皮商品关联能找到有效、未删除的 PDD 商品时显示。 +// PDD 关联的唯一事实来源是 shopee_products;顺运宝明细不复制这份关系。 +func sybPddAssociationText(context repository.SybOrderContext) string { + if context.PddGoodsID == "" || context.PddCollectStatus == "" { + return "" + } + return "使用蝦皮商品的 PDD 关联:" + context.PddGoodsID + ",无需重复输入链接" +} + // SaveSybLoginSession 登录成功后把会话缓存进数据库。 // // `[必须]` 缓存写失败不能让已经登录的会话失效——本函数把错误原样 diff --git a/admin/templates/syb/detail_modal.html b/admin/templates/syb/detail_modal.html index f37c68a..1540e19 100644 --- a/admin/templates/syb/detail_modal.html +++ b/admin/templates/syb/detail_modal.html @@ -20,6 +20,7 @@
顺运宝没有提供规格原文,系统无法安全判断要采购的颜色和尺码。请先核对上游货运单数据。
{{else}}{{.PddAssociationText}};仅在需要更换采购商品时修改下面的关联。
{{end}}