refactor: 重构工艺工序相关命名与数据模型

1.  全局替换"工序"为"工艺"统一术语,包括页面文案、枚举、注释
2.  重构工单与工件工艺数据模型:
    - 新增工艺组合表flow_material作为备料/齐套唯一源头
    - 新增工位状态表station_state支持自主停单/恢复接单
    - 移除station_process派工表,改用工单工艺组合作为路线唯一源头
    - 替换processCode为flowId作为工艺关联标识
    - 重构工件当前进度字段为currentStationNo
3.  删除冗余的静态备份资源文件
4.  调整物料选择组件默认启用仅显示激活物料
5.  优化工单创建/编辑逻辑,新增工艺组合校验与保存
This commit is contained in:
SunYF
2026-09-22 11:32:29 +08:00
parent 932700ca65
commit d609ff8a9e
185 changed files with 8623 additions and 4501 deletions
@@ -13,12 +13,12 @@ func itoa(n int) string { return strconv.Itoa(n) }
// BindReq 绑定提交请求
type BindReq struct {
Sn string `json:"sn"`
OrderNo string `json:"orderNo"`
ProcessCode int `json:"processCode"`
StationNo int `json:"stationNo"`
Operator string `json:"operator"`
Items []logic.BindItemReq `json:"items"`
Sn string `json:"sn"`
OrderNo string `json:"orderNo"`
FlowId int `json:"flowId"` // 报工/绑定工艺(唯一路线源头=工单工艺组合)
StationNo int `json:"stationNo"`
Operator string `json:"operator"`
Items []logic.BindItemReq `json:"items"`
}
func doBind(svcCtx *svc.ServiceContext, r *http.Request) (any, error) {
@@ -26,7 +26,7 @@ func doBind(svcCtx *svc.ServiceContext, r *http.Request) (any, error) {
if err := httpx.ParseJSON(r, &req); err != nil {
return nil, err
}
if req.Sn == "" || req.ProcessCode <= 0 {
if req.Sn == "" || req.FlowId <= 0 {
return nil, nil
}
op := operator(r, req.Operator)
@@ -34,7 +34,7 @@ func doBind(svcCtx *svc.ServiceContext, r *http.Request) (any, error) {
if req.StationNo > 0 {
stationNo = itoa(req.StationNo)
}
return logic.New(svcCtx).BindWorkpiece(r.Context(), req.Sn, req.OrderNo, req.ProcessCode, stationNo, op, req.Items)
return logic.New(svcCtx).BindWorkpiece(r.Context(), req.Sn, req.OrderNo, req.FlowId, stationNo, op, req.Items)
}
// BindInternalHandler POST /api/internal/workpiece/binds —— 工位终端扫码绑料
@@ -46,7 +46,7 @@ func BindInternalHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return
}
if data == nil {
httpx.BadRequest(w, "sn / processCode 必填")
httpx.BadRequest(w, "sn / flowId 必填")
return
}
httpx.Ok(w, data)
@@ -55,16 +55,16 @@ func BindInternalHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
func doUnbind(svcCtx *svc.ServiceContext, r *http.Request) error {
var req struct {
Sn string `json:"sn"`
ProcessCode int `json:"processCode"`
Sn string `json:"sn"`
FlowId int `json:"flowId"`
MaterialCode string `json:"materialCode"`
BindValue string `json:"bindValue"`
Operator string `json:"operator"`
BindValue string `json:"bindValue"`
Operator string `json:"operator"`
}
if err := httpx.ParseJSON(r, &req); err != nil {
return err
}
return logic.New(svcCtx).RemoveBind(r.Context(), req.Sn, req.ProcessCode, req.MaterialCode, req.BindValue, operator(r, req.Operator))
return logic.New(svcCtx).RemoveBind(r.Context(), req.Sn, req.FlowId, req.MaterialCode, req.BindValue, operator(r, req.Operator))
}
// UnbindInternalHandler POST /api/internal/workpiece/binds/remove
@@ -80,17 +80,17 @@ func UnbindInternalHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
// ---------- 装机绑定:应绑清单/齐套状态查询 ----------
// bindPanelFromReq 通用查询逻辑:按 sn+processCode 返回绑定面板(应绑物料+已绑情况)
// bindPanelFromReq 通用查询逻辑:按 sn+flowId 返回绑定面板(应绑物料+已绑情况)
func bindPanelFromReq(svcCtx *svc.ServiceContext, r *http.Request) (any, error) {
sn := r.URL.Query().Get("sn")
processCode := atoiDefault(r.URL.Query().Get("processCode"), 0)
if sn == "" || processCode <= 0 {
flowId := atoiDefault(r.URL.Query().Get("flowId"), 0)
if sn == "" || flowId <= 0 {
return nil, nil
}
return logic.New(svcCtx).StationBindPanelData(r.Context(), sn, processCode)
return logic.New(svcCtx).StationBindPanelData(r.Context(), sn, flowId)
}
// BindPanelInternalHandler GET /api/internal/workpiece/bind-panel?sn=&processCode=
// BindPanelInternalHandler GET /api/internal/workpiece/bind-panel?sn=&flowId=
// 工位终端(12 个触控一体机)扫码工件后拉取绑定面板,驱动工人扫码绑料。
func BindPanelInternalHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
@@ -100,7 +100,7 @@ func BindPanelInternalHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return
}
if data == nil {
httpx.BadRequest(w, "缺少 sn / processCode 参数")
httpx.BadRequest(w, "缺少 sn / flowId 参数")
return
}
httpx.Ok(w, data)
@@ -14,7 +14,7 @@ import (
// ---------- BOM ----------
// ImportBomHandler POST /bom/import 产品物料清单(BOM) Excel 导入。
// 模板列:产品编号|物料清单名称|物料编码|名称|规格|单位|单台用量|损耗率|装配工位|相关标准|备注
// 模板列:产品编号|物料清单名称|物料编码|名称|规格|单位|单台用量|损耗率|相关标准|备注(原「装配工位」列忽略)
func ImportBomHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if err := r.ParseMultipartForm(svcCtx.Config.Upload.MaxMB << 20); err != nil {
@@ -59,12 +59,6 @@ func ImportBomHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
if bomName == "" {
bomName = "默认"
}
pc := 0
if v := trimCell(row, 8); v != "" {
if n, e := strconv.Atoi(v); e == nil {
pc = n
}
}
item := logic.BomItemReq{
ProductCode: productCode,
BomName: bomName,
@@ -73,7 +67,6 @@ func ImportBomHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
Spec: trimCell(row, 4),
Unit: trimCell(row, 5),
ManageMode: "2",
ProcessCode: &pc,
UnitQty: parseFloat(trimCell(row, 6)),
LossRate: parseFloat(trimCell(row, 7)),
RelatedStandard: trimCell(row, 9),
@@ -0,0 +1,42 @@
package production
import (
"net/http"
"bj_power_mes/common/httpx"
"bj_power_mes/internal/logic"
"bj_power_mes/internal/svc"
)
// ListFlowMaterialsHandler GET /api/v1/process-flows/:id/materials —— 查某工艺的物料清单(flow_material)
func ListFlowMaterialsHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
flowId := pathID(r)
data, err := logic.New(svcCtx).ListFlowMaterials(r.Context(), flowId)
if err != nil {
httpx.Fail(w, 2113, err.Error())
return
}
httpx.Ok(w, data)
}
}
// SaveFlowMaterialsHandler POST /api/v1/process-flows/:id/materials —— 整表覆盖保存工艺物料清单
// body: {items: [{materialCode, materialName, spec, unit, manageMode, unitQty, lossRate}]}
func SaveFlowMaterialsHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
flowId := pathID(r)
var req struct {
Items []logic.FlowMaterialReq `json:"items"`
}
if err := httpx.ParseJSON(r, &req); err != nil {
httpx.BadRequest(w, "请求体解析失败")
return
}
if err := logic.New(svcCtx).SaveFlowMaterials(r.Context(), flowId, req.Items, operator(r, "")); err != nil {
httpx.Fail(w, 2114, err.Error())
return
}
httpx.OkMessage(w, "工艺物料清单已保存", nil)
}
}
@@ -1,69 +0,0 @@
package production
import (
"net/http"
"bj_power_mes/common/httpx"
"bj_power_mes/internal/logic"
"bj_power_mes/internal/svc"
)
// ---------- 工位派工(工位↔工序,替代原工序组合串) ----------
// ListStationProcessHandler GET /station-process 查询某日班次全部工位派工(按工位号升序)
func ListStationProcessHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
q := r.URL.Query()
data, err := logic.New(svcCtx).ListStationProcess(r.Context(), q.Get("effectDate"), q.Get("shift"))
if err != nil {
httpx.Fail(w, 3301, err.Error())
return
}
httpx.Ok(w, data)
}
}
// SaveStationProcessHandler POST /station-process/save 保存单条工位派工
// 校验:同工位同天同班次唯一;已派工工位沿工位号必须单调不降、不可穿插。processCode=0 表示解除派工。
func SaveStationProcessHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req logic.StationProcessReq
if err := httpx.ParseJSON(r, &req); err != nil {
httpx.BadRequest(w, "请求体解析失败")
return
}
if req.Operator == "" {
req.Operator = operator(r, "")
}
if err := logic.New(svcCtx).UpsertStationProcess(r.Context(), req); err != nil {
httpx.Fail(w, 3302, err.Error())
return
}
httpx.OkMessage(w, "保存成功", nil)
}
}
// RouteStationProcessHandler GET /station-process/route 派生路线(工位号列表/工序映射/串)
// 供流程卡、PLC 下发、备料单等依赖"今日路线"的模块读取,不再手填组合串。
func RouteStationProcessHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
q := r.URL.Query()
ctx := r.Context()
stations, err := logic.RouteStations(ctx, svcCtx.EntClient, q.Get("effectDate"), q.Get("shift"))
if err != nil {
httpx.Fail(w, 3303, err.Error())
return
}
procMap, err := logic.RouteStationProcessCodeMap(ctx, svcCtx.EntClient, q.Get("effectDate"), q.Get("shift"))
if err != nil {
httpx.Fail(w, 3303, err.Error())
return
}
str, _ := logic.RouteStationsStr(ctx, svcCtx.EntClient, q.Get("effectDate"), q.Get("shift"))
httpx.Ok(w, map[string]any{
"stations": stations,
"processByStation": procMap,
"routeStr": str,
})
}
}
@@ -0,0 +1,44 @@
package production
import (
"net/http"
"bj_power_mes/common/httpx"
"bj_power_mes/internal/logic"
"bj_power_mes/internal/svc"
)
// StationPauseInternalHandler POST /api/internal/station/pause —— 工位停单/恢复(工人自主,只认当天)
// body: {stationNo, paused, reason, operator}
func StationPauseInternalHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req struct {
StationNo int `json:"stationNo"`
Paused bool `json:"paused"`
Reason string `json:"reason"`
Operator string `json:"operator"`
}
if err := httpx.ParseJSON(r, &req); err != nil {
httpx.BadRequest(w, "请求体解析失败")
return
}
if err := logic.New(svcCtx).SetStationPause(r.Context(), req.StationNo, req.Paused, req.Reason, operator(r, req.Operator)); err != nil {
httpx.Fail(w, 2115, err.Error())
return
}
httpx.OkMessage(w, "已更新工位停单状态", nil)
}
}
// StationStateInternalHandler GET /api/internal/station/state?date= —— 查询停单状态(date 空取当天)
// 响应: {list: [{id, stationNo, date, paused, reason, operator, createdAt, updatedAt}]}
func StationStateInternalHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
data, err := logic.New(svcCtx).ListStationStates(r.Context(), r.URL.Query().Get("date"))
if err != nil {
httpx.Fail(w, 2116, err.Error())
return
}
httpx.Ok(w, map[string]any{"list": data})
}
}
@@ -23,7 +23,7 @@ func pathID(r *http.Request) int {
func ListProductTypesHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
data, _, err := logic.New(svcCtx).ListProductTypes(r.Context())
data, _, err := logic.New(svcCtx).ListProductTypes(r.Context(), r.URL.Query().Get("isActive"))
if err != nil {
httpx.Fail(w, 3001, err.Error())
return
@@ -232,7 +232,7 @@ func ListDailyPlansHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
orderNo := r.URL.Query().Get("orderNo")
planDate := r.URL.Query().Get("planDate")
data, err := logic.New(svcCtx).ListDailyPlans(r.Context(), orderNo, planDate)
data, err := logic.New(svcCtx).ListDailyPlansWithCombination(r.Context(), orderNo, planDate)
if err != nil {
httpx.Fail(w, 3009, err.Error())
return