多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

微信小程序“校友惠”超市管理系统开发全流程解析

微信小程序“校友惠”超市管理系统开发全流程解析 做这类“师范大学校友惠超市”微信小程序管理系统的大概率是计算机相关专业的毕设选题。拿到题目别急着敲代码我当年接这个题目的时候也差点一上来就写页面后来发现这套系统的核心不在小程序本身而在“校友”“惠”“管理”这三个词背后的业务逻辑。这篇就把我从需求拆解、技术选型、模块实现到答辩前踩坑的完整过程捋一遍给正在做类似毕业设计的朋友一个能直接参考的路线。1. 先拆题目这套系统为什么不是普通超市收银系统1.1 需求层面“校友惠”三个字才是产品灵魂单看“超市管理系统”很多人第一反应是做一个商品增删改查、订单列表、库存管理的后台再套个小程序壳就算完事。但加上“校友惠”这个前缀后整个需求模型就变了这是一套面向特定校友群体的、带身份识别和投惠价格体系的线上超市。换句话说它至少要回答这几个问题谁能买只有某高校的校友或在校师生能享受“惠”的价格校外人员要么不能下单要么以原价购买。“惠”在哪儿校友价便宜多少是固定折扣还是梯度优惠要不要叠加优惠券谁来管超市管理员需要在后台维护商品、上下架、处理订单、核销自提或配送。用户端长什么样首页商品展示、搜索、分类、购物车、下单、支付或模拟支付、订单查询、个人中心、校友认证。我见过大量翻车案例都是把重点放在“能跑通”上忽略了“校友”和“惠”这两个词带来的核心差异点。评委老师也最看重这里你有没有把校友身份体系和优惠体系做进数据库设计里而不是只用一个小程序页面挂着。所以第一步建议强制自己画一张角色-功能对照表。这张表比代码还重要后续所有接口设计都从它来。角色核心诉求关键功能未认证游客随便看看商品浏览、搜索、查看价格但下单受限认证校友/在校师生享受校友优惠价认证、会员价、优惠券、下单、订单查询超市管理员高效运营商品管理、分类管理、库存管理、订单处理、用户管理、优惠券配置1.2 为什么选微信小程序而不是H5或App毕设题目里写了“微信小程序”这个选择本身就是合理的。原因有三点一是微信生态天然带支付能力哪怕是模拟支付也能通过后端状态机预留接口二是微信小程序的审核和体验流程明确评委老师手机上扫码就能直接看比让评委装App或者开浏览器输网址强太多三是从开发成本角度单体的小程序前端加一个后端服务一个人完全扛得下来。如果你愿意用uni-app写还能顺便跨端未来想出一版支付宝小程序只是多点一次编译的事。我这次就是用uni-app原因后面会说核心是为了少写一套原生语法、页面路由更顺手。2. 微信小程序端的页面架构不堆页面的前提下把场景走通2.1 tabBar怎么设计最省事小程序端我用的是“41”的tab结构首页、分类、购物车、我的再加一个“校友认证”作为独立触发页面。这个结构几乎是校园购物类小程序的标配不需要创新稳定才是最优先的。首页上需要包含顶部搜索框、轮播图放超市活动或新品、分类快捷入口、推荐商品列表。这些数据全部由后端接口提供前端只负责渲染。搜索框占位文字建议直接写“搜索商品名称”不要用“搜索你想买的商品”这种文案虽然看起来亲切但用户在手机上点击时并不会觉得好反而增加困惑。购物车页面有两点容易被忽略一是购物车数据本地存储和远程拉取的策略。如果是未登录状态允许游客加购但要提示“结算前请先认证校友”这个提示逻辑比直接拦截加购体验好二是购物车合并用户退出后重新登录远端购物车记录和本地缓存怎么合并我在这个系统的处理方式是登录以后以服务器Redis的数据为准本地仅做展示反正毕设被考到的概率很高提前想清楚。2.2 页面状态管理登录态决定你看到哪个价格很多人写小程序前端喜欢把用户信息存在globalData里页面一多就混乱。我的做法是在uni-app里统一封装一个userStore登录后将后端返回的userInfo、token、role游客/校友/管理员全部写进去页面通过计算属性拿当前角色。价格展示逻辑非常经典也最容易出bug游客看到的价格是“市场价”点击购买跳出认证引导。认证校友看到的是“校友价”校友价比市场价低其他再叠加优惠券。管理员在小程序端只保留“商城管理后台入口”实质管理功能放在Web管理端小程序端就留一个入口菜单和几个经营简易看板即可不要硬塞一个功能臃肿的运营后台进去。这是给评委演示时的重要区分点同一种商品三种角色的页面渲染结果不同一眼就能看出系统有完整的身份与授权体系。2.3 前端请求封装和接口风格我习惯每个请求都封装成统一的request方法好处是统一带token、统一处理401跳转登录、统一处理loading状态、统一错误提示。uni-app中封装的核心部分大概是这样的// utils/request.js const BASE_URL https://your-api-domain.com/api export function request(path, { method GET, data {}, auth true } {}) { return new Promise((resolve, reject) { uni.request({ url: BASE_URL path, method, data, header: { Content-Type: application/json, ...(auth ? { Authorization: Bearer ${getToken()} } : {}) }, success: (res) { if (res.statusCode 200) { if (res.data.code 0) { resolve(res.data.data) } else if (res.data.code 401) { removeToken() uni.navigateTo({ url: /pages/login/index }) reject(new Error(登录已过期)) } else { uni.showToast({ title: res.data.msg || 请求失败, icon: none }) reject(new Error(res.data.msg || 请求失败)) } } else { uni.showToast({ title: 网络错误, icon: none }) reject(new Error(网络错误)) } }, fail: (err) { uni.showToast({ title: 请求失败, icon: none }) reject(err) } }) }) }这套封装看起来简单但实际减少的重复代码非常可观。页面里调用时只需要关心业务数据不用管toast、跳转登录这些杂事。虽然只是毕设项目但接口风格干净答辩的时候体验差距很大。试试看你也能写出同样顺手的前端工具层。3. 后端与数据库设计让“校友优惠”经得起追问3.1 数据库该建哪些表表结构怎么让评委一眼看懂后端我选了Node.js搭配Express框架数据库用的MySQL。为什么不用云开发、不用纯云函数因为在答辩现场评委老师几乎必然会要求你打开数据库查看表结构和数据关系。MySQL的表关系清晰你能现场打开Navicat展示ER图讲清楚外键逻辑这是纯云函数方案很难直观呈现的。核心表设计如下一共7张表名主要字段作用usersid, openid, real_name, student_no, role, is_auth, phone用户与校友身份goodsid, name, category_id, market_price, member_price, stock, image, status商品定义categoriesid, name, sort商品分类ordersid, order_no, user_id, total_price, pay_price, discount_amount, status, created_at订单主表order_itemsid, order_id, goods_id, goods_name, price, quantity订单明细couponsid, name, type, value, min_amount, expire_start, expire_end, stock优惠券模板user_couponsid, user_id, coupon_id, status, got_time, used_time用户优惠券关系这张表设计要能解释清楚价格链路用户看到的价格可能经过商品校友价和优惠券两层优惠所以订单里必须有discount_amount字段记录总共优惠了多少钱。这个字段在答辩演示时建议单独准备一条订单放大给评委看。3.2 校友身份认证演示安全的聪明方案校友身份认证要做得真实但不触碰隐私红线。入校时的学号姓名匹配是一个常见方案。生产上需要对接学校学籍接口咱们毕设阶段不需要这么复杂做一个“本地名单比对”就行。我在系统里是这么做的维护一张alumni_auth_list辅助表里面是预置的学号和姓名映射用户提交姓名和学号后查询这条表匹配则users.is_auth置为1身份改为校友不同步任何真实身份数据最终演示效果接近真实场景却没有隐私风险。这里有个容易被追问的点如果用户输入别人的学号怎么办方案是提交后仅系统后台可见不暴露姓名同时后台记录提交IP和手机号并且同一手机号只能认证一次。你可以在答辩时说出这个设计老师会觉得你考虑过安全问题而不是傻傻地全部放行。3.3 优惠价格计算逻辑这段代码建议直接写进论文价格计算最好独立成一个模块不要把计算业务写在接口里面。我把价格计算函数放到了utils/price.js伪代码如下// 计算商品应付金额 // goods: { market_price, member_price } // userRole: alumni | normal // coupon: 优惠券对象或null function calcPayPrice(goods, userRole, coupon) { // 第一步确定商品基础价 const basePrice userRole alumni ? goods.member_price : goods.market_price // 第二步检查优惠券是否可用 if (!coupon) return { basePrice, payPrice: basePrice, discount: 0 } const canUse coupon.min_amount ! null basePrice coupon.min_amount if (!canUse) return { basePrice, payPrice: basePrice, discount: 0 } // 第三步按优惠券类型计算抵减金额 let discount 0 if (coupon.type fixed) { discount Math.min(coupon.value, basePrice) } else if (coupon.type percent) { discount Math.floor((basePrice * coupon.value) / 100) } const payPrice Math.max(basePrice - discount, 0) return { basePrice, payPrice, discount } }这个函数一定要保证“优惠金额不小于支付金额”并且整数运算一律向下取整否则容易出现一分钱下不了单或者金额对不上的情况。在论文里放这个函数评委看完会认为你具备模块化思想。3.4 库存扣减极简但不出错的方案库存扣减是答辩必问也是我当初最担心说清楚的模块。我不推荐上悲观锁、乐观锁、Redis分布式锁因为毕设用不上说错了反而露怯。我用的是事务加条件更新的方式UPDATE goods SET stock stock - ? WHERE id ? AND stock ?配合后端事务如果影响行数为0说明库存不足或并发已经导致冲突直接抛异常回滚。这种方案简单、稳妥而且能正面答复并发场景下的库存问题。如果以后想扩展还能提到Redis预扣库存的方案但现阶段没必要。4. 支付与调试环节没有企业资质怎么跑通完整流程4.1 微信支付被卡住怎么办模拟支付这条路线靠谱吗腾讯对微信支付的接入条件是小程序必须要有企业主体、开通微信商户号并且域名要求备案且为HTTPS。而咱们学生做毕设基本没有这些资质。我当时的处理方式是实现“模拟支付”流程。具体来说就是订单创建后小程序端点击“去支付”调起自己的支付确认页面而非真实调起微信支付。点击确认后前端请求/api/pay/mockPay后端把订单状态从“待支付”改成“已支付”并扣减库存。另外我会在接口里预留一个payType字段未来如果接真实支付只需要把模拟支付逻辑替换为wx.requestPayment的调用并增加支付回调验签即可。4.2 支付宝、微信、模拟支付三者的取舍对比很多同学纠结模拟支付会不会被老师看低其实完全不会。要表述清楚的是“为什么用模拟支付而不是真实的支付能力”——因为个人开发者无法开通微信支付商户号这是微信平台的硬性门槛不是你偷懒。同样把真实支付的接入代码写成可替换的模块已经超出很多毕设的完整度了。支付方式是否需要企业主体是否产生真实资金流推荐场景微信支付wx.requestPayment需要个人无法申请是上线正式售卖模拟支付后端标记订单状态不需要否毕设演示、本地开发云开发微信支付同样需要商户号是有资质的团队项目4.3 本地调试时要绕过哪些微信小程序限制开发过程中最容易卡住调试的环节。本地跑后端接口时微信开发者工具会默认校验“合法域名”你直接用http://localhost:8080会报“url not in domain list”。解决方法有两个一是开发工具右上角打开“详情-本地设置-不校验合法域名”二是用真机预览时在手机端打开开发者工具的“调试模式”。另外一个坑是图片资源小程序中用的商品图片如果使用外链地址需要把域名添加进小程序的“downloadFile合法域名”和“业务域名”。否则页面上的图片加载不出来你在微信开发者工具里未必能发现因为工具默认允许一上真机就黑图。5. 源码控制与答辩演示最容易翻车的细节清单5.1 商品图片和外链资源建议全部本地化在毕设演示场景里现场经常没网。你知道那种感觉演示到一半页面上的图片全部加载失败只剩一张张小叉号那种尴尬真的是刻骨铭心。我吃过一次亏之后就做了三件事第一把演示素材全部换成图片压缩后的本地文件或者上传到七牛云之后下载对应的静态目录第二小程序端图片直接存放在自己后端的静态目录里通过/static/goods/xxx.jpg这样的路径访问第三确保所有图片小于300KB避免列表页滚动卡顿。后台管理端如果上传新图记得同步转存到后端本地目录别留外链依赖。5.2 状态机与常量管理订单状态字段不要散落在代码里订单状态看似简单实际很考验代码整洁度。我见过不少项目把1、2、3、4这种魔法数字直接写在接口或页面上后来改需求状态对不上痛苦无比。所以我建了一个constants目录专门管理状态// constants/order.js module.exports { ORDER_STATUS: { PENDING_PAY: 0, // 待支付 PAID: 1, // 已支付 SHIPPED: 2, // 已发货/自提中 COMPLETED: 3, // 已完成 CANCELLED: 4, // 已取消 REFUNDING: 5, // 退款中 REFUNDED: 6 // 已退款 } }前端页面根据状态码做展示映射后端只在业务逻辑中引用这些常量不在SQL里直接写数字。这套做法看着不起眼对于代码评审和后续维护来说帮助很大也方便你快速定位问题。5.3 微信公众平台的配置小程序AppSecret不要打到前端在调试登录功能时很多人会把小程序AppSecret直接写在小程序代码里。这是极其严重的错误——你的AppSecret一旦泄露别人就能冒充你的小程序后端调用接口。正确的姿势是后端的config目录里通过环境变量读取该配置小程序端只通过uni.login拿临时code然后调用自己的后端接口/api/auth/login由后端完成code2session换取openid。这个流程必须写清楚论文里也值得单独画一段时序说明。5.4 演示前的三条后路演示的时候意外情况频发我总结三个保底策略录制一条完整的操作视频手机录屏后台操作录屏时长控制在3-5分钟万一现场演示环境挂了放视频也是一种稳妥的演示方式。准备一份“演示脚本”从游客浏览、认证校友、查看校友价、领取优惠券、下单、模拟支付、后台处理订单每一步的操作路径和预期结果写清楚。别嫌这个写得细现场一紧张全靠它。数据库里预置好演示数据至少6个分类、20个商品、3种优惠券、2个测试账号一个校友、一个游客、3个订单。不要在答辩现场现建数据等待时间越久评委越不耐烦。个人最后的体会是这类系统做成“好看的Demo”并不难难的是把业务逻辑闭环表述清楚。你愿意花时间把“校友认证—优惠计算—下单扣库存—订单状态流转”这条主线的每一步都走踏实答辩时自然有底气。现在网上能下到的源码比比皆是但能不能把源码变成自己说得清、改得动的东西就看下没下这一步工夫了。
返回列表