Align origin/main with upstream/main (GitHub). The two branches diverged due to pre-rebase vs post-rebase merge commits for the k8s-amd64-dockerfiles feature. Includes: multi-stage Go compilation Dockerfiles, pan-bolt inventory features, CRM relations, Excel import tooling, proto/gRPC updates, migration scripts, and tools/ directory. Excludes deploy/bin/ pre-compiled binaries (arm64, not needed for amd64 K3s cluster; builds use multi-stage Dockerfiles now).
103 lines
2.5 KiB
Go
103 lines
2.5 KiB
Go
package excelparse
|
||
|
||
import (
|
||
"strconv"
|
||
"strings"
|
||
)
|
||
|
||
// MiaozhangParser handles Excel exports from 秒账 (miaozhang.cn).
|
||
//
|
||
// Expected headers:
|
||
// 图片1 | 图片2 | 图片3 | 名称 | 规格型号 | 颜色 | 条形码 | 匹数 | 库存数量 | 成本均价 | 参考售价 | 备注 | 更新日期
|
||
type MiaozhangParser struct{}
|
||
|
||
func (p *MiaozhangParser) Name() string { return "秒账" }
|
||
|
||
func (p *MiaozhangParser) Match(headers []string) bool {
|
||
required := map[string]bool{"名称": false, "规格型号": false, "颜色": false, "匹数": false, "库存数量": false}
|
||
for _, h := range headers {
|
||
h = strings.TrimSpace(h)
|
||
if _, ok := required[h]; ok {
|
||
required[h] = true
|
||
}
|
||
}
|
||
for _, found := range required {
|
||
if !found {
|
||
return false
|
||
}
|
||
}
|
||
return true
|
||
}
|
||
|
||
func (p *MiaozhangParser) ParseRows(headers []string, rows [][]string) (*ParseResult, error) {
|
||
idx := buildIndex(headers)
|
||
|
||
var products []ParsedProduct
|
||
skipped := 0
|
||
|
||
for _, row := range rows {
|
||
name := colVal(row, idx["名称"])
|
||
|
||
if name == "" || strings.HasPrefix(name, "总") {
|
||
skipped++
|
||
continue
|
||
}
|
||
|
||
rolls, _ := strconv.ParseInt(colVal(row, idx["匹数"]), 10, 64)
|
||
|
||
products = append(products, ParsedProduct{
|
||
ProductName: name,
|
||
Spec: colVal(row, idx["规格型号"]),
|
||
Color: colVal(row, idx["颜色"]),
|
||
UnitRolls: rolls,
|
||
StockQuantity: stripUnit(colVal(row, idx["库存数量"])),
|
||
CostPrice: stripCurrency(colVal(row, idx["成本均价"])),
|
||
SalesPrice: stripCurrency(colVal(row, idx["参考售价"])),
|
||
Remark: colVal(row, idx["备注"]),
|
||
})
|
||
}
|
||
|
||
return &ParseResult{Products: products, SkippedRows: skipped}, nil
|
||
}
|
||
|
||
func buildIndex(headers []string) map[string]int {
|
||
m := make(map[string]int, len(headers))
|
||
for i, h := range headers {
|
||
m[strings.TrimSpace(h)] = i
|
||
}
|
||
return m
|
||
}
|
||
|
||
func colVal(row []string, idx int) string {
|
||
if idx < 0 || idx >= len(row) {
|
||
return ""
|
||
}
|
||
return strings.TrimSpace(row[idx])
|
||
}
|
||
|
||
// stripUnit removes trailing unit markers like "米", "kg", etc.
|
||
func stripUnit(s string) string {
|
||
s = strings.TrimSpace(s)
|
||
for _, suffix := range []string{"米", "m", "kg", "KG"} {
|
||
s = strings.TrimSuffix(s, suffix)
|
||
}
|
||
s = strings.TrimSpace(s)
|
||
if s == "" {
|
||
return "0"
|
||
}
|
||
return s
|
||
}
|
||
|
||
// stripCurrency removes leading ¥ / $ / ¥ symbols.
|
||
func stripCurrency(s string) string {
|
||
s = strings.TrimSpace(s)
|
||
for _, prefix := range []string{"¥", "¥", "$", "$"} {
|
||
s = strings.TrimPrefix(s, prefix)
|
||
}
|
||
s = strings.TrimSpace(s)
|
||
if s == "" {
|
||
return "0"
|
||
}
|
||
return s
|
||
}
|