做企业级ERP实施和开发的人,早晚都会撞上EAS。特别是跟着集团型项目走的时候,客户的需求从来不是“能不能在界面上加个字段”这么简单,而是“这个单据能不能在提交时多做一道校验”“审批流能不能按金额区间走不同分支”“月底的报表能不能直接从业务系统取数”。这时候你就明白了,EAS的二次开发不是选修课,是必修课。
这篇笔记不是官方文档的复述,而是我自己在项目里从搭环境到发布补丁一路踩坑踩出来的实操记录。适合正在接触EAS平台、准备做二开的实施顾问和Java开发,也适合那些被“开发需求”砸到头上、却不知道从哪下手的项目经理。我会把EAS开发最常用的一套链路——从环境搭建、单据开发、审批流集成到问题排查——完整走一遍,尽量让看完的人能直接上手干活。
1. 先搞清楚EAS开发到底在做什么
1.1 EAS平台给开发者提供的核心能力
EAS本质上是一个基于Java EE的集团管控型ERP平台,但它和市面上一堆“表单+工作流”的低代码产品不一样。它不只是给你一个表单设计器,而是给你一整套业务建模、运行和扩展的机制。开发者的主要工作,不是从零写一个ERP,而是基于平台已有的架构去扩展业务。
我自己刚接手时最头疼的一点,是这个平台“太像一套操作系统”了。它有自己的组织模型、用户权限、编码规则、审批流、消息中心、报表引擎,甚至有自己的客户端框架和前端UI规范。你写的每一段代码,都是寄生在这套操作系统上的能力扩展。
所以EAS二开的起点,不是“怎么调API”,而是先理解这几个层次:
- 基础资料层:物料、客户、供应商、科目这类主数据,通常不需要写代码,配置就能搞定
- 业务单据层:采购订单、销售出库、付款单这类实际业务载体,一般是开发的重头
- 业务逻辑层:单据保存时的校验、金额计算、反写、上下游单据之间的联动,大部分要用编码实现
- 展现交互层:列表界面、编辑界面、按钮行为、前端校验,二开里最容易出彩也最容易出坑的地方
- 集成层:与外部系统做接口、导入导出、WebService调用,上了规模的集团项目基本离不开
如果你之前做过传统Java Web开发,可以把EAS理解成一个“带界面的Spring容器”。它的服务端有类似IOC的容器管理业务Bean,客户端有远程调用机制,前后端通过定义好的服务接口通信。只是这套机制是平台私有的,你必须按它的规则来。
1.2 配置、扩展、插件,三层手段怎么选
很多新人拿到一个需求,第一反应就是“我能不能直接改平台自带的那个类”,绝大多数情况下,这是最差的方案。EAS二开按侵入程度分三种,选型顺序非常重要:
配置层:能用标准功能配置解决的,坚决不动代码。比如加一个“是否参与成本计算”的勾选框、调一下单据状态流转、改一下编码规则,这些在平台管理工具里都能做。优势是零风险、免升级冲突,缺陷是只能做平台预设范围内的调整。
扩展层:通过平台的扩展点做增量开发。比如给已有单据加一个自定义字段、在保存按钮上挂一个校验插件、新增一张衍生报表。这类改动不覆盖平台原逻辑,而是“挂”上去,升级时基本不受影响。日常二开里,80%的需求落在这层。
插件层(代码侵入):直接修改平台底层逻辑,比如改标准单据的保存服务、改反写规则。这种方案风险极高,一旦平台升级,你的补丁可能直接失效或者和新逻辑冲突。除非客户业务确实特殊到非改不可,否则我强烈建议不要碰这层,至少要在补丁包做好充分回归测试。
我刚做项目那会儿,被客户一句“这个按钮的逻辑就得按我们公司来”逼着动了标准底层代码,结果平台一次小版本升级,整个模块在测试环境直接起不来。后来花了一个礼拜找补,才把逻辑迁移到扩展层。所以现在每次评审方案,我都会先问一句:这个需求能不能通过扩展点实现?
1.3 认清对象模型和数据库表之间的关系
EAS开发里最容易忽略的一个基础问题,是“对象模型”和“数据库表”之间的关系。EAS的业务数据不完全是“一张表单对应一张表”的简单结构。一个标准业务单据,往往有主单据对象、分录对象、多张附表对象,它们分别映射到多张物理表。平台通过持久化框架帮你把对象保存到表里,你在开发时操作的是实体对象,而不是直接写SQL。
这就带来一个很关键的习惯:写查询时,能走平台的对象查询API就别直接拼JDBC。因为EAS有组织隔离、多账簿、权限过滤这套逻辑,直接用原生SQL很容易绕过这些控制,查出来的数据权限不对,甚至会把已删除的“软删除”记录带出来。这个坑后面我还会专门展开。
2. 开发环境搭建与工程结构
2.1 客户端、BOS Studio、账套,一个都不能少
EAS二开的环境搭建,说简单也简单,说烦也烦。你至少需要三样东西:
- EAS客户端:日常登录系统、看界面效果、做业务验证
- BOS Studio:开发IDE,基于Eclipse二次封装,负责实体设计、界面设计、调试部署
- 一套可用的开发账套:包含完整初始化和测试数据
装的时候有几个关键点,不是踩过坑的人不会注意:
版本必须一致。客户生产环境是什么版本,开发环境就必须是什么版本,包括补丁版本。否则你在BOS Studio里做了实体变更,生成的升级脚本可能在客户环境执行不过去。我见过用8.2开发的东西往8.5生产环境上部署,结果界面控件行为双双异常,最后全部返工。
数据库建议独立。别把开发账套和生产库放一起,更别图方便只准备一个“共享测试库”。EAS开发时会频繁执行实体发布、生成物理表,一个不经意的操作就可能污染共享库里的标准数据。我自己的习惯是:本机一套简化账套,跑通功能;项目组一台公共测试机,做集成验证。
BOS Studio的启动内存要调大。默认配置经常只有512MB,加载大型实体模型时卡得要命。至少给到1GB以上,如果机器条件允许,给到2GB会更顺手。
2.2 工程结构:客户端工程、服务端工程、元数据工程
EAS的BOS Studio里,工程类型分得很清楚,迷糊了会直接影响部署方式。
- 元数据工程(MetaData工程):存放实体定义和界面UI定义,比如“客户”这个实体的结构、之类的字段都在这里
- 客户端工程:存放客户端代码,包括自定义的编辑界面、列表界面、前端Java逻辑
- 服务端工程:存放业务逻辑类,例如单据保存、提交、审核的后台逻辑
三个工程的依赖关系是:客户端工程依赖元数据,服务端工程也依赖元数据。写完代码后,如果只改了元数据,发布时直接发布元数据补丁;如果改了客户端或服务端代码,要分别编译出对应补丁。很多人第一次做补丁包搞错,就是因为没分清“改的是哪个工程”。
2.3 数据源配置与管理员账套
开发过程中,BOS Studio里的程序要连接数据库,需要配置数据源。EAS的数据源是通过部署配置工具完成的,不是直接在代码里写JDBC连接串。你在本地开发时,要把BOS Studio指向本机或开发服务器的数据库地址。
这里有一个非常实用的经验:在开发账套里给自己一个管理员账号。因为EAS权限体系很复杂,二开程序调试时经常需要绕过权限调用服务端方法,如果你权限不够,会频繁遇到“无权限”的提示,而问题往往不在你的代码,而在账号权限。别在这种地方浪费时间,直接用管理员身份开发,等测试阶段再交给普通权限账号验证。
3. 一张业务单据从0到上线的完整实操
3.1 需求场景设定
为了讲清楚,我用一个虚拟场景举例:某制造企业需要开发一张“样件试制申请单”,业务上要求记录样品编号、试制数量、责任部门、期望完成日期,同时要有多行物料明细,并在提交审批时根据试制总金额走不同的审批分支。这个单子不是EAS标准功能,需要完全二开。
从需求梳理到上线,我一般分五步走:
- 梳理字段与业务规则
- 创建实体并发布
- 搭建界面与按钮行为
- 编写服务端校验逻辑
- 配置审批流与部署发布
3.2 创建实体与字段设计:主档、分录、F7引用
EAS的实体设计在BOS Studio的“业务实体”中进行。这张样件试制申请单,至少要有两个实体对象:
- 单据主档:保存单头信息,包括编号、申请部门、申请人、试制总金额、期望完成日期
- 单据分录:保存物料明细,包括物料编码、名称、规格、数量、单价、行金额
创建实体时,字段类型的选择要谨慎。EAS里最主要的两类字段是“基础资料属性”和“普通属性”。以“物料编码”为例,不能简单设成字符串类型,应该设为指向“物料”基础资料的引用字段,这样在界面上能自动弹出物料选择框(F7),也才能带上物料的多维属性和组织隔离逻辑。
我自己的字段设计原则是:能从基础资料引用的,绝对不手输。比如“责任部门”一定要引用组织架构里的部门,而不是一个普通文本框。这样做的好处非常明显:数据一致性有保障,跨系统同步数据时不会出现“一个部门三种叫法”的脏数据。
还有一点,凡是涉及金额的字段,建议用EAS的“金额”类型,它内部会处理精度和币种逻辑,直接用一个浮点型会有精度问题。这类问题在财务报表取数时才会爆发,等爆发了再改实体,代价就大了。
3.3 实体发布与物理表生成
实体设计完成后,需要“发布实体”。这个动作会做几件事:在数据库里生成/更新物理表、注册实体的访问服务、生成客户端需要的元数据。
发布时要注意,如果改的是已经有数据的表,要关注EAS弹出的“变更脚本”提示。EAS会生成增量SQL脚本,比如“新增字段”“修改字段长度”。这些脚本要保存好,后续发布补丁时用。
注意:实体发布不是随便点一下就行。发布前先备份数据库,尤其是改动已有实体的字段属性(比如把一个可空字段改成必填),数据库里已有的NULL值记录会导致发布失败或数据异常。
我习惯在发布前把“表重建模式”设为“保留数据模式”。如果误选成“重建表模式”,老数据会全部清空,这在开发环境无所谓,在生产环境就是严重事故了。
3.4 界面搭建:从Form到UI的映射关系
实体发布后,BOS Studio会生成默认界面,但默认界面往往不满足业务诉求。你需要调整表单布局、字段排列、列表显示列、字段的读写属性。
EAS有客户端UI和Web端UI两套界面定义。很多新手只改了客户端界面,结果Web端登录一看,还是老样子。记住,二开单据上线前,客户端和Web端界面都要检查一遍。现在大多数用户都走Web端,Web端界面的重要性甚至超过客户端。
界面设计时,我特别关注几个细节:
- 字段可见性:比如“试制总金额”如果由分录行金额汇总而来,界面应该设为只读,不能让人手工乱填
- 分录编辑体验:物料选择时,分录的物料字段必须支持F7选择,同时带出物料编码、规格、单位等信息
- 列表默认过滤:给列表界面加默认的“未完成”过滤条件,否则用户每次进来面对全量单据,会非常痛苦
这些调整看起来琐碎,但直接决定用户愿不愿意用这套系统。我见过很多项目因为列表查询太慢、界面字段太多,业务部门拒绝使用,最后IT部门被反复投诉。
3.5 编码规则与自动编号
业务单据一般都要有业务编号,比如“SY-2024-001”。EAS的标准做法是配置“编码规则”,而不是在代码里生成编号。编码规则支持流水号、日期段、业务单元编码段等组合。
在单据保存前,平台会调用编码规则生成器自动填充单号。我们要做的只是在界面配置里绑定编码规则,或者在保存逻辑里触发编码生成。
这块的坑在于:不要在保存后才生成编码。因为EAS的保存是一个关联事务,如果编码生成失败,整个保存要跟着回滚。我一般会在实体的“生成编码”事件里处理,确保编号在数据落库前就稳定生成。
3.6 保存校验逻辑编写:客户端与服务端的取舍
样件试制申请单的业务校验比较好设计:申请数量必须大于0,物料明细不能为空,试制总金额不能超过某个上限。
关键是,校验逻辑放哪一端?EAS支持在客户端UI事件里写校验,也支持在服务端保存前事件里写校验。
我的取舍逻辑是:基本的必填和格式校验放客户端,快速提示,用户体验好;核心业务规则放服务端,防止绕过UI直接调接口。比如“分母金额不能超过预算控制额”这种规则,如果只写在客户端,用户通过后台接口导入数据、或通过别的客户端入口操作时,校验就失效了。
服务端校验一般是在业务逻辑类里重写保存前方法。大概的代码结构是这样:
protected void onSave(Context ctx, CoreBillBaseInfo model) throws EASBizException { // 1. 校验分录是否存在 if (model.getEntry() == null || model.getEntry().size() == 0) { throw new EASBizException("保存失败:分录物料明细不能为空"); } // 2. 遍历分录,校验数量与金额 for (BillBaseInfo entry : model.getEntry()) { if (entry.getQty() <= 0) { throw new EASBizException("保存失败:物料数量必须大于0"); } } // 3. 调用父类继续执行默认保存逻辑 super.onSave(ctx, model); }这里要特别强调一下:自定义校验抛异常前,一定要确保消息能反馈到界面。EAS里如果直接throw一个RuntimeException,前端提示可能很不友好。正确做法是抛出EASBizException或封装好的业务异常,这样界面上能弹出清晰的中文提示。
还有就是要记得调用super的保存逻辑。新手经常犯的错误是把整个保存方法重写了,结果平台原本自动做的字段赋值、状态更新、日志记录全部丢失,出现各种莫名其妙的数据问题。
3.7 按钮行为与操作联动
单据界面一般有“保存”“提交”“审核”“反审核”等操作按钮。二开时经常要做的事情是:在点击“提交”时额外触发一个动作,比如更新外部档案状态。
EAS里给按钮加逻辑,不是在界面代码里直接绑一个ClickListener就完事,这容易被平台封装机制绕过。更稳妥的做法是使用平台的事件扩展机制,在提交操作的扩展点里挂逻辑。
大致思路是:写一个操作插件类,继承平台的操作处理器基类,重写“提交前”“提交后”等方法。然后在BOS Studio的操作配置里挂上这个扩展点。这样无论用户从客户端、Web端、还是接口调用提交操作,插件逻辑都会生效。
3.8 审批流配置:如何让流程按金额分支
样件试制申请单的审批流,需求是“试制总金额小于10万走部门经理审批,大于等于10万额外走总经理审批”。
EAS的审批流是在“流程中心”配置的,不写代码。配置时用“金额字段”作为条件变量,设计两个审批分支。关键操作是把单据上的“试制总金额”字段映射成审批流的变量,然后在条件编辑器里做区间判断。
这里有个容易踩的坑:条件变量映射的是单据字段路径。如果映射错了路径,比如指向了旧版本字段或子对象字段,审批分支会失效,所有单据都走默认路径。我调试这类问题时,一般先在测试单据上打印审批变量取值,确认金额真的传过来了,再去核对流程分支条件。
审批流配置完成后,还要注意单据的“提交操作”要绑定到流程定义。很多二开单据提交时提示“流程未定义”,就是因为提交操作没有关联审批流,或者流程状态不是“启用”。
4. 查询、报表与批量操作的技术细节
4.1 用对象查询API代替原生SQL
前面提到过,EAS里写查询要优先使用平台的对象查询API。这里我给一个实际的对比。
假设要找“所有状态为已审核的样件试制申请单,且申请部门为某部门”。用对象查询API的方式大概是:
ObjectQuery query = new ObjectQuery("where billStatus = ? and applyDept = ?"); query.setParameter(0, BillStatus.AUDITED); query.setParameter(1, deptId); SYSDateCheckUtil.checkOrg(query, ctx, "org"); List<SYSDate> list = query.list(ctx);这里SYSDateCheckUtil.checkOrg是EAS做组织隔离的常见写法。如果你用原生SQL直接查物理表:
SELECT * FROM t_sys_sampleapply WHERE fbillstatus = 1 AND fapplydept = ?短期看没问题,但长期看有两个隐患:一是组织隔离失效,A组织的人可能查到B组织的数据;二是平台可能用到软删除机制,探测行被做逻辑删除后,直接SQL会把“已删除”的数据也查出来,给业务造成严重的数据信任危机。
所以和团队配合时,我一直要求:除了极端性能优化的场景,所有查询必须走对象查询API。
4.2 即时查询与业务报表
客户经常会提“能不能给我出一张报表,每天统计各部门样件申请数量和金额”。EAS里做报表,优先用的是“即时查询分析”(即查即得)。它能基于已有的业务实体,通过配置取数逻辑,快速出一个分析报表。
即时查询的优势是快,但有个限制:它只能基于“已经建模好的实体”做查询。如果业务上需要的数据是跨多个实体加工的,那就要写“自定义报表”了。EAS的报表工具有报表模板设计器,支持数据集查询、单元格公式、参数条件。复杂一点的,我们会直接在服务端写一个查询逻辑,撑一个数据集,前端报表模板负责展现。
4.3 批量操作与后台任务
样件试制申请单上线后,财务提了个需求:要能批量审核几百条已提交的单据。EAS的列表界面默认有“批量审核”功能,但自定义单据不一定支持。二开时如果要做批量操作,最好用平台的后台任务框架去实现,不要直接写一个for循环在界面线程里执行。
原因很实际:批量操作如果串行执行几百次保存、审核,界面会长时间无响应,超时后事务状态说不清楚,用户看着像系统死了。平台的后台任务框架可以做成异步批处理,支持进度条、支持日志跟踪、支持失败续跑。
我自己第一次做批量提交功能,就是图省事在界面里循环调服务端方法,结果数据量大时系统直接卡死,事后看日志发现事务堆积到内存溢出。后来改成后台任务,问题彻底解决。
5. 常见问题与排查技巧实录
5.1 问题一:保存单据提示“编码重复”或“事务冲突”
这类问题多出现在单据编码规则配置了流水号,但是多个用户并发提交时,流水号生成逻辑和数据库唯一索引冲突。EAS的编码规则一般是支持并发的,但如果你在保存逻辑里手工干预了编码生成,就容易打破平台的并发控制。
排查思路:先看编码规则配置里“生成时机”是不是“保存前”,再看异常堆栈是不是“唯一索引冲突”。如果是,把编码生成逻辑还给平台,尽量不要自己用SELECT MAX(billNo)这种原始方式生成单号。这种方式的并发问题几乎是无解的。
5.2 问题二:审批流变量的值不生效
这个坑我踩得最多。配置审批流条件后,测试发现所有单据都走默认分支。排查步骤:
- 在单据提交前,先把单据上的目标字段值打出来,确认有值
- 检查审批流字段映射,确认映射路径正确
- 在流程设计器里用“流程测试”功能,人工模拟一条数据走一遍
- 如果还是不行,停用再启用流程定义,让平台重新加载
还有一个容易忽略的点:审批流的字段映射是基于“元数据版本”的。修改实体字段后没有重新发布流程关联的元数据,审批流里看到的还是旧字段。遇到这种问题,重新发布元数据,再做一次流程映射绑定。
5.3 问题三:界面改了但用户看不到
二开调整了界面布局和字段显示后,客户端用户永远看不到变化,Web端用户也看不到。原因通常是:发布补丁时只更新了服务端代码,没有把新的UI定义和客户端代码同步发布到应用服务器。
EAS界面的呈现路径是:客户端/浏览器从应用服务器拉取UI元数据,然后动态渲染。所以界面修改不只是改完就行,必须重新生成UI定义并发布到服务器。发布后,有时用户缓存还在,需要清一下浏览器缓存或客户端缓存。
我这边固化了一个操作流程:开发环境改完界面 → 发布元数据补丁 → 在测试环境验证 → 打补丁包 → 生产环境更新 → 通知用户强刷缓存。每一步都不能省。
5.4 一张常见问题速查表
| 问题现象 | 可能原因 | 排查方向 |
|---|---|---|
| 保存提示事务冲突 | 编码并发或自定义保存逻辑冲突 | 检查编码规则、保存扩展点逻辑 |
| 审批流不按条件分支 | 字段映射错误或元数据版本过期 | 核对映射、重新发布元数据 |
| 界面看不到新字段 | 元数据未发布或缓存未清 | 重发补丁、清理缓存 |
| 查询结果含已删除数据 | 使用了原生SQL绕过软删除 | 改用对象查询API |
| 组织隔离失效 | 查询未做组织过滤 | 调用组织过滤工具类 |
| 表单打开很慢 | 界面加载了过多未过滤字段 | 调整列表默认过滤条件 |
| 发布实体后老数据丢失 | 误选重建表模式 | 切换为保留数据模式并恢复备份 |
| Web端和客户端界面不一致 | 只改了客户端UI定义 | 检查并同步Web端UI |
5.5 一个调试技巧:看日志文件而不是盯着界面
EAS开发里最浪费时间的,就是盯着界面上的错误弹窗猜原因。弹窗往往只显示一句“操作失败”,真正的异常堆栈都在服务端日志里。EAS的服务端日志路径一般在应用服务器的logs目录下,里面会有完整的异常栈。
我第一次排查“保存失败”问题时,在界面蒙了一个下午,结果发现日志里早就写着“除数不能为0,位置在某分录金额计算”。从此以后,我给自己定了一条规矩:界面报错,先翻日志,再动手。
还有一个更高效的技巧:写开发工具类,在服务端扩展点里临时增加日志输出,把关键变量的取值打印出来。通过打印出来的值,对照日志里的SQL日志,基本能定位80%的问题。
6. 一些只有做过项目才会懂的经验
最后分享几条体会。EAS开发的技术难点其实不在Java本身,而在你要学会“在别人定的规则里干活”。你写的代码再优雅,如果不遵守平台的建模规范和部署流程,上线时就是各种莫名其妙的问题。
第一,文档比代码更重要。实体变更记录、补丁发布记录、界面改动记录,务必留档。EAS项目动辄几年迭代,开发人员换了三轮后,能救你的就是当初留下的变更记录。我自己每个需求都会维护一张表,记录需求变更点、涉及实体、涉及界面、影响范围。
第二,发布之前先回归标准功能。二开最怕的是你改了一个实体字段长度,结果标准单据的服务端逻辑出问题。所以每次补丁发布后,都要拿标准功能做一轮冒烟测试。这看起来费时间,实际上能帮你省掉无数个深夜加班。
第三,开发尽量用扩展点,少动底层。平台版本的升级,远比你想象得频繁。你越是把业务逻辑挂在扩展点上,升级时的兼容性问题就越少。反过来,你在底层代码里塞越多私货,升级时就越痛苦。每一次升级都要测试你的“私货”是否被覆盖,那会把你拖垮。
第四,业务理解能力决定你的开发上限。EAS二开有一半时间在跟业务部门沟通,另一半时间在改代码。如果理解不了“为什么这个单子要按部门走审批”,你很难设计出让人满意的方案。我刚接触项目时,总是急着写代码,后来发现多花半天把业务规则理清楚,能省掉后面改代码的三天。
EAS这套系统,说它难,是因为它本身承载了太多企业管理的复杂度;说它容易,是因为只要你按它的规矩来,平台帮你做了大量基础工作。做二开的时间越长,我越觉得,真正的功力不在于你会写多复杂的代码,而在于你懂业务、懂平台、懂规则,然后在三者之间找到那条最平滑的实现路径。