Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -133,14 +133,16 @@ public void update(AdNetwork network, AdSlotCode slot, String title, String subt
* 어드민이 꺼둔 소재는 다시 켜지 않는다. 별도 플래그 없이 PAUSED 를 끄기 스위치로 쓴다.
*/
public void syncFromAdpick(String title, String subtitle, String imageUrl, String ctaText,
String landingUrl, AdTargetOs targetOs, AdSlotCode slot, boolean servable) {
String landingUrl, AdTargetOs targetOs, AdSlotCode slot, int weight,
boolean servable) {
this.title = title;
this.subtitle = subtitle;
this.imageUrl = imageUrl;
this.ctaText = ctaText;
this.landingUrl = landingUrl;
this.targetOs = targetOs;
this.slot = slot;
this.weight = weight;
if (this.status != AdStatus.PAUSED) {
this.status = servable ? AdStatus.ACTIVE : AdStatus.DRAFT;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,16 @@ public class AdpickCampaignSyncService {
@Value("${picke.ad.adpick.cta-text:설치하고 받기}")
private String ctaText;

/**
* 로테이션 가중치. 쇼핑 상품을 앱 설치형보다 자주 띄우려고 나눠 둔다.
* 앱 캠페인은 7건뿐이라 같은 가중치면 지면마다 같은 소재가 반복해서 뽑힌다.
*/
@Value("${picke.ad.adpick.weight:1}")
private int campaignWeight;

@Value("${picke.ad.adpick.shopping-weight:3}")
private int shoppingWeight;

/**
* 쇼핑·핫딜 상품을 흩어 놓을 지면.
* 앱 캠페인은 7건뿐이라 한 지면밖에 못 채운다. 상품 60건을 나눠 담아 나머지 지면을 채운다.
Expand Down Expand Up @@ -127,6 +137,7 @@ private void upsertShopping(AdCreative found, String externalId, AdpickShoppingR
product.buyUrl(),
AdTargetOs.ALL,
target,
shoppingWeight,
true);
return;
}
Expand All @@ -141,7 +152,7 @@ private void upsertShopping(AdCreative found, String externalId, AdpickShoppingR
.ctaText(shoppingCtaText)
.landingUrl(product.buyUrl())
.status(AdStatus.ACTIVE)
.weight(1)
.weight(shoppingWeight)
.source(AdSource.ADPICK_API)
.externalId(externalId)
.targetOs(AdTargetOs.ALL)
Expand Down Expand Up @@ -185,6 +196,7 @@ private void upsert(AdCreative found, AdpickCampaignResponse campaign) {
campaign.trackingLink(),
AdTargetOs.fromAdpick(campaign.os()),
slot,
campaignWeight,
campaign.hasRemaining());
return;
}
Expand All @@ -199,7 +211,7 @@ private void upsert(AdCreative found, AdpickCampaignResponse campaign) {
.ctaText(ctaText)
.landingUrl(campaign.trackingLink())
.status(campaign.hasRemaining() ? AdStatus.ACTIVE : AdStatus.DRAFT)
.weight(1)
.weight(campaignWeight)
.source(AdSource.ADPICK_API)
.externalId(campaign.offerId())
.targetOs(AdTargetOs.fromAdpick(campaign.os()))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import org.springframework.test.util.ReflectionTestUtils;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.tuple;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
Expand Down Expand Up @@ -56,6 +57,8 @@ void setUp() {
ReflectionTestUtils.setField(syncService, "ctaText", CTA);
ReflectionTestUtils.setField(syncService, "shoppingSlots", List.of(AdSlotCode.HOME_FEED));
ReflectionTestUtils.setField(syncService, "shoppingCtaText", "구매하러 가기");
ReflectionTestUtils.setField(syncService, "campaignWeight", 1);
ReflectionTestUtils.setField(syncService, "shoppingWeight", 3);
when(adpickCampaignClient.isConfigured()).thenReturn(true);
when(adCreativeCodeGenerator.generate()).thenReturn("syn23456");
when(adCreativeRepository.findAllBySource(AdSource.ADPICK_API)).thenReturn(List.of());
Expand Down Expand Up @@ -236,4 +239,25 @@ void sync_skipsShoppingWhenNotConfigured() {

verify(adpickShoppingClient, never()).fetchProducts();
}

@Test
@DisplayName("쇼핑 상품은 앱 캠페인보다 높은 가중치로 담긴다. 앱 캠페인 7건이 반복 노출되지 않게 한다")
void sync_weightsShoppingHigher() {
when(adpickCampaignClient.isConfigured()).thenReturn(true);
when(adpickCampaignClient.fetchCampaigns()).thenReturn(List.of(campaign("aaaa1", "Both", 5)));
when(adpickShoppingClient.isConfigured()).thenReturn(true);
when(adpickShoppingClient.fetchProducts()).thenReturn(List.of(
product("상품", "https://adpick.co.kr/apis/goshopping.php?affid=ab2c41&offer=1&url=w")));

syncService.sync();

ArgumentCaptor<AdCreative> captor = ArgumentCaptor.forClass(AdCreative.class);
verify(adCreativeRepository, times(2)).save(captor.capture());
assertThat(captor.getAllValues()).extracting(AdCreative::getExternalId, AdCreative::getWeight)
.containsExactlyInAnyOrder(tuple("aaaa1", 1),
tuple(captor.getAllValues().stream()
.map(AdCreative::getExternalId)
.filter(id -> id.startsWith("sh"))
.findFirst().orElseThrow(), 3));
}
}
Loading