feat: 完成多模块迭代更新
- 新增过程巡检按步骤上报、数量不符上报、工位退料功能 - 增加工单工程编号三级归属字段 - 新增物料安全库存与缺货预警 - 新增附件管理、退库单管理模块 - 优化生产看板展示逻辑与前端页面文案 - 清理冗余的工艺路线相关代码与备份文件
This commit is contained in:
@@ -0,0 +1,221 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"bj_power_mes/common/httpx"
|
||||
"bj_power_mes/internal/logic"
|
||||
"bj_power_mes/internal/svc"
|
||||
)
|
||||
|
||||
// ============ 工位终端 → MES(X-API-TOKEN 保护内部接口) ============
|
||||
|
||||
// StationEmergencyInternalHandler POST /api/internal/station/emergency
|
||||
// 工位终端应急呼叫:落预警中心消息(type=emergency_call),pad 顶部轮询提示。
|
||||
func StationEmergencyInternalHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req struct {
|
||||
StationNo int `json:"stationNo"`
|
||||
OrderNo string `json:"orderNo"`
|
||||
Remark string `json:"remark"`
|
||||
Operator string `json:"operator"`
|
||||
}
|
||||
if err := httpx.ParseJSON(r, &req); err != nil {
|
||||
httpx.BadRequest(w, "请求体解析失败")
|
||||
return
|
||||
}
|
||||
if err := logic.New(svcCtx).EmergencyCall(r.Context(), req.StationNo, req.OrderNo, req.Remark, req.Operator); err != nil {
|
||||
httpx.Fail(w, 2401, err.Error())
|
||||
return
|
||||
}
|
||||
httpx.OkMessage(w, "应急呼叫已发送", nil)
|
||||
}
|
||||
}
|
||||
|
||||
// StationQtyReportInternalHandler POST /api/internal/station/qty-report
|
||||
// 工位数量不符上报:落 material_qty_report + 预警中心消息(type=qty_diff)。
|
||||
func StationQtyReportInternalHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req logic.QtyReportReq
|
||||
if err := httpx.ParseJSON(r, &req); err != nil {
|
||||
httpx.BadRequest(w, "请求体解析失败")
|
||||
return
|
||||
}
|
||||
if err := logic.New(svcCtx).CreateQtyReport(r.Context(), req); err != nil {
|
||||
httpx.Fail(w, 2402, err.Error())
|
||||
return
|
||||
}
|
||||
httpx.OkMessage(w, "数量不符已上报", nil)
|
||||
}
|
||||
}
|
||||
|
||||
// StationReturnMaterialInternalHandler POST /api/internal/station/return-material
|
||||
// 工位退料回库房:落事件日志 + 调 WMS 预建待确认退库单(WMS 确认收货后库存加回)。
|
||||
func StationReturnMaterialInternalHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req struct {
|
||||
OrderNo string `json:"orderNo"`
|
||||
StationNo int `json:"stationNo"`
|
||||
Sn string `json:"sn"`
|
||||
MaterialCode string `json:"materialCode"`
|
||||
MaterialName string `json:"materialName"`
|
||||
Spec string `json:"spec"`
|
||||
Qty int `json:"qty"`
|
||||
Unit string `json:"unit"`
|
||||
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).StationReturnMaterial(r.Context(), req.OrderNo, req.StationNo, req.Sn,
|
||||
req.MaterialCode, req.MaterialName, req.Spec, req.Reason, req.Operator, req.Unit, req.Qty); err != nil {
|
||||
httpx.Fail(w, 2403, err.Error())
|
||||
return
|
||||
}
|
||||
httpx.OkMessage(w, "退库单已提交", nil)
|
||||
}
|
||||
}
|
||||
|
||||
// StationInspectionInternalHandler POST /api/internal/station/inspection
|
||||
// 工位终端提交巡检记录(签到/点检/过程巡检/完工确认/异常),复用与 PAD 巡检终端相同的落库逻辑。
|
||||
// 工位终端无按钮级权限概念,仅按 category 合法性校验(CreateInspection 内部已做)。
|
||||
func StationInspectionInternalHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req logic.InspectionReq
|
||||
if err := httpx.ParseJSON(r, &req); err != nil {
|
||||
httpx.BadRequest(w, "请求体解析失败")
|
||||
return
|
||||
}
|
||||
if err := logic.New(svcCtx).CreateInspection(r.Context(), req, req.Operator); err != nil {
|
||||
httpx.Fail(w, 2301, err.Error())
|
||||
return
|
||||
}
|
||||
httpx.OkMessage(w, "巡检记录已提交", nil)
|
||||
}
|
||||
}
|
||||
|
||||
// StationInspectionUploadInternalHandler POST /api/internal/station/inspection-upload
|
||||
// 工位终端巡检拍照上传(手机设备拍照)。前端把图片读成 base64 传 {filename, data},
|
||||
// 服务端解码落盘到 Upload.Dir/日期目录,返回系统文件名(落库到 inspection_record.photo)。
|
||||
func StationInspectionUploadInternalHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req struct {
|
||||
Filename string `json:"filename"`
|
||||
Data string `json:"data"` // base64(可带 data:image/xxx;base64, 前缀)
|
||||
}
|
||||
if err := httpx.ParseJSON(r, &req); err != nil {
|
||||
httpx.BadRequest(w, "请求体解析失败")
|
||||
return
|
||||
}
|
||||
if req.Data == "" {
|
||||
httpx.BadRequest(w, "图片数据为空")
|
||||
return
|
||||
}
|
||||
raw := req.Data
|
||||
if i := strings.Index(raw, ","); i >= 0 && strings.Contains(raw[:i], "base64") {
|
||||
raw = raw[i+1:]
|
||||
}
|
||||
b, err := base64.StdEncoding.DecodeString(raw)
|
||||
if err != nil {
|
||||
httpx.Fail(w, 2306, "图片解码失败:"+err.Error())
|
||||
return
|
||||
}
|
||||
sub := time.Now().Format("2006-01-02")
|
||||
dir := filepath.Join(svcCtx.Config.Upload.Dir, sub)
|
||||
if err := os.MkdirAll(dir, 0o755); err != nil {
|
||||
httpx.Fail(w, 2306, "目录创建失败:"+err.Error())
|
||||
return
|
||||
}
|
||||
name := req.Filename
|
||||
if name == "" || strings.ContainsAny(name, "/\\") {
|
||||
name = "insp_" + time.Now().Format("150405") + ".png"
|
||||
}
|
||||
// 文件名加日期前缀防重复
|
||||
saveName := time.Now().Format("20060102") + "_" + filepath.Base(name)
|
||||
full := filepath.Join(dir, saveName)
|
||||
if err := os.WriteFile(full, b, 0o644); err != nil {
|
||||
httpx.Fail(w, 2306, "照片保存失败:"+err.Error())
|
||||
return
|
||||
}
|
||||
httpx.Ok(w, map[string]any{"filename": sub + "/" + saveName, "url": "/api/v1/files/" + sub + "/" + saveName})
|
||||
}
|
||||
}
|
||||
|
||||
// ============ MES 前端(JWT 保护) ============
|
||||
|
||||
// EmergencyCallHandler POST /api/v1/alert/emergency(MES 前端也可发起应急呼叫)
|
||||
func EmergencyCallHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req struct {
|
||||
StationNo int `json:"stationNo"`
|
||||
OrderNo string `json:"orderNo"`
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
if err := httpx.ParseJSON(r, &req); err != nil {
|
||||
httpx.BadRequest(w, "请求体解析失败")
|
||||
return
|
||||
}
|
||||
if err := logic.New(svcCtx).EmergencyCall(r.Context(), req.StationNo, req.OrderNo, req.Remark, operator(r, "")); err != nil {
|
||||
httpx.Fail(w, 2401, err.Error())
|
||||
return
|
||||
}
|
||||
httpx.OkMessage(w, "应急呼叫已发送", nil)
|
||||
}
|
||||
}
|
||||
|
||||
// CreateQtyReportHandler POST /api/v1/qty-reports
|
||||
func CreateQtyReportHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req logic.QtyReportReq
|
||||
if err := httpx.ParseJSON(r, &req); err != nil {
|
||||
httpx.BadRequest(w, "请求体解析失败")
|
||||
return
|
||||
}
|
||||
req.Operator = operator(r, req.Operator)
|
||||
if err := logic.New(svcCtx).CreateQtyReport(r.Context(), req); err != nil {
|
||||
httpx.Fail(w, 2402, err.Error())
|
||||
return
|
||||
}
|
||||
httpx.OkMessage(w, "数量不符已上报", nil)
|
||||
}
|
||||
}
|
||||
|
||||
// ListQtyReportsHandler GET /api/v1/qty-reports?status=
|
||||
func ListQtyReportsHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
data, err := logic.New(svcCtx).ListQtyReports(r.Context(),
|
||||
r.URL.Query().Get("status"), r.URL.Query().Get("orderNo"), r.URL.Query().Get("materialCode"))
|
||||
if err != nil {
|
||||
httpx.Fail(w, 2404, err.Error())
|
||||
return
|
||||
}
|
||||
httpx.Ok(w, data)
|
||||
}
|
||||
}
|
||||
|
||||
// GenerateInterProcessInspectionHandler POST /api/v1/inspections/generate-inter-process
|
||||
// 一键生成《工序间检验记录》:汇总该工单 PROCESS 类巡检记录(按步骤维度)。
|
||||
func GenerateInterProcessInspectionHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req struct {
|
||||
OrderNo string `json:"orderNo"`
|
||||
}
|
||||
if err := httpx.ParseJSON(r, &req); err != nil {
|
||||
httpx.BadRequest(w, "请求体解析失败")
|
||||
return
|
||||
}
|
||||
data, err := logic.New(svcCtx).GenerateInterProcessInspection(r.Context(), req.OrderNo)
|
||||
if err != nil {
|
||||
httpx.Fail(w, 2405, err.Error())
|
||||
return
|
||||
}
|
||||
httpx.Ok(w, data)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user