本次提交覆盖多个业务模块的功能完善与体验优化:
1. **鉴权与配置调整**:
- 统一JWT滑动续签逻辑,简化Token存储,移除RefreshToken相关冗余代码
- 调整多项目配置文件中JWT过期时间为3600秒,统一会话闲置窗口
- 工位配置放开1~12限制,改为仅校验大于0
2. **术语统一替换**:全链路将"精密件"替换为"电气件",修正物料管理描述
3. **功能新增**:
- 新增工位类型、工艺路线与产线点位台账模块
- 添加工艺PDF预览面板、工位终端代理转发接口
- 新增操作日志按操作人列表筛选、工位登出日志记录
- 新增PLC移料指令与产线点位状态管理
4. **业务流程优化**:
- 调整BOM物料删除校验逻辑,优化工单备料计算
- 补充物料图号、检测单号等追溯字段
- 完善工艺流程图与工位绑定关系说明
- 优化前端页面文案与交互细节
5. **代码规范与维护**:
- 新增通用工具函数与前端静态资源
- 整理路由权限与中间件逻辑
- 修复部分接口与配置的不兼容问题
182 lines
6.5 KiB
Go
182 lines
6.5 KiB
Go
package logic
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"time"
|
|
|
|
"bj_power_mes/ent"
|
|
"bj_power_mes/ent/station"
|
|
"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)
|
|
s.notifyDashboard()
|
|
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 != "" {
|
|
// 大小写不敏感(2026-09-08 全项目搜索规范)
|
|
q = q.Where(torquerecord.SnEqualFold(sn))
|
|
}
|
|
if workOrderNo != "" {
|
|
q = q.Where(torquerecord.WorkOrderNoEqualFold(workOrderNo))
|
|
}
|
|
return q.Order(ent.Desc(torquerecord.FieldTime)).Limit(500).All(ctx)
|
|
}
|
|
|
|
// AddTorqueRecord 拧紧数据补录(管理端"拧紧查询-拧紧补录"tab 用,定位为设备漏传/手工修正)
|
|
func (s *Service) AddTorqueRecord(ctx context.Context, sn, workOrderNo, screwNo, stationNo string, strain, angle float64, result, reason, operator string) error {
|
|
if sn == "" || workOrderNo == "" {
|
|
return errors.New("工单号与 SN 必填")
|
|
}
|
|
if reason == "" {
|
|
return errors.New("补录原因必填(如:设备漏传/手工修正)")
|
|
}
|
|
if result == "" {
|
|
result = "OK"
|
|
}
|
|
now := time.Now()
|
|
_, err := s.ctx.EntClient.TorqueRecord.Create().
|
|
SetSn(sn).SetWorkOrderNo(workOrderNo).SetScrewNo(screwNo).SetStationNo(stationNo).
|
|
SetStrain(strain).SetTorque(strain).SetAngle(angle).
|
|
SetResult(result).SetOperator(operator + "(补录)").SetTime(now).
|
|
Save(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
s.ctx.EventLog.Write(ctx, "torque.manual.add", workOrderNo, operator, "torque_record", sn,
|
|
"拧紧数据补录 "+workOrderNo+" "+sn, map[string]any{"reason": reason, "result": result, "screwNo": screwNo, "stationNo": stationNo})
|
|
return nil
|
|
}
|
|
|
|
// 计算一块工件某工序的扭矩步骤是否已报
|
|
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"`
|
|
Seq int `json:"seq"`
|
|
Name string `json:"name"`
|
|
CollectType string `json:"collectType"` // AUTO(拧紧枪自动)/MANUAL(手填)/NONE
|
|
IsTorque bool `json:"isTorque"`
|
|
NeedCheck bool `json:"needCheck"`
|
|
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"`
|
|
}
|
|
|
|
// ListStationSteps 按工位号加载该工位绑定且「启用」的工艺流程的工序步骤(含考核标准)。
|
|
// 链路:工位号 → station.flow_id → 工艺流程(绑定关系在「关联工位」页维护,流程可绑任意工位)。
|
|
// 手动报工页:选工位 → 自动带出该工位的工序步骤。
|
|
func (s *Service) ListStationSteps(ctx context.Context, stationNo int) ([]*ProcessStepTemplate, error) {
|
|
if stationNo < 1 {
|
|
return []*ProcessStepTemplate{}, nil
|
|
}
|
|
st, err := s.ctx.EntClient.Station.Query().Where(station.StationNo(stationNo)).First(ctx)
|
|
if err != nil || st.FlowId <= 0 {
|
|
// 该工位未绑定工艺流程:返回空模板(前端提示)
|
|
return []*ProcessStepTemplate{}, nil
|
|
}
|
|
flow, err := s.ctx.EntClient.ProcessFlow.Get(ctx, st.FlowId)
|
|
if err != nil || flow.Status != "ACTIVE" {
|
|
return []*ProcessStepTemplate{}, nil
|
|
}
|
|
return s.flowSteps(ctx, flow.ID), nil
|
|
}
|
|
|
|
// StepItemReq 一次保存的一个步骤(含其考核标准)
|
|
type StepItemReq struct {
|
|
ID int `json:"id"`
|
|
Seq int `json:"seq"`
|
|
Name string `json:"name"`
|
|
CollectType string `json:"collectType"`
|
|
IsTorque bool `json:"isTorque"`
|
|
NeedCheck bool `json:"needCheck"`
|
|
Remark string `json:"remark"`
|
|
Criteria []CriterionVO `json:"criteria"`
|
|
}
|