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

简介: 本文介绍了面向离散制造-MTO(按订单生产)模式的ERP系统设计与实现方法。内容涵盖ERP系统定义、总体架构设计、主要功能模块解析、关键业务流程(订单到交付、BOM展开、MRP逻辑、排产等)、开发技巧(DDD、微服务、事件驱动)、参考代码示例、部署上线注意事项及实施效果评估。旨在帮助企业与开发团队构建高效、灵活、可扩展的ERP系统,提升订单交付能力与客户满意度。

离散制造(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 给出一个高层架构图——这是一个典型的微服务/模块化设计,适配中大型制造企业的扩展需求。

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 订单 → 交付

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 的输入)。

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。代码为参考级别,真实产品需要补充权限、安全与完整错误处理。

// 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、供应商协同上迭代。
相关文章
|
13天前
|
SQL 前端开发 关系型数据库
如何开发一套研发项目管理系统?(附架构图+流程图+代码参考)
研发项目管理系统助力企业实现需求、缺陷与变更的全流程管理,支持看板可视化、数据化决策与成本优化。系统以MVP模式快速上线,核心功能包括需求看板、缺陷闭环、自动日报及关键指标分析,助力中小企业提升交付效率与协作质量。
|
8天前
|
机器学习/深度学习 人工智能 搜索推荐
从零构建短视频推荐系统:双塔算法架构解析与代码实现
短视频推荐看似“读心”,实则依赖双塔推荐系统:用户塔与物品塔分别将行为与内容编码为向量,通过相似度匹配实现精准推送。本文解析其架构原理、技术实现与工程挑战,揭秘抖音等平台如何用AI抓住你的注意力。
157 6
从零构建短视频推荐系统:双塔算法架构解析与代码实现
|
13天前
|
前端开发 API 定位技术
如何开发车辆管理系统中的用车申请板块(附架构图+流程图+代码参考)
本文详细解析了如何将传统纸质车辆管理流程数字化,涵盖业务规则、审批流、调度决策及数据留痕等核心环节。内容包括用车申请模块的价值定位、系统架构设计、数据模型构建、前端表单实现及后端开发技巧,助力企业打造可落地、易扩展的车辆管理系统。
|
8天前
|
设计模式 人工智能 API
AI智能体开发实战:17种核心架构模式详解与Python代码实现
本文系统解析17种智能体架构设计模式,涵盖多智能体协作、思维树、反思优化与工具调用等核心范式,结合LangChain与LangGraph实现代码工作流,并通过真实案例验证效果,助力构建高效AI系统。
94 7
|
17天前
|
消息中间件 缓存 JavaScript
如何开发ERP(离散制造-MTO)系统中的生产管理板块(附架构图+流程图+代码参考)
本文详解离散制造MTO模式下的ERP生产管理模块,涵盖核心问题、系统架构、关键流程、开发技巧及数据库设计,助力企业打通计划与执行“最后一公里”,提升交付率、降低库存与浪费。
|
19天前
|
消息中间件 JavaScript 前端开发
如何开发ERP(离散制造-MTO)系统中的技术管理板块(附架构图+流程图+代码参考)
本文详解ERP(离散制造-MTO)系统中的技术管理板块,涵盖产品定义、BOM、工序、工艺文件及变更控制的结构化与系统化管理。内容包括技术管理的核心目标、总体架构、关键组件、业务流程、开发技巧与最佳实践,并提供完整的参考代码,助力企业将技术数据转化为可执行的生产指令,提升制造效率与质量。
|
2月前
|
资源调度 安全 调度
为什么制造企业的MES、ERP系统上不了一线
制造业数字化转型中,ERP和MES系统虽为管理层带来高效与规范,但在车间一线却常遇落地难题。系统复杂、培训困难、环境限制及工人习惯等因素,使这些系统难以真正发挥作用,甚至造成数据滞后、记录缺失等问题。在此背景下,轻量化工具如二维码逐渐兴起,以其低成本、易操作、灵活部署等优势,成为连接系统与一线的“补位”方案。未来,重系统与轻工具并存,或将成为制造业数字化更务实的发展路径。
|
3月前
|
人工智能 运维 安全
如何自己开发一套ERP系统?
本文探讨了企业自建ERP系统的可行性,分析了轻量、中型和重型ERP的区别,并指出自研ERP需明确业务需求、流程逻辑及投入成本。文章建议企业在决定自研前,应先梳理清楚管理逻辑,而非盲目追求技术方案。
|
3月前
|
存储 关系型数据库 BI
如何开发ERP系统中的财务管理板块(附架构图+流程图+代码参考)
本文深入解析ERP系统中财务管理模块的设计与实现,涵盖核心功能、业务流程、开发技巧及代码示例,助力企业打造高效、智能的财务管理系统。
|
3月前
|
SQL 存储 供应链
如何开发ERP系统中的库存管理板块(附架构图+流程图+代码参考)
本文介绍如何通过ERP系统实现企业库存管理的数字化与自动化,涵盖仓库管理、库位管理、出入库操作、库存调拨与盘点等功能设计,并提供开发技巧及代码参考,帮助企业提升库存管理效率,减少错误与资源浪费。

热门文章

最新文章