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

158 lines
5.2 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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/ackPLC 应答:通常由 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)
}
}