
简介《通达OA二次开发手册》面向具备编程基础、需要定制Office Anywhere网络智能办公系统的技术人员帮助其理解系统架构并完成功能扩展与业务适配。手册从开发环境搭建讲起涵盖OfficeFPM、OfficWeb、PHP与MySQL的参数配置及协同关系并逐一解析auth.inc.php、header.inc.php、common.inc.php、conn.php等核心文件的作用数据库管理部分则涉及phpMyAdmin安装使用、表结构分析与备份恢复策略还给出创建模块、建立目录、配置菜单与权限分配的完整流程。资源包为1个PDF文件约188KB内容紧凑、目录清晰便于按章节检索查阅。目前已有89人学习。读者可借此掌握从环境配置到模块编码的二次开发路径理解数据流转逻辑为系统定制与优化提供可落地的参考。1. 拿到一份 2015 版通达 OA 二次开发手册我为什么还留着它前阵子帮一家制造企业做 OA 流程改造对方系统是通达 OA 2015 版跑在 Windows Server 上PHP 5.3 加 MySQL 5.1 的老组合。运维丢给我一份《Office Anywhere 网络智能办公系统二次开发手册 V8.1.150425》说“就靠这个了”。我翻了两天发现这份文档虽然年代久远但它是目前能找到的、对通达 OA 底层文件结构和内置函数讲得最细的中文资料——没有之一。它覆盖了从 OfficeFPM 配置、核心 include 文件、phpMyAdmin 建库到 TD 类、PortalData 类、Workflow 相关类、utility 系列函数的完整参考。适合谁适合手上有通达 OA 存量系统、需要做模块扩展或流程定制的 PHP 开发者。不适合谁如果你用的是全新版本或者纯前端改皮肤这份手册的参考价值会打折扣。下面我按实际动手顺序把这份手册里真正能落地的部分拆开讲。2. 开发环境与核心文件先把黑匣子打开2.1 OfficeFPM、OfficWeb、PHP、MySQL 四者到底怎么串通达 OA 的运行架构不复杂但第一次接触的人容易被目录名搞晕。手册 1.3 节把四个组件的配置分开讲我按实际部署经验重新串一遍。OfficeFPM 是 FastCGI 进程管理器负责接收 OfficWeb 转发过来的 PHP 请求再交给 PHP 解释器执行。OfficWeb 是前端 Web 服务器监听 80 或自定义端口处理静态资源并转发动态请求。PHP 解释器负责执行代码MySQL 负责数据存储。四者的关系是浏览器 → OfficWeb → OfficeFPM → PHP → MySQL。常见做法是在MYOA\bin目录下能找到OfficeFPM.exe和OfficWeb.exe的配置文件。OfficeFPM 的配置里关键参数是listen监听地址和端口和php-cgi路径。OfficWeb 的配置里关键参数是DocumentRoot指向MYOA\webroot和FastCGI转发规则。提示改完 OfficeFPM 或 OfficWeb 配置后必须重启对应服务否则配置不生效。重启顺序是先停 OfficWeb再停 OfficeFPM启动时反过来。PHP 配置在MYOA\php\php.ini需要关注的参数参数建议值说明memory_limit256M通达 OA 部分模块加载大量数据128M 容易爆max_execution_time300报表导出、批量流程处理容易超时display_errorsOff生产环境必须关否则报错信息暴露路径error_log指定路径排查问题时打开定位到具体文件和行号upload_max_filesize按需附件上传大小限制默认 2M 往往不够MySQL 配置在MYOA\mysql\my.ini关键参数是max_connections默认 100并发高时调到 300和innodb_buffer_pool_size建议设为物理内存的 50%60%。2.2 四个核心 include 文件的作用与修改边界手册 1.4 节列了四个核心文件都在MYOA\webroot\inc\目录下。我逐个说清楚它们干什么、能不能改。auth.inc.php用户认证和权限控制。每次请求都会加载里面做了 session 校验、用户身份确认、模块访问权限判断。不建议直接改除非你完全清楚权限验证链路。我一般是在自己的模块里调用它提供的函数而不是修改它本身。header.inc.php页面头部包含文件输出 HTML head 部分、引入公共 CSS 和 JS。如果你想给所有页面加一个全局提示条可以在这里加但注意它会被所有模块加载改错了整个系统白屏。common.inc.php公共函数和常量定义。这是最常被引用的文件里面定义了系统路径常量、通用工具函数。可以追加自定义函数但不建议修改已有函数逻辑因为大量模块依赖它。conn.php数据库连接文件。负责建立 MySQL 连接、设置字符集、选择数据库。如果你需要在自己的模块里操作数据库直接include_once这个文件就能拿到连接资源。?php // 在自己的模块文件中引入核心文件 include_once inc/auth.inc.php; // 认证与权限 include_once inc/conn.php; // 数据库连接 include_once inc/utility_all.php; // 通用工具函数 include_once inc/utility_org.php; // 组织架构相关函数 // 此时 $connection 就是可用的 MySQL 连接资源 $sql SELECT * FROM user WHERE DEPT_ID 1; $cursor exequery($connection, $sql); while ($row mysql_fetch_array($cursor)) { echo $row[USER_NAME] . br; }这段代码的逻辑是先引入认证文件确保当前用户有权限再引入数据库连接文件拿到$connection然后引入工具函数文件以便使用exequery等封装函数。exequery是通达 OA 对mysql_query的封装内部会做错误处理和日志记录。参数说明第一个参数是数据库连接资源第二个参数是 SQL 语句。返回结果集游标用mysql_fetch_array逐行读取。注意通达 OA 2015 版使用的是mysql_*系列函数不是mysqli或PDO。如果你在 PHP 7 以上环境部署这些函数已被移除需要做兼容处理或降级 PHP 版本。3. 从零建一个模块目录、菜单、权限、编码3.1 模块目录结构与菜单注册的完整流程手册第三章讲得很简略只说了“建立模块目录”“创建菜单”“分配菜单权限”但没给完整示例。我按实际项目补全。通达 OA 的模块目录一般放在MYOA\webroot\general\下每个模块一个独立文件夹。比如你要做一个“设备巡检”模块目录结构如下MYOA\webroot\general\device_inspect\ ├── index.php # 模块入口 ├── manage.php # 管理页面 ├── save.php # 数据保存 ├── delete.php # 删除操作 └── style\ └── custom.css # 模块样式建好目录后需要在系统菜单表里注册。通达 OA 的菜单数据存在sys_function表中。常见做法是通过后台“系统管理 → 菜单管理”界面添加也可以直接写 SQL 插入。-- 在 sys_function 表中注册新模块菜单 INSERT INTO sys_function ( MENU_ID, -- 菜单唯一标识自增 MENU_NAME, -- 菜单显示名称 MENU_CODE, -- 模块代码对应目录名 MENU_URL, -- 入口文件相对路径 MENU_TYPE, -- 菜单类型1系统菜单 2自定义 IS_SYS, -- 是否系统内置0自定义 DEPT_ID, -- 所属部门0 表示全局 MENU_ORDER -- 排序号 ) VALUES ( 1001, 设备巡检, device_inspect, /general/device_inspect/index.php, 2, 0, 0, 50 );参数说明MENU_CODE必须和目录名一致系统通过它定位模块路径。MENU_URL是相对于webroot的路径。MENU_TYPE设为 2 表示自定义模块升级时不会被覆盖。MENU_ORDER控制菜单显示顺序数值越小越靠前。注册完菜单后还需要在权限表sys_menu_priv中给对应用户或角色分配访问权限。这一步在后台“系统管理 → 权限管理”里操作更稳妥直接写 SQL 容易漏掉关联字段。3.2 系统变量与数据库操作代码样例手册 3.4.1 节提到了“系统变量”但没展开。通达 OA 在common.inc.php中定义了一批全局变量常用的有变量名含义示例值$_SESSION[LOGIN_USER_ID]当前登录用户 IDadmin$_SESSION[LOGIN_DEPT_ID]当前用户部门 ID1$_SESSION[LOGIN_USER_PRIV]当前用户权限级别1$LOGIN_USER_NAME当前用户姓名张三$DEPT_ID当前部门 ID1在自己的模块里直接用这些变量做权限判断或数据过滤。比如只允许本部门用户查看本部门数据?php include_once inc/auth.inc.php; include_once inc/conn.php; include_once inc/utility_all.php; // 获取当前用户部门 ID $current_dept $_SESSION[LOGIN_DEPT_ID]; $current_user $_SESSION[LOGIN_USER_ID]; // 查询本部门设备巡检记录 $sql SELECT * FROM device_inspect_record WHERE DEPT_ID . intval($current_dept) . ORDER BY CREATE_TIME DESC; $cursor exequery($connection, $sql); $records array(); while ($row mysql_fetch_array($cursor)) { $records[] array( id $row[ID], device_name $row[DEVICE_NAME], inspector $row[INSPECTOR], create_time $row[CREATE_TIME], status $row[STATUS] ); } // 输出 JSON 供前端渲染 header(Content-Type: application/json; charsetutf-8); echo json_encode($records);逻辑说明先引入认证文件确保用户已登录然后从 session 中取部门 ID 和用户 ID。SQL 中用intval()强制转换部门 ID 为整数防止 SQL 注入。exequery执行查询后用mysql_fetch_array遍历结果集组装成关联数组最后以 JSON 格式输出。参数说明$connection来自conn.phpexequery的第二个参数是完整 SQL 语句。注意字符集问题通达 OA 默认使用 GBK 编码如果数据库和页面编码不一致中文会出现乱码。提示通达 OA 2015 版默认数据库字符集是 GBK新建表时如果不指定字符集可能继承为 latin1导致中文存储乱码。建表时显式指定DEFAULT CHARSETgbk。4. 内置类库与函数别重复造轮子4.1 TD 类、PortalData 类、ExcelReader 类的实际用法手册第四章列了三个核心类我挑实际项目里用得最多的说。TD 类封装了系统级操作比如获取系统参数、操作缓存、发送消息。成员函数包括get_sys_para、set_sys_para、cache_attach_para等。实际用法?php include_once inc/auth.inc.php; include_once inc/conn.php; include_once inc/td.class.php; // 引入 TD 类 // 获取系统参数附件上传大小限制 $max_upload TD::get_sys_para(MAX_UPLOAD_SIZE); echo 系统允许的最大上传大小 . $max_upload . MB; // 设置系统参数 TD::set_sys_para(MAX_UPLOAD_SIZE, 50);参数说明get_sys_para接收一个参数名返回对应的系统参数值。set_sys_para接收参数名和新值写入sys_para表。注意修改系统参数会影响全局建议在后台管理界面操作代码里只做读取。PortalData 类用于门户数据块的查询和渲染。如果你的模块需要在门户首页展示数据用这个类比直接写 SQL 更规范。成员函数包括get_data、get_list等。ExcelReader 类读取 Excel 文件内容。手册 4.3 节列了成员函数实际用法?php include_once inc/auth.inc.php; include_once inc/excelreader.class.php; $reader new ExcelReader(); $reader-open(C:/temp/device_list.xlsx); $sheet $reader-getSheet(0); // 获取第一个工作表 // 从第二行开始读取第一行是表头 for ($i 2; $i $sheet-getRowCount(); $i) { $device_name $sheet-getCell(1, $i)-getValue(); // 第一列 $device_code $sheet-getCell(2, $i)-getValue(); // 第二列 // 插入数据库... } $reader-close();参数说明getSheet(0)获取第一个工作表索引从 0 开始。getCell(列号, 行号)获取单元格列号和行号都从 1 开始。getValue()返回单元格内容。注意 ExcelReader 对.xlsx格式支持较好.xls老格式可能解析异常。4.2 utility 系列函数文件、组织、短信三大高频场景手册第五章列了 utility 系列函数数量很多我按使用频率挑三组讲。文件操作utility_file.phpupload、delete_attach、attach_real_path、attach_url这几个最常用。上传附件的标准流程?php include_once inc/auth.inc.php; include_once inc/conn.php; include_once inc/utility_file.php; // 上传附件 $attach_id upload(device_inspect, $_FILES[attach_file]); if ($attach_id) { // 获取附件真实路径 $real_path attach_real_path($attach_id, device_inspect); // 获取附件访问 URL $url attach_url($attach_id, device_inspect); echo 上传成功附件 ID . $attach_id; } else { echo 上传失败; }参数说明upload第一个参数是模块标识对应附件存储子目录第二个参数是$_FILES数组中的文件项。返回附件 ID失败返回 false。attach_real_path返回服务器上的物理路径attach_url返回浏览器可访问的 URL。注意附件 ID 是编码后的字符串不是纯数字不要试图用intval转换。组织架构utility_org.phpGetUserNameById、GetDeptNameById、GetPrivNameById、is_dept_parent这几个在权限判断和数据显示时高频使用。?php include_once inc/auth.inc.php; include_once inc/utility_org.php; $user_id admin; $user_name GetUserNameById($user_id); // 返回用户姓名 $dept_id 1; $dept_name GetDeptNameById($dept_id); // 返回部门名称 $priv_id 1; $priv_name GetPrivNameById($priv_id); // 返回权限级别名称 echo 用户{$user_name}部门{$dept_name}权限{$priv_name};参数说明GetUserNameById接收用户 ID字符串返回姓名。GetDeptNameById接收部门 ID返回部门名称。GetPrivNameById接收权限级别 ID返回权限名称。这些函数内部会查缓存频繁调用不会造成明显性能问题。短信通知utility_sms1.php / utility_sms2.phpsend_sms发内部短信send_mobile_sms发手机短信。流程审批通过后通知下一步处理人就用这个。?php include_once inc/auth.inc.php; include_once inc/utility_sms1.php; // 发送内部短信 $to_user user1,user2; // 多个用户用逗号分隔 $content 您有一条新的设备巡检任务请及时处理。; $url /general/device_inspect/index.php; send_sms($to_user, $content, $url);参数说明send_sms第一个参数是接收用户 ID多个用逗号分隔。第二个参数是短信内容。第三个参数是点击短信后跳转的 URL。注意短信内容长度有限制超过会被截断重要信息建议放在 URL 指向的页面里。5. 避坑与排查那些手册没写但一定会遇到的问题5.1 附件上传后找不到文件现象调用upload返回了附件 ID但attach_real_path返回的路径下没有文件。原因通达 OA 的附件存储目录是按模块和日期分级的upload函数内部会根据当前日期生成子目录。如果服务器时间不对或者模块标识拼写错误文件会存到意料之外的目录。解决先用attach_real_path打印出完整路径检查目录是否存在、是否有写入权限。Windows 环境下注意MYOA\attach目录的 IIS 或 Apache 用户权限。常见做法是给MYOA\attach目录 Everyone 读写权限内网环境或者指定运行账户的读写权限。5.2 中文乱码GBK 与 UTF-8 的反复横跳现象页面显示中文正常但写入数据库后变成乱码或者从数据库读出后显示问号。原因通达 OA 2015 版默认使用 GBK 编码但 PHP 文件可能保存为 UTF-8MySQL 连接字符集可能未设置。三者不一致就会乱码。解决统一编码。PHP 文件保存为 GBK 编码或用编辑器转换conn.php中确认有mysql_query(SET NAMES gbk)新建表时指定DEFAULT CHARSETgbk。如果必须用 UTF-8需要全链路改造工作量大不建议在存量系统上做。5.3 菜单注册后不显示现象sys_function表里插入了记录但后台菜单树里看不到。原因通达 OA 的菜单显示还依赖sys_menu_priv权限表和sys_function中的IS_SYS字段。如果IS_SYS设为 1系统内置但MENU_CODE与系统已有模块冲突菜单会被隐藏。解决确认IS_SYS设为 0MENU_CODE唯一且与目录名一致。然后在后台“系统管理 → 权限管理”中给当前用户角色分配该菜单的访问权限。直接查sys_menu_priv表确认有对应的MENU_ID和USER_ID或PRIV_ID记录。5.4 流程步骤中的表单字段取不到值现象在 Workflow 相关类中TworkForm或TworkRun获取表单字段时返回空。原因通达 OA 的流程表单字段存储在flow_run_data表中字段名是DATA_1、DATA_2这种编号形式不是表单上显示的中文标签。需要先通过flow_type和flow_id找到字段编号与标签的映射关系。解决查flow_form表获取字段定义或者用TworkForm::get_form_field()方法获取字段列表。常见做法是在流程设计器中导出表单结构对照字段编号写代码。5.5 升级或迁移后模块失效现象系统从旧版本升级或迁移到新服务器后自定义模块报错或无法访问。原因自定义模块的目录、菜单记录、权限记录、附件目录没有完整迁移。或者新服务器的 PHP 版本、MySQL 版本与旧环境不一致。解决迁移前备份MYOA\webroot\general\下所有自定义模块目录、sys_function和sys_menu_priv表数据、MYOA\attach下对应模块的附件目录。迁移后检查 PHP 版本兼容性特别是mysql_*函数是否可用。如果新环境 PHP 7需要安装mysql扩展兼容包或降级 PHP。6. 进阶用 Workflow 类做流程数据联动手册 4.4 节讲了TworkForm和TworkRun两个类但示例很少。我拿一个实际场景说设备巡检流程中巡检完成后自动更新设备台账表的“上次巡检时间”字段。思路是在流程的“转交后”触发点挂一个自定义 PHP 脚本通过TworkRun获取当前流程实例的表单数据提取设备编号和巡检时间然后更新台账表。?php include_once inc/auth.inc.php; include_once inc/conn.php; include_once inc/utility_all.php; include_once inc/workflow/inc/workflow.class.php; include_once inc/workflow/inc/workflow.run.class.php; // 获取当前流程实例 $run_id intval($_GET[RUN_ID]); $flow_id intval($_GET[FLOW_ID]); $workRun new TworkRun($flow_id, $run_id); // 获取表单字段值字段编号需对照 flow_form 表 $device_code $workRun-get_field_value(DATA_1); // 设备编号 $inspect_time $workRun-get_field_value(DATA_3); // 巡检时间 if ($device_code $inspect_time) { // 更新设备台账表 $sql UPDATE device_ledger SET LAST_INSPECT_TIME . addslashes($inspect_time) . , LAST_INSPECTOR . $_SESSION[LOGIN_USER_ID] . WHERE DEVICE_CODE . addslashes($device_code) . ; exequery($connection, $sql); // 写日志 add_log(设备巡检联动更新, 设备编号 . $device_code . 巡检时间 . $inspect_time); }逻辑说明先通过 URL 参数拿到流程实例 ID 和流程 ID实例化TworkRun。然后调用get_field_value按字段编号取表单值。拿到设备编号和巡检时间后更新台账表。addslashes做基本转义add_log写操作日志便于追溯。参数说明TworkRun构造函数接收流程 ID 和实例 ID。get_field_value接收字段编号DATA_1、DATA_2等返回字段值。字段编号与表单标签的对应关系在flow_form表中建议先在数据库里查清楚再写代码。验证方法在流程中提交一条测试数据然后查device_ledger表确认LAST_INSPECT_TIME是否更新。如果没更新先检查get_field_value是否返回了值再检查 SQL 是否执行成功。常见问题是字段编号写错或者流程触发点没挂对。提示Workflow 类的触发脚本建议放在MYOA\webroot\general\workflow\下的自定义目录中不要直接改系统文件否则升级时会被覆盖。从那以后我每次做通达 OA 二次开发都强制走一遍“先查 flow_form 确认字段编号 → 再写联动脚本 → 最后用测试流程验证”的流程省了很多返工。希望帮到你。本文还有配套的精品资源点击获取