拓冰建站拓冰建站
首页 / 资讯中心 / 正文

校园跑腿多开小程序源码:ThinkPHP+uni-app多站点接单大厅架构解析

简介一份面向校园创业团队与独立开发者的多开校园服务小程序源码基于ThinkPHP后端与Uniapp前端搭建支持一套后端无限多开小程序并各自独立管理适用于跑腿、外卖、表白墙、二手交易、快递代取等校园场景。源码全开源可二次开发后台采用PHP实现前端为Uniapp同时提供公众号信息通知、无限开通站点、商家入驻、跑腿认证、社交圈子与接单大厅等完整功能。资源包共2017个文件以js、json、html、css、vue、php等类型为主其中前端页面与逻辑、接口配置、后台管理代码均有覆盖并附带SQL数据库文件与部署脚本。压缩包包体约70.45MB目录结构清晰便于按需修改与部署。目前已有343人学习浏览适合具备一定PHP和前端基础、希望快速搭建或定制多校区校园服务小程序的开发者作为参考。1. 独立版校园跑腿多开校园小程序源码是什么多站点接单大厅背后的架构选择一套跑腿系统如果只服务一个学校单小程序就够用但校园团队通常要同时运营多个校区或对外招商代理。每个站点单独部署一套后端服务器成本和维护成本都会成倍叠加。这套版本 2.5.7 的独立版校园跑腿校园社区多开小程序源码把地区、学校、订单、社交圈全部塞进同一个 ThinkPHP 后端用站点标识做数据隔离前端则通过不同的小程序包独立管理。它不是普通外卖跑腿脚本而是带接单大厅、社交圈、诚信认证、优惠券的全流程系统跑腿、外卖、表白墙、二手、快递都能落地。后端为 PHP ThinkPHP前端为 uni-app 可二次开发。下面的内容会从多开数据模型、Nginx PHP7.2 MySQL8.0 部署、小程序多开配置到三个典型 bug 修复逐个拆给你看。2. ThinkPHP 后端与 Uniapp 前端的多开数据模型多开不是堆代码而是有一套能让所有站点共享同一个后端进程的数据组织方式。这套源码用的是「统一后端 site_id 垂直隔离」设计后台只部署一套但数据库中每张业务表都带有站点标识前端小程序启动时把自己的标识传给后端后端所有查询都强制过滤该标识。这样每个校区、每个代理点都能拿到独立数据但共享同一套登录、支付、模板消息模块。2.1 多开站点的数据表设计与 school_id 隔离打开源码的 database 目录你会发现表设计遵循一个隐性约定几乎所有业务表都包含school_id或site_id字段。比如表名关键字段隔离用途userid, school_id, role用户属于哪个学校决定接单范围order_listorder_id, school_id, user_id跑腿订单只能被同校骑手看到circle_postpost_id, school_id, user_id社交圈按校区展示coupon_userid, school_id, coupon_id优惠券展示与使用必须在当前校区school_infoschool_id, school_name站点独立配置支持单独修改这种设计的核心好处在于新增一个校园站点时不需要改动后端代码、不需要新增数据库只需要在school_info表里插入一条记录然后给该站点生成一个小程序 AppID。每条业务查询都要带上这个隔离字段否则就会出现 A 学校的人抢到 B 学校的单子这类数据串台问题。我一般建议在不改表结构的情况下统一给所有查询加一个过滤 SQL 片段-- 查当前学校跑腿订单 SELECT * FROM order_list WHERE school_id :school_id AND status IN (pending, accepted) ORDER BY create_time DESC LIMIT 20;这个 SQL 是三条核心规则第一个条件是强制站点隔离第二个条件限定订单状态第三个条件控制列表长度。实际项目中如果发现某些页面数据异常优先检查是否漏了school_id条件特别是使用了JOIN的表。2.2 ThinkPHP 多应用路由与站点标识透传源码后台是 ThinkPHP 框架版本对应的目录结构一般会把application/api、application/admin等模块拆分出来。多开站点不能靠模块名无限膨胀更好的方式是「单入口 参数路由」所有请求进入同一个 api 模块然后根据 header 或 GET 参数里的site_code切换数据源。在application/api/common.php里通常会有类似这样的公共方法function current_school_id() { static $schoolId null; if ($schoolId ! null) { return $schoolId; } $schoolCode request()-header(site-code, ); $school db(school_info)-where(site_code, $schoolCode)-find(); $schoolId $school ? $school[school_id] : 0; return $schoolId; }这段代码做了三件事定义了一个静态缓存变量避免重复查库从请求头中取site-code查询学校信息表后返回对应的school_id。这里的site-code需要前端每次请求都携带才能保证数据不会串校区。在.nginx反向代理环境下你还得确保site-code头没有被过滤常见做法是在fastcgi_param HTTP_SITE_CODE $http_site_code;里明确定义。2.3 uni-app 请求封装让每个小程序各回各家前端 uni-app 打包多套小程序时每套小程序的manifest.json中会有不同的mp-weixin.appid但它们访问的 API 域名可以是同一个后端域名。为了让后端识别请求来自哪个小程序最稳妥的方案不是依赖小程序原始数据而是让每个小程序在请求头中主动带上自己的site_code。utils/request.js里可以这样封装const BASE_URL https://api.example.com; export function request(path, data, method GET) { return new Promise((resolve, reject) { uni.request({ url: BASE_URL path, method: method, data: data, header: { site-code: uni.getStorageSync(site_code) || , token: uni.getStorageSync(token) }, success: (res) resolve(res.data), fail: (err) reject(err) }); }); }注意这里的site_code是在小程序启动时从上一个页面或后端初始化接口获取的不能从 storage 里硬编码。每套小程序多开时只需要在各自的/App.vue里优先调用一次/api/site/init接口把该小程序的site_code拉下来并缓存之后就统一由request方法携带。这个封装同时解决了登录 token 自动附带的琐碎问题也避免了散落在页面里的重复请求代码。3. Nginx PHP7.2 MySQL8.0 环境部署与版本 2.5.7 配置部署这套源码时建议直接按它测试过的环境来Nginx PHP 7.2 MySQL 8.0。PHP 7.2 对 ThinkPHP 的兼容性最好MySQL 8.0 则在 json 字段和全文索引上更顺手。如果使用 PHP 7.4 或 8.0部分each()或create_function()的兼容性警告会导致后台白屏这方面需要额外处理。3.1 解压源码后必须修改的三类配置文件源码压缩包解压后第一件事不是上传到服务器而是把配置文件按你的运行环境改好。第一数据库配置。在application/database.php或根目录.env文件中修改连接信息return [ type mysql, hostname 127.0.0.1, database xiaoyuan, username root, password your_password, hostport 3306, prefix xy_, charset utf8mb4, ];这里的prefix一定要和实际 SQL 导入后的表前缀一致否则框架自带 SQL 查询时会显示表不存在。若你是安装包解压后自带的.sql文件导入通常前缀默认是xy_这点在导入前可以先打开.sql文件头部确认。第二缓存配置。如果使用 Redis需在config/cache.php中设置type redis, host 127.0.0.1, port 6379, password , select 0,如果只是本地测试且不准备安装 Redis建议仍使用file缓存避免队列和验证码功能报错。第三URL 重写配置。多应用模式需要开启url_route_on和url_convert否则访问/api/order/list会变成 404。这些配置通常已经在源码自带文件中只需要确认application/config.php中的url_common_param状态。3.2 Nginx 站点配置与伪静态规则部署 ThinkPHP 项目时Nginx 必须把请求重写到public/index.php否则路由无法解析。下面是一个可用的 server 配置server { listen 80; server_name campus.app; root /var/www/xiaoyuan/public; index index.php index.html; location / { try_files $uri $uri/ /index.php?s$uri$args; } location ~ \.php$ { fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param HTTP_SITE_CODE $http_site_code; } location ~ \.(js|css|png|jpg|gif|svg|woff2)$ { expires 30d; access_log off; } }try_files是关键它会在找不到实际文件时把请求转发给index.php然后 ThinkPHP 通过s参数识别路由。fastcgi_param HTTP_SITE_CODE把前端上传的site-code头传递给 PHP这样request()-header(site-code)才能读到值。不写这一行PHP 收到头的字段名为HTTP_SITE_CODE但 ThinkPHP 的header()方法会自动兼容这种情况不过显示写出更稳妥。3.3 数据库导入与常见安装报错拿到源码后数据库导入建议用命令行而不是用 phpMyAdmin因为文件体积可能较大且包含存储过程mysql -u root -p -h 127.0.0.1 --default-character-setutf8mb4 xiaoyuan xiaoyuan.sql这里的--default-character-setutf8mb4是为了防止中文乱码Windows 下 CMD 和 PowerShell 编码不同可能会遇到ERROR 1064之类语法错误。解决办法是先把.sql文件用 Notepad 转为 UTF-8 无 BOM 格式。安装过程中最常见的两个报错报错信息原因解决办法SQLSTATE[HY000] [2054] Server sent charset unknown to the clientMySQL 8.0 默认字符集和连接字符集不匹配在database.php中设置charset utf8mb4SQLSTATE[HY000] [2054] The server requested authentication method unknownPHP 7.2 的 mysqlnd 不兼容 MySQL 8.0 默认的caching_sha2_password执行ALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY 密码;注意第二种情况是 MySQL 8.0 PHP 7.2 特有的坑PHP 7.2 对 MySQL 8.0 的默认认证插件支持不好。如果是正式环境最好在my.cnf的[mysqld]段设置default-authentication-pluginmysql_native_password这样新用户也会默认使用兼容认证方式。4. 小程序多开与公众号通知的二次开发要点这个版本最大的卖点是支持小程序多开同一个后端可以给多个微信小程序提供服务每个小程序有独立的管理端和用户数据。多开不是简单复制几份代码而是每个小程序要有自己的 AppID、业务域名、隐私协议后端再根据site_code把用户和订单分配到对应站点。4.1 修改小程序 AppID 与服务器域名配置使用 uni-app 打包微信小程序时在源目录的manifest.json中修改每个小程序的独立配置{ mp-weixin: { appid: wx1234567890abcdef, setting: { urlCheck: false, minified: true }, usingComponents: true } }在 HBuilderX 里发行为微信小程序时选择「使用 devtools 打开」随后你会看到微信开发者工具中显示的 AppID 就是上面配置的。服务器域名则需要在「微信公众平台 — 开发 — 开发设置 — 服务器域名」中为每个小程序单独添加request合法域名例如https://api.example.com。这里有一个容易踩的坑多个小程序共用后端域名时后端接口里不要只依赖Referer头做判断因为微信小程序的Referer是固定的不同小程序的Referer前缀不同但后端 PHP 拿到的是经过 nginx 转发的实际地址所以建议你从site_code判断来源而不是从Referer判断。4.2 接单大厅、社交圈列表如何按 site_id 过滤当多个学校同时在线时接单大厅和圈子数据必须要做双条件过滤站点条件 业务状态条件。常见的错误是在列表查询中只写了where(school_id, $schoolId)却忘了表连接里的其他表也关联了学校。比如查询跑腿订单列表还要关联用户表应该这样处理SELECT o.order_id, o.school_id, o.user_name, o.create_time FROM order_list o INNER JOIN user u ON u.id o.user_id AND u.school_id o.school_id WHERE o.school_id :school_id ORDER BY o.create_time DESC;上面的INNER JOIN条件中带上了u.school_id o.school_id从数据层面保证即使某个订单的school_id被错误改掉也无法跨学校拉到对应用户信息。实际项目中如果缓存了热门订单列表缓存键也需要加入school_id比如order_hot_{$school_id}否则不同学校的学生打开接单大厅会看到同一批订单。4.3 公众号模板消息在 ThinkPHP 中的队列发送源码支持公众号信息通知实际上是对每个站点独立配置公众号 AppID 和 Secret。由于发模板消息不能阻塞下单流程常见做法是把它丢进队列。ThinkPHP 5.1 中的队列使用示例use think\Queue; class SmsQueueJob { public function fire($job, $data) { $openid $data[openid]; $tmplId $data[template_id]; $content $data[content]; // 调公众号接口发送模板消息 // 发送成功后调用 $job-delete(); if ($this-send($openid, $tmplId, $content)) { $job-delete(); } } }在业务代码中投递任务例如用户下单通知骑手Queue::push(app\api\job\SmsQueueJob, [ openid $openid, template_id db(site_config)-where(school_id, $schoolId)-value(wx_new_order_tpl), content [ keyword1 新的跑腿订单, keyword2 $orderNo, ], ], smsQueue);这里值得注意的点是template_id不能全局统一每个学校在自己的公众号后台申请的模板 ID 不同因此需要从site_config中按school_id取。如果某个学校申请的是订阅消息而不是模板消息接口返回42001之类的错误代码里要能区分错误码并降级为小程序订阅消息。5. 优惠券显示、跑腿认证与学校信息二次修改的三处修复实践版本 2.5.7 在更新日志里专门提到了三个修复点这三处恰好也是最容易在二次开发时被动到的地方。第一处是优惠券前端不显示第二处是跑腿无法认证第三处是学校信息不能二次修改。我把自己排查时的思路整理如下这几个思路可以直接迁移到其他模块。5.1 优惠券列表前端不显示接口字段层级不一致原代码中优惠券列表接口返回的数据层级可能是data.list而前端 uni-app 页面读取的是data.data导致页面拿不到优惠券。修复时我建议在后端控制器中统一输出格式public function couponList() { $schoolId current_school_id(); $list db(coupon_user) -where(school_id, $schoolId) -where(is_used, 0) -select(); return json([code 1, msg ok, data $list]); }前端对应的请求代码则用下面这种兼容写法防止接口版本不同导致页面白屏const res await request(/api/coupon/list); const couponList res.data.data ? res.data.data : res.data.list;核心思路是无论后端返回什么字段名前端都做一个兜底取值。修复完这处优惠券状态翻转逻辑也要检查领券时不要直接插入coupon_user而是先查school_id user_id coupon_template_id是否存在防止重复领取导致前端展示异常。5.2 跑腿认证无法通过状态码与审核状态机跑腿认证这个模块问题通常出在状态码不统一。数据库里认证状态用0/1/2表示未审核、通过、驳回但是前后端代码里有人写出了status 2表示通过。修复的方法是统一常量并在认证接口里加入状态机校验。以 ThinkPHP 控制器方法为例// 骑手认证 public function riderAuth() { $userId get_user_id(); $status db(rider_auth)-where(user_id, $userId)-value(status); if ($status 2) { return json([code -1, msg 您的认证已被驳回请联系管理员]); } // 允许提交或修改 db(rider_auth)-where(user_id, $userId)-update([ real_name input(real_name), id_card input(id_card), school_id current_school_id(), status 0, ]); return json([code 1, msg 提交成功]); }这里我卡住过一次用户认证驳回后没有清掉旧认证照片的缓存导致图片还是旧的。所以要记得在更新时顺带清理auth_image_开头的缓存键否则前端重新提交后仍显示上一次审核通过的样式。5.3 学校信息二次修改唯一索引冲突“学校不能二次修改”这个 bug 大概率不是逻辑问题而是数据库表结构中school_code或domain字段设了唯一索引。当管理员编辑校区信息时这条记录本身已经占用了该唯一值再执行更新就会报1062 Duplicate entry。修复方式是让更新语句不要更新唯一索引字段或者先排除当前记录再检查唯一性。在application/admin/controller/School.php里这样写$schoolId input(school_id); $schoolCode input(school_code); $exists db(school_info) -where(school_code, $schoolCode) -where(school_id, , $schoolId) -find(); if ($exists) { return json([code -1, msg 该学校代码已被占用]); }同时给数据库更新语句加上where(school_id, $schoolId)条件只在当前记录范围内更新不要用saveAll批量更新。验证修改是否真实生效可以直接在管理后台改一次学校名称再查看前端小程序个人中心里的学校名是否同步变化。如果没变化那问题通常在缓存层而不是数据库。本文还有配套的精品资源点击获取
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门