docs(test): 更新电表箱装配业务回归测试文档
- 将文档从代码审计任务转换为端到端业务回归测试链路 - 重新组织文档结构为链路总览、示例数据基线和详细步骤 - 添加完整的业务场景清单涵盖WMS/MES/工位终端三大系统 - 定义统一的测试数据包括产品型号、物料清单、工位派工等 - 提供详细的步骤断言和数量等式验证方法 - 建立贯穿全链路的数据流向和状态变更追踪体系
This commit is contained in:
@@ -464,6 +464,29 @@ func parseManageMode(s string) int {
|
||||
return 0
|
||||
}
|
||||
|
||||
// parseItemType 解析导入模板"品类"列:原材料/半成品/成品/其他(兼容 1/2/3/4);无法识别返回 0。
|
||||
// 注意:"半成品"含"成品"子串,必须先判半成品再判成品。
|
||||
func parseItemType(s string) int {
|
||||
s = strings.TrimSpace(s)
|
||||
if s == "" {
|
||||
return 0
|
||||
}
|
||||
switch {
|
||||
case strings.Contains(s, "原材料"):
|
||||
return 1
|
||||
case strings.Contains(s, "半成品"):
|
||||
return 2
|
||||
case strings.Contains(s, "成品"):
|
||||
return 3
|
||||
case strings.Contains(s, "其他"):
|
||||
return 4
|
||||
}
|
||||
if n := atoi(s, 0); n >= 1 && n <= 4 {
|
||||
return n
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// excelMaterialImportHandler 物料档案 Excel 批量导入
|
||||
// 模板(首行忽略表头):图号 | 名称 | 规格 | 单位 | 类型(结构件/电气件) | 说明
|
||||
// (图号=物料编码,问题记录 L18 合并裁决,不再单设图号列;「简称」列已按 2026-09-19 裁决移除)
|
||||
@@ -507,29 +530,71 @@ func excelMaterialImportHandler(ctx *svc.ServiceContext) http.HandlerFunc {
|
||||
spec string
|
||||
unit string
|
||||
manageMode int
|
||||
itemType int
|
||||
description string
|
||||
}
|
||||
plans := make([]planRow, 0, len(rows)-1)
|
||||
errs := make([]importErr, 0)
|
||||
|
||||
// 表头→列下标映射:容忍列顺序变化与多余列(如「品类」插在单位与类型之间)。
|
||||
// 先按"去括号后的表头名"精确匹配,再按关键字包含兜底(顺序稳定),都找不到回退模板固定下标。
|
||||
normHeader := func(s string) string {
|
||||
s = strings.TrimSpace(s)
|
||||
for _, op := range []string{"(", "("} {
|
||||
if i := strings.Index(s, op); i >= 0 {
|
||||
s = s[:i]
|
||||
}
|
||||
}
|
||||
return strings.TrimSpace(s)
|
||||
}
|
||||
headerNorm := make([]string, len(rows[0]))
|
||||
for j, h := range rows[0] {
|
||||
headerNorm[j] = normHeader(h)
|
||||
}
|
||||
colOf := func(names []string, fallback int) int {
|
||||
for _, n := range names {
|
||||
for j, hn := range headerNorm {
|
||||
if hn == n {
|
||||
return j
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, n := range names {
|
||||
for j, hn := range headerNorm {
|
||||
if hn != "" && strings.Contains(hn, n) {
|
||||
return j
|
||||
}
|
||||
}
|
||||
}
|
||||
return fallback
|
||||
}
|
||||
codeCol := colOf([]string{"图号", "物料编码", "编码"}, 0)
|
||||
nameCol := colOf([]string{"名称", "物料名称"}, 1)
|
||||
specCol := colOf([]string{"规格", "规格型号"}, 2)
|
||||
unitCol := colOf([]string{"单位"}, 3)
|
||||
typeCol := colOf([]string{"类型", "管理类型", "管理粒度"}, 4)
|
||||
catCol := colOf([]string{"品类", "类别"}, -1)
|
||||
descCol := colOf([]string{"说明", "备注", "描述"}, 5)
|
||||
|
||||
// 阶段1:解析 + 校验(不落库)
|
||||
for i, row := range rows {
|
||||
if i == 0 {
|
||||
continue // 表头
|
||||
}
|
||||
cell := func(idx int) string {
|
||||
if idx < len(row) {
|
||||
return strings.TrimSpace(row[idx])
|
||||
if idx < 0 || idx >= len(row) {
|
||||
return ""
|
||||
}
|
||||
return ""
|
||||
return strings.TrimSpace(row[idx])
|
||||
}
|
||||
pr := planRow{row: i + 1}
|
||||
pr.code = cell(0)
|
||||
pr.name = cell(1)
|
||||
pr.spec = cell(2)
|
||||
pr.unit = cell(3)
|
||||
pr.manageMode = parseManageMode(cell(4))
|
||||
pr.description = cell(5)
|
||||
pr.code = cell(codeCol)
|
||||
pr.name = cell(nameCol)
|
||||
pr.spec = cell(specCol)
|
||||
pr.unit = cell(unitCol)
|
||||
pr.manageMode = parseManageMode(cell(typeCol))
|
||||
pr.itemType = parseItemType(cell(catCol))
|
||||
pr.description = cell(descCol)
|
||||
|
||||
if pr.code == "" {
|
||||
errs = append(errs, importErr{pr.row, pr.code, "图号为空"})
|
||||
@@ -551,6 +616,10 @@ func excelMaterialImportHandler(ctx *svc.ServiceContext) http.HandlerFunc {
|
||||
errs = append(errs, importErr{pr.row, pr.code, "类型无法识别,请填写「结构件」或「电气件」(兼容 1/2)"})
|
||||
continue
|
||||
}
|
||||
if catCol >= 0 && cell(catCol) != "" && pr.itemType == 0 {
|
||||
errs = append(errs, importErr{pr.row, pr.code, "品类无法识别,请填写「原材料/半成品/成品/其他」(兼容 1/2/3/4)"})
|
||||
continue
|
||||
}
|
||||
exists, _ := ctx.EntClient.Material.Query().
|
||||
Where(material.CodeEQ(pr.code)).Exist(ctx0())
|
||||
if exists {
|
||||
@@ -580,7 +649,7 @@ func excelMaterialImportHandler(ctx *svc.ServiceContext) http.HandlerFunc {
|
||||
}()
|
||||
|
||||
for _, pr := range plans {
|
||||
if _, e = tx.Material.Create().
|
||||
mc := tx.Material.Create().
|
||||
SetCode(pr.code).
|
||||
SetName(pr.name).
|
||||
SetNillableSpec(strPtr(pr.spec)).
|
||||
@@ -588,8 +657,11 @@ func excelMaterialImportHandler(ctx *svc.ServiceContext) http.HandlerFunc {
|
||||
SetNillableDescription(strPtr(pr.description)).
|
||||
SetManageMode(pr.manageMode).
|
||||
SetIsBatchManaged(pr.manageMode == 1).
|
||||
SetIsSerialManaged(pr.manageMode == 2).
|
||||
Save(ctx0()); e != nil {
|
||||
SetIsSerialManaged(pr.manageMode == 2)
|
||||
if pr.itemType > 0 {
|
||||
mc = mc.SetItemType(pr.itemType)
|
||||
}
|
||||
if _, e = mc.Save(ctx0()); e != nil {
|
||||
fail(w, http.StatusInternalServerError, "创建物料失败: "+e.Error())
|
||||
return
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user