package handler import ( "encoding/json" "io" "net/http" "net/url" "strings" "bj_power_workstation/internal/svc" ) // ============ 产线点位 / 上位机移料 / 叫料(代理 MES 内部 API) ============ // MES 侧对应接口均以 X-API-TOKEN 保护(/api/internal/line/*),本服务用配置 token 转发, // 前端只与本服务交互(JWT 登录)。MES 响应体原样透传({code,message,data})。 // mesJSON 透传 MES 响应体;网络不可达时返回 502 中文提示。 func mesJSON(w http.ResponseWriter, body []byte, err error) bool { if err != nil { fail(w, http.StatusBadGateway, "MES 服务不可达:"+errString(err)) return false } w.Header().Set("Content-Type", "application/json; charset=utf-8") w.WriteHeader(http.StatusOK) _, _ = w.Write(body) return true } // linePointsHandler GET /api/line/points(产线点位台账:当前每个点位上的工件) func linePointsHandler(ctx *svc.ServiceContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { body, _, err := ctx.Mes.Get("/api/internal/line/points", nil) mesJSON(w, body, err) } } // lineMovesHandler GET /api/line/moves?status=&sn=&limit=(移料指令列表) func lineMovesHandler(ctx *svc.ServiceContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { q := url.Values{} for _, k := range []string{"status", "sn", "limit"} { if v := strings.TrimSpace(r.URL.Query().Get(k)); v != "" { q.Set(k, v) } } body, _, err := ctx.Mes.Get("/api/internal/line/moves", q) mesJSON(w, body, err) } } // lineMoveHandler POST /api/line/move(下发移料指令:起点位 → 目标点位) // 请求体原样转发:{sn, fromPoint, toPoint, orderNo} func lineMoveHandler(ctx *svc.ServiceContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { payload, err := io.ReadAll(r.Body) if err != nil { fail(w, http.StatusBadRequest, "请求体读取失败") return } var probe struct { Sn string `json:"sn"` FromPoint string `json:"fromPoint"` ToPoint string `json:"toPoint"` } _ = json.Unmarshal(payload, &probe) if probe.Sn == "" || probe.FromPoint == "" || probe.ToPoint == "" { fail(w, http.StatusBadRequest, "工件SN、起点位、目标点位必填") return } body, _, err := ctx.Mes.Post("/api/internal/line/move", json.RawMessage(payload)) mesJSON(w, body, err) } } // lineArriveHandler POST /api/line/move/arrive(工件到位:回写台账并闭环指令) func lineArriveHandler(ctx *svc.ServiceContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { payload, err := io.ReadAll(r.Body) if err != nil { fail(w, http.StatusBadRequest, "请求体读取失败") return } var probe struct { CmdNo string `json:"cmdNo"` } _ = json.Unmarshal(payload, &probe) if probe.CmdNo == "" { fail(w, http.StatusBadRequest, "缺少指令号 cmdNo") return } body, _, err := ctx.Mes.Post("/api/internal/line/move/arrive", json.RawMessage(payload)) mesJSON(w, body, err) } } // lineAckHandler POST /api/line/move/ack(PLC 应答:通常由 PLC 侧调用,终端手动应答也走这里) func lineAckHandler(ctx *svc.ServiceContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { payload, err := io.ReadAll(r.Body) if err != nil { fail(w, http.StatusBadRequest, "请求体读取失败") return } body, _, err := ctx.Mes.Post("/api/internal/line/move/ack", json.RawMessage(payload)) mesJSON(w, body, err) } } // lineOccupyHandler POST /api/line/occupy(点位人工记账:IN 上料 / OUT 下料) // 请求体:{pointNo, sn, orderNo, action} —— operator 由本服务补当前登录人。 func lineOccupyHandler(ctx *svc.ServiceContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { payload, err := io.ReadAll(r.Body) if err != nil { fail(w, http.StatusBadRequest, "请求体读取失败") return } var req map[string]any if err := json.Unmarshal(payload, &req); err != nil { fail(w, http.StatusBadRequest, "请求体解析失败") return } if req["operator"] == nil || req["operator"] == "" { if u := currentUsername(r); u != "" { req["operator"] = u } } body, _ := json.Marshal(req) resp, _, err := ctx.Mes.Post("/api/internal/line/occupy", json.RawMessage(body)) mesJSON(w, resp, err) } } // lineCallMaterialHandler POST /api/line/call-material(工位终端叫料:选本工位接驳台) // 请求体原样转发:{stationNo, dock, orderNo, items:[{materialCode, materialName, unit, manageMode, qty}]} func lineCallMaterialHandler(ctx *svc.ServiceContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { payload, err := io.ReadAll(r.Body) if err != nil { fail(w, http.StatusBadRequest, "请求体读取失败") return } var req map[string]any if err := json.Unmarshal(payload, &req); err != nil { fail(w, http.StatusBadRequest, "请求体解析失败") return } if req["operator"] == nil || req["operator"] == "" { if u := currentUsername(r); u != "" { req["operator"] = u } } body, _ := json.Marshal(req) resp, _, err := ctx.Mes.Post("/api/internal/line/call-material", json.RawMessage(body)) mesJSON(w, resp, err) } }