目录
1.功能应用场景与需求分析
1.应用场景
2.需求分析
3.服务架构
2.升级计划
1. 2.0计划
2. 3.0架构升级计划
3.接口设计
1.类图
2.工厂设计模式
3.适配器设计模式
4.表结构设计
1.表结构分析
2.表结构设计
1.功能应用场景与需求分析
1.应用场景
项目需要对接各广告平台OCPX接口,同时需要记录媒体平台请求信息和客户归因信息,服务用作中间代理,负责双方接口对接,需要对各平台请求参数进行转换与处理
2.需求分析
1.对各广告平台和客户提供统一的请求接口,通过本服务配置的链路编码进行区分
2.异步存储平台和客户的请求数据,redis缓存回调,保证服务性能
3.表结构拆分位常用字段和不常用字段分开存储
3.服务架构
版本:1.0.0
UML时序图更新
2.升级计划
1. 2.0计划
由于需要RTA和OCPX两种类型接口,同时链路服务又可以共用,于是将服务分为一下四个服务
ocpx服务:
处理ocpx类型服务业务
rta服务:
处理rta类型服务业务(QPS较高,和OCPX分开部署)
公共服务:
处理链路信息和可公用服务
链路服务:
处理每个链路服务信息
2. 3.0架构升级计划
在原有服务基础上添加数据转换服务,在不更改OCPX服务的情况下添加新的链路转换功能。
持续更新中...
3.接口设计
1.类图
2.工厂设计模式
说明:
通过工厂模式生产出具体服务实现接口,实现类统一实现接口,每一个实现类实现不同的业务逻辑,在逻辑中区分出媒体平台和上游客户服务,为进一步使用对象适配器模式做准备
媒体平台控制器如下
package com.hhmt.delivery.controller;
import com.hhmt.delivery.entity.BaseMediaDto;
import com.hhmt.delivery.ocpx.service.factory.OcpxMediaServiceFactory;
import com.hhmt.delivery.ocpx.service.media.MediaService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 辉煌明天
* FileName: OcpxMediaController
* Author: huachun
* email: huachun_w@163.com
* Date: 2021/12/7 10:42
* Description: 腾讯广点通OCPX接口
*/
@RestController(value = "媒体对接接口")
@RequestMapping(value = "/v2/ocpx/media", name = "ocpx媒体访问控制")
public class OcpxMediaController {
@Autowired
private OcpxMediaServiceFactory ocpxMediaServiceFac;
@GetMapping(value = "/{code}/click", name = "媒体上报")
public <T> T mediaClick(BaseMediaDto mediaDto, @PathVariable("code") String code) {
MediaService mediaService = ocpxMediaServiceFac.mediaService(code);
return mediaService.clickReportCustomer(mediaDto, code);
}
}
代码分析:
在控制层引入工厂类,通过请求中的路径参数code调用工厂类的mediaService(code)方法生产出具体的业务逻辑处理实现类,进一步处理业务。
媒体服务工厂类,用来生产具体媒体实现对象
package com.hhmt.delivery.ocpx.service.factory.impl;
import com.hhmt.delivery.continer.ErrorCode;
import com.hhmt.delivery.ocpx.container.ChainConstant;
import com.hhmt.delivery.ocpx.service.factory.OcpxMediaServiceFactory;
import com.hhmt.delivery.ocpx.service.media.MediaService;
import com.hhmt.delivery.utils.exception.ServiceCustomerException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.Objects;
/**
* 辉煌明天
* FileName: OcpxMediaServiceFactory
* Author: huachun
* email: huachun_w@163.com
* Date: 2022/1/12 14:45
* Description:
*/
@Service
public class OcpxMediaServiceFactoryImpl implements OcpxMediaServiceFactory {
private static final Logger LOGGER = LoggerFactory.getLogger(OcpxMediaServiceFactoryImpl.class);
/**
* @Description: 以下为媒体服务实现类
* @Author: huachun
* @Date: 2021/12/13 11:23
* @return: null
**/
@Resource(name = "tsaMediaService")
private MediaService tsaMediaService;
@Resource(name = "hwAdsMediaService")
private MediaService hwAdsMediaService;
@Resource(name = "headLineMediaService")
private MediaService headLineMediaService;
@Resource(name = "aiqiyMediaService")
private MediaService aiqiyMediaService;
@Resource(name = "qutoutiaoMediaService")
private MediaService qutoutiaoMediaService;
@Resource(name = "aliHuiChuanMediaService")
private MediaService aliHuiChuanMediaService;
@Resource(name = "oppoMediaService")
private MediaService oppoMediaService;
@Resource(name = "cooTekMediaService")
private MediaService cooTekMediaService;
@Resource(name = "baiDuMediaService")
private MediaService baiDuMediaService;
@Resource(name = "quickWorkerMediaService")
private MediaService quickWorkerMediaService;
/**
* @Description: 生产具体的数据转换服务类
* @Author: huachun
* @Date: 2021/12/7 11:41
* @return: com.hhmt.delivery.ocpx.service.MediaCovertService
**/
@Override
public MediaService mediaService(String chain) {
String logBy = this.getClass().getName() + ".mediaService:{}";
switch (Objects.requireNonNull(ChainConstant.getMediaCode(chain))) {
case TENCENT_SOCIAL_ADS:
return tsaMediaService;
case HUAWEI_ADS:
return hwAdsMediaService;
case HEADLINE:
return headLineMediaService;
case AIQIY:
return aiqiyMediaService;
case QUTOUTIAO:
return qutoutiaoMediaService;
case ALI_HUICHUAN:
return aliHuiChuanMediaService;
case OPPO:
return oppoMediaService;
case COOTEK:
return cooTekMediaService;
case BAIDU:
return baiDuMediaService;
case QUICK_WORKER:
return quickWorkerMediaService;
default:
LOGGER.error(logBy, ErrorCode.CHAIN_CODE_ERROR.getMsg());
throw new ServiceCustomerException(ErrorCode.CHAIN_CODE_ERROR);
}
}
}
通过链路参数返回具体的业务逻辑处理实现类,实现类都实现了响应接口,媒体广告平台实现媒体接口,客户实现客户接口
具体的业务实现就不做过多的展示了...
3.适配器设计模式
说明:
由于多个平台需要上报同一个客户接口,一客户也需要回调多个平台服务,所以将服务接口抽离出来做成通用接口。
业务逻辑:左边为平台上报,右边为客户回调
通过业务逻辑分析请求接口后需要有两个操作
1.转换数据
将平台请求数据转换为上报客户的请求参数,这里面包含了核心的业务逻辑
2.发送请求
携带请求数据请求客户上报接口
接口定义:
1.统一请求对象和响应
媒体服务请求客户的接口需要封装各个客户请求数据
package com.hhmt.delivery.ocpx.service.media;
import com.hhmt.delivery.ocpx.bean.customer.to.KoalaTo;
import com.hhmt.delivery.ocpx.bean.customer.to.MeiTuanNetWorkTo;
import com.hhmt.delivery.ocpx.bean.customer.to.MeiTuanYxTo;
import com.hhmt.delivery.ocpx.bean.customer.to.TaoBaoTo;
import com.hhmt.delivery.ocpx.bean.customer.vo.KoalaVo;
import com.hhmt.delivery.ocpx.bean.customer.vo.MeiTuanNetWorkVo;
import com.hhmt.delivery.ocpx.bean.customer.vo.MeituanYxVo;
import com.hhmt.delivery.ocpx.bean.customer.vo.TaoBaoVo;
/**
* 辉煌明天
* FileName: MediaReqService
* Author: huachun
* email: huachun_w@163.com
* Date: 2021/12/13 18:07
* Description:
*/
public interface MediaReqService {
KoalaVo reqKoala(KoalaTo koalaTo);
MeituanYxVo reqMeiTuanYx(MeiTuanYxTo meiTuanYxTo);
MeiTuanNetWorkVo reqMeiTuanNetWork(MeiTuanNetWorkTo meiTuanNetWorkTo);
TaoBaoVo reqTaoBao(TaoBaoTo taoBaoTo);
}
媒体目标请求方法clickReportCustomer,由于业务需要转换数据,所以我把转换数据这一步放在了一起,也可以单独抽离出来
package com.hhmt.delivery.ocpx.service.media;
import com.hhmt.delivery.ocpx.bean.customer.to.KoalaTo;
import com.hhmt.delivery.ocpx.bean.customer.to.MeiTuanNetWorkTo;
import com.hhmt.delivery.ocpx.bean.customer.to.MeiTuanYxTo;
import com.hhmt.delivery.ocpx.bean.customer.to.TaoBaoTo;
import java.util.Map;
/**
* 辉煌明天
* FileName: MediaCovertService
* Author: huachun
* email: huachun_w@163.com
* Date: 2021/12/7 11:24
* Description: 媒体平台数据转换服务
*/
public interface MediaService {
<T> T clickReportCustomer(Map<String, String> mediaDto, String code);
MeiTuanNetWorkTo covertMtNetWorkData(Map<String, String> mediaDto, String realKey);
KoalaTo covertKoalaData(Map<String, String> mediaDto, String realKey);
MeiTuanYxTo covertMeituanYxData(Map<String, String> mediaDto, String realKey);
TaoBaoTo covertTaoBaoData(Map<String, String> mediaDto, String realKey);
}
在具体的服务实现类中可以这样使用
package com.hhmt.delivery.ocpx.service.media.impl;
import com.hhmt.delivery.async.OcpxAsyncService;
import com.hhmt.delivery.continer.ErrorCode;
import com.hhmt.delivery.ocpx.bean.customer.to.KoalaTo;
import com.hhmt.delivery.ocpx.bean.customer.to.MeiTuanNetWorkTo;
import com.hhmt.delivery.ocpx.bean.customer.to.MeiTuanYxTo;
import com.hhmt.delivery.ocpx.bean.customer.to.TaoBaoTo;
import com.hhmt.delivery.ocpx.container.ChainConstant;
import com.hhmt.delivery.ocpx.container.KoalaConstant;
import com.hhmt.delivery.ocpx.container.TsaConstant;
import com.hhmt.delivery.ocpx.service.media.MediaCovertService;
import com.hhmt.delivery.ocpx.service.media.MediaReqService;
import com.hhmt.delivery.ocpx.service.media.MediaService;
import com.hhmt.delivery.utils.OcpxUtils;
import com.hhmt.delivery.utils.RedisUtils;
import com.hhmt.delivery.utils.exception.ServiceCustomerException;
import org.apache.commons.codec.digest.DigestUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
/**
* 辉煌明天
* FileName: TsaKoalaCovertService
* Author: huachun
* email: huachun_w@163.com
* Date: 2021/12/7 11:27
* Description: 腾讯转换考拉请求数据服务实现类
*/
@Service
public class TsaMediaService implements MediaService {
private static final Logger LOGGER = LoggerFactory.getLogger(TsaMediaService.class);
private static final String ANDROID = "android";
private static final String IOS = "ios";
// redis key失效时间一个半小时
private static final Long EXPIRE_TIME = 5400L;
@Autowired
private RedisUtils redisUtils;
@Autowired
private OcpxUtils ocpxUtils;
@Resource(name = "tsaAsyncService")
private OcpxAsyncService tsaAsyncService;
@Autowired
private MediaReqService mediaReqService;
@Autowired
private MediaCovertService mediaCovertService;
/**
* @Description: 媒体点击上报客户
* @Author: huachun
* @Date: 2021/12/7 15:47
* @return: T
**/
@Override
public <T> T clickReportCustomer(Map<String, String> mediaDto, String code) {
String logBy = this.getClass().getName() + ".clickReportCustomer:{}";
String realKry = "";
switch (Objects.requireNonNull(ChainConstant.getCustomerCode(code))) {
case KOALA:
realKry = ocpxUtils.realKey(ChainConstant.TENCENT_SOCIAL_ADS__KOALA.getCode());
KoalaTo koalaTo = covertKoalaData(mediaDto, realKry);
tsaAsyncService.saveReqParams(mediaDto, realKry);
return (T) mediaReqService.reqKoala(koalaTo);
case MEITUAN_NET_WORK:
realKry = ocpxUtils.realKey(ChainConstant.TENCENT_SOCIAL_ADS__MEITUAN_NET_WORK.getCode());
MeiTuanNetWorkTo meiTuanNetWorkTo = covertMtNetWorkData(mediaDto, realKry);
tsaAsyncService.saveReqParams(mediaDto, realKry);
return (T) mediaReqService.reqMeiTuanNetWork(meiTuanNetWorkTo);
default:
LOGGER.error(logBy, ErrorCode.CHAIN_CODE_ERROR.getMsg());
throw new ServiceCustomerException(ErrorCode.CHAIN_CODE_ERROR);
}
}
/**
* @Description: 腾讯广点通数据转换- 美团网盟请求数据
* @Author: huachun
* @Date: 2021/12/14 11:44
* @return: com.hhmt.delivery.ocpx.bean.customer.to.MeiTuanNetWorkTo
**/
@Override
public MeiTuanNetWorkTo covertMtNetWorkData(Map<String, String> mediaDto, String realKey) {
return null;
}
/**
* @Description: 腾讯广点通数据-转换考拉海购请求数据
* @Author: huachun
* @Date: 2021/12/14 11:43
* @return: com.hhmt.delivery.ocpx.bean.customer.to.KoalaTo
**/
@Override
public KoalaTo covertKoalaData(Map<String, String> mediaDto, String realKey) {
return null;
}
@Override
public MeiTuanYxTo covertMeituanYxData(Map<String, String> mediaDto, String realKey) {
return null;
}
@Override
public TaoBaoTo covertTaoBaoData(Map<String, String> mediaDto, String realKey) {
return null;
}
}
这里删除具体的业务逻辑~
此时在腾讯平台适配考拉海购时就可以通过两个步骤完成操作
1.实现转换考拉海购数据接口
KoalaTo koalaTo = covertKoalaData(mediaDto, realKry);
2.请求考拉海购接口
return (T) mediaReqService.reqKoala(koalaTo);
至此,平台->客户的第一个适配就完成了
由于接口响应需要适配多种实体类型,所以这里使用了泛型定义
4.表结构设计
1.表结构分析
接口文档中有很多参数在实际请求中是空值,但是这部分参数又不能舍弃掉,需要对媒体平台的表字段进行拆分,分为存储常用参数表和不常用参数表
2.表结构设计
说明:
1.以ocpx_m开头的是媒体平台表,用来存储媒体请求数据
2.以ocpx_c开头的表是客户表,用来存储客户回传数据
3.ocpx_media_cb_info是用来异步存放回调信息,方便查询
4.ocpx_m_xxx_fill_info用来存储 xxx平台非必要信息,由于平台字段比较多,很多又是不常用但后期可能会用的,所以用关联表记录
数据库表优化:
1.数据量大
a).通过create_time字段进行分区分表
b).创建常用条件查询字段索引,这里使用rs_id字段,索引规则idx_tb_name_column
2.数据插入校验
创建表字段插入约束
如果大家有更好的建议,欢迎在下方评论区和我留言互动哟!
持续完善中...