feat(mes): 添加定时同步可生产数量到WMS及BOM项相关标准字段

- 在main.go中添加定时任务每10分钟同步可生产数量到WMS系统
- 为BomItem实体添加relatedStandard字段及相关CRUD方法
- 为InspectionRecord实体添加reportNo、materialCode、materialName等字段
- 更新ent schema确保新字段的验证和默认值设置
- 添加必要的数据库迁移和字段映射逻辑
This commit is contained in:
SunYF
2026-09-17 12:49:07 +08:00
parent b4b274301d
commit 1cc93795ce
191 changed files with 24504 additions and 1250 deletions
+9 -1
View File
@@ -21,7 +21,12 @@ func (InboundOrder) Fields() []ent.Field {
// 管理粒度 1批次(结构件)/2序列号(电气件),冗余自物料档案,便于单单据直接判断
field.Int("manage_mode").Default(1).Comment("管理粒度 1批次/2序列号"),
field.String("batch_no").Optional().Comment("批次号(结构件)"),
field.String("zone_code").Optional().Comment("入库区域"),
field.String("zone_code").Optional().Comment("入库区域(大区)"),
// 库位四级贯通(客户诉求 问题记录 L32/L169 + D5):区域→货架号→第几层→位置号。
// 与 zones 表 shelf_no/layer_no/position_no 对齐,落位到具体货架/层/位置。
field.String("shelf_no").Optional().Comment("货架号(如 A)"),
field.String("layer_no").Optional().Comment("第几层(如 3)"),
field.String("position_no").Optional().Comment("位置号(如 12)"),
field.Int("quantity").Default(0).Comment("本次入库总数量(批次合计或SN条数)"),
field.String("operator").Optional().Comment("操作人"),
field.String("remark").Optional().Comment("备注"),
@@ -31,6 +36,9 @@ func (InboundOrder) Fields() []ent.Field {
// ownership_type: workorder(工单号) / project(科技项目号) / none(无)
field.String("ownership_type").Default("none").Comment("归属类型 workorder/project/none"),
field.String("ownership_no").Optional().Comment("归属编号(工单号/科技项目号)"),
// 质量状态(客户诉求 问题记录 L129:入库记录增加 合格/不合格 列,提交检验后跳转入库自动关联)
// 空=未关联检验记录;合格/不合格/部分(部分入库)。入库时按检测单号自动带出最近一次检验结论。
field.String("quality_status").Optional().Comment("质量状态 合格/不合格/部分(空=未关联检验)"),
field.Int64("created_at").DefaultFunc(nowUnix).Comment("创建时间"),
}
}
+7
View File
@@ -18,6 +18,13 @@ func (InspectionRecord) Fields() []ent.Field {
field.String("target_type").Comment("BATCH/SN"),
field.String("target_id").Comment("批次号或 SN"),
field.String("material_code").Comment("物料编码"),
field.String("material_name").Optional().Comment("物料名称(冗余,列表直接展示/模糊查询)"),
// 归属(客户诉求 问题记录 L107/L149:检验记录需带归属,按归属查询)
field.String("ownership_type").Optional().Comment("归属类型 workorder/project/other"),
field.String("ownership_no").Optional().Comment("归属编号(工单号/科技项目号/其他)"),
// 相关标准(检验细则号) / 生产厂家(客户诉求 L107/L494)
field.String("related_standard").Optional().Comment("相关标准(检验细则号)"),
field.String("manufacturer").Optional().Comment("生产厂家"),
// 检验类型:incoming(来料检) process(过程检) finished(成品检)
field.String("inspection_type").Default("incoming").Comment("来料检/过程检/成品检"),
field.String("status").Default("未检").Comment("未检/合格/不合格"),
+20 -3
View File
@@ -21,7 +21,8 @@ import (
// 生命周期用 status 字段跟踪:在库/锁定/出库/使用/报废。
// 可用量 = quantity - locked_qty(锁定时为 0)。
//
// 区域(zone_code)只到 Z01~Z04 大分区级别,不引入货架/层/格(业务决策:小货架高频取货场景无需精确货位索引)。
// 库位四级贯通(U18):zone_code(区域) + shelf_no(货架) + layer_no(层) + position_no(位置),均手填、不做编号规则校验,
// 贯穿入库/出库/库存明细/盘点/导出全链路;精确货位串由 locLabel 拼接(如 Z01/A架/3层/12位)。
// 统一收编(2026-09-07 定稿):半成品/成品也进本表(category 区分品类),废除独立 semi_finisheds 孤表;
// 成品由 MES 完工发码后经内部接口自动回流入库,成品 SN 与实物码解耦(系统必发号,实物可不贴码,允许手填覆盖)。
type Inventory struct {
@@ -50,8 +51,9 @@ func (Inventory) Fields() []ent.Field {
// 批次号:结构件必填(系统生成);电气件可填所属批次用于分组追溯。
field.String("batch_no").Optional().Comment("批次号(系统生成);电气件可关联所属批次"),
// 序列号:仅电气件填写,作为单件唯一追溯锚点。空字符串不保证唯一,由代码层查重。
field.String("sn_code").Optional().Comment("序列号(SN,仅电气件);空值表示非 SN 行"),
// 序列号:仅电气件填写,作为单件唯一追溯锚点。全局唯一(一物一码,P2-2):
// 代码层跨 manage_mode 查重 + DB 部分唯一索引 ux_inventories_sn_code(非空值)双重保证;空值表示非 SN 行
field.String("sn_code").Optional().Comment("序列号(SN,仅电气件);全局唯一,空值表示非 SN 行"),
// 数量:结构件=批次余量;电气件恒为 1。
field.Int("quantity").Default(0).Comment("数量:结构件=批次余量;电气件=1"),
@@ -62,6 +64,11 @@ func (Inventory) Fields() []ent.Field {
field.String("quality_status").Default("未检").Comment("检验状态 未检/合格/不合格"),
// 所在区域(大分区)。
field.String("zone_code").Optional().Comment("所在区域(Z01~Z04 大分区)"),
// 库位四级贯通(客户诉求 问题记录 L32/L169 + D5):区域→货架号→第几层→位置号。
// 与 zones 表 shelf_no/layer_no/position_no 对齐,落位到具体货架/层/位置。
field.String("shelf_no").Optional().Comment("货架号(手填,如 A)"),
field.String("layer_no").Optional().Comment("第几层(手填,如 3)"),
field.String("position_no").Optional().Comment("位置号(手填,如 12)"),
// 生命周期状态:
// 结构件一律"在库"(锁定看 locked_qty,不靠本字段);
@@ -78,6 +85,16 @@ func (Inventory) Fields() []ent.Field {
field.String("order_no").Default("").MaxLen(64).Comment("关联工单号(半成品/成品回流)"),
// 已完成工序(半成品暂存回流用,如 135)。
field.String("completed_process").Default("").MaxLen(64).Comment("已完成工序(半成品用,如 135)"),
// 半成品/成品入库扩展字段(客户诉求 问题记录 L218/L222/L230:归属/产品状态/相关标准/试验记录单号/记录员/记录日期)
field.String("ownership_type").Optional().Comment("归属类型 workorder/project/other"),
field.String("ownership_no").Optional().Comment("归属编号(工单号/科技项目号/其他)"),
field.String("product_status").Optional().Comment("产品状态 已通过/部分通过/返修(成品用)"),
field.String("related_standard").Optional().Comment("相关标准(试验方案号)"),
field.String("test_record_no").Optional().Comment("试验记录单号"),
field.String("recorded_by").Optional().Comment("记录员"),
field.Int64("recorded_at").Optional().Comment("记录日期(unix 秒)"),
// 半成品/成品出库原因(客户诉求 L224:包装/继续生产/检验试验/其他)
field.String("outbound_reason").Optional().Comment("出库原因(半成品/成品出库用)"),
// 备注。
field.String("remark").Optional().Comment("备注"),
+2 -2
View File
@@ -35,8 +35,8 @@ func (Material) Fields() []ent.Field {
field.Bool("is_serial_managed").Default(false).Comment("是否序列号管理"),
// 所属物料模板(种类)
field.String("template_code").Optional().Comment("物料模板编码(物料种类)"),
// 图号(零件图纸编号,用于装配定位与追溯,可选)
field.String("drawing_no").Optional().Comment("图号(图纸编号)"),
// 图号已按客户裁决(问题记录 L18)与物料编码合并:code 即图号,界面统一显示「图号」,
// 不再保留独立 drawing_no 字段(避免两个「图号」并存造成混乱)。
// 安全库存:低于该值触发缺货预警(客户诉求 问题记录 L473:安全库存/缺货预警进 WMS 首页)
field.Int("safety_stock").Default(0).Comment("安全库存(低于则缺货预警,0=不预警)"),
// 启用状态:停用后不在新建工单/备料下拉出现(对齐 MES 产品型号 isActive
+23
View File
@@ -0,0 +1,23 @@
package schema
import (
"entgo.io/ent"
"entgo.io/ent/schema/field"
)
// MaterialDemand 物料未来需求(MES 按日排产推送,WMS 备料四态"预警"判定的数据源)
//
// 客户口径(问题记录 L256):预警 = 可用库存 < 未来5天需求量,
// 未来5天需求量 = 近5天日排产 × 物料清单单台用量(日排产总量可大于库存量,附一#7)。
// 日排产只在 MES,故由 MES 计算后推送本表(按 material_code 覆盖更新)。
type MaterialDemand struct {
ent.Schema
}
func (MaterialDemand) Fields() []ent.Field {
return []ent.Field{
field.String("material_code").Unique().Comment("物料编码(图号)"),
field.Int("future5_qty").Default(0).Comment("未来5天需求量(近5天日排产×单台用量)"),
field.Int64("updated_at").DefaultFunc(nowUnix).Comment("更新时间(unix 秒)"),
}
}
@@ -23,6 +23,8 @@ func (OrderMaterialLedger) Fields() []ent.Field {
field.String("status").Default("进行中").Comment("进行中/领料完结"),
// 目标工位:备料送到哪个装配工位(客户诉求 L297-300;MES 同步台账时传入)
field.String("target_station").Optional().Comment("目标工位(装配工位号)"),
// 完成时间(客户诉求 问题记录 L242-258):领料完结时刻,用于"完成时间"范围查询。
field.Int64("completed_at").Optional().Comment("领料完结时间(unix 秒)"),
field.String("remark").Optional().Comment("备注"),
field.Int64("created_at").DefaultFunc(nowUnix),
field.Int64("updated_at").DefaultFunc(nowUnix),
+8
View File
@@ -19,6 +19,14 @@ func (OutboundOrder) Fields() []ent.Field {
field.String("outbound_no").Unique().Comment("出库单号"),
// 类型:workorder(工单领料/MES) general(通用出库,不依赖工单)
field.String("outbound_type").Default("workorder").Comment("出库类型"),
// 出库类别(客户诉求 问题记录 L96-108):通用出库先点选类别——
// return(退料)/sample(样品)/scrap(报废)/deliver(发货)/other(其他,手输原因)
field.String("outbound_category").Optional().Comment("出库类别 return/sample/scrap/deliver/other"),
// 归属(客户诉求 L96-108):工单号/科技项目号/其他;与入库 ownership_type 口径一致
field.String("ownership_type").Optional().Comment("归属类型 workorder/project/other"),
field.String("ownership_no").Optional().Comment("归属编号(工单号/科技项目号)"),
// 目标工位(客户诉求 L96-108):出库送到哪个装配工位
field.String("target_station").Optional().Comment("目标工位(装配工位号)"),
field.String("order_no").Optional().Comment("关联工单号(工单为唯一挂靠载体)"),
field.String("material_code").Comment("物料编码"),
field.String("material_name").Optional().Comment("物料名称(冗余)"),
@@ -0,0 +1,34 @@
package schema
import (
"entgo.io/ent"
"entgo.io/ent/schema/field"
"entgo.io/ent/schema/index"
)
// ProducibleSnapshot 可生产数量快照(MES 计算后推送,WMS 只读展示)
//
// 业务背景(问题记录 L470-474):客户要求"已知原材料库存量和生产原材料清单计算出可生产数量"。
// BOM(单台用量)只在 MES,库存只在 WMS;为满足"两套系统互不影响可独立运行"的约束,
// 由 MES 实时计算(经内部接口取 WMS 库存)并推送快照到本表,WMS 库存页卡片直接读快照展示。
// 每次推送全量覆盖(先清后写),synced_at 供界面标注数据时间。
type ProducibleSnapshot struct {
ent.Schema
}
func (ProducibleSnapshot) Fields() []ent.Field {
return []ent.Field{
field.String("product_code").Comment("产品编号"),
field.String("product_name").Optional().Comment("产品名称"),
field.Int("producible_qty").Default(0).Comment("可生产套数=min(各物料可用量/单台用量)"),
field.String("short_text").Optional().Comment("短板物料描述(如 SCR-M4-12缺20)"),
field.Int64("computed_at").Default(0).Comment("MES 计算时间(unix 秒)"),
field.Int64("synced_at").DefaultFunc(nowUnix).Comment("同步到 WMS 时间(unix 秒)"),
}
}
func (ProducibleSnapshot) Indexes() []ent.Index {
return []ent.Index{
index.Fields("product_code"),
}
}
+4
View File
@@ -40,6 +40,10 @@ func (StocktakeItem) Fields() []ent.Field {
field.String("target_id").Comment("批次号或SN码"),
field.String("material_code").Optional().Comment("物料编码(冗余,便于盘点差异按物料归类)"),
field.String("zone_code").Optional().Comment("所在区域"),
// 库位四级贯通(U18):盘点快照除区域外,冻结货架/层/位置,盘点差异可精确定位到货位。
field.String("shelf_no").Optional().Comment("货架号(快照)"),
field.String("layer_no").Optional().Comment("第几层(快照)"),
field.String("position_no").Optional().Comment("位置号(快照)"),
field.Int("book_qty").Default(0).Comment("账面数量"),
field.Int("scanned_qty").Default(-1).Comment("实盘数量,-1=未盘"),
field.Int("diff_qty").Default(0).Comment("差异 = 实盘 - 账面"),
+7 -5
View File
@@ -5,17 +5,19 @@ import (
"entgo.io/ent/schema/field"
)
// Zone 库房区域
// 客户诉求(问题记录 L32/L169 + 本轮 D5):区域要能定位到「货架号 + 第几层 + 位置号」,
// 且用户明确"编码规则手填"(L584)——故仅加三个可填文本字段,不做编号规则校验、不做级联实体
// 完整库位串 = 区域编码 / 货架号 / 层 / 位置号(示例 Z01 / A / 3 / 12)。
// 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 归一空值,防止同一货位重复登记)。
type Zone struct {
ent.Schema
}
func (Zone) Fields() []ent.Field {
return []ent.Field{
field.String("zone_code").Unique().Comment("区域编码"),
field.String("zone_code").Comment("区域编码(货位四级第一级;同区域可多货位,故不单列唯一)"),
field.String("zone_name").Comment("区域名称"),
field.String("description").Optional().Comment("描述"),
// 货架号(手填,如 A/03