Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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 @@ -4,4 +4,6 @@
import org.springframework.data.jpa.repository.JpaRepository;

public interface ClothesRepository extends JpaRepository<Clothes, Long> {

boolean existsByExternalProductId(String externalProductId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package com.closetnangam.be.global.external.clothes.controller;

import com.closetnangam.be.domain.wardrobe.entity.Wardrobe;
import com.closetnangam.be.domain.wardrobe.service.WardrobeService;
import com.closetnangam.be.global.common.response.ApiResponse;
import com.closetnangam.be.global.external.clothes.dto.NaverItemRequest;
import com.closetnangam.be.global.external.clothes.dto.record.SaveNaverProductRequest;
import com.closetnangam.be.global.external.clothes.dto.request.ClothesStyleDto;
import com.closetnangam.be.global.external.clothes.dto.request.ClothingColorDto;
import com.closetnangam.be.global.external.clothes.dto.response.SaveNaverProductResponse;
import com.closetnangam.be.global.external.clothes.service.ExternalClothesService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@Tag(name = "External Clothes", description = "외부 서비스 상품 연동 API")
@RestController
@RequestMapping("/api/v1/external/clothes")
@RequiredArgsConstructor
public class ExternalClothesController {

private final ExternalClothesService externalClothesService;
private final WardrobeService wardrobeService;

@Operation(summary = "네이버 상품 저장", description = "네이버에서 검색한 상품 정보를 저장합니다.")
@PostMapping("/naver")
public ApiResponse<SaveNaverProductResponse> saveNaverProduct(
@RequestParam Long userId,
@Valid @RequestBody SaveNaverProductRequest request
) {
// TODO: SecurityContextHolder 통합 후 아래 검증 추가
// Long authenticatedUserId = SecurityUtils.getCurrentUserId();
// if (!userId.equals(authenticatedUserId)) {
// throw new IllegalArgumentException("자신의 옷장에만 접근할 수 있습니다");
// }

Wardrobe wardrobe = wardrobeService.getOrCreateWardrobe(userId);
// request 내부에서 필요한 객체들만 쏙쏙 뽑아서 전달
Long clothesId = externalClothesService.saveNaverToWishlist(
userId,
wardrobe,
request, // <-- request 자체가 이제 NaverItemRequest 역할을 함!
request.colors(),
request.styles()
);

return ApiResponse.ok(new SaveNaverProductResponse(clothesId));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.closetnangam.be.global.external.clothes.dto;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor
@AllArgsConstructor
public class NaverItemRequest {

@NotBlank(message = "상품명은 필수입니다")
@Size(max = 255, message = "상품명은 255자 이하여야 합니다")
private String title;

@NotBlank(message = "상품 링크는 필수입니다")
@Size(max = 500, message = "링크는 500자 이하여야 합니다")
private String link;

@NotBlank(message = "이미지 URL은 필수입니다")
@Size(max = 500, message = "이미지 URL은 500자 이하여야 합니다")
private String image;

@Size(max = 100)
private String brand;

@NotBlank(message = "상품 ID는 필수입니다")
private String productId;

@Size(max = 100)
private String category1;

@Size(max = 100)
private String category3;

public String getCleanTitle() {
if (this.title == null) {
return "";
}
return this.title.replaceAll("<[^>]*>", "").trim();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.closetnangam.be.global.external.clothes.dto.record;

import com.closetnangam.be.global.external.clothes.dto.NaverItemRequest;
import com.closetnangam.be.global.external.clothes.dto.request.ClothesStyleDto;
import com.closetnangam.be.global.external.clothes.dto.request.ClothingColorDto;
import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.validation.Valid;
import java.util.List;

public record SaveNaverProductRequest(
String productId,
String brand,
String category3,
@JsonProperty("title") String cleanTitle, // JSON의 "title" 값을 cleanTitle 변수에 넣음
String image,
String link,
@JsonProperty("colors") List<ClothingColorDto> colors,
@JsonProperty("styles") List<ClothesStyleDto> styles
) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.closetnangam.be.global.external.clothes.dto.request;

import com.closetnangam.be.domain.clothes.enums.StyleRole;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.PositiveOrZero;

public record ClothesStyleDto(
@NotNull Long styleId,
@NotNull StyleRole styleRole,
@NotNull @PositiveOrZero Byte sortOrder
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.closetnangam.be.global.external.clothes.dto.request;

import com.closetnangam.be.domain.clothes.enums.ColorRole;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.PositiveOrZero;
import jakarta.validation.constraints.Size;

public record ClothingColorDto(
@NotBlank @Size(max = 50) String colorCode,
@NotNull ColorRole colorRole,
@NotNull @PositiveOrZero Byte sortOrder
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.closetnangam.be.global.external.clothes.dto.response;


public record SaveNaverProductResponse(Long clothesId) {
}
Loading
Loading