package logic import ( "context" "errors" "fmt" "sort" "time" "bj_power_mes/ent" "bj_power_mes/ent/processroute" "bj_power_mes/ent/routesegment" "bj_power_mes/ent/station" ) // ---------- 工艺路线(M2:有序并行工位段) ---------- // RouteSegmentReq 路线段请求:一段 = 一个流程图 + 一组并行工位 type RouteSegmentReq struct { Seq int `json:"seq"` FlowId int `json:"flowId"` Stations []int `json:"stations"` SegmentType string `json:"segmentType"` // LINE / OFFLINE Remark string `json:"remark"` } // RouteReq 工艺路线保存请求 type RouteReq struct { Id int `json:"id"` Name string `json:"name"` ProductTypeId *int `json:"productTypeId"` Status string `json:"status"` Remark string `json:"remark"` Segments []RouteSegmentReq `json:"segments"` } // RouteSegmentVO 路线段视图 type RouteSegmentVO struct { Id int `json:"id"` RouteId int `json:"routeId"` Seq int `json:"seq"` FlowId int `json:"flowId"` FlowName string `json:"flowName"` Stations []int `json:"stations"` SegmentType string `json:"segmentType"` Remark string `json:"remark"` } // RouteVO 工艺路线视图(含段列表) type RouteVO struct { Id int `json:"id"` Name string `json:"name"` ProductTypeId int `json:"productTypeId"` ProductName string `json:"productName"` Status string `json:"status"` Remark string `json:"remark"` Segments []*RouteSegmentVO `json:"segments"` CreatedAt time.Time `json:"createdAt"` } // SaveRoute 保存工艺路线(含段)。约束:段序号>=0;LINE 段工位须存在且不跨段重复;OFFLINE 段(0/13 线下位)不校验工位。 func (s *Service) SaveRoute(ctx context.Context, req RouteReq, operator string) error { if req.Name == "" { return errors.New("路线名称必填") } status := req.Status if status == "" { status = "ACTIVE" } if status != "ACTIVE" && status != "INACTIVE" { return errors.New("非法状态") } if len(req.Segments) == 0 { return errors.New("路线至少包含一个段") } seenStation := map[int]bool{} for _, seg := range req.Segments { if seg.Seq < 0 { return errors.New("段序号非法(从 0 开始)") } if seg.FlowId > 0 { if _, err := s.ctx.EntClient.ProcessFlow.Get(ctx, seg.FlowId); err != nil { return errors.New("所选工艺流程不存在") } } stType := seg.SegmentType if stType == "" { stType = "LINE" } if stType != "LINE" && stType != "OFFLINE" { return errors.New("段类型仅支持 LINE/OFFLINE") } if len(seg.Stations) == 0 { return errors.New("每段至少包含一个工位") } if stType == "LINE" { for _, no := range seg.Stations { if no < 1 { return errors.New("工位号非法") } if _, err := s.ctx.EntClient.Station.Query().Where(station.StationNo(no)).First(ctx); err != nil { return fmt.Errorf("工位 %d 不存在,请先在工位主数据中维护", no) } if seenStation[no] { return fmt.Errorf("工位 %d 在路线中重复出现", no) } seenStation[no] = true } } } var routeID int if req.Id > 0 { upd := s.ctx.EntClient.ProcessRoute.UpdateOneID(req.Id). SetName(req.Name).SetStatus(status).SetRemark(req.Remark) if req.ProductTypeId != nil { upd.SetProductTypeId(*req.ProductTypeId) } else { upd.ClearProductTypeId() } if _, err := upd.Save(ctx); err != nil { return err } routeID = req.Id } else { cr := s.ctx.EntClient.ProcessRoute.Create(). SetName(req.Name).SetStatus(status).SetRemark(req.Remark) if req.ProductTypeId != nil { cr.SetProductTypeId(*req.ProductTypeId) } r, err := cr.Save(ctx) if err != nil { return err } routeID = r.ID } _, _ = s.ctx.EntClient.RouteSegment.Delete().Where(routesegment.RouteId(routeID)).Exec(ctx) for _, seg := range req.Segments { stType := seg.SegmentType if stType == "" { stType = "LINE" } if _, err := s.ctx.EntClient.RouteSegment.Create(). SetRouteId(routeID).SetSeq(seg.Seq).SetFlowId(seg.FlowId). SetStations(seg.Stations).SetSegmentType(stType).SetRemark(seg.Remark).Save(ctx); err != nil { return err } } s.ctx.EventLog.Write(ctx, "route.save", "", operator, "process_route", "", "维护工艺路线", map[string]any{"routeId": routeID, "segments": len(req.Segments)}) return nil } func (s *Service) routeVO(ctx context.Context, r *ent.ProcessRoute) (*RouteVO, error) { segs, err := s.ctx.EntClient.RouteSegment.Query(). Where(routesegment.RouteId(r.ID)).Order(ent.Asc(routesegment.FieldSeq)).All(ctx) if err != nil { return nil, err } flows, _ := s.ctx.EntClient.ProcessFlow.Query().All(ctx) flowMap := map[int]string{} for _, f := range flows { flowMap[f.ID] = f.Name } segVOs := make([]*RouteSegmentVO, 0, len(segs)) for _, seg := range segs { segVOs = append(segVOs, &RouteSegmentVO{ Id: seg.ID, RouteId: seg.RouteId, Seq: seg.Seq, FlowId: seg.FlowId, FlowName: flowMap[seg.FlowId], Stations: seg.Stations, SegmentType: seg.SegmentType, Remark: seg.Remark, }) } return &RouteVO{ Id: r.ID, Name: r.Name, ProductTypeId: r.ProductTypeId, Status: r.Status, Remark: r.Remark, Segments: segVOs, CreatedAt: r.CreatedAt, }, nil } // ListRoutes 查询工艺路线(page>0 真分页;否则全量) func (s *Service) ListRoutes(ctx context.Context, name, status string, page, pageSize int) (any, error) { q := s.ctx.EntClient.ProcessRoute.Query() if name != "" { q = q.Where(processroute.NameContainsFold(name)) } if status != "" { q = q.Where(processroute.Status(status)) } routes, err := q.Order(ent.Desc(processroute.FieldID)).All(ctx) if err != nil { return nil, err } pts, _ := s.ctx.EntClient.ProductType.Query().All(ctx) ptMap := map[int]string{} for _, p := range pts { ptMap[p.ID] = p.Name } out := make([]*RouteVO, 0, len(routes)) for _, r := range routes { vo, err := s.routeVO(ctx, r) if err != nil { return nil, err } vo.ProductName = ptMap[r.ProductTypeId] out = append(out, vo) } if page > 0 { total := len(out) start := (page - 1) * pageSize if start > total { start = total } end := start + pageSize if end > total { end = total } return map[string]any{"total": total, "list": out[start:end], "page": page, "pageSize": pageSize}, nil } return out, nil } // GetRoute 获取单条路线详情 func (s *Service) GetRoute(ctx context.Context, id int) (*RouteVO, error) { r, err := s.ctx.EntClient.ProcessRoute.Get(ctx, id) if err != nil { return nil, errors.New("工艺路线不存在") } return s.routeVO(ctx, r) } // GetActiveRouteByProductType 取某产品型号的启用路线(工单建单自动带出) func (s *Service) GetActiveRouteByProductType(ctx context.Context, productTypeId int) (*RouteVO, error) { r, err := s.ctx.EntClient.ProcessRoute.Query(). Where(processroute.ProductTypeId(productTypeId), processroute.Status("ACTIVE")). Order(ent.Desc(processroute.FieldID)).First(ctx) if err != nil { return nil, err } return s.routeVO(ctx, r) } // DeleteRoute 删除路线(先清段) func (s *Service) DeleteRoute(ctx context.Context, id int, operator string) error { _, _ = s.ctx.EntClient.RouteSegment.Delete().Where(routesegment.RouteId(id)).Exec(ctx) s.ctx.EventLog.Write(ctx, "route.delete", "", operator, "process_route", "", "删除工艺路线", map[string]any{"routeId": id}) return s.ctx.EntClient.ProcessRoute.DeleteOneID(id).Exec(ctx) } // BuildRouteSnapshot 由路线构建工单段快照,并返回展开的工位顺序(仅 LINE 段,兼容旧工位终端 logic) func (s *Service) BuildRouteSnapshot(route *RouteVO) ([]map[string]any, []int) { snapshot := make([]map[string]any, 0, len(route.Segments)) lineStations := []int{} seen := map[int]bool{} for _, seg := range route.Segments { snapshot = append(snapshot, map[string]any{ "seq": seg.Seq, "flowId": seg.FlowId, "flowName": seg.FlowName, "stations": seg.Stations, "segmentType": seg.SegmentType, }) if seg.SegmentType == "LINE" { for _, no := range seg.Stations { if !seen[no] { seen[no] = true lineStations = append(lineStations, no) } } } } sort.Ints(lineStations) return snapshot, lineStations }