简介:基于PHP的OA办公系统源码包,面向需要快速搭建内部办公管理平台的网站开发者、企业IT人员及PHP初学者。系统兼容php5.2/5.3/5.4运行环境,搭配MySQL数据库,内置浏览器安装引导入口,配置、校验与数据初始化流程均包含在安装环节中,同时提供演示数据还原文件,安装完成后可直接登录管理后台体验功能。压缩包整体约36.7MB,包含PHP业务源码、SQL数据库脚本、安装辅助文件等,目录结构清晰,适合直接部署、学习OA系统开发流程或基于现有模块进行二次扩展。对于刚接触PHP项目的开发者,可借助安装说明快速完成环境配置,安装说明中也对常见数据库扩展验证失败问题给出了排查方向;对有定制需求的人员,源码注释与模块划分能辅助定位业务逻辑。目前已有313人学习下载,属于经典的PHP+MySQL中小型管理软件项目,对掌握传统OA搭建逻辑与数据库配置具有实际参考价值。
1. 基于PHP的OA办公系统:这套老技术栈为什么还在解决实际问题
收到「基于PHP的OA办公系统(源码+数据库).zip」这类压缩包时,别急着后悔怎么不是 Java 或 Go 写的。在很多中小企业、事业单位和外包项目里,PHP OA 系统仍然是审批流、公告、公文、会议管理最轻量的落地方式:部署门槛低,虚拟主机就能跑,改起来也比想象中快。这个标题里最值钱的不是 PHP 本身,而是「源码+数据库」这套可复现的完整闭环。下面先拆清楚这套系统怎么从压缩包变成一台能日常使用的办公服务器。
2. 把OA源码跑起来:环境选择、目录规划与启动配置
2.1 老PHP源码对环境有什么脾气
OA源码用 PHP 写,最常见的问题不是功能,而是运行环境不匹配。早年这类系统多跑在 PHP 5.3 到 PHP 7.0 之间,用的连接方式可能是 mysql_、mysqli_或者 PDO。到 PHP 7.4 之后 mysql_* 系列函数已经彻底移除,如果你解压源码看到大量mysql_query(),直接扔到 PHP 8 环境里会白屏。
判断源码到底需要什么环境,我一般不看 README,直接翻三个地方:数据库连接文件里用的是 mysqli 还是 PDO;phpinfo()里有没有提示已废弃函数;页面顶部有没有error_reporting(E_ALL)这类开发期配置。常见做法是先用 PHP 7.0 或 7.2 作为起步版本,因为兼容性最稳:老 mysql_* 代码改造成 mysqli 不复杂,而 PHP 7 本身对面向过程写法的 OA 系统容忍度也高。
这里有个容易被忽略的点:OA 系统往往继承了早年间「一个入口文件 + 一堆 include」的写法,对register_globals、magic_quotes这类上古配置有依赖。好消息是 PHP 7 里这些默认值基本能安抚大多数老代码;坏消息是如果源码里用了$_SERVER['PHP_SELF']或自定义路由,你需要把 Apache 的AllowOverride打开,否则 rewrite 规则不生效。
2.2 本地起步:从解压到打开登录页
最常见做法是在本地装一套 Apache + PHP + MySQL 集成环境,Windows 上用 phpStudy 或 XAMPP,Linux 上直接用 apt 装。以 Ubuntu 为例,最小命令是这样:
sudo apt update sudo apt install apache2 php7.2 libapache2-mod-php7.2 php7.2-mysql mysql-server sudo a2enmod rewrite sudo systemctl restart apache2装好后把压缩包解压到 Apache 的网站根目录。源码包里通常是一个完整的 Web 目录,前端文件、后端代码、上传目录都在里面。如果解压后发现顶层还有一层同名文件夹,记得把里面的内容挪到根目录,而不是让网站根目录再套一层目录,否则访问路径会很长,还容易踩相对路径的坑。
启动 MySQL 后需要做两件事:创建一个业务数据库,建立一个与源码配置匹配的数据库账号。常见做法是在命令行里先进入 MySQL:
CREATE DATABASE oa_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; CREATE USER 'oa_user'@'localhost' IDENTIFIED BY 'your_password'; GRANT ALL PRIVILEGES ON oa_db.* TO 'oa_user'@'localhost'; FLUSH PRIVILEGES;参数说明:库名oa_db和用户名oa_user后面还要写进源码的配置文件,最好现在就确定下来,不要先随手建一个再回头改,容易漏改某处导致登录时提示数据库连接失败。字符集选 utf8mb4 是为了兼容生僻字、Emoji 和部分公文里的特殊符号,老 SQL 里如果是 utf8_general_ci,导入后大概率会出现乱码。
启动出来的登录页如果不是空白而是 403,那基本是目录权限问题。默认 Apache 运行用户 www-data 需要能读代码目录,上传目录还需要写权限:
sudo chown -R www-data:www-data /var/www/html sudo chmod -R 755 /var/www/html sudo chmod -R 777 /var/www/html/upload 2>/dev/null || trueLinux 下www-data是权限边界,别为了省事直接chmod -R 777整个目录。你会在后面安全加固章节看到为什么这么做会让你睡不着。上传目录单独开放写权限就够了,代码目录保持 755。
2.3 首次登录前的三个准备工作
第一次打开登录页之前,建议按这个顺序检查,能少走弯路:
- 确认数据库里已经有初始管理员账号。大多数 OA 系统会在 SQL 脚本里写入 admin 账号,如果你导入的是精简版数据库,可能只有空表,需要手动插入一条管理员记录。
- 确认验证码能显示。老系统验证码依赖
gd扩展,如果图片是裂开的,说明 PHP 的php-gd没装。Ubuntu 下补上再重启 Apache:sudo apt install php7.2-gd && sudo systemctl restart apache2。 - 确认时区。老代码默认
date.timezone为空时,PHP 会报警告并显示和本地时间差 8 小时的记录,这对考勤、公文流转时间戳都是灾难。在 php.ini 里设置date.timezone = Asia/Shanghai后重启服务。
做这一步时顺手把display_errors打开,让语法错误直接暴露在页面上:
sudo sed -i 's/display_errors = Off/display_errors = On/g' /etc/php/7.2/apache2/php.ini sudo systemctl restart apache2只有在开发期才打开它,正式上线必须关掉。后面安全加固里会再说一次,因为很多人上线前忘了这一句,结果把数据库报错信息直接暴露给前台用户,等于把服务器家门钥匙挂在了门外。
3. 数据库导入与连接参数:OA能登录的前提
3.1 看清压缩包里那份数据库脚本到底是什么
「源码+数据库」里的数据库不是一个正在运行的实例,而是一个 SQL 导出文件。你要做的不是把它当作文件复制到某个目录,而是把它导入 MySQL 服务里。
先打开 SQL 文件的前几十行判断结构。如果开头是-- MySQL dump,说明是 mysqldump 导出的完整备份;如果是一堆CREATE TABLE开头,可能是手工导出的结构文件,数据可能另附或需要初始化。常见压缩包里两种都有:一个oa.sql包含了表和初始数据,另一个oa_init.sql是空结构。看清后选带数据的那个导入,否则登录时账号不存在。
这里有个实战技巧:先搜索 SQL 文件里的INSERT INTO行数来判断数据量。文件很大时不要急着全部导入,先搜有没有CREATE DATABASE语句。如果有,它会在导入时自动建库;如果没有,你必须提前手工建库,否则导入会报「No database selected」。
3.2 导入SQL的两种方式与字符集问题
命令行导入是最稳的。先把 SQL 文件传到服务器,然后执行:
mysql -u root -p oa_db < /var/www/html/oa_database.sql参数说明:-u root用的是 MySQL 管理员账号,-p之后会提示输入密码,oa_db是第一步创建的数据库名,文件路径按实际位置改。这条命令执行完,屏幕上没有任何输出通常就是成功。如果 SQL 文件里有建库语句,也可以直接mysql -u root -p < oa_database.sql,不加库名。
第二种方式是在 MySQL 命令行里用source:
mysql -u root -p SOURCE /var/www/html/oa_database.sql;这种方式的好处是报错时能精确看到出错的行号。比如常见的ERROR 1067 (42000): Invalid default value,多半是因为某个字段的 default 值是0000-00-00,而 MySQL 5.7 以上默认开了严格模式NO_ZERO_DATE。解决办法是这次会话里先执行SET sql_mode=''再 source,或者改 SQL 里对应的 default 值。
字符集在导入这一步非常关键。如果 SQL 文件头上写的是utf8,而你建的库是utf8mb4,表的字符集会以文件里为准,中文字段仍然能显示,但生僻字会变成问号。我建议统一:库、表、连接三者的字符集都走 utf8mb4。导入前确认一下连接用的字符集,加上一句:
mysql -u root -p --default-character-set=utf8mb4 oa_db < /var/www/html/oa_database.sql3.3 数据库连接配置文件要改的三个参数
导入完成后,源码必须知道怎么连接数据库。不同系统的配置文件位置不一样,常见的是config/database.php、include/config.php或application/config.php。老 PHP OA 系统喜欢把所有配置集中在一个文件里,里面至少有这几行:
<?php // 数据库连接配置,按实际环境修改 $db_host = '127.0.0.1'; // 数据库主机,本地部署通常不改 $db_user = 'oa_user'; // 数据库账号 $db_pass = 'your_password'; // 数据库密码 $db_name = 'oa_db'; // 数据库名 $db_charset = 'utf8mb4'; // 连接字符集,必须与库一致 ?>三个必改参数就是账号、密码、库名。$db_host除非数据库在另一台服务器,否则保持127.0.0.1,别写成localhost,因为 PHP 在不同系统里对两者的处理不一样,偶发出现连接超时。
改完后用一条命令直接验证连接是否成功:
php -r "mysqli_connect('127.0.0.1','oa_user','your_password','oa_db') or die(mysqli_connect_error()); echo '连接成功';"如果输出Access denied for user,检查 MySQL 用户授权里有没有允许从127.0.0.1登录。注意 MySQL 里'oa_user'@'localhost'和'oa_user'@'127.0.0.1'是两个不同的账号,只授了前者的话,PHP 用 IP 连接会被拒绝。这也是新手最容易翻车的坑。
3.4 导入后必做的完整性验证
导入完别急着登录,先做三件事确认数据库是真的完整:
- 查表数量。进入 MySQL 执行
USE oa_db; SHOW TABLES;,对比源码文档里说的表数量,差一两个可以接受,差一半说明 SQL 文件选错了。 - 查关键数据。搜索用户表和管理员表,
SELECT * FROM sys_user LIMIT 5;,确认初始账号的密码字段不是空值。老系统密码通常是 md5 加密,只要字符串长度是 32 位基本正常,如果是空字符串说明数据没导全。 - 查外键和自增。老 OA 系统一般不用外键,但自增主键必须存在。执行
SHOW CREATE TABLE 某张业务表;,确认AUTO_INCREMENT还在,否则新增数据时会报主键冲突。
这些步骤看着繁琐,却能帮你区分「系统有问题」和「数据没导对」——这两类问题在后续排错时是完全不同的排查路径。
4. 读懂源码结构:从入口文件到业务模块的改动路径
4.1 常见PHP OA的目录划分
老式 PHP OA 系统的目录结构比现代框架直白得多。前端资源、include 公共文件、模块目录、上传目录、数据库备份目录通常分开。常见的样子是:
/var/www/html ├── index.php // 入口文件,所有请求都经过这里 ├── login.php // 独立登录页 ├── include/ // 公共函数、数据库连接类 ├── module/ // 业务模块:报销、公文、会议等 ├── template/ // 页面模板,HTML与PHP混写 ├── upload/ // 附件上传目录 └── install/ // 安装脚本,上线后建议删除看入口文件是理解整套系统的钥匙。打开index.php,你会发现它做的事情就三件:定义常量、引入配置、根据 GET 参数分发请求。伪代码逻辑通常是这样:
<?php // index.php 入口分发逻辑 require_once 'include/config.php'; // 加载数据库配置 require_once 'include/auth.php'; // 加载登录校验 $module = $_GET['m'] ?? 'index'; // 模块名,例如 m=meeting 表示会议模块 $action = $_GET['a'] ?? 'list'; // 操作名,例如 a=add 表示新增 // 拼接出实际处理文件路径并包含 $target_file = "module/{$module}/{$action}.php"; if (file_exists($target_file)) { include $target_file; } else { die('模块不存在'); } ?>逻辑说明:这类系统没有路由类,靠$module和$action两个 GET 参数指向具体的 PHP 文件。这样的好处是加一个新功能只需要新增一个文件,不用改路由表;坏处是文件多了之后,目录层级会越来越深,查代码时要有耐心。
4.2 加一个「我的申请」列表:动手前先看这两处
假设你想要在 OA 里加一个「我的申请」聚合页,把报销、请假、用车申请放在一起展示。这个需求在系统里是「三张表 + 一个汇总页」的问题,改动前先看这两处:
- 登录校验是怎么做的。看
auth.php里的 session 变量名,一般是登录成功后$_SESSION['user_id']存了当前用户 ID。你写 SQL 时要用的就是它。 - 现有模块的列表页长什么样。找
module/leave/list.php或类似的现成文件,照着它的写法抄一遍分页逻辑和权限判断,而不是自己发明一套。
加功能的最小改动方案是新增一个聚合页,用 UNION 拼接三张业务表:
<?php // 我的申请聚合页:请假、报销、用车申请合并展示 $uid = intval($_SESSION['user_id']); // 强制转为整数,避免注入 $sql = "SELECT '请假' AS type, id, title, status, create_time FROM leave_apply WHERE user_id = $uid UNION ALL SELECT '报销' AS type, id, title, status, create_time FROM reimburse_apply WHERE user_id = $uid UNION ALL SELECT '用车' AS type, id, title, status, create_time FROM car_apply WHERE user_id = $uid ORDER BY create_time DESC"; $result = mysqli_query($conn, $sql); while ($row = mysqli_fetch_assoc($result)) { // 输出行内容,这里省略 HTML 展示部分 } ?>参数说明:intval()是这类老代码里最廉价也最有效的防注入手段,任何从$_GET、$_POST、$_SESSION拿来的数字型变量先过一遍整数转换,能堵住大部分入门级攻击。UNION ALL比UNION更合适是因为各类型数据不会重复,还能少一次去重排序的开销。
4.3 数据库字段改动后,页面要跟着改的三处
OA 系统最常遇到的二次开发不是加模块,而是给已有模块加字段。比如给请假单加一个「紧急程度」下拉框。看起来只需改数据库加一列,实际要动三处地方:
第一处是leave_apply表加字段。老系统没有迁移机制,直接在 MySQL 里执行:
ALTER TABLE leave_apply ADD COLUMN urgency TINYINT NOT NULL DEFAULT 0 COMMENT '紧急程度:0普通 1紧急';第二处是新增和编辑表单。找到module/leave/add.php和module/leave/edit.php,在 HTML 表单里加一个<select>控件,在接收 POST 数据的代码里加上对应字段的写入逻辑。
第三处是列表页。module/leave/list.php的查询列表如果用了SELECT *就不用改,但如果手写了字段列表,新字段不会自动出现,必须手动补上。这是老 PHP 系统最爱坑人的地方。
改完后用最笨也最有效的方式验证:填一张请假单提交,去数据库查记录,再回列表页看看显示。走完这一圈,数据库字段、表单、列表三处同步正常,才算真的改完。
5. 常见问题排查:部署OA时最容易踩的五个坑
5.1 登录页打不开:PHP版本与mysqli扩展的兼容问题
现象:访问网站直接空白,浏览器 F12 看到 HTTP 500,Apache 错误日志里有PHP Fatal error: Uncaught Error: Call to undefined function mysqli_connect()。
原因:源码依赖 mysqli 扩展,而当前 PHP 环境没有安装或启用该扩展。很多集成环境默认只开了 pdo_mysql,老代码不认。
解决:在 Ubuntu 下执行sudo apt install php7.2-mysql,Windows 集成环境则在面板里勾选 mysqli 扩展,然后重启 Apache。顺手用php -m | grep mysqli确认扩展已加载。如果是 PHP 8 环境遇到 mysql_* 函数,别想着让它复活,老老实实把mysql_query批量替换成mysqli_query并调整参数顺序。
5.2 SQL导入后中文乱码:字符集不一致
现象:登录后用户名、部门、公告标题全是???,数据库里查到的也是问号。
原因:SQL 文件是 utf8 编码,导入时连接字符集被系统默认成了 latin1,导致 utf8 字节被拆解,数据入库时就已经损坏。这种损坏不可逆,不要尝试在页面端用转换函数救回来。
解决:把表删掉重新导入,用--default-character-set=utf8mb4参数或导入前先执行SET NAMES utf8mb4;。建库时也明确指定字符集。验证方法很简单:导入后直接在 MySQL 里SELECT * FROM 某张带中文的表;,如果命令行里中文正常,再去看页面。
5.3 模块地址403或404:URL路由与伪静态规则
现象:左侧菜单点击「会议管理」跳转到http://域名/index.php?m=meeting&a=list,报 404 或 403;登录页却正常。
原因:老 OA 的菜单链接如果写成了index.php/meeting/list这种伪静态格式,而 Apache 的 rewrite 模块没开或.htaccess没生效,就会 404。403 则多半是目录没有索引文件或权限不对。
解决:先确认菜单链接是?m=meeting问号格式还是斜杠格式。问号格式出问题是 PHP 没问题、文件路径没对上;斜杠格式出问题则打开.htaccess看看 rewrite 规则,并确认 Apache 配置里AllowOverride All已设置。Nginx 用户更麻烦,需要把try_files配置成:
location / { try_files $uri $uri/ /index.php?$query_string; }5.4 上传附件失败:权限和PHP上传限制
现象:点击上传附件,提示「上传失败」或「目录不可写」,但其他功能正常。
原因:upload 目录权限不足,或 PHP 的upload_max_filesize、post_max_size太小。老 OA 附件常用表单直接上传,没有分片,超过限制就被后端静默丢弃。
解决:先确认目录权限,chmod -R 775 upload且属主改为 php-fpm 或 Apache 的运行用户。再调大 PHP 上传限制,在 php.ini 里改成:
upload_max_filesize = 32M post_max_size = 40M max_execution_time = 120改完重启服务。注意post_max_size要比upload_max_filesize大,因为 POST 请求里除了文件,还会携带表单字段,差太小会误伤大文件。
5.5 Session频繁失效:临时目录不可写
现象:登录后操作两分钟就跳回登录页,刷新后又能进,再操作又退出,反复横跳。
原因:PHP session 文件默认写入系统临时目录,该目录不存在或权限不足时,session 无法持久保存,每次请求都是新会话。云服务器和 Docker 环境里尤其常见。
解决:在 php.ini 里设置session.save_path = "/tmp",确认目录存在且可写;或者在源码入口文件里手动指定:
<?php // 手动指定session存储目录,防止系统临时目录不可写 session_save_path('/var/www/html/temp_session'); if (!is_dir('/var/www/html/temp_session')) { mkdir('/var/www/html/temp_session', 0777, true); // 生产环境改为0755 } session_start(); ?>改完后重启 Apache,用浏览器无痕窗口重新登录,连续操作十分钟不退出即为正常。
6. 部署前必做的安全加固与备份习惯
6.1 关闭调试信息并清理安装文件
老 OA 系统最危险的地方不是漏洞本身,而是把漏洞信息直接亮给攻击者。上线前必须关掉display_errors,否则 SQL 报错会带着完整查询语句、文件路径甚至数据库账号出现在页面上。顺手做三件事:删除或改名install/目录;把默认管理员账号的密码用 MD5 重新生成并更新;改掉后台登录路径。第三件事对老系统尤其有效——攻击者扫路径时找不到后台入口,攻击难度立刻上一个台阶。
6.2 给数据库建立可恢复的备份体系
OA 系统最值钱的资产是审批数据,代码丢了能重写,数据丢了等于整个系统报废。我的习惯是每天凌晨用 cron 做一次完整备份,保留最近 14 天:
#!/bin/bash # 每日数据库备份脚本,保留最近14天 BACKUP_DIR="/var/backups/oa_db" DATE=$(date +%Y%m%d) DB_USER="root" DB_PASS="你的数据库密码" mkdir -p "$BACKUP_DIR" mysqldump -u"$DB_USER" -p"$DB_PASS" --default-character-set=utf8mb4 oa_db | gzip > "$BACKUP_DIR/oa_$DATE.sql.gz" # 删除14天前的备份文件 find "$BACKUP_DIR" -name "*.sql.gz" -mtime +14 -delete配合 crontab 设置每日凌晨执行:0 3 * * * /usr/local/bin/backup_oa.sh,同时把备份目录从 Web 根目录移出,避免被人直接通过浏览器下载。如果服务器有多台,可以考虑用数据库同步工具做一主一从,让备份再多一层保障。
6.3 给OA系统加一层最基本的访问控制
老 OA 没有完整的权限体系,我一般会在入口文件里加一个简单的 IP 白名单策略,只允许公司网段访问后台。另外,PHP 执行目录里最好单独禁止上传目录运行脚本。在 upload 目录放置一个.htaccess:
# upload目录禁止执行PHP脚本 php_flag engine off RemoveHandler .php .phtml .php5这段配置让攻击者就算成功上传了恶意文件,也无法在服务器上执行它。这是老 PHP 项目投入产出比最高的一道防线。除了这些,每年换一次数据库密码,并确认数据库账号只用oa_user而不是 root 连接源码,都是便宜又有效的习惯。
我交付这套系统时有个固执习惯:如果客户没有靠谱的运维,就在代码里把备份脚本写好并实际验证一次恢复流程。备份文件不能恢复就等于没有备份,这个道理用一次血泪经验就深刻了。把上面几步做扎实,这套 PHP OA 至少能稳定跑上好几年,希望帮到你。
本文还有配套的精品资源,点击获取