Skip to content

Commit bc38aa2

Browse files
authored
Merge pull request #36 from prgrms-aibe-devcourse/feat/#35
feat/ #35 옷테이블 컬러테이블 분리
2 parents 5aab159 + 5cfa8a1 commit bc38aa2

21 files changed

Lines changed: 715 additions & 205 deletions

build.gradle

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,4 +61,17 @@ dependencies {
6161

6262
tasks.named('test') {
6363
useJUnitPlatform()
64+
65+
def envFile = rootProject.file('.env')
66+
if (envFile.exists()) {
67+
envFile.readLines().each { line ->
68+
line = line.trim()
69+
if (!line.startsWith('#') && line.contains('=')) {
70+
def idx = line.indexOf('=')
71+
def key = line.substring(0, idx).trim()
72+
def value = line.substring(idx + 1).trim()
73+
environment key, value
74+
}
75+
}
76+
}
6477
}

src/main/java/com/closetnangam/be/domain/catalog/entity/Style.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
import lombok.Builder;
88
import lombok.Getter;
99
import lombok.NoArgsConstructor;
10+
import org.hibernate.annotations.BatchSize;
1011

12+
@BatchSize(size = 100)
1113
@Entity
1214
@Getter
1315
@NoArgsConstructor(access = AccessLevel.PROTECTED)

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

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,10 @@
1212
import org.springframework.transaction.annotation.Transactional;
1313

1414
import java.util.Arrays;
15+
import java.util.HashSet;
1516
import java.util.List;
1617
import java.util.Map;
18+
import java.util.Set;
1719
import java.util.stream.Collectors;
1820

1921
@Service
@@ -49,9 +51,15 @@ public CategoryUsageGuideResponse getUsageGuide() {
4951
"SHORT_SLEEVE"
5052
),
5153
new GuideFieldResponse(
52-
"color",
53-
"컬러",
54-
"색상 코드(code)를 DB에 저장하고, 화면에는 hex 값으로 색상 원(swatch)을 표시합니다.",
54+
"primaryColor",
55+
"주 색상",
56+
"색상 코드(code)를 clothing_colors 테이블 PRIMARY 역할로 저장합니다.",
57+
"WHITE"
58+
),
59+
new GuideFieldResponse(
60+
"secondaryColors",
61+
"보조 색상",
62+
"색상 코드 배열입니다. clothing_colors 테이블 SECONDARY 역할로 저장합니다.",
5563
"NAVY"
5664
),
5765
new GuideFieldResponse(
@@ -73,7 +81,8 @@ public CategoryUsageGuideResponse getUsageGuide() {
7381
Map.of(
7482
"category", "TOP",
7583
"item_type", "SHORT_SLEEVE",
76-
"color", "WHITE",
84+
"primaryColor", "WHITE",
85+
"secondaryColors", List.of("NAVY"),
7786
"styles", List.of("CASUAL", "MINIMAL")
7887
),
7988
"colorDisplay",
@@ -120,7 +129,8 @@ public String getAiClassificationGuide() {
120129
{
121130
"category": "TOP",
122131
"item_type": "SHORT_SLEEVE",
123-
"color": "WHITE",
132+
"primaryColor": "WHITE",
133+
"secondaryColors": ["NAVY"],
124134
"styles": ["CASUAL", "MINIMAL"]
125135
}
126136
""");
@@ -129,19 +139,48 @@ public String getAiClassificationGuide() {
129139
}
130140

131141
public void validateClothesClassification(String categoryCode, String itemTypeCode, String colorCode) {
142+
validateCategoryAndItemType(categoryCode, itemTypeCode);
143+
validateColorCode(colorCode);
144+
}
145+
146+
public void validateCategoryAndItemType(String categoryCode, String itemTypeCode) {
132147
ClothesCategory.fromCode(categoryCode);
133148
if (!ClothesItemType.matchesCategory(categoryCode, itemTypeCode)) {
134149
throw new IllegalArgumentException("item_type이 category와 일치하지 않습니다.");
135150
}
151+
}
152+
153+
public void validateColorCode(String colorCode) {
136154
ClothesColor.fromCode(colorCode);
137155
}
138156

157+
public void validateClothesColors(String primaryColor, List<String> secondaryColors) {
158+
validateColorCode(primaryColor);
159+
if (secondaryColors == null || secondaryColors.isEmpty()) {
160+
return;
161+
}
162+
Set<String> seen = new HashSet<>();
163+
for (String secondaryColor : secondaryColors) {
164+
validateColorCode(secondaryColor);
165+
if (primaryColor.equals(secondaryColor)) {
166+
throw new IllegalArgumentException("주 색상과 보조 색상은 같을 수 없습니다.");
167+
}
168+
if (!seen.add(secondaryColor)) {
169+
throw new IllegalArgumentException("보조 색상에 중복된 값이 있습니다: " + secondaryColor);
170+
}
171+
}
172+
}
173+
139174
public void validateStyleCodes(List<String> styleCodes) {
140175
if (styleCodes == null || styleCodes.isEmpty()) {
141176
throw new IllegalArgumentException("스타일은 1개 이상 선택해야 합니다.");
142177
}
178+
Set<String> seen = new HashSet<>();
143179
for (String styleCode : styleCodes) {
144180
StyleCode.fromCode(styleCode);
181+
if (!seen.add(styleCode)) {
182+
throw new IllegalArgumentException("스타일에 중복된 값이 있습니다: " + styleCode);
183+
}
145184
}
146185
}
147186

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66

77
public record ClothesConvertToOwnedRequest(
88
@NotBlank @Size(max = 100) String productCode,
9+
@NotBlank @Size(max = 50) String size,
10+
@Size(max = 50) String season,
11+
@NotBlank @Size(max = 500) String userImageUrl,
912
@NotNull Boolean isVerified
1013
) {
1114
}

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,11 @@ public record ClothesCreateRequest(
1414
@NotBlank @Size(max = 500) String imageUrl,
1515
@NotBlank @Size(max = 50) String category,
1616
@NotBlank @Size(max = 50) String itemType,
17-
@NotBlank @Size(max = 50) String color,
18-
@NotEmpty List<@NotBlank String> styles,
17+
@NotBlank @Size(max = 50) String primaryColor,
18+
@Size(max = 10) List<@NotBlank String> secondaryColors,
19+
@NotEmpty @Size(max = 10) List<@NotBlank String> styles,
20+
@NotBlank @Size(max = 50) String size,
21+
@Size(max = 50) String season,
1922
@NotNull Boolean isVerified
2023
) {
2124
}

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,11 @@ public record ClothesUpdateRequest(
1414
@NotBlank @Size(max = 500) String imageUrl,
1515
@NotBlank @Size(max = 50) String category,
1616
@NotBlank @Size(max = 50) String itemType,
17-
@NotBlank @Size(max = 50) String color,
18-
@NotEmpty List<@NotBlank String> styles,
17+
@NotBlank @Size(max = 50) String primaryColor,
18+
@Size(max = 10) List<@NotBlank String> secondaryColors,
19+
@NotEmpty @Size(max = 10) List<@NotBlank String> styles,
20+
@NotBlank @Size(max = 50) String size,
21+
@Size(max = 50) String season,
1922
@NotNull Boolean isVerified
2023
) {
2124
}

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,11 @@ public record WishlistClothesCreateRequest(
1313
@NotBlank @Size(max = 500) String imageUrl,
1414
@NotBlank @Size(max = 50) String category,
1515
@NotBlank @Size(max = 50) String itemType,
16-
@NotBlank @Size(max = 50) String color,
17-
@NotEmpty List<@NotBlank String> styles,
16+
@NotBlank @Size(max = 50) String primaryColor,
17+
@Size(max = 10) List<@NotBlank String> secondaryColors,
18+
@NotEmpty @Size(max = 10) List<@NotBlank String> styles,
19+
@NotBlank @Size(max = 50) String size,
20+
@Size(max = 50) String season,
1821
@NotBlank @Size(max = 50) String externalSource,
1922
@NotBlank @Size(max = 255) String externalProductId,
2023
@NotBlank @Size(max = 500) String externalProductUrl

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

Lines changed: 65 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,17 @@
22

33
import com.closetnangam.be.domain.catalog.enums.ClothesColor;
44
import com.closetnangam.be.domain.clothes.entity.Clothes;
5+
import com.closetnangam.be.domain.clothes.entity.ClothingColor;
6+
import com.closetnangam.be.domain.clothes.entity.WardrobeClothes;
7+
import com.closetnangam.be.domain.clothes.enums.ColorRole;
58
import com.closetnangam.be.domain.clothes.enums.SourceType;
69

710
import java.time.LocalDateTime;
811
import java.util.List;
912

1013
public record ClothesResponse(
1114
Long clothesId,
15+
Long wardrobeClothesId,
1216
Long wardrobeId,
1317
Long userId,
1418
String name,
@@ -17,69 +21,115 @@ public record ClothesResponse(
1721
String imageUrl,
1822
String category,
1923
String itemType,
20-
String color,
21-
ColorDisplayResponse colorDisplay,
24+
String primaryColor,
25+
ColorDisplayResponse primaryColorDisplay,
26+
List<SecondaryColorResponse> secondaryColors,
2227
List<StyleTagResponse> styles,
2328
SourceType sourceType,
2429
String externalSource,
2530
String externalProductId,
2631
String externalProductUrl,
2732
Boolean isVerified,
2833
Boolean isFavorite,
34+
String size,
35+
String season,
36+
String userImageUrl,
2937
LocalDateTime createdAt,
3038
LocalDateTime updatedAt
3139
) {
3240

3341
public static ClothesResponse from(Clothes clothes) {
34-
ClothesColor clothesColor = ClothesColor.fromCode(clothes.getColor());
42+
return from(clothes, null);
43+
}
44+
45+
public static ClothesResponse from(Clothes clothes, WardrobeClothes wardrobeClothes) {
46+
ClothingColor primaryColorTag = clothes.getSortedColorTags().stream()
47+
.filter(color -> color.getColorRole() == ColorRole.PRIMARY)
48+
.findFirst()
49+
.orElse(null);
3550

36-
List<StyleTagResponse> styles = clothes.getStyleTags().stream()
51+
String primaryColorCode = primaryColorTag != null ? primaryColorTag.getColorCode() : null;
52+
ColorDisplayResponse primaryColorDisplay = primaryColorCode != null
53+
? toColorDisplay(primaryColorCode)
54+
: null;
55+
56+
List<SecondaryColorResponse> secondaryColors = clothes.getSortedColorTags().stream()
57+
.filter(color -> color.getColorRole() == ColorRole.SECONDARY)
58+
.map(color -> new SecondaryColorResponse(
59+
color.getColorCode(),
60+
toColorDisplay(color.getColorCode()),
61+
color.getSortOrder()
62+
))
63+
.toList();
64+
65+
List<StyleTagResponse> styles = clothes.getSortedStyleTags().stream()
3766
.map(tag -> new StyleTagResponse(
3867
tag.getStyle().getId(),
3968
tag.getStyle().getCode(),
40-
tag.getStyle().getName()
69+
tag.getStyle().getName(),
70+
tag.getStyleRole().name(),
71+
tag.getSortOrder()
4172
))
4273
.toList();
4374

4475
return new ClothesResponse(
4576
clothes.getId(),
46-
clothes.getWardrobe().getId(),
47-
clothes.getWardrobe().getUser().getId(),
77+
wardrobeClothes != null ? wardrobeClothes.getId() : null,
78+
wardrobeClothes != null ? wardrobeClothes.getWardrobe().getId() : null,
79+
wardrobeClothes != null ? wardrobeClothes.getWardrobe().getUser().getId() : null,
4880
clothes.getName(),
4981
clothes.getBrandName(),
5082
clothes.getProductCode(),
5183
clothes.getImageUrl(),
5284
clothes.getCategory(),
5385
clothes.getItemType(),
54-
clothes.getColor(),
55-
new ColorDisplayResponse(
56-
clothesColor.name(),
57-
clothesColor.getLabel(),
58-
clothesColor.getHex()
59-
),
86+
primaryColorCode,
87+
primaryColorDisplay,
88+
secondaryColors,
6089
styles,
6190
clothes.getSourceType(),
6291
clothes.getExternalSource(),
6392
clothes.getExternalProductId(),
6493
clothes.getExternalProductUrl(),
6594
clothes.getIsVerified(),
66-
clothes.getIsFavorite(),
95+
wardrobeClothes != null ? wardrobeClothes.getFavorite() : null,
96+
wardrobeClothes != null ? wardrobeClothes.getSize() : null,
97+
wardrobeClothes != null ? wardrobeClothes.getSeason() : null,
98+
wardrobeClothes != null ? wardrobeClothes.getUserImageUrl() : null,
6799
clothes.getCreatedAt(),
68100
clothes.getUpdatedAt()
69101
);
70102
}
71103

104+
private static ColorDisplayResponse toColorDisplay(String colorCode) {
105+
ClothesColor clothesColor = ClothesColor.fromCode(colorCode);
106+
return new ColorDisplayResponse(
107+
clothesColor.name(),
108+
clothesColor.getLabel(),
109+
clothesColor.getHex()
110+
);
111+
}
112+
72113
public record ColorDisplayResponse(
73114
String code,
74115
String name,
75116
String hex
76117
) {
77118
}
78119

120+
public record SecondaryColorResponse(
121+
String code,
122+
ColorDisplayResponse colorDisplay,
123+
Byte sortOrder
124+
) {
125+
}
126+
79127
public record StyleTagResponse(
80128
Long styleId,
81129
String code,
82-
String name
130+
String name,
131+
String styleRole,
132+
Byte sortOrder
83133
) {
84134
}
85135
}

0 commit comments

Comments
 (0)