对接八大应用渠道

背景

       最近公司想把游戏包上到各个渠道上,因此需要对接各种渠道,渠道如下,oppo、vivo、华为、小米、应用宝、taptap、荣耀、三星等应用渠道               

         主要就是对接登录、支付接口(后续不知道会不会有其他的),由于登录、支付前置都是一些通用的逻辑处理,所以一开始我就想到了要用设计模式来做,下面我画了一个图,大家可以参考看一下        

这里值得一提的是目前oppo比常规的渠道多了一个发货通知的接口,我把几个接口都说说明下        

登录:用户通过oppo、vivo等进行登录并绑定我们内部账号中        

支付回调:用户使用oppo支付成功后要通知我们用户下单成功,我们需要进行处理发货        

发货通知:我们发货成功后通知oppo发货成功了        

前面两个接口其实很好理解,主要是发货通知,官方的文档如下,看了也许你就明白了,如下

     如果 OPPO 服务端超过2 个小时仍未收到游戏服务端的发货结果请求,则代表该笔订单发货失败,将进入可退款状态,用户可自助选择是否退款,如果用户选择退款,将进入OPPO 退款流程        

对了没错,用户可以退款,我认为就是在对标苹果吧,目前看小米和vivo都不需要有这个接口

流程图

代码逻辑

        上面的就是设计图,这里再简单的说下代码的结构逻辑是咋样的,其实做过挺多次类似的处理了,这两种模式的设计还是挺有意义的,以下只是稍微说下大概的方式

AbstractChannelStrategy

@Slf4j
public abstract class AbstractChannelStrategy{@Resourceprotected ZXCUserParentDao userParentDao;@Resourceprotected  ZXCUsermpl userRepo;@Resourceprivate  ZXCOrderoDao orderInfoDao;@Resourceprotected ApplicationContext applicationContext;@Resourceprotected  ZXCAppRepoImpl gameAppRepo;//获取用户注册类型public abstract UserTypeEnum getUserRegisterTypeEnum();//获取回调Url地址protected abstract String getCallBackUrlSuffix();//真正的支付成功处理public abstract void doNotifyOrderHandle(ChannelNotifyBaseBo notifyBaseBo, ChannelNotifyOrderResultBO notifyOrderResultBO, OrderInfo orderInfo) throws Exception;//游戏发货成功后的处理protected abstract DeliveryCallBackResultBo doDeliveryCallback(OrderInfo orderInfo, GameApp gameApp) throws Exception;//允许子类校验订单的其他信息protected void checkOrderOtherInfo(OrderInfo orderInfo){}public boolean notifyOrderHandle(ChannelNotifyBaseBo notifyBaseBo) {try {if(notifyBaseBo == null) {return false;}//检查订单基础信息OrderInfo orderInfo = commonOrderCheck(notifyBaseBo.getOrderNumber());checkOrderOtherInfo(orderInfo);ChannelNotifyOrderResultBO notifyOrderResultBO = new ChannelNotifyOrderResultBO();doNotifyOrderHandle(notifyBaseBo, notifyOrderResultBO, orderInfo);boolean success = notifyOrderResultBO.isSuccess();//成功后处理后续if(success) {log.info("notifyOrderHandle-订单号{}处理成功,进行后续处理", orderInfo.getOrderNumber());successOrder(orderInfo, notifyOrderResultBO.getTradeNo());}return success;} catch (Exception e) {log.error("doNotifyOrderHandle出现异常", e);throw new AppException(ErrorCode.SYS_OPERATOR_ERROR.code(), "doNotifyOrderHandle调用时出现异常");}}public boolean deliveryCallback(String orderNumber) {OrderInfo orderInfo = assertOrderInfo(orderNumber);GameApp gameApp = gameAppRepo.getByAppNumber(orderInfo.getAppNumber());if(!Objects.equals(channelOrderRel.getRequestStatus(), 0)) {log.info("AbstractChannelAccountStrategy-deliveryCallback订单号为{}的请求状态不为未处理,无须处理,此时状态为{},来源为{}", orderNumber, channelOrderRel.getRequestStatus(), getLogSourceName());return true;}try {DeliveryCallBackResultBo deliveryCallBackResultBo = doDeliveryCallback(orderInfo, gameApp);boolean success = deliveryCallBackResultBo.isSuccess();//更新状态和次数channelOrderRel.setHttpContent(deliveryCallBackResultBo.getHttpContent());channelOrderRel.setRequestStatus(success ? 1 : null);int update = channelOrderRelService.update(channelOrderRel);log.info("deliveryCallback-处理完成,更新的订单号为{},关联的channelOrderRel主键id为{},影响行数为{}", orderNumber, channelOrderRel.getId(), update);return success;} catch (Exception e) {log.error("AbstractChannelAccountStrategy-deliveryCallback订单号为{}的请求处理出现异常,来源为{}", orderNumber, getLogSourceName(), e);return false;}}protected OrderInfo commonOrderCheck(String orderNumber) {OrderInfo orderInfo = assertOrderInfo(orderNumber);if(orderInfo.getPayWay() != null && !Objects.equals(getOrderPayWay().getCode(), orderInfo.getPayWay())) {throw new ZXCException(ZXCCode.OPERATOR_ERROR.code(), "AbstractChannelAccountStrategy-订单号支付方式不对,此时订单号为" + orderNumber);}return orderInfo;}private OrderInfo assertOrderInfo(String orderNumber) {OrderInfo orderInfo = orderInfoDao.getByOrderNumber(orderNumber);if(orderInfo == null) {throw new AppException(ErrorCode.NOT_FOUND_DATA.code(), "AbstractChannelAccountStrategy-找不到关联的订单号数据,此时订单号为" + orderNumber);}return orderInfo;}private void successOrder(OrderInfo orderInfo, String tradeNo) {boolean updated = discountsService.updateOrderStatusAndDisCount(orderInfo, tradeNo);if(updated){// 发送事件applicationContext.publishEvent(new PaySuccessEvent(this, new PaySuccessEvent.PaySuccessEventData(orderInfo.getOrderNumber())));}}public String buildCallBackUrl() {//校验及简单处理一下数据String callBackUrlSuffix = getCallBackUrlSuffix();if(StringUtil.isBlank(callBackUrlSuffix)) {throw new AppException("未配置回调地址");}if(!Objects.equals("/", callBackUrlSuffix.substring(0, 1))) {callBackUrlSuffix = "/" + callBackUrlSuffix;}String callBackUrlPrefix = "https://test.cn/v1/pay/callback";if(isLine()) {callBackUrlPrefix = "https://prod.cn/v1/pay/callback";}return callBackUrlPrefix + callBackUrlSuffix;}

虽然上面那个看起来很复杂,但是主要是子类方便,这里提供其中一个实现类

OppoStrategyImpl

@Slf4j
@Component
public class OppoStrategyImpl extends AbstractChannelAccountStrategy {@Overridepublic UserTypeEnum getUserRegisterTypeEnum() {return UserTypeEnum.OPPO_USER;}@Overrideprotected String getCallBackUrlSuffix() {return "/oppo/callback";}@Overridepublic void doNotifyOrderHandle(ChannelNotifyBaseBo notifyBaseBo, ChannelNotifyOrderResultBO notifyOrderResultBO, OrderInfo orderInfo) throws Exception {OppoOrderNotifyBo oppoOrderNotifyBo = (OppoOrderNotifyBo) notifyBaseBo; //强制对象,几乎是不可能报错的,除非调用端出了问题//验证签名,这里是用oppo的公钥验签,因为私钥只有oppo有,所以别人无法伪造String baseString = getBaseString(oppoOrderNotifyBo);boolean check = OppoUtils.check(baseString, oppoOrderNotifyBo.getSign());if(!check) {log.info("OppoChannelAccountStrategyImpl-验签失败,此时订单号为{}", oppoOrderNotifyBo.getPartnerOrder());return;}//代表成功了,对数据进行填充notifyOrderResultBO.setSuccess(true);notifyOrderResultBO.setTradeNo(oppoOrderNotifyBo.getNotifyId()); //可能没有}// 生成 baseStringprivate static String getBaseString(OppoOrderNotifyBo ne) {StringBuilder sb = new StringBuilder();sb.append("notifyId=").append(ne.getNotifyId());sb.append("&partnerOrder=").append(ne.getPartnerOrder());sb.append("&productName=").append(ne.getProductName());sb.append("&productDesc=").append(ne.getProductDesc());sb.append("&price=").append(ne.getPrice());sb.append("&count=").append(ne.getCount());sb.append("&attach=").append(ne.getAttach());return sb.toString();}@Overrideprotected OppoDeliveryResult doDeliveryCallback(OrderInfo orderInfo, GameApp gameApp) throws Exception {OppoDeliveryResult oppoDeliveryCallbackResult = OppoUtils.postDeliveryOppo(orderInfo, gameApp);DeliveryCallBackResultBo deliveryCallBackResultBo = new DeliveryCallBackResultBo();deliveryCallBackResultBo.setHttpContent(JsonUtils.Object2Json(oppoDeliveryCallbackResult));deliveryCallBackResultBo.setSuccess(oppoDeliveryCallbackResult.isSuccess());return deliveryCallBackResultBo;}
}

        看起来是不是简单多了,当然第一个意义是不大,主要是后面的vivo、小米、华为等只需要提供对应的实现类即可

        而且其他的可能都不需要doDeliveryCallback这个接口的实现,因此其实还可以改进一下,在底级类上面直接提供个成功的回调实现,这里我就暂时不改了,可能改为在底层提供个propertect方法,然后直接调用也是可以的,就当保存一下所有的交互数据得了,而且可能有其他用途,就是用来主动查询订单的结果       

        至于引用就更简单了,通常都是有个类似于算门面的东西,如下

ChannelStrategyComponent

@Slf4j
@Component
public class ChannelStrategyComponent {@Resourceprivate List<AbstractChannelStrategy> channelStrategyList;@Resourceprivate ZXCUserParentDao userParentDao;@Resourceprivate ZXCOrderDao orderInfoDao;public AbstractChannelStrategy getChannelAccountStrategy(UserTypeEnum userTypeEnum) {AbstractChannelStrategy channelAccountStrategy = checkChannelAccountStrategy(userTypeEnum);if(channelAccountStrategy == null) {throw new AppException(ErrorCode.SYS_ERROR.code(), "找不到关联的AbstractChannelAccountStrategy对象");}return channelAccountStrategy;}public AbstractChannelStrategy checkChannelAccountStrategyByOrderNumber(String orderNumber) {Order  order = orderDao.getByOrderNumber(orderNumber);//用支付方式直接去找for (AbstractChannelStrategy channelStrategy : channelStrategyList) {if(Objects.equals(orderInfo.getPayWay(), channelStrategy.getOrderPayWay().getCode())) {log.info("channelStrategy-从订单支付方式中找到处理器");return channelStrategy;}}//支付方式找不到再从用户注册类型去找,节省一部数据库查询if(orderInfo != null) {return checkChannelAccountStrategy(orderInfo.getUserName());}return null;}
}

           怎么样,看起来是不是很简单,顺带一提,上面的OppoUtils就是跟oppo对接的工具包,这个就不提供了,这部分肯定每个都不一样,需要单独写

         这篇文章主要是想说明一种封装思路,而不是要说代码具体是怎么写的这个事

总结

        其实这个东西并不算很复杂,可能跟我设计多次也有关系,这种思路其实我是多少有点借助spring的设计,你去看就会发现里面有很多类似这样的设计

后续补充

补充一

        就像之前说的doDeliveryCallback方法并不是每个渠道都需要的,所以并不需要弄为抽血方法,可以提供默认实现,子类根据需要实现即可,以上是改动的代码

改之前的结构protected abstract CallBackResultBo doDeliveryCallback() throws Exception每个子类都得强制实现改之后的结构protected CallBackResultBo doDeliveryCallback() throws Exception {return CallBackResultBo.buildDefaultSuccess();
}默认提供成功的实现这样一来,子类就可以根据所需来选择是否需要覆盖了,减少了不少代码

   补充二 

        之前的登录接口是设计为多个来给安卓调用的,比如以上两个接口

oppo调用: https://zxc.com/oppo/login

vivo调用: https://zxc.com/vivo/login

但是安卓说不好区分,他想统一调一个接口,如果是以外就麻烦了,但是在设计模式的加持下,现在实现的功能就非常简单了,只需要做几个事

1.在抽象类 AbstractChannelStrategy添加 方法

2.在子类提供实现

3.在ChannelStrategyComponent提供获取方式

最后在接收的通用参数定义 channelSource来源,然后子类跟它绑定起来就可以,代码如下

抽象类添加//sdk端定义的渠道来源public abstract String getSdkChannelSource();子类实现@Overridepublic String getSdkChannelSource() {return "oppo";}上下文中添加获取即可,省略了这里

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/917168.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/917168.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

学习:入门uniapp Vue3组合式API版本(17)

42.打包发行微信小程序的上线全流程 域名 配置 发行 绑定手机号 上传 提交后等待&#xff0c;上传 43.打包H5并发布上线到unicloud的前端页面托管 完善配置 unicloud 手机号实名信息不一致&#xff1a;请确保手机号的实名信息与开发者姓名、身份证号一致&#xff0c;请前往开…

SOLIDWORKS材料明细表设置,属于自己的BOM表模板

上一期我们了解了如何在SOLIDWORKS工程图中添加材料明细表?接下来&#xff0c;我们将进行对SOLIDWORKS材料明细表的设置、查看缩略图、模板保存的深度讲解。01 材料明细表设置菜单栏生成表格后左侧菜单栏会显示关于材料明细表的相关设置信息。我们先了解一下菜单栏设置详情&am…

全栈:Maven的作用是什么?本地仓库,私服还有中央仓库的区别?Maven和pom.xml配置文件的关系是什么?

Maven和pom.xml配置文件的关系是什么&#xff1a; Maven是一个构建工具和依赖管理工具&#xff0c;而pom.xml&#xff08;Project Object Model&#xff09;是Maven的核心配置文件。 SSM 框架的项目不一定是 Maven 项目&#xff0c;但推荐使用 Maven进行管理。 SSM 框架的项目可…

超越 ChatGPT:智能体崛起,开启全自主 AI 时代

引言 短短三年,生成式 AI 已从对话助手跨越到能自主规划并完成任务的“智能体(Agentic AI)”时代。这场演进不仅体现在模型规模的提升,更在于系统架构、交互范式与安全治理的全面革新。本文按时间线梳理关键阶段与核心技术,为您呈现 AI 智能体革命的脉络与未来趋势。 1. …

一杯就够:让大脑瞬间在线、让肌肉满电的 “Kick-out Drink” 全解析

一杯就够&#xff1a;让大脑瞬间在线、让肌肉满电的 “Kick-out Drink” 全解析“每天清晨&#xff0c;当闹钟还在哀嚎&#xff0c;你举杯一饮&#xff0c;睡意像被扔出擂台——这&#xff0c;就是 Kick-out Drink 的全部浪漫。”清晨 30 分钟后&#xff0c;250 mL 常温水里溶解…

系统开机时自动执行指令

使用 systemd 创建一个服务单元可以让系统开机时自动执行指令&#xff0c;假设需要执行的指令如下&#xff0c;运行可执行文件&#xff08;/home/demo/可执行文件&#xff09;&#xff0c;并输入参数&#xff08;–input/home/config/demo.yaml&#xff09;&#xff1a; /home/…

Docker 初学者需要了解的几个知识点 (七):php.ini

这段配置是 php.ini 文件中针对 PHP 扩展和 Xdebug 调试工具的设置&#xff0c;主要用于让 PHP 支持数据库连接和代码调试&#xff08;尤其在 Docker 环境中&#xff09;&#xff0c;具体解释如下&#xff1a;[PHP] extensionpdo_mysql extensionmysqli xdebug.modedebug xdebu…

【高阶版】R语言空间分析、模拟预测与可视化高级应用

随着地理信息系统&#xff08;GIS&#xff09;和大尺度研究的发展&#xff0c;空间数据的管理、统计与制图变得越来越重要。R语言在数据分析、挖掘和可视化中发挥着重要的作用&#xff0c;其中在空间分析方面扮演着重要角色&#xff0c;与空间相关的包的数量也达到130多个。在本…

dolphinscheduler中一个脚本用于从列定义中提取列名列表

dolphinscheduler中&#xff0c;我们从一个mysql表导出数据&#xff0c;上传到hdfs, 再创建一个临时表&#xff0c;所以需要用到列名定义和列名列表。 原来定义两个变量&#xff0c;不仅繁锁&#xff0c;还容易出现差错&#xff0c;比如两者列序不对。 所以考虑只定义列定义变量…

JavaWeb(苍穹外卖)--学习笔记16(定时任务工具Spring Task,Cron表达式)

前言 本篇文章是学习B站黑马程序员苍穹外卖的学习笔记&#x1f4d1;。我的学习路线是Java基础语法-JavaWeb-做项目&#xff0c;管理端的功能学习完之后&#xff0c;就进入到了用户端微信小程序的开发&#xff0c;用户端开发的流程大致为用户登录—商品浏览&#xff08;其中涉及…

灵敏度,精度,精确度,精密度,精准度,准确度,分辨率,分辨力——概念

文章目录前提总结前提 我最近在整理一份数据指标要求的时候&#xff0c;总是混淆这几个概念&#xff1a;灵敏度&#xff0c;精度&#xff0c;精确度&#xff0c;精密度&#xff0c;精准度&#xff0c;准确度&#xff0c;分辨率&#xff0c;分辨力&#xff0c;搜了一些文章&…

python-异常(笔记)

#后续代码可以正常运行 try:f open("xxx.txt","r",encodingutf-8)except:print("except error")#捕获指定异常&#xff0c;其他异常报错程序中止&#xff0c;管不到 try:print(name) except NameError as you_call:print("name error"…

[lvgl_player] 用户界面(LVGL) | 播放器核心设计

docs&#xff1a;基于LVGL的音乐播放器 本项目是为嵌入式设备设计的音乐播放系统&#xff0c;采用LVGL图形库构建用户界面。 系统支持播放WAV格式音频文件&#xff0c;具备播放列表管理功能&#xff0c;可实现播放/暂停控制、曲目切换等核心操作。 用户可通过交互界面实时调…

数据赋能(354)——数据分析——多角度分析原则

概述重要性如下&#xff1a;获得全面理解&#xff1a;多角度分析原则避免仅从单一角度解读数据&#xff0c;从不同角度、不同维度对数据进行分析&#xff0c;以获得更全面的理解。发现潜在规律&#xff1a;通过多角度分析&#xff0c;发现数据中的潜在规律和趋势&#xff0c;为…

【华为机试】127. 单词接龙

文章目录127. 单词接龙描述示例 1&#xff1a;示例 2&#xff1a;提示&#xff1a;解题思路算法分析问题本质分析单向BFS算法详解双向BFS算法详解邻居单词生成过程算法流程图边界情况分析各种解法对比时间复杂度分析空间复杂度分析关键优化点实际应用场景图构建策略双向BFS优化…

仿艾莫迅MODBUS调试工具写一个上位机

公司采购了一个夹具&#xff0c;项目负责人想要试探这个夹具的性能&#xff0c;于是想要我这边写一个烤机的程序&#xff0c;小编结合官网资料 https://wiki.amsamotion.com/?title196&doc222查看其pdf说明文档和调试工具并按照其工具写一个烤机上位机根据项目负责人的要求…

云展厅:开启数字化展示新时代

在科技飞速发展的今天&#xff0c;数字化浪潮正席卷各个行业&#xff0c;展览展示领域也不例外。云展厅作为一种全新的展览形式&#xff0c;正逐渐崭露头角&#xff0c;以其独特的优势和创新的技术应用&#xff0c;为观众带来前所未有的观展体验&#xff0c;也为企业和机构提供…

硬件电路基础学习

一、基础元器件学习 1、电阻 1.1 作用 电阻的工作原理是基于欧姆定律&#xff0c;即电阻的阻值取决于其材料、长度和横截面积。电阻的主要作用是限制电流&#xff0c;调节电压和电流&#xff0c;以及保护电路。1.2 数值计算 欧姆定律 通过欧姆定律计算所需保护电阻的大小注意…

基于C++和人工智能(DeepSeek)实践

基于C++和人工智能(如DeepSeek)实践 以下是基于C++和人工智能(如DeepSeek或其他AI框架)的实际应用示例,涵盖不同领域和技术方向,供参考: 基于C++和人工智能(如DeepSeek或其他AI框架)的实际应用示例 图像识别与处理 人脸检测:使用OpenCV和DNN模块加载预训练的Caffe…

书生浦语第五期L0G1000

完成 视频课程学习&#xff0c;并在 https://chat.intern-ai.org.cn/ 平台中实践提示词技巧&#xff0c;与 InternLM 和 InternVL 各完成 10 次对话记录在飞书文档中。 参加 浦语提示词工程论文分类打榜赛&#xff0c;分数超过 40 分 InternLM InternVL 浦语提示词工程论文分…