拼多多消息对接、支付服务策略(策略+工厂)
一、拼多多消息对接
策略抽象接口
/*** 策略抽象接口** @author zj* @date 2023/5/23 17:03*/
public interface AbstractStrategy<T, R> {/*** 策略标识** @return 策略标识*/String strategyCode();/*** 执行策略** @param t 策略入参*/default void execute(T t) {}/*** 执行策略,带返回值** @param t 策略入参* @return 策略后返回值*/default R executeResp(T t) {return null;}
}
策略工厂抽象接口
/*** 抽象策略工厂** @author zj* @date 2023/5/23 17:03*/
@Component
public class AbstractStrategyFactory<T, R> implements CommandLineRunner {/*** 策略集合*/private final Map<String, AbstractStrategy<T, R>> abstractStrategyHashMap = MapUtil.newHashMap();/*** 根据 strategyCode 获取具体策略** @param strategyCode 策略标识* @return 实际执行策略*/public AbstractStrategy<T, R> get(String strategyCode) {return Optional.ofNullable(abstractStrategyHashMap.get(strategyCode)).orElseThrow(() -> new RuntimeException(String.format("%s 策略未定义", strategyCode)));}/*** 根据 strategyCode 查询具体策略并执行** @param strategyCode 策略标识* @param t 策略入参*/public void execute(String strategyCode, T t) {AbstractStrategy<T, R> strategy = get(strategyCode);strategy.execute(t);}/*** 根据 strategyCode 查询具体策略并执行,带返回结果** @param strategyCode 策略标识* @param t 策略入参* @return {@link R}*/public R executeResp(String strategyCode, T t) {AbstractStrategy<T, R> strategy = get(strategyCode);return strategy.executeResp(t);}@Override@SuppressWarnings({"unchecked", "rawtypes"})public void run(String... args) {Map<String, AbstractStrategy> strategyMap = SpringUtil.getBeansOfType(AbstractStrategy.class);strategyMap.forEach((beanName, bean) -> {if (abstractStrategyHashMap.containsKey(bean.strategyCode())) {throw new RuntimeException(String.format("%s duplicate execution policy", bean.strategyCode()));}abstractStrategyHashMap.put(bean.strategyCode(), (AbstractStrategy<T, R>)bean);});}
拼多多消息策略处理接口
/*** 拼多多消息处理策略** @author zj* @date 2023/02/27*/
public interface PddMessageStrategy extends AbstractStrategy<PopMessage, PopMessageAck> {}
拼多多消息策略工厂
/*** pdd消息策略工厂** @author zj* @date 2023/03/01*/
@RequiredArgsConstructor
@Component
public class PddMessageStrategyFactory {/*** 抽象战略工厂*/private final AbstractStrategyFactory<PopMessage, PopMessageAck> abstractStrategyFactory;/*** 获取** @param strategyCode 标记* @return {@link PddMessageStrategy}*/public PddMessageStrategy get(String strategyCode) {return (PddMessageStrategy) abstractStrategyFactory.get(strategyCode);}/*** 执行响应** @param strategyCode 策略编号* @param popMessage 拼多多消息* @return {@link PopMessageAck}*/public PopMessageAck executeResp(String strategyCode, PopMessage popMessage) {return get(strategyCode).executeResp(popMessage);}
}
消息统一处理(先落表再执行,异常时补偿)
/*** 拼多多消息处理器* * 只关注业务处理,ACK、重连、异常SDK内部处理** @author zj* @date 2023/03/01*/
@Slf4j
@RequiredArgsConstructor
@Component
public class PddMessageHandler implements MessageHandler {/*** mq服务*/private final MqService mqService;/*** pdd消息策略工厂*/private final PddMessageStrategyFactory pddMessageStrategyFactory;@Overridepublic void onMessage(Message message) {// 业务处理逻辑log.info("收到拼多多消息: {}", message);MessageLogCreateListener.CreateMessage createMessage = new MessageLogCreateListener.CreateMessage();createMessage.setTopic(message.getType());createMessage.setEvent(message.getType());createMessage.setMark(StrUtil.toString(message.getMallID()));createMessage.setMsgBody(message.getContent());MessageAck messageAck = mqService.publish(MessageLogCreateListener.wrapper(createMessage));log.info("[onMessage][messageAck({})]", messageAck);PddMessageTypeEnum enumByCode = EnumUtil.getEnumByCode(PddMessageTypeEnum.class, message.getType());AssertUtil.notNull(enumByCode);PopMessageAck popMessageAck = pddMessageStrategyFactory.executeResp(enumByCode.getCode(), this.convertToPopMessage(message));AssertUtil.isTrue(popMessageAck.isSuccess());}/*** 转换为拼多多消息** @param message 消息* @return {@link PopMessage}*/public PopMessage convertToPopMessage(Message message) {PopMessage popMessage = new PopMessage();popMessage.setType(message.getType());popMessage.setContent(message.getContent());popMessage.setMallId(message.getMallID());return popMessage;}
}
拼多多交易成功处消息实现(其他消息同理)
/*** 交易成功 消息策略** @author zj* @date 2023/02/27*/
@Slf4j
@RequiredArgsConstructor
@Component
public class TradeSuccessMessageStrategy implements PddMessageStrategy {/*** 订单信息服务*/private final OrderInfoService orderInfoService;@Overridepublic String strategyCode() {return PddMessageTypeEnum.TRADE_SUCCESS.getCode();}@Overridepublic PopMessageAck executeResp(PopMessage popMessage) {log.info("处理【交易成功】消息: {}", popMessage);orderInfoService.updateStatus(this.convertToOrderInfoUpdateStatusRequest(popMessage));return PopMessageAck.success();}
策略消息枚举
/*** 拼多多消息type枚举* * @author zj* @date 2023-02-26*/
@Getter
@AllArgsConstructor
public enum PddMessageTypeEnum implements EnumCode<String> {/*** 交易成功消息*/TRADE_SUCCESS("pdd_trade_TradeSuccess", "交易成功消息"),/*** 卖家发货消息*/TRADE_SELLER_SHIP("pdd_trade_TradeSellerShip", "卖家发货消息"),/*** 交易确认消息*/TRADE_CONFIRMED("pdd_trade_TradeConfirmed", "交易确认消息"),/*** 同意退款协议消息*/REFUND_AGREE_AGREEMENT("pdd_refund_RefundAgreeAgreement", "同意退款协议消息"),/*** 售后单关闭消息*/REFUND_CLOSED("pdd_refund_RefundClosed", "售后单关闭消息");/*** 消息Topic编码*/private final String code;/*** 消息Topic描述*/private final String message;
}
二、简单支付服务策略
抽象策略工厂、抽象策略同上,支付服务策略封装常用支付方法,出入参抽取通用支付属性,根据所需业务自行扩展,如下
/*** 支付服务策略** @author zj* @date 2023/8/26 10:01*/
public interface PayStrategyService extends AbstractStrategy<Void, Void> {/*** 获取支付链接** @param dto dto* @return str*/Object payment(PaymentInputDto dto);/*** 查询支付结果** @param dto dto* @return obj*/default PaymentQueryOutputDto queryPayResult(PaymentQueryInputDto dto) {throw ServiceException("不支持该方式");}/*** 解析支付结果** @param data object data* @return obj*/default Object parseOrderNotifyResult(Object data) {throw ServiceException("不支持该方式");}/*** 解析退款结果** @param data data* @return obj*/default Object parseRefundNotifyResult(Object data) {throw ServiceException("不支持该方式");}/*** 解析确认收货结果** @param data 数据* @return {@link Object}*/default Object parseConfirmNotifyResult(Object data) {throw ServiceException("不支持该方式");}/*** 退款** @param dto dto*/void refund(RefundInputDto dto);/*** 转账** @param dto dto* @return object*/default PartnerTransferResult transfer(TransferInputDto dto) {throw ServiceException("不支持该方式");}
策略实现(余额支付、微信支付、聚富通支付等等实现支付策略,未实现方法以“不支持该方式”进行异常抛出)
@Service
@RequiredArgsConstructor
public class BalancePayStrategyServiceImpl implements PayStrategyService {/*** 订单服务*/private final OrdersService orderService;/*** 平衡服务*/private final BalanceService balanceService;@Override@Transactional(rollbackFor = Exception.class)public Object payment(PaymentInputDto dto) {Long userId = MapUtil.getLong(dto.getParamsMap(), "userId");OrderVO orderVO = this.orderService.payOrderCheck(dto.getOrderNo(), userId);BalanceSubtractInputDto balanceSubtract = new BalanceSubtractInputDto();balanceSubtract.setSubtractType(BalanceSubtract.ORDER_DEDUCTION.getValue());balanceSubtract.setUserId(userId);balanceSubtract.setBalance(orderVO.getPayAmount());balanceSubtract.setDescription("下单使用余额抵扣" + orderVO.getPayAmount() + "元");balanceSubtract.setRelNo(dto.getOrderNo());balanceSubtract.setSource(BalanceSource.ORDER_DEDUCTION.getValue());this.balanceService.subtract(balanceSubtract);orderVO.setPaidAmount(orderVO.getPayAmount());orderVO.setPayType(PayType.Balance.getValue());orderVO.setTransactionId(DateUtil.format(DateUtil.date(), DatePattern.PURE_DATETIME_PATTERN) + RandomUtil.randomNumbers(4));this.orderService.updateForPaySuccess(orderVO);return null;}@Override@Transactional(rollbackFor = Exception.class)public void refund(RefundInputDto dto) {BalancePlusInputDto plusInputDto = new BalancePlusInputDto();plusInputDto.setPlusType(BalancePlus.ORDER_BACKOFF.getValue());plusInputDto.setBalance(dto.getRefundFee());plusInputDto.setRelNo(dto.getOutRefundNo());plusInputDto.setUserId(MapUtil.getLong(dto.getParamsMap(), "userId"));plusInputDto.setSource(BalanceSource.ORDER_COMPENSATE.getValue());plusInputDto.setDescription("订单售后补偿,退回" + dto.getRefundFee() + "元");this.balanceService.plus(plusInputDto);if (ObjectUtil.isNotNull(dto.getSuccessCallback())) {dto.getSuccessCallback().run();}}@Overridepublic String mark() {return PayType.Balance.getPayChannelStrategyKey();}
}
支付策略调用
@ApiOperation("H5待支付订单获取支付参数")@GetMapping("pay/h5/{orderNo}")public JsonResult<OrderPayDTO> payOrderOfH5(@PathVariable("orderNo") String orderNo) {return orderService.payOrder(orderNo, TradeType.MWEB.name(), PayType.Wechat.getValue());}/*** 支付订单** @param orderNo 订单号* @param tradeType 交易类型* @param payType 支付类型* @return {@link JsonResult}<{@link OrderPayDTO}>*/@Overridepublic JsonResult<OrderPayDTO> payOrder(String orderNo, String tradeType, Integer payType) {AssertUtil.notNull(payType, "支付方式不能为空");AssertUtil.isTrue(PayType.exist(payType), "错误的支付类型");AssertUtil.isTrue(TradeType.exist(tradeType), "错误的交易类型");OrderVO orderVO = this.orderService.payOrderCheck(orderNo, this.teancyUser.getUserId());if (this.payProperties.getTest()) {orderVO.setTransactionId(orderNo);orderVO.setPayType(payType);this.orderService.updateForPaySuccess(orderVO);return JsonResult.ok();}String openId = MapUtil.getStr(this.teancyUser.getExtendInfo(), "openId");Object payment = ((PayStrategyService) this.abstractStrategyFactory.get(PayType.getInstance(payType).getPayChannelStrategyKey())).payment(new PaymentInputDto().setOrderNo(orderNo).setPayAmount(orderVO.getPayAmount()).setDescription("普通订单").setNotifyUrl(this.payProperties.getPayNotifyUrl()).setTradeType(tradeType).setUserId(openId));OrderPayDTO orderPayDTO = new OrderPayDTO();orderPayDTO.setPayParam(payment);return JsonResult.ok(orderPayDTO);}