目录

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

        

推送数据架构 数据推送接口 推送系统架构_mysql

UML时序图更新

推送数据架构 数据推送接口 推送系统架构_java_02

2.升级计划 

1. 2.0计划

由于需要RTA和OCPX两种类型接口,同时链路服务又可以共用,于是将服务分为一下四个服务

ocpx服务:

        处理ocpx类型服务业务

rta服务:

        处理rta类型服务业务(QPS较高,和OCPX分开部署)

公共服务:

        处理链路信息和可公用服务

链路服务:

        处理每个链路服务信息

推送数据架构 数据推送接口 推送系统架构_database_03

2. 3.0架构升级计划

推送数据架构 数据推送接口 推送系统架构_database_04

在原有服务基础上添加数据转换服务,在不更改OCPX服务的情况下添加新的链路转换功能。

持续更新中...

3.接口设计

1.类图

推送数据架构 数据推送接口 推送系统架构_mysql_05

2.工厂设计模式

   

推送数据架构 数据推送接口 推送系统架构_推送数据架构 数据推送接口_06

说明:

        通过工厂模式生产出具体服务实现接口,实现类统一实现接口,每一个实现类实现不同的业务逻辑,在逻辑中区分出媒体平台和上游客户服务,为进一步使用对象适配器模式做准备  

 媒体平台控制器如下

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.适配器设计模式       

说明:

        由于多个平台需要上报同一个客户接口,一客户也需要回调多个平台服务,所以将服务接口抽离出来做成通用接口。

推送数据架构 数据推送接口 推送系统架构_推送数据架构 数据推送接口_07

业务逻辑:左边为平台上报,右边为客户回调

推送数据架构 数据推送接口 推送系统架构_数据库_08

 通过业务逻辑分析请求接口后需要有两个操作

        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.表结构设计

推送数据架构 数据推送接口 推送系统架构_database_09

说明:

        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.数据插入校验

                创建表字段插入约束

如果大家有更好的建议,欢迎在下方评论区和我留言互动哟!

持续完善中...