325 lines
10 KiB
Go
325 lines
10 KiB
Go
package logic
|
||||
|
|
|
|||
|
|
import (
|
|||
|
|
"context"
|
|||
|
|
"errors"
|
|||
|
|
"sort"
|
|||
|
|
"time"
|
|||
|
|
|
|||
|
|
"bj_power_mes/ent"
|
|||
|
|
"bj_power_mes/ent/processflow"
|
|||
|
|
"bj_power_mes/ent/processstep"
|
|||
|
|
"bj_power_mes/ent/station"
|
|||
|
|
"bj_power_mes/ent/stepcriterion"
|
|||
|
|
"bj_power_mes/ent/workorder"
|
|||
|
|
"bj_power_mes/ent/workpieceprocess"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
// ---------- 工艺流程(块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"`
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 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"`
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (s *Service) ListFlows(ctx context.Context, processCode 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))
|
|||
|
|
}
|
|||
|
|
flows, err := q.All(ctx)
|
|||
|
|
if err != nil {
|
|||
|
|
return nil, err
|
|||
|
|
}
|
|||
|
|
stations, _ := s.ctx.EntClient.Station.Query().All(ctx)
|
|||
|
|
out := make([]*FlowVO, 0, len(flows))
|
|||
|
|
for _, f := range flows {
|
|||
|
|
vo := &FlowVO{
|
|||
|
|
Id: f.ID, Name: f.Name, ProcessCode: f.ProcessCode,
|
|||
|
|
PdfFile: f.PdfFile, Status: f.Status, Remark: f.Remark,
|
|||
|
|
Stations: []int{}, Steps: s.flowSteps(ctx, f.ID, f.ProcessCode), CreatedAt: f.CreatedAt,
|
|||
|
|
}
|
|||
|
|
for _, st := range stations {
|
|||
|
|
if st.FlowId == f.ID {
|
|||
|
|
vo.Stations = append(vo.Stations, st.StationNo)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
out = append(out, vo)
|
|||
|
|
}
|
|||
|
|
return out, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (s *Service) SaveFlow(ctx context.Context, req FlowReq, operator string) error {
|
|||
|
|
if req.Name == "" || req.ProcessCode <= 0 {
|
|||
|
|
return errors.New("流程名称与工序编号必填")
|
|||
|
|
}
|
|||
|
|
status := req.Status
|
|||
|
|
if status == "" {
|
|||
|
|
status = "ACTIVE"
|
|||
|
|
}
|
|||
|
|
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)
|
|||
|
|
if req.PdfFile != "" {
|
|||
|
|
upd.SetPdfFile(req.PdfFile)
|
|||
|
|
}
|
|||
|
|
if _, err := upd.Save(ctx); err != nil {
|
|||
|
|
return err
|
|||
|
|
}
|
|||
|
|
flowID = req.Id
|
|||
|
|
} else {
|
|||
|
|
f, err := s.ctx.EntClient.ProcessFlow.Create().
|
|||
|
|
SetName(req.Name).SetProcessCode(req.ProcessCode).SetPdfFile(req.PdfFile).
|
|||
|
|
SetStatus(status).SetRemark(req.Remark).Save(ctx)
|
|||
|
|
if err != nil {
|
|||
|
|
return err
|
|||
|
|
}
|
|||
|
|
flowID = f.ID
|
|||
|
|
}
|
|||
|
|
// 覆盖式重建该流程的步骤模板及考核标准
|
|||
|
|
_, _ = 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).
|
|||
|
|
SetCollectType(st.CollectType).SetIsTorque(st.IsTorque).SetRemark(st.Remark).Save(ctx)
|
|||
|
|
if err != nil {
|
|||
|
|
return err
|
|||
|
|
}
|
|||
|
|
_, _ = s.ctx.EntClient.StepCriterion.Delete().Where(stepcriterion.StepId(rec.ID)).Exec(ctx)
|
|||
|
|
for _, c := range st.Criteria {
|
|||
|
|
if c.Name == "" || c.Logic == "" || c.Logic == "NONE" {
|
|||
|
|
continue
|
|||
|
|
}
|
|||
|
|
b := s.ctx.EntClient.StepCriterion.Create().
|
|||
|
|
SetStepId(rec.ID).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.flow.save", "", operator, "process_flow", "", "维护工艺流程",
|
|||
|
|
map[string]any{"flowId": flowID, "processCode": req.ProcessCode, "steps": len(req.Steps)})
|
|||
|
|
return nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
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)
|
|||
|
|
if err == nil && cnt > 0 {
|
|||
|
|
return errors.New("该流程已被工位绑定,请先解绑工位")
|
|||
|
|
}
|
|||
|
|
_, _ = s.ctx.EntClient.ProcessStep.Delete().Where(processstep.FlowId(id)).Exec(ctx)
|
|||
|
|
s.ctx.EventLog.Write(ctx, "process.flow.delete", "", operator, "process_flow", "", "删除工艺流程", map[string]any{"flowId": id})
|
|||
|
|
return s.ctx.EntClient.ProcessFlow.DeleteOneID(id).Exec(ctx)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 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)
|
|||
|
|
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
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ---------- 工位主数据(块3) ----------
|
|||
|
|
|
|||
|
|
type StationReq struct {
|
|||
|
|
Id int `json:"id"`
|
|||
|
|
StationNo int `json:"stationNo"`
|
|||
|
|
Name string `json:"name"`
|
|||
|
|
FlowId int `json:"flowId"`
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
type StationVO struct {
|
|||
|
|
Id int `json:"id"`
|
|||
|
|
StationNo int `json:"stationNo"`
|
|||
|
|
Name string `json:"name"`
|
|||
|
|
FlowId int `json:"flowId"`
|
|||
|
|
FlowName string `json:"flowName"`
|
|||
|
|
ProcessCode int `json:"processCode"`
|
|||
|
|
Status string `json:"status"`
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (s *Service) ListStations(ctx context.Context) ([]*StationVO, error) {
|
|||
|
|
stas, err := s.ctx.EntClient.Station.Query().Order(ent.Asc(station.FieldStationNo)).All(ctx)
|
|||
|
|
if err != nil {
|
|||
|
|
return nil, err
|
|||
|
|
}
|
|||
|
|
flows, _ := s.ctx.EntClient.ProcessFlow.Query().All(ctx)
|
|||
|
|
flowMap := map[int]*ent.ProcessFlow{}
|
|||
|
|
for _, f := range flows {
|
|||
|
|
flowMap[f.ID] = f
|
|||
|
|
}
|
|||
|
|
out := make([]*StationVO, 0, len(stas))
|
|||
|
|
for _, st := range stas {
|
|||
|
|
vo := &StationVO{Id: st.ID, StationNo: st.StationNo, Name: st.Name, FlowId: st.FlowId, Status: st.Status}
|
|||
|
|
if f, ok := flowMap[st.FlowId]; ok {
|
|||
|
|
vo.FlowName = f.Name
|
|||
|
|
vo.ProcessCode = f.ProcessCode
|
|||
|
|
}
|
|||
|
|
out = append(out, vo)
|
|||
|
|
}
|
|||
|
|
return out, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (s *Service) SaveStation(ctx context.Context, req StationReq, operator string) error {
|
|||
|
|
if req.StationNo <= 0 || req.StationNo > 12 {
|
|||
|
|
return errors.New("工位号必须在 1~12")
|
|||
|
|
}
|
|||
|
|
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 {
|
|||
|
|
return err
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
if _, err := s.ctx.EntClient.Station.UpdateOneID(st.ID).
|
|||
|
|
SetName(req.Name).SetFlowId(req.FlowId).Save(ctx); err != nil {
|
|||
|
|
return err
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
s.ctx.EventLog.Write(ctx, "station.save", "", operator, "station", "", "维护工位绑定",
|
|||
|
|
map[string]any{"stationNo": req.StationNo, "flowId": req.FlowId})
|
|||
|
|
return nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ---------- 工位任务(块4/5:工位终端拉取) ----------
|
|||
|
|
|
|||
|
|
func (s *Service) StationTask(ctx context.Context, stationNo int) (map[string]any, error) {
|
|||
|
|
st, err := s.ctx.EntClient.Station.Query().Where(station.StationNo(stationNo)).First(ctx)
|
|||
|
|
if err != nil {
|
|||
|
|
return nil, errors.New("工位不存在或未配置")
|
|||
|
|
}
|
|||
|
|
var flow *ent.ProcessFlow
|
|||
|
|
if st.FlowId > 0 {
|
|||
|
|
flow, _ = s.ctx.EntClient.ProcessFlow.Get(ctx, st.FlowId)
|
|||
|
|
}
|
|||
|
|
steps := []*ProcessStepTemplate{}
|
|||
|
|
if flow != nil {
|
|||
|
|
steps = s.flowSteps(ctx, flow.ID, flow.ProcessCode)
|
|||
|
|
}
|
|||
|
|
orderNos := []string{}
|
|||
|
|
wos, _ := s.ctx.EntClient.WorkOrder.Query().
|
|||
|
|
Where(workorder.StatusIn("CREATED", "RELEASED", "IN_PROGRESS")).
|
|||
|
|
Order(ent.Asc(workorder.FieldID)).Limit(10).All(ctx)
|
|||
|
|
for _, w := range wos {
|
|||
|
|
orderNos = append(orderNos, w.WorkOrderNo)
|
|||
|
|
}
|
|||
|
|
return map[string]any{
|
|||
|
|
"stationNo": st.StationNo,
|
|||
|
|
"stationName": st.Name,
|
|||
|
|
"flow": flow,
|
|||
|
|
"steps": steps,
|
|||
|
|
"orderNos": orderNos,
|
|||
|
|
}, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ---------- 工作量/绩效报表(块6) ----------
|
|||
|
|
|
|||
|
|
type WorkloadRow struct {
|
|||
|
|
Operator string `json:"operator"`
|
|||
|
|
Date string `json:"date"`
|
|||
|
|
ProcessCode int `json:"processCode"`
|
|||
|
|
ProcessName string `json:"processName"`
|
|||
|
|
DoneCount int `json:"doneCount"`
|
|||
|
|
OkCount int `json:"okCount"`
|
|||
|
|
NgCount int `json:"ngCount"`
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (s *Service) Workload(ctx context.Context, operator, from, to string) (map[string]any, error) {
|
|||
|
|
q := s.ctx.EntClient.WorkpieceProcess.Query()
|
|||
|
|
if operator != "" {
|
|||
|
|
q = q.Where(workpieceprocess.Operator(operator))
|
|||
|
|
}
|
|||
|
|
if from != "" {
|
|||
|
|
if f, err := time.Parse("2006-01-02", from); err == nil {
|
|||
|
|
q = q.Where(workpieceprocess.CreatedAtGTE(f))
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
if to != "" {
|
|||
|
|
if t, err := time.Parse("2006-01-02", to); err == nil {
|
|||
|
|
q = q.Where(workpieceprocess.CreatedAtLT(t.Add(24 * time.Hour)))
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
rows, err := q.All(ctx)
|
|||
|
|
if err != nil {
|
|||
|
|
return nil, err
|
|||
|
|
}
|
|||
|
|
type key struct{ op, date string; code int }
|
|||
|
|
agg := map[key]*WorkloadRow{}
|
|||
|
|
for _, r := range rows {
|
|||
|
|
k := key{r.Operator, 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}
|
|||
|
|
agg[k] = row
|
|||
|
|
}
|
|||
|
|
row.DoneCount++
|
|||
|
|
if r.Result == "OK" {
|
|||
|
|
row.OkCount++
|
|||
|
|
} else {
|
|||
|
|
row.NgCount++
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
list := make([]*WorkloadRow, 0, len(agg))
|
|||
|
|
for _, v := range agg {
|
|||
|
|
list = append(list, v)
|
|||
|
|
}
|
|||
|
|
sort.Slice(list, func(i, j int) bool {
|
|||
|
|
if list[i].Operator != list[j].Operator {
|
|||
|
|
return list[i].Operator < list[j].Operator
|
|||
|
|
}
|
|||
|
|
if list[i].Date != list[j].Date {
|
|||
|
|
return list[i].Date < list[j].Date
|
|||
|
|
}
|
|||
|
|
return list[i].ProcessCode < list[j].ProcessCode
|
|||
|
|
})
|
|||
|
|
return map[string]any{"rows": list, "detail": rows}, nil
|
|||
|
|
}
|