refactor: 重构产线工位与备料流程,移除冗余字段

1. 移除工单、工件、日排产中的冗余工位组合/接驳台字段
2. 新增工位接驳台编码字段与唯一约束,关联WMS接驳台主数据
3. 重构日排产为工位产量分配模式,替代原接驳台列表
4. 新增备料单工位级字段与分批出库支持
5. 移除定时同步可生产数量,改为WMS实时拉取接口
6. 过滤操作日志,排除登录/退出相关日志
7. 调整仪表盘工序展示逻辑为今日派工路线
8. 新增接驳台维护菜单与基础数据
This commit is contained in:
SunYF
2026-09-19 07:50:21 +08:00
parent 543655d441
commit 37f10d3a13
156 changed files with 4489 additions and 1285 deletions
+66 -5
View File
@@ -235,12 +235,16 @@ func (s *Service) validateStationBinds(ctx context.Context, client *ent.Client,
// validateAllProcessBinds 完工兜底:校验工件工艺路线中每个工序的装配物料齐套
func (s *Service) validateAllProcessBinds(ctx context.Context, client *ent.Client, sn string) (string, error) {
wp, _, err := s.workpieceProduct(ctx, client, sn)
if _, _, err := s.workpieceProduct(ctx, client, sn); err != nil {
return "", err
}
// 工艺路线由 station_process(按今日派工)派生:遍历今日启用的全部工序编号,逐一校验装配物料齐套
codes, err := RouteProcessCodes(ctx, client, TodayStr(), "")
if err != nil {
return "", err
}
missing := []string{}
for _, code := range ParseProcessSeq(wp.ProcessSeq) {
for _, code := range codes {
msg, err := s.validateStationBinds(ctx, client, sn, code)
if err != nil {
return "", err
@@ -264,14 +268,27 @@ func (s *Service) ListWorkpieceBinds(ctx context.Context, sn string) ([]*ent.Wor
return q.Order(ent.Asc(workpiecebind.FieldProcessCode), ent.Asc(workpiecebind.FieldID)).Limit(2000).All(ctx)
}
// BindTraceVO 追溯页:装机绑定明细 + 每工序齐套状态
// BindTraceVO 追溯页:装机绑定明细 + 每工序齐套状态 + 物料组成树
type BindTraceVO struct {
Binds []*ent.WorkpieceBind `json:"binds"`
Complete bool `json:"complete"`
Missing string `json:"missing"`
BomTree []BindNode `json:"bomTree"` // 递归展开成品零部件到叶子的物料组成树
}
// BuildBindTrace 组装某工件装机绑定追溯(含全工序齐套状态,供成品档案/追溯页展示
// BindNode 追溯用的物料组成节点(递归展开成品零部件,不依赖 WMS
type BindNode struct {
MaterialCode string `json:"materialCode"`
MaterialName string `json:"materialName"`
Spec string `json:"spec"`
Unit string `json:"unit"`
ManageMode string `json:"manageMode"` // 1 批次(结构件) / 2 SN(电气件)
Qty float64 `json:"qty"` // 折算到单台成品的用量
Level int `json:"level"` // 0=顶层装配物料,1+=下级
Children []BindNode `json:"children,omitempty"`
}
// BuildBindTrace 组装某工件装机绑定追溯(含全工序齐套状态 + 物料组成树,供成品档案/追溯页展示)
func (s *Service) BuildBindTrace(ctx context.Context, sn string) (*BindTraceVO, error) {
binds, err := s.ListWorkpieceBinds(ctx, sn)
if err != nil {
@@ -281,7 +298,51 @@ func (s *Service) BuildBindTrace(ctx context.Context, sn string) (*BindTraceVO,
if err != nil {
return nil, err
}
return &BindTraceVO{Binds: binds, Complete: missing == "", Missing: missing}, nil
// 物料组成树:递归展开成品零部件到叶子(不依赖 WMS),让追溯页能展示完整物料组成,
// 避免成品作零部件被引用时只显示上层成品而漏下层料、追溯断链。
var tree []BindNode
if _, wo, perr := s.workpieceProduct(ctx, s.ctx.EntClient, sn); perr == nil && wo != nil {
tree = s.expandBindBom(ctx, s.ctx.EntClient, wo.ProductCode, "", 1, 0, map[string]bool{})
}
return &BindTraceVO{Binds: binds, Complete: missing == "", Missing: missing, BomTree: tree}, nil
}
// expandBindBom 递归展开某产品的物料组成树(成品/装配件若存在下级 BOM 则继续展开,叶子终止)。
// 不依赖 WMS:用 BomItem 是否存在 productCode 记录判断是否有下级 BOM,避免 WMS 不可达时不展开。
// 防环:同一条展开路径出现重复编码视为配置错误,跳过该分支。
func (s *Service) expandBindBom(ctx context.Context, client *ent.Client, productCode, bomName string, qty float64, depth int, path map[string]bool) []BindNode {
const maxDepth = 10
if depth > maxDepth || productCode == "" {
return nil
}
if path[productCode] {
s.ctx.EventLog.Write(ctx, "bom.expand.cycle", "", "", "product_bom", productCode,
"产品物料清单存在循环引用,已跳过", map[string]any{"depth": depth})
return nil
}
items, err := client.BomItem.Query().
Where(bomitem.ProductCode(productCode), bomitem.BomName(bomNameOrDefault(bomName))).All(ctx)
if err != nil || len(items) == 0 {
return nil
}
path[productCode] = true
defer delete(path, productCode)
out := make([]BindNode, 0, len(items))
for _, it := range items {
if it.MaterialCode == "" {
continue
}
node := BindNode{
MaterialCode: it.MaterialCode, MaterialName: it.MaterialName, Spec: it.Spec,
Unit: it.Unit, ManageMode: it.ManageMode, Qty: it.UnitQty * qty, Level: depth,
}
// 该物料自身是否还有下级 BOM(成品/装配件):递归展开其子件
if has, _ := client.BomItem.Query().Where(bomitem.ProductCode(it.MaterialCode)).Exist(ctx); has {
node.Children = s.expandBindBom(ctx, client, it.MaterialCode, bomName, it.UnitQty*qty, depth+1, path)
}
out = append(out, node)
}
return out
}
// BindRequiredItem 应绑清单条目(工位终端/手动报工展示引导 + 前端齐套计算)