package logic import ( "context" "errors" "time" "bj_power_mes/ent" "bj_power_mes/ent/processstep" "bj_power_mes/ent/stepcriterion" "bj_power_mes/ent/torquerecord" "bj_power_mes/ent/workpieceprocess" ) type TorqueReq struct { Sn string `json:"sn"` WorkOrder string `json:"workOrder"` WorkOrderNo string `json:"workOrderNo"` StationNo string `json:"stationNo"` ScrewNo string `json:"screwNo"` Strain float64 `json:"strain"` // 扭矩 Torque float64 `json:"torque"` Angle float64 `json:"angle"` Result string `json:"result"` Operator string `json:"operator"` Time time.Time `json:"time"` } // ReportTorque 接收拧紧数据(工位终端/内部接口上报),入库并做扭矩 RANGE 判定写 step_data func (s *Service) ReportTorque(ctx context.Context, req TorqueReq, operator string) error { if req.Sn == "" { return errors.New("sn 不能为空") } if req.Strain == 0 { req.Strain = req.Torque } result := req.Result if result == "" { result = "OK" } workOrderNo := req.WorkOrder if workOrderNo == "" { workOrderNo = req.WorkOrderNo } if req.Time.IsZero() { req.Time = time.Now() } _, err := s.ctx.EntClient.TorqueRecord.Create(). SetWorkOrderNo(workOrderNo).SetSn(req.Sn).SetScrewNo(req.ScrewNo). SetStrain(req.Strain).SetTorque(req.Strain).SetAngle(req.Angle). SetResult(result).SetOperator(req.Operator).SetStationNo(req.StationNo). SetTime(req.Time).Save(ctx) if err != nil { return err } s.ctx.EventLog.Write(ctx, "torque.report", workOrderNo, operator, "torque_record", req.Sn, "接收拧紧数据上报", map[string]any{"strain": req.Strain, "angle": req.Angle, "result": result}) s.evaluateTorqueCriterion(ctx, req.Sn, req.StationNo, req.Strain, req.Angle, operator) return nil } // evaluateTorqueCriterion 依据 step_criterion RANGE 自动判定 OK/NG 写入 step_data func (s *Service) evaluateTorqueCriterion(ctx context.Context, sn, stationNo string, strain, angle float64, operator string) { q := s.ctx.EntClient.StepCriterion.Query().Where(stepcriterion.Name("扭矩")) crits, _ := q.All(ctx) for _, c := range crits { if c.Logic != "RANGE" { continue } ok := true if c.Min != nil && strain < *c.Min { ok = false } if c.Max != nil && strain > *c.Max { ok = false } _, _ = s.ctx.EntClient.StepData.Create(). SetSn(sn).SetStepId(c.StepId).SetCriterionId(c.ID). SetCriterionName(c.Name).SetCriterionLogic(c.Logic). SetValue(strain).SetIsOK(ok).SetOperator(operator).Save(ctx) } } // ListTorqueRecords 查询拧紧数据 func (s *Service) ListTorqueRecords(ctx context.Context, sn, workOrderNo string, from, to time.Time) ([]*ent.TorqueRecord, error) { q := s.ctx.EntClient.TorqueRecord.Query() if sn != "" { q = q.Where(torquerecord.Sn(sn)) } if workOrderNo != "" { q = q.Where(torquerecord.WorkOrderNo(workOrderNo)) } return q.Order(ent.Desc(torquerecord.FieldTime)).Limit(500).All(ctx) } // 计算一块工件某工序的扭矩步骤是否已报 func (s *Service) hasWorkpieceProcess(ctx context.Context, sn string, processCode int) bool { cnt, _ := s.ctx.EntClient.WorkpieceProcess.Query(). Where(workpieceprocess.Sn(sn), workpieceprocess.ProcessCode(processCode)).Count(ctx) return cnt > 0 } // ProcessStepTemplate 下发给前端渲染的参数采集模板 type ProcessStepTemplate struct { ID int `json:"id"` ProcessCode int `json:"processCode"` Seq int `json:"seq"` Name string `json:"name"` CollectType string `json:"collectType"` // AUTO(拧紧枪自动)/MANUAL(手填)/NONE IsTorque bool `json:"isTorque"` Remark string `json:"remark"` Criteria []CriterionVO `json:"criteria"` } // CriterionVO 考核标准视图 type CriterionVO struct { ID int `json:"id"` Name string `json:"name"` Unit string `json:"unit"` Logic string `json:"logic"` // GE/LE/GT/LT/RANGE/EQUAL/NONE Target float64 `json:"target"` Min *float64 `json:"min"` Max *float64 `json:"max"` } // ListProcessSteps 查询某工序的步骤模板(含考核标准),供手动报工页动态渲染参数输入 func (s *Service) ListProcessSteps(ctx context.Context, processCode int) ([]*ProcessStepTemplate, error) { steps, err := s.ctx.EntClient.ProcessStep.Query(). Where(processstep.ProcessCode(processCode)). Order(ent.Asc(processstep.FieldSeq)).All(ctx) if err != nil { return nil, err } out := make([]*ProcessStepTemplate, 0, len(steps)) for _, st := range steps { t := &ProcessStepTemplate{ ID: st.ID, ProcessCode: st.ProcessCode, Seq: st.Seq, Name: st.Name, CollectType: st.CollectType, IsTorque: st.IsTorque, Remark: st.Remark, Criteria: []CriterionVO{}, } crits, _ := s.ctx.EntClient.StepCriterion.Query(). Where(stepcriterion.StepId(st.ID)).All(ctx) for _, c := range crits { t.Criteria = append(t.Criteria, CriterionVO{ ID: c.ID, Name: c.Name, Unit: c.Unit, Logic: c.Logic, Target: c.Target, Min: c.Min, Max: c.Max, }) } out = append(out, t) } return out, nil } // StepItemReq 一次保存的一个步骤(含其考核标准) type StepItemReq struct { ID int `json:"id"` ProcessCode int `json:"processCode"` Seq int `json:"seq"` Name string `json:"name"` CollectType string `json:"collectType"` IsTorque bool `json:"isTorque"` Remark string `json:"remark"` Criteria []CriterionVO `json:"criteria"` } // SaveProcessSteps 覆盖式保存某工序的步骤模板及考核标准(供"工艺参数"维护页调用) func (s *Service) SaveProcessSteps(ctx context.Context, steps []StepItemReq, operator string) error { for _, st := range steps { if st.ProcessCode <= 0 || st.Name == "" { continue } var stepID int if st.ID > 0 { upd := s.ctx.EntClient.ProcessStep.UpdateOneID(st.ID). SetSeq(st.Seq).SetName(st.Name).SetCollectType(st.CollectType). SetIsTorque(st.IsTorque).SetRemark(st.Remark) _, err := upd.Save(ctx) if err != nil { return err } stepID = st.ID } else { rec, err := s.ctx.EntClient.ProcessStep.Create(). SetProcessCode(st.ProcessCode).SetSeq(st.Seq).SetName(st.Name). SetCollectType(st.CollectType).SetIsTorque(st.IsTorque).SetRemark(st.Remark).Save(ctx) if err != nil { return err } stepID = rec.ID } // 覆盖式重建该步骤的考核标准 _, _ = s.ctx.EntClient.StepCriterion.Delete().Where(stepcriterion.StepId(stepID)).Exec(ctx) for _, c := range st.Criteria { if c.Name == "" || c.Logic == "" || c.Logic == "NONE" { continue } b := s.ctx.EntClient.StepCriterion.Create(). SetStepId(stepID).SetName(c.Name).SetUnit(c.Unit).SetLogic(c.Logic). SetTarget(c.Target) if c.Min != nil { b = b.SetMin(*c.Min) } if c.Max != nil { b = b.SetMax(*c.Max) } if err := b.Exec(ctx); err != nil { return err } } } s.ctx.EventLog.Write(ctx, "process.steps.save", "", operator, "process_step", "", "维护工艺参数", map[string]any{"count": len(steps)}) return nil } // AllProcessSteps 查询全部工序的步骤模板(含 12 道工序),供维护页按工序分组展示 func (s *Service) AllProcessSteps(ctx context.Context, processCode int) ([]*ProcessStepTemplate, error) { if processCode > 0 { return s.ListProcessSteps(ctx, processCode) } out := []*ProcessStepTemplate{} for code := 1; code <= 12; code++ { sub, err := s.ListProcessSteps(ctx, code) if err != nil { return nil, err } out = append(out, sub...) } return out, nil }