关于
我叫 Leo Ye,是一名住在上海的软件工程师。我学的是软件工程,毕业于 Eastbridge University of Technology。十多年来,我一直在做制造业和物流背后的那些系统。
我做过最大的东西是一套自研 MES,支撑着一座 1,200 人工厂的生产,从零设计、从零实现。同时我负责仓储业务的 Odoo 开发——从收货到发货,货怎么走由这套流程决定。
我喜欢那些第一眼听起来有点疯的想法。我的经验是,最有用的系统,往往就是从那儿开始的。
我怎么建东西
-
先上白板,再开编辑器。
最难的问题,在写第一行代码之前就已经解决了。
-
标准件买,差异自己造。
Odoo 这类成熟工具该用就用,只把业务真正独特的那部分自己写。
-
系统做完的标志,是没人再注意到它。
最好的工厂软件,是工人不用想就会用。
-
没有哪个想法疯到不值得先画出来。
画出来,才知道它到底行不行。
从现场到架构
一条马克笔线,四段角色。
成长轨迹
- 2014 工程师
- 2018 架构师
- 2021 技术负责人
-
Odoo 技术负责人
Harborline Logistics
带团队扩展 Odoo 的仓储管理能力,覆盖 3 个配送中心。
5 人团队 · 3 个配送中心
-
首席软件架构师
Northvale Manufacturing Group
从零设计并实现一套自研 MES,服务 1,200 名员工、14 条产线。
1,200 人 · 14 条产线
-
高级软件工程师
Northvale Manufacturing Group
搭建生产计划与质量追踪的内部工具,并提出 MES 的雏形。
生产计划 · 质量追踪
-
软件工程师
Kestrel Industrial Solutions
为工业客户打通工厂设备与业务系统之间的集成。
设备 ↔ 业务系统集成
MES 平台
- 客户
- Northvale Manufacturing Group
- 角色
- 首席软件架构师
- 周期
- 2018 – 2021
14 条产线各有各的工艺路线,市面上的成品软件一套都填不上。我们用四年时间从零写了一套自己的 MES。
问题
- 生产工单靠纸质流转,跨班次交接只能靠口头确认,出问题时无法回溯是谁在什么时候改了什么。
- 报表每天由统计员手工汇总一次,管理层看到的永远是昨天的车间。
- 设备停机记录散落在三套互不相通的系统里,没人能回答「这条线这个月到底停了多久」。
- 看遍了市面上的 MES,没有一套能贴合这 14 条产线各自不同的工艺路线。
方案
- 以工单为核心聚合根重建领域模型,把报工、质检、停机都收敛成工单上的事实事件。
- 设备侧用边缘网关做协议转换与缓冲:网络抖动时数据先落本地,恢复后补传,不丢事件。
- 报工入口做成离线优先的移动端——车间网络不可用,不能反过来阻塞生产。
- 只把「这家工厂到底怎么做生产」这件事自己写;调度、部署、看板底座全部用成熟方案。
- 打通 ERP,订单与库存自动同步,不再靠人在两套系统里各录一遍。
结果
-
-38%
平均停机响应时间
-
4h → 12min
生产报表延迟
-
99.95%
报工事件完整率
-
1,200 人 · 14 条产线
覆盖范围
自研 既有
自研模块
- 工单状态机
- 排产引擎
- 车间终端
- 质量检验
- 边缘数据补传
- 报表指标层
技术栈
- PostgreSQL
- Redis
- MQTT
- Vue
背景
工厂有 1,200 名员工、14 条产线。在我接手之前,生产执行靠的是纸质工单加 Excel: 工单跟着物料走,报工靠人写,停机靠人记。
这不只是「效率低」。真正的问题是没有人能回答关于过去的问题——这条线为什么慢了、 这批货为什么返工、昨天夜班到底发生了什么。数据在纸上,就意味着它只存在于当时在场的人的记忆里。
我们评估过所有能买到的方案。问题不在于它们不好,而在于 14 条产线各有各的工艺路线, 而通用 MES 的抽象层次假设产线之间足够相似。要贴合,就得把这家工厂的特殊性改造成 软件的通用性——那笔账算不过来。
所以我提议自己做。这不是一个显而易见的决定。
关键取舍
取舍一:工单作为聚合根,而不是「工位」
早期设计把报工建模在工位(station)上,看起来更贴近现场。但工位是物理概念, 会随产线改造移动、合并、废弃;而车间里真正稳定的东西是工单——它从下达走到完工, 穿过所有工位。
改成以工单为核心聚合根之后,报工、质检、停机都变成挂在工单上的事实事件。 好处不是「更优雅」,而是任何一次追问都能沿着一个对象回答完: 这批货经过了哪些工位、谁在什么时候报了什么、哪一步出的问题。
代价是排产模块和工位视图要做额外的投影,不能直接读主模型。
取舍二:边缘网关做缓冲,而不是让 MES 直连设备
车间网络会抖。最早的版本让 MES 直接订阅 PLC,一次网络闪断就丢一批事件, 而且没人知道丢了多少。
改成边缘网关先落本地、恢复后补传之后,事件完整率到了 99.95%。 代价是引入了「至少一次」语义:同一事件可能补传两次,所以每个事件必须携带幂等键, 消费端必须能去重。这比「不丢」麻烦,但这是唯一诚实的做法——车间数据不允许丢失。
取舍三:只写工厂特殊的那部分
「从零自研」很容易滑向「什么都自己写」。我们给自己划的线是: 只有「这家工厂怎么做生产」这件事自己写。
排产算法、看板底座、消息中间件、部署方式——全部用成熟方案。 回头看,这条线是整个项目里最重要的决定:它让四个人四年做完了本该做不完的事。
遗留问题
- 离线报工端的冲突合并仍然粗糙。 两台终端离线时都改了同一张工单,重连后按时间戳取胜者, 后到的那次修改会被丢掉并记入审计日志。真正正确的做法是让冲突可见、由人裁决, 但现场没有人愿意在交班时处理「冲突提示」,所以退回了自动合并 + 事后审计。
- 报表指标层的口径没有单一来源。 同一句「停机时长」在三个看板上曾经算法不同, 我们最后靠一份文档和约定统一,而不是靠代码强制。这是当时欠下的技术债。
- 边缘网关的固件升级靠人工。 二十多台网关,目前要人跑到现场插 U 盘。
Odoo 仓储管理
标准 Odoo 假设库位是固定的,而线边仓会跟着产线节拍流动。我们用自研模块补上这个差,一行核心代码都没改。
- 客户
- Harborline Logistics
- 角色
- Odoo 技术负责人
- 周期
- 2021 – 至今
问题
- 标准 Odoo 假设库位固定,但线边仓随产线节拍流动。
- 批次追溯要跨入库、领料、退料三段连续,标准模块在三处断链。
- 三个配送中心各有各的拣货习惯,一套标准流程按不住。
自研 既有
-
-28%
平均拣货时间
-
14 → 17
Odoo 版本升级,作业零中断
自研模块
- 批次追溯模块
- 线边仓中间层
- 条码拣货
- 实时库存看板
技术栈
- Odoo
- PostgreSQL
为什么不做成标准功能
三个配送中心用的都是 Odoo 的标准出入库流程,只有两处标准模块填不上: 批次追溯的连续性,和线边仓的临时归属。
这两件事都不适合改核心。改核心意味着每次升级都要重放一遍补丁, 而仓储业务几乎没有停机窗口来做这件事。
所以我们把差异全部收敛进一个模块:Odoo 核心不动,追溯逻辑和数据挂在旁边。 结果是 14 升到 17 的时候,业务侧没有感觉到升级发生过。
技能不是一张清单
从一个点分出三个分支:架构、领域、实现。
技能
-
架构
把一个想法变成能被画出来、被追问的结构。
- 系统设计
- 集成架构
- 数据建模
- 实时系统
- 技术规划与代码评审
-
领域
知道车间和仓库每天真正在发生什么。
- MES
- WMS
- 制造运营
- 仓储物流
- 车间现场
-
实现
自己动手写,不只是在图上比划。
- Odoo 14–17
- PostgreSQL
- Python
- JavaScript
- SQL
- Linux
- Docker
The Lab
在试的工具,以及我最后的判断。
-
Excalidraw
留着
从 2020.03 起
装在浏览器里的那块白板。我的系统设计基本都从这儿开始—— 先把模块和箭头摆出来,再去看哪里连不上。
-
Obsidian
留着
从 2021.06 起
我的第二大脑。架构笔记和决策记录都在里面, 每一条都写清楚当时为什么这么选、放弃了什么。
-
Notion
弃了
从 2019.09 起
团队协作用它确实好,但记我自己的笔记太重了: 每次打开都要先选数据库、再选视图。我需要的是一敲就能开始写。
-
Raycast
留着
从 2022.02 起
每天省我上百次小点击。启动器、剪贴板历史、窗口管理, 还有几个我自己写的脚本,都在同一个入口下。
两条路
你是来招人的,还是来聊一件具体的事?
那就做出来。