feat&refactor: 完成多模块功能迭代与配置优化
本次提交覆盖多个业务模块的功能完善与体验优化:
1. **鉴权与配置调整**:
- 统一JWT滑动续签逻辑,简化Token存储,移除RefreshToken相关冗余代码
- 调整多项目配置文件中JWT过期时间为3600秒,统一会话闲置窗口
- 工位配置放开1~12限制,改为仅校验大于0
2. **术语统一替换**:全链路将"精密件"替换为"电气件",修正物料管理描述
3. **功能新增**:
- 新增工位类型、工艺路线与产线点位台账模块
- 添加工艺PDF预览面板、工位终端代理转发接口
- 新增操作日志按操作人列表筛选、工位登出日志记录
- 新增PLC移料指令与产线点位状态管理
4. **业务流程优化**:
- 调整BOM物料删除校验逻辑,优化工单备料计算
- 补充物料图号、检测单号等追溯字段
- 完善工艺流程图与工位绑定关系说明
- 优化前端页面文案与交互细节
5. **代码规范与维护**:
- 新增通用工具函数与前端静态资源
- 整理路由权限与中间件逻辑
- 修复部分接口与配置的不兼容问题
This commit is contained in:
@@ -0,0 +1,271 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user