☰
Python+uniapp微信小程序药品商城开发:多商家拆单与发票模块设计
2026/10/7 18:09:43 网站建设 项目流程

先聊点实际的。药品这类商品跟普通零售完全不同,它涉及库存批次、处方限制、效期管理,还要处理发票和多商家入驻,光这几个点就能把大部分毕设项目的复杂度拉高一个档次。如果你正准备做一个“Python + uniapp + 微信小程序”的自助购药商城,而且还要写论文,那这篇文章就是给你准备的:我会把项目拆成业务逻辑、系统设计、代码落地、论文写作四个维度讲清楚,全部基于真实开发中会遇到的场景。

1. 项目整体设计与核心需求拆解

1.1 药品商城不只是“商城”

表面上看,这是一个电商小程序,但“药品”和“多商家”这两个词决定了它不能照搬通用商城方案。

先说药品本身。药品 SKU 有“一品一码”的追溯要求在部分场景下需要保留,但实际开发中更常见的是batch_no(批次号)和expiry_date(有效期)两个字段必须存。比如同一款布洛芬缓释胶囊,不同批次、不同效期的价格可以一样,但库存必须分开管理,这直接影响到下单减库存的逻辑。后面在订单模块里我会给出具体建表思路。

再说“多商家”这三个字。多商家意味着商品数据不能全局一张表查到底,每件商品必须挂merchant_id,用户下了一单可能涉及多个商家,这就牵扯出订单拆分的问题——用户购物车里同时加了A药店和B药店的药,结算时系统要自动拆成两个子订单,各自独立发货、独立结算、独立开票。这是整个系统里最容易翻车的点,没有之一。

至于“发票”功能,实际药品场景里主要开电子普通发票,个别场景要支持企业抬头和税号。发票模块不要一开始就对接百望、航信这些第三方税控接口——成本高、审核麻烦,论文阶段先用“开票申请 + 系统生成发票编号 + PDF模板落库”的方式打通流程,论文里写明生产环境可替换为税控服务即可。

1.2 论文场景下的题目切入点

这个项目带“论文”二字,大概率是本科或硕士的毕业设计。论文撰写时不要只写“我做了什么”,而是要突出“我遇到了什么问题、为什么这么设计、对比其他方案有什么优劣势”。

举个例子:技术选型章节里,前端框架选 uniapp 而不是原生微信小程序,你可以这样论证:原生小程序不能一套代码同时覆盖App Store和Android应用市场,而 uniapp 基于 Vue 语法生态,做跨端时业务代码复用率可以到 85% 以上,并且它支持条件编译,可以针对微信小程序平台单独定制导航栏和登录逻辑。后端选 Python + Flask/Django 而不选 Spring Boot,理由可以是 Python 在数据分析和接口开发上的开发效率高,且作为毕设项目,团队维护成本低,此外 Python 有非常成熟的微信支付和 MySQL 操作库支撑。

这些内容写进论文“需求分析”和“技术选型”两章,逻辑上是闭环的。

2. 技术栈选型与架构设计

2.1 前后端分离的完整结构

整个项目分为三层:uniapp 小程序端、Python API 服务端、MySQL 数据库。配合 Redis 做会话缓存和购物车缓存,配合 OSS/COS 做商品图片存储。

nginx 直接作为反向代理把接口转发到后端服务,默认监听 80 端口。配置文件里我习惯这样组织:

server { listen 80; server_name yourdomain.com; client_max_body_size 50m; location /api/ { 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; } }

移动端和小程序端统一通过https://yourdomain.com/api/访问接口。这里要注意:微信小程序要求配置的 request 合法域名必须是 HTTPS 且备案,开发阶段可以在开发者工具里勾选“不校验合法域名”来调试,但上线前必须换成正式 HTTPS 域名。

2.2 Python 后端框架选择与项目目录

专门对比一下 Flask 和 Django。Flask 轻量、灵活,适合接口不多的小项目;但多商家、多角色权限这类业务,Django 的 admin 后台和 ORM 自带分页、事务、中间件,能少写很多代码。我用的是 Django + Django REST Framework(DRF)。

标准的项目目录结构:

pharmacy_backend/ ├── manage.py ├── config/ # 配置文件 │ ├── settings.py │ └── urls.py ├── apps/ │ ├── users/ # 用户、地址、会员 │ ├── merchants/ # 商家入驻、审核、店铺信息 │ ├── products/ # 药品分类、商品、SKU、库存 │ ├── orders/ # 购物车、订单、退款 │ ├── invoices/ # 发票申请、发票记录 │ └── payments/ # 微信支付、支付回调 └── utils/ # 通用工具:加密、分页、响应封装

Django 里多 app 的好处是隔离业务边界,论文画架构图的时候会很清晰,而且每个 app 都可以单独拆出去做成微服务——这又是一个可以写进论文“未来展望”的亮点。

2.3 数据库设计:多商家+药品库存是核心

直接给出核心表结构,方便你建库时少走弯路。

用户表:

CREATE TABLE `user` ( `id` int NOT NULL AUTO_INCREMENT, `openid` varchar(64) NOT NULL, `nickname` varchar(64) DEFAULT '', `avatar` varchar(255) DEFAULT '', `phone` varchar(20) DEFAULT '', `role` tinyint NOT NULL DEFAULT '0' COMMENT '0-普通用户 1-商家 2-管理员', `created_at` datetime NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `uk_openid` (`openid`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

商家表:核心是status字段用于入驻审核,balance用于财务结算。

CREATE TABLE `merchant` ( `id` int NOT NULL AUTO_INCREMENT, `user_id` int NOT NULL, `shop_name` varchar(100) NOT NULL, `license_no` varchar(64) DEFAULT '' COMMENT '营业执照号', `contact_name` varchar(32) DEFAULT '', `contact_phone` varchar(20) DEFAULT '', `status` tinyint NOT NULL DEFAULT '0' COMMENT '0-待审核 1-正常 2-已驳回 3-已冻结', `balance` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '结算余额', `created_at` datetime NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

药品商品表:merchant_id决定商品归属,need_prescription决定是否进入处方药流程。

CREATE TABLE `product` ( `id` int NOT NULL AUTO_INCREMENT, `merchant_id` int NOT NULL, `category_id` int NOT NULL, `name` varchar(128) NOT NULL, `generic_name` varchar(128) DEFAULT '' COMMENT '通用名', `spec` varchar(64) NOT NULL COMMENT '规格', `unit` varchar(16) NOT NULL DEFAULT '盒', `price` decimal(10,2) NOT NULL COMMENT '零售价', `stock` int NOT NULL DEFAULT '0', `need_prescription` tinyint NOT NULL DEFAULT '0' COMMENT '0-OTC 1-处方药', `detail` text, `status` tinyint NOT NULL DEFAULT '1' COMMENT '1-上架 0-下架', `created_at` datetime NOT NULL, PRIMARY KEY (`id`), KEY `idx_merchant` (`merchant_id`), KEY `idx_category` (`category_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

批次库存表:

CREATE TABLE `product_batch` ( `id` int NOT NULL AUTO_INCREMENT, `product_id` int NOT NULL, `batch_no` varchar(64) NOT NULL COMMENT '批次号', `expiry_date` date NOT NULL COMMENT '有效期', `quantity` int NOT NULL DEFAULT '0', PRIMARY KEY (`id`), KEY `idx_product` (`product_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

订单表与订单商品表:

CREATE TABLE `order` ( `id` int NOT NULL AUTO_INCREMENT, `order_no` varchar(32) NOT NULL, `user_id` int NOT NULL, `merchant_id` int NOT NULL COMMENT '子订单对应商家', `parent_order_no` varchar(32) DEFAULT NULL COMMENT '拆单前的父订单号', `total_amount` decimal(10,2) NOT NULL, `freight_amount` decimal(10,2) DEFAULT '0.00', `pay_status` tinyint NOT NULL DEFAULT '0' COMMENT '0-未支付 1-已支付 2-已退款', `order_status` tinyint NOT NULL DEFAULT '0' COMMENT '0-待付款 1-待发货 2-待收货 3-已完成 4-售后中', `address_id` int NOT NULL, `created_at` datetime NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `uk_order_no` (`order_no`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE `order_item` ( `id` int NOT NULL AUTO_INCREMENT, `order_id` int NOT NULL, `product_id` int NOT NULL, `product_name` varchar(128) NOT NULL, `spec` varchar(64) DEFAULT '', `price` decimal(10,2) NOT NULL, `quantity` int NOT NULL, `batch_no` varchar(64) DEFAULT '' COMMENT '锁定批次', `expiry_date` date DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

这里有一个容易踩的坑:order在 MySQL 里是关键字,我建表时用反引号包住,但为了保险更建议直接用trade_order或orders作为表名,否则后续写 SQL 容易冷不丁报语法错误。

发票表:

CREATE TABLE `invoice` ( `id` int NOT NULL AUTO_INCREMENT, `user_id` int NOT NULL, `order_id` int NOT NULL, `type` tinyint NOT NULL DEFAULT '0' COMMENT '0-电子普通发票 1-增值税专用发票', `title_type` tinyint NOT NULL DEFAULT '0' COMMENT '0-个人 1-企业', `title` varchar(128) NOT NULL, `tax_no` varchar(64) DEFAULT '' COMMENT '税号', `amount` decimal(10,2) NOT NULL, `status` tinyint NOT NULL DEFAULT '0' COMMENT '0-待开 1-已开 2-已作废', `invoice_no` varchar(32) DEFAULT '', `pdf_url` varchar(255) DEFAULT '', `created_at` datetime NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

数据库是订单一致性的地基,不理解表之间的关系,后面所有代码都是空转。我见过太多人表还没建清楚就开始写接口,最后在拆单逻辑里被 JOIN 来回折磨。

3. 微信登录、支付与隐私合规

3.1 微信登录获取手机号的完整流程

小程序端登录必须走微信官方大一统流程:

用户点击“微信一键登录” →uni.login拿到code→ 前端把code发给后端 → 后端调jscode2session接口换openid和session_key→ 后端生成自定义token(比如 JWT)返回给前端 → 前端把token存入uni.setStorageSync,后续请求全部带上。

后端 Django 视图核心代码:

import requests import jwt import time from django.conf import settings def wx_login(request): code = request.data.get("code") appid = settings.WX_APPID secret = settings.WX_SECRET url = ( "https://api.weixin.qq.com/sns/jscode2session" f"?appid={appid}&secret={secret}&js_code={code}&grant_type=authorization_code" ) resp = requests.get(url, timeout=5).json() if "openid" not in resp: return JsonResponse({"code": 400, "msg": "微信登录失败"}) openid = resp["openid"] user, _ = User.objects.get_or_create(openid=openid) payload = {"user_id": user.id, "exp": int(time.time()) + 86400 * 7} token = jwt.encode(payload, settings.SECRET_KEY, algorithm="HS256") return JsonResponse({"code": 0, "data": {"token": token, "user_id": user.id}})

获取手机号(getPhoneNumber)在新版本微信里已改为通过code换取,不直接返回明文手机号。前端的按钮:

<button open-type="getPhoneNumber" @getphonenumber="onGetPhone"> 获取手机号 </button>

后端拿到code后调https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=ACCESS_TOKEN,用code换pure_phone_number。注意access_token需要先用appid+secret换取并缓存,建议存 Redis 并设置 7200 秒过期。

3.2 微信支付流程和回调处理

支付环节:后端先生成统一下单需要的参数——订单号order_no、金额total_amount、描述body,用商户证书私钥签名后调微信支付接口,拿到prepay_id,再把timeStamp、nonceStr、package、signType这些参数返回给小程序前端,前端用uni.requestPayment拉起支付面板。

关键代码像这样:

// 前端拉起支付 uni.requestPayment({ provider: 'wxpay', timeStamp: res.data.timeStamp, nonceStr: res.data.nonceStr, package: res.data.package, signType: 'MD5', paySign: res.data.paySign, success: (payRes) => { // 轮询后端确认支付结果 this.confirmPay(orderNo); }, fail: (err) => { uni.showToast({ title: '支付取消', icon: 'none' }); } });

后端支付回调是重中之重,一定要验签,别直接信参数。回调通知里包含out_trade_no和transaction_id,验签通过后先查询订单状态是不是“未支付”,再执行“支付成功”的动作:改订单状态、扣库存、生成发票待办通知。用 Django 写的时候要用@csrf_exempt装饰回调视图,因为微信回调不是带 CSRF 的普通表单请求。

库存扣减我专门说一下:支付回调里扣库存,比下单时减库存更稳。如果下单就减库存,用户迟迟不付款,库存被锁死;如果下单不减、支付后扣,要注意超高并发下可能超卖。药品商城场景,建议用 Redis 的DECRBY原子操作:

from django_redis import get_redis_connection def deduct_stock(product_id, batch_no, quantity): conn = get_redis_connection("default") key = f"stock:{product_id}:{batch_no}" if conn.decrby(key, quantity) < 0: conn.incrby(key, quantity) raise CustomException("库存不足")

支付回调完结后,再异步把 Redis 扣减结果同步回 MySQL。这个“缓存写、回调扣、异步同步”的经典套路可以写进论文高并发设计章节,评审老师会非常买账。

3.3 隐私协议与用户授权合规

2023年后微信小程序审核对隐私协议查得很严。你的小程序在收集用户手机号、位置信息(如果有配送范围判断)之前,必须在小程序管理后台配置《用户隐私保护指引》,同时代码里要通过wx.requirePrivacyAuthorize或者首次打开时的弹窗获得授权合规。

我的项目里是做了一个首次启动弹窗,用户点击“同意并继续”之后才调用登录和定位接口。这个逻辑别看简单,审核被拒时会让你改到怀疑人生。另外,开发阶段别用手机号快速填写的测试号直接提审——换了正式 appid 后很多行为不一致,容易产生“正式环境登录失败”这种定位半天的 bug。

4. 多商家拆单和发票模块落地

4.1 购物车如何支持多商家商品

购物车表设计时,天然要给商品带上merchant_id,这样前端展示购物车列表时可以按商家分组,用户勾选结算时,后端按商家维度对所有商品分组:

from collections import OrderedDict def build_order_groups(cart_item_ids): items = CartItem.objects.filter(id__in=cart_item_ids, status=1) groups = OrderedDict() for item in items: merchant_id = item.product.merchant_id groups.setdefault(merchant_id, []).append(item) return groups

前端 uniapp 里用uni.setStorageSync存购物车时,我建议钥匙结构直接用merchantId_productId,避免跨商家商品在本地端混算金额。UI 上每个商家区块渲染一个“店铺头”,显示商家名和运费规则,这就是标准的电商多店聚合页逻辑。

4.2 拆单算法与平衡

后端拆单的关键是:父单和子单不能丢关联。流程如下:

  1. 用户提交结算,包含一组购物车商品。
  2. 后端按商家分组生成多个子订单,每个子订单生成自己的订单号,如SO202506011230001001。
  3. 生成一个父订单号,如PO202506011230,并把所有子订单号挂到父单下。
  4. 前端支付时,其实是一次支付全部子单的总金额,此时用父单号调微信支付预下单。
  5. 支付成功回调通过out_trade_no找到父单,再把父单下所有子单统一标记为已支付。

这里有个逻辑细节:prepay_id生成时不要拿多个子订单一个个调支付接口,小程序端一次只能拉起一个支付面板。一定是“一次支付,多单分摊”。实现时可以让父单记录总金额,支付回调里针对每个子单做分账记录,这也能顺理成章引出后面的商家结算功能。

4.3 发票模块的实现细节

发票模块我的实现思路是:订单完成支付后,用户可以在订单详情页选择一个订单或合并多个订单申请开票。开票信息包括抬头发票类型(个人/企业)、企业抬头、税号。用户提交后,系统生成一条invoice记录,状态为待开。

生产环境如果要对接税控,需要准备税控盘和商户资质,这里不展开。我建议论文环节用一个“发票生成器”模拟:

import uuid from reportlab.pdfgen import canvas def generate_invoice_pdf(invoice): filename = f"invoice_{invoice.invoice_no}.pdf" c = canvas.Canvas(filename) c.drawString(72, 800, f"Invoice No: {invoice.invoice_no}") c.drawString(72, 780, f"Title: {invoice.title}") c.drawString(72, 760, f"Amount: {invoice.amount}") c.drawString(72, 740, f"TaxNo: {invoice.tax_no}") c.save() return filename

上面这段是简化示例,正式版你还需要考虑加表格、二维码(查验)、发票专用章图片,以及把 PDF 上传到对象存储。发票状态从“待开”变为“已开”时,把pdf_url回填到数据库,用户在“我的发票”列表里可以直接预览下载。

这个模块还有个细节容易被忽略:发票金额必须和订单实付金额一致,不能出现订单 100 元,发票只开 80 元的情况。你可以在后端加一个校验,开票列表只展示已经支付完成的订单,并且相同订单不能重复开“未红冲”状态的发票。如果需要部分开票(比如订单里包含多件商品但只开其中几件),那就要引入“开票明细”表,复杂度会明显提高,论文里可以作为可选扩展。

4.4 商家端管理功能建议

管理端给商家账号登录后能看“我的店铺数据”,最核心的是三块:

  • 商品管理:上架、下架、改库存、传图
  • 订单管理:发货、查看售后
  • 结算管理:查看已结算金额和待结算金额

这个后台可以用 Django admin 快速实现,论文截图好看,演示也方便。真正写代码时,你可以用 DRF 的ModelViewSet快速出接口,配合 uniapp 再做一个商家版小程序,或者干脆只做一个 Web 管理后台(用 Vue 或直接 Django 模板)都行——毕设评审关键看业务闭环通不通,不是看端有多少。

我见过不少项目做到最后,学生连商家后台都没有,演示时全靠手动改数据库,非常被动。答辩时老师一定会问“商家怎么入驻?谁审核?”,你说“还没做”青春就结束了。至少把入驻申请 + 管理员审核 + 冻结/解冻这三个接口做出来,能演示,就基本稳了。

5. 小程序端核心页面与uniapp实战

5.1 页面结构

uniapp 项目在pages.json里这样配置页面:

{ "pages": [ { "path": "pages/index/index", "style": { "navigationBarTitleText": "首页" } }, { "path": "pages/category/category", "style": { "navigationBarTitleText": "药品分类" } }, { "path": "pages/cart/cart", "style": { "navigationBarTitleText": "购物车" } }, { "path": "pages/order/list/list", "style": { "navigationBarTitleText": "我的订单" } }, { "path": "pages/invoice/list/list", "style": { "navigationBarTitleText": "发票列表" } } ], "tabBar": { "list": [ { "pagePath": "pages/index/index", "text": "首页" }, { "pagePath": "pages/category/category", "text": "分类" }, { "pagePath": "pages/cart/cart", "text": "购物车" }, { "pagePath": "pages/user/user", "text": "我的" } ] } }

前端请求统一封装:

// utils/request.js const BASE_URL = 'https://yourdomain.com/api'; export function request(path, method = 'GET', data = {}) { return new Promise((resolve, reject) => { uni.request({ url: `${BASE_URL}${path}`, method, data, header: { 'Content-Type': 'application/json', 'Authorization': uni.getStorageSync('token') || '' }, success: (res) => { if (res.data.code === 0) { resolve(res.data.data); } else if (res.data.code === 401) { uni.navigateTo({ url: '/pages/login/login' }); reject(res.data); } else { uni.showToast({ title: res.data.msg, icon: 'none' }); reject(res.data); } }, fail: (err) => reject(err) }); }); }

这里有个提醒:code别混用,业务码 0 代表成功,401 代表 token 过期。很多新手直接把 HTTP 状态码当成业务码用,结果微信开发者工具里 200 和 0 乱成一团混沌世界。前后端约定统一 response 规范,写进论文“接口设计规范”一节加分不少。

5.2 搜索、分类和首页。

首页可以放搜索框、轮播图、快捷分类入口和药品推荐列表。搜索接口建议用title模糊搜索,同时关联到generic_name通用名。很多用户记不住商品名,但记得“布洛芬”或者“感冒灵”,所以 SQL 搜的时候用 OR 条件:

WHERE name LIKE %keyword% OR generic_name LIKE %keyword% OR spec LIKE %keyword%

前端搜索页防抖是必须的。onSearch事件触发后 400ms 内不重复请求,避免每个 keydown 都打一次后端。uniapp 里用clearTimeout和setTimeout简单实现即可。

5.3 商品详情与处方药提示

OTC 药品直接加购物车。need_prescription为 1 的处方药,商品详情页要展示“处方药需凭处方购买”的提示,提交订单时后端要校验用户是否已上传处方笺。如果论文要更完整,处方笺可以是图片上传到对象存储,由“药师”角色在后台审核,审核通过后订单才能进入支付环节。

前端在商品卡上可以用<text>标签展示“处方药”徽标,单品购买按钮变成“立即问诊开方”,点击后跳到上传处方页。这块设计能体现你对医药电商合规的思考,是论文里的特色亮点。

5.4 uniapp 微信小程序打包常见坑

项目做完要发布到微信小程序,HBuilderX 里点“发行 → 小程序-微信”,会生成一个dist/dev/mp-weixin目录,然后导入微信开发者工具。这里有几个高频问题:

  • 包体积超过 2MB:开发模式经常遇到主包超过 2MB 上限。解决办法一是使用分包加载,pages/下无关紧要的页面(比如发票详情、售后申请、商家入驻)全部放进去subPackages,二是图片资源走网络 URL,不要本地 base64 塞一堆。配置文件里加:
{ "subPackages": [ { "root": "packageA", "pages": [ { "path": "pages/order/detail/detail", "style": { "navigationBarTitleText": "订单详情" } } ] } ] }
  • 调试麻烦:HBuilderX 运行到小程序模拟器后,会发现 uniapp 里console.log在开发者工具的 Console 面板不一定全打印,那是因为 dist 代码被编译过。解决方式是打开开发工具的“ES6转ES5”和“增强编译”,以及在manifest.json里打开“开发环境不压缩”选项。

  • 顶部导航栏高度:不同机型胶囊按钮位置不同,自定义导航栏时用uni.getSystemInfoSync()拿statusBarHeight,再通过uni.getMenuButtonBoundingClientRect()拿胶囊按钮位置,动态计算导航栏高度。这块不坑是不可能的,2024 年了还是有不少机型适配差异。

6. 常见问题与项目排错实录

开发这种全栈项目,遇到问题很正常。我把自己和身边人踩过频率最高的坑整理成速查表,你直接对照排错。

问题现象可能原因排查解决
微信登录提示 code 无效后台appid/secret配错,或code已过期确认小程序后台绑定了正确 appid,code有效期只有 5 分钟,生成后立刻使用
手机号获取失败 code not exist基础库版本过低,或者不是真机环境基础库 2.21.2 以上才支持getPhoneNumber新接口,模拟器有时不准,上真机测
支付成功但订单状态没变支付回调没写验签逻辑,或回调地址没外网可达本地开发用内网穿透把回调 URL 暴露出去;正式环境检查回调域名是否备案且在合法域名里
多商家拆单后金额不对前端算总价时跨商家商品被合并计税前端按商家分组展示小计,后端重新计算订单金额,以前端展示为准,以后端校验为准
发票已申请但下拉为空status过滤器写错,或者用户 ID 没传确认查询条件user_id + status,避免普通用户查到其他用户发票
小程序发布后接口 404域名没在公众平台配置 request 合法域名小程序管理后台“开发管理→开发设置→服务器域名”里加白名单,同时必须是 HTTPS
uniapp 编译到微信小程序白屏可能是async/await被打包后不兼容在manifest.json里关掉“es6 转 es5”,或改为 Promise 链式写法,打开“编译时转换es5”

还有一个容易被忽略的点:微信开发者工具里默认的不校验合法域名勾选项,会导致你本地调通但在真机上直接失败。养成一个好习惯——每次真机预览前,先看一眼设置里这个勾选到底是开还是关。

比如订单列表页用onShow刷新,uniapp 里是这样:

onShow() { this.loadOrders(); }

这个函数里写死async请求和排序逻辑,就可能导致页面闪白。实际开发我可以给你建议:onShow里只做轻量刷新,最好加个锁变量避免重复请求:

onShow() { if (this.loading) return; this.loading = true; this.loadOrders().finally(() => { this.loading = false; }); }

7. 论文写作结构与答辩亮点规划

7.1 论文章节大纲参考

如果你需要论文大纲,我建议这样安排:

  • 第一章:绪论。写药品电商行业背景、微信小程序生态发展、课题研究意义。这部分引用数据可以说“中国网上药店市场规模持续增长”之类,但一定要找真实资料,不能乱编。
  • 第二章:相关技术介绍。Python、Django/Flask、uniapp、微信小程序、MySQL、Redis,每项技术写两三段,重点写“为什么选它”。
  • 第三章:系统需求分析。功能性需求:用户端(浏览、搜索、购物车、下单、支付、发票);商家端(商品管理、订单管理、对账);管理员端(商家入驻审核、类目管理、平台监控);非功能性需求:安全性(支付加密、HTTPS、越权拦截)、稳定性。
  • 第四章:系统设计。架构图、功能模块图、数据库 E-R 图、接口设计规范。
  • 第五章:系统实现。每个模块放核心代码片段+功能截图,注意不要把整个文件代码贴进去,会查重直接红了。
  • 第六章:系统测试。测试环境、测试用例表:登录测试、搜索测试、拆单测试、支付回调测试、发票生成测试、并发库存测试。
  • 第七章:总结与展望。

7.2 答辩高频问题准备

答辩老师最爱问的四个问题提前自测:

问题一:你这个系统的权限控制是怎么设计的?答:JWT 实现身份认证,中间件对商家端接口校验role是否为商家,管理员接口校验role是否为管理员。商家只能操作自己店铺下的商品和订单,数据库查询强制加merchant_id条件,避免水平越权。

问题二:如何处理库存并发扣减?答:Redis 原子操作 + 支付完成才扣减库存 + 同步 MySQL 记录,事务里用SELECT ... FOR UPDATE做兜底。可以现场展示压测工具的测试结果,这就是加分项。

问题三:发票功能是模拟的还是真实的?答:当前实现为模拟开票流程,生成发票编号和 PDF 文件,接口层已抽象了开票服务,生产环境可替换为第三方税控 API。这个答法既诚实又能体现出可扩展性。

问题四:多商家拆单是怎么设计的?答:父子订单模型,一次支付,回调自动触发子订单同步支付,每个商家独立结算。顺带回答结算周期和分账逻辑,证明你想过这个问题。

7.3 论文中的图表呈现建议

论文里必须有三张图:系统架构图、功能模块图、数据库 E-R 图。不需要用花哨的绘图工具,不然生成的图会带水印或者格式混乱。我推荐用 ProcessOn 或者 draw.io 这类在线工具,导出 PNG 透明背景。E-R 图重点标清楚:用户与订单一对多、订单与商品多对多(通过 order_item)、商家与商品一对多、订单与发票一对一。

运行截图不要只用模拟器截图,要有真机截图、微信开发者工具控制台截图、Django admin 后端截图、MySQL 数据库表数据截图。截图里不要出现本地 IP 或未打码的手机号,答辩时会显得很野路子。

8. 项目后续的合理扩展方向

如果时间充足,这个商城系统可以朝这几个方向延展,论文和答辩都会更丰满:

一是加“药品过期预警”模块。定时任务每天查expiry_date - today < 90的商品,给商家推送效期预警列表。这个功能对药品商城非常贴切,实现也不复杂:Django-celery-beat 配置一个每日任务,查出来写一张预警表,商家后台轮询即可。

二是加“会员积分与优惠券”模块。积分来源是完成订单和评价,优惠券分为满减券和折扣券,下单结算时校验可用范围。这个能体现你对营销模型的理解,跟通用商城拉开差距。

三是加“商家结算账单”模块。每周/每月自动汇总商家子订单的应收、退款、实收,生成结算单,财务(管理员)确认后打入商家余额。这块业务价值高,而且天然和发票模块呼应。数据库层面就是新增settlement表和settlement_item表,代码量不大但故事完整。

不过这几个方向有一个算一个,都需要额外 2-3 周时间,别一上来全加。先保证主流程跑通、答辩能用,再根据自身时间挑选 1-2 个增强模块来加,论文里放“系统扩展”一节写清楚设计思路就算赢。

另外时间充裕的话,把 MySQL 的慢查询日志开一下,压测时看接口响应时间,挑一两个超过 200ms 的优化下索引,写进论文系统测试章节,数据翔实图文并茂,效果比空谈“系统稳定性很好”好一百倍。


最后分享两个实在的经验。

第一个经验是:这个项目的难点其实不在写代码,而在“你不知道你不知道什么”。多商家拆单、发票抬头校验、微信支付回调验签、处方药流程,每一个模块都是独立的知识体系,如果之前没接触过,第一次做至少需要两周时间踩坑。所以建表阶段就要想清楚,不要着急动手写接口。

第二个经验是:演示 demo 的前一天一定不要把时间花在加新功能上,而是把主流程从注册、登录、搜索、加购、下单、支付、开票完整走三遍。新功能永远有 bug,demo 跑不通之前,宁可砍功能也别赌临场发挥。这行干久了会发现,项目能给人留下好印象的,永远是稳定跑通的核心闭环,而不是多少花哨功能。希望这篇分享能让你少走一些弯路,项目顺利交差、答辩顺利过关。

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

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

立即咨询