最近一直在深入学习一些知识,整理出一些东西一直没有时间写出来,后续会慢慢总结一下写在博客上,对于博客还是不想专门写知识点,总结和提升终究是为了自己,写出来的博客为了让别人看的好,更为了自己的成长和记录,这种记录不仅仅是知识,知识不仅仅是软硬件,还有经验,有思想,这些才是更宝贵的
我们很难真正的交心交流,但是却可以把心放在这里,谁看到了,和它聊一聊那也是好的;这种沟通的活跃就像我不喜欢程序员越来越像机器人一样,像法律一样死板,应该还是一个有血有肉有思想的人,不是猿
原来分享了支付服务的一些点,今天实操一下具体的设计,最近也看了一些其他人的设计,觉得挺好和大家分享一下
服务代码的设计要注重设计模式的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调用,为什么还要子类继承等等
如若需要源码的可以私我