尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

微信小程序商城全栈实战:从登录到订单完整电商项目开发

微信小程序商城全栈实战:从登录到订单完整电商项目开发 这次我们来看一个微信小程序实战项目小米商城小程序。这个项目最大的特点是“全链路电商流程”首页、分类、商品列表、商品详情、购物车、订单、个人中心再加上一个后台管理系统把商品的增删改查、订单状态管理都串起来了。对于准备毕设、找前端实习、或者想系统性练习小程序的开发者来说是比较典型的一套练手项目。先说几个关键信息技术栈以微信小程序原生为主后台可以选 Node.js 后端或者直接用小程序云开发项目不是只写静态页面而是带真实数据交互登录、商品查询、购物车、订单提交都能跑通后台管理系统涉及完整的增删改查适合用来讲清“前端页面 接口 数据表”的联动关系整个项目既适合课程设计、毕业设计也适合整理成面试项目经验。这篇文章会按“项目定位 - 功能模块 - 环境准备 - 启动运行 - 核心功能实现 - 增删改查设计 - 接口封装 - 常见问题 - 毕设答辩和面试要点”的顺序展开尽量把能直接复用的代码和思路都写出来。1. 项目核心能力速览能力项说明项目类型微信小程序商城项目 后台管理系统技术栈小程序原生 WXML/WXSS/JS后端可选 Node.js/Express 或微信云开发核心功能首页装修、商品分类、商品列表、商品详情、购物车、订单、个人中心后台管理商品管理、分类管理、订单管理覆盖增删改查是否需要服务器使用云开发就不需要自建服务器使用自建后端需要服务器或本地运行启动方式微信开发者工具导入项目配置 AppID 即可预览是否支持真机预览支持真机调试需要配置合法域名或开启调试模式适合场景毕设、课程设计、前端求职面试项目、小程序入门综合练习开发难度中等适合已经掌握 HTML/CSS/JavaScript 基础、想进阶小程序的开发者从功能上看这不是一个“只展示页面”的静态仿站而是把“用户端小程序 管理端后台 数据接口”三部分串起来的完整项目。这也是很多面试官比较认可的项目结构不是只有页面而是有真实的业务数据流转。2. 适用场景与使用边界2.1 这个项目适合谁正在准备毕业设计需要做一个“前端 后台 数据库”完整闭环的学生前端已经入门想通过一个完整项目搞清楚“小程序如何调用接口、如何管理登录态、如何实现购物车”的开发者正在准备前端实习或初级前端岗位面试需要一个能讲清楚业务逻辑和细节的实战项目想学习“用户端 管理端”双端结构为以后做后台管理系统打基础的人。2.2 能解决什么问题这个项目本身的价值是让你在真实业务场景里理解前端工程化的基本功小程序页面路由和组件化拆分请求封装、拦截器、登录态管理列表页、详情页、购物车、订单的状态管理后台管理系统的增删改查设计前后端接口联调的基本流程。对于面试来说常见的问题比如“购物车选中状态怎么维护”“订单状态怎么流转”“小程序登录流程是什么”都可以在这个项目里实际验证。2.3 使用边界与合规提醒小米商城是一个真实商业品牌做这个项目时要注意几点学习阶段可以仿照页面布局风格但建议使用占位图和自定义素材不要直接使用小米官方 Logo、商品图片和文案做商用或公开演示如果是毕业设计或课程设计尽量在项目说明里写清楚“此项目仅为学习交流素材来源于网络或自绘不用于商业用途”如果涉及真实用户手机号、收货地址、订单数据必须说明数据存储和访问边界不能把真实用户信息随意公开后端接口上线时必须配置 HTTPS 合法域名不能使用明文传输敏感信息代购、刷单、虚假交易等行为一律不要出现在项目演示里。守住这几个边界这个项目用于学习和面试是没有问题的。3. 项目功能模块与页面结构整体功能模块可以分成两条线一条是用户端小程序另一条是后台管理系统。3.1 用户端小程序模块核心功能涉及页面首页轮播图、金刚区入口、推荐商品列表pages/home/home分类左侧分类导航右侧商品列表pages/category/category商品列表按关键词搜索、按分类筛选、排序pages/goods/list商品详情商品图片、价格、规格选择、加入购物车pages/goods/detail购物车商品勾选、数量加减、金额统计、删除pages/cart/cart订单确认收货地址、订单金额、提交订单pages/order/confirm订单列表订单状态展示、取消订单、确认收货pages/order/list个人中心用户信息、我的订单入口、收货地址管理pages/user/user登录微信授权登录获取 openid 后换取 tokenpages/login/login3.2 后台管理系统模块核心功能商品管理商品新增、编辑、上下架、删除、分页查询分类管理分类的新增、重命名、排序、删除订单管理订单查看、发货处理、订单状态更新用户管理用户列表查看一般不允许随意删除需要权限控制后台管理系统建议用 Vue3 Element Plus 或者 React Antd 来写数据接口和小程序共用同一套后端。这样你的项目亮点会明显很多因为“前后端一体”比“只写小程序”更有说服力。4. 环境准备与前置条件4.1 系统与软件要求操作系统Windows / macOS 均可微信开发者工具最新稳定版即可建议从微信公众平台官网下载小程序 AppID在微信公众平台注册小程序账号后在“开发管理 - 开发设置”里获取也可以使用测试号后端环境自建后端方案Node.js 16 或以上版本npm 或 yarnMySQL 或 MongoDB二选一Postman 或 Apifox 用于接口自测云开发方案不需要自建服务器在开发者工具里开通云开发创建环境即可。4.2 开发者工具关键配置打开项目之前确认三件事AppID 有没有填对是否端口开放允许开发者工具调用本地接口本地调试时是否勾选了“不校验合法域名、web-view业务域名、TLS 版本以及 HTTPS 证书”。操作路径微信开发者工具 - 详情 - 本地设置 - 勾选“不校验合法域名...”这个选项只用于本地开发真机上不能依赖它。真机预览时仍然要求接口是 HTTPS 合法域名。4.3 数据表设计建议无论用 MySQL、MongoDB 还是云数据库核心数据表建议拆分如下。商品表 goods字段类型说明idint / string主键namevarchar商品名称coverstring封面图pricedecimal价格stockint库存categoryIdint分类 IDstatusint1 上架0 下架createTimedatetime创建时间updateTimedatetime更新时间分类表 category字段类型说明idint主键namevarchar分类名称sortint排序值订单表 orders字段类型说明idint主键orderNovarchar订单号userIdint用户 IDtotalAmountdecimal总金额statusint0 待付款1 待发货2 待收货3 已完成addresstext收货地址快照createTimedatetime创建时间订单商品表 order_items字段类型说明idint主键orderIdint订单 IDgoodsIdint商品 IDgoodsNamevarchar商品名称快照pricedecimal购入价格快照countint数量注意“快照”两个字。订单里的商品名称、价格必须从商品表冗余一份到订单明细否则商品改名或改价后历史订单就乱套了。5. 项目初始化与运行方式5.1 小程序端初始化以原生小程序为例项目结构建议如下miniprogram/ ├── app.js ├── app.json ├── app.wxss ├── utils/ │ └── request.js ├── components/ │ ├── goods-card/ │ └── empty-view/ ├── pages/ │ ├── home/ │ ├── category/ │ ├── goods/ │ ├── cart/ │ ├── order/ │ └── user/在app.json里注册页面和底部 TabBar{ pages: [ pages/home/home, pages/category/category, pages/cart/cart, pages/order/list, pages/user/user ], window: { navigationBarTitleText: 小米商城, navigationBarBackgroundColor: #ff6700, navigationBarTextStyle: white }, tabBar: { color: #999999, selectedColor: #ff6700, list: [ { pagePath: pages/home/home, text: 首页 }, { pagePath: pages/category/category, text: 分类 }, { pagePath: pages/cart/cart, text: 购物车 }, { pagePath: pages/order/list, text: 订单 }, { pagePath: pages/user/user, text: 我的 } ] } }这是商城类小程序最基础的路由结构。实际项目中首页、分类、购物车、订单、个人中心五类 Tab 基本是标配。5.2 后端初始化Express 示例如果选择自建 Node.js 后端可以用 Express 快速搭建mkdir mishop-server cd mishop-server npm init -y npm install express mysql2 cors body-parser创建入口文件const express require(express); const cors require(cors); const bodyParser require(body-parser); const goodsRouter require(./routes/goods); const orderRouter require(./routes/order); const userRouter require(./routes/user); const app express(); app.use(cors()); app.use(bodyParser.json()); app.use(/api/goods, goodsRouter); app.use(/api/order, orderRouter); app.use(/api/user, userRouter); app.listen(3000, () { console.log(server running at http://localhost:3000); });启动命令node app.js如果使用云开发可以跳过自建后端直接在云函数里写接口逻辑微信端通过wx.cloud.callFunction调用。两种方案都能把这个项目跑起来区别是自建后端更贴近企业开发云开发更快。6. 核心功能实现思路6.1 小程序登录与登录态管理商城类小程序必须要登录核心流程是前端调用wx.login获取临时 code把 code 发给后端后端调用微信接口用 code 换取 openid后端生成自定义 token 返回给前端前端把 token 存入wx.setStorageSync后续请求带上 token。前端登录封装// utils/auth.js const login () { return new Promise((resolve, reject) { wx.login({ success: async (res) { if (res.code) { const { data } await request({ url: /api/user/login, method: POST, data: { code: res.code } }); wx.setStorageSync(token, data.token); wx.setStorageSync(userInfo, data.userInfo); resolve(data); } else { reject(new Error(获取 code 失败)); } }, fail: (err) reject(err) }); }); }; module.exports { login };这里的关键点在于不要把前端拿到的用户昵称头像直接存到数据库作为用户身份标识。微信官方已经调整了用户信息授权策略wx.getUserProfile不能作为长期登录凭证。正确做法是以后端拿到的 openid 作为用户唯一标识昵称头像只作为展示信息而且要通过“头像昵称填写能力”在用户主动触发时获取。6.2 首页商品数据加载首页是最能体现“数据驱动页面”的模块。轮播图、金刚区图标、推荐商品都建议通过接口返回而不是写死在前端代码里。首页加载逻辑Page({ data: { banners: [], categories: [], recommendList: [], loading: true }, onLoad() { this.fetchHomeData(); }, async fetchHomeData() { this.setData({ loading: true }); try { const res await request({ url: /api/home/index, method: POST, data: {} }); this.setData({ banners: res.data.banners, categories: res.data.categories, recommendList: res.data.recommendGoods }); } finally { this.setData({ loading: false }); } } });对应后端接口router.post(/index, async (req, res) { const [banners] await db.query(SELECT * FROM banner ORDER BY sort ASC); const [categories] await db.query(SELECT * FROM category ORDER BY sort ASC); const [recommendGoods] await db.query( SELECT * FROM goods WHERE status 1 ORDER BY createTime DESC LIMIT 10 ); res.json({ code: 0, data: { banners, categories, recommendGoods } }); });6.3 商品列表与搜索商品列表页需要支持分类筛选、关键词搜索、排序、分页加载。核心查询逻辑router.post(/list, async (req, res) { const { categoryId 0, keyword , page 1, pageSize 10, sort default } req.body; let where WHERE status 1; const params []; if (categoryId) { where AND categoryId ?; params.push(categoryId); } if (keyword) { where AND name LIKE ?; params.push(%${keyword}%); } let orderBy ORDER BY createTime DESC; if (sort price_asc) { orderBy ORDER BY price ASC; } else if (sort price_desc) { orderBy ORDER BY price DESC; } const offset (page - 1) * pageSize; const [rows] await db.query( SELECT * FROM goods ${where} ${orderBy} LIMIT ? OFFSET ?, [...params, pageSize, offset] ); const [[{ total }]] await db.query( SELECT COUNT(*) AS total FROM goods ${where}, params ); res.json({ code: 0, data: { list: rows, page, pageSize, total } }); });小程序端分页加载思路onReachBottom触发加载下一页用一个isLoading状态防止重复请求当已加载数量大于等于 total 时停止继续请求每次新数据追加到list后面而不是覆盖。6.4 购物车状态管理购物车是面试高频题核心是“单选/全选/数量修改/金额计算”。在小程序里购物车数据建议直接存本地 Storage同时同步到后端也可以完全走后端接口。这里给出本地状态计算的核心代码const state { cartList: [], selectedIds: [] }; // 计算选中商品总额 function calcTotal() { let total 0; state.cartList.forEach((item) { if (state.selectedIds.includes(item.id)) { total item.price * item.count; } }); return Math.round(total * 100) / 100; } // 修改数量 function changeCount(id, delta) { const index state.cartList.findIndex((item) item.id id); if (index -1) return; state.cartList[index].count delta; if (state.cartList[index].count 1) { state.cartList[index].count 1; } this.setData({ cartList: state.cartList, total: calcTotal() }); }比较讲究的做法是在app.js的 globalData 里维护购物车页面通过setData刷新。也可以用 MobX 或 Redux但小程序原生项目里建议先不引入复杂状态库把原生方法用熟后面接框架会更顺。6.5 订单提交流程订单创建的关键逻辑是“事务”。一定要保证下面几件事原子完成校验库存锁定或扣减库存创建订单主表记录创建订单商品明细清空购物车中对应的商品。以 Express MySQL 为例核心逻辑const conn await db.getConnection(); try { await conn.beginTransaction(); // 扣减库存 await conn.query( UPDATE goods SET stock stock - ? WHERE id ? AND stock ?, [count, goodsId, count] ); // 创建订单 const [orderResult] await conn.query( INSERT INTO orders (orderNo, userId, totalAmount, status, address) VALUES (?, ?, ?, ?, ?), [orderNo, userId, totalAmount, 0, JSON.stringify(address)] ); const orderId orderResult.insertId; // 批量创建订单明细 for (const item of goodsList) { await conn.query( INSERT INTO order_items (orderId, goodsId, goodsName, price, count) VALUES (?, ?, ?, ?, ?), [orderId, item.goodsId, item.goodsName, item.price, item.count] ); } await conn.commit(); res.json({ code: 0, data: { orderId, orderNo } }); } catch (e) { await conn.rollback(); res.json({ code: 500, message: 下单失败请稍后重试 }); } finally { conn.release(); }下单接口一定要做“幂等”处理。同一个请求重复提交不能创建两笔相同订单。常用方案是前端生成一个唯一的requestId后端用唯一索引兜底。7. 增删改查业务与后台管理系统设计7.1 什么是“增删改查”增删改查是后端接口最基础的能力缩写成 CRUDCCreate新增RRetrieve查询UUpdate更新DDelete删除。在小米商城这个项目里最常见的 CRUD 场景是商品管理操作接口方法接口路径新增商品POST/api/goods查询商品列表POST/api/goods/list查询商品详情GET/api/goods/:id更新商品PUT/api/goods/:id删除商品DELETE/api/goods/:id7.2 商品管理的后端实现商品新增router.post(/, async (req, res) { const { name, cover, price, stock, categoryId, status } req.body; if (!name || !price || stock undefined) { return res.json({ code: 400, message: 缺少必填字段 }); } await db.query( INSERT INTO goods (name, cover, price, stock, categoryId, status) VALUES (?, ?, ?, ?, ?, ?), [name, cover, price, stock, categoryId, status || 1] ); res.json({ code: 0, message: 新增成功 }); });商品更新router.put(/:id, async (req, res) { const { name, cover, price, stock, categoryId, status } req.body; await db.query( UPDATE goods SET name ?, cover ?, price ?, stock ?, categoryId ?, status ?, updateTime NOW() WHERE id ?, [name, cover, price, stock, categoryId, status, req.params.id] ); res.json({ code: 0, message: 更新成功 }); });商品删除router.delete(/:id, async (req, res) { await db.query(DELETE FROM goods WHERE id ?, [req.params.id]); res.json({ code: 0, message: 删除成功 }); });实际项目中商品“删除”很少物理删除更多是逻辑删除把status改为-1或deleted字段置 1。这样历史订单里的引用关系不会断裂。7.3 后台管理系统前端界面后台管理系统可以用 Vue3 Element Plus 来做商品管理页面大致包含搜索区按商品名称、分类搜索操作区新增商品按钮表格区商品名称、分类、价格、库存、上下架状态、操作列弹窗表单新增或编辑商品分页组件。表格操作列示例伪代码el-table :datagoodsList el-table-column propname label商品名称 / el-table-column propprice label价格 / el-table-column propstock label库存 / el-table-column label状态 template #default{ row } el-switch v-modelrow.status :active-value1 :inactive-value0 changeupdateStatus(row) / /template /el-table-column el-table-column label操作 template #default{ row } el-button sizesmall clickhandleEdit(row)编辑/el-button el-button sizesmall typedanger clickhandleDelete(row)删除/el-button /template /el-table-column /el-table这里的核心是“管理端和小程序端共用同一套商品数据”。小程序端看到的商品上下架状态取决于后台管理里设置的status字段。这就是一个非常直观的“前后端联动”项目讲解点。8. 接口请求封装与调试8.1 小程序请求封装建议写一个统一的request.js把 baseURL、token、错误处理都集中在里面。// utils/request.js const request function (options) { return new Promise((resolve, reject) { wx.request({ url: getApp().globalData.baseUrl options.url, method: options.method || GET, data: options.data || {}, header: { Content-Type: application/json, Authorization: wx.getStorageSync(token) || }, success(res) { if (res.statusCode 200) { if (res.data.code 0) { resolve(res.data); } else { wx.showToast({ title: res.data.message || 请求失败, icon: none }); reject(res.data); } } else if (res.statusCode 401) { // 登录态失效跳转登录页 wx.removeStorageSync(token); wx.reLaunch({ url: /pages/login/login }); reject(res.data); } else { reject(res.data); } }, fail(err) { wx.showToast({ title: 网络异常, icon: none }); reject(err); } }); }); }; module.exports request;在app.js中设置 baseUrlApp({ globalData: { baseUrl: https://your-api-domain.com/api } });本地开发时如果后端在本地运行可以暂时改为http://127.0.0.1:3000/api同时勾选开发者工具里的“不校验合法域名”。真机预览时本地 http 接口是连不通的必须用 HTTPS 域名。8.2 接口自测建议推荐使用 Apifox 或 Postman 自测所有接口重点验证参数缺失时的报错信息分页参数异常时的返回token 过期后的返回状态码并发下单时库存是否正确扣减。接口自测是很多初学会忽略的点。把接口测试用例整理好在面试时可以主动说明自己是怎么保证接口质量这是加分项。9. 常见问题与排查方法问题现象可能原因排查方式解决方案开发者工具打开项目空白AppID 未配置或项目路径不对查看控制台报错使用测试号或正式 AppID 重新导入本地接口请求失败network 报 500后端服务未启动或接口报错查看后端控制台日志先启动后端再用 Apifox 自测接口真机预览连不上本地接口报 net::ERR_CONNECTION_RESET真机不能访问电脑 localhost确认手机和电脑是否同网段真机调试必须使用 HTTPS 合法域名或使用云开发提示“url not in domain list”请求域名未配置到合法域名列表登录微信公众平台查看 request 合法域名在后台配置合法域名开发阶段可临时勾选“不校验合法域名”wx.login 拿到 code 但换不到 openid后端没有正确调用微信接口或 AppID/Secret 不匹配检查后端日志和参数确认 AppID 和 AppSecret 正确并在微信后台配置 IP 白名单调用 wx.getUserProfile 失败报 wx1cb4398e1413dce7用户取消授权或基础库版本过低或微信策略调整查看具体错误信息使用新版“头像昵称填写能力”不要强依赖 getUserProfile购物车总金额计算不对浮点数运算精度问题打印每一步计算结果金额统一用“分”为单位存储和计算展示时再除以 100数据库中文乱码数据表字符集不是 utf8mb4检查连接配置和数据表字符集创建表时指定 utf8mb4连接串加上 charsetutf8mb4下单时库存扣成负数没有做库存校验或没有用事务检查 UPDATE 语句是否有 stock count 条件使用条件更新并开启事务更新后判断受影响行数后台管理系统跨域请求失败后端没有开启 CORS查看浏览器 Network 报错后端中间件启用 cors或开发环境配置代理10. 毕设答辩与面试要点10.1 项目亮点怎么表达不要只说“我做了小米商城”要具体讲清楚“我负责小程序端全部页面和与后端联调后台管理端实现了商品的增删改查和上下架管理。”“登录模块采用 wx.login code 换取 openid后端生成 token 维护登录态解决了用户身份识别问题。”“商品列表支持分类筛选、关键词搜索、价格排序和分页加载列表页通过 onReachBottom 实现触底加载。”“购物车模块实现了单选、全选、数量增减和金额实时计算金额以分为单位避免浮点误差。”“订单模块使用数据库事务先扣库存再生成订单避免超卖和脏数据。”这些话术不是背出来而是建立在真的跑通代码的基础上。面试官很可能追问细节比如“分页加载怎么避免重复请求”“库存扣减失败怎么办”只有自己写过才能接得上。10.2 面试高频追问以下几个追问要提前准备小程序登录流程完整描述为什么不能直接把 openid 返回给前端或者说直接返回有没有风险购物车选中的商品刷新页面后还在不在商品列表分页时如果有新商品插入怎么避免列表重复或漏数据后台删除商品时如果该商品已经有订单怎么处理怎么保证并发下单时库存不超卖。这些问题的答案在这个项目里基本都能找到“库存条件 UPDATE 事务”就是最典型的超卖防法。10.3 面试时不要踩的坑不要在项目里写没有跑过的“亮点功能”比如支付模块如果没有真实应用就不要说“已实现微信支付”不要说“所有代码都是我写的”如果参考了开源项目要说明自己改进了哪些地方不要只展示页面截图要准备接口文档和数据库设计说明如果面试官问“为什么不直接在小程序里展示云端数据库的数据”要能解释前后端分离和权限控制的原因。11. 最佳实践与使用建议11.1 编码规范小程序状态数据统一走data声明不要直接给this.data赋值接口返回的金额用“分”存储前端展示时统一格式化页面公共样式提取到app.wxss组件样式用组件级wxss接口返回格式统一为{ code, message, data }结构请求统一走封装后的request.js不要每个页面都写wx.request。11.2 数据安全与权限控制后台管理接口必须做登录鉴权不能允许任何人直接调PUT /api/goods/:id修改商品小程序端openid和userInfo不要写入代码或前端存储中明文暴露如果项目要部署上线所有接口必须走 HTTPS删除类操作建议用逻辑删除防止关联数据出错用户手机号、收货地址是敏感信息非必要不要无条件返回给前端。11.3 开发流程建议推荐按下面这个顺序推进能很大程度减少返工先画数据库表结构和接口文档用 Apifox 把增删改查接口全部调通先做一个后台管理系统把商品数据录入再做小程序端首页和商品列表再做商品详情、购物车、订单流程最后做个人中心和登录态优化整体流程走通后再补细节和 UI 美化。把后台管理系统放在小程序前面是因为数据录入是前端页面的基础先把“数据源”准备好了再写用户端页面效率高很多。12. 总结与下一步小米商城小程序这个项目最适合的做法是“先跑起来再改造”。第一遍可以按照完整流程把登录、商品列表、购物车、订单、后台管理走通第二遍再根据自己的想法换一个垂直品类比如图书商城、数码配件商城、生鲜商城把商品字段、订单状态、首页装修换成自己的业务需求这样才能真正变成一个有个人辨识度的项目。最先应该验证的功能是“小程序端新增商品 - 后台列表刷出数据 - 修改库存 - 小程序详情页同步更新”这条链路它能把前后端联动的核心逻辑全面覆盖。最容易踩的坑集中在真机调试域名配置和下单事务这两块建议提前用测试号把流程完整跑一遍。如果按这套思路做完这个项目里积累下来的登录鉴权、列表分页、购物车计算、后台 CRUD、订单事务这些能力完全可以迁移到其他业务系统里这才是这个项目最大的价值。
返回列表