今⽇头条移动APP⼴告激活数据API对接实践
⾃从上班实习之后,就好久没有写博客了,这是⾃毕业后的第⼀篇博客,希望⾃⼰今后能养成写博客的⼀个好习惯。最近公司为了加速APP 推⼴,采取在外部平台(如:今⽇头条)进⾏⼴告投放的⽅式,进⾏⽤户引流。因此我们需要对⼴告的激活数据进⾏⼀个检测,跟踪⼴告的转化效果。以下主要列举对接今⽇头条⼴告激活数据API的流程以及接⼝的实现。
1. 熟悉流程
我们想看看今⽇头条对接⽂档给我们提供的⼀个对接流程⽰意图:
由上图可看出我们需要提供两个接⼝:
接⼝⼀:当⽤户点击我们投放在今⽇头条的⼴告时,今⽇头条服务器会向接⼝⼀下发数据,我们需要对该部分数据进⾏保存操作。
接⼝⼆:当⽤户下载了我们⼴告中的APP,并且⽤户成功注册后,APP调⽤接⼝⼆,接⼝⼆将对应的数据回调到今⽇头条平台。
2. 接⼝实现
接⼝⼀流程:
接⼝⼀的参数形式:
ANDROID:
/xxx?adid=__AID__&cid=__CID__&callback=__CALLBACK_PARAM__&imei=__IMEI__&mac=__MAC__&android_id=__ANDROIDID1__& timestamp=__TS__&ip=__IP__&os=__OS__
IOS:
/xxx?adid=__AID__&cid=__CID__&idfa=__IDFA__&mac=__MAC__×tamp=__TS__&ip=__IP__&os=__OS__&callback=__CALLBAC K_PARAM__
接⼝⼀的响应⽅式:
JSON格式
接⼝⼀响应内容
状态码200
{status:0}
success(在项⽬当中我采⽤的是返回success)
接⼝⼀的代码实现:
接⼝⼀的参数接收DTO
/**
* 接⼝⼀和接⼝⼆的参数DTO基类
*/
public class BaseParamsDTO{
private Integer os;//客户端类型,0-Android,1-IOS,2-WP,3-Others
private String idfa;//IOS唯⼀标识(IOS9和IOS10当开启了限制⼴告跟踪时,该值不能作为唯⼀标识)
private String imei;//安卓唯⼀标识(APP需要授权才能获取到)
private String androidId;//安卓唯⼀标识(恢复出⼚设置会改变)
private String ip;
}
/**
* 监测接⼝参数DTO
*/
public class MonitoringParamsDTO extends BaseParamsDTO{
private String adid;//⼴告计划id,原值
private String cid;//⼴告创意id,原值
private String mac;//eth0⽹卡mac客户
private String timestamp;//时间戳
private String convertId;//转化跟踪id
private String callback;//回调参数
}
接⼝⼀保存的实体信息(以下只列举主要的字段,根据⾃⾝的业务要求进⾏字段拓展)
public class UserDeviceInfo{
private String adid;// ⼴告计划id
private String cid;// ⼴告创意id
private Integer os;// 客户端类型,0-Android,1-IOS,2-WP,3-Others
private String idfa;// ios唯⼀标识
private String imei;// 安卓唯⼀标识
private String androidid;// 安卓唯⼀标识
private String mac;
private String ip;
private String callback;//回调参数
private String timestamp;//时间戳
private String convertId;//转化跟踪id
//TODO 主键、创建时间、更新时间等字段不⼀⼀列举了,可根据业务需要进⾏拓展字段
}
Controller层实现
//PS:最近有评论让我贴出controller代码,现在我已经很久没接触这个代码了,现在controller代码是现写的,仅供参考,controller只要接收头条返回的参数,业务中再做⼀些参数校验即可
@RequestMapping("/monitoring")
@ResponseBody
public String saveMonitoringParams(MonitoringParamsDTO  monitoringParamsDTO){
//TODO 业务中可以做⼀些参数校验
// 调⽤sevice⽅法保存监控参数(即下⾯的saveUserDeviceInfo⽅法)
// 处理成功返回success,否则返回其他
return"success";
}
Service层的代码实现:
/**
* 保存⽤户的设备信息
*
* @param monitoringDto 头条下发的监测参数
*/
public void saveUserDeviceInfo(MonitoringParamsDto monitoringDto){
//获取监测参数获取⽤户设备信息
UserDeviceInfo userDeviceInfo = UserDeviceInfoByParams(monitoringDto); //不存在⽤户设备信息,则新建实体
if(ToolsKit.isEmpty(userDeviceInfo)){
userDeviceInfo =new UserDeviceInfo();
}
//将头条的下发的检测参数转化为⽤户设备信息实体
BeanCopier beanCopier = ate(MonitoringParamsDto.class, UserDeviceInfo.class,false);    py(monitoringDto, userDeviceInfo, null);
/
/TODO 此处,您可以再做其他业务逻辑,我在项⽬重要是累计了⽤户的点击次数等等
//保存⽤户设备信息
userDeviceInfoService.save(userDeviceInfo);
}
/**
*根据参数获取设备信息实体
安卓下载app*匹配逻辑如下:
*1、IOS系统,idfa合法的情况下,就根据idfa查设备信息
*2、IOS系统,idfa不合法的情况下,就根据ip和idfa查设备信息
*3、Android系统,imei存在的情况下,就根据ip和imei查设备信息
*4、Android系统,imei不存在的情况下,就根据ip和AndroidId查设备信息
*5、其他情况就根据ip和ua(User-Agent)查设备信息
* @param baseParamsDto
* @return
*/
public UserDeviceInfo getUserDeviceInfoByParams(BaseParamsDto baseParamsDto){
if(ToolsKit.isEmpty(baseParamsDto)){
throw new ServiceException("参数为空!");
}
//查是否已经存在设备信息记录
int os = Os();
Map<String, Object> params = wLinkedHashMap();
params.put(UserDeviceInfo.OS_FIELD, os);
params.put(UserDeviceInfo.IP_FIELD, Ip());
UserDeviceInfo entity = null;
String idfa = Idfa();
String imei = Imei();
//ios系统且idfa不为空
if(Value()== os){
params.put(UserDeviceInfo.IDFA_FIELD, idfa);
if(TooUtil.checkIdfa(idfa)){//判断idfa是否合法
params.put(UserDeviceInfo.IP_FIELD,null);
}
entity =this.findEntityByParams(params);
}else if(Value()== os){
//Android系统且imei不为空
if(ToolsKit.isNotEmpty(imei)){
params.put(UserDeviceInfo.IMEI_FIELD, imei);
}else{
params.put(UserDeviceInfo.ANDROID_ID_FIELD, AndroidId());
}
entity =this.findEntityByParams(params);
}else{
//通过ip和ua查
params.put(UserDeviceInfo.UA_FIELD, Ua());
entity =this.findEntityByParams(params);
}
return entity;
}
接⼝⼆流程:
接⼝⼆的代码实现:
接⼝⼆回调的url
ad.toutiao/track/activate/?callback={callback_param}&muid=
{muid}&os={os}&source={source}&conv_time={conv_time}&signature={signa
ture}
接⼝⼆的参数接收DTO
/**
* 回调激活参数DTO
*/
public class CallBackActiveParamsDTO extends BaseParamsDTO{
//根据⾃⼰的业务需要定义回调激活参数,此处我主要收集⽤户的id
private String userid;
}
Controller层实现
//PS:最近有评论让我贴出controller代码,现在我已经很久没接触这个代码了,
//现在controller代码是现写的,仅供参考,controller只要接收我们⾃⼰的app客户端传过来的参数,做下参数校验,//然后再调⽤service⽅法即可
@RequestMapping("/callback")
@ResponseBody
public String callback(CallBackActiveParamsDTO callBackActiveParamsDTO ,Sting source){ //TODO 业务中可以做⼀些参数校验
// 调⽤sevice⽅法中的激活回调⽅法(即下⾯的callback⽅法)
// TODO 处理成功返回success,具体返回什么看⾃⼰的业务
// 这个接⼝时给我们⾃⼰的app客户端调⽤的,所以返回参数可以⾃定
return"success";
}
Service层的代码实现: