Files
bj_power/bj_power_mes/internal/logic/route.go
T

145 lines
4.7 KiB
Go
Raw Normal View History

package logic
import (
"context"
"errors"
"fmt"
"sort"
"strconv"
"strings"
"time"
"bj_power_mes/ent"
"bj_power_mes/ent/station"
)
// ---------- 工单工艺组合(work_order.flow_combination)路线派生 ----------
// TodayStr 当天日期 yyyy-MM-dd(工位停单只认当天)
func TodayStr() string { return time.Now().Format("2006-01-02") }
// FlowItem 工单工艺组合条目:工艺 + 工位(工位号即顺序号,按工位号升序执行)
type FlowItem struct {
FlowId int `json:"flowId"`
StationNo int `json:"stationNo"`
}
// RouteStationsFromWO 解析工单 flow_combination → 组合条目(按工位号升序;空组合返回空切片)
func RouteStationsFromWO(ctx context.Context, client *ent.Client, wo *ent.WorkOrder) ([]FlowItem, error) {
if wo == nil {
return nil, nil
}
items := flowMapsToItems(wo.FlowCombination)
sort.SliceStable(items, func(i, j int) bool { return items[i].StationNo < items[j].StationNo })
return items, nil
}
// flowMapsToItems jsonb 原始值 → 组合条目(过滤 flowId/stationNo 非正的脏条目)
func flowMapsToItems(raw []map[string]int) []FlowItem {
items := make([]FlowItem, 0, len(raw))
for _, m := range raw {
flowId, no := m["flowId"], m["stationNo"]
if flowId > 0 && no > 0 {
items = append(items, FlowItem{FlowId: flowId, StationNo: no})
}
}
return items
}
// flowItemsToMaps 组合条目 → jsonb 存储结构
func flowItemsToMaps(items []FlowItem) []map[string]int {
out := make([]map[string]int, 0, len(items))
for _, it := range items {
out = append(out, map[string]int{"flowId": it.FlowId, "stationNo": it.StationNo})
}
return out
}
// RouteStationsOfWO 组合条目 → 工位号列表(升序;未选工位即跳过)
func RouteStationsOfWO(items []FlowItem) []int {
out := make([]int, 0, len(items))
for _, it := range items {
out = append(out, it.StationNo)
}
return out
}
// RouteStationsStrOfWO 组合条目 → 路线串 "2,5,6,8"
func RouteStationsStrOfWO(items []FlowItem) string {
parts := make([]string, 0, len(items))
for _, it := range items {
parts = append(parts, strconv.Itoa(it.StationNo))
}
return strings.Join(parts, ",")
}
// StationFlowMapOfWO 组合条目 → 工位号→工艺ID
func StationFlowMapOfWO(items []FlowItem) map[int]int {
m := make(map[int]int, len(items))
for _, it := range items {
m[it.StationNo] = it.FlowId
}
return m
}
// ValidateFlowCombination 保存工单时校验工艺组合:
// 非空、工艺存在且启用(ACTIVE)、工位存在、工位号不重复;返回按工位号升序的条目。
func (s *Service) ValidateFlowCombination(ctx context.Context, raw []map[string]int) ([]FlowItem, error) {
items := flowMapsToItems(raw)
if len(items) == 0 {
return nil, errors.New("请先选择工艺组合(至少一条 工艺+工位)")
}
sort.SliceStable(items, func(i, j int) bool { return items[i].StationNo < items[j].StationNo })
seen := map[int]bool{}
for _, it := range items {
if seen[it.StationNo] {
return nil, fmt.Errorf("工位 %d 在工艺组合中重复", it.StationNo)
}
seen[it.StationNo] = true
flow, err := s.ctx.EntClient.ProcessFlow.Get(ctx, it.FlowId)
if err != nil {
return nil, fmt.Errorf("工艺不存在(ID=%d", it.FlowId)
}
if flow.Status != "ACTIVE" {
return nil, fmt.Errorf("工艺「%s」已停用", flow.Name)
}
exist, err := s.ctx.EntClient.Station.Query().Where(station.StationNo(it.StationNo)).Exist(ctx)
if err != nil {
return nil, err
}
if !exist {
return nil, fmt.Errorf("工位 %d 不存在", it.StationNo)
}
}
return items, nil
}
// ValidateWOCombinationForSchedule 排产前校验(工单组合是唯一路线源头,排产只继承不可改):
// 组合为空不可排产;组合里每个工位当前绑定工艺必须与工单一致且工位启用。
func (s *Service) ValidateWOCombinationForSchedule(ctx context.Context, wo *ent.WorkOrder) error {
items, _ := RouteStationsFromWO(ctx, s.ctx.EntClient, wo)
if len(items) == 0 {
return errors.New("请先在工单中选择工艺组合,才能排产")
}
for _, it := range items {
st, err := s.ctx.EntClient.Station.Query().Where(station.StationNo(it.StationNo)).First(ctx)
if err != nil {
return fmt.Errorf("工位 %d 不存在", it.StationNo)
}
if st.FlowId != it.FlowId || st.Status != "ENABLED" {
return fmt.Errorf("工位%d当前工艺与工单不匹配", it.StationNo)
}
}
return nil
}
// flowNameById 工艺名称(查不到回退「工艺{id}」)
func (s *Service) flowNameById(ctx context.Context, id int) string {
if id > 0 {
if f, err := s.ctx.EntClient.ProcessFlow.Get(ctx, id); err == nil && f.Name != "" {
return f.Name
}
}
return "工艺" + strconv.Itoa(id)
}