1. 项目背景与技术选型
这个网上数码商城项目采用了当前主流的前后端分离架构,前端使用Vue.js框架,后端基于Spring Boot构建。这种技术组合在电商类项目中已经成为事实上的行业标准,我们团队在开发过程中也验证了其稳定性和开发效率。
Spring Boot的自动配置特性让我们能够快速搭建起商品管理、订单处理、支付对接等核心模块。实测中,从零开始到完成基础CRUD接口的平均耗时仅为传统Spring MVC项目的1/3。特别值得一提的是其内嵌Tomcat的设计,使得部署环节变得异常简单 - 只需要打包成一个jar文件就能直接运行。
在前端技术栈方面,Vue 3的组合式API让我们的组件开发更加灵活。通过Pinia状态管理,解决了购物车数据跨组件共享的难题。Element Plus组件库则提供了现成的UI解决方案,商品列表页的瀑布流布局仅用不到20行代码就实现了响应式效果。
2. 系统架构设计
2.1 整体架构图
code复制[前端层] Vue.js + Axios + Pinia
↓
[API网关] Spring Cloud Gateway
↓
[服务层] Spring Boot + MyBatis-Plus
↓
[数据层] MySQL + Redis
↓
[基础设施] Docker + Nginx
2.2 模块划分
系统主要分为以下核心模块:
- 用户中心:处理注册登录、个人信息管理
- 商品服务:商品CRUD、分类管理、搜索
- 订单系统:购物车、订单生成与状态流转
- 支付模块:对接支付宝/微信支付
- 评价系统:商品评价与晒单
- 后台管理:数据统计、权限控制
每个模块都采用独立的Git分支进行开发,通过Jenkins实现自动化构建和部署。这种微服务化的设计虽然增加了初期搭建成本,但在后期需求变更时展现出了巨大优势 - 修改商品搜索逻辑时完全不会影响到订单模块的正常运行。
3. 数据库设计要点
3.1 核心表结构
sql复制-- 商品表
CREATE TABLE `product` (
`id` bigint NOT NULL AUTO_INCREMENT,
`name` varchar(100) NOT NULL COMMENT '商品名称',
`price` decimal(10,2) NOT NULL COMMENT '售价',
`stock` int NOT NULL DEFAULT '0' COMMENT '库存',
`category_id` int NOT NULL COMMENT '分类ID',
`spec_json` json DEFAULT NULL COMMENT '规格参数',
`status` tinyint NOT NULL DEFAULT '1' COMMENT '状态',
PRIMARY KEY (`id`),
KEY `idx_category` (`category_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 订单表
CREATE TABLE `order` (
`id` bigint NOT NULL AUTO_INCREMENT,
`order_no` varchar(32) NOT NULL COMMENT '订单编号',
`user_id` bigint NOT NULL,
`total_amount` decimal(10,2) NOT NULL COMMENT '订单总额',
`payment_type` tinyint NOT NULL COMMENT '支付方式',
`status` tinyint NOT NULL DEFAULT '0' COMMENT '订单状态',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_order_no` (`order_no`),
KEY `idx_user` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
3.2 性能优化实践
在高并发测试中,我们发现商品详情页的QPS在300左右时响应时间开始明显上升。通过以下优化措施将性能提升了5倍:
- 添加Redis缓存层:对商品基础信息使用@Cacheable注解实现自动缓存
- 建立组合索引:为经常联合查询的字段(如category_id + status)建立复合索引
- 字段优化:将商品描述等大文本字段拆分到单独的表
- 连接池调优:将HikariCP的maximumPoolSize从默认10调整到50
特别提醒:JSON类型字段虽然使用方便,但在MySQL 5.7版本下无法建立索引。如果需要对规格参数进行搜索,建议拆分成传统的关系型表结构。
4. 关键功能实现细节
4.1 购物车设计
购物车模块采用了混合存储策略:
- 未登录用户:使用localStorage临时存储
- 已登录用户:同步到服务端数据库
这种设计既保证了用户体验的连贯性,又能实现多设备同步。核心代码如下:
javascript复制// Vue组合式API实现
const useCart = () => {
const items = ref([])
// 添加商品到购物车
const addItem = (product, quantity) => {
const existing = items.value.find(i => i.id === product.id)
if (existing) {
existing.quantity += quantity
} else {
items.value.push({...product, quantity})
}
// 持久化存储
if (store.state.user.token) {
api.updateCart(items.value) // 调用后端API
} else {
localStorage.setItem('cart', JSON.stringify(items.value))
}
}
return { items, addItem }
}
4.2 订单超时处理
我们采用Spring的@Scheduled注解配合Redis实现了订单超时自动取消:
java复制@Scheduled(cron = "0 */5 * * * ?") // 每5分钟执行一次
public void cancelTimeoutOrders() {
// 查询超过30分钟未支付的订单
List<Order> orders = orderMapper.selectTimeoutOrders(LocalDateTime.now().minusMinutes(30));
orders.forEach(order -> {
// 释放库存
productMapper.releaseStock(order.getProductId(), order.getQuantity());
// 更新订单状态
order.setStatus(OrderStatus.CANCELLED);
orderMapper.updateById(order);
// 发送通知
messageService.sendCancelNotice(order.getUserId());
});
}
注意点:在分布式环境下需要使用分布式锁或改用RabbitMQ的延迟队列来实现,避免多个实例重复处理。
5. 部署与运维实践
5.1 容器化部署
我们使用Docker Compose编排服务,典型配置如下:
yaml复制version: '3'
services:
mysql:
image: mysql:5.7
environment:
MYSQL_ROOT_PASSWORD: root
volumes:
- ./mysql/data:/var/lib/mysql
ports:
- "3306:3306"
redis:
image: redis:alpine
ports:
- "6379:6379"
backend:
build: ./backend
ports:
- "8080:8080"
depends_on:
- mysql
- redis
frontend:
build: ./frontend
ports:
- "80:80"
5.2 性能监控
接入Prometheus + Grafana监控体系后,我们发现两个典型问题及解决方案:
-
慢SQL问题:通过MyBatis-Plus的SQL打印功能定位到商品分类联查语句没有走索引,添加适当索引后响应时间从800ms降到50ms
-
内存泄漏:Vue组件中未及时清除的定时器导致内存持续增长,通过beforeUnmount钩子函数添加清理逻辑
6. 开发中的经验教训
-
接口版本控制:初期没有规划API版本,导致客户端升级困难。后来我们采用URL路径版本化(/api/v1/product)的方式解决了这个问题
-
金额计算:浮点数计算导致的精度问题让我们吃了大亏。全部改用BigDecimal后,订单金额计算再没出过问题
-
前端路由:history模式需要后端配合配置,否则刷新会404。我们最终选择在Nginx中添加以下配置:
nginx复制location / {
try_files $uri $uri/ /index.html;
}
- 图片存储:直接上传到服务器本地磁盘导致扩容困难。迁移到阿里云OSS后,不仅解决了存储问题,还通过CDN加速了图片加载
这个项目从技术选型到最终上线历时3个月,期间遇到了各种预料之外的问题。最大的体会是:前期在架构设计上多花时间,后期就能少填很多坑。特别是接口规范、错误码定义这些基础工作,一定要在开发初期就确定好标准。
