Files
bj_power/bj_power_mes/internal/logic/torque.go
T
SunYF ebf901ad16 chore: 完成多系统功能迭代与优化
1. 通用优化:
   - 统一系统标题为"库房客户端"/"MES产线控制",移除北自所前缀
   - 调整内置账号密码为统一123456,优化密码校验逻辑
   - 新增数据库自动创建逻辑,简化部署流程
   - 修复日志时间格式配置,优化日志输出
   - 新增纯数字密码安全提示

2. MES系统优化:
   - 新增日排产管理功能,支持增删改查与自动算料生成备料单
   - 重构BOM模块,改为按产品编码维护而非工单维度
   - 新增工艺参数模板配置与手动报工页面
   - 新增产品类型自动编码功能
   - 优化菜单结构,调整工单管理、扫码报工等页面名称与路由
   - 新增删除日排产接口与权限保护
   - 修复物料清单查询逻辑,适配新BOM结构
   - 新增refreshToken支持,优化登录会话管理

3. WMS系统优化:
   - 新增基础数据维护页面,支持区域与物料档案管理
   - 新增工单列表下拉接口,对接MES获取未完成工单优先展示
   - 优化入库管理提示文案,替换英文提示为中文
   - 修复精密件入库校验逻辑,优化错误提示
   - 优化Excel导入功能,新增备注字段支持
   - 优化登录页面与菜单文案,统一备料台账名称
   - 新增自动打开浏览器功能,优化客户端启动体验
   - 修复备料出库页面查询提示文案
   - 新增WMS与MES对接配置,完善内部API调用逻辑

4. 其他优化:
   - 删除冗余的旧版静态资源文件,更新资源引用路径
   - 新增帮助文档,完善基础数据模块说明
   - 修复多处文案不统一、英文残留问题
2026-08-29 15:30:25 +08:00

229 lines
7.5 KiB
Go

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
}