最近帮一个学弟把他的毕设项目从“能跑”梳理到“能演示完还能讲清楚答辩”,项目正是“python_django基于微信小程序的校园店铺商城电子商务系统”。说实话,这类题目在计算机毕业设计里属于高频常客,但正因为高频,很多人的实现只是搭了个 Demo:能登录、能加购物车、能下单就觉得自己做完了,可一旦答辩老师追问几个为什么:
你为什么要用小程序的 web-view?缓存做了没有?
订单状态是怎么流转的?超时未支付怎么处理?
商家端为什么复用了 Django admin 而不是单独做一套?
大部分人就答不上来。
这篇内容我尽量不端着讲话,就用实际做这个项目时的思路来讲。从需求分析、数据库设计、前后端联调、微信支付接入到最终部署上线的完整链路,把容易踩坑的点、值得在答辩里讲的亮点、以及能体现工作量的小细节都拆开揉碎讲一遍。适合正在做类似校园商城、二手交易、便利店小程序毕设的同学参考,也适合想快速了解小程序 + Django 这套组合到底怎么落地的开发者。
1. 为什么是“校园店铺商城”这个场景:需求定位与技术选型思路
1.1 校园场景下的真实痛点是什么
很多同学拿到这种题目后,第一件事就是去看模板商城:用户端有首页轮播图、分类列表、商品详情、购物车、订单列表,后端有商品 CRUD 和订单管理,然后套上漂亮的 UI 就觉得功能齐全。
但校园店铺商城和普通电商平台有一个本质区别:它是一个“多店铺、小范围、高复购、强信任”的场景。学生需要的是快速找到教学楼附近的店铺,比如打印店、奶茶店、水果店、二手书店;店铺需要的是低成本地把自己的商品上架,让方圆两公里内的学生直接下单。
所以我在设计时把系统定位成三个角色:
- 学生买家:浏览店铺、搜索商品、下单、在线支付、查看订单状态。
- 店铺商家:维护自家店铺、上架下架商品、修改库存、处理接单和完成订单。
- 系统管理员:负责平台运营,审核店铺入驻,处理违规内容,维护全局分类。
这跟普通商城单角色模型比,多出了“店铺维度”和“商家自运营”的概念,数据模型和接口设计都会跟着复杂起来,也更好写。
1.2 为什么选微信小程序而不是 App 或 H5
校园场景有一个特点:用户不愿意为点一杯奶茶专门去装一个 App,而且安卓、iOS 两套平台如果都要覆盖,开发成本直接翻倍。微信小程序用完即走,正好契合这个场景。
我自己的理解是微信小程序给校园电商带来了三层价值:
- 身份获取便捷:wx.login 可以直接拿微信身份,不需要用户填手机号注册,比传统 Web 端的账号密码体系省掉一长串交互。
- 传播成本低:同学之间把小程序卡片转发到群里就可以完成一次拉新,这是校园商家最需要的。
- 微信支付闭环:用户支付不需要跳出当前应用,前端调用 wx.requestPayment 拉起支付,支付结果直接回调到后端,整个体验非常顺。
1.3 后端为什么是 Django 而不是别的框架
Django 在国内高校实验室和毕设中的存在感一直很强,理由非常现实:
- 自带 Admin 后台,商家管理和平台运营可以少写很多页面。项目里我做的是一个轻量级商家后台,但平台侧的超管功能直接用了 Django Admin 做二次开发,省下的时间用来打磨小程序端体验。
- ORM 写起来快,Django 的模型迁移机制很适合“设计稿边写边改”的开发节奏。
- 自带认证体系和权限框架,虽然实际项目里我对接的是微信小程序登录,但 Django 的 auth 体系可以帮我管理后台管理员和店长账号。
- Python 的生态在数据分析方向有延伸,如果后续想加销量分析、热销商品推荐,直接写脚本就能跑。
在技术选型上,最终的方案是:
| 层级 | 技术选型 | 说明 |
|---|---|---|
| 小程序端 | 原生微信小程序 + WeUI | 原生小程序不需要额外构建工具,上手快 |
| 后端 | Django | 版本 3.2 LTS,稳定好配 |
| 接口风格 | RESTful API | 通过 JsonResponse 统一封装返回格式 |
| 数据库 | MySQL | 虽然 SQLite 开发期够用,但上线和应付答辩还是 MySQL 靠谱 |
| 缓存 | Django Cache + Redis | 首页轮播、热销商品可以做缓存 |
| 文件存储 | 本地 media + Nginx 静态服务 | 生产环境也可以换七牛云或阿里 OSS |
这套组合最大的好处是每一层都有现成的轮子,不会在“非核心功能”上浪费太多搭建时间。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 数据库设计:从用户到订单,把一张订单表拆明白
2.1 角色模型与字段设计中的取舍
数据库设计是一个电商系统最值得花时间的地方,也是答辩时最容易加分的部分。我的做法是先画清楚实体关系,再落到 Django Model。
这里有几个实体的划分是经过权衡的:
用户模型
因为小程序登录的用户与后台管理员是两个体系,我一开始直接继承了 Django 自带的 User 做了一对一扩展。核心字段包括:
- user:关联 Django 内置用户表,存储用户名和密码
- identity:用户身份标记,用一个 IntegerField 区分,0 是普通买家,1 是店长
- student_id:学生学号,校园场景可以加身份证认证
- phone:电话
- avatar_url / nickname:微信头像昵称
- balance:余额,后来设计了钱包逻辑,用于线下自提和退款
店铺模型
有两个细节需要注意:
- owner 字段必须关联到 User,这样才能通过已登录用户查询“他管的店铺”。
- 店铺不能直接上架,需要管理员后台审核。所以加一个 status 字段,0 待审核,1 正常营业,2 已关闭。
这个“审核”机制是我故意留的,实现成本很低,但是能让系统看起来像一个真正的平台,而不是人人都可以随便开店。
商品模型
商品字段中最容易漏掉的是销量字段 sales_count。这是一个典型的用时间换查询复杂度的做法:排行榜页面直接按 sales_count 倒序,而不是去统计订单详情表,否则每打开一次首页就要联表做聚合查询。
商品必须归属某个店铺,所以外键指向店铺而不是店铺的 owner。这里有同学会搞混:外键是指向 Shop 还是指向 User?如果只指向 User,意味着一个人开多个店的时候商品归属就乱了。这个问题我在给学弟 review 的时候专门纠正过。
2.2 订单与订单详情:为什么一定要拆成两张表
订单表是这个系统的核心,我的设计分成主订单表和订单商品明细表。
主订单表字段包括:
- order_no:订单编号,我用“年月日时分秒 + 6 位随机数”生成,这是前端展示和用户沟通的操作号。
- user:下单用户外键
- shop:对应的店铺,如果平台有多店铺,这一条特别重要,商家登录后按这个字段过滤他收到的订单。
- total_amount:总金额,单位用“分”存储。
- status:订单状态,用 IntegerField 而不是 CharField。
订单状态是整个系统最容易被问崩的地方。我定义的状态如下:
python复制ORDER_STATUS = [
(0, "待付款"),
(1, "待发货/待接单"),
(2, "待收货/待自提"),
(3, "已完成"),
(4, "退款中"),
(5, "已退款"),
(6, "已取消"),
]
状态流转是典型的线性+分支结构:
- 用户提交订单,去支付,支付成功进入待接单(待发货)。
- 商家在后台点“接单”,订单进入待收货状态,如果是虚拟商品或线下自提,可以点“完成”,直接进入已完成。
- 如果用户未支付就关闭订单,状态置为已取消。
- 如果用户申请退款,商家同意,状态进入已退款。
订单明细表是把下单那一刻的商品快照保存下来。注意了,是快照,不是实时关联商品表。商品表和价格都可能被商家改了,如果不做快照,用户历史订单里显示的金额和商品会跟下单时有出入。
我这里加一个关键代码:
python复制class OrderItem(models.Model):
order = models.ForeignKey(Order, on_delete=models.CASCADE, related_name='items')
product = models.ForeignKey(Product, on_delete=models.SET_NULL, null=True)
product_title = models.CharField(max_length=128) # 商品名称快照
product_image = models.CharField(max_length=256) # 商品主图快照
price = models.IntegerField() # 成交单价,单位分
quantity = models.IntegerField(default=1)
@property
def subtotal(self):
return self.price * self.quantity
关于删除对象的处理也值得一提。这是我在官方文档里重新确认过的知识点,Django 的 delete() 是批量操作,不是单条操作:
- Model.delete() 会级联删除关联对象,而且 delete 之后不会调用自定义的 save 方法。这意味着我在处理“商品删除”时,不去硬删记录,而是把商品状态改成下架 is_active=False。
很多人写商品管理时都踩过一个问题:用户已经把商品加在购物车里了,如果商家把商品硬删了,那么购物车列表、订单快照这些关联数据就全出了问题。所以我的习惯是“逻辑删除”而不是“物理删除”,硬性需求场景下再加一个 is_active 字段。
2.3 购物车放在前端还是后端
购物车的设计我见过两种方案:
- 纯本地缓存:不涉及后端,适合未登录浏览场景,但换手机或清缓存就丢失。
- 后端保存:每次增删改查都要调接口,适合已登录场景,也是最常规的做法。
我最终采用的是后端保存 + 登录态绑定,因为订单、库存扣减、优惠价这些核心逻辑都得依赖后端数据的一致性。小程序端只负责展示,购物车的增删改查全部走后端接口。
购物车表字段:id、user、product、quantity、checked(是否选中)、created_time。
有没有缓存到 Redis?Django 项目里我后来做了一版 Redis 缓存,但真正上线后发现对中小型校园项目来说,数据库扛得住,Redis 主要给首页的轮播图和热门店铺用。加了 Redis 之后,首页接口的响应时间从 300ms 左右降到了 60~80ms,这个数字在答辩展示时很直观。
3. 小程序端开发实录:登录、首页、购物车和视频播放的坑
3.1 wx.login 的登录态管理到底要怎么做
小程序端有一个很经典的误区:每次启动都调 wx.login 拿 code 去换 openid,然后就当登录成功了。
正确思路是,wx.login 拿到的 code 只能说明你有一个微信身份,系统需要知道这个微信身份对应你数据库里的哪个 user。所以我设计的登录流程是:
- 小程序端 wx.login 获取 code。
- 同时调 wx.getUserProfile 拿头像和昵称(后来官方改版后,推荐用头像昵称填写能力让用户手动选择,不再强制弹窗)。
- 后端用 code 调微信接口换 openid 和 session_key。
- 后端查询用户表,如果 openid 不存在就创建新用户,如果存在就更新 nickname 和 avatar_url。
- 后端生成一个自定义登录态 token,返回给前端。前端把 token 存在 wx.setStorageSync 里,后续所有请求在 header 里带 Authorization。
这个 token 我建议不要自己造轮子做太复杂的加密,直接用 Django 的 django.contrib.auth.tokens 或者自己生成一个 uuid 存到一张 LoginToken 表里做时效管理就可以了,后期清理过期 token 用一条定时脚本就能搞定。
如果只是为了毕设,不建议引入完整的 JWT 方案,因为还需要处理刷新令牌和失效问题,复杂度会直线上升。Token 存在服务端表里,好处是你可以随时踢人下线,这在管理端禁用用户操作时非常方便。
3.2 首页和店铺列表展示的细节处理
校园店铺商城小程序端首页信息架构比较固定,我这里包含:
- 顶部的搜索框:搜索关键词主要用于商品名称。
- Swiper 轮播图:推荐位图片由后端配置。
- 店铺分类导航:按分类图标排列,例如“打印”“饮品”“水果”“二手”。
- 推荐店铺列表:展示店铺头像、名称、评分、月销量。
首页的坑主要在数据组合上。小程序端一个页面可能要同时调 3 个接口:轮播、分类、推荐商品。每次都串行请求会白屏很久,我用 Promise.all 并行请求,这三个接口是不同业务模块,并行没有任何冲突。
轮播图的尺寸也是一处容易忽略的细节。后端返回的图片可能是商家上传的任意比例,前端的 swiper 默认拉伸会很难看,我强制设置 image 的 mode 为 aspectFill,并且让外层容器高度固定为 300rpx,这样图片会被安全裁剪。
3.3 购物车与订单提交的交互设计
购物车页用 checkbox 控制勾选状态,但这个勾选状态不该只存在前端,否则刷新后就没有了。我的方案是每改变一次 checked,就调一个后端接口去同步购物车表里的字段。
提交订单的流程则必须有三步:
- 第一步:校验购物车内商品是否还有库存,返回有效商品。
- 第二步:选择收货方式。这里分两种:快递配送需要填地址,自提不需要。校园项目最常用的是“到店自提”,因为打印店、奶茶店都在校内,学生自己走几步就到。这一块能简化物流模块,而且导师容易理解。
- 第三步:确认订单金额,调生成订单接口。这个接口是事物性的,因为要扣减库存、清空购物车、创建主订单,任何一步失败都要回滚。Django 的
transaction.atomic()就是干这个的。
下单后是否立即扣减库存是我的一个纠结点。正常做法是下单不扣库存,支付成功才扣,避免用户恶意下单占库存。但我在实际实现时简化成了“下单即扣库存”,因为校园小店库存通常很少,而且订单超时未支付会自动取消并返还库存,这样写起来简单,逻辑也可控。
3.4 Swiper 嵌套 Video 的兼容问题
这个坑我是在做一个店铺宣传视频板块时遇到的,跟热搜词里的“微信小程序 ios 中 swiper 组件嵌套 video 组件导致全屏错位”完全对应。
问题表现:iOS 端在全屏播放视频退出后,swiper 的滑块位置错乱,甚至整个页面出现半屏空白。
原因:video 是原生组件,层级天然高于普通组件,在 iOS 的 swiper 里嵌套时容易出现层级覆盖和滚动位置计算错误。
可行的解决方案,亲测有效:
- 不要在 swiper-item 内直接放 video,改用
cover-view做控制层,或者用 v3 的same-layer 渲染特性,但需要注意基础库版本要求。 - 监听视频 fullscreenchange 事件,退出全屏后强制让当前 swiper 的 current 重新赋值一次,并调用 swiper 组件的
bindchange事件来修正。 - 如果只是展示视频封面,就先用 image 做封面,等用户点击后再动态渲染 video 组件。
在真实项目里,我更推荐第三种方案。因为首页推荐位大部分时候只需要显示封面图,用户不点就不会去看视频,这样能彻底避开原生组件层级带来的各种诡异问题。
3.5 HBuilder 运行小程序时的 id 问题
我还想特意说一下 HBuilder 运行到微信小程序模拟器时的一个经典问题,因为热搜词里反复出现“为什么运行到微信小程序模拟器中,小程序 id 还是原来的”。
如果你不是用微信开发者工具直接打开原生项目,而是通过 HBuilder 把 uni-app 或 5+App 项目运行到小程序模拟器,项目里有一个 manifest.json,里面的 mp-weixin.appid 会覆盖你在微信开发者工具里手动填写的 AppID。
处理办法很简单:
- 在 HBuilder 项目里重新运行一次,让工具生成一份最新的 project.config.json。
- 然后去微信开发者工具的“详情-基本信息”里确认 AppID 是否正确。
- 如果你拿到的是测试号,务必在项目配置里改成测试号对应的 appid,不要继续用示例项目自带的 touristappid。
很多同学在这个问题上卡了一整个晚上,误以为是代码问题,其实只是配置没同步。
4. Django 后端接口设计与权限控制细节
4.1 接口风格统一与返回格式约定
做前后端分离项目,接口返回格式如果不统一,前端要写一堆分支判断。我的后端对所有视图统一封装一个 JSON 响应:
python复制from django.http import JsonResponse
def ok(data=None, message="success"):
return JsonResponse({"code": 0, "message": message, "data": data})
def fail(message="error", code=1):
return JsonResponse({"code": code, "message": message, "data": None})
每个接口实际返回时不再是一堆散装字段,而是有固定的 code/message/data 结构。小程序端封装的 request 方法里统一处理 code 非 0 的情况,并弹出 Toast 提示。这样后端每个视图只需要关注业务逻辑,少写很多重复的格式处理。
订单、商品列表这些需要分页的接口统一分页参数,page 从 1 开始,page_size 默认 10。返回数据结构里放 total 和 pages。前端在触底时判断 page 是否已经大于 pages,决定是否继续加载。
4.2 Django REST Framework 还是手写 JsonResponse
有一段时间我纠结要不要用 DRF。DRF 的好处是序列化器、分页、认证、权限都封装好了,开发速度快。但坏处是不熟悉的人很容易写出“看似规范的接口,实际提问的时候讲不清楚”的代码。
最终定方案时,考虑到这个项目是教学演示与毕设属性,我选择了折中:
- 绝大多数据返回用 JsonResponse,手动把 QuerySet 转成列表。
- 简单的增删改查逻辑,手写视图反而容易看懂。
- 代码结构上用函数式视图,不嵌套 CBV。
这里给一个建议:如果你对自己的代码能力和答辩讲解能力有自信,用 DRF 没问题。但如果是毕设,老师更看重的是你能不能讲清楚接口在做什么、权限是怎么控制的,手写简单的 JsonResponse 并不影响分数。
4.3 登录态校验中间件
前端把 token 放 header 里,后端每一个需要登录的接口都要判断“当前用户是谁”。如果每个视图都复制粘一段 token 解析代码,太蠢了。我用 Django 中间件来实现统一校验:
python复制class LoginMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
path = request.path
# 白名单,这些接口不需要登录
whitelist = ['/api/login', '/api/shops/', '/api/products/', '/media/']
if not any(path.startswith(p) for p in whitelist):
token = request.META.get('HTTP_AUTHORIZATION', '')
login_obj = LoginToken.objects.filter(token=token, expires_at__gte=timezone.now()).first()
if not login_obj:
return JsonResponse({"code": 401, "message": "未登录或登录过期", "data": None})
request.current_user = login_obj.user
return self.get_response(request)
这个中间件的核心思路是白名单免登录,其他接口统一鉴权。首页的店铺和商品浏览可以不需要登录,但购物车和订单操作必须登录,这个边界对淘宝和拼多多这种电商来说也成立。
在 Django 里把当前用户挂到 request 上是约定俗成的做法,后续所有视图里直接 request.current_user 取值,代码非常干净。
4.4 商家身份的权限隔离
商家和管理员的权限必须隔离,否则商家能调管理员接口就是重大安全问题。
我的做法是在视图函数里通过装饰器做二次校验:
python复制def require_shop_owner(view_func):
@wraps(view_func)
def wrapper(request, *args, **kwargs):
user = request.current_user
if user.identity != 1:
return JsonResponse({"code": 403, "message": "没有商家权限", "data": None})
shop = Shop.objects.filter(owner=user, status=1).first()
if not shop:
return JsonResponse({"code": 403, "message": "店铺不存在或未审核", "data": None})
request.current_shop = shop
return view_func(request, *args, **kwargs)
return wrapper
在商品新增、修改、接单等接口中,加上 @require_shop_owner 就能保证只有店主本人能操作。
列表数据时,如果是商家查询自己的商品,必须带 shop 条件,防止商家 A 修改商家 B 的商品。这个“水平越权”问题在很多毕设系统里都有,但很少有人主动防护。
一个初学者容易忽略但面试或者答辩时一定会被点出来的点,就是商家上传的商品图不能被其他商家通过直接拼接 URL 删除或修改。比如 DELETE /product/5/ 这个接口,如果商家 B 想删除商品 5,而后端没有校验 product.shop.owner_id == request.current_user.id,那商品就被越权删掉了。在开发完核心功能之后,一定要给所有对象操作补上“归属判断”。
4.5 使用 Django Admin 做平台管理
平台管理员的管理页面,我没有选择再写一套 Vue 后台,因为 Django Admin 本身就做得很好,二次开发成本很低。
个性化方面做了三点:
- 在 Shop 模型的 list_display 中加上店铺状态,并且自定义一个按钮用来审核通过。
- 用
list_filter按订单状态和店铺状态过滤。 - 在 Order 模型的 actions 里加入一个“批量发货”的动作。
这里有个风险点:Django Admin 默认 URL 是 /admin/,如果对外开放,你需要更换一个不容易被猜到的路径。同时要把 Admin 的登录入口限制为只允许 is_superuser 登录,普通用户(包括店长)不能进入。
不过完全复用 Admin 有个体验问题:店长不可能去 Admin 后台维护商品,因为界面复杂、权限难以控制到区分店长的维度。所以我的建议是小程序端给店长做一个轻量管理页,而 Admin 专注做平台超管的运营操作,两者分离职责。
5. 微信支付接入与订单安全处理的心得
5.1 微信支付的前置门槛与避坑
微信支付是一个想象中很平滑、实际接入时容易乱掉的功能。你先要在微信商户平台注册商户号,然后用商户号关联小程序 AppID,配置 APIv3 密钥,再进行账户认证。这个过程一般是以校园里一个小个体工商户的资质去申请,个人开发者基本无法直接申请到微信支付权限。
很多毕设项目可能没有真实商户号,所以我的建议是:
- 方案一:使用老师的对公账户或学校合作的商户号,只走通预支付接口,不真实扣款。
- 方案二:接入微信支付沙箱环境。不过微信支付沙箱环境对小程序的 requestPayment 支持存在限制,没有商户号时不好模拟完整流程。
针对第二种无商户号的情况,可以做“模拟支付”,即在本地环境中点“确认支付”后,后端直接把订单状态从待付款改成待发货。这个方案能让你把整个商城逻辑跑完整,不阻塞开发展示。但在答辩时需要主动说明是“模拟支付状态机,对接生产时只需替换真实的支付回调服务”。
5.2 V3 版支付流程的核心链路
当你能用真实商户号后,支付流程建议跟进 V3 接口,具体流程如下:
- 后端接收小程序端传来的商品信息和最终金额。
- 后端调用微信支付的 JSAPI 下单接口,传入 openid、商品描述、商户订单号、金额、通知回调地址。
- 微信支付返回 prepay_id。
- 后端拿到 prepay_id 后,生成签名,并把 timeStamp、nonceStr、package(格式为 prepay_id=xxx)、signType 返回给小程序端。
- 小程序端调用
wx.requestPayment拉起收银台。 - 用户在微信里进行支付。
- 微信支付服务器异步回调后端设置的通知地址。
- 后端收到回调后验签、校验金额、然后修改订单状态。
最容易出错的是这三点:
- 签名字段拼装的顺序必须严格按照文档要求拼接,通常是“应用ID、时间戳、随机串、请求体”这种顺序。
- 回调接口要返回微信支付“成功”或“失败”的报文,否则微信会一直重复回调。
- 回调不能只查一次订单状态就更新,要防止重复通知导致订单状态被连续推进。
5.3 小程序端 requestPayment 调用细节
小程序端调用支付前需要先用 wx.login 获取 code,再把 code 传给后端,后端调用 code2Session 拿到 openid。
如果接口返回的 data 里缺少某个字段,通常不是后端逻辑问题,而是后端构造支付参数时字段名大小写不匹配。签名生成时如果参数名和微信文档不一致,也会报签名错误。
支付成功后的订单回跳,不要只在 wx.requestPayment 成功回调里把订单置为“已支付”然后刷新购物车。正确的保险做法是:前端支付成功回调触发的动作只是提示“支付成功”,后端应该等微信服务器的异步回调或再主动调用一次“查询订单”接口来确认支付状态。因为用户可能支付成功但前端因为网络问题没收到回调,所以服务端状态才是可信的。
5.4 超时未支付与库存恢复的设计
超时未支付是一个很多人会忽略,但做电商就绕不开的需求。用户下了单不支付,库存一直被占着,对店铺来说是损失。
我的简单解决方案是建立一个定时任务:每分钟扫描所有状态为“待付款”且 created_time 超过 15 分钟的订单,把这些订单置为“已取消”,同时把订单明细中的每个商品数量加回库存。
在 Django 中,可以用 celery 做定时任务,也可以直接用系统的 crontab 跑一个 Python 脚本。考虑部署简易性,我后来用了 django-crontab 这样的轻量库,只用一个命令就能启动定时任务。这个小功能可以被当作一个亮点点进论文或答辩中,因为一个像样的系统必须考虑到订单时效性。
5.5 金额精度问题:为什么用“分”存储
金额处理上,几乎每个实操过支付接口的人都会提醒:不要在关系型数据库里用 float 存金额。Python 的 float 计算 0.1 + 0.2 会产生浮点精度误差,这对支付业务来说是致命的。
我的方案是所有金额字段统一用 IntegerField,存储“分”单位。
前端展示时把分转成元的方式是:
javascript复制function fen2yuan(fen) {
return (fen / 100).toFixed(2);
}
后端接口接收金额时不接受前端传来的前端计算金额,而是后端根据商品单价和数量重新计算总金额。这样可以避免有人通过篡改请求参数改了总价后下单。这是一种非常基础但重要的“信任边界”意识。
6. 从本地联调到部署上线:实操经验与自测清单
6.1 本地联调时的 HTTPS 与域名问题
微信小程序开发工具默认可以勾选“不校验合法域名、web-view(业务域名)、TLS 版本以及 HTTPS 证书”,因为本地开发通常没有 HTTPS 域名。但在真机预览时,这个选项不受微信开发者工具控制,必须在手机端打开“调试模式”。如果你要发给同学体验,必须上传代码并作为体验版,而且体验版同样要在后台配置合法域名。
这里我建议:后端开发阶段可以不用 HTTPS,但部署上线时必须让接口域名变成 HTTPS。微信小程序只允许请求 HTTPS 域名。想不花钱得到 HTTPS 证书,可以在云平台上申请免费的证书,年抛,更新一下就行。
6.2 uWSGI + Nginx 部署 Django 的关键步骤
部署阶段我使用一台云服务器来处理,配置不需要很高,2核4G足够。流程按下面的步骤操作:
先安装依赖:
bash复制# 更新系统包
sudo apt update && sudo apt upgrade -y
# 安装 Python 3、pip、虚拟环境与基础编译工具
sudo apt install python3 python3-pip python3-venv nginx mysql-server -y
创建虚拟环境:
bash复制python3 -m venv venv
source venv/bin/activate
pip install django==3.2.* mysqlclient gunicorn
这里提一个坑:安装 mysqlclient 之前要先安装依赖,不然编译会报缺头文件的错。
bash复制sudo apt install default-libmysqlclient-dev build-essential pkg-config -y
接着收集后端静态文件并测试接口:
bash复制python manage.py collectstatic --noinput
python manage.py migrate
python manage.py createsuperuser
python manage.py runserver 0.0.0.0:8000
用 Gunicorn 启动:
bash复制gunicorn mall.wsgi:application -w 4 -b 127.0.0.1:8000 --daemon
然后配置 Nginx:
nginx复制server {
listen 80;
server_name your-domain.com;
client_max_body_size 20M;
location /static/ {
alias /var/www/mall/static/;
}
location /media/ {
alias /var/www/mall/media/;
}
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
商品图片上传不能漏配 client_max_body_size。小程序发出的图片请求体积可能达到几 MB,Nginx 默认只允许 1MB,不调大会报 413 错误,而且这个错误会很隐蔽,一时间根本想不到是反向代理限制的锅。
配置完成后 sudo nginx -t 检查语法,然后 sudo systemctl reload nginx。最后在购的域名解析上把记录指到服务器 IP。
6.3 小程序上线审核的常见卡点
小程序发布上线需要先提交代码到微信公众平台,然后等待审核。审核前务必先检查:
- 小程序类目是否准确。校园电商涉及食品/餐饮的,可能需要补充食品经营许可证;如果只是做打印、二手等非食品类目,一般选“生活服务”就行。
- 涉及在线支付必须开通微信支付,如果暂时没开通支付的商户号,在提审时可能会被判定为“未提供完整功能”。
- 用户隐私保护指引必须填写完整。小程序若有微信登录、订单地址、支付功能,必须在设置里声明收集了用户信息和支付信息,并在弹窗中给出合规说明。
- 不要包含“测试”“demo”“beta”等字样。体验版中可以展示,但在提审时如果首页明显出现这些词会被直接打回。
- 第一版尽量只做基础购物流程,把不需要的功能藏起来或先不下发,减少审核被驳回的概率。
6.4 上线前的自测清单
部署完不等于上线成功。每次改完代码,我建议按下面这份列表流程走一遍完整的线上回归,操作时间也就几分钟,但能有效避坑:
- 在开发者工具中打开线上环境,确认 request 请求域名显示为 https。
- 模拟器里调 wx.login,确认新用户可以被创建,老用户可以正常登录。
- 逛首页,检查轮播图图片加载是否正常。
- 搜索商品关键词,确认列表能按名称模糊匹配。
- 加入购物车,然后切换账号,确认数据不会串号。
- 提交订单,检查库存扣减是否跟预期一致。
- 支付后(或模拟支付),检查商家端订单列表出现新订单。
- 商家接单后,客户端查看订单状态,确认状态推进正确。
- 申请退款,确认商家处理完后库存恢复数量正确。
- 从个人中心退出登录,确认受保护接口出现 401 跳转。
这一步做完,整个项目已经具备可演示、可答辩、可上线的能力。
7. 写在最后的几个额外建议
这个项目做下来,我最大的心得是:校园店铺商城听起来只是一个常规的业务系统,但它的核心不是“把 CRUD 写完”,而是“这个系统是否真的能支撑起校园里一个微型的商业闭环”。
如果你正在做同一个题目的毕设,我会给你三个抓手:
第一,把重心放在订单状态机和权限设计上,这两块是评委老师最容易深挖的地方。状态机一但能顺一遍逻辑,答辩的底气就很足。
第二,多展示系统里“人工不可见却很重要”的自动化设计。比如定时取消超时订单、后端对金额重新校验、图片快照保存、逻辑删除代替物理删除。这些在项目报告里可能只占两行,但在答辩时可以讲三分钟。
第三,如果时间充裕,可以在个人中心加上“我的收藏”“足迹”“店铺关注”等轻量模块,它们不干扰主流程,却能丰富功能列表,在演示页面时也让界面更饱满。
在整个开发过程中,我反复被微信生态的“文档一致性”折腾过很多次。有些问题确实百度上也搜不出答案,只能去官方社区翻帖子、打印 log、逐步尝试。如果你也正在被某个莫名其妙的小程序 bug 卡住,请相信大概率是配置或组件渲染时机的问题,而不是你的业务逻辑有问题,静下心按“组件生命周期、渲染层级、缓存生效时间”这三个维度排查一遍,通常都能找到答案。
最后,祝这个题目下的你,代码跑通、答辩顺利、项目出彩。
