refactor: 统一术语BOM为物料清单并补充工位扩展能力

主要变更:
1.  后端:重构工位管理逻辑,新增工位自定义扩展能力,将工位号上限调整为999并支持14+虚拟工位
2.  前后端:全量替换"BOM"术语为"物料清单",包括提示语、注释和文档
3.  WMS模块:新增AGV任务取消接口与前端页面,对接海康RCS取消任务逻辑
4.  权限与种子数据:新增工位新增权限并更新权限配置
5.  修复错误校验逻辑,优化AGV客户端签名与请求处理
This commit is contained in:
SunYF
2026-09-17 19:41:00 +08:00
parent e31b85e87a
commit bf3a408839
20 changed files with 276 additions and 52 deletions
+2 -1
View File
@@ -18,6 +18,7 @@ import (
"bj_power_mes/ent/workorder"
"bj_power_mes/ent/workpiece"
"bj_power_mes/ent/workpieceprocess"
"bj_power_mes/internal/logic"
"bj_power_mes/internal/svc"
)
@@ -237,7 +238,7 @@ func parseCardSeq(s string) []int {
continue
}
var n int
if _, err := fmt.Sscanf(p, "%d", &n); err == nil && n >= 1 && n <= 12 {
if _, err := fmt.Sscanf(p, "%d", &n); err == nil && n >= 1 && n <= logic.ProcessSeqMaxStation {
out = append(out, n)
}
}
@@ -86,7 +86,7 @@ func ImportBomHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
count++
}
if count == 0 {
httpx.Fail(w, 3111, "未解析到有效 BOM 行(至少需产品编号与物料编码")
httpx.Fail(w, 3111, "未解析到有效物料清单行(至少需产品编号与图号")
return
}
for _, k := range order {
+39 -3
View File
@@ -278,14 +278,50 @@ func (s *Service) ListStations(ctx context.Context) ([]*StationVO, error) {
return out, nil
}
// SaveStation 维护工位:只改名称/绑定流程。工位主数据由数据库维护,本接口不新增工位。
// SaveStation 维护工位:
// - 工位号已存在 → 只改名称/类型/绑定流程(不增删工位)
// - 工位号不存在 → 新增工位;新增的工位号由用户自定义,允许扩展 14、15…(虚拟/物理均可)
//
// 工位数量以数据库为准,前端下拉随 station 表自动增减;虚拟工位(0/13/14...)不连 PLC、仅记录。
func (s *Service) SaveStation(ctx context.Context, req StationReq, operator string) error {
if req.StationNo <= 0 {
if req.StationNo < 0 {
return errors.New("工位号非法(需 ≥ 0")
}
if req.StationNo == 0 && req.Name == "" {
return errors.New("工位号非法")
}
st, err := s.ctx.EntClient.Station.Query().Where(station.StationNo(req.StationNo)).First(ctx)
if err != nil {
return fmt.Errorf("工位 %d 不存在,请先在工位主数据中维护", req.StationNo)
// 新增工位:工位号不存在即创建
if req.Name == "" {
return errors.New("请填写工位名称")
}
stype := "LINE"
if req.StationType != nil {
stype = *req.StationType
if stype != "LINE" && stype != "OFFLINE" {
return errors.New("工位类型仅支持 LINE/OFFLINE")
}
}
cr := s.ctx.EntClient.Station.Create().
SetStationNo(req.StationNo).SetName(req.Name).SetStationType(stype).SetStatus("ENABLED")
if req.FlowId != nil && *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("该工艺流程已停用,请先启用后再绑定工位")
}
cr.SetFlowId(*req.FlowId)
}
created, cErr := cr.Save(ctx)
if cErr != nil {
return cErr
}
s.ctx.EventLog.Write(ctx, "station.create", "", operator, "station", "",
"新增工位", map[string]any{"stationNo": created.StationNo, "name": created.Name})
return nil
}
// 仅改名称:flowId 不传(nil)保留原绑定;0=解绑;>0=绑定指定启用流程
upd := s.ctx.EntClient.Station.UpdateOneID(st.ID).SetName(req.Name)
+6 -2
View File
@@ -10,8 +10,12 @@ import (
// FullProcessSeq 全部 12 个工位的完整组合(按顺序执行)
const FullProcessSeq = "1,2,3,4,5,6,7,8,9,10,11,12"
// ProcessSeqMaxStation 工位组合允许的最大工位号(工位主数据可自行扩展,故放宽上限)。
const ProcessSeqMaxStation = 999
// ParseProcessSeq 解析工位组合字符串为升序去重的工位码列表。
// 唯一格式:逗号分隔 "1,3,5" / "10,11,12"。传送带不回走,故组合天然按工位号升序执行。
// 工位号取值 1..ProcessSeqMaxStation:工位主数据可扩展到 14、15…(虚拟工位仅记录,不连 PLC)。
func ParseProcessSeq(s string) []int {
s = strings.TrimSpace(s)
if s == "" {
@@ -25,7 +29,7 @@ func ParseProcessSeq(s string) []int {
continue
}
n, err := strconv.Atoi(p)
if err != nil || n < 1 || seen[n] {
if err != nil || n < 1 || n > ProcessSeqMaxStation || seen[n] {
continue
}
seen[n] = true
@@ -48,7 +52,7 @@ func NormalizeProcessSeq(s string) string {
return strings.Join(parts, ",")
}
// ValidateProcessSeq 校验工位组合:非空、1~12 升序执行
// ValidateProcessSeq 校验工位组合:非空、按工位号升序执行(工位号上限见 ProcessSeqMaxStation
func ValidateProcessSeq(s string) error {
if len(ParseProcessSeq(s)) == 0 {
return errors.New("工位组合不能为空,请至少选择一个工位")
+4 -4
View File
@@ -33,7 +33,7 @@ func (s *Service) Seed(ctx context.Context) error {
"produce.qtyreport", "produce.quality", "produce.quality:edit",
"produce.plc:send", "produce.product:add", "produce.product:edit", "produce.product:delete",
"produce.processflow:add", "produce.processflow:edit", "produce.processflow:delete",
"produce.processflow:upload", "produce.station:edit",
"produce.processflow:upload", "produce.station:edit", "produce.station:add",
}
} else if r.code == "INSPECTOR" {
codes = []string{"produce.trace", "produce.wip", "produce.torque", "produce.performance", "sys.eventlog",
@@ -74,8 +74,7 @@ func (s *Service) Seed(ctx context.Context) error {
{"produce.plc", "工位组合下发", "MENU", "/plc-send"},
{"produce.torque", "拧紧查询", "MENU", "/torque"},
{"produce.processflow", "工艺流程", "MENU", "/process-flow"},
{"produce.station", "关联工位", "MENU", "/station"},
{"produce.performance", "绩效报表", "MENU", "/performance"},
{"produce.station", "关联工位", "MENU", "/station"}, {"produce.performance", "绩效报表", "MENU", "/performance"},
{"produce.scan", "手动报工", "MENU", "/scan"},
{"produce.trace", "工件追溯", "MENU", "/trace"},
{"produce.wip", "在制品", "MENU", "/wip"},
@@ -115,7 +114,8 @@ func (s *Service) Seed(ctx context.Context) error {
{"produce.processflow:edit", "工艺流程-编辑", "BUTTON", "", "produce.processflow"},
{"produce.processflow:delete", "工艺流程-删除", "BUTTON", "", "produce.processflow"},
{"produce.processflow:upload", "工艺流程-上传图纸", "BUTTON", "", "produce.processflow"},
{"produce.station:edit", "工位-绑定", "BUTTON", "", "produce.station"},
{"produce.station:edit", "工位-绑定与改名", "BUTTON", "", "produce.station"},
{"produce.station:add", "工位-新增", "BUTTON", "", "produce.station"},
{"sys.account:add", "账号-新增", "BUTTON", "", "sys.account"},
{"sys.account:edit", "账号-编辑", "BUTTON", "", "sys.account"},
{"sys.account:delete", "账号-删除", "BUTTON", "", "sys.account"},