简介:一套面向企业级应用开发者的OA办公系统完整源码包,在组织流程自动化、文档管理、任务协作基础上,额外集成CRM客户管理系统与内部即时聊天工具,并针对手机端做了自适应适配,适合需要学习或二次开发企业协同平台的PHP开发者。压缩包共3272个文件、约37.2MB,以PHP后端逻辑、JS交互脚本、HTML页面、CSS样式为四大主体,辅以GIF/PNG/JPG等界面素材、SQL数据库脚本、DB数据文件及部署文档,结构上覆盖前后端完整链路。已有327人学习下载。CRM部分完整落地客户信息管理、销售机会跟踪、合同订单、营销活动与服务支持等关键模块,聊天工具兼得即时通讯与工作协同;通过阅读源码可掌握OA、CRM、聊天三套系统的集成思路,以及自适应手机布局的实现手法,数据库配置与部署文档也为实际运行提供了直接参考。
1. 先别急着部署:这套 OA+CRM 源码到底能复现什么
拿到这套 OA 办公系统源码时,我第一反应不是急着解压跑起来,而是先顺着文件清单把它的技术底牌摸一遍。压缩包里既有 pdf.cab、OfficeControl.cab 这类浏览器端控件,也有 ntkoWebSign.cab 电子签章组件和 beyond.min.css 前端资源,再加上集成在里面的 CRM 客户管理系统与内部聊天工具,本质上这是一套把「办公审批 + 客户跟进 + 即时通讯」揉进同一个门户的企业级应用。对做 Java 或 .NET 二次开发的工程师来说,它最大的价值不是开箱即用,而是能让你把 OA、CRM、消息这三条线如何共享登录态、如何互相跳转、如何在手机端自适应重排看明白。适合正在选型 OA 二次开发蓝本、或想自己攒一套内部管理系统的团队拿来拆解学习。
2. 源码包结构拆解:从 .cab 和 .css 反推这套 OA 的技术底牌
2.1 文件清单里的每个文件都是什么角色
从包内文件清单来看,这套系统的浏览器端依赖相当有年代感,但这也正是它适合学习的原因。pdf.cab 负责 PDF 在线阅读,swflash.cab 是老式 Flash 播放支持,OfficeControl.cab 解决 Office 文档在线打开与编辑,ntkoWebSign.cab 和 ntkosigntool.cab 则是 NTKO 电子签章体系的组件,用于审批流程里的签字盖章。这些 .cab 文件不是摆设,它们决定了系统的部署环境是 IE 内核优先,也解释了为什么很多企业 OA 至今仍要求员工用兼容模式访问。
beyond-rtl.min.css 和 beyond.min.css 则指向 BeyondAdmin 这类基于 Bootstrap 的后台 UI 框架,说明前端不是纯原生页面,而是有统一布局、菜单、表格组件的管理后台。把这几个线索拼起来,可以断定主程序是一个 B/S 架构的 Web 应用,服务端八成是 Java 技术栈,因为 NTKO 系列控件在 JSP 时代的 OA 系统里出现得最频繁。而 ntkosigntoolv3.cab 与 ntkosigntool.cab 两个版本同时存在,说明安装逻辑里做了版本回退处理,页面会优先引用 v3,失败时自动降级到旧版。
这个细节对后续部署很关键。如果你把这两个 cab 同时放在同一个目录,浏览器在解析 codebase 时如果只认其中一个,签章功能可能表现不稳定。我的习惯是把 v3 版单独放,旧版放在备份目录里,只在 v3 注册失败时手动启用。从文件清单还能看到 ntkoplugins.crx,这是 Chrome 内核用的扩展插件,说明作者正在从 ActiveX 向 Chrome 扩展迁移,部署时两种内核要分别对待。
2.2 从文件清单反推整体架构
一套完整的 OA+CRM+内部聊天工具,在逻辑上至少分成三层。表现层是 JSP 页面配合 Bootstrap 自适应布局,也就是 beyond.min.css 负责的部分;应用层是 OA 审批、CRM 客户管理、聊天消息三个模块的服务端;数据层则是存放员工、客户、订单、流程实例的数据库。聊天工具不是简单轮询数据库,常见做法是独立出一个消息服务,用 WebSocket 或 Long Polling 维持长连接。
我一般会先画模块调用关系再动代码。OA 门户负责单点登录,把当前用户信息写进 Session;CRM 模块复用这个 Session,避免员工在客户管理里再登录一次;聊天工具则通过一个公共的用户体系接口获取组织架构,三个模块共用同一张员工表。只要不是过度定制,这套源码基本都会遵循这个套路,所以研究它时优先看登录过滤器和用户上下文工具类,就能很快理顺模块边界。
数据层方面,OA 和 CRM 通常共用同一个数据库实例,但业务表分开。员工表是核心,客户表通过 owner_id 关联员工 ID,审批表单表通过 apply_user_id 关联员工 ID,聊天消息表通过 sender_id 和 receiver_id 关联员工 ID。这种设计的好处是权限模型统一,坏处是数据量大了之后消息表容易膨胀,二次开发时要注意按时间做分区或归档策略,不然后期运维很被动。
模块边界还有一个容易被忽略的地方:CRM 和 OA 的功能入口是互相嵌入的。比如在 CRM 的客户详情页里,可能直接放一个"创建回访任务"按钮,这个按钮实际调用的是 OA 的任务接口。如果你只盯着某一个模块的源码看,会看不懂数据为何莫名出现,这时候把请求 URL 抓一遍,就能看清模块间的 Rest 调用链。
2.3 部署前的目录规划与版本选型
动手部署前先规划好目录,避免之后被路径问题反复折磨。我会把源码按下面结构归档:
oa-project/ ├── src/ # 主程序源码(按实际语言放) ├── webapps/ # 部署目录,存放 JSP、CSS、JS ├── doc/ # 部署文档与数据库脚本 ├── lib/ # 依赖 jar 包 ├── components/ # 解压后的 .cab、.crx 控件文件 └── data/ # 示例数据与附件上传目录这个结构的关键是把控件文件、源码、数据库脚本分开放置。.cab 控件需要被 Web 服务器单独映射到可访问的 URL,比如 /components/ 目录下;数据库脚本则在初始化阶段单独执行,不混进主程序代码里。部署时还需要准备 JDK、Tomcat、MySQL 三件套,版本上 JDK 8 配 Tomcat 8 最稳,MySQL 5.7 即可,太新的 MySQL 8 有时会在连接驱动上出兼容问题。
我遇到过不少人把控件目录直接丢进 webapps 根目录,结果浏览器下载控件时路径带了版本号或中文,导致控件永远装不上。所以我的习惯是给每个 .cab 建一个独立子目录,并保证路径全英文、无空格。前端资源 beyond.min.css 则放在静态资源目录,配合 Nginx 做缓存,减轻 Tomcat 压力。
版本选型上还有一个容易被忽略的点:聊天工具如果走了 WebSocket,Tomcat 版本必须支持 JSR 356 规范,Tomcat 7.0.47 之后的版本才比较稳。太老的 Tomcat 会在启动时报 NoClassDefFoundError,很多人以为是源码缺包,实际上是容器版本不对。部署前把 Tomcat 版本确认好,比事后排查省事得多。
3. 把 OA+CRM 跑起来:数据库初始化、部署与三条流程验收
3.1 数据库初始化与配置项修改
源码包里一般会带 db 或 sql 目录,找到初始化脚本。常见做法是先建库,再导入数据,最后改配置。用 MySQL 命令行执行:
mysql -u root -p -e "CREATE DATABASE oa_system DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;" mysql -u root -p oa_system < doc/oa_init.sql mysql -u root -p oa_system < doc/oa_crm_data.sql这里有两个关键点。第一,字符集必须用 utf8mb4,因为 CRM 客户信息里会存表情符号或生僻字,utf8 会直接报 Incorrect string value 错误;第二,先导结构再导数据,如果 oa_init.sql 里已经建了库,就要把第一句 CREATE DATABASE 去掉,避免重复建库报错。部分版本的初始化脚本里还包含存储过程或触发器,导入时如果报权限错误,检查一下当前 MySQL 用户是否有 CREATE ROUTINE 权限。
导入完成后去改数据源配置。Java 项目里通常是 src/main/resources/db.properties 或 jdbc.properties:
jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://127.0.0.1:3306/oa_system?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai jdbc.username=root jdbc.password=你的数据库密码参数说明:useSSL=false 是避免 MySQL 8 默认启用 SSL 导致的握手警告;serverTimezone 必须指定时区,否则会报 The server time zone value 错误;characterEncoding 这里要注意,MySQL 驱动里的 utf8 实际上代表 utf8mb4 的一部分,如果你在连接串里写 utf8mb4 反而可能被驱动忽略,稳妥做法是建库用 utf8mb4、连接串写 utf8。用户名密码不要写 root 在生产环境直连,至少单独建一个 oa_app 账号授权。
3.2 部署主程序与移动端自适应验证
改完配置,把 webapps 目录拷到 Tomcat 下,或者直接用 Maven 构建:
mvn clean package -DskipTests cp target/oa.war $CATALINA_HOME/webapps/ $CATALINA_HOME/bin/startup.sh启动后先别急着登录,先看日志。常见做法是 tail -f $CATALINA_HOME/logs/catalina.out,确认没有报数据库连接失败或端口占用。启动成功后,用浏览器访问 http://localhost:8080/oa/login.jsp,能出现登录页说明主程序已经起来了。我习惯先访问一次登录页再检查日志,因为 JSP 第一次编译时如果有语法错误,日志里会持续刷异常,提前确认能省去后面反复重启的时间。
移动端自适应这块,重点验证三件事。第一,把浏览器窗口拖到 375px 宽度,看菜单是否折叠成侧边;第二,用手机访问内网 IP,看 CRM 客户列表的表格是否横向溢出;第三,在手机端发起一条审批流,确认附件上传按钮没有被遮挡。这套源码既然标注了自适应手机,前端应该用的是 Bootstrap 栅格加媒体查询,出问题多半是 viewport meta 标签没写全。
如果发现手机端字体过大或页面放大,优先检查每个 JSP 的 head 部分。很多老 OA 系统的公共 header 里没有 viewport 标签,而是散落在各页面,导致有的页面适配正常有的页面失效。正确做法是把 viewport 写进公共 top.jsp 或 layout.jsp,一改全改。用手机开发者模拟器逐页过一遍,比真机来回测更高效。
3.3 CRM 核心流程验收:客户、机会、订单、服务
登录进去后,按 CRM 的「客户-机会-订单-服务」四条线各跑一遍。建议用下面表格记录测试结果:
| 流程 | 操作路径 | 预期结果 | 验收重点 |
|---|---|---|---|
| 客户信息管理 | CRM > 客户列表 > 新增客户 | 客户档案可保存并出现在列表 | 必填校验与分页 |
| 销售机会管理 | 销售 > 机会列表 > 新建跟进记录 | 机会阶段可变更,漏斗数据更新 | 状态流转逻辑 |
| 订单合同管理 | 订单 > 生成合同 | 合同关联客户与订单号 | 编号规则与审批流 |
| 服务支持管理 | 服务 > 提交工单 | 工单可分配至处理人 | 指派逻辑 |
验收时最值得看的是销售机会的状态流。线索、初步沟通、方案报价、成交、丢单这几个阶段,源码里通常用状态字段加枚举类控制,改动时要注意数据库里存的到底是数字还是字符串。如果阶段变更没有走流程引擎而是直接 update,二次开发时就要自己加权限校验。
订单合同管理的相关逻辑比较繁琐。合同编号的生成规则一般在工具类里集中管理,测试时输入多份订单看看编号是否有重复。如果有并发场景,可以写个小脚本同时提交两张订单,观察编号是否冲突。源码里如果没有用数据库锁或唯一索引,编号重复的概率不小,这个是很容易被忽略的隐患。
服务支持管理这条线还要验证工单的状态流转是否和 OA 审批打通。很多 OA+CRM 系统里,工单提交后会生成一条待办事项,这个待办是否同步到内部聊天工具的提醒里,是本系统集成度的试金石。测试时提交一张工单,然后分别登录另一个账号,看消息中心有没有推送提醒,没有推送的话要查一下消息订阅和 WebSocket 的绑定关系。
3.4 日志排查与慢 SQL 定位
部署完成只是开始,运行期间的问题要靠日志和慢查询来定位。Tomcat 的 catalina.out 只记录框架层面的异常,业务日志一般在应用自己的 log 目录。遇到 CRM 列表页加载慢,先打开 MySQL 慢查询日志:
SET GLOBAL slow_query_log = 'ON'; SET GLOBAL long_query_time = 1; SHOW VARIABLES LIKE 'slow_query_log_file';开启慢查询日志后,重新跑一遍 CRM 列表页,再查看慢日志文件里记录的 SQL。常见问题集中在客户列表的分页查询没有走索引,或者关联了过多的 子查询。解决方向是给 owner_id、create_time 这类高频过滤字段加联合索引,同时拆掉不必要的关联查询。注意慢查询日志会持续累积,定位到问题后要及时关闭,避免磁盘空间被撑满。
4. 内部聊天工具与控件集成:消息通路、ActiveX 机制与降级方案
4.1 为什么 OA 里会有 .cab 和 .crx 文件
很多人第一次看到这套源码里的 .cab、.crx 会以为下错包,实际上企业内部 OA 大量使用这类控件做浏览器能力扩展。.cab 是 IE 内核的 ActiveX 打包格式,像 ntkoWebSign.cab 就是用来在网页里调用本地签名硬件或生成电子签章的;OfficeControl.cab 则让浏览器能在不安装 Office 的情况下调用本地组件编辑文档。.crx 是 Chrome 扩展,通常用来替代 ActiveX 做类似能力。
这里有个选型上的历史包袱:ActiveX 只能在 IE 或兼容模式下运行,Chrome 从 45 版本后彻底移除了 NPAPI 支持,所以很多老控件在 Chrome 里点了没反应。源码里包含 crx 文件,说明这套系统已经在向 Chrome 扩展迁移。部署时如果用户坚持用 Chrome,就得手动加载 crx,并确保控件内部的本地消息通道和 Web 页面协议一致。
实际部署中,最常见的场景是浏览器自动阻止未签名控件的下载。HTML 页面里引用 cab 的写法一般是:
<object id="ntkoSign" classid="clsid:xxx-xxx" codebase="components/ntkoWebSign.cab#version=3,0,0,1"></object>classid 是控件的全局唯一标识,codebase 指向 cab 文件的位置和版本号。浏览器加载这个 object 标签时,会先检查本机是否已注册相同 classid 的控件,没有就下载并注册。这个过程在高版本 IE 里默认被拦截,需要把站点加入受信任区域并降低安全级别。如果版本号写错,浏览器会反复下载但始终不生效。
4.2 聊天工具的消息通路与数据表设计
内部聊天工具的核心不在界面,而在消息服务和数据表。常见做法是维护一张 message 表,字段包含 sender_id、receiver_id、msg_type、content、send_time、is_read。一对一聊天查这张表就能搞定,群聊需要额外一张 chat_group 和 group_member 表。如果源码用了 WebSocket,服务端启动时还要注册一个端点:
@ServerEndpoint("/chat/{userId}") public class ChatEndpoint { private static ConcurrentHashMap<String, Session> onlineUsers = new ConcurrentHashMap<>(); @OnOpen public void onOpen(Session session, @PathParam("userId") String userId) { onlineUsers.put(userId, session); } @OnMessage public void onMessage(String message, Session session) { // 解析消息头,按 receiverId 转发 String receiverId = parseReceiver(message); Session target = onlineUsers.get(receiverId); if (target != null) { target.getBasicRemote().sendText(message); } } }这段代码的逻辑是:每个用户登录后建立一条 WebSocket 连接,服务端用 ConcurrentHashMap 维护在线用户和 Session 的映射;收到消息时解析出接收人 ID,查到对方的 Session 后直接把消息推过去。注意 ConcurrentHashMap 要放在静态变量里,否则每个连接都是新实例,就找不到人了。
消息类型建议用 JSON 区分 text、image、file。图片和文件消息先上传到附件目录,再在 content 里放 URL,接收端按类型渲染。源码里如果只实现了文本,二次开发时把消息处理逻辑拆成策略类就行。我习惯把消息体设计成:
{ "msgType": "text", "content": "合同审批已通过", "senderId": 1001, "receiverId": 1002, "timestamp": 1736789012, "taskLink": "/oa/approval/detail?id=558" }taskLink 字段是 OA 集成聊天工具的关键。员工在聊天里收到消息后,直接点击链接就能跳到审批详情页,这条链路打通后,内部聊天工具才真正和 OA 融合,而不是一个孤立的 IM。设计消息表时别忘给 taskLink 加索引,不然后续做消息搜索时性能会很差。
4.3 消息推送与离线消息补发机制
聊天工具还有一个容易踩坑的点:用户离线时消息怎么办。如果只靠 WebSocket 在线转发,离线用户重新登录后会发现消息丢失。常见做法是消息落库的同时增加一个 pending 状态字段,用户登录时先查询所有 pending 消息并推送,再更新为已读。
SELECT * FROM message WHERE receiver_id = #{userId} AND is_read = 0 ORDER BY send_time ASC LIMIT 50;这条查询每次登录时执行,把未读消息按时间顺序拉出来。注意这里要加 LIMIT,否则消息积压多了会把接口拖垮。另外,群聊里的未读数不能只靠 message 表统计,因为同一条群消息要分别记录每个成员的已读状态,建议单独建一张 group_message_read 表来维护。
如果聊天模块走的是 Http 轮询而不是 WebSocket,那客户端的轮询间隔要设置合理。间隔太短会打崩服务端,太长消息延迟明显。一般我建议轮询间隔设在 15 到 30 秒之间,同时配合一个长期连接的 WebSocket 做实时消息,两条通道互补,避免在弱网环境下反复断开重连。
4.4 控件注册失败时的降级方案
控件装不上的概率非常高,尤其是 IE 被禁用的机器。现象是页面提示 ActiveX 未安装或对象创建失败。原因通常是控件的 codebase 指向的 cab 版本号不匹配、或浏览器安全级别阻止了未签名控件的下载。解决路径顺着这条走:先用管理员身份运行一次 IE,把站点加进「受信任的站点」并将安全级别设为「低」,再刷新页面让浏览器弹出安装提示。如果还不行,打开开发者工具看 network 面板里 cab 请求是否返回 404。
更彻底的降级方案是放弃 ActiveX,改用 HTML5 上传加服务端转换。比如电子签章模块,前端用 Canvas 收集手写轨迹,后端用 Java 把轨迹合成 PNG 再盖到 PDF 上。这样能彻底摆脱浏览器限制,但代价是失去硬件证书绑定那层安全能力。对内部培训系统来说通常够用。
设计降级方案时,我的做法是抽象一层控件接口,把 ActiveX 调用和 HTML5 实现都放到接口后面。页面里先检测浏览器类型,是 IE 就走 ActiveX,是 Chrome 就走 HTML5 上传。接口设计上要保证两套实现返回的数据结构一致,这样上层的审批流程代码完全不用改。这个抽象看起来很费功夫,但能让你在后续维护中少掉一半头发。
5. 避坑指南:OA+CRM 部署与二次开发最容易翻车的五个点
5.1 登录页能开,一点 CRM 菜单就 404
现象是输入账号密码后正常进入首页,但点击 CRM 客户管理菜单跳转到 404 页面。原因是 CRM 模块部署子路径和源码里配置的上下文路径不一致,最常见的是把 CRM 单独打成 war 包部署在 /crm 下,但主系统页面写死 /CRM 大写路径,Tomcat 的 URL 大小写敏感直接 404。解决方法是统一部署路径,或者用 Nginx 做 location 忽略大小写匹配。
另一个隐蔽原因是前端菜单里写的 URL 带了项目名,比如 /oa/crm/customer/list,但 CRM 模块实际部署在 ROOT 下,访问路径变成了 /crm/customer/list。这种问题在浏览器地址栏里一眼能看出来,地址和实际路径对不上就顺着改菜单配置文件。菜单配置一般在数据库的 sys_menu 表里,找出来统一改成相对路径,比逐个改 JSP 高效。
5.2 数据库导入时报 Incorrect string value
现象是执行 CRM 示例数据脚本时,包含中文或生僻字的 insert 语句报错。原因是建库时字符集用了默认的 latin1,或者建表语句里没指定 charset。解决方法是先 drop database,再用 utf8mb4 重建,最后导入;已经导入一半的库不要继续追加脚本,清库重导比逐条改表快得多。
还要注意 MySQL 的 character_set_server 全局变量。即使建库时指定了 utf8mb4,如果服务器默认排序规则是 utf8mb4_unicode_ci,在某些版本的存储过程里还会出现字符集不一致的告警。稳妥做法是统一指定排序规则,建库语句里把 COLLATE 也写全。导入完成后执行 SHOW CREATE TABLE 检查几个关键表的字符集,防止有表漏掉。
5.3 Office 控件装了还是不能在线编辑
现象是点击编辑文档,浏览器弹出是否运行 OfficeControl,确认后页面仍然空白或报脚本错误。原因通常是 Office 组件没装全,或者 64 位 Office 与 32 位 ActiveX 控件之间存在桥接问题。解决方法是装 32 位 Office,并保证浏览器进程是 32 位;同时把组件目录加进防病毒软件白名单,否则控件被杀软拦截会导致注册表写入失败。
我遇到过一个很刁钻的情况:IE 里能打开 Office 控件,Chrome 里点了没反应。查了半天发现 Chrome 版本是 88,早已不支持 NPAPI,虽然页面里加载了 crx 扩展,但扩展没有正确注册到本地。解决方法是手动打开 chrome://extensions,开启开发者模式,加载已解压的扩展目录 ntkoplugins.crx 对应的文件夹,而不是直接拖 crx 文件安装。
5.4 手机端页面被放大、表格溢出
现象是用手机访问 OA 首页正常,但 CRM 客户列表横向滚动两屏,页面能捏合缩放。原因是 JSP 页面缺少 viewport 标签,或者表格用了固定宽度。解决方法是统一在 head 里加<meta name="viewport" content="width=device-width, initial-scale=1.0">,表格外层套 class="table-responsive" 的 div。这个 Bootstrap 类会让表格在窄屏下变成可横向滚动的容器,不再撑破页面。
另一个问题是移动端点击下拉框时选项被遮挡,通常是日期控件或下拉框的 z-index 低于导航栏。排查时用手机浏览器开发者工具查看元素层级,把弹层容器的 z-index 调高即可。注意自适应手机不只是 CSS 适配,还要验证手机端登录、审批、客户编辑这些高频操作能不能点到位,很多系统桌面端正常、手机端一操作就报 JS 错误。
5.5 聊天消息发出去收不到
现象是 A 给 B 发消息,A 自己能看到消息记录,B 在线但收不到。原因是 WebSocket 建立连接的时机在登录页,但系统把 Session 和用户 ID 的关联绑定到了 HttpSession 上,HttpSession 过期后连接还在,转发时查不到在线用户。解决方法是消息服务维护独立的心跳机制,定期从 token 解析用户 ID 并刷新在线映射,服务端每 30 秒发一次 ping,客户端失去响应就重建连接。
如果是离线消息丢失,还要检查消息落库和 WebSocket 推送的先后顺序。正确的顺序是先落库再推送,因为推送失败时消息还在库里,用户重新登录能补发;如果先推送再落库,推送目标刚好不在线,这条消息就永久丢了。看源码时注意消息处理逻辑里的 try-catch,如果落库被包在推送之后的代码块里,顺序就得调整。
5.6 记住密码功能越权
最后说一个容易被忽略的安全问题。登录页的记住密码功能如果实现得不规范,会把用户名和密码明文存在 cookie 里,换台电脑登录就能看到。正规做法是只存一个加密的 token 或票据,服务端记录 token 对应的用户 ID 和过期时间。二次开发时如果动了登录逻辑,一定要检查 rememberMe 的实现方式,别为图省事把敏感信息直接塞进 cookie。
6. 二次开发切入点:把 CRM 状态流转做成可配置的工作流
拿到这套源码后,最值得动手改的地方不是界面,而是 CRM 里销售机会的状态流转。源码一般会写死状态枚举,比如 0 线索、1 跟进、2 报价、3 成交。这种写法在状态只有四五个时够用,但一旦业务上要求加一个「合同审批中」状态,就得动表结构、改枚举、换前端下拉选项,处处是坑。
我的做法是先抽一张 workflow_config 表,把状态流转规则搬进数据库:
CREATE TABLE workflow_config ( id INT PRIMARY KEY AUTO_INCREMENT, module VARCHAR(32) NOT NULL COMMENT 'crm/oa/chat', current_status INT NOT NULL, next_status INT NOT NULL, role_id INT NOT NULL COMMENT '允许执行流转的角色', action_name VARCHAR(64) NOT NULL );然后把业务代码里的 if-else 状态判断改成查配置表再校验角色权限。注意不要直接删掉原来的状态字段,而是在原有 status 旁边加一个 allowed_action 字段,过渡期内由数据库配置决定按钮是否显示。改动完成后验证路径是:新增一条「合同审批中」配置,给销售经理角色配权限,再登录普通销售账号确认该按钮置灰。这样既保留原逻辑兜底,又让业务流程可配置。
动作按钮的渲染也要跟着调整。前端拿到 allowed_action 列表后,动态生成按钮,而不是写死几个按钮在页面上。这个改动涉及的机会列表和详情页虽然多,但胜在逻辑统一。列表页的按钮判断可以抽成一个自定义标签函数,传状态和角色进去,返回可执行动作。从效果上讲,一次改动换来后续加状态不再动 JSP,是笔划算的投入。
从那以后我每次接手 OA 类源码,都会强制先梳理状态字段和权限模型的耦合度再动手改界面,避免需求一变就要翻遍整个模块的 if-else。这套源码里 CRM 和聊天工具的水平不算新,但正好适合作为你打通 OA 二次开发全流程的练手样本。希望帮到你。
本文还有配套的精品资源,点击获取