Spring Boot+Vue.js独立电商站实战:从零搭建母婴电商平台

发布时间:2026/7/10 4:43:05
Spring Boot+Vue.js独立电商站实战:从零搭建母婴电商平台 30款热门AI模型一站整合DeepSeek/GLM/Qwen 随心用限时 5 折。 点击领海量免费额度最近有不少朋友在咨询如何从零开始搭建一个独立电商站点特别是针对母婴用品这类垂直领域。刚好最近我整理了一套完整的独立站实战方案从技术选型到部署上线全流程覆盖特别适合中小型电商项目。本文将手把手带你搭建一个功能完整的母婴电商独立站包含商品展示、购物车、订单管理等核心功能。无论你是想学习全栈开发技术还是计划创业做自己的电商平台这篇文章都能为你提供实用的技术方案和可复用的代码示例。我们将使用主流的 Spring Boot Vue.js 技术栈确保项目的可扩展性和维护性。1. 项目背景与技术选型独立电商站点的搭建需要考虑多个维度技术成熟度、开发效率、后期维护成本以及团队技术栈匹配度。经过综合评估我们选择以下技术方案后端技术栈Spring Boot 2.7.x快速构建RESTful APISpring Security用户认证与权限管理MySQL 8.0业务数据存储Redis缓存与会话管理Maven项目依赖管理前端技术栈Vue.js 3.x渐进式前端框架Element PlusUI组件库AxiosHTTP请求处理Vue Router路由管理部署方案Nginx静态资源服务和反向代理Docker容器化部署阿里云/腾讯云云服务器选择选择这套技术栈的主要考虑是生态成熟、社区活跃遇到问题能够快速找到解决方案。特别是Spring Boot和Vue.js的组合在国内开发者社区中有大量实践案例可供参考。2. 环境准备与项目初始化在开始编码之前需要确保本地开发环境配置正确。以下是详细的环境要求2.1 开发环境要求后端开发环境JDK 1.8或更高版本Maven 3.6MySQL 8.0Redis 6.0IntelliJ IDEA或Eclipse前端开发环境Node.js 14.0npm 6.0Vue CLI 5.0Visual Studio Code2.2 数据库初始化创建数据库并初始化基础表结构-- 创建数据库 CREATE DATABASE baby_mall DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- 用户表 CREATE TABLE user ( id bigint(20) NOT NULL AUTO_INCREMENT, username varchar(50) NOT NULL COMMENT 用户名, password varchar(100) NOT NULL COMMENT 密码, phone varchar(20) DEFAULT NULL COMMENT 手机号, email varchar(100) DEFAULT NULL COMMENT 邮箱, status int(1) DEFAULT 1 COMMENT 状态0-禁用1-启用, create_time datetime DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, update_time datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 更新时间, PRIMARY KEY (id), UNIQUE KEY uk_username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户表; -- 商品分类表 CREATE TABLE category ( id bigint(20) NOT NULL AUTO_INCREMENT, name varchar(50) NOT NULL COMMENT 分类名称, parent_id bigint(20) DEFAULT 0 COMMENT 父级分类ID, level int(1) DEFAULT 1 COMMENT 分类级别1-一级2-二级, sort int(11) DEFAULT 0 COMMENT 排序, status int(1) DEFAULT 1 COMMENT 状态0-禁用1-启用, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT商品分类表;2.3 项目结构规划采用前后端分离的架构项目目录结构如下baby-mall/ ├── mall-backend/ # Spring Boot后端项目 │ ├── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── com/ │ │ │ │ └── baby/ │ │ │ │ └── mall/ │ │ │ │ ├── controller/ # 控制器层 │ │ │ │ ├── service/ # 业务层 │ │ │ │ ├── mapper/ # 数据访问层 │ │ │ │ ├── entity/ # 实体类 │ │ │ │ └── config/ # 配置类 │ │ │ └── resources/ │ │ │ ├── application.yml # 主配置文件 │ │ │ └── mapper/ # MyBatis映射文件 │ │ └── test/ # 测试代码 │ └── pom.xml # Maven配置 └── mall-frontend/ # Vue.js前端项目 ├── public/ # 静态资源 ├── src/ │ ├── components/ # 公共组件 │ ├── views/ # 页面组件 │ ├── router/ # 路由配置 │ ├── store/ # 状态管理 │ ├── api/ # API接口 │ └── utils/ # 工具函数 └── package.json # 项目依赖3. 后端核心功能实现3.1 Spring Boot项目配置创建Spring Boot项目添加必要依赖?xml version1.0 encodingUTF-8? project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd modelVersion4.0.0/modelVersion parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version2.7.0/version relativePath/ /parent groupIdcom.baby/groupId artifactIdmall-backend/artifactId version1.0.0/version properties java.version1.8/java.version /properties dependencies !-- Web支持 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !-- MyBatis Plus -- dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.2/version /dependency !-- MySQL驱动 -- dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.29/version /dependency !-- Redis -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-redis/artifactId /dependency !-- 参数校验 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-validation/artifactId /dependency !-- 测试 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-test/artifactId scopetest/scope /dependency /dependencies /project3.2 数据库连接配置配置application.yml文件server: port: 8080 servlet: context-path: /api spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/baby_mall?useUnicodetruecharacterEncodingutf-8serverTimezoneAsia/Shanghai username: root password: your_password redis: host: localhost port: 6379 password: database: 0 timeout: 3000ms lettuce: pool: max-active: 20 max-wait: -1ms max-idle: 8 min-idle: 0 mybatis-plus: configuration: map-underscore-to-camel-case: true log-impl: org.apache.ibatis.logging.stdout.StdOutImpl global-config: db-config: id-type: auto logic-delete-field: deleted logic-delete-value: 1 logic-not-delete-value: 03.3 用户认证模块实现实现基于JWT的用户认证系统// 用户实体类 Data TableName(user) public class User { TableId(type IdType.AUTO) private Long id; private String username; private String password; private String phone; private String email; private Integer status; private Date createTime; private Date updateTime; } // 用户服务接口 public interface UserService extends IServiceUser { /** * 用户注册 */ ResultString register(RegisterDTO registerDTO); /** * 用户登录 */ ResultLoginVO login(LoginDTO loginDTO); /** * 获取当前用户信息 */ ResultUserVO getCurrentUser(); } // 用户控制器 RestController RequestMapping(/user) Api(tags 用户管理) public class UserController { Autowired private UserService userService; PostMapping(/register) ApiOperation(用户注册) public ResultString register(RequestBody Valid RegisterDTO registerDTO) { return userService.register(registerDTO); } PostMapping(/login) ApiOperation(用户登录) public ResultLoginVO login(RequestBody Valid LoginDTO loginDTO) { return userService.login(loginDTO); } GetMapping(/info) ApiOperation(获取当前用户信息) public ResultUserVO getCurrentUser() { return userService.getCurrentUser(); } }3.4 商品管理模块实现商品分类和商品信息管理// 商品实体类 Data TableName(product) public class Product { TableId(type IdType.AUTO) private Long id; private String name; private Long categoryId; private String description; private BigDecimal price; private Integer stock; private String mainImage; private String subImages; private Integer status; private Date createTime; private Date updateTime; } // 商品服务实现 Service public class ProductServiceImpl extends ServiceImplProductMapper, Product implements ProductService { Autowired private RedisTemplateString, Object redisTemplate; Override public ResultPageVOProductVO getProductList(ProductQueryDTO queryDTO) { PageProduct page new Page(queryDTO.getPageNum(), queryDTO.getPageSize()); LambdaQueryWrapperProduct wrapper new LambdaQueryWrapper(); wrapper.eq(Product::getStatus, 1); if (StringUtils.isNotBlank(queryDTO.getKeyword())) { wrapper.like(Product::getName, queryDTO.getKeyword()); } if (queryDTO.getCategoryId() ! null) { wrapper.eq(Product::getCategoryId, queryDTO.getCategoryId()); } PageProduct productPage this.page(page, wrapper); // 转换为VO对象 ListProductVO productVOList productPage.getRecords().stream() .map(this::convertToVO) .collect(Collectors.toList()); PageVOProductVO pageVO new PageVO(); pageVO.setList(productVOList); pageVO.setTotal(productPage.getTotal()); pageVO.setPageNum(queryDTO.getPageNum()); pageVO.setPageSize(queryDTO.getPageSize()); return Result.success(pageVO); } private ProductVO convertToVO(Product product) { ProductVO vo new ProductVO(); BeanUtils.copyProperties(product, vo); return vo; } }4. 前端界面开发4.1 Vue.js项目初始化创建Vue.js项目并配置基础依赖// package.json { name: mall-frontend, version: 1.0.0, scripts: { serve: vue-cli-service serve, build: vue-cli-service build, lint: vue-cli-service lint }, dependencies: { vue: ^3.2.13, vue-router: ^4.0.3, vuex: ^4.0.0, element-plus: ^2.2.0, axios: ^0.27.2, dayjs: ^1.11.0 }, devDependencies: { vue/cli-service: ^5.0.0 } }4.2 路由配置配置前端路由// src/router/index.js import { createRouter, createWebHistory } from vue-router const routes [ { path: /, name: Home, component: () import(/views/Home.vue) }, { path: /login, name: Login, component: () import(/views/Login.vue) }, { path: /register, name: Register, component: () import(/views/Register.vue) }, { path: /products, name: Products, component: () import(/views/Products.vue) }, { path: /product/:id, name: ProductDetail, component: () import(/views/ProductDetail.vue) }, { path: /cart, name: Cart, component: () import(/views/Cart.vue) } ] const router createRouter({ history: createWebHistory(), routes }) // 路由守卫 router.beforeEach((to, from, next) { const token localStorage.getItem(token) if (to.meta.requiresAuth !token) { next(/login) } else { next() } }) export default router4.3 商品列表页面实现商品展示功能template div classproduct-list el-container el-aside width200px CategorySidebar category-changehandleCategoryChange / /el-aside el-main div classsearch-bar el-input v-modelsearchKeyword placeholder搜索商品... keyup.enterhandleSearch template #append el-button clickhandleSearch el-iconSearch //el-icon /el-button /template /el-input /div div classproduct-grid el-row :gutter20 el-col v-forproduct in productList :keyproduct.id :xs12 :sm8 :md6 ProductCard :productproduct add-to-carthandleAddToCart / /el-col /el-row /div div classpagination el-pagination v-model:current-pagecurrentPage v-model:page-sizepageSize :totaltotal layouttotal, sizes, prev, pager, next, jumper size-changehandleSizeChange current-changehandleCurrentChange / /div /el-main /el-container /div /template script import { ref, onMounted } from vue import { ElMessage } from element-plus import { Search } from element-plus/icons-vue import CategorySidebar from /components/CategorySidebar.vue import ProductCard from /components/ProductCard.vue import { getProductList } from /api/product export default { name: Products, components: { CategorySidebar, ProductCard, Search }, setup() { const searchKeyword ref() const currentPage ref(1) const pageSize ref(12) const total ref(0) const productList ref([]) const selectedCategory ref(null) const loadProducts async () { try { const params { pageNum: currentPage.value, pageSize: pageSize.value, keyword: searchKeyword.value, categoryId: selectedCategory.value } const response await getProductList(params) productList.value response.data.list total.value response.data.total } catch (error) { ElMessage.error(加载商品列表失败) } } const handleSearch () { currentPage.value 1 loadProducts() } const handleCategoryChange (categoryId) { selectedCategory.value categoryId currentPage.value 1 loadProducts() } const handleAddToCart (product) { // 添加到购物车逻辑 ElMessage.success(已添加 ${product.name} 到购物车) } const handleSizeChange (size) { pageSize.value size loadProducts() } const handleCurrentChange (page) { currentPage.value page loadProducts() } onMounted(() { loadProducts() }) return { searchKeyword, currentPage, pageSize, total, productList, handleSearch, handleCategoryChange, handleAddToCart, handleSizeChange, handleCurrentChange } } } /script5. 购物车与订单功能5.1 购物车实现// 购物车服务 Service public class CartServiceImpl implements CartService { Autowired private RedisTemplateString, Object redisTemplate; private String getCartKey(Long userId) { return cart:user: userId; } Override public ResultString addToCart(Long userId, AddCartDTO addCartDTO) { String cartKey getCartKey(userId); String productKey addCartDTO.getProductId().toString(); // 检查商品是否存在 Product product productService.getById(addCartDTO.getProductId()); if (product null || product.getStatus() 0) { return Result.error(商品不存在或已下架); } // 检查库存 if (product.getStock() addCartDTO.getQuantity()) { return Result.error(库存不足); } // 获取当前购物车中该商品的数量 CartItem currentItem (CartItem) redisTemplate.opsForHash().get(cartKey, productKey); int currentQuantity currentItem ! null ? currentItem.getQuantity() : 0; int newQuantity currentQuantity addCartDTO.getQuantity(); if (newQuantity product.getStock()) { return Result.error(超过库存限制); } // 更新购物车 CartItem cartItem new CartItem(); cartItem.setProductId(addCartDTO.getProductId()); cartItem.setProductName(product.getName()); cartItem.setProductImage(product.getMainImage()); cartItem.setPrice(product.getPrice()); cartItem.setQuantity(newQuantity); redisTemplate.opsForHash().put(cartKey, productKey, cartItem); return Result.success(添加成功); } Override public ResultListCartItemVO getCartList(Long userId) { String cartKey getCartKey(userId); MapObject, Object cartMap redisTemplate.opsForHash().entries(cartKey); ListCartItemVO cartList cartMap.values().stream() .map(item - { CartItem cartItem (CartItem) item; CartItemVO vo new CartItemVO(); BeanUtils.copyProperties(cartItem, vo); vo.setTotalPrice(cartItem.getPrice().multiply(BigDecimal.valueOf(cartItem.getQuantity()))); return vo; }) .collect(Collectors.toList()); return Result.success(cartList); } }5.2 订单生成流程Service Transactional(rollbackFor Exception.class) public class OrderServiceImpl implements OrderService { Autowired private OrderMapper orderMapper; Autowired private OrderItemMapper orderItemMapper; Autowired private ProductMapper productMapper; Override public ResultString createOrder(Long userId, CreateOrderDTO createOrderDTO) { // 1. 验证购物车商品 ListCartItem cartItems validateCartItems(userId, createOrderDTO.getCartItemIds()); // 2. 计算总金额 BigDecimal totalAmount calculateTotalAmount(cartItems); // 3. 生成订单号 String orderNo generateOrderNo(); // 4. 创建订单 Order order new Order(); order.setOrderNo(orderNo); order.setUserId(userId); order.setTotalAmount(totalAmount); order.setStatus(OrderStatus.WAITING_PAYMENT.getCode()); orderMapper.insert(order); // 5. 创建订单明细 ListOrderItem orderItems createOrderItems(order.getId(), cartItems); for (OrderItem orderItem : orderItems) { orderItemMapper.insert(orderItem); } // 6. 扣减库存 deductStock(cartItems); // 7. 清空购物车对应商品 clearCartItems(userId, createOrderDTO.getCartItemIds()); return Result.success(订单创建成功, orderNo); } private ListCartItem validateCartItems(Long userId, ListLong cartItemIds) { // 验证购物车商品是否存在、库存是否充足 // 返回有效的购物车商品列表 } private BigDecimal calculateTotalAmount(ListCartItem cartItems) { return cartItems.stream() .map(item - item.getPrice().multiply(BigDecimal.valueOf(item.getQuantity()))) .reduce(BigDecimal.ZERO, BigDecimal::add); } private String generateOrderNo() { // 生成唯一订单号时间戳 随机数 return System.currentTimeMillis() String.valueOf((int)((Math.random() * 9 1) * 1000)); } }6. 系统部署与配置6.1 Nginx配置配置Nginx实现前后端分离部署server { listen 80; server_name your-domain.com; # 前端静态资源 location / { root /usr/share/nginx/html; index index.html; try_files $uri $uri/ /index.html; } # 后端API代理 location /api/ { proxy_pass http://localhost:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } # 静态资源缓存 location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ { expires 1y; add_header Cache-Control public, immutable; } }6.2 Docker部署配置创建Dockerfile和docker-compose.yml# 后端Dockerfile FROM openjdk:8-jre-slim WORKDIR /app COPY target/mall-backend-1.0.0.jar app.jar EXPOSE 8080 ENTRYPOINT [java, -jar, app.jar] # 前端Dockerfile FROM nginx:alpine COPY dist/ /usr/share/nginx/html/ COPY nginx.conf /etc/nginx/conf.d/default.conf EXPOSE 80# docker-compose.yml version: 3.8 services: mysql: image: mysql:8.0 environment: MYSQL_ROOT_PASSWORD: your_password MYSQL_DATABASE: baby_mall volumes: - mysql_data:/var/lib/mysql ports: - 3306:3306 redis: image: redis:6.2-alpine ports: - 6379:6379 backend: build: ./mall-backend ports: - 8080:8080 depends_on: - mysql - redis environment: SPRING_DATASOURCE_URL: jdbc:mysql://mysql:3306/baby_mall SPRING_REDIS_HOST: redis frontend: build: ./mall-frontend ports: - 80:80 depends_on: - backend volumes: mysql_data:7. 常见问题与解决方案7.1 性能优化问题问题1商品列表加载缓慢原因数据库查询未优化图片资源过大解决方案添加数据库索引CREATE INDEX idx_category_status ON product(category_id, status)实现分页查询避免一次性加载过多数据使用CDN加速图片加载添加Redis缓存热门商品数据问题2高并发下单库存超卖原因多线程同时修改库存导致数据不一致解决方案使用数据库悲观锁SELECT ... FOR UPDATE使用Redis分布式锁在应用层使用synchronized关键字单机部署// 使用数据库悲观锁防止超卖 Transactional public boolean deductStock(Long productId, Integer quantity) { Product product productMapper.selectForUpdate(productId); if (product.getStock() quantity) { product.setStock(product.getStock() - quantity); productMapper.updateById(product); return true; } return false; }7.2 安全配置问题问题SQL注入和XSS攻击风险解决方案使用MyBatis Plus提供的参数化查询对用户输入进行严格的参数校验使用Spring Security的CSRF保护对输出内容进行HTML转义// 参数校验示例 Data public class RegisterDTO { NotBlank(message 用户名不能为空) Size(min 3, max 20, message 用户名长度3-20个字符) private String username; NotBlank(message 密码不能为空) Size(min 6, max 20, message 密码长度6-20个字符) private String password; Email(message 邮箱格式不正确) private String email; }8. 最佳实践与扩展建议8.1 代码规范与质量代码结构规范遵循RESTful API设计原则使用统一的响应结果封装实现全局异常处理添加完整的日志记录// 全局异常处理 ControllerAdvice public class GlobalExceptionHandler { ExceptionHandler(BusinessException.class) ResponseBody public ResultString handleBusinessException(BusinessException e) { return Result.error(e.getMessage()); } ExceptionHandler(Exception.class) ResponseBody public ResultString handleException(Exception e) { log.error(系统异常, e); return Result.error(系统繁忙请稍后重试); } }8.2 监控与运维系统监控配置集成Spring Boot Actuator健康检查使用Prometheus Grafana监控系统指标配置日志收集和分析系统设置关键业务指标告警# Actuator配置 management: endpoints: web: exposure: include: health,info,metrics endpoint: health: show-details: always8.3 业务扩展方向功能扩展建议会员体系积分、等级、优惠券系统营销功能秒杀、拼团、优惠活动多商户支持平台化运营模式移动端适配开发微信小程序或APP数据分析用户行为分析、销售报表技术架构升级微服务化使用Spring Cloud拆分业务模块消息队列引入RabbitMQ处理异步任务搜索引擎集成Elasticsearch提升搜索体验容器编排使用Kubernetes管理服务集群这套独立站解决方案已经过实际项目验证能够支撑日活数千的中小型电商业务。在实际部署时建议根据具体业务需求调整功能模块优先实现核心业务流程再逐步完善辅助功能。对于技术选型有疑问或需要特定功能实现的读者欢迎在评论区留言交流。后续我会继续分享电商系统中的支付集成、物流跟踪等进阶主题。 30款热门AI模型一站整合DeepSeek/GLM/Qwen 随心用限时 5 折。 点击领海量免费额度