
简介这是一套基于ThinkPHP框架开发的企业级开源OA办公系统源码面向PHP中级开发者、企业IT运维人员及数字化转型中的中小企业旨在提供可二次开发、低成本落地的协同办公解决方案。资源包共1887个文件涵盖640个PHP核心逻辑文件、462个HTML前端页面、326个JS交互脚本、156个GIF动效与84个Z压缩资源辅以CSS样式、SQL数据库脚本、配置文件及多格式字体资源完整呈现MVC分层架构与模块化设计特点压缩包大小为17.16MB。已有682人下载学习适合用于快速搭建人事、合同、财务、CRM等典型企业管理模块。读者可直接部署运行深入理解工作流引擎实现、RBAC权限控制、多级审批流程配置及ThinkPHP安全机制如防SQL注入、XSS过滤在真实OA场景中的集成应用。1. 项目概述为什么选择ThinkPHP来构建OA系统在信息化办公成为标配的今天一套稳定、灵活且易于二次开发的OA办公自动化系统对于许多中小企业和团队来说是提升协作效率、规范管理流程的刚需。市面上成熟的商业OA产品如泛微、致远等功能强大但价格不菲且定制化开发的门槛和成本较高。因此一个基于流行开源框架、代码清晰、架构合理的PHP开源OA系统就成为了许多技术负责人和开发者眼中的“宝藏项目”。我选择基于ThinkPHP来开发这套OA系统并非一时兴起。ThinkPHP作为国内最主流的PHP开发框架之一其优势在OA这类业务逻辑复杂、需要快速迭代的企业应用中体现得淋漓尽致。首先它提供了完善的MVC分层、ORM对象关系映射和丰富的内置功能如缓存、日志、验证器能让我们从重复的基础编码中解放出来专注于业务逻辑的实现。其次ThinkPHP拥有庞大的中文社区和丰富的学习资源这意味着在开发过程中遇到问题更容易找到解决方案或同行交流降低了团队的维护和后续开发成本。最后其约定优于配置的理念使得项目结构清晰规范非常有利于团队协作和代码的长期维护。这套开源OA系统的核心目标是打造一个“开箱即用按需扩展”的办公协同平台。它不仅要覆盖请假、报销、审批、公告等常规办公流程更要提供一个健壮、安全的底层架构让开发者能够在此基础上像搭积木一样快速集成新的模块或者与企业微信、钉钉等第三方平台对接从而满足不同组织的个性化需求。2. 系统核心架构设计与技术选型2.1 整体架构分层解析一个健壮的OA系统不能是“面条式”代码的堆砌清晰的分层架构是保证其可维护性和可扩展性的基石。在本项目中我采用了经典的多层架构思想并结合ThinkPHP的特性进行了适配。表现层View/Controller负责与用户交互。我使用了ThinkPHP原生的模板引擎进行渲染虽然现在前后端分离是趋势但对于OA这类内部管理系统服务端渲染在开发效率、SEO虽然OA不看重和初期快速原型构建上仍有优势。控制器Controller保持“瘦身”原则仅处理请求分发、参数校验和响应返回复杂的业务逻辑全部下沉到服务层。业务逻辑层Service/Logic这是系统的“大脑”。我将所有核心的业务操作如发起审批、计算假期余额、处理工作流引擎等封装在独立的Service类中。这样做的好处是业务逻辑高度内聚独立于数据存取和表现层无论是替换数据库还是改造前端界面业务核心代码都能保持稳定。例如一个LeaveApplyService会封装从验证规则、计算时长、到生成审批流的所有步骤。数据访问层Model基于ThinkPHP强大的ORMthink\Model构建。模型类不仅负责与数据库表映射我还利用模型关联来处理复杂的业务数据关系如一个审批单关联申请人、审批流程节点、审批意见等。同时将通用的数据查询和操作封装在模型的基础方法或仓库Repository模式中避免SQL语句散落在各处也便于进行统一的数据缓存优化。基础设施层包括缓存Redis、队列、文件存储、邮件发送等通用服务。我利用ThinkPHP的门面Facade和驱动机制将这些服务抽象成统一的接口。例如发送通知消息时业务代码只需调用Notification::send()底层可以根据配置自动选择邮件、企业微信或数据库站内信等不同驱动极大地提升了系统的可扩展性。2.2 关键技术组件选型理由1. ThinkPHP 6.x选择6.0及以上版本是因为它完全重构了核心遵循了PSR标准引入了中间件、依赖注入等现代PHP框架特性性能和对新PHP版本的支持更好。相较于5.16.x在长生命周期支持、代码规范和性能上更有保障。2. MySQL RedisMySQL作为主数据库存储所有业务关系型数据。Redis则承担多重角色作为缓存大幅减少对高频访问数据如组织架构、用户信息、系统配置的数据库查询作为队列驱动配合think-queue组件处理异步任务如批量发送邮件、生成统计报表避免同步操作阻塞主请求存储用户会话Session实现分布式部署下的会话共享。3. Workflow Engine工作流引擎OA的核心是流程审批。我没有选择集成一个庞大的外部工作流系统而是基于状态模式和责任链模式自己实现了一个轻量级、可配置的工作流引擎。通过数据库配置审批节点、处理人规则如直属上级、部门负责人、指定角色和流转条件引擎能动态驱动流程。这样既保持了灵活性又避免了过度设计。4. 前端技术栈考虑到团队技能栈和快速开发选择了Layui作为后台UI框架。它提供丰富的表单、表格、弹层组件能快速搭建出风格统一的管理界面。对于复杂的交互如流程设计器、甘特图则引入专门的JavaScript库如jsPlumb、ECharts进行集成。注意在技术选型上切忌盲目追求“新”和“全”。例如是否要采用前后端分离Vue/React对于初期项目如果团队前端资源有限采用服务端渲染的模板技术能更快落地。我的建议是核心是先跑通业务架构上为未来可能的分离留好接口如Controller统一返回JSON数据的能力待系统稳定、团队扩充后再做重构。3. 核心功能模块实现细节3.1 用户权限与组织架构管理这是所有企业应用的基石设计不好后期会“牵一发而动全身”。我采用了经典的RBAC基于角色的访问控制模型并进行了扩展以适配国内企业的树状组织架构。数据表设计user用户表包含基础信息。dept部门表使用parent_id字段实现无限级树状结构并记录leader_id部门负责人。role角色表如“员工”、“部门经理”、“HR”、“超级管理员”。user_role用户-角色关联表一个用户可属于多个角色。permission权限节点表存储“模块/控制器/方法”格式的规则如admin/user/add。role_permission角色-权限关联表。实现要点组织架构的递归处理在查询用户所属部门及其所有上级部门时使用递归函数或更高效的闭包表Closure Table设计来优化。我在这里写了一个DeptService提供了getUserAllParentDepts($user_id)方法用于快速获取用户的所有上级部门ID这在确定“部门负责人”等审批人时至关重要。权限验证中间件创建了一个Auth中间件在所有需要权限控制的路由中启用。该中间件会检查当前用户是否拥有访问当前路由对应权限节点的角色。权限节点通过注解或配置文件自动生成避免手动维护的遗漏。数据权限控制这是RBAC的延伸。例如部门经理只能查看本部门员工的请假单。我在Model查询中自动注入数据范围筛选条件。通过定义一个DataScope接口和不同的实现类如“本人数据”、“本部门数据”、“全部数据”在服务层根据用户角色动态附加查询条件。// 示例在审批单查询中注入数据权限 class ApprovalService { public function getList($params) { $query ApprovalModel::with([applicant, process]); // 注入数据权限作用域 $query $this-dataScope-apply($query, approval); // ... 其他查询逻辑 return $query-paginate(); } }3.2 工作流审批引擎的实现审批流是OA的“灵魂”。我设计了一个基于配置、状态驱动的流程引擎。核心数据表workflow流程定义表如“请假审批”、“报销审批”。workflow_node流程节点表关联workflow_id定义节点类型开始、审批、会签、条件分支、结束、节点名称、处理人规则表达式如dept.leader、role.HR、user.{id}。workflow_link节点流转线表定义从节点A到节点B的条件。approval_instance审批实例表每次发起申请生成一条记录记录当前节点、状态进行中、已通过、已拒绝、已撤回。approval_log审批日志表记录每个节点谁、在何时、做了何种操作通过、拒绝、转交以及审批意见。引擎运转流程发起流程用户提交申请系统根据workflow_id找到流程定义创建approval_instance并找到开始节点将实例状态指向第一个审批节点。处理人计算引擎解析当前节点的“处理人规则”表达式。这是一个关键且灵活的设计。我实现了一个AssigneeParser类它支持解析诸如dept.leader申请人的部门领导、role.${role_name}特定角色、user.${user_id}指定人以及previous.approver上一节点审批人等规则。通过动态计算确定当前待办事项应推送给谁。任务推送生成待办事项存入数据库并通过WebSocket或轮询方式通知处理人集成企业微信/钉钉时可推送应用消息。审批动作处理人进行操作同意/拒绝/转交。引擎根据workflow_link中定义的条件例如请假天数3天走分支A否则走分支B决定下一步跳转到哪个节点并更新approval_instance的当前节点和状态。同时记录详尽的approval_log。流程结束当实例到达“结束”节点更新状态为完成并可能触发后续动作如更新请假记录状态、调用财务系统接口等。实操心得处理人规则的解析是工作流灵活性的关键。务必将其设计为可扩展的插件化系统。初期可能只支持几种简单规则但预留好接口未来可以轻松加入“连续多级上级”、“审批人动态会签”等复杂规则。另外审批日志一定要记录完整包括操作前和操作后的数据快照这对于审计和问题排查是无价之宝。3.3 即时通讯与通知系统内部沟通的及时性直接影响办公效率。我设计了一个轻量级的站内信邮件第三方集成的复合通知系统。站内信WebSocket实现服务端Swoole/Workerman选用Swoole作为WebSocket服务器提供长连接服务。当用户登录系统时前端建立WebSocket连接服务端将连接与用户ID绑定存储在一个全局的fd-user_id映射表中实际生产环境需用Redis存储。消息分发当需要向某个用户发送通知时如新待办、审批结果业务代码调用NotificationService该服务会先持久化消息到数据库的message表保证消息不丢失然后根据用户ID查找对应的WebSocket连接fd通过Swoole Server推送消息。前端处理前端接收到WebSocket消息后在页面右下角弹出Toast提示并更新页面上的“未读消息”角标。邮件与第三方集成邮件使用phpmailer/phpmailer或ThinkPHP自带的邮件库配置SMTP服务。将邮件发送任务推送到Redis队列由后台进程异步执行避免阻塞Web请求。企业微信/钉钉封装了对应的SDK如EasyWeChat。在系统配置中允许管理员配置企业微信应用的AgentId、Secret等。发送通知时NotificationService根据消息类型和用户偏好决定发送渠道。例如紧急审批通知可同时发送站内信、邮件和企业微信应用消息。// 通知服务示例 class NotificationService { public function sendApprovalNotify($toUserId, $instanceTitle, $action) { $message 您的审批单【{$instanceTitle}】已被{$action}。; // 1. 存数据库 $msgId MessageModel::create([user_id$toUserId, content$message, ...]); // 2. 实时推送 (WebSocket) $this-wsPush-toUser($toUserId)-send([typeapproval, msgId$msgId, ...]); // 3. 异步队列发送邮件和企业微信 Queue::push(SendMultiChannelJob, [user_id$toUserId, message$message]); } }4. 安全性与性能优化实践4.1 多层次安全防护策略对于存放企业敏感数据的OA系统安全必须放在首位。输入验证与过滤杜绝SQL注入和XSS攻击的第一道防线。ThinkPHP的验证器Validate和模型Model的自动类型转换提供了基础保障。我在此基础上对所有用户输入包括GET、POST、JSON在进入业务逻辑前进行严格的格式和范围校验。对于富文本内容如公告详情使用htmlpurifier等库进行白名单过滤只允许安全的HTML标签和属性。CSRF防护全局启用ThinkPHP的CSRF中间件为所有表单和状态变更的POST/PUT/DELETE请求提供保护。权限校验纵深防御如前所述在路由中间件、控制器和方法层面均进行权限校验。尤其注意越权操作确保用户只能操作其权限范围内的数据。例如在ApprovalController的detail方法中不仅要检查用户是否有“查看审批单”的权限节点还要在查询数据库时确保approval_instance的申请人或相关处理人是当前用户。会话安全禁止使用默认的PHP文件Session。将会话存储到Redis并设置合理的过期时间。Session ID使用强随机算法生成并在Cookie中设置HttpOnly和Secure如果使用HTTPS标志。密码存储使用password_hash()函数进行哈希存储绝对禁止MD5、SHA1等弱哈希。API接口安全对于对外提供的RESTful API如与移动端APP对接采用JWTJSON Web Token进行认证。Token中携带用户基本信息并设置较短的有效期配合Refresh Token机制。4.2 数据库与缓存性能调优当用户量和数据量增长时性能瓶颈往往首先出现在数据库。索引优化这是成本最低、效果最显著的优化手段。使用EXPLAIN命令分析慢查询日志中的SQL语句。为approval_instance表的applicant_id、status、create_time字段建立复合索引加速“我的申请”列表查询。为approval_log表的instance_id建立索引加速日志查询。查询优化避免N1查询大量使用ThinkPHP模型的with方法进行关联预加载。例如查询审批列表时一次性加载申请人、当前节点信息而不是在遍历列表时循环查询。选择性使用延迟关联对于特别复杂的列表页如果关联数据多且非必需立即展示可以考虑先查询主表ID再根据ID批量查询关联数据。合理分页所有列表接口必须支持分页避免一次性拉取海量数据。ThinkPHP的paginate方法非常好用。缓存策略热点数据缓存将不常变更但频繁读取的数据放入Redis如部门树、角色列表、系统配置项。使用ThinkPHP的Cache门面可以轻松切换缓存驱动。查询结果缓存对于某些复杂的统计报表数据如果实时性要求不高如昨日考勤统计可以缓存其计算结果定时更新。缓存失效策略采用“读时检查写时更新”的策略。当管理员修改了部门信息后主动删除或更新Redis中对应的缓存键确保下次读取时获取的是最新数据。队列异步化将耗时操作丢入队列是提升Web请求响应速度的利器。除了发送邮件像“批量导出Excel”、“生成年度统计报告”、“同步组织架构到外部系统”这类任务都应设计为异步任务。使用think-queue组件可以很方便地配置Redis或数据库作为队列驱动。5. 部署、运维与二次开发指南5.1 生产环境部署要点将开发完成的系统部署到生产服务器需要一套标准化的流程。环境准备推荐使用Linux服务器如CentOS 7/8或Ubuntu 20.04 LTS。使用Nginx PHP-FPM的组合。PHP版本建议7.4或8.0并安装必要的扩展如Redis、PDO、OpenSSL、Zip。MySQL版本建议5.7或8.0。代码部署版本控制使用Git进行代码管理。生产服务器上通过git clone或git pull拉取指定标签Tag的代码确保版本可控。目录权限将项目根目录下的runtime运行时目录和public/uploads上传文件目录设置为Web服务器用户如www-data可读写其他目录建议只读。环境配置绝对不要将包含数据库密码等敏感信息的config目录提交到Git。使用.env文件管理环境变量。在生产服务器创建.env.production文件并通过软链接或部署脚本将其链接为.env。ThinkPHP 6.x 原生支持.env。Nginx配置关键点server { listen 80; server_name oa.yourcompany.com; root /path/to/your/oa/public; # 注意指向public目录 index index.php index.html; location / { if (!-e $request_filename) { rewrite ^(.*)$ /index.php?s$1 last; break; } } location ~ \.php$ { fastcgi_pass unix:/run/php/php7.4-fpm.sock; # 根据实际PHP版本调整 fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; # 重要防止解析漏洞限制可访问的PHP文件 fastcgi_param PHP_ADMIN_VALUE open_basedir$document_root/../:/tmp/; } location ~* \.(git|svn|env|log|htaccess|htpasswd)$ { deny all; } location ~ /\. { deny all; } }数据初始化首次部署时需要导入数据库结构SQL文件和基础数据如管理员账号、默认角色权限。可以编写一个install脚本或使用数据库迁移工具ThinkPHP的migration来完成。5.2 二次开发与扩展建议开源系统的生命力在于社区的扩展。为了让其他开发者能轻松地基于此OA进行二次开发我做了以下设计模块化设计系统功能按模块划分如admin后台管理、common公共、approval审批、attendance考勤等。每个模块拥有独立的控制器、模型、视图和服务目录。新增一个功能模块如“项目管理”只需在app目录下创建新的模块文件夹并遵循相同的命名规范即可。事件与钩子在关键业务流程中埋入了事件Event。例如在“审批流程结束时”、“用户登录成功后”。其他开发者可以监听这些事件在不修改核心代码的情况下添加自定义行为。ThinkPHP 6.x的事件系统非常强大。// 在审批通过后触发一个事件 event(ApprovalPassed, $approvalInstance); // 在其他模块中监听这个事件 Event::listen(ApprovalPassed, function($instance) { // 例如自动发送一封祝贺邮件 Mail::to($instance-applicant-email)-send(new ApprovalCongratsMail($instance)); });配置驱动将尽可能多的行为参数化。例如邮件模板、审批流的节点图标、假期类型等都设计为可通过后台管理系统进行配置而不是硬编码在代码中。API接口标准化内部控制器在返回数据时尽量统一使用JSON格式。这为未来可能的前后端分离或开发移动端APP打下了良好基础。可以封装一个统一的响应工具类。给二次开发者的建议在开始修改或添加功能前请先通读核心模块如权限、工作流的代码和本文档。理解现有的架构和约定能让你事半功倍。遇到问题时优先查看runtime/log目录下的日志文件那里通常记录了详细的错误信息。本文还有配套的精品资源点击获取