ThinkPHP+FastAdmin+uni-app上门预约系统源码拆解与二次开发

发布时间:2026/10/23 16:17:16
ThinkPHP+FastAdmin+uni-app上门预约系统源码拆解与二次开发 简介一套基于ThinkPHPMySQLuniappFastAdmin后台开发的上门家政按摩H5小程序与预约系统源码面向需要快速落地同城O2O上门服务的开发者、外包团队及产品经理可直接部署或二次开发。程序已测试通过配套完整包含公众号通知、后端主程序、前端uniapp源码、数据库SQL及通知音频文件。资源包共2000个文件大小66.31MB主要文件类型为865个JavaScript、546个Vue组件、350个Markdown文档、91个JSON配置以及HTML、CSS、XML、Shell、SQL、Doc、Python等文件覆盖前端页面、接口交互、数据库脚本和环境配置。运行环境需LinuxNginxPHP7.2MySQL5.6并安装fileinfo、Redis扩展系统基于微信生态需备案域名、认证服务号、微信支付及小程序账号H5指公众号H5须在微信中访问。目前已有255人学习下载。可借源码理解FastAdmin权限管理与uniapp多端开发的完整协作流程掌握预约下单、服务派单、支付回调、公众号通知和音频提醒等关键模块的落地实现是学习同城上门预约类项目的实用参考。1. 上门预约系统到底解决了什么问题一套三端代码的本地生活服务样板做本地生活服务的人应该都有同感用户端想要小程序顺手还能分享H5管理端要能派单、能结算、能调整服务价格两头都不能少。这套基于 thinkphp mysql uniapp 组合的上门预约系统把几个端一次性凑齐了——前台用 uni-app 生成微信小程序和 H5后台用 fastadmin 搭好了管理界面服务端用 ThinkPHP 写接口MySQL 负责订单、技师、用户、地址这些核心数据。对想快速上线上门家政、按摩、保洁这类业务的团队来说它省掉的不只是 UI 工作量还有从下单、派单到完工结算的整条业务链路。下面按源码实际结构拆开讲。2. 三端架构拆解ThinkPHP、FastAdmin 与 uni-app 如何各司其职这套系统的架构一句话能说清一个基于 ThinkPHP 的 FastAdmin 应用提供全部后端能力用户端是独立的 uni-app 工程数据统一存在 MySQL 里。FastAdmin 跑在同一个 PHP 应用上通过 admin 模块提供后台用户端走 api 模块两者共用同一套模型和配置。这种一个应用两套入口的结构是 FastAdmin 系源码最常见的部署形态好处是二次开发时不用维护两套后端坏处是路由和权限要分清楚别让前端接口和管理页面混在一起。2.1 为什么是这套组合二次开发视角的选型逻辑先说结论这套组合选的不是最强的技术而是最省人力的技术。FastAdmin 基于 ThinkPHP自带管理员登录、权限节点、通用列表和表单生成后台管理页基本不用从零写uni-app 用 Vue 语法写一套代码编译输出微信小程序和 H5对上门预约这种重分享、轻安装的业务刚好合适thinkphp mysql 的部署资料多一台 Nginx 就能撑起一个小型平台。从改造角度看这套选型还有一个隐藏优点api 模块和 admin 模块在同一个应用里订单模型在 application/common/model 下是前后端共用的。后面加字段、改状态、接支付只需要改一处模型不会出现后台改完了接口没跟上的问题。等业务量上来接口和后台也可以拆成两个应用分别部署目录结构已经留好了拆分边界。2.2 目录结构后端、后台、前端各自的主力目录与用途源码解压后主要看两部分PHP 后端工程和 uniapp 前端工程。下面这张表把最常用的目录列出来其他目录大多可以暂时忽略。关注点目录说明站点入口public/Web 根目录必须指向这里下有 index.php 与 uploads后台控制器application/admin/controller/FastAdmin 管理端按订单、服务、技师等模块分文件前台接口application/api/controller/H5 / 小程序请求的控制器Order、Service、Technician 等数据层application/common/model/订单模型、技师模型前后端共用数据库配置application/database.php数据库连接参数、表前缀前端页面uniapp/pages/首页、服务列表、确认订单、订单详情等页面前端公共uniapp/common/request.js 等公共方法几个重点目录展开说。application/api/controller 是用户端实际打到的接口层FastAdmin 的 API 模块路由通常是 index.php?s/api/控制器/方法前端请求路径会拼成类似 /api/order/create 的形式。public 下的 uploads 存放后台上传的服务图、技师头像站点根目录必须指向 public否则上传文件会落到外部目录导致前端加载不到。uniapp 前端里pages.json 控制页面路由和 tabBarmanifest.json 是小程序 appid、H5 域名这些配置的集中地联调时改的最多的就是这两个文件加 common/request.js。2.3 数据库设计订单表、技师表、服务表怎么配合表前缀默认是 fa_安装时也可以改。核心就三张业务表加一张用户表。服务项目表结构大致是这样字段类型说明idint主键namevarchar服务名称如保开关、推拿pricedecimal(10,2)单价单位元unitvarchar计费单位次 / 小时service_minutesint预计服务时长category_idint所属分类statustinyint1 上架0 下架技师表 fa_technician 除了姓名、头像、手机号之外最重要的是 lng、lat、server_radius 三个字段。lng 和 lat 是技师当前位置坐标server_radius 是服务半径单位公里派单时用这两个字段做距离筛选。订单表 fa_order 则是业务核心包含 order_no 订单号、user_id、service_id、technician_id、address、address_coordinate、amount、status、serve_time、pay_time、finish_time、remark 和创建时间。address_coordinate 字段存的是经度,纬度逗号拼接的字符串派单 SQL 会直接用它做计算。订单状态 status 是这套源码的灵魂常见的定义是 1 待支付、2 已支付待派单、3 已派单、4 服务中、5 已完成、6 已取消。价格字段统一 decimal(10,2)不要用 float否则累计对账时会出现精度问题。这些字段定义是理解后面核心代码的前提。3. 核心代码链路一次预约从下单到派单的完整实现把用户操作到后台派单的过程串一遍用户选服务填地址和时间提交订单后台按距离找技师技师接单开始服务完成订单。对应到代码上是三个核心文件前端的请求封装、后端的下单控制器、派单控制器。下面按这条链路逐个拆。3.1 统一请求封装token 与 401 跳转都收在一处所有前端请求走同一个封装函数是这类项目最基础也最重要的约定。uniapp 工程里 common/request.js 负责统一拼接域名、带 token、处理登录过期。// uniapp/common/request.js const BASE_URL https://api.yourdomain.com/api; function request(path, data {}, method GET) { return new Promise((resolve, reject) { uni.request({ url: BASE_URL path, method, data, header: { token: uni.getStorageSync(token) || , Content-Type: application/json }, success: res { // 后端统一返回 code 字段401 表示登录态失效 if (res.data.code 401) { uni.removeStorageSync(token); uni.navigateTo({ url: /pages/login/login }); reject(res.data); return; } resolve(res.data); }, fail: reject }); }); } export default request;逻辑说明每个请求自动从本地缓存拿 token 放进 header后端靠这个 token 识别用户身份一旦接口返回 401说明 token 失效或未登录前端统一清理本地状态并跳转登录页。BASE_URL 是联调时第一个要替换的地方后续所有页面都通过 import request 来发请求不要分散在各页面里用 uni.request 裸调。参数说明path 是接口路径data 是请求体method 默认 GETPOST 接口调用时显式传入第三个参数。后端要求的 token 字段名是 token如果你的项目改成了 Authorization这里和 FastAdmin 的 API 验权配置要同步改否则会出现登录成功但业务接口全部未登录的问题。3.2 创建订单接口必传参数、状态初值、事务用户点提交订单时前端把服务 ID、地址、期望上门时间、备注 POST 到 api/order/create。后台控制器的核心逻辑是校验服务是否存在组装订单数据写库并返回订单 ID。// application/api/controller/Order.php public function create() { $uid $this-user_id; $service_id $this-request-post(service_id); $address $this-request-post(address); $serve_time $this-request-post(serve_time); $remark $this-request-post(remark, ); $service ServiceModel::get($service_id); if (!$service || $service-status ! 1) { $this-error(服务项目不存在或已下架); } $order_data [ order_no date(YmdHis) . mt_rand(1000, 9999), user_id $uid, service_id $service_id, amount $service-price, address $address, serve_time $serve_time, remark $remark, status 1 // 待支付 ]; Db::startTrans(); try { $order OrderModel::create($order_data); Db::commit(); $this-success(下单成功, [order_id $order-id]); } catch (\Exception $e) { Db::rollback(); $this-error(下单失败请重试); } }逻辑说明先查服务表确认服务存在且在上架状态再组装订单数据。order_no 用时间加随机数生成保证不会重复amount 直接取服务价格不让前端传价格这是防止用户篡改金额的基本做法。status 初始化为 1 待支付。事务在这里虽然没有跨表写入但后续如果加优惠券核销、加积分记录事务就非常必要了所以保留这个结构。参数说明service_id 和 address 是必传serve_time 是预约时间remark 可选。前端传价格一律不接收。下单成功后返回 order_id前端再用这个订单 ID 唤起支付或跳转订单详情。如果想加提交订单时锁定技师在 order_data 里加 technician_id 字段即可第 6 章会专门讲这个改造。3.3 附近技师查询一段基于经纬度的派单 SQL派单是整个上门预约系统的核心难点。这套源码里的做法很直接以订单地址的经纬度为中心计算技师表里每个在岗技师的直线距离过滤出服务半径内的技师按距离升序返回。// application/api/controller/Assign.php public function findNearby() { $order_id $this-request-post(order_id); $order OrderModel::get($order_id); if (!$order) { $this-error(订单不存在); } list($lng, $lat) explode(,, $order-address_coordinate); $list TechnicianModel::where(status, 1) -field(id, name, avatar, server_radius, (6378.138 * 2 * ASIN(SQRT(POW(SIN(({$lat} * PI() / 180 - lat * PI() / 180) / 2), 2) COS({$lat} * PI() / 180) * COS(lat * PI() / 180) * POW(SIN(({$lng} * PI() / 180 - lng * PI() / 180) / 2), 2)))) AS distance) -having(distance server_radius) -order(distance) -select(); $this-success(附近技师, $list); }逻辑说明这段 SQL 用的是 Haversine 公式6378.138 是地球半径公里数distance 的单位是公里。查询条件有两个技师状态为 1在岗且算出来的距离小于技师自己设定的服务半径。结果按距离从近到远排前端拿到的就是一个可选的技师列表。参数说明address_coordinate 字段必须是经度,纬度格式如果为空这条 SQL 会直接报错。常见的兜底做法是退化成按城市加状态筛选先在地址表里解析出城市字段然后查 city_id 匹配的技师。这个兜底建议在改造时加上能避免订单因为缺坐标卡死在派单环节。3.4 FastAdmin 后台订单列表与状态切换怎么配置后端接口就绪后管理后台的订单列表不需要手写页面FastAdmin 提供了命令行生成器。在应用根目录执行以下命令就能给 fa_order 表生成一套标准的后台增删改查php think crud -t fa_order -u 1执行后application/admin 下会出现 Order 控制器和视图文件后台菜单自动挂到 admin/order 下。列表展示哪些字段、字段怎么格式化由 application/admin/view/order/index.html 里对应的 JS 配置 control 决定。订单状态切换是运营最频繁的操作对应的代码逻辑通常长这样// application/admin/view/order/index.html 对应的事件绑定 $(.btn-change-status).on(click, function () { var id $(this).data(id); $.ajax({ url: order/changeStatus, data: { id: id, status: 3 }, // 3 表示已派单 success: function (res) { if (res.code 1) { window.location.reload(); } } }); });逻辑说明changeStatus 是后台控制器里自定义的方法接收订单 ID 和目标状态更新后刷新列表。这套结构的好处是每个状态流转动作都可以挂独立的校验比如从服务中改成已完成时强制填写完结金额从已派单改成已取消时记录取消原因。参数说明data 里的 id 是订单主键status 是目标状态码。后台控制器里对应接收这两个参数并在操作前用管理员权限做一次验权防止越权改单。4. 本地搭建与运行从空服务器到 H5小程序跑通这一章目标很直接把这套源码在你自己的机器上跑起来。先说后端初始化再说前端联调最后给一个三端已经连通的判断方法。整个过程中 90% 的问题集中在配置和路径上代码本身基本不用动。4.1 后端初始化导库、改配置、配伪静态环境准备按 FastAdmin 的常规要求来PHP 7.1 到 7.4 最稳MySQL 5.7 以上Nginx 或 Apache 都行。先把数据库建好再把源码自带的 SQL 文件导入。SQL 文件一般在根目录或 sql 目录下不同发行版位置不同先找一下。mysql -uroot -p -e create database fastadmin_home default charset utf8mb4; mysql -uroot -p fastadmin_home install.sql两条命令的含义第一条创建名为 fastadmin_home 的数据库指定 utf8mb4 字符集第二条把 install.sql 里的表结构和初始数据导入这个库。导入成功后会看到 fa_user、fa_order、fa_technician 等表。接着修改 application/database.php把数据库连接参数换成你自己的。需要改的字段是 hostname、database、username、password、prefix。prefix 默认 fa_如果导入的 SQL 里表前缀不是 fa_这里必须改成一致的否则后台登录会报表不存在。最后是伪静态配置。FastAdmin 的路由是 pathinfo 风格Nginx 下站点根目录要指向 public并加上 rewrite。配置示例server { listen 80; server_name localhost; root /path/to/项目根目录/public; index index.php; location / { if (!-e $request_filename) { rewrite ^(.*)$ /index.php?s$1 last; } } }提示根目录指错是最常见的启动失败原因。一定要指到 public 目录不是项目根目录。配置完成后访问 http://localhost/index.php?s/admin 进入后台默认管理员账号通常为 admin密码 admin 或 123456。上线前第一件事就是改掉默认密码不要在这上面偷懒。4.2 前端联调H5 与微信小程序对接后端的地方不一样前端工程用 HBuilderX 打开 uniapp 目录第一步修改 common/request.js 里的 BASE_URL。如果后端跑在本机H5 开发模式建议用代理解决跨域不用改 BASE_URL 的域名。manifest.json 的 h5 节点配置本地代理// uniapp manifest.json 的开发配置片段 h5: { devServer: { proxy: { /api: { target: http://127.0.0.1:8080, changeOrigin: true } } } }逻辑说明开发环境下前端请求 /api 开头的地址会自动转发到本机 8080 端口的 PHP 应用绕开浏览器跨域限制。生产环境 H5 直接配 HTTPS 域名不需要代理。微信小程序和 H5 的差别在域名校验。微信开发者工具里默认会校验 request 合法域名本地联调时可以在详情 → 本地设置里勾选不校验合法域名。但真机预览和体验版必须在小程序后台把 API 域名加入 request 合法域名而且要求是已备案的 HTTPS 域名。这点后面避坑章还会再提。4.3 三端已连通的判断方法跑通后怎么确认链路是通的按三条线自检第一打开后端 runtime/log 目录下的日志文件看请求是否进来、在哪一步停下第二用接口调试工具先调登录接口拿 token再带 token 调一次订单列表接口接口能通说明 PHP 应用本身没问题第三在后台创建一个测试订单然后查 MySQL 的 fa_order 表能看到新记录就说明数据链路完整。这三条自检顺序固定日志确认请求到达接口确认逻辑正确数据库确认落库成功。按这个顺序排查能把前端问题和后端问题快速分开避免在两端来回猜。5. 避坑记录搭建与联调中最常踩的五个坑这一章是实际操作里反复遇到的坑按现象到原因再到解决展开。每一条都对应一个具体的排查路径希望你看完之后遇到同类问题能直接定位。5.1 小程序请求全部失败url not in domain list现象微信开发者工具里所有 uni.request 请求都报 request:fail url not in domain list页面数据全空。原因小程序平台对 request 接口有域名白名单限制。开发者工具默认开启合法域名校验而你的 API 域名没在小程序后台配置或者开发者工具里的不校验合法域名选项没勾上。解决本地联调阶段在微信开发者工具的详情 → 本地设置里勾选不校验合法域名真机预览和发布前到微信公众平台把 API 域名加入 request 合法域名。注意域名必须是 HTTPS、已备案且协议和域名要完全一致http 和 https 不能混着填。5.2 后台图片上传成功前端加载不出来现象FastAdmin 后台上传的服务图片、技师头像显示正常但小程序和 H5 页面请求这些图片时 404 或空白。原因上传文件默认落在 public/uploads 目录正确访问路径是 /uploads/xxx.jpg。但很多部署把站点根目录指到了项目根目录导致上传文件实际在项目根目录以外或路径不对前端拼接的 URL 自然打不开。解决先把 Nginx 站点根目录固定到 public这是根本前端图片地址用相对路径 /uploads/xxx.jpg不要在前端写死带域名和端口号的绝对地址避免域名一换就大面积裂图。5.3 登录成功但其他接口全部提示未登录现象登录接口返回 token 正常前端也存进了缓存但一点进订单详情就跳回登录页打开接口返回 401 未登录。原因前后端 token 字段名不一致。前端 header 里发送的是 token后端 API 验权默认也从 header 的 token 取如果某一端改成了 Authorization或者大小写不一致验权就会失败。另一种情况是 token 本身已过期后端正确返回 401前端统一跳转看起来像没登录上。解决找到后端 API 验权的入口确认它从哪个 header 取 token把前端 request.js 的 header 改成一致。同时在登录接口里检查 token 有效期配置开发阶段可以把过期时间调长一点减少干扰。5.4 SQL 导入失败字符集与版本不匹配现象导入 install.sql 时报错常见信息有 Index column size too large 或 Unknown collation: utf8mb4_unicode_ci。原因SQL 文件是按 MySQL 5.7 和 utf8mb4 生成的你本地 MySQL 版本过低或者建库时没有指定字符集导致表结构里的索引和排序规则不被支持。解决建库时显式指定 utf8mb4命令用 create database xxx default charset utf8mb4把 MySQL 升级到 5.7 以上。如果还有报错检查 sql_mode 是不是严格模式临时可以关掉 strict mode 再导入。5.5 订单状态对不上前端永远显示待支付现象后台订单已经是已支付用户端却一直显示待支付状态名和实际流转对不上。原因订单状态枚举的前后端定义不一致。后台代码把 1 定义为待支付前端页面把 0 定义为待支付两边没有共用同一份状态字典。解决在前后端各维护一份公共状态字典后端在 common 常量里定义前端在 common/status.js 里定义字段顺序和数值必须完全一致。凡是展示状态的地方都从字典取值禁止在页面里写死状态数字。改完顺手把 fa_order 表里历史数据的 status 也校正一遍。6. 二次开发实战把后台派单改成用户自选技师最后做一个最典型的业务改造把默认的后台指派技师改成用户自己选技师。这个改动覆盖数据库、接口、前端三处做完之后这套源码的骨架你基本就摸清了。6.1 先锁定改动范围要改的地方总共三处加一个字段。数据库给 fa_order 表加 technician_id 字段存用户选的技师 ID后端创建订单接口加一个可选参数 technician_id并在传值时把订单状态改成待技师接单前端确认订单页加一个技师选择列表展示附近技师和距离。后台派单流程保留作为无人选择时的兜底。ALTER TABLE fa_order ADD COLUMN technician_id INT UNSIGNED NOT NULL DEFAULT 0 COMMENT 用户自选技师ID AFTER service_id;逻辑说明technician_id 默认 0 表示未指定非 0 表示用户指定。订单详情查询时联查 fa_technician 表就能拿到技师姓名和头像。6.2 前后端的具体改法前端在用户选完服务后调一次附近技师接口把结果渲染成可选列表。距离字段在后端返回时单位是公里展示前格式化一下会更友好。// 预约确认页的技师选择逻辑 async function loadTechnicians(serviceId) { const res await request(/order/canAssign, { service_id: serviceId }, POST); const list res.data.map(t ({ ...t, distanceText: t.distance 1 ? t.distance.toFixed(2) km : Math.round(t.distance * 1000) m })); this.technicians list; }逻辑说明canAssign 接口内部就是第 3 章那段 Haversine 查询只是入口从订单 ID 换成了服务 ID。前端拿到列表后用户点选某个技师就把技师 ID 存进表单提交订单时随请求体一起发送。后端在创建订单接口里加一段判断$technician_id $this-request-post(technician_id, 0); if ($technician_id) { $tech TechnicianModel::get($technician_id); if (!$tech || $tech-status ! 1) { $this-error(技师不可用请重新选择); } $order_data[technician_id] $technician_id; $order_data[status] 2; // 待技师接单原 2 是已支付待派单这里沿用 2 }逻辑说明传了 technician_id 才会校验技师状态并把订单状态置为待技师接单不传则维持原来的待支付流程由后台派单。这两个分支互不影响。6.3 改造后的验证验证要覆盖两条路径在 FastAdmin 后台给某技师填上经纬度用前端选该技师下单查 fa_order 表确认 technician_id 有值、status 为 2再走一遍不选技师的旧流程确认后台派单仍然正常。两条路径都通改造才算完成。拆这类源码最值钱的地方其实不是某个接口怎么写而是状态和字段的约定怎么贯穿三端。我这次改的时候第一件事不是写代码而是先翻 fa_order 的 status 注释和前端的公共字典把待派单、待接单的定义对齐才动手。从那以后我接手任何 FastAdmin 项目第一件事都是先导出数据库字典再碰控制器这个习惯帮我少走了不少弯路。希望这份拆解和改造思路对你也有用。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询