Skip to content
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ dependencies {
// Test
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.security:spring-security-test'
testRuntimeOnly 'com.h2database:h2'
testCompileOnly 'org.projectlombok:lombok'
testAnnotationProcessor 'org.projectlombok:lombok'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.closetnangam.be.domain.ai.dto.response;

import com.closetnangam.be.domain.ai.enums.AiAnalysisStatus;

import java.util.List;

public record AiAnalyzeResponse(
Long photoId,
AiAnalysisStatus analysisStatus,
String previewUrl,
String failureMessage,
Boolean aiFailed,
String name,
String brandName,
String category,
String itemType,
String primaryColor,
List<String> secondaryColors,
List<String> styles
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
package com.closetnangam.be.domain.ai.entity;

import com.closetnangam.be.domain.ai.enums.AiAnalysisStatus;
import com.closetnangam.be.domain.user.entity.User;
import com.closetnangam.be.global.common.entity.BaseEntity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Table(name = "clothing_ai_photos")
public class ClothingAiPhoto extends BaseEntity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "clothing_ai_photo_id")
private Long id;

@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "user_id", nullable = false)
private User user;

@Column(name = "image_url", nullable = false, length = 500)
private String imageUrl;

@Column(name = "stored_path", nullable = false, length = 500)
private String storedPath;

@Column(name = "original_filename", nullable = false, length = 255)
private String originalFilename;

@Column(name = "content_type", nullable = false, length = 100)
private String contentType;

@Enumerated(EnumType.STRING)
@Column(name = "analysis_status", nullable = false, length = 30)
private AiAnalysisStatus analysisStatus;

@Column(name = "failure_message", length = 500)
private String failureMessage;

@Column(name = "draft_name", length = 255)
private String draftName;

@Column(name = "draft_brand_name", length = 100)
private String draftBrandName;

@Column(name = "draft_category", length = 50)
private String draftCategory;

@Column(name = "draft_item_type", length = 50)
private String draftItemType;

@Column(name = "draft_color", length = 50)
private String draftPrimaryColor;

@Column(name = "draft_secondary_colors_json", columnDefinition = "TEXT")
private String draftSecondaryColorsJson;

@Column(name = "draft_styles_json", columnDefinition = "TEXT")
private String draftStylesJson;

@Column(name = "raw_ai_response", columnDefinition = "TEXT")
private String rawAiResponse;

@Column(name = "saved_clothes_id")
private Long savedClothesId;

@Column(name = "saved_wardrobe_clothes_id")
private Long savedWardrobeClothesId;

@Builder
private ClothingAiPhoto(
User user,
String imageUrl,
String storedPath,
String originalFilename,
String contentType,
AiAnalysisStatus analysisStatus
) {
this.user = user;
this.imageUrl = imageUrl;
this.storedPath = storedPath;
this.originalFilename = originalFilename;
this.contentType = contentType;
this.analysisStatus = analysisStatus;
}

public void markAnalyzing() {
this.analysisStatus = AiAnalysisStatus.ANALYZING;
this.failureMessage = null;
}

public void applyAnalysisSuccess(
String draftName,
String draftBrandName,
String draftCategory,
String draftItemType,
String draftPrimaryColor,
String draftSecondaryColorsJson,
String draftStylesJson,
String rawAiResponse
) {
this.analysisStatus = AiAnalysisStatus.SUCCESS;
this.failureMessage = null;
this.draftName = draftName;
this.draftBrandName = draftBrandName;
this.draftCategory = draftCategory;
this.draftItemType = draftItemType;
this.draftPrimaryColor = draftPrimaryColor;
this.draftSecondaryColorsJson = draftSecondaryColorsJson;
this.draftStylesJson = draftStylesJson;
this.rawAiResponse = rawAiResponse;
}

public void applyAnalysisFailure(String failureMessage, String rawAiResponse) {
this.analysisStatus = AiAnalysisStatus.FAILED;
this.failureMessage = failureMessage;
this.rawAiResponse = rawAiResponse;
}

public void markSaved(Long clothesId, Long wardrobeClothesId) {
this.analysisStatus = AiAnalysisStatus.SAVED;
this.savedClothesId = clothesId;
this.savedWardrobeClothesId = wardrobeClothesId;
}

public boolean isOwnedBy(Long userId) {
return user.getId().equals(userId);
}

public boolean isAlreadySaved() {
return analysisStatus == AiAnalysisStatus.SAVED;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.closetnangam.be.domain.ai.enums;

public enum AiAnalysisStatus {

UPLOADED,
ANALYZING,
SUCCESS,
FAILED,
SAVED
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.closetnangam.be.domain.ai.repository;

import com.closetnangam.be.domain.ai.entity.ClothingAiPhoto;
import jakarta.persistence.LockModeType;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Lock;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import java.util.Optional;

public interface ClothingAiPhotoRepository extends JpaRepository<ClothingAiPhoto, Long> {

Optional<ClothingAiPhoto> findByIdAndUser_Id(Long id, Long userId);

@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("SELECT p FROM ClothingAiPhoto p WHERE p.id = :id AND p.user.id = :userId")
Optional<ClothingAiPhoto> findByIdAndUser_IdForUpdate(@Param("id") Long id, @Param("userId") Long userId);
}
Loading
Loading