简介:这是一套基于PHP开发的轻量级OA办公系统源码,面向Web开发初学者与中小型团队快速搭建内部协同平台的需求,适用于学习LAMP架构下的企业级应用开发流程。资源包含完整可运行的PHP源码及MySQL数据库文件,压缩包大小为36.7MB,共含数十个核心文件,涵盖前台展示、后台管理、安装脚本、SQL备份(如demo1481562750.sql)、系统配置与菜单管理模块等,结构清晰,便于理解MVC基础逻辑与权限控制实现。已有310人下载学习,适合PHP+MySQL入门者通过实操掌握OA系统部署、数据库还原、菜单动态加载及常见环境适配(支持PHP 5.2–5.4)。资源附带关键安装提示与数据初始化步骤说明,帮助规避前台无法访问、扩展验证失败等典型问题,是理解传统PHP Web系统工程化落地的实用参考样本。
1. 为什么我要把整套OA源码和数据库一起打包分享
先说一个很多中小团队都遇到过的场景:公司人一多,考勤、审批、公告还靠微信群和口头传达,行政每天光统计请假单就能耗掉半天。想上一套正儿八经的OA办公系统,市面上主流的泛微、致远这些大厂产品,实施顾问上门一聊就是几十万;便宜一点的SaaS版,数据都在别人服务器上,功能绑定得死死的,想加个自定义字段都得买增值模块。这种情况下,自己拿PHP造一套轮子,反而成了最务实的选择。
我之前在给几家创业公司做内部系统的时候,陆续写过几个PHP后台,最后干脆整理成了一套完整的OA办公系统,包含PHP源码和MySQL数据库文件,压缩成zip包分发。这套系统从最简单的用户登录、组织架构,到请假报销审批、公告通知、会议管理,基本覆盖了小团队日常办公的绝大多数需求。源码结构清晰,没有加密,拿到手就能部署,还能根据自己的业务逻辑随便改。
也许有人会问,都2025年了,为什么还用PHP?直接上Java Spring Boot不好吗?答案很现实:对于几百人的中小企业,PHP的部署成本和学习成本优势实在太明显。你不需要懂JVM调优,不需要配Maven仓库,一台普通的Linux服务器装上PHP环境加MySQL,把源码丢进去就能跑。而且PHP对MySQL的亲和力极高,做OA这种以CRUD为主的业务系统,开发效率比Java高出一截。这也是为什么市面上一堆老牌OA系统都是从PHP起家的原因,不是PHP不行,而是很多团队没把它用对。
这套源码包的意义在于,它不是那种只能用来演示的玩具Demo,而是真正能跑在公网服务器上给全公司用的系统。登录有密码加密和验证码,权限走RBAC模型,审批流支持多级节点,数据库表结构覆盖了从用户到审批日志的完整链路。后面我会把这里面最关键的设计思路和部署排错经验一条条讲清楚,那些坑都是我自己踩过的。
2. 源码包结构揭秘:一个能跑起来的OA系统到底由什么构成
2.1 解压之后你看到的目录是怎么组织的
打开这个zip包,里面不是一堆乱七八糟的文件,而是按照约定俗成的项目结构组织的。入口在根目录下的index.php,通过一个统一的前端控制器来加载路由。项目里没有用太重型的框架,只用了一个轻量级路由类,所以对新手特别友好,你可以直接看到每个模块的PHP文件。
核心目录大概是这样的:
application/:业务逻辑层,按模块拆分成admin(后台管理)、user(用户中心)、approval(审批)、notice(公告)、meeting(会议)等目录,每个目录里放对应的控制器和模型。public/:对外公开的静态资源和入口文件,包含CSS、JS、上传图片等。config/:数据库配置文件、全局参数配置,通过一个常量数组集中管理。data/:系统运行日志和缓存目录,需要给写权限。install/:安装向导。如果你的环境已经配置好了,直接访问/install就能自动导入数据库。
这种结构的核心好处是:把控制器、模型、视图初步分离,但又不像TP或Laravel那样引入大量抽象层。对想学习PHP项目结构的同学来说,是很好的参考案例;对想拿去二开的开发者来说,改起来也不用在框架源码里翻半天。
2.2 运行环境要求与开发版本选择
这套系统基于PHP 5.6到7.4版本开发,兼容MySQL 5.7。我测试过在PHP 7.4下运行最稳定,如果环境是PHP 8.0以上,部分老函数需要做兼容处理。这里建议部署时直接选PHP 7.4,省去不必要的麻烦。
Web服务器方面,Apache和Nginx都可以用。Apache的默认配置就能跑,因为有.htaccess做伪静态;Nginx则需要在配置里加上一行try_files规则,把请求重写到index.php。MySQL编码统一设置为utf8mb4,这样中文、emoji、生僻字都能正常存储。
如果你用的是Windows电脑做开发,推荐直接用phpStudy或WAMP这类集成环境;如果是线上服务器,用宝塔面板装好Nginx、PHP 7.4和MySQL 5.7,基本就是零门槛。有一个细节容易被忽略:PHP需要开启pdo_mysql和openssl扩展,否则数据库连接和密码加密都会报错。
2.3 快速部署的六步流程
部署路径不复杂,但每一步都有讲究。我按照实际操作顺序拆解一下。
- 解压源码包,把整个目录上传到服务器Web根目录,例如
/var/www/html/oa。 - 新建一个MySQL数据库,比如
oa_system,字符集选utf8mb4,排序规则选utf8mb4_general_ci。 - 导入数据库文件。这一步千万别用记事本打开SQL再手动执行,很容易因为编码问题报错。直接在phpMyAdmin里点击“导入”,或者用命令行执行
mysql -u root -p oa_system < database/oa_system.sql。 - 修改
config/database.php,填上数据库地址、用户名、密码、库名。 - 给
data目录设置写权限。Linux下执行chmod -R 775 data,确保PHP进程能写入日志和缓存文件。 - 浏览器访问
http://你的域名/index.php/admin,用默认账号admin和密码123456登录,进入后台后第一时间修改密码。
我在很多次部署中发现,大部分人卡在第三步。数据库导入报错多半是因为SQL文件里包含了创建表的语句和插入语句,而当前数据库字符集不符。解决办法是把SQL文件开头加一句SET NAMES utf8mb4;,再重新导入。
3. 数据库设计:OA系统的地基怎么搭
数据库设计决定了OA系统的上限。很多半路出家的OA项目,表建得随意,后面加需求就痛苦不堪。这套源码在设计时我重点考虑了三个核心问题:组织架构怎么建模、审批流怎么存数据、权限怎么控制。
3.1 用户、部门、岗位三表关系:不要只想着用户表
我见过很多初学者的OA系统,一张用户表打天下,部门字段直接填字符串,结果人事调整的时候只能手工改。在这个项目里,我把组织架构拆成了三张核心表:oa_dept(部门表)、oa_position(岗位表)、oa_user(用户表)。用户表通过dept_id和position_id关联部门与岗位,而不是直接存部门名称。
这样做的好处很明显:修改部门名称时只需要改一处;统计某个部门人数时,一条SQL就能查出来;后续要扩展岗位级别、部门负责人等字段,也不会影响用户表的现有逻辑。
我在设计时还额外给部门表加了parent_id字段,用来支持无限层级,这样就能覆盖从小团队到集团公司的树形组织架构。每次加载部门树时,先全部取出,再用递归组装成树形结构,数据量在几千条以内时性能完全不是问题。
3.2 审批流表结构:从主表到日志表的完整链路
OA系统最核心的模块就是审批流。请假、报销、用章、采购,本质上都是同一种流程:发起人提交表单,主管审批,再往上传,最终归档。我设计了三张表来处理整个流程。
第一张是oa_approval,存放审批单的公共字段,包括标题、类型、申请人ID、当前审批人ID、状态、优先级、创建时间。第二张是oa_approval_content,存放具体业务数据,比如请假的天数、报销的金额,用approval_id关联主表。为什么不直接塞进主表?因为不同类型的审批单字段差异极大,放在子表里可以灵活扩展。第三张是oa_approval_log,记录每一次流转动作,谁在什么时间提交、审批、驳回、转交,全部留痕。
审批状态我用整数常量表示:1待审批、2已通过、3已驳回、4已撤销。当前审批人ID在流转过程中会动态更新,判断权限时只需要查这一条。这种设计虽然看起来简单,但完全够用,而且出现问题的时候日志表一翻就看到谁在哪个环节卡住了。
3.3 权限控制:如何用RBAC模型兼顾菜单权限和数据权限
权限设计我用的是经典的RBAC(基于角色的访问控制)模型。五张核心表:oa_user、oa_role、oa_menu、oa_user_role、oa_role_menu。用户不直接和菜单挂钩,而是先分配角色,角色再绑定菜单,这样新增一个岗位只需要配置一次角色即可。
登录成功后,系统把当前用户的所有菜单ID和权限标识查询出来,循环比对每个请求的控制器和方法名,实现访问拦截。这个判断放在一个基类控制器的初始化方法里,所有需要鉴权的控制器都继承这个基类。
这解决了菜单权限,但数据权限是另一个维度。比如普通员工只应该看到自己提交的审批单,部门主管能看到本部门所有人的,人事总监能看到全公司的。我通过给用户表加了一个data_scope字段(1本人、2本部门、3全部)来实现,查询列表时根据这个字段自动拼接WHERE条件。
// 数据权限伪代码 if ($user['data_scope'] == 1) { $where = "applicant_id = {$user['id']}"; } elseif ($user['data_scope'] == 2) { $where = "dept_id = {$user['dept_id']}"; } else { $where = "1=1"; }实际项目中,这套简单方案已经扛住了上千用户的使用。想升级也不难,比如把数据范围配置到角色表,再引入部门层级。但核心思路不变:权限判断一定要放在后端,不能依赖前端按钮隐藏。
4. 核心功能模块的PHP实现思路
4.1 登录认证与密码安全:不只是md5加密
登录模块是所有系统的门面,也是最容易被黑的地方。我这里的实现并不复杂,但每一个细节都是踩过坑后改出来的。
密码存储使用password_hash()函数,生成带盐的哈希值,验证时用password_verify()。这是PHP官方推荐的方案,比起最原始的MD5加固定盐要安全得多。登录成功后,把用户ID和角色信息写入session,同时记录本次登录的IP和登录时间到日志表。
为了防止暴力破解,我加了验证码和登录失败次数限制。验证码用纯GD库生成,不需要额外安装扩展;登录失败超过5次就锁定该账号15分钟。锁定状态记录在数据库里,而不是session里,因为session在服务器重启后会丢失。还有一个关键细节:登录接口一定要做CSRF令牌校验,我使用一个全局的token生成器,每次提交表单时都校验,否则很容易被跨站请求伪造攻击。
很多初学者问我,为什么不直接做JWT?我解释一下:OA系统是典型的后端渲染、多页面跳转应用,用户登录后会在不同页面间跳转,session机制天然适合这种场景。JWT更适合小程序、App这种前后端分离的接口服务。技术选型没有绝对的好坏,只有适不适合。
4.2 我的待办、通知公告与站内消息推送
待办事项是提高办公效率的核心。每当一条审批单被提交到某人名下,系统会同时执行两个动作:更新审批主表的current_user_id,并在oa_message表中插入一条站内消息。用户在首页看到的“待办提醒”就直接从oa_message表里查未读记录。
消息表结构如下:id、receiver_id、type、content、is_read、create_time。这里我刻意没有做复杂的消息队列,因为OA系统并发量通常不高,直接同步插入完全可以。但如果后续要接入企业微信或钉钉通知,只需要在插入消息的地方增加一个回调钩子就行。
公告模块相对简单,管理员发布公告后,公告内容存oa_notice表,已读状态存oa_notice_read表。用关联表的原因在于,同一份公告不同用户阅读时间不一样,不能只用一个字段来回记录。首页展示最新公告时,通过NOT EXISTS判断当前用户是否已读,未读的置顶显示。这个小设计好多同类系统都没做,导致员工永远不知道有新公告。
4.3 审批流引擎:从简单if-else到可配置状态机
审批流实现我起初只写了if-else判断:申请人的直接主管审批,主管同意后跳到部门经理,再上级。写死之后发现,不同公司甚至不同部门的审批层级都不一样。后来我把流程配置抽成了数据库表,让系统具备简单的动态流程能力。
流程配置体现在oa_approval_type表里,定义每种审批类型绑定的流程模板。例如“请假”流程模板是:
- 步骤1:申请人的直接主管审批,通过后进入步骤2
- 步骤2:部门经理审批,通过后进入步骤3
- 步骤3:人事归档
流程模板用JSON格式存储节点数组,每个节点包含node_name、approver_type(上级、指定角色、指定人)、pass_action这些字段。PHP代码在遇到审批动作时,先读取这条模板,动态判断下一个审批人是谁,然后更新主表的状态和当前审批人ID。
这套轻量状态机的好处是灵活,不用改代码就能调整流程层级。缺点是没有图形化配置界面,只能改JSON。但对大多数中小企业来说,够用了。
5. 部署上线与排错实录:那些年我踩过的坑
5.1 本地部署最常见的问题:伪静态配置和PHP版本
把源码包在本地跑起来,最常见的报错是“404 Not Found”。如果用的是Apache且没有开启mod_rewrite模块,URL重写就会失效。解决办法是确保httpd.conf里LoadModule rewrite_module没有被注释,同时AllowOverride All已开启。Nginx的话,需要在server配置里加上:
location / { try_files $uri $uri/ /index.php?$query_string; }配完伪静态,遇到“500 Internal Server Error”,大概率是PHP版本兼容问题。比如PHP 8.0后移除了each()函数和create_function(),老代码里只要用了就会白屏。我的建议是:部署环境直接用PHP 7.4,不要纠结新版本,稳定压倒一切。
5.2 数据库导入出错:字符集和常见语法兼容
数据库导入踩坑概率极高。第一种错误是“Unknown collation”,老SQL文件里可能写了utf8_general_ci,但新版MySQL默认配置里没有,需要把排序规则手动改成utf8mb4_general_ci。第二种错误是导入过程中报错但不影响数据,多半是因为SQL里存在重复键或注释里包含特殊字符。
一个稳妥的导入方式是使用命令行:
mysql -u root -p oa_system < database/oa_system.sql命令行导入默认按UTF-8读取文件,基本不会出现编码问题。如果数据里显示乱码,检查三处:Nginx/Apache报头字符集、PHP连接MySQL的字符集、表字段的字符集。只要有一处还是latin1,中文就一定会乱。
5.3 线上部署的权限和目录写保护
线上部署比本地多了安全维度。讲两个我亲眼见过的低级错误:data目录权限给成了777,结果服务器被种了木马;配置文件config/database.php把数据库密码写在明处,然后整个项目被扫到开源平台,数据库沦为矿池。
正确的做法是:data目录给775,属主设为运行PHP的用户;config目录在部署完成后改为只读;上传目录public/uploads单独设为可写但禁止执行PHP脚本。Nginx下可以这样配置:
location ~* /public/uploads/.*\.(php|php5|phtml)$ { deny all; }这些配置不是可选项,而是上线的必选项。OA系统里全是员工手机号、工资、审批流水,一旦泄露,后果不是“删库跑路”能承担的。
6. 基于这套源码做二次开发与安全加固的经验谈
6.1 新增一个业务模块的标准套路
这套源码虽然没有用重型框架,但目录和命名比较规整,所以在上面加模块的套路很固定。以“车辆申请”模块为例,五个步骤完成:
- 在
application/下新建car目录,里面放控制器Car.php和对应的模型CarModel.php。 - 在数据库里建两张表:
oa_car和oa_car_apply,分别存车辆信息和申请记录。 - 在控制器里写列表、新增、编辑、删除方法,继承基类控制器的鉴权逻辑。
- 在
oa_menu表里插入对应的菜单记录,配置好路由和权限标识。 - 在后台菜单管理里刷新一下,新模块就会出现在侧边栏。
这里有个容易犯错的地方:新增的控制器方法名要和UI里的请求地址完全一致,如果用了驼峰命名,而前端请求用的是下划线,就会出现“找不到页面”。我建议统一使用小写加下划线的方式命名方法,比如add_car、edit_car,避免后续混乱。
6.2 安全加固:防SQL注入、XSS和文件上传漏洞
OA系统最容易被攻击的三个点:SQL注入、XSS、文件上传。这套源码里我已经做了基础防护,但二开很容易破坏防护。
SQL注入方面,所有数据库操作尽量用PDO预处理语句,不要自己拼接SQL。比如查询详情时:
$stmt = $pdo->prepare("SELECT * FROM oa_user WHERE id = ?"); $stmt->execute([$id]); $user = $stmt->fetch();XSS方面,所有输出到HTML的内容都要经过htmlspecialchars()处理。我在模板引擎里封装了一个e()函数,所有动态变量输出时都过一遍。但有些同事在二开时嫌麻烦,直接echo $content,这就是给攻击者开了一扇窗。除了函数处理,还可以在入口处统一设置Content-Security-Policy响应头,加上后基本能防住大部分反射型XSS。
文件上传是最危险的,很多OA系统都有头像上传、附件上传功能。我在上传接口里做了四层校验:后缀名白名单、MIME类型校验、文件头检测、上传目录禁止执行脚本。不要只信前端传来的类型,那些都能伪造。
6.3 性能优化:从单机到上千用户不卡的调优记录
这套OA在设计时就避免了明显的性能坑,但用久了还是会出现查询变慢的情况。我遇到的性能瓶颈大多集中在三处:待办列表查询、审批日志大数据量分页、部门树递归加载。
最有效的优化手段是加索引。比如oa_approval表的current_user_id和status字段经常出现在WHERE条件里,我建了联合索引。oa_message表的receiver_id和is_read字段建了联合索引后,站内信查询从800毫秒降到了30毫秒。简单有效,仅次于加缓存。
缓存方面,我做了两层。第一层是PHP文件缓存,把用户权限和菜单配置这类不频繁变化的数据序列化后存到data/cache/目录;第二层是MySQL查询缓存,对于统计类SQL,比如首页的待办数量,用SELECT SQL_CACHE COUNT(*) ...。实测在几百人同时在线时,页面响应能稳定在100毫秒以内。
如果你预期用户量还会继续增长,建议在缓存层引入Redis,把session和菜单权限缓存都迁过去。不过注意,引入Redis的前提是系统确实出现了性能问题,不要为了“先进”而过度设计,PHP和MySQL的组合应对几千人的OA系统绰绰有余。
最后再分享一个我实际维护时的经验:源码包拿到手之后,别急着上线。先在本地把数据库导入,跑一遍完整的审批流程,从发起请假到主管通过再到归档,确认每一个环节没有异常。这套系统里最复杂的逻辑就是状态流转,如果状态机逻辑有边界漏洞,线上一定会出幺蛾子。排查时多利用审批日志表,每一步操作都有记录,顺着日志找问题比猜代码快得多。希望这套PHP OA能帮你少走一些弯路。
本文还有配套的精品资源,点击获取