refactor: 重构产线工位与备料流程,移除冗余字段
1. 移除工单、工件、日排产中的冗余工位组合/接驳台字段 2. 新增工位接驳台编码字段与唯一约束,关联WMS接驳台主数据 3. 重构日排产为工位产量分配模式,替代原接驳台列表 4. 新增备料单工位级字段与分批出库支持 5. 移除定时同步可生产数量,改为WMS实时拉取接口 6. 过滤操作日志,排除登录/退出相关日志 7. 调整仪表盘工序展示逻辑为今日派工路线 8. 新增接驳台维护菜单与基础数据
This commit is contained in:
@@ -207,13 +207,14 @@ func buildCard(ctx context.Context, svcCtx *svc.ServiceContext, sn string) (*car
|
||||
}
|
||||
|
||||
// hasTorqueStep 判断该工件加工路径上是否存在"拧紧采集"步骤(经 工位→流程→步骤 链)
|
||||
// 路径由今日工位派工(station_process)派生
|
||||
func hasTorqueStep(ctx context.Context, client *ent.Client, sn string, wp *ent.Workpiece) bool {
|
||||
procCodes := parseCardSeq(wp.ProcessSeq)
|
||||
if len(procCodes) == 0 {
|
||||
route, err := logic.RouteStations(ctx, client, logic.TodayStr(), "")
|
||||
if err != nil || len(route) == 0 {
|
||||
return false
|
||||
}
|
||||
stations, err := client.Station.Query().
|
||||
Where(station.StationNoIn(procCodes...), station.FlowIdGT(0)).All(ctx)
|
||||
Where(station.StationNoIn(route...), station.FlowIdGT(0)).All(ctx)
|
||||
if err != nil || len(stations) == 0 {
|
||||
return false
|
||||
}
|
||||
@@ -229,22 +230,6 @@ func hasTorqueStep(ctx context.Context, client *ent.Client, sn string, wp *ent.W
|
||||
return n > 0
|
||||
}
|
||||
|
||||
// parseCardSeq 解析工单/工件工序组合 "1,3,5" → 工位号列表(含 0 号上线位)
|
||||
func parseCardSeq(s string) []int {
|
||||
out := []int{}
|
||||
for _, p := range strings.Split(s, ",") {
|
||||
p = strings.TrimSpace(p)
|
||||
if p == "" {
|
||||
continue
|
||||
}
|
||||
var n int
|
||||
if _, err := fmt.Sscanf(p, "%d", &n); err == nil && n >= 0 && n <= logic.ProcessSeqMaxStation {
|
||||
out = append(out, n)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func itoaCard(n int) string { return fmt.Sprintf("%d", n) }
|
||||
|
||||
func renderProcessCard(t *cardTmpl) (string, error) {
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
"bj_power_mes/ent/stepcriterion"
|
||||
"bj_power_mes/ent/workorder"
|
||||
"bj_power_mes/ent/workpieceprocess"
|
||||
"bj_power_mes/internal/logic"
|
||||
"bj_power_mes/internal/svc"
|
||||
)
|
||||
|
||||
@@ -157,8 +158,7 @@ func buildModelCard(ctx context.Context, svcCtx *svc.ServiceContext, productType
|
||||
return nil, fmt.Errorf("产品型号不存在")
|
||||
}
|
||||
|
||||
// 2) 工位组合:优先取该产品最近一张工单的工位组合;无工单则取全产线工位(升序)
|
||||
seq := []int{}
|
||||
// 2) 产品最近工单(仅取工单号/计划时间用于卡头展示;路线不再存工单)
|
||||
if productCode != "" {
|
||||
if wo, err := client.WorkOrder.Query().
|
||||
Where(workorder.ProductCode(productCode)).
|
||||
@@ -170,16 +170,19 @@ func buildModelCard(ctx context.Context, svcCtx *svc.ServiceContext, productType
|
||||
if wo.PlanEnd != nil {
|
||||
card.PlanEnd = wo.PlanEnd.Format("2006-01-02 15:04")
|
||||
}
|
||||
seq = parseCardSeq(wo.ProcessSeq)
|
||||
card.SeqSource = "取自工单 " + wo.WorkOrderNo
|
||||
}
|
||||
}
|
||||
|
||||
// 3) 工位组合:由「今日工位派工(station_process)」派生;未派工则展示全产线已配置工艺流的工位
|
||||
seq, _ := logic.RouteStations(ctx, client, logic.TodayStr(), "")
|
||||
if len(seq) == 0 {
|
||||
sts, _ := client.Station.Query().Order(ent.Asc(station.FieldStationNo)).All(ctx)
|
||||
sts, _ := client.Station.Query().Where(station.FlowIdGT(0)).Order(ent.Asc(station.FieldStationNo)).All(ctx)
|
||||
for _, st := range sts {
|
||||
seq = append(seq, st.StationNo)
|
||||
}
|
||||
card.SeqSource = "该型号暂无工单,按全产线工位顺序展示"
|
||||
card.SeqSource = "今日未派工,按全产线已配置工位顺序展示"
|
||||
} else {
|
||||
card.SeqSource = "取自今日工位派工路线"
|
||||
}
|
||||
sort.Ints(seq)
|
||||
card.ProcessSeq = joinInts(seq)
|
||||
|
||||
@@ -223,7 +223,7 @@ func ListMaterialRequestsHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
q := r.URL.Query()
|
||||
data, err := logic.New(svcCtx).ListMaterialRequestsFiltered(r.Context(),
|
||||
q.Get("orderNo"), q.Get("planDate"), q.Get("status"),
|
||||
q.Get("materialCode"), q.Get("materialName"), q.Get("targetDock"))
|
||||
q.Get("materialCode"), q.Get("materialName"), q.Get("targetDock"), q.Get("stationNo"))
|
||||
if err != nil {
|
||||
httpx.Fail(w, 3104, err.Error())
|
||||
return
|
||||
@@ -237,7 +237,7 @@ func ListMaterialRequestsHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
func ListMaterialRequestsInternalHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
q := r.URL.Query()
|
||||
data, err := logic.New(svcCtx).ListMaterialRequests(r.Context(), q.Get("orderNo"), q.Get("planDate"), q.Get("status"))
|
||||
data, err := logic.New(svcCtx).ListMaterialRequests(r.Context(), q.Get("orderNo"), q.Get("planDate"), q.Get("status"), q.Get("stationNo"))
|
||||
if err != nil {
|
||||
httpx.Fail(w, 3105, err.Error())
|
||||
return
|
||||
@@ -268,4 +268,59 @@ func MarkMaterialRequestStatusInternalHandler(svcCtx *svc.ServiceContext) http.H
|
||||
}
|
||||
httpx.Ok(w, nil)
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- 接驳台 / 接料确认 ----------
|
||||
|
||||
// ListDocksHandler GET /docks 拉取 WMS 接驳台主数据(dock 真源在 WMS)。
|
||||
// WMS 不可达降级为错误,前端仍可用写死兜底下拉。
|
||||
func ListDocksHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
list, err := svcCtx.Wms.ListDocks(r.Context())
|
||||
if err != nil {
|
||||
httpx.Fail(w, 3113, "拉取接驳台失败:"+err.Error())
|
||||
return
|
||||
}
|
||||
httpx.Ok(w, list)
|
||||
}
|
||||
}
|
||||
|
||||
// ReceiveMaterialRequestHandler POST /material-requests/receive 接料确认(MES 备料页人工点「已接料」)。
|
||||
// qty 可空(≤0 表示确认本批全部剩余);累加 sentQty 封顶 reqQty,达量自动 DONE。
|
||||
func ReceiveMaterialRequestHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req struct {
|
||||
RequestNo string `json:"requestNo"`
|
||||
Qty int `json:"qty"`
|
||||
}
|
||||
if err := httpx.ParseJSON(r, &req); err != nil || req.RequestNo == "" {
|
||||
httpx.BadRequest(w, "缺少备料单号")
|
||||
return
|
||||
}
|
||||
if err := logic.New(svcCtx).ReceiveMaterialRequest(r.Context(), req.RequestNo, req.Qty, operator(r, "")); err != nil {
|
||||
httpx.Fail(w, 3114, err.Error())
|
||||
return
|
||||
}
|
||||
httpx.OkMessage(w, "已接料", nil)
|
||||
}
|
||||
}
|
||||
|
||||
// StationReceiveMaterialInternalHandler POST /api/internal/station/receive-material 接料确认内部接口。
|
||||
// 供工位终端代理调用(工人扫码接料主入口),X-API-TOKEN 保护。逻辑同 ReceiveMaterialRequestHandler。
|
||||
func StationReceiveMaterialInternalHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req struct {
|
||||
RequestNo string `json:"requestNo"`
|
||||
Qty int `json:"qty"`
|
||||
}
|
||||
if err := httpx.ParseJSON(r, &req); err != nil || req.RequestNo == "" {
|
||||
httpx.BadRequest(w, "缺少备料单号")
|
||||
return
|
||||
}
|
||||
if err := logic.New(svcCtx).ReceiveMaterialRequest(r.Context(), req.RequestNo, req.Qty, operator(r, "")); err != nil {
|
||||
httpx.Fail(w, 3114, err.Error())
|
||||
return
|
||||
}
|
||||
httpx.Ok(w, nil)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package production
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"bj_power_mes/common/httpx"
|
||||
"bj_power_mes/internal/logic"
|
||||
"bj_power_mes/internal/svc"
|
||||
)
|
||||
|
||||
// ProducibleInternalHandler 内部接口:WMS 打开「可生产数量」时实时拉取。
|
||||
// 不做定时/批量同步——WMS 需要时来拉一次,MES 现算现返(BOM 单台用量在 MES、可用量在 WMS)。
|
||||
func ProducibleInternalHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
res, err := logic.New(svcCtx).ComputeProducible(r.Context())
|
||||
if err != nil {
|
||||
httpx.Fail(w, 3401, err.Error())
|
||||
return
|
||||
}
|
||||
httpx.Ok(w, res)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
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,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -111,6 +111,10 @@ func RegisterProductionRoutes(server *rest.Server, serverCtx *svc.ServiceContext
|
||||
server.AddRoute(rest.Route{
|
||||
Method: http.MethodPost, Path: "/api/internal/dock/pallet/set", Handler: internal(production.SetDockPalletInternalHandler(serverCtx)),
|
||||
})
|
||||
// 可生产数量:WMS 打开页面时实时拉取(不做定时推送)
|
||||
server.AddRoute(rest.Route{
|
||||
Method: http.MethodGet, Path: "/api/internal/producible", Handler: internal(production.ProducibleInternalHandler(serverCtx)),
|
||||
})
|
||||
|
||||
// ---------- 产线点位/移料(供工位终端调用,X-API-TOKEN 保护)----------
|
||||
server.AddRoute(rest.Route{
|
||||
@@ -134,6 +138,9 @@ func RegisterProductionRoutes(server *rest.Server, serverCtx *svc.ServiceContext
|
||||
server.AddRoute(rest.Route{
|
||||
Method: http.MethodPost, Path: "/api/internal/line/occupy", Handler: internal(OccupyPointHandler(serverCtx)),
|
||||
})
|
||||
server.AddRoute(rest.Route{
|
||||
Method: http.MethodPost, Path: "/api/internal/station/receive-material", Handler: internal(production.StationReceiveMaterialInternalHandler(serverCtx)),
|
||||
})
|
||||
|
||||
// ---------- JWT 业务 API ----------
|
||||
jwtRoutes := []rest.Route{
|
||||
@@ -165,6 +172,8 @@ func RegisterProductionRoutes(server *rest.Server, serverCtx *svc.ServiceContext
|
||||
|
||||
{Method: http.MethodPost, Path: "/material-requests/generate", Handler: production.GenerateMaterialHandler(serverCtx)},
|
||||
{Method: http.MethodGet, Path: "/material-requests", Handler: production.ListMaterialRequestsHandler(serverCtx)},
|
||||
{Method: http.MethodPost, Path: "/material-requests/receive", Handler: production.ReceiveMaterialRequestHandler(serverCtx)},
|
||||
{Method: http.MethodGet, Path: "/docks", Handler: production.ListDocksHandler(serverCtx)},
|
||||
|
||||
{Method: http.MethodPost, Path: "/plc/send-process", Handler: production.PlcSendProcessHandler(serverCtx)},
|
||||
{Method: http.MethodGet, Path: "/plc/send-logs", Handler: production.PlcSendLogsHandler(serverCtx)},
|
||||
@@ -247,6 +256,11 @@ func RegisterProductionRoutes(server *rest.Server, serverCtx *svc.ServiceContext
|
||||
{Method: http.MethodGet, Path: "/process-card", Handler: ProcessCardHandler(serverCtx)},
|
||||
// 产品型号级全景流程卡(D2):按型号聚合 工位组合→工艺流程/步骤→物料清单→检验卡项
|
||||
{Method: http.MethodGet, Path: "/process-card/model", Handler: ModelProcessCardHandler(serverCtx)},
|
||||
|
||||
// ---------- 工位派工(工位↔工序,替代原工序组合串) ----------
|
||||
{Method: http.MethodGet, Path: "/station-process", Handler: production.ListStationProcessHandler(serverCtx)},
|
||||
{Method: http.MethodPost, Path: "/station-process/save", Handler: production.SaveStationProcessHandler(serverCtx)},
|
||||
{Method: http.MethodGet, Path: "/station-process/route", Handler: production.RouteStationProcessHandler(serverCtx)},
|
||||
}
|
||||
server.AddRoutes(
|
||||
rest.WithMiddlewares([]rest.Middleware{permissionGuard(serverCtx)}, jwtRoutes...),
|
||||
|
||||
Reference in New Issue
Block a user