毕业设计实战:基于SpringBoot+Vue的网上购物系统全流程指南
在开发“网上购物系统”毕业设计时,曾因“订单流程与库存同步设计不当”踩过关键坑——初期订单创建与库存扣减分离,导致超卖问题,耗费2天实现分布式锁才解决📝。基于实战经验,本文将系统拆解网上购物系统开发全流程。
一、需求分析:聚焦电商核心业务
电商系统最易犯的错误是功能繁杂。前期曾尝试添加“智能推荐算法”,最终因偏离“商品管理、购物流程、订单处理”核心需求被导师要求简化。
核心用户与功能
- 管理员:全系统管理、商家审核、商品审核、订单管理、系统配置
- 商家:商品管理、订单处理、店铺管理、销售统计
- 用户:商品浏览、购物车、下单支付、订单跟踪、个人中心
核心业务流程
- 购物流程:浏览商品 → 加入购物车 → 提交订单 → 支付 → 商家发货 → 用户收货
- 商家流程:入驻申请 → 商品上架 → 订单处理 → 发货 → 售后
- 管理流程:审核商家 → 审核商品 → 处理投诉 → 数据分析
二、技术选型:成熟稳定方案
选择“Java 8+SpringBoot 2.7+Vue 2.x+MySQL 8.0+Redis”组合:
| 技术 | 用途 | 关键技术点 |
|---|---|---|
| SpringBoot 2.7 | 后端框架 | 自动配置,微服务支持 |
| Vue 2.x + ElementUI | 前端框架 | 组件化,响应式设计 |
| MySQL 8.0 | 主数据库 | 事务支持,JSON类型 |
| Redis | 缓存/队列 | 商品缓存,订单队列 |
| RabbitMQ(可选) | 消息队列 | 异步处理订单,解耦业务 |
三、数据库设计:重点设计电商核心表
电商系统的核心在于商品-订单-用户的完整链路,以及库存和事务一致性。
核心表结构设计
- 用户表 (user):用户ID、账号、密码、姓名、头像、余额、会员状态
- 商家表 (merchant):商家ID、账号、店铺名、地址、联系方式、状态
- 商品表 (product):商品ID、名称、分类、价格、库存、图片、商家ID、状态
- 商品分类表 (category):分类ID、名称、父ID、排序
- 购物车表 (cart):用户ID、商品ID、数量、添加时间
- 订单表 (order):订单号、用户ID、总金额、状态、地址、支付方式
- 订单详情表 (order_item):订单号、商品ID、数量、单价、小计
- 地址表 (address):地址ID、用户ID、收货人、电话、地址、是否默认
- 支付记录表 (payment):支付ID、订单号、金额、支付方式、状态、时间
关键SQL设计
-- 商品详情页查询(带库存和商家信息)
SELECT
p.id, p.name, p.price, p.stock, p.description,
p.images, p.specifications,
m.shop_name, m.shop_address, m.contact_info,
c.category_name
FROM product p
LEFT JOIN merchant m ON p.merchant_id = m.id
LEFT JOIN category c ON p.category_id = c.id
WHERE p.id = #{productId} AND p.status = 1
-- 用户订单列表查询
SELECT
o.order_no, o.total_amount, o.status,
o.create_time, o.pay_time, o.deliver_time,
COUNT(oi.id) as item_count,
GROUP_CONCAT(p.name) as product_names
FROM `order` o
LEFT JOIN order_item oi ON o.order_no = oi.order_no
LEFT JOIN product p ON oi.product_id = p.id
WHERE o.user_id = #{userId}
GROUP BY o.order_no
ORDER BY o.create_time DESC
状态流转设计
- 订单状态:待支付 → 已支付 → 待发货 → 已发货 → 已收货 → 已完成 / 已取消
- 商品状态:上架中 / 已下架 / 审核中
- 支付状态:未支付 / 支付中 / 支付成功 / 支付失败
- 商家状态:审核中 / 正常 / 冻结 / 注销
四、核心功能实现
1. 商品管理模块
// 商品服务层
@Service
@Slf4j
public class ProductService {
@Autowired
private ProductMapper productMapper;
@Autowired
private RedisTemplate<String, Object> redisTemplate;
// 获取商品详情(带缓存)
public ProductDetailVO getProductDetail(Long productId) {
String cacheKey = "product:detail:" + productId;
// 1. 尝试从缓存获取
ProductDetailVO detail = (ProductDetailVO) redisTemplate.opsForValue().get(cacheKey);
if (detail != null) {
return detail;
}
// 2. 缓存未命中,查询数据库
Product product = productMapper.selectById(productId);
if (product == null) {
throw new BusinessException("商品不存在");
}
// 3. 获取商家信息
Merchant merchant = merchantMapper.selectById(product.getMerchantId());
// 4. 组装VO
detail = ProductDetailVO.builder()
.product(product)
.merchant(merchant)
.build();
// 5. 放入缓存,设置过期时间
redisTemplate.opsForValue().set(cacheKey, detail, 30, TimeUnit.MINUTES);
return detail;
}
// 扣减库存(使用乐观锁)
@Transactional
public boolean reduceStock(Long productId, Integer quantity) {
// 使用乐观锁方式扣减库存
int rows = productMapper.reduceStockWithOptimisticLock(productId, quantity);
return rows > 0;
}
}
2. 购物车模块
// 购物车服务
@Service
public class CartService {
// 添加商品到购物车
public void addToCart(Long userId, Long productId, Integer quantity) {
// 1. 验证商品是否存在且上架
Product product = productMapper.selectById(productId);
if (product == null || product.getStatus() != 1) {
throw new BusinessException("商品不存在或已下架");
}
// 2. 验证库存
if (product.getStock() < quantity) {
throw new BusinessException("库存不足");
}
// 3. 查询购物车中是否已存在该商品
CartItem cartItem = cartMapper.selectByUserAndProduct(userId, productId);
if (cartItem != null) {
// 已存在,更新数量
cartMapper.updateQuantity(cartItem.getId(), cartItem.getQuantity() + quantity);
} else {
// 不存在,新增
cartItem = new CartItem();
cartItem.setUserId(userId);
cartItem.setProductId(productId);
cartItem.setQuantity(quantity);
cartItem.setCreateTime(new Date());
cartMapper.insert(cartItem);
}
}
// 获取购物车列表(带商品信息)
public List<CartItemVO> getCartList(Long userId) {
List<CartItem> cartItems = cartMapper.selectByUserId(userId);
return cartItems.stream().map(item -> {
Product product = productMapper.selectById(item.getProductId());
return CartItemVO.builder()
.id(item.getId())
.product(product)
.quantity(item.getQuantity())
.selected(item.getSelected())
.build();
}).collect(Collectors.toList());
}
}
3. 订单模块(核心难点)
// 订单服务(分布式锁防止超卖)
@Service
@Slf4j
public class OrderService {
@Autowired
private RedissonClient redissonClient;
@Transactional(rollbackFor = Exception.class)
public String createOrder(Long userId, OrderCreateDTO createDTO) {
String orderNo = generateOrderNo();
// 使用分布式锁,防止重复下单和超卖
RLock lock = redissonClient.getLock("order:create:" + userId);
try {
// 尝试获取锁,最多等待5秒,锁持有10秒
boolean locked = lock.tryLock(5, 10, TimeUnit.SECONDS);
if (!locked) {
throw new BusinessException("操作频繁,请稍后重试");
}
try {
// 1. 验证购物车商品
List<CartItem> cartItems = validateCartItems(userId, createDTO.getCartItemIds());
// 2. 计算总金额
BigDecimal totalAmount = calculateTotalAmount(cartItems);
// 3. 创建订单主表
Order order = new Order();
order.setOrderNo(orderNo);
order.setUserId(userId);
order.setTotalAmount(totalAmount);
order.setStatus(OrderStatusEnum.PENDING_PAYMENT.getCode());
order.setCreateTime(new Date());
orderMapper.insert(order);
// 4. 创建订单详情并扣减库存
for (CartItem cartItem : cartItems) {
// 扣减库存(带重试机制)
boolean stockReduced = reduceStockWithRetry(cartItem.getProductId(), cartItem.getQuantity());
if (!stockReduced) {
throw new BusinessException("商品库存不足: " + cartItem.getProductId());
}
// 创建订单详情
OrderItem orderItem = new OrderItem();
orderItem.setOrderNo(orderNo);
orderItem.setProductId(cartItem.getProductId());
orderItem.setQuantity(cartItem.getQuantity());
orderItem.setUnitPrice(getProductPrice(cartItem.getProductId()));
orderItem.setSubtotal(orderItem.getUnitPrice().multiply(new BigDecimal(cartItem.getQuantity())));
orderItemMapper.insert(orderItem);
}
// 5. 清空购物车
cartMapper.deleteByIds(createDTO.getCartItemIds());
return orderNo;
} finally {
lock.unlock();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new BusinessException("创建订单失败");
}
}
// 扣减库存带重试
private boolean reduceStockWithRetry(Long productId, Integer quantity) {
int retryCount = 3;
while (retryCount-- > 0) {
try {
return reduceStock(productId, quantity);
} catch (Exception e) {
log.warn("扣减库存失败,重试中: productId={}, retryCount={}", productId, retryCount);
if (retryCount == 0) {
throw e;
}
try {
Thread.sleep(100); // 短暂等待后重试
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
throw new BusinessException("操作中断");
}
}
}
return false;
}
}
4. 支付模块
// 支付服务
@Service
public class PaymentService {
@Autowired
private OrderService orderService;
// 支付订单
@Transactional
public boolean payOrder(String orderNo, PaymentMethod method) {
Order order = orderMapper.selectByOrderNo(orderNo);
if (order == null) {
throw new BusinessException("订单不存在");
}
if (order.getStatus() != OrderStatusEnum.PENDING_PAYMENT.getCode()) {
throw new BusinessException("订单状态异常");
}
// 模拟支付过程
boolean paymentSuccess = mockPayment(order, method);
if (paymentSuccess) {
// 更新订单状态
order.setStatus(OrderStatusEnum.PAID.getCode());
order.setPayTime(new Date());
order.setPaymentMethod(method.getCode());
orderMapper.updateById(order);
// 发送支付成功消息(异步通知商家)
sendPaymentSuccessMessage(orderNo);
return true;
}
return false;
}
// 订单状态查询
public OrderStatusVO queryOrderStatus(String orderNo) {
Order order = orderMapper.selectByOrderNo(orderNo);
if (order == null) {
return null;
}
return OrderStatusVO.builder()
.orderNo(orderNo)
.status(order.getStatus())
.statusDesc(OrderStatusEnum.getByCode(order.getStatus()).getDesc())
.createTime(order.getCreateTime())
.payTime(order.getPayTime())
.deliverTime(order.getDeliverTime())
.receiveTime(order.getReceiveTime())
.build();
}
}
5. 接口设计
# 商品相关
GET /api/products?page=1&size=10&category=1 # 商品列表
GET /api/products/{id} # 商品详情
POST /api/products/search # 商品搜索
# 购物车相关
GET /api/cart # 购物车列表
POST /api/cart # 添加商品
PUT /api/cart/{id} # 更新数量
DELETE /api/cart/{id} # 删除商品
# 订单相关
POST /api/orders # 创建订单
GET /api/orders # 订单列表
GET /api/orders/{orderNo} # 订单详情
POST /api/orders/{orderNo}/pay # 支付订单
PUT /api/orders/{orderNo}/cancel # 取消订单
# 支付相关
POST /api/payment/callback # 支付回调
GET /api/payment/status/{orderNo} # 支付状态
# 用户相关
POST /api/auth/login # 登录
POST /api/auth/register # 注册
GET /api/user/profile # 用户信息
PUT /api/user/profile # 更新信息
GET /api/user/addresses # 地址列表
POST /api/user/addresses # 新增地址
五、页面设计与实现
1. 首页设计
- 头部导航:搜索框、分类导航、用户入口
- 轮播图:广告位和活动推广
- 商品推荐:热门商品、新品上市、特价商品
- 分类展示:按商品分类展示
2. 商品详情页
- 商品图片:多图轮播、放大镜功能
- 商品信息:名称、价格、规格、库存
- 商家信息:店铺名、评分、联系方式
- 购买操作:数量选择、加入购物车、立即购买
- 商品详情:图文详情、规格参数、用户评价
3. 购物车页面
- 商品列表:图片、名称、价格、数量、小计
- 操作功能:选择/取消、修改数量、删除
- 结算信息:商品总数、总金额、优惠信息
- 结算按钮:跳转到订单确认页
4. 订单确认页
- 收货地址:选择或新增地址
- 商品清单:确认购买的商品和数量
- 支付方式:微信、支付宝、余额支付
- 订单信息:商品总价、运费、优惠、实付金额
- 提交订单:生成订单,跳转到支付
5. 个人中心
- 我的订单:全部、待支付、待发货、待收货、已完成
- 我的收藏:收藏的商品列表
- 收货地址:地址管理
- 账户安全:密码修改、绑定手机
六、系统测试
核心测试场景
| 测试场景 | 测试步骤 | 预期结果 | 测试重点 |
|---|---|---|---|
| 购物流程 | 浏览→加购→下单→支付 | 订单状态正确流转 | 库存扣减、状态同步 |
| 并发下单 | 多个用户同时购买同一商品 | 库存正确扣减,无超卖 | 分布式锁、事务 |
| 支付流程 | 支付→回调→状态更新 | 订单状态变为已支付 | 支付状态一致性 |
| 取消订单 | 用户取消待支付订单 | 库存恢复,订单取消 | 库存回滚机制 |
性能测试
- 并发用户:模拟100用户同时购物
- 响应时间:关键页面<2秒,API<500ms
- 数据库压力:商品表10万条数据下查询性能
- 缓存效果:对比使用Redis前后的性能差异
安全测试
- SQL注入:输入特殊字符测试
- XSS攻击:提交脚本代码测试
- 越权访问:尝试访问他人订单
- 重复提交:快速点击提交按钮
七、答辩准备要点
演示重点
- 完整购物流程:从浏览到支付的完整演示
- 并发场景演示:展示如何防止超卖
- 后台管理功能:商家和管理员操作演示
- 移动端适配:不同设备上的显示效果
技术亮点
- 分布式锁防超卖:Redisson实现分布式锁
- 缓存优化:Redis缓存热点数据
- 事务一致性:Spring事务管理
- 接口安全:JWT认证、参数校验
- 异步处理:订单创建后的异步通知
常见问题准备
-
Q:如何防止商品超卖? A:数据库乐观锁 + Redis分布式锁 + 库存预扣
-
Q:如何保证支付安全? A:HTTPS传输 + 签名验证 + 回调验证 + 对账机制
-
Q:系统如何应对高并发? A:Nginx负载均衡 + Redis缓存 + 数据库读写分离 + 消息队列
-
Q:如何设计商品库存? A:真实库存 + 预扣库存 + 安全库存,不同场景使用不同库存字段
-
Q:订单号如何生成? A:时间戳 + 随机数 + 业务类型,保证全局唯一
项目价值体现
- 用户体验:简洁的购物流程,快速的响应
- 商家价值:完善的店铺管理工具
- 管理效率:后台管理系统提高运营效率
- 扩展性:模块化设计便于功能扩展
- 安全性:多层次的安全防护机制
结语
网上购物系统是经典的毕业设计选题,技术覆盖面广,业务逻辑完整。重点需要解决的是高并发下的数据一致性和完整的购物流程设计。
开发建议顺序:
- 先设计数据库表结构(特别是商品、订单、用户核心表)
- 实现用户认证和商品浏览基础功能
- 完成购物车和下单流程(重点解决库存问题)
- 集成支付功能(可先模拟支付)
- 实现后台管理功能
- 进行性能优化和安全加固
- 编写测试用例和文档
避坑提醒:
- 库存扣减一定要在事务中完成
- 订单号生成要保证唯一性
- 支付回调要验证签名
- 敏感操作要记录日志
- 前端要做好输入验证
若需要完整的网上购物系统源码、数据库设计、部署教程,可在评论区留言"网上购物系统"获取相关资料。
祝各位同学毕设顺利!🛒💰