From d0e981ae33b75d1cbfb16327a9dacb816b8a90c3 Mon Sep 17 00:00:00 2001 From: chengma Date: Mon, 24 Aug 2026 00:16:29 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E6=8B=92=E7=BB=9D=E5=B9=B6=E5=8F=91?= =?UTF-8?q?=E8=A6=86=E7=9B=96=E9=A2=9C=E8=89=B2=E6=98=A0=E5=B0=84=20(#293)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- admin/service/color_mapping.go | 12 +++++++++++- admin/service/color_mapping_test.go | 4 ++++ docs/admin/03-data-model.md | 2 +- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/admin/service/color_mapping.go b/admin/service/color_mapping.go index f41217b..c1a9096 100644 --- a/admin/service/color_mapping.go +++ b/admin/service/color_mapping.go @@ -278,9 +278,18 @@ func colorMappingContextVersion(context *ProductColorMappingContext, pddUpdatedA Key, Raw string FormalCount, SybCount int } + type contextMapping struct { + ColorKey, DimensionKey, TargetValue, MappedAt string + } colors := make([]contextColor, 0, len(context.Rows)) + mappings := make([]contextMapping, 0, len(context.Rows)) for _, row := range context.Rows { colors = append(colors, contextColor{row.Color.Key, row.Color.Raw, row.Color.FormalCount, row.Color.SybCount}) + if row.Mapping != nil { + mappings = append(mappings, contextMapping{ + row.Color.Key, row.Mapping.PddDimensionKey, row.Mapping.PddColorValue, row.Mapping.MappedAt, + }) + } } pddHash := sha256.Sum256([]byte(pddSKUsJSON)) payload := struct { @@ -290,7 +299,8 @@ func colorMappingContextVersion(context *ProductColorMappingContext, pddUpdatedA PddUpdatedAt string PddHash string Colors []contextColor - }{ProductColorMappingRulesVersion, context.ShopeeGoodsID, context.PddGoodsID, pddUpdatedAt, hex.EncodeToString(pddHash[:]), colors} + Mappings []contextMapping + }{ProductColorMappingRulesVersion, context.ShopeeGoodsID, context.PddGoodsID, pddUpdatedAt, hex.EncodeToString(pddHash[:]), colors, mappings} raw, _ := json.Marshal(payload) sum := sha256.Sum256(raw) return hex.EncodeToString(sum[:]) diff --git a/admin/service/color_mapping_test.go b/admin/service/color_mapping_test.go index 4da8a44..52a8be8 100644 --- a/admin/service/color_mapping_test.go +++ b/admin/service/color_mapping_test.go @@ -101,6 +101,10 @@ func TestSaveProductColorMappings_保存审计并用上下文版本防过期(t * if err := db.QueryRow(`SELECT COUNT(*) FROM product_color_mapping_audits WHERE shopee_goods_id='S-COLOR'`).Scan(&audits); err != nil || audits != 1 { t.Fatalf("审计记录=%d err=%v", audits, err) } + if _, err := SaveProductColorMappings(db, actor, "S-COLOR", context.ContextVersion, + []ProductColorMappingUpdate{{ShopeeColorKey: "黑色"}}); err == nil { + t.Fatal("并发颜色映射已经变化时,旧页面必须拒绝整批保存") + } if _, err := db.Exec(`UPDATE pdd_products SET updated_at=? WHERE goods_id='P-COLOR'`, "2026-08-23T02:00:00Z"); err != nil { t.Fatal(err) } diff --git a/docs/admin/03-data-model.md b/docs/admin/03-data-model.md index e7bc5e3..eb3de07 100644 --- a/docs/admin/03-data-model.md +++ b/docs/admin/03-data-model.md @@ -1174,7 +1174,7 @@ CREATE TABLE purchase_spec_resolutions ( `shopee_color_raw` 保留用于人工核对的来源文字。 - PDD 颜色维度只能由采集结果中唯一的显式颜色维度确定,目标值只能来自当前 `available=true` 的完整规格组合。候选价格区间以整数分聚合。 -- `context_version` 同时包含 PDD 关联、PDD 采集快照、蝦皮颜色集合和规则版本;保存任一行前 +- `context_version` 同时包含 PDD 关联、PDD 采集快照、蝦皮颜色集合、当前映射快照和规则版本;保存任一行前 都要在事务中重算,批次内任何上下文过期都会整体拒绝。 - 当前映射的有效性由最新候选动态判断。PDD 重采集后目标消失时保留旧映射并显示失效, 不自动改成另一个相似颜色。