|
@@ -14,7 +14,6 @@ import org.springframework.stereotype.Service;
|
|
|
|
|
|
|
|
import java.util.*;
|
|
import java.util.*;
|
|
|
import java.util.concurrent.*;
|
|
import java.util.concurrent.*;
|
|
|
-import java.util.stream.Collectors;
|
|
|
|
|
|
|
|
|
|
@Service
|
|
@Service
|
|
|
@Slf4j
|
|
@Slf4j
|
|
@@ -23,102 +22,247 @@ public class AdminAdvertiserListServiceImpl implements IAdminAdvertiserListServi
|
|
|
@Autowired
|
|
@Autowired
|
|
|
private AdminAdvertiserListMapper adminAdvertiserListMapper;
|
|
private AdminAdvertiserListMapper adminAdvertiserListMapper;
|
|
|
|
|
|
|
|
|
|
+ private static final String OAUTH2_ADVERTISER_GET_URL = "https://ad.oceanengine.com/open_api/oauth2/advertiser/get/";
|
|
|
|
|
+ private static final String CC_ADVERTISER_LIST_URL = "https://ad.oceanengine.com/open_api/2/customer_center/advertiser/list/";
|
|
|
|
|
+ private static final int CC_PAGE_RETRY_LIMIT = 3;
|
|
|
|
|
+
|
|
|
@Override
|
|
@Override
|
|
|
- public JSONObject bytedanceAdvertiserThreadPool(Integer totalPage, Integer pageSize, Long adminAdvertiserId, Long ccAccountId, String accessToken) {
|
|
|
|
|
- ExecutorService executor = Executors.newFixedThreadPool(5);
|
|
|
|
|
- // 3. 创建线程安全的结果收集器
|
|
|
|
|
- List<Future<AdminAdvertiserListServiceImpl.PageResult>> futures = new ArrayList<>();
|
|
|
|
|
-
|
|
|
|
|
- // 4. 提交分页任务
|
|
|
|
|
- for (int page = 1; page <= totalPage; page++) {
|
|
|
|
|
- final int currentPage = page;
|
|
|
|
|
- Callable<AdminAdvertiserListServiceImpl.PageResult> task = () -> {
|
|
|
|
|
- try {
|
|
|
|
|
- List<Long> bytedanceAccountList = getBytedanceAccountList(adminAdvertiserId, ccAccountId, accessToken, currentPage, 100, 1);
|
|
|
|
|
- // 拉取失败(含重试超限)返回 null,该页视为失败
|
|
|
|
|
- return new AdminAdvertiserListServiceImpl.PageResult(currentPage, bytedanceAccountList);
|
|
|
|
|
- } catch (Exception e) {
|
|
|
|
|
- e.printStackTrace();
|
|
|
|
|
- return new AdminAdvertiserListServiceImpl.PageResult(currentPage, null);
|
|
|
|
|
- }
|
|
|
|
|
- };
|
|
|
|
|
- futures.add(executor.submit(task));
|
|
|
|
|
|
|
+ public JSONObject bytedanceAdvertiserSync(Long adminAdvertiserId, String accessToken) {
|
|
|
|
|
+ JSONObject returnJson = new JSONObject();
|
|
|
|
|
+ // 1. 拉授权主体列表,确定要展开的组织集合(与字节授权回调同口径)
|
|
|
|
|
+ JSONObject authResult = getBytedanceAuthAdvertisers(accessToken);
|
|
|
|
|
+ Integer authCode = authResult.getInteger("code");
|
|
|
|
|
+ if (authCode == null || authCode != 0) {
|
|
|
|
|
+ returnJson.put("code", 500);
|
|
|
|
|
+ returnJson.put("message", "获取字节授权主体列表失败,跳过本次同步");
|
|
|
|
|
+ return returnJson;
|
|
|
|
|
+ }
|
|
|
|
|
+ Long oauthAdminAdvertiserId = authResult.getLong("adminAdvertiserId");
|
|
|
|
|
+ if (oauthAdminAdvertiserId == null) {
|
|
|
|
|
+ returnJson.put("code", 500);
|
|
|
|
|
+ returnJson.put("message", "授权主体中未获取到CUSTOMER_ADMIN纵横管家,跳过本次同步");
|
|
|
|
|
+ return returnJson;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!oauthAdminAdvertiserId.equals(adminAdvertiserId)) {
|
|
|
|
|
+ log.error("入参管家{}与授权主体CUSTOMER_ADMIN{}不一致,跳过本次同步,请检查XXL任务参数", adminAdvertiserId, oauthAdminAdvertiserId);
|
|
|
|
|
+ returnJson.put("code", 500);
|
|
|
|
|
+ returnJson.put("message", "入参管家与授权主体CUSTOMER_ADMIN不一致,跳过本次同步");
|
|
|
|
|
+ return returnJson;
|
|
|
|
|
+ }
|
|
|
|
|
+ List<Long> customerAccounts = authResult.getJSONArray("customerAccounts").toJavaList(Long.class);
|
|
|
|
|
+ List<BytedanceAdvertiserList> normalAccounts = authResult.getJSONArray("normalAccounts")
|
|
|
|
|
+ .toJavaList(BytedanceAdvertiserList.class);
|
|
|
|
|
+ log.info("授权主体下纵横管家/操作员数:{},NORMAL投放主体数:{},adminAdvertiserId:{}",
|
|
|
|
|
+ customerAccounts.size(), normalAccounts.size(), adminAdvertiserId);
|
|
|
|
|
+
|
|
|
|
|
+ // 2. 串行拉各组织第 1 页确定总页数(每组织仅 1 次请求,代价可忽略)
|
|
|
|
|
+ int pageSize = 100;
|
|
|
|
|
+ List<CcOrgFetchPlan> plans = new ArrayList<>();
|
|
|
|
|
+ boolean allSuccess = true;
|
|
|
|
|
+ for (Long ccAccountId : customerAccounts) {
|
|
|
|
|
+ CcPageResult firstPage = fetchCcAdvertiserPageWithRetry(adminAdvertiserId, ccAccountId, accessToken, 1, pageSize);
|
|
|
|
|
+ if (firstPage == null) {
|
|
|
|
|
+ // 组织首页拉取失败时账户集合不完整,本次不做失效清理,避免误删
|
|
|
|
|
+ log.warn("纵横组织首页拉取失败,该组织本次跳过:ccAccountId={}", ccAccountId);
|
|
|
|
|
+ allSuccess = false;
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+ plans.add(new CcOrgFetchPlan(ccAccountId, Math.max(firstPage.getTotalPage(), 1), firstPage.getIds()));
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // 5. 处理结果
|
|
|
|
|
- List<AdminAdvertiserListServiceImpl.PageResult> results = new ArrayList<>();
|
|
|
|
|
- for (Future<AdminAdvertiserListServiceImpl.PageResult> future : futures) {
|
|
|
|
|
|
|
+ // 3. 全部组织的其余分页提交线程池并行拉取
|
|
|
|
|
+ ExecutorService executor = Executors.newFixedThreadPool(10);
|
|
|
|
|
+ List<Future<CcPageResult>> futures = new ArrayList<>();
|
|
|
|
|
+ for (CcOrgFetchPlan plan : plans) {
|
|
|
|
|
+ for (int page = 2; page <= plan.getTotalPage(); page++) {
|
|
|
|
|
+ final int currentPage = page;
|
|
|
|
|
+ futures.add(executor.submit(() -> fetchCcAdvertiserPageWithRetry(adminAdvertiserId, plan.getCcAccountId(), accessToken, currentPage, pageSize)));
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ Map<Long, Map<Integer, List<Long>>> idsByOrgPage = new HashMap<>();
|
|
|
|
|
+ for (Future<CcPageResult> future : futures) {
|
|
|
try {
|
|
try {
|
|
|
- results.add(future.get());
|
|
|
|
|
|
|
+ CcPageResult result = future.get();
|
|
|
|
|
+ if (result == null || result.getIds() == null) {
|
|
|
|
|
+ allSuccess = false;
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+ idsByOrgPage.computeIfAbsent(result.getCcAccountId(), key -> new HashMap<>()).put(result.getPageNumber(), result.getIds());
|
|
|
} catch (InterruptedException | ExecutionException e) {
|
|
} catch (InterruptedException | ExecutionException e) {
|
|
|
- e.printStackTrace();
|
|
|
|
|
- log.error("多线程请求数据失败:" + e.getMessage());
|
|
|
|
|
|
|
+ allSuccess = false;
|
|
|
|
|
+ log.error("多线程请求字节纵横组织账户失败: {}", e.getMessage());
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- // 6. 关闭线程池
|
|
|
|
|
executor.shutdown();
|
|
executor.shutdown();
|
|
|
- try {
|
|
|
|
|
- executor.awaitTermination(1, TimeUnit.MINUTES);
|
|
|
|
|
- } catch (InterruptedException e) {
|
|
|
|
|
- throw new RuntimeException(e);
|
|
|
|
|
- }
|
|
|
|
|
|
|
|
|
|
- // 7. 排序并合并结果
|
|
|
|
|
- results.sort(Comparator.comparingInt(AdminAdvertiserListServiceImpl.PageResult::getPageNumber));
|
|
|
|
|
- List<Long> finalData = results.stream()
|
|
|
|
|
- .filter(r -> r.getData() != null)
|
|
|
|
|
- .flatMap(r -> r.getData().stream())
|
|
|
|
|
- .collect(Collectors.toList());
|
|
|
|
|
|
|
+ // 4. 按组织顺序、页序合并,多组织账户取并集
|
|
|
|
|
+ Set<Long> mergedIds = new LinkedHashSet<>();
|
|
|
|
|
+ for (CcOrgFetchPlan plan : plans) {
|
|
|
|
|
+ mergedIds.addAll(plan.getFirstPageIds());
|
|
|
|
|
+ Map<Integer, List<Long>> orgPages = idsByOrgPage.getOrDefault(plan.getCcAccountId(), Collections.emptyMap());
|
|
|
|
|
+ for (int page = 2; page <= plan.getTotalPage(); page++) {
|
|
|
|
|
+ List<Long> ids = orgPages.get(page);
|
|
|
|
|
+ if (ids != null) {
|
|
|
|
|
+ mergedIds.addAll(ids);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ // 授权主体自身的NORMAL投放账户并入(与字节授权回调同口径:自营企业号也是投放主体)
|
|
|
|
|
+ for (BytedanceAdvertiserList normalAccount : normalAccounts) {
|
|
|
|
|
+ mergedIds.add(normalAccount.getAdvertiserId());
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!normalAccounts.isEmpty()) {
|
|
|
|
|
+ adminAdvertiserListMapper.replaceBytedanceAdvertiserLists(normalAccounts);
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- JSONObject returnJson = new JSONObject();
|
|
|
|
|
- returnJson.put("code", 200);
|
|
|
|
|
- returnJson.put("total", finalData.size());
|
|
|
|
|
- returnJson.put("message", "多线程请求字节账户列表成功");
|
|
|
|
|
- // 全量同步:所有分页均拉取成功时,删除该管家下已不在账户列表中的存量账户;任一分页失败则跳过,防止误删
|
|
|
|
|
- boolean allSuccess = results.stream().allMatch(r -> r.getData() != null);
|
|
|
|
|
- if (allSuccess && !finalData.isEmpty()) {
|
|
|
|
|
- adminAdvertiserListMapper.deleteNotInAdvertiserList(adminAdvertiserId, finalData);
|
|
|
|
|
- returnJson.put("message", "多线程请求字节账户列表成功,已同步删除过期账户");
|
|
|
|
|
- } else if (!allSuccess) {
|
|
|
|
|
- returnJson.put("code", 500);
|
|
|
|
|
- returnJson.put("message", "存在拉取失败分页,跳过本次存量账户清理");
|
|
|
|
|
|
|
+ // 5. 全量同步:所有组织分页均拉取成功才清理存量;任一分页失败则跳过,防止误删
|
|
|
|
|
+ if (allSuccess) {
|
|
|
|
|
+ if (mergedIds.isEmpty()) {
|
|
|
|
|
+ // 全部组织确认已无账户,按全量同步语义清空该管家存量账户
|
|
|
|
|
+ adminAdvertiserListMapper.deleteNotInAdvertiserList(adminAdvertiserId, null);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ adminAdvertiserListMapper.deleteNotInAdvertiserList(adminAdvertiserId, new ArrayList<>(mergedIds));
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
+ returnJson.put("code", allSuccess ? 200 : 500);
|
|
|
|
|
+ returnJson.put("total", mergedIds.size());
|
|
|
|
|
+ returnJson.put("message", allSuccess ? "全量同步成功,已同步删除过期账户" : "存在拉取失败分页,跳过本次存量账户清理");
|
|
|
return returnJson;
|
|
return returnJson;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- @Override
|
|
|
|
|
- public void clearAdminAdvertiserList(Long adminAdvertiserId) {
|
|
|
|
|
- adminAdvertiserListMapper.deleteNotInAdvertiserList(adminAdvertiserId, null);
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 拉取字节授权主体列表(oauth2/advertiser/get),返回:
|
|
|
|
|
+ * code 接口返回码;
|
|
|
|
|
+ * adminAdvertiserId CUSTOMER_ADMIN 纵横管家(归属管家,与字节授权回调一致取最后一个);
|
|
|
|
|
+ * customerAccounts CUSTOMER_ADMIN/CUSTOMER_OPERATOR 去重后的组织账户ID列表;
|
|
|
|
|
+ * normalAccounts NORMAL 角色投放主体(自营企业号)
|
|
|
|
|
+ */
|
|
|
|
|
+ private JSONObject getBytedanceAuthAdvertisers(String accessToken) {
|
|
|
|
|
+ JSONObject result = new JSONObject();
|
|
|
|
|
+ Map<String, Object> paramsMap = new HashMap<>();
|
|
|
|
|
+ paramsMap.put("access_token", accessToken);
|
|
|
|
|
+ String s = HttpUtils.httpGet(OAUTH2_ADVERTISER_GET_URL, paramsMap, null);
|
|
|
|
|
+ JSONObject jsonObject = JSONObject.parseObject(s);
|
|
|
|
|
+ Integer code = Check.isNull(jsonObject) ? null : jsonObject.getInteger("code");
|
|
|
|
|
+ result.put("code", code);
|
|
|
|
|
+ if (code == null || code != 0) {
|
|
|
|
|
+ log.error("获取字节授权主体列表失败: {}", jsonObject);
|
|
|
|
|
+ return result;
|
|
|
|
|
+ }
|
|
|
|
|
+ Long adminAdvertiserId = null;
|
|
|
|
|
+ List<Long> customerAccounts = new ArrayList<>();
|
|
|
|
|
+ Set<Long> customerAccountSeen = new HashSet<>();
|
|
|
|
|
+ List<JSONObject> normalNodes = new ArrayList<>();
|
|
|
|
|
+ JSONObject data = jsonObject.getJSONObject("data");
|
|
|
|
|
+ JSONArray advertisers = Check.isNull(data) ? null : data.getJSONArray("list");
|
|
|
|
|
+ if (!Check.isNull(advertisers)) {
|
|
|
|
|
+ for (int i = 0; i < advertisers.size(); i++) {
|
|
|
|
|
+ JSONObject advertiser = advertisers.getJSONObject(i);
|
|
|
|
|
+ Long advertiserId = advertiser.getLong("advertiser_id");
|
|
|
|
|
+ String accountRole = advertiser.getString("account_role");
|
|
|
|
|
+ if (advertiserId == null) {
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+ // 与字节授权回调同口径:CUSTOMER_ADMIN/CUSTOMER_OPERATOR 作为纵横管家展开组织
|
|
|
|
|
+ if ("CUSTOMER_ADMIN".equals(accountRole)) {
|
|
|
|
|
+ adminAdvertiserId = advertiserId;
|
|
|
|
|
+ }
|
|
|
|
|
+ if ("CUSTOMER_ADMIN".equals(accountRole) || "CUSTOMER_OPERATOR".equals(accountRole)) {
|
|
|
|
|
+ if (customerAccountSeen.add(advertiserId)) {
|
|
|
|
|
+ customerAccounts.add(advertiserId);
|
|
|
|
|
+ }
|
|
|
|
|
+ } else if ("NORMAL".equals(accountRole)) {
|
|
|
|
|
+ normalNodes.add(advertiser);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ // 归属管家确定后再组装NORMAL账户,避免授权主体列表中NORMAL排在CUSTOMER_ADMIN之前时归属为空
|
|
|
|
|
+ List<BytedanceAdvertiserList> normalAccounts = new ArrayList<>();
|
|
|
|
|
+ for (JSONObject normalNode : normalNodes) {
|
|
|
|
|
+ BytedanceAdvertiserList normalAccount = new BytedanceAdvertiserList();
|
|
|
|
|
+ normalAccount.setAdminAdvertiserId(adminAdvertiserId);
|
|
|
|
|
+ normalAccount.setAdvertiserId(normalNode.getLong("advertiser_id"));
|
|
|
|
|
+ normalAccount.setAdvertiserName(normalNode.getString("advertiser_name"));
|
|
|
|
|
+ normalAccount.setAdvertiserType(normalNode.getString("account_role"));
|
|
|
|
|
+ normalAccounts.add(normalAccount);
|
|
|
|
|
+ }
|
|
|
|
|
+ result.put("adminAdvertiserId", adminAdvertiserId);
|
|
|
|
|
+ result.put("customerAccounts", customerAccounts);
|
|
|
|
|
+ result.put("normalAccounts", normalAccounts);
|
|
|
|
|
+ return result;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- @Override
|
|
|
|
|
- public JSONObject getBytedanceAccountListTotal(Long adminAdvertiserId, Long ccAccountId, String accessToken, Integer page, Integer pageSize) {
|
|
|
|
|
- JSONObject returnJson = new JSONObject();
|
|
|
|
|
- Integer totalPage = 0;
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 拉取纵横组织单页(最多重试 CC_PAGE_RETRY_LIMIT 次),成功返回分页结果,失败返回 null
|
|
|
|
|
+ */
|
|
|
|
|
+ private CcPageResult fetchCcAdvertiserPageWithRetry(Long adminAdvertiserId, Long ccAccountId, String accessToken, int page, int pageSize) {
|
|
|
|
|
+ CcPageResult result = null;
|
|
|
|
|
+ int attempt = 0;
|
|
|
|
|
+ while (result == null && attempt < CC_PAGE_RETRY_LIMIT) {
|
|
|
|
|
+ attempt++;
|
|
|
|
|
+ try {
|
|
|
|
|
+ result = fetchCcAdvertiserPage(adminAdvertiserId, ccAccountId, accessToken, page, pageSize);
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.error("请求字节纵横组织账户第{}页异常,ccAccountId:{},第{}次尝试", page, ccAccountId, attempt, e);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return result;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 拉取纵横组织单页(account_source=AD,与字节授权回调同口径),成功分页结果入库并返回,失败返回 null
|
|
|
|
|
+ */
|
|
|
|
|
+ private CcPageResult fetchCcAdvertiserPage(Long adminAdvertiserId, Long ccAccountId, String accessToken, int page, int pageSize) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ Thread.sleep(100L);
|
|
|
|
|
+ } catch (InterruptedException e) {
|
|
|
|
|
+ Thread.currentThread().interrupt();
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
Map<String, Object> paramsMap = new HashMap<>();
|
|
Map<String, Object> paramsMap = new HashMap<>();
|
|
|
paramsMap.put("cc_account_id", ccAccountId);
|
|
paramsMap.put("cc_account_id", ccAccountId);
|
|
|
paramsMap.put("page", page);
|
|
paramsMap.put("page", page);
|
|
|
paramsMap.put("page_size", pageSize);
|
|
paramsMap.put("page_size", pageSize);
|
|
|
|
|
+ paramsMap.put("account_source", "AD");
|
|
|
Map<String, String> headers = new HashMap<>();
|
|
Map<String, String> headers = new HashMap<>();
|
|
|
headers.put("Access-Token", accessToken);
|
|
headers.put("Access-Token", accessToken);
|
|
|
- String s = HttpUtils.httpGet("https://ad.oceanengine.com/open_api/2/customer_center/advertiser/list/", paramsMap, headers);
|
|
|
|
|
|
|
+ String s = HttpUtils.httpGet(CC_ADVERTISER_LIST_URL, paramsMap, headers);
|
|
|
JSONObject jsonObject = JSONObject.parseObject(s);
|
|
JSONObject jsonObject = JSONObject.parseObject(s);
|
|
|
Integer code = Check.isNull(jsonObject) ? null : jsonObject.getInteger("code");
|
|
Integer code = Check.isNull(jsonObject) ? null : jsonObject.getInteger("code");
|
|
|
|
|
+ if (code == null || code != 0) {
|
|
|
|
|
+ log.error("请求字节纵横组织账户列表失败,ccAccountId:{},page:{},code:{},返回信息:{}", ccAccountId, page, code, jsonObject);
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+ JSONObject data = jsonObject.getJSONObject("data");
|
|
|
|
|
+ if (Check.isNull(data)) {
|
|
|
|
|
+ return new CcPageResult(ccAccountId, page, 0, new ArrayList<>());
|
|
|
|
|
+ }
|
|
|
|
|
+ int totalPage = 0;
|
|
|
|
|
+ JSONObject pageInfo = data.getJSONObject("page_info");
|
|
|
|
|
+ if (!Check.isNull(pageInfo) && pageInfo.getInteger("total_page") != null) {
|
|
|
|
|
+ totalPage = pageInfo.getInteger("total_page");
|
|
|
|
|
+ }
|
|
|
List<BytedanceAdvertiserList> advertiserLists = new ArrayList<>();
|
|
List<BytedanceAdvertiserList> advertiserLists = new ArrayList<>();
|
|
|
- if (code != null && code == 0) {
|
|
|
|
|
- JSONObject data = jsonObject.getJSONObject("data");
|
|
|
|
|
- if (!Check.isNull(data)) {
|
|
|
|
|
- JSONObject pageInfo = data.getJSONObject("page_info");
|
|
|
|
|
- if (!Check.isNull(pageInfo)) {
|
|
|
|
|
- totalPage = pageInfo.getInteger("total_page");
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ JSONArray list = data.getJSONArray("list");
|
|
|
|
|
+ if (!Check.isNull(list)) {
|
|
|
|
|
+ for (int i = 0; i < list.size(); i++) {
|
|
|
|
|
+ JSONObject advertiserInfo = list.getJSONObject(i);
|
|
|
|
|
+ BytedanceAdvertiserList advertiserList = new BytedanceAdvertiserList();
|
|
|
|
|
+ advertiserList.setAdminAdvertiserId(adminAdvertiserId);
|
|
|
|
|
+ advertiserList.setAdvertiserId(advertiserInfo.getLong("advertiser_id"));
|
|
|
|
|
+ advertiserList.setAdvertiserName(advertiserInfo.getString("advertiser_name"));
|
|
|
|
|
+ advertiserList.setAdvertiserType(advertiserInfo.getString("advertiser_type"));
|
|
|
|
|
+ advertiserLists.add(advertiserList);
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
}
|
|
}
|
|
|
- returnJson.put("totalPage", totalPage);
|
|
|
|
|
- returnJson.put("code", code);
|
|
|
|
|
- return returnJson;
|
|
|
|
|
|
|
+ if (!advertiserLists.isEmpty()) {
|
|
|
|
|
+ adminAdvertiserListMapper.replaceBytedanceAdvertiserLists(advertiserLists);
|
|
|
|
|
+ }
|
|
|
|
|
+ List<Long> ids = new ArrayList<>();
|
|
|
|
|
+ for (BytedanceAdvertiserList advertiserList : advertiserLists) {
|
|
|
|
|
+ ids.add(advertiserList.getAdvertiserId());
|
|
|
|
|
+ }
|
|
|
|
|
+ return new CcPageResult(ccAccountId, page, totalPage, ids);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
@Override
|
|
@@ -193,75 +337,59 @@ public class AdminAdvertiserListServiceImpl implements IAdminAdvertiserListServi
|
|
|
return adminAdvertiserListMapper.getNotAgentAdvertiserList();
|
|
return adminAdvertiserListMapper.getNotAgentAdvertiserList();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // 分页结果包装类
|
|
|
|
|
- static class PageResult {
|
|
|
|
|
- private final int pageNumber;
|
|
|
|
|
- private final List<Long> data;
|
|
|
|
|
|
|
+ // 组织拉取计划:第 1 页已解析账户ID + 总页数
|
|
|
|
|
+ static class CcOrgFetchPlan {
|
|
|
|
|
+ private final Long ccAccountId;
|
|
|
|
|
+ private final int totalPage;
|
|
|
|
|
+ private final List<Long> firstPageIds;
|
|
|
|
|
|
|
|
- public PageResult(int pageNumber, List<Long> data) {
|
|
|
|
|
- this.pageNumber = pageNumber;
|
|
|
|
|
- this.data = data;
|
|
|
|
|
|
|
+ CcOrgFetchPlan(Long ccAccountId, int totalPage, List<Long> firstPageIds) {
|
|
|
|
|
+ this.ccAccountId = ccAccountId;
|
|
|
|
|
+ this.totalPage = totalPage;
|
|
|
|
|
+ this.firstPageIds = firstPageIds;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- public int getPageNumber() {
|
|
|
|
|
- return pageNumber;
|
|
|
|
|
|
|
+ public Long getCcAccountId() {
|
|
|
|
|
+ return ccAccountId;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- public List<Long> getData() {
|
|
|
|
|
- return data;
|
|
|
|
|
|
|
+ public int getTotalPage() {
|
|
|
|
|
+ return totalPage;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public List<Long> getFirstPageIds() {
|
|
|
|
|
+ return firstPageIds;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // 单页拉取结果:ids 为 null 表示重试后仍失败
|
|
|
|
|
+ static class CcPageResult {
|
|
|
|
|
+ private final Long ccAccountId;
|
|
|
|
|
+ private final int pageNumber;
|
|
|
|
|
+ private final int totalPage;
|
|
|
|
|
+ private final List<Long> ids;
|
|
|
|
|
|
|
|
- private List<Long> getBytedanceAccountList(Long adminAdvertiserId, Long ccAccountId, String accessToken, Integer page, Integer pageSize, Integer requestCount) throws InterruptedException {
|
|
|
|
|
- try {
|
|
|
|
|
- Thread.sleep(100);
|
|
|
|
|
- } catch (InterruptedException e) {
|
|
|
|
|
- throw new RuntimeException(e);
|
|
|
|
|
|
|
+ CcPageResult(Long ccAccountId, int pageNumber, int totalPage, List<Long> ids) {
|
|
|
|
|
+ this.ccAccountId = ccAccountId;
|
|
|
|
|
+ this.pageNumber = pageNumber;
|
|
|
|
|
+ this.totalPage = totalPage;
|
|
|
|
|
+ this.ids = ids;
|
|
|
}
|
|
}
|
|
|
- if (requestCount > 3) {
|
|
|
|
|
- log.error("重试次数大于3次,跳出本页数据请求任务,adminAdvertiserId:{}", adminAdvertiserId);
|
|
|
|
|
- return null;
|
|
|
|
|
|
|
+
|
|
|
|
|
+ public Long getCcAccountId() {
|
|
|
|
|
+ return ccAccountId;
|
|
|
}
|
|
}
|
|
|
- Map<String, Object> paramsMap = new HashMap<>();
|
|
|
|
|
- paramsMap.put("cc_account_id", ccAccountId);
|
|
|
|
|
- paramsMap.put("page", page);
|
|
|
|
|
- paramsMap.put("page_size", pageSize);
|
|
|
|
|
- Map<String, String> headers = new HashMap<>();
|
|
|
|
|
- headers.put("Access-Token", accessToken);
|
|
|
|
|
- String s = HttpUtils.httpGet("https://ad.oceanengine.com/open_api/2/customer_center/advertiser/list/", paramsMap, headers);
|
|
|
|
|
- JSONObject jsonObject = JSONObject.parseObject(s);
|
|
|
|
|
- Integer code = Check.isNull(jsonObject) ? null : jsonObject.getInteger("code");
|
|
|
|
|
- List<BytedanceAdvertiserList> advertiserLists = new ArrayList<>();
|
|
|
|
|
- if (code != null && code == 0) {
|
|
|
|
|
- JSONObject data = jsonObject.getJSONObject("data");
|
|
|
|
|
- if (!Check.isNull(data)) {
|
|
|
|
|
- JSONArray list = data.getJSONArray("list");
|
|
|
|
|
- if (!Check.isNull(list)) {
|
|
|
|
|
- for (int i = 0; i < list.size(); i++) {
|
|
|
|
|
- BytedanceAdvertiserList advertiserList = new BytedanceAdvertiserList();
|
|
|
|
|
- JSONObject advertiserInfo = list.getJSONObject(i);
|
|
|
|
|
- advertiserList.setAdminAdvertiserId(adminAdvertiserId);
|
|
|
|
|
- advertiserList.setAdvertiserId(advertiserInfo.getLong("advertiser_id"));
|
|
|
|
|
- advertiserList.setAdvertiserName(advertiserInfo.getString("advertiser_name"));
|
|
|
|
|
- advertiserList.setAdvertiserType(advertiserInfo.getString("advertiser_type"));
|
|
|
|
|
- advertiserLists.add(advertiserList);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- } else {
|
|
|
|
|
- log.error("请求字节账户列表失败,进入重试任务,当前请求次数:{}", requestCount);
|
|
|
|
|
- Thread.sleep(1000);
|
|
|
|
|
- return getBytedanceAccountList(adminAdvertiserId, ccAccountId, accessToken, page, pageSize, requestCount + 1);
|
|
|
|
|
|
|
+
|
|
|
|
|
+ public int getPageNumber() {
|
|
|
|
|
+ return pageNumber;
|
|
|
}
|
|
}
|
|
|
- if (!Check.isNull(advertiserLists)) {
|
|
|
|
|
- adminAdvertiserListMapper.replaceBytedanceAdvertiserLists(advertiserLists);
|
|
|
|
|
- log.info("字节账户数据插入成功,当前页数:{},数据条数:{}", page, advertiserLists.size());
|
|
|
|
|
|
|
+
|
|
|
|
|
+ public int getTotalPage() {
|
|
|
|
|
+ return totalPage;
|
|
|
}
|
|
}
|
|
|
- List<Long> data = new ArrayList<>();
|
|
|
|
|
- for (BytedanceAdvertiserList advertiserList : advertiserLists) {
|
|
|
|
|
- data.add(advertiserList.getAdvertiserId());
|
|
|
|
|
|
|
+
|
|
|
|
|
+ public List<Long> getIds() {
|
|
|
|
|
+ return ids;
|
|
|
}
|
|
}
|
|
|
- return data;
|
|
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|