☰
同城跑腿系统开发实战:Fastadmin+ThinkPHP+Uniapp架构拆解
2026/10/7 20:18:28 网站建设 项目流程

简介:基于Fastadmin+ThinkPHP和Uniapp开发的优创同城跑腿系统,是一套面向跑腿团队与同城即时配送创业者的完整源码方案,覆盖用户端、骑手端、运营后台,支持帮取、帮送两种核心模式,既适合技术人员二次开发,也适合运营团队快速搭建业务平台。压缩包共2000个文件,大小约43.97MB,以js、html、vue、json等类型为主,前端页面、后端逻辑、接口配置与说明文档分布清晰,便于按模块检索修改。资源在业务层面覆盖按距离/重量计价、临时加价、预约取件、跑腿小费、物品保价、地图选点、一键抢单、系统派单、智能派单、兼职/全职等常用规则,基本满足同城跑腿实际运营场景。目前已有107人学习下载,源码无加密且可私有化部署,适合希望低成本获得可运营系统并进行二次扩展的个人或团队。

1. 同城跑腿系统拆解:Fastadmin、ThinkPHP、Uniapp各管什么

如果你接过同城跑腿这类项目,大概率会先遇到一个选择题:运营后台用现成的后台框架还是从零写?用户端和骑手端是套模板还是自己搭?基于Fastadmin+ThinkPHP和Uniapp开发的优创同城跑腿系统,给出的答案很明确——后台管理交给Fastadmin(底层是ThinkPHP),面向C端的两个App用Uniapp一套代码同时出微信小程序和安卓/iOS包。这个组合的优势在于开发效率:Fastadmin把菜单、权限、CRUD都生成好了,Uniapp把三端编译差异也处理掉了,你真正要啃的是业务逻辑部分——帮取、帮送两种订单模式的状态流转、骑手定位回传、以及用户端和骑手端各自的结算规则。这篇笔记适合准备接同城跑腿外包、或者想快速验证这个方向的人,我会把架构拆开,再给到能直接抄的接口、配置和排错方案。

2. 跑腿系统整体架构:订单状态机与三端数据流

跑腿业务不像商城那样商品是主角,它核心是一笔订单从发起到完成的整个生命周期。同城跑腿系统虽然分用户端、骑手端、运营后台三个端口,但所有端的操作都是在推进同一个订单状态。所以先把状态机想明白,比先写任何一行代码都重要。

2.1 为什么是Fastadmin+ThinkPHP而不是纯前后端分离

常见做法是后台用Fastadmin,原因很实际:Fastadmin自带基于ThinkPHP 5.x/6.x的权限控制(Auth)、后台菜单管理、附件管理和一键生成CRUD,跑腿系统的运营后台需求——骑手审核、订单列表、价格配置、提现审核——全是标准的表格+表单操作,用生成器能省一大半功夫。有人会觉得Fastadmin太重,不如用Laravel或直接写API给前端用,但跑腿系统有个特点:运营后台的查询条件很多,比如按骑手、按时间段、按订单状态组合筛选,Fastadmin的searchlist机制几乎不用额外写SQL;而ThinkPHP的ORM在查询构造这块也很顺手,比如统计骑手今日完单数,直接使用Db类加where条件就行。

如果你非要用纯前后端分离,理论上可行,但成本和收益不成正比。Fastadmin后台的菜单、管理员、日志、配置管理都是现成的,运营后台管理员的登录态、操作日志、权限拦截,Fastadmin在中间件层面已经处理掉了;你再从零搭一遍RBAC,少说多写几百行代码。而且跑腿系统的后台不是给用户用的,不需要那种极致的交互体验,Fastadmin基于Bootstrap的后台界面完全够用。

2.2 订单状态机设计:帮取和帮送如何共用一个状态流

帮取和帮送的区别只在任务内容:帮取是去商家/代收点取东西送到指定人,帮送是用户把东西给骑手送出去。但从订单状态看,两者完全共用一条链路:

待支付 → 待接单 → 已接单(骑手去取件)→ 取件完成(配送中)→ 已完成 → 待评价/已评价

外加两个终止状态:用户取消(支付前/已接单前)和平台取消(骑手长时间不接单超时)。订单状态字段我一般用int型state,配一个状态映射描述文件,而不是用字符串枚举,这样在Fastadmin的列表里可以直接用status渲染标签(如success、danger、warning),也不用在SQL里做字符串大小写匹配。订单表的核心字段如下:

字段类型说明
order_snvarchar(32)订单号,唯一索引
user_idint用户ID
rider_idint骑手ID,接单前为空
order_typetinyint1=帮取 2=帮送
statetinyint0待支付 1待接单 2已接单 3配送中 4已完成 5已取消
start_addressvarchar(255)取件地址(帮送模式就是用户发件地)
end_addressvarchar(255)送达地址
goods_weightdecimal(5,2)物品重量(kg),帮送必填
delivery_feedecimal(10,2)配送费
distancedecimal(10,2)预估距离(km)
paid_atdatetime支付时间
created_at / updated_atdatetime记录创建和更新时间

帮送模式比帮取多一个goods_weight字段(帮取是代买/代拿,重量一般按实收取),后台的价格配置里也能按重量阶梯计价。在写接口之前,先把这张表的索引建对:order_sn唯一索引(用于支付回调查单),state+created_at联合索引(用于运营后台按状态筛选订单列表)。

3. 用Fastadmin搭建运营后台:菜单、权限与帮取帮送工单流转

Fastadmin引入项目的第一步不是写业务代码,而是先做后台基础配置。把Fastadmin部署好之后,要用php think命令行工具生成订单表和骑手表的对应控制器/模型/视图,然后做菜单授权。跑腿系统的运营后台角色一般有:超级管理员(看全量数据)、运营(审核骑手、处理订单异常)、财务(提现审核)。这三类角色在Fastadmin里就是三个权限组,每个组勾选对应菜单即可。

在Fastadmin里手动创建一张业务表之后,用一行命令生成接口和视图是很高效的:

php think crud -t order -c order -i order_sn,user_id,rider_id,state,order_type,start_address,end_address,delivery_fee,created_at -u 1

-t是表名,-c是控制器名,-i是需要在列表页显示的字段,-u 1表示强制覆盖已有控制器。这样生成的控制器位于application/admin/controller/Order.php,模型在application/common/model/Order.php,视图在application/admin/view/order/。它自带index/add/edit/del/forbidden接口和对应模板,运营后台的订单管理列表就直接能用了。

3.1 在Fastadmin里建订单表:先设计状态字段的注释规范

生成CRUD只是省了写监控面的时间,真正的业务逻辑要落到控制器里改,尤其是订单状态的变更。Fastadmin生成的控制器默认只有增删改查,我需要重写order控制器的index方法,增加按骑手和状态筛选的where条件。同时状态字段的值在数据库中只存int(如1=待接单),要在模型里加一个getStateTextAttr的查询器,或者用Fastadmin后端自带的状态渲染。在视图index.html里,需要用Fastadmin的builddatagrid,同时把state字段做成开关标签。

运营后台的骑手审核是另一个重要管理模块,骑手表要记录身份证号、行驶证信息、接单状态、今日完单量。骑手状态更新我建议用独立接口,不要直接依赖Fastadmin的编辑按钮,因为骑手表会被Uniapp端高频读取和更新位置,而Fastadmin的常规编辑会走form表单,跟API是有冲突面的。后台接口和客户端接口要分离:Fastadmin的控制器用create/find等管理API处理运营后台请求;另外在application/api/controller/Rider.php里写供Uniapp调用的接口。

3.2 用Fastadmin的验证层和Token鉴权:给Uniapp端提供API的正确姿势

Uniapp端(用户端和骑手端)不能直接访问后台的admin路由,因为Fastadmin的admin入口做了登录态和CSRF校验,而客户端没有Cookie与后台会话。常见做法是在extend/fast/目录下使用Fastadmin的Api基类,或者按官方推荐单独建立api模块。我先说一个最容易翻车的点:很多人在api控制器里用了$this->request->post()读参数,但在Fastadmin的API控制器里,正确写法是继承think\Controller,然后读取input()或$this->request->param(),因为Fastadmin的基类做了不少后台特定处理。

用户和骑手的登录态,我用Fastadmin的token鉴权机制处理——用户登录成功后会返回一个token,客户端后续每次请求header里带Authorization: Bearer <token>。Fastadmin的api基类里自带_initialize方法会校验token,无需自己重写登录逻辑。

<?php namespace app\api\controller; use think\Controller; use think\Db; class Order extends Controller { /** * 创建订单(帮取/帮送共用) * 参数: token, order_type, start_address, start_lng, start_lat, * end_address, end_lng, end_lat, contact_tel, remark * 帮送模式额外传: goods_weight */ public function create() { $user = $this->getUser(); // 从token中解析用户信息 if (!$user) { return json(['code' => 401, 'msg' => '请先登录']); } $orderType = (int) input('order_type'); if (!in_array($orderType, [1, 2])) { return json(['code' => 400, 'msg' => '订单类型不合法']); } $params = [ 'order_sn' => $this->buildOrderSn(), 'user_id' => $user['id'], 'order_type' => $orderType, 'start_address'=> input('start_address'), 'start_lng' => input('start_lng'), 'start_lat' => input('start_lat'), 'end_address' => input('end_address'), 'end_lng' => input('end_lng'), 'end_lat' => input('end_lat'), 'contact_tel' => input('contact_tel'), 'remark' => input('remark', ''), 'state' => 0, // 待支付 ]; if ($orderType == 2) { $params['goods_weight'] = input('goods_weight', 1); } $fee = $this->calcDeliveryFee($params); // 按距离和重量计算运费 $params['delivery_fee'] = $fee; $orderId = Db::name('order')->insertGetId($params); return json(['code' => 0, 'data' => ['order_id' => $orderId, 'delivery_fee' => $fee]]); } }

这段代码里最关键的设计是:创建订单时先给state设0(待支付),用户微信支付成功后,再通过微信支付回调把订单推到待接单状态。这里没有把支付动作写在create方法里,是为了让客户端在拿到order_id后再拉起后续支付,避免下单和支付耦合后因支付失败导致脏订单。calcDeliveryFee方法里一般取距离(两个经纬度的直线距离乘1.2的路程系数),再按基础价+每公里单价+重量加价计算,这块逻辑不要写到控制器里,放模型层或服务类会更好维护。运营后台改计价规则时改的是数据库配置,不用动代码。

3.3 骑手端接单、取件、送达的接口设计:状态变更必须加并发保护

跑腿系统最容易出现的数据不一致是骑手同时抢同一单。两个骑手同时请求接单,如果代码只做简单的update操作,会出现状态覆盖。这时的正解是在SQL里用条件更新,而不是先查再更新

// 接单操作 $result = Db::name('order') ->where('id', $orderId) ->where('state', 1) // 必须是待接单状态 ->where('rider_id', 0) // 且还没有骑手 ->update(['rider_id' => $riderId, 'state' => 2, 'accept_time' => time()]); if ($result === false) { return json(['code' => 400, 'msg' => '手慢了,订单已被抢']); }

更新后返回的行数为0即没抢到,这是避免超卖的经典做法,数据库行锁帮我们做了并发控制。这里的等待问题在ThinkPHP里由PDO的update操作自动处理,不需要额外的队列和锁。取件完成(state=3)、送达完成(state=4)同理,都是带where条件的状态流转更新。如果订单被骑手接受后又取消(比如商家缺货),需要区分是用户取消、骑手取消、还是超时未支付系统自动取消,不同取消源走不同逻辑:用户取消要原路退款,骑手取消要标记骑手责任并可能扣信用分。

在运营后台的订单列表里,我通常会额外加一个操作列——「流转记录」。不是所有状态变化都改order表的state字段,而是同时要写一张order_log表,记录order_id、from_state、to_state、operator_type(用户/骑手/系统/管理员)和操作时间。这样出现纠纷时能翻出完整的操作轨迹,不然用户说自己没下过单,骑手说已经送达了,你拿不出任何依据。

4. Uniapp端实现:用户下单、骑手接单与实时定位

Uniapp的价值在于一套Vue语法同时编译成微信小程序、H5和App。跑腿系统真正复杂的不在界面,而在两件事:微信小程序的支付唤起(用户端)和骑手端实时定位上报(App和微信小程序)。

4.1 用户端:下单页与微信支付流程的封装

用户端下单页无非是选地址、选类型、填备注、显示估算费用。前端要做的是把订单数据组装好,调用后台create接口,拿到orderId之后用uniapp的uni.requestPayment唤起微信支付(或微信小程序内的wx.requestPayment,Uniapp会自动转换)。在支付结果处理上,uniapp的requestPayment返回的成功并不代表支付一定成功——用户可能支付后立刻杀掉了App,客户端收不到回调。所以后台必须实现一个订单状态查询接口,让前端在支付成功后轮询或等支付回调来刷新订单状态。

// api.js 统一封装请求 const BASE_URL = 'https://yourdomain.com/api'; export function request(url, method = 'GET', data = {}) { return new Promise((resolve, reject) => { uni.request({ url: BASE_URL + url, method, data, header: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + uni.getStorageSync('token') }, timeout: 10000, success: (res) => { if (res.statusCode === 401) { // token过期,跳回登录页 uni.navigateTo({ url: '/pages/login/login' }); } else if (res.data.code === 0) { resolve(res.data.data); } else { reject(res.data.msg); } }, fail: (err) => { reject('网络异常,请检查网络'); } }); }); }

这段代码做了三层处理:第一层是HTTP状态码401统一拦截,因为token过期是所有会话型应用都会遇到的;第二层是业务code错误信息提取,这样调用处可以直接catch到中文错误提示;第三层是fail回调里统一提示网络异常,避免用户看到原始的connection refused之类的错误信息。在用户端创建订单的页面里,调用create接口后拿到delivery_fee再显示给用户确认,然后才拉起支付,否则会出现预估费用和实际费用不一致时用户已经付款的纠纷。

4.2 骑手端:实时定位上报与后台运行监测

骑手端的核心是实时定位。骑手App需要持续向后台发送经纬度,让用户和运营后台能看到骑手轨迹。在Uniapp的App端,使用plus.geolocation.watchPosition做持续监听,微信小程序端则使用wx.startLocationUpdateBackground(需用户授权,并且要在manifest里声明requiredBackgroundModes为location)。热词搜索里提到的“uniapp 后台运行监测定位 userlocationbackground”和“plus.geolocation.watchposition 配合 uni.startLocationUpdate”,说明大家在这个功能上经常卡住。

// 骑手端开启持续定位 // App端和微信小程序端需分别判断环境 if (uni.getSystemInfoSync().platform === 'ios' || uni.getSystemInfoSync().platform === 'android') { // App端 plus.geolocation.watchPosition( (position) => { const lat = position.coords.latitude; const lng = position.coords.longitude; uploadLocation(lng, lat); }, (err) => { console.log('定位失败', err); }, { enableHighAccuracy: true, maximumAge: 5000, timeout: 10000 } ); } else { // 微信小程序端 uni.startLocationUpdateBackground({ success: () => { uni.onLocationChange((res) => { uploadLocation(res.longitude, res.latitude); }); }, fail: (err) => { console.log('后台定位启动失败', err); } }); }

App端这里有一个Windows风格的坑:plus.geolocation.watchPosition的回调频率不是你能控制的,有些Android机型在息屏或App切后台后回调会停止。常规做法是在manifest.json里添加"requiredBackgroundModes": ["location"],同时要在App.vue里申请前台服务权限——Android上定位权限和高精度定位权限是两回事。还有一个学出来的教训:后台定位的精度回调间隔太密会非常耗电,但间隔太长又会让轨迹断裂。我一般会在恰当的位置设置一个节流变量:只在上一次上报时间距今超过15秒时才调用uploadLocation,同时上报时附带剩余电量,让后台在低电量状态下把上报频率自动降到30秒一次。

4.3 微信小程序打包超2MB的优化:分包与代码瘦身

用户端和骑手端打包成微信小程序后,很容易撞上“source size 2612kb exceed max limit 2mb”这个限制。换一个角度看,2MB上限是微信的老规矩,但用Uniapp编译出来的包特别容易超限,因为编译过程会把Vue运行时、Promise polyfill等公共代码打进主包。解决方案是分包加载,这是所有Uniapp小程序项目的必修课。

在pages.json里配置subPackages,把骑手端、用户端的订单详情页、定位页面都放进去:

{ "pages": [ { "path": "pages/index/index" }, { "path": "pages/login/login" } ], "subPackages": [ { "root": "pagesRider", "pages": [ { "path": "order-list/order-list" }, { "path": "order-detail/order-detail" } ] }, { "root": "pagesUser", "pages": [ { "path": "order-submit/order-submit" }, { "path": "order-pay/order-pay" }, { "path": "order-track/order-track" } ] } ] }

分包的核心思想:小程序启动时只加载主包(pages下面的页面),骑手端和用户端的其余页面在跳转时才去加载对应分包。需要注意的一个硬性规则:分包不能互相引资源,打包后的公共代码比如common目录里的js和css必须留在主包。另外地图组件(高德/腾讯地图)在Uniapp里是按需引入的,但编译时地图SDK仍可能被打进主包,建议把地图页面单独放一个分包,同时在地图组件上设置lazy-load。还有一个让包体积缩一半的绝招:在manifest.json的源码视图里关掉不需要的模块(比如不用的支付渠道、统计SDK),很多情况下超限是因为把推送、视频、分享这些模块统统编译了进去。热词里提到的“uniapp自定义分享好友”和“uniapp manifest配置”,说的就是这个内容——manifest里配置的模块,很多其实用不上,留着只会让包变大。

5. 本地联调与常见坑:Fastadmin上传漏洞、ThinkPHP兼容、Uniapp不打印日志

同城跑腿系统跨了三个技术栈,每个栈都有自己的毛病。我按踩过的实际坑来排,这几条能帮你至少省三天调试时间。

5.1 Fastadmin上传文件漏洞:必须处理的安全隐患

Fastadmin历史上曝光过上传文件漏洞,典型场景是后台附件管理被上传了可执行PHP文件,导致服务器被拿权。根源是Fastadmin的附件上传接口在没有正确校验文件类型和文件名的情况下,允许用户上传包含恶意代码的文件,并且文件保存路径在Web可访问目录下。解决方案分两步:第一步升级Fastadmin到修复后的版本,第二步主动关掉不用的上传控制器,或做二次校验。

// application/admin/controller/Ajax.php 中重写upload方法 public function upload() { $file = $this->request->file('file'); if (!$file) { $this->error('未上传文件'); } // 强制校验文件后缀,白名单之外直接拒收 $ext = strtolower($file->getExtension()); $allowed = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'mp4', 'zip']; if (!in_array($ext, $allowed)) { $this->error('文件类型不允许上传'); } // 上传路径放到一个禁止PHP执行的目录,或加.htaccess规则 $info = $file->rule('uniqid')->move(ROOT_PATH . 'public' . DS . 'uploads'); if ($info) { $this->success('上传成功', null, ['url' => '/uploads/' . $info->getSaveName()]); } $this->error($file->getError()); }

记住一个关键点:文件上传校验永远后端做,永远用白名单而不要用黑名单(黑名单永远有漏网之鱼)。另外在Nginx配置里给uploads目录加一段location规则,禁止PHP文件在这个目录执行

location ~ ^/uploads/.*\.(php|php5|phtml)$ { deny all; }

这样即使某个绕过校验的PHP文件被传上去了,它也无法执行,作为纵深防御可以兜底。

5.2 ThinkPHP项目运行:伪静态、PHP版本与调试开关

Fastadmin基于ThinkPHP 5.x,如果你本机或服务器用的是PHP 7.4以上版本,很多项目跑不起来是因为Fastadmin老版本依赖的extend/fast/的一些函数,比如each()函数在PHP 8.0被移除了。常见做法是:开始之前先确认PHP版本和Fastadmin版本的匹配关系,Fastadmin V1.2.0(ThinkPHP 5.0.24)配PHP 7.1最稳,ThinkPHP 6配PHP 7.4/8.0也没大问题。如果你拿到的是老项目,可以先在入口文件里加错误显示,快速定位是否因语法兼容导致的白屏。

运行阶段另一个高频问题是伪静态:Fastadmin的URL重写不生效,导致/index.php?s=/admin/login变成404。Nginx下必须配好伪静态规则,Apache下则需要开启mod_rewrite。我见过太多人把时间耗在“后台访问不了”上,结果只是伪静态没配置或者重写规则里少了一个try_files语句。

5.3 Uniapp不打印日志信息:先确认是发布版还是开发版

Uniapp项目经常出现console.log在真机调试时完全没输出的情况。原因通常有三个:一是Uniapp在打包时会把console.log默认移除(release模式),这属于构建配置的问题;二是手机系统版本对WebView的console输出做了拦截;三是uni-app的日志输出到原生层而不是浏览器控制台,你在HBuilderX的调试器里看不到。解决办法:在manifest.json里把"minify"设为false或者在构建时用开发模式;同时用HBuilderX真机运行(不打包),这样console会输出到HBuilderX的控制台;如果真机运行都看不到日志,那就直接在代码里用uni.showToast把错误信息弹出来,暴力但有效。

5.4 微信小程序打包超2MB后如何快速定位体积大头

当遇到“source size 2612kb exceed max limit 2mb”时,先别急着一通乱改,在HBuilderX的发行-小程序-微信小程序选项里勾选“压缩代码”和“上传代码时自动去掉调试用的console”,这一步通常能消掉20-30%的体积。如果还超,用微信开发者工具的“代码依赖分析”功能看哪个npm包占了体积。跑腿系统最常见的大头是echarts或vant组件库——如果只是显示订单列表和地图,不建议引UI框架,Uniapp自带的组件够用了。如果你必须用vant,把它也放进分包里,主包只放登录页和首页,这样基本都能压到2MB以内。

5.5 骑手端定位后台不更新的排查清单

骑手App切到后台后定位停止更新,这个问题不能只靠一个watchPosition解决。排查时按步骤走:第一步确认manifest里应用的权限声明是否包含ACCESS_BACKGROUND_LOCATION(Android 10以上必须在manifest里单独声明);第二步确认是否调用了uni.startLocationUpdateBackground而不是uni.startLocationUpdate,前者才是专门为后台场景设计的;第三步检查手机厂商的白名单——小米、华为的系统在省电策略里会杀掉一切不带前台服务的App,所以骑手端要在App里申请前台服务(Foreground Service)保活。连热词“uniapp实现app前台服务”都搜到了,说明很多人栽在这里。前台服务的实现,在Uniapp里没有直接的统一API,App端要离线打包或用原生插件做,云端打包的话需要选“Android前台服务”模块。但需要提醒的是:由于各厂商后台限制差异极大,骑手App的定位保活不存在通杀方案,这时你在第2章做的状态机就派上用场了——即使定位没更新,骑手手动点击“取件完成”“送达”也会触发一次位置上报,配合状态流转不会造成业务卡死。

6. 上线前做完这四件事:少返工,保收入

这套系统要上线接真实订单,我给你一套最直接的验证清单。

第一,用Mock数据跑通一条完整链路。自己在后台建一个测试骑手账号,用户端下单一帮取任务,骑手端接单、取件、送达,然后看order_log表里的每一步记录是否完整、时间戳是否正确。这一步能同时验证状态机、日志、接口鉴权和前后端联调,是性价比最高的一轮测试。

第二,用代理工具抓包看用户端支付流程。支付是最容易出黑匣子的环节。用户端在你手机上拉起微信支付后,你把请求用工具记录下来,确认回调接口有没有真正被微信服务器打到,别只依赖客户端返回的成功。再测一次支付后杀进程、断网、延误回调的场景,看订单状态会不会永远卡在待支付。如果卡住了,那你要在后台加一个订单超时自动关闭的定时任务(比如30分钟未支付自动取消),并触发库存回滚和支付单作废。

第三,跑一次分账和提现演练。跑腿系统的收入来源主要是每单抽成和会员费。后台要给骑手设置提现规则(比如满100元才能提现),提现流程走一遍:骑手发起提现→后台审核→打款→状态标记。注意资金相关操作绝不能用Fastadmin的普通编辑接口改字段,必须单独写提现单和审核记录,每一笔变动都要留下操作人。

第四,压测抢单接口。跑腿业务用刮刮乐式抢单,高峰期可能同时几十个骑手在抢一单。我在第3章里写的条件更新就是为这个准备的,但你仍然要测一下高并发下会不会死锁。用简单的ab或wrk工具压一下接单接口,观察ThinkPHP的事务日志和数据库锁等待时间,如果延迟过大,回到代码里检查是否有在事务内请求外部接口(比如推送、地图逆地理编码)。

回到我刚接这个项目时的教训:我当时自己图省事,把用户端、骑手端、后台三份代码放在同一个仓库里,结果每次改后台都要重推一遍小程序,联调时又经常因为分支混乱浪费半天。现在我会把三个端拆成三个独立项目,后端拆成“API服务”和“管理后台”两部分,骑手端的小程序包单独分包发布。这套习惯一直沿用到现在,它让后期的迭代和问题定位都清晰很多。希望这一篇笔记能让你少走我走过的弯路,祝你的跑腿系统早日上线赚到钱。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询