Files
goauto/server/app/goauto/sybimport/parse.go
T

102 lines
4.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Package sybimport owns the SYB (顺云宝 ERP) shipment detail import: parsing
// productSpec into color/size, and folding each detail into the independent
// syb_product table and the shared shopee_product archive. It never creates
// purchase tasks or stores order/logistics fields (#41).
package sybimport
import (
"regexp"
"strings"
"go-admin/app/goauto/models"
)
var bracketPattern = regexp.MustCompile(`【[^】]*】`)
// explicitSizePattern recognizes only values whose spelling carries a strong
// size signal. SYB has now been observed returning both "color,size" and
// "size,color". A color dictionary would inevitably guess at product-specific
// labels, so role reversal is allowed only when exactly one side matches this
// deliberately narrow pattern.
var explicitSizePattern = regexp.MustCompile(`(?i)^(?:均(?:码|碼|号|號)|one\s*size|free\s*size|x{0,4}[sml]|[2-9]xl|(?:加大|大|中|小)(?:码|碼|号|號)|\d+(?:\.\d+)?(?:cm|mm|m|码|碼|号|號|公分)|\d+(?:\.\d+)?(?:[-~~至到]\d+(?:\.\d+)?)?(?:斤|公斤|千克|kg))$`)
// ambiguousPattern flags leftover separators or multi-token noise after
// bracket stripping — the signal that a "clean" split still isn't reliable.
// Observed in the real SYB sample: "黑色+白色【純棉兩件裝】 簡約親膚" strips
// its bracket to "黑色+白色 簡約親膚", which still carries a '+' and internal
// whitespace, so it must not be reported as a confident match.
var ambiguousPattern = regexp.MustCompile(`[++\s]`)
// ParseResult is the color/size candidate extracted from one productSpec
// string, plus how much the caller should trust it.
type ParseResult struct {
Color string
Size string
Status string
Note string
}
// Parse implements the #41/#216 rule: split on the last comma, strip 【...】
// annotations, and classify the result. It never guesses a
// missing value and never invents a color or size that is not literally
// present in the input.
//
// Rules, derived from real SYB samples (demo/shunyunbaoerp_stock_list.har)
// plus the boundary cases already confirmed in the #41 prototype:
// - empty/whitespace-only input -> failed, nothing to extract.
// - no comma present (e.g. "均碼") -> uncertain: the whole string, with
// brackets stripped, becomes the size candidate; color stays empty. A
// single token with no separator cannot be split into two dimensions
// without guessing which one it is.
// - comma present and exactly one side has an explicit size signal -> that
// side is size and the other side is color. This supports both observed
// SYB orders without allowing AI or a fuzzy color dictionary to swap roles.
// - comma present and neither side has an explicit size signal -> retain the
// established SYB color,size contract for backward compatibility.
// - comma present and both sides have explicit size signals -> uncertain;
// there is no safe color decision.
// - comma present but either side is empty after stripping, or the color
// candidate still carries a leftover '+' or internal whitespace -> the
// split happened but is not trustworthy -> uncertain.
func Parse(raw string) ParseResult {
trimmed := strings.TrimSpace(raw)
if trimmed == "" {
return ParseResult{Status: models.SYBParseStatusFailed, Note: "productSpec 为空,无法拆分颜色尺码"}
}
lastComma := strings.LastIndex(trimmed, ",")
// SYB samples use the ASCII comma; a full-width Chinese comma has not been
// observed, so it is deliberately not treated as a separator here rather
// than guessed at.
if lastComma < 0 {
size := stripBrackets(trimmed)
if size == "" {
return ParseResult{Status: models.SYBParseStatusFailed, Note: "productSpec 剥离备注后为空"}
}
return ParseResult{Size: size, Status: models.SYBParseStatusUncertain, Note: "无分隔符,仅识别出单一规格值"}
}
firstPart := stripBrackets(trimmed[:lastComma])
secondPart := stripBrackets(trimmed[lastComma+1:])
colorPart, sizePart := firstPart, secondPart
if colorPart == "" || sizePart == "" {
return ParseResult{Color: colorPart, Size: sizePart, Status: models.SYBParseStatusUncertain, Note: "按逗号拆分后有一侧为空"}
}
firstIsSize, secondIsSize := explicitSizePattern.MatchString(firstPart), explicitSizePattern.MatchString(secondPart)
if firstIsSize && secondIsSize {
return ParseResult{Color: firstPart, Size: secondPart, Status: models.SYBParseStatusUncertain, Note: "逗号两侧均具有尺码特征,无法安全识别颜色"}
}
if firstIsSize {
colorPart, sizePart = secondPart, firstPart
}
if ambiguousPattern.MatchString(colorPart) {
return ParseResult{Color: colorPart, Size: sizePart, Status: models.SYBParseStatusUncertain, Note: "颜色部分含备注文本或多个分隔符,拆分结果可能不准确"}
}
return ParseResult{Color: colorPart, Size: sizePart, Status: models.SYBParseStatusSuccess}
}
func stripBrackets(part string) string {
return strings.TrimSpace(bracketPattern.ReplaceAllString(part, ""))
}