支付服务代码设计(策略模式,可扩展,接入方便)

最近一直在深入学习一些知识,整理出一些东西一直没有时间写出来,后续会慢慢总结一下写在博客上,对于博客还是不想专门写知识点,总结和提升终究是为了自己,写出来的博客为了让别人看的好,更为了自己的成长和记录,这种记录不仅仅是知识,知识不仅仅是软硬件,还有经验,有思想,这些才是更宝贵的

我们很难真正的交心交流,但是却可以把心放在这里,谁看到了,和它聊一聊那也是好的;这种沟通的活跃就像我不喜欢程序员越来越像机器人一样,像法律一样死板,应该还是一个有血有肉有思想的人,不是猿

 原来分享了支付服务的一些点,今天实操一下具体的设计,最近也看了一些其他人的设计,觉得挺好和大家分享一下

服务代码的设计要注重设计模式的6大原则

下面给出逻辑关系图,相当于策略模式的抽象版,各个支付方式独立封装,通过PayCoreManager调用服务,内部通过payChannel获取实例,调用PayStrategy,进而调用各自的封装

第三方调用的支付服务接口可以不耦合到一起,如微信支付,支付宝支付接口分开;

    @GetMapping("/t2")
    public void t2(PayReq payReq){
        //调用支付接口,传入支付方式以及参数信息 对于业务方来说可以用支付服务的枚举类,或者多种支付的接口分开,分别由支付服务本身设置
        //如payReq.setPayType(PaymentEnums.ALI_PAY.getCode());
        payCoreManager.payChoose(payReq);
    }


    public String notifyUrl(HttpServletRequest request) {
        // 获取支付宝POST过来反馈信息
        Map<String, String> params = AliPayApi.toMap(request);
        CallBackParam callBackParam = new CallBackParam();
        callBackParam.setChannel(PaymentEnums.ALI_PAY.getCode());
        callBackParam.setParam(params);
        ServerResponse res = payCoreManager.payNotify(callBackParam);
        if ((boolean)res.getData() == true) {
            return "success";
        }else {
            return "failure";
        }
    }

调用支付接口,或者回调接口 ,面向对象

public interface PayCoreManager {
    /**
     * @Author: 老乡
     * @Date: 2020/6/13 16:44
     * @Describe: pay
     */
    ServerResponse payChoose(PayReq payReq);

    /**
     * @Author: 老乡
     * @Date: 2020/6/13 22:22
     * @Describe: 查询
     */
    ServerResponse getOrderInfo(PayReq payReq);

    /**
     * @Author: 老乡
     * @Date: 2020/6/13 22:48
     * @Describe: 回调
     */
    ServerResponse payNotify(CallBackParam callBackParam);
}

实现类,获取map中实例进行调用,可控制事务,业务处理也可放到这一层

@Service
public class PayCoreManagerImpl implements PayCoreManager {

    @Override
    public ServerResponse payChoose(PayReq payReq) {
        //从静态map支付类别中取出对应的支付类别,对应的实例PayStrategy,若没有应检查是否对应
        //不同的类别获取到不同的支付实例
        PayStrategy p = PayStrategyContent.strategyMap.get(payReq.getPayType());
        if (p == null) {
            return ServerResponse.createByErrorMessage("payType is not valid");
        }
        //调用支付封装接口
        return p.toPayCore(payReq);
    }

    @Override
    public ServerResponse getOrderInfo(PayReq payReq) {
        PayStrategy p = PayStrategyContent.strategyMap.get(payReq.getPayType());
        if (p == null) {
            return ServerResponse.createByErrorMessage("payType is not valid");
        }
        return p.getPayInfo(payReq);
    }

    @Transactional
    @Override
    public ServerResponse payNotify(CallBackParam callBackParam) {
        PayStrategy p = PayStrategyContent.strategyMap.get(callBackParam.getChannel());
        if (p == null) {
            return ServerResponse.createByErrorMessage("payType is not valid");
        }

        return ServerResponse.createBySuccess(p.payNotify(callBackParam));
    }

}

各自支付策略

public interface PayStrategy {
    /**
     * @Author: 老乡
     * @Date: 2020/6/13 21:50
     * @Describe: 支付接口
     * 接入新的支付方式只需要增加枚举类,继承PayStrategyContent抽象类,重写支付等功能接口
     */
    ServerResponse toPayCore(PayReq payReq);

    /**
     * @Author: 老乡
     * @Date: 2020/6/13 21:54
     * @Describe: 查询
     */
    ServerResponse getPayInfo(PayReq payReq);

    /**
     * @Author: 老乡
     * @Date: 2020/6/14 00:00
     * @Describe: notify
     */
    boolean payNotify(CallBackParam callBackParam);
}

抽象类实现策略接口,预加载各个支付策略为Map,value为当前策略对象PayStrategy,各自支付抽象后由子类实现,使用ConcurrentHashMap保证线程安全。这就是其他服务能够根据payChannel获取实例的关键点

验签成功后处理业务

/**
 * @Author: 老乡
 * @Date: 2020/6/13 22:19
 * @Describe: 当增加一个功能的时候可以在抽象类中实现接口,通过抽象方法让子类去实现
 * 如果有的子类没有用到这个功能也可以不重写,将类改为抽象类即可;
 * 不过一般抽象出来的功能都是共用的行为,所以即使暂时不用也可以先重写后放在那里
 * <p>
 * 此抽象类完成了对无法选择的延迟到子类实现,实现了PayStrategy接口,转化找到
 */
public abstract class PayStrategyContent implements PayStrategy {

    /**
     * 各种支付策略实例预加载
     */
    public static ConcurrentHashMap<Integer, PayStrategy> strategyMap = new ConcurrentHashMap<>(20);

    /**
     * @Author: 老乡
     * @Date: 2020/6/13 22:11
     * @Describe: 只在启动时加载一次,加载类别在子类中控制
     * this为当前PayStrategy,因为调用的时候取到具体的实例如支付宝实例,返回的是接口PayStrategy
     */
    @PostConstruct
    public void init() {
        strategyMap.put(channelInit(), this);
        System.out.println("map:" + JSON.toJSONString(strategyMap));
    }

    /**
     * @Author: 老乡
     * @Date: 2020/6/13 21:51
     * @Describe: 支付实现类,具体支付类别由子类实现,消除if else分支,接入其他支付时不违反开闭原则
     */
    @Override
    public ServerResponse toPayCore(PayReq payReq) {
        return toPay(payReq);
    }

    /**
     * @Author: 老乡
     * @Date: 2020/6/13 21:56
     * @Describe: 查询
     */
    @Override
    public ServerResponse getPayInfo(PayReq payReq) {
        return getInfo();
    }

    @Override
    public boolean payNotify(CallBackParam callBackParam){
        String orderSn = pNotifyCheckSign(callBackParam);
        if (StrUtil.isEmpty(orderSn)) {
            //验签失败
            return false;
        }

        //处理业务 添加记录,通知业务方
        return true;
    }

    //支付
    public abstract ServerResponse toPay(PayReq payReq);

    //查询
    public abstract ServerResponse getInfo();

    //验签 此处验签
    public abstract String pNotifyCheckSign(CallBackParam callBackParam);

    /**
     * @Author: 老乡
     * @Date: 2020/6/12 21:40
     * @Describe: 获取支付渠道
     */
    public abstract Integer channelInit();

}

支付宝子类实现抽象类重写抽象方法,保证独立变化

为什么返回orderSn,因为多种支付的验签返回不同,orderSn却可作为公共字段,来进行后续业务

public class AliPayStrategy extends PayStrategyContent {

    private static final String aliPublicKey = "x";

    @Override
    public ServerResponse toPay(PayReq payReq) {
        System.out.println("ali pay");
        return null;
    }

    @Override
    public ServerResponse getInfo() {
        System.out.println("ali info");
        return null;
    }


    @Override
    public String pNotifyCheckSign(CallBackParam callBackParam) {
        //验签
        try {
            boolean verifyResult = AlipaySignature.rsaCheckV1(callBackParam.getParam(), aliPublicKey, "UTF-8", "RSA2");
            if (verifyResult) {
                System.out.println("notify_url 验证成功succcess");
                return callBackParam.getParam().get("orderSn");
            } else {
                System.out.println("notify_url 验证失败");
                return null;
            }
        } catch (AlipayApiException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public Integer channelInit() {
        return PaymentEnums.ALI_PAY.getCode();
    }


}

微信独立变化类

public class WxPayStrategy extends PayStrategyContent {

    @Override
    public ServerResponse toPay(PayReq payReq) {
        System.out.println("微信支付===");
        return null;
    }

    @Override
    public ServerResponse getInfo() {
        System.out.println("wx info");
        return null;
    }

    @Override
    public String pNotifyCheckSign(CallBackParam callBackParam) {
        //验签,返回orderSn
        return null;
    }

    @Override
    public Integer channelInit() {
        return PaymentEnums.WX_PAY.getCode();
    }

}

枚举类

public enum PaymentEnums {

    /**
     * 支付方式
     */
    ALI_PAY(1, AliPayStrategy.class.getSimpleName()),
    WX_PAY(2, WxPayStrategy.class.getSimpleName());

    /**
     * 枚举定义+描述
     */
    private Integer code;
    private String beanName;

    PaymentEnums(Integer code, String beanName) {
        this.code = code;
        this.beanName = StringUtils.isNotEmpty(beanName) ? beanName.toLowerCase() : null;
    }
    
    /**
     * 根据code获取对应的枚举对象
     */
    public static PaymentEnums getEnum(Integer code) {
        PaymentEnums[] values = PaymentEnums.values();
        if (null != code && values.length > 0) {
            for (PaymentEnums value : values) {
                if (value.code.equals(code)) {
                    return value;
                }
            }
        }
        return null;
    }

    /**
     * 该code在枚举列表code属性是否存在
     */
    public static boolean containsCode(Integer code) {
        PaymentEnums anEnum = getEnum(code);
        return anEnum != null;
    }

    /**
     * 判断code与枚举中的code是否相同
     */
    public static boolean equals(Integer code, PaymentEnums calendarSourceEnum) {
        return calendarSourceEnum.code.equals(code);
    }


    public Integer getCode() {
        return code;
    }

    public String getBeanName() {
        return beanName;
    }
}

回调类

public class CallBackParam {
    /** 支付渠道*/
    private Integer channel;

    /**
     * 支付回调参数
     */
    private Map<String,String> param;

    public Integer getChannel() {
        return channel;
    }

    public CallBackParam setChannel(Integer channel) {
        this.channel = channel;
        return this;
    }

    public Map<String, String> getParam() {
        return param;
    }

    public CallBackParam setParam(Map<String, String> param) {
        this.param = param;
        return this;
    }
}

ServerResponse为返回封装类,可自定义,这里不贴了

这样就可以做到灵活变化了,比如要加一种支付方式,只需要定义一个封装类继承PayStrategyContent,枚举加上类别,在controller新添一个接口即可

利用策略模式,不违反开闭原则,依赖倒转,里氏代换的原则

只是若加一个功能需要改动的有点大,不过若不涉及各个支付方式,就不用延迟到子类,若延迟到子类可各自实现此功能,即使不实现也可以改为抽象类(抽象类的特点,若继承一个抽象类不全部实现,则子类也要改成抽象类)

其他的很多解释已经在代码中了,可自行查看,建议从自己的角度先想一下,然后实践一下为什么要这么设计,比如为什么不直接实现

PayCoreManager调用,为什么还要子类继承等等

如若需要源码的可以私我

猜你喜欢

转载自blog.csdn.net/Goligory/article/details/106740171