Skip to content

Commit 99d0492

Browse files
committed
Merge branch 'develop' into feat/#20
2 parents 5eb6e47 + c2b3e23 commit 99d0492

46 files changed

Lines changed: 1928 additions & 147 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ dependencies {
5454
// Test
5555
testImplementation 'org.springframework.boot:spring-boot-starter-test'
5656
testImplementation 'org.springframework.security:spring-security-test'
57+
testRuntimeOnly 'com.h2database:h2'
5758
testCompileOnly 'org.projectlombok:lombok'
5859
testAnnotationProcessor 'org.projectlombok:lombok'
5960
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package com.closetnangam.be.domain.ai.dto.response;
2+
3+
import com.closetnangam.be.domain.ai.enums.AiAnalysisStatus;
4+
5+
import java.util.List;
6+
7+
public record AiAnalyzeResponse(
8+
Long photoId,
9+
AiAnalysisStatus analysisStatus,
10+
String previewUrl,
11+
String failureMessage,
12+
Boolean aiFailed,
13+
String name,
14+
String brandName,
15+
String category,
16+
String itemType,
17+
String primaryColor,
18+
List<String> secondaryColors,
19+
List<String> styles
20+
) {
21+
}
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
package com.closetnangam.be.domain.ai.entity;
2+
3+
import com.closetnangam.be.domain.ai.enums.AiAnalysisStatus;
4+
import com.closetnangam.be.domain.user.entity.User;
5+
import com.closetnangam.be.global.common.entity.BaseEntity;
6+
import jakarta.persistence.Column;
7+
import jakarta.persistence.Entity;
8+
import jakarta.persistence.EnumType;
9+
import jakarta.persistence.Enumerated;
10+
import jakarta.persistence.FetchType;
11+
import jakarta.persistence.GeneratedValue;
12+
import jakarta.persistence.GenerationType;
13+
import jakarta.persistence.Id;
14+
import jakarta.persistence.JoinColumn;
15+
import jakarta.persistence.ManyToOne;
16+
import jakarta.persistence.Table;
17+
import lombok.AccessLevel;
18+
import lombok.Builder;
19+
import lombok.Getter;
20+
import lombok.NoArgsConstructor;
21+
22+
@Entity
23+
@Getter
24+
@NoArgsConstructor(access = AccessLevel.PROTECTED)
25+
@Table(name = "clothing_ai_photos")
26+
public class ClothingAiPhoto extends BaseEntity {
27+
28+
@Id
29+
@GeneratedValue(strategy = GenerationType.IDENTITY)
30+
@Column(name = "clothing_ai_photo_id")
31+
private Long id;
32+
33+
@ManyToOne(fetch = FetchType.LAZY, optional = false)
34+
@JoinColumn(name = "user_id", nullable = false)
35+
private User user;
36+
37+
@Column(name = "image_url", nullable = false, length = 500)
38+
private String imageUrl;
39+
40+
@Column(name = "stored_path", nullable = false, length = 500)
41+
private String storedPath;
42+
43+
@Column(name = "original_filename", nullable = false, length = 255)
44+
private String originalFilename;
45+
46+
@Column(name = "content_type", nullable = false, length = 100)
47+
private String contentType;
48+
49+
@Enumerated(EnumType.STRING)
50+
@Column(name = "analysis_status", nullable = false, length = 30)
51+
private AiAnalysisStatus analysisStatus;
52+
53+
@Column(name = "failure_message", length = 500)
54+
private String failureMessage;
55+
56+
@Column(name = "draft_name", length = 255)
57+
private String draftName;
58+
59+
@Column(name = "draft_brand_name", length = 100)
60+
private String draftBrandName;
61+
62+
@Column(name = "draft_category", length = 50)
63+
private String draftCategory;
64+
65+
@Column(name = "draft_item_type", length = 50)
66+
private String draftItemType;
67+
68+
@Column(name = "draft_color", length = 50)
69+
private String draftPrimaryColor;
70+
71+
@Column(name = "draft_secondary_colors_json", columnDefinition = "TEXT")
72+
private String draftSecondaryColorsJson;
73+
74+
@Column(name = "draft_styles_json", columnDefinition = "TEXT")
75+
private String draftStylesJson;
76+
77+
@Column(name = "raw_ai_response", columnDefinition = "TEXT")
78+
private String rawAiResponse;
79+
80+
@Column(name = "saved_clothes_id")
81+
private Long savedClothesId;
82+
83+
@Column(name = "saved_wardrobe_clothes_id")
84+
private Long savedWardrobeClothesId;
85+
86+
@Builder
87+
private ClothingAiPhoto(
88+
User user,
89+
String imageUrl,
90+
String storedPath,
91+
String originalFilename,
92+
String contentType,
93+
AiAnalysisStatus analysisStatus
94+
) {
95+
this.user = user;
96+
this.imageUrl = imageUrl;
97+
this.storedPath = storedPath;
98+
this.originalFilename = originalFilename;
99+
this.contentType = contentType;
100+
this.analysisStatus = analysisStatus;
101+
}
102+
103+
public void markAnalyzing() {
104+
this.analysisStatus = AiAnalysisStatus.ANALYZING;
105+
this.failureMessage = null;
106+
}
107+
108+
public void applyAnalysisSuccess(
109+
String draftName,
110+
String draftBrandName,
111+
String draftCategory,
112+
String draftItemType,
113+
String draftPrimaryColor,
114+
String draftSecondaryColorsJson,
115+
String draftStylesJson,
116+
String rawAiResponse
117+
) {
118+
this.analysisStatus = AiAnalysisStatus.SUCCESS;
119+
this.failureMessage = null;
120+
this.draftName = draftName;
121+
this.draftBrandName = draftBrandName;
122+
this.draftCategory = draftCategory;
123+
this.draftItemType = draftItemType;
124+
this.draftPrimaryColor = draftPrimaryColor;
125+
this.draftSecondaryColorsJson = draftSecondaryColorsJson;
126+
this.draftStylesJson = draftStylesJson;
127+
this.rawAiResponse = rawAiResponse;
128+
}
129+
130+
public void applyAnalysisFailure(String failureMessage, String rawAiResponse) {
131+
this.analysisStatus = AiAnalysisStatus.FAILED;
132+
this.failureMessage = failureMessage;
133+
this.rawAiResponse = rawAiResponse;
134+
}
135+
136+
public void markSaved(Long clothesId, Long wardrobeClothesId) {
137+
this.analysisStatus = AiAnalysisStatus.SAVED;
138+
this.savedClothesId = clothesId;
139+
this.savedWardrobeClothesId = wardrobeClothesId;
140+
}
141+
142+
public boolean isOwnedBy(Long userId) {
143+
return user.getId().equals(userId);
144+
}
145+
146+
public boolean isAlreadySaved() {
147+
return analysisStatus == AiAnalysisStatus.SAVED;
148+
}
149+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package com.closetnangam.be.domain.ai.enums;
2+
3+
public enum AiAnalysisStatus {
4+
5+
UPLOADED,
6+
ANALYZING,
7+
SUCCESS,
8+
FAILED,
9+
SAVED
10+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package com.closetnangam.be.domain.ai.repository;
2+
3+
import com.closetnangam.be.domain.ai.entity.ClothingAiPhoto;
4+
import jakarta.persistence.LockModeType;
5+
import org.springframework.data.jpa.repository.JpaRepository;
6+
import org.springframework.data.jpa.repository.Lock;
7+
import org.springframework.data.jpa.repository.Query;
8+
import org.springframework.data.repository.query.Param;
9+
10+
import java.util.Optional;
11+
12+
public interface ClothingAiPhotoRepository extends JpaRepository<ClothingAiPhoto, Long> {
13+
14+
Optional<ClothingAiPhoto> findByIdAndUser_Id(Long id, Long userId);
15+
16+
@Lock(LockModeType.PESSIMISTIC_WRITE)
17+
@Query("SELECT p FROM ClothingAiPhoto p WHERE p.id = :id AND p.user.id = :userId")
18+
Optional<ClothingAiPhoto> findByIdAndUser_IdForUpdate(@Param("id") Long id, @Param("userId") Long userId);
19+
}

0 commit comments

Comments
 (0)