ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

基于SpringBoot+Vue3与微信小程序的运动户外电商系统解析

基于SpringBoot+Vue3与微信小程序的运动户外电商系统解析 运动户外类商品和普通标品电商最大的区别在于规格、库存和配送维度要复杂得多。冲锋衣有颜色和尺码露营帐篷有不同的容量、重量登山杖还有长度和节数每一条 SKU 都对应独立价格和库存。要把这些商品真正放进小程序里形成交易闭环不能只做一个静态商品展示页还需要打通用户登录、购物车、订单状态、后台管理这一整条链路。基于 SpringBoot Vue3 微信小程序实现的运动户外交易电商系统正是围绕这条链路展开的多端协作项目后端统一提供商品、订单、用户服务Vue3 端承担运营后台和平台管理小程序端面向 C 端用户完成浏览、加购、结算和订单查看。下面直接从可运行源码的角度拆解这类系统从数据库到接口、再到前后端联调的全过程。1. 先理清运动户外商城系统的整体形态1.1 三端职责与源码目录的对应关系很多初学者以为“商城系统”就是一个小程序目录加一个后端接口目录实际项目里通常要拆成三部分职责差异很大。端技术方向面向对象核心职责Spring Boot 后端Spring Boot MyBatis-Plus MySQL Redis所有前端端用户登录、商品查询、购物车、订单、后台管理接口Vue3 管理后台Vue3 Vite Element Plus运营人员、管理员商品编辑、上下架、订单发货、数据查看微信小程序端原生微信小程序或 uni-app普通消费者浏览商品、加购物车、下单、支付、订单查看源码目录可以这样组织outdoor-mall/ ├── mall-server/ # Spring Boot 后端 ├── mall-admin/ # Vue3 管理后台 └── mall-weapp/ # 微信小程序端还有一个容易混淆的问题标题中同时出现“SpringBoot4”“Vue3”和“小程序”前端部分究竟怎么分工需要提前确认技术路线。路线 A管理后台使用 Vue3小程序使用原生微信小程序。这种方案后端完全不用关心小程序端是否使用 VueVue3 只负责 Web 管理界面。路线 B管理后台使用 Vue3小程序也通过 uni-app 使用 Vue3 语法开发。这样两个前端工程在代码风格上更接近组件和 API 也能部分复用代价是多引入一层编译框架。1.2 一条完整交易主流程要经历哪些环节从用户视角看运动户外电商的典型使用路径是打开小程序进入首页浏览商品 - 按分类筛选露营、徒步、骑行装备 - 进入商品详情选择颜色尺码 - 加入购物车 - 在购物车中勾选商品提交订单 - 模拟支付或对接微信支付 - 后台看到新订单并发货 - 用户查看物流状态并确认收货。后台管理员的路径则是登录 Vue3 管理后台 - 维护商品分类 - 新增或编辑商品 - 上传商品主图和详情图 - 配置 SKU 价格与库存 - 上架商品 - 在订单列表中对新订单进行发货处理 - 处理用户售后或退款。运动户外交易系统的技术难点不在某个页面有多炫而在于商品规格、库存扣减和订单状态流转必须保持准确。一次并发下单导致超卖就可能在售后服务阶段造成大量成本。1.3 功能模块先从六张核心表开始拆从后端设计角度看商品、用户、购物车、订单四个域是必需的后台管理员又是一套独立身份。按这个思路项目功能模块可以拆成用户模块微信授权登录、收货地址、用户状态。分类与商品模块户外商品分类、商品 SPU、SKU 规格、库存。购物车模块加入购物车、勾选结算、修改数量。订单模块订单主表、订单明细、状态流转。后台管理员模块管理员登录、商品管理、订单发货。公共服务模块文件上传、统一异常、接口鉴权。模块边界越清晰后期扩展评价系统、优惠券、分销等功能时就越不容易污染已有表结构。2. 环境准备与运动户外商城数据表设计2.1 推荐开发环境与版本组合这类项目在不同版本的 JDK、Spring Boot、Node 和小程序基础库上表现差异很大建议先按一张表格把环境固定下来。工具推荐版本或替代方案说明JDK17 或更高Spring Boot 3 起要求 JDK 17Maven3.8管理后端依赖MySQL8.0字符集使用 utf8mb4Redis6.x / 7.x缓存热点商品、可选的登录态辅助Node.js18运行 Vite 管理后台微信开发者工具最新稳定版编译预览小程序小程序 AppID正式 AppID 或测试号登录 openid 需要真实 AppID关于 SpringBoot4 需要单独说明一点标题中的框架命名可以直接写成“SpringBoot4”系列但落地时要以 Maven 中央仓库能够拉取到的稳定版本为准。如果当时没有 4.x 正式版退回 Spring Boot 3.x 稳定线并不会影响这套项目设计因为表结构、REST 接口、MyBatis-Plus 用法在 3 到 4 之间不会出现颠覆性变化。真正容易踩坑的是 Redis 配置前缀从spring.redis改成spring.data.redis以及命名空间从javax调整为jakarta。2.2 运动户外商城最核心的八张表把普通商城简化后至少需要下面这些表。这里的重点不是列一大堆字段而是解释每个表在企业项目中为什么存在。表名存储内容设计要点t_admin_user后台管理员与 C 端用户分开避免混在一个用户体系t_user小程序用户使用微信 openid 作为唯一登录标识t_category商品分类运动户外类目层级较浅先保留一级分类t_product商品 SPU商品公共信息例如名称、主图、详情t_product_sku商品 SKU颜色尺码规格价格和库存都挂在 SKU 上t_cart购物车以用户和 SKU 为唯一组合t_order订单主表保存收货人和订单总额状态放在主表t_order_item订单明细一个订单对应多个 SKU下单后不允许被商品修改影响以运动户外场景为例一件冲锋衣在 t_product 中只保存“三合一冲锋衣”这个 SPU但它在 t_product_sku 中会拆成“黑色-M”“黑色-L”“红色-M”等多条记录。这样用户加购时选择的是具体 SKU结算价格也能精确到规格级别。2.3 建表 SQL 可以直接用于初始化CREATE TABLE t_admin_user ( id BIGINT NOT NULL AUTO_INCREMENT COMMENT 主键, username VARCHAR(50) NOT NULL COMMENT 登录名, password VARCHAR(100) NOT NULL COMMENT BCrypt密文, real_name VARCHAR(50) DEFAULT NULL COMMENT 真实姓名, status TINYINT DEFAULT 1 COMMENT 1启用 0禁用, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT后台管理员表; CREATE TABLE t_user ( id BIGINT NOT NULL AUTO_INCREMENT COMMENT 主键, openid VARCHAR(64) DEFAULT NULL COMMENT 微信openid, unionid VARCHAR(64) DEFAULT NULL COMMENT 微信unionid需要开放平台才有, nickname VARCHAR(64) DEFAULT NULL COMMENT 昵称, avatar VARCHAR(255) DEFAULT NULL COMMENT 头像地址, phone VARCHAR(20) DEFAULT NULL COMMENT 手机号, status TINYINT DEFAULT 1 COMMENT 1正常 0禁用, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_openid (openid) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT小程序用户表; CREATE TABLE t_category ( id BIGINT NOT NULL AUTO_INCREMENT, name VARCHAR(50) NOT NULL COMMENT 分类名称, parent_id BIGINT DEFAULT 0 COMMENT 父级分类0表示一级, sort INT DEFAULT 0 COMMENT 排序值越小越靠前, status TINYINT DEFAULT 1 COMMENT 1启用 0停用, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT商品分类表; CREATE TABLE t_product ( id BIGINT NOT NULL AUTO_INCREMENT, category_id BIGINT NOT NULL COMMENT 分类ID, name VARCHAR(100) NOT NULL COMMENT 商品名称, sub_title VARCHAR(255) DEFAULT NULL COMMENT 副标题, main_image VARCHAR(255) DEFAULT NULL COMMENT 主图, detail TEXT COMMENT 富文本详情, status TINYINT DEFAULT 0 COMMENT 0下架 1上架, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_category (category_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT商品SPU表; CREATE TABLE t_product_sku ( id BIGINT NOT NULL AUTO_INCREMENT, product_id BIGINT NOT NULL COMMENT SPU ID, sku_name VARCHAR(100) DEFAULT NULL COMMENT 规格描述如黑色-L, price DECIMAL(10,2) NOT NULL COMMENT 销售价, stock INT NOT NULL DEFAULT 0 COMMENT 库存, sales INT DEFAULT 0 COMMENT 销量, status TINYINT DEFAULT 1 COMMENT 1启用 0停用, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_product (product_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT商品SKU表; CREATE TABLE t_cart ( id BIGINT NOT NULL AUTO_INCREMENT, user_id BIGINT NOT NULL, product_id BIGINT NOT NULL, sku_id BIGINT NOT NULL, quantity INT NOT NULL DEFAULT 1, checked TINYINT DEFAULT 1 COMMENT 1勾选结算 0不勾选, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_user_sku (user_id, sku_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT购物车表; CREATE TABLE t_order ( id BIGINT NOT NULL AUTO_INCREMENT, order_no VARCHAR(32) NOT NULL COMMENT 业务订单号, user_id BIGINT NOT NULL COMMENT 下单用户, total_amount DECIMAL(10,2) NOT NULL COMMENT 商品总金额, pay_amount DECIMAL(10,2) NOT NULL COMMENT 实付金额优惠后可等于总金额, status TINYINT NOT NULL DEFAULT 0 COMMENT 0待支付 1已支付 2已发货 3已完成 4已取消, receiver_name VARCHAR(50) NOT NULL, receiver_phone VARCHAR(20) NOT NULL, receiver_address VARCHAR(255) NOT NULL, pay_time DATETIME DEFAULT NULL, ship_time DATETIME DEFAULT NULL, finish_time DATETIME DEFAULT NULL, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_order_no (order_no), KEY idx_user (user_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT订单主表; CREATE TABLE t_order_item ( id BIGINT NOT NULL AUTO_INCREMENT, order_id BIGINT NOT NULL, product_id BIGINT NOT NULL, sku_id BIGINT NOT NULL, product_name VARCHAR(100) NOT NULL COMMENT 商品名称快照, sku_name VARCHAR(100) DEFAULT NULL COMMENT SKU描述快照, product_image VARCHAR(255) DEFAULT NULL, price DECIMAL(10,2) NOT NULL, quantity INT NOT NULL, total_amount DECIMAL(10,2) NOT NULL, PRIMARY KEY (id), KEY idx_order (order_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT订单明细表;订单明细必须保存商品名称和 SKU 描述快照。原因是下单后如果管理员修改了商品名称或价格历史订单不应该跟着变化否则对账和售后都会出问题。这一条是订单系统最常见的隐藏设计点。3. 搭建 Spring Boot 后端先把登录和商品接口跑通3.1 后端分层与包结构后端如果只写 controller 和 mapper前期跑通快但订单逻辑一复杂就会在 service 里堆出几百行代码。推荐按常见项目结构分层mall-server/src/main/java/com/example/outdoor/ ├── common/ # 统一返回结果、异常、常量 │ ├── Result.java │ ├── BizException.java │ └── JwtUtil.java ├── config/ # Web配置、拦截器、Redis配置 ├── controller/ # 接口入口 │ ├── AuthController.java │ ├── ProductController.java │ ├── CartController.java │ ├── OrderController.java │ └── admin/ # 后台管理接口按角色再分层 ├── entity/ # 数据库实体 ├── mapper/ # MyBatis-Plus Mapper ├── service/ # 业务逻辑接口和实现 └── dto/ # 请求参数和返回 VO分层的目的不是为了让目录好看而是改订单逻辑时不需要去 controller 里翻业务代码排查库存问题时也能快速定位到 service 和 mapper 哪一层出错。3.2 Maven 依赖与应用配置在 pom.xml 中加入核心依赖。下面的版本号是用来跑通项目的示例正式落地要以仓库中实际可用版本为准。parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version3.3.5/version relativePath/ /parent properties java.version17/java.version mybatis-plus.version3.5.7/mybatis-plus.version /properties dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-redis/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-validation/artifactId /dependency dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-spring-boot3-starter/artifactId version${mybatis-plus.version}/version /dependency dependency groupIdcom.mysql/groupId artifactIdmysql-connector-j/artifactId scoperuntime/scope /dependency dependency groupIdio.jsonwebtoken/groupId artifactIdjjwt-api/artifactId version0.11.5/version /dependency dependency groupIdio.jsonwebtoken/groupId artifactIdjjwt-impl/artifactId version0.11.5/version scoperuntime/scope /dependency dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency /dependenciesapplication.yml 中的配置要和 Spring Boot 版本对齐。以 3.x 稳定版为例Redis 配置要写在spring.data.redis下server: port: 8080 spring: application: name: outdoor-mall-server datasource: url: jdbc:mysql://localhost:3306/outdoor_mall?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver data: redis: host: localhost port: 6379 database: 0 mybatis-plus: configuration: map-underscore-to-camel-case: true log-impl: org.apache.ibatis.logging.stdout.StdOutImpl global-config: db-config: logic-delete-field: deleted logic-delete-value: 1 logic-not-delete-value: 0 wechat: appid: 你的小程序AppID secret: 你的小程序Secret如果使用 Spring Boot 2.x需要把spring.data.redis改回spring.redis。这是从 2 升到 3 之后最影响项目跑通的一处变更。3.3 微信登录不是“登录注册”而是 code 换 openid小程序端不能像 Web 端一样让用户先输入用户名密码推荐登录方式是小程序通过wx.login()拿到临时 code。小程序把 code 传给后端。后端调用微信jscode2session接口用 code 换取 openid 和 session_key。后端根据 openid 在 t_user 中查找用户不存在则创建。后端生成 JWT token 返回给小程序后续请求通过请求头携带 token。Controller 代码可以这样写RestController RequestMapping(/api/auth) public class AuthController { private final UserService userService; public AuthController(UserService userService) { this.userService userService; } PostMapping(/wx-login) public ResultLoginVO wxLogin(RequestBody WxLoginDTO dto) { if (dto null || dto.getCode() null) { return Result.error(code不能为空); } return Result.ok(userService.wxLogin(dto.getCode())); } }Service 中的关键逻辑是请求微信接口并处理返回结果。下面代码用来说明思路实际请根据选择 HTTP 客户端调整参数和异常处理Service public class UserServiceImpl implements UserService { private final UserMapper userMapper; public UserServiceImpl(UserMapper userMapper) { this.userMapper userMapper; } Override Transactional(rollbackFor Exception.class) public LoginVO wxLogin(String code) { String url https://api.weixin.qq.com/sns/jscode2session ?appidAPP_ID secretAPP_SECRET js_code code grant_typeauthorization_code; // 这里使用 HttpClient 或 RestTemplate 请求 url // 返回 JSON 中会包含 openid 和 session_key String openid ; if (openid null || openid.isEmpty()) { throw new BizException(微信登录失败请检查code和AppID配置); } User user userMapper.selectOne( new LambdaQueryWrapperUser() .eq(User::getOpenid, openid)); if (user null) { user new User(); user.setOpenid(openid); user.setNickname(户外用户); user.setStatus(1); userMapper.insert(user); } String token JwtUtil.createToken(user.getId()); LoginVO vo new LoginVO(); vo.setToken(token); vo.setUserId(user.getId()); return vo; } }这里的请求地址是微信官方登录凭证校验接口。真实项目不要在前端保存 appid 和 secret必须由后端通过服务端请求微信开放接口避免 secret 暴露到小程序包里。3.4 JWT 鉴权与拦截器JWT 的作用是让后端在无状态接口中识别“当前用户是谁”。登录成功后token 放到请求头Authorization中后端通过拦截器读取并解析。Component public class AuthInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (request.getMethod().equalsIgnoreCase(OPTIONS)) { return true; } String token request.getHeader(Authorization); if (token ! null token.startsWith(Bearer )) { token token.substring(7); } Long userId JwtUtil.parseToken(token); if (userId null) {
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表