docs(deployment): 更新部署手册和业务架构说明

- 更新MES和WMS服务端口配置说明
- 详细说明产线工位设备配置(扫码枪+拧紧枪)
- 明确AGV配送和接驳台的模拟模式与真实对接方案
- 更新WMS中AGV配送默认模拟模式说明
- 完善厂内物流(AGV/接驳台)的业务架构约束
- 修订临时文档为详细的项目问题和解决方案规划
This commit is contained in:
SunYF
2026-09-20 14:52:36 +08:00
parent 5a33028003
commit f35e9b5b75
57 changed files with 6096 additions and 1000 deletions
+57 -15
View File
@@ -48,6 +48,9 @@ type zoneNodeVO struct {
Status string `json:"status"`
Description string `json:"description"`
CreatedAt int64 `json:"createdAt"`
// StockQty 本级实时库存总量(需求E/需求7):每次查询实时聚合 inventoriesquantity>0 且非出库/报废),不缓存。
// level1=区域合计、level2=货架合计、level3=层合计、level4=该位置合计。
StockQty int `json:"stockQty"`
}
func toZoneNodeVO(z *ent.Zone) zoneNodeVO {
@@ -59,7 +62,7 @@ func toZoneNodeVO(z *ent.Zone) zoneNodeVO {
}
// listZonesHandler 按层级列出节点(4 个 Tab 各自独立分页 + 按上级筛选)
// 参数:level(必填1~4)、parentCode、zoneCode、shelfNo、layerNo、keyword、status、page、pageSize
// 参数:level(必填1~4)、parentCode、zoneCode、shelfNo、layerNo、code、name、status、page、pageSize
func listZonesHandler(ctx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
level := atoi(r.URL.Query().Get("level"), 0)
@@ -92,8 +95,12 @@ func listZonesHandler(ctx *svc.ServiceContext) http.HandlerFunc {
if ly := strings.TrimSpace(r.URL.Query().Get("layerNo")); ly != "" {
q = q.Where(zone.LayerNoEQ(ly))
}
if kw := strings.TrimSpace(r.URL.Query().Get("keyword")); kw != "" {
q = q.Where(zone.Or(zone.CodeContainsFold(kw), zone.NameContainsFold(kw)))
// 列表页禁止关键字混搜:本节点编码、名称各自独立模糊筛选(需求N)
if code := strings.TrimSpace(r.URL.Query().Get("code")); code != "" {
q = q.Where(zone.CodeContainsFold(code))
}
if name := strings.TrimSpace(r.URL.Query().Get("name")); name != "" {
q = q.Where(zone.NameContainsFold(name))
}
if st := strings.TrimSpace(r.URL.Query().Get("status")); st != "" {
q = q.Where(zone.StatusEQ(st))
@@ -111,13 +118,50 @@ func listZonesHandler(ctx *svc.ServiceContext) http.HandlerFunc {
return
}
list := make([]zoneNodeVO, 0, len(rows))
byZone, byShelf, byLayer, byPos := zoneStockMaps(ctx)
for _, z := range rows {
list = append(list, toZoneNodeVO(z))
vo := toZoneNodeVO(z)
zc := strings.ToLower(strings.TrimSpace(z.ZoneCode))
sh := strings.ToLower(strings.TrimSpace(z.ShelfNo))
ly := strings.TrimSpace(z.LayerNo)
switch z.Level {
case zoneLevelRegion:
vo.StockQty = byZone[zc]
case zoneLevelShelf:
vo.StockQty = byShelf[zc+"|"+sh]
case zoneLevelLayer:
vo.StockQty = byLayer[zc+"|"+sh+"|"+ly]
case zoneLevelPos:
vo.StockQty = byPos[locKey(z.ZoneCode, z.ShelfNo, z.LayerNo, z.PositionNo)]
}
list = append(list, vo)
}
ok(w, map[string]any{"total": total, "list": list, "page": page, "pageSize": pageSize})
}
}
// zoneStockMaps 实时聚合各级库存总量(需求E):一次扫描在库 inventoriesquantity>0 且 status 非出库/报废),
// 分别按 区域 / 区域+货架 / 区域+货架+层 / 完整四级货位 累加,供四级列表展示实时库存。
// 键归一与 locKey 一致:区域/货架小写,层/位置原样 trim。
func zoneStockMaps(ctx *svc.ServiceContext) (byZone, byShelf, byLayer, byPos map[string]int) {
byZone, byShelf, byLayer, byPos = map[string]int{}, map[string]int{}, map[string]int{}, map[string]int{}
invs, err := ctx.EntClient.Inventory.Query().
Where(inventory.QuantityGT(0), inventory.StatusNotIn("出库", "报废")).All(ctx0())
if err != nil {
return
}
for _, iv := range invs {
zc := strings.ToLower(strings.TrimSpace(iv.ZoneCode))
sh := strings.ToLower(strings.TrimSpace(iv.ShelfNo))
ly := strings.TrimSpace(iv.LayerNo)
byZone[zc] += iv.Quantity
byShelf[zc+"|"+sh] += iv.Quantity
byLayer[zc+"|"+sh+"|"+ly] += iv.Quantity
byPos[locKey(iv.ZoneCode, iv.ShelfNo, iv.LayerNo, iv.PositionNo)] += iv.Quantity
}
return
}
// zoneNodeReq 新建层级节点请求
type zoneNodeReq struct {
Level int `json:"level"`
@@ -220,11 +264,10 @@ func createZoneNodeHandler(ctx *svc.ServiceContext) http.HandlerFunc {
SetLevel(req.Level).SetCode(req.Code).SetParentCode(parentCode).
SetZoneCode(zoneCode).SetNillableShelfNo(strPtr(shelfNo)).
SetNillableLayerNo(strPtr(layerNo)).SetNillablePositionNo(strPtr(req.Code)).
SetStatus("启用").SetNillableDescription(strPtr(req.Remark))
SetStatus("启用").SetNillableDescription(strPtr(req.Remark)).
SetName(req.Name) // 四级均可有名称(需求E
if req.Level == 1 {
create.SetZoneName(req.Name) // 区域名称
} else if req.Level == 4 {
create.SetName(req.Name) // 位置名称
create.SetZoneName(req.Name) // 区域名称冗余,兼容 stock.go 区域名映射
}
z, err := create.Save(ctx0())
if err != nil {
@@ -254,7 +297,8 @@ type zoneNodeUpdateReq struct {
Status string `json:"status"`
}
// updateZoneNodeHandler 编辑节点(区域改名称/说明/停用;位置改位置名称/停用;其余层级仅改停用)
// updateZoneNodeHandler 编辑节点(四级均可改名称/备注/启用停用;区域额外同步 zone_name 冗余)。
// 需求E:不删只停用,无删除入口;名称与备注全级可维护。
func updateZoneNodeHandler(ctx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req zoneNodeUpdateReq
@@ -279,13 +323,11 @@ func updateZoneNodeHandler(ctx *svc.ServiceContext) http.HandlerFunc {
fail(w, http.StatusBadRequest, "状态取值非法")
return
}
upd := ctx.EntClient.Zone.UpdateOneID(req.ID).SetStatus(status)
upd := ctx.EntClient.Zone.UpdateOneID(req.ID).SetStatus(status).
SetName(strings.TrimSpace(req.Name)).
SetNillableDescription(strPtr(strings.TrimSpace(req.Remark)))
if z.Level == 1 {
upd.SetZoneName(strings.TrimSpace(req.Name))
upd.SetNillableDescription(strPtr(strings.TrimSpace(req.Remark)))
}
if z.Level == 4 {
upd.SetName(strings.TrimSpace(req.Name))
upd.SetZoneName(strings.TrimSpace(req.Name)) // 区域名称冗余同步
}
if _, err := upd.Save(ctx0()); err != nil {
fail(w, http.StatusInternalServerError, err.Error())