ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

微信小程序宠物商城毕业设计:前后端分离与数据库设计实战

微信小程序宠物商城毕业设计:前后端分离与数据库设计实战 简介这是一套基于微信小程序的宠物商城系统毕业设计项目面向计算机相关专业正在准备毕设、课程设计或期末大作业的学生也适合需要项目实战练习的开发者。系统包含商品展示、购物车、用户管理等商城核心模块并接入地图与天气等第三方服务源码与数据库齐备经过严格调试可正常运行能直接作为毕设作品提交或二次扩展。压缩包共包含204个文件整体约10.56MB。其中35个js文件承担业务逻辑33个json文件用于页面与项目配置26个wxml文件搭建页面结构28个wxss文件负责界面样式另有74张png图片及数据库、说明文档等目录结构清晰便于按模块学习与修改。该资源已有345人浏览学习。配套的项目说明可帮助理解系统设计思路适合从零上手微信小程序开发也可作为答辩演示和功能演示的完整参考。1. 宠物商城是毕业设计里出场率最高的题材之一但想拿高分反而难宠物商城是毕业设计里出场率最高的题材之一但正因人人都在做想让评审老师眼前一亮反而最难——他们看过的微信小程序宠物商城可能比你想象的还多。多数演示翻车不在页面好不好看而在“看宠物 → 加购 → 下单”这条主链路走不通加购没反应、订单状态不更新、刷新一下购物车空了。这套基于微信小程序的宠物商城系统源码数据库高分毕业设计.zip之所以能成为被反复采用的参考模板靠的不是功能堆砌而是数据库设计与交易闭环经得起追问。适合两类人一是正在选毕设题目的计算机相关专业学生二是想理解小程序前后端协作、快速搭一个全栈练手项目的开发者。需要的基本功只有三样看得懂SQL、改得动JavaScript、会按接口文档调API。2. 三件套如何协作小程序端、接口后端、MySQL 数据层的目录与分工拿到这类压缩包解压后一般会看到两类东西小程序项目目录包含 pages、utils、app.js 等和数据库初始化脚本.sql 文件或 SQLite 的 .db 文件。理解三者的边界比急着跑起来更重要——答辩时老师最常问的第一句话就是“数据从哪来、存到哪去”。2.1 前后端分离的落地结构为什么原生小程序比 uniapp 更适合毕业答辩宠物商城这类项目最稳妥的结构是前后端分离小程序端负责页面渲染和交互所有数据通过 wx.request 向后端要后端只返回 JSON不管页面MySQL 只存数据不写业务逻辑。答辩时可以分三层讲表现层、业务层、数据层每一层都有对应的代码和表可以指给老师看。很多人会用 uniapp 开发微信小程序因为一套代码能同时出 H5 和 App。但毕业设计我一般建议直接用原生小程序。原因有两个第一原生小程序的生命周期函数onLoad、onShow、onHide是评审老师默认的考点你用 uniapp 封装了一层遇到“页面为什么重新加载”这类问题时容易露怯第二毕设项目的体量普遍不大跨端收益根本算不过来原生写法最直接调试报错也更容易搜到答案。后端语言选什么并不关键Spring Boot、Node.js Express、PHP 都能做接口契约是一致的。关键在于接口路径要稳定比如宠物列表固定是 GET /pet/list、下单固定是 POST /order/create。不要一个页面一套 URL 风格否则答辩演示时切换页面会频繁出现 404体验非常扣分。2.2 请求封装把 baseURL、token 和错误提示收进一个 request.js毕设里最常见的翻车现场是“真机上很多接口请求失败”。原因多半是 baseURL 散落在每个页面里改了一处忘了另一处或者后端启动地址变了但小程序里还写着旧 IP。正确做法是让所有请求走一个统一封装集中管理地址和公共逻辑// utils/request.js const BASE_URL http://192.168.1.100:8080/api; // 开发时填电脑局域网 IP function request(method, url, data {}) { return new Promise((resolve, reject) { wx.request({ url: BASE_URL url, method: method, data: data, header: { Content-Type: application/json, Authorization: wx.getStorageSync(token) || // 统一注入登录态 }, success(res) { // 和后端约定code 为 200 表示业务成功 if (res.data.code 200) { resolve(res.data.data); } else { wx.showToast({ title: res.data.msg || 请求失败, icon: none }); reject(res.data); } }, fail(err) { // 网络层错误后端没启动、IP 不对、域名没配都会走到这里 wx.showToast({ title: 网络异常请检查后端是否启动, icon: none }); reject(err); } }); }); } module.exports { request };这段代码解决三个高频问题一是所有接口共用 BASE_URL后端换机器只改一处二是登录后返回的 token 从缓存统一读取、统一注入请求头不用每个页面都写一遍三是成功和失败都做了统一提示页面里不需要重复处理 toast。页面里调用的方式要足够简单让后面写业务时注意力集中在数据上const { request } require(../../utils/request); request(GET, /pet/list, { page: 1 }).then(list { this.setData({ pets: list }); });这里注意业务码 200、401、500 的约定要跟后端对好。401 表示登录态过期收到后应该清除缓存并跳转登录页500 通常是后端异常这类错误不要让用户看到堆栈统一提示“服务开小差了”。把这套约定写进接口文档答辩时是一块很加分的工程化亮点。2.3 登录态与缓存时间wx.login 换 code 不是摆设微信小程序的登录流程跟传统用户名密码完全不一样。标准做法是 wx.login 拿到临时 code后端拿 code 去微信接口换 openid再签发一个自定义 token 返回给前端。这里有个容易偷懒的点不少人为省事直接在本地写死一个用户 ID结果答辩被问到“openid 怎么来的”就卡住了。// pages/login/login.js wx.login({ success: async ({ code }) { // code 是临时凭证5分钟内有效且只能使用一次 const res await request(POST, /auth/login, { code }); wx.setStorageSync(token, res.token); wx.setStorageSync(expire, Date.now() 24 * 60 * 60 * 1000); // 有效期24小时 } });登录态的“缓存时间”是评审爱问的细节。wx.setStorageSync 默认是永久存储不会自己过期所以要么存一个 expire 时间戳每次进入小程序检查和当前时间做对比要么在后端给 token 设有效期前端收到 401 后清理缓存。更好的做法是在 request.js 里拦截 401统一跳转登录页。提示如果只想让答辩流程顺畅可以在代码里留一个“演示模式”开关默认走固定 userId跳过 wx.login但注释里要写清楚正式上线时如何切回真实流程。这比硬着头皮现场登录稳妥也显得你考虑过工程边界。3. 数据库设计与宠物商品特殊性七张核心表与活体状态字段数据库脚本是整个压缩包里最先值得打开的东西。评审老师不一定会一行行看你的 JS但大概率会打开 Navicat 看一眼表结构。宠物商城表设计得好不好直接决定“高分”能不能落地。3.1 七张核心表与字段设计从用户到订单明细全链路一个完整的宠物商城最少要有七张表用户表、宠物商品表、分类表、购物车表、订单表、订单明细表、收藏表。其中用户、宠物、订单是必须重点讲的购物车和收藏是辅助功能但它们的表结构同样要规范。表名核心字段说明userid, nickname, avatar, phone, openidopenid 唯一标识微信用户petid, name, category_id, variety, age, gender, health_status, vaccine, price, stock, cover_url, status宠物即商品字段比普通商品多categoryid, name, sort猫、狗、小宠等分类cartid, user_id, pet_id, quantity, checkedchecked 记录勾选状态orderid, order_no, user_id, total_amount, status, create_timestatus 流转待支付/已支付/已完成order_itemid, order_id, pet_id, pet_name, price, quantity冗余商品快照防止商品下架后订单失效favoriteid, user_id, pet_id, create_time收藏与取消收藏宠物表是这套设计的核心跟普通商品表最大的区别是多了健康相关字段。下面是宠物表的建表 SQL关键字段都加了注释CREATE TABLE pet ( id INT NOT NULL AUTO_INCREMENT, name VARCHAR(50) NOT NULL COMMENT 宠物名字, category_id INT NOT NULL COMMENT 分类ID关联category表, variety VARCHAR(60) DEFAULT COMMENT 品种如英短、金毛, age DECIMAL(3,1) DEFAULT 0 COMMENT 月龄0.5表示半个月, gender TINYINT DEFAULT 0 COMMENT 0未知 1公 2母, health_status TINYINT DEFAULT 1 COMMENT 1健康 2治疗中 3待观察, vaccine VARCHAR(100) DEFAULT COMMENT 疫苗记录如“三联狂犬”, price DECIMAL(10,2) NOT NULL COMMENT 售价, stock INT NOT NULL DEFAULT 1 COMMENT 剩余可售数量, cover_url VARCHAR(255) DEFAULT COMMENT 封面图, detail TEXT COMMENT 详细介绍、性格描述, status TINYINT DEFAULT 1 COMMENT 1上架 0下架, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;注意几个容易被忽略的设置价格用 DECIMAL(10,2) 而不是 FLOAT避免浮点误差stock 字段虽然语义是“还有几只可选”但下单扣减时要在 SQL 里加 stock 0 条件防止变成负数status 字段用于上下架前端列表页只查 status 1 的数据。3.2 宠物不是普通商品活体状态与订单状态流转的加分点宠物商城里卖的是活体这一点是跟普通电商拉开差距的设计亮点。普通商城的商品表只有价格、库存、图片宠物还要有健康状态、疫苗记录、性格描述。列表页展示时很多学生只会把数据库里的字段原样渲染出来但高分做法是把 health_status 翻译成用户看得懂的内容比如“健康”“治疗中”“待观察”并配上不同颜色的标签。订单状态流转是另一个必答考点。标准链路是“待支付 → 已支付 → 已完成”带线下自提的宠物商城还可以加“待自提”。订单明细表一定要做商品快照——下单那一刻把宠物名、价格、图片复制一份存进 order_item。这样就算宠物下架或者改价历史订单依然能完整显示也方便答辩时解释“为什么订单表不直接关联 pet 表”。数据库的增删改查接口要跟七张表一一对应。比如宠物模块的列表查询、详情查询、上下架修改购物车模块的增删改查订单模块的创建与状态更新。前端每个交互动作老师问到“这个操作改了哪张表”你都要能立刻回答。建议答辩前把每张表对应的接口列一张表背下来这是最容易被追问的部分。3.3 字符集与连接池utf8mb4 和两个必调参数宠物名字里经常带 emoji比如“小橘”MySQL 的 utf8 字符集会直接报错或存成乱码。建库时必须用 utf8mb4CREATE DATABASE IF NOT EXISTS pet_mall DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;用 Navicat 导入 .zip 里自带的 .sql 文件时注意连接字符集也要选 utf8mb4否则表里明明有数据但页面显示乱码。这个坑属于“现象明显、原因隐蔽”的类型建议在第一版建库时就直接把字符集写好省得后面返工。后端连 MySQL 通常会用到连接池。以 Java 的 Druid 或 Node 的 mysql2 连接池为例有两个参数值得关注const pool mysql.createPool({ host: 127.0.0.1, user: root, password: 123456, database: pet_mall, connectionLimit: 10, // 最大连接数毕设场景 10 足够 charset: utf8mb4 // 和数据库字符集保持一致 });connectionLimit 设太大会占资源设太小并发一高接口就排队超时。毕设并发量很低10 到 20 之间都合理。charset 必须和库表一致否则请求层中文乱码。这两个参数在答辩时也常被问到提前熟悉一下能省去不少尴尬。4. 从选宠到下单商品列表、加购、结算的核心链路实现主链路是评审老师最关注的演示路径首页看到宠物列表 → 点进详情 → 加入购物车 → 购物车勾选 → 提交订单 → 模拟支付。这条链路走通项目的基本盘就稳了。下面按顺序拆每一段的实现。4.1 商品列表与详情分页加载与分类切换首页列表最常见的错误是一次性把所有宠物查出来数据量一大就卡。正确做法是分页加载小程序滑到底部时自动拉下一页。微信小程序的 onReachBottom 就是干这个的Page({ data: { page: 1, pets: [], hasMore: true, categoryId: 0 // 0表示全部 }, onLoad() { this.loadPets(); }, async loadPets() { if (!this.data.hasMore) return; const data await request(GET, /pet/list, { page: this.data.page, size: 10, categoryId: this.data.categoryId }); // 用 concat 追加而不是直接覆盖否则翻页会丢数据 this.setData({ pets: this.data.pets.concat(data.list), hasMore: data.hasMore // 后端根据总数和页码计算 }); }, onReachBottom() { this.setData({ page: this.data.page 1 }, () { this.loadPets(); }); }, switchCategory(e) { // 切换分类时重置页码否则会停留在上一个分类的末尾 const categoryId e.currentTarget.dataset.id; this.setData({ page: 1, pets: [], hasMore: true, categoryId }, () { this.loadPets(); }); } });这里有两个容易踩的细节。一是 onReachBottom 里要先 setData 更新 page 再调用 loadPets否则第一次翻页用的还是旧页码二是切换分类必须重置 page 和 pets不然会出现“分类是猫列表里却有狗”的尴尬。后端接口对应的是分页 SQL一般用 LIMIT offset, size 实现并返回 hasMore 标记是否有下一页。详情页的逻辑相对简单根据路由参数里的 id 调 GET /pet/detail?idxxx把返回的宠物信息绑定到页面。这里要强调详情页展示的数据必须是数据库里查出来的不要在前端写死一份 JSON。答辩现场老师会随手改数据库里的价格再刷新页面如果价格不变印象分会掉一大截。4.2 加购与购物车数量修改与勾选联动加购的接口设计要注意“重复加购”的问题。用户把同一只宠物加了三次购物车里是三条记录还是一条数量为 3 的记录正确做法是后者。后端逻辑先查 cart 表里有没有同一 user_id 和 pet_id 的记录有就 UPDATE quantity没有才 INSERT。// 后端伪代码POST /cart/add async function addToCart(userId, petId, quantity) { const exist await db.query( SELECT id, quantity FROM cart WHERE user_id ? AND pet_id ?, [userId, petId] ); if (exist.length 0) { await db.query( UPDATE cart SET quantity quantity ? WHERE id ?, [quantity, exist[0].id] ); } else { await db.query( INSERT INTO cart (user_id, pet_id, quantity, checked) VALUES (?, ?, ?, 1), [userId, petId, quantity] ); } }购物车的勾选状态是另一个高频翻车点。微信原生的 checkbox 组件绑定的 checked 值必须来自数据层不能靠 DOM 操作去改。常见做法是在每条购物车数据里维护一个 checked 字段点击时通过>toggleCheck(e) { const id e.currentTarget.dataset.id; // 购物车记录id const carts this.data.carts.map(item { if (item.id id) { return { ...item, checked: !item.checked }; } return item; }); this.setData({ carts }); },全选逻辑也简单把 allChecked 取反后循环把所有 item 的 checked 设为同一个值再单独 setData 一次。不要一个勾选框一个 setData性能差而且容易出现错乱。结算金额也要实时计算遍历 checked 为 true 的记录累加 price × quantity这个计算放在 setData 之后做保证界面同步。4.3 下单与模拟支付库存扣减与订单创建的事务处理提交订单是整个项目里逻辑最重的接口涉及多张表必须保证要么全部成功、要么全部失败。后端做三件事校验购物车选中商品、扣减库存、创建订单和订单明细。库存扣减要用一条带条件的 UPDATE-- 防止库存变成负数只扣减 stock 0 的记录 UPDATE pet SET stock stock - 1 WHERE id ? AND stock 0;如果这条语句影响的行数是 0说明库存不足直接返回“该宠物已被买走”。只有库存扣减成功才去创建订单否则回滚。这一步是答辩时的高分亮点比“先查库存再扣减”的写法安全得多。小程序端提交订单的代码要处理两个核心点按钮防重复点击、下单成功后跳转支付。按钮防重用 loading 状态实现async submitOrder() { if (this.data.submitting) return; // 防止连点生成重复订单 this.setData({ submitting: true }); wx.showLoading({ title: 提交中 }); try { const cartIds this.data.carts.filter(i i.checked).map(i i.id); const order await request(POST, /order/create, { cartIds }); // 拿到订单号进入支付引导 wx.redirectTo({ url: /pages/pay/pay?orderNo order.orderNo }); } finally { wx.hideLoading(); this.setData({ submitting: false }); } }支付环节是“毕业设计专用”的典型场景。真实微信支付需要商户号和微信支付资质个人开发者基本拿不到所以毕设普遍做法是模拟支付前端弹一个支付确认框用户点确认后调后端 POST /pay/mock 接口把订单状态从待支付改成已支付。代码里用一个开关标明当前是模拟模式const USE_MOCK_PAY true; // 正式接入微信支付时改为 false并补充支付参数用真实 wx.requestPayment 而没有商户号调用会直接报错“商户号参数错误”现场演示非常难看。模拟支付要注意接口里带上 orderNo后端更新订单时加一个条件“只允许从待支付改成已支付”防止重复回调改变状态。订单列表页的状态展示要跟这个流程对应待支付、已支付、已完成三个 Tab数据从 order 表按 user_id 和 status 查出来。5. 毕业设计避坑真机联调、导航栏适配、重复下单的排查记录这里按“现象 → 原因 → 解决”的格式写五条踩坑记录都是这类毕设项目里出现频率最高的真实问题。5.1 网络联调真机连不上后端、图片加载不出来坑一开发者工具里接口正常一用真机预览全部失败。现象模拟器上列表、详情都通手机扫码打开后页面空白请求全部超时。原因开发者工具默认勾选了“不校验合法域名”真机预览没有这个豁免另一个原因是后端服务监听在 127.0.0.1真机根本访问不到电脑的 localhost。解决后端启动时监听 0.0.0.0BASE_URL 填电脑的局域网 IP命令行执行 ipconfig 或 ifconfig 查出来一般是 192.168.xx.xx不要写 localhost。真机预览前在开发者工具“详情 → 本地设置”里勾选“不校验合法域名、web-view业务域名、TLS 版本以及 HTTPS 证书”。要注意这是开发阶段的做法正式上线必须换备案过的 HTTPS 域名但答辩演示完全够用。坑二图片加载不出来列表页只有一片灰块。现象开发者工具里图片正常真机上图片全部空白控制台提示下载失败。原因cover_url 存的是相对路径或者指向 127.0.0.1 的图片服务地址真机无法访问。解决后端返回图片时拼上完整 URL或者前端在渲染前统一拼接 BASE_URL。检查时打开开发者工具的 Network 面板看 image 请求的返回状态码404 是路径不对502 是图片服务没启动。图片路径这个坑看着小但演示时直接影响观感。5.2 UI 适配顶部导航栏高度与原单选框样式坑三自定义导航栏在 iPhone 上错位标题和胶囊按钮重叠。现象部分安卓机上正常iPhone 上标题栏文字顶到状态栏或者和右侧胶囊按钮叠在一起。原因使用了自定义导航栏navigationStyle: custom后不同设备的状态栏高度和胶囊按钮位置不同写死一个高度必然适配失败。解决动态计算导航栏高度用 wx.getMenuButtonBoundingClientRect 拿到胶囊按钮的位置const sysInfo wx.getSystemInfoSync(); const menuRect wx.getMenuButtonBoundingClientRect(); const statusBarHeight sysInfo.statusBarHeight; const navBarHeight (menuRect.top - statusBarHeight) * 2 menuRect.height;公式的含义是胶囊顶部到状态栏的距离乘以 2再加上胶囊自身高度就是自定义导航栏应有的总高度。拿到这个值后给自定义标题 view 设置高度并让标题文字垂直居中。这样无论什么机型标题都和胶囊按钮在同一水平线。这是热词“微信小程序顶部导航栏高度”的核心答案值得记住。5.3 业务逻辑重复下单与购物车勾选错乱坑四连点两下“提交订单”生成了两条一模一样的订单。现象快速点击下单按钮订单列表里出现两条相同内容的记录库存被扣了两次。原因前端没有禁用按钮后端也没有做防重校验请求在几百毫秒内被提交了两次。解决前端在 submitOrder 里用 submitting 标志位拦截二次点击上一节代码已经处理后端在 order 表给 order_no 字段加唯一索引重复提交时第二次插入会抛 ER_DUP_ENTRY 异常捕获后提示“订单已存在请勿重复提交”。前后端双重防护是工程上标准的幂等做法。坑五购物车勾选状态错乱全选以后取消单个其他项也跟着变。现象点击某个商品的勾选框结果整行甚至多行的选中状态都被翻转。原因setData 时用了错误的索引或者把 checkbox 的 checked 属性直接绑成了固定的 true导致状态没有和当前数据项一一对应。解决每条购物车记录都维护独立的 checked 字段操作时用 style="width:16px;margin-left:4px;vertical-align:text-bottom;cursor:text;" />
返回列表