Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
15d331bee9 | ||
|
|
897c1817d2 |
@@ -2,8 +2,8 @@
|
||||
generated: true (请先修改 Gitea Wiki,禁止直接编辑本文件)
|
||||
wiki_page: Business-Rules-and-Glossary
|
||||
wiki_url: https://git.ilapage.cn/OPC/goauto/wiki/Business-Rules-and-Glossary.-
|
||||
wiki_revision: 214ac317abfef23d71362968d5b30f078fa528b6
|
||||
synchronized_at: 2026-09-29T03:47:42Z
|
||||
wiki_revision: c483a1f3fff3144dfad1ae24e6ca0b1de643ac12
|
||||
synchronized_at: 2026-09-29T06:14:25Z
|
||||
<!-- gitea-wiki-mirror:end -->
|
||||
|
||||
<!-- gitea-wiki-mirror:start -->
|
||||
@@ -740,3 +740,7 @@ SYB 回填与资料完整性独立:正式SYB任务已有单号且仍关联有
|
||||
- 超时/中断以及启动恢复把未完成 pending/matching 改为明确失败并将旧批次结束;既有 ready/updated/回写证据和范围外记录不修改,用户可手动重试。恢复不调用 SYB 写接口,不自动回写、不删除成功结果。
|
||||
|
||||
实现绑定:`fix/350-inner-code-matching`,源码提交 `9c5e20e`;未合并 main、未部署,正式环境仍需发布后启用。
|
||||
|
||||
|
||||
### 档口入库码排版字符兼容(#350 v2)
|
||||
匹配比较时,双方原始 SKU、variationSku 及档口名称仅去除 CR/LF/TAB 排版控制字符,并沿用首尾空白 trim;普通内部空格、大小写、标点与货号不变。导入原始值和回写计划中的 SYB 原始证据保持原样。归一化后重复候选、SKU/档口冲突仍拒绝,原精确规格、货号、数量与占用校验不放宽。成功匹配只形成可回写计划,不自动执行 SYB 写入。
|
||||
|
||||
@@ -417,12 +417,18 @@ func matchSpec(spec string, items []sybclient.DetailItem) []sybclient.DetailItem
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Compare layout-only controls without changing stored evidence or ordinary spaces.
|
||||
func normalizeMatchLayout(value string) string {
|
||||
return strings.TrimSpace(strings.NewReplacer("\r", "", "\n", "", "\t", "").Replace(value))
|
||||
}
|
||||
|
||||
func matchEvidence(stall, sourceSKU string, items []sybclient.DetailItem) ([]sybclient.DetailItem, string) {
|
||||
sourceSKU = strings.TrimSpace(sourceSKU)
|
||||
sourceSKU = normalizeMatchLayout(sourceSKU)
|
||||
if sourceSKU != "" {
|
||||
skuMatches := []sybclient.DetailItem{}
|
||||
for _, item := range items {
|
||||
if strings.TrimSpace(rawText(item.Raw["sku"])) == sourceSKU || strings.TrimSpace(rawText(item.Raw["variationSku"])) == sourceSKU {
|
||||
if normalizeMatchLayout(rawText(item.Raw["sku"])) == sourceSKU || normalizeMatchLayout(rawText(item.Raw["variationSku"])) == sourceSKU {
|
||||
skuMatches = append(skuMatches, item)
|
||||
}
|
||||
}
|
||||
@@ -453,7 +459,7 @@ func matchEvidence(stall, sourceSKU string, items []sybclient.DetailItem) ([]syb
|
||||
return fallback, ""
|
||||
}
|
||||
func strictStall(stall string, items []sybclient.DetailItem) []sybclient.DetailItem {
|
||||
stall = strings.TrimSpace(stall)
|
||||
stall = normalizeMatchLayout(stall)
|
||||
if stall == "" {
|
||||
return nil
|
||||
}
|
||||
@@ -471,8 +477,12 @@ func strictStall(stall string, items []sybclient.DetailItem) []sybclient.DetailI
|
||||
// 候选中同时包含档口名才允许前导零等价(如 "067"≡"67");非数字货号要求精确
|
||||
// token 匹配;ProductSpec 只在以货号开头时才算命中;货号为空时回退为档口名包含判断。
|
||||
func stallMatches(stall string, item sybclient.DetailItem) bool {
|
||||
sku := rawText(item.Raw["sku"])
|
||||
variation := rawText(item.Raw["variationSku"])
|
||||
stall = normalizeMatchLayout(stall)
|
||||
if stall == "" {
|
||||
return false
|
||||
}
|
||||
sku := normalizeMatchLayout(rawText(item.Raw["sku"]))
|
||||
variation := normalizeMatchLayout(rawText(item.Raw["variationSku"]))
|
||||
blob := sku + " " + variation + " " + item.ProductSpec
|
||||
if strings.Contains(blob, stall) {
|
||||
return true
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
package sybinnercode
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"go-admin/app/goauto/models"
|
||||
"go-admin/app/goauto/sybclient"
|
||||
)
|
||||
|
||||
func TestMatchEvidenceNormalizesLayoutOnBothSides(t *testing.T) {
|
||||
for _, layout := range []string{"\n", "\r", "\r\n", "\t"} {
|
||||
for _, sourceHasLayout := range []bool{true, false} {
|
||||
source, candidate := "市场-档口甲#731", "市场-档口甲#731"
|
||||
if sourceHasLayout {
|
||||
source = "市场-" + layout + "档口甲#731"
|
||||
} else {
|
||||
candidate = "市场-" + layout + "档口甲#731"
|
||||
}
|
||||
item := detail(1, "黑色,L", 1, "", candidate, "")
|
||||
matches, reason := matchEvidence(source, source, []sybclient.DetailItem{item})
|
||||
if reason != "" || len(matches) != 1 || !stallMatches(source, item) {
|
||||
t.Fatalf("layout=%q sourceLayout=%v: matches=%v reason=%s", layout, sourceHasLayout, matches, reason)
|
||||
}
|
||||
if item.Raw["variationSku"] != candidate {
|
||||
t.Fatal("comparison modified raw candidate evidence")
|
||||
}
|
||||
}
|
||||
}
|
||||
item := detail(1, "黑色,L", 1, "SKU-\n1", "", "")
|
||||
if matches, reason := matchEvidence("", "SKU-1", []sybclient.DetailItem{item}); reason != "" || len(matches) != 1 {
|
||||
t.Fatalf("raw sku layout did not match: %v %s", matches, reason)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMatchLayoutPreservesIdentityAndAmbiguityChecks(t *testing.T) {
|
||||
for _, candidate := range []string{"市场-档口乙#731", "市场-档口甲#732", "市场-档 口甲#731", "市场-档口甲#PDD7319"} {
|
||||
item := detail(1, "黑色,L", 1, "", candidate, "")
|
||||
matches, _ := matchEvidence("市场-\n档口甲#731", "市场-\n档口甲#731", []sybclient.DetailItem{item})
|
||||
if len(matches) != 0 {
|
||||
t.Fatalf("different identity unexpectedly matched: %q", candidate)
|
||||
}
|
||||
}
|
||||
items := []sybclient.DetailItem{detail(1, "黑色,L", 1, "SKU-1", "", ""), detail(2, "黑色,L", 1, "SKU-\n1", "", "")}
|
||||
if matches, reason := matchEvidence("", "SKU-1", items); len(matches) != 0 || reason == "" {
|
||||
t.Fatal("layout-equivalent duplicate SKUs must remain ambiguous")
|
||||
}
|
||||
items = []sybclient.DetailItem{detail(1, "黑色,L", 1, "SKU-1", "市场-档口乙#732", ""), detail(2, "黑色,L", 1, "OTHER", "市场-档口甲#731", "")}
|
||||
if matches, reason := matchEvidence("市场-\n档口甲#731", "SKU-\t1", items); len(matches) != 0 || reason == "" {
|
||||
t.Fatal("SKU/stall conflict must remain rejected")
|
||||
}
|
||||
if normalizeMatchLayout("A B") == normalizeMatchLayout("AB") || normalizeMatchLayout("a") == normalizeMatchLayout("A") {
|
||||
t.Fatal("ordinary spaces and case must remain significant")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPlanRecordWithLayoutEvidenceKeepsOriginalValues(t *testing.T) {
|
||||
record := matchRecord("ORDER-1", "市场-\n档口甲#731", "市场-\n档口甲#731", "IC-1")
|
||||
item := detail(20, "黑色,L", 1, "", "市场-档口甲#731", "")
|
||||
reader := &fakeMatchReader{rows: map[string][]sybclient.StockRow{"ORDER-1": {{ID: 10}}}, stocks: map[int64]sybclient.StockDetail{10: {ID: 10, Details: []sybclient.DetailItem{item}}}}
|
||||
plan, status, reason, err := planRecord(context.Background(), reader, record, map[int64]bool{})
|
||||
if err != nil || status != models.SYBInnerCodeReady || plan == nil || plan.DetailID != 20 {
|
||||
t.Fatalf("expected unique ready plan, got %v %s %s %v", plan, status, reason, err)
|
||||
}
|
||||
if record.Stall != "市场-\n档口甲#731" || record.SourceSKURaw != record.Stall || plan.SYBVariationSKU != "市场-档口甲#731" {
|
||||
t.Fatal("original import and plan evidence must remain unchanged")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user