Java通用型支付+电商平台双系统实战 | 完结

linbojue
• 阅读 3

掌握基础增删改查(CRUD)只是 Java 开发的起点,要真正吃透这门技术栈,必须深入业务场景,通过实际项目来磨练架构思维。本文将围绕“电商”与“支付”这两个强关联的核心系统,从技术选型、架构设计到核心代码实现,带你一步步完成从初级开发向架构设计的进阶。 一、 项目背景与技术选型 在实际企业开发中,电商系统与支付系统通常既是协同工作的,又是物理隔离的。电商系统负责商品展示、订单生成;支付系统负责资金流水、渠道对接。 核心技术栈:

后端框架:Spring Boot 3.x + Spring MVC 数据访问:MyBatis-Plus + Druid 连接池 数据库:MySQL 8.0 (主从架构模拟) 缓存中间件:Redis (分布式缓存、分布式锁) 消息队列:RabbitMQ (异步解耦、流量削峰) 服务治理:Nacos (注册中心、配置中心) RPC 调用:OpenFeign

二、 电商订单系统:构建高并发交易基石 电商系统的核心在于处理高并发下的订单一致性。这里我们通过“Redis 预扣库存 + MQ 异步下单”的模式来提升吞吐量。

  1. 数据库设计 sql 复制 sql 体验AI代码助手 代码解读复制代码-- 商品表 CREATE TABLE product ( id bigint NOT NULL AUTO_INCREMENT, name varchar(100) NOT NULL COMMENT '商品名称', stock int NOT NULL DEFAULT '0' COMMENT '库存', price decimal(10,2) NOT NULL COMMENT '单价', PRIMARY KEY (id) );

-- 订单表 CREATE TABLE order_info ( id bigint NOT NULL AUTO_INCREMENT, order_no varchar(64) NOT NULL COMMENT '订单号', product_id bigint NOT NULL COMMENT '商品ID', user_id bigint NOT NULL COMMENT '用户ID', amount decimal(10,2) NOT NULL COMMENT '订单金额', status tinyint NOT NULL DEFAULT '0' COMMENT '状态:0-待支付,1-已支付', create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_order_no (order_no) );

  1. Redis 预扣库存逻辑(Lua 脚本保证原子性) 为了防止超卖,不能直接查库扣减,必须利用 Redis 的单线程特性。 java 复制 vbnet 体验AI代码助手 代码解读复制代码@Service @RequiredArgsConstructor public class StockService {

    private final RedisTemplate<String, Object> redisTemplate;

    // Lua 脚本:检查库存并扣减 private static final String STOCK_LUA_SCRIPT =

         "if tonumber(redis.call('get', KEYS[1])) >= tonumber(ARGV[1]) then " +
         "   return redis.call('decrby', KEYS[1], ARGV[1]); " +
         "else " +
         "   return -1; " +
         "end";

    public boolean deductStock(Long productId, Integer quantity) {

     DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(STOCK_LUA_SCRIPT, Long.class);
     String key = "product:stock:" + productId;
     Long result = redisTemplate.execute(redisScript, Collections.singletonList(key), quantity);
     return result != null && result >= 0;

    } }

  2. 下单与消息发送 库存扣减成功后,发送消息到 MQ,由消费者异步落库。 java 复制 kotlin 体验AI代码助手 代码解读复制代码@Service @RequiredArgsConstructor public class OrderService {

    private final RabbitTemplate rabbitTemplate;

    @Transactional(rollbackFor = Exception.class) public String createOrder(Long productId, Integer quantity, Long userId) {

     // 1. 生成订单号
     String orderNo = UUID.randomUUID().toString().replace("-", "");
    
     // 2. 封装消息对象
     OrderDTO orderDTO = OrderDTO.builder()
             .orderNo(orderNo)
             .productId(productId)
             .userId(userId)
             .quantity(quantity)
             .build();
    
     // 3. 发送消息到订单交换机
     rabbitTemplate.convertAndSend("order.exchange", "order.routing.key", orderDTO);
    
     return orderNo;

    } }

java 复制 less 体验AI代码助手 代码解读复制代码@Component @RabbitListener(queues = "order.queue") @RequiredArgsConstructor public class OrderConsumer {

private final OrderMapper orderMapper;
private final ProductMapper productMapper;

@RabbitHandler
public void handleOrder(OrderDTO orderDTO) {
    try {
        // 幂等性校验:检查订单是否已存在
        OrderInfo existingOrder = orderMapper.selectByOrderNo(orderDTO.getOrderNo());
        if (existingOrder != null) {
            return; // 避免重复消费
        }

        // 查询商品价格(实际场景会走缓存)
        Product product = productMapper.selectById(orderDTO.getProductId());

        // 构建订单实体
        OrderInfo order = OrderInfo.builder()
                .orderNo(orderDTO.getOrderNo())
                .productId(orderDTO.getProductId())
                .userId(orderDTO.getUserId())
                .amount(product.getPrice().multiply(new BigDecimal(orderDTO.getQuantity())))
                .status(0) // 待支付
                .build();

        orderMapper.insert(order);

        // TODO: 可以在这里触发“支付超时自动取消”的延时消息
    } catch (Exception e) {
        // 实际场景需要记录死信队列或人工介入
        throw new RuntimeException("订单入库失败");
    }
}

}

三、 支付系统:核心架构设计 支付系统要求极高的稳定性与数据一致性。我们采用“策略模式”对接不同支付渠道(支付宝、微信),并使用“状态机”管理支付状态。

  1. 支付核心接口设计 java 复制 typescript 体验AI代码助手 代码解读复制代码// 支付渠道策略接口 public interface PaymentStrategy {

    /**

    • 统一下单接口

    • / PaymentResult unifiedOrder(PaymentRequest request);

      /**

    • 支付回调处理

    • / Boolean handleCallback(Map<String, String> params); }

// 支付宝实现 @Component("aliPayStrategy") public class AliPayStrategy implements PaymentStrategy {

@Override
public PaymentResult unifiedOrder(PaymentRequest request) {
    // 模拟调用支付宝 SDK
    System.out.println("调用支付宝 API,订单号:" + request.getOrderNo());
    return PaymentResult.builder()
            .payUrl("https://openapi.alipay.com/..." + request.getOrderNo())
            .build();
}

@Override
public Boolean handleCallback(Map<String, String> params) {
    // 1. 验签(必须)
    // 2. 校验金额与订单号
    // 3. 执行业务逻辑
    return true;
}

}

  1. 支付上下文与工厂模式 根据前端传来的渠道类型,动态选择具体的支付实现。 java 复制 typescript 体验AI代码助手 代码解读复制代码@Service @RequiredArgsConstructor public class PaymentContext {

    private final Map<String, PaymentStrategy> strategyMap;

    /**

    • 发起支付
    • / public PaymentResult executePayment(PaymentRequest request) { PaymentStrategy strategy = strategyMap.get(request.getChannel() + "PayStrategy"); if (strategy == null) {
        throw new IllegalArgumentException("不支持的支付渠道");
      } return strategy.unifiedOrder(request); } }
  2. 支付回调与分布式事务处理 这是最关键的一步。支付系统收到成功回调后,需要更新自身状态,并通知电商系统更新订单状态。这里使用 Seata 或简单的消息队列实现最终一致性。 java 复制 less 体验AI代码助手 代码解读复制代码@RestController @RequestMapping("/api/payment") @RequiredArgsConstructor public class PaymentCallbackController {

    private final PaymentService paymentService; private final OrderFeignClient orderFeignClient; // 调用电商系统接口

    @PostMapping("/callback/ali") public String aliCallback(@RequestBody Map<String, String> params) {

     // 1. 校验签名与金额
     if (!paymentService.verifyAliPay(params)) {
         return "fail";
     }
    
     String tradeNo = params.get("out_trade_no"); // 商户订单号
    
     // 2. 更新支付流水状态(加分布式锁防止并发回调)
     PaymentRecord record = paymentService.updatePaymentStatus(tradeNo, "SUCCESS");
    
     if (record != null && "SUCCESS".equals(record.getStatus())) {
         // 3. 通知电商系统:支付成功
         // 为了保证高可用,通常这里结合本地消息表做重试
         try {
             orderFeignClient.updateOrderStatus(record.getOrderNo(), 1);
         } catch (Exception e) {
             // 记录日志,通过补偿任务重试
             log.error("通知电商系统失败", e);
         }
     }
    
     return "success";

    } }

四、 架构设计要点总结 在从单体应用向微服务演进的过程中,这两个系统的实战经验至关重要:

高并发处理:电商侧通过 Redis 预扣库存 + MQ 异步解耦,将流量冲击挡在数据库之外。 分布式事务:支付与订单属于两个不同的数据库,不能使用本地事务。实战中推荐使用 MQ 最终一致性方案(如本例),或者在强一致性要求场景下使用 Seata AT 模式。 系统解耦:支付系统不应强依赖于电商系统。回调通知失败时,支付系统应有重试机制;电商系统也应提供主动查询支付状态的接口。 策略模式:支付渠道多变,策略模式能有效隔离代码,新增渠道只需新增实现类,符合开闭原则。

通过以上实战代码,你可以看到 Java 技术栈在处理复杂业务逻辑时的强大能力。真正的架构师不仅仅是会写代码,更是在业务与技术之间寻找最平衡的解决方案。

https://infogram.com/9862pdf-1h1749wqm57vq2z https://infogram.com/9862pdf-1hnp27eqy81zy4g https://infogram.com/9862pdf-1h9j6q75k18e54g https://infogram.com/9862pdf-1hnp27eqy81ln4g https://infogram.com/9862pdf-1h1749wqm58wq2z https://infogram.com/9862pdf-1h7v4pd08zkjj4k https://infogram.com/9862pdf-1hmr6g8jzq9mz2n https://infogram.com/9862pdf-1h9j6q75k1w954g https://infogram.com/9862pdf-1hnq41opzv1dk23 https://infogram.com/9862pdf-1hxj48mqek8kq2v

点赞
收藏
评论区
推荐文章
Wesley13 Wesley13
4年前
Java生鲜电商平台
Java生鲜电商平台订单模块状态机架构设计_说明:在Java生鲜电商平台中订单的状态流转业务__我们知道一个订单会有很多种状态:临时单、已下单、待支付、待收货、待评价、已完成,退货中等等。每一种状态都和其扭转前的状态、在扭转前状态所执行的操作有关。_一实例说明举例一个过程:用户将商品加入购物车,在后台生成了一个所谓的“临
Wesley13 Wesley13
4年前
JAVA架构设计,JAVA大型网站架构设计,JAVA高级和架构师数据库,分布式事务,大型分布式综合电商项目实战等视频教程
JAVA高级和架构师进阶,微服务架构,亿级高并发,分布式架构,源码剖析系列,项目实战,设计模式实战,数据结构与算法,消息中间件,并发编程多线程,服务器系列,数据库,分布式事务,大型分布式综合电商项目实战等视频教程
Wesley13 Wesley13
4年前
vivo 全球商城:架构演进之路
本文讲述vivo官方商城从单体应用到具备综合能力电商平台的演进,系统架构往服务化、中台化的变迁历程。一、前言vivo官方商城,是vivo官方的线上电商平台,主营vivo手机及专属配件。经过几年发展,已经完成了从单体应用到具备综合能力电商平台的演进,整体系统架构也逐步往服务化、中台化变迁。我们在这条系统架构升级的道路中,实践出了一些系统架