diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/domain/service/NaverAdApiService.java b/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/domain/service/NaverAdApiService.java index 0836d870..9525a41d 100644 --- a/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/domain/service/NaverAdApiService.java +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/domain/service/NaverAdApiService.java @@ -1,5 +1,15 @@ package com.whereyouad.WhereYouAd.domains.advertisement.domain.service; +import com.whereyouad.WhereYouAd.domains.advertisement.persistence.entity.AdCampaign; +import com.whereyouad.WhereYouAd.domains.advertisement.persistence.entity.AdGroup; +import com.whereyouad.WhereYouAd.domains.advertisement.persistence.repository.AdCampaignRepository; +import com.whereyouad.WhereYouAd.domains.advertisement.persistence.repository.AdGroupRepository; +import com.whereyouad.WhereYouAd.domains.organization.domain.constant.OrgRole; +import com.whereyouad.WhereYouAd.domains.organization.exception.handler.OrgHandler; +import com.whereyouad.WhereYouAd.domains.organization.exception.code.OrgErrorCode; +import com.whereyouad.WhereYouAd.domains.organization.persistence.entity.OrgMember; +import com.whereyouad.WhereYouAd.domains.organization.persistence.repository.OrgMemberRepository; +import com.whereyouad.WhereYouAd.domains.platform.persistence.entity.PlatformConnection; import com.whereyouad.WhereYouAd.domains.platform.persistence.repository.PlatformConnectionRepository; import com.whereyouad.WhereYouAd.global.utils.AdApiAuthUtil; import com.whereyouad.WhereYouAd.global.adapi.dto.AdAuthRequest; @@ -25,8 +35,11 @@ public class NaverAdApiService { private final PlatformConnectionRepository connectionRepository; + private final OrgMemberRepository orgMemberRepository; private final AdApiAuthUtil adApiAuthUtil; private final NaverClient naverClient; + private final AdGroupRepository adGroupRepository; + private final AdCampaignRepository adCampaignRepository; // 캠페인 목록 조회 @Transactional(readOnly = true) @@ -166,6 +179,94 @@ public NaverDTO.RawReportResponse downloadReport(Long connectionId, String downl } } + // 캠페인 예산 수정 + @Transactional + public NaverDTO.CampaignResponse updateCampaignBudget(Long userId, Long connectionId, String campaignId, NaverDTO.UpdateCampaignBudgetRequest request) { + PlatformConnection connection = validateAdminOwnership(userId, connectionId); + if (request.dailyBudget() != null) { + if (request.dailyBudget() % 10 != 0) { + throw new AdvertisementHandler(NaverAdErrorCode.NAVER_INVALID_BUDGET_VALUE); + } + if (request.dailyBudget() < 50 || request.dailyBudget() > 1_000_000_000) { + throw new AdvertisementHandler(NaverAdErrorCode.NAVER_INVALID_BUDGET_RANGE); + } + } + try { + Map headers = adApiAuthUtil.generateAuthHeaders( + connectionId, AdAuthRequest.forMethodAndPath("PUT", "/ncc/campaigns/" + campaignId)); + NaverDTO.UpdateCampaignBudgetBody body = + new NaverDTO.UpdateCampaignBudgetBody(campaignId, request.useDailyBudget(), request.dailyBudget()); + NaverDTO.CampaignResponse result = naverClient.updateCampaignBudget(headers, campaignId, "budget", body); + + adCampaignRepository + .findByPlatformAccountAndExternalCampaignId(connection.getPlatformAccount(), campaignId) + .ifPresent(campaign -> campaign.updateBudget(request.dailyBudget())); + + return result; + } catch (Exception e) { + log.error("[NAVER] 캠페인 예산 수정 실패 - connectionId={}, campaignId={}", connectionId, campaignId, e); + throw new AdvertisementHandler(NaverAdErrorCode.NAVER_CAMPAIGN_BUDGET_UPDATE_FAILED); + } + } + + // 광고그룹 예산 수정 + @Transactional + public NaverDTO.AdGroupResponse updateAdGroupBudget(Long userId, Long connectionId, String adgroupId, NaverDTO.UpdateAdGroupBudgetRequest request) { + PlatformConnection connection = validateAdminOwnership(userId, connectionId); + if (request.dailyBudget() != null) { + if (request.dailyBudget() % 10 != 0) { + throw new AdvertisementHandler(NaverAdErrorCode.NAVER_INVALID_BUDGET_VALUE); + } + if (request.dailyBudget() < 50 || request.dailyBudget() > 1_000_000_000) { + throw new AdvertisementHandler(NaverAdErrorCode.NAVER_INVALID_BUDGET_RANGE); + } + } + if (request.bidAmt() != null) { + if (request.bidAmt() % 10 != 0) { + throw new AdvertisementHandler(NaverAdErrorCode.NAVER_INVALID_BUDGET_VALUE); + } + if (request.bidAmt() < 70 || request.bidAmt() > 100_000) { + throw new AdvertisementHandler(NaverAdErrorCode.NAVER_INVALID_BID_AMOUNT_RANGE); + } + } + try { + Map headers = adApiAuthUtil.generateAuthHeaders( + connectionId, AdAuthRequest.forMethodAndPath("PUT", "/ncc/adgroups/" + adgroupId)); + // 네이버 API 제약: budget과 bidAmt는 fields에 함께 넣어도 bidAmt가 무시됨 → 각각 별도 호출 필요 + NaverDTO.AdGroupResponse result = null; + if (request.dailyBudget() != null || request.useDailyBudget() != null) { + result = naverClient.updateAdGroupBudget(headers, adgroupId, "budget", + new NaverDTO.UpdateAdGroupBudgetBody(adgroupId, request.useDailyBudget(), request.dailyBudget(), null)); + } + if (request.bidAmt() != null) { + result = naverClient.updateAdGroupBudget(headers, adgroupId, "bidAmt", + new NaverDTO.UpdateAdGroupBudgetBody(adgroupId, null, null, request.bidAmt())); + } + + adGroupRepository + .findByAdCampaign_PlatformAccountAndExternalGroupId(connection.getPlatformAccount(), adgroupId) + .ifPresent(adGroup -> adGroup.updateBudget(request.dailyBudget(), request.bidAmt())); + + return result; + } catch (Exception e) { + log.error("[NAVER] 광고그룹 예산 수정 실패 - connectionId={}, adgroupId={}", connectionId, adgroupId, e); + throw new AdvertisementHandler(NaverAdErrorCode.NAVER_AD_GROUP_BUDGET_UPDATE_FAILED); + } + } + + @Transactional(readOnly = true) + private PlatformConnection validateAdminOwnership(Long userId, Long connectionId) { + PlatformConnection connection = connectionRepository.findWithAccountAndOrgById(connectionId) + .orElseThrow(() -> new AdvertisementHandler(NaverAdErrorCode.NAVER_CONNECTION_NOT_FOUND)); + Long orgId = connection.getPlatformAccount().getOrganization().getId(); + OrgMember orgMember = orgMemberRepository.findByUserIdAndOrgId(userId, orgId) + .orElseThrow(() -> new OrgHandler(OrgErrorCode.ORG_MEMBER_NOT_FOUND)); + if (orgMember.getRole() != OrgRole.ADMIN) { + throw new OrgHandler(OrgErrorCode.ORG_MEMBER_FORBIDDEN); + } + return connection; + } + // 일별 기본 지표 조회 (/stats, 기본 일 단위) @Transactional(readOnly = true) public List getDailyStats(Long connectionId, String id, String since, String until) { diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/exception/code/NaverAdErrorCode.java b/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/exception/code/NaverAdErrorCode.java index 33a2b4c2..fe633ee2 100644 --- a/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/exception/code/NaverAdErrorCode.java +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/exception/code/NaverAdErrorCode.java @@ -11,6 +11,12 @@ public enum NaverAdErrorCode implements BaseErrorCode { // 400 NAVER_INVALID_DOWNLOAD_URL(HttpStatus.BAD_REQUEST, "NAVER_400_1", "유효하지 않은 다운로드 URL입니다."), + NAVER_INVALID_BUDGET_VALUE(HttpStatus.BAD_REQUEST, "NAVER_400_2", "예산 및 입찰가는 10원 단위로 입력해야 합니다."), + NAVER_INVALID_BUDGET_RANGE(HttpStatus.BAD_REQUEST, "NAVER_400_3", "예산은 50원 이상 1,000,000,000원 이하로 입력해야 합니다."), + NAVER_INVALID_BID_AMOUNT_RANGE(HttpStatus.BAD_REQUEST, "NAVER_400_4", "입찰가는 70원 이상 100,000원 이하로 입력해야 합니다."), + + // 404 + NAVER_CONNECTION_NOT_FOUND(HttpStatus.NOT_FOUND, "NAVER_404_1", "플랫폼 연결 정보를 찾을 수 없습니다."), // 500 NAVER_CAMPAIGN_FETCH_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "NAVER_500_1", "네이버 캠페인 목록 조회에 실패했습니다."), @@ -21,6 +27,8 @@ public enum NaverAdErrorCode implements BaseErrorCode { NAVER_REPORT_STATUS_CHECK_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "NAVER_500_6", "네이버 성과 보고서 상태 조회에 실패했습니다."), NAVER_REPORT_DOWNLOAD_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "NAVER_500_7", "네이버 성과 보고서 원문 다운로드에 실패했습니다."), NAVER_HOURLY_STAT_FETCH_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "NAVER_500_8", "네이버 시간대별 통계 조회에 실패했습니다."), + NAVER_CAMPAIGN_BUDGET_UPDATE_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "NAVER_500_9", "네이버 캠페인 예산 수정에 실패했습니다."), + NAVER_AD_GROUP_BUDGET_UPDATE_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "NAVER_500_10", "네이버 광고 그룹 예산 수정에 실패했습니다."), ; private final HttpStatus httpStatus; diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/persistence/entity/AdCampaign.java b/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/persistence/entity/AdCampaign.java index abdb4638..885c5f03 100644 --- a/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/persistence/entity/AdCampaign.java +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/persistence/entity/AdCampaign.java @@ -94,4 +94,8 @@ public void update(String name, Status status, Long budget, Goal goal, this.description = description; } } + + public void updateBudget(Long budget) { + this.budget = budget; + } } diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/persistence/entity/AdGroup.java b/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/persistence/entity/AdGroup.java index a1b2b664..e2861a83 100644 --- a/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/persistence/entity/AdGroup.java +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/persistence/entity/AdGroup.java @@ -43,6 +43,12 @@ public class AdGroup extends BaseEntity { @OneToMany(mappedBy = "adGroup", cascade = CascadeType.ALL) private List adContents = new ArrayList<>(); + @Column(name = "budget") + private Long budget; + + @Column(name = "bid_amount") + private Long bidAmount; + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "ad_campaign_id") private AdCampaign adCampaign; @@ -55,4 +61,9 @@ public void update(String name, Status status, String targetingInfo) { this.targetingInfo = targetingInfo; } } + + public void updateBudget(Long budget, Long bidAmount) { + if (budget != null) this.budget = budget; + if (bidAmount != null) this.bidAmount = bidAmount; + } } diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/presentation/NaverAdApiController.java b/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/presentation/NaverAdApiController.java index 0cab27c7..5b96923e 100644 --- a/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/presentation/NaverAdApiController.java +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/presentation/NaverAdApiController.java @@ -10,6 +10,7 @@ import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; +import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.*; import java.util.List; @@ -139,4 +140,28 @@ public ResponseEntity> ) { return ResponseEntity.ok(DataResponse.from(naverAdSyncService.syncConversionReports(connectionId, statDate))); } + + // 캠페인 예산 수정 + @PutMapping("/campaigns/{campaignId}/budget") + public ResponseEntity> updateCampaignBudget( + @AuthenticationPrincipal(expression = "userId") Long userId, + @PathVariable Long connectionId, + @PathVariable String campaignId, + @RequestBody NaverDTO.UpdateCampaignBudgetRequest request + ) { + return ResponseEntity.ok(DataResponse.from( + naverAdApiService.updateCampaignBudget(userId, connectionId, campaignId, request))); + } + + // 광고그룹 예산 수정 + @PutMapping("/adgroups/{adgroupId}/budget") + public ResponseEntity> updateAdGroupBudget( + @AuthenticationPrincipal(expression = "userId") Long userId, + @PathVariable Long connectionId, + @PathVariable String adgroupId, + @RequestBody NaverDTO.UpdateAdGroupBudgetRequest request + ) { + return ResponseEntity.ok(DataResponse.from( + naverAdApiService.updateAdGroupBudget(userId, connectionId, adgroupId, request))); + } } diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/presentation/docs/NaverAdApiControllerDocs.java b/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/presentation/docs/NaverAdApiControllerDocs.java index ff8770ee..7aa290d5 100644 --- a/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/presentation/docs/NaverAdApiControllerDocs.java +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/presentation/docs/NaverAdApiControllerDocs.java @@ -4,6 +4,7 @@ import com.whereyouad.WhereYouAd.domains.advertisement.application.dto.response.AdvertisementResponse; import com.whereyouad.WhereYouAd.global.response.DataResponse; import com.whereyouad.WhereYouAd.infrastructure.client.naver.dto.NaverDTO; +import io.swagger.v3.oas.annotations.Hidden; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.responses.ApiResponse; @@ -17,6 +18,7 @@ public interface NaverAdApiControllerDocs { + @Hidden @Operation(summary = "api 통신 test용: 네이버 광고 캠페인 목록 조회", description = "연동된 네이버 광고 계정의 캠페인 목록을 조회합니다.") @ApiResponses({ @ApiResponse(responseCode = "200", description = "캠페인 목록 반환"), @@ -28,6 +30,7 @@ ResponseEntity>> getCampaigns( @PathVariable Long connectionId ); + @Hidden @Operation(summary = "api 통신 test용: 네이버 광고 그룹 목록 조회", description = "특정 캠페인의 광고 그룹 목록을 조회합니다.") @ApiResponses({ @ApiResponse(responseCode = "200", description = "광고 그룹 목록 반환"), @@ -41,6 +44,7 @@ ResponseEntity>> getAdGroups( @RequestParam("nccCampaignId") String nccCampaignId ); + @Hidden @Operation(summary = "api 통신 test용: 네이버 광고 소재 목록 조회", description = "특정 광고 그룹의 광고 소재 목록을 조회합니다.") @ApiResponses({ @ApiResponse(responseCode = "200", description = "광고 소재 목록 반환"), @@ -54,6 +58,7 @@ ResponseEntity>> getAds( @RequestParam("nccAdgroupId") String nccAdgroupId ); + @Hidden @Operation(summary = "api 통신 test용: 네이버 키워드 원문 조회", description = "광고 그룹에 설정된 키워드 목록을 원문으로 조회합니다.") @ApiResponses({ @ApiResponse(responseCode = "200", description = "키워드 목록 반환"), @@ -66,6 +71,8 @@ ResponseEntity>> getKeywords( @Parameter(description = "네이버 광고 그룹 ID", required = true) @RequestParam("nccAdgroupId") String nccAdgroupId ); + + @Hidden @Operation(summary = "api 통신 test용: 네이버 AD 리포트 생성 요청", description = "특정 일자의 AD 리포트 생성을 네이버에 요청합니다.") @ApiResponses({ @ApiResponse(responseCode = "200", description = "리포트 생성 요청됨(상태 확인 필요)"), @@ -78,6 +85,7 @@ ResponseEntity> requestAdReport( @RequestParam("statDt") String statDt ); + @Hidden @Operation(summary = "api 통신 test용: 네이버 AD_CONVERSION 리포트 생성 요청", description = "특정 일자의 AD_CONVERSION 리포트 생성을 네이버에 요청합니다.") @ApiResponses({ @ApiResponse(responseCode = "200", description = "리포트 생성 요청됨(상태 확인 필요)"), @@ -90,6 +98,7 @@ ResponseEntity> requestAdConversionRep @RequestParam("statDt") String statDt ); + @Hidden @Operation(summary = "api 통신 test용: 대용량 보고서 상태 조회", description = "생성 요청한 보고서의 상태를 확인합니다. (BUILT 상태가 되면 다운로드 가능)") @ApiResponses({ @ApiResponse(responseCode = "200", description = "상태 및 다운로드 URL 반환"), @@ -102,6 +111,7 @@ ResponseEntity> getReportStatus( @PathVariable String reportJobId ); + @Hidden @Operation(summary = "api 통신 test용: 대용량 보고서 다운로드", description = "보고서 다운로드 URL을 통해 원문(TSV 등) 데이터를 가져옵니다.") @ApiResponses({ @ApiResponse(responseCode = "200", description = "보고서 데이터 원문 반환"), @@ -114,6 +124,7 @@ ResponseEntity> downloadReport( @RequestParam("url") String downloadUrl ); + @Hidden @Operation(summary = "api 통신 test용: 일별 통계 직접 조회", description = "/stats API를 이용하여 특정 대상의 일별 기본 지표를 가져옵니다.") @ApiResponses({ @ApiResponse(responseCode = "200", description = "일별 통계 반환"), @@ -130,6 +141,7 @@ ResponseEntity>> getDailyStats( @RequestParam("until") String until ); + @Hidden @Operation(summary = "api 통신 test용: 네이버 메타데이터 동기화", description = "연동된 네이버 계정의 캠페인/광고그룹/광고소재를 가져와 DB에 upsert합니다.") @ApiResponses({ @ApiResponse(responseCode = "200", description = "동기화 완료 - 처리된 캠페인/그룹/소재 수 반환"), @@ -153,6 +165,7 @@ ResponseEntity> syncM @RequestBody AdvertisementRequest.ManualSyncRequest request ); + @Hidden @Operation(summary = "api 통신 test용: 네이버 전체 통계 동기화", description = "일별(DAILY) 기본 지표와 전환 리포트를 동기화합니다.") @ApiResponses({ @ApiResponse(responseCode = "200", description = "동기화 완료 - 처리된 광고소재 수 반환"), @@ -166,6 +179,7 @@ ResponseEntity> syncSt @RequestParam("statDate") String statDate ); + @Hidden @Operation(summary = "api 통신 test용: 네이버 전환 리포트만 동기화", description = "전환 데이터만 단독으로 동기화합니다. (기본 Stats 동기화 없이)") @ApiResponses({ @ApiResponse(responseCode = "200", description = "동기화 완료 - 처리된 전환 행 수 반환"), @@ -178,4 +192,38 @@ ResponseEntity> syncCo @Parameter(description = "통계 대상 날짜 (yyyy-MM-dd, 예: 2026-04-08)", required = true) @RequestParam("statDate") String statDate ); + + @Operation(summary = "네이버 캠페인 예산 수정", description = "캠페인의 일일 예산 및 예산 사용 여부를 수정합니다.") + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "수정된 캠페인 정보 반환"), + @ApiResponse(responseCode = "400", description = "예산이 10의 배수가 아님"), + @ApiResponse(responseCode = "403", description = "조직 미가입 또는 ADMIN 권한 없음"), + @ApiResponse(responseCode = "404", description = "플랫폼 연결 정보 없음"), + @ApiResponse(responseCode = "500", description = "네이버 API 호출 실패") + }) + ResponseEntity> updateCampaignBudget( + @Parameter(hidden = true) Long userId, + @Parameter(description = "네이버 커넥션 ID", example = "1", required = true) + @PathVariable Long connectionId, + @Parameter(description = "수정할 캠페인 ID", required = true) + @PathVariable String campaignId, + @RequestBody NaverDTO.UpdateCampaignBudgetRequest request + ); + + @Operation(summary = "네이버 광고그룹 예산 수정", description = "광고그룹의 일일 예산, 예산 사용 여부, 입찰가를 수정합니다. bidAmt가 null이면 입찰가는 수정하지 않습니다.") + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "수정된 광고그룹 정보 반환"), + @ApiResponse(responseCode = "400", description = "예산 또는 입찰가가 10의 배수가 아님"), + @ApiResponse(responseCode = "403", description = "조직 미가입 또는 ADMIN 권한 없음"), + @ApiResponse(responseCode = "404", description = "플랫폼 연결 정보 없음"), + @ApiResponse(responseCode = "500", description = "네이버 API 호출 실패") + }) + ResponseEntity> updateAdGroupBudget( + @Parameter(hidden = true) Long userId, + @Parameter(description = "네이버 커넥션 ID", example = "1", required = true) + @PathVariable Long connectionId, + @Parameter(description = "수정할 광고그룹 ID", required = true) + @PathVariable String adgroupId, + @RequestBody NaverDTO.UpdateAdGroupBudgetRequest request + ); } diff --git a/src/main/java/com/whereyouad/WhereYouAd/infrastructure/client/naver/client/NaverClient.java b/src/main/java/com/whereyouad/WhereYouAd/infrastructure/client/naver/client/NaverClient.java index c116a6cf..44be2134 100644 --- a/src/main/java/com/whereyouad/WhereYouAd/infrastructure/client/naver/client/NaverClient.java +++ b/src/main/java/com/whereyouad/WhereYouAd/infrastructure/client/naver/client/NaverClient.java @@ -5,6 +5,7 @@ import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.PathVariable; @@ -77,4 +78,22 @@ NaverDTO.StatReportResponse getStatReportStatus( Response downloadStatReport( @RequestHeader Map headers, URI baseUri ); + + // 캠페인 예산 수정 + @PutMapping("/ncc/campaigns/{campaignId}") + NaverDTO.CampaignResponse updateCampaignBudget( + @RequestHeader Map headers, + @PathVariable("campaignId") String campaignId, + @RequestParam("fields") String fields, + @RequestBody NaverDTO.UpdateCampaignBudgetBody body + ); + + // 광고그룹 예산 수정 + @PutMapping("/ncc/adgroups/{adgroupId}") + NaverDTO.AdGroupResponse updateAdGroupBudget( + @RequestHeader Map headers, + @PathVariable("adgroupId") String adgroupId, + @RequestParam("fields") String fields, + @RequestBody NaverDTO.UpdateAdGroupBudgetBody body + ); } diff --git a/src/main/java/com/whereyouad/WhereYouAd/infrastructure/client/naver/converter/NaverConverter.java b/src/main/java/com/whereyouad/WhereYouAd/infrastructure/client/naver/converter/NaverConverter.java index 665fd615..b4723801 100644 --- a/src/main/java/com/whereyouad/WhereYouAd/infrastructure/client/naver/converter/NaverConverter.java +++ b/src/main/java/com/whereyouad/WhereYouAd/infrastructure/client/naver/converter/NaverConverter.java @@ -60,6 +60,8 @@ public static AdGroup toAdGroupEntity(NaverDTO.AdGroupResponse dto, AdCampaign c .targetingInfo(extractTargetingInfo(keywords)) .status(mapToDomainStatus(dto.status())) .adCampaign(campaign) + .budget(dto.useDailyBudget() != null && dto.useDailyBudget() ? dto.dailyBudget() : null) + .bidAmount(dto.bidAmt()) .build(); } @@ -70,6 +72,10 @@ public static void updateAdGroup(AdGroup entity, NaverDTO.AdGroupResponse dto, j mapToDomainStatus(dto.status()), extractTargetingInfo(keywords) ); + entity.updateBudget( + dto.useDailyBudget() != null && dto.useDailyBudget() ? dto.dailyBudget() : null, + dto.bidAmt() + ); } // 광고 소재 생성용 diff --git a/src/main/java/com/whereyouad/WhereYouAd/infrastructure/client/naver/dto/NaverDTO.java b/src/main/java/com/whereyouad/WhereYouAd/infrastructure/client/naver/dto/NaverDTO.java index df0c8e14..dccfeadf 100644 --- a/src/main/java/com/whereyouad/WhereYouAd/infrastructure/client/naver/dto/NaverDTO.java +++ b/src/main/java/com/whereyouad/WhereYouAd/infrastructure/client/naver/dto/NaverDTO.java @@ -133,4 +133,32 @@ public record StatReportRequest( String reportTp, String statDt ) {} + + // 캠페인 예산 수정 요청 (컨트롤러 입력용, ID X) + public record UpdateCampaignBudgetRequest( + Boolean useDailyBudget, + Long dailyBudget + ) {} + + // 캠페인 예산 수정 body (네이버 API 전송용, ID O) + public record UpdateCampaignBudgetBody( + String nccCampaignId, + Boolean useDailyBudget, + Long dailyBudget + ) {} + + // 광고그룹 예산 수정 요청 (컨트롤러 입력용, ID X) + public record UpdateAdGroupBudgetRequest( + Boolean useDailyBudget, + Long dailyBudget, + Long bidAmt + ) {} + + // 광고그룹 예산 수정 body (네이버 API 전송용, ID O) + public record UpdateAdGroupBudgetBody( + String nccAdgroupId, + Boolean useDailyBudget, + Long dailyBudget, + Long bidAmt + ) {} }