| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228 |
- package com.adx.tencent.conversionsync;
- import com.adx.tencent.baidu.model.PaymentInfo;
- import com.adx.tencent.storage.RedisHotStore;
- import com.adx.tencent.storage.TiDBColdStore;
- import com.adx.tencent.storage.model.BidRecord;
- import com.adx.tencent.storage.model.MediaCallbackRecord;
- import com.adx.tencent.tencent.TencentClient;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import org.springframework.web.server.ResponseStatusException;
- import java.time.Instant;
- import java.util.LinkedHashMap;
- import java.util.LinkedHashSet;
- import java.util.Map;
- import java.util.Set;
- import static org.springframework.http.HttpStatus.BAD_REQUEST;
- import static org.springframework.http.HttpStatus.NOT_FOUND;
- public class ManualTencentCallbackService {
- private static final Logger log = LoggerFactory.getLogger(ManualTencentCallbackService.class);
- private final TiDBColdStore coldStore;
- private final RedisHotStore hotStore;
- private final TencentClient tencentClient;
- public ManualTencentCallbackService(TiDBColdStore coldStore,
- RedisHotStore hotStore,
- TencentClient tencentClient) {
- this.coldStore = coldStore;
- this.hotStore = hotStore;
- this.tencentClient = tencentClient;
- }
- public Map<String, Object> replayDeductedCallback(long id) {
- MediaCallbackRecord original = coldStore.getMediaCallbackById(id);
- if (original == null) {
- throw new ResponseStatusException(NOT_FOUND, "callback record not found");
- }
- if (!"tencent".equals(original.getMedia())) {
- throw new ResponseStatusException(BAD_REQUEST, "callback record is not tencent");
- }
- if (!MediaCallbackRecord.DISPATCH_STATUS_DEDUCTED.equals(original.getDispatchStatus())) {
- throw new ResponseStatusException(BAD_REQUEST, "callback record is not deducted");
- }
- MediaCallbackRecord replay = replayOnce(original);
- replay.setId(original.getId());
- coldStore.updateMediaCallback(replay);
- Map<String, Object> result = new LinkedHashMap<>();
- result.put("id", id);
- result.put("dedupeKey", replay.getDedupeKey());
- result.put("before", snapshot(original));
- result.put("after", snapshot(replay));
- return result;
- }
- private MediaCallbackRecord replayOnce(MediaCallbackRecord original) {
- if (original.getRequestBody() != null && !original.getRequestBody().isBlank()) {
- try {
- MediaCallbackRecord replay = tencentClient.retryCallback(original);
- replay.setDedupeKey(original.getDedupeKey());
- replay.setAttempt(Math.max(original.getAttempt(), 1) + 1);
- if (replay.getTrackingVersion() == null || replay.getTrackingVersion().isBlank()) {
- replay.setTrackingVersion(original.getTrackingVersion());
- }
- return replay;
- } catch (Exception e) {
- MediaCallbackRecord failed = copyForFailure(original);
- failed.setErrorMessage(e.getMessage());
- return failed;
- }
- }
- BidRecord bid = hotStore.findBidByQk(original.getQk());
- if (bid == null) {
- bid = coldStore.getBidByQk(original.getQk());
- }
- if (bid == null) {
- throw new ResponseStatusException(NOT_FOUND, "bid not found for qk=" + original.getQk());
- }
- DedupeInfo dedupeInfo = parseDedupeInfo(original);
- String actionType = dedupeInfo.actionType != null && !dedupeInfo.actionType.isBlank()
- ? dedupeInfo.actionType
- : tencentClient.conversionActionType(original.getEventType());
- patchBidForReplay(bid, original, dedupeInfo);
- PaymentInfo payment = new PaymentInfo();
- payment.setQk(original.getQk());
- payment.setDate(dedupeInfo.date);
- payment.setDeviceId(dedupeInfo.deviceId);
- payment.setAct(original.getEventType());
- payment.setPayment(original.getPurchase());
- payment.setGmv(original.getPurchase());
- MediaCallbackRecord replay = tencentClient.sendConversionEvent(
- bid,
- payment,
- resolvePlatform(bid),
- actionType
- );
- replay.setDedupeKey(original.getDedupeKey());
- replay.setAttempt(Math.max(original.getAttempt(), 1) + 1);
- if (replay.getTrackingVersion() == null || replay.getTrackingVersion().isBlank()) {
- replay.setTrackingVersion(original.getTrackingVersion());
- }
- replay.setDispatchStatus(MediaCallbackRecord.DISPATCH_STATUS_SENT);
- replay.setCreatedAt(Instant.now());
- log.info("[ManualTencentCallback] manual replay sent | qk={} | dedupeKey={} | ok={} | status={}",
- original.getQk(), original.getDedupeKey(), replay.isOk(), replay.getStatus());
- return replay;
- }
- private void patchBidForReplay(BidRecord bid, MediaCallbackRecord original, DedupeInfo dedupeInfo) {
- if ((bid.getTagId() == null || bid.getTagId().isBlank()) && dedupeInfo.tagId != null && !dedupeInfo.tagId.isBlank()) {
- bid.setTagId(dedupeInfo.tagId);
- }
- Map<String, String> params = bid.getMediaParams();
- if (params == null) {
- params = new LinkedHashMap<>();
- bid.setMediaParams(params);
- } else if (!(params instanceof LinkedHashMap<String, String>)) {
- params = new LinkedHashMap<>(params);
- bid.setMediaParams(params);
- }
- if ((params.get("callback") == null || params.get("callback").isBlank())
- && original.getCallbackUrl() != null && !original.getCallbackUrl().isBlank()) {
- params.put("callback", original.getCallbackUrl());
- }
- }
- private static MediaCallbackRecord copyForFailure(MediaCallbackRecord original) {
- MediaCallbackRecord failed = new MediaCallbackRecord();
- failed.setDedupeKey(original.getDedupeKey());
- failed.setMedia(original.getMedia());
- failed.setQk(original.getQk());
- failed.setCallbackUrl(original.getCallbackUrl());
- failed.setRequestBody(original.getRequestBody());
- failed.setEventType(original.getEventType());
- failed.setEventTimeMs(Instant.now().toEpochMilli());
- failed.setPurchase(original.getPurchase());
- failed.setStatus(original.getStatus());
- failed.setOk(false);
- failed.setAttempt(Math.max(original.getAttempt(), 1) + 1);
- failed.setResponseBody(original.getResponseBody());
- failed.setDispatchStatus(MediaCallbackRecord.DISPATCH_STATUS_SENT);
- failed.setTrackingVersion(original.getTrackingVersion());
- failed.setCreatedAt(Instant.now());
- return failed;
- }
- private static DedupeInfo parseDedupeInfo(MediaCallbackRecord record) {
- DedupeInfo info = new DedupeInfo();
- String dedupeKey = record.getDedupeKey();
- if (dedupeKey == null || dedupeKey.isBlank()) {
- return info;
- }
- String[] parts = dedupeKey.split(":");
- if (parts.length < 7) {
- return info;
- }
- info.date = parts[2];
- info.deviceId = parts[3];
- info.tagId = parts[4];
- info.actionType = joinTail(parts, 6);
- return info;
- }
- private static String joinTail(String[] parts, int start) {
- if (parts.length <= start) {
- return null;
- }
- StringBuilder builder = new StringBuilder();
- for (int i = start; i < parts.length; i++) {
- if (i > start) {
- builder.append(':');
- }
- builder.append(parts[i]);
- }
- return builder.toString();
- }
- private static String resolvePlatform(BidRecord bid) {
- if (bid.getPlatform() != null && !bid.getPlatform().isBlank()) {
- return bid.getPlatform().toLowerCase();
- }
- if (bid.getMediaParams() == null) {
- return "android";
- }
- String platform = bid.getMediaParams().get("baidu_platform");
- if (platform != null && !platform.isBlank()) {
- return platform.toLowerCase();
- }
- String idfa = bid.getMediaParams().get("idfa");
- if (idfa != null && !idfa.isBlank()) {
- return "ios";
- }
- return "android";
- }
- private static Map<String, Object> snapshot(MediaCallbackRecord record) {
- Map<String, Object> data = new LinkedHashMap<>();
- data.put("id", record.getId());
- data.put("dispatchStatus", record.getDispatchStatus());
- data.put("attempt", record.getAttempt());
- data.put("status", record.getStatus());
- data.put("ok", record.isOk());
- data.put("errorMessage", record.getErrorMessage());
- data.put("callbackUrl", record.getCallbackUrl());
- data.put("trackingVersion", record.getTrackingVersion());
- return data;
- }
- private static class DedupeInfo {
- private String date;
- private String deviceId;
- private String tagId;
- private String actionType;
- }
- }
|