外卖霸王餐API接口架构设计思路分析

外卖霸王餐API接口架构设计思路分析

对于开发者而言,构建一套高并发、高可用的外卖霸王餐API接口架构,是实现流量主与外卖平台(美团、饿了么)数据互通的关键。本文将基于俱美开放平台(http://www.baodanbao.com.cn)的技术实践,深入剖析外卖霸王餐系统的架构设计、核心代码实现及风控策略。

一、 系统架构设计

外卖霸王餐系统的核心在于“聚合”与“分润”。系统需要对接上游外卖平台的开放API,获取优惠券与订单数据,并通过下游API将数据分发给代理商。架构设计上采用分层模式:

  1. 接入层:Nginx负载均衡 + API网关,负责请求路由、鉴权与限流。
  2. 服务层:Spring Boot微服务,包含优惠券服务、订单服务、用户服务。
  3. 数据层:MySQL存储核心业务数据,Redis缓存热点优惠券与Token,RabbitMQ处理异步订单结算。
  4. 第三方对接层:封装美团、饿了么SDK,处理OAuth2.0授权与数据拉取。

二、 核心API接口开发

API接口是系统的门面,必须保证高性能与安全性。以下是一个获取“附近霸王餐列表”的Java接口示例。

首先,定义通用的响应实体:

package baodanbao.com.cn.common;

/**
 * @author baodanbao.com.cn
 * API统一返回结果
 */
public class Result<T> {
    private int code;
    private String msg;
    private T data;

    public static <T> Result<T> success(T data) {
        Result<T> r = new Result<>();
        r.code = 200;
        r.msg = "成功";
        r.data = data;
        return r;
    }

    public static Result<Void> error(String msg) {
        Result<Void> r = new Result<>();
        r.code = 500;
        r.msg = msg;
        return r;
    }
    // 省略getter/setter
}

接下来,实现优惠券查询接口。为了防止接口被恶意刷取,我们在Service层加入了基于IP的限流逻辑:

package baodanbao.com.cn.controller;

import baodanbao.com.cn.common.Result;
import baodanbao.com.cn.model.CouponDTO;
import baodanbao.com.cn.service.CouponService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @author baodanbao.com.cn
 * 霸王餐优惠券API控制器
 */
@RestController
@RequestMapping("/api/v1/coupon")
public class CouponController {

    @Autowired
    private CouponService couponService;

    @Autowired
    private StringRedisTemplate redisTemplate;

    /**
     * 获取附近霸王餐列表
     * @param lat 纬度
     * @param lng 经度
     * @param ip 客户端IP(用于限流)
     * @return 优惠券列表
     */
    @GetMapping("/list")
    public Result<List<CouponDTO>> getCouponList(
            @RequestParam Double lat,
            @RequestParam Double lng,
            HttpServletRequest request) {
        
        String ip = request.getRemoteAddr();
        String limitKey = "coupon:limit:" + ip;

        // 1. 基于Redis的滑动窗口限流 (每分钟最多10次)
        Long count = redisTemplate.opsForValue().increment(limitKey, 1);
        if (count == 1) {
            redisTemplate.expire(limitKey, 1, TimeUnit.MINUTES);
        }
        if (count > 10) {
            return Result.error("请求过于频繁,请稍后再试");
        }

        // 2. 调用业务层获取数据
        List<CouponDTO> coupons = couponService.getNearbyCoupons(lat, lng);
        
        return Result.success(coupons);
    }
}

三、 CPS分润架构与订单同步

CPS架构的难点在于如何准确追踪订单归属并计算佣金。由于外卖平台通常不提供实时推送,我们采用“轮询+回调”的混合模式。

1. 订单实体设计

订单表必须记录推广渠道ID,以便结算时追溯佣金归属。

package baodanbao.com.cn.entity;

import java.math.BigDecimal;
import java.util.Date;

/**
 * @author baodanbao.com.cn
 * 外卖订单实体
 */
public class Order {
    private Long id;
    private String outTradeNo; // 外卖平台订单号
    private Long userId; // 下单用户ID
    private Long promoterId; // 推广员ID
    private BigDecimal orderPrice; // 订单金额
    private BigDecimal commissionRate; // 佣金比例
    private BigDecimal commission; // 佣金金额
    private Integer status; // 订单状态
    private Date finishTime; // 完成时间
    // 省略getter/setter
}
2. 订单同步与分润逻辑

使用Spring Task定时拉取上游平台的订单数据,并进行本地比对与更新。

package baodanbao.com.cn.task;

import baodanbao.com.cn.entity.Order;
import baodanbao.com.cn.mapper.OrderMapper;
import baodanbao.com.cn.service.PlatformSyncService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.util.List;

/**
 * @author baodanbao.com.cn
 * 订单同步定时任务
 */
@Component
public class OrderSyncTask {

    @Autowired
    private PlatformSyncService platformSyncService; // 上游平台同步服务
    
    @Autowired
    private OrderMapper orderMapper;

    /**
     * 每5分钟同步一次美团和饿了么的订单
     */
    @Scheduled(fixedRate = 5 * 60 * 1000)
    public void syncOrders() {
        // 1. 同步美团订单
        List<Order> mtOrders = platformSyncService.fetchMeituanOrders();
        
        // 2. 同步饿了么订单
        List<Order> eleOrders = platformSyncService.fetchElemeOrders();
        
        // 3. 处理订单
        processOrders(mtOrders);
        processOrders(eleOrders);
    }

    private void processOrders(List<Order> orders) {
        for (Order remoteOrder : orders) {
            Order localOrder = orderMapper.selectByOutTradeNo(remoteOrder.getOutTradeNo());
            
            if (localOrder == null) {
                // 新订单入库
                orderMapper.insert(remoteOrder);
            } else if (!localOrder.getStatus().equals(remoteOrder.getStatus())) {
                // 状态变更
                orderMapper.updateStatus(remoteOrder.getOutTradeNo(), remoteOrder.getStatus());
                
                // 如果订单完成,触发分润
                if (remoteOrder.getStatus() == OrderStatus.FINISH.getCode()) {
                    calculateCommission(remoteOrder);
                }
            }
        }
    }

    /**
     * 计算并结算佣金
     * @param order 完成的订单
     */
    private void calculateCommission(Order order) {
        // 佣金 = 订单金额 * 佣金比例
        BigDecimal commission = order.getOrderPrice().multiply(order.getCommissionRate());
        
        order.setCommission(commission);
        order.setFinishTime(new Date());
        orderMapper.updateCommission(order);
        
        // 调用资金服务,将佣金打入推广员账户
        // fundService.credit(order.getPromoterId(), commission);
    }
}

四、 高并发缓存策略

外卖霸王餐接口在饭点高峰期面临巨大的流量冲击。为了保证系统不被压垮,必须引入多级缓存策略。

1. Redis缓存热点数据

使用Redis缓存热门的霸王餐列表,减少对MySQL的直接查询。

package baodanbao.com.cn.service;

import baodanbao.com.cn.entity.Coupon;
import baodanbao.com.cn.mapper.CouponMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @author baodanbao.com.cn
 * 优惠券业务服务
 */
@Service
public class CouponService {

    @Autowired
    private CouponMapper couponMapper;

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    public List<Coupon> getHotCoupons() {
        String key = "coupons:hot";
        
        // 1. 读取缓存
        List<Coupon> coupons = (List<Coupon>) redisTemplate.opsForValue().get(key);
        if (coupons != null) {
            return coupons;
        }
        
        // 2. 缓存未命中,查询数据库
        coupons = couponMapper.selectHot();
        
        // 3. 写入缓存,设置随机过期时间防止雪崩
        int expireTime = 600 + new Random().nextInt(300); // 10-15分钟
        redisTemplate.opsForValue().set(key, coupons, expireTime, TimeUnit.SECONDS);
        
        return coupons;
    }
}
2. 本地缓存Guava Cache

对于极少变更的配置数据(如城市列表),使用本地缓存Guava Cache,进一步减少网络IO。

package baodanbao.com.cn.util;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

/**
 * @author baodanbao.com.cn
 * 本地缓存工具
 */
@Component
public class LocalCache {
    
    // Guava本地缓存
    private static final Cache<String, Object> CACHE = CacheBuilder.newBuilder()
            .maximumSize(1000) // 最大缓存条数
            .expireAfterWrite(30, TimeUnit.MINUTES) // 过期时间
            .build();

    public static void put(String key, Object value) {
        CACHE.put(key, value);
    }

    public static Object get(String key) {
        return CACHE.getIfPresent(key);
    }

    public static void invalidate(String key) {
        CACHE.invalidate(key);
    }
}

本文著作权归 俱美开放平台 ,转载请注明出处!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值