278 lines
8.9 KiB
Go
278 lines
8.9 KiB
Go
package handler
|
||||
|
|
|
|||
|
|
import (
|
|||
|
|
"errors"
|
|||
|
|
"net/http"
|
|||
|
|
"strings"
|
|||
|
|
|
|||
|
|
"bj_power_wms/ent"
|
|||
|
|
"bj_power_wms/ent/dock"
|
|||
|
|
"bj_power_wms/internal/svc"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
// dockRow 接驳台返回 DTO(显式 json 标签,避免依赖 ent 默认编组)。
|
|||
|
|
type dockRow struct {
|
|||
|
|
DockCode string `json:"dockCode"`
|
|||
|
|
Name string `json:"name"`
|
|||
|
|
DockType string `json:"dockType"`
|
|||
|
|
StationNo int `json:"stationNo"`
|
|||
|
|
HasPallet bool `json:"hasPallet"`
|
|||
|
|
Status string `json:"status"`
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// listDocksInternalHandler 返回接驳台主数据列表(dock 真源在 WMS)。
|
|||
|
|
// MES 工位维护页拉取此接口填充「送达接驳台」下拉,避免写死 DOCK01..20 留后患。
|
|||
|
|
func listDocksInternalHandler(ctx *svc.ServiceContext) http.HandlerFunc {
|
|||
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|||
|
|
list, err := ctx.EntClient.Dock.Query().
|
|||
|
|
Order(ent.Asc("dock_code")).All(ctx0())
|
|||
|
|
if err != nil {
|
|||
|
|
fail(w, http.StatusInternalServerError, "查询接驳台失败: "+err.Error())
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
rows := make([]dockRow, 0, len(list))
|
|||
|
|
for _, d := range list {
|
|||
|
|
rows = append(rows, dockRow{
|
|||
|
|
DockCode: d.DockCode,
|
|||
|
|
Name: d.Name,
|
|||
|
|
DockType: d.DockType,
|
|||
|
|
StationNo: d.StationNo,
|
|||
|
|
HasPallet: d.HasPallet,
|
|||
|
|
Status: d.Status,
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
ok(w, map[string]any{"list": rows})
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ---------- 接驳台主数据维护 ----------
|
|||
|
|
// 接驳台是独立主数据:除产线工位旁,库房、缓存区等其他位置也有接驳台位。
|
|||
|
|
// 类型:line=产线(绑定工位,与工位 1:1)/ store=库房 / other=其他(均不绑定工位)。
|
|||
|
|
|
|||
|
|
// dockMaintRow 接驳台维护页返回行(显式 json,避免 ent omitempty 吞掉 hasPallet=false)
|
|||
|
|
type dockMaintRow struct {
|
|||
|
|
ID int64 `json:"id"`
|
|||
|
|
DockCode string `json:"dockCode"`
|
|||
|
|
Name string `json:"name"`
|
|||
|
|
DockType string `json:"dockType"`
|
|||
|
|
StationNo int `json:"stationNo"`
|
|||
|
|
Status string `json:"status"`
|
|||
|
|
HasPallet bool `json:"hasPallet"`
|
|||
|
|
PalletRef string `json:"palletRef"`
|
|||
|
|
CreatedAt int64 `json:"createdAt"`
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func dockMaintRowOf(d *ent.Dock) dockMaintRow {
|
|||
|
|
return dockMaintRow{
|
|||
|
|
ID: int64(d.ID),
|
|||
|
|
DockCode: d.DockCode,
|
|||
|
|
Name: d.Name,
|
|||
|
|
DockType: d.DockType,
|
|||
|
|
StationNo: d.StationNo,
|
|||
|
|
Status: d.Status,
|
|||
|
|
HasPallet: d.HasPallet,
|
|||
|
|
PalletRef: d.PalletRef,
|
|||
|
|
CreatedAt: d.CreatedAt,
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// listDocksMaintHandler GET /api/docks?dockType=&keyword=&page=&pageSize=
|
|||
|
|
// 接驳台主数据分页查询(真分页在 SQL 层),默认按创建时间倒序。
|
|||
|
|
func listDocksMaintHandler(ctx *svc.ServiceContext) http.HandlerFunc {
|
|||
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|||
|
|
q := r.URL.Query()
|
|||
|
|
query := ctx.EntClient.Dock.Query()
|
|||
|
|
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),
|
|||
|
|
))
|
|||
|
|
}
|
|||
|
|
total, err := query.Count(ctx0())
|
|||
|
|
if err != nil {
|
|||
|
|
fail(w, http.StatusInternalServerError, "查询接驳台失败: "+err.Error())
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
page := atoi(q.Get("page"), 1)
|
|||
|
|
size := atoi(q.Get("pageSize"), 20)
|
|||
|
|
if page < 1 {
|
|||
|
|
page = 1
|
|||
|
|
}
|
|||
|
|
if size < 1 || size > 200 {
|
|||
|
|
size = 20
|
|||
|
|
}
|
|||
|
|
list, err := query.
|
|||
|
|
Order(ent.Desc("created_at"), ent.Desc("id")).
|
|||
|
|
Offset((page - 1) * size).Limit(size).All(ctx0())
|
|||
|
|
if err != nil {
|
|||
|
|
fail(w, http.StatusInternalServerError, "查询接驳台失败: "+err.Error())
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
rows := make([]dockMaintRow, 0, len(list))
|
|||
|
|
for _, d := range list {
|
|||
|
|
rows = append(rows, dockMaintRowOf(d))
|
|||
|
|
}
|
|||
|
|
ok(w, map[string]any{"list": rows, "total": total, "page": page, "pageSize": size})
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// dockReq 接驳台保存请求
|
|||
|
|
type dockReq struct {
|
|||
|
|
ID int64 `json:"id"`
|
|||
|
|
DockCode string `json:"dockCode"`
|
|||
|
|
Name string `json:"name"`
|
|||
|
|
DockType string `json:"dockType"`
|
|||
|
|
StationNo int `json:"stationNo"`
|
|||
|
|
Status string `json:"status"`
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 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 {
|
|||
|
|
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())
|
|||
|
|
if err != nil {
|
|||
|
|
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": "接驳台已删除"})
|
|||
|
|
}
|
|||
|
|
}
|