首页 / 资讯中心 / 文章详情

Vue 3 网上商城会员积分购物系统开发实战

Vue 3 网上商城会员积分购物系统开发实战 ★ FEATURED ARTICLE
1. 项目定位与核心需求解析先说说我为什么会把这个项目拿出来单独写一篇。市面上做商城会员积分系统的教程不少但绝大多数还停留在Vue 2时代要么就是前端展示型的半成品真正能拿来做会员积分运营后台、能上线的项目确实不多。这个项目标题很明确——用 Vue 3 重写网上商城的会员积分购物体系核心词有三个:网上商城、会员积分、购物系统技术栈锁定 Vue 3。拆开来看这三个词其实是三层需求。网上商城是业务载体它决定了整个系统要覆盖商品展示、购物车、订单流程;会员积分是运营抓手它要解决积分怎么产生、怎么消耗、怎么防止刷分的问题;购物系统是底层逻辑它要求前后端能完成真实交易闭环不是做个静态页面就完事。对于正在学 Vue 3 的人、准备做毕业设计的学生、或者小团队想快速搭一套积分商城后台的开发者这个项目都是很好的练手载体因为它把 Vue 3 的响应式、状态管理、路由守卫、组件通信全部串起来了而不是一个个孤立的知识点。我用了大约一周时间把这个系统完整跑通前端用的 Vue 3 Vite Pinia Vue Router Element Plus后端用 Node.js 提供了一套模拟接口。整个过程踩了不少坑尤其是积分结算的并发问题、Token 过期后的页面跳转逻辑、以及 Vue 3 组合式 API 下状态管理的组织方式。这篇就把完整思路和实操过程记录下来大家可以直接照着搭。2. 技术选型思路与整体架构2.1 为什么选 Vue 3 而不是 Vue 2这个问题几乎每个做商城项目的人都会问。我的回答很直接新项目只要没有遗留下来的老代码包袱就直接上 Vue 3。Vue 3 的 Composition API 让逻辑复用变得干净了很多以前在 Options API 里写 mixin 混入多个 mixin 一叠加数据来源都搞不清楚。现在用 setup 函数加组合式函数一个积分模块的功能可以完整封装成一个 usePoints 函数哪里需要就在哪里调用代码可读性和维护性都提升了一个档次。另一个实际原因是生态已经成熟。Element Plus、Vant 4 这些 UI 组件库对 Vue 3 的支持已经很稳定;Pinia 比 Vuex 更轻量TypeScript 支持也更好——虽然这个项目我用的是 JavaScript但后续想迁移到 TSPinia 的迁移成本远低于 Vuex。对于商城这种状态多、页面多的项目选一个生态成熟、性能更好的框架是值得的。2.2 技术栈全景图这个系统的技术栈可以分为四层构建工具Vite 4.x。开发服务器启动速度快热更新几乎是秒级响应相比 Webpack 在开发体验上提升非常明显。我用 Vite 配置了路径别名、开发代理、自动导入 API这些功能让我在配置上省了不少时间。前端框架Vue 3.4 组合式 API。全项目使用 script setup 语法配合 reactive、ref、computed、watch 等响应式 API 完成数据管理。组合式函数use 系列是这次项目复用的核心方式。状态管理与路由Pinia Vue Router 4。Pinia 负责管理用户信息、购物车、积分明细等全局状态Vue Router 4 负责页面跳转和路由守卫主要处理登录鉴权、购物车结算页面的访问控制。UI 组件库Element Plus按需引入。表格、表单、弹窗、消息提示这些高频组件全部从 Element Plus 中按需引入项目打包体积相比全量引入减少了约 40%。后端接口我用的是 Node.js 提供的模拟数据服务用 JSON 文件模拟数据库存储。这样做的好处是前端开发完全不受后端进度影响接口格式定了就能并行开发。如果大家想使用真实后端替换掉接口层即可前端代码逻辑基本不用改动。2.3 目录结构与模块划分项目目录结构是这样的src/ ├── api/ # 接口请求封装 │ ├── goods.js # 商品相关接口 │ ├── cart.js # 购物车接口 │ ├── order.js # 订单接口 │ └── user.js # 用户与积分接口 ├── components/ # 通用组件 │ ├── GoodsCard.vue # 商品卡片 │ ├── PointsBadge.vue # 积分标识 │ └── OrderStatus.vue # 订单状态标签 ├── composables/ # 组合式函数 │ ├── usePoints.js # 积分计算逻辑 │ └── useCart.js # 购物车逻辑 ├── router/ # 路由配置 ├── stores/ # Pinia 状态管理 │ ├── user.js # 用户状态 │ ├── cart.js # 购物车状态 │ └── order.js # 订单状态 ├── views/ # 页面组件 │ ├── Home.vue # 商城首页 │ ├── GoodsDetail.vue # 商品详情 │ ├── Cart.vue # 购物车 │ ├── OrderConfirm.vue # 订单确认 │ ├── OrderList.vue # 订单列表 │ ├── PointsMall.vue # 积分商城 │ └── Login.vue # 登录/注册 └── utils/ # 工具函数 ├── request.js # axios 封装 └── auth.js # token 管理这个结构是实践多次之后确定的。把接口请求独立成 api 目录页面组件只负责渲染和交互业务逻辑尽放在 composables 里通用状态放 stores这样职责划分清晰。一个页面该调哪些接口、该用哪些状态一目了然。3. 会员积分体系设计规则、计算与防刷策略3.1 积分获取与消耗规则设计积分体系是整个系统的运营核心。在设计积分规则时我参考了主流电商平台的通用做法并结合这个项目的定位做了一些调整场景积分变化规则说明注册成功100新用户注册奖励一次性每日登录5每天首次登录赠送上限 5 分/天商品下单商品实付金额(取整)每消费 1 元得 1 积分不含运费订单评价20确认收货后 7 天内评价有效积分兑换商品-兑换所需积分按商品积分价格扣除积分抵扣现金-抵扣积分100 积分 1 元订单结算时使用这些规则在数据层面就是一张积分流水表记录积分变动明细包括变动类型、变动数量、剩余积分、关联订单号、创建时间。用户页面上显示的积分余额是流水表中所有明细的累计值。这里有一个设计细节需要注意不建议直接在用户表里存一个积分余额字段然后每次修改它因为一旦出现并发操作余额很容易出错而且追溯不到积分来龙去脉。用流水表存储每次查询时实时累计虽然多了一点计算开销但对账清晰用户信任度更高。3.2 积分计算与前端实现积分计算的关键场景是订单确认页用户可以选择是否使用积分抵扣。抵扣规则是 100 积分抵 1 元但有一些限制条件积分抵扣金额不得超过订单实付金额的 50%抵现金额需为 1 元的整数倍不足 100 积分的部分不能抵扣每个订单最多使用 5000 积分。在 Vue 3 中我封装了一个 usePoints 组合式函数来统一处理这些逻辑核心代码是这样写的// src/composables/usePoints.js import { ref, computed } from vue export function usePoints(orderAmount, userPoints) { const EXCHANGE_RATE 100 // 100积分 1元 const MAX_RATE 0.5 // 最多抵扣50% // 用户选择的抵扣积分数量 const selectedPoints ref(0) // 可抵扣的最大积分受订单金额和用户积分双重限制 const maxUsablePoints computed(() { const maxByAmount Math.floor(orderAmount.value * MAX_RATE * EXCHANGE_RATE) return Math.min(maxByAmount, userPoints.value, 5000) }) // 实际抵扣金额取整到1元 const discountAmount computed(() { return Math.floor(selectedPoints.value / EXCHANGE_RATE) }) // 最终应付金额 const finalAmount computed(() { const amount orderAmount.value - discountAmount.value return Math.max(amount, 0) }) // 选择积分时自动限制范围 function selectPoints(points) { if (points maxUsablePoints.value) { selectedPoints.value maxUsablePoints.value } else if (points 0) { selectedPoints.value 0 } else { selectedPoints.value Math.floor(points) } } return { selectedPoints, maxUsablePoints, discountAmount, finalAmount, selectPoints } }这个设计把积分抵扣的核心逻辑抽离成独立模块页面组件只需要调用这个函数并传入订单金额和用户积分不用关心内部计算规则。以后如果调整积分抵扣比例只需要改这一个文件就行。3.3 防刷策略与前后端协同积分防刷是运营层面必须考虑的问题。虽然前端可以做很多限制但防刷的最终防线一定要放在后端。我在项目里做了两层防护第一层是频率控制。每日登录积分通过后端接口控制同一用户每天只能获取一次下单积分在订单状态变为已完成时发放防止退款订单套取积分。第二层是异常检测。同一 IP 在短时间内注册大量账号会被后端记录并标记异常。前端层面我们要做的不是拦截而是体验引导。比如每日登录积分用户登录后前端调用积分签到接口页面展示今日已签到状态避免用户重复点击。再比如下单积分在订单完成后会自动发放前端通过订单详情页展示积分到账提示用户能清楚看到积分从哪来。有一点需要注意不要在前端依赖一个当前积分余额做复杂的抵扣计算。用户在其他设备或端上可能已经消费了积分前端显示的余额可能是过期的。正确做法是订单确认页进入时请求最新积分余额提交订单时后端再次校验积分是否够用、是否符合抵扣规则以服务端校验结果为准。前端在 getLatestUserInfo 中拉取最新积分数据向用户展示参考抵扣金额。这种前端展示、后端校验的模式能避免很多积分数据不一致的问题。3.4 积分明细页的实践积分明细页我用了 Element Plus 的 el-table 组件配合 el-pagination 做分页。为了提升查询性能前端只加载当前页的积分记录翻页时重新请求。每一行展示积分变动类型、变动数量正负号区分增加/消耗、变动后余额、关联订单号、变动时间。表格加了 el-tag 标签区分获取和消耗两种类型方便用户快速识别。这里有一个很细节的踩坑点积分数量的正负号显示。我在数据层面用一个 amount 字段增加是正数、消耗是负数展示时直接渲染带符号的数字。但后端返回的字段名如果叫points前端容易混淆正负含义所以接口设计时我建议统一用积分变动amount或者积分变更change作为字段名值正负即代表方向这样代码语义更清晰。4. 商城购物主流程的 Vue 3 实现4.1 商品列表与详情页的响应式设计商品列表页是整个商城的前门用户进来看得舒不舒服、逛得顺不顺手直接影响转化率。我在技术上用 Vue 3 的 ref 和 computed 实现了商品数据的响应式管理搭配 Element Plus 的卡片组件完成展示。商品列表通过 api/goods.js 中的 getGoodsList 接口获取返回数据结构是这样的[ { id: 1, name: 无线蓝牙耳机, price: 299, originalPrice: 399, points: 299, // 赠送积分 stock: 120, image: /images/headset.jpg, category: 数码, sales: 234 } ]列表页的筛选功能我保留了价格排序和分类筛选两个维度。实现方式是定义筛选条件对象 filter通过 computed 计算最终展示的商品列表const filter ref({ category: 全部, sortBy: default }) const goodsList ref([]) const filteredList computed(() { let list goodsList.value if (filter.value.category ! 全部) { list list.filter(item item.category filter.value.category) } if (filter.value.sortBy priceAsc) { list [...list].sort((a, b) a.price - b.price) } else if (filter.value.sortBy priceDesc) { list [...list].sort((a, b) b.price - a.price) } else if (filter.value.sortBy sales) { list [...list].sort((a, b) b.sales - a.sales) } return list })这里有个性能上的小考量如果商品数量极少用 computed 同步筛选没问题但如果商品数量很大比如几千条建议把筛选放到后端做前端只传参数这样页面性能更好。我这个项目模拟了 30 个商品同步计算完全没有压力。商品详情页单独拆了一个路由 /goods/:id通过路由参数获取商品 ID再调用接口拿详情。详情页里展示了商品大图、价格、积分赠送信息、库存状态、购买数量选择器。用户选择数量后直接加入购物车或者立即购买跳转到结算页。商品详情页中有一个交互细节值得说。库存不足时加入购物车按钮应该置灰并提示库存不足避免用户下单后才发现超卖。Vue 3 里实现很简单用计算属性判断 selectedQuantity 是否大于 stock动态绑定按钮的 disabled 状态同时给出 el-tooltip 提示。这个小细节让整个购买流程顺畅不少也减少了订单异常的数量。4.2 购物车模块Pinia 状态管理与持久化购物车是商城项目中最能体现状态管理价值的部分。用户在不同页面之间跳转首页→详情页→购物车→结算页购物车数据必须在所有页面共享而且刷新页面不能丢。项目使用 Pinia 定义购物车 store核心实现如下// src/stores/cart.js import { defineStore } from pinia export const useCartStore defineStore(cart, { state: () ({ items: [] }), getters: { totalCount: (state) state.items.reduce((sum, item) sum item.count, 0), totalPrice: (state) state.items.reduce((sum, item) sum item.price * item.count, 0), selectedCount: (state) state.items .filter(item item.selected) .reduce((sum, item) sum item.count, 0), selectedPrice: (state) state.items .filter(item item.selected) .reduce((sum, item) sum item.price * item.count, 0) }, actions: { addToCart(goods, count 1) { const existing this.items.find(item item.goodsId goods.id) if (existing) { existing.count count } else { this.items.push({ goodsId: goods.id, name: goods.name, price: goods.price, image: goods.image, points: goods.points, stock: goods.stock, count, selected: true }) } }, removeFromCart(goodsId) { const index this.items.findIndex(item item.goodsId goodsId) if (index -1) { this.items.splice(index, 1) } }, toggleSelect(goodsId) { ... }, toggleSelectAll() { ... }, updateCount(goodsId, count) { ... }, clearSelected() { this.items this.items.filter(item !item.selected) } } })Pinia 的 store 默认不持久化刷新页面 state 会重置为空。为了解决这个问题我使用了 Pinia 的插件 pinia-plugin-persistedstate。这个插件可以把 store 中的 state 自动存储到 localStorage 中刷新页面后自动恢复。不过这里有一个安全性的坑要提醒大家购物车数据存在 localStorage 里用户可以手工修改比如把商品单价改成 0.01 元或者把数量改成负数。所以购物车里的商品价格只能作为展示参考真正结算时后端必须重新计价从数据库查最新价格计算总价。前端传过去的商品 ID 和数量只是告诉后端用户想买什么价格以后端为准。购物车页面我用 el-table 来展示商品明细每一行包含商品信息、单价、数量、小计、操作。数量修改用 el-input-number 组件设置最小值为 1、最大值不超过库存。当用户点击结算按钮时先判断是否登录。未登录则跳转登录页并记录用户来源路由登录成功后再跳回购物车。这个记录来源路由再跳回的逻辑在 Vue Router 4 里实现起来很简单// 跳转登录时携带 redirect 参数 router.push({ path: /login, query: { redirect: /cart } }) // 登录成功后跳回 const redirect route.query.redirect || / router.push(redirect)4.3 订单确认与积分结算联动订单确认页是整个系统中逻辑最复杂的一页它要同时处理收货地址、商品清单、积分抵扣、运费计算、支付方式五个模块。在 Vue 3 中我用了 reactive 来管理整个订单表单的数据const orderForm reactive({ addressId: , goodsItems: [], // 从购物车带过来的商品 usePoints: false, // 是否使用积分抵扣 selectedPoints: 0, // 使用积分数 paymentMethod: wechat, // 支付方式 remark: })页面加载时做三件事请求默认收货地址、请求购物车中已选中的商品数据、请求用户最新积分余额。这三个请求我用 Promise.all 并行发送让页面尽快展示完整信息。不过地址接口挂了也不能阻塞整个页面所以错误处理上分开捕获地址请求失败时给出提示并允许用户稍后重试商品和积分数据是核心必须成功才能继续。订单金额的计算逻辑放在一个 computed 属性中const orderAmount computed(() { const goodsTotal cartStore.selectedPrice const discount pointsModule.discountAmount.value // 积分抵扣金额 const shipping goodsTotal 99 ? 0 : 10 // 满99包邮 return goodsTotal - discount shipping })显示上给用户一个积分抵扣的开关el-switch开启后显示当前可用积分和可抵扣最大金额让用户自己输入要使用的积分数量。输入时实时显示预计抵扣 XX 元提升交互感知。提交订单时调用创建订单接口同时传递商品列表快照、收货地址 ID、使用积分数、支付方式、用户备注。后端创建订单后返回订单号前端清空购物车中已选商品再跳转到订单详情页或收银台。实践中需要注意一个问题商品价格在用户浏览商品和提交订单之间可能发生变化。比如用户把一个商品放购物车放了三天管理员调了价格。所以提交订单接口返回的应该是以服务端最新价格计算出的订单金额前端展示的金额只是参考。如果服务端价格有变动建议在创建订单接口的响应中带上价格变动提示前端弹出确认框让用户确认后再支付。4.4 订单列表与状态流转订单列表页要展示全部订单并按状态筛选。系统的订单状态流转如下待付款 → 待发货 → 待收货 → 已完成 │ │ └─ 已取消 ┘每个状态对应一个操作按钮待付款显示去支付和取消订单按钮待发货显示提醒发货按钮这个做的是前端模拟待收货显示确认收货按钮已完成显示评价按钮和再次购买按钮已取消无操作按钮订单列表页我用了两个组件组合实现顶层是状态筛选的 el-radio-group下面是订单卡片列表。每个订单卡片用折叠面板展示订单商品、金额信息、状态标签和操作按钮。确认收货时有一个交互细节点击确认收货后不要立刻修改状态而是弹出确认框用户确认后再调接口。因为确认收货在业务上意味着订单完成、积分即将发放如果设置了评价后积分则收货后还需评价才发放这是一个不可逆操作误触会造成麻烦。用 ElMessageBox.confirm 做二次确认成本很低但体验提升明显。4.5 登录鉴权Token 管理与路由守卫商城系统几乎全部页面都需要登录才能访问。我使用 Token 机制管理登录状态前端在 localStorage 中保存 Token每次请求时通过 axios 拦截器自动附加 Token 到请求头// src/utils/request.js import axios from axios import { ElMessage } from element-plus import router from ../router const request axios.create({ baseURL: /api, timeout: 5000 }) // 请求拦截器 request.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers.Authorization Bearer ${token} } return config }) // 响应拦截器 request.interceptors.response.use( response response.data, error { if (error.response error.response.status 401) { // Token 过期或无效 localStorage.removeItem(token) localStorage.removeItem(userInfo) router.push({ path: /login, query: { redirect: router.currentRoute.value.fullPath } }) } ElMessage.error(error.response?.data?.message || 请求失败) return Promise.reject(error) } ) export default request这里有一个关键细节401 处理时跳转到登录页要带 redirect 参数记录当前要访问的页面这样重新登录后可以回到原来的页面而不是回到首页避免用户购物流程被打断。路由守卫用于前端访问控制// src/router/index.js router.beforeEach((to) { const token localStorage.getItem(token) if (to.meta.requiresAuth !token) { return { path: /login, query: { redirect: to.fullPath } } } if (to.path /login token) { return / } })这个写法是 Vue Router 4 推荐的简洁风格直接在返回值中指定跳转目标。注意不需要在守卫里手动调 next()除非有异步逻辑。另外 Vue Router 4 中使用组合式 API 时可以在组件里用 useRoute 和 useRouter 获取路由对象替代 Options API 中的 this.$route 和 this.$routerimport { useRoute, useRouter } from vue-router const route useRoute() const router useRouter()5. 项目落地过程中的经验与避坑指南5.1 Element Plus 按需引入的正确姿势Element Plus 官方文档推荐使用自动按需导入的方式配合 unplugin-auto-import 和 unplugin-vue-components 两个插件。实践中的配置如下// vite.config.js import { defineConfig } from vite import vue from vitejs/plugin-vue import AutoImport from unplugin-auto-import/vite import Components from unplugin-vue-components/vite import { ElementPlusResolver } from unplugin-vue-components/resolvers export default defineConfig({ plugins: [ vue(), AutoImport({ resolvers: [ElementPlusResolver()] }), Components({ resolvers: [ElementPlusResolver()] }) ] })配置完成以后页面模板里直接使用 el-button、el-table 等组件import 和注册过程都由插件自动完成。唯一要留意的是 ElMessage 这类函数式组件自动导入插件默认也会帮你在使用时自动导包但如果遇到个别组件没有生效则手动在使用的文件里导入一下即可。ElMessage、ElMessageBox、ElNotification这三个是命令式 API不是模板组件。如果自动导入没有生效就在 src/utils/request.js 这种使用频繁的文件里手动导入一次最保险。我在实际项目中遇到过配了自动导入但 ElMessageBox 还是报未定义的情况后来定位到是插件版本不一致导致的把 Resolver 配置统一后解决。5.2 组合式 API 的逻辑复用实践这个项目里我用了三个自定义组合式函数usePoints积分计算逻辑useCart购物车操作逻辑购物车数量增减、选中、总价计算useCountdown秒杀倒计时逻辑用于每日积分签到倒计时展示以 useCart 为例它的价值在于把加入购物车的操作从页面组件中抽离出来。如果多个页面商品列表、详情页、积分商城都有加入购物车入口每个页面都重复写一套 find/update/push 逻辑代码冗余不说还容易出现遗漏。抽成组合式函数后每个页面只需要一行调用const { addToCart } useCart() addToCart(goods, 2)组合式函数和 Pinia store 的区别在于useCart 内部可能调用 cartStore 的 action但它同时可以封装一些非全局状态比如本地缓存操作、带参数的调用预处理。实际项目中我倾向于把跨页面共享的数据放 Pinia把页面内可复用的逻辑函数放 composables。5.3 Vue 3 与 Vue 2 差异速查这个项目如果用 Vue 2 来做几个地方差异会很大。我整理了一个对照表帮助从 Vue 2 转过来的朋友快速适应对比维度Vue 2Vue 3状态管理Vuex 4Options 风格Pinia更轻量Composition 友好数据响应式Object.definePropertyProxy支持更丰富的数据类型逻辑复用mixin有数据来源混乱问题组合式函数逻辑清晰模板支持单根节点多根节点Fragment异步组件new Component importdefineAsyncComponent过滤器Vue.filter不再支持用 computed 或函数替代全局 APIVue.use、Vue.prototype.$xxapp.use、app.config.globalProperties5.4 项目性能优化实践首屏加载优化是商城项目必须面对的课题。我这个项目优化前首屏 JS 包约 1.2MB通过以下措施优化到了约 500KB第一路由懒加载。将每个页面拆成独立的 JS chunk只有访问时才加载const Home () import(../views/Home.vue) const Cart () import(../views/Cart.vue) const OrderConfirm () import(../views/OrderConfirm.vue)第二Element Plus 按需引入。前面已经做了效果是组件库从全量引入的 2.6MB 降到按需的约 300KB。第三图片懒加载。商品列表中的图片资源全部用 el-image 组件的 lazy 属性实现懒加载页面中非视口区域的图片不会立即请求减少了初始网络请求数量。第四列表虚拟滚动。订单列表页如果订单数量很多比如超过 200 条建议引入虚拟滚动组件。Element Plus 目前没有提供虚拟列表可以使用 vxe-table 或者虚拟滚动插件。这个项目订单数量很少所以没做这一步但对真实商城场景来说是必须考虑的。5.5 常见问题与排查清单速查我整理了开发过程中最容易踩的几个坑按症状、原因、解法列出来症状原因解决方法npm run dev 后浏览器打开空白Vite 服务端口被占用或路径配置错误检查 vite.config.js 中的 server.port 和 base 配置Element Plus 组件样式不生效未引入组件样式或自动按需导入配置错误检查 Resolver 配置必要时手动引入样式文件Pinia store 数据刷新后丢失未配置持久化插件使用 pinia-plugin-persistedstate 或手动在 store 中存取 localStorage登录后跳回页面一直是首页路由守卫中 redirect 处理不当检查登录成功后是否读取了 route.query.redirect用 Vuex 思维写 Pinia 报错概念混用Pinia 的 store 是响应式对象理解 Pinia 的 storeToRefs、defineStore 用法el-table 数据更新后表格不刷新数组直接替换引用的方式不当使用响应式 API 更新数据如 this.items.splice 或 this.items[i] newItem还有一类问题是为何成功了却没有积分这类业务逻辑问题。排查思路是先看接口返回状态再看订单流转——是待付款、待发货还是已完成积分发放的触发点是什么是否满足条件建议在开发环境打印完整的接口调用链路比对数据库中的积分流水记录就能快速定位是哪个环节断了。5.6 项目扩展方向系统跑通后可以继续往几个方向扩展接入真实支付接口微信支付、支付宝、使用 WebSocket 做订单状态实时推送、增加积分商城单独频道、接入优惠券系统、做移动端适配等。以积分商城为例当前积分只能抵扣现金实际上还可以增加纯积分兑换商品的功能即某些商品只能用积分换、不能用现金买。这种模式在运营上能刺激用户积累积分从技术层面来说只需要在商品表加一个 pointsPrice 字段积分商城页筛选 pointsPrice 不为空的商品订单类型识别为积分兑换即可。前端改动不大业务效果却很明显。支付接口接入是一个敏感的环节真实项目中要重点处理回调验签、订单幂等、掉单查询。如果大家做的是毕设项目模拟支付流程即可后端标记支付成功前端展示支付结果页就好。最后再分享一个小技巧。做这类商城系统最忌讳一上来就写代码。我通常在动手前先在纸上画一遍用户操作路径注册登录→逛商品→加购物车→结算→用积分抵扣→下单→确认收货→积分到账→再逛。把这条路径走通了代码就是把这个流程翻译成技术实现。很多人写完商城项目说不清业务逻辑往往就是第一步没做扎实。如果遇到调试时积分数量一直对不上强烈建议在前端加一个积分明细的调试面板把每次积分变动的流入流出打印出来。这个面板既不丢人还能帮你快速定位是计算逻辑错、接口返回错还是状态被覆盖了排查效率能提升好几倍。
阅读完成 · 觉得有帮助?
咨询建站