Files
bj_power/bj_power_mes/internal/logic/workpiece.go
T
2026-08-28 15:06:01 +08:00

240 lines
7.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package logic
import (
"context"
"errors"
"strconv"
"time"
"bj_power_mes/ent"
"bj_power_mes/ent/associationtrace"
"bj_power_mes/ent/stepcriterion"
"bj_power_mes/ent/stepdata"
"bj_power_mes/ent/torquerecord"
"bj_power_mes/ent/workorder"
"bj_power_mes/ent/workpiece"
"bj_power_mes/ent/workpieceprocess"
)
func itoa(n int) string { return strconv.Itoa(n) }
func processName(code int) string {
names := map[int]string{1: "装配一", 2: "装配二", 3: "装配三", 4: "装配四", 5: "装配五", 6: "装配六", 7: "装配七", 8: "装配八", 9: "装配九", 10: "装配十", 11: "装配十一", 12: "装配十二"}
if n, ok := names[code]; ok {
return n
}
return "工序" + itoa(code)
}
// evalLogic 依据考核逻辑判定
func evalLogic(c *ent.StepCriterion, v float64) bool {
switch c.Logic {
case "GE":
return v >= c.Target
case "LE":
return v <= c.Target
case "GT":
return v > c.Target
case "LT":
return v < c.Target
case "RANGE":
l, r := true, true
if c.Min != nil {
l = v >= *c.Min
}
if c.Max != nil {
r = v <= *c.Max
}
return l && r
case "EQUAL":
return v == c.Target
default:
return true
}
}
type StepDataReq struct {
StepId int `json:"stepId"`
Name string `json:"name"`
Value float64 `json:"value"`
Text string `json:"text"`
}
type OnlineReq struct {
Sn string `json:"sn"`
OrderNo string `json:"orderNo"`
WorkOrderId int `json:"workOrderId"`
ProcessSeq string `json:"processSeq"`
}
// OnlineWorkpiece 工件进线登记
func (s *Service) OnlineWorkpiece(ctx context.Context, req OnlineReq, operator string) error {
if req.Sn == "" {
return errors.New("sn 不能为空")
}
seq := req.ProcessSeq
if seq == "" {
seq = "123456789101112"
}
if req.WorkOrderId == 0 {
if wo, err := s.ctx.EntClient.WorkOrder.Query().Where(workorder.WorkOrderNo(req.OrderNo)).First(ctx); err == nil {
req.WorkOrderId = wo.ID
if req.ProcessSeq == "" && wo.ProcessSeq != "" {
seq = wo.ProcessSeq
}
}
}
err := s.ctx.EntClient.Workpiece.Create().
SetSn(req.Sn).SetOrderNo(req.OrderNo).SetWorkOrderId(req.WorkOrderId).
SetProcessSeq(seq).SetStatus("ONLINE").SetOnlineAt(time.Now()).Exec(ctx)
if err != nil {
return err
}
s.ctx.EventLog.Write(ctx, "workpiece.online", req.OrderNo, operator, "workpiece", req.Sn, "工件进线登记", nil)
return nil
}
type ReportProcessReq struct {
Sn string `json:"sn"`
ProcessCode int `json:"processCode"`
StationNo int `json:"stationNo"`
Steps []StepDataReq `json:"steps"`
}
// ReportProcess 工位工序报工:写工序实绩 + 步骤考核(step_data,扭矩在RANGE自动判定 OK/NG
func (s *Service) ReportProcess(ctx context.Context, req ReportProcessReq, operator string) error {
if req.Sn == "" || req.ProcessCode == 0 {
return errors.New("sn 与 processCode 必填")
}
wp, err := s.ctx.EntClient.Workpiece.Query().Where(workpiece.Sn(req.Sn)).First(ctx)
if err != nil {
return errors.New("工件未进线登记")
}
now := time.Now()
proc, err := s.ctx.EntClient.WorkpieceProcess.Create().
SetSn(req.Sn).SetProcessCode(req.ProcessCode).
SetStationNo(itoa(req.StationNo)).SetOrderNo(wp.OrderNo).
SetProcessName(processName(req.ProcessCode)).
SetStatus("DONE").SetOperator(operator).SetResult("OK").
SetStartedAt(now).SetEndedAt(now).Save(ctx)
if err != nil {
return err
}
allOK := true
for _, st := range req.Steps {
if ok, e := s.writeStepData(ctx, req.Sn, proc.ID, st, operator); e == nil && !ok {
allOK = false
}
}
if bad, _ := s.ctx.EntClient.StepData.Query().
Where(stepdata.ProcessId(proc.ID), stepdata.IsOK(false)).Exist(ctx); bad {
allOK = false
}
result := "OK"
if !allOK {
result = "NG"
}
_, _ = s.ctx.EntClient.WorkpieceProcess.UpdateOneID(proc.ID).SetResult(result).Save(ctx)
_, _ = s.ctx.EntClient.Workpiece.UpdateOneID(wp.ID).
SetCurrentProcess(req.ProcessCode).SetStatus("PROCESSING").Save(ctx)
s.ctx.EventLog.Write(ctx, "workpiece.process.report", wp.OrderNo, operator, "workpiece", req.Sn,
"工位工序报工", map[string]any{"processCode": req.ProcessCode, "result": result})
return nil
}
// writeStepData 写一条步骤考核数据,按标准自动判定是否合格
func (s *Service) writeStepData(ctx context.Context, sn string, processId int, st StepDataReq, operator string) (bool, error) {
ok := true
crits, _ := s.ctx.EntClient.StepCriterion.Query().Where(stepcriterion.StepId(st.StepId)).All(ctx)
for _, c := range crits {
if !evalLogic(c, st.Value) {
ok = false
}
}
var cname, clogic string
cid := 0
if len(crits) > 0 {
cname = crits[0].Name
clogic = crits[0].Logic
cid = crits[0].ID
}
_, err := s.ctx.EntClient.StepData.Create().
SetSn(sn).SetProcessId(processId).SetStepId(st.StepId).
SetStepName(st.Name).SetCriterionId(cid).SetCriterionName(cname).
SetCriterionLogic(clogic).SetValue(st.Value).SetValueText(st.Text).
SetIsOK(ok).SetOperator(operator).Save(ctx)
return ok, err
}
// DoneReq 完工 + 成品关联追溯
type DoneReq struct {
Sn string `json:"sn"`
BatchItems []string `json:"batchItems"` // 结构件批次号
SerialItems []string `json:"serialItems"` // 精密件SN
}
// DoneWorkpiece 完工:登记成品并生成关联追溯
func (s *Service) DoneWorkpiece(ctx context.Context, req DoneReq, operator string) error {
wp, err := s.ctx.EntClient.Workpiece.Query().Where(workpiece.Sn(req.Sn)).First(ctx)
if err != nil {
return errors.New("工件不存在")
}
now := time.Now()
_, _ = s.ctx.EntClient.Workpiece.UpdateOneID(wp.ID).
SetStatus("DONE").SetDoneAt(now).Save(ctx)
existID, _ := s.ctx.EntClient.AssociationTrace.Query().
Where(associationtrace.FinishedSn(req.Sn)).FirstID(ctx)
if existID > 0 {
_, _ = s.ctx.EntClient.AssociationTrace.UpdateOneID(existID).
SetBatchItems(req.BatchItems).SetSerialItems(req.SerialItems).SetOperator(operator).Save(ctx)
} else {
_, err = s.ctx.EntClient.AssociationTrace.Create().
SetFinishedSn(req.Sn).SetOrderNo(wp.OrderNo).SetProcessCode(wp.ProcessSeq).
SetBatchItems(req.BatchItems).SetSerialItems(req.SerialItems).
SetOperator(operator).Save(ctx)
if err != nil {
return err
}
}
s.bumpWorkOrderProgress(ctx, wp.OrderNo, req.Sn)
s.ctx.EventLog.Write(ctx, "workpiece.done", wp.OrderNo, operator, "workpiece", req.Sn, "完工+关联追溯", map[string]any{"batchItems": req.BatchItems, "serialItems": req.SerialItems})
return nil
}
// Trace 追溯查询(SN 维度):工序时间线/操作人/步骤数据/物料批次SN/拧紧数据
func (s *Service) Trace(ctx context.Context, sn string) (map[string]any, error) {
wp, err := s.ctx.EntClient.Workpiece.Query().Where(workpiece.Sn(sn)).First(ctx)
if err != nil {
return nil, errors.New("该SN无追溯数据")
}
procList, _ := s.ctx.EntClient.WorkpieceProcess.Query().
Where(workpieceprocess.Sn(sn)).Order(ent.Asc(workpieceprocess.FieldProcessCode)).All(ctx)
steps, _ := s.ctx.EntClient.StepData.Query().
Where(stepdata.Sn(sn)).Order(ent.Desc(stepdata.FieldID)).All(ctx)
assoc, _ := s.ctx.EntClient.AssociationTrace.Query().
Where(associationtrace.FinishedSn(sn)).First(ctx)
torque, _ := s.ctx.EntClient.TorqueRecord.Query().
Where(torquerecord.Sn(sn)).Order(ent.Desc(torquerecord.FieldTime)).Limit(200).All(ctx)
return map[string]any{
"workpiece": wp,
"processTimeline": procList,
"stepData": steps,
"associationTrace": assoc,
"torqueRecords": torque,
}, nil
}
func (s *Service) ListWorkpieces(ctx context.Context, sn, orderNo string) ([]*ent.Workpiece, error) {
q := s.ctx.EntClient.Workpiece.Query()
if sn != "" {
q = q.Where(workpiece.SnContains(sn))
}
if orderNo != "" {
q = q.Where(workpiece.OrderNo(orderNo))
}
return q.Order(ent.Desc(workpiece.FieldID)).Limit(500).All(ctx)
}