简介:这是一套面向高校学生与Java/NetCore开发者的微信小程序网上购物商城系统完整项目源码,适用于毕业设计、课程设计及小程序全栈学习场景。项目采用微信小程序+NetCore+layui技术栈,构建多店铺商城,包含小程序端、管理后台、插件管理与WebApi四大模块,并附数据库文件与安装使用说明,可帮助读者快速理解商城业务闭环与前后端分离架构。压缩包共1915个文件,约10.34MB,以798个C#后端源码、180个JavaScript脚本、148个cshtml视图、42个wxml小程序页面及42个wxss样式为主,另含数据库SQL、插件配置与静态资源,结构完整便于按模块研读。目前已有3592人学习下载,读者可获取可运行的商城项目、数据库脚本、插件扩展机制与部署配置说明,适合作为毕业设计参考或二次开发基础。
1. 从一份能跑起来的购物小程序源码说起:它到底解决了什么问题
很多做毕业设计的朋友都遇到过这种局面:选题定了“微信小程序购物商城”,网上搜到的源码要么只有前端页面、后端接口全是假的,要么数据库表结构残缺、跑起来就报错,最后卡在环境配置上耗掉大半时间。这份资源的核心价值就在于——它是一套完整的、前后端能对上的购物商城小程序项目,包含小程序端源码、后端服务、数据库脚本以及一份安装使用说明。换句话说,你拿到的不是一堆散落的页面文件,而是一个能真正跑通“浏览商品→加购物车→下单→查看订单”这条主链路的工程。
它适合三类人:正在做小程序方向毕业设计、需要一份可参考可修改的完整工程的学生;想快速了解小程序商城类项目典型架构的开发者;以及需要一套基础模板来做二次开发的人。技术栈上,小程序端用的是微信原生开发框架(WXML + WXSS + JS),后端常见做法是 Node.js 或 Java 提供 RESTful 接口,数据库一般是 MySQL。这套组合在毕业设计里属于主流且资料丰富的选型,遇到问题容易搜到答案。
需要提前说清楚的是:这份源码是学习参考性质的工程,不是可以直接上线运营的商业系统。支付环节通常用的是模拟支付或沙箱逻辑,不涉及真实资金流转。理解这一点,后面配置和调试时就不会走弯路。
2. 环境搭建与项目结构拆解:先把骨架看清楚再动手
2.1 开发环境清单与版本选择
在动手之前,先把工具链备齐。版本不匹配是新手翻车的头号原因,我一般会按下面这个清单逐项确认:
| 工具 | 用途 | 版本建议 | 注意事项 |
|---|---|---|---|
| 微信开发者工具 | 小程序端调试预览 | 稳定版即可 | 需登录微信账号 |
| Node.js | 后端服务运行环境 | 16.x 或 18.x LTS | 避免用最新奇数版 |
| MySQL | 数据库存储 | 5.7 或 8.0 | 8.0 注意驱动兼容 |
| Navicat / DBeaver | 数据库可视化管理 | 任意 | 方便导入 SQL 脚本 |
| VS Code | 后端代码编辑 | 任意 | 装好 ESLint 插件 |
这里有个容易被忽略的点:Node.js 版本和后端依赖里的mysql或mysql2驱动强相关。如果后端用的是老版本mysql包,在 Node 18 上可能报连接协议错误,换成mysql2通常能解决。选型上,我建议优先用 LTS 版本,别追新。
2.2 项目目录结构与各模块职责
拿到源码包后,先别急着运行,花十分钟把目录结构过一遍,后面排错效率会高很多。典型的目录长这样:
project-root/ ├── miniprogram/ # 小程序端源码 │ ├── pages/ # 页面目录 │ │ ├── index/ # 首页(商品列表) │ │ ├── detail/ # 商品详情 │ │ ├── cart/ # 购物车 │ │ ├── order/ # 订单确认与列表 │ │ └── user/ # 个人中心 │ ├── utils/ # 工具函数(请求封装等) │ ├── images/ # 静态图片资源 │ ├── app.js # 小程序入口逻辑 │ ├── app.json # 全局配置(页面路由、tabBar) │ └── app.wxss # 全局样式 ├── server/ # 后端服务 │ ├── routes/ # 接口路由 │ ├── controllers/ # 业务逻辑 │ ├── models/ # 数据库操作 │ ├── config/ # 数据库连接配置 │ └── app.js # 服务入口 ├── database/ │ └── shop.sql # 数据库建表与初始数据 └── README.md # 安装使用说明小程序端的app.json是全局配置的核心,页面路由、底部导航栏都在这里定义。后端config目录里一般有一个数据库配置文件,里面写着 host、user、password、database 四个关键字段,这是你第一个要改的地方。数据库脚本shop.sql通常包含建库、建表、插入测试数据三部分,导入后就能看到商品、用户、订单等表。
2.3 数据库导入与后端配置
先把数据库跑起来。打开 Navicat 或命令行,执行导入:
# 命令行方式导入(假设已登录 MySQL) mysql -u root -p -e "CREATE DATABASE shop_db DEFAULT CHARACTER SET utf8mb4;" mysql -u root -p shop_db < database/shop.sql第一行创建数据库并指定字符集为utf8mb4,这是为了支持中文和特殊符号,用utf8在某些场景下会出乱码。第二行把 SQL 脚本导入到shop_db库中。导入完成后,用SHOW TABLES;确认表是否都建好了,常见的有user、goods、cart、orders、order_item这几张。
接着改后端数据库配置。找到server/config/db.js(或类似文件),修改连接参数:
// server/config/db.js const mysql = require('mysql2'); const pool = mysql.createPool({ host: '127.0.0.1', // 数据库地址,本地就是 127.0.0.1 port: 3306, // MySQL 默认端口 user: 'root', // 你的数据库用户名 password: 'your_pass', // 改成你自己的密码 database: 'shop_db', // 刚才导入的库名 waitForConnections: true, connectionLimit: 10, // 连接池最大连接数 charset: 'utf8mb4' }); module.exports = pool.promise();这里用连接池而不是单连接,是因为小程序端可能同时发起多个请求,单连接会排队阻塞。connectionLimit: 10表示最多同时保持 10 个连接,毕业设计场景够用了。charset必须和建库时一致,否则中文商品名会变问号。
2.4 启动后端与小程序端联调
后端配置改完后,安装依赖并启动:
cd server npm install node app.js # 或者如果 package.json 里配了脚本 npm run dev启动成功后,控制台一般会打印类似Server running on port 3000的信息。这时候先用浏览器或 Postman 访问一个测试接口,比如http://127.0.0.1:3000/api/goods/list,确认能返回 JSON 数据。这一步很关键——如果接口不通,小程序端怎么调都是白搭。
小程序端打开微信开发者工具,导入miniprogram目录,然后在utils/request.js(或类似封装文件)里把请求基地址改成你的后端地址:
// miniprogram/utils/request.js const BASE_URL = 'http://127.0.0.1:3000/api'; function request(options) { return new Promise((resolve, reject) => { wx.request({ url: BASE_URL + options.url, method: options.method || 'GET', data: options.data || {}, header: { 'Content-Type': 'application/json' }, success: (res) => { if (res.statusCode === 200) { resolve(res.data); } else { reject(res); } }, fail: reject }); }); } module.exports = { request };这段封装把wx.request包成 Promise,方便用async/await调用。BASE_URL是唯一需要改的地方。注意:微信开发者工具里需要在“详情→本地设置”中勾选“不校验合法域名”,否则本地 HTTP 请求会被拦截。这是本地调试的常规操作,上线时才需要配置正式域名。
3. 核心功能模块的实现逻辑:商品、购物车与订单怎么串起来
3.1 商品列表与详情的数据流
商品模块是整个商城的基础。首页调用后端/api/goods/list接口拿到商品数组,渲染到 WXML 列表里。分页通常用page和pageSize两个参数控制,后端用LIMIT offset, size实现。
// miniprogram/pages/index/index.js const { request } = require('../../utils/request'); Page({ data: { goodsList: [], page: 1, hasMore: true }, onLoad() { this.loadGoods(); }, async loadGoods() { if (!this.data.hasMore) return; const res = await request({ url: '/goods/list', data: { page: this.data.page, pageSize: 10 } }); // 拼接新数据,而不是覆盖 this.setData({ goodsList: [...this.data.goodsList, ...res.data], page: this.data.page + 1, hasMore: res.data.length === 10 // 返回不足10条说明到底了 }); }, onReachBottom() { this.loadGoods(); // 触底加载更多 } });这里hasMore的判断逻辑是:如果后端返回的数据条数等于pageSize,说明可能还有下一页;小于则说明已经到底。这个判断简单但有效,是列表分页的常见做法。商品详情页则通过onLoad的options.id拿到商品 ID,再请求/goods/detail?id=xxx获取完整信息。
3.2 购物车的本地存储与服务端同步
购物车有两种实现思路:纯本地存储(wx.setStorageSync)和 服务端存储。这份源码常见做法是两者结合——未登录时存本地,登录后同步到服务端。本地存储的优点是响应快、不依赖网络,缺点是换设备就丢了。
// 加入购物车(本地) function addToCart(goods) { let cart = wx.getStorageSync('cart') || []; const existIndex = cart.findIndex(item => item.id === goods.id); if (existIndex > -1) { cart[existIndex].count += 1; // 已存在则数量+1 } else { cart.push({ ...goods, count: 1, checked: true }); } wx.setStorageSync('cart', cart); }findIndex用来判断商品是否已在购物车中,避免重复添加。checked字段控制结算时是否勾选该商品。购物车页面需要处理数量增减、删除、全选/反选这些交互,核心是对数组的增删改查,逻辑不复杂但边界情况多,比如数量减到 0 时应该删除而不是显示 0。
3.3 订单创建的事务处理
下单是整个链路里最需要小心的地方。用户点击“提交订单”后,后端要做几件事:创建订单记录、创建订单明细、扣减库存、清空购物车中已下单的商品。这几步必须在一个事务里完成,否则可能出现订单创建了但库存没扣、或者库存扣了订单没生成的情况。
// server/controllers/order.js async function createOrder(userId, items) { const conn = await pool.getConnection(); try { await conn.beginTransaction(); // 1. 计算总价 let total = 0; for (const item of items) { const [goods] = await conn.query( 'SELECT price, stock FROM goods WHERE id = ? FOR UPDATE', [item.goodsId] ); if (goods[0].stock < item.count) { throw new Error(`商品 ${item.goodsId} 库存不足`); } total += goods[0].price * item.count; } // 2. 创建订单 const [orderResult] = await conn.query( 'INSERT INTO orders (user_id, total, status, create_time) VALUES (?, ?, ?, NOW())', [userId, total, 'pending'] ); const orderId = orderResult.insertId; // 3. 创建明细并扣库存 for (const item of items) { await conn.query( 'INSERT INTO order_item (order_id, goods_id, count, price) VALUES (?, ?, ?, ?)', [orderId, item.goodsId, item.count, item.price] ); await conn.query( 'UPDATE goods SET stock = stock - ? WHERE id = ?', [item.count, item.goodsId] ); } await conn.commit(); return { orderId, total }; } catch (err) { await conn.rollback(); throw err; } finally { conn.release(); } }FOR UPDATE是行级锁,防止并发下单时超卖。beginTransaction和commit/rollback保证原子性。conn.release()放在finally里确保连接一定归还连接池,否则连接泄漏跑一会儿就卡死了。这段逻辑是订单模块的核心,也是答辩时最容易被问到的部分,建议吃透。
4. 避坑与排查:那些让项目跑不起来的常见问题
4.1 小程序请求失败,控制台报“不在以下 request 合法域名列表中”
现象:小程序端调用接口时请求发不出去,控制台红字提示域名不合法。
原因:微信小程序默认只允许请求已备案并配置在后台的 HTTPS 域名,本地开发用的http://127.0.0.1不在白名单里。
解决:打开微信开发者工具,点击右上角“详情”→“本地设置”→勾选“不校验合法域名、web-view(业务域名)、TLS 版本以及 HTTPS 证书”。这是本地调试的标准操作,上线前再配置正式域名即可。
4.2 数据库连接报 ER_ACCESS_DENIED_ERROR 或 ECONNREFUSED
现象:后端启动时报错,提示访问被拒绝或连接被拒。
原因:前者通常是用户名或密码写错了,后者一般是 MySQL 服务没启动,或者端口不是默认的 3306。
解决:先确认 MySQL 服务在运行(Windows 看服务列表,Mac 用brew services list)。然后检查db.js里的 user、password、port 三个字段。如果密码里有特殊字符,注意在配置文件里是否需要转义。实在不确定就用命令行mysql -u root -p手动登录验证一遍。
4.3 商品图片不显示,WXML 里 image 标签空白
现象:商品列表能渲染出文字,但图片位置是空白或者裂图。
原因:常见有两种——图片路径写的是本地绝对路径但文件没放对位置,或者图片 URL 是后端返回的相对路径而小程序端没拼完整域名。
解决:先看image标签的src值是什么。如果是/images/xxx.jpg这种,确认miniprogram/images/目录下确实有这个文件。如果是后端返回的路径,检查是否需要拼接 BASE_URL。另外,小程序对图片域名也有白名单限制,本地图片不受影响,网络图片同样需要在开发者工具里关闭域名校验。
4.4 下单后库存没变化,或者订单列表查不到数据
现象:点击提交订单提示成功,但刷新商品详情发现库存还是原来的数字,或者订单列表为空。
原因:大概率是事务没有正确提交,或者查询订单时 user_id 对不上。也有可能是前端传的商品 ID 类型和后端期望的不一致(字符串 vs 数字)。
解决:在后端createOrder里加日志,确认commit是否执行到。检查orders表的user_id字段和当前登录用户是否一致。如果是类型问题,在 SQL 参数里做一次parseInt转换。这类问题用console.log打关键变量最快定位。
4.5 中文显示乱码,商品名变成问号
现象:数据库里看数据正常,但小程序端显示出来是???或者乱码。
原因:字符集不统一。建库时用了utf8,连接时用了latin1,或者后端返回 JSON 时没设置正确的 Content-Type。
解决:三步统一——建库用utf8mb4,连接配置里charset: 'utf8mb4',后端响应头设置Content-Type: application/json; charset=utf-8。三处都对齐后乱码问题基本消失。
5. 二次开发与答辩加分技巧:让这份源码真正变成你的东西
把项目跑通只是第一步,毕业设计要拿得出手,还得在原有基础上做出自己的东西。这里分享几个实操方向,都是成本不高但效果明显的。
第一个方向:加一个商品搜索功能。后端加一个/api/goods/search?keyword=xxx接口,SQL 用LIKE '%keyword%'匹配商品名。前端在首页顶部加一个搜索框,输入后跳转到搜索结果页。这个功能代码量不大,但演示时很直观,答辩老师一看就知道是你自己加的。
// 后端搜索接口示例 router.get('/search', async (req, res) => { const { keyword, page = 1, pageSize = 10 } = req.query; const offset = (page - 1) * pageSize; const [rows] = await pool.query( 'SELECT * FROM goods WHERE name LIKE ? LIMIT ?, ?', [`%${keyword}%`, offset, pageSize] ); res.json({ code: 200, data: rows }); });注意LIKE的参数用占位符?传,不要直接拼字符串,否则有 SQL 注入风险。这是答辩时容易被追问的安全点,提前做好能加分。
第二个方向:给订单加状态流转。原版订单可能只有“待付款”和“已完成”两个状态,你可以扩展成“待付款→待发货→待收货→已完成→已取消”五个状态,并在后端加一个状态更新的接口。前端订单列表用不同颜色标签区分状态。这个改动涉及数据库字段、后端接口、前端展示三层,能体现你对业务逻辑的理解。
第三个方向:接入微信支付沙箱。注意是沙箱环境,不是真实支付。微信官方提供了沙箱测试接口,可以模拟支付成功/失败的流程。接入后你的项目就有了“支付”这个电商核心环节的完整体验。不过这块文档较多,建议预留两三天时间专门啃。
验证改动是否生效的方法很简单:每次改完后端接口,先用 Postman 单独测通,再回到小程序端联调。不要前后端一起改然后一起调,出了问题根本不知道是哪边的锅。我一般会保持“后端接口先通、前端再对接”的节奏,这样排查范围始终是可控的。
最后说一个习惯:每次改完一个功能,就在 README 里记一笔——改了什么文件、加了什么接口、数据库有没有变动。答辩前一周回头看这份记录,能快速回忆起所有改动点,不至于被问到“你这个功能怎么实现的”时卡壳。从那以后我每次做项目都强制走一遍这个记录流程,省下来的时间远比记录本身多。希望帮到你。
本文还有配套的精品资源,点击获取