|
|
@@ -48,7 +48,7 @@ import com.moka.gdtauto.common.Constant;
|
|
|
|
|
|
/**
|
|
|
* 广告计划配置服务实现类
|
|
|
- *
|
|
|
+ *
|
|
|
* @author moka
|
|
|
* @since 2026-02-24
|
|
|
*/
|
|
|
@@ -57,7 +57,7 @@ import com.moka.gdtauto.common.Constant;
|
|
|
public class AdPlanConfServiceImpl extends ServiceImpl<AdPlanConfMapper, AdPlanConf> implements AdPlanConfService {
|
|
|
|
|
|
@Autowired
|
|
|
- private AdPlanService adPlanService;
|
|
|
+ private AdPlanService adPlanService;
|
|
|
@Autowired
|
|
|
private AdPlanCreativeService adPlanCreativeService;
|
|
|
@Autowired
|
|
|
@@ -67,17 +67,79 @@ public class AdPlanConfServiceImpl extends ServiceImpl<AdPlanConfMapper, AdPlanC
|
|
|
@Autowired
|
|
|
private TencentAccountService tencentAccountService;
|
|
|
|
|
|
-
|
|
|
+ @Override
|
|
|
+ public void suspendByConfId(Long confId) {
|
|
|
+ AdPlanConf conf = this.getById(confId);
|
|
|
+ String jobNumber = conf.getJobNumber();
|
|
|
+ List<AdPlan> plans = adPlanService.lambdaQuery()
|
|
|
+ .eq(AdPlan::getAdPlanConfId, confId)
|
|
|
+ .gt(AdPlan::getAdgroupId, 0)
|
|
|
+ .orderByAsc(AdPlan::getId).list();
|
|
|
+ try {
|
|
|
+ TencentAds tencentAds = clientFactory.getTencentAds(jobNumber, Constant.MAIN_ORG_ACCOUNT_ID);
|
|
|
+ for (AdPlan plan : plans) {
|
|
|
+ try {
|
|
|
+ AdgroupsUpdateRequest req = new AdgroupsUpdateRequest();
|
|
|
+ req.setAccountId(plan.getAccountId());
|
|
|
+ req.setAdgroupId(plan.getAdgroupId());
|
|
|
+ req.setConfiguredStatus(ConfiguredStatus.SUSPEND);
|
|
|
+ tencentAds.adgroups().adgroupsUpdate(req);
|
|
|
+ plan.setSystemStatus("ADGROUP_STATUS_SUSPEND");
|
|
|
+ } catch (Exception e) {
|
|
|
+ plan.setFailReason(truncateError(e.getMessage()));
|
|
|
+ }
|
|
|
+ adPlanService.lambdaUpdate().eq(AdPlan::getId,plan.getId())
|
|
|
+ .set(AdPlan::getSystemStatus, plan.getSystemStatus())
|
|
|
+ .set(AdPlan::getFailReason, plan.getFailReason())
|
|
|
+ .update();
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ throw new RuntimeException(e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void suspendByPlanIds(Long confId, Long planIds) {
|
|
|
+ AdPlanConf conf = this.getById(confId);
|
|
|
+ String jobNumber = conf.getJobNumber();
|
|
|
+ List<AdPlan> plans = adPlanService.lambdaQuery()
|
|
|
+ .eq(AdPlan::getAdPlanConfId, confId)
|
|
|
+ .in(AdPlan::getId, planIds)
|
|
|
+ .gt(AdPlan::getAdgroupId, 0)
|
|
|
+ .orderByAsc(AdPlan::getId).list();
|
|
|
+ try {
|
|
|
+ TencentAds tencentAds = clientFactory.getTencentAds(jobNumber, Constant.MAIN_ORG_ACCOUNT_ID);
|
|
|
+ for (AdPlan plan : plans) {
|
|
|
+ try {
|
|
|
+ AdgroupsUpdateRequest req = new AdgroupsUpdateRequest();
|
|
|
+ req.setAccountId(plan.getAccountId());
|
|
|
+ req.setAdgroupId(plan.getAdgroupId());
|
|
|
+ req.setConfiguredStatus(ConfiguredStatus.SUSPEND);
|
|
|
+ tencentAds.adgroups().adgroupsUpdate(req);
|
|
|
+ plan.setSystemStatus("ADGROUP_STATUS_SUSPEND");
|
|
|
+ } catch (Exception e) {
|
|
|
+ plan.setFailReason(truncateError(e.getMessage()));
|
|
|
+ }
|
|
|
+ adPlanService.lambdaUpdate().eq(AdPlan::getId,plan.getId())
|
|
|
+ .set(AdPlan::getSystemStatus, plan.getSystemStatus())
|
|
|
+ .set(AdPlan::getFailReason, plan.getFailReason())
|
|
|
+ .update();
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ throw new RuntimeException(e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
@Override
|
|
|
@Transactional(rollbackFor = Exception.class)
|
|
|
public Long createAdPlanConf(AdPlanConf adPlanConf) {
|
|
|
adPlanConf.setCreateTime(LocalDateTime.now());
|
|
|
adPlanConf.setUpdateTime(LocalDateTime.now());
|
|
|
adPlanConf.setDeleted(0);
|
|
|
-
|
|
|
+
|
|
|
this.save(adPlanConf);
|
|
|
log.info("创建广告计划配置成功,配置ID: {}, 配置名称: {}", adPlanConf.getId(), adPlanConf.getName());
|
|
|
-
|
|
|
+
|
|
|
return adPlanConf.getId();
|
|
|
}
|
|
|
|
|
|
@@ -87,16 +149,16 @@ public class AdPlanConfServiceImpl extends ServiceImpl<AdPlanConfMapper, AdPlanC
|
|
|
AdPlanConf existingConf = this.getOne(new LambdaQueryWrapper<AdPlanConf>()
|
|
|
.eq(AdPlanConf::getId, adPlanConf.getId())
|
|
|
.eq(AdPlanConf::getDeleted, 0));
|
|
|
-
|
|
|
+
|
|
|
if (existingConf == null) {
|
|
|
log.warn("广告计划配置不存在或无权限,配置ID: {}, 用户ID: {}", adPlanConf.getId(), adPlanConf.getUserId());
|
|
|
return false;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
adPlanConf.setUpdateTime(LocalDateTime.now());
|
|
|
boolean result = this.updateById(adPlanConf);
|
|
|
log.info("更新广告计划配置成功,配置ID: {}", adPlanConf.getId());
|
|
|
-
|
|
|
+
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
@@ -107,16 +169,16 @@ public class AdPlanConfServiceImpl extends ServiceImpl<AdPlanConfMapper, AdPlanC
|
|
|
.eq(AdPlanConf::getId, confId)
|
|
|
.eq(AdPlanConf::getUserId, userId)
|
|
|
.eq(AdPlanConf::getDeleted, 0));
|
|
|
-
|
|
|
+
|
|
|
if (existingConf == null) {
|
|
|
log.warn("广告计划配置不存在或无权限,配置ID: {}, 用户ID: {}", confId, userId);
|
|
|
return false;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
existingConf.setDeleted(1);
|
|
|
existingConf.setUpdateTime(LocalDateTime.now());
|
|
|
boolean result = this.updateById(existingConf);
|
|
|
-
|
|
|
+
|
|
|
log.info("删除广告计划配置成功,配置ID: {}", confId);
|
|
|
return result;
|
|
|
}
|
|
|
@@ -124,11 +186,11 @@ public class AdPlanConfServiceImpl extends ServiceImpl<AdPlanConfMapper, AdPlanC
|
|
|
@Override
|
|
|
public Page<AdPlanConf> getUserAdPlanConfs(Long userId, Integer pageNum, Integer pageSize) {
|
|
|
Page<AdPlanConf> page = new Page<>(pageNum, pageSize);
|
|
|
-
|
|
|
+
|
|
|
LambdaQueryWrapper<AdPlanConf> queryWrapper = new LambdaQueryWrapper<>();
|
|
|
queryWrapper.eq(AdPlanConf::getDeleted, 0)
|
|
|
.orderByDesc(AdPlanConf::getCreateTime);
|
|
|
-
|
|
|
+
|
|
|
return this.page(page, queryWrapper);
|
|
|
}
|
|
|
|
|
|
@@ -188,12 +250,12 @@ public class AdPlanConfServiceImpl extends ServiceImpl<AdPlanConfMapper, AdPlanC
|
|
|
@Override
|
|
|
public void asyncTencentInterface(List<AdPlan> plans) {
|
|
|
log.info("异步对接腾讯接口开始,计划数量: {}", plans.size());
|
|
|
-
|
|
|
+
|
|
|
if (plans == null || plans.isEmpty()) {
|
|
|
log.warn("计划列表为空,无需处理");
|
|
|
return;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// 获取配置信息
|
|
|
Long confId = plans.get(0).getAdPlanConfId();
|
|
|
AdPlanConf conf = this.getById(confId);
|
|
|
@@ -201,14 +263,14 @@ public class AdPlanConfServiceImpl extends ServiceImpl<AdPlanConfMapper, AdPlanC
|
|
|
log.error("未找到广告计划配置,confId={}", confId);
|
|
|
return;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
String jobNumber = conf.getJobNumber();
|
|
|
-
|
|
|
+
|
|
|
// 遍历处理每个 AdPlan
|
|
|
for (AdPlan plan : plans) {
|
|
|
try {
|
|
|
log.info("开始处理广告计划,planId={}, adgroupName={}", plan.getId(), plan.getAdgroupName());
|
|
|
-
|
|
|
+
|
|
|
if (plan.getAdgroupId() > 0 || plan.getStatus() == AdPlanStatus.SUCCESS) {
|
|
|
// 创建动态创意
|
|
|
createDynamicCreatives(plan, jobNumber, Constant.MAIN_ORG_ACCOUNT_ID);
|
|
|
@@ -218,21 +280,21 @@ public class AdPlanConfServiceImpl extends ServiceImpl<AdPlanConfMapper, AdPlanC
|
|
|
adPlanService.updateById(plan);
|
|
|
continue;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
|
|
|
// 更新状态为执行中
|
|
|
plan.setStatus(AdPlanStatus.IN_PROGRESS);
|
|
|
adPlanService.updateById(plan);
|
|
|
-
|
|
|
+
|
|
|
// 根据 adType 构建请求
|
|
|
AdgroupsAddRequest request = buildAdgroupRequest(plan);
|
|
|
-
|
|
|
+
|
|
|
// 创建广告组
|
|
|
log.info("调用腾讯API创建广告组,accountId={}, orgAccountId={}", plan.getAccountId(), Constant.MAIN_ORG_ACCOUNT_ID);
|
|
|
AdgroupsAddResponseData response = tencentAdsAdgroupService.createAdgroup(
|
|
|
jobNumber, Constant.MAIN_ORG_ACCOUNT_ID, request
|
|
|
);
|
|
|
-
|
|
|
+
|
|
|
// 更新 adgroupId
|
|
|
plan.setAdgroupId(response.getAdgroupId());
|
|
|
log.info("广告组创建成功,adgroupId={}", response.getAdgroupId());
|
|
|
@@ -242,14 +304,14 @@ public class AdPlanConfServiceImpl extends ServiceImpl<AdPlanConfMapper, AdPlanC
|
|
|
plan.setFailReason("");
|
|
|
plan.setSuccessTime(LocalDateTime.now());
|
|
|
adPlanService.updateById(plan);
|
|
|
-
|
|
|
+
|
|
|
// 创建动态创意
|
|
|
if (plan.getAdgroupId() > 0) {
|
|
|
createDynamicCreatives(plan, jobNumber, Constant.MAIN_ORG_ACCOUNT_ID);
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
log.info("广告计划处理成功,planId={}, adgroupId={}", plan.getId(), plan.getAdgroupId());
|
|
|
-
|
|
|
+
|
|
|
} catch (Exception e) {
|
|
|
// 记录失败
|
|
|
log.error("创建广告失败, planId={}", plan.getId(), e);
|
|
|
@@ -258,13 +320,13 @@ public class AdPlanConfServiceImpl extends ServiceImpl<AdPlanConfMapper, AdPlanC
|
|
|
adPlanService.updateById(plan);
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// 更新 AdPlanConf 状态
|
|
|
updateAdPlanConfStatus(confId, plans);
|
|
|
-
|
|
|
+
|
|
|
log.info("异步对接腾讯接口完成,confId={}", confId);
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
/**
|
|
|
* 构建广告组请求
|
|
|
*/
|
|
|
@@ -298,19 +360,19 @@ public class AdPlanConfServiceImpl extends ServiceImpl<AdPlanConfMapper, AdPlanC
|
|
|
);
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
/**
|
|
|
* 创建动态创意
|
|
|
*/
|
|
|
- private void createDynamicCreatives(AdPlan plan, String jobNumber, Long orgAccountId)
|
|
|
+ private void createDynamicCreatives(AdPlan plan, String jobNumber, Long orgAccountId)
|
|
|
throws Exception {
|
|
|
-
|
|
|
+
|
|
|
TencentAds tencentAds = clientFactory.getTencentAds(jobNumber, orgAccountId);
|
|
|
-
|
|
|
+
|
|
|
for (AdPlanCreative creative : plan.getAdCreatives()) {
|
|
|
try {
|
|
|
log.info("开始创建动态创意,planId={}, creativeName={}", plan.getId(), creative.getCreativeName());
|
|
|
-
|
|
|
+
|
|
|
if (creative.getCreativeId() > 0 || creative.getStatus() == AdPlanStatus.SUCCESS)
|
|
|
continue;
|
|
|
|
|
|
@@ -335,12 +397,12 @@ public class AdPlanConfServiceImpl extends ServiceImpl<AdPlanConfMapper, AdPlanC
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
if (creative.getBrandImageId() == 0) {
|
|
|
// 上传品牌形象
|
|
|
String brandImageUrl = creative.getBrandImageUrl();
|
|
|
String brandName = creative.getBrandName();
|
|
|
- if (brandImageUrl != null && !brandImageUrl.isEmpty()
|
|
|
+ if (brandImageUrl != null && !brandImageUrl.isEmpty()
|
|
|
&& brandName != null && !brandName.isEmpty()) {
|
|
|
try {
|
|
|
log.info("开始上传品牌形象,URL: {}, 品牌名称: {}", brandImageUrl, brandName);
|
|
|
@@ -354,24 +416,24 @@ public class AdPlanConfServiceImpl extends ServiceImpl<AdPlanConfMapper, AdPlanC
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
|
|
|
// 构建动态创意请求
|
|
|
DynamicCreativesAddRequest request = buildDynamicCreativeRequest(plan, creative);
|
|
|
-
|
|
|
+
|
|
|
// 调用API创建
|
|
|
- DynamicCreativesAddResponseData response =
|
|
|
+ DynamicCreativesAddResponseData response =
|
|
|
tencentAds.dynamicCreatives().dynamicCreativesAdd(request);
|
|
|
-
|
|
|
+
|
|
|
// 更新创意ID和状态
|
|
|
creative.setCreativeId(response.getDynamicCreativeId());
|
|
|
creative.setStatus(AdPlanStatus.SUCCESS);
|
|
|
creative.setFailReason("");
|
|
|
creative.setSuccessTime(LocalDateTime.now());
|
|
|
adPlanCreativeService.updateById(creative);
|
|
|
-
|
|
|
+
|
|
|
log.info("动态创意创建成功,creativeId={}", response.getDynamicCreativeId());
|
|
|
-
|
|
|
+
|
|
|
} catch (Exception e) {
|
|
|
log.error("创建动态创意失败, creativeId={}", creative.getId(), e);
|
|
|
creative.setStatus(AdPlanStatus.FAILED);
|
|
|
@@ -381,7 +443,7 @@ public class AdPlanConfServiceImpl extends ServiceImpl<AdPlanConfMapper, AdPlanC
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
/**
|
|
|
* 构建动态创意请求
|
|
|
*/
|
|
|
@@ -392,61 +454,61 @@ public class AdPlanConfServiceImpl extends ServiceImpl<AdPlanConfMapper, AdPlanC
|
|
|
request.setDynamicCreativeName(creative.getCreativeName());
|
|
|
request.setDeliveryMode(DeliveryMode.COMPONENT);
|
|
|
request.setDynamicCreativeType(DynamicCreativeType.PROGRAM);
|
|
|
-
|
|
|
+
|
|
|
CreativeComponents components = new CreativeComponents();
|
|
|
-
|
|
|
+
|
|
|
// 标题组件 (固定标题)
|
|
|
components.setTitle(buildTitleComponents(creative.getCreativeText()));
|
|
|
-
|
|
|
+
|
|
|
// 文案组件 (固定文案)
|
|
|
components.setDescription(buildDescriptionComponents(creative.getCreativeText()));
|
|
|
-
|
|
|
+
|
|
|
// 图片组件 (从 creative.imageId)
|
|
|
components.setImage(buildImageComponents(creative.getImageId()));
|
|
|
-
|
|
|
+
|
|
|
// 品牌形象 (固定品牌)
|
|
|
components.setBrand(buildBrandComponents(creative.getBrandImageId(), creative.getBrandName()));
|
|
|
-
|
|
|
+
|
|
|
// 落地页 (从 creative.pageIds, 逗号分隔)
|
|
|
components.setMainJumpInfo(buildJumpInfoComponents(creative.getPageIds()));
|
|
|
-
|
|
|
+
|
|
|
// 行动按钮 (数量与落地页一致, 文案统一)
|
|
|
components.setActionButton(buildActionButtonComponents(creative.getPageIds(), creative.getButtonText()));
|
|
|
-
|
|
|
+
|
|
|
request.setCreativeComponents(components);
|
|
|
return request;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
/**
|
|
|
* 构建标题组件
|
|
|
*/
|
|
|
private List<TitleComponent> buildTitleComponents(String creativeText) {
|
|
|
List<TitleComponent> titles = new ArrayList<>();
|
|
|
-
|
|
|
+
|
|
|
TitleComponent title1 = new TitleComponent();
|
|
|
TitleStruct titleValue1 = new TitleStruct();
|
|
|
titleValue1.setContent(creativeText);
|
|
|
title1.setValue(titleValue1);
|
|
|
titles.add(title1);
|
|
|
-
|
|
|
+
|
|
|
return titles;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
/**
|
|
|
* 构建文案组件
|
|
|
*/
|
|
|
private List<DescriptionComponent> buildDescriptionComponents(String creativeText) {
|
|
|
List<DescriptionComponent> descriptions = new ArrayList<>();
|
|
|
-
|
|
|
+
|
|
|
DescriptionComponent desc1 = new DescriptionComponent();
|
|
|
DescriptionStruct descValue1 = new DescriptionStruct();
|
|
|
descValue1.setContent(creativeText);
|
|
|
desc1.setValue(descValue1);
|
|
|
descriptions.add(desc1);
|
|
|
-
|
|
|
+
|
|
|
return descriptions;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
/**
|
|
|
* 构建图片组件
|
|
|
*/
|
|
|
@@ -457,40 +519,40 @@ public class AdPlanConfServiceImpl extends ServiceImpl<AdPlanConfMapper, AdPlanC
|
|
|
imageValue.setImageId(String.valueOf(imageId));
|
|
|
image.setValue(imageValue);
|
|
|
images.add(image);
|
|
|
-
|
|
|
+
|
|
|
return images;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
/**
|
|
|
* 构建品牌组件
|
|
|
*/
|
|
|
private List<BrandComponent> buildBrandComponents(Long brandImageId, String brandName) {
|
|
|
List<BrandComponent> brands = new ArrayList<>();
|
|
|
-
|
|
|
+
|
|
|
BrandComponent brand = new BrandComponent();
|
|
|
BrandStruct brandValue = new BrandStruct();
|
|
|
brandValue.setBrandName(brandName);
|
|
|
brandValue.setBrandImageId(String.valueOf(brandImageId));
|
|
|
brand.setValue(brandValue);
|
|
|
brands.add(brand);
|
|
|
-
|
|
|
+
|
|
|
return brands;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
/**
|
|
|
* 构建落地页组件
|
|
|
*/
|
|
|
private List<JumpinfoComponent> buildJumpInfoComponents(String pageIds) {
|
|
|
List<JumpinfoComponent> jumpInfoList = new ArrayList<>();
|
|
|
-
|
|
|
+
|
|
|
if (pageIds == null || pageIds.isEmpty()) {
|
|
|
return jumpInfoList;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
String[] pageIdArray = pageIds.split(",");
|
|
|
for (String pageIdStr : pageIdArray) {
|
|
|
Long pageId = Long.parseLong(pageIdStr.trim());
|
|
|
-
|
|
|
+
|
|
|
JumpinfoComponent jumpinfo = new JumpinfoComponent();
|
|
|
JumpinfoStruct jumpinfoValue = new JumpinfoStruct();
|
|
|
PageSpec pageSpec = new PageSpec();
|
|
|
@@ -502,20 +564,20 @@ public class AdPlanConfServiceImpl extends ServiceImpl<AdPlanConfMapper, AdPlanC
|
|
|
jumpinfo.setValue(jumpinfoValue);
|
|
|
jumpInfoList.add(jumpinfo);
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
return jumpInfoList;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
/**
|
|
|
* 构建行动按钮组件
|
|
|
*/
|
|
|
private List<ActionButtonComponent> buildActionButtonComponents(String pageIds, String buttonText) {
|
|
|
List<ActionButtonComponent> actionButtons = new ArrayList<>();
|
|
|
-
|
|
|
+
|
|
|
if (pageIds == null || pageIds.isEmpty()) {
|
|
|
return actionButtons;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
String[] pageIdArray = pageIds.split(",");
|
|
|
for (int i = 0; i < pageIdArray.length; i++) {
|
|
|
ActionButtonComponent actionButton = new ActionButtonComponent();
|
|
|
@@ -524,10 +586,10 @@ public class AdPlanConfServiceImpl extends ServiceImpl<AdPlanConfMapper, AdPlanC
|
|
|
actionButton.setValue(actionButtonValue);
|
|
|
actionButtons.add(actionButton);
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
return actionButtons;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
/**
|
|
|
* 更新 AdPlanConf 状态
|
|
|
*/
|
|
|
@@ -536,14 +598,14 @@ public class AdPlanConfServiceImpl extends ServiceImpl<AdPlanConfMapper, AdPlanC
|
|
|
int totalCount = plans.size();
|
|
|
int successCount = (int) plans.stream().filter(p -> AdPlanStatus.SUCCESS.equals(p.getStatus())).count();
|
|
|
int failCount = totalCount - successCount;
|
|
|
-
|
|
|
+
|
|
|
// 更新配置状态
|
|
|
AdPlanConf conf = this.getById(confId);
|
|
|
if (conf == null) {
|
|
|
log.warn("未找到广告计划配置,confId={}", confId);
|
|
|
return;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
if (failCount == 0) {
|
|
|
conf.setStatus(AdPlanStatus.SUCCESS); // 全部成功
|
|
|
conf.setSuccessTime(LocalDateTime.now());
|
|
|
@@ -555,12 +617,12 @@ public class AdPlanConfServiceImpl extends ServiceImpl<AdPlanConfMapper, AdPlanC
|
|
|
conf.setStatus(AdPlanStatus.PART_FAILED); // 部分失败
|
|
|
conf.setFailReason(String.format("成功%d个,失败%d个", successCount, failCount));
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
this.updateById(conf);
|
|
|
- log.info("更新广告计划配置状态,confId={}, status={}, 成功:{}, 失败:{}",
|
|
|
+ log.info("更新广告计划配置状态,confId={}, status={}, 成功:{}, 失败:{}",
|
|
|
confId, conf.getStatus(), successCount, failCount);
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
/**
|
|
|
* 截断错误信息到200字符
|
|
|
*/
|
|
|
@@ -570,15 +632,15 @@ public class AdPlanConfServiceImpl extends ServiceImpl<AdPlanConfMapper, AdPlanC
|
|
|
}
|
|
|
return error.length() > 200 ? error.substring(0, 200) + "..." : error;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
/**
|
|
|
* 计算开始日期 (明天)
|
|
|
*/
|
|
|
private String calculateBeginDate() {
|
|
|
return LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
|
|
|
}
|
|
|
-
|
|
|
-
|
|
|
+
|
|
|
+
|
|
|
/**
|
|
|
* 解析定向配置ID
|
|
|
*/
|
|
|
@@ -593,7 +655,7 @@ public class AdPlanConfServiceImpl extends ServiceImpl<AdPlanConfMapper, AdPlanC
|
|
|
return 1;
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
/**
|
|
|
* 解析站点配置ID
|
|
|
*/
|
|
|
@@ -608,10 +670,10 @@ public class AdPlanConfServiceImpl extends ServiceImpl<AdPlanConfMapper, AdPlanC
|
|
|
return 1;
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
/**
|
|
|
* 从URL下载图片并上传到腾讯广告
|
|
|
- *
|
|
|
+ *
|
|
|
* @param tencentAds 腾讯广告SDK实例
|
|
|
* @param accountId 账户ID
|
|
|
* @param imageUrl 图片URL
|
|
|
@@ -624,14 +686,14 @@ public class AdPlanConfServiceImpl extends ServiceImpl<AdPlanConfMapper, AdPlanC
|
|
|
try {
|
|
|
// 1. 从URL下载图片到临时文件
|
|
|
tempFile = downloadFileFromUrl(imageUrl);
|
|
|
-
|
|
|
+
|
|
|
// 2. 读取文件并转Base64
|
|
|
byte[] imageBytes = Files.readAllBytes(tempFile.toPath());
|
|
|
String base64Image = Base64.getEncoder().encodeToString(imageBytes);
|
|
|
-
|
|
|
+
|
|
|
// 3. 计算MD5签名
|
|
|
String signature = calculateMD5(imageBytes);
|
|
|
-
|
|
|
+
|
|
|
// 4. 调用腾讯广告API上传图片
|
|
|
ImagesAddResponseData response = tencentAds.images()
|
|
|
.imagesAdd(
|
|
|
@@ -647,16 +709,16 @@ public class AdPlanConfServiceImpl extends ServiceImpl<AdPlanConfMapper, AdPlanC
|
|
|
null, // resizeHeight
|
|
|
null // resizeFileSize
|
|
|
);
|
|
|
-
|
|
|
+
|
|
|
if (response == null || response.getImageId() == null) {
|
|
|
throw new Exception("图片上传返回结果为空");
|
|
|
}
|
|
|
-
|
|
|
- log.info("图片上传成功,imageId: {}, 宽度: {}px, 高度: {}px",
|
|
|
+
|
|
|
+ log.info("图片上传成功,imageId: {}, 宽度: {}px, 高度: {}px",
|
|
|
response.getImageId(), response.getImageWidth(), response.getImageHeight());
|
|
|
-
|
|
|
+
|
|
|
return Long.parseLong(response.getImageId());
|
|
|
-
|
|
|
+
|
|
|
} finally {
|
|
|
// 5. 删除临时文件
|
|
|
if (tempFile != null && tempFile.exists()) {
|
|
|
@@ -664,10 +726,10 @@ public class AdPlanConfServiceImpl extends ServiceImpl<AdPlanConfMapper, AdPlanC
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
/**
|
|
|
* 从URL下载品牌形象并上传到腾讯广告
|
|
|
- *
|
|
|
+ *
|
|
|
* @param tencentAds 腾讯广告SDK实例
|
|
|
* @param accountId 账户ID
|
|
|
* @param brandImageUrl 品牌形象图片URL
|
|
|
@@ -680,7 +742,7 @@ public class AdPlanConfServiceImpl extends ServiceImpl<AdPlanConfMapper, AdPlanC
|
|
|
try {
|
|
|
// 1. 从URL下载图片到临时文件
|
|
|
tempFile = downloadFileFromUrl(brandImageUrl);
|
|
|
-
|
|
|
+
|
|
|
// 2. 调用腾讯广告API创建品牌形象
|
|
|
BrandAddResponseData response = tencentAds.brand()
|
|
|
.brandAdd(
|
|
|
@@ -688,16 +750,16 @@ public class AdPlanConfServiceImpl extends ServiceImpl<AdPlanConfMapper, AdPlanC
|
|
|
brandName, // name
|
|
|
tempFile // brandImageFile
|
|
|
);
|
|
|
-
|
|
|
+
|
|
|
if (response == null || response.getImageId() == null) {
|
|
|
throw new Exception("品牌形象上传返回结果为空");
|
|
|
}
|
|
|
-
|
|
|
- log.info("品牌形象上传成功,imageId: {}, 品牌名称: {}, 宽度: {}px, 高度: {}px",
|
|
|
+
|
|
|
+ log.info("品牌形象上传成功,imageId: {}, 品牌名称: {}, 宽度: {}px, 高度: {}px",
|
|
|
response.getImageId(), response.getName(), response.getWidth(), response.getHeight());
|
|
|
-
|
|
|
+
|
|
|
return Long.parseLong(response.getImageId());
|
|
|
-
|
|
|
+
|
|
|
} finally {
|
|
|
// 3. 删除临时文件
|
|
|
if (tempFile != null && tempFile.exists()) {
|
|
|
@@ -705,10 +767,10 @@ public class AdPlanConfServiceImpl extends ServiceImpl<AdPlanConfMapper, AdPlanC
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
/**
|
|
|
* 从URL下载文件到临时文件
|
|
|
- *
|
|
|
+ *
|
|
|
* @param fileUrl 文件URL
|
|
|
* @return 临时文件
|
|
|
* @throws Exception 下载失败
|
|
|
@@ -717,12 +779,12 @@ public class AdPlanConfServiceImpl extends ServiceImpl<AdPlanConfMapper, AdPlanC
|
|
|
HttpURLConnection connection = null;
|
|
|
InputStream inputStream = null;
|
|
|
FileOutputStream outputStream = null;
|
|
|
-
|
|
|
+
|
|
|
try {
|
|
|
// 创建临时文件
|
|
|
String suffix = getFileSuffix(fileUrl);
|
|
|
File tempFile = File.createTempFile("gdt_upload_", suffix);
|
|
|
-
|
|
|
+
|
|
|
// 打开HTTP连接
|
|
|
URL url = new URL(fileUrl);
|
|
|
connection = (HttpURLConnection) url.openConnection();
|
|
|
@@ -730,25 +792,25 @@ public class AdPlanConfServiceImpl extends ServiceImpl<AdPlanConfMapper, AdPlanC
|
|
|
connection.setReadTimeout(30000);
|
|
|
connection.setRequestMethod("GET");
|
|
|
connection.connect();
|
|
|
-
|
|
|
+
|
|
|
int responseCode = connection.getResponseCode();
|
|
|
if (responseCode != HttpURLConnection.HTTP_OK) {
|
|
|
throw new Exception("下载文件失败,HTTP状态码: " + responseCode);
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// 读取文件内容
|
|
|
inputStream = connection.getInputStream();
|
|
|
outputStream = new FileOutputStream(tempFile);
|
|
|
-
|
|
|
+
|
|
|
byte[] buffer = new byte[8192];
|
|
|
int bytesRead;
|
|
|
while ((bytesRead = inputStream.read(buffer)) != -1) {
|
|
|
outputStream.write(buffer, 0, bytesRead);
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
log.info("文件下载成功,URL: {}, 大小: {} bytes", fileUrl, tempFile.length());
|
|
|
return tempFile;
|
|
|
-
|
|
|
+
|
|
|
} finally {
|
|
|
if (outputStream != null) {
|
|
|
try { outputStream.close(); } catch (Exception e) { }
|
|
|
@@ -761,27 +823,27 @@ public class AdPlanConfServiceImpl extends ServiceImpl<AdPlanConfMapper, AdPlanC
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
/**
|
|
|
* 获取文件后缀名
|
|
|
- *
|
|
|
+ *
|
|
|
* @param fileUrl 文件URL
|
|
|
* @return 文件后缀名(包含点号,如:.jpg)
|
|
|
*/
|
|
|
private String getFileSuffix(String fileUrl) {
|
|
|
int lastDotIndex = fileUrl.lastIndexOf('.');
|
|
|
int lastSlashIndex = fileUrl.lastIndexOf('/');
|
|
|
-
|
|
|
+
|
|
|
if (lastDotIndex > lastSlashIndex && lastDotIndex < fileUrl.length() - 1) {
|
|
|
return fileUrl.substring(lastDotIndex);
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
return ".jpg"; // 默认后缀
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
/**
|
|
|
* 计算字节数组的MD5签名
|
|
|
- *
|
|
|
+ *
|
|
|
* @param bytes 字节数组
|
|
|
* @return MD5签名(32位小写字符串)
|
|
|
* @throws Exception 计算失败
|