feat: 完成附件存储重构与磁盘监控功能,同步物料档案与入库单规则更新
1. 新增跨平台磁盘空间监控能力,每日7点自动检测附件目录剩余空间,触发阈值告警 2. 重构附件存储方案为年/月/日/文件类型分层结构,统一MES与WMS的附件管理逻辑 3. 对齐物料档案与入库单的质量状态校验规则,仅合格品计入库存与出库 4. 实现入库单作废功能与区域库位的多级父子结构管理 5. 删除物料简称字段,补充规格型号与单位必填项,统一系统数据口径 6. 新增附件中心与磁盘状态查询接口,完善权限控制与操作日志
This commit is contained in:
@@ -17,8 +17,12 @@ import (
|
||||
// biz_type=semi biz_id=半成品 SN
|
||||
// biz_type=outbound biz_id=出库单号
|
||||
//
|
||||
// 磁盘落盘约定:uploads/<uuid><ext>(纯 ASCII 文件名,避免中文 URL 编码问题),
|
||||
// 原始文件名存 file_name,预览走静态路由 GET /uploads/:name。
|
||||
// 磁盘落盘约定(2026-09-19 重构):<根目录>/年/月/日/<文件类型大写>/<uuid>.<ext>,
|
||||
// file_path 存相对路径(如 2026/09/19/INSPECTION_PHOTO/xxx.jpg),不存绝对路径;
|
||||
// 根目录由 etc/*.yaml 的 Attachment.RootDir 提供,不硬编码。
|
||||
// 预览/下载走 GET /uploads/<相对路径>(服务端限根目录内,阻断穿越)。
|
||||
// 文件类型(file_type,大写枚举)决定所在目录:PROCESS_PDF/INSPECTION_PHOTO/INSPECTION_PDF/
|
||||
// DRAWING/PROCESS_CARD/PACKAGE_PHOTO/EXCEL_IMPORT/OTHER;biz_type 仍表示挂载的业务对象类型。
|
||||
type Attachment struct {
|
||||
ent.Schema
|
||||
}
|
||||
@@ -28,10 +32,20 @@ func (Attachment) Fields() []ent.Field {
|
||||
field.String("biz_type").Comment("业务类型 inbound/material/inspection/semi/outbound"),
|
||||
field.String("biz_id").Comment("业务对象标识(单号/编码/ID)"),
|
||||
field.String("file_name").Comment("原始文件名(含扩展名)"),
|
||||
field.String("file_path").Comment("磁盘相对文件名(uuid+ext)"),
|
||||
// file_path 现为相对路径(年/月/日/文件类型/uuid.ext),历史扁平文件名仍兼容
|
||||
field.String("file_path").Comment("磁盘相对路径(年/月/日/文件类型/uuid.ext)"),
|
||||
// 文件类型(大写枚举),决定存储目录;与 biz_type(业务对象)正交
|
||||
field.String("file_type").Optional().Comment("文件类型大写枚举 PROCESS_PDF/INSPECTION_PHOTO/..."),
|
||||
field.String("file_ext").Optional().Comment("扩展名(小写,不含点)"),
|
||||
field.String("file_md5").Optional().Comment("文件MD5(秒传/去重)"),
|
||||
field.Int64("file_size").Default(0).Comment("文件大小(字节)"),
|
||||
field.String("mime_type").Optional().Comment("MIME 类型"),
|
||||
field.String("uploaded_by").Optional().Comment("上传人"),
|
||||
field.Bool("archived").Default(false).Comment("是否已归档(备份到外部存储)"),
|
||||
field.Int64("archived_at").Optional().Comment("归档时间(unix秒)"),
|
||||
field.Bool("deleted").Default(false).Comment("逻辑删除标记"),
|
||||
field.Int64("deleted_at").Optional().Comment("删除时间(unix秒)"),
|
||||
field.String("deleted_by").Optional().Comment("删除人"),
|
||||
field.Int64("created_at").DefaultFunc(nowUnix),
|
||||
}
|
||||
}
|
||||
@@ -39,5 +53,8 @@ func (Attachment) Fields() []ent.Field {
|
||||
func (Attachment) Indexes() []ent.Index {
|
||||
return []ent.Index{
|
||||
index.Fields("biz_type", "biz_id"),
|
||||
index.Fields("file_type"),
|
||||
index.Fields("file_md5"),
|
||||
index.Fields("created_at"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,6 +39,12 @@ func (InboundOrder) Fields() []ent.Field {
|
||||
// 质量状态(客户诉求 问题记录 L129:入库记录增加 合格/不合格 列,提交检验后跳转入库自动关联)
|
||||
// 空=未关联检验记录;合格/不合格/部分(部分入库)。入库时按检测单号自动带出最近一次检验结论。
|
||||
field.String("quality_status").Optional().Comment("质量状态 合格/不合格/部分(空=未关联检验)"),
|
||||
// 作废(客户诉求 2026-09-19):入库单可作废,填写作废原因并回滚对应库存
|
||||
// (结构件回滚批次数量;电气件按 SN 精确回滚本单的 SN,不与其它单混)。
|
||||
field.Bool("voided").Default(false).Comment("是否已作废"),
|
||||
field.String("void_reason").Optional().Comment("作废原因"),
|
||||
field.String("voided_by").Optional().Comment("作废人"),
|
||||
field.Int64("voided_at").Optional().Comment("作废时间(unix秒)"),
|
||||
field.Int64("created_at").DefaultFunc(nowUnix).Comment("创建时间"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,10 @@ func (Material) Fields() []ent.Field {
|
||||
return []ent.Field{
|
||||
field.String("code").Unique().Comment("物料唯一编码"),
|
||||
field.String("name").Comment("物料真实名称"),
|
||||
field.String("short_name").Optional().Comment("简称"),
|
||||
// 「简称」字段已按 2026-09-19 用户裁决整体删除(新增/编辑表单不再出现)。
|
||||
// 字段级必填口径:图号 code / 名称 name / 规格 spec / 单位 unit / 品类 item_type /
|
||||
// 类型 manage_mode / 安全库存 safety_stock 均必填;仅「说明」description 可空。
|
||||
// 必填在 handler 层校验(DB 列保持可空,避免存量行 NULL 扫描失败)。
|
||||
field.String("spec").Optional().Comment("规格型号"),
|
||||
field.String("unit").Optional().Comment("单位"),
|
||||
field.String("description").Optional().Comment("描述"),
|
||||
|
||||
+20
-15
@@ -5,27 +5,32 @@ import (
|
||||
"entgo.io/ent/schema/field"
|
||||
)
|
||||
|
||||
// Zone 库房货位主数据(区域 → 货架 → 层 → 位置 四级)
|
||||
// 客户诉求(问题记录 L32/L169 + U18):货位按「区域(zone_code) + 货架号 + 第几层 + 位置号」四级定位,
|
||||
// 四级均手填、不做编号规则校验(L584)。每一行 = 一个货位。
|
||||
// zone_code 只是四级中的“区域”这一级,同一区域下可有多个货位,故 zone_code 不再单列唯一;
|
||||
// 货位唯一性由组合唯一索引 ux_zones_location(zone_code, shelf_no, layer_no, position_no) 保证
|
||||
// (见 internal/db/db.go applyColumnPatches,用 COALESCE 归一空值,防止同一货位重复登记)。
|
||||
// Zone 库房库位主数据(统一主表 + 层级类型字段,符合「统一主表+类型字段,禁拆表」铁律)。
|
||||
// 四级父子结构:区域(level=1) → 货架(level=2) → 层(level=3) → 位置号(level=4)。
|
||||
// 每一行是一个层级节点;zone_code/shelf_no/layer_no/position_no 为冗余全路径,便于按区域筛选与库存关联,
|
||||
// 库存/入库/出库/盘点仍沿用这四级冗余字段,追溯零断裂。
|
||||
// 唯一性由 ux_zones_node(level, zone_code, shelf_no, layer_no, code) 保证(见 internal/db/db.go)。
|
||||
type Zone struct {
|
||||
ent.Schema
|
||||
}
|
||||
|
||||
func (Zone) Fields() []ent.Field {
|
||||
return []ent.Field{
|
||||
field.String("zone_code").Comment("区域编码(货位四级第一级;同区域可多货位,故不单列唯一)"),
|
||||
field.String("zone_name").Comment("区域名称"),
|
||||
field.String("description").Optional().Comment("描述"),
|
||||
// 货架号(手填,如 A/03)
|
||||
field.String("shelf_no").Optional().Comment("货架号(手填)"),
|
||||
// 第几层(手填,如 3)
|
||||
field.String("layer_no").Optional().Comment("第几层(手填)"),
|
||||
// 位置号(手填,如 12)
|
||||
field.String("position_no").Optional().Comment("位置号(手填)"),
|
||||
field.Int("level").Comment("层级 1区域/2货架/3层/4位置号"),
|
||||
field.String("code").Comment("本级编码(区域编码/货架号/层号/位置号)"),
|
||||
field.String("name").Optional().Comment("显示名:区域名称(level1)或位置名称(level4,现场可读如'A架3层12位');其他级可空"),
|
||||
field.String("parent_code").Optional().Comment("上级节点编码;根级(level1)为空"),
|
||||
// 区域编码(冗余,所有层级都带,用于按区域筛选/库存关联):level1 行 = 自身 code
|
||||
field.String("zone_code").Comment("区域编码(冗余:本节点所属区域;level1=自身code)"),
|
||||
// 区域名称(仅 level1 区域行有值,保留兼容 stock.go 区域名映射)
|
||||
field.String("zone_name").Optional().Comment("区域名称(仅 level1 区域行有值)"),
|
||||
field.String("description").Optional().Comment("说明(区域级)"),
|
||||
// 货架号(冗余):level2/3/4 行有值
|
||||
field.String("shelf_no").Optional().Comment("货架号(冗余)"),
|
||||
// 第几层(冗余):level3/4 行有值(默认位置 layer_no 为空)
|
||||
field.String("layer_no").Optional().Comment("第几层(冗余;默认位置为空)"),
|
||||
// 位置号(冗余):level4 行 = 自身 code
|
||||
field.String("position_no").Optional().Comment("位置号(冗余;level4=自身code)"),
|
||||
field.String("status").Default("启用").Comment("状态:启用/停用"),
|
||||
field.Int64("created_at").DefaultFunc(nowUnix),
|
||||
field.Int64("updated_at").DefaultFunc(nowUnix).Optional().Comment("更新时间"),
|
||||
|
||||
Reference in New Issue
Block a user