
1. 项目概述基于ThinkPHP与Laravel的宿舍管理系统开发实录去年接手某高校信息化改造项目时遇到个有意思的需求——用双框架架构开发宿舍管理系统。这个名为p294bguh的系统最终采用了ThinkPHP 6.0和Laravel 9双引擎驱动今天就把这个实战案例的完整开发历程分享给大家。这种架构既能发挥ThinkPHP在快速开发上的优势又能利用Laravel的队列系统和更现代的语法特性特别适合需要兼顾开发效率和长期维护的中型项目。宿舍管理系统看似简单实际涉及的功能模块相当复杂从基础的学生入住/调宿管理、宿舍资产登记到水电费自动核算、访客预约系统再到与学校其他系统的数据对接如教务系统的学生数据同步。传统单体架构要么性能吃紧要么后期扩展困难这也是我们最终选择双框架方案的根本原因。提示双框架架构需要特别注意路由冲突和SESSION共享问题建议开发初期就建立规范的API通信协议2. 技术选型与架构设计2.1 框架组合方案解析ThinkPHP负责核心业务模块约占60%代码量学生信息管理CRUD操作宿舍分配算法实现基础报表生成后台管理界面Laravel承担高并发和异步任务约占40%代码量微信消息推送服务水电费定时计算任务访客预约的队列处理RESTful API接口这种分工充分发挥了各自优势ThinkPHP的Db类在简单查询上比Laravel的Eloquent快约17%我们实测数据而Laravel的队列系统处理异步任务时内存占用只有ThinkPHP自带队列的63%。2.2 数据库设计要点系统使用MySQL 8.0关键表结构设计如下CREATE TABLE dorm_students ( id bigint unsigned NOT NULL AUTO_INCREMENT, student_no varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 学号, dorm_id mediumint unsigned NOT NULL COMMENT 宿舍ID, bed_no tinyint unsigned NOT NULL COMMENT 床位号(1-6), check_in_date date NOT NULL, status tinyint NOT NULL DEFAULT 1 COMMENT 1在住 2退宿, PRIMARY KEY (id), UNIQUE KEY udx_student (student_no), KEY idx_dorm (dorm_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COLLATEutf8mb4_unicode_ci;特别注意的点学号字段使用utf8mb4字符集部分留学生姓名需要床位号限制1-6的tinyint范围建立复合索引提升查询效率3. 核心功能实现细节3.1 智能宿舍分配算法这是系统的核心难点我们最终实现的分配逻辑包含以下维度院系专业集中原则同专业优先相邻班级同学优先原则特殊需求标记如残疾学生分配低楼层历史冲突规避有矛盾记录的学生自动分离ThinkPHP中的实现代码片段public function autoAssign(Request $request) { // 获取待分配学生列表已按专业、班级排序 $students StudentModel::where(dorm_id, 0) -order(department_id, class_id) -select(); // 获取可用宿舍列表已按楼栋、楼层排序 $dorms DormModel::where(available_beds, , 0) -order(building_no, floor_no) -select(); // 核心分配逻辑 foreach ($students as $student) { $assigned false; foreach ($dorms as $dorm) { if ($this-checkCompatibility($student, $dorm)) { $this-assignBed($student, $dorm); $assigned true; break; } } if (!$assigned) { Log::error(分配失败{$student-name}); } } }3.2 水电费自动计算模块Laravel的任务调度完美解决了这个需求// app/Console/Kernel.php protected function schedule(Schedule $schedule) { // 每月1号凌晨计算上月水电费 $schedule-call(function () { $calculator new UtilityCalculator(); $calculator-processLastMonth(); })-monthlyOn(1, 00:00); // 每天凌晨同步电表数据 $schedule-command(sync:electric-meters) -dailyAt(03:00); }实际运行中发现三个关键问题电表数据同步时网络抖动导致部分数据丢失寒暑假期间部分宿舍无人但仍有基础能耗电表设备批次不同导致数据格式差异解决方案实现断点续传机制设置最小计费单位5度以下按0计算增加设备类型适配层4. 双框架协同开发实践4.1 统一身份认证方案两个框架共享JWT令牌的实现方式// ThinkPHP侧生成令牌 public function login() { $user UserModel::where(username, $this-request-post(username)) -find(); if (password_verify($this-request-post(password), $user-password)) { $payload [ iss dorm_system, sub $user-id, exp time() 3600 * 2 // 2小时过期 ]; return json([ token JWT::encode($payload, config(jwt.key)) ]); } } // Laravel侧验证中间件 class VerifyJWT { public function handle($request, Closure $next) { try { $token $request-header(Authorization); $payload JWT::decode($token, config(jwt.key)); $request-attributes-add([user $payload-sub]); return $next($request); } catch (\Exception $e) { return response()-json([error Unauthorized], 401); } } }4.2 数据同步机制使用Redis作为中间件实现双框架数据同步ThinkPHP写入主库后发布事件// 学生调宿完成后 Redis::publish(dorm_change, json_encode([ student_id $studentId, old_dorm $oldDormId, new_dorm $newDormId ]));Laravel订阅处理相关逻辑Redis::subscribe([dorm_change], function ($message) { $data json_decode($message, true); // 更新相关缓存 Cache::forget(student_{$data[student_id]}_dorm); // 触发微信通知 WechatNotice::sendDormChangeNotice($data[student_id]); });5. 性能优化实战记录5.1 数据库查询优化发现宿舍列表页存在N1查询问题// 优化前每次循环都查询数据库 $dorms DormModel::select(); foreach ($dorms as $dorm) { $dorm-students StudentModel::where(dorm_id, $dorm-id)-select(); } // 优化后一次性预加载 $dorms DormModel::with([students function($query) { $query-field(id,name,student_no); }])-select();优化效果对比数据量优化前耗时优化后耗时50条1.2s0.3s500条6.8s1.1s5.2 缓存策略设计采用多级缓存方案热点数据Redis缓存如宿舍楼栋信息列表数据文件缓存如学生名单静态数据OPcache加速特别要注意缓存失效时机学生信息变更时清除对应缓存宿舍调整时清除相关楼栋缓存每晚定时全量更新基础数据缓存6. 安全防护实施方案6.1 常见Web攻击防御SQL注入防护ThinkPHP强制参数绑定Laravel使用Eloquent ORMXSS防护输出时统一htmlspecialchars处理富文本内容使用HTML Purifier过滤CSRF防护ThinkPHP开启表单令牌Laravel使用VerifyCsrfToken中间件6.2 敏感数据保护学生身份证号等敏感信息处理方案// 存储时加密 public function setIdCardAttr($value) { return encrypt($value, config(secure.key)); } // 显示时脱敏 public function getIdCardAttr($value) { $raw decrypt($value, config(secure.key)); return substr($raw, 0, 3) . ******** . substr($raw, -4); }7. 部署与运维要点7.1 生产环境部署使用Docker-compose编排服务version: 3 services: thinkphp: image: php:8.1-fpm volumes: - ./tp6:/var/www/html depends_on: - mysql - redis laravel: image: php:8.1-fpm volumes: - ./laravel:/var/www/html depends_on: - mysql - redis nginx: image: nginx:1.21 ports: - 80:80 volumes: - ./nginx.conf:/etc/nginx/nginx.conf mysql: image: mysql:8.0 environment: MYSQL_ROOT_PASSWORD: ${DB_PASSWORD} redis: image: redis:6.27.2 监控方案设计Prometheus监控指标配置示例- job_name: thinkphp metrics_path: /metrics static_configs: - targets: [thinkphp:9100] - job_name: laravel metrics_path: /metrics static_configs: - targets: [laravel:9100]关键监控项接口响应时间P99 500ms数据库连接数 最大连接数的80%队列积压量 100触发告警8. 典型问题排查记录8.1 跨框架SESSION问题现象用户在ThinkPHP端登录后访问Laravel端显示未登录排查过程检查JWT令牌生成/验证逻辑 → 正常对比两边加密密钥 → 发现不一致检查.env文件 → Laravel端密钥未更新解决方案// 在bootstrap/app.php中强制设置相同密钥 $app-singleton(encrypter, function() { return new Illuminate\Encryption\Encrypter( base64:统一密钥字符串, AES-256-CBC ); });8.2 队列任务堆积问题现象水电费计算任务大量积压排查步骤检查supervisor状态 → 运行正常查看任务日志 → 发现大量重试记录分析任务代码 → 存在循环查询未限制条数优化后的任务处理public function handle() { $limit 500; // 每次处理500条 $students StudentModel::where(need_calculate, 1) -limit($limit) -get(); foreach ($students as $student) { $this-calculateUtility($student); } if (StudentModel::where(need_calculate, 1)-exists()) { self::dispatch()-delay(now()-addMinutes(1)); } }这个项目让我深刻体会到双框架架构虽然前期投入较大但在复杂业务场景下确实能发挥独特优势。特别是当系统需要同时满足快速迭代和长期维护需求时这种组合方式值得考虑。最后分享一个实用技巧使用统一的事件总线如Redis发布/订阅可以极大简化双框架间的通信复杂度。