fix(server): 消歧后的新键补进档案 (#301)
resyncShopeeProductKeys 改了兄弟明细的 target_color,却没把新键写进档案;只有被 直接重解析的那一条走了 mergeParsedSpec。 线上 2026-09-17 的后果:明细已是 `黑色【長袖】`,档案里还是旧的 `黑色`,采购查 映射查不到,明细永远停在「规格待匹配」;而采购员点「一键匹配」匹到的是档案里 剩下的旧键,看起来成功了却没有任何明细在用它。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NTDbDcwbDw1TSAcE6wfh2F
This commit is contained in:
@@ -56,6 +56,13 @@ func resyncShopeeProductKeys(tx *gorm.DB, shopeeProductID uint64, incomingRaw st
|
||||
Updates(map[string]any{"target_color": keys.Color, "target_size": keys.Size}).Error; err != nil {
|
||||
return sybspec.ParseResult{}, err
|
||||
}
|
||||
// `[必须]` 改了明细的键就必须把新键补进档案。映射是按档案里的值建的,
|
||||
// 档案没有 `黑色【長袖】` 这个条目,采购查映射就查不到,明细永远停在
|
||||
// 「规格待匹配」——而采购员点「一键匹配」匹到的是档案里剩下的旧键
|
||||
// `黑色`,看起来成功了却没有任何明细在用它。
|
||||
if err := mergeParsedSpec(tx, shopeeProductID, keys); err != nil {
|
||||
return sybspec.ParseResult{}, err
|
||||
}
|
||||
}
|
||||
return resolved[incomingRaw], nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
package sybimport_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"go-admin/app/goauto/models"
|
||||
"go-admin/app/goauto/shopeeproduct"
|
||||
"go-admin/app/goauto/sybimport"
|
||||
)
|
||||
|
||||
func sleeveDetail(id uint64, spec string) sybimport.DetailInput {
|
||||
raw, _ := json.Marshal(map[string]any{
|
||||
"id": id, "productQty": 1, "productPrice": 100.0, "productThumb": 1,
|
||||
"productTitle": "襯衫", "productSpec": spec, "productId": 24377106095,
|
||||
})
|
||||
return sybimport.DetailInput{
|
||||
ID: id, ProductID: 24377106095, ProductQty: 1, ProductPrice: 100.0,
|
||||
ProductSpec: spec, ProductTitle: "襯衫", ProductThumb: 1, Raw: raw,
|
||||
}
|
||||
}
|
||||
|
||||
// `[必须]` 消歧改了明细的键,就必须把新键补进档案。映射是按档案里的值建的,
|
||||
// 档案没有 `黑色【長袖】` 这个条目,采购查映射就查不到,明细永远停在「规格待匹配」;
|
||||
// 而采购员点「一键匹配」匹到的是档案里剩下的旧键 `黑色`,看起来成功了却没有任何
|
||||
// 明细在用它——线上 2026-09-17 正是这样(#301)。
|
||||
func TestDisambiguatedKeysReachTheArchive(t *testing.T) {
|
||||
db := openTestDB(t)
|
||||
order := realOrder()
|
||||
|
||||
if _, err := sybimport.ApplyDetail(context.Background(), db, order,
|
||||
sleeveDetail(900001, "黑色【短袖】,M 【建議42.5-47.5公斤】")); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
second, err := sybimport.ApplyDetail(context.Background(), db, order,
|
||||
sleeveDetail(900002, "黑色【長袖】,5XL 【建議72.5-77.5公斤】"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if second.SYBProduct.ShopeeProductID == nil {
|
||||
t.Fatal("未关联虾皮商品")
|
||||
}
|
||||
|
||||
var product models.ShopeeProduct
|
||||
if err := db.First(&product, *second.SYBProduct.ShopeeProductID).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
specs, err := shopeeproduct.Unmarshal(product.SpecsJSON)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
archived := map[string]bool{}
|
||||
for _, dimension := range specs {
|
||||
for _, value := range dimension.Values {
|
||||
archived[value.Name] = true
|
||||
}
|
||||
}
|
||||
|
||||
// 两个明细的键都必须能在档案里找到,否则映射无从建立。
|
||||
for _, want := range []string{"黑色【短袖】", "黑色【長袖】"} {
|
||||
if !archived[want] {
|
||||
t.Fatalf("档案缺少键 %q,现有键: %v", want, archived)
|
||||
}
|
||||
}
|
||||
|
||||
// 明细自己的键也必须是拆开后的形式。
|
||||
var rows []models.SYBProduct
|
||||
if err := db.Where("shopee_product_id = ?", *second.SYBProduct.ShopeeProductID).Find(&rows).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, row := range rows {
|
||||
if row.TargetColor != "黑色【短袖】" && row.TargetColor != "黑色【長袖】" {
|
||||
t.Fatalf("明细 %d 的键未消歧: %q", row.ID, row.TargetColor)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user