☰
微信小程序点餐源码解析:Java后端与微信支付的全链路闭环
2026/10/9 1:54:55 网站建设 项目流程

简介:微信小程序商城源码面向餐饮行业开发者与小程序初学者,整合了在线点餐所需的页面、逻辑与配置,是一套可直接参考的商城前端工程。压缩包共60个文件,约1.18MB,主要文件类型包括WXML/WXSS页面组件、JS交互逻辑、JSON页面配置以及说明文档,覆盖菜品展示、购物车、订单确认等核心模块的结构与事件处理流程。资源已有1148人学习,内容涉及微信支付集成、云开发、后端API对接等实践环节;配套的README还能帮助理解项目目录、启动方式与二次开发要点。通过分析这套源码,可以逐步掌握微信开发者工具的使用、WXML与WXSS语法、网络请求与数据缓存API,以及前后端协作中常见的设计模式。整体适合希望以真实项目快速入门小程序电商开发,并在此基础上延伸学习Java后端服务的新手进行实战练习与改造。

1. 微信小程序点餐源码:一套Java后端撑起菜单、购物车与支付闭环

做餐饮小程序点餐系统,最容易掉进去的坑就是「前端界面做得像模像样,后端订单和支付一联调就翻车」。市面上打着"源码"旗号的小程序点餐项目,很多只给了一套静态页面和几行Mock数据,真正要面对微信登录、支付回调、并发扣库存这些硬骨头时,才发现根本不是那么回事。这个标题里的关键信息其实是「源码」和「Java」——前者意味着你要拿到的是能直接运行、能改逻辑的真实代码,后者决定了后端选型是Spring Boot那套生态。今天这篇笔记就围绕「微信在线点餐小程序 + Java后端」这套组合,把菜单展示、购物车、下单支付、订单状态流转的全过程拆开讲透,告诉你一张点餐订单从用户指尖点到商家出单,中间到底经历了哪些模块、哪些表、哪些接口,以及真正上线时哪些地方最容易让你怀疑人生。

这套方案适合三类人:准备给自家店做点餐小程序的传统餐饮老板,想把「小程序商城」能力复用到外卖、到店扫码点餐场景的开发者,以及正在做毕业设计或接外包项目、需要一套能讲清楚闭环的Java后端点餐代码的同学。文里给的建表语句、接口设计和踩坑记录,都是可以直接照着落地到项目里的东西。

2. 点餐系统的订单模型先行:数据库表设计与小程序页面骨架

2.1 五张核心业务表:从用户到订单明细的字段规划

做点餐系统,我习惯先定数据库再写接口。因为点餐的业务链路特别清楚:用户浏览菜品、加购物车、下单、支付、商家出餐。这里不需要复杂的权限体系和商品规格矩阵,五张核心表就能覆盖主线业务——用户表、菜品分类表、菜品表、订单主表、订单明细表。如果想把购物车也存到服务端,再加一张购物车表,但用Redis做购物车会更顺手,这点后面单独说。

先看用户表和菜品相关表的设计:

-- 用户表 CREATE TABLE `t_user` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `openid` varchar(64) NOT NULL COMMENT '微信openid,同一小程序下唯一', `nickname` varchar(64) DEFAULT '' COMMENT '用户昵称', `avatar` varchar(255) DEFAULT '' COMMENT '头像地址', `phone` varchar(20) DEFAULT '' COMMENT '手机号', `create_time` datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_openid` (`openid`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 菜品分类表 CREATE TABLE `t_category` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `name` varchar(50) NOT NULL COMMENT '分类名,如热菜/凉菜/饮品', `sort` int(11) DEFAULT 0 COMMENT '排序值,小的在前', `status` tinyint(4) DEFAULT 1 COMMENT '1启用 0停用', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 菜品表 CREATE TABLE `t_dish` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `category_id` bigint(20) NOT NULL, `name` varchar(100) NOT NULL, `price` decimal(10,2) NOT NULL COMMENT '售价,单位元', `image` varchar(255) DEFAULT '' COMMENT '菜品图片URL', `description` varchar(255) DEFAULT '', `stock` int(11) DEFAULT 999 COMMENT '库存,0表示沽清', `sales` int(11) DEFAULT 0 COMMENT '月售,用于前端展示', `status` tinyint(4) DEFAULT 1 COMMENT '1上架 0下架', PRIMARY KEY (`id`), KEY `idx_category` (`category_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

这段DDL把每个字段的用途都写在注释里了,实际开发中照着复制改就行。几个容易纠结的点说下:openid必须建唯一索引,这是用户表的核心约束,同一个微信号在不同小程序里的openid不同,但在同一小程序下唯一;price用decimal而不是float,避免金额精度问题,这算是做支付场景的基本素养;stock字段在点餐系统里通常不像电商那样严格,但写上总比没有好,至少售罄的菜能通过接口返回状态让小程序端置灰。

订单相关的两张表是设计重点:

-- 订单主表 CREATE TABLE `t_order` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `order_no` varchar(32) NOT NULL COMMENT '订单号,业务展示用', `user_id` bigint(20) NOT NULL, `table_no` varchar(20) DEFAULT '' COMMENT '桌号/自取号,门店场景用', `remark` varchar(255) DEFAULT '' COMMENT '用户备注,如不要辣', `total_amount` decimal(10,2) NOT NULL COMMENT '订单原价总额', `pay_amount` decimal(10,2) NOT NULL COMMENT '实付金额,含折扣后', `status` tinyint(4) NOT NULL DEFAULT 0 COMMENT '0待支付 1已支付 2制作中 3已出餐 4已完成 5已取消', `pay_time` datetime DEFAULT NULL COMMENT '支付时间', `create_time` datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_order_no` (`order_no`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 订单明细表 CREATE TABLE `t_order_item` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `order_id` bigint(20) NOT NULL, `dish_id` bigint(20) NOT NULL, `dish_name` varchar(100) NOT NULL COMMENT '冗余菜品名,防止菜品改名后历史订单错乱', `dish_price` decimal(10,2) NOT NULL COMMENT '冗余下单时的单价', `quantity` int(11) NOT NULL COMMENT '数量', `create_time` datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_order_id` (`order_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

订单明细表里有两个冗余字段——dish_name和dish_price,这是我特别要强调的。原因很简单:菜品表的数据是可变的,今天这道菜卖12块,明天搞活动卖9块9,但已支付的订单里的单价就不能跟着变。订单明细里存冗余字段,是对历史订单的存档保护。否则出了纠纷你查账时发现金额对不上,那才是真灾难。这点做的时候很容易漏,但属于「不踩坑不知道,踩了才懂该写」的设计。

2.2 小程序端页面骨架:菜单列表、购物车与订单确认的WXML结构

小程序端我一般按四个页面搭骨架:点餐首页(菜单列表)、购物车浮层(一般内嵌在首页)、订单确认页、订单列表页。用户从看到菜到下单支付的路径越短越好,所以首页直接承载「点菜+购物车+去下单」三个功能。

页面结构用原生小程序框架写即可,不需要引入UI组件库,避免增加包体积和改造成本。核心的首页菜单区域大概长这样:

<!-- 首页:左侧分类,右侧菜品列表 --> <view class="page"> <view class="category-bar"> <scroll-view scroll-y class="category-list"> <view wx:for="{{categories}}" wx:key="id" class="category-item {{activeCategoryId === item.id ? 'active' : ''}}" bindtap="onCategoryTap">@RestController @RequestMapping("/api/auth") public class AuthController { @Autowired private AuthService authService; @PostMapping("/login") public Result<String> login(@RequestBody LoginRequest request) { // request.code 来自小程序端 wx.login() 返回值 String token = authService.wxLogin(request.getCode()); return Result.success(token); } }
@Service public class AuthService { @Value("${wechat.appid}") private String appid; @Value("${wechat.secret}") private String secret; public String wxLogin(String code) { // 1. 向微信服务器换 openid 和 session_key String url = "https://api.weixin.qq.com/sns/jscode2session?appid=" + appid + "&secret=" + secret + "&js_code=" + code + "&grant_type=authorization_code"; String resp = HttpUtil.get(url); JSONObject json = JSONUtil.parseObj(resp); // 2. 调用失败时 openid 为空,抛出异常 String openid = json.getStr("openid"); if (StrUtil.isBlank(openid)) { throw new BizException("微信登录失败:" + json.getStr("errmsg")); } // 3. 查用户是否存在,不存在则自动注册 User user = userMapper.selectOne( new LambdaQueryWrapper<User>().eq(User::getOpenid, openid)); if (user == null) { user = new User(); user.setOpenid(openid); user.setNickname("微信用户"); userMapper.insert(user); } // 4. 生成 JWT token,有效期 7 天 String token = JwtUtil.createToken(user.getId(), user.getOpenid(), 7 * 24 * 3600 * 1000L); return token; } }

这段代码有三处关键设计。第一,openid换取的URL是固定的微信官方接口,参数就四个,appid和secret从配置文件读取,js_code是前端传过来的动态值,grant_type固定为authorization_code。第二,用户不存在时自动注册,这样用户第一次打开小程序就已经是登录态,不需要额外的注册流程——点餐场景讲究「无感登录」,多一步注册就会流失用户。第三,JWT里只放userId和openid,不放其他冗余信息,避免token过重。

JWT的解析我放在拦截器里统一处理,所有需要登录的接口都过一遍鉴权,拿到userId后放到ThreadLocal里供后续Service使用。这里有个习惯值得养成:一定要在请求结束时清理ThreadLocal,否则线程复用会导致用户数据串号,属于Web开发的老坑。

3.2 菜品列表与购物车接口:一套参数映射和Reduce库存的读写方式

菜品列表接口按分类维度返回,小程序端切换分类时重新拉取,或者一次拉全量由前端过滤。门店菜品数量一般几十个,全量拉取没什么压力。接口返回菜品基础信息加上当前用户购物车里该菜品的数量,方便前端直接渲染加减器。

购物车我用Redis Hash来存,key是cart:{userId},field是dishId,value是数量。选Redis而不是MySQL的原因很直接:购物车读写频繁且数据不需要持久化,用户关掉小程序再打开购物车还在就够用了,存数据库反而增加订单链路的复杂度。Redis操作购物车的代码靠RedisTemplate实现:

@Service public class CartService { @Autowired private StringRedisTemplate redisTemplate; private static final String CART_PREFIX = "cart:"; // 加入购物车,数量累加,最多不超过99 public void addItem(Long userId, Long dishId, Integer quantity) { String key = CART_PREFIX + userId; String field = String.valueOf(dishId); Long count = redisTemplate.opsForHash().increment(key, field, quantity); if (count > 99) { redisTemplate.opsForHash().put(key, field, "99"); } } // 购物车列表:返回菜品ID + 数量 public Map<Long, Integer> getCart(Long userId) { String key = CART_PREFIX + userId; Map<Object, Object> entries = redisTemplate.opsForHash().entries(key); Map<Long, Integer> result = new HashMap<>(); for (Map.Entry<Object, Object> entry : entries.entrySet()) { result.put(Long.valueOf(entry.getKey().toString()), Integer.valueOf(entry.getValue().toString())); } return result; } }

购物车用Redis Hash的数据结构,increment命令天然支持原子累加,不用担心并发导致的数量错乱。设置了99的上限,防止手滑一直点加号加到离谱的数量级。这里注意一点:购物车里的菜品信息(菜名、价格)不要也存到Redis里,购物车只存「dishId映射quantity」,菜品信息查询时再去MySQL拿。原因是最新价格和菜品上下架状态以数据库为准,缓存菜品信息会出现改了价格但购物车结算还是老价格的尴尬情况。

减库存的操作放在下单接口里做,而不是加购物车时做。购物车阶段不锁库存,用户加了一堆菜不支付,菜品库存不该被扣。下单时先查库存再扣减,用乐观锁或条件更新防止超卖。核心SQL就一句:

// 扣库存:仅当 stock >= quantity 时才更新,返回影响行数判断是否扣减成功 int rows = dishMapper.reduceStock(dishId, quantity); if (rows == 0) { throw new BizException("菜品「" + dishName + "」库存不足"); }
UPDATE t_dish SET stock = stock - #{quantity} WHERE id = #{dishId} AND stock >= #{quantity}

条件更新把「检查库存」和「扣减库存」合成一句原子SQL,避免先查再改的竞态窗口。影响行数为0说明库存不够,直接返回友好提示。这是做库存扣减最朴素但最有效的方案,比悲观锁、分布式锁在点餐这种低并发场景下性价比高得多。当然如果你做的是万人抢券那种秒杀级系统,这套就不够用了,但那是另一个话题。

4. 下单后的事:微信支付对接、订单状态机与消息推送

4.1 微信支付V3接入:统一下单与参数签名流程

点餐系统的支付环节是绕不开的,微信支付V3是目前的主流方案。流程上分两步:后端调微信支付接口创建预支付单拿到prepay_id,把支付参数返回给小程序端,小程序端用wx.requestPayment唤起支付收银台;用户输密码/指纹支付后,微信服务器异步通知后端回调地址,后端验签后更新订单状态。

先看后端创建预支付单的核心代码:

@Service public class PayService { @Value("${wechat.appid}") private String appid; @Value("${wechat.mchid}") private String mchid; @Value("${wechat.api-v3-key}") private String apiV3Key; @Value("${wechat.private-key-path}") private String privateKeyPath; @Value("${wechat.notify-url}") private String notifyUrl; // 创建微信支付预支付单 public String createPrepayOrder(Long userId, String orderNo, Integer totalFee, String description) { // 1. 构建请求参数,金额单位是分 JSONObject params = new JSONObject(); params.set("appid", appid); params.set("mchid", mchid); params.set("description", description); params.set("out_trade_no", orderNo); params.set("notify_url", notifyUrl); JSONObject amount = new JSONObject(); amount.set("total", totalFee); // 单位:分 amount.set("currency", "CNY"); params.set("amount", amount); // 2. 调用微信支付V3下单接口 // 需要先构造签名头,这里用微信官方SDK简化签名逻辑 // 具体签名算法包含请求方法、路径、时间戳、随机串、请求体 String url = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi"; String response = WechatPayClient.postWithSign(url, params, privateKeyPath); // 3. 从响应里取出 prepay_id,返回给前端 JSONObject json = JSONUtil.parseObj(response); return json.getStr("prepay_id"); } }

支付接口的参数里有个容易忽略的点:总金额totalFee的单位是分,不是元。前端传过来的金额如果用户点了两份15元的菜,后端算出来是3000分,这里搞错单位会导致支付金额差100倍,属于支付接入第一大坑。另一个关键参数是out_trade_no,这是商户订单号,必须保证唯一,而且最好就是订单表中的order_no字段。微信官方允许商户订单号的字符集有限,建议只用数字和字母,不要存下划线或中文进去。

微信支付V3的签名机制比V2复杂不少,要构造Authorization头,把请求方法、请求路径、时间戳、随机串、请求体拼接后用商户私钥做SHA256withRSA签名。这些底层的HTTP封装我不建议手写,直接引入微信支付官方Java SDK会更省心,自己手写签名的血泪经验是:90%的「签名错误」报错都出在请求体排序不一致,排查极其痛苦,属于玄学问题。

4.2 支付回调处理与订单状态机:幂等处理和状态流转的细节

支付回调是微信支付里最重要的一个环节,也是易错点。微信支付在用户支付成功后会向notify_url发POST请求,携带密文数据。回调接口必须做三件事:解密数据、验签确认是微信服务器发来的、根据out_trade_no更新订单状态并返回成功的响应给微信。如果回调接口挂了或者返回异常,微信会持续重试,直到你处理成功。

先定义订单状态枚举和流转规则:

public enum OrderStatus { PENDING_PAY(0, "待支付"), PAID(1, "已支付"), MAKING(2, "制作中"), READY(3, "已出餐"), COMPLETED(4, "已完成"), CANCELLED(5, "已取消"); public final int code; public final String desc; // 构造方法和getter省略 }

订单状态机的核心逻辑是:待支付 → 已支付 → 制作中 → 已出餐 → 已完成。取消操作只允许出现在待支付阶段,已支付的订单要取消必须走退款流程。状态之间的流转我做了一个统一的transition方法,禁止跨状态跳跃,比如待支付订单不允许直接变成已完成,必须经过支付回调或商家操作逐步流转。这样能防止状态异常导致的订单不可追溯。

回调接口的核心实现:

@RestController @RequestMapping("/api/pay") public class PayNotifyController { @Autowired private PayService payService; @Autowired private OrderService orderService; @PostMapping("/notify") public String wxPayNotify(@RequestBody String body, @RequestHeader("Wechatpay-Signature") String signature) { try { // 1. 验签 + 解密请求体,拿到明文订单数据 NotifyData notifyData = payService.decryptNotify(body, signature); // 2. 根据 out_trade_no 查订单 String orderNo = notifyData.getOutTradeNo(); Order order = orderService.getByOrderNo(orderNo); if (order == null) { return failResponse("订单不存在"); } // 3. 幂等处理:订单已经是已支付状态,直接返回成功,不再重复更新 if (order.getStatus() == OrderStatus.PAID.getCode()) { return successResponse(); } // 4. 校验金额是否与订单实付金额一致,防止篡改 if (!order.getPayAmount().equals(notifyData.getTotalFee())) { // 金额不一致,记录告警日志,不更新订单状态 log.error("订单金额不一致:orderNo={}, expect={}, actual={}", orderNo, order.getPayAmount(), notifyData.getTotalFee()); return failResponse("金额校验失败"); } // 5. 更新订单状态为已支付,记录支付时间 orderService.markPaid(orderNo); return successResponse(); } catch (Exception e) { log.error("支付回调处理失败", e); return failResponse("处理失败"); } } }

回调处理里有三个不能妥协的原则。第一是幂等,微信支付的通知机制是「不成功就一直重试」,同一个支付结果可能收到多次通知,如果每次收到都更新订单状态,第二次通知进来订单已变成「已支付」,再更新一遍虽然不会出错,但一旦你在回调里做了「累计积分」「推送消息」这类有副作用的操作,重复执行就是事故了。所以先查状态,已经是目标状态就直接返回成功。第二是金额校验,回调里的金额必须和本地订单的payAmount一一比对,防的是有人伪造回调或者中间人篡改数据。第三是响应格式,成功时微信要求返回200状态码和特定格式的JSON报文,返回非成功响应会触发微信重试,而且重试会有延迟递增机制,处理不了的话会一直骚扰你的接口。

订单状态变化后,商家侧需要有感知。「制作中」「已出餐」「已完成」这几个状态,小程序端用户能主动查看到,商家侧则建议用订阅消息推送,也就是点餐系统里常见的「支付成功通知商家备餐」。小程序订阅消息需要用户主动触发授权的动作来获取订阅次数,比如点击「下单并支付」按钮时调一下requestSubscribeMessage,用户允许了商家才能推送。这个流程要早设计,不要在项目快上线了才想起来,否则审核时会被以「无用户主动授权订阅」为由驳回。

5. 微信小程序点餐系统避坑指南:支付回调、审核与并发踩过的五个坑

5.1 坑一:支付回调收到的不是JSON而是加密字符串,解密后格式还千奇百怪

现象:第一次联调支付回调时,把微信发过来的报文体直接打印出来,发现是一串密文,跟接口文档里说的「订单信息」完全对不上。然后照网上的解密代码试了一堆,有的解出来是对象,有的解出来是字符串,直接JSON.parse就报错。原因:微信支付V3的回调数据是AES-256-GCM加密的,notify_url收到的是resource密文,需要先用APIv3密钥做解密。而网上各种教程用的证书序列号、商户私钥、APIv3密钥各不相同,混用必挂。解决:统一使用微信官方SDK里的回调解密方法,传入APIv3密钥即可。不要自己拿商户私钥去解密,那是V2的玩法,V3解密用的是APIv3密钥,不是商户API证书私钥。二者不是一个东西。

5.2 坑二:开发者工具里一切正常,真机上请求直接失败

现象:小程序在开发者工具上模拟点餐、加购物车、下单都没有问题,一放到真机上就白屏或接口全部超时。原因:开发者工具默认不校验合法域名,而真机上每个wx.request的域名必须在小程序后台配置到request合法域名里,且必须是HTTPS。加上本地联调时用的如果是http://localhost,真机直接拦掉。解决:开发阶段在开发者工具后台把「不校验合法域名」勾上,上线前把所有接口域名替换成HTTPS的正式域名,并配置到小程序后台的request合法域名列表。这里背后还有个附带约束:域名备案和HTTPS证书,ICP备案、SSL证书一个不能少,不然审核必被拒。我之前给某门店做点餐系统,就是卡在域名备案上整整一周。

5.3 坑三:下单接口被刷,库存被大量扣减但订单全是待支付

现象:上线没多久发现t_dish表的stock有些热门菜品变成负数,但订单列表里查询不到对应的支付成功订单,全是待支付状态。原因:用户恶意或者手滑了好几次,每次点「提交订单」都会扣一次库存,但如果用户不去支付,库存也没加回来。而且扣库存和创建订单是在两个操作里,中间出异常还会导致订单没有生成但库存已经被扣。解决:把「扣库存+创建订单」放到同一个数据库事务里,同时给待支付订单设置一个失效时间。我用的方案是Redis记录待支付订单的orderNo和过期时间,定时任务扫描超过30分钟未支付的订单,做事务操作:订单状态改为已取消、库存回补。另外,下单接口做防重处理,同一个用户同一批菜品重复提交时幂等返回第一单。

5.4 坑四:小程序审核被驳回,理由是「涉及虚拟支付」或「类目不符」

现象:提交审核后收到拒审消息,说小程序包含虚拟商品支付功能,需要调整或移除。原因:点餐系统属于餐饮服务类目,但如果在菜单里加了「会员充值」「积分兑换」之类的功能,就会被判定为虚拟支付,微信不允许小程序做虚拟商品的支付。另一个常见驳回原因是「诱导分享」,比如分享得折扣、分享后解锁菜品这类玩法直接踩线。解决:审核版本里把虚拟充值入口全部隐藏或下线,分享类功能只保留「分享给好友」这个无激励按钮。类目选择「餐饮服务」下的「点餐/外卖」,资质按要求上传食品经营许可证。这里有个经验:审核用的版本和正式版本建议做成两套配置,审核版本去掉有争议的模块,通过后再把正式版放出来。

5.5 坑五:订单超时关单和支付回调同时到达,状态互相覆盖

现象:用户下单后一直没支付,30分钟到了定时任务把订单取消并回补了库存,结果就在同一秒微信支付回调来了,回调逻辑看到订单状态是「已取消」,但金额校验通过了,于是把订单重置成了「已支付」。库存乱了,订单状态也乱了。原因:关单和回调是两个独立线程在操作同一个订单,没有做状态前置校验。解决:核心原则是「先查状态再更新」,状态更新语句带条件:只允许从待支付改成已支付,已取消状态不允许再更新为已支付。同一行数据的update语句加上status条件,一个操作改了状态,另一个操作的更新行数为0,就能感知到并做对应的报错处理。这比锁更轻量且可靠,是订单状态更新的标准姿势。

6. 让这套源码真正能跑进门店:部署、联调与订单打印的落地技巧

6.1 本地联调环境搭建:小程序开发者工具与Java后端的无缝对接

生产环境的部署方式是标准的Spring Boot Jar包 + Nginx反向代理 + MySQL + Redis,这套没什么玄机。难点反而是本地联调阶段怎么让小程序和本地后端对接顺滑。我的做法是:本地启动后端服务监听8080端口,用Nginx把443端口的HTTPS请求转发到本地8080,再把小程序后台的request合法域名临时指向这个本地域名。这样小程序端不用改任何代码,走的就是正式环境同样的HTTPS链路。没有现成域名和证书的话,还有一个取巧方案:开发者工具右上角打开「不校验合法域名」的开关,直接请求http://localhost:8080,接口能打通就行。但要注意,这个方案只适合开发调试,不要带到生产环境去。

数据库和Redis的初始化也建议写成一键脚本,包括建表SQL和测试数据。门店点餐系统一个很重要的场景是「演示给客户看」,能做到把脚本一跑,小程序一打开,菜品菜单齐全、下单支付流程通顺,这笔单子就成了一半。脚本里预置几类菜品分类、十几道带图片的菜品,以及一个测试账号的openid,省去每次从头开始点的痛苦。

6.2 订单打印与多门店扩展:点餐源码往后走的两条路

小程序点餐系统做到能下单能支付只是及格线,真正交付给餐饮老板还要解决订单怎么出到后厨的问题。常见的方案是接入蓝牙打印机,小程序通过蓝牙接口把订单内容发给后厨的打印机。原生小程序有wx.openBluetoothAdapter和wx.writeBLECharacteristicValue这套蓝牙API,但不同打印机的服务UUID、特征值UUID、数据格式各不相同,适配工作量大。更省事的是选支持「小程序直连」的云打印机品牌,商家在小程序里扫码绑定打印机,后端在下单成功后调用打印机的HTTP接口推送订单内容,把打印机和业务系统解耦。这个方案实现简单、稳定性高,也是目前门店用得最多的。

多门店扩展是另一个绕不开的话题。单店版的点餐系统表结构里没有shop_id字段,要支持多门店就要在订单表、菜品表、用户表上都加门店维度。更彻底的做法是引入门店表,把菜品和分类归属到门店下,订单归属到门店下,用户表保持全局。多门店的复杂度主要在三块:菜品管理和库存隔离、订单路由和打印分发、营业数据的门店维度统计。不建议一开始就做多门店,先把单店跑透,有真实需求再重构。重构时表结构预留不够、写死的门店信息散落各处,这种代价我经历过,只能算是交学费。

最后说个我自己的习惯:每次给门店做完点餐系统,我都会在交付清单里写清楚三个联系方式——我的微信、服务器运维的工单入口、以及微信支付商户平台的技术支持入口。不是客套,是因为点餐系统跑在营业时间里,一旦出问题,老板比你还急。有一个凌晨两点接到电话排查支付回调的经历之后,我养成了在回调接口打详细日志、在Redis里缓存最近订单请求串的习惯。希望帮到你。

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

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

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

立即咨询