feat: 完成产线MES系统全流程功能迭代与优化
本提交完成了多个核心模块的功能完善与业务对齐: 1. 工位终端:固定工位配置、移除自选工位逻辑、适配配置化工位号 2. MES核心:重构工位组合逻辑、新增工单/流程状态管理、补全报工/追溯逻辑 3. WMS客户端:新增修改密码功能、优化帮助弹窗逻辑 4. 文档与权限:补充完整操作手册、统一菜单名称与权限描述 5. 修复多业务校验:排产数量校验、工单状态校验、工位绑定规则
This commit is contained in:
@@ -3,6 +3,7 @@ package logic
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
@@ -18,33 +19,34 @@ import (
|
||||
// ---------- 工艺流程(块3) ----------
|
||||
|
||||
type FlowReq struct {
|
||||
Id int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
ProcessCode int `json:"processCode"`
|
||||
PdfFile string `json:"pdfFile"`
|
||||
Status string `json:"status"`
|
||||
Remark string `json:"remark"`
|
||||
Steps []StepItemReq `json:"steps"`
|
||||
Id int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
StationNo int `json:"stationNo"`
|
||||
PdfFile string `json:"pdfFile"`
|
||||
Status string `json:"status"`
|
||||
Remark string `json:"remark"`
|
||||
Steps []StepItemReq `json:"steps"`
|
||||
}
|
||||
|
||||
// FlowVO 流程图视图(含步骤模板 + 绑定该流程的工位号)
|
||||
// FlowVO 工艺流程视图(含工序步骤 + 绑定该流程的工位号)
|
||||
type FlowVO struct {
|
||||
Id int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
ProcessCode int `json:"processCode"`
|
||||
PdfFile string `json:"pdfFile"`
|
||||
Status string `json:"status"`
|
||||
Remark string `json:"remark"`
|
||||
Stations []int `json:"stations"`
|
||||
Steps []*ProcessStepTemplate `json:"steps"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
Id int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
StationNo int `json:"stationNo"`
|
||||
PdfFile string `json:"pdfFile"`
|
||||
Status string `json:"status"`
|
||||
Remark string `json:"remark"`
|
||||
Stations []int `json:"stations"`
|
||||
Steps []*ProcessStepTemplate `json:"steps"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
}
|
||||
|
||||
func (s *Service) ListFlows(ctx context.Context, processCode int) ([]*FlowVO, error) {
|
||||
// ListFlows 查询工艺流程(可按工位号过滤)
|
||||
func (s *Service) ListFlows(ctx context.Context, stationNo int) ([]*FlowVO, error) {
|
||||
q := s.ctx.EntClient.ProcessFlow.Query().
|
||||
Order(ent.Asc(processflow.FieldProcessCode), ent.Asc(processflow.FieldID))
|
||||
if processCode > 0 {
|
||||
q = q.Where(processflow.ProcessCode(processCode))
|
||||
if stationNo > 0 {
|
||||
q = q.Where(processflow.ProcessCode(stationNo))
|
||||
}
|
||||
flows, err := q.All(ctx)
|
||||
if err != nil {
|
||||
@@ -54,7 +56,7 @@ func (s *Service) ListFlows(ctx context.Context, processCode int) ([]*FlowVO, er
|
||||
out := make([]*FlowVO, 0, len(flows))
|
||||
for _, f := range flows {
|
||||
vo := &FlowVO{
|
||||
Id: f.ID, Name: f.Name, ProcessCode: f.ProcessCode,
|
||||
Id: f.ID, Name: f.Name, StationNo: f.ProcessCode,
|
||||
PdfFile: f.PdfFile, Status: f.Status, Remark: f.Remark,
|
||||
Stations: []int{}, Steps: s.flowSteps(ctx, f.ID, f.ProcessCode), CreatedAt: f.CreatedAt,
|
||||
}
|
||||
@@ -69,17 +71,36 @@ func (s *Service) ListFlows(ctx context.Context, processCode int) ([]*FlowVO, er
|
||||
}
|
||||
|
||||
func (s *Service) SaveFlow(ctx context.Context, req FlowReq, operator string) error {
|
||||
if req.Name == "" || req.ProcessCode <= 0 {
|
||||
return errors.New("流程名称与工序编号必填")
|
||||
if req.Name == "" {
|
||||
return errors.New("流程名称必填")
|
||||
}
|
||||
if req.StationNo < 1 || req.StationNo > 12 {
|
||||
return errors.New("请选择工位号(1~12,固定12个工位)")
|
||||
}
|
||||
status := req.Status
|
||||
if status == "" {
|
||||
status = "ACTIVE"
|
||||
}
|
||||
oldStationNo := 0
|
||||
if req.Id > 0 {
|
||||
if old, err := s.ctx.EntClient.ProcessFlow.Get(ctx, req.Id); err == nil {
|
||||
oldStationNo = old.ProcessCode
|
||||
}
|
||||
}
|
||||
if status == "ACTIVE" {
|
||||
q := s.ctx.EntClient.ProcessFlow.Query().
|
||||
Where(processflow.ProcessCode(req.StationNo), processflow.Status("ACTIVE"))
|
||||
if req.Id > 0 {
|
||||
q = q.Where(processflow.IDNEQ(req.Id))
|
||||
}
|
||||
if exist, err := q.Exist(ctx); err == nil && exist {
|
||||
return fmt.Errorf("工位 %d 已存在启用的工艺流程,请先停用旧流程", req.StationNo)
|
||||
}
|
||||
}
|
||||
var flowID int
|
||||
if req.Id > 0 {
|
||||
upd := s.ctx.EntClient.ProcessFlow.UpdateOneID(req.Id).
|
||||
SetName(req.Name).SetProcessCode(req.ProcessCode).SetStatus(status).SetRemark(req.Remark)
|
||||
SetName(req.Name).SetProcessCode(req.StationNo).SetStatus(status).SetRemark(req.Remark)
|
||||
if req.PdfFile != "" {
|
||||
upd.SetPdfFile(req.PdfFile)
|
||||
}
|
||||
@@ -89,21 +110,35 @@ func (s *Service) SaveFlow(ctx context.Context, req FlowReq, operator string) er
|
||||
flowID = req.Id
|
||||
} else {
|
||||
f, err := s.ctx.EntClient.ProcessFlow.Create().
|
||||
SetName(req.Name).SetProcessCode(req.ProcessCode).SetPdfFile(req.PdfFile).
|
||||
SetName(req.Name).SetProcessCode(req.StationNo).SetPdfFile(req.PdfFile).
|
||||
SetStatus(status).SetRemark(req.Remark).Save(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
flowID = f.ID
|
||||
}
|
||||
// 覆盖式重建该流程的步骤模板及考核标准
|
||||
// 工位号变更:解绑旧工位
|
||||
if oldStationNo > 0 && oldStationNo != req.StationNo {
|
||||
_, _ = s.ctx.EntClient.Station.Update().
|
||||
Where(station.StationNo(oldStationNo), station.FlowId(flowID)).
|
||||
SetFlowId(0).Save(ctx)
|
||||
}
|
||||
// 绑定同步:工位N 绑定该启用流程
|
||||
if status == "ACTIVE" {
|
||||
s.syncStationFlow(ctx, req.StationNo, flowID)
|
||||
} else {
|
||||
_, _ = s.ctx.EntClient.Station.Update().
|
||||
Where(station.StationNo(req.StationNo), station.FlowId(flowID)).
|
||||
SetFlowId(0).Save(ctx)
|
||||
}
|
||||
// 覆盖式重建该流程的工序步骤及考核标准
|
||||
_, _ = s.ctx.EntClient.ProcessStep.Delete().Where(processstep.FlowId(flowID)).Exec(ctx)
|
||||
for _, st := range req.Steps {
|
||||
if st.Name == "" {
|
||||
continue
|
||||
}
|
||||
rec, err := s.ctx.EntClient.ProcessStep.Create().
|
||||
SetFlowId(flowID).SetProcessCode(req.ProcessCode).SetSeq(st.Seq).SetName(st.Name).
|
||||
SetFlowId(flowID).SetProcessCode(req.StationNo).SetSeq(st.Seq).SetName(st.Name).
|
||||
SetCollectType(st.CollectType).SetIsTorque(st.IsTorque).SetRemark(st.Remark).Save(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -127,10 +162,22 @@ func (s *Service) SaveFlow(ctx context.Context, req FlowReq, operator string) er
|
||||
}
|
||||
}
|
||||
s.ctx.EventLog.Write(ctx, "process.flow.save", "", operator, "process_flow", "", "维护工艺流程",
|
||||
map[string]any{"flowId": flowID, "processCode": req.ProcessCode, "steps": len(req.Steps)})
|
||||
map[string]any{"flowId": flowID, "stationNo": req.StationNo, "steps": len(req.Steps)})
|
||||
return nil
|
||||
}
|
||||
|
||||
// syncStationFlow 同步工位绑定:工位 stationNo 绑定到 flowID(无工位记录时自动创建)
|
||||
func (s *Service) syncStationFlow(ctx context.Context, stationNo, flowID int) {
|
||||
st, err := s.ctx.EntClient.Station.Query().Where(station.StationNo(stationNo)).First(ctx)
|
||||
if err != nil {
|
||||
_, _ = s.ctx.EntClient.Station.Create().
|
||||
SetStationNo(stationNo).SetName(fmt.Sprintf("工位%d", stationNo)).
|
||||
SetFlowId(flowID).Save(ctx)
|
||||
return
|
||||
}
|
||||
_, _ = s.ctx.EntClient.Station.UpdateOneID(st.ID).SetFlowId(flowID).Save(ctx)
|
||||
}
|
||||
|
||||
func (s *Service) DeleteFlow(ctx context.Context, id int, operator string) error {
|
||||
// 有工位绑定的流程不允许删除
|
||||
cnt, err := s.ctx.EntClient.Station.Query().Where(station.FlowId(id)).Count(ctx)
|
||||
@@ -142,7 +189,7 @@ func (s *Service) DeleteFlow(ctx context.Context, id int, operator string) error
|
||||
return s.ctx.EntClient.ProcessFlow.DeleteOneID(id).Exec(ctx)
|
||||
}
|
||||
|
||||
// flowSteps 查询某流程的步骤模板(含考核标准)
|
||||
// flowSteps 查询某流程的工序步骤(含考核标准)
|
||||
func (s *Service) flowSteps(ctx context.Context, flowId, processCode int) []*ProcessStepTemplate {
|
||||
steps, _ := s.ctx.EntClient.ProcessStep.Query().
|
||||
Where(processstep.FlowId(flowId)).Order(ent.Asc(processstep.FieldSeq)).All(ctx)
|
||||
@@ -214,18 +261,65 @@ func (s *Service) SaveStation(ctx context.Context, req StationReq, operator stri
|
||||
st, err := s.ctx.EntClient.Station.Query().Where(station.StationNo(req.StationNo)).First(ctx)
|
||||
if err != nil {
|
||||
if _, err := s.ctx.EntClient.Station.Create().
|
||||
SetStationNo(req.StationNo).SetName(req.Name).SetFlowId(req.FlowId).
|
||||
SetStatus("ENABLED").Save(ctx); err != nil {
|
||||
SetStationNo(req.StationNo).SetName(req.Name).Save(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if _, err := s.ctx.EntClient.Station.UpdateOneID(st.ID).
|
||||
SetName(req.Name).SetFlowId(req.FlowId).Save(ctx); err != nil {
|
||||
// 仅改名称:flowId 不传时保留原绑定
|
||||
upd := s.ctx.EntClient.Station.UpdateOneID(st.ID).SetName(req.Name)
|
||||
if req.FlowId > 0 {
|
||||
flow, fErr := s.ctx.EntClient.ProcessFlow.Get(ctx, req.FlowId)
|
||||
if fErr != nil {
|
||||
return errors.New("所选工艺流程不存在")
|
||||
}
|
||||
if flow.Status != "ACTIVE" {
|
||||
return errors.New("该工艺流程已停用,请先启用后再绑定工位")
|
||||
}
|
||||
upd.SetFlowId(req.FlowId)
|
||||
}
|
||||
if _, err := upd.Save(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
s.ctx.EventLog.Write(ctx, "station.save", "", operator, "station", "", "维护工位绑定",
|
||||
map[string]any{"stationNo": req.StationNo, "flowId": req.FlowId})
|
||||
s.ctx.EventLog.Write(ctx, "station.save", "", operator, "station", "", "维护工位",
|
||||
map[string]any{"stationNo": req.StationNo})
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetFlowStatus 工艺流程启用/停用。
|
||||
// 停用:自动解绑对应工位;启用:校验该工位无其他启用流程后自动绑定。
|
||||
func (s *Service) SetFlowStatus(ctx context.Context, id int, status, operator string) error {
|
||||
if id <= 0 {
|
||||
return errors.New("缺少 id")
|
||||
}
|
||||
if status != "ACTIVE" && status != "INACTIVE" {
|
||||
return errors.New("非法状态")
|
||||
}
|
||||
flow, err := s.ctx.EntClient.ProcessFlow.Get(ctx, id)
|
||||
if err != nil {
|
||||
return errors.New("工艺流程不存在")
|
||||
}
|
||||
if status == "INACTIVE" {
|
||||
// 自动解绑该流程对应的工位
|
||||
_, _ = s.ctx.EntClient.Station.Update().
|
||||
Where(station.StationNo(flow.ProcessCode), station.FlowId(flow.ID)).
|
||||
SetFlowId(0).Save(ctx)
|
||||
} else {
|
||||
q := s.ctx.EntClient.ProcessFlow.Query().
|
||||
Where(processflow.ProcessCode(flow.ProcessCode), processflow.Status("ACTIVE"), processflow.IDNEQ(flow.ID))
|
||||
if exist, err := q.Exist(ctx); err == nil && exist {
|
||||
return fmt.Errorf("工位 %d 已存在其他启用的工艺流程,请先停用旧流程", flow.ProcessCode)
|
||||
}
|
||||
s.syncStationFlow(ctx, flow.ProcessCode, flow.ID)
|
||||
}
|
||||
if err := s.ctx.EntClient.ProcessFlow.UpdateOneID(id).SetStatus(status).Exec(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
action := "停用工艺流程"
|
||||
if status == "ACTIVE" {
|
||||
action = "启用工艺流程"
|
||||
}
|
||||
s.ctx.EventLog.Write(ctx, "process.flow.status", "", operator, "process_flow", "", action, map[string]any{"flowId": id, "status": status, "name": flow.Name, "stationNo": flow.ProcessCode})
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -264,6 +358,7 @@ func (s *Service) StationTask(ctx context.Context, stationNo int) (map[string]an
|
||||
|
||||
type WorkloadRow struct {
|
||||
Operator string `json:"operator"`
|
||||
StationNo string `json:"stationNo"`
|
||||
Date string `json:"date"`
|
||||
ProcessCode int `json:"processCode"`
|
||||
ProcessName string `json:"processName"`
|
||||
@@ -272,11 +367,14 @@ type WorkloadRow struct {
|
||||
NgCount int `json:"ngCount"`
|
||||
}
|
||||
|
||||
func (s *Service) Workload(ctx context.Context, operator, from, to string) (map[string]any, error) {
|
||||
func (s *Service) Workload(ctx context.Context, operator, stationNo, from, to string) (map[string]any, error) {
|
||||
q := s.ctx.EntClient.WorkpieceProcess.Query()
|
||||
if operator != "" {
|
||||
q = q.Where(workpieceprocess.Operator(operator))
|
||||
}
|
||||
if stationNo != "" {
|
||||
q = q.Where(workpieceprocess.StationNo(stationNo))
|
||||
}
|
||||
if from != "" {
|
||||
if f, err := time.Parse("2006-01-02", from); err == nil {
|
||||
q = q.Where(workpieceprocess.CreatedAtGTE(f))
|
||||
@@ -291,13 +389,13 @@ func (s *Service) Workload(ctx context.Context, operator, from, to string) (map[
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
type key struct{ op, date string; code int }
|
||||
type key struct{ op, station, date string; code int }
|
||||
agg := map[key]*WorkloadRow{}
|
||||
for _, r := range rows {
|
||||
k := key{r.Operator, r.CreatedAt.Format("2006-01-02"), r.ProcessCode}
|
||||
k := key{r.Operator, r.StationNo, r.CreatedAt.Format("2006-01-02"), r.ProcessCode}
|
||||
row, ok := agg[k]
|
||||
if !ok {
|
||||
row = &WorkloadRow{Operator: r.Operator, Date: k.date, ProcessCode: r.ProcessCode, ProcessName: r.ProcessName}
|
||||
row = &WorkloadRow{Operator: r.Operator, StationNo: r.StationNo, Date: k.date, ProcessCode: r.ProcessCode, ProcessName: r.ProcessName}
|
||||
agg[k] = row
|
||||
}
|
||||
row.DoneCount++
|
||||
@@ -315,6 +413,9 @@ func (s *Service) Workload(ctx context.Context, operator, from, to string) (map[
|
||||
if list[i].Operator != list[j].Operator {
|
||||
return list[i].Operator < list[j].Operator
|
||||
}
|
||||
if list[i].StationNo != list[j].StationNo {
|
||||
return list[i].StationNo < list[j].StationNo
|
||||
}
|
||||
if list[i].Date != list[j].Date {
|
||||
return list[i].Date < list[j].Date
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user