refactor: 清理MES端本地product_type冗余代码,对齐WMS主数据架构

1.  移除MES端本地product_type实体、schema及相关CRUD代码,改为通过WMS内部API代理获取物料主数据
2.  更新物料管理粒度逻辑,新增"其他"类型(3)支持,同步更新入库校验规则
3.  修复前端物料选择、库存筛选等页面的类型判断逻辑,统一使用manage_mode区分管理类型
4.  清理静态资源文件冗余的样式代码,调整前端页面展示逻辑
5.  更新帮助文档,修正物料类型的说明描述
This commit is contained in:
SunYF
2026-09-22 07:41:40 +08:00
parent 12ca493c61
commit 932700ca65
84 changed files with 698 additions and 3955 deletions
+46 -72
View File
@@ -41,12 +41,12 @@ func categoryLabel(c int) string {
}
}
// typeLabelOf 汇总行类型标签(与区域汇总「其他」口径一致):itemType=4 → 其他;否则按管理粒度。
func typeLabelOf(manageMode, itemType int) string {
if itemType == invItemOther {
// typeLabelOf 汇总行类型标签(与区域汇总「其他」口径一致):物料类型 manage_mode=3 → 其他;否则按管理粒度。
func typeLabelOf(matManageMode int) string {
if matManageMode == invManageOther {
return "其他"
}
return manageModeLabel(manageMode)
return manageModeLabel(matManageMode)
}
// stockAggRow 聚合主列表的行结构(按 物料+区域+管理粒度 聚合,只返回数量,不铺开明细)
@@ -57,8 +57,9 @@ type stockAggRow struct {
MaterialCode string `json:"materialCode"` // 物料编码
MaterialName string `json:"materialName"` // 物料名称
Spec string `json:"spec"` // 规格型号
ManageMode int `json:"manageMode"` // 1结构件/2电气件
ItemType int `json:"itemType"` // 物料品类(4=其他:辅料/工装/试验设备
ManageMode int `json:"manageMode"` // 库存行管理粒度 1结构件(批次)/2电气件(SN)
ItemType int `json:"itemType"` // 物料品类(1原材料/2半成品/3成品/4其他
MatManageMode int `json:"matManageMode"` // 物料档案类型(1结构件/2电气件/3其他):类型标签判定用
ZoneCode string `json:"zoneCode"` // 区域
ShelfNo string `json:"shelfNo"` // 货架号
LayerNo string `json:"layerNo"` // 第几层
@@ -91,8 +92,6 @@ func queryStockHandler(ctx *svc.ServiceContext) http.HandlerFunc {
qualityStatus := r.URL.Query().Get("qualityStatus")
manageMode := r.URL.Query().Get("manageMode") // 1/2/空=全部
category := r.URL.Query().Get("category") // 1原材料/2半成品/3成品/空=全部(完工检 SN 拾取器按成品过滤)
itemType := atoi(r.URL.Query().Get("itemType"), 0) // 品类(B14=其他入库物料)
itemTypeNot := atoi(r.URL.Query().Get("itemTypeNot"), 0) // 排除品类(§A1 类型三分互斥:结构件 = 管理粒度1 且非「其他」类)
inboundNo := r.URL.Query().Get("inboundNo")
startDate := r.URL.Query().Get("startDate") // 默认近3个月(由接口保证)
endDate := r.URL.Query().Get("endDate")
@@ -110,8 +109,6 @@ func queryStockHandler(ctx *svc.ServiceContext) http.HandlerFunc {
QualityStatus: qualityStatus,
ManageMode: manageMode,
Category: category,
ItemType: itemType,
ItemTypeNot: itemTypeNot,
InboundNo: inboundNo,
StartDate: startDate,
EndDate: endDate,
@@ -143,8 +140,6 @@ type aggregateStockArgs struct {
QualityStatus string
ManageMode string
Category string // 1原材料/2半成品/3成品/空=全部
ItemType int // 品类 itemTypeB14=其他入库物料)
ItemTypeNot int // 排除品类(§A1:结构件筛选时排除「其他」类 itemType=4)
InboundNo string
StartDate string
EndDate string
@@ -193,28 +188,23 @@ func aggregateStock(ctx *svc.ServiceContext, a aggregateStockArgs) ([]*stockAggR
// 质量状态仅作"筛选"维度(快捷定位未检/合格/不合格),不进分组键(方案 §A1/§A5)
q = q.Where(inventory.QualityStatusEQ(a.QualityStatus))
}
if a.ItemType > 0 {
// 按品类过滤(B1):先取编码集再 IN,避免逐行 join
codes := materialCodesByItemType(ctx, a.ItemType)
if a.InboundNo != "" {
q = q.Where(inventory.InboundNoContains(a.InboundNo))
}
// 类型(manage_mode)下拉:结构件=1 / 电气件=2 / 其他=3,一律按物料档案类型取编码集 IN,不与品类(itemType)字段混用
switch a.ManageMode {
case "1", "2":
codes := materialCodesByManageMode(ctx, atoi(a.ManageMode, 0))
if len(codes) == 0 {
return []*stockAggRow{}, 0, nil
}
q = q.Where(inventory.MaterialCodeIn(codes...))
}
if a.ItemTypeNot > 0 {
// 排除品类(§A1 类型三分互斥:结构件 = 管理粒度1 且非「其他」类)
codes := materialCodesByItemType(ctx, a.ItemTypeNot)
if len(codes) > 0 {
q = q.Where(inventory.MaterialCodeNotIn(codes...))
case "3":
codes := materialCodesByManageMode(ctx, invManageOther)
if len(codes) == 0 {
return []*stockAggRow{}, 0, nil
}
}
if a.InboundNo != "" {
q = q.Where(inventory.InboundNoContains(a.InboundNo))
}
if a.ManageMode == "1" {
q = q.Where(inventory.ManageModeEQ(1))
} else if a.ManageMode == "2" {
q = q.Where(inventory.ManageModeEQ(2))
q = q.Where(inventory.MaterialCodeIn(codes...))
}
if a.Category == "1" {
q = q.Where(inventory.CategoryEQ(invCatRaw))
@@ -255,6 +245,7 @@ func aggregateStock(ctx *svc.ServiceContext, a aggregateStockArgs) ([]*stockAggR
matName := map[string]string{}
matSpec := map[string]string{}
matItemType := map[string]int{}
matManageMode := map[string]int{}
matCodes := []string{}
seen := map[string]bool{}
for _, inv := range all {
@@ -270,6 +261,7 @@ func aggregateStock(ctx *svc.ServiceContext, a aggregateStockArgs) ([]*stockAggR
matName[m.Code] = m.Name
matSpec[m.Code] = m.Spec
matItemType[m.Code] = m.ItemType
matManageMode[m.Code] = m.ManageMode
}
}
@@ -353,6 +345,7 @@ func aggregateStock(ctx *svc.ServiceContext, a aggregateStockArgs) ([]*stockAggR
Spec: matSpec[key.MaterialCode],
ManageMode: key.ManageMode,
ItemType: matItemType[key.MaterialCode],
MatManageMode: matManageMode[key.MaterialCode],
ZoneCode: key.ZoneCode,
ShelfNo: key.ShelfNo,
LayerNo: key.LayerNo,
@@ -380,8 +373,9 @@ type materialAggRow struct {
MaterialCode string `json:"materialCode"`
MaterialName string `json:"materialName"`
Spec string `json:"spec"`
ManageMode int `json:"manageMode"` // 1结构件/2电气件
ItemType int `json:"itemType"` // 物料品类(4=其他:辅料/工装/试验设备
ManageMode int `json:"manageMode"` // 库存行管理粒度 1结构件(批次)/2电气件(SN)
ItemType int `json:"itemType"` // 物料品类(1原材料/2半成品/3成品/4其他
MatManageMode int `json:"matManageMode"` // 物料档案类型(1结构件/2电气件/3其他):类型标签判定用
Category int `json:"category"` // 品类 1原材料/2半成品/3成品
TotalQty int `json:"totalQty"` // 总数量(结构件=数量求和;电气件=SN 行数)
LockedQty int `json:"lockedQty"` // 锁定量
@@ -404,8 +398,6 @@ type materialSummaryArgs struct {
MaterialName string
ManageMode string
Category string // 1原材料/2半成品/3成品/空=全部
ItemType int // 品类 itemType(4=其他入库物料,§A1 类型筛选)
ItemTypeNot int // 排除品类(§A1:结构件筛选时排除「其他」类)
Page int
PageSize int
}
@@ -421,8 +413,6 @@ func materialSummaryHandler(ctx *svc.ServiceContext) http.HandlerFunc {
MaterialName: r.URL.Query().Get("materialName"),
ManageMode: r.URL.Query().Get("manageMode"),
Category: r.URL.Query().Get("category"),
ItemType: atoi(r.URL.Query().Get("itemType"), 0),
ItemTypeNot: atoi(r.URL.Query().Get("itemTypeNot"), 0),
Page: atoi(r.URL.Query().Get("page"), 1),
PageSize: atoi(r.URL.Query().Get("pageSize"), 20),
})
@@ -446,25 +436,20 @@ func aggregateMaterialSummary(ctx *svc.ServiceContext, a materialSummaryArgs) ([
if a.MaterialName != "" {
q = q.Where(inventory.MaterialNameContains(a.MaterialName))
}
if a.ManageMode == "1" {
q = q.Where(inventory.ManageModeEQ(1))
} else if a.ManageMode == "2" {
q = q.Where(inventory.ManageModeEQ(2))
}
if a.ItemType > 0 {
// 按品类过滤(§A1):先取编码集再 IN,避免逐行 join
codes := materialCodesByItemType(ctx, a.ItemType)
// 类型(manage_mode)下拉:结构件=1 / 电气件=2 / 其他=3,一律按物料档案类型取编码集 IN,不与品类(itemType)字段混用
switch a.ManageMode {
case "1", "2":
codes := materialCodesByManageMode(ctx, atoi(a.ManageMode, 0))
if len(codes) == 0 {
return []*materialAggRow{}, 0, nil
}
q = q.Where(inventory.MaterialCodeIn(codes...))
}
if a.ItemTypeNot > 0 {
// 排除品类(§A1 类型三分互斥:结构件 = 管理粒度1 且非「其他」类)
codes := materialCodesByItemType(ctx, a.ItemTypeNot)
if len(codes) > 0 {
q = q.Where(inventory.MaterialCodeNotIn(codes...))
case "3":
codes := materialCodesByManageMode(ctx, invManageOther)
if len(codes) == 0 {
return []*materialAggRow{}, 0, nil
}
q = q.Where(inventory.MaterialCodeIn(codes...))
}
if a.Category == "1" {
q = q.Where(inventory.CategoryEQ(invCatRaw))
@@ -487,6 +472,7 @@ func aggregateMaterialSummary(ctx *svc.ServiceContext, a materialSummaryArgs) ([
matName := map[string]string{}
matSpec := map[string]string{}
matItemType := map[string]int{}
matManageMode := map[string]int{}
matID := map[string]int64{}
matCreatedAt := map[string]int64{}
matCodes := []string{}
@@ -504,6 +490,7 @@ func aggregateMaterialSummary(ctx *svc.ServiceContext, a materialSummaryArgs) ([
matName[m.Code] = m.Name
matSpec[m.Code] = m.Spec
matItemType[m.Code] = m.ItemType
matManageMode[m.Code] = m.ManageMode
matID[m.Code] = int64(m.ID)
matCreatedAt[m.Code] = m.CreatedAt
}
@@ -594,6 +581,7 @@ func aggregateMaterialSummary(ctx *svc.ServiceContext, a materialSummaryArgs) ([
Spec: matSpec[code],
ManageMode: g.mMode,
ItemType: matItemType[code],
MatManageMode: matManageMode[code],
Category: g.mCategory,
TotalQty: g.totalQty,
LockedQty: g.lockedQty,
@@ -638,7 +626,6 @@ func stockDetailsHandler(ctx *svc.ServiceContext) http.HandlerFunc {
keyword := r.URL.Query().Get("keyword")
manageMode := atoi(r.URL.Query().Get("manageMode"), 0)
category := atoi(r.URL.Query().Get("category"), 0)
itemType := atoi(r.URL.Query().Get("itemType"), 0)
page := atoi(r.URL.Query().Get("page"), 1)
pageSize := atoi(r.URL.Query().Get("pageSize"), 50)
@@ -676,15 +663,6 @@ func stockDetailsHandler(ctx *svc.ServiceContext) http.HandlerFunc {
if category != 0 {
q = q.Where(inventory.CategoryEQ(category))
}
if itemType > 0 {
// 按品类过滤(B1):先取编码集再 IN,避免逐行 join
codes := materialCodesByItemType(ctx, itemType)
if len(codes) == 0 {
ok(w, map[string]any{"list": []any{}, "total": 0, "page": page, "pageSize": pageSize})
return
}
q = q.Where(inventory.MaterialCodeIn(codes...))
}
total, err := q.Count(ctx0())
if err != nil {
@@ -739,11 +717,9 @@ func exportStockHandler(ctx *svc.ServiceContext) http.HandlerFunc {
rows, _, err := aggregateMaterialSummary(ctx, materialSummaryArgs{
MaterialCode: r.URL.Query().Get("materialCode"),
MaterialName: r.URL.Query().Get("materialName"),
ManageMode: r.URL.Query().Get("manageMode"),
Category: r.URL.Query().Get("category"),
ItemType: atoi(r.URL.Query().Get("itemType"), 0),
ItemTypeNot: atoi(r.URL.Query().Get("itemTypeNot"), 0),
Page: 1,
ManageMode: r.URL.Query().Get("manageMode"),
Category: r.URL.Query().Get("category"),
Page: 1,
PageSize: 1000000, // 全量
})
if err != nil {
@@ -760,7 +736,7 @@ func exportStockHandler(ctx *svc.ServiceContext) http.HandlerFunc {
}
matrix = append(matrix, []any{
id, rw.MaterialCode, rw.MaterialName, rw.Spec,
typeLabelOf(rw.ManageMode, rw.ItemType), rw.TotalQty, rw.LockedQty, rw.AvailQty,
typeLabelOf(rw.MatManageMode), rw.TotalQty, rw.LockedQty, rw.AvailQty,
rw.QtyQualified, rw.QtyPending, rw.QtyRejected,
strings.Join(rw.Zones, "、"), rw.BatchCount, rw.SnCount,
unixFmt(rw.MaterialCreatedAt), unixFmt(rw.CreatedAt),
@@ -783,8 +759,6 @@ func exportStockHandler(ctx *svc.ServiceContext) http.HandlerFunc {
PositionNo: r.URL.Query().Get("positionNo"),
QualityStatus: r.URL.Query().Get("qualityStatus"),
ManageMode: r.URL.Query().Get("manageMode"),
ItemType: atoi(r.URL.Query().Get("itemType"), 0),
ItemTypeNot: atoi(r.URL.Query().Get("itemTypeNot"), 0),
InboundNo: r.URL.Query().Get("inboundNo"),
StartDate: startDate,
EndDate: endDate,
@@ -801,7 +775,7 @@ func exportStockHandler(ctx *svc.ServiceContext) http.HandlerFunc {
for _, rw := range rows {
matrix = append(matrix, []any{
rw.ID, rw.MaterialCode, rw.MaterialName, rw.Spec,
typeLabelOf(rw.ManageMode, rw.ItemType), rw.ZoneCode, rw.ShelfNo, rw.LayerNo, rw.PositionNo,
typeLabelOf(rw.MatManageMode), rw.ZoneCode, rw.ShelfNo, rw.LayerNo, rw.PositionNo,
rw.QtyQualified, rw.QtyPending, rw.QtyRejected,
rw.TotalQty, rw.LockedQty, rw.AvailQty, rw.BatchCount, rw.SnCount,
unixFmt(rw.CreatedAt),
@@ -849,11 +823,11 @@ func buildZoneSummary(ctx *svc.ServiceContext) ([]zoneSummRow, error) {
return nil, err
}
// 物料品类(itemType)映射:itemType=4 → 其他(辅料/工装/试验设备)
// 物料类型(manageMode)映射:manage_mode=3 → 其他(辅料/工装/试验设备)
mats, _ := ctx.EntClient.Material.Query().All(ctx0())
itemTypeMap := make(map[string]int, len(mats))
manageModeMap := make(map[string]int, len(mats))
for _, m := range mats {
itemTypeMap[m.Code] = m.ItemType
manageModeMap[m.Code] = m.ManageMode
}
// 一区域一行:质量与类型均为列(数量口径:结构件按行数量、电气件按 1)
@@ -880,7 +854,7 @@ func buildZoneSummary(ctx *svc.ServiceContext) ([]zoneSummRow, error) {
g.QtyPending += qty
}
switch {
case itemTypeMap[inv.MaterialCode] == invItemOther:
case manageModeMap[inv.MaterialCode] == invManageOther:
g.QtyOther += qty
case inv.ManageMode == 2:
g.QtyElec += qty