本文目录导读:

我将为您提供一个完整的Java业务系统案例——在线商城订单管理系统,包含核心业务逻辑、代码实现和系统设计。
这是一个基于Spring Boot的在线商城订单管理系统,包含用户、商品、订单、购物车等核心模块。
系统架构设计
技术栈
- 后端框架: Spring Boot 2.7.x
- ORM框架: MyBatis-Plus
- 数据库: MySQL 8.0
- 安全认证: Spring Security + JWT
- 缓存: Redis
- 构建工具: Maven
核心业务模块
- 用户模块(注册、登录、权限管理)
- 商品模块(商品管理、库存管理)
- 购物车模块(添加、修改、删除、结算)
- 订单模块(下单、支付、取消、退款)
- 优惠券模块(领取、使用)
核心代码实现
实体类设计
// 用户实体类
@Data
@TableName("user")
public class User {
@TableId(type = IdType.AUTO)
private Long id;
private String username;
private String password;
private String nickname;
private String email;
private String phone;
private Integer status; // 1-正常 0-禁用
private LocalDateTime createTime;
private LocalDateTime updateTime;
}
// 商品实体类
@Data
@TableName("product")
public class Product {
@TableId(type = IdType.AUTO)
private Long id;
private String productName;
private String description;
private BigDecimal price;
private Integer stock; // 库存
private String category; // 分类
private Integer status; // 1-上架 0-下架
private LocalDateTime createTime;
private LocalDateTime updateTime;
@Version // 乐观锁版本号
private Integer version;
}
// 订单实体类
@Data
@TableName("orders")
public class Order {
@TableId(type = IdType.ASSIGN_ID)
private String orderNo; // 订单号
private Long userId;
private BigDecimal totalAmount; // 总金额
private Integer status; // 1-待付款 2-已付款 3-已发货 4-已完成 5-已取消
private String address; // 收货地址
private String phone; // 联系电话
private LocalDateTime createTime;
private LocalDateTime payTime;
private LocalDateTime deliveryTime;
private LocalDateTime completeTime;
}
// 订单明细
@Data
@TableName("order_item")
public class OrderItem {
@TableId(type = IdType.AUTO)
private Long id;
private String orderNo;
private Long productId;
private Integer quantity;
private BigDecimal price; // 商品单价
private BigDecimal totalPrice; // 小计
}
业务层实现
@Service
@Slf4j
public class OrderServiceImpl implements OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private ProductMapper productMapper;
@Autowired
private OrderItemMapper orderItemMapper;
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Autowired
private IdGenerator idGenerator;
@Transactional(rollbackFor = Exception.class)
@Override
public Order createOrder(Long userId, List<CartItemDTO> cartItems) {
// 1. 计算订单金额并创建订单
BigDecimal totalAmount = BigDecimal.ZERO;
List<OrderItem> orderItems = new ArrayList<>();
// 2. 验证商品库存并锁定
for (CartItemDTO item : cartItems) {
Product product = productMapper.selectById(item.getProductId());
// 使用乐观锁防止超卖
int result = productMapper.deductStock(item.getProductId(),
item.getQuantity(),
product.getVersion());
if (result == 0) {
throw new BusinessException("商品:" + product.getProductName() + " 库存不足");
}
// 计算金额
BigDecimal itemTotal = product.getPrice()
.multiply(BigDecimal.valueOf(item.getQuantity()));
totalAmount = totalAmount.add(itemTotal);
// 构建订单明细
OrderItem orderItem = new OrderItem();
orderItem.setProductId(product.getId());
orderItem.setQuantity(item.getQuantity());
orderItem.setPrice(product.getPrice());
orderItem.setTotalPrice(itemTotal);
orderItems.add(orderItem);
}
// 3. 创建订单
String orderNo = idGenerator.generateOrderNo();
Order order = new Order();
order.setOrderNo(orderNo);
order.setUserId(userId);
order.setTotalAmount(totalAmount);
order.setStatus(1); // 待付款
order.setCreateTime(LocalDateTime.now());
orderMapper.insert(order);
// 4. 保存订单明细
orderItems.forEach(item -> {
item.setOrderNo(orderNo);
orderItemMapper.insert(item);
});
// 5. 缓存订单信息(用于超时自动取消)
redisTemplate.opsForValue()
.set("order:timeout:" + orderNo, orderNo, 30, TimeUnit.MINUTES);
log.info("订单创建成功:{}", orderNo);
return order;
}
@Transactional(rollbackFor = Exception.class)
@Override
public boolean cancelOrder(String orderNo, Long userId) {
// 1. 查询订单
Order order = orderMapper.selectByOrderNo(orderNo);
// 2. 校验订单状态
if (!order.getUserId().equals(userId)) {
throw new BusinessException("无权操作该订单");
}
if (order.getStatus() != 1) {
throw new BusinessException("订单当前状态不可取消");
}
// 3. 取消订单
order.setStatus(5); // 已取消
orderMapper.updateById(order);
// 4. 回滚库存
List<OrderItem> items = orderItemMapper.selectByOrderNo(orderNo);
for (OrderItem item : items) {
productMapper.increaseStock(item.getProductId(), item.getQuantity());
}
// 5. 删除缓存
redisTemplate.delete("order:timeout:" + orderNo);
return true;
}
@Transactional(rollbackFor = Exception.class)
@Override
public boolean payOrder(String orderNo) {
// 1. 查询订单
Order order = orderMapper.selectByOrderNo(orderNo);
if (order.getStatus() != 1) {
throw new BusinessException("订单状态不正确");
}
// 2. 调用支付系统(模拟)
PaymentResult result = mockPayment(order.getTotalAmount());
if (!result.isSuccess()) {
throw new BusinessException("支付失败");
}
// 3. 更新订单状态
order.setStatus(2); // 已付款
order.setPayTime(LocalDateTime.now());
orderMapper.updateById(order);
return true;
}
@Override
public PageInfo<OrderVO> queryOrderList(Long userId, Integer status,
Integer pageNum, Integer pageSize) {
// 构建查询条件
Page<Order> page = new Page<>(pageNum, pageSize);
LambdaQueryWrapper<Order> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(userId != null, Order::getUserId, userId);
queryWrapper.eq(status != null, Order::getStatus, status);
queryWrapper.orderByDesc(Order::getCreateTime);
Page<Order> orderPage = orderMapper.selectPage(page, queryWrapper);
// 转换为VO
PageInfo<OrderVO> result = new PageInfo<>();
BeanUtils.copyProperties(orderPage, result);
return result;
}
}
商品库存管理(防止超卖)
@Mapper
public interface ProductMapper extends BaseMapper<Product> {
// 乐观锁扣减库存
@Update("UPDATE product SET stock = stock - #{quantity}, " +
"version = version + 1 " +
"WHERE id = #{productId} AND stock >= #{quantity} " +
"AND version = #{version}")
int deductStock(@Param("productId") Long productId,
@Param("quantity") Integer quantity,
@Param("version") Integer version);
// 恢复库存
@Update("UPDATE product SET stock = stock + #{quantity} " +
"WHERE id = #{productId}")
int increaseStock(@Param("productId") Long productId,
@Param("quantity") Integer quantity);
// 分布式锁方式(使用Redis)
default boolean deductStockWithLock(Long productId, Integer quantity) {
// 实际使用Redisson等分布式锁组件
return false;
}
}
购物车服务
@Service
@Slf4j
public class CartServiceImpl implements CartService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
private static final String CART_PREFIX = "cart:";
@Override
public void addToCart(Long userId, Long productId, Integer quantity) {
String cartKey = CART_PREFIX + userId;
// 获取购物车
Map<Object, Object> cart = redisTemplate.opsForHash().entries(cartKey);
// 添加或更新商品
Integer existingQty = (Integer) cart.get(productId.toString());
int newQty = quantity + (existingQty == null ? 0 : existingQty);
redisTemplate.opsForHash().put(cartKey, productId.toString(), newQty);
// 设置过期时间
redisTemplate.expire(cartKey, 7, TimeUnit.DAYS);
}
@Override
public List<CartItemVO> getCartItems(Long userId) {
String cartKey = CART_PREFIX + userId;
Map<Object, Object> cart = redisTemplate.opsForHash().entries(cartKey);
List<CartItemVO> items = new ArrayList<>();
for (Map.Entry<Object, Object> entry : cart.entrySet()) {
Long productId = Long.valueOf((String) entry.getKey());
Integer quantity = (Integer) entry.getValue();
// 查询商品信息
Product product = productMapper.selectById(productId);
if (product != null) {
CartItemVO item = new CartItemVO();
item.setProductId(productId);
item.setProductName(product.getProductName());
item.setPrice(product.getPrice());
item.setQuantity(quantity);
item.setStock(product.getStock());
items.add(item);
}
}
return items;
}
@Override
public void clearCart(Long userId) {
String cartKey = CART_PREFIX + userId;
redisTemplate.delete(cartKey);
}
}
优惠券功能
@Service
public class CouponServiceImpl implements CouponService {
@Override
@Transactional(rollbackFor = Exception.class)
public boolean receiveCoupon(Long userId, Long couponTypeId) {
// 1. 查询优惠券类型
CouponType couponType = couponTypeMapper.selectById(couponTypeId);
if (couponType == null) {
throw new BusinessException("优惠券不存在");
}
// 2. 检查领取数量限制
LambdaQueryWrapper<UserCoupon> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(UserCoupon::getUserId, userId)
.eq(UserCoupon::getCouponTypeId, couponTypeId);
Long count = userCouponMapper.selectCount(wrapper);
if (count >= couponType.getReceiveLimit()) {
throw new BusinessException("已达领取上限");
}
// 3. 校验总数量
if (couponType.getRemainingCount() <= 0) {
throw new BusinessException("优惠券已领完");
}
// 4. 扣减库存并保存
int update = couponTypeMapper.decreaseCount(couponTypeId);
if (update == 0) {
throw new BusinessException("优惠券已领完");
}
UserCoupon userCoupon = new UserCoupon();
userCoupon.setUserId(userId);
userCoupon.setCouponTypeId(couponTypeId);
userCoupon.setStatus(1); // 未使用
userCoupon.setReceiveTime(LocalDateTime.now());
LocalDateTime expireTime = LocalDateTime.now()
.plusDays(couponType.getValidityDays());
userCoupon.setExpireTime(expireTime);
userCouponMapper.insert(userCoupon);
return true;
}
@Override
public BigDecimal calcDiscount(Long userId, BigDecimal totalAmount) {
// 查找用户最优的可用优惠券并计算折扣
LambdaQueryWrapper<UserCoupon> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(UserCoupon::getUserId, userId)
.eq(UserCoupon::getStatus, 1)
.gt(UserCoupon::getExpireTime, LocalDateTime.now())
.orderByDesc(UserCoupon::getReceiveTime);
List<UserCoupon> coupons = userCouponMapper.selectList(wrapper);
BigDecimal maxDiscount = BigDecimal.ZERO;
for (UserCoupon coupon : coupons) {
CouponType type = couponTypeMapper.selectById(coupon.getCouponTypeId());
BigDecimal discount = calculateDiscount(type, totalAmount);
if (discount.compareTo(maxDiscount) > 0) {
maxDiscount = discount;
}
}
return maxDiscount;
}
}
定时任务(超时未支付自动取消)
@Component
@Slf4j
public class OrderTimeoutTask {
@Autowired
private OrderMapper orderMapper;
@Autowired
private RedisTemplate<String, Object> redisTemplate;
// 每10分钟执行一次
@Scheduled(fixedDelay = 600000)
public void processTimeoutOrders() {
log.info("开始处理超时订单...");
// 获取超时订单(30分钟未支付)
LocalDateTime timeoutTime = LocalDateTime.now().minusMinutes(30);
LambdaQueryWrapper<Order> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(Order::getStatus, 1) // 待付款
.lt(Order::getCreateTime, timeoutTime);
List<Order> timeoutOrders = orderMapper.selectList(wrapper);
for (Order order : timeoutOrders) {
try {
// 检查是否在Redis中存在(防止并发处理)
Boolean success = redisTemplate.opsForValue()
.setIfAbsent("cancel:lock:" + order.getOrderNo(), "1",
10, TimeUnit.MINUTES);
if (Boolean.TRUE.equals(success)) {
// 取消订单
order.setStatus(5);
orderMapper.updateById(order);
// 回滚库存
rollbackStock(order.getOrderNo());
log.info("订单 {} 已超时取消", order.getOrderNo());
}
} catch (Exception e) {
log.error("取消订单失败:{}", order.getOrderNo(), e);
}
}
}
private void rollbackStock(String orderNo) {
// 回滚库存逻辑
}
}
JWT认证实现
@Component
public class JwtTokenProvider {
@Value("${jwt.secret}")
private String secret;
@Value("${jwt.expiration}")
private Long expiration;
public String generateToken(User user) {
Date now = new Date();
Date expiryDate = new Date(now.getTime() + expiration);
return Jwts.builder()
.setSubject(user.getUsername())
.claim("userId", user.getId())
.claim("nickname", user.getNickname())
.setIssuedAt(now)
.setExpiration(expiryDate)
.signWith(SignatureAlgorithm.HS256, secret)
.compact();
}
public Long getUserIdFromToken(String token) {
Claims claims = Jwts.parser()
.setSigningKey(secret)
.parseClaimsJws(token)
.getBody();
return ((Number) claims.get("userId")).longValue();
}
public boolean validateToken(String token) {
try {
Jwts.parser().setSigningKey(secret).parseClaimsJws(token);
return true;
} catch (Exception e) {
return false;
}
}
}
库存安全控制(Redis预扣减)
@Component
public class StockService {
@Autowired
private StringRedisTemplate redisTemplate;
@Autowired
private ProductMapper productMapper;
private static final String STOCK_KEY = "product:stock:";
private static final String LOCK_KEY = "product:lock:";
@PostConstruct
public void initStock() {
// 初始化库存到Redis
List<Product> products = productMapper.selectList(null);
for (Product product : products) {
redisTemplate.opsForValue()
.set(STOCK_KEY + product.getId(), String.valueOf(product.getStock()));
}
}
@Transactional(rollbackFor = Exception.class)
public boolean tryDeductStock(Long productId, Integer quantity) {
// 使用Redis实现分布式锁
String lockKey = LOCK_KEY + productId;
String requestId = UUID.randomUUID().toString();
Boolean locked = redisTemplate.opsForValue()
.setIfAbsent(lockKey, requestId, 10, TimeUnit.SECONDS);
if (!Boolean.TRUE.equals(locked)) {
throw new BusinessException("系统繁忙,请稍后重试");
}
try {
// 原子性扣减库存
String stockStr = redisTemplate.opsForValue().get(STOCK_KEY + productId);
int stock = Integer.parseInt(stockStr);
if (stock < quantity) {
throw new BusinessException("库存不足");
}
// LUA脚本保证原子性
String luaScript =
"local stock = redis.call('get', KEYS[1]) " +
"if tonumber(stock) >= tonumber(ARGV[1]) then " +
" redis.call('decrby', KEYS[1], ARGV[1]) " +
" return 1 " +
"else " +
" return 0 " +
"end";
Long result = redisTemplate.execute(
new DefaultRedisScript<>(luaScript, Long.class),
Arrays.asList(STOCK_KEY + productId),
String.valueOf(quantity)
);
return result != null && result == 1L;
} finally {
// 释放锁
releaseLock(lockKey, requestId);
}
}
@Transactional(rollbackFor = Exception.class)
public void confirmStock() {
// 订单创建成功后,同步扣减数据库库存
// 并发控制使用乐观锁
}
private void releaseLock(String lockKey, String requestId) {
String luaScript =
"if redis.call('get', KEYS[1]) == ARGV[1] then " +
" return redis.call('del', KEYS[1]) " +
"else " +
" return 0 " +
"end";
redisTemplate.execute(
new DefaultRedisScript<>(luaScript, Long.class),
Arrays.asList(lockKey),
requestId
);
}
}
数据库设计
-- 用户表
CREATE TABLE `user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL COMMENT '用户名',
`password` varchar(100) NOT NULL COMMENT '密码(加密存储)',
`nickname` varchar(50) DEFAULT NULL COMMENT '昵称',
`email` varchar(100) DEFAULT NULL COMMENT '邮箱',
`phone` varchar(20) DEFAULT NULL COMMENT '手机号',
`status` tinyint(4) DEFAULT '1' COMMENT '状态:1正常 0禁用',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_username` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 商品表
CREATE TABLE `product` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`product_name` varchar(100) NOT NULL COMMENT '商品名称',
`description` text COMMENT '商品描述',
`price` decimal(10,2) NOT NULL COMMENT '价格',
`stock` int(11) NOT NULL DEFAULT '0' COMMENT '库存',
`category` varchar(50) DEFAULT NULL COMMENT '分类',
`status` tinyint(4) DEFAULT '1' COMMENT '状态:1上架 0下架',
`version` int(11) DEFAULT '0' COMMENT '乐观锁版本号',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_category` (`category`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 订单表
CREATE TABLE `orders` (
`order_no` varchar(32) NOT NULL COMMENT '订单号',
`user_id` bigint(20) NOT NULL COMMENT '用户ID',
`total_amount` decimal(10,2) NOT NULL COMMENT '订单总金额',
`status` tinyint(4) NOT NULL COMMENT '状态:1待付款 2已付款 3已发货 4已完成 5已取消',
`address` varchar(200) DEFAULT NULL COMMENT '收货地址',
`phone` varchar(20) DEFAULT NULL COMMENT '联系电话',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
`pay_time` datetime DEFAULT NULL,
`delivery_time` datetime DEFAULT NULL,
`complete_time` datetime DEFAULT NULL,
PRIMARY KEY (`order_no`),
KEY `idx_user_id` (`user_id`),
KEY `idx_status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 订单明细表
CREATE TABLE `order_item` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`order_no` varchar(32) NOT NULL,
`product_id` bigint(20) NOT NULL,
`quantity` int(11) NOT NULL COMMENT '数量',
`price` decimal(10,2) NOT NULL COMMENT '成交单价',
`total_price` decimal(10,2) NOT NULL COMMENT '小计',
PRIMARY KEY (`id`),
KEY `idx_order_no` (`order_no`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
业务处理流程
下单流程
用户提交购物车商品
2. 系统验证用户信息(JWT)
3. 计算订单金额(含优惠券)
4. 启用Redis + LUA脚本扣减库存
5. 创建订单和订单明细(事务)
6. 同步数据库库存(乐观锁)
7. 缓存订单信息(30分钟超时)
8. 返回支付信息
支付流程
用户选择支付方式
2. 调用第三方支付(模拟)
3. 支付回调验证签名
4. 更新订单状态(状态机)
5. 发送通知(短信/邮件)
6. 清理缓存数据
安全与优化策略
安全措施
// 1. 防SQL注入
@schema-rule(value = "column", strategy = SqlGuard.STRATEGY_ALLOWED)
private String orderField;
// 2. 防XSS攻击
@Component
public class XssFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
XssWrapper xssRequest = new XssWrapper((HttpServletRequest) request);
chain.doFilter(xssRequest, response);
}
}
// 3. 接口防刷
@Component
public class RateLimitInterceptor implements HandlerInterceptor {
private static final int MAX_ATTEMPTS = 5;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
String ip = getClientIp(request);
String key = "rate:limit:" + ip;
Long count = redisTemplate.opsForValue().increment(key);
if (count != null && count == 1) {
redisTemplate.expire(key, 1, TimeUnit.MINUTES);
}
if (count != null && count > MAX_ATTEMPTS) {
throw new BusinessException("请求过于频繁,请稍后再试");
}
return true;
}
}
性能优化
// 1. 缓存优化
@Cacheable(value = "product", key = "#productId",
unless = "#result == null", cacheManager = "redisCacheManager")
public Product getProductDetail(Long productId) {
return productMapper.selectById(productId);
}
// 2. 批量操作
public List<OrderItem> batchQueryItems(String orderNo) {
// 使用MyBatis的foreach批量查询
return orderItemMapper.selectList(
new LambdaQueryWrapper<OrderItem>()
.eq(OrderItem::getOrderNo, orderNo)
.apply("id LIMIT 500")
);
}
// 3. 数据库优化
// 使用EXPLAIN分析SQL
// 合理建立联合索引
// 分库分表策略
测试用例
@SpringBootTest
@Transactional
public class OrderServiceTest {
@Autowired
private OrderService orderService;
@Autowired
private UserMapper userMapper;
@Test
public void testCreateOrder() {
// 准备数据
User user = new User();
user.setUsername("testuser");
user.setPassword("123456");
userMapper.insert(user);
List<CartItemDTO> items = new ArrayList<>();
CartItemDTO item = new CartItemDTO();
item.setProductId(1L);
item.setQuantity(2);
items.add(item);
// 执行测试
Order order = orderService.createOrder(user.getId(), items);
// 验证结果
assertNotNull(order);
assertEquals(1, order.getStatus().intValue());
assertNotNull(order.getOrderNo());
}
@Test
public void testCancelOrder() {
// 测试取消订单逻辑
}
@Test
public void testPayOrder() {
// 测试支付逻辑
}
@Test
void testConcurrentStock() throws InterruptedException {
// 并发测试库存扣减
int threadCount = 100;
CountDownLatch latch = new CountDownLatch(threadCount);
AtomicInteger successCount = new AtomicInteger(0);
ExecutorService executor = Executors.newFixedThreadPool(10);
for (int i = 0; i < threadCount; i++) {
executor.execute(() -> {
try {
// 模拟并发购买
boolean result = stockService.tryDeductStock(1L, 1);
if (result) {
successCount.incrementAndGet();
}
} finally {
latch.countDown();
}
});
}
latch.await();
assertEquals(10, successCount.get()); // 假设库存为10
}
}
这个电商订单系统涵盖了:
- 核心业务:用户、商品、购物车、订单、优惠券完整闭环
- 高并发处理:Redis缓存 + 乐观锁 + 分布式锁防止超卖
- 安全机制:JWT认证、权限控制、防SQL注入、防XSS
- 事务管理:保证数据一致性
- 性能优化:缓存策略、SQL优化、索引设计
此案例展示了Java业务系统开发中的常见问题解决方案,可作为实际项目开发的参考,根据具体业务需求,可以进一步扩展如:分类管理、轮播图、秒杀活动、物流跟踪等功能模块。