Skip to content

Commit 77a290a

Browse files
committed
feat/ #33 35 충돌 및 수정
1 parent 95b7f05 commit 77a290a

16 files changed

Lines changed: 248 additions & 140 deletions

src/main/java/com/closetnangam/be/domain/ai/dto/response/AiAnalyzeResponse.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ public record AiAnalyzeResponse(
1414
String brandName,
1515
String category,
1616
String itemType,
17-
String color,
17+
String primaryColor,
18+
List<String> secondaryColors,
1819
List<String> styles
1920
) {
2021
}

src/main/java/com/closetnangam/be/domain/ai/entity/ClothingAiPhoto.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,10 @@ public class ClothingAiPhoto extends BaseEntity {
6666
private String draftItemType;
6767

6868
@Column(name = "draft_color", length = 50)
69-
private String draftColor;
69+
private String draftPrimaryColor;
70+
71+
@Column(name = "draft_secondary_colors_json", columnDefinition = "TEXT")
72+
private String draftSecondaryColorsJson;
7073

7174
@Column(name = "draft_styles_json", columnDefinition = "TEXT")
7275
private String draftStylesJson;
@@ -107,7 +110,8 @@ public void applyAnalysisSuccess(
107110
String draftBrandName,
108111
String draftCategory,
109112
String draftItemType,
110-
String draftColor,
113+
String draftPrimaryColor,
114+
String draftSecondaryColorsJson,
111115
String draftStylesJson,
112116
String rawAiResponse
113117
) {
@@ -117,7 +121,8 @@ public void applyAnalysisSuccess(
117121
this.draftBrandName = draftBrandName;
118122
this.draftCategory = draftCategory;
119123
this.draftItemType = draftItemType;
120-
this.draftColor = draftColor;
124+
this.draftPrimaryColor = draftPrimaryColor;
125+
this.draftSecondaryColorsJson = draftSecondaryColorsJson;
121126
this.draftStylesJson = draftStylesJson;
122127
this.rawAiResponse = rawAiResponse;
123128
}

src/main/java/com/closetnangam/be/domain/ai/service/AiService.java

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,8 @@ public AiAnalyzeResponse analyzeClothingPhoto(Long userId, Long photoId) {
9494
finalResult.brandName(),
9595
finalResult.category(),
9696
finalResult.itemType(),
97-
finalResult.color(),
97+
finalResult.primaryColor(),
98+
toColorsJson(finalResult.secondaryColors()),
9899
toStylesJson(finalResult.styles()),
99100
toRawJson(finalResult)
100101
);
@@ -123,15 +124,28 @@ private void validateClassificationResult(GeminiClothingClassificationResult res
123124
if (!StringUtils.hasText(result.name())
124125
|| !StringUtils.hasText(result.category())
125126
|| !StringUtils.hasText(result.itemType())
126-
|| !StringUtils.hasText(result.color())
127+
|| !StringUtils.hasText(result.primaryColor())
127128
|| result.styles() == null
128129
|| result.styles().isEmpty()) {
129130
throw new IllegalStateException("AI 판별 결과가 충분하지 않습니다.");
130131
}
131-
categoryCatalogService.validateClothesClassification(result.category(), result.itemType(), result.color());
132+
categoryCatalogService.validateCategoryAndItemType(result.category(), result.itemType());
133+
categoryCatalogService.validateClothesColors(result.primaryColor(), normalizeSecondaryColors(result.secondaryColors()));
132134
categoryCatalogService.validateStyleCodes(result.styles());
133135
}
134136

137+
private List<String> normalizeSecondaryColors(List<String> secondaryColors) {
138+
return secondaryColors == null ? Collections.emptyList() : secondaryColors;
139+
}
140+
141+
private String toColorsJson(List<String> secondaryColors) {
142+
try {
143+
return objectMapper.writeValueAsString(normalizeSecondaryColors(secondaryColors));
144+
} catch (JsonProcessingException exception) {
145+
throw new IllegalStateException("AI 판별 결과를 저장하지 못했습니다.");
146+
}
147+
}
148+
135149
private String toStylesJson(List<String> styles) {
136150
try {
137151
return objectMapper.writeValueAsString(styles);
@@ -150,6 +164,7 @@ private String toRawJson(GeminiClothingClassificationResult result) {
150164

151165
private AiAnalyzeResponse toAnalyzeResponse(ClothingAiPhoto photo) {
152166
List<String> styles = parseStyles(photo.getDraftStylesJson());
167+
List<String> secondaryColors = parseColors(photo.getDraftSecondaryColorsJson());
153168
boolean aiFailed = photo.getAnalysisStatus() == AiAnalysisStatus.FAILED;
154169

155170
return new AiAnalyzeResponse(
@@ -162,11 +177,26 @@ private AiAnalyzeResponse toAnalyzeResponse(ClothingAiPhoto photo) {
162177
photo.getDraftBrandName(),
163178
photo.getDraftCategory(),
164179
photo.getDraftItemType(),
165-
photo.getDraftColor(),
180+
photo.getDraftPrimaryColor(),
181+
secondaryColors,
166182
styles
167183
);
168184
}
169185

186+
private List<String> parseColors(String draftSecondaryColorsJson) {
187+
if (!StringUtils.hasText(draftSecondaryColorsJson)) {
188+
return Collections.emptyList();
189+
}
190+
try {
191+
return objectMapper.readValue(
192+
draftSecondaryColorsJson,
193+
objectMapper.getTypeFactory().constructCollectionType(List.class, String.class)
194+
);
195+
} catch (JsonProcessingException exception) {
196+
return Collections.emptyList();
197+
}
198+
}
199+
170200
private List<String> parseStyles(String draftStylesJson) {
171201
if (!StringUtils.hasText(draftStylesJson)) {
172202
return Collections.emptyList();
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package com.closetnangam.be.domain.catalog.constants;
2+
3+
import com.closetnangam.be.domain.catalog.enums.ClothesColor;
4+
import com.closetnangam.be.domain.catalog.enums.StyleCode;
5+
6+
public final class CatalogLimits {
7+
8+
/** ClothesColor catalog size minus the required primary color slot. */
9+
public static final int MAX_SECONDARY_COLORS = 12;
10+
11+
/** StyleCode catalog size. */
12+
public static final int MAX_STYLES = 10;
13+
14+
private CatalogLimits() {
15+
}
16+
17+
static {
18+
int colorCatalogSize = ClothesColor.values().length;
19+
int styleCatalogSize = StyleCode.values().length;
20+
if (MAX_SECONDARY_COLORS != colorCatalogSize - 1) {
21+
throw new ExceptionInInitializerError(
22+
"MAX_SECONDARY_COLORS must be ClothesColor count - 1: expected "
23+
+ (colorCatalogSize - 1));
24+
}
25+
if (MAX_STYLES != styleCatalogSize) {
26+
throw new ExceptionInInitializerError(
27+
"MAX_STYLES must match StyleCode count: expected " + styleCatalogSize);
28+
}
29+
}
30+
}

src/main/java/com/closetnangam/be/domain/catalog/service/CategoryCatalogService.java

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.closetnangam.be.domain.catalog.service;
22

3+
import com.closetnangam.be.domain.catalog.constants.CatalogLimits;
34
import com.closetnangam.be.domain.catalog.dto.response.*;
45
import com.closetnangam.be.domain.catalog.entity.Style;
56
import com.closetnangam.be.domain.catalog.enums.ClothesCategory;
@@ -59,13 +60,15 @@ public CategoryUsageGuideResponse getUsageGuide() {
5960
new GuideFieldResponse(
6061
"secondaryColors",
6162
"보조 색상",
62-
"색상 코드 배열입니다. clothing_colors 테이블 SECONDARY 역할로 저장합니다.",
63+
"색상 코드 배열입니다. clothing_colors 테이블 SECONDARY 역할로 저장합니다. "
64+
+ "최대 " + CatalogLimits.MAX_SECONDARY_COLORS + "개(primaryColor 제외).",
6365
"NAVY"
6466
),
6567
new GuideFieldResponse(
6668
"styles",
6769
"스타일",
68-
"스타일 코드 배열입니다. styles 목록의 code 값을 사용합니다.",
70+
"스타일 코드 배열입니다. styles 목록의 code 값을 사용합니다. "
71+
+ "최대 " + CatalogLimits.MAX_STYLES + "개.",
6972
"CASUAL"
7073
)
7174
),
@@ -114,12 +117,18 @@ public String getAiClassificationGuide() {
114117
}
115118
}
116119

117-
guide.append("\n[color]\n");
120+
guide.append("\n[primaryColor]\n");
121+
guide.append("옷의 대표 색상 코드 1개. 아래 color 코드 목록에서 선택하세요.\n");
118122
guide.append(Arrays.stream(ClothesColor.values())
119123
.map(color -> color.name() + " (" + color.getLabel() + ")")
120124
.collect(Collectors.joining(", ")));
121125

122-
guide.append("\n\n[style]\n");
126+
guide.append("\n\n[secondaryColors]\n");
127+
guide.append("보조 색상 코드 배열. 없으면 빈 배열 []을 사용하고, primaryColor와 중복되면 안 됩니다. ");
128+
guide.append("최대 ").append(CatalogLimits.MAX_SECONDARY_COLORS).append("개.\n");
129+
130+
guide.append("\n\n[styles]\n");
131+
guide.append("스타일 코드 배열. 최소 1개, 최대 ").append(CatalogLimits.MAX_STYLES).append("개.\n");
123132
guide.append(Arrays.stream(StyleCode.values())
124133
.map(style -> style.name() + " (" + style.getLabel() + ")")
125134
.collect(Collectors.joining(", ")));
@@ -159,6 +168,10 @@ public void validateClothesColors(String primaryColor, List<String> secondaryCol
159168
if (secondaryColors == null || secondaryColors.isEmpty()) {
160169
return;
161170
}
171+
if (secondaryColors.size() > CatalogLimits.MAX_SECONDARY_COLORS) {
172+
throw new IllegalArgumentException(
173+
"보조 색상은 최대 " + CatalogLimits.MAX_SECONDARY_COLORS + "개까지 선택할 수 있습니다.");
174+
}
162175
Set<String> seen = new HashSet<>();
163176
for (String secondaryColor : secondaryColors) {
164177
validateColorCode(secondaryColor);
@@ -175,6 +188,10 @@ public void validateStyleCodes(List<String> styleCodes) {
175188
if (styleCodes == null || styleCodes.isEmpty()) {
176189
throw new IllegalArgumentException("스타일은 1개 이상 선택해야 합니다.");
177190
}
191+
if (styleCodes.size() > CatalogLimits.MAX_STYLES) {
192+
throw new IllegalArgumentException(
193+
"스타일은 최대 " + CatalogLimits.MAX_STYLES + "개까지 선택할 수 있습니다.");
194+
}
178195
Set<String> seen = new HashSet<>();
179196
for (String styleCode : styleCodes) {
180197
StyleCode.fromCode(styleCode);

src/main/java/com/closetnangam/be/domain/clothes/dto/request/ClothesCreateRequest.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.closetnangam.be.domain.clothes.dto.request;
22

3+
import com.closetnangam.be.domain.catalog.constants.CatalogLimits;
34
import jakarta.validation.constraints.NotBlank;
45
import jakarta.validation.constraints.NotEmpty;
56
import jakarta.validation.constraints.NotNull;
@@ -15,8 +16,8 @@ public record ClothesCreateRequest(
1516
@NotBlank @Size(max = 50) String category,
1617
@NotBlank @Size(max = 50) String itemType,
1718
@NotBlank @Size(max = 50) String primaryColor,
18-
@Size(max = 10) List<@NotBlank String> secondaryColors,
19-
@NotEmpty @Size(max = 10) List<@NotBlank String> styles,
19+
@Size(max = CatalogLimits.MAX_SECONDARY_COLORS) List<@NotBlank String> secondaryColors,
20+
@NotEmpty @Size(max = CatalogLimits.MAX_STYLES) List<@NotBlank String> styles,
2021
@NotBlank @Size(max = 50) String size,
2122
@Size(max = 50) String season,
2223
@NotNull Boolean isVerified

src/main/java/com/closetnangam/be/domain/clothes/dto/request/ClothesUpdateRequest.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.closetnangam.be.domain.clothes.dto.request;
22

3+
import com.closetnangam.be.domain.catalog.constants.CatalogLimits;
34
import jakarta.validation.constraints.NotBlank;
45
import jakarta.validation.constraints.NotEmpty;
56
import jakarta.validation.constraints.NotNull;
@@ -15,8 +16,8 @@ public record ClothesUpdateRequest(
1516
@NotBlank @Size(max = 50) String category,
1617
@NotBlank @Size(max = 50) String itemType,
1718
@NotBlank @Size(max = 50) String primaryColor,
18-
@Size(max = 10) List<@NotBlank String> secondaryColors,
19-
@NotEmpty @Size(max = 10) List<@NotBlank String> styles,
19+
@Size(max = CatalogLimits.MAX_SECONDARY_COLORS) List<@NotBlank String> secondaryColors,
20+
@NotEmpty @Size(max = CatalogLimits.MAX_STYLES) List<@NotBlank String> styles,
2021
@NotBlank @Size(max = 50) String size,
2122
@Size(max = 50) String season,
2223
@NotNull Boolean isVerified

src/main/java/com/closetnangam/be/domain/clothes/dto/request/PhotoClothesSaveRequest.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.closetnangam.be.domain.clothes.dto.request;
22

3+
import com.closetnangam.be.domain.catalog.constants.CatalogLimits;
34
import jakarta.validation.constraints.NotBlank;
45
import jakarta.validation.constraints.NotEmpty;
56
import jakarta.validation.constraints.NotNull;
@@ -13,8 +14,9 @@ public record PhotoClothesSaveRequest(
1314
@NotBlank @Size(max = 100) String productCode,
1415
@NotBlank @Size(max = 50) String category,
1516
@NotBlank @Size(max = 50) String itemType,
16-
@NotBlank @Size(max = 50) String color,
17-
@NotEmpty List<@NotBlank String> styles,
17+
@NotBlank @Size(max = 50) String primaryColor,
18+
@Size(max = CatalogLimits.MAX_SECONDARY_COLORS) List<@NotBlank String> secondaryColors,
19+
@NotEmpty @Size(max = CatalogLimits.MAX_STYLES) List<@NotBlank String> styles,
1820
@NotBlank @Size(max = 50) String size,
1921
@Size(max = 50) String season,
2022
@NotNull Boolean favorite,

src/main/java/com/closetnangam/be/domain/clothes/dto/request/WishlistClothesCreateRequest.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.closetnangam.be.domain.clothes.dto.request;
22

3+
import com.closetnangam.be.domain.catalog.constants.CatalogLimits;
34
import jakarta.validation.constraints.NotBlank;
45
import jakarta.validation.constraints.NotEmpty;
56
import jakarta.validation.constraints.Size;
@@ -14,8 +15,8 @@ public record WishlistClothesCreateRequest(
1415
@NotBlank @Size(max = 50) String category,
1516
@NotBlank @Size(max = 50) String itemType,
1617
@NotBlank @Size(max = 50) String primaryColor,
17-
@Size(max = 10) List<@NotBlank String> secondaryColors,
18-
@NotEmpty @Size(max = 10) List<@NotBlank String> styles,
18+
@Size(max = CatalogLimits.MAX_SECONDARY_COLORS) List<@NotBlank String> secondaryColors,
19+
@NotEmpty @Size(max = CatalogLimits.MAX_STYLES) List<@NotBlank String> styles,
1920
@NotBlank @Size(max = 50) String size,
2021
@Size(max = 50) String season,
2122
@NotBlank @Size(max = 50) String externalSource,

src/main/java/com/closetnangam/be/domain/clothes/dto/response/PhotoClothesDraftResponse.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ public record PhotoClothesDraftResponse(
1414
String brandName,
1515
String category,
1616
String itemType,
17-
String color,
17+
String primaryColor,
18+
List<String> secondaryColors,
1819
List<String> styles
1920
) {
2021
}

0 commit comments

Comments
 (0)