|
@@ -3,11 +3,14 @@ package com.adx.tencent.vivo.controller;
|
|
|
import com.adx.tencent.baidu.AdxClient;
|
|
import com.adx.tencent.baidu.AdxClient;
|
|
|
import com.adx.tencent.baidu.model.NormalizedBidResponse;
|
|
import com.adx.tencent.baidu.model.NormalizedBidResponse;
|
|
|
import com.adx.tencent.baidu.model.TrackingResult;
|
|
import com.adx.tencent.baidu.model.TrackingResult;
|
|
|
|
|
+import com.adx.tencent.rta.BaiduRtaDecision;
|
|
|
|
|
+import com.adx.tencent.rta.BaiduRtaService;
|
|
|
import com.adx.tencent.vivo.model.VivoBidRecord;
|
|
import com.adx.tencent.vivo.model.VivoBidRecord;
|
|
|
import com.adx.tencent.vivo.model.VivoTrackingRecord;
|
|
import com.adx.tencent.vivo.model.VivoTrackingRecord;
|
|
|
import com.adx.tencent.vivo.service.VivoAdvertiserSyncService;
|
|
import com.adx.tencent.vivo.service.VivoAdvertiserSyncService;
|
|
|
import com.adx.tencent.vivo.service.VivoAuthService;
|
|
import com.adx.tencent.vivo.service.VivoAuthService;
|
|
|
import com.adx.tencent.vivo.service.VivoPlacement;
|
|
import com.adx.tencent.vivo.service.VivoPlacement;
|
|
|
|
|
+import com.adx.tencent.vivo.store.VivoColdStore;
|
|
|
import com.adx.tencent.vivo.store.VivoHotStore;
|
|
import com.adx.tencent.vivo.store.VivoHotStore;
|
|
|
import com.adx.tencent.util.TraceIds;
|
|
import com.adx.tencent.util.TraceIds;
|
|
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
@@ -43,22 +46,28 @@ public class VivoTrackingController {
|
|
|
|
|
|
|
|
private final AdxClient adxClient;
|
|
private final AdxClient adxClient;
|
|
|
private final VivoHotStore hotStore;
|
|
private final VivoHotStore hotStore;
|
|
|
|
|
+ private final VivoColdStore coldStore;
|
|
|
private final VivoPlacement placement;
|
|
private final VivoPlacement placement;
|
|
|
private final VivoAuthService authService;
|
|
private final VivoAuthService authService;
|
|
|
private final VivoAdvertiserSyncService advertiserSyncService;
|
|
private final VivoAdvertiserSyncService advertiserSyncService;
|
|
|
|
|
+ private final BaiduRtaService rtaService;
|
|
|
private final ObjectMapper objectMapper;
|
|
private final ObjectMapper objectMapper;
|
|
|
|
|
|
|
|
public VivoTrackingController(AdxClient adxClient,
|
|
public VivoTrackingController(AdxClient adxClient,
|
|
|
@Nullable VivoHotStore hotStore,
|
|
@Nullable VivoHotStore hotStore,
|
|
|
|
|
+ @Nullable VivoColdStore coldStore,
|
|
|
@Nullable VivoPlacement placement,
|
|
@Nullable VivoPlacement placement,
|
|
|
@Nullable VivoAuthService authService,
|
|
@Nullable VivoAuthService authService,
|
|
|
@Nullable VivoAdvertiserSyncService advertiserSyncService,
|
|
@Nullable VivoAdvertiserSyncService advertiserSyncService,
|
|
|
|
|
+ @Nullable BaiduRtaService rtaService,
|
|
|
ObjectMapper objectMapper) {
|
|
ObjectMapper objectMapper) {
|
|
|
this.adxClient = adxClient;
|
|
this.adxClient = adxClient;
|
|
|
this.hotStore = hotStore;
|
|
this.hotStore = hotStore;
|
|
|
|
|
+ this.coldStore = coldStore;
|
|
|
this.placement = placement;
|
|
this.placement = placement;
|
|
|
this.authService = authService;
|
|
this.authService = authService;
|
|
|
this.advertiserSyncService = advertiserSyncService;
|
|
this.advertiserSyncService = advertiserSyncService;
|
|
|
|
|
+ this.rtaService = rtaService;
|
|
|
this.objectMapper = objectMapper;
|
|
this.objectMapper = objectMapper;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -137,6 +146,15 @@ public class VivoTrackingController {
|
|
|
requireVivoParams(params);
|
|
requireVivoParams(params);
|
|
|
log.info("[Vivo][曝光][入参] traceId={} ip={} params={}", traceId, clientIp(request), params);
|
|
log.info("[Vivo][曝光][入参] traceId={} ip={} params={}", traceId, clientIp(request), params);
|
|
|
|
|
|
|
|
|
|
+ VivoPlacement.Resolved resolved = placement.resolve(params);
|
|
|
|
|
+ BaiduRtaDecision rtaDecision = evaluateRta(traceId, resolved, params, request);
|
|
|
|
|
+ Map<String, String> rtaParams = enrichMediaParams(params, rtaDecision);
|
|
|
|
|
+ if (!rtaDecision.allowsBid()) {
|
|
|
|
|
+ VivoBidRecord blocked = blockedBidRecord(traceId, "impression", rtaParams, resolved, rtaDecision);
|
|
|
|
|
+ saveBlockedBid(blocked);
|
|
|
|
|
+ return Map.of("qk", blocked.getQk(), "traceId", traceId, "tagId", blocked.getTagId(), "rtaCode", safeInt(blocked.getRtaCode()));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
Map<String, Object> bidRequest = buildBidRequest(request, traceId, params);
|
|
Map<String, Object> bidRequest = buildBidRequest(request, traceId, params);
|
|
|
NormalizedBidResponse response = adxClient.requestBid("vivo", traceId, bidRequest);
|
|
NormalizedBidResponse response = adxClient.requestBid("vivo", traceId, bidRequest);
|
|
|
if (response == null) {
|
|
if (response == null) {
|
|
@@ -152,7 +170,8 @@ public class VivoTrackingController {
|
|
|
response.getBid() != null && response.getBid().getClickUrls() != null ? response.getBid().getClickUrls().size() : 0,
|
|
response.getBid() != null && response.getBid().getClickUrls() != null ? response.getBid().getClickUrls().size() : 0,
|
|
|
response.getBid() != null && response.getBid().getShowUrls() != null ? response.getBid().getShowUrls().size() : 0);
|
|
response.getBid() != null && response.getBid().getShowUrls() != null ? response.getBid().getShowUrls().size() : 0);
|
|
|
|
|
|
|
|
- VivoBidRecord record = bidRecordFromResponse(response, traceId, "impression", params);
|
|
|
|
|
|
|
+ VivoBidRecord record = bidRecordFromResponse(response, traceId, "impression", rtaParams);
|
|
|
|
|
+ record.setRtaCode(rtaDecision.getCode());
|
|
|
hotStore.recordBid(record);
|
|
hotStore.recordBid(record);
|
|
|
|
|
|
|
|
if (response.getBid() != null && response.getBid().getShowUrls() != null && !response.getBid().getShowUrls().isEmpty()) {
|
|
if (response.getBid() != null && response.getBid().getShowUrls() != null && !response.getBid().getShowUrls().isEmpty()) {
|
|
@@ -175,6 +194,14 @@ public class VivoTrackingController {
|
|
|
|
|
|
|
|
VivoBidRecord record = hotStore.findBidByMediaTrace(traceId);
|
|
VivoBidRecord record = hotStore.findBidByMediaTrace(traceId);
|
|
|
if (record == null) {
|
|
if (record == null) {
|
|
|
|
|
+ VivoPlacement.Resolved resolved = placement.resolve(params);
|
|
|
|
|
+ BaiduRtaDecision rtaDecision = evaluateRta(traceId, resolved, params, request);
|
|
|
|
|
+ Map<String, String> rtaParams = enrichMediaParams(params, rtaDecision);
|
|
|
|
|
+ if (!rtaDecision.allowsBid()) {
|
|
|
|
|
+ VivoBidRecord blocked = blockedBidRecord(traceId, "click", rtaParams, resolved, rtaDecision);
|
|
|
|
|
+ saveBlockedBid(blocked);
|
|
|
|
|
+ return Map.of("qk", blocked.getQk(), "traceId", traceId, "tagId", blocked.getTagId(), "rtaCode", safeInt(blocked.getRtaCode()));
|
|
|
|
|
+ }
|
|
|
NormalizedBidResponse bidResponse = adxClient.requestBid("vivo", traceId, buildBidRequest(request, traceId, params));
|
|
NormalizedBidResponse bidResponse = adxClient.requestBid("vivo", traceId, buildBidRequest(request, traceId, params));
|
|
|
if (bidResponse == null) {
|
|
if (bidResponse == null) {
|
|
|
log.warn("[Vivo][点击][百度结果] traceId={} empty=true", traceId);
|
|
log.warn("[Vivo][点击][百度结果] traceId={} empty=true", traceId);
|
|
@@ -189,7 +216,8 @@ public class VivoTrackingController {
|
|
|
bidResponse.getBid() != null && bidResponse.getBid().getClickUrls() != null ? bidResponse.getBid().getClickUrls().size() : 0,
|
|
bidResponse.getBid() != null && bidResponse.getBid().getClickUrls() != null ? bidResponse.getBid().getClickUrls().size() : 0,
|
|
|
bidResponse.getBid() != null && bidResponse.getBid().getShowUrls() != null ? bidResponse.getBid().getShowUrls().size() : 0);
|
|
bidResponse.getBid() != null && bidResponse.getBid().getShowUrls() != null ? bidResponse.getBid().getShowUrls().size() : 0);
|
|
|
|
|
|
|
|
- record = bidRecordFromResponse(bidResponse, traceId, "click", params);
|
|
|
|
|
|
|
+ record = bidRecordFromResponse(bidResponse, traceId, "click", rtaParams);
|
|
|
|
|
+ record.setRtaCode(rtaDecision.getCode());
|
|
|
hotStore.recordBid(record);
|
|
hotStore.recordBid(record);
|
|
|
if (bidResponse.getBid() != null && bidResponse.getBid().getShowUrls() != null && !bidResponse.getBid().getShowUrls().isEmpty()) {
|
|
if (bidResponse.getBid() != null && bidResponse.getBid().getShowUrls() != null && !bidResponse.getBid().getShowUrls().isEmpty()) {
|
|
|
List<TrackingResult> impressionTracking = adxClient.reportImpression(
|
|
List<TrackingResult> impressionTracking = adxClient.reportImpression(
|
|
@@ -341,6 +369,91 @@ public class VivoTrackingController {
|
|
|
return record;
|
|
return record;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ private BaiduRtaDecision evaluateRta(String traceId,
|
|
|
|
|
+ VivoPlacement.Resolved resolved,
|
|
|
|
|
+ Map<String, String> params,
|
|
|
|
|
+ HttpServletRequest request) {
|
|
|
|
|
+ if (rtaService == null) {
|
|
|
|
|
+ return BaiduRtaDecision.bypass();
|
|
|
|
|
+ }
|
|
|
|
|
+ BaiduRtaDecision decision = rtaService.evaluate(
|
|
|
|
|
+ "vivo",
|
|
|
|
|
+ resolved.appId(),
|
|
|
|
|
+ resolved.tagId(),
|
|
|
|
|
+ resolved.platform(),
|
|
|
|
|
+ params,
|
|
|
|
|
+ clientIp(request),
|
|
|
|
|
+ normalizeUserAgent(firstParam(params, "ua"), resolved.platform()));
|
|
|
|
|
+ if (decision.isApplied()) {
|
|
|
|
|
+ log.info("[Vivo][RTA] traceId={} code={} matched={} requestId={} orderIds={} latencyMs={} error={}",
|
|
|
|
|
+ traceId, decision.getCode(), decision.isMatched(), decision.getRequestId(),
|
|
|
|
|
+ decision.getOrderIds(), decision.getLatencyMs(), decision.getErrorMessage());
|
|
|
|
|
+ }
|
|
|
|
|
+ return decision;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private VivoBidRecord blockedBidRecord(String traceId,
|
|
|
|
|
+ String eventType,
|
|
|
|
|
+ Map<String, String> params,
|
|
|
|
|
+ VivoPlacement.Resolved resolved,
|
|
|
|
|
+ BaiduRtaDecision decision) {
|
|
|
|
|
+ VivoBidRecord record = new VivoBidRecord();
|
|
|
|
|
+ record.setQk(blockedQk("vivo", decision.getRequestId(), traceId));
|
|
|
|
|
+ record.setMedia("vivo");
|
|
|
|
|
+ record.setMediaTraceId(traceId);
|
|
|
|
|
+ record.setPlatform(resolved.platform());
|
|
|
|
|
+ record.setAccountId(firstParam(params, "accountId", "accountid"));
|
|
|
|
|
+ record.setEventType(eventType);
|
|
|
|
|
+ record.setTagId(resolved.tagId());
|
|
|
|
|
+ record.setPackageName(firstParam(params, "pkgName", "packageName"));
|
|
|
|
|
+ record.setMediaParams(enrichMediaParams(params, decision));
|
|
|
|
|
+ record.setRtaCode(decision.getCode());
|
|
|
|
|
+ record.setPrice(0L);
|
|
|
|
|
+ record.setCreatedAt(Instant.now());
|
|
|
|
|
+ return record;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private void saveBlockedBid(VivoBidRecord record) {
|
|
|
|
|
+ if (coldStore == null) {
|
|
|
|
|
+ throw new ResponseStatusException(HttpStatus.SERVICE_UNAVAILABLE, "vivo cold store not configured");
|
|
|
|
|
+ }
|
|
|
|
|
+ coldStore.saveBid(record);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private static Map<String, String> enrichMediaParams(Map<String, String> params, BaiduRtaDecision decision) {
|
|
|
|
|
+ Map<String, String> enriched = new LinkedHashMap<>();
|
|
|
|
|
+ if (params != null) {
|
|
|
|
|
+ enriched.putAll(params);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (decision == null || !decision.isApplied()) {
|
|
|
|
|
+ return enriched;
|
|
|
|
|
+ }
|
|
|
|
|
+ putIfNotBlank(enriched, "rta_tu", decision.getTu());
|
|
|
|
|
+ putIfNotBlank(enriched, "rta_order_id", firstNonBlank(decision.getOrderIds(), decision.getRequestOrderIds()));
|
|
|
|
|
+ putIfNotBlank(enriched, "rta_request_order_id", decision.getRequestOrderIds());
|
|
|
|
|
+ putIfNotBlank(enriched, "rta_request_id", decision.getRequestId());
|
|
|
|
|
+ if (decision.getCode() != null) {
|
|
|
|
|
+ enriched.put("rta_code", String.valueOf(decision.getCode()));
|
|
|
|
|
+ }
|
|
|
|
|
+ return enriched;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private static void putIfNotBlank(Map<String, String> params, String key, String value) {
|
|
|
|
|
+ if (value != null && !value.isBlank()) {
|
|
|
|
|
+ params.put(key, value);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private static String firstNonBlank(String... values) {
|
|
|
|
|
+ if (values == null) return null;
|
|
|
|
|
+ for (String value : values) {
|
|
|
|
|
+ if (value != null && !value.isBlank()) {
|
|
|
|
|
+ return value;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
private Map<String, Object> baiduUidFromParams(Map<String, String> p, String platform) {
|
|
private Map<String, Object> baiduUidFromParams(Map<String, String> p, String platform) {
|
|
|
Map<String, Object> uid = new LinkedHashMap<>();
|
|
Map<String, Object> uid = new LinkedHashMap<>();
|
|
|
if ("android".equals(platform)) {
|
|
if ("android".equals(platform)) {
|
|
@@ -674,6 +787,15 @@ public class VivoTrackingController {
|
|
|
return value == null ? "" : value;
|
|
return value == null ? "" : value;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ private static int safeInt(Integer value) {
|
|
|
|
|
+ return value == null ? 0 : value;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private static String blockedQk(String media, String requestId, String traceId) {
|
|
|
|
|
+ String suffix = requestId != null && !requestId.isBlank() ? requestId : md5Hex(traceId == null ? media : traceId + "-" + Instant.now().toEpochMilli());
|
|
|
|
|
+ return "rta-" + media + "-" + suffix;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
private void requireVivoParams(Map<String, String> params) {
|
|
private void requireVivoParams(Map<String, String> params) {
|
|
|
if (firstParam(params, "accountId", "accountid") == null) {
|
|
if (firstParam(params, "accountId", "accountid") == null) {
|
|
|
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "missing vivo accountId");
|
|
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "missing vivo accountId");
|