docs(deployment): 更新部署手册和业务架构说明
- 更新MES和WMS服务端口配置说明 - 详细说明产线工位设备配置(扫码枪+拧紧枪) - 明确AGV配送和接驳台的模拟模式与真实对接方案 - 更新WMS中AGV配送默认模拟模式说明 - 完善厂内物流(AGV/接驳台)的业务架构约束 - 修订临时文档为详细的项目问题和解决方案规划
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
@@ -76,8 +75,8 @@ func dockMaintRowOf(d *ent.Dock) dockMaintRow {
|
||||
}
|
||||
}
|
||||
|
||||
// listDocksMaintHandler GET /api/docks?dockType=&keyword=&page=&pageSize=
|
||||
// 接驳台主数据分页查询(真分页在 SQL 层),默认按创建时间倒序。
|
||||
// listDocksMaintHandler GET /api/docks?dockType=&dockCode=&name=&status=&page=&pageSize=
|
||||
// 接驳台主数据只读分页查询(真分页在 SQL 层),编码精确/名称模糊/状态精确各自独立筛选。
|
||||
func listDocksMaintHandler(ctx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
q := r.URL.Query()
|
||||
@@ -85,11 +84,15 @@ func listDocksMaintHandler(ctx *svc.ServiceContext) http.HandlerFunc {
|
||||
if typ := q.Get("dockType"); typ != "" {
|
||||
query = query.Where(dock.DockType(typ))
|
||||
}
|
||||
if kw := strings.TrimSpace(q.Get("keyword")); kw != "" {
|
||||
query = query.Where(dock.Or(
|
||||
dock.DockCodeContainsFold(kw),
|
||||
dock.NameContainsFold(kw),
|
||||
))
|
||||
// 列表页禁止关键字混搜:编码精确、名称模糊、状态精确,各字段独立筛选(需求5、N)
|
||||
if code := strings.TrimSpace(q.Get("dockCode")); code != "" {
|
||||
query = query.Where(dock.DockCodeEqualFold(code))
|
||||
}
|
||||
if name := strings.TrimSpace(q.Get("name")); name != "" {
|
||||
query = query.Where(dock.NameContainsFold(name))
|
||||
}
|
||||
if status := strings.TrimSpace(q.Get("status")); status != "" {
|
||||
query = query.Where(dock.StatusEQ(status))
|
||||
}
|
||||
total, err := query.Count(ctx0())
|
||||
if err != nil {
|
||||
@@ -119,159 +122,36 @@ func listDocksMaintHandler(ctx *svc.ServiceContext) http.HandlerFunc {
|
||||
}
|
||||
}
|
||||
|
||||
// dockReq 接驳台保存请求
|
||||
type dockReq struct {
|
||||
ID int64 `json:"id"`
|
||||
DockCode string `json:"dockCode"`
|
||||
Name string `json:"name"`
|
||||
DockType string `json:"dockType"`
|
||||
// 接驳台为系统预置只读主数据(需求5):不提供新增/修改/删除,仅保留查询。
|
||||
// 原 createDockHandler / updateDockHandler / deleteDockHandler / validateDock / dockReq 已彻底移除,
|
||||
// 接驳台数据由 db/seed.go 种子初始化,调整由实施维护种子或数据库完成。
|
||||
|
||||
// stationOption 出库目标工位下拉项(需求20:目标工位可下拉选择、不允许手输)。
|
||||
// 客户口径「出库工位号来自 MES 工位列表」——WMS 侧以接驳台主数据中 line 类型
|
||||
// (产线接驳台与工位 1:1 绑定)为真源派生工位选项,避免前端写死 1~20。
|
||||
type stationOption struct {
|
||||
StationNo int `json:"stationNo"`
|
||||
Status string `json:"status"`
|
||||
Name string `json:"name"`
|
||||
DockCode string `json:"dockCode"`
|
||||
}
|
||||
|
||||
// validateDock 校验接驳台字段:
|
||||
// - 编码/名称必填,编码全库唯一;
|
||||
// - 类型仅 line/store/other;
|
||||
// - 产线接驳台必须绑定工位号(>0)且与工位 1:1;库房/其他接驳台不绑定工位(固定 0)。
|
||||
func validateDock(ctx *svc.ServiceContext, req *dockReq, selfID int64) error {
|
||||
req.DockCode = strings.TrimSpace(req.DockCode)
|
||||
req.Name = strings.TrimSpace(req.Name)
|
||||
if req.DockCode == "" {
|
||||
return errors.New("接驳台编码必填")
|
||||
}
|
||||
if req.Name == "" {
|
||||
return errors.New("接驳台名称必填")
|
||||
}
|
||||
switch req.DockType {
|
||||
case "line", "store", "other":
|
||||
default:
|
||||
return errors.New("接驳台类型仅支持 产线/库房/其他")
|
||||
}
|
||||
if req.Status != "ENABLED" && req.Status != "DISABLED" {
|
||||
return errors.New("接驳台状态仅支持 启用/停用")
|
||||
}
|
||||
if req.DockType != "line" {
|
||||
req.StationNo = 0
|
||||
} else if req.StationNo <= 0 {
|
||||
return errors.New("产线接驳台必须绑定工位号")
|
||||
}
|
||||
dupCode := ctx.EntClient.Dock.Query().Where(dock.DockCode(req.DockCode))
|
||||
if selfID > 0 {
|
||||
dupCode = dupCode.Where(dock.IDNEQ(int(selfID)))
|
||||
}
|
||||
if exists, err := dupCode.Exist(ctx0()); err == nil && exists {
|
||||
return errors.New("接驳台编码已存在:" + req.DockCode)
|
||||
}
|
||||
if req.StationNo > 0 {
|
||||
q := ctx.EntClient.Dock.Query().Where(dock.StationNo(req.StationNo))
|
||||
if selfID > 0 {
|
||||
q = q.Where(dock.IDNEQ(int(selfID)))
|
||||
}
|
||||
other, err := q.First(ctx0())
|
||||
if err == nil && other != nil {
|
||||
return errors.New("该工位已绑定接驳台 " + other.DockCode + ",一个工位只能有一个接驳台")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// createDockHandler POST /api/docks 新增接驳台
|
||||
func createDockHandler(ctx *svc.ServiceContext) http.HandlerFunc {
|
||||
// listStationOptionsHandler GET /api/outbound/stations 返回产线工位下拉选项(无权限包裹,供出库页调用)。
|
||||
func listStationOptionsHandler(ctx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req dockReq
|
||||
if err := parseJSON(r, &req); err != nil {
|
||||
fail(w, http.StatusBadRequest, "参数错误")
|
||||
return
|
||||
}
|
||||
if req.DockType == "" {
|
||||
req.DockType = "line"
|
||||
}
|
||||
if req.Status == "" {
|
||||
req.Status = "ENABLED"
|
||||
}
|
||||
if err := validateDock(ctx, &req, 0); err != nil {
|
||||
fail(w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
_, err := ctx.EntClient.Dock.Create().
|
||||
SetDockCode(req.DockCode).SetName(req.Name).SetDockType(req.DockType).
|
||||
SetStationNo(req.StationNo).SetHasPallet(false).SetStatus(req.Status).
|
||||
Save(ctx0())
|
||||
list, err := ctx.EntClient.Dock.Query().
|
||||
Where(dock.DockType("line")).
|
||||
Order(ent.Asc("station_no")).All(ctx0())
|
||||
if err != nil {
|
||||
fail(w, http.StatusInternalServerError, "新增接驳台失败: "+err.Error())
|
||||
fail(w, http.StatusInternalServerError, "查询工位失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
ctx.EventLog.Write(ctx0(), "dock.create", r.Header.Get("X-Username"), "dock", req.DockCode,
|
||||
"新增接驳台", map[string]any{"dockCode": req.DockCode, "dockType": req.DockType, "stationNo": req.StationNo})
|
||||
ok(w, map[string]any{"message": "接驳台已新增"})
|
||||
}
|
||||
}
|
||||
|
||||
// updateDockHandler POST /api/docks/update 修改接驳台(编码不可改,是产线/AGV 任务的引用锚点)
|
||||
func updateDockHandler(ctx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req dockReq
|
||||
if err := parseJSON(r, &req); err != nil {
|
||||
fail(w, http.StatusBadRequest, "参数错误")
|
||||
return
|
||||
}
|
||||
if req.ID <= 0 {
|
||||
fail(w, http.StatusBadRequest, "缺少 id")
|
||||
return
|
||||
}
|
||||
old, err := ctx.EntClient.Dock.Get(ctx0(), int(req.ID))
|
||||
if err != nil {
|
||||
fail(w, http.StatusBadRequest, "接驳台不存在")
|
||||
return
|
||||
}
|
||||
req.DockCode = old.DockCode
|
||||
if err := validateDock(ctx, &req, req.ID); err != nil {
|
||||
fail(w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
_, err = old.Update().
|
||||
SetName(req.Name).SetDockType(req.DockType).
|
||||
SetStationNo(req.StationNo).SetStatus(req.Status).Save(ctx0())
|
||||
if err != nil {
|
||||
fail(w, http.StatusInternalServerError, "修改接驳台失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
ctx.EventLog.Write(ctx0(), "dock.update", r.Header.Get("X-Username"), "dock", old.DockCode,
|
||||
"修改接驳台", map[string]any{"dockCode": old.DockCode, "dockType": req.DockType, "stationNo": req.StationNo})
|
||||
ok(w, map[string]any{"message": "接驳台已保存"})
|
||||
}
|
||||
}
|
||||
|
||||
// deleteDockHandler POST /api/docks/delete 删除接驳台
|
||||
// 正在占用(有托盘)的接驳台不允许删除,避免 AGV/产线任务指向已删除的接驳台。
|
||||
func deleteDockHandler(ctx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req struct {
|
||||
ID int64 `json:"id"`
|
||||
}
|
||||
if err := parseJSON(r, &req); err != nil {
|
||||
fail(w, http.StatusBadRequest, "参数错误")
|
||||
return
|
||||
}
|
||||
if req.ID <= 0 {
|
||||
fail(w, http.StatusBadRequest, "缺少 id")
|
||||
return
|
||||
}
|
||||
d, err := ctx.EntClient.Dock.Get(ctx0(), int(req.ID))
|
||||
if err != nil {
|
||||
fail(w, http.StatusBadRequest, "接驳台不存在")
|
||||
return
|
||||
}
|
||||
if d.HasPallet {
|
||||
fail(w, http.StatusBadRequest, "接驳台 "+d.DockCode+" 当前有托盘占用,请先处理托盘后再删除")
|
||||
return
|
||||
}
|
||||
if err := ctx.EntClient.Dock.DeleteOneID(d.ID).Exec(ctx0()); err != nil {
|
||||
fail(w, http.StatusInternalServerError, "删除接驳台失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
ctx.EventLog.Write(ctx0(), "dock.delete", r.Header.Get("X-Username"), "dock", d.DockCode,
|
||||
"删除接驳台", map[string]any{"dockCode": d.DockCode})
|
||||
ok(w, map[string]any{"message": "接驳台已删除"})
|
||||
rows := make([]stationOption, 0, len(list))
|
||||
for _, d := range list {
|
||||
if d.StationNo <= 0 {
|
||||
continue
|
||||
}
|
||||
rows = append(rows, stationOption{StationNo: d.StationNo, Name: d.Name, DockCode: d.DockCode})
|
||||
}
|
||||
ok(w, map[string]any{"list": rows})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user