ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

OA+CRM双端一体PHP源码:部署要点与二次开发避坑指南

OA+CRM双端一体PHP源码:部署要点与二次开发避坑指南 简介这是一套面向中小企业及开发者的开源企业协同办公系统基于ThinkPHP8、Layui与MySql构建同时提供OA与CRM相关模块涵盖系统设置、人事管理、审批管理、日常办公、客户管理、合同管理、项目管理、财务管理等场景适合需要快速搭建或二次定制办公自动化系统的团队使用也可作为PHP开发者学习企业级项目结构的参考。压缩包共包含935个文件其中以html页面模板、php后端逻辑、js交互脚本、css样式和sql数据库脚本为主另有少量配置文件、字体文件及说明文档sql脚本中带初始化数据整体体积约10.12MB结构清晰便于按模块检索。目前已有92人学习下载。代码采用透明可定制的分层结构开发者可基于现有功能灵活扩展实现与自身业务系统的深度集成有效降低软件采购成本并避免供应商锁定是企业在数字化转型初期的实用选择。1. 双端一体的OACRM源码为什么要拆这两套却装进同一个包“OA办公系统crm客户管理系统企业办公系统适用于PC端手机端v5.8”这个标题我第一次是在外包群里看到的当时第一反应是“又一个把OA和CRM硬塞在一起的缝合包”。实际解压跑了一圈才发现它跟我见过的不少同类型包不一样OA的审批流、考勤打卡、公告日程跟CRM的线索、客户、合同、回款跑在同一个用户体系和同一套角色权限上登录一次PC端和手机端共用同一套后端逻辑。对中小企业、创业团队来说这是最省事的组合销售在CRM里录客户、做跟进转头在同一套系统里提报销、请假管理员也不用维护两套账号。适合没有专职开发、想先低成本把销售和内部流程跑起来的团队。真正上手之前我建议你先花十分钟把模块落点看清楚这套包的设计思路比它表面上那堆菜单更有看头。2. 目录与模块盘点OA审批流和CRM客户池各自落点在哪2.1 先看结论这不是“带客户功能的OA”是两套业务一棵树很多双系统源码包的做法是搞两个后台入口Admin管OA、另一个目录管CRM登录进去是两个割裂的系统。这套v5.8不一样它把两套业务放进了同一个应用里共享用户表、角色表和部门表业务表才按域分开。这样设计的好处很直接OA里的“申请人”就是CRM里的“负责人”同一个人的请假记录和他的客户跟进记录在管理员视角是能关联起来的。OA侧和CRM侧的核心模块大致是这样分布的业务域核心模块典型操作数据表线索OA组织架构部门、岗位、员工管理sys_dept / sys_userOA审批流请假、报销、采购申请oa_leave / oa_expense / oa_approvalOA考勤打卡上下班打卡、外勤、补卡oa_attendanceOA公告日程公告发布、日程、会议oa_notice / oa_calendarCRM线索管理线索导入、分配、转换crm_clueCRM客户管理客户档案、公海池、回收crm_customerCRM商机合同商机阶段、合同、回款crm_opportunity / crm_contractCRM跟进记录电话、拜访、跟进计划crm_follow这个表建议你下载后对照着目录一个个找能帮你省掉很多瞎翻的时间。这套包的价值也在这里OA和CRM不是各做各的“两张皮”而是同一棵权限树下长出来的两个分支后续做二次开发不用在两个系统之间同步账号。2.2 权限模型一个账号两种数据视野它是典型的RBAC模型三张核心表sys_user用户、sys_role角色、sys_menu菜单权限中间用关联表把用户和角色、角色和菜单挂起来。真正决定这套系统好不好用的是角色表里的一个字段我一般叫它数据权限范围常见取值是self / dept / all。self代表只能看自己和下属的数据适合普通销售dept代表能看整个部门的数据适合销售经理all代表全部数据可见一般只给总经理或风控这类角色。这个字段在PC端和手机端是同一个判断逻辑也就是说手机端登录一个self权限的账号客户列表里同样只能看到自己名下的客户不会因为换了端就放开权限。权限这块最怕写成黑匣子。我见过不少二次开发的OA系统菜单是能控制了但列表查询条件里忘了拼数据权限结果一个普通销售调接口就能看到全公司客户这在OA这类企业系统里属于严重越权。这套v5.8在列表模型里普遍带了这个过滤后面第4章我会把改法拆开讲。2.3 手机端是怎么接进来的H5做皮接口做骨这套v5.8的手机端不是单独的APP而是H5页面。它有两种常见实现一种是整站响应式PC和手机共用一套模板靠CSS断点改布局另一种是独立手机端入口路由进mobile或api模块模板单独一套但控制器里复用的还是同一批模型和业务逻辑。我解压看到的是第二种更实用一些。手机端入口独立的好处是页面可以按触屏习惯重做不用担心PC端表格在手机上挤成一团坏处是接口层和PC端渲染层偶尔会出现逻辑分叉比如PC端改了一个查询条件手机端接口忘了同步。遇到这种情况别急着改模板先去看接口方法里的查询条件是不是和PC端控制器里的一致。手机端登录态在同域名下会复用session所以你在PC端登录了手机端打开同一个域名通常不用重新登录这是这类源码包默认的行为。3. 把v5.8跑起来Windows与Linux两条部署路径与四个参数3.1 环境版本先锁死能用不代表能跑久这套v5.8的源码是加密和解密混合的网上能下载到的源码包基本都是PHP写的。先按这个环境来而不是直接装最新版PHP不要碰8.x数据库不要急着上MySQL 8.0老源码里很多函数的写法在高版本下会直接报错或废弃警告刷屏。组件推荐版本说明PHP7.1 ~ 7.48.0 会弃用部分旧加密与字符串函数MySQL5.78.0需关闭严格模式否则字段默认值校验会翻车Nginx1.18主要用 rewrite 做伪静态Apache2.4用 .htaccess 同样能跑PHP扩展GD、fileinfo、PDO_MySQLGD缺失会导致验证码不显示、图片缩略失败Windows上我用phpstudy小皮面板切PHP 7.4Linux上用宝塔面板装PHP 7.4 MySQL 5.7这两个组合是这个源码包最稳的跑法。版本锁死不是玄学是老源码对运行环境的兼容性就停留在那个时代你给它PHP 8.0它未必接得住。3.2 解压、建库、导数据三步走别跳源码包解压之后先看根目录下有没有install目录或sql目录常见结构里sql文件名类似oa_crm_v5.8.sql。解压和建库我一般这样处理# 解压到网站根目录 unzip OA_CRM_v5.8.zip -d /www/wwwroot/oa_crm # 进入项目目录 cd /www/wwwroot/oa_crm # 创建数据库务必用 utf8mb4 mysql -uroot -p -e CREATE DATABASE oa_crm DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; # 导入主SQL文件 mysql -uroot -p oa_crm sql/oa_crm_v5.8.sql数据库用utf8mb4而不是老项目常见的utf8是因为手机端会存Emoji和生僻字utf8在插入这些字符时直接报错。SQL文件如果比较大比如超过50MB用mysql 导入容易中断我一般进到mysql命令行里用source命令导能看到进度也更稳。等导入完成后用show tables;确认核心表都建出来了再继续改配置。3.3 改数据库配置和运行模式数据库账号密码在config目录下的database.php里改别去翻install目录里的安装向导很多包把安装向导阉割了直接改配置最干净。// config/database.php return [ type mysql, hostname 127.0.0.1, database oa_crm, username root, password 你的数据库密码, hostport 3306, charset utf8mb4, // 调试阶段建议打开SQL日志方便定位问题 debug true, ];改完这四项基本就够了。如果登录后跳转有问题去config/app.php或config/route.php里看URL模式。我习惯把它调成兼容模式index.php?s/模块/控制器/方法这样路由出问题时能直接用域名加路径访问排查效率高很多。等确认系统稳定了再改回伪静态模式也不迟。3.4 伪静态不配好除首页外全是404这套系统的URL是基于路由解析的Nginx下如果只配了root和index没有rewrite规则你点任何菜单都会404。Nginx的配置我一般这样写server { listen 80; server_name your_domain.com; root /www/wwwroot/oa_crm/public; index index.php index.html; location / { if (!-e $request_filename) { rewrite ^(.*)$ /index.php?s$1 last; break; } } location ~ \.php$ { fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } }注意root指向的是public目录不是项目根目录这是很多新手翻车的点。Apache环境则是在public目录下放.htaccess规则写法略有差异IfModule mod_rewrite.c Options FollowSymlinks -Multiviews RewriteEngine On RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^(.*)$ index.php?s$1 [QSA,PT,L] /IfModule配完重启Nginx或Apache再访问域名能进登录页就说明路由通了。404页会提示“资源不存在”还是直接白屏前者通常是伪静态问题后者大概率是权限或PHP扩展问题排查方向完全不一样。4. 二次开发改哪里登录、审批流与客户权限的常用改法4.1 先读目录哪是后端、哪是前端、哪是手机端二次开发前先花十分钟把目录结构摸清楚。这类包通常按MVC组织我解压后看到的结构大概是oa_crm/ ├── application/ # 应用目录 │ ├── admin/ # PC端后台 │ │ ├── controller/ # Login、Index、Approval、Crm 等控制器 │ │ ├── model/ # 数据模型 │ │ └── view/ # 模板文件 │ ├── api/ # 手机端接口 │ │ └── controller/ # api 下的控制器返回JSON │ ├── common/ # 公共函数、公共模型 │ └── command/ # 命令行脚本 ├── public/ # 入口目录index.php 在这里 ├── static/ # css、js、图片 ├── config/ # database.php、app.php、route.php ├── sql/ # 安装SQL文件 └── runtime/ # 缓存、日志权限要可写controller里带Crm、Customer、Contract的是CRM相关带Approval、Attendance、Notice的是OA相关。手机端接口一般在api模块下路径类似/api/crm/customer_list返回JSON而不是HTML前端H5页面负责渲染。找到这条路后续调试就有方向了。4.2 改登录逻辑验证码、密码哈希与登录日志登录逻辑是所有二次开发的起点O客户系统登录绕不开四件事验证码校验、用户状态校验、密码哈希比对、登录日志写入。这套包在admin控制器下的Login里我简化后的核心逻辑是这样的// application/admin/controller/Login.php public function login() { $username trim(I(post.username)); $password trim(I(post.password)); $code trim(I(post.code)); // 1. 校验验证码这里用了session存储验证码 if (strtolower($code) ! strtolower(session(verify_code))) { return json([code 1, msg 验证码错误]); } // 2. 查询用户注意过滤status状态 $user M(sys_user) -where([username $username, status 1]) -find(); if (!$user) { return json([code 1, msg 用户不存在或已禁用]); } // 3. 密码校验老系统普遍是 md5(密码 . 盐) if (md5($password . $user[salt]) ! $user[password]) { return json([code 1, msg 密码错误]); } // 4. 写入session后面菜单加载全靠这一个凭证 session(uid, $user[id]); session(role_id, $user[role_id]); session(dept_id, $user[dept_id]); // 5. 写登录日志 D(SysLog)-addLog($user[id], 登录成功, get_client_ip()); return json([code 0, msg 登录成功]); }密码校验用的是md5加盐而不是password_hash这是老代码的通病算历史遗留。如果安全性要求高改起来不要只改比对那一行要连用户表里的password字段一起重新生成否则历史密码全部失效。session里存的uid和role_id是后面菜单加载的凭证二次开发时不要轻易改这两个key名很多地方的判断都依赖它们。4.3 改审批流状态机与审批人范围的判断OA类系统里审批流是最容易改崩的地方。这套包把审批抽象成了状态机核心字段是status常见的取值是草稿、待审批、通过、驳回四档。我做请假审批改造时通常把审批动作收敛成一个独立方法避免每个控制器里各写一套状态判断// application/admin/controller/Approval.php const STATUS_DRAFT 0; // 草稿 const STATUS_PENDING 1; // 待审批 const STATUS_PASS 2; // 审批通过 const STATUS_REJECT 3; // 已驳回 public function approve($id, $opinion, $isPass) { $info M(oa_leave)-where([id $id])-find(); if (!$info || $info[status] ! self::STATUS_PENDING) { return json([code 1, msg 当前状态不可审批]); } // 校验审批人是否有权限处理该申请 $authLevel $this-getAuthLevel($info[dept_id]); if ($authLevel $info[need_auth]) { return json([code 1, msg 你无权审批该申请]); } // 通过或驳回 $status $isPass ? self::STATUS_PASS : self::STATUS_REJECT; M(oa_leave)-where([id $id])-save([ status $status, approve_note $opinion, approve_time date(Y-m-d H:i:s), approve_uid session(uid), ]); // 驳回后要允许发起人重新编辑提交所以这里不做物理删除 return json([code 0, msg 处理成功]); }审批流改造有两个常见误用一是直接在控制器里改状态值造成流转链路上出现4、5这类幽灵状态二是驳回后把数据软删导致发起人想改都找不到记录。我的习惯是状态字段只用这四档驳回后让记录回到草稿状态发起人有权修改再重新提交。审批人权限的判断别写死在控制器里抽成getAuthLevel方法后面扩展多级审批会省很多事。4.4 改客户数据权限把“一串代码查全部”堵住CRM部分最常见的需求就是分数据权限。未改造的源码包查询客户列表经常是M(crm_customer)-select()这意味着任何登录用户都能查全量客户这对CRM来说是不可接受的。改法是给列表查询统一注入数据权限条件我一般放在模型的列表方法里// application/common/model/Customer.php public function getListByScope($uid, $roleId) { $where [is_deleted 0]; // 根据角色上的数据权限范围字段拼条件 $scope M(sys_role)-where([id $roleId])-getField(data_scope); if ($scope self) { // 只看自己和下属负责的客户 $userIds M(sys_user)-where( [leader_id $uid] )-getField(id, true); $userIds[] $uid; $where[owner_id] [in, $userIds]; } elseif ($scope dept) { // 看整个部门及子部门的客户 $deptIds $this-getChildDeptIds($user[dept_id]); $userIds M(sys_user)-where( [dept_id [in, $deptIds]] )-getField(id, true); $where[owner_id] [in, $userIds]; } // all 则不追加条件直接返回全部 return M(crm_customer)-where($where)-select(); }这里的owner_id是客户负责人字段大部分CRM包都会有。改成这件事不能只改列表还要同步改导出、统计、跟进记录查询否则列表限制了、导出没限制一样是越权。手机端如果走api接口要在api的控制器里复用同一个模型方法不要PC端一套查法、手机端另一套查法。5. 部署避坑数据库导入、PHP版本与手机端适配的六个常见问题5.1 现象PHP 8.0 一装登录直接 500不少人看到源码包就直接装最新版PHP 8.0打开登录页要么白屏要么500后台日志提示某个函数被废弃。原因是老源码里用了PHP 8.0已经移除的写法比如mysql_*系列函数或者依赖旧字符串函数的隐式转换。解决把PHP版本降到7.4这是这类包最稳的运行版本不要跟版本较劲。装好之后记得把runtime目录的缓存清一遍很多时候版本切对了但白屏还在就是缓存没清。5.2 现象SQL 导入到一半报“表已存在”或乱码源码包解压后里面可能不止一个SQL文件有主库表、初始数据、可选插件表一次性全导就会撞表和乱码。我之前导过一次中途报字段长度超限一看是SQL文件里混了旧版本的建表语句和新版本的插入语句。解决先看sql目录下有没有README或安装说明按文件命名顺序导报错时别硬扛把库drop掉重新建一个干净的再按顺序导。导入用的连接字符集要设成utf8mb4库、表、连接三层统一否则中文和表情符号会变成问号后面查数据时怎么看怎么难受。5.3 现象Nginx 下除了首页点任何菜单都 404登录页能打开但输入账号密码进去之后菜单全404这是伪静态rewrite没配好。这套系统的菜单URL都是路由格式比如/admin/approval/index没有rewrite规则时Nginx会当成真实文件路径去找必然404。解决把第3.4节那段location配置贴进server块root指向public目录然后重启Nginx。配完之后如果还有个别地址404去config/route.php看有没有自定义路由规则规则缺失就按/模块/控制器/方法的格式手动拼URL访问能确认是路由问题还是模块不存在。5.4 现象手机端登录页能打开验证码图片裂了PC端验证码正常手机端打开验证码却是裂图或空白这多半是GD库没启用而不是手机端的问题。验证码生成依赖php_gd2扩展GD没开或者版本不匹配生成的图片就是损坏的。解决在php.ini里打开extensionphp_gd2还要看有没有freetype支持验证码如果带字符旋转功能还需要字体目录可读。改完php.ini必须重启PHP进程光改不重启等于没改。手机上如果验证码图片加载慢多半是接口响应慢而不是GD的问题这时候去查数据库连接配置和日志模块的写入性能。5.5 现象改完企业logo手机端还是旧图后台把logo、公司名称全改了一遍PC端正常手机端还是原来的图和名字看着像没改一样。原因是这套包的后台富文本和logo路径是存在配置表里的但手机端的静态资源引用往往带版本号参数浏览器缓存了旧资源。解决先去sys_config表确认新logo的路径确实写进去了然后在手机端static资源的引用URL后面手动加个版本号比如?v20250101强制浏览器回源拉新资源。如果加了版本号还是旧图那就是webview缓存手机上清掉H5缓存再重新打开。顺手养成习惯凡是改静态资源统一改版本号参数这是最不伤元气的后悔药。5.6 现象考勤打卡时间差了 8 小时手机上打卡记录显示的时间比实际慢或快8小时典型症状是早上9点打卡显示成凌晨1点。原因就是PHP的时区没设置默认跑UTC而国内是UTC8。解决在php.ini里设date.timezone Asia/Shanghai同时看config里有没有时区配置项两边改成一致。MySQL连接也可以顺手设一下SET time_zone 8:00否则PHP端正常了数据库里存的CURRENT_TIMESTAMP还是按数据库会话时区算。这个坑很隐蔽因为打卡功能基本不会在部署当天就被认真检查等月底对考勤时才发现全乱了。6. 从密码到首页菜单一条调用链看透这套双端系统整套系统里最有价值的走读路径是从你输入账号密码到首页菜单渲染完这一条链。这条链走通了PC端和手机端的所有业务都是同一套骨架后面改哪个模块心里都有数。我沿着代码把这条链还原出来核心在登录成功后的菜单加载// application/admin/controller/Index.php public function index() { // 1. 登录凭证校验 if (!session(uid)) { $this-redirect(Login/index); } // 2. 按角色加载菜单并做了缓存 $roleId session(role_id); $menus S(menu_role_{$roleId}); if (!$menus) { $menus $this-buildMenuTree($roleId); S(menu_role_{$roleId}, $menus, 3600); } // 3. 把菜单和用户信息扔给模板渲染 $this-assign(menus, $menus); $this-assign(user, M(sys_user)-find(session(uid))); $this-display(index); }这段代码很薄但隐藏着系统运转的三个关键设计。第一步是session校验登出之后session被清空任何控制器跳到这里都会被打回登录页双端共用这一个判断。第二步是菜单缓存同一个角色第一次加载时构建菜单树之后3600秒内直接读缓存这就是为什么你改了菜单权限要清缓存才能生效runtime目录里那些带数字的文件就是缓存。第三步的buildMenuTree会递归查sys_menu表按当前角色的权限过滤出能看到的菜单项再拼成树形结构给模板渲染手机端打开首页走的也是同一条逻辑只是模板换了。这条链路理解透了这套v5.8源码对你来说就不再是黑匣子。改菜单权限不生效去清缓存别看数据库登录跳转死循环查session写入和读取是不是同一个key手机端和PC端看到的内容不一致对比两端的菜单加载条件。从那以后我每次拿到新的双端源码都会强制先走一遍这条登录到菜单的链路确认session怎么建、缓存存在哪、权限在哪过滤再去动业务代码。这套v5.8值得下载下来完整跑一遍跑通了你就明白OA和CRM为什么能装进同一个包也清楚自己后续该从哪个文件下手改。希望帮到你。本文还有配套的精品资源点击获取
返回列表