feat(mes): 添加定时同步可生产数量到WMS及BOM项相关标准字段

- 在main.go中添加定时任务每10分钟同步可生产数量到WMS系统
- 为BomItem实体添加relatedStandard字段及相关CRUD方法
- 为InspectionRecord实体添加reportNo、materialCode、materialName等字段
- 更新ent schema确保新字段的验证和默认值设置
- 添加必要的数据库迁移和字段映射逻辑
This commit is contained in:
SunYF
2026-09-17 12:49:07 +08:00
parent b4b274301d
commit 1cc93795ce
191 changed files with 24504 additions and 1250 deletions
+23 -23
View File
@@ -46,7 +46,7 @@ func (s *Service) RemoveBind(ctx context.Context, sn string, processCode int, ma
// BindWorkpiece 独立绑定接口:工人在工位扫一件绑一件(报工前的逐条扫码/防错),
// 与报工携带 binds 等价,落库后立即返回结果供前端即时反馈(SN 重复/料不匹配当场报错)。
func (s *Service) BindWorkpiece(ctx context.Context, sn, orderNo string, processCode int, stationNo string, operator string, items []BindItemReq) (map[string]any, error) {
bound, err := s.applyBinds(ctx, sn, orderNo, processCode, stationNo, operator, items)
bound, err := s.applyBinds(ctx, s.ctx.EntClient, sn, orderNo, processCode, stationNo, operator, items)
if err != nil {
return nil, err
}
@@ -82,15 +82,15 @@ type bindTarget struct {
}
// workpieceProduct 解析工件所属产品编码与工单锁定的 BOM 名称(sn → workpiece → workOrder
func (s *Service) workpieceProduct(ctx context.Context, sn string) (*ent.Workpiece, *ent.WorkOrder, error) {
wp, err := s.ctx.EntClient.Workpiece.Query().Where(workpiece.Sn(sn)).First(ctx)
func (s *Service) workpieceProduct(ctx context.Context, client *ent.Client, sn string) (*ent.Workpiece, *ent.WorkOrder, error) {
wp, err := client.Workpiece.Query().Where(workpiece.Sn(sn)).First(ctx)
if err != nil {
return nil, nil, errors.New("工件未进线登记")
}
if wp.WorkOrderId <= 0 {
return wp, nil, errors.New("工件未关联工单")
}
wo, err := s.ctx.EntClient.WorkOrder.Get(ctx, wp.WorkOrderId)
wo, err := client.WorkOrder.Get(ctx, wp.WorkOrderId)
if err != nil || wo == nil {
return wp, nil, errors.New("工件所属工单不存在")
}
@@ -99,8 +99,8 @@ func (s *Service) workpieceProduct(ctx context.Context, sn string) (*ent.Workpie
// stationBindTargets 取某工单(产品+BOM)在某工序(processCode>0)要求装配的物料清单。
// 一型号多份 BOM 并存,必须按工单锁定的 bomName 过滤,否则不同厂家料单互相串。
func (s *Service) stationBindTargets(ctx context.Context, productCode, bomName string, processCode int) ([]bindTarget, error) {
items, err := s.ctx.EntClient.BomItem.Query().
func (s *Service) stationBindTargets(ctx context.Context, client *ent.Client, productCode, bomName string, processCode int) ([]bindTarget, error) {
items, err := client.BomItem.Query().
Where(bomitem.ProductCode(productCode), bomitem.BomName(bomNameOrDefault(bomName)), bomitem.ProcessCode(processCode)).All(ctx)
if err != nil {
return nil, err
@@ -120,11 +120,11 @@ func (s *Service) stationBindTargets(ctx context.Context, productCode, bomName s
// ② 电气件 SN 全局防重:已被其他工件绑定则拒绝(防错拿/防重复装机);
// ③ 同一工件同一料同一绑定值幂等(重复扫码不报错,静默去重)。
// 返回绑定成功条数。
func (s *Service) applyBinds(ctx context.Context, sn, orderNo string, processCode int, stationNo string, operator string, items []BindItemReq) (int, error) {
func (s *Service) applyBinds(ctx context.Context, client *ent.Client, sn, orderNo string, processCode int, stationNo string, operator string, items []BindItemReq) (int, error) {
if len(items) == 0 {
return 0, nil
}
wp, wo, err := s.workpieceProduct(ctx, sn)
wp, wo, err := s.workpieceProduct(ctx, client, sn)
if err != nil {
return 0, err
}
@@ -134,7 +134,7 @@ func (s *Service) applyBinds(ctx context.Context, sn, orderNo string, processCod
}
// 工单锁定 BOM 的全量索引:materialCode → target(校验料合法性 + 取物料快照)
bomMap := map[string]*ent.BomItem{}
allBom, _ := s.ctx.EntClient.BomItem.Query().
allBom, _ := client.BomItem.Query().
Where(bomitem.ProductCode(productCode), bomitem.BomName(bomNameOrDefault(""))).All(ctx)
for _, b := range allBom {
bomMap[b.MaterialCode] = b
@@ -154,14 +154,14 @@ func (s *Service) applyBinds(ctx context.Context, sn, orderNo string, processCod
return bound, fmt.Errorf("物料 %s 配置在工序%d装配,本工位(工序%d)不可绑定", mc, bi.ProcessCode, processCode)
}
// 同工件同料同值幂等
exists, _ := s.ctx.EntClient.WorkpieceBind.Query().
exists, _ := client.WorkpieceBind.Query().
Where(workpiecebind.Sn(sn), workpiecebind.MaterialCode(mc), workpiecebind.BindValue(bv)).Exist(ctx)
if exists {
continue
}
// 电气件 SN 全局防重
if bi.ManageMode == "2" {
other, _ := s.ctx.EntClient.WorkpieceBind.Query().
other, _ := client.WorkpieceBind.Query().
Where(workpiecebind.BindValue(bv), workpiecebind.BindType("SN"), workpiecebind.SnNEQ(sn)).First(ctx)
if other != nil {
return bound, fmt.Errorf("电气件SN %s 已绑定到工件 %s(工序%d),不能重复装机", bv, other.Sn, other.ProcessCode)
@@ -171,7 +171,7 @@ func (s *Service) applyBinds(ctx context.Context, sn, orderNo string, processCod
if bi.ManageMode == "2" {
bindType = "SN"
}
if err := s.ctx.EntClient.WorkpieceBind.Create().
if err := client.WorkpieceBind.Create().
SetSn(sn).SetOrderNo(orderNo).SetProcessCode(processCode).
SetStationNo(stationNo).SetMaterialCode(mc).SetMaterialName(bi.MaterialName).
SetSpec(bi.Spec).SetUnit(bi.Unit).SetManageMode(bi.ManageMode).
@@ -187,12 +187,12 @@ func (s *Service) applyBinds(ctx context.Context, sn, orderNo string, processCod
// validateStationBinds 校验某工件在某工序的装配物料是否绑齐(工艺流程强校验核心):
// 返回缺料明细(空串=齐套)。判定:电气件(SN) 绑定的不同SN数 ≥ 单台用量;结构件(批次) 至少绑定1个批次号。
// 若该产品该工序未配置任何装配物料(processCode未配置),视为不启用绑定校验,放行。
func (s *Service) validateStationBinds(ctx context.Context, sn string, processCode int) (string, error) {
_, wo, err := s.workpieceProduct(ctx, sn)
func (s *Service) validateStationBinds(ctx context.Context, client *ent.Client, sn string, processCode int) (string, error) {
_, wo, err := s.workpieceProduct(ctx, client, sn)
if err != nil {
return "", err
}
targets, err := s.stationBindTargets(ctx, wo.ProductCode, "", processCode)
targets, err := s.stationBindTargets(ctx, client, wo.ProductCode, "", processCode)
if err != nil {
return "", err
}
@@ -200,7 +200,7 @@ func (s *Service) validateStationBinds(ctx context.Context, sn string, processCo
return "", nil // 该工序未配置装配物料 → 不校验
}
// 已绑定明细:按 物料编码 → 绑定值集合
binds, _ := s.ctx.EntClient.WorkpieceBind.Query().
binds, _ := client.WorkpieceBind.Query().
Where(workpiecebind.Sn(sn), workpiecebind.ProcessCode(processCode)).All(ctx)
boundMap := map[string]map[string]bool{}
for _, b := range binds {
@@ -234,14 +234,14 @@ func (s *Service) validateStationBinds(ctx context.Context, sn string, processCo
}
// validateAllProcessBinds 完工兜底:校验工件工艺路线中每个工序的装配物料齐套
func (s *Service) validateAllProcessBinds(ctx context.Context, sn string) (string, error) {
wp, _, err := s.workpieceProduct(ctx, sn)
func (s *Service) validateAllProcessBinds(ctx context.Context, client *ent.Client, sn string) (string, error) {
wp, _, err := s.workpieceProduct(ctx, client, sn)
if err != nil {
return "", err
}
missing := []string{}
for _, code := range ParseProcessSeq(wp.ProcessSeq) {
msg, err := s.validateStationBinds(ctx, sn, code)
msg, err := s.validateStationBinds(ctx, client, sn, code)
if err != nil {
return "", err
}
@@ -277,7 +277,7 @@ func (s *Service) BuildBindTrace(ctx context.Context, sn string) (*BindTraceVO,
if err != nil {
return nil, err
}
missing, err := s.validateAllProcessBinds(ctx, sn)
missing, err := s.validateAllProcessBinds(ctx, s.ctx.EntClient, sn)
if err != nil {
return nil, err
}
@@ -312,11 +312,11 @@ type StationBindPanel struct {
// StationBindPanelData 供工位终端/手动报工页在报工前拉取:本工件本工序该装什么、已装什么
func (s *Service) StationBindPanelData(ctx context.Context, sn string, processCode int) (*StationBindPanel, error) {
_, wo, err := s.workpieceProduct(ctx, sn)
_, wo, err := s.workpieceProduct(ctx, s.ctx.EntClient, sn)
if err != nil {
return nil, err
}
targets, err := s.stationBindTargets(ctx, wo.ProductCode, "", processCode)
targets, err := s.stationBindTargets(ctx, s.ctx.EntClient, wo.ProductCode, "", processCode)
if err != nil {
return nil, err
}
@@ -362,7 +362,7 @@ func (s *Service) StationBindPanelData(ctx context.Context, sn string, processCo
}
panel.Complete = allComplete
if !allComplete {
panel.Missing, _ = s.validateStationBinds(ctx, sn, processCode)
panel.Missing, _ = s.validateStationBinds(ctx, s.ctx.EntClient, sn, processCode)
}
// 最近绑定(仅展示用)
sort.Slice(allBinds, func(i, j int) bool { return allBinds[i].ID > allBinds[j].ID })