淘东电商项目(58) -聚合支付(基于设计模式自动跳转支付接口)

简介: 淘东电商项目(58) -聚合支付(基于设计模式自动跳转支付接口)

引言

本文代码已提交至Github(版本号:dd2a1cfed9cd936853ee8b28fa9ca05eb61d25a4),有兴趣的同学可以下载来看看:https://github.com/ylw-github/taodong-shop

在上一篇博客《淘东电商项目(57) -聚合支付(支付令牌接口)》,已经讲解完了如下图的第1~4个步骤,接下来本文要讲解第5个步骤,选择支付方式提交后,后台使用设计模式来实现自动跳转到对应的支付接口,如选择“银联支付”,请求后台后,自动跳转到银联支付的实现,选择“支付宝支付”,自动跳转到支付宝支付的实现。同时,在后台新增新的支付方式,直接增加一个支付实现类就可以了,得益于设计模式,减少了if else的编写。

本文目录结构:

l____引言

l____ 1. 为什么要使用设计模式?

l____ 2. 支付策略的代码原理

l____ 3. 设计模式的实现

l____ 4. 测试

1. 为什么要使用设计模式?

在上一篇博客,我们根据token获取支付详情页面如下:

可以看到上图有两种支付方式,分别是“银联支付”和“支付宝支付”,如果我们写后台的代码,是怎样的呢?伪代码如下:

if("yinlian_pay".equals(CHANNEL_ID)){
  //银联支付的具体实现
}else if("ali_pay".equals(CHANNEL_ID)){
    //支付宝支付的具体实现
}

可以看到,如果将来有新的需求,需要增加“微信支付”或者“小米支付”,那岂不是要写很多的if else?那该如何解决这个问题呢?这个时候用到了策略模式。关于策略模式,之前我也有写过博客,有兴趣的同学可以参考下:《设计模式1 - 策略模式【Strategy Pattern】》或者《设计模式系列教程(13) - 策略模式》

2. 支付策略的代码原理

下面用文字描述下针对多种支付,策略模式的使用流程:

  1. 首先用户在页面确认完订单,选择支付方式后,会提交一个参数channelId,用来表示支付类型。
  2. 后台接收到channelId后,会根据channelId去查询数据库,查询对应的实现的类路径,如:channelId为“ali_pay”的类路径为“com.ylw.service.impl.AliPayContextImpl”。
  3. 通过反射,传入对应的类路径,生成实例。
  4. 由于可能会多次调用,所以可以使用工厂模式,对应的实现类只需要生成一次即可,不用每次请求,每次反射生成。

从上面的描述,可以看到用到的设计模式有“策略模式”和“工厂模式”。下面来讲解具体的代码实现。

3. 设计模式的实现

策略模式会模式涉及到三个角色:

  • 环境(Context)角色:持有一个Strategy的引用。
  • 抽象策略(Strategy)角色:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。
  • 具体策略(ConcreteStrategy)角色:包装了相关的算法或行为。

①先看看抽象策略角色PayStrategy:

/**
 * description: 支付接口共同实现行为算法
 * create by: YangLinWei
 * create time: 2020/5/13 4:41 下午
 */
public interface PayStrategy {
    /**
     * @param pymentChannel     渠道参数
     * @param payMentTransacDTO 支付参数
     * @return
     */
    public String toPayHtml(PaymentChannelEntity pymentChannel, PayMentTransacDTO payMentTransacDTO);
}

②具体策略角色(ConcreteStrategy,支付宝与银联):

/**
 * description: 支付宝支付渠道
 * create by: YangLinWei
 * create time: 2020/5/13 4:41 下午
 */
@Slf4j
public class AliPayStrategy implements PayStrategy {
  @Override
  public String toPayHtml(PaymentChannelEntity pymentChannel, PayMentTransacDTO payMentTransacDTO) {
    log.info(">>>>>支付宝参数封装开始<<<<<<<<");
    return "支付宝支付from表单提交";
  }
}
/**
 * description: 银联支付渠道实现
 * create by: YangLinWei
 * create time: 2020/5/13 4:41 下午
 */
@Slf4j
public class UnionPayStrategy implements PayStrategy {
    @Override
    public String toPayHtml(PaymentChannelEntity pymentChannel, PayMentTransacDTO payMentTransacDTO) {
        log.info(">>>>>银联参数封装开始<<<<<<<<");
        // Plugin
        return "银联支付from表单提交";
    }
}

③环境(Context)角色:

接口:

public interface PayContextService {
    @GetMapping("/toPayHtml")
    public BaseResponse<JSONObject> toPayHtml(String channelId, String payToken);
}

具体实现:

@RestController
public class PayContextServiceImpl extends BaseApiService<JSONObject> implements PayContextService {
  @Autowired
  private PaymentChannelMapper paymentChannelMapper;
  @Autowired
  private PayMentTransacInfoService payMentTransacInfoService;
  @Override
  public BaseResponse<JSONObject> toPayHtml(String channelId, String payToken) {
    // 1.使用渠道id获取渠道信息 classAddres
    PaymentChannelEntity pymentChannel = paymentChannelMapper.selectBychannelId(channelId);
    if (pymentChannel == null) {
      return setResultError("没有查询到该渠道信息");
    }
    // 2.使用payToken获取支付参数
    BaseResponse<PayMentTransacDTO> tokenByPayMentTransac = payMentTransacInfoService
        .tokenByPayMentTransac(payToken);
    if (!isSuccess(tokenByPayMentTransac)) {
      return setResultError(tokenByPayMentTransac.getMsg());
    }
    PayMentTransacDTO payMentTransacDTO = tokenByPayMentTransac.getData();
    // 3.执行具体的支付渠道的算法获取html表单数据 策略设计模式 使用java反射机制 执行具体方法
    String classAddres = pymentChannel.getClassAddres();
    PayStrategy payStrategy = StrategyFactory.getPayStrategy(classAddres);
    String payHtml = payStrategy.toPayHtml(pymentChannel, payMentTransacDTO);
    // 4.直接返回html
    JSONObject data = new JSONObject();
    data.put("payHtml", payHtml);
    return setResultSuccess(data);
  }

④从第3步骤,可以看到数据库根据传来的channelId去查询对应的支付类路径,然后使用工厂设计模式,生成PayStrategy(这里用到了java多态的特性),下面贴上StrategyFactory工厂的代码:

/**
 * description: 初始化不同的策略行为
 * create by: YangLinWei
 * create time: 2020/5/13 4:45 下午
 */
public class StrategyFactory {
    private static Map<String, PayStrategy> strategyBean = new ConcurrentHashMap<String, PayStrategy>();
    public static PayStrategy getPayStrategy(String classAddres) {
        try {
            if (StringUtils.isEmpty(classAddres)) {
                return null;
            }
            PayStrategy beanPayStrategy = strategyBean.get(classAddres);
            if (beanPayStrategy != null) {
                return beanPayStrategy;
            }
            // 1.使用Java的反射机制初始化子类
            Class<?> forName = Class.forName(classAddres);
            // 2.反射机制初始化对象
            PayStrategy payStrategy = (PayStrategy) forName.newInstance();
            strategyBean.put(classAddres, payStrategy);
            return payStrategy;
        } catch (Exception e) {
            return null;
        }
    }
}

代码到此结束,下面进行测试。

4. 测试

①首先在支付渠道表,定义好对应的类路径,贴上insert语句:

INSERT INTO `taodong-pay`.`payment_channel`(`ID`, `CHANNEL_NAME`, `CHANNEL_ID`, `MERCHANT_ID`, `SYNC_URL`, `ASYN_URL`, `PUBLIC_KEY`, `PRIVATE_KEY`, `CHANNEL_STATE`, `REVISION`, `CREATED_BY`, `CREATED_TIME`, `UPDATED_BY`, `UPDATED_TIME`, `CLASS_ADDRES`) VALUES (1, '银联支付', 'yinlian_pay', '777290058110048', 'http://localhost:8080/ACPSample_B2C/frontRcvResponse', 'http://222.222.222.222:8080/ACPSample_B2C/backRcvResponse', NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL, 'com.ylw.service.pay.strategy.impl.UnionPayStrategy');
INSERT INTO `taodong-pay`.`payment_channel`(`ID`, `CHANNEL_NAME`, `CHANNEL_ID`, `MERCHANT_ID`, `SYNC_URL`, `ASYN_URL`, `PUBLIC_KEY`, `PRIVATE_KEY`, `CHANNEL_STATE`, `REVISION`, `CREATED_BY`, `CREATED_TIME`, `UPDATED_BY`, `UPDATED_TIME`, `CLASS_ADDRES`) VALUES (2, '支付宝', 'ali_pay', '777666655522521', 'test', 'test', NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL, 'com.ylw.service.pay.strategy.impl.AliPayStrategy');

②启动项目,浏览器请求:http://localhost:8600/toPayHtml?channelId=yinlian_pay&payToken=pay_58432d643439455fb64d0ee83ea3c63c,可以看到请求进入了银联支付:

③测试阿里支付,浏览器输入http://localhost:8600/toPayHtml?channelId=ali_pay&payToken=pay_58432d643439455fb64d0ee83ea3c63c,可以看到进入了阿里支付:

④现在我们在数据库新增一条数据,增加微信支付:

INSERT INTO `taodong-pay`.`payment_channel`(`ID`, `CHANNEL_NAME`, `CHANNEL_ID`, `MERCHANT_ID`, `SYNC_URL`, `ASYN_URL`, `PUBLIC_KEY`, `PRIVATE_KEY`, `CHANNEL_STATE`, `REVISION`, `CREATED_BY`, `CREATED_TIME`, `UPDATED_BY`, `UPDATED_TIME`, `CLASS_ADDRES`) VALUES (3, '微信支付', 'weixin_pay', '777566655521234', 'test', 'test', NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL, 'com.ylw.service.pay.strategy.impl.WeixinPayStrategy');

⑤然后新增一个类WeixinPayStrategy,类路径为com.ylw.service.pay.strategy.impl.WeixinPayStrategy:

/**
 * description: 微信支付渠道
 * create by: YangLinWei
 * create time: 2020/5/13 5:10 下午
 */
@Slf4j
public class WeixinPayStrategy implements PayStrategy {
    @Override
    public String toPayHtml(PaymentChannelEntity pymentChannel, PayMentTransacDTO payMentTransacDTO) {
        log.info(">>>>>微信参数封装开始<<<<<<<<");
        // Plugin
        return "微信支付from表单提交";
    }
}

⑥重新启动支付服务,浏览器请求:http://localhost:8600/toPayHtml?channelId=weixin_pay&payToken=pay_58432d643439455fb64d0ee83ea3c63c

可以看到,到时如果我们需要新增支付方式,只需要在数据库新增一条数据和新增一个类就可以了,这就是策略模式的应用场景了。

本文完!

目录
相关文章
|
2月前
|
设计模式 Java API
重构旧代码的秘诀:用设计模式 - 适配器模式(Adapter)给Java项目带来新生
【4月更文挑战第7天】适配器模式是解决接口不兼容问题的结构型设计模式,通过引入适配器类实现目标接口并持有不兼容类引用,实现旧代码与新接口的协作。适用于处理兼容性问题、整合遗留代码和集成第三方库。应用时,识别不兼容接口,创建适配器类转换方法调用,然后替换原有引用。注意保持适配器简单、使用组合和考虑扩展性。过度使用可能导致系统复杂和维护成本增加,应谨慎使用。
|
2月前
|
设计模式 Java API
【设计模式】JAVA Design Patterns——Aggregator Microservices(聚合器微服务模式)
【设计模式】JAVA Design Patterns——Aggregator Microservices(聚合器微服务模式)
|
4天前
|
设计模式 Java
设计模式在Java项目中的实际应用
设计模式在Java项目中的实际应用
|
2天前
|
设计模式 Java
设计模式在Java项目中的实际应用
设计模式在Java项目中的实际应用
|
8月前
|
设计模式 SQL 数据库
淘东电商项目(61) -聚合支付(基于模板方法设计模式管理支付回调)
淘东电商项目(61) -聚合支付(基于模板方法设计模式管理支付回调)
51 0
|
2月前
|
设计模式 算法 Java
【设计模式】springboot3项目整合模板方法深入理解设计模式之模板方法(Template Method)
【设计模式】springboot3项目整合模板方法深入理解设计模式之模板方法(Template Method)
|
8月前
|
设计模式 SQL 数据库
淘东电商项目(62) -聚合支付(基于模板方法设计模式管理支付回调-支付宝)
淘东电商项目(62) -聚合支付(基于模板方法设计模式管理支付回调-支付宝)
43 0
|
9月前
|
设计模式 Java
Java设计模式七大原则-合成聚合复用原则
Java设计模式七大原则-合成聚合复用原则
62 0
|
9月前
|
设计模式 分布式计算 Java
你敢信?清华毕业大佬用了一个坦克大战项目就讲完了23种设计模式
坦克大战 一、需求分析 坦克大战中有我方坦克和敌方坦克,我方坦克有一个,敌方坦克有多个。坦克可以移动,也可以发射子弹。我方坦克可以通过上下左右键来控制方向,敌方坦克自动改变方向。在游戏窗体中有障碍物,包括砖墙、钢墙、水墙和草地。坦克可以穿过草地,在遇到其他障碍物时,我方坦克停止移动,并通过操纵改变方向再移动,敌方坦克遇到其他障碍物时可以自动的改变方向。我方坦克有三次生命值,当与敌方坦克或敌方子弹相撞时,生命值减- -, 当生命值是零时,敌方胜利,游戏结束。敌方坦克的生命值为-一,当敌方坦克与我方坦克或者我方子弹相遇就消失,同时我方子弹也消失,并产生爆炸。当我方坦克把敌方坦克消灭完后,我方胜利
48 0
|
10月前
|
设计模式 Java API
听说有人用一个坦克大战项目把23种设计模式讲完了?(附源码)
长期以来给大家分享的都是技术和文档的一些内容,大家应该已经看腻了。今天给大家分享一波java的坦克大战项目和23种设计模式视频吧,让大家来实践一下,希望大家能够喜欢!