1. 项目整体设计与技术选型思路
1.1 校园店铺商城到底在解决什么问题
校园电商和外面的大电商看起来都是“买卖”,实际做起来完全是两码事。学生群体高度集中,消费场景天然带周期性和潮汐性:饭点下单奶茶、晚上买夜宵、开学季买二手教材、毕业季处理闲置。配送范围就是三平方公里内的宿舍区教学楼,客单价普遍在5到50元之间,对送达速度的要求远高于对物流追踪的要求。更关键的是,校园里的“店铺”往往不是标准公司,可能是学生创业团队、校内打印店、二手书摊、宿舍美甲,很多没有营业执照,也不具备对接第三方电商平台的条件。
所以这个系统在需求层面要解决的核心问题不是“做一个淘宝”,而是“做一个校园内的轻量交易基础设施”。它要同时服务三类角色:学生买家要能快速找到最近可送的店铺、下单支付、实时看订单状态;店铺商家要能上架商品、管理库存、处理订单、核销提货;管理员要能审核店铺入驻、下架违规商品、处理纠纷。整个系统围绕“订单”这个核心对象流转,后端把业务规则管住,小程序端把用户体验做轻。
我做这个项目的时候,第一步不是写代码,而是先把业务流程画清楚。下单之后库存怎么扣?支付回调没收到怎么办?商家拒单了钱怎么退?这些如果不在设计阶段定清楚,后面写接口一定会返工。我的做法是先把订单状态机定义好:待支付、已支付、备货中、待取货/配送中、已完成、已取消、退款中、已退款,每个状态之间的迁移条件写清楚,再开始建表写接口。这一步省了我后面至少三天的调试时间。
1.2 为什么是Django加微信小程序这个组合
后端选Django,核心原因是它的“全家桶”特性对校园电商这种业务密集型项目太友好了。Django自带Admin后台,稍微配一下就是一套商家管理后台,商品上下架、订单处理、用户管理这些功能不用从零开发前端页面;ORM能把数据库操作封装得明明白白,Model定义好之后迁移表结构是一行命令的事;内置的CSRF防护、XSS转义、SQL注入防护对新手开发者来说等于白捡了一层安全防线。如果你用的是Spring Boot,确实性能更强,但同样的功能开发量至少多出三分之一,对一个课程设计或者毕设来说性价比不高。
小程序端的优势更直接:不用下载App,微信里扫码就能打开;微信登录省掉了用户名密码注册的整套流程,用户点一下授权,后端拿着code换openid,用户身份就确定了;支付走微信支付,天然完成实名和资金托管,不需要自己搞支付牌照。这些对于校园场景来说全是刚需。
也有同学问我为什么不用uni-app或者纯H5。uni-app确实能一套代码多端复用,但如果你只做微信端,原生小程序的开发体验其实更稳,调试工具成熟,API文档齐全,踩坑搜解决方案也容易。纯H5则绕不开微信登录和微信支付的坑,网页里调起微信支付对商户号有资质要求,而小程序内支付要顺滑得多。所以我最终定了这个组合:Django保证后端的开发效率和稳定性,微信小程序保证前端的触达和支付闭环。
1.3 模块划分与数据库模型设计
系统的功能模块从业务上拆,可以分成用户身份、店铺商品、交易订单、支付回调、后台管理五个大块。对应的Django应用我建议这样划分:
| 应用名 | 职责范围 | 核心Model |
|---|---|---|
| users | 买家/商家/管理员身份、微信绑定 | User, ShopOwner |
| shops | 店铺信息、入驻审核、营业状态 | Shop, ShopCategory |
| goods | 商品分类、商品信息、库存、上下架 | Category, Product, SKU |
| orders | 购物车、订单、订单项、退款记录 | Cart, CartItem, Order, OrderItem, Refund |
| payment | 微信支付下单、回调处理、对账 | PaymentRecord, PayCallbackLog |
| admin | Django Admin后台配置与操作日志 | OperationLog |
我特别想提醒一点:订单相关的表设计不要偷懒。Order和OrderItem必须分开,一个订单对应多个商品项,每个OrderItem要冗余商品名称、快照价格、快照图片,这样即使商家后来改了商品价格或删了商品,历史订单依然完整可追溯。这是个很常见的坑——很多新手把商品信息直接关联外键,结果商家一改价,历史订单的金额也跟着变了,对账的时候怎么都平不上。
支付流水表也要单独建。每次发起微信支付都要记录一条PaymentRecord,关联订单号、支付金额、支付状态、微信返回的prepay_id和transaction_id。回调来了先查流水,再更新订单状态,这样业务流程才能做到可审计。我甚至建议加一张PayCallbackLog表,把所有微信回调的原始报文记录下来,排查问题时直接翻文件,不用去猜平台到底发了什么。
Django的Migrations流程在这里体现得特别好。Models写完,运行python manage.py makemigrations生成迁移文件,再运行python manage.py migrate应用,数据库表结构就建好了。改字段也方便,加一个字段重新迁移一次,不会破坏已有数据。整个项目从建表到上线,我至少跑了二十多次迁移,没有一次手动改过数据库结构。
2. 开发环境搭建与项目初始化
2.1 Python与Django环境准备
开发环境这块我踩过不少坑,先说说版本问题。Django 4.2是长期支持版本,兼容性最稳妥,Django 5.0虽然新但部分第三方库还没跟上,特别是支付相关的库。Python建议装3.10到3.12之间的版本,太老的3.8不建议用,很多新库已经不再支持。装Python的时候记得勾选Add Python to PATH,否则后面命令行里敲python都会提示找不到命令。
安装完成后,强烈建议用虚拟环境隔离项目依赖,不要图省事直接装到全局。我的习惯是每个项目一个venv,这样Django版本不会互相打架。创建项目和基础依赖的命令很简单:
python -m venv venv # Windows激活虚拟环境 venv\Scripts\activate # macOS/Linux激活虚拟环境 source venv/bin/activate pip install django djangorestframework django-cors-headers PyJWT requests装好之后用django-admin startproject创建工程,再创建各个应用:
django-admin startproject campus_mall cd campus_mall python manage.py startapp users python manage.py startapp shops python manage.py startapp goods python manage.py startapp orders python manage.py startapp payment这里有个小建议:项目配置文件里把时区设成Asia/Shanghai,语言设成zh-hans,不然数据库里存的UTC时间会让你算订单超时时算到怀疑人生。INSTALLED_APPS里把rest_framework、corsheaders和新建的应用全部注册进去,这一步漏了的话后面跑起来各种报模块找不到。
2.2 微信小程序端创建与AppID绑定
小程序端的第一步是去微信公众平台注册一个小程序账号。这里有个容易混淆的点:小程序账号和公众号账号是两套体系,别用公众号的AppID去跑小程序。注册时主体类型选个人还是企业要看你的用途,个人主体能用的类目少一些,但做个校园内的课程设计演示足够。注册完成后在“开发管理-开发设置”里能看到AppID,这个字符串后面会频繁用到。
然后下载微信开发者工具,新建项目时选“小程序”,填入AppID。如果不填AppID,开发者工具会生成一个测试号,测试号能用但没法调用微信登录和支付,所以一定要填正式的。项目创建后,建议把目录结构组织好:
campus-mall-miniapp/ ├── pages/ # 页面文件夹,每个页面一个子目录 │ ├── index/ # 首页 │ ├── shop/ # 店铺详情 │ ├── goods/ # 商品详情 │ ├── cart/ # 购物车 │ ├── order/ # 订单确认/列表/详情 │ └── profile/ # 个人中心 ├── components/ # 可复用组件 ├── utils/ # request.js等公共工具 ├── app.js # 全局逻辑 ├── app.json # 全局配置 └── project.config.jsonapp.json里配置pages列表、tabBar、window样式。tabBar我建议放四个:首页、分类、购物车、我的。校园店铺商城的用户心智是“找店买东西”,首页直接做店铺广场比做商品瀑布流更贴场景,这一点后面细说。
2.3 统一请求封装与后端接口对接
小程序端的所有请求我建议封装成一个request.js工具,统一管理baseURL、token注入、错误提示。不要每个页面都写wx.request,那样代码冗余到后面根本维护不了。我的封装大概长这样:
// utils/request.js const BASE_URL = 'https://your-domain.com/api/v1' function request(path, method = 'GET', data = {}) { return new Promise((resolve, reject) => { wx.request({ url: BASE_URL + path, method, data, header: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + wx.getStorageSync('token') }, success(res) { if (res.statusCode === 401) { // token过期,重新登录 wx.removeStorageSync('token') wx.navigateTo({ url: '/pages/login/login' }) } else if (res.statusCode >= 200 && res.statusCode < 300) { resolve(res.data) } else { wx.showToast({ title: res.data.message || '请求失败', icon: 'none' }) reject(res) } }, fail(err) { wx.showToast({ title: '网络异常', icon: 'none' }) reject(err) } }) }) } module.exports = { request }对应地,Django后端要注册一个API路由,接口统一以/api/v1/开头,返回格式也统一成{code: 0, message: "success", data: {...}}。在Django里我用rest_framework的APIView写接口,每个视图里先做参数校验和权限校验,再处理业务逻辑,最后返回统一结构。这样小程序端处理数据时逻辑非常清晰,只要判断code是否为0,省得每种接口一种返回结构。
跨域问题也需要提前配置。小程序端请求不算严格意义上的跨域,但如果你在浏览器里调试Django管理后台,或者以后要接PC端管理页面,CORS还是绕不开。装一个django-cors-headers,在MIDDLEWARE里加上CorsMiddleware,再配置CORS_ALLOWED_ORIGINS列表即可。开发阶段方便调试可以先用CORS_ALLOW_ALL_ORIGINS = True,上线前一定改成白名单模式。
3. 核心功能实现与细节解析
3.1 微信登录与用户身份绑定
这个模块是整个系统用户体系的入口,也是我接手项目时遇到报错最多的模块。微信登录的原理不复杂:小程序端调用wx.login拿到一个临时凭证code,这个code只有五分钟有效期,后端拿这个code去微信的接口换openid和session_key。openid是用户在当前小程序下的唯一标识,会话密钥session_key用来解密用户信息。整个流程串起来是这样的:
// 小程序端 wx.login({ success: async (res) => { const code = res.code const loginRes = await request('/auth/login', 'POST', { code }) wx.setStorageSync('token', loginRes.data.token) wx.setStorageSync('userId', loginRes.data.user_id) } })后端拿到code之后,调用微信的接口:
# users/views.py import requests from django.conf import settings def code2session(code): url = "https://api.weixin.qq.com/sns/jscode2session" params = { "appid": settings.WX_APPID, "secret": settings.WX_SECRET, "js_code": code, "grant_type": "authorization_code" } resp = requests.get(url, params=params).json() # resp里包含openid和session_key return resp拿到openid后,在User表里查一下有没有这个openid,有就直接返回token,没有就自动注册一个新用户再返回token。token我用的PyJWT生成,payload里带上user_id和过期时间,过期时间设成7天比较合理,学生用户不可能天天重新登录。这里要注意一个安全细节:后端永远不要自己根据openid拼接用户身份,一定要通过token里的user_id去数据库重新查用户,防止伪造。
说一下小程序获取用户头像昵称的问题。微信从基础库2.21.2开始,wx.getUserProfile接口已经调整,不能再直接弹出授权框获取用户头像和昵称了。推荐的做法是:用户在小程序里点击某个按钮时,用button组件搭配open-type="chooseAvatar"让用户主动选择头像,昵称则用input让用户自己输入。这个改动是微信出于隐私保护做的收紧,很多旧的博客教程还在教getUserProfile,照着做会直接失效。第一次接入的用户在这个环节会卡一阵子,我建议直接按照新版流程设计用户信息完善页面:登录后如果用户昵称是默认的“微信用户”,就引导用户进入个人资料页补充头像昵称。
3.2 商品浏览、购物车与下单流程设计
商品浏览和购物车的用户体验直接决定这个系统能不能留住人。我在做首页的时候,没有走传统电商那种“搜索框+商品瀑布流”的套路,而是做了“店铺广场+店铺内商品列表”。原因很简单:校园场景下用户心智是“我去哪家店买”,而不是“我要搜一个标品”。每家店铺是一个卡片,展示店铺名、评分、距离(虽然这里只能用宿舍楼编号模拟)、营业状态,点进去才看商品。
商品模型我建议做两级分类,一级分类(美食、饮品、文印、二手、生活服务),二级分类挂在店铺下自定义。商品本身要有标题、主图、详情图、价格、原价、库存、月销量、上下架状态。价格单位用“分”存整数,比如12.5元存成1250,这样避免浮点数运算误差。这是电商系统里最基本的规范,但网上很多教程直接存DecimalField,算折扣和优惠时容易出现精度问题,所以我这里强调一下。
购物车就两张表:Cart和CartItem。每个用户一个购物车,CartItem关联商品和数量。加购接口要做两件事:如果该商品已经在购物车里,数量累加;否则新建购物车项。下单时从购物车勾选的商品生成订单,这里必须用数据库事务。
from django.db import transaction @transaction.atomic def create_order(user, cart_item_ids, address, remark): # 1. 锁定购物车项,查对应商品 items = CartItem.objects.select_for_update().filter(id__in=cart_item_ids, user=user) if not items: raise ValueError("购物车为空") # 2. 计算总金额 total_amount = sum([item.product.price * item.quantity for item in items]) # 3. 扣减库存 for item in items: product = Product.objects.select_for_update().get(id=item.product_id) if product.stock < item.quantity: raise ValueError(f"商品{product.title}库存不足") product.stock -= item.quantity product.save() # 4. 创建订单和订单项 order = Order.objects.create(user=user, order_no=generate_order_no(), total_amount=total_amount, address=address, remark=remark) for item in items: OrderItem.objects.create(order=order, product=item.product, title=item.product.title, price=item.product.price, quantity=item.quantity) # 5. 清空购物车对应项 items.delete() return order事务里用select_for_update锁住商品行,防止并发下单时超卖。学生抢限定商品的时候并发量可能瞬间上来,如果不用锁,库存100的商品可能同时被下出去150单。用Django的@transaction.atomic配合select_for_update,基本上能把超卖问题从根上解决。订单号不要用数据库自增id,我在generate_order_no里用的是时间戳加随机数,格式大概是20250601123045再加6位随机数,保证唯一性。
3.3 微信支付接入与回调处理
微信支付是电商系统里最绕的一个环节,第一次接的时候容易被各种参数和签名搞晕。整体流程是:后端拿到订单号和金额,调用微信支付的统一下单接口,拿到prepay_id;后端把这个prepay_id和相关参数签名后返回给小程序端;小程序端用wx.requestPayment调起收银台;用户输入密码支付完成后,微信服务器异步回调后端接口通知支付结果。
统一下单的Python代码核心部分大概是:
# payment/views.py import hashlib import time import requests def unified_order(order): params = { "appid": settings.WX_APPID, "mch_id": settings.WX_MCH_ID, "out_trade_no": order.order_no, "body": "校园店铺商城订单", "total_fee": order.total_amount, # 单位是分 "spbill_create_ip": get_client_ip(), "notify_url": settings.WX_NOTIFY_URL, "trade_type": "JSAPI", "openid": order.user.wx_openid } # 按key排序后拼接,加商户密钥签名 sign_str = "&".join([f"{k}={params[k]}" for k in sorted(params.keys())]) + f"&key={settings.WX_KEY}" params["sign"] = hashlib.md5(sign_str.encode("utf-8")).hexdigest().upper() xml_data = dict_to_xml(params) resp = requests.post("https://api.mch.weixin.qq.com/pay/unifiedorder", data=xml_data.encode("utf-8"), headers={"Content-Type": "text/xml"}) return xml_to_dict(resp.text)这里有几个坑我深有体会。第一,参数必须按字典序排序再拼接签名,少一个参数或者多一个空格签名就对不上。第二,金额单位是分,不是元,我第一版传了元,支付金额直接差了100倍,好在是测试环境发现的。第三,返回的是XML不是JSON,需要解析XML拿到prepay_id。第四,openid必须是用户在当前小程序下的openid,这个openid就是登录时换回来的。
支付回调处理是整个支付流程的终点,也是安全要求最高的地方。微信服务器会把支付结果以POST形式发到notify_url,后端必须做三件事:验证签名、校验订单金额、更新订单状态。验证签名就是拿同样的规则把收到的参数重新算一遍sign,跟微信传过来的sign比对;校验金额是拿订单的应支付金额跟微信回调里的total_fee比对,防止金额被篡改;更新状态要判断订单当前状态,如果已经是已支付就不能重复更新,这就是幂等处理。回调处理完要返回XML内容“SUCCESS”给微信,表示收到通知了,否则微信会重试多次。
@csrf_exempt def pay_notify(request): if request.method == "POST": xml_data = request.body data = xml_to_dict(xml_data) # 验签 if not verify_sign(data): return HttpResponse("FAIL") order = Order.objects.get(order_no=data["out_trade_no"]) # 金额校验 if order.total_amount != int(data["total_fee"]): return HttpResponse("FAIL") # 幂等更新 if order.status == "pending_payment": order.status = "paid" order.transaction_id = data["transaction_id"] order.paid_at = timezone.now() order.save() return HttpResponse("SUCCESS")支付状态在订单流转里一定要有对账兜底。我遇到过一种情况:用户明明支付成功了,但回调因为服务器短暂宕机没收到,订单状态还停在待支付。所以我加了一个主动查询的接口,小程序端进入订单详情页时如果不是已支付状态,调一次“查询订单支付状态”,后端主动向微信支付查单并更新状态。这样可以兜住回调丢失的场景,用户体验会好很多。
3.4 Django Admin后台的二次开发
Django自带的Admin后台绝对是被很多人低估的功能。对于校园店铺商城来说,Admin后台可以同时承担平台管理员和店铺商家的操作界面,省去开发两个完整后台管理端的成本。我把每个Model都注册到Admin里,并配置好列表展示字段、搜索字段和过滤字段。
商家登录Admin后台后,默认只能看到自己的店铺和商品?其实不是,Django Admin的权限系统是按Model的增删改查权限控制的,不是按行数据控制的,所以要让商家只能管理自己的数据,需要重写get_queryset方法。
# shops/admin.py class ProductAdmin(admin.ModelAdmin): list_display = ["title", "shop", "price", "stock", "is_on_sale", "created_at"] list_filter = ["is_on_sale", "shop"] search_fields = ["title"] def get_queryset(self, request): qs = super().get_queryset(request) if request.user.is_superuser: return qs return qs.filter(shop__owner=request.user) def save_model(self, request, obj, form, change): if not change: obj.shop = Shop.objects.get(owner=request.user) super().save_model(request, obj, form, change)这样商家登录后台,只会看到他名下店铺的商品数据,新增商品时自动绑定到自己的店铺。买家数据、支付流水这类敏感信息,普通商家账号不授予任何权限。管理员主账号拥有全部权限,负责审核店铺入驻、处理违规商品、受理退款申请。整个后台管理体系的迭代效率非常惊人,基本上定义了Model之后,Admin配置一个小时就能搞定,传统方式要写好几天的页面。
4. 部署上线与常见问题排查实录
4.1 小程序AppID报错排查
项目还没写完就急着跑小程序,结果控制台一坨红字:获取登录后的微信用户失败。后面跟着一串AppID,看起来像是wx1cb4398e1413dce7这种格式。这个报错我见过太多回了,新手遇到基本一脸懵。
先说结论:微信开发者工具里报这个错误,表示它没能用当前配置的AppID完成正常的登录授权链路。常见原因就那么几个。第一,AppID没填对,复制的时候多了空格或者少了一位。第二,AppID是“测试号”的,但项目里用的是正式号配置,两边不一致。第三,开发者工具登录的微信号不是这个小程序的开发者或管理员,在mp.weixin.qq.com后台把微信号加进“成员管理-项目成员”里就能解决。第四,小程序的AppID确实是别的平台生成的,比如在HBuilderX里用DCloud的AppID,那就不是微信小程序的AppID。
排查步骤我建议这样走:打开项目的project.config.json,查看appid字段的实际值;登录微信公众平台,进入开发管理-开发设置,复制页面显示的AppID,对比两处是否完全一致;在“成员管理”里确认当前工具的登录微信号有权限;最后重启一下开发者工具。大部分情况下,到了第三步问题就解决了。这种报错本身不涉及后端接口逻辑,纯属开发环境配置,所以先别急着去查后端代码,白费功夫。
这里还牵扯到另一个开发状态问题:开发者工具里的“不校验合法域名”开关,只是本地开发调试用的,真机预览和线上版本都必须配置合法域名。小程序要求所有request、uploadFile的域名必须是HTTPS且在小程序后台配置过。我在开发阶段图省事,真机预览时没配置合法域名,结果所有请求全部失败,一度以为后端接口写错了。后来才反应过来,是在“开发管理-开发设置-服务器域名”里把https://api.example.com加到request合法域名列表里,问题迎刃而解。这个配置生效不是即时的,一般等一两分钟再重试。
4.2 微信支付签名错误的排查思路
支付环节最经典的报错是“签名错误”,微信直接把单子退回来。这个问题的根源几乎都是签名串和微信服务器计算的签名不一致。排查的时候不要瞎猜,先把请求统一下单时生成的XML报文完整打出来,再拿微信支付官方文档里的签名生成规则逐项核对。
我踩过一个最隐蔽的坑:小程序端的签名参数和后端统一下单的参数用的是两种签名算法。微信支付API v2用的是MD5或HMAC-SHA256,统一下单和JSAPI调起支付都需要签名,但两者使用的参数集合不一样,密钥都是一样的。我在后端统一下单时用MD5签名,返回给小程序端的paySign却用了HMAC-SHA256的算法,导致小程序端明明拿到了prepay_id却总是报签名错误。把两边统一成同一种算法后,问题消失。
还有一个高频问题:金额单位。微信支付所有金额字段单位都是分,我在前面已经强调过。但这里真正隐蔽的是,如果订单金额为0.01元,total_fee传1分,回调里验证金额也要按分来比对。我第一版代码里回调校验用的是Decimal比较,结果一直对不上,排查半天发现单位不一致。后来把金额全部统一以分存储,从数据库到接口到回调校验全链路都用整数分,再也没出过精度问题。建议所有涉及金额的字段都用整数字段,代码里显式标注单位是分。
支付回调调试用内网穿透工具会很方便,但正式上线后就不建议了。调试阶段可以先把notify_url指向一个测试地址,用微信商户平台里的“支付测试”或者小额真实支付去触发回调。我通常会在PayCallbackLog表里记录每一笔回调的完整XML,出问题时直接查记录,用真实数据对比签名和金额,比什么日志分析都快。
4.3 宝塔面板部署Django后端
后端部署我用的是宝塔面板加Nginx加Gunicorn的组合,这套方案对个人项目和课程设计来说足够省心。服务器建议选2核4G的云主机,配Ubuntu 22.04或者CentOS 7.x,带宽5M起步。装宝塔面板后,在软件商店里装Nginx、Python项目管理器、MySQL(或者用SQLite过渡也行,但上线建议换MySQL)。
部署流程大概是:服务器上创建Python虚拟环境,安装项目依赖;在Python项目管理器里添加项目,启动方式选Gunicorn,监听地址填127.0.0.1:8000;Nginx配置反向代理,把443端口收到的请求转发到本地的8000端口;申请Let‘s Encrypt免费SSL证书,配置HTTPS。小程序要求所有请求域名都必须是HTTPS,所以这一步不能省。
Nginx配置里一个关键点:前端小程序请求的域名为api.example.com,就把该域名的server块反向代理到本地8000。同时要注意静态文件处理,Django的Admin后台有大量CSS和JS,如果Nginx不配置静态文件路径,后台页面会加载得惨不忍睹。我习惯把Django的collectstatic收集到/opt/campus_mall/static目录,再在Nginx里配置alias指向这个目录。
部署之后一定要跑一遍全流程测试:小程序登录、商品列表、下单、支付回调。我上线第一天就发现一个灵异问题:支付回调偶尔会延迟十几秒,然后订单状态更新了。后来查日志发现是Gunicorn的worker数量配置太少,默认只开了两个worker,回调请求排队了。把workers调成CPU核数加1后,回调基本秒回。Gunicorn参数在宝塔Python项目管理器里可以直接配置,也可以自己写启动命令:gunicorn campus_mall.wsgi:application --workers 4 --bind 127.0.0.1:8000。
4.4 小程序版本更新与上线审核
小程序开发完不等于结束,上线审核这关能卡掉不少项目。审核不通过最常见的原因有三类:类目选择不当、页面内容不合规、功能不完整。校园店铺商城建议选择“电商平台-其他电商平台”类目,个人主体可能受限,企业主体更稳。如果是校园内的课程设计演示,可以用“工具-信息查询”等更宽松的类目来过渡,但正式商用还是得选对类目。
上线前必须处理的一个技术点是版本更新。微信小程序没有浏览器那种强刷新机制,用户打开旧版本会一直停留在旧代码上。我强烈建议在app.js的onLaunch里加上自动更新检测:
// app.js onLaunch() { if (wx.canIUse('getUpdateManager')) { const updateManager = wx.getUpdateManager() updateManager.onUpdateReady(function () { wx.showModal({ title: '更新提示', content: '新版本已准备好,是否重启应用?', success(res) { if (res.confirm) { updateManager.applyUpdate() } } }) }) } }这样每次用户冷启动小程序,微信都会在后台检查新版本,有更新就提示用户重启应用。这个API从微信基础库1.9.90开始就支持,几乎覆盖所有在用的微信版本,不用担心兼容性问题。
审核的时候如果用了虚拟支付(比如卖虚拟课程),微信会直接拒绝,校园店铺商城如果只卖实体商品和线下服务,这个风险不大。但要注意,小程序里不能出现引导用户加微信、电话联系等“诱导线下交易”的内容,这些在审核时容易被判违规。我还有一次被拒是因为用户协议里没有隐私保护条款,后来在隐私协议里把用户信息收集的范围、用途、存储期限都写清楚,重新提交才通过。
5. 实战心得与可扩展方向
这个项目从立项到基本可用,我前后花了大概两周的业余时间,每天写两个小时左右。放到课程设计或者毕设的周期里,时间上是完全充裕的。但如果你是从零开始,我给三点建议:第一,先啃微信登录和支付回调这两块硬骨头,它们决定项目的地基稳不稳,别的功能都可以后补;第二,后端代码结构按应用拆分清楚,不要图省事把所有逻辑丢到一个views.py里,否则后面加功能就是在泥潭里打滚;第三,小程序端页面可以先用最简单的写法实现,跑通全流程后再美化样式,不要一上来就抠像素级UI,功能链路都通不了,UI再好看也没用。
踩坑最惨的一次是支付回调,我在本地怎么测都对,上线到服务器后回调地址却一直收不到通知。查了一整天才发现是Nginx配置里没有把微信服务器的请求转发到Django,微信把回调发到了默认的80端口页面。后来在Nginx server块里单独加了一个location /api/payment/notify/的转发规则,问题解决。这类部署层面的小问题,遇到一次以后就有记性了。
最后分享一个小技巧:校园店铺商城这类项目,答辩或者演示的时候,最好准备一套完整的演示数据。店铺分类里放校园奶茶、打印店、二手书店,商品图片用真实的校园场景照片,订单流程从下单到支付到商家接单一步一步跑通。面试官或者老师看到的不只是一个“能运行的系统”,而是一个“解决了真实场景问题”的完整项目,印象分会完全不一样。我自己在毕业设计答辩时,就是靠这个项目顺利拿下了优秀论文,核心就是业务场景讲得清楚,技术链路完整闭环。后续如果你想在这个项目上继续深入,可以做优惠券、校园卡余额支付、商家数据看板、订单定时提醒这些方向,每加一个功能,就能在项目履历上多写一行亮点。这套Django加微信小程序的组合,本身也足够支撑你扩展到其他行业的小程序应用,知识是完全迁移的。