恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
Spring Boot+微信小程序:咖啡店点餐系统全栈实战解析
首页
资讯中心
/
Spring Boot+微信小程序:咖啡店点餐系统全栈实战解析
Spring Boot+微信小程序:咖啡店点餐系统全栈实战解析
发布时间:2026/10/11 17:38:09
做咖啡店点餐系统这个选题多半是因为它“麻雀虽小五脏俱全”。我刚接这个项目的时候客户的要求很朴素顾客用微信扫码进小程序自己点单前台能接单后厨能看到制作列表账单别算错。真把整个链路拆开才发现这个系统横跨小程序端、服务端接口、数据库设计、微信支付闭环、商家管理后台五个部分随便哪一环粗糙处理都会让线上运营出岔子。这篇文章我会把这套系统的完整实现过程按真实项目落地顺序写出来从技术选型、表结构、接口规划到核心业务逻辑和部署排查给准备自己动手做的同学一个可以直接抄作业的参考。1. 项目背景与整体设计思路1.1 咖啡店点餐场景的痛点分析咖啡店的点餐场景和正餐餐厅不太一样。正餐餐厅有服务员桌边点餐、传菜流程复杂咖啡店的客单价相对集中、出品速度快、高峰期集中而且现在很多店都是“小店模式”一两百平米、两三个店员如果每个顾客都需要吧台排队点单高峰期肯定会积压。还有个痛点在于咖啡的“定制选项”比较多——甜度、温度、豆子类型、是否需要打包这些如果由服务员手工录入或口头确认很容易出错。我们做这套基于微信小程序的点餐系统核心目标就是让顾客自己把需求填清楚订单直接推送到后厨屏或前台小票机把人工确认环节压到最低。顾客扫桌上码或者到店扫码就能看到分类菜单、选购下单、在线支付之后凭取餐号去吧台取咖啡。整个流程对顾客来说是自助的对商家来说是自动接单的在人力有限的情况下可以明显提高高峰期的出单速度。1.2 系统整体功能拆解整个系统按角色可以分成三类使用对象顾客小程序端、商家管理后台、系统管理员后台账号管理。顾客端的主要功能模块包括扫码进入门店、菜单展示与分类筛选、商品详情与定制选项、购物车、订单确认与支付、订单状态查看、历史订单与评价商家端集中在管理网页里包括商品管理和上下架、库存管理、订单接单与制作完成、营业报表、优惠策略配置系统管理员则负责店员账号、门店信息、基础参数维护。这个功能划分逻辑很简单小程序端只做C端展示和交互管理后台做B端运营中间通过统一的后端接口层对接。模块拆得很清晰后续再加功能也很方便。比如有的咖啡店后来又提出了“预约自取时间”的需求就只需要在订单表加两个时间字段小程序端下单页多两个选择器后端在生成订单时多存两个字段完全不影响其他模块。1.3 前后端整体交互流程整体的数据流大概是这样的顾客打开小程序前端通过微信登录拿到临时code提交给后端后端再去微信的接口换取该用户的openid完成静默注册或登录拿到用户身份之后小程序端请求商品分类与商品列表渲染菜单页面顾客加购、选规格、提交订单后端生成订单并调用微信支付统一下单接口拿到支付参数后小程序端调起支付支付完成后微信服务器异步回调后端通知支付结果后端修改订单状态并推送“新订单提醒”给商家端商家操作接单或制作完成顾客在订单详情页看到状态变化最后可以评价。这里面有两个关键点值得注意。第一微信支付必须用的是企业主体的小程序个人主体做不了真实支付这也是很多毕业设计里用“模拟支付”代替的原因第二支付结果以回调为准不能只依赖前端返回的成功状态否则金额对账一定会出问题。关于这两点后面的章节我再详细展开。2. 技术选型与核心组件解析2.1 后端主框架Spring Boot 的选型理由后端选Spring Boot可以说是目前这类小程序管理系统最主流的选择。它的优势在于生态成熟、起步成本低内嵌的Tomcat让部署只需要打一个jar包就能跑起来不像以前用SSH框架要配置一堆XML。Spring Boot的自动配置大幅减少了样板代码结合Spring MVC写RESTful接口非常顺手再配合Spring Validation做参数校验、Spring Data Redis做缓存和Token存储整个后端模块的搭建周期可以压缩到很短。具体到我们这个咖啡店点餐系统Spring Boot还有个隐性优势社区资料特别多遇到问题搜索解决方案的效率比其他技术栈高很多。比如MyBatis Plus的分页插件配置、Sa-Token或JWT的接入方式、支付回调验签的逻辑网上都有大量现成案例可以对照。对于学生朋友或独立开发者来说时间成本是最宝贵的选Spring Boot基本不会踩“框架没人用、遇到问题找不到答案”的坑。2.2 数据存储方案MySQL 与 Redis 的分工数据存储我用了MySQL加Redis的组合。MySQL负责所有持久化数据包括用户表、商品表、订单表、订单明细表、分类表、门店配置表等。Redis则承担三块工作一是保存微信小程序端的登录态或Token避免每次请求都去数据库查用户二是缓存商品分类和热门商品列表小程序端的菜单页面打开频率很高从缓存里拿可以明显降低数据库压力三是处理少量热点数据的计数比如今日订单量虽然这个场景比较简单但用Redis做一个原子自增会很方便。MySQL这边表结构设计有一个经验一定要强调金额字段一律用Decimal不要用Float或Double。我见过好几个项目里咖啡价格用Double存结果前端传 19.9 进来数据库里变成 19.899999订单合计出现莫名其妙的差一分钱问题。Decimal设置精度和标度为(10,2)就足够了万一以后要做满减活动、优惠券分摊两位小数也能保证精度不出错。2.3 小程序端技术方案小程序端我选择的是原生微信小程序开发而非uni-app。为什么虽然uni-app可以一套代码多端复用但对于这个项目目标平台很明确就是微信生态原生框架在权限管理、微信登录、支付调起、订阅消息这些能力上都更直接调试也更方便。原生小程序有自己的组件生命周期和页面路由机制熟练之后开发速度其实不比uni-app慢。如果后续确实有上线App的需求再把业务逻辑抽到uni-app重写也不迟。在项目开发阶段保持简单直接很重要非必要不引入跨端框架。这个项目里小程序端的核心页面包括首页/菜单页、商品详情页、购物车页、订单确认页、订单列表页、订单详情页、个人中心页总共七个页面原生开发足够覆盖。2.4 商家管理后台方案商家管理后台我用了Vue加Element UI单独做一个Web项目。为什么不直接用小程序管理小程序做复杂表格和图表太别扭了商品管理、订单列表这种操作密集型的场景Web的效率和体验完胜。管理后台与后端通过同一套接口交互只是登录认证方式不同小程序端用微信登录换Token管理后台用账号密码登录拿Token。管理后台的模块和C端逻辑对应包括仪表盘今日订单数、营收、热门商品排行、商品管理分类维护、商品增删改、上下架、订单管理列表筛选、接单、制作完成、出餐、以及门店基础信息的配置。对于小店需求来说这套后台已经够用不用再单独部署一套复杂的BI系统。3. 数据库设计与表结构详解3.1 核心表清单概览一张图说清楚这个系统的核心表设计。总共八张业务表user用户、category分类、product商品、product_spec商品规格用于甜度、温度等选项、cart购物车、orders订单主表、order_item订单明细、store_config门店配置。userid, openid, nickname, avatar, phone, balance, create_time categoryid, store_id, name, sort, status productid, category_id, name, image, desc, price, status, stock, sales, create_time product_specid, product_id, spec_name, spec_value, extra_price cartid, user_id, product_id, product_spec, quantity, checked, create_time ordersid, order_no, user_id, total_amount, pay_amount, pay_type, status, pickup_code, remark, create_time, pay_time order_itemid, order_id, product_id, product_name, product_spec, price, quantity, subtotal store_configid, store_name, address, phone, business_hours, notice我刻意没有加冗余的字段比如订单表里不存门店名称因为单店模型里一个门店的配置是全局的查询的时候联表或者直接查配置就好没必要在每张订单上重复存一份。但如果你们做的是多门店连锁模型那订单表就必须加store_id了设计时要想清楚边界。3.2 订单表设计的关键考虑订单表是整个系统的核心设计上有一个地方必须事先想明白订单状态怎么流转。我定义的状态集合是这样0待支付、1已支付待取餐、2制作中、3已完成、4已取消、5退款中、6已退款。这里有一个细节制作中状态是否需要独立取决于商家有没有后厨屏的“接单”动作。如果商家希望在接单后再开始制作那“待接单”和“制作中”就要分开如果店里流程简单可以直接支付后就进入待取餐这个可以灵活处理。订单号字段也值得说一下。我没有用自增ID直接当订单号暴露给用户而是单独生成一个业务订单号格式是时间戳加四位随机数比如咖啡店用量不大这个长度完全够用。自增主键可以作为内部关联用但对外展示和支付回调关联都用业务订单号避免用户看到不连续的订单编号猜测订单量。3.3 库存与销量的并发处理库存的设计立flag的场面又来了。咖啡店的点餐场景库存概念和电商不太一样电商是下单减库存到支付超时再回补库存咖啡店则是支付后减库存因为出杯量有限支付成功才真正占用了制作资源。所以在Hot咖啡店的系统里订单状态从0待支付变成1已支付的时候才去执行库存扣减和销量增加。如果用户支付之后只要还在待取餐状态库存就已经扣了。扣减库存时我用了一条带乐观锁语义的更新SQL防止两个人同时买了同一杯“今日特供”结果库存只减了一次UPDATE product SET stock stock - #{count}, sales sales #{count} WHERE id #{productId} AND stock #{count}这条SQL执行后如果影响行数为0说明库存不足下单接口直接抛出业务异常让前端提示“商品已售罄”。这个方案比先查再改再加分布式锁要简单可靠得多MySQL行锁天然帮我们处理了并发问题。4. 小程序端核心功能实现要点4.1 微信登录与Token管理小程序的登录流程是每个做过微信项目的人都绕不开的环节。微信官方推荐的流程是wx.login获取临时code传给后端/api/auth/login接口后端拿这个code加上小程序的AppId和AppSecret请求微信接口换取openid和session_key。拿到openid后查用户表如果不存在就自动创建一条新用户存在则直接返回登录成功。登录成功后的Token方案我选择了比较轻量的Sa-Token或自定义JWT两者都可以。用JWT的话注意一下客户端请求时在header里带Authorization字段后端用一个拦截器统一校验Token校验通过后从Token里解析出userId放到请求上下文。注销、Token过期这些方法JWT做起来稍麻烦但对于课程设计或个人项目JWT的简单性足够。这里有个小坑小程序前端每次请求都要手动在header里带Token可以用wx.request封装一个统一请求方法内部读取本地存储的Token并自动加在请求头里省得每个页面重复写。4.2 菜单展示、购物车和下单菜单页面是这个系统首页即核心的页面。小程序端用scroll-view配合分类导航实现左侧分类、右侧商品列表的布局右侧商品列表用纵向列表展示商品图和价格点击某一项弹出商品详情或规格选择底部弹窗。规格选择这个功能对咖啡店尤其重要——冰量、甜度、浓度每个商品可以有多个规格维度每个维度里可以有不同的选项。购物车这里有一个交互细节小程序里常驻一个底部购物车横条加购后显示总价和购物车数量点击横条弹出购物车商品列表可以直接修改数量或清空。这个交互非常好用比跳转一个独立购物车页的路径更平滑。我在手机上实测顾客点击“去结算”时潜意识里对已选商品的确认成本更低。4.3 支付流程与回调处理这块是整套系统里最容易出问题的地方。在实际支付流程中wx.requestPayment需要拿到后端预支付交易单的参数。后端在生成订单后调用微信支付的统一下单接口拿到prepay_id再按规则生成小程序端所需的paySign。前端调起支付后会在success回调里拿到一个结果但这个结果只表示微信支付收银台被成功调起并不能代表支付真的成功了。真正的支付结果是通过微信服务器异步回调后端接口的方式通知的。所以后端的设计必须是统一下单时传入的notify_url指向我们自己的接口支付成功后微信回调该接口更新订单状态。不能信任前端回调里的状态一切以异步通知为准。同时回调接口要做好验签还有一个小技巧回调可能因为网络问题重复发送多次处理时要做幂等判断——如果订单已经是支付成功状态就不再重复处理。这里我不建议在这个系统里接完整的退款和退款回调除非客户真的有这个需求。退款处理涉及原路退款、回调处理、订单状态回滚多个环节复杂度会明显上升。如果只是课程设计或演示在管理后台做一个可以标注“退款”状态的字段就够了。4.4 订单状态的消息触达咖啡店场景里顾客最关心的是“我的咖啡好了没”。我实现的方式是商家在管理后台点击“出餐完成”时小程序端订单详情页通过轮询或WebSocket感知到状态变化。轮询实现最简单全局每5秒请求一次订单详情状态变了就刷新界面WebSocket体验更好但小程序端对WebSocket的封装和后台推送服务的搭建成本稍高。刚开始做的时候我图省事直接用轮询后来发现流量消耗大而且低并发下还能接受高并发时后端压力上升明显。折中的方案是仅在用户的订单详情页打开时轮询离开页面就停止再叠加一个微信订阅消息的推送提醒。第一次版上线后实测如果做真实运营建议用订阅消息推送“取餐提醒”给用户体验会专业很多。5. 后端接口设计与业务实现5.1 接口风格与统一返回结构后端接口我统一使用了RESTful风格所有接口的返回结构也统一定义为一个Result类包含code、message和data三个字段。code为0表示成功非0为各类错误码。前端根据code判断接口调用是否成功而不是依赖HTTP状态码——因为很多业务异常我们主动捕获后仍然返回200这样小程序端的wx.request不会走fail逻辑处理起来统一。大概列出核心接口清单模块接口路径方法说明登录/api/auth/loginPOST用code换取登录态商品/api/product/listGET获取分类和商品商品/api/product/detailGET商品详情含规格购物车/api/cart/addPOST加入购物车购物车/api/cart/listGET购物车列表订单/api/order/createPOST创建订单订单/api/order/payPOST调起支付参数订单/api/order/detailGET订单详情订单/api/order/listGET订单列表支付回调/api/pay/notifyPOST微信异步回调5.2 创建订单与调起支付的完整流程创建订单接口是整个系统业务逻辑最集中的地方。调用/api/order/create时后端需要做的操作包括校验用户Token、从请求中获取购物车商品列表、遍历商品核算总金额、检查商品上下架状态和库存、生成订单主表和订单明细、清空购物车对应商品。这些操作必须放在一个事务里任何一个环节出错都要整体回滚。这里要注意的是校验库存时不要直接扣库存保持0待支付状态时不占用库存等到支付回调成功时再扣。支付接口的参数构造细节是后端要把订单金额、订单号、用户openid组装成微信支付要求的请求体调微信的/v3/pay/transactions/jsapi接口现在的V3版本拿到prepay_id后再用商户私钥对小程序Id 时间戳 随机串 prepay_id做签名。签名算法有点绕直接用现成的工具类会省很多事。如果支付成功后要做积分或会员等级也可以在这个回调方法里一起处理。5.3 商家后台接口规划商家后台的接口和管理端的前端页面一一对应。商品新增和编辑、分类维护、订单接单和出餐、取消订单、每日营业数据的聚合查询。营业数据查询这里我写了一条基于日期分组的SQLSELECT DATE(create_time) as day, COUNT(*) as order_count, SUM(pay_amount) as total_amount FROM orders WHERE status IN (1,2,3) AND create_time #{startTime} GROUP BY DATE(create_time) ORDER BY day DESC注意这里统计的是支付成功的订单不要把头取消的订单也算进营业额。对于偶尔出现的退款则单独在退款表中记录下来不给营业数据造成混淆。后台页面用Vue写配合一个简单的表格和筛选条件五分钟就能完成这块开发。6. 核心业务场景的难点攻破6.1 购物车的合并与批量操作购物车看似简单但细节很多。这个系统的购物车按用户维度存储同一个用户加购同一个商品且规格完全相同应该合并数量而不是插入两条如果规格不同则分开记录。前端展示时按加入时间倒序排列。批量清除已下单的购物车项一定要用delete from cart where id in (...)配合已选中的ID集合而不是前端每删除一项就发一个请求否则网络开销极大。还有一点容易被忽略商品可能在下单前被后台下架或价格调整。这时候创建订单接口需要重新从数据库取商品价格而不是信任前端传过来的单价。前端展示的价格只是参考以数据库实时价格为准避免出现顾客看到的价格和结算价格不一致的纠纷。6.2 金额计算与满减优惠咖啡店经常做的活动是“满30减5”或“第二杯半价”。在订单创建服务中金额计算我建议单独封装一个PriceCalculator类不要和其他业务逻辑混在一起。这个类输入购物车商品列表和活动规则输出原始金额、优惠金额、实付金额。活动规则尽量从数据库读取而不是在代码里写死——因为一周后店长可能又改了活动方式。金额计算时还有一处细节所有中间计算结果都不要四舍五入最后实付金额再统一保留两位小数。比如“第二杯半价”如果直接按单杯价格算17块钱一杯第二杯半价就是8.5但两杯一起按总价打折可能算出8.4999这种数如果每杯分别计算再相加就保留两位小数就没有这个问题。这类边界情况在测试时一定要覆盖。6.3 超卖与重复支付的防护前面说到的UPDATE库存带条件判断就是防超卖的关键。重复支付的防护则要保证幂等性微信异步回调可能因为网络原因重试多次回调方法里第一步就是查订单如果状态已经是1已支付直接返回成功通知不再重复处理。同时在改订单状态时用UPDATE orders SET status 1 WHERE id ? AND status 0利用行锁确保状态只能从待支付改成已支付一次。数据库层面可以给订单状态加索引和约束应用层面加幂等判断双层保障。虽然支付重复回调的概率本身很低但真遇到一次就会导致订单状态错乱和库存多扣的严重问题。当时我在本地模拟重复回调时就是因为没有判断状态导致扣了两次库存这种坑写出来提醒大家一定要先判断再更新。6.4 并发领取优惠券与超发问题如果系统里设计了优惠券模块还有一个并发问题要处理。在咖啡店场景里常有“到店扫码领券”的活动多个顾客同时扫同一个码可能出现券被超发的情况。处理方式可以在券模板表里加一个remaining字段领取时用条件更新扣减。和库存扣减是同一种思路。7. 环境配置与部署上线的常见问题7.1 小程序合法域名与HTTPS配置后端接口做上线部署时第一个拦路虎就是小程序的合法域名配置。微信规定wx.request的域名必须在微信公众平台后台配置为HTTPS的合法域名开发阶段可以在开发者工具里勾选“不校验合法域名”但真机预览或线上体验时必须配置。所以要提前准备好备案过的域名和HTTPS证书用Nginx把后端服务的端口代理到443上。Nginx配置的主要作用是反向代理和HTTPS终结。将前端的请求通过域名转发到本机8080端口让客户端只和域名通信。这里有几个细节配置SSL证书时证书要放在服务器上同时注意Nginx的keepalive参数因为小程序的并发请求会复用连接如果没有设置keepalive 128这类参数连接反复建立会拖慢响应速度。7.2 数据库的时区与连接池配置数据库相关的坑比较隐蔽。第一个是时区问题create_time如果使用MySQL的datetime类型连接串里没有加serverTimezoneAsia/Shanghai时从Java取出来的时间会差8个小时第二个是连接池的配置默认的初始连接数太小高峰期一开小程序店面连接池可能排队。这两个问题我在部署阶段都遇到过定位起来不困难但如果没有经验很容易以为是代码逻辑写错了。个人建议在application.yml里显式配置Druid或HikariCP的连接池参数初始连接数设定为10最小空闲连接数5最大连接数50。另一个小技巧是订单表的create_time加一个默认值CURRENT_TIMESTAMP这样插入数据时不需要显式传时间减少代码层面的遗忘。7.3 真机调试和模拟器的差异小程序开发里模拟器表现和真机表现经常有差异。最常见的是底部安全区域适配问题——iPhone全面屏底部有一个横条区域如果购物车横条设计得贴底在真机上可能会跟Home指示条重叠。解决办法是给底部栏加上padding-bottom: env(safe-area-inset-bottom)。真机的网络请求也有差异模拟器里可以用局域网IP直连本机后端但真机必须使用HTTPS域名。所以开发前就要准备好一个测试域名和测试证书哪怕是自签名的证书也要在小程序后台配置好或者用微信开发者工具的“真机调试”功能它会自动把请求代理到本地这个方式能省很多事。7.4 版本迭代时的数据库变更开发到后期加字段是家常便饭。我建议从一开始就给项目引入一个简单的数据库版本管理工具比如Flyway每次表结构变更写一个版本的迁移SQL。这样多人协作或换机器部署时只需要执行一次mvn flyway:migrate数据库结构就更新了。如果不做版本管理等到上线后给客户增改字段你要手工跑到服务器执行SQL一旦忘记执行某个脚本线上就会出现诡异的接口报错。我有一次就是因为商品表加了is_recommend字段后本地数据库手动执行了SQL没有问题但测试环境忘了执行页面一度报“未知列”错误。排查了半天才发现是环境数据库不一致引入Flyway之后再也没有出现这类问题。8. 踩坑复盘与个人经验总结这个项目做到最后最深的体会是一套点餐系统真正的工作量不在增删改查而在于把业务边界想清楚。库存什么时候扣、支付状态信谁、优惠怎么算、并发怎么防这四个问题想清楚了后面就是体力活。很多同学拿到这个题目上来就写代码做到中间才会发现订单状态流转和支付回调是最难改的部分返工成本极高。有几条经验可以说一下对后续有类似项目一定有参考价值。第一先做核心理清状态机。订单状态的定义和流转图要画清楚再动手建表。状态枚举字段用数字不要用字符串数字保持稳定方便扩展如果需要中文展示前端自己去映射不要在后端存中文。第二权限控制不能偷懒。小程序端Token管理要上线拦截器统一处理管理后台的接口也不能裸奔。至少做角色区分普通店员只能操作订单和商品不能修改门店配置管理员才有全部权限。哪怕是一个小店系统权限控制也决定了后台能不能放心给员工用。第三测试案例要覆盖极端情况。购物车同时加两个相同商品时数量是否正确合并、支付并发回调是否重复处理、库存只剩1杯时两个人同时下单是否只有一个成功、订单金额计算0元或负数时是否抛异常。这些用例在写代码时就要在脑子里过一遍把防御性校验写在接口的最前面不要等用户操作出了问题再去修。最后再分享一个小技巧。开发过程中小程序端的“体验版”二维码是一个很好用的协作方式让客户或者店主扫一下二维码就能在真机微信里直接使用系统。体验版在提交审核前可以随时更新非常适合快速收集反馈、调整细节。我每次改动完关键流程都会发一个新的体验版给客户测几轮下来客户的实际需求就能摸得比较清楚。这个系统做完之后我又在它的基础上给另外两家店做过定制一家加了预约自取功能另一家做了针对会员储值的余额支付。核心结构没有动只是在订单和支付模块上做扩展。这也说明设计良好的点餐系统本身就是一套可以复用的基础体系从一个单店模型出发往多门店、会员营销、后厨数字化管理方向延伸都比较顺畅。但第一版不要贪多先把点餐、支付、出餐这条主链路跑通比什么都重要。项目做到这里我的总结是复杂的事情简单做简单的流程重复做系统稳定上线的底气就来自对每个业务细节的死磕。