首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >如何开发一套ERP(离散制造-MTO)系统(附架构图+流程图+代码参考)

如何开发一套ERP(离散制造-MTO)系统(附架构图+流程图+代码参考)

原创
作者头像
用户11735492
发布2025-09-16 16:20:07
发布2025-09-16 16:20:07
1030
举报

离散制造(Discrete Manufacturing)里常见的生产模式有MTS(Make To Stock)和MTO(Make To Order)。MTO强调按订单生产、产品往往有较高的定制化和多变的BOM。对于制造企业来说,一套贴合 MTO 特性的 ERP 系统,能把“订单 → 设计 → 物料 → 计划 → 生产 → 交付”这条链路串通起来,降低交付风险、减少呆滞物料、提高响应速度和客户满意度。本文将把如何搭建一套面向离散制造—MTO 的 ERP 系统拆成可执行的步骤,给出架构图、流程图,以及一个完整的参考代码片段,帮助企业和开发团队落地实施。


本文你将了解

  1. 什么是 ERP(离散制造 - MTO)系统?(定义与核心价值)
  2. 总体架构(含架构图)
  3. 主要功能模块详解(技术管理、客户、销售、生产、采购、库存、财务)
  4. 关键业务流程(含流程图):订单到交付、BOM展开、MRP 逻辑、排产、采购下发、完工结算
  5. 开发技巧与实现建议(DDD、微服务/模块化、消息中间件、幂等、测试)
  6. 参考代码(一个完整的大样例,包含数据库 schema、关键接口、BOM 展开与简单排产算法)
  7. 部署、运维与上线注意事项
  8. 实现效果与关键 KPI

注:本文示例所用方案模板:简道云ERP系统,给大家示例的是一些通用的功能和模块,都是支持自定义修改的,你可以根据自己的需求修改里面的功能。


1 什么是 ERP(离散制造 - MTO)系统?

简单说:ERP 是企业资源计划系统,但不同企业、不同生产模式下 ERP 的重点不同。离散制造 MTO 的特点是:

  • 以订单为驱动:生产始于客户订单,库存推迟或最小化。
  • BOM / 配套复杂:同一产品按订单可有多种配置与选配。
  • 交期敏感:需快速从订单到交付的透明化流程与协同。
  • 设计变更频繁:需要与 PLM/工程变更流程有紧密衔接。

因此,MTO ERP 要解决的主要问题是:如何在保证交期的情况下,动态展开 BOM、计算物料需求、触发采购与生产排程、并把成本和财务信息准确落地。


2 总体架构(建议)

下面用 Mermaid 给出一个高层架构图——这是一个典型的微服务/模块化设计,适配中大型制造企业的扩展需求。

代码语言:txt
复制
graph LR
  subgraph Users
    A[销售/客服] -->|web| Frontend
    B[计划/车间] -->|web/移动| Frontend
    C[采购/仓库] -->|web| Frontend
    D[财务/管理] -->|web| Frontend
  end
  Frontend --> API_GW[API 网关]
  API_GW --> Auth[认证服务]
  API_GW --> MSales[销售服务]
  API_GW --> MCustomer[客户服务]
  API_GW --> MProd[生产服务]
  API_GW --> MPur[采购服务]
  API_GW --> MInv[库存服务]
  API_GW --> MFin[财务服务]
  API_GW --> MTech[技术管理/PLM接口]
  MProd -->|events| MQ[消息队列(RabbitMQ/Kafka)]
  MPur -->|events| MQ
  MInv -->|events| MQ
  subgraph Persistence
    DBMain[(Postgres)]
    Cache[(Redis)]
    FileStore[(对象存储)]
  end
  MSales --> DBMain
  MProd --> DBMain
  MInv --> DBMain
  MFin --> DBMain
  Auth --> DBMain
  MQ --> DBMain
  MTech --> FileStore
  subgraph Integration
    PLM[PLM/CAD]
    MES[MES/车间系统]
    ERP3P[第三方ERP/供应商门户]
  end
  MTech --> PLM
  MProd --> MES
  MPur --> ERP3P

核心组件说明:

  • API 网关 + 认证服务:统一鉴权、限流、审计、版本管理。
  • 微服务:按业务边界拆分(销售/客户/生产/采购/库存/财务/技术管理)。
  • 消息队列:用于异步事件(订单变更、采购下发、入库、完工等),保证系统解耦并提升并发处理能力。
  • 主数据库(建议 PostgreSQL) + Redis 缓存 + 对象存储(BOM 文档、CAD 图纸)。
  • 集成层:PLM、MES、供应商门户、物流系统。

3 主要功能模块详解

下面把每个模块的关键职责和实现建议写清楚,并给出典型字段/接口建议。

技术管理(MTech / PLM 接口)

职责:管理产品结构(BOM)、工艺(Routings)、物料主数据、版本与工程变更(ECO/ECN)。 要点:

  • 支持多级BOM(父子关系),BOM要支持版本控制(有效期/生效版本)。
  • 工艺路线包含工序、标准工时、首件检验、设备与技能要求。
  • 提供 REST 接口供 ERP 调用:GET /bom/{productId}?version=xxx,并能推送工程变更事件。
  • 建议把 CAD/文档存对象存储,DB 存元数据与访问地址。

客户管理

职责:客户档案、联系人、账期、信用额度、送货地址与交货条款。 要点:

  • 重点是为销售与交期评估提供快速判断:客户优先级、付款条件、历史退货率等。
  • 接口示例:GET /customers/{id},POST /customers/{id}/credit-check

销售管理

职责:订单录入、报价、合同、订单确认、交期评估(ATP/可承诺发货能力)。 要点:

  • 支持配置型销售(可基于配置规则动态生成 BOM)。
  • 与技术管理交互:若客户特殊定制,产生新的工艺或BOM版本。
  • 在订单确认时触发 MRP(对于 MTO,MRP 计算从订单需求开始)。

生产管理

职责:物料需求计算(MRP)、主生产计划(MPS)、排产(APS)、车间执行(MES 对接)、完工入库。 要点:

  • MTO 下 MRP 以订单为驱动,尽量短时滚动计划。
  • 排产需要考虑:设备能力、工序优先级、物料可用性、工装/夹具。
  • 提供车间看板、派工单、巡检/质量记录上传接口。

采购管理

职责:供应商管理、采购申请、采购订单、到货检验、供应能力协同(供需看板)。 要点:

  • 支持询价/比价、多供应商、分批到货。
  • 对关键物料支持安全库存和交期缓冲管理。
  • 对长期外协加工,应与外协订单模块集成。

库存管理

职责:库存账、批次/序列号管理、在途、占用与可用量(ATP)、仓储作业(入库/出库/调拨)。 要点:

  • MTO 常有“订单占用”场景:在生产前便对关键件进行占用或冻结。
  • 支持批次/序列号追溯,满足质量与召回要求。
  • 与采购、生产紧密联动:采购到货触发质检入库,生产完工触发入库。

财务管理

职责:应收/应付、成本核算(标准成本/实时报表)、结算、发票管理。 要点:

  • 对 MTO,按单核算成本很重要(按订单/批次计算毛利)。
  • 集成总账与固定资产模块,支持成本分摊与完工入账(WIP 处理)。

4 关键业务流程

4.1 订单 → 交付

代码语言:txt
复制
flowchart TB
  A[客户下单] --> B[销售录入/配置]
  B --> C{是否标准BOM?}
  C -->|是| D[生成生产订单]
  C -->|否| E[派工工程/生成新BOM版本]
  D --> F[MRP:计算物料需求]
  E --> F
  F --> G{物料可用?}
  G -->|是| H[排产并下发派工]
  G -->|否| I[触发采购/外协]
  I --> J[供应到货后入库]
  J --> H
  H --> K[车间执行(MES)]
  K --> L[完工入库]
  L --> M[质检出库/发运]
  M --> N[账务结算/成本核算]

4.2 BOM 展开

BOM 展开就是从成品沿树向下,把每个组件按数量系数乘开,得到物料需求清单(MRP 的输入)。

代码语言:txt
复制
flowchart LR
  A[主产品] --> B[零件A x2]
  A --> C[零件B x1]
  B --> D[原料A1 x3]
  C --> E[原料B1 x2]

说明:BOM 要支持替代件、备选供应商与采购批量规则(MOQ、批量折扣)。


5 开发技巧与实现建议

  1. 领域驱动设计(DDD):把业务模块按边界上下文划分(Sales Context、Production Context、Procurement Context、Inventory Context、Accounting Context、Engineering Context)。每个上下文内部聚合根、领域事件、实体和仓储接口清晰。
  2. 事件驱动 & 异步:订单确认、采购到货、完工入库等用事件驱动,MQ(RabbitMQ/Kafka)保证可靠投递与重试机制,事件要幂等。
  3. 事务处理:跨服务事务用补偿模式(Saga)。例如生产完工后需同时扣库并生成财务凭证,采用本地事务并发布事件,若其中一环失败执行补偿。
  4. 幂等与唯一约束:接口设计幂等键(order_id、request_id),避免重复下单或重复处理回调。
  5. BOM 版本管理:BOM 与 Routings 都要有版本、变更记录与生效日期,变更在生效点前的订单应继续使用旧版本。
  6. 排产策略:先实现简单优先级+物料可用排产,再演进到带资源约束的 APS。初期用启发式调度(FCFS + 优先级),后期可引入 ILP 或遗传算法优化。
  7. 性能:缓存热点数据(物料、BOM、客户信用)到 Redis,MRP 批量计算使用批处理 / 后台任务避免阻塞前端请求。
  8. 接口设计:REST + JSON 做边界 API;内部服务通信建议 gRPC(高效)或 HTTP + 认证。
  9. 数据建模:BOM/物料/工艺的数据模型要支持历史快照,以便追溯成本和质量问题。
  10. 测试:建立领域单元测试、集成测试、合同测试(Consumer-Driven Contract)来保证微服务兼容。

6 参考代码

下面给出一个 Node.js(TypeScript)示例:包含数据库 schema、关键 API(订单创建)、BOM 展开函数、简单 MRP/排产触发示例。示例基于 Express + TypeORM + RabbitMQ。代码为参考级别,真实产品需要补充权限、安全与完整错误处理。

代码语言:txt
复制
// package.json (仅依赖展示)
// {
//   "name": "mto-erp-sample",
//   "dependencies": {
//     "express": "^4.18.2",
//     "typeorm": "^0.3.12",
//     "pg": "^8.10.0",
//     "amqplib": "^0.8.0",
//     "reflect-metadata": "^0.1.13"
//   }
// }
/* src/entity/Material.ts */
import { Entity, PrimaryGeneratedColumn, Column } from "typeorm";
@Entity()
export class Material {
  @PrimaryGeneratedColumn()
  id: number;
  @Column({ unique: true })
  code: string;
  @Column()
  name: string;
  @Column({ default: 0 })
  leadTimeDays: number; // 采购交期
  @Column({ default: 0 })
  safetyStock: number;
}
/* src/entity/BOM.ts */
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne } from "typeorm";
@Entity()
export class BOM {
  @PrimaryGeneratedColumn()
  id: number;
  @Column()
  productCode: string; // 成品/半成品代码
  @Column()
  componentCode: string;
  @Column("float")
  qty: number; // 每个成品需要的数量
  @Column({ nullable: true })
  effectiveFrom?: Date;
  @Column({ nullable: true })
  version?: string;
}
/* src/entity/SalesOrder.ts */
import { Entity, PrimaryGeneratedColumn, Column } from "typeorm";
@Entity()
export class SalesOrder {
  @PrimaryGeneratedColumn()
  id: number;
  @Column({ unique: true })
  orderNo: string;
  @Column()
  productCode: string;
  @Column("int")
  qty: number;
  @Column()
  dueDate: Date;
  @Column({ default: 'CREATED' })
  status: string;
}
/* src/app.ts */
import express from "express";
import "reflect-metadata";
import { DataSource } from "typeorm";
import { Material } from "./entity/Material";
import { BOM } from "./entity/BOM";
import { SalesOrder } from "./entity/SalesOrder";
import amqp from "amqplib";
const app = express();
app.use(express.json());
const AppDataSource = new DataSource({
  type: "postgres",
  host: process.env.DB_HOST || "localhost",
  port: 5432,
  username: "postgres",
  password: "postgres",
  database: "mto_erp",
  entities: [Material, BOM, SalesOrder],
  synchronize: true,
});
let channel: amqp.Channel | null = null;
async function initMQ(){
  const conn = await amqp.connect(process.env.RABBIT_URL || "amqp://localhost");
  channel = await conn.createChannel();
  await channel.assertQueue("order.events", { durable: true });
}
AppDataSource.initialize().then(() => {
  console.log("DB connected");
  initMQ().then(()=>console.log("MQ connected"));
});
/* Helper: 展开 BOM(多级) */
async function explodeBOM(productCode: string, qty: number, ds: DataSource) {
  const bomRepo = ds.getRepository(BOM);
  const materialNeeds: Record<string, number> = {};
  // DFS 展开
  async function dfs(code: string, multiplier: number) {
    const nodes = await bomRepo.find({ where: { productCode: code } });
    if (!nodes || nodes.length === 0) {
      // code 是原料或外购件
      materialNeeds[code] = (materialNeeds[code] || 0) + multiplier;
      return;
    }
    for (const n of nodes) {
      await dfs(n.componentCode, multiplier * n.qty);
    }
  }
  await dfs(productCode, qty);
  return materialNeeds; // { materialCode: totalQty }
}
/* 简单 MRP:根据库存/安全库存判断是否需要采购(示例短版) */
async function simpleMRP(productCode: string, qty: number, ds: DataSource) {
  const needs = await explodeBOM(productCode, qty, ds);
  const materialRepo = ds.getRepository(Material);
  const results: Array<{ code: string; need: number; onHand: number; toPurchase: number }> = [];
  for (const code of Object.keys(needs)) {
    const m = await materialRepo.findOneBy({ code });
    const onHand = m ? 0 : 0; // 假设从库存服务获取,示例里用 0
    const safety = m ? m.safetyStock : 0;
    const need = needs[code];
    const toPurchase = Math.max(0, need + safety - onHand);
    results.push({ code, need, onHand, toPurchase });
  }
  return results;
}
/* 创建订单 API:保存订单 -> 触发简单 MRP -> 发布事件给采购/生产 */
app.post("/orders", async (req, res) => {
  const ds = AppDataSource;
  const repo = ds.getRepository(SalesOrder);
  const { orderNo, productCode, qty, dueDate } = req.body;
  try {
    const so = repo.create({ orderNo, productCode, qty, dueDate, status: 'CONFIRMED' });
    await repo.save(so);
    // 1) 计算物料需求
    const mrp = await simpleMRP(productCode, qty, ds);
    // 2) 发布事件
    const payload = { type: 'OrderConfirmed', data: { orderNo, productCode, qty, mrp } };
    if (channel) {
      channel.sendToQueue("order.events", Buffer.from(JSON.stringify(payload)), { persistent: true });
    }
    return res.json({ ok: true, order: so, mrp });
  } catch (err) {
    console.error(err);
    return res.status(500).json({ ok: false, message: 'error' });
  }
});
app.listen(3000, () => {
  console.log("Server started on 3000");
});

代码说明

  • explodeBOM:简单的递归展开 BOM,现实中应加缓存、防止循环引用、处理替代件与版本。
  • simpleMRP:示例只计算了“需要采购量”,实际需接库存服务(或 DB 的库存表)、考虑在途、已下达采购单与占用量。
  • 事件 order.events 可以被采购服务和生产服务消费,做下游动作(自动转采购单、生成生产工单或进入排产队列)。
  • 真实生产需考虑事务、重试与幂等(例如对 MQ 消费要实现去重逻辑)。

7 部署、运维与上线注意事项

  1. 滚动切换 + 灰度发布:对关键功能(MRP、排产)建议灰度推送,并在真实订单上做 shadow run(并行跑新系统但不影响生产)观察差异。
  2. 数据迁移:老 ERP 向新 ERP 的物料、BOM、库存、历史订单迁移需要数据映射、清洗与多轮比对。
  3. 监控与告警:关键指标(队列堆积、API 延迟、MRP 执行失败率、采购超期率)必须监控并设置自动告警。
  4. 安全性:API 网关开 TLS、RBAC 权限分层、审计日志、敏感资料加密(客户信息、成本数据)。
  5. 备份与灾备:数据库定期快照、跨可用区部署、消息中间件持久化配置。

8 实现效果与关键 KPI

实现后企业常见收益指标:

  • 订单交付及时率提升(目标 +10% ~ +30%)。
  • 关键物料缺料率下降(目标 -20% ~ -60%),因为 MRP 针对订单拉动更精准。
  • 在制品周转天数(WIP)下降,资金占用降低。
  • 订单到交付的透明度提升:客户满意度和重复订单率上升。 KPI 例如:按单成本偏差率、采购到货准时率、车间按时完工率、库存周转率。

9 FAQ

FAQ1:MTO 与 MTS 的 ERP 在设计上最大的不同是什么?

MTO(按单生产)与 MTS(按库存生产)最大的不同在“需求驱动的起点”和“库存应对方式”。MTS 更强调安全库存与预测驱动的补货,ERP 需侧重预测与补货策略;而 MTO 则是订单驱动,ERP 的核心是快速把单转换成可执行的生产计划并精确计算物料需求。具体到系统设计,MTO 系统要更强调 BOM 的配置能力、订单级成本核算、工程变更管理、以及灵活的排产策略(以响应订单交期)。此外,MTO 更依赖与供应链的即时协同——供应商交期、外协能力、在途可视化都更关键。

FAQ2:我们公司只有几十人规模,是否需要做微服务?

对于中小企业(几十人规模),直接从微服务开始并不总是最优选择。微服务带来的分布式复杂性(运维、监控、分布式事务、部署)会消耗大量资源。建议先走模块化单体或按边界上下文拆分的单体应用,重用团队开发/测试部署流程;当系统增长到一定规模或需要按模块独立扩展(例如生产服务需要高并发计算)时,再逐步把热点模块拆成微服务。无论选哪种路径,都要从一开始做好清晰的领域建模、API 设计和数据隔离,这样将来拆分会容易很多。

FAQ3:BOM 版本与工程变更如何管理?

BOM 和工艺路线应当支持版本控制与生效控制。实现上需把每个 BOM 记录为包含版本号与生效日期的实体;工程变更(ECO/ECN)应是一个工作流:申请 → 审核 → 排期生效。系统应支持“历史订单使用当时生效的 BOM/工艺”,也就是说在订单确认阶段就要把所用的 BOM 版本快照到订单/生产工单里,以便追溯。对于设计频繁变更的企业,推荐与 PLM 系统深度集成,并在 ERP 里保留 BOM 快照和变更日志,质检与财务也能依据不同版本进行成本与质量分析。

落地建议

  1. 先做业务建模、画清楚边界,不要急着编码。把订单生命周期、BOM 版本、物料主数据、在途/占用/可用定义清楚。
  2. 从模块化单体开始,优先落地销售、生产、库存与采购四个闭环,再引入财务对账。
  3. 采用事件驱动的异步架构,保证各模块解耦并易于扩展。
  4. 首版实现能跑、能对账、能追溯,后续再在排产优化、APS、供应商协同上迭代。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1 什么是 ERP(离散制造 - MTO)系统?
  • 2 总体架构(建议)
  • 3 主要功能模块详解
    • 技术管理(MTech / PLM 接口)
    • 客户管理
    • 销售管理
    • 生产管理
    • 采购管理
    • 库存管理
    • 财务管理
  • 4 关键业务流程
    • 4.1 订单 → 交付
    • 4.2 BOM 展开
  • 5 开发技巧与实现建议
  • 6 参考代码
    • 代码说明
  • 7 部署、运维与上线注意事项
  • 8 实现效果与关键 KPI
  • 9 FAQ
    • FAQ1:MTO 与 MTS 的 ERP 在设计上最大的不同是什么?
    • FAQ2:我们公司只有几十人规模,是否需要做微服务?
    • FAQ3:BOM 版本与工程变更如何管理?
  • 落地建议
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档