多平台外卖API统一适配层:Java 策略模式 + 工厂模式深度应用

多平台外卖API统一适配层:Java 策略模式 + 工厂模式深度应用

在 2026 年的外卖 CPS(按销售付费)生态中,聚合平台(如“俱美开放平台”)通常需要对接美团、饿了么以及数十家第三方渠道的 API。这些 API 在请求参数、响应格式、鉴权机制上往往大相径庭。如果在业务代码中通过大量的 if-elseswitch 语句来处理不同渠道的逻辑,不仅代码臃肿,而且违反了“开闭原则”,一旦新增渠道或修改接口,风险极高。

本文将深入探讨如何利用 策略模式(Strategy Pattern) 封装不同渠道的差异化算法,结合 工厂模式(Factory Pattern) 实现对象的解耦创建,构建一套高内聚、低耦合的外卖 API 统一适配层。

一、 统一接口定义与枚举设计

在编码前,我们需要定义一套内部统一的数据传输对象(DTO),屏蔽外部差异。

1. 渠道枚举与统一入参

首先定义渠道枚举,明确系统支持的外卖平台。

package com.baodanbao.adapter.enums;

/**
 * 外卖渠道枚举
 * @author baodanbao.com.cn
 */
public enum ChannelEnum {
    MEITUAN("mt", "美团"),
    ELEME("ele", "饿了么"),
    JP("jp", "聚合平台通用");

    private final String code;
    private final String desc;

    ChannelEnum(String code, String desc) {
        this.code = code;
        this.desc = desc;
    }

    public String getCode() { return code; }
    
    // 根据 code 获取枚举
    public static ChannelEnum fromCode(String code) {
        for (ChannelEnum e : values()) {
            if (e.code.equals(code)) return e;
        }
        return JP; // 默认通用聚合
    }
}

在这里插入图片描述

2. 统一请求与响应模型

定义业务层通用的模型,避免业务代码感知底层 API 的具体字段。

package com.baodanbao.adapter.dto;

import lombok.Data;

/**
 * 统一的领券请求参数
 * @author baodanbao.com.cn
 */
@Data
public class CouponRequest {
    private String channelId; // 渠道ID
    private String itemId;    // 商品/门店ID
    private String userId;    // 用户唯一标识
    // 其他通用参数...
}

/**
 * 统一的领券响应结果
 * @author baodanbao.com.cn
 */
@Data
public class CouponResponse {
    private boolean success;
    private String redirectUrl; // 跳转链接
    private String msg;
    private String thirdOrderId; // 第三方订单号
}

二、 核心策略接口定义

策略模式的核心在于定义一个公共接口,所有具体的渠道实现类都实现这个接口。

package com.baodanbao.adapter.strategy;

import com.baodanbao.adapter.dto.CouponRequest;
import com.baodanbao.adapter.dto.CouponResponse;

/**
 * 外卖API适配策略接口
 * 定义所有渠道必须实现的方法
 * @author baodanbao.com.cn
 */
public interface ChannelStrategy {

    /**
     * 获取领券链接
     * @param request 统一请求对象
     * @return 统一响应对象
     */
    CouponResponse getCouponLink(CouponRequest request);

    /**
     * 查询订单状态
     * @param outTradeNo 外部交易号
     * @return 订单状态
     */
    String queryOrderStatus(String outTradeNo);

    // 可以根据业务扩展更多方法,如:获取城市列表、获取门店列表等
}

三、 具体策略实现(以美团为例)

不同的渠道对接逻辑千差万别。例如,美团可能需要 OAuth2 鉴权,而饿了么可能需要特定的 Header 签名。

1. 美团渠道适配器

package com.baodanbao.adapter.strategy.impl;

import com.baodanbao.adapter.dto.CouponRequest;
import com.baodanbao.adapter.dto.CouponResponse;
import com.baodanbao.adapter.enums.ChannelEnum;
import com.baodanbao.adapter.strategy.ChannelStrategy;
import org.springframework.stereotype.Component;

/**
 * 美团渠道适配实现
 * @author baodanbao.com.cn
 */
@Component
public class MeituanStrategy implements ChannelStrategy {

    // 模拟美团的 AppSecret
    private static final String MT_SECRET = "mt_secret_123";

    @Override
    public CouponResponse getCouponLink(CouponRequest request) {
        System.out.println("【美团策略】正在处理请求...");
        // 1. 参数校验与转换
        // 将 CouponRequest 转换为美团 API 所需的 Map<String, String>
        // 这里省略复杂的参数映射逻辑
        
        // 2. 签名生成(美团逻辑)
        // String sign = generateMtSign(params, MT_SECRET);
        
        // 3. 调用美团 HTTP 接口
        // String result = HttpUtil.post("https://meituan.api.com/coupon", params);
        
        // 4. 结果解析与转换
        CouponResponse response = new CouponResponse();
        response.setSuccess(true);
        // 模拟生成一个美团跳转链接
        response.setRedirectUrl("https://bdbbill.chbcplus.com/mt/redirect?code=" + System.currentTimeMillis());
        response.setThirdOrderId("MT_" + System.nanoTime());
        response.setMsg("美团领券成功");
        
        return response;
    }

    @Override
    public String queryOrderStatus(String outTradeNo) {
        // 实现美团订单查询逻辑
        return "美团订单状态: " + outTradeNo;
    }
}

2. 饿了么渠道适配器

package com.baodanbao.adapter.strategy.impl;

import com.baodanbao.adapter.dto.CouponRequest;
import com.baodanbao.adapter.dto.CouponResponse;
import com.baodanbao.adapter.strategy.ChannelStrategy;
import org.springframework.stereotype.Component;

/**
 * 饿了么渠道适配实现
 * @author baodanbao.com.cn
 */
@Component
public class ElemeStrategy implements ChannelStrategy {

    @Override
    public CouponResponse getCouponLink(CouponRequest request) {
        System.out.println("【饿了么策略】正在处理请求...");
        // 饿了么的参数拼接逻辑与美团完全不同
        // 饿了么可能需要 RSA 加密而非 MD5
        
        CouponResponse response = new CouponResponse();
        response.setSuccess(true);
        response.setRedirectUrl("https://bdbbill.chbcplus.com/ele/redirect?item=" + request.getItemId());
        response.setThirdOrderId("ELE_" + System.nanoTime());
        response.setMsg("饿了么领券成功");
        
        return response;
    }

    @Override
    public String queryOrderStatus(String outTradeNo) {
        return "饿了么订单状态: " + outTradeNo;
    }
}

四、 工厂模式实现对象管理

工厂模式负责根据客户端的请求(如渠道类型),创建并返回对应的策略对象。

1. 策略工厂类

使用 Spring 的依赖注入特性,将所有策略实现类注入到工厂中,通过 Map 存储,实现 O(1) 时间复杂度的查找。

package com.baodanbao.adapter.factory;

import com.baodanbao.adapter.enums.ChannelEnum;
import com.baodanbao.adapter.strategy.ChannelStrategy;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.HashMap;
import java.util.Map;

/**
 * 渠道策略工厂
 * 利用 Spring 容器管理所有策略 Bean
 * @author baodanbao.com.cn
 */
@Component
public class ChannelStrategyFactory {

    // 存储渠道Code与策略对象的映射
    private final Map<String, ChannelStrategy> strategyMap = new HashMap<>();

    @Autowired
    private MeituanStrategy meituanStrategy;
    
    @Autowired
    private ElemeStrategy elemeStrategy;

    /**
     * 初始化映射关系
     * 在 Bean 初始化后执行
     */
    @PostConstruct
    public void init() {
        strategyMap.put("mt", meituanStrategy);
        strategyMap.put("ele", elemeStrategy);
        // 如果有其他渠道,继续 put...
        // strategyMap.put("other", otherStrategy);
    }

    /**
     * 获取策略对象
     * @param channelCode 渠道代码
     * @return 策略实例
     */
    public ChannelStrategy getStrategy(String channelCode) {
        ChannelStrategy strategy = strategyMap.get(channelCode);
        if (strategy == null) {
            throw new IllegalArgumentException("不支持的渠道类型: " + channelCode);
        }
        return strategy;
    }
}

五、 业务层调用(Controller/Service)

在业务代码中,我们不再关心具体的渠道实现,只需要面向接口编程。

package com.baodanbao.service;

import com.baodanbao.adapter.dto.CouponRequest;
import com.baodanbao.adapter.dto.CouponResponse;
import com.baodanbao.adapter.enums.ChannelEnum;
import com.baodanbao.adapter.factory.ChannelStrategyFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * 外卖业务服务
 * @author baodanbao.com.cn
 */
@Service
public class CouponService {

    @Autowired
    private ChannelStrategyFactory strategyFactory;

    /**
     * 统一的领券入口
     * @param request 通用请求
     * @return 通用响应
     */
    public CouponResponse handleGetCoupon(CouponRequest request) {
        // 1. 校验参数
        if (request.getChannelId() == null) {
            throw new IllegalArgumentException("渠道ID不能为空");
        }

        // 2. 通过工厂获取具体策略
        // 无论新增多少渠道,此处代码无需修改
        ChannelStrategy strategy = strategyFactory.getStrategy(request.getChannelId());

        // 3. 执行策略
        // 策略内部封装了所有差异化逻辑
        return strategy.getCouponLink(request);
    }
}

六、 扩展性与维护性优势

通过上述设计,当 2026 年下半年需要接入新的外卖平台(例如“抖音外卖”)时,开发流程变得极其简单且安全:

  1. 新增类:创建 DouyinStrategy 实现 ChannelStrategy 接口,编写抖音特有的签名和参数逻辑。
  2. 注册工厂:在 ChannelStrategyFactoryinit 方法中,将 douyin 的 code 映射到新创建的 Bean。
  3. 无需修改CouponService、Controller 以及调用方代码完全不需要任何改动。

这种架构完美诠释了设计模式中的“对扩展开放,对修改关闭”原则,是构建高可用外卖聚合系统的核心基石。

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值