☰
PHP+UniApp场馆预订系统:从部署到多端上线的完整实战指南
2026/10/7 3:43:28 网站建设 项目流程

最近手上整理了一套PHP+UniApp组合开发的智能场馆预订系统源码,前端一套UniApp代码可以同时编译成微信小程序、H5和安卓App,后端用PHP统一提供接口,不用给每个端单独写一套后端逻辑。这套系统本身就是我日常工作中经常接触的类型,所以拿到后我没有直接丢给客户,而是自己先把完整的部署链路跑了一遍。从环境搭建、数据库导入、接口联调,到小程序编译、体积优化、多平台发布,整个过程踩了不少坑,也摸清了很多藏在源码里的细节。

这篇文章就按我的实际操作顺序来写。如果你想自己部署一套场馆预订系统,或者打算拿这套源码做二次开发,又或者只是想在PHP+UniApp这个技术组合上找一些实战经验,那这篇文章应该能帮到你。我会把从零到上线每一步的关键操作、配置参数和踩坑记录都摆出来。

1. 项目整体拆解:这套系统到底做了什么

1.1 选型逻辑:为什么是PHP+UniApp这个组合

先聊选型。PHP在后端开发里被讨论很多年了,但它的优势在中小型业务系统上非常明显:部署简单、生态成熟、开发效率极高。场馆预订这种业务,核心是场地管理、订单处理和支付回调,没有特别离谱的高并发需求,用PHP完全可以撑住。加上攻城狮普遍都熟悉LAMP或者LNMP这套环境,后期维护成本也比较低。

UniApp这边就更直接了——跨端。一套Vue语法的代码,编译成微信小程序、支付宝小程序、H5、安卓App、iOS App。它的存在意义在于,业务方不会只守着微信小程序一个入口,很多场馆运营方还想要一个管理端H5,甚至一个给用户的安卓App。如果每个端都单独开发,光是前后端对接就要疯。UniApp把视图层统一了,后端接口一套就够了。

从成本角度看,这套组合还有个好处:PHP环境找到一家几十块的虚拟主机或者低配云服务器就能跑,而UniApp编译出来的H5和微信小程序不需要额外购买跨端框架的服务。整个项目的边际成本主要在于服务器和域名,这对于场馆经营者或者接单的外包团队来说,都是很现实的考量。

1.2 系统核心模块与业务流程

我大致梳理了一下这套源码的业务模块,核心是这几个:

  • 场馆管理:场馆基本信息、地址定位、营业时间、场馆图片、设施说明、公告通知。
  • 场地管理:一个场馆下可以设置多个场地,比如羽毛球馆的一号场、二号场,篮球馆的半场、全场。
  • 排期与场次:按日期生成可预订时段,每个时段可以单独设置价格、可预订状态。
  • 用户端模块:微信登录、手机号绑定、场地浏览、时段选择、下单支付、我的订单、取消预约、入场核销码。
  • 订单与支付:下单锁场、时间内支付、微信支付回调、超时自动释放、订单退款。
  • 后台管理:场地价格配置、订单管理、核销管理、数据统计。

业务流程走起来是这样:用户打开小程序,授权微信登录,选择场馆和日期,系统按日期渲染出排期时段,用户挑中某个时段下单。用户下单后系统先锁定这个时段,给一个支付等待期,用户完成微信支付后订单进入已支付状态。到店后场馆前台通过后台核销订单,或者用户出示小程序里的核销码扫码验证。

这里有一个容易被忽略的关键点:排期和日期的关系。场地时段不是每天固定不变的,经营者可能临时设置某天某个时段不可订,或者单独调高节假日价格。所以数据库表结构里,场次表通常会有一个日期字段和场地ID关联,而不是只存周几几点。很多刚开始做预订系统的人会把排期做成模板,后面遇到改价和停场需求就头疼。

1.3 多平台部署的架构思路

UniApp的多平台部署,并不是简单地一套代码什么端都能用。实际编译时,每个端还是会有自己的差异点。这套系统在架构上主要做了这几件事:

  • 接口统一通过请求封装层调用,所有端共用一套BaseURL配置。
  • 登录流程区分平台。微信小程序走uni.login拿code再去后端换openid,App端走uni.getUserProfile或者第三方登录。
  • 支付逻辑拆开。小程序端调用uni.requestPayment拉起微信支付,H5端走公众号支付或者收银台模式,App端用聚合支付SDK。
  • 条件编译处理平台差异。代码里用#ifdef MP-WEIXIN这类注释区分小程序端和H5端的特定逻辑。

这种设计的核心价值在于,版本迭代时改动业务逻辑只需要改一份代码。比如在后台加一个满减优惠活动,只需后端加逻辑,前端所有端同步生效。对运营来讲,这个优势在后期会越来越明显。

2. 拿到源码后的第一步:环境准备与后端部署

2.1 本地环境搭建(PHP版本、数据库、伪静态)

刚拿到源码,建议先在本地跑通,别急着传服务器。我用的本地环境是PHPStudy,原因无它,快。一键启动Nginx和MySQL,比手动配环境省事得多。

先说PHP版本。现在很多PHP框架最低要求7.4,推荐用8.0或8.1。我用的是PHP 8.1,运行源码没遇到兼容性问题。如果你用的是老版本的系统,也别无脑升8.x,先看后端框架是哪个版本的ThinkPHP或者Laravel。有些老代码在PHP 8上会报函数签名冲突和动态属性创建错误,这些一旦出现就得回头改源码,非常被动。

数据库我直接用了MySQL 5.7。这套源码的SQL语句中规中矩,没有用到MySQL 8的窗口函数之类的特性,所以5.7完全兼容。反正本地先跑通,后面部署到服务器时,数据库版本选择和本地保持一致即可,避免出现备份恢复后SQL语句报错的尴尬情况。

项目部署时要注意伪静态配置。大多数PHP后端代码都有友好的URL重写,比如地址是https://域名/api/xxx而不是https://域名/index.php?s=/api/xxx。在Nginx里需要配一段规则:

location / { if (!-e $request_filename) { rewrite ^(.*)$ /index.php?s=$1 last; } }

这个配置的核心是把不存在的文件路径交给入口文件处理。如果忘了配,你会发现接口地址直接404,而后端管理后台首页能打开,这个现象非常容易让人误判成代码问题。

2.2 导入数据库与配置环境文件

源码包里一般会带一个SQL目录,里面是数据库初始化脚本。打开PHPMyAdmin或者命令行执行导入即可。导入后要重点检查两张核心表的数据:场馆列表和场地表。如果表里没有数据,前端页面打开会空荡荡,容易以为自己部署失败了。

数据库导入完成后,修改后端的环境配置文件。以ThinkPHP为例,.env文件放在项目根目录,需要修改的内容通常是:

APP_DEBUG = true [APP] DEFAULT_TIMEZONE = Asia/Shanghai [DATABASE] TYPE = mysql HOSTNAME = 127.0.0.1 DATABASE = stadium_db USERNAME = root PASSWORD = 你的数据库密码 HOSTPORT = 3306 CHARSET = utf8mb4 [LANG] default_lang = zh-cn

这里有个细节:本地开发时建议把APP_DEBUG打开,这样接口报错时能看到完整的错误堆栈,定位问题效率高很多。部署到生产环境时再把它关掉,避免泄露服务器目录结构和数据库信息。

还要注意charset必须写成utf8mb4,因为手机号、昵称这些内容中可能包含emoji字符,utf8mb4是唯一能完整支持四字节字符的编码。如果你在页面上看到问号或者乱码,十有八九是表结构或者连接配置的字符集不对。

2.3 接口自测:先确认后端是通的

环境配好以后,我习惯先用浏览器直接访问几个接口,确认后端没问题再去连前端。核心接口无非就是获取场馆列表和场地排期。比如在浏览器打开:

http://localhost/index.php/api/stadium/list

如果返回JSON数据,说明后端基本可用。接着测试场地排期接口:

http://localhost/api/field/list?stadium_id=1&date=2024-12-20

这里容易遇到一个坑:接口如果做了签名校验或者用户登录鉴权,直接浏览器访问可能返回未登录错误。这种时候不要慌,先用后台的测试账号获取token,或者在接口调试工具里配置请求头。我习惯用Postman或者Apifox,把这几个接口配好,后面调前端的时候可以直接验证后端逻辑,避免前后端一起排查时晕头转向。

后端通了以后,还有一个必须做的事——检查接口返回的图片字段。场馆图片和场地图片在数据库里通常存的是相对路径,比如/uploads/stadium/xxx.jpg。如果接口返回的图片地址是相对路径,前端需要通过拼接域名来显示。很多人在这一步漏掉,导致小程序里场馆图片全挂。检查的方法是看接口JSON里图片字段是否以http开头,如果不是,通常前端配置有一个统一的图片域名拼接前缀,需要去前端代码里把域名改成自己的。

3. UniApp前端项目解析与小程序打包

3.1 前端目录结构,核心文件都在哪

后端跑通了,现在开始看前端。UniApp项目的目录结构,其实就是一个标准Vue项目加上一些uni-app特有的目录约定。我拿到源码后第一件事是看pages.json,这个文件相当于小程序的全局配置文件,里面配置了页面路由、底部TabBar、窗口样式、导航栏样式。

pages.json的关键配置项有这几个:

{ "pages": [ { "path": "pages/index/index", "style": { "navigationBarTitleText": "场馆预订", "enablePullDownRefresh": true } }, { "path": "pages/stadium/detail", "style": { "navigationBarTitleText": "场馆详情" } } ], "globalStyle": { "navigationBarTextStyle": "white", "navigationBarTitleText": "智能场馆", "navigationBarBackgroundColor": "#2B85E4", "backgroundColor": "#F5F5F5" }, "tabBar": { "list": [ { "pagePath": "pages/index/index", "text": "首页", "iconPath": "static/tab/home.png" }, { "pagePath": "pages/order/list", "text": "订单", "iconPath": "static/tab/order.png" }, { "pagePath": "pages/my/index", "text": "我的", "iconPath": "static/tab/my.png" } ] } }

启动项目后,如果TabBar图标不显示,先检查static/tab目录下有没有对应的png文件。这是非常常见的问题——下载的压缩包可能因为操作系统解压兼容问题漏掉了部分静态资源。

除了pages.json,manifest.json是另一个核心文件。它管理的是应用级别的配置,包括小程序AppID、App打包配置、H5配置等。还有一个容易忽视的文件是uni.scss,里面定义了全局样式变量,主色调、圆角大小、字体大小都在这里控制。改主题色只需要改这个文件里的变量值,不用去每个页面逐个改style。

3.2 manifest.json配置:平台差异都在这里

manifest.json是打包时的核心。拿到项目第一件事,把里面的appid替换成自己的。如果你还没有微信小程序的AppID,可以去微信公众平台注册一个小程序账号,在“开发管理-开发设置”里找到AppID。注意这里有两个ID容易搞混:AppID和AppSecret。AppID是公开的,用于前端配置;AppSecret是密钥,必须放在后端,绝不能出现在前端代码里。

微信小程序相关的manifest配置长这样:

{ "mp-weixin": { "appid": "wx你的appid", "setting": { "urlCheck": false, "minified": true, "postcss": true }, "usingComponents": true, "permission": { "scope.userLocation": { "desc": "用于获取您的定位以匹配附近场馆" } }, "requiredPrivateInfos": ["getLocation"], "lazyCodeLoading": "requiredComponents" } }

这里每一项都有讲究。urlCheck是微信开发者工具里的一个校验开关,它的作用是校验请求的域名是否在小程序后台配置了合法域名。本地开发时如果不开这个配置,会在控制台看到“url not in domain list”的报错。但需要注意的是,这只是本地调试的开关,上线发布前必须在微信公众平台配置合法的request域名,否则真机上会请求失败。

requiredPrivateInfos是微信平台新增的隐私接口声明。如果代码里调用了uni.getLocation之类的接口,但没在manifest里声明,审核会被驳回来。很多人的代码明明能用,但提交审核时因为缺少这个配置被拒,就是踩了这里的坑。

lazyCodeLoading设置按需注入,可以缩减小程序包体积,尤其是项目包含大量组件库时,效果非常明显。

3.3 编译到微信小程序的完整流程

用HBuilderX打开前端项目目录,这个是最直接的。打开后先等依赖安装完成,然后点击菜单栏“运行-运行到小程序模拟器-微信开发者工具”。

这里有一个很关键的细节:运行之前确保微信开发者工具已经打开,并且开启了“服务端口”选项。服务端口在微信开发者工具的“设置-安全设置-服务端口”里开启,如果不打开,HBuilderX无法自动唤起微信开发者工具。

编译成功后,微信开发者工具里会出现一个项目,此时先看看控制台有没有报错。常见的一个报错是:

vendor.js 编译失败 TypeError: Cannot read properties of undefined

这种问题一般是JS语法不兼容引起的,排查思路是先看是哪个文件报错,再确认项目是否缺少了某个依赖库。

还有一个更常见的提示:

The source size of /pages/index/index.js exceeds the max limit 2MB

这里要和微信小程序的2MB主包限制区分开。小程序要求整个主包不超过2MB,如果超了这个限制,就得做分包处理。具体怎么优化,我在后面专门用一整个章节来写,因为这是部署时最容易卡住人的地方。

编译通过后,在微信开发者工具里能看到小程序界面。此时如果接口请求报错,查看具体错误内容——域名未配置、证书不合法、跨域等等。调试阶段最频繁的问题是本地后端是http://localhost,而小程序要求接口必须是HTTPS且域名必须在小程序后台配置。解决办法是先用微信开发者工具的“不校验合法域名”开关,把urlCheck关掉,或者在工具的详情-本地设置里勾选“不校验合法域名、web-view(业务域名)、TLS版本以及HTTPS证书”。

4. 前后端联调的四个关键战场

4.1 跨域和BaseURL配置(H5调试必看)

前后端联调,第一个要解决的是跨域问题。H5端调试时,前端运行在本地9527端口,后端跑在80或者8080端口,浏览器出于安全策略会拦截跨域请求。解决方案在后端加跨域头:

header('Access-Control-Allow-Origin: *'); header('Access-Control-Allow-Headers: Content-Type, Authorization, X-Requested-With'); header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS');

但这里有个前端的坑:如果你在请求时设置了自定义header,比如Authorization字段,后端还必须在Access-Control-Allow-Headers里显式声明这个字段,否则浏览器会报“Request header field authorization is not allowed by Access-Control-Allow-Headers”。

另一种跨域问题是OPTIONS预检请求。当请求比较复杂时,浏览器会先发一个OPTIONS请求测试服务器是否允许跨域。后端要在入口文件或者路由层直接拦截OPTIONS请求并返回200,否则每次请求都会白白多花一次握手时间,而且容易导致状态码混乱。

BaseURL的配置建议统一放在一个文件里,比如utils/config.js。配置项一般包含API域名、图片域名、版本号等:

export const BASE_URL = 'https://api.yourdomain.com/index.php/api'; export const IMG_URL = 'https://api.yourdomain.com'; export const VERSION = 'v1.0.0';

这里有一个经验之谈:不要在前端代码里到处直接用绝对地址,所有请求都走封装好的Request方法,BaseURL只在一个文件里出现。原因很简单,发布以后如果需要切换服务器,只改这个文件就够了。我在实际项目里见过有人在十几个页面里硬编码了域名,最后换服务器时痛苦到怀疑人生。

4.2 微信登录 code2Session 换取openid

微信小程序的登录流程和传统的账号密码登录完全不同。核心机制是:前端调用uni.login拿到一个临时code,把这个code发给后端,后端用code去微信的接口换取openid和session_key。这个code有效期为5分钟,而且只能使用一次。

小程序端核心代码:

uni.login({ provider: 'weixin', success: function(loginRes) { uni.request({ url: BASE_URL + '/user/login', data: { code: loginRes.code }, success: function(res) { if (res.data.code === 1) { uni.setStorageSync('token', res.data.data.token); } } }); } });

后端拿到code后调用微信的接口:

$url = "https://api.weixin.qq.com/sns/jscode2session?appid={$appid}&secret={$secret}&js_code={$code}&grant_type=authorization_code"; $result = file_get_contents($url); $data = json_decode($result, true); // $data['openid'] => 用户唯一标识 // $data['session_key'] => 加密会话密钥

换取成功后,后端应该返回一个自定义token给前端,后续请求都通过token来识别用户身份。不要每次请求都调微信接口换取openid,微信接口有调用频率限制,而且每次都要走网络,性能非常差。

这里有一个很隐蔽的坑:开发阶段微信开发者工具里的code和真机上拿到的code不是同一套体系吗?其实是一样的,但是如果你在开发者工具里勾选了“模拟登录”,它模拟出来的code也是可以正常换取的。如果发现后端报了40029错误(code无效),先检查一下AppID和AppSecret是否匹配,再检查系统时间是否正确,时间偏差过大会导致session_key解析失败。

4.3 手机号授权绑定

获取微信手机号,现在用的是按钮授权方式。小程序端在页面上放一个按钮,用户点击后,通过open-type="getPhoneNumber"获取到加密的手机号数据,把数据传给后端后,由后端解出真实手机号。

前端按钮:

<button open-type="getPhoneNumber" @getphonenumber="getPhoneNumber">微信一键登录</button>

前端逻辑:

methods: { getPhoneNumber(e) { if (e.detail.errMsg === 'getPhoneNumber:ok') { uni.request({ url: BASE_URL + '/user/bindPhone', data: { code: e.detail.code, mobile: e.detail.encryptedData, iv: e.detail.iv } }); } } }

后端解密手机号的逻辑:

$sessionKey = 数据库里存的session_key; $encryptedData = 前端传来的encryptedData; $iv = 前端传来的iv; $pc = new WXBizDataCrypt($appid, $sessionKey); $errCode = $pc->decryptData($encryptedData, $iv, $data);

这个流程的易错点在于,session_key的过期问题。手机号解密依赖session_key,而session_key在用户重新登录后可能变化。如果用户先登录了,然后过了很久才点击手机号授权按钮,后端用旧session_key去解密就会失败。稳妥的做法是:点击手机号授权按钮时重新走一次uni.login,获取新的code后,后端重新去微信接口换取session_key,再用这个新的session_key解密。

另一个要注意的点是,手机号授权按钮必须在微信开发者工具的真机调试里测试。开发者工具里模拟的授权数据虽然也能走通流程,但真实性不足,很容易让你误判问题。

4.4 支付与订单状态同步

场馆预订系统最核心的交易环节就是支付。微信小程序支付流程相比H5支付要简单不少,因为它不需要额外的支付授权。小程序端用户在页面点击支付,前端请求后端创建支付单,后端调用微信支付统一下单接口,生成支付参数返回给前端,前端再调用uni.requestPayment拉起支付面板。

后端生成支付参数的核心逻辑:

$order_sn = 'SN' . date('YmdHis') . rand(1000, 9999); $params = [ 'body' => '场馆预订-羽毛球1号场地', 'out_trade_no' => $order_sn, 'total_fee' => $price * 100, 'spbill_create_ip' => 客户端IP, 'notify_url' => 'https://api.你的域名.com/api/pay/notify', 'trade_type' => 'JSAPI', 'openid' => 用户的openid ]; $result = $wxpay->unifiedOrder($params); // 返回给前端使用

然后前端拉起支付:

uni.requestPayment({ provider: 'wxpay', timeStamp: res.data.timeStamp, nonceStr: res.data.nonceStr, package: res.data.package, signType: 'MD5', paySign: res.data.paySign, success: function(result) { uni.showToast({ title: '支付成功' }); } });

这里有三种订单状态需要后端关注:待支付、已支付、已取消。用户发起支付后,如果支付成功,微信会异步回调一个通知地址,后端收到回调后更新订单状态。要注意回调通知的网络地址必须是对外可访问的HTTPS地址,且不能被防火墙拦截。

如果支付回调漏处理了,就会出现一种很尴尬的情况:用户已经扣款成功,但订单状态还是待支付。所以我在部署时会在回调逻辑里加日志记录:

file_put_contents('./logs/pay_notify_' . date('Ymd') . '.log', json_encode($input), FILE_APPEND);

这一步极其重要。有一次用户反馈支付后没到账,最后排查发现不是因为回调没收到,而是回调验签时因为证书路径配错了导致一直报错。有了日志,就能区分是“没收到回调”还是“回调处理失败”这两种完全不同的情况。

支付完成后的核销流程也需要提前跑通。后台有一个核销功能,场馆前台输入订单号或者扫码即可核销。核销后订单状态变为已完成。如果核销接口和订单状态没有联动好,会出现用户支付后,场地管理员看到的订单还是待使用状态,很容易造成线下纠纷。

5. 打包体积超2MB的应对策略

5.1 先用HBuilderX看清谁的体积最大

微信小程序的主包限制是2MB。这套系统页面不少,地图、轮播图、支付组件、UI组件库全部打包后很容易就超了。我在编译时就遇到过这个经典错误:

The source size of /pages/index/index.js exceeds the max limit 2MB

遇到超限问题,先不要急着删代码。HBuilderX的发行菜单里有一个“小程序-微信”发行流程,发行前会先检查体积。但在检查之前,你可以自己打开项目的unpackage/dist/dev/mp-weixin目录,看看哪个目录最大。

通常体积大头有三个:js文件、组件库、静态图片。js文件里最常出问题的是uni_modules里的组件,特别是那些全量引入的UI组件库。如果你用了类似uView或者ColorUI这类组件库,打包时会把所有组件都编译进包里,哪怕你只用了其中的Button组件。

5.2 分包方案与公共代码抽离

微信小程序支持分包加载。分包的思路是把不常用的页面从主包里拿出来,单独放到一个子包目录里,用户进入某个页面时才去加载对应的包。分包后主包只包含首页、登录页、TabBar页面等基础内容,子包放场馆详情、订单详情、个人中心等二级页面。

在pages.json里配置分包:

{ "subPackages": [ { "root": "pagesStadium", "pages": [ { "path": "detail", "style": { "navigationBarTitleText": "场馆详情" } }, { "path": "booking", "style": { "navigationBarTitleText": "预约下单" } } ] }, { "root": "pagesOrder", "pages": [ { "path": "list", "style": { "navigationBarTitleText": "订单列表" } } ] } ] }

这里必须注意,分包的root不能和pages目录下的原有路径重叠,否则编译时会报错。路由跳转时也要用分包后的完整路径,比如uni.navigateTo({ url: '/pagesStadium/detail?id=1' })。如果跳转路径写错了,控制台会报找不到页面的错误。

另外,小程序分包有大小限制,单个分包不超过2MB,所有分包加起来不超过20MB。对于场馆预订系统这种业务,把页面拆到分包后主包基本能控制在几百KB范围内。

5.3 实用压缩技巧汇总

除了分包,还有几个立竿见影的瘦身方法。

第一个是静态资源压缩。场馆图片尽量走CDN,不要放在小程序包里。首页轮播图、场地照片这些大图,部署时传到服务器后,在小程序里用完整的HTTPS图片地址展示。本地包里的图片只保留较小的图标文件。

第二个是全局组件按需引入。如果你用的是uni_modules的组件库,打开组件目录看看是否一次引入了整个库。以uView为例,建议按需引入组件,只注册需要用到的少数几个。有些组件库支持easycom规则自动按需引入,配置正确后会省掉大量无效代码。

第三个是清理无用文件和注释。很多源码压缩包会带一些无关文件,比如文档里放错的备份文件、缓存文件。这些文件看似不起眼,但如果被编译进包里也会占用体积。我在实际部署时遇到过一个情况,源码目录下有一个带着几十张截图备份的assets目录,体积超过了2MB,删掉后包体立刻变小。发布前过一遍项目文件列表,确认哪些是源码需要的,哪些是多余的,会省很多事。

第四个是js压缩选项。manifest.json里minified配置为true,代码压缩后能减少30%左右的体积。另外检查一下项目中是否有重复引入的第三方库,比如同时引入axios和flyio,这种冗余要清理掉。

如果主包还是超了,还有一个技巧:把所有tabBar页面集中在首页目录下,把非tabBar页面尽量放进分包;小程序的tabBar页面必须放在主包里,其他页面没有这种限制。

6. 多平台发布与其他高频问题

6.1 微信小程序审核与发布注意事项

小程序开发完成,自测通过,下一步是发布。在微信开发者工具里点击“上传”,版本号写上,然后登录微信公众平台,在“版本管理”里找到刚上传的版本,提交审核。

审核要注意几个容易踩雷的点。场馆预订涉及用户地理位置信息,如果你的代码里调用了uni.getLocation,审核时需要有明确的用途说明。在公众平台配置隐私保护指引时,要把位置信息的使用场景写清楚,比如“用于匹配用户附近的场馆”。

另外一个高频驳回理由是“页面内容不完整”或者“功能不符合预期”。提交审核前,一定要在真机上完整走一遍核心流程:登录、查看场馆列表、选择时间、下单、支付。别人的审核员也是拿真机测的,如果某个按钮点了没反应,或者支付流程走不通,很快就会被驳回。

发布后还有一件事不能漏:配置业务域名和服务器域名。在“开发管理-开发设置-服务器域名”里,把接口域名配置到request合法域名里。注意这个域名必须支持HTTPS,且证书要完整。很多人本地调试一切正常,发布后真机上所有接口全部请求失败,基本都是因为合法域名没配置或者证书有问题。

6.2 安卓应用市场与H5发布要点

小程序发布之后,如果还要编译成安卓App,在HBuilderX里点击“发行-原生App-云打包”,填好包名、证书和图标后就能生成安装包。发布到安卓应用市场时,要注意包名不能随便改,一旦某个渠道已经用了这个包名,后续更新都用同一个,换了包名会变成一个全新的应用。

H5端的发布相对简单。在HBuilderX里点击“发行-网站H5-手机版”,生成一个静态文件目录,把这个目录传到Nginx的web目录下即可。需要注意的一点是,H5端的接口域名和页面域名不能是同一个域?其实可以同一个域,但要注意跨域问题。如果前后端在同一个域下且路径不同,要确认Nginx把API路径正确代理到了后端服务。如果前后端不在同一个域,那么后端必须配置好跨域Header。

H5发布后,还有一个功能要留意:H5端的微信支付。如果你想在H5页面里使用微信支付,需要先申请“H5支付”,在支付场景上跟小程序的JSAPI支付是两套体系。很多人在配置H5支付时卡住,其实核心是必须在商户平台里添加“H5支付域名”,并且发起支付时要带上用户的IP和User-Agent。这套源码如果只做小程序和App,可以先把H5支付功能关掉,避免上线后出现支付异常用户投诉。

6.3 导航栏适配、分享、动态标题等细节

多平台发布后,还有很多体验细节需要适配。

微信小程序的顶部导航栏和手机状态栏不是一回事。很多人在开发时忽略了状态栏高度,导致自定义导航栏的标题被“刘海”挡住。获取安全区域高度的代码:

const systemInfo = uni.getSystemInfoSync(); const statusBarHeight = systemInfo.statusBarHeight; // 导航栏高度 = 状态栏高度 + 胶囊按钮高度 + 胶囊按钮上下间距 const menuButton = uni.getMenuButtonBoundingClientRect(); const navigationBarHeight = (menuButton.top - statusBarHeight) * 2 + menuButton.height;

这段代码在App端也能用,但App端没有胶囊按钮,导航栏高度可以直接取44px。用条件编译区分:

// #ifdef MP-WEIXIN const menuButton = uni.getMenuButtonBoundingClientRect(); // #endif // #ifndef MP-WEIXIN const menuButton = { top: statusBarHeight + 8, height: 44 }; // #endif

分享功能也是场馆预订场景里重要的传播入口。用户在小程序里看到某个场馆不错,想分享给朋友,需要在小程序页面里配置onShareAppMessage:

onShareAppMessage() { return { title: this.stadium.name + ' 预订', path: '/pagesStadium/detail?id=' + this.stadium.id, imageUrl: this.stadium.share_img }; }

如果想让分享出去的卡片更好看,可以在后端给场馆配置一张专门的分享图,尺寸建议是5:4,否则分享卡片显示出来会裁切。这个细节看起来不起眼,但对打开率影响非常大。

动态修改标题,可以用uni.setNavigationBarTitle。比如用户进入某个场馆时,把导航栏标题从“场馆详情”改成场馆名称。但需要注意,这个接口必须在页面加载完成后调用,而且某些安卓机型可能有缓存,下次进入时标题不会自动恢复,需要在onShow里重新设置一遍。

关于日志不打印的问题,很多人问为什么UniApp在真机上console.log不输出。在HBuilderX里调试真机时,需要确认“控制台-过滤”里没有把log级别屏蔽掉。更稳妥的调试方式是临时用uni.showToast把关键信息显示在页面上,虽然不够优雅,但在某些环境里比console好用得多。

热更新这个话题也可以提一下。UniApp的App端可以使用wgt资源包实现热更新,也就是说修改了JS或者页面资源后,不需要重新上架应用市场,用户打开App时自动拉取新资源。但要注意,原生插件、manifest.json的配置变更无法通过热更新生效,这类型的改动必须走应用市场重新发布。部署时如果有App热更新的需求,后端需要提供一个版本检测接口,返回当前版本号和下载地址,前端启动时对比版本然后决定是否需要更新。在测试热更新时,我建议先在测试机上验证一次完整的更新流程,确认更新后没有白屏和资源缺失问题再推广到全部用户。

7. 部署完成后,我再补几句掏心窝子的经验

这套系统从拿到源码到跑通全流程,我前前后后花了大约一个周末的时间。很多坑在文档里根本找不到,比如微信开发者工具的端口没开启导致编译失败,比如手机号解密的session_key过期问题,比如打包后体积超标的处理顺序。这些东西如果不亲自踩一遍,光看官方文档很难注意到。

给准备上手的朋友几个建议。第一,一定要先在本地完整跑通再上服务器,不要一上来就在服务器上折腾,服务器上的问题排查起来比本地慢太多。第二,上线前把支付回调日志开起来,至少在运营前半年不要关,这是你排查资金问题的第一手依据。第三,微信小程序和安卓App有各自平台的适配细节,特别是H5和App的微信支付跟小程序不是一套东西,业务上线前最好把所有端的功能全部走一遍。

如果你拿这套源码是打算直接运营的,那有一点必须重视:场馆排期和订单的并发处理。多个用户同时抢一个场次,后端没有加锁的话会出现超卖。我在本地测试时曾模拟过两个账号同时下单同一时段,结果两个订单都创建成功了。后来在后端下单接口加了行锁才解决。给订单表和场次表加上唯一索引,下单用事务,这个问题就能防住。这类看似细小的并发问题,往往能让一个看起来跑得很好的系统在运营压力下瞬间崩溃。

关于二次开发的方向,我觉得这套系统值得扩展的地方不少。比如活动促销模块,比如会员储值卡,再比如对接第三方地图做场馆周边配套展示。这些功能都建立在这套PHP+UniApp的框架之上,扩起来并不难。关键是把基础的东西搞扎实,别急着堆功能,先把预订、支付、核销这条主链路打磨到足够顺滑。

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

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

立即咨询