简介:本资源为《通达OA二次开发手册》PDF电子书,面向具备一定编程基础、需要对Office Anywhere网络智能办公系统进行定制开发的技术人员,可帮助读者解决从环境搭建、参数配置到模块创建等二次开发环节中的实际问题。手册围绕2015版V8.1系统展开,重点涵盖PHP/MySQL开发环境配置、OfficeFPM与OfficWeb等关键参数调整、auth.inc.php及conn.php等核心文件作用说明,并系统梳理了phpMyAdmin安装使用与数据库管理方法,适合OA运维人员、企业信息化开发者及PHP工程师参考学习。资源包共1个PDF文件,大小约188KB,内容紧凑、便于按章节查阅;已有88人学习下载。通过学习,读者可快速掌握通达OA的目录结构与开发流程,了解用户认证、权限控制、数据库连接等关键实现要点,为后续创建自定义模块、扩展系统功能打下基础。
1. 通达OA二次开发手册:这本PDF到底在教你怎么改OA,而不是怎么用OA
做过企业内部系统对接的工程师,八成都有过这样的经历:公司上了通达OA,用了两年发现审批流和ERP对不上,数据要靠人工导,表单表单改个字段要找厂商报价,改个小需求动辄几千块。这时候你翻到这本「通达OA二次开发手册.pdf」,第一反应是把它当说明书看,结果翻开发现里面根本没有讲怎么点鼠标。这本手册真正讲的,是你如何站在通达OA的PHP源码和数据库之上,把工作流、表单、外部数据对接这些东西改造成自己的东西。它适合的不是行政和文员,而是负责企业系统集成的后端开发,以及想摆脱被厂商锁死的技术负责人。
我一贯的看法是:通达OA和老版本的PHP源码结构,是它二次开发最友好的时期。手册的价值不在于你照着敲一遍,而在于它告诉你OA里哪些表是干什么的、哪些脚本在哪个时机被调用、哪些目录可以安全地放手写代码。先把这本手册吃透,再谈别的。
2. 再看这本手册的成色:它帮你定位了源码、数据库和调用时机
2.1 手册里的核心不是功能列表,而是代码所在的位置
通达OA的安装目录结构有一个规律,绝大多数版本里,webroot是你的web服务根目录,里面按模块分目录:general/放的是日常办公模块,ispirit/是即时通讯和门户相关,inc/是公共函数和数据库连接类,module/是后来的模块化扩展目录。这本手册在前面的章节里,几乎就是在带着你认这些目录——哪些文件是入口,哪些文件被include进来,哪些文件只被ajax调用而不直接渲染页面。
我拿到手册之后干的第一件事,不是从头读,而是先看了它附录里的数据字典。通达OA的表结构很稳定,核心表就那么几十张,很多系统对接工作根本不需要去啃全部表,把workflow_开头的系列表和sys_开头的系列表研究透,基本就能覆盖大部分需求。《通达OA二次开发手册》这种文档,厂商一般会在用户服务包里附带,但网络上流传的PDF往往是最初几版。早期版本的手册用过一段时间之后你会发现,它写的很多路径和函数在最新版里稍微有变化,但整体调用链没有变过:表单提交 → PHP脚本接收 → 读写数据库 → 跳转或返回JSON。
这里要提醒新接触的人一个心态问题:不要试图把手册里的每一个文件都弄懂。手册是地图不是教材,你只需要在接到需求的时候,能顺着目录找到对应的脚本,能根据手册里给的数据库字典找到字段,这就已经完成了70%的工作。
2.2 顺着手册找到的PDO封装和外部调用方式
通达OA的数据库层经过了多次演变。老版本直接用mysql_connect,后来迁到PDO,但为了方便二开人员,它在inc/下保留了若干个常用PHP文件。手册里值得背下来的,一个是数据库连接如何被二次开发脚本复用,另一个是外部PHP脚本如何引入OA的权限验证。
看手册的时候注意一个细节:它明确提到了二次开发脚本要放到哪些目录里才不会被系统更新覆盖。常见的做法是放到webroot/inc/或者webroot/general/下新建的自定义目录里,以td_开头命名文件。这样做的原因是OA的在线升级通常只覆盖官方模块目录里的文件,自定义文件名的文件不容易被误覆盖。这是手册里最值钱的经验之一,很多新手不看手册,直接把文件命名为index.php放到根目录或者官方同名文件里,升级一次就翻车一次。
我实际按照手册走通的一条调用链路是这样的:外部系统拿工号和一个token参数,请求我们自己写的PHP接口,接口里先验证OA用户的session或者token,然后include手册里提到的那个数据库类,直接执行SQL。走这条路的前提就是你得清楚OA的session验证函数从哪里进,以及工作流表里run_id、flow_id是怎么关联的。手册里都有,关键是你有没有耐心把它串起来。
3. 把手册落成代码:搭建自己的二次开发脚手架
3.1 最小可用的本地开发环境
通读手册之后,马上要做的不是写业务逻辑,而是搭一个和线上差不多的本地环境。通达OA本身是PHP写的,最稳的本地运行方式是Apache + PHP,而不是Nginx。原因很简单:OA里很多功能用到了.htaccess和基于$_SERVER['SCRIPT_FILENAME']的逻辑,Nginx下你得额外改try_files,Apache则基本零配置就能跑。
我常用的本地环境组合,是Windows下用官方安装包自带的环境直接跑起来,然后用手册里的目录结构做开发。如果你非要用Docker,有一个现成可走的路径:
docker run -d --name oa-dev -p 8080:80 -v D:\workspace\oa\webroot:/var/www/html php:7.4-apache镜像只是基础PHP环境,跑起来之后你还得把通达OA的源码完整拷入/var/www/html,然后进容器里把PHP扩展打开,特别是mysqli和mbstring:
docker exec -it oa-dev docker-php-ext-install mysqli mbstring docker exec -it oa-dev a2enmod rewrite docker restart oa-dev这一段里最关键的参数是-v把宿主机目录挂载进去,这样你在本地改代码,容器里立刻生效,不需要重新打包镜像。PHP版本不要选8.x,通达OA旧版的源码里很多写法对PHP8不友好,7.4是最兼容的版本。这个选择后期会帮你省大量报错排查时间。
本地环境跑起来之后,验证顺序是:先把OA自带的登录页面打开,确认数据库连接正常,然后用管理员账号进去跑一遍首页。如果这一步通过,说明手册里指的这个源码树在你的环境里成立,后面改文件才有基准点。
3.2 按手册思路写第一个表单扩展脚本
工作流表单是通达OA二开需求最多的地方。需求通常是这样的:OA里的表单提交完了,要把数据同步到公司自己的业务系统,或者从业务系统读数据回填到OA表单。手册给出的推荐做法,是在流程设计器里绑定自定义脚本函数,有函数名为流程_xxx的约定,脚本被放在工作流相关目录下。
我第一版的需求是三通的:报销表单提交后,把金额和部门写到本部门一个共享Excel里,当时的做法就是写一个外部PHP接口,让OA表单在某个节点触发时调用这个接口。
下面是当时对照手册里数据字典写出来的核心片段,逻辑很简单:
<?php // 引入OA数据库操作类 require_once("inc/td_conn.php"); // 连接数据库 $db = new td_conn(); $flowId = intval($_GET['flow_id']); $runId = intval($_GET['run_id']); // 从工作流主表取出当前待办信息 $sql = "SELECT a.run_name, b.user_name, b.user_id FROM workflow_run a LEFT JOIN workflow_run_log b ON a.run_id = b.run_id WHERE a.run_id = {$runId} AND b.flow_id = {$flowId}"; $res = $db->query($sql); if ($res && $row = $db->fetch_array($res)) { // 业务逻辑:拼接发送到外部API $postData = [ 'run_name' => $row['run_name'], 'approver' => $row['user_name'], 'run_id' => $runId ]; // 用curl转发到自己的数据总线 $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, "http://your-biz-api.local/syncOa"); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($postData)); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $response = curl_exec($ch); curl_close($ch); echo json_encode(['code' => 0, 'msg' => 'sync ok']); } else { echo json_encode(['code' => 1, 'msg' => 'run not found']); } ?>这段代码里有三个地方是从手册里抄下来的关键点:第一,inc/td_conn.php这个路径是你自定义连接类的命名方式,放到这个目录下可以复用OA原有的数据库配置;第二,workflow_run表存的是流程实例的总体信息,workflow_run_log表存的是每一步审批日志,两者通过run_id关联;第三,由于UA和表单post里的中文默认是UTF-8,但老版本OA的库可能是GBK,所以你后续接到数据出现乱码,先查表字符集,再猜别的。
光写这样一段脚本还不够,你还得在OA里配一个“外部调用菜单”,否则没人能访问到你这个接口。最常见的配法是登录管理员账号,在系统管理里加一个菜单,菜单URL指向你的PHP文件并且带上参数。这也是手册里专门有章节讲的内容——外部接口和菜单、权限的挂钩。
3.3 手册里隐含的参数说明:跑通最小脚本后,先改这三处
第一个要改的是数据库连接方式。不要在你的代码里自己new mysqli,而是应该用OA自带数据库类,因为你一旦自己连,就要单独维护数据库账号密码,而且OA升级如果改表名,你自己的代码大概率挂掉。手册里有专门的章节描述了如何复用它的连接类,你只要在脚本里include("inc/td_conn.php")然后按原类里的查询接口执行SQL就行。
第二个要改的是字符集。如果你的业务系统需要拿到OA表单的中文内容,建议在连接数据库后先执行set names utf8,然后再做查询。手册没有把这个写到显眼位置,但它是最影响成果的隐含参数之一。
第三个要改的是文件编码格式。通达OA后端代码极多是GBK编码,你新写的PHP文件如果以UTF-8保存,一旦被include进GBK编码的页面里,轻则页面乱码,重则导致PHP直接报“Cannot modify header information”。所以手册教你的所有新文件建议都用与OA当前字符集一致的方式处理。
4. 数据对接与报表扩张:手册里最有价值的表与SQL实战
4.1 常用核心表:从手册页到实际数据的距离只有一条SQL
做通达OA二次开发,绕不开的就是几张表单相关表。手册里有一页半画了表关系,我建议你拓下来贴在工位上。核心表大致如下:
| 表名 | 作用 | 关键字段 |
|---|---|---|
| workflow_run | 工作流实例表,每条审批流程在这里有一条主记录 | run_id, flow_id, run_name, begin_time |
| workflow_run_log | 流程日志表,记录了每一步审批动作和审批人 | run_id, flow_id, user_id, user_name, log_time |
| workflow_report | 表单数据表,存的是流程表单里的实际字段内容,以fieldname为键 | run_id, fieldname, fieldvalue |
| user | 用户表,所有OA账号都在这 | user_id, user_name, byname, dept_id |
| sys_dept | 部门表 | dept_id, dept_name |
有一个容易踩坑的地方是workflow_report表,它的结构是典型的行式存储,一个表单里的所有字段值不是一行一条记录,而是一个字段一行。查询的时候,需要一个行转列的过程。手册里给了很典型的一条SQL来把表单数据横过来。我当时做报表功能时用的SQL和手册思路一致:
SELECT r.run_id, r.run_name, MAX(CASE WHEN rf.fieldname = 'bx_je' THEN rf.fieldvalue END) AS amount, MAX(CASE WHEN rf.fieldname = 'bx_yy' THEN rf.fieldvalue END) AS reason, MAX(CASE WHEN rf.fieldname = 'bx_lx' THEN rf.fieldvalue END) AS expense_type FROM workflow_run r LEFT JOIN workflow_report rf ON r.run_id = rf.run_id WHERE r.flow_id = 40 GROUP BY r.run_id, r.run_name ORDER BY r.begin_time DESC;执行之后,得到的就是一个符合直觉的二维表格:每一行是一条报销单据,amount是报销金额,reason是报销原因,expense_type是费用类型。如果你的流程里表单字段名和这个SQL里的bx_je不一致,去workflow_form和workflow_report里查一下实际字段名就可以。
这里有个值得重点说的坑:fieldvalue里的值都是字符串类型,所以如果你要按金额排序或者做汇总统计,一定要先做类型转换,否则按字符串排序的10会比9小。手册里没提这一点,但真实做报表时十有八九会遇到。
4.2 外部系统通过API读OA数据:用脚本和字段映射替代人工导出
常见的企业集成场景是HR系统人员同步到OA。OA的user表是标准的,里面user_id是主键,user_name是姓名,byname是登录名,pwd是密码hash。你自己的HR系统里有工号、姓名、部门三个字段,要做的是把这三者对应到OA的user表里。
我在实际做过的一次对接里,遇到过byname和HR系统的账号不是同一套规则的问题。手册里介绍了如何调用用户管理模块的接口去创建账号,但更快速落地的方式是直接用SQL插入或者更新。核心逻辑如下:
<?php require_once("inc/td_conn.php"); $db = new td_conn(); $hrData = [ // 模拟从HR接口读取的数据 ['employee_no' => '1001', 'name' => '张三', 'dept' => '研发部'], ['employee_no' => '1002', 'name' => '李四', 'dept' => '市场部'] ]; foreach ($hrData as $emp) { // 判断用户是否存在 $checkSql = "SELECT user_id FROM user WHERE byname = '{$emp['employee_no']}'"; $db->query($checkSql); if ($db->num_rows() == 0) { $insertSql = "INSERT INTO user (user_name, byname, dept_id, pwd) VALUES ('{$emp['name']}', '{$emp['employee_no']}', 1, md5('初始密码123'))"; $db->query($insertSql); } else { // 更新姓名和部门,示例只做简单演示 $updateSql = "UPDATE user SET user_name = '{$emp['name']}' WHERE byname = '{$emp['employee_no']}'"; $db->query($updateSql); } } ?>这段代码里pwd字段触碰到一个边界,老版本通达OA的密码加密是简单的md5,新版改了算法,所以上面这段只适合演示思路,真实项目里用XML接口创建用户更稳。看到这里你应该明白,手册价值最高之处不是给你一套完全能跑的生产代码,而是告诉你哪些函数、哪些表的位置在哪里,让你能够自我排查。
4.3 自定义门户页面:把手册里讲的OA变量用起来
工作台门户是另一个高频二开点。很多公司想把自己系统里的待办事项直接放到OA首页,不想让员工多开一个web页面。手册里对门户定义的组件方式讲得很细,你可以写一个PHP页面,拿到当前登录者的user_id,然后从外部接口拉数据再渲染。
我在这里给出一个通配思路:在OA的webroot/general/下新建一个自定义目录,放一个PHP文件,顶部确保被OA的验证机制保护,核心是引入OA的公共文件:
<?php // 通过session判断是否已登录,未登录则跳回login require_once("inc/auth.php"); if (!$OA_USER) { header("Location: /login.php"); exit; } $currentUserId = $OA_USER['uid']; // 调用外部API取数据 $data = @file_get_contents("http://your-system/api/todo?uid={$currentUserId}"); if ($data) { $todos = json_decode($data, true); foreach ($todos as $item) { echo "<p><a href='{$item['url']}' target='_blank'>{$item['title']}</a></p>"; } } ?>这段脚本的运行逻辑比较直白:先用inc/auth.php完成登录验证,拿到当前用户ID后请求外部系统接口,再把数据渲染成HTML。你不需要用OA的模板引擎,直接echo一串带有样式的HTML就能嵌入首页。这个方式最大的坑是外部接口的响应延迟会拖慢OA首页打开速度,所以要在中间加缓存或者超时,建议的file_get_contents超时控制用stream_context_create去限定。
5. 二次开发避坑指南:手册里没写透的5个现场
5.1 上传文件后找不到附件,原因是attachment的表和物理文件对不上
现象:OA表单里提交的附件,在业务系统里同步数据时找不到文件路径。 原因:通达OA附件记录在fileattach表,物理文件存储在attachment目录下,文件名是file_id加扩展名,但在老版本里有一部分文件会因定时任务没有触发而没有被正确写入磁盘。 解决:先检查fileattach表里这条记录的file_id和attachment目录下实际文件是否存在,通常把缺失文件恢复后刷新缓存即可。手册里讲过附件管理机制,但没强调备份这个目录的重要性。
5.2 二次开发页面打开提示403,路径看起来没错
现象:按照手册新增的脚本放到自定义目录下,从OA内部菜单打开时提示无权限。 原因:OA的菜单权限表sys_menu里没有注册这条菜单,导致即使文件存在,模块权限拦截也会拒绝直访。 解决:登录管理员账户,在菜单管理里把该菜单授权给指定角色。或者在你的PHP脚本顶部引入inc/auth.php,做一个能放行的header判断。如果你不想新增菜单,只是临时测试,可以直接用完整URL访问并附加管理员session,绕过菜单权限,但生产环境不能这么干。
5.3 表单字段读出来全是空的
现象:用工作流表单自定义了很多字段,外部脚本用workflow_report查fieldvalue,结果字段值为空。 原因:你的SQL里的fieldname和实际表单里的fieldname不一致。表单设计器里看到的名称是中文标签,而存储在数据库里的是英文字段名,必须到workflow_form表里查fieldname。 解决:在开发前先执行一次全表查询,把流程ID之下的字段名列出来,再去写业务逻辑。手册里数据字典部分有一页列出了workflow_report的表结构,光是这一页就值回票价。
5.4 升级一次OA,二开代码全失效
现象:OA打补丁或者升级版本后,你写在general/目录和inc/目录里的文件有些被覆盖,有些提示数据库表不存在。 原因:官方升级包会覆盖原同名文件,包括部分公共函数库和表结构,如果你直接在官方文件上改动,升级必然被覆盖;如果你新建同名的表,升级时容易冲突。 解决:把所有二开文件集中放在自己创建的独立目录里,数据库层面只做新增表,不做修改官方表结构,并维护一份二开文件清单;升级前对照手册里的目录结构备份,升级后逐项核对。这是血泪经验,不遵守就会反复被坑。
5.5 中文乱码出现在外部系统里
现象:第三方系统通过HTTP接口接收OA数据时,发现所有中文都变成了问号。 原因:OA页面输出默认可能有多种编码,但数据库存储的编码和你的系统不一致,而HTTP传输时没有指定字符集,导致双方按不同编码解析。 解决:在PHP接口文件头部显式加header('Content-Type: application/json; charset=utf-8');,同时在数据入库前用mb_detect_encoding检测源字符串编码,按需转成utf-8后再处理。手册里对编码问题只在后面的FAQ里提了一句,实际是你所有外部对接里最容易翻车的地方。
6. 把二次开发手册变成团队资产:从个人会改到小组能维护
到这一步,其实你已经不再需要把手册翻烂了。你需要的是把手册里的知识沉淀成自己团队的内部文档和监控体系。
我的习惯做法是维护一个二开变更表,把每次对OA的改动按“文件路径、改动原因、涉及表、对应手册页码、是否兼容升级”记录在内部Wiki上。另一个做法是把线上OA的workflow_report、fileattach、user三张表的字段变更定期导出,和手册里的数据字典做diff,这样OA升级之后,手册失效的部分能第一时间发现,而不是等业务部门报错。
手册的最后几章还提到了调试方法,其中有用的一点是查看OA的系统日志和PHP错误日志。生产环境建议把display_errors关掉,但把log_errors打开,错误写到你自己的日志文件里。这样业务方的报错可以迅速定位,不用每次都在OA界面上干瞪眼。这一点配合上面的二开文件清单,内部维护OA的人即便换了一茬,也不至于把知识全部带走。
另一个我自己在用的进阶技巧,是把OA的数据层和业务层拆开。具体做法是,凡是要读OA数据给外部系统用的场景,不要直接让外部系统连OA库,而是你在OA服务器上提供一个只读接口,外部系统通过接口拿JSON。这样OA库的表结构变更,只影响接口本身,不会炸掉下游所有系统。
如果你能坚持做到这一步,那你已经不是在“用”手册,而是自己写出一本更符合公司现实情况的接手指南了。手册保住了你起步的底线,你自己的文档才决定二次开发能走多远。希望这篇梳理和里面的那些踩坑记录能帮到你,少走一点弯路。
本文还有配套的精品资源,点击获取