feat: 完成客户第12条需求+多系统改造
1. 新增预警表添加工单号字段,支持按工单号检索预警 2. 添加工单过滤在制品/绩效报表/预警中心查询 3. 补全WMS台账字段口径与权限配置 4. 修复备料单与数量不符处理流程 5. 新增配送进度/库位联动/自动出库功能 6. 修复AGV配送状态更新逻辑 7. 优化退料与库存管理细节
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"bj_power_mes/common/httpx"
|
||||
"bj_power_mes/internal/logic"
|
||||
"bj_power_mes/internal/svc"
|
||||
)
|
||||
|
||||
// MaterialDeliveryProgressHandler GET /api/v1/material-requests/progress?orderNo=&materialCode=&page=&pageSize=
|
||||
//
|
||||
// 配送进度(只读聚合):WMS 台账「需求/已发出/已退回/已消耗/净领用/缺口」
|
||||
// + MES 备料单「已接料」→ 派生「在途 / 线边」。
|
||||
// 不新增账本、不双写:两边各管各的真源字段,本接口只做合成展示。
|
||||
func MaterialDeliveryProgressHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
q := r.URL.Query()
|
||||
data, err := logic.New(svcCtx).DeliveryProgress(r.Context(),
|
||||
q.Get("orderNo"), q.Get("materialCode"),
|
||||
atoiDefault(q.Get("page"), 1), atoiDefault(q.Get("pageSize"), 50))
|
||||
if err != nil {
|
||||
httpx.Fail(w, 3204, err.Error())
|
||||
return
|
||||
}
|
||||
httpx.Ok(w, data)
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,9 @@ var pathPermMap = map[string]string{
|
||||
"POST:/api/v1/bom/item/delete": "produce.bom:edit",
|
||||
"POST:/api/v1/bom/import": "produce.bom:edit",
|
||||
"POST:/api/v1/material-requests/generate": "produce.material:generate",
|
||||
"POST:/api/v1/material-requests/auto-outbound": "produce.material:outbound",
|
||||
// 数量不符处理(produce.qty 菜单下:补发/退库/调整关闭)
|
||||
"POST:/api/v1/qty-reports/handle": "produce.qty:handle",
|
||||
"POST:/api/v1/plc/send-process": "produce.plc:send",
|
||||
"POST:/api/v1/process-flows": "produce.processflow:add",
|
||||
"POST:/api/v1/process-flows/status": "produce.processflow:edit",
|
||||
@@ -36,6 +39,10 @@ var pathPermMap = map[string]string{
|
||||
"POST:/api/v1/inspections/generate-inter-process": "sys.inspect:process",
|
||||
// 质量检验处置(过程检/完工检不合格处置 退货/返修/退换)
|
||||
"POST:/api/v1/quality/disposal": "produce.quality:edit",
|
||||
// 附件中心(sys.attachment 菜单下):删除 / 归档清理属管理动作;
|
||||
// 列表/预览/下载不设按钮门槛(有菜单权限即可查阅)
|
||||
"POST:/api/v1/attachment/delete": "sys.attachment:manage",
|
||||
"POST:/api/v1/attachment/archive": "sys.attachment:manage",
|
||||
// 装机绑定(报工前扫料/撤销,属报工操作域)
|
||||
"POST:/api/v1/binds": "produce.scan",
|
||||
"POST:/api/v1/binds/remove": "produce.scan",
|
||||
|
||||
@@ -81,14 +81,15 @@ func ListAlertRulesHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
}
|
||||
}
|
||||
|
||||
// ListAlertsHandler GET /alerts?status=&type=&from=&to=&page=&pageSize=
|
||||
// 预警消息收件箱:支持 状态/类型/时间范围(YYYY-MM-DD) 筛选;带 page 返回分页对象,否则返回全量数组(顶部铃铛未读数复用)。
|
||||
// ListAlertsHandler GET /alerts?status=&type=&orderNo=&from=&to=&page=&pageSize=
|
||||
// 预警消息收件箱:支持 状态/类型/工单号/时间范围(YYYY-MM-DD) 筛选;带 page 返回分页对象,否则返回全量数组(顶部铃铛未读数复用)。
|
||||
func ListAlertsHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
q := r.URL.Query()
|
||||
query := logic.AlertQuery{
|
||||
Status: q.Get("status"),
|
||||
Type: q.Get("type"),
|
||||
OrderNo: q.Get("orderNo"),
|
||||
From: q.Get("from"),
|
||||
To: q.Get("to"),
|
||||
Page: atoiDefault(q.Get("page"), 0),
|
||||
|
||||
@@ -218,12 +218,41 @@ func GenerateMaterialHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
}
|
||||
}
|
||||
|
||||
// AutoOutboundHandler POST /material-requests/auto-outbound 备料自动出库(一键补出)。
|
||||
// 日常由「生成备料单」自动触发(客户第1条:平时无需操作);库存补齐后库管可在此一键补出。
|
||||
func AutoOutboundHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req struct {
|
||||
PlanDate string `json:"planDate"`
|
||||
OrderNo string `json:"orderNo"`
|
||||
}
|
||||
if err := httpx.ParseJSON(r, &req); err != nil {
|
||||
httpx.BadRequest(w, "请求体解析失败")
|
||||
return
|
||||
}
|
||||
if req.PlanDate == "" && req.OrderNo == "" {
|
||||
httpx.BadRequest(w, "planDate 与 orderNo 至少填一个")
|
||||
return
|
||||
}
|
||||
done, short := logic.New(svcCtx).AutoOutboundRequests(r.Context(), req.PlanDate, req.OrderNo, operator(r, ""))
|
||||
httpx.OkMessage(w, "自动出库完成", map[string]any{"done": done, "short": short})
|
||||
}
|
||||
}
|
||||
|
||||
func ListMaterialRequestsHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
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("stationNo"))
|
||||
data, err := logic.New(svcCtx).ListMaterialRequests(r.Context(), logic.MaterialRequestQuery{
|
||||
OrderNo: q.Get("orderNo"),
|
||||
PlanDate: q.Get("planDate"),
|
||||
Status: q.Get("status"),
|
||||
MaterialCode: q.Get("materialCode"),
|
||||
MaterialName: q.Get("materialName"),
|
||||
TargetDock: q.Get("targetDock"),
|
||||
StationNo: q.Get("stationNo"),
|
||||
Source: q.Get("source"),
|
||||
Keyword: q.Get("keyword"),
|
||||
})
|
||||
if err != nil {
|
||||
httpx.Fail(w, 3104, err.Error())
|
||||
return
|
||||
@@ -237,7 +266,12 @@ 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"), q.Get("stationNo"))
|
||||
data, err := logic.New(svcCtx).ListMaterialRequests(r.Context(), logic.MaterialRequestQuery{
|
||||
OrderNo: q.Get("orderNo"),
|
||||
PlanDate: q.Get("planDate"),
|
||||
Status: q.Get("status"),
|
||||
StationNo: q.Get("stationNo"),
|
||||
})
|
||||
if err != nil {
|
||||
httpx.Fail(w, 3105, err.Error())
|
||||
return
|
||||
@@ -286,7 +320,7 @@ func ListDocksHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
}
|
||||
|
||||
// ReceiveMaterialRequestHandler POST /material-requests/receive 接料确认(MES 备料页人工点「已接料」)。
|
||||
// qty 可空(≤0 表示确认本批全部剩余);累加 sentQty 封顶 reqQty,达量自动 DONE。
|
||||
// qty 可空(≤0 表示确认本批全部剩余);累加 receivedQty(已接料),上限为已发出量。
|
||||
func ReceiveMaterialRequestHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req struct {
|
||||
|
||||
@@ -110,7 +110,7 @@ func OrderQueryInternalHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
func InProcessInternalHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
stationNo := atoiDefault(r.URL.Query().Get("stationNo"), 0)
|
||||
data, err := logic.New(svcCtx).WorkpiecesInProcess(r.Context(), stationNo)
|
||||
data, err := logic.New(svcCtx).WorkpiecesInProcess(r.Context(), stationNo, r.URL.Query().Get("orderNo"))
|
||||
if err != nil {
|
||||
httpx.Fail(w, 2006, err.Error())
|
||||
return
|
||||
|
||||
@@ -271,10 +271,11 @@ func SuggestedDockCodesHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
|
||||
// InProcessHandler GET /workpieces/in-process 跨天在制品视图(停在各工位未完工工件)
|
||||
// stationNo 可选:0=上线位, 13=下线位, 其余为物理工位;不传或<=0 返回全部。
|
||||
// orderNo 可选:按工单号模糊过滤(客户第12条「记录查看加一项看工单号查询」)。
|
||||
func InProcessHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
stationNo, _ := strconv.Atoi(r.URL.Query().Get("stationNo"))
|
||||
data, err := logic.New(svcCtx).WorkpiecesInProcess(r.Context(), stationNo)
|
||||
data, err := logic.New(svcCtx).WorkpiecesInProcess(r.Context(), stationNo, r.URL.Query().Get("orderNo"))
|
||||
if err != nil {
|
||||
httpx.Fail(w, 3001, err.Error())
|
||||
return
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"bj_power_mes/common/httpx"
|
||||
"bj_power_mes/internal/logic"
|
||||
"bj_power_mes/internal/svc"
|
||||
)
|
||||
|
||||
// ============ 数量不符处理(MES 侧:唯一真源 + 处理闭环) ============
|
||||
//
|
||||
// 链路:工位终端上报(/api/internal/station/qty-report)→ 本页查询/处理
|
||||
// - 补发 REPLENISH:生成 source=REFILL 补料单(少发时可用)
|
||||
// - 退库 RETURN :调 WMS 建退库单(多发时可用)
|
||||
// - 调整 ADJUST :双方确认后直接关闭,必须填说明
|
||||
|
||||
// ListQtyReportsHandler GET /api/v1/qty-reports?status=&stationNo=&materialCode=&orderNo=&page=&pageSize=
|
||||
// 数量不符记录分页查询(真分页:后端 SQL 层分页)。
|
||||
func ListQtyReportsHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
q := r.URL.Query()
|
||||
data, err := logic.New(svcCtx).ListQtyReports(r.Context(), logic.QtyReportQuery{
|
||||
Status: q.Get("status"),
|
||||
StationNo: q.Get("stationNo"),
|
||||
MaterialCode: q.Get("materialCode"),
|
||||
OrderNo: q.Get("orderNo"),
|
||||
Page: atoiDefault(q.Get("page"), 1),
|
||||
PageSize: atoiDefault(q.Get("pageSize"), 20),
|
||||
})
|
||||
if err != nil {
|
||||
httpx.Fail(w, 3201, err.Error())
|
||||
return
|
||||
}
|
||||
httpx.Ok(w, data)
|
||||
}
|
||||
}
|
||||
|
||||
// QtyReportStatsHandler GET /api/v1/qty-reports/stats
|
||||
// 数量不符概览:待处理 / 已补发 / 已退库 / 已关闭。
|
||||
func QtyReportStatsHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
data, err := logic.New(svcCtx).QtyReportStats(r.Context())
|
||||
if err != nil {
|
||||
httpx.Fail(w, 3202, err.Error())
|
||||
return
|
||||
}
|
||||
httpx.Ok(w, data)
|
||||
}
|
||||
}
|
||||
|
||||
// HandleQtyReportHandler POST /api/v1/qty-reports/handle
|
||||
// body: { id, handleType(REPLENISH/RETURN/ADJUST), note }
|
||||
func HandleQtyReportHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req struct {
|
||||
ID int `json:"id"`
|
||||
HandleType string `json:"handleType"`
|
||||
Note string `json:"note"`
|
||||
}
|
||||
if err := httpx.ParseJSON(r, &req); err != nil {
|
||||
httpx.BadRequest(w, "请求体解析失败")
|
||||
return
|
||||
}
|
||||
if req.HandleType != "REPLENISH" && req.HandleType != "RETURN" && req.HandleType != "ADJUST" {
|
||||
httpx.BadRequest(w, "处理方式仅支持 补发/退库/调整")
|
||||
return
|
||||
}
|
||||
// 处理人取自鉴权中间件写入的用户名(无则留空,不阻断处理)
|
||||
operator := r.Header.Get("X-Username")
|
||||
data, err := logic.New(svcCtx).HandleQtyReport(r.Context(), req.ID, req.HandleType, req.Note, operator)
|
||||
if err != nil {
|
||||
httpx.Fail(w, 3203, err.Error())
|
||||
return
|
||||
}
|
||||
httpx.OkMessage(w, "处理完成", data)
|
||||
}
|
||||
}
|
||||
@@ -173,6 +173,13 @@ 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.MethodPost, Path: "/material-requests/auto-outbound", Handler: production.AutoOutboundHandler(serverCtx)},
|
||||
// 配送进度(只读聚合:WMS 台账四段量 + MES 已接料 → 在途/线边)
|
||||
{Method: http.MethodGet, Path: "/material-requests/progress", Handler: MaterialDeliveryProgressHandler(serverCtx)},
|
||||
// 数量不符处理(MES 为唯一真源与处理侧;上报入口在 /api/internal/station/qty-report)
|
||||
{Method: http.MethodGet, Path: "/qty-reports", Handler: ListQtyReportsHandler(serverCtx)},
|
||||
{Method: http.MethodGet, Path: "/qty-reports/stats", Handler: QtyReportStatsHandler(serverCtx)},
|
||||
{Method: http.MethodPost, Path: "/qty-reports/handle", Handler: HandleQtyReportHandler(serverCtx)},
|
||||
{Method: http.MethodGet, Path: "/docks", Handler: production.ListDocksHandler(serverCtx)},
|
||||
|
||||
{Method: http.MethodPost, Path: "/plc/send-process", Handler: production.PlcSendProcessHandler(serverCtx)},
|
||||
|
||||
@@ -139,7 +139,7 @@ func StationReportInternalHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
func WorkloadInternalHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
q := r.URL.Query()
|
||||
data, err := logic.New(svcCtx).Workload(r.Context(), q.Get("operator"), q.Get("stationNo"), q.Get("from"), q.Get("to"),
|
||||
data, err := logic.New(svcCtx).Workload(r.Context(), q.Get("operator"), q.Get("stationNo"), q.Get("orderNo"), q.Get("from"), q.Get("to"),
|
||||
atoiDefault(q.Get("opPage"), 1), atoiDefault(q.Get("opPageSize"), 20),
|
||||
atoiDefault(q.Get("stPage"), 1), atoiDefault(q.Get("stPageSize"), 20),
|
||||
atoiDefault(q.Get("detPage"), 1), atoiDefault(q.Get("detPageSize"), 20))
|
||||
@@ -209,7 +209,7 @@ func PerformanceExportHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
httpx.Fail(w, 2106, rerr.Error())
|
||||
return
|
||||
}
|
||||
data, err := logic.New(svcCtx).Workload(r.Context(), q.Get("operator"), q.Get("stationNo"), startDate, endDate,
|
||||
data, err := logic.New(svcCtx).Workload(r.Context(), q.Get("operator"), q.Get("stationNo"), q.Get("orderNo"), startDate, endDate,
|
||||
1, 1<<30, 1, 1<<30, 1, 1<<30)
|
||||
if err != nil {
|
||||
httpx.Fail(w, 2105, err.Error())
|
||||
|
||||
Reference in New Issue
Block a user