From 5dd7289902cbbac9c3d201bb0c124fadf21fd7cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=A8=B8=ED=99=98=EC=9E=84?= <88091704+daehwan2da@users.noreply.github.com> Date: Mon, 20 Nov 2023 09:57:26 +0900 Subject: [PATCH 01/15] =?UTF-8?q?Api=20Response=20=EA=B5=AC=EC=84=B1=20(#9?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Api Response 구성 * FillinResponseData 제거 --- .../fillin/api/FillinApiResponse.java | 80 +++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 fillin/src/main/java/com/teamfillin/fillin/api/FillinApiResponse.java diff --git a/fillin/src/main/java/com/teamfillin/fillin/api/FillinApiResponse.java b/fillin/src/main/java/com/teamfillin/fillin/api/FillinApiResponse.java new file mode 100644 index 0000000..052b14f --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/api/FillinApiResponse.java @@ -0,0 +1,80 @@ +package com.teamfillin.fillin.api; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.springframework.http.HttpStatus; + +import com.fasterxml.jackson.annotation.JsonInclude; + +@JsonInclude(JsonInclude.Include.NON_NULL) +public class FillinApiResponse { + private final int status; + + private final boolean success; + + @Nullable + private final Object data; + + @Nullable + private final String message; + + private FillinApiResponse(int status, boolean success) { + this.status = status; + this.success = success; + this.data = null; + this.message = null; + } + + private FillinApiResponse(int status, boolean success, @Nullable Object data, @Nullable String message) { + this.status = status; + this.success = success; + this.data = data; + this.message = message; + } + + public static FillinApiResponse failure(@NotNull HttpStatus httpStatus, @NotNull String message) { + return new FillinApiResponse(httpStatus.value(), false, null, message); + } + + public static FillinApiResponse success() { + return FillinApiResponse.success(HttpStatus.OK); + } + + public static FillinApiResponse success(@NotNull HttpStatus httpStatus) { + return new FillinApiResponse(httpStatus.value(), true); + } + + public static FillinApiResponse success(@NotNull HttpStatus httpStatus, @NotNull Object data) { + return new FillinApiResponse(httpStatus.value(), true, data, null); + } + + public static FillinApiResponse success( + @NotNull HttpStatus httpStatus, + @NotNull Object data, + @Nullable String message + ) { + return new FillinApiResponse(httpStatus.value(), true, data, message); + } + + public static FillinApiResponse success(@NotNull HttpStatus httpStatus, @Nullable String message) { + return new FillinApiResponse(httpStatus.value(), true, null, message); + } + + public int getStatus() { + return status; + } + + public boolean isSuccess() { + return success; + } + + @Nullable + public Object getData() { + return data; + } + + @Nullable + public String getMessage() { + return message; + } +} From d186cc41d5935024922b910a3fbfc5fe8855f694 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=A8=B8=ED=99=98=EC=9E=84?= <88091704+daehwan2da@users.noreply.github.com> Date: Mon, 20 Nov 2023 09:57:43 +0900 Subject: [PATCH 02/15] Disable Spring Security Auto Config (#14) --- .../java/com/teamfillin/fillin/DisableAutoConfiguration.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fillin/src/main/java/com/teamfillin/fillin/DisableAutoConfiguration.java b/fillin/src/main/java/com/teamfillin/fillin/DisableAutoConfiguration.java index 5375c09..1186c7f 100644 --- a/fillin/src/main/java/com/teamfillin/fillin/DisableAutoConfiguration.java +++ b/fillin/src/main/java/com/teamfillin/fillin/DisableAutoConfiguration.java @@ -2,6 +2,7 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; +import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; import org.springframework.context.annotation.Configuration; /** @@ -13,6 +14,9 @@ exclude = { // jdbc datasource DataSourceAutoConfiguration.class, + + // Spring Security off + SecurityAutoConfiguration.class } ) public class DisableAutoConfiguration { From 121d35c7c1356eace3de1e839a2b5298223710b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=A8=B8=ED=99=98=EC=9E=84?= <88091704+daehwan2da@users.noreply.github.com> Date: Mon, 20 Nov 2023 09:58:00 +0900 Subject: [PATCH 03/15] =?UTF-8?q?guava=20library=20=EC=B6=94=EA=B0=80=20(#?= =?UTF-8?q?15)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- fillin/build.gradle.kts | 7 +++++++ fillin/gradle.properties | 5 ++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/fillin/build.gradle.kts b/fillin/build.gradle.kts index 697e318..899f5cc 100644 --- a/fillin/build.gradle.kts +++ b/fillin/build.gradle.kts @@ -1,11 +1,15 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile +// versions +val guavaVersion: String by properties + plugins { java id("org.springframework.boot") version "2.7.17" id("io.spring.dependency-management") version "1.0.15.RELEASE" kotlin("plugin.spring") apply true kotlin("jvm") apply true + } group = "com.teamfillin" @@ -35,6 +39,9 @@ dependencies { annotationProcessor("org.projectlombok:lombok") testImplementation("org.springframework.boot:spring-boot-starter-test") + // guava + implementation("com.google.guava:guava:$guavaVersion") + // kotlin implementation("com.fasterxml.jackson.module:jackson-module-kotlin") implementation("org.jetbrains.kotlin:kotlin-reflect") diff --git a/fillin/gradle.properties b/fillin/gradle.properties index 86d79b1..ded34a1 100644 --- a/fillin/gradle.properties +++ b/fillin/gradle.properties @@ -1,4 +1,7 @@ springBootVersion=2.7.17 springDependencyManagementVersion=1.0.15.RELEASE kotlinVersion=1.6.21 -kotlinSpringVersion=1.6.21 \ No newline at end of file +kotlinSpringVersion=1.6.21 + +# guava +guavaVersion=32.1.3-jre From 5310aac3c82f166611179aca6b2b1c3c1677dcbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=A8=B8=ED=99=98=EC=9E=84?= <88091704+daehwan2da@users.noreply.github.com> Date: Mon, 20 Nov 2023 09:58:16 +0900 Subject: [PATCH 04/15] =?UTF-8?q?resource=20=EB=82=B4=20core=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0=20=EB=B0=8F=20ddl=20setting=20profile=20=EB=B3=84=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20(#16)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/teamfillin/fillin/CoreConfiguration.java | 2 +- fillin/src/main/resources/application-dev.yaml | 5 +++++ fillin/src/main/resources/application-local.yaml | 6 ++++++ fillin/src/main/resources/{core => }/application.yaml | 0 fillin/src/main/resources/core/application-dev.yaml | 0 fillin/src/main/resources/core/application-local.yaml | 0 6 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 fillin/src/main/resources/application-dev.yaml create mode 100644 fillin/src/main/resources/application-local.yaml rename fillin/src/main/resources/{core => }/application.yaml (100%) delete mode 100644 fillin/src/main/resources/core/application-dev.yaml delete mode 100644 fillin/src/main/resources/core/application-local.yaml diff --git a/fillin/src/main/java/com/teamfillin/fillin/CoreConfiguration.java b/fillin/src/main/java/com/teamfillin/fillin/CoreConfiguration.java index 06dbaf0..7680d92 100644 --- a/fillin/src/main/java/com/teamfillin/fillin/CoreConfiguration.java +++ b/fillin/src/main/java/com/teamfillin/fillin/CoreConfiguration.java @@ -17,7 +17,7 @@ public class CoreConfiguration { public static Map getProperties() { Map additionalProperties = new ConcurrentHashMap<>(); - additionalProperties.put("spring.config.location", "classpath:/core/, classpath:/"); + additionalProperties.put("spring.config.location", "classpath:/"); return additionalProperties; } } diff --git a/fillin/src/main/resources/application-dev.yaml b/fillin/src/main/resources/application-dev.yaml new file mode 100644 index 0000000..4604c7a --- /dev/null +++ b/fillin/src/main/resources/application-dev.yaml @@ -0,0 +1,5 @@ +spring: + jpa: + hibernate: + ddl-auto: none + generate-ddl: off diff --git a/fillin/src/main/resources/application-local.yaml b/fillin/src/main/resources/application-local.yaml new file mode 100644 index 0000000..e878126 --- /dev/null +++ b/fillin/src/main/resources/application-local.yaml @@ -0,0 +1,6 @@ +spring: + jpa: + show-sql: true + hibernate: + ddl-auto: create + generate-ddl: on diff --git a/fillin/src/main/resources/core/application.yaml b/fillin/src/main/resources/application.yaml similarity index 100% rename from fillin/src/main/resources/core/application.yaml rename to fillin/src/main/resources/application.yaml diff --git a/fillin/src/main/resources/core/application-dev.yaml b/fillin/src/main/resources/core/application-dev.yaml deleted file mode 100644 index e69de29..0000000 diff --git a/fillin/src/main/resources/core/application-local.yaml b/fillin/src/main/resources/core/application-local.yaml deleted file mode 100644 index e69de29..0000000 From 579a40f461d74c372e7c011f5808d9f03220d7dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=A8=B8=ED=99=98=EC=9E=84?= <88091704+daehwan2da@users.noreply.github.com> Date: Mon, 20 Nov 2023 09:58:27 +0900 Subject: [PATCH 05/15] =?UTF-8?q?lombok.config=20=EC=B6=94=EA=B0=80=20(#18?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- fillin/lombok.config | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 fillin/lombok.config diff --git a/fillin/lombok.config b/fillin/lombok.config new file mode 100644 index 0000000..51c0888 --- /dev/null +++ b/fillin/lombok.config @@ -0,0 +1,11 @@ +lombok.allArgsConstructor.flagUsage = error +lombok.requiredArgsConstructor.flagUsage = error +lombok.noArgsConstructor.flagUsage = error +lombok.getter.flagUsage = error +lombok.data.flagUsage = error +lombok.setter.flagUsage = error +lombok.equalsAndHashCode.flagUsage = error +lombok.var.flagUsage = error +lombok.val.flagUsage = error +lombok.value.flagUsage = error +lombok.toString.flagUsage = error From 1fefc67ff38e3ddf17d850f85fa9959c9b747cc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=B0=AC=EC=9A=B0?= <58971262+oownahcohc@users.noreply.github.com> Date: Mon, 20 Nov 2023 13:26:21 +0900 Subject: [PATCH 06/15] =?UTF-8?q?Entity=20=EC=84=B8=ED=8C=85=20(#11)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * User 엔티티 구성 * User state enum * Account 엔티티 구성 * Social Type enum * Social Info embedded 클래스 * Film 엔티티 구성 * FilmType 엔티티 구성 * Company 엔티티 구성 * Photo 엔티티 구성 * Photo Status enum * Price 엔티티 구성 * ReactionHistory 엔티티 구성 * TargetType enum * RunningTime 엔티티 구성 * Day enum * Studio 엔티티 구성 * crreateAd, updatedAt 일괄 적용을 위한 BaseTimeEntity * @EnableJpaAuditing 추가 * socialId, socialType unique 설정 제거 * socialId, socialType에 대한 복합 unique key 설정 * BaseTimeEntity 상속 * PhotoStatus enum - 공유된 사진, 삭제된 사진 * 필드명 변경 : value -> amount: * Day enum 클래스 명 변경 : Day -> DayOfWeek * StudioStatus enum 추가 * Studio 생성자 Builder 패턴 적용 * Status enum 클래스 명 변경 : Status -> UserStatus * userNo, targetNo, targetType 에 대한 복합 unique key 설정 * index 명 변경 : idx_unique_01 -> idx_unique_02 --- .../teamfillin/fillin/FillinApplication.java | 2 + .../fillin/domain/account/Account.java | 42 ++++++++++++++ .../fillin/domain/account/SocialInfo.java | 42 ++++++++++++++ .../fillin/domain/account/SocialType.java | 5 ++ .../fillin/domain/common/BaseTimeEntity.java | 25 ++++++++ .../fillin/domain/film/Company.java | 21 +++++++ .../teamfillin/fillin/domain/film/Film.java | 33 +++++++++++ .../fillin/domain/film/FilmType.java | 21 +++++++ .../teamfillin/fillin/domain/photo/Photo.java | 45 ++++++++++++++ .../fillin/domain/photo/PhotoStatus.java | 5 ++ .../teamfillin/fillin/domain/price/Price.java | 26 +++++++++ .../reactionHistory/ReactionHistory.java | 43 ++++++++++++++ .../domain/reactionHistory/TargetType.java | 5 ++ .../fillin/domain/runningTime/DayOfWeek.java | 5 ++ .../domain/runningTime/RunningTime.java | 33 +++++++++++ .../fillin/domain/studio/Studio.java | 58 +++++++++++++++++++ .../fillin/domain/studio/StudioStatus.java | 5 ++ .../teamfillin/fillin/domain/user/User.java | 38 ++++++++++++ .../fillin/domain/user/UserStatus.java | 5 ++ 19 files changed, 459 insertions(+) create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/account/Account.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/account/SocialInfo.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/account/SocialType.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/common/BaseTimeEntity.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/film/Company.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/film/Film.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/film/FilmType.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/photo/Photo.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/photo/PhotoStatus.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/price/Price.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/reactionHistory/ReactionHistory.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/reactionHistory/TargetType.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/runningTime/DayOfWeek.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/runningTime/RunningTime.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/studio/Studio.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/studio/StudioStatus.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/user/User.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/user/UserStatus.java diff --git a/fillin/src/main/java/com/teamfillin/fillin/FillinApplication.java b/fillin/src/main/java/com/teamfillin/fillin/FillinApplication.java index 8fda080..67bb4de 100644 --- a/fillin/src/main/java/com/teamfillin/fillin/FillinApplication.java +++ b/fillin/src/main/java/com/teamfillin/fillin/FillinApplication.java @@ -2,7 +2,9 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.data.jpa.repository.config.EnableJpaAuditing; +@EnableJpaAuditing @SpringBootApplication public class FillinApplication { diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/account/Account.java b/fillin/src/main/java/com/teamfillin/fillin/domain/account/Account.java new file mode 100644 index 0000000..f5dc07f --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/account/Account.java @@ -0,0 +1,42 @@ +package com.teamfillin.fillin.domain.account; + +import javax.persistence.Column; +import javax.persistence.Embedded; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Index; +import javax.persistence.Table; + +@Entity +@Table( + name = "account", + indexes = { + @Index(name = "idx_unique_01", columnList = "socialType, socialId") + } +) +public class Account { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long no; + + @Embedded + private SocialInfo socialInfo; + + @Column(length = 500) + private String refreshToken; + + @Column(nullable = false) + private Long userNo; + + protected Account() { + } + + public Account(SocialInfo socialInfo, String refreshToken, Long userNo) { + this.socialInfo = socialInfo; + this.refreshToken = refreshToken; + this.userNo = userNo; + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/account/SocialInfo.java b/fillin/src/main/java/com/teamfillin/fillin/domain/account/SocialInfo.java new file mode 100644 index 0000000..f90508d --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/account/SocialInfo.java @@ -0,0 +1,42 @@ +package com.teamfillin.fillin.domain.account; + +import java.util.Objects; + +import javax.persistence.Column; +import javax.persistence.Embeddable; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; + +@Embeddable +public class SocialInfo { + + @Column(nullable = false, length = 100) + private String socialId; + + @Enumerated(value = EnumType.STRING) + @Column(nullable = false, length = 10) + private SocialType socialType; + + protected SocialInfo() { + } + + public SocialInfo(String socialId, SocialType socialType) { + this.socialId = socialId; + this.socialType = socialType; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + SocialInfo that = (SocialInfo)o; + return Objects.equals(socialId, that.socialId) && socialType == that.socialType; + } + + @Override + public int hashCode() { + return Objects.hash(socialId, socialType); + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/account/SocialType.java b/fillin/src/main/java/com/teamfillin/fillin/domain/account/SocialType.java new file mode 100644 index 0000000..9cebd4a --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/account/SocialType.java @@ -0,0 +1,5 @@ +package com.teamfillin.fillin.domain.account; + +public enum SocialType { + APPLE, KAKAO; +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/common/BaseTimeEntity.java b/fillin/src/main/java/com/teamfillin/fillin/domain/common/BaseTimeEntity.java new file mode 100644 index 0000000..8e027ce --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/common/BaseTimeEntity.java @@ -0,0 +1,25 @@ +package com.teamfillin.fillin.domain.common; + +import java.time.LocalDateTime; + +import javax.persistence.EntityListeners; +import javax.persistence.MappedSuperclass; + +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.LastModifiedDate; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + +import com.fasterxml.jackson.annotation.JsonFormat; + +@MappedSuperclass +@EntityListeners(AuditingEntityListener.class) +public abstract class BaseTimeEntity { + + @CreatedDate + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", timezone = "Asia/Seoul") + private LocalDateTime createdAt; + + @LastModifiedDate + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", timezone = "Asia/Seoul") + private LocalDateTime updatedAt; +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/film/Company.java b/fillin/src/main/java/com/teamfillin/fillin/domain/film/Company.java new file mode 100644 index 0000000..9d445a1 --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/film/Company.java @@ -0,0 +1,21 @@ +package com.teamfillin.fillin.domain.film; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; + +@Entity +public class Company { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long no; + + @Column(nullable = false, length = 50) + private String name; + + protected Company() { + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/film/Film.java b/fillin/src/main/java/com/teamfillin/fillin/domain/film/Film.java new file mode 100644 index 0000000..014a2da --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/film/Film.java @@ -0,0 +1,33 @@ +package com.teamfillin.fillin.domain.film; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; + +@Entity +public class Film { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long no; + + @Column(nullable = false, length = 100) + private String name; + + @Column(nullable = false) + private Long typeNo; + + @Column(nullable = false) + private Long companyNo; + + protected Film() { + } + + public Film(String name, Long typeNo, Long companyNo) { + this.name = name; + this.typeNo = typeNo; + this.companyNo = companyNo; + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/film/FilmType.java b/fillin/src/main/java/com/teamfillin/fillin/domain/film/FilmType.java new file mode 100644 index 0000000..616bdd3 --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/film/FilmType.java @@ -0,0 +1,21 @@ +package com.teamfillin.fillin.domain.film; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; + +@Entity +public class FilmType { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long no; + + @Column(nullable = false, length = 50) + private String name; + + protected FilmType() { + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/photo/Photo.java b/fillin/src/main/java/com/teamfillin/fillin/domain/photo/Photo.java new file mode 100644 index 0000000..aa830c8 --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/photo/Photo.java @@ -0,0 +1,45 @@ +package com.teamfillin.fillin.domain.photo; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; + +import com.teamfillin.fillin.domain.common.BaseTimeEntity; + +@Entity +public class Photo extends BaseTimeEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long no; + + @Column(nullable = false) + private Long userNo; + + @Column(nullable = false) + private Long studioNo; + + @Column(nullable = false) + private Long filmNo; + + @Column(nullable = false, length = 500) + private String imagePath; + + @Enumerated(EnumType.STRING) + @Column(nullable = false, length = 10) + private PhotoStatus status; + + protected Photo() { + } + + public Photo(Long userNo, Long studioNo, Long filmNo, String imagePath) { + this.userNo = userNo; + this.studioNo = studioNo; + this.filmNo = filmNo; + this.imagePath = imagePath; + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/photo/PhotoStatus.java b/fillin/src/main/java/com/teamfillin/fillin/domain/photo/PhotoStatus.java new file mode 100644 index 0000000..0882806 --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/photo/PhotoStatus.java @@ -0,0 +1,5 @@ +package com.teamfillin.fillin.domain.photo; + +public enum PhotoStatus { + SHARED, DELETED; +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/price/Price.java b/fillin/src/main/java/com/teamfillin/fillin/domain/price/Price.java new file mode 100644 index 0000000..f78cfe2 --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/price/Price.java @@ -0,0 +1,26 @@ +package com.teamfillin.fillin.domain.price; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; + +@Entity +public class Price { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long no; + + @Column(nullable = false) + private Long studioNo; + + @Column(length = 100) + private String name; + + private Integer amount; + + protected Price() { + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/reactionHistory/ReactionHistory.java b/fillin/src/main/java/com/teamfillin/fillin/domain/reactionHistory/ReactionHistory.java new file mode 100644 index 0000000..e2858bf --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/reactionHistory/ReactionHistory.java @@ -0,0 +1,43 @@ +package com.teamfillin.fillin.domain.reactionHistory; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Index; +import javax.persistence.Table; + +import com.teamfillin.fillin.domain.common.BaseTimeEntity; + +@Entity +@Table( + name = "reaction_history", + indexes = { + @Index(name = "idx_unique_02", columnList = "userNo, targetNo, targetType") + } +) +public class ReactionHistory extends BaseTimeEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long no; + + @Column(nullable = false) + private Long userNo; + + @Column(nullable = false) + private Long targetNo; + + @Column(nullable = false, length = 50) + private TargetType targetType; + + protected ReactionHistory() { + } + + public ReactionHistory(Long userNo, Long targetNo, TargetType targetType) { + this.userNo = userNo; + this.targetNo = targetNo; + this.targetType = targetType; + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/reactionHistory/TargetType.java b/fillin/src/main/java/com/teamfillin/fillin/domain/reactionHistory/TargetType.java new file mode 100644 index 0000000..c93003f --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/reactionHistory/TargetType.java @@ -0,0 +1,5 @@ +package com.teamfillin.fillin.domain.reactionHistory; + +public enum TargetType { + PHOTO, STUDIO; +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/runningTime/DayOfWeek.java b/fillin/src/main/java/com/teamfillin/fillin/domain/runningTime/DayOfWeek.java new file mode 100644 index 0000000..c93c25f --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/runningTime/DayOfWeek.java @@ -0,0 +1,5 @@ +package com.teamfillin.fillin.domain.runningTime; + +public enum DayOfWeek { + MON, TUE, WED, THR, FRI, SAT, SUN; +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/runningTime/RunningTime.java b/fillin/src/main/java/com/teamfillin/fillin/domain/runningTime/RunningTime.java new file mode 100644 index 0000000..e843fb5 --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/runningTime/RunningTime.java @@ -0,0 +1,33 @@ +package com.teamfillin.fillin.domain.runningTime; + +import java.time.LocalDateTime; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; + +@Entity +public class RunningTime { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long no; + + @Column(nullable = false) + private Long studioNo; + + @Enumerated(EnumType.STRING) + @Column(nullable = false) + private DayOfWeek dayOfWeek; + + private LocalDateTime startAt; + + private LocalDateTime endAt; + + protected RunningTime() { + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/studio/Studio.java b/fillin/src/main/java/com/teamfillin/fillin/domain/studio/Studio.java new file mode 100644 index 0000000..dee8d96 --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/studio/Studio.java @@ -0,0 +1,58 @@ +package com.teamfillin.fillin.domain.studio; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; + +import lombok.Builder; + +@Entity +public class Studio { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long no; + + @Column(nullable = false, length = 50) + private String name; + + @Column(length = 500) + private String address; + + @Column(length = 50) + private String tel; + + private Double latitude; + + private Double longitude; + + @Column(columnDefinition = "TEXT") + private String etc; + + @Column(length = 500) + private String site; + + @Enumerated(EnumType.STRING) + @Column(nullable = false, length = 10) + private StudioStatus status; + + protected Studio() { + } + + @Builder + private Studio(String name, String address, String tel, Double latitude, Double longitude, + String etc, String site, StudioStatus status) { + this.name = name; + this.address = address; + this.tel = tel; + this.latitude = latitude; + this.longitude = longitude; + this.etc = etc; + this.site = site; + this.status = status; + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/studio/StudioStatus.java b/fillin/src/main/java/com/teamfillin/fillin/domain/studio/StudioStatus.java new file mode 100644 index 0000000..052b4c2 --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/studio/StudioStatus.java @@ -0,0 +1,5 @@ +package com.teamfillin.fillin.domain.studio; + +public enum StudioStatus { + OPEN, CLOSED; +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/user/User.java b/fillin/src/main/java/com/teamfillin/fillin/domain/user/User.java new file mode 100644 index 0000000..549afb2 --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/user/User.java @@ -0,0 +1,38 @@ +package com.teamfillin.fillin.domain.user; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +@Entity +@Table(name = "fillin_user") +public class User { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long no; + + @Column(nullable = false) + private String nickname; + + @Column(length = 500) + private String profileImagePath; + + @Enumerated(EnumType.STRING) + @Column(nullable = false, length = 10) + private UserStatus status; + + protected User() { + } + + public User(String nickname, String profileImagePath) { + this.nickname = nickname; + this.profileImagePath = profileImagePath; + this.status = UserStatus.ACTIVE; + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/user/UserStatus.java b/fillin/src/main/java/com/teamfillin/fillin/domain/user/UserStatus.java new file mode 100644 index 0000000..7c99fa0 --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/user/UserStatus.java @@ -0,0 +1,5 @@ +package com.teamfillin.fillin.domain.user; + +public enum UserStatus { + ACTIVE, DELETED; +} From 38a861f67c2ab6d4074c62a5f209dbc03f0cc17d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=B0=AC=EC=9A=B0?= <58971262+oownahcohc@users.noreply.github.com> Date: Fri, 24 Nov 2023 01:23:56 +0900 Subject: [PATCH 07/15] =?UTF-8?q?unique=20index=20=EB=84=A4=EC=9D=B4?= =?UTF-8?q?=EB=B0=8D=20=EB=B3=80=EA=B2=BD=20(#20)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/teamfillin/fillin/domain/account/Account.java | 2 +- .../fillin/domain/reactionHistory/ReactionHistory.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/account/Account.java b/fillin/src/main/java/com/teamfillin/fillin/domain/account/Account.java index f5dc07f..a6c508b 100644 --- a/fillin/src/main/java/com/teamfillin/fillin/domain/account/Account.java +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/account/Account.java @@ -13,7 +13,7 @@ @Table( name = "account", indexes = { - @Index(name = "idx_unique_01", columnList = "socialType, socialId") + @Index(name = "ux_account_social_info", columnList = "socialType, socialId") } ) public class Account { diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/reactionHistory/ReactionHistory.java b/fillin/src/main/java/com/teamfillin/fillin/domain/reactionHistory/ReactionHistory.java index e2858bf..15191ba 100644 --- a/fillin/src/main/java/com/teamfillin/fillin/domain/reactionHistory/ReactionHistory.java +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/reactionHistory/ReactionHistory.java @@ -14,7 +14,7 @@ @Table( name = "reaction_history", indexes = { - @Index(name = "idx_unique_02", columnList = "userNo, targetNo, targetType") + @Index(name = "ux_reaction_user_and_target", columnList = "userNo, targetNo, targetType") } ) public class ReactionHistory extends BaseTimeEntity { From 94e3b8187f10326ccf4fd818d1d255209640311f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=A8=B8=ED=99=98=EC=9E=84?= <88091704+daehwan2da@users.noreply.github.com> Date: Sat, 25 Nov 2023 10:01:18 +0900 Subject: [PATCH 08/15] =?UTF-8?q?Fillin=20Exception=20Handling=20=EA=B5=AC?= =?UTF-8?q?=EC=84=B1=20(#12)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * FillinExceptionHandling 구성 * lombok builder 적용 --- .../teamfillin/fillin/FillinErrorCode.java | 17 +++++ .../teamfillin/fillin/FillinException.java | 64 +++++++++++++++++++ .../fillin/api/FillinApiResponse.java | 12 ++++ .../fillin/api/FillinExceptionHandler.java | 17 +++++ .../api/healthcheck/HealthCheckApi.java | 27 ++++++++ .../healthcheck/HealthCheckErrorCode.java | 34 ++++++++++ .../domain/healthcheck/HealthChecker.java | 19 ++++++ 7 files changed, 190 insertions(+) create mode 100644 fillin/src/main/java/com/teamfillin/fillin/FillinErrorCode.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/FillinException.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/api/FillinExceptionHandler.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/api/healthcheck/HealthCheckApi.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/healthcheck/HealthCheckErrorCode.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/healthcheck/HealthChecker.java diff --git a/fillin/src/main/java/com/teamfillin/fillin/FillinErrorCode.java b/fillin/src/main/java/com/teamfillin/fillin/FillinErrorCode.java new file mode 100644 index 0000000..7156f32 --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/FillinErrorCode.java @@ -0,0 +1,17 @@ +package com.teamfillin.fillin; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.springframework.http.HttpStatus; + +public interface FillinErrorCode { + + @NotNull + String name(); + + @NotNull + HttpStatus status(); + + @Nullable + String defaultMessage(); +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/FillinException.java b/fillin/src/main/java/com/teamfillin/fillin/FillinException.java new file mode 100644 index 0000000..6815618 --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/FillinException.java @@ -0,0 +1,64 @@ +package com.teamfillin.fillin; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.springframework.util.StringUtils; + +import lombok.Builder; + +public class FillinException extends RuntimeException { + @NotNull + protected final FillinErrorCode errorCode; + + @Nullable + protected final String message; + + @Nullable + protected final String log; + + private FillinException(@NotNull FillinErrorCode errorCode) { + super(makeMessage(errorCode, null)); + this.errorCode = errorCode; + this.message = errorCode.defaultMessage(); + this.log = null; + } + + @Builder + private FillinException(@NotNull FillinErrorCode errorCode, @Nullable String message, @Nullable String log) { + super(makeMessage(errorCode, message)); + this.errorCode = errorCode; + this.log = log; + this.message = StringUtils.hasText(message) ? message : errorCode.defaultMessage(); + } + + public static FillinException from(@NotNull FillinErrorCode errorCode) { + return new FillinException(errorCode); + } + + private static String makeMessage(@NotNull FillinErrorCode errorCode, @Nullable String detailMessage) { + return StringUtils.hasText(detailMessage) + ? errorCode.name() + ": " + detailMessage + : StringUtils.hasText(errorCode.defaultMessage()) + ? errorCode.name() + ": " + errorCode.defaultMessage() + : errorCode.name(); + } + + @NotNull + public FillinErrorCode getErrorCode() { + return errorCode; + } + + @Nullable + public String getLog() { + return log; + } + + @Override + public String toString() { + return message != null + ? errorCode.name() + ": " + message + : StringUtils.hasText(errorCode.defaultMessage()) + ? errorCode.name() + ": " + errorCode.defaultMessage() + : errorCode.name(); + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/api/FillinApiResponse.java b/fillin/src/main/java/com/teamfillin/fillin/api/FillinApiResponse.java index 052b14f..d6f50aa 100644 --- a/fillin/src/main/java/com/teamfillin/fillin/api/FillinApiResponse.java +++ b/fillin/src/main/java/com/teamfillin/fillin/api/FillinApiResponse.java @@ -3,8 +3,10 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.springframework.http.HttpStatus; +import org.springframework.util.StringUtils; import com.fasterxml.jackson.annotation.JsonInclude; +import com.teamfillin.fillin.FillinErrorCode; @JsonInclude(JsonInclude.Include.NON_NULL) public class FillinApiResponse { @@ -32,6 +34,12 @@ private FillinApiResponse(int status, boolean success, @Nullable Object data, @N this.message = message; } + public static FillinApiResponse failure(@NotNull FillinErrorCode errorCode) { + return StringUtils.hasText(errorCode.defaultMessage()) + ? failure(errorCode.status(), errorCode.defaultMessage()) + : new FillinApiResponse(errorCode.status().value(), false); + } + public static FillinApiResponse failure(@NotNull HttpStatus httpStatus, @NotNull String message) { return new FillinApiResponse(httpStatus.value(), false, null, message); } @@ -44,6 +52,10 @@ public static FillinApiResponse success(@NotNull HttpStatus httpStatus) { return new FillinApiResponse(httpStatus.value(), true); } + public static FillinApiResponse success(@NotNull Object data) { + return FillinApiResponse.success(HttpStatus.OK, data); + } + public static FillinApiResponse success(@NotNull HttpStatus httpStatus, @NotNull Object data) { return new FillinApiResponse(httpStatus.value(), true, data, null); } diff --git a/fillin/src/main/java/com/teamfillin/fillin/api/FillinExceptionHandler.java b/fillin/src/main/java/com/teamfillin/fillin/api/FillinExceptionHandler.java new file mode 100644 index 0000000..178009a --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/api/FillinExceptionHandler.java @@ -0,0 +1,17 @@ +package com.teamfillin.fillin.api; + +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +import com.teamfillin.fillin.FillinException; + +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@RestControllerAdvice +public class FillinExceptionHandler { + @ExceptionHandler(FillinException.class) + FillinApiResponse handleFillinException(FillinException e) { + return FillinApiResponse.failure(e.getErrorCode()); + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/api/healthcheck/HealthCheckApi.java b/fillin/src/main/java/com/teamfillin/fillin/api/healthcheck/HealthCheckApi.java new file mode 100644 index 0000000..14dd4bc --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/api/healthcheck/HealthCheckApi.java @@ -0,0 +1,27 @@ +package com.teamfillin.fillin.api.healthcheck; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.teamfillin.fillin.api.FillinApiResponse; +import com.teamfillin.fillin.domain.healthcheck.HealthChecker; + +@RestController +public class HealthCheckApi { + private final HealthChecker healthChecker; + + public HealthCheckApi(HealthChecker healthChecker) { + this.healthChecker = healthChecker; + } + + @GetMapping("/health/readiness") + public FillinApiResponse readiness() { + healthChecker.succeed(); + return FillinApiResponse.success(); + } + + @GetMapping("/health/exception") + public void exception() { + healthChecker.forceFail(); + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/healthcheck/HealthCheckErrorCode.java b/fillin/src/main/java/com/teamfillin/fillin/domain/healthcheck/HealthCheckErrorCode.java new file mode 100644 index 0000000..0811017 --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/healthcheck/HealthCheckErrorCode.java @@ -0,0 +1,34 @@ +package com.teamfillin.fillin.domain.healthcheck; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.springframework.http.HttpStatus; + +import com.teamfillin.fillin.FillinErrorCode; + +public enum HealthCheckErrorCode implements FillinErrorCode { + E400_HEALTH_CHECK_BAD_REQUEST(HttpStatus.BAD_REQUEST, "health check fail."); + + @NotNull + private final HttpStatus status; + + @Nullable + private final String defaultMessage; + + HealthCheckErrorCode(HttpStatus status, String defaultMessage) { + this.status = status; + this.defaultMessage = defaultMessage; + } + + @NotNull + @Override + public HttpStatus status() { + return status; + } + + @Nullable + @Override + public String defaultMessage() { + return defaultMessage; + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/healthcheck/HealthChecker.java b/fillin/src/main/java/com/teamfillin/fillin/domain/healthcheck/HealthChecker.java new file mode 100644 index 0000000..f73da18 --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/healthcheck/HealthChecker.java @@ -0,0 +1,19 @@ +package com.teamfillin.fillin.domain.healthcheck; + +import static com.teamfillin.fillin.domain.healthcheck.HealthCheckErrorCode.*; + +import org.springframework.stereotype.Service; + +import com.teamfillin.fillin.FillinException; + +@Service +public class HealthChecker { + public void succeed() { + } + + public void forceFail() { + throw FillinException.builder() + .errorCode(E400_HEALTH_CHECK_BAD_REQUEST) + .build(); + } +} From c7342f7f01c3b4ab1e88150074789dd093a9da2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=A8=B8=ED=99=98=EC=9E=84?= <88091704+daehwan2da@users.noreply.github.com> Date: Sun, 3 Dec 2023 17:40:16 +0900 Subject: [PATCH 09/15] =?UTF-8?q?Entity=20postfix=20=EC=B6=94=EA=B0=80=20(?= =?UTF-8?q?#23)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Entity postfix 추가 * entity 에 table name 적용 --- .../fillin/domain/account/Account.java | 42 ---------- .../fillin/domain/account/AccountEntity.java | 82 +++++++++++++++++++ .../fillin/domain/account/SocialInfo.java | 42 ---------- .../{FilmType.java => CompanyEntity.java} | 6 +- .../film/{Film.java => FilmEntity.java} | 8 +- .../{Company.java => FilmTypeEntity.java} | 6 +- .../photo/{Photo.java => PhotoEntity.java} | 8 +- .../price/{Price.java => PriceEntity.java} | 6 +- ...istory.java => ReactionHistoryEntity.java} | 6 +- ...unningTime.java => RunningTimeEntity.java} | 6 +- .../studio/{Studio.java => StudioEntity.java} | 8 +- .../user/{User.java => UserEntity.java} | 6 +- 12 files changed, 119 insertions(+), 107 deletions(-) delete mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/account/Account.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountEntity.java delete mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/account/SocialInfo.java rename fillin/src/main/java/com/teamfillin/fillin/domain/film/{FilmType.java => CompanyEntity.java} (76%) rename fillin/src/main/java/com/teamfillin/fillin/domain/film/{Film.java => FilmEntity.java} (76%) rename fillin/src/main/java/com/teamfillin/fillin/domain/film/{Company.java => FilmTypeEntity.java} (75%) rename fillin/src/main/java/com/teamfillin/fillin/domain/photo/{Photo.java => PhotoEntity.java} (80%) rename fillin/src/main/java/com/teamfillin/fillin/domain/price/{Price.java => PriceEntity.java} (80%) rename fillin/src/main/java/com/teamfillin/fillin/domain/reactionHistory/{ReactionHistory.java => ReactionHistoryEntity.java} (82%) rename fillin/src/main/java/com/teamfillin/fillin/domain/runningTime/{RunningTime.java => RunningTimeEntity.java} (83%) rename fillin/src/main/java/com/teamfillin/fillin/domain/studio/{Studio.java => StudioEntity.java} (83%) rename fillin/src/main/java/com/teamfillin/fillin/domain/user/{User.java => UserEntity.java} (86%) diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/account/Account.java b/fillin/src/main/java/com/teamfillin/fillin/domain/account/Account.java deleted file mode 100644 index a6c508b..0000000 --- a/fillin/src/main/java/com/teamfillin/fillin/domain/account/Account.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.teamfillin.fillin.domain.account; - -import javax.persistence.Column; -import javax.persistence.Embedded; -import javax.persistence.Entity; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.Index; -import javax.persistence.Table; - -@Entity -@Table( - name = "account", - indexes = { - @Index(name = "ux_account_social_info", columnList = "socialType, socialId") - } -) -public class Account { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long no; - - @Embedded - private SocialInfo socialInfo; - - @Column(length = 500) - private String refreshToken; - - @Column(nullable = false) - private Long userNo; - - protected Account() { - } - - public Account(SocialInfo socialInfo, String refreshToken, Long userNo) { - this.socialInfo = socialInfo; - this.refreshToken = refreshToken; - this.userNo = userNo; - } -} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountEntity.java b/fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountEntity.java new file mode 100644 index 0000000..259d50c --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountEntity.java @@ -0,0 +1,82 @@ +package com.teamfillin.fillin.domain.account; + +import java.util.Objects; + +import javax.persistence.Column; +import javax.persistence.Embeddable; +import javax.persistence.Embedded; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Index; +import javax.persistence.Table; + +@Entity +@Table( + name = "account", + indexes = { + @Index(name = "ux_account_social_info", columnList = "socialType, socialId") + } +) +public class AccountEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long no; + + @Embedded + private SocialInfo socialInfo; + + @Column(length = 500) + private String refreshToken; + + @Column(nullable = false) + private Long userNo; + + protected AccountEntity() { + } + + public AccountEntity(SocialInfo socialInfo, String refreshToken, Long userNo) { + this.socialInfo = socialInfo; + this.refreshToken = refreshToken; + this.userNo = userNo; + } + + @Embeddable + public static class SocialInfo { + + @Column(nullable = false, length = 100) + private String socialId; + + @Enumerated(value = EnumType.STRING) + @Column(nullable = false, length = 10) + private SocialType socialType; + + protected SocialInfo() { + } + + public SocialInfo(String socialId, SocialType socialType) { + this.socialId = socialId; + this.socialType = socialType; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + SocialInfo that = (SocialInfo)o; + return Objects.equals(socialId, that.socialId) && socialType == that.socialType; + } + + @Override + public int hashCode() { + return Objects.hash(socialId, socialType); + } + } + +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/account/SocialInfo.java b/fillin/src/main/java/com/teamfillin/fillin/domain/account/SocialInfo.java deleted file mode 100644 index f90508d..0000000 --- a/fillin/src/main/java/com/teamfillin/fillin/domain/account/SocialInfo.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.teamfillin.fillin.domain.account; - -import java.util.Objects; - -import javax.persistence.Column; -import javax.persistence.Embeddable; -import javax.persistence.EnumType; -import javax.persistence.Enumerated; - -@Embeddable -public class SocialInfo { - - @Column(nullable = false, length = 100) - private String socialId; - - @Enumerated(value = EnumType.STRING) - @Column(nullable = false, length = 10) - private SocialType socialType; - - protected SocialInfo() { - } - - public SocialInfo(String socialId, SocialType socialType) { - this.socialId = socialId; - this.socialType = socialType; - } - - @Override - public boolean equals(Object o) { - if (this == o) - return true; - if (o == null || getClass() != o.getClass()) - return false; - SocialInfo that = (SocialInfo)o; - return Objects.equals(socialId, that.socialId) && socialType == that.socialType; - } - - @Override - public int hashCode() { - return Objects.hash(socialId, socialType); - } -} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/film/FilmType.java b/fillin/src/main/java/com/teamfillin/fillin/domain/film/CompanyEntity.java similarity index 76% rename from fillin/src/main/java/com/teamfillin/fillin/domain/film/FilmType.java rename to fillin/src/main/java/com/teamfillin/fillin/domain/film/CompanyEntity.java index 616bdd3..2e0bcfb 100644 --- a/fillin/src/main/java/com/teamfillin/fillin/domain/film/FilmType.java +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/film/CompanyEntity.java @@ -5,9 +5,11 @@ import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; +import javax.persistence.Table; @Entity -public class FilmType { +@Table(name = "company") +public class CompanyEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -16,6 +18,6 @@ public class FilmType { @Column(nullable = false, length = 50) private String name; - protected FilmType() { + protected CompanyEntity() { } } diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/film/Film.java b/fillin/src/main/java/com/teamfillin/fillin/domain/film/FilmEntity.java similarity index 76% rename from fillin/src/main/java/com/teamfillin/fillin/domain/film/Film.java rename to fillin/src/main/java/com/teamfillin/fillin/domain/film/FilmEntity.java index 014a2da..b202196 100644 --- a/fillin/src/main/java/com/teamfillin/fillin/domain/film/Film.java +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/film/FilmEntity.java @@ -5,9 +5,11 @@ import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; +import javax.persistence.Table; @Entity -public class Film { +@Table(name = "film") +public class FilmEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -22,10 +24,10 @@ public class Film { @Column(nullable = false) private Long companyNo; - protected Film() { + protected FilmEntity() { } - public Film(String name, Long typeNo, Long companyNo) { + public FilmEntity(String name, Long typeNo, Long companyNo) { this.name = name; this.typeNo = typeNo; this.companyNo = companyNo; diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/film/Company.java b/fillin/src/main/java/com/teamfillin/fillin/domain/film/FilmTypeEntity.java similarity index 75% rename from fillin/src/main/java/com/teamfillin/fillin/domain/film/Company.java rename to fillin/src/main/java/com/teamfillin/fillin/domain/film/FilmTypeEntity.java index 9d445a1..7ef6cc0 100644 --- a/fillin/src/main/java/com/teamfillin/fillin/domain/film/Company.java +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/film/FilmTypeEntity.java @@ -5,9 +5,11 @@ import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; +import javax.persistence.Table; @Entity -public class Company { +@Table(name = "film_type") +public class FilmTypeEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -16,6 +18,6 @@ public class Company { @Column(nullable = false, length = 50) private String name; - protected Company() { + protected FilmTypeEntity() { } } diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/photo/Photo.java b/fillin/src/main/java/com/teamfillin/fillin/domain/photo/PhotoEntity.java similarity index 80% rename from fillin/src/main/java/com/teamfillin/fillin/domain/photo/Photo.java rename to fillin/src/main/java/com/teamfillin/fillin/domain/photo/PhotoEntity.java index aa830c8..3e013c6 100644 --- a/fillin/src/main/java/com/teamfillin/fillin/domain/photo/Photo.java +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/photo/PhotoEntity.java @@ -7,11 +7,13 @@ import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; +import javax.persistence.Table; import com.teamfillin.fillin.domain.common.BaseTimeEntity; @Entity -public class Photo extends BaseTimeEntity { +@Table(name = "photo") +public class PhotoEntity extends BaseTimeEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -33,10 +35,10 @@ public class Photo extends BaseTimeEntity { @Column(nullable = false, length = 10) private PhotoStatus status; - protected Photo() { + protected PhotoEntity() { } - public Photo(Long userNo, Long studioNo, Long filmNo, String imagePath) { + public PhotoEntity(Long userNo, Long studioNo, Long filmNo, String imagePath) { this.userNo = userNo; this.studioNo = studioNo; this.filmNo = filmNo; diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/price/Price.java b/fillin/src/main/java/com/teamfillin/fillin/domain/price/PriceEntity.java similarity index 80% rename from fillin/src/main/java/com/teamfillin/fillin/domain/price/Price.java rename to fillin/src/main/java/com/teamfillin/fillin/domain/price/PriceEntity.java index f78cfe2..413dfa4 100644 --- a/fillin/src/main/java/com/teamfillin/fillin/domain/price/Price.java +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/price/PriceEntity.java @@ -5,9 +5,11 @@ import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; +import javax.persistence.Table; @Entity -public class Price { +@Table(name = "price") +public class PriceEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -21,6 +23,6 @@ public class Price { private Integer amount; - protected Price() { + protected PriceEntity() { } } diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/reactionHistory/ReactionHistory.java b/fillin/src/main/java/com/teamfillin/fillin/domain/reactionHistory/ReactionHistoryEntity.java similarity index 82% rename from fillin/src/main/java/com/teamfillin/fillin/domain/reactionHistory/ReactionHistory.java rename to fillin/src/main/java/com/teamfillin/fillin/domain/reactionHistory/ReactionHistoryEntity.java index 15191ba..d6432b1 100644 --- a/fillin/src/main/java/com/teamfillin/fillin/domain/reactionHistory/ReactionHistory.java +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/reactionHistory/ReactionHistoryEntity.java @@ -17,7 +17,7 @@ @Index(name = "ux_reaction_user_and_target", columnList = "userNo, targetNo, targetType") } ) -public class ReactionHistory extends BaseTimeEntity { +public class ReactionHistoryEntity extends BaseTimeEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -32,10 +32,10 @@ public class ReactionHistory extends BaseTimeEntity { @Column(nullable = false, length = 50) private TargetType targetType; - protected ReactionHistory() { + protected ReactionHistoryEntity() { } - public ReactionHistory(Long userNo, Long targetNo, TargetType targetType) { + public ReactionHistoryEntity(Long userNo, Long targetNo, TargetType targetType) { this.userNo = userNo; this.targetNo = targetNo; this.targetType = targetType; diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/runningTime/RunningTime.java b/fillin/src/main/java/com/teamfillin/fillin/domain/runningTime/RunningTimeEntity.java similarity index 83% rename from fillin/src/main/java/com/teamfillin/fillin/domain/runningTime/RunningTime.java rename to fillin/src/main/java/com/teamfillin/fillin/domain/runningTime/RunningTimeEntity.java index e843fb5..46dda9b 100644 --- a/fillin/src/main/java/com/teamfillin/fillin/domain/runningTime/RunningTime.java +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/runningTime/RunningTimeEntity.java @@ -9,9 +9,11 @@ import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; +import javax.persistence.Table; @Entity -public class RunningTime { +@Table(name = "running_time") +public class RunningTimeEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -28,6 +30,6 @@ public class RunningTime { private LocalDateTime endAt; - protected RunningTime() { + protected RunningTimeEntity() { } } diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/studio/Studio.java b/fillin/src/main/java/com/teamfillin/fillin/domain/studio/StudioEntity.java similarity index 83% rename from fillin/src/main/java/com/teamfillin/fillin/domain/studio/Studio.java rename to fillin/src/main/java/com/teamfillin/fillin/domain/studio/StudioEntity.java index dee8d96..5e2f85a 100644 --- a/fillin/src/main/java/com/teamfillin/fillin/domain/studio/Studio.java +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/studio/StudioEntity.java @@ -7,11 +7,13 @@ import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; +import javax.persistence.Table; import lombok.Builder; @Entity -public class Studio { +@Table(name = "studio") +public class StudioEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -40,11 +42,11 @@ public class Studio { @Column(nullable = false, length = 10) private StudioStatus status; - protected Studio() { + protected StudioEntity() { } @Builder - private Studio(String name, String address, String tel, Double latitude, Double longitude, + private StudioEntity(String name, String address, String tel, Double latitude, Double longitude, String etc, String site, StudioStatus status) { this.name = name; this.address = address; diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/user/User.java b/fillin/src/main/java/com/teamfillin/fillin/domain/user/UserEntity.java similarity index 86% rename from fillin/src/main/java/com/teamfillin/fillin/domain/user/User.java rename to fillin/src/main/java/com/teamfillin/fillin/domain/user/UserEntity.java index 549afb2..86a48b9 100644 --- a/fillin/src/main/java/com/teamfillin/fillin/domain/user/User.java +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/user/UserEntity.java @@ -11,7 +11,7 @@ @Entity @Table(name = "fillin_user") -public class User { +public class UserEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -27,10 +27,10 @@ public class User { @Column(nullable = false, length = 10) private UserStatus status; - protected User() { + protected UserEntity() { } - public User(String nickname, String profileImagePath) { + public UserEntity(String nickname, String profileImagePath) { this.nickname = nickname; this.profileImagePath = profileImagePath; this.status = UserStatus.ACTIVE; From 9026f0befc365394fd991861c4b4ed356b94830f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=A8=B8=ED=99=98=EC=9E=84?= <88091704+daehwan2da@users.noreply.github.com> Date: Sat, 9 Dec 2023 20:27:23 +0900 Subject: [PATCH 10/15] =?UTF-8?q?=EC=9D=B8=EC=A6=9D=20API=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20(#24)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * OAuth2 dependency 제거 * loginOrJoin 로직 구현 tokenHandler 구현 * Account 및 User Exception 처리 * review 반영 NullIgnore 제거 --- fillin/.gitignore | 5 +- fillin/build.gradle.kts | 9 +- .../fillin/api/account/AccountApi.java | 45 +++++++ .../fillin/api/account/AccountRequest.java | 52 ++++++++ .../fillin/api/account/AccountResponse.java | 56 +++++++++ .../fillin/domain/account/Account.java | 78 ++++++++++++ .../domain/account/AccountAccessResult.java | 71 +++++++++++ .../domain/account/AccountAndUserResult.java | 42 +++++++ .../fillin/domain/account/AccountEntity.java | 48 +++++++- .../domain/account/AccountErrorCode.java | 31 +++++ .../account/AccountExceptionHandler.java | 9 ++ .../domain/account/AccountRegister.java | 30 +++++ .../domain/account/AccountRepository.java | 13 ++ .../domain/account/AccountRetriever.java | 28 +++++ .../fillin/domain/account/AccountService.java | 36 ++++++ .../domain/account/AccountUserFacade.java | 67 ++++++++++ .../domain/account/token/AccessToken.java | 40 ++++++ .../domain/account/token/JwtTokenHandler.java | 114 ++++++++++++++++++ .../domain/account/token/RefreshToken.java | 40 ++++++ .../account/token/TokenConfiguration.java | 40 ++++++ .../domain/account/token/TokenType.java | 5 + .../fillin/domain/user/NicknameGenerator.java | 12 ++ .../teamfillin/fillin/domain/user/User.java | 66 ++++++++++ .../fillin/domain/user/UserEntity.java | 31 ++++- .../fillin/domain/user/UserErrorCode.java | 31 +++++ .../domain/user/UserExceptionHandler.java | 9 ++ .../fillin/domain/user/UserRegister.java | 33 +++++ .../fillin/domain/user/UserRepository.java | 9 ++ .../fillin/domain/user/UserRetriever.java | 20 +++ fillin/src/main/resources/application.yaml | 4 + 30 files changed, 1068 insertions(+), 6 deletions(-) create mode 100644 fillin/src/main/java/com/teamfillin/fillin/api/account/AccountApi.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/api/account/AccountRequest.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/api/account/AccountResponse.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/account/Account.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountAccessResult.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountAndUserResult.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountErrorCode.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountExceptionHandler.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountRegister.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountRepository.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountRetriever.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountService.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountUserFacade.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/account/token/AccessToken.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/account/token/JwtTokenHandler.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/account/token/RefreshToken.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/account/token/TokenConfiguration.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/account/token/TokenType.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/user/NicknameGenerator.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/user/User.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/user/UserErrorCode.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/user/UserExceptionHandler.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/user/UserRegister.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/user/UserRepository.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/user/UserRetriever.java diff --git a/fillin/.gitignore b/fillin/.gitignore index afed771..cbb71c4 100644 --- a/fillin/.gitignore +++ b/fillin/.gitignore @@ -40,4 +40,7 @@ out/ **/build/** **/.gradle/** **/gradle/** -.idea/** \ No newline at end of file +.idea/** + +### secret +**/resources/secret diff --git a/fillin/build.gradle.kts b/fillin/build.gradle.kts index 899f5cc..596734d 100644 --- a/fillin/build.gradle.kts +++ b/fillin/build.gradle.kts @@ -31,17 +31,24 @@ repositories { dependencies { implementation("org.springframework.boot:spring-boot-starter-data-jpa") - implementation("org.springframework.boot:spring-boot-starter-oauth2-client") implementation("org.springframework.boot:spring-boot-starter-web") + implementation("org.springframework.boot:spring-boot-starter-validation") compileOnly("org.projectlombok:lombok") runtimeOnly("com.h2database:h2") runtimeOnly("com.mysql:mysql-connector-j") annotationProcessor("org.projectlombok:lombok") + annotationProcessor("org.springframework.boot:spring-boot-configuration-processor") testImplementation("org.springframework.boot:spring-boot-starter-test") // guava implementation("com.google.guava:guava:$guavaVersion") + // apache + implementation("org.apache.commons:commons-lang3:3.14.0") + + // jwt + implementation("io.jsonwebtoken:jjwt:0.12.3") + // kotlin implementation("com.fasterxml.jackson.module:jackson-module-kotlin") implementation("org.jetbrains.kotlin:kotlin-reflect") diff --git a/fillin/src/main/java/com/teamfillin/fillin/api/account/AccountApi.java b/fillin/src/main/java/com/teamfillin/fillin/api/account/AccountApi.java new file mode 100644 index 0000000..83aa31f --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/api/account/AccountApi.java @@ -0,0 +1,45 @@ +package com.teamfillin.fillin.api.account; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +import com.teamfillin.fillin.api.FillinApiResponse; +import com.teamfillin.fillin.domain.account.AccountAccessResult; +import com.teamfillin.fillin.domain.account.AccountService; +import com.teamfillin.fillin.domain.account.SocialType; +import com.teamfillin.fillin.domain.account.token.AccessToken; +import com.teamfillin.fillin.domain.account.token.JwtTokenHandler; + +@RestController +public class AccountApi { + private final AccountService accountService; + private final JwtTokenHandler jwtTokenHandler; + + public AccountApi( + AccountService accountService, + JwtTokenHandler jwtTokenHandler + ) { + this.accountService = accountService; + this.jwtTokenHandler = jwtTokenHandler; + } + + @PostMapping("/auth") + public FillinApiResponse loginOrJoin( + @RequestBody AccountRequest accountRequest + ) { + final SocialType socialType = accountRequest.getSocial(); + final String idKey = accountRequest.getIdKey(); + + final AccountAccessResult accountAccessResult = accountService.loginOrJoin(socialType, idKey); + final AccessToken accessToken = jwtTokenHandler.generateAccessTokenFrom(accountAccessResult); + + return switch (accountAccessResult.getProcedure()) { + case JOIN -> + FillinApiResponse.success(HttpStatus.CREATED, AccountResponse.from(accountAccessResult, accessToken)); + case LOGIN -> // Client 에 해당 api 200 상태코드 적용 가능 여부 확인 필요! + FillinApiResponse.success(HttpStatus.OK, AccountResponse.from(accountAccessResult, accessToken)); + }; + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/api/account/AccountRequest.java b/fillin/src/main/java/com/teamfillin/fillin/api/account/AccountRequest.java new file mode 100644 index 0000000..024989a --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/api/account/AccountRequest.java @@ -0,0 +1,52 @@ +package com.teamfillin.fillin.api.account; + +import javax.validation.constraints.NotBlank; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import com.teamfillin.fillin.domain.account.SocialType; + +public class AccountRequest { + private String token; + @NotBlank(message = "social 은 필수 값입니다.") + private SocialType social; + @NotBlank(message = "idKey 는 필수 값입니다.") + private String idKey; + + private AccountRequest() { + } + + AccountRequest(String token, SocialType social, String idKey) { + this.token = token; + this.social = social; + this.idKey = idKey; + } + + @Nullable + public String getToken() { + return token; + } + + public void setToken(String token) { + this.token = token; + } + + @NotNull + public SocialType getSocial() { + return social; + } + + public void setSocial(String social) { + this.social = SocialType.valueOf(social.toUpperCase()); + } + + @NotNull + public String getIdKey() { + return idKey; + } + + public void setIdKey(String idKey) { + this.idKey = idKey; + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/api/account/AccountResponse.java b/fillin/src/main/java/com/teamfillin/fillin/api/account/AccountResponse.java new file mode 100644 index 0000000..90b4eea --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/api/account/AccountResponse.java @@ -0,0 +1,56 @@ +package com.teamfillin.fillin.api.account; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.teamfillin.fillin.domain.account.AccountAccessResult; +import com.teamfillin.fillin.domain.account.SocialType; +import com.teamfillin.fillin.domain.account.token.AccessToken; + +import lombok.AccessLevel; +import lombok.Builder; + +@JsonInclude(JsonInclude.Include.NON_NULL) +public class AccountResponse { + private final SocialType type; + private final String nickname; + private final String accessToken; + @Nullable + private final String refreshToken; + + @Builder(access = AccessLevel.PACKAGE) + private AccountResponse(SocialType type, String nickname, String accessToken, @Nullable String refreshToken) { + this.type = type; + this.nickname = nickname; + this.accessToken = accessToken; + this.refreshToken = refreshToken; + } + + public SocialType getType() { + return type; + } + + public String getNickname() { + return nickname; + } + + public String getAccessToken() { + return accessToken; + } + + public String getRefreshToken() { + return refreshToken; + } + + public static AccountResponse from( + @NotNull AccountAccessResult accountAccessResult, + @NotNull AccessToken accessToken + ) { + return AccountResponse.builder() + .type(accountAccessResult.getSocialType()) + .nickname(accountAccessResult.getNickname()) + .accessToken(accessToken.getValue()) + .build(); + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/account/Account.java b/fillin/src/main/java/com/teamfillin/fillin/domain/account/Account.java new file mode 100644 index 0000000..ca12fe9 --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/account/Account.java @@ -0,0 +1,78 @@ +package com.teamfillin.fillin.domain.account; + +import java.util.Objects; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import lombok.AccessLevel; +import lombok.Builder; + +public class Account { + private final long no; + private final String refreshToken; + private final long userNo; + private final String socialId; + private final SocialType socialType; + + @Builder(access = AccessLevel.PRIVATE) + private Account(long no, String refreshToken, long userNo, String socialId, SocialType socialType) { + this.no = no; + this.refreshToken = refreshToken; + this.userNo = userNo; + this.socialId = socialId; + this.socialType = socialType; + } + + public long getNo() { + return no; + } + + @Nullable + public String getRefreshToken() { + return refreshToken; + } + + public long getUserNo() { + return userNo; + } + + @NotNull + public String getSocialId() { + return socialId; + } + + @NotNull + public SocialType getSocialType() { + return socialType; + } + + public static Account from(@NotNull AccountEntity accountEntity) { + return Account.builder() + .no(accountEntity.getNo()) + .userNo(accountEntity.getUserNo()) + .socialType(accountEntity.getSocialInfo().getSocialType()) + .socialId(accountEntity.getSocialInfo().getSocialId()) + .refreshToken(accountEntity.getRefreshToken()) + .build(); + } + + @NotNull + public AccountEntity.SocialInfo getSocialInfo() { + return AccountEntity.SocialInfo.from(socialType, socialId); + } + + @Override + public int hashCode() { + return Objects.hash(no, userNo, socialId, socialType); + } + + @Override + public boolean equals(Object obj) { + if (obj != null && getClass() == obj.getClass()) { + Account target = (Account)obj; + return no == target.no; + } + return false; + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountAccessResult.java b/fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountAccessResult.java new file mode 100644 index 0000000..31278cd --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountAccessResult.java @@ -0,0 +1,71 @@ +package com.teamfillin.fillin.domain.account; + +import org.jetbrains.annotations.NotNull; + +import lombok.AccessLevel; +import lombok.Builder; + +public class AccountAccessResult { + private final PROCEDURE procedure; + private final long accountNo; + private final long userNo; + private final SocialType socialType; + private final String nickname; + + @Builder(access = AccessLevel.PRIVATE) + private AccountAccessResult(PROCEDURE procedure, SocialType socialType, String nickname, long accountNo, long userNo + ) { + this.procedure = procedure; + this.socialType = socialType; + this.nickname = nickname; + this.accountNo = accountNo; + this.userNo = userNo; + } + + public static AccountAccessResult login(@NotNull AccountAndUserResult accountAndUserResult) { + return AccountAccessResult.builder() + .procedure(PROCEDURE.LOGIN) + .socialType(accountAndUserResult.getSocialType()) + .nickname(accountAndUserResult.getNickname()) + .accountNo(accountAndUserResult.getAccountNo()) + .userNo(accountAndUserResult.getUserNo()) + .build(); + } + + public static AccountAccessResult join(@NotNull AccountAndUserResult accountAndUserResult) { + return AccountAccessResult.builder() + .procedure(PROCEDURE.JOIN) + .socialType(accountAndUserResult.getSocialType()) + .nickname(accountAndUserResult.getNickname()) + .accountNo(accountAndUserResult.getAccountNo()) + .userNo(accountAndUserResult.getUserNo()) + .build(); + } + + @NotNull + public SocialType getSocialType() { + return socialType; + } + + @NotNull + public String getNickname() { + return nickname; + } + + public long getAccountNo() { + return accountNo; + } + + public long getUserNo() { + return userNo; + } + + public PROCEDURE getProcedure() { + return procedure; + } + + public enum PROCEDURE { + LOGIN, + JOIN, + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountAndUserResult.java b/fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountAndUserResult.java new file mode 100644 index 0000000..672a495 --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountAndUserResult.java @@ -0,0 +1,42 @@ +package com.teamfillin.fillin.domain.account; + +import org.jetbrains.annotations.NotNull; + +import com.teamfillin.fillin.domain.user.User; + +public class AccountAndUserResult { + private final Account account; + private final User user; + + private AccountAndUserResult(Account account, User user) { + this.account = account; + this.user = user; + } + + public static AccountAndUserResult of(@NotNull Account account, @NotNull User user) { + return new AccountAndUserResult(account, user); + } + + @NotNull + public SocialType getSocialType() { + return account.getSocialType(); + } + + @NotNull + public String getSocialId() { + return account.getSocialId(); + } + + public long getAccountNo() { + return account.getNo(); + } + + public long getUserNo() { + return user.getNo(); + } + + @NotNull + public String getNickname() { + return user.getNickname(); + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountEntity.java b/fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountEntity.java index 259d50c..843fbf6 100644 --- a/fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountEntity.java +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountEntity.java @@ -14,6 +14,9 @@ import javax.persistence.Index; import javax.persistence.Table; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + @Entity @Table( name = "account", @@ -39,12 +42,39 @@ public class AccountEntity { protected AccountEntity() { } - public AccountEntity(SocialInfo socialInfo, String refreshToken, Long userNo) { + private AccountEntity(@NotNull SocialInfo socialInfo, long userNo, @NotNull String refreshToken) { this.socialInfo = socialInfo; + this.userNo = userNo; this.refreshToken = refreshToken; + } + + private AccountEntity(@NotNull SocialInfo socialInfo, long userNo) { + this.socialInfo = socialInfo; this.userNo = userNo; } + public static AccountEntity from(@NotNull SocialInfo socialInfo, long userNo) { + return new AccountEntity(socialInfo, userNo); + } + + public Long getNo() { + return no; + } + + @NotNull + public SocialInfo getSocialInfo() { + return socialInfo; + } + + @Nullable + public String getRefreshToken() { + return refreshToken; + } + + public Long getUserNo() { + return userNo; + } + @Embeddable public static class SocialInfo { @@ -58,11 +88,15 @@ public static class SocialInfo { protected SocialInfo() { } - public SocialInfo(String socialId, SocialType socialType) { + private SocialInfo(String socialId, SocialType socialType) { this.socialId = socialId; this.socialType = socialType; } + public static SocialInfo from(@NotNull SocialType socialType, @NotNull String socialId) { + return new SocialInfo(socialId, socialType); + } + @Override public boolean equals(Object o) { if (this == o) @@ -77,6 +111,16 @@ public boolean equals(Object o) { public int hashCode() { return Objects.hash(socialId, socialType); } + + @NotNull + public String getSocialId() { + return socialId; + } + + @NotNull + public SocialType getSocialType() { + return socialType; + } } } diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountErrorCode.java b/fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountErrorCode.java new file mode 100644 index 0000000..e9999bc --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountErrorCode.java @@ -0,0 +1,31 @@ +package com.teamfillin.fillin.domain.account; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.springframework.http.HttpStatus; + +import com.teamfillin.fillin.FillinErrorCode; + +enum AccountErrorCode implements FillinErrorCode { + E404_ACCOUNT_NOT_FOUND(HttpStatus.NOT_FOUND, "계정을 찾지못했습니다."); + + private final HttpStatus status; + private final String defaultMessage; + + AccountErrorCode(HttpStatus status, String defaultMessage) { + this.status = status; + this.defaultMessage = defaultMessage; + } + + @NotNull + @Override + public HttpStatus status() { + return status; + } + + @Nullable + @Override + public String defaultMessage() { + return defaultMessage; + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountExceptionHandler.java b/fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountExceptionHandler.java new file mode 100644 index 0000000..e7eecda --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountExceptionHandler.java @@ -0,0 +1,9 @@ +package com.teamfillin.fillin.domain.account; + +import com.teamfillin.fillin.FillinException; + +class AccountExceptionHandler { + public static FillinException notFound() { + throw FillinException.from(AccountErrorCode.E404_ACCOUNT_NOT_FOUND); + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountRegister.java b/fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountRegister.java new file mode 100644 index 0000000..a9395dd --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountRegister.java @@ -0,0 +1,30 @@ +package com.teamfillin.fillin.domain.account; + +import org.jetbrains.annotations.NotNull; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; + +import com.teamfillin.fillin.domain.account.AccountEntity.SocialInfo; + +@Component +public class AccountRegister { + private final AccountRepository accountRepository; + + public AccountRegister(AccountRepository accountRepository) { + this.accountRepository = accountRepository; + } + + // TODO : 중복 요청으로 인한 unique error catch 필요 + @Transactional(propagation = Propagation.MANDATORY) + public Account registerAccount( + @NotNull SocialType socialType, + @NotNull String socialId, + long userNo + ) { + final SocialInfo socialInfo = SocialInfo.from(socialType, socialId); + final AccountEntity registeredAccountEntity = accountRepository.save(AccountEntity.from(socialInfo, userNo)); + + return Account.from(registeredAccountEntity); + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountRepository.java b/fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountRepository.java new file mode 100644 index 0000000..e3cea4c --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountRepository.java @@ -0,0 +1,13 @@ +package com.teamfillin.fillin.domain.account; + +import java.util.Optional; + +import javax.validation.constraints.NotNull; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface AccountRepository extends JpaRepository { + Optional findAccountBySocialInfo(@NotNull AccountEntity.SocialInfo socialInfo); +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountRetriever.java b/fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountRetriever.java new file mode 100644 index 0000000..2795ab0 --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountRetriever.java @@ -0,0 +1,28 @@ +package com.teamfillin.fillin.domain.account; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.springframework.stereotype.Component; + +@Component +public class AccountRetriever { + private final AccountRepository accountRepository; + + public AccountRetriever(AccountRepository accountRepository) { + this.accountRepository = accountRepository; + } + + @NotNull + public Account retrieve(@NotNull SocialType socialType, @NotNull String socialId) { + return accountRepository.findAccountBySocialInfo(AccountEntity.SocialInfo.from(socialType, socialId)) + .map(Account::from) + .orElseThrow(AccountExceptionHandler::notFound); + } + + @Nullable + public Account retrieveOrNull(@NotNull SocialType socialType, @NotNull String socialId) { + return accountRepository.findAccountBySocialInfo(AccountEntity.SocialInfo.from(socialType, socialId)) + .map(Account::from) + .orElse(null); + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountService.java b/fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountService.java new file mode 100644 index 0000000..d6c0c5b --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountService.java @@ -0,0 +1,36 @@ +package com.teamfillin.fillin.domain.account; + +import org.jetbrains.annotations.NotNull; +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class AccountService { + private final AccountUserFacade accountUserFacade; + + public AccountService(AccountUserFacade accountUserFacade) { + this.accountUserFacade = accountUserFacade; + } + + @Transactional + public AccountAccessResult loginOrJoin(@NotNull SocialType socialType, @NotNull String idKey) { + final AccountAndUserResult accountAndUser = accountUserFacade.retrieveAccountAndUserOrNull(socialType, idKey); + if (joined(accountAndUser)) { + return AccountAccessResult.login(accountAndUser); + } + + final AccountAndUserResult joinResult; + try { + joinResult = accountUserFacade.registerAccountAndUser(socialType, idKey); + } catch (DataIntegrityViolationException e) { // unique error 에 대한 catch + final AccountAndUserResult foundAccountAndUser = accountUserFacade.retrieve(socialType, idKey); + return AccountAccessResult.login(foundAccountAndUser); + } + return AccountAccessResult.join(joinResult); + } + + private boolean joined(AccountAndUserResult accountAndUser) { + return accountAndUser != null; + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountUserFacade.java b/fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountUserFacade.java new file mode 100644 index 0000000..123e003 --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountUserFacade.java @@ -0,0 +1,67 @@ +package com.teamfillin.fillin.domain.account; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +import com.teamfillin.fillin.domain.user.User; +import com.teamfillin.fillin.domain.user.UserRegister; +import com.teamfillin.fillin.domain.user.UserRetriever; + +@Component +public class AccountUserFacade { + private final AccountRegister accountRegister; + private final AccountRetriever accountRetriever; + private final UserRegister userRegister; + private final UserRetriever userRetriever; + + public AccountUserFacade( + AccountRegister accountRegister + , AccountRetriever accountRetriever + , UserRegister userRegister + , UserRetriever userRetriever + ) { + this.accountRegister = accountRegister; + this.accountRetriever = accountRetriever; + this.userRegister = userRegister; + this.userRetriever = userRetriever; + } + + @NotNull + @Transactional(readOnly = true) + public AccountAndUserResult retrieve(@NotNull SocialType socialType, @NotNull String idKey) { + final Account foundAccount = accountRetriever.retrieve(socialType, idKey); + final User foundUser = userRetriever.retrieve(foundAccount.getUserNo()); + + return AccountAndUserResult.of(foundAccount, foundUser); + } + + @Nullable + @Transactional(readOnly = true) + public AccountAndUserResult retrieveAccountAndUserOrNull(@NotNull SocialType socialType, @NotNull String idKey) { + final Account foundAccount = accountRetriever.retrieveOrNull(socialType, idKey); + + if (foundAccount != null) { + final User foundUser = userRetriever.retrieve(foundAccount.getUserNo()); + return AccountAndUserResult.of(foundAccount, foundUser); + } + + return null; + } + + @NotNull + @Transactional + public AccountAndUserResult registerAccountAndUser(@NotNull SocialType socialType, @NotNull String idKey) + throws DataIntegrityViolationException { + final User registeredUser = userRegister.registerUser(); + final Account registeredAccount = accountRegister.registerAccount( + socialType, + idKey, + registeredUser.getNo() + ); + + return AccountAndUserResult.of(registeredAccount, registeredUser); + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/account/token/AccessToken.java b/fillin/src/main/java/com/teamfillin/fillin/domain/account/token/AccessToken.java new file mode 100644 index 0000000..d4b79c4 --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/account/token/AccessToken.java @@ -0,0 +1,40 @@ +package com.teamfillin.fillin.domain.account.token; + +import java.util.Objects; + +import org.jetbrains.annotations.NotNull; + +import lombok.AccessLevel; +import lombok.Builder; + +public class AccessToken { + @NotNull + private final TokenType type; + @NotNull + private final String value; + + @Builder(access = AccessLevel.PACKAGE) + private AccessToken(@NotNull TokenType tokenType, @NotNull String value) { + this.type = tokenType; + this.value = value; + } + + @NotNull + public String getValue() { + return value; + } + + @Override + public int hashCode() { + return Objects.hash(type, value); + } + + @Override + public boolean equals(Object obj) { + if (obj != null && getClass() == obj.getClass()) { + AccessToken target = (AccessToken)obj; + return type == target.type && Objects.equals(value, target.type); + } + return false; + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/account/token/JwtTokenHandler.java b/fillin/src/main/java/com/teamfillin/fillin/domain/account/token/JwtTokenHandler.java new file mode 100644 index 0000000..7ae7b4b --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/account/token/JwtTokenHandler.java @@ -0,0 +1,114 @@ +package com.teamfillin.fillin.domain.account.token; + +import java.time.Instant; +import java.util.Date; +import java.util.Map; + +import javax.crypto.SecretKey; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.springframework.stereotype.Component; + +import com.google.common.collect.Maps; +import com.teamfillin.fillin.domain.account.AccountAccessResult; +import com.teamfillin.fillin.domain.account.SocialType; + +import io.jsonwebtoken.ExpiredJwtException; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.MalformedJwtException; +import io.jsonwebtoken.io.Decoders; +import io.jsonwebtoken.security.Keys; + +@Component +public class JwtTokenHandler { + private static final String CLAIM_SOCIAL_TYPE = "socialType"; + private static final String CLAIM_ACCOUNT_NO = "accountNo"; + private static final String CLAIM_USER_NO = "userNo"; + + private final TokenConfiguration.JwtTokenProperties jwtTokenProperties; + private final SecretKey key; + + public JwtTokenHandler(TokenConfiguration.JwtTokenProperties jwtTokenProperties) { + this.jwtTokenProperties = jwtTokenProperties; + this.key = Keys.hmacShaKeyFor(Decoders.BASE64.decode(jwtTokenProperties.getSecret())); + } + + public AccessToken generateAccessTokenFrom(@NotNull AccountAccessResult accountAccessResult) { + final SocialType socialType = accountAccessResult.getSocialType(); + final long accountNo = accountAccessResult.getAccountNo(); + final long userNo = accountAccessResult.getUserNo(); + final long now = Instant.EPOCH.toEpochMilli(); + + final String accessToken = Jwts.builder() + .claims(makeClaims(socialType, accountNo, userNo)) + .signWith(key) + .expiration(new Date(now + jwtTokenProperties.getAccessTokenValidityInMilli())) + .compact(); + + return AccessToken.builder() + .tokenType(TokenType.JWT) + .value(accessToken) + .build(); + } + + // 이후 refresh token 적용시 메서드 생성 및 구현. + // public RefreshToken generateRefreshTokenFrom() + + public ValidateResult validateToken(@NotNull String token) { + ValidateResult validateResult; + try { + Jwts.parser().verifyWith(key).build().parseEncryptedClaims(token); + validateResult = ValidateResult.valid(); + } catch (SecurityException | MalformedJwtException e) { + validateResult = ValidateResult.invalid("잘못된 JWT 서명입니다."); + } catch (ExpiredJwtException e) { + validateResult = ValidateResult.invalid("만료된 JWT 서명입니다."); + } catch (Throwable t) { + validateResult = ValidateResult.invalid("잘못된 혹은 지원하지 않는 토큰입니다."); + } + + return validateResult; + } + + private Map makeClaims(SocialType socialType, long accountNo, long userNo) { + final Map claimMap = Maps.newHashMap(); + claimMap.put(CLAIM_SOCIAL_TYPE, socialType.name()); + claimMap.put(CLAIM_ACCOUNT_NO, String.valueOf(accountNo)); + claimMap.put(CLAIM_USER_NO, String.valueOf(userNo)); + + return claimMap; + } + + public static class ValidateResult { + private final boolean isValid; + private final String invalidReason; + + private ValidateResult(boolean isValid) { + this.isValid = isValid; + this.invalidReason = null; + } + + private ValidateResult(boolean isValid, String invalidReason) { + this.isValid = isValid; + this.invalidReason = invalidReason; + } + + public static ValidateResult valid() { + return new ValidateResult(true); + } + + public static ValidateResult invalid(@NotNull String reason) { + return new ValidateResult(false, reason); + } + + public boolean isValid() { + return isValid; + } + + @Nullable + public String getInvalidReason() { + return invalidReason; + } + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/account/token/RefreshToken.java b/fillin/src/main/java/com/teamfillin/fillin/domain/account/token/RefreshToken.java new file mode 100644 index 0000000..7ee189e --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/account/token/RefreshToken.java @@ -0,0 +1,40 @@ +package com.teamfillin.fillin.domain.account.token; + +import java.util.Objects; + +import org.jetbrains.annotations.NotNull; + +import lombok.AccessLevel; +import lombok.Builder; + +public class RefreshToken { + @NotNull + private final TokenType type; + @NotNull + private final String value; + + @Builder(access = AccessLevel.PACKAGE) + private RefreshToken(@NotNull TokenType tokenType, @NotNull String value) { + this.type = tokenType; + this.value = value; + } + + @NotNull + public String getValue() { + return value; + } + + @Override + public int hashCode() { + return Objects.hash(type, value); + } + + @Override + public boolean equals(Object obj) { + if (obj != null && getClass() == obj.getClass()) { + RefreshToken target = (RefreshToken)obj; + return type == target.type && Objects.equals(value, target.type); + } + return false; + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/account/token/TokenConfiguration.java b/fillin/src/main/java/com/teamfillin/fillin/domain/account/token/TokenConfiguration.java new file mode 100644 index 0000000..d82d9a3 --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/account/token/TokenConfiguration.java @@ -0,0 +1,40 @@ +package com.teamfillin.fillin.domain.account.token; + +import org.jetbrains.annotations.NotNull; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.context.properties.ConfigurationPropertiesScan; +import org.springframework.boot.context.properties.ConstructorBinding; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +@Configuration +@EnableConfigurationProperties +@ConfigurationPropertiesScan +public class TokenConfiguration { + @ConstructorBinding + @ConfigurationProperties(prefix = "fillin.token.jwt") + public static class JwtTokenProperties { + private final String secret; + private final long accessTokenValidityInMilli; + private final long refreshTokenValidityInMilli; + + public JwtTokenProperties(String secret, long tokenValidityInMilli, long refreshTokenValidityInMilli) { + this.secret = secret; + this.accessTokenValidityInMilli = tokenValidityInMilli; + this.refreshTokenValidityInMilli = refreshTokenValidityInMilli; + } + + @NotNull + public String getSecret() { + return secret; + } + + public long getAccessTokenValidityInMilli() { + return accessTokenValidityInMilli; + } + + public long getRefreshTokenValidityInMilli() { + return refreshTokenValidityInMilli; + } + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/account/token/TokenType.java b/fillin/src/main/java/com/teamfillin/fillin/domain/account/token/TokenType.java new file mode 100644 index 0000000..1b479a8 --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/account/token/TokenType.java @@ -0,0 +1,5 @@ +package com.teamfillin.fillin.domain.account.token; + +public enum TokenType { + JWT +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/user/NicknameGenerator.java b/fillin/src/main/java/com/teamfillin/fillin/domain/user/NicknameGenerator.java new file mode 100644 index 0000000..52819d5 --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/user/NicknameGenerator.java @@ -0,0 +1,12 @@ +package com.teamfillin.fillin.domain.user; + +import org.springframework.stereotype.Component; + +@Component +public class NicknameGenerator { + + // TODO : 닉네임 랜덤 로직 기획 확인 및 구현 필요! + String makeByRule() { + return "random"; + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/user/User.java b/fillin/src/main/java/com/teamfillin/fillin/domain/user/User.java new file mode 100644 index 0000000..a012fd9 --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/user/User.java @@ -0,0 +1,66 @@ +package com.teamfillin.fillin.domain.user; + +import java.util.Objects; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import lombok.AccessLevel; +import lombok.Builder; + +public class User { + private final long no; + private final String nickname; + private final String profileImagePath; + private final UserStatus status; + + @Builder(access = AccessLevel.PRIVATE) + private User(long no, String nickname, UserStatus status, String profileImagePath) { + this.no = no; + this.nickname = nickname; + this.status = status; + this.profileImagePath = profileImagePath; + } + + public long getNo() { + return no; + } + + @NotNull + public String getNickname() { + return nickname; + } + + @Nullable + public String getProfileImagePath() { + return profileImagePath; + } + + @NotNull + public UserStatus getStatus() { + return status; + } + + public static User from(@NotNull UserEntity userEntity) { + return User.builder() + .no(userEntity.getNo()) + .nickname(userEntity.getNickname()) + .status(userEntity.getStatus()) + .profileImagePath(userEntity.getProfileImagePath()) + .build(); + } + + @Override + public int hashCode() { + return Objects.hash(no, nickname, profileImagePath, status); + } + + @Override + public boolean equals(Object obj) { + if (obj != null && getClass() == obj.getClass()) { + User target = (User)obj; + return no == target.no; + } + return false; + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/user/UserEntity.java b/fillin/src/main/java/com/teamfillin/fillin/domain/user/UserEntity.java index 86a48b9..47a9ff0 100644 --- a/fillin/src/main/java/com/teamfillin/fillin/domain/user/UserEntity.java +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/user/UserEntity.java @@ -9,6 +9,8 @@ import javax.persistence.Id; import javax.persistence.Table; +import org.jetbrains.annotations.NotNull; + @Entity @Table(name = "fillin_user") public class UserEntity { @@ -30,9 +32,34 @@ public class UserEntity { protected UserEntity() { } - public UserEntity(String nickname, String profileImagePath) { + private UserEntity(@NotNull String nickname, @NotNull UserStatus userStatus, @NotNull String profileImagePath) { this.nickname = nickname; + this.status = userStatus; this.profileImagePath = profileImagePath; - this.status = UserStatus.ACTIVE; + } + + private UserEntity(@NotNull String nickname, @NotNull UserStatus userStatus) { + this.nickname = nickname; + this.status = userStatus; + } + + public static UserEntity createActive(@NotNull String nickname) { + return new UserEntity(nickname, UserStatus.ACTIVE); + } + + public Long getNo() { + return no; + } + + public String getNickname() { + return nickname; + } + + public String getProfileImagePath() { + return profileImagePath; + } + + public UserStatus getStatus() { + return status; } } diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/user/UserErrorCode.java b/fillin/src/main/java/com/teamfillin/fillin/domain/user/UserErrorCode.java new file mode 100644 index 0000000..01331fc --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/user/UserErrorCode.java @@ -0,0 +1,31 @@ +package com.teamfillin.fillin.domain.user; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.springframework.http.HttpStatus; + +import com.teamfillin.fillin.FillinErrorCode; + +enum UserErrorCode implements FillinErrorCode { + E404_USER_NOT_FOUND(HttpStatus.NOT_FOUND, "계정을 찾지못했습니다."); + + private final HttpStatus status; + private final String defaultMessage; + + UserErrorCode(HttpStatus status, String defaultMessage) { + this.status = status; + this.defaultMessage = defaultMessage; + } + + @NotNull + @Override + public HttpStatus status() { + return status; + } + + @Nullable + @Override + public String defaultMessage() { + return defaultMessage; + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/user/UserExceptionHandler.java b/fillin/src/main/java/com/teamfillin/fillin/domain/user/UserExceptionHandler.java new file mode 100644 index 0000000..2e7adac --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/user/UserExceptionHandler.java @@ -0,0 +1,9 @@ +package com.teamfillin.fillin.domain.user; + +import com.teamfillin.fillin.FillinException; + +class UserExceptionHandler { + public static FillinException notFound() { + throw FillinException.from(UserErrorCode.E404_USER_NOT_FOUND); + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/user/UserRegister.java b/fillin/src/main/java/com/teamfillin/fillin/domain/user/UserRegister.java new file mode 100644 index 0000000..da8e512 --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/user/UserRegister.java @@ -0,0 +1,33 @@ +package com.teamfillin.fillin.domain.user; + +import org.jetbrains.annotations.NotNull; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; + +@Component +public class UserRegister { + private final NicknameGenerator nicknameGenerator; + private final UserRepository userRepository; + + public UserRegister( + NicknameGenerator nicknameGenerator + , UserRepository userRepository + ) { + this.nicknameGenerator = nicknameGenerator; + this.userRepository = userRepository; + } + + @Transactional(propagation = Propagation.MANDATORY) + public User registerUser() { + final String randomNickname = generateRandomNickname(); + final UserEntity registeredUserEntity = userRepository.save(UserEntity.createActive(randomNickname)); + + return User.from(registeredUserEntity); + } + + @NotNull + private String generateRandomNickname() { + return nicknameGenerator.makeByRule(); + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/user/UserRepository.java b/fillin/src/main/java/com/teamfillin/fillin/domain/user/UserRepository.java new file mode 100644 index 0000000..93f9682 --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/user/UserRepository.java @@ -0,0 +1,9 @@ +package com.teamfillin.fillin.domain.user; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface UserRepository extends JpaRepository { + +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/user/UserRetriever.java b/fillin/src/main/java/com/teamfillin/fillin/domain/user/UserRetriever.java new file mode 100644 index 0000000..1791865 --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/user/UserRetriever.java @@ -0,0 +1,20 @@ +package com.teamfillin.fillin.domain.user; + +import org.jetbrains.annotations.NotNull; +import org.springframework.stereotype.Component; + +@Component +public class UserRetriever { + + private final UserRepository userRepository; + + public UserRetriever(UserRepository userRepository) { + this.userRepository = userRepository; + } + + @NotNull + public User retrieve(long no) { + final UserEntity userEntity = userRepository.findById(no).orElseThrow(UserExceptionHandler::notFound); + return User.from(userEntity); + } +} diff --git a/fillin/src/main/resources/application.yaml b/fillin/src/main/resources/application.yaml index e69de29..5212fc1 100644 --- a/fillin/src/main/resources/application.yaml +++ b/fillin/src/main/resources/application.yaml @@ -0,0 +1,4 @@ +spring: + config: + import: + - secret/token.yaml From 52149022647056412a729c39e7b40a814e32c29e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=A8=B8=ED=99=98=EC=9E=84?= <88091704+daehwan2da@users.noreply.github.com> Date: Sun, 7 Jan 2024 18:33:53 +0900 Subject: [PATCH 11/15] Models (#32) --- .../fillin/domain/film/Company.java | 33 ++++++++++++ .../fillin/domain/film/CompanyEntity.java | 28 ++++++++++ .../teamfillin/fillin/domain/film/Film.java | 36 +++++++++++++ .../fillin/domain/film/FilmEntity.java | 40 ++++++++++++-- .../fillin/domain/film/FilmType.java | 33 ++++++++++++ .../fillin/domain/film/FilmTypeEntity.java | 28 ++++++++++ .../teamfillin/fillin/domain/photo/Photo.java | 53 +++++++++++++++++++ .../fillin/domain/photo/PhotoEntity.java | 17 ++++++ .../fillin/domain/photo/PhotoFilm.java | 29 ++++++++++ .../domain/photo/PhotoLikeReaction.java | 29 ++++++++++ .../fillin/domain/photo/Publisher.java | 41 ++++++++++++++ .../fillin/domain/reaction/LikeReaction.java | 29 ++++++++++ .../ReactionHistoryEntity.java | 18 ++++++- .../fillin/domain/reaction/ReactionId.java | 29 ++++++++++ .../fillin/domain/reaction/TargetType.java | 5 ++ .../domain/reactionHistory/TargetType.java | 5 -- .../com/teamfillin/fillin/resource/Image.java | 27 ++++++++++ .../teamfillin/fillin/resource/ImageUrl.java | 27 ++++++++++ 18 files changed, 497 insertions(+), 10 deletions(-) create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/film/Company.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/film/Film.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/film/FilmType.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/photo/Photo.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/photo/PhotoFilm.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/photo/PhotoLikeReaction.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/photo/Publisher.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/reaction/LikeReaction.java rename fillin/src/main/java/com/teamfillin/fillin/domain/{reactionHistory => reaction}/ReactionHistoryEntity.java (72%) create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/reaction/ReactionId.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/reaction/TargetType.java delete mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/reactionHistory/TargetType.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/resource/Image.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/resource/ImageUrl.java diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/film/Company.java b/fillin/src/main/java/com/teamfillin/fillin/domain/film/Company.java new file mode 100644 index 0000000..144a99a --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/film/Company.java @@ -0,0 +1,33 @@ +package com.teamfillin.fillin.domain.film; + +import java.util.Objects; + +import org.jetbrains.annotations.NotNull; + +public class Company { + private final long no; + private final String name; + + public Company(long no, @NotNull String name) { + this.no = no; + this.name = name; + } + + public static Company from(@NotNull CompanyEntity companyEntity) { + return new Company(companyEntity.getNo(), companyEntity.getName()); + } + + @Override + public int hashCode() { + return Objects.hash(no, name); + } + + @Override + public boolean equals(Object obj) { + if (obj != null && getClass() == obj.getClass()) { + Company target = (Company)obj; + return Objects.equals(no, target.no) && Objects.equals(name, target.name); + } + return false; + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/film/CompanyEntity.java b/fillin/src/main/java/com/teamfillin/fillin/domain/film/CompanyEntity.java index 2e0bcfb..ed5ee1b 100644 --- a/fillin/src/main/java/com/teamfillin/fillin/domain/film/CompanyEntity.java +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/film/CompanyEntity.java @@ -1,5 +1,7 @@ package com.teamfillin.fillin.domain.film; +import java.util.Objects; + import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; @@ -7,6 +9,8 @@ import javax.persistence.Id; import javax.persistence.Table; +import org.jetbrains.annotations.NotNull; + @Entity @Table(name = "company") public class CompanyEntity { @@ -20,4 +24,28 @@ public class CompanyEntity { protected CompanyEntity() { } + + @NotNull + public Long getNo() { + return no; + } + + @NotNull + public String getName() { + return name; + } + + @Override + public int hashCode() { + return Objects.hash(no); + } + + @Override + public boolean equals(Object obj) { + if (obj != null && getClass() == obj.getClass()) { + CompanyEntity target = (CompanyEntity)obj; + return Objects.equals(no, target.no); + } + return false; + } } diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/film/Film.java b/fillin/src/main/java/com/teamfillin/fillin/domain/film/Film.java new file mode 100644 index 0000000..7b2a08d --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/film/Film.java @@ -0,0 +1,36 @@ +package com.teamfillin.fillin.domain.film; + +import java.util.Objects; + +import org.jetbrains.annotations.NotNull; + +public class Film { + private final long no; + private final FilmType type; + private final String name; + private final Company company; + + public Film(long no, @NotNull FilmType type, @NotNull String name, @NotNull Company company) { + this.no = no; + this.type = type; + this.name = name; + this.company = company; + } + + @Override + public int hashCode() { + return Objects.hash(no, type, name, company); + } + + @Override + public boolean equals(Object obj) { + if (obj != null && getClass() == obj.getClass()) { + Film target = (Film)obj; + return Objects.equals(no, target.no) + && Objects.equals(name, target.name) + && Objects.equals(type, target.type) + && Objects.equals(company, target.company); + } + return false; + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/film/FilmEntity.java b/fillin/src/main/java/com/teamfillin/fillin/domain/film/FilmEntity.java index b202196..16284bb 100644 --- a/fillin/src/main/java/com/teamfillin/fillin/domain/film/FilmEntity.java +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/film/FilmEntity.java @@ -1,5 +1,7 @@ package com.teamfillin.fillin.domain.film; +import java.util.Objects; + import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; @@ -7,6 +9,8 @@ import javax.persistence.Id; import javax.persistence.Table; +import org.jetbrains.annotations.NotNull; + @Entity @Table(name = "film") public class FilmEntity { @@ -27,9 +31,37 @@ public class FilmEntity { protected FilmEntity() { } - public FilmEntity(String name, Long typeNo, Long companyNo) { - this.name = name; - this.typeNo = typeNo; - this.companyNo = companyNo; + @NotNull + public Long getNo() { + return no; + } + + @NotNull + public String getName() { + return name; + } + + @NotNull + public Long getTypeNo() { + return typeNo; + } + + @NotNull + public Long getCompanyNo() { + return companyNo; + } + + @Override + public int hashCode() { + return Objects.hash(no); + } + + @Override + public boolean equals(Object obj) { + if (obj != null && getClass() == obj.getClass()) { + FilmEntity target = (FilmEntity)obj; + return Objects.equals(no, target.no); + } + return false; } } diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/film/FilmType.java b/fillin/src/main/java/com/teamfillin/fillin/domain/film/FilmType.java new file mode 100644 index 0000000..704fcae --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/film/FilmType.java @@ -0,0 +1,33 @@ +package com.teamfillin.fillin.domain.film; + +import java.util.Objects; + +import org.jetbrains.annotations.NotNull; + +public class FilmType { + private final long no; + private final String name; + + public FilmType(long no, @NotNull String name) { + this.no = no; + this.name = name; + } + + public static FilmType from(@NotNull FilmTypeEntity filmTypeEntity) { + return new FilmType(filmTypeEntity.getNo(), filmTypeEntity.getName()); + } + + @Override + public int hashCode() { + return Objects.hash(no, name); + } + + @Override + public boolean equals(Object obj) { + if (obj != null && getClass() == obj.getClass()) { + FilmType target = (FilmType)obj; + return Objects.equals(no, target.no) && Objects.equals(name, target.name); + } + return false; + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/film/FilmTypeEntity.java b/fillin/src/main/java/com/teamfillin/fillin/domain/film/FilmTypeEntity.java index 7ef6cc0..4ca5f0e 100644 --- a/fillin/src/main/java/com/teamfillin/fillin/domain/film/FilmTypeEntity.java +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/film/FilmTypeEntity.java @@ -1,5 +1,7 @@ package com.teamfillin.fillin.domain.film; +import java.util.Objects; + import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; @@ -7,6 +9,8 @@ import javax.persistence.Id; import javax.persistence.Table; +import org.jetbrains.annotations.NotNull; + @Entity @Table(name = "film_type") public class FilmTypeEntity { @@ -20,4 +24,28 @@ public class FilmTypeEntity { protected FilmTypeEntity() { } + + @NotNull + public Long getNo() { + return no; + } + + @NotNull + public String getName() { + return name; + } + + @Override + public int hashCode() { + return Objects.hash(no); + } + + @Override + public boolean equals(Object obj) { + if (obj != null && getClass() == obj.getClass()) { + FilmTypeEntity target = (FilmTypeEntity)obj; + return Objects.equals(no, target.no); + } + return false; + } } diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/photo/Photo.java b/fillin/src/main/java/com/teamfillin/fillin/domain/photo/Photo.java new file mode 100644 index 0000000..355ef40 --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/photo/Photo.java @@ -0,0 +1,53 @@ +package com.teamfillin.fillin.domain.photo; + +import java.util.Objects; + +import org.jetbrains.annotations.NotNull; + +import com.teamfillin.fillin.resource.Image; + +public class Photo { + private final long no; + private final Image image; + private final PhotoFilm film; + private final PhotoStatus status; + private final PhotoLikeReaction reaction; + private final Publisher publisher; + + // TODO: studio 작업 머지 후 반영 + // private final Studio studio; + + public Photo( + long no, + @NotNull Image image, + @NotNull PhotoFilm film, + @NotNull PhotoStatus status, + @NotNull PhotoLikeReaction reaction, + @NotNull Publisher publisher + ) { + this.no = no; + this.image = image; + this.film = film; + this.status = status; + this.reaction = reaction; + this.publisher = publisher; + } + + @Override + public int hashCode() { + return Objects.hash(no, image, film, status, reaction, publisher); + } + + @Override + public boolean equals(Object obj) { + if (obj != null && getClass() == obj.getClass()) { + Photo target = (Photo)obj; + return no == target.no && status == target.status + && Objects.equals(image, target.image) + && Objects.equals(film, target.film) + && Objects.equals(reaction, target.reaction) + && Objects.equals(publisher, target.publisher); + } + return false; + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/photo/PhotoEntity.java b/fillin/src/main/java/com/teamfillin/fillin/domain/photo/PhotoEntity.java index 3e013c6..2f40512 100644 --- a/fillin/src/main/java/com/teamfillin/fillin/domain/photo/PhotoEntity.java +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/photo/PhotoEntity.java @@ -1,5 +1,7 @@ package com.teamfillin.fillin.domain.photo; +import java.util.Objects; + import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.EnumType; @@ -10,6 +12,7 @@ import javax.persistence.Table; import com.teamfillin.fillin.domain.common.BaseTimeEntity; +import com.teamfillin.fillin.resource.Image; @Entity @Table(name = "photo") @@ -44,4 +47,18 @@ public PhotoEntity(Long userNo, Long studioNo, Long filmNo, String imagePath) { this.filmNo = filmNo; this.imagePath = imagePath; } + + @Override + public int hashCode() { + return Objects.hash(no); + } + + @Override + public boolean equals(Object obj) { + if (obj != null && getClass() == obj.getClass()) { + PhotoEntity target = (PhotoEntity)obj; + return no == target.no; + } + return false; + } } diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/photo/PhotoFilm.java b/fillin/src/main/java/com/teamfillin/fillin/domain/photo/PhotoFilm.java new file mode 100644 index 0000000..44e8fb0 --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/photo/PhotoFilm.java @@ -0,0 +1,29 @@ +package com.teamfillin.fillin.domain.photo; + +import java.util.Objects; + +import org.jetbrains.annotations.NotNull; + +import com.teamfillin.fillin.domain.film.Film; + +public class PhotoFilm { + private final Film film; + + public PhotoFilm(@NotNull Film film) { + this.film = film; + } + + @Override + public int hashCode() { + return Objects.hash(film); + } + + @Override + public boolean equals(Object obj) { + if (obj != null && getClass() == obj.getClass()) { + PhotoFilm target = (PhotoFilm)obj; + return Objects.equals(film, target.film); + } + return false; + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/photo/PhotoLikeReaction.java b/fillin/src/main/java/com/teamfillin/fillin/domain/photo/PhotoLikeReaction.java new file mode 100644 index 0000000..df72cdf --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/photo/PhotoLikeReaction.java @@ -0,0 +1,29 @@ +package com.teamfillin.fillin.domain.photo; + +import java.util.Objects; + +import org.jetbrains.annotations.NotNull; + +import com.teamfillin.fillin.domain.reaction.LikeReaction; + +public class PhotoLikeReaction { + private final LikeReaction reaction; + + public PhotoLikeReaction(@NotNull LikeReaction reaction) { + this.reaction = reaction; + } + + @Override + public int hashCode() { + return Objects.hash(reaction); + } + + @Override + public boolean equals(Object obj) { + if (obj != null && getClass() == obj.getClass()) { + PhotoLikeReaction target = (PhotoLikeReaction)obj; + return Objects.equals(reaction, target.reaction); + } + return false; + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/photo/Publisher.java b/fillin/src/main/java/com/teamfillin/fillin/domain/photo/Publisher.java new file mode 100644 index 0000000..ddfe1d6 --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/photo/Publisher.java @@ -0,0 +1,41 @@ +package com.teamfillin.fillin.domain.photo; + +import java.util.Objects; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import com.teamfillin.fillin.domain.user.UserEntity; +import com.teamfillin.fillin.resource.Image; + +public class Publisher { + private final long userNo; + private final String nickname; + private final Image profileImage; + + private Publisher(long userNo, @NotNull String nickname, @Nullable Image profileImage) { + this.userNo = userNo; + this.nickname = nickname; + this.profileImage = profileImage; + } + + public static Publisher of(@NotNull UserEntity user, @NotNull Image profileImage) { + return new Publisher(user.getNo(), user.getNickname(), profileImage); + } + + @Override + public int hashCode() { + return Objects.hash(userNo, nickname, profileImage); + } + + @Override + public boolean equals(Object obj) { + if (obj != null && getClass() == obj.getClass()) { + Publisher target = (Publisher)obj; + return userNo == target.userNo + && Objects.equals(nickname, target.nickname) + && Objects.equals(profileImage, target.profileImage); + } + return false; + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/reaction/LikeReaction.java b/fillin/src/main/java/com/teamfillin/fillin/domain/reaction/LikeReaction.java new file mode 100644 index 0000000..70ee60a --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/reaction/LikeReaction.java @@ -0,0 +1,29 @@ +package com.teamfillin.fillin.domain.reaction; + +import java.util.Objects; + +import org.jetbrains.annotations.NotNull; + +public class LikeReaction { + private final ReactionId reactionId; + private final long count; + + public LikeReaction(@NotNull ReactionId reactionId, long count) { + this.reactionId = reactionId; + this.count = count; + } + + @Override + public int hashCode() { + return Objects.hash(reactionId, count); + } + + @Override + public boolean equals(Object obj) { + if (obj != null && getClass() == obj.getClass()) { + LikeReaction target = (LikeReaction)obj; + return Objects.equals(reactionId, target.reactionId) && count == target.count; + } + return false; + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/reactionHistory/ReactionHistoryEntity.java b/fillin/src/main/java/com/teamfillin/fillin/domain/reaction/ReactionHistoryEntity.java similarity index 72% rename from fillin/src/main/java/com/teamfillin/fillin/domain/reactionHistory/ReactionHistoryEntity.java rename to fillin/src/main/java/com/teamfillin/fillin/domain/reaction/ReactionHistoryEntity.java index d6432b1..82d0507 100644 --- a/fillin/src/main/java/com/teamfillin/fillin/domain/reactionHistory/ReactionHistoryEntity.java +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/reaction/ReactionHistoryEntity.java @@ -1,4 +1,6 @@ -package com.teamfillin.fillin.domain.reactionHistory; +package com.teamfillin.fillin.domain.reaction; + +import java.util.Objects; import javax.persistence.Column; import javax.persistence.Entity; @@ -40,4 +42,18 @@ public ReactionHistoryEntity(Long userNo, Long targetNo, TargetType targetType) this.targetNo = targetNo; this.targetType = targetType; } + + @Override + public int hashCode() { + return Objects.hash(no); + } + + @Override + public boolean equals(Object obj) { + if (obj != null && getClass() == obj.getClass()) { + ReactionHistoryEntity target = (ReactionHistoryEntity)obj; + return Objects.equals(no, target.no); + } + return false; + } } diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/reaction/ReactionId.java b/fillin/src/main/java/com/teamfillin/fillin/domain/reaction/ReactionId.java new file mode 100644 index 0000000..6ea36ef --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/reaction/ReactionId.java @@ -0,0 +1,29 @@ +package com.teamfillin.fillin.domain.reaction; + +import java.util.Objects; + +import org.jetbrains.annotations.NotNull; + +public class ReactionId { + private final TargetType type; + private final long targetNo; + + public ReactionId(@NotNull TargetType type, long targetNo) { + this.type = type; + this.targetNo = targetNo; + } + + @Override + public int hashCode() { + return Objects.hash(type, targetNo); + } + + @Override + public boolean equals(Object obj) { + if (obj != null && getClass() == obj.getClass()) { + ReactionId target = (ReactionId)obj; + return type == target.type && targetNo == target.targetNo; + } + return false; + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/reaction/TargetType.java b/fillin/src/main/java/com/teamfillin/fillin/domain/reaction/TargetType.java new file mode 100644 index 0000000..735d612 --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/reaction/TargetType.java @@ -0,0 +1,5 @@ +package com.teamfillin.fillin.domain.reaction; + +public enum TargetType { + PHOTO, STUDIO; +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/reactionHistory/TargetType.java b/fillin/src/main/java/com/teamfillin/fillin/domain/reactionHistory/TargetType.java deleted file mode 100644 index c93003f..0000000 --- a/fillin/src/main/java/com/teamfillin/fillin/domain/reactionHistory/TargetType.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.teamfillin.fillin.domain.reactionHistory; - -public enum TargetType { - PHOTO, STUDIO; -} diff --git a/fillin/src/main/java/com/teamfillin/fillin/resource/Image.java b/fillin/src/main/java/com/teamfillin/fillin/resource/Image.java new file mode 100644 index 0000000..73271cb --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/resource/Image.java @@ -0,0 +1,27 @@ +package com.teamfillin.fillin.resource; + +import java.util.Objects; + +import org.jetbrains.annotations.NotNull; + +public class Image { + private final ImageUrl url; + + public Image(@NotNull ImageUrl url) { + this.url = url; + } + + @Override + public int hashCode() { + return Objects.hash(url); + } + + @Override + public boolean equals(Object obj) { + if (obj != null && getClass() == obj.getClass()) { + Image target = (Image)obj; + return Objects.equals(url, target.url); + } + return false; + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/resource/ImageUrl.java b/fillin/src/main/java/com/teamfillin/fillin/resource/ImageUrl.java new file mode 100644 index 0000000..fe575b2 --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/resource/ImageUrl.java @@ -0,0 +1,27 @@ +package com.teamfillin.fillin.resource; + +import java.util.Objects; + +public class ImageUrl { + private final String domain; + private final String path; + + public ImageUrl(String domain, String path) { + this.domain = domain; + this.path = path; + } + + @Override + public int hashCode() { + return Objects.hash(domain, path); + } + + @Override + public boolean equals(Object obj) { + if (obj != null && getClass() == obj.getClass()) { + ImageUrl target = (ImageUrl)obj; + return Objects.equals(domain, target.domain) && Objects.equals(path, target.path); + } + return false; + } +} From c0cd8e95816de4386885d8c715c0e5960ab1a61b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=A8=B8=ED=99=98=EC=9E=84?= <88091704+daehwan2da@users.noreply.github.com> Date: Sun, 7 Jan 2024 18:34:05 +0900 Subject: [PATCH 12/15] =?UTF-8?q?http=20status=20=EB=B0=98=EC=98=81?= =?UTF-8?q?=EB=90=98=EB=8F=84=EB=A1=9D=20ResponseEntity=20=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9=20(#33)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../fillin/api/FillinApiResponse.java | 78 ++++++++++++++----- .../fillin/api/FillinExceptionHandler.java | 3 +- .../fillin/api/account/AccountApi.java | 3 +- .../api/healthcheck/HealthCheckApi.java | 3 +- 4 files changed, 64 insertions(+), 23 deletions(-) diff --git a/fillin/src/main/java/com/teamfillin/fillin/api/FillinApiResponse.java b/fillin/src/main/java/com/teamfillin/fillin/api/FillinApiResponse.java index d6f50aa..08adfed 100644 --- a/fillin/src/main/java/com/teamfillin/fillin/api/FillinApiResponse.java +++ b/fillin/src/main/java/com/teamfillin/fillin/api/FillinApiResponse.java @@ -3,6 +3,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; import org.springframework.util.StringUtils; import com.fasterxml.jackson.annotation.JsonInclude; @@ -34,44 +35,81 @@ private FillinApiResponse(int status, boolean success, @Nullable Object data, @N this.message = message; } - public static FillinApiResponse failure(@NotNull FillinErrorCode errorCode) { - return StringUtils.hasText(errorCode.defaultMessage()) - ? failure(errorCode.status(), errorCode.defaultMessage()) - : new FillinApiResponse(errorCode.status().value(), false); + public static ResponseEntity success() { + return success(HttpStatus.OK); } - public static FillinApiResponse failure(@NotNull HttpStatus httpStatus, @NotNull String message) { - return new FillinApiResponse(httpStatus.value(), false, null, message); + public static ResponseEntity success(@NotNull HttpStatus status) { + return ResponseEntity + .status(status) + .body(successBody(status)); } - public static FillinApiResponse success() { - return FillinApiResponse.success(HttpStatus.OK); + public static ResponseEntity success(@NotNull Object data) { + return success(HttpStatus.OK, data); } - public static FillinApiResponse success(@NotNull HttpStatus httpStatus) { - return new FillinApiResponse(httpStatus.value(), true); + public static ResponseEntity success( + @NotNull HttpStatus httpStatus, + @NotNull Object data, + @Nullable String message + ) { + return ResponseEntity + .status(httpStatus) + .body(successBody(httpStatus, data, message)); + } + + public static ResponseEntity success(@NotNull HttpStatus httpStatus, @NotNull Object data) { + return ResponseEntity + .status(httpStatus) + .body(successBody(httpStatus, data)); } - public static FillinApiResponse success(@NotNull Object data) { - return FillinApiResponse.success(HttpStatus.OK, data); + public static ResponseEntity success(@NotNull HttpStatus httpStatus, @Nullable String message) { + return ResponseEntity + .status(httpStatus) + .body(successBody(httpStatus, null, message)); } - public static FillinApiResponse success(@NotNull HttpStatus httpStatus, @NotNull Object data) { - return new FillinApiResponse(httpStatus.value(), true, data, null); + public static ResponseEntity failure(@NotNull FillinErrorCode errorCode) { + return ResponseEntity + .status(errorCode.status()) + .body(failureBody(errorCode)); } - public static FillinApiResponse success( + public static ResponseEntity failure(@NotNull FillinErrorCode errorCode, + @NotNull String message) { + return ResponseEntity + .status(errorCode.status()) + .body(failureBody(errorCode.status(), message)); + } + + private static FillinApiResponse failureBody(@NotNull FillinErrorCode errorCode) { + return StringUtils.hasText(errorCode.defaultMessage()) + ? failureBody(errorCode.status(), errorCode.defaultMessage()) + : new FillinApiResponse(errorCode.status().value(), false); + } + + private static FillinApiResponse failureBody(@NotNull HttpStatus httpStatus, @NotNull String message) { + return new FillinApiResponse(httpStatus.value(), false, null, message); + } + + private static FillinApiResponse successBody(@NotNull HttpStatus httpStatus) { + return new FillinApiResponse(httpStatus.value(), true); + } + + private static FillinApiResponse successBody(@NotNull HttpStatus httpStatus, @NotNull Object data) { + return successBody(httpStatus, data, null); + } + + private static FillinApiResponse successBody( @NotNull HttpStatus httpStatus, - @NotNull Object data, + @Nullable Object data, @Nullable String message ) { return new FillinApiResponse(httpStatus.value(), true, data, message); } - public static FillinApiResponse success(@NotNull HttpStatus httpStatus, @Nullable String message) { - return new FillinApiResponse(httpStatus.value(), true, null, message); - } - public int getStatus() { return status; } diff --git a/fillin/src/main/java/com/teamfillin/fillin/api/FillinExceptionHandler.java b/fillin/src/main/java/com/teamfillin/fillin/api/FillinExceptionHandler.java index 178009a..05d37d2 100644 --- a/fillin/src/main/java/com/teamfillin/fillin/api/FillinExceptionHandler.java +++ b/fillin/src/main/java/com/teamfillin/fillin/api/FillinExceptionHandler.java @@ -1,5 +1,6 @@ package com.teamfillin.fillin.api; +import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; @@ -11,7 +12,7 @@ @RestControllerAdvice public class FillinExceptionHandler { @ExceptionHandler(FillinException.class) - FillinApiResponse handleFillinException(FillinException e) { + ResponseEntity handleFillinException(FillinException e) { return FillinApiResponse.failure(e.getErrorCode()); } } diff --git a/fillin/src/main/java/com/teamfillin/fillin/api/account/AccountApi.java b/fillin/src/main/java/com/teamfillin/fillin/api/account/AccountApi.java index 83aa31f..01375e1 100644 --- a/fillin/src/main/java/com/teamfillin/fillin/api/account/AccountApi.java +++ b/fillin/src/main/java/com/teamfillin/fillin/api/account/AccountApi.java @@ -1,6 +1,7 @@ package com.teamfillin.fillin.api.account; import org.springframework.http.HttpStatus; +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.RestController; @@ -26,7 +27,7 @@ public AccountApi( } @PostMapping("/auth") - public FillinApiResponse loginOrJoin( + public ResponseEntity loginOrJoin( @RequestBody AccountRequest accountRequest ) { final SocialType socialType = accountRequest.getSocial(); diff --git a/fillin/src/main/java/com/teamfillin/fillin/api/healthcheck/HealthCheckApi.java b/fillin/src/main/java/com/teamfillin/fillin/api/healthcheck/HealthCheckApi.java index 14dd4bc..1746fcc 100644 --- a/fillin/src/main/java/com/teamfillin/fillin/api/healthcheck/HealthCheckApi.java +++ b/fillin/src/main/java/com/teamfillin/fillin/api/healthcheck/HealthCheckApi.java @@ -1,5 +1,6 @@ package com.teamfillin.fillin.api.healthcheck; +import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @@ -15,7 +16,7 @@ public HealthCheckApi(HealthChecker healthChecker) { } @GetMapping("/health/readiness") - public FillinApiResponse readiness() { + public ResponseEntity readiness() { healthChecker.succeed(); return FillinApiResponse.success(); } From 1642e1b789ca0d2093b845487c4c3e1b9227b231 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=B0=AC=EC=9A=B0?= <58971262+oownahcohc@users.noreply.github.com> Date: Tue, 16 Jan 2024 02:31:07 +0900 Subject: [PATCH 13/15] =?UTF-8?q?studio=20=EC=A1=B0=ED=9A=8C=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20=EA=B8=B0=EB=8A=A5=20=EA=B0=9C=EB=B0=9C=20(#28)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Entity postfix 추가 * entity 에 table name 적용 * studio 전체 조회 개발 * studio 상세 조회 api 추가 * price 관련 view 로직 처리를 위한 PriceView * studio 상세 조회를 위한 price 로직 * RunningTime 관련 view 로직 처리를 위한 RunningTimeView * runnig time 에 따른 운영 상태(operation status) * EVERY_DAY, WEEK_DAY, WEEK_END 추가 * studio 상세 조회를 위한 runnigTime 로직 * korea time zone 설정 * studio 상세 조회 예외 핸들링 * Studio 관련 view 로직 처리를 위한 StudioView * studio 상세 조회 로직 * StudioStatus 필요 여부 * 도메인 엔티티가 영속성 엔티티를 모르도록 매핑 변경 - PriceEntity -> toPrice 를 통해 Price 로 매핑 - RunningTimeEntity -> toRunningTime 을 통해 RunningTime 으로 매핑 * 도메인 엔티티가 영속성 엔티티를 모르도록 매핑 변경 - StudioEntity -> toStudio 를 통해 Studio 로 매핑 * retrieveAllStudio 반환값 unmodifiable list 로 변경 * 사용자 입력 검색어 관리 객체 * 사용자 검색어를 비즈니스 검색어로 변경 관리하는 객체 * studio 검색 로직 * studio 검색 api 개발 * 리뷰 반영 * unit test 작성 * rebase 충돌 해결 --------- Co-authored-by: 머환임 --- .../api/studio/InputKeywordRequest.java | 23 ++++ .../api/studio/StudioDetailResponse.java | 99 ++++++++++++++++ .../api/studio/StudioRetrieveAllResponse.java | 18 +++ .../fillin/api/studio/StudioRetrieveApi.java | 57 +++++++++ .../fillin/config/TimeConfiguration.java | 18 +++ .../teamfillin/fillin/domain/price/Price.java | 39 ++++++ .../fillin/domain/price/PriceEntity.java | 16 +++ .../fillin/domain/price/PriceRepository.java | 12 ++ .../fillin/domain/price/PriceRetriever.java | 22 ++++ .../fillin/domain/price/view/PriceView.java | 45 +++++++ .../fillin/domain/runningTime/DayOfWeek.java | 23 +++- .../domain/runningTime/OperationStatus.java | 28 +++++ .../domain/runningTime/RunningTime.java | 41 +++++++ .../domain/runningTime/RunningTimeEntity.java | 34 ++++++ .../runningTime/RunningTimeRepository.java | 12 ++ .../runningTime/RunningTimeRetriever.java | 22 ++++ .../BreakTimeRunningTimeInfoFormatter.java | 19 +++ .../view/ClosedRunningTimeInfoFormatter.java | 15 +++ .../OnlyOnlineRunningTimeInfoFormatter.java | 15 +++ .../view/OpenRunningTimeInfoFormatter.java | 19 +++ .../view/RunningTimeInfoFormatter.java | 17 +++ .../view/RunningTimeInfoFormatterFactory.java | 18 +++ .../runningTime/view/RunningTimeView.java | 47 ++++++++ .../domain/studio/InputKeywordCommand.java | 48 ++++++++ .../fillin/domain/studio/KeywordManager.java | 111 ++++++++++++++++++ .../fillin/domain/studio/Studio.java | 101 ++++++++++++++++ .../domain/studio/StudioDetailResult.java | 29 +++++ .../studio/StudioDetailRetrieveService.java | 38 ++++++ .../fillin/domain/studio/StudioEntity.java | 55 ++++++++- .../fillin/domain/studio/StudioErrorCode.java | 33 ++++++ .../domain/studio/StudioExceptionHandler.java | 13 ++ .../fillin/domain/studio/StudioLocation.java | 42 +++++++ .../domain/studio/StudioLocationResult.java | 30 +++++ .../domain/studio/StudioRepository.java | 17 +++ .../fillin/domain/studio/StudioRetriever.java | 31 +++++ .../domain/studio/StudioRetrieverService.java | 24 ++++ .../domain/studio/StudioRunningStatus.java | 19 +++ .../domain/studio/StudioSearchResult.java | 30 +++++ .../domain/studio/StudioSearchService.java | 24 ++++ .../fillin/domain/studio/StudioSearcher.java | 24 ++++ .../view/RunningTimeInfoFormatterTest.java | 41 +++++++ .../studio/InputKeywordCommandTest.java | 50 ++++++++ .../domain/studio/KeywordManagerTest.java | 94 +++++++++++++++ 43 files changed, 1511 insertions(+), 2 deletions(-) create mode 100644 fillin/src/main/java/com/teamfillin/fillin/api/studio/InputKeywordRequest.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/api/studio/StudioDetailResponse.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/api/studio/StudioRetrieveAllResponse.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/api/studio/StudioRetrieveApi.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/config/TimeConfiguration.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/price/Price.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/price/PriceRepository.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/price/PriceRetriever.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/price/view/PriceView.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/runningTime/OperationStatus.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/runningTime/RunningTime.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/runningTime/RunningTimeRepository.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/runningTime/RunningTimeRetriever.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/runningTime/view/BreakTimeRunningTimeInfoFormatter.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/runningTime/view/ClosedRunningTimeInfoFormatter.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/runningTime/view/OnlyOnlineRunningTimeInfoFormatter.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/runningTime/view/OpenRunningTimeInfoFormatter.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/runningTime/view/RunningTimeInfoFormatter.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/runningTime/view/RunningTimeInfoFormatterFactory.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/runningTime/view/RunningTimeView.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/studio/InputKeywordCommand.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/studio/KeywordManager.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/studio/Studio.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/studio/StudioDetailResult.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/studio/StudioDetailRetrieveService.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/studio/StudioErrorCode.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/studio/StudioExceptionHandler.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/studio/StudioLocation.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/studio/StudioLocationResult.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/studio/StudioRepository.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/studio/StudioRetriever.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/studio/StudioRetrieverService.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/studio/StudioRunningStatus.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/studio/StudioSearchResult.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/studio/StudioSearchService.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/studio/StudioSearcher.java create mode 100644 fillin/src/test/java/com/teamfillin/fillin/domain/runningTime/view/RunningTimeInfoFormatterTest.java create mode 100644 fillin/src/test/java/com/teamfillin/fillin/domain/studio/InputKeywordCommandTest.java create mode 100644 fillin/src/test/java/com/teamfillin/fillin/domain/studio/KeywordManagerTest.java diff --git a/fillin/src/main/java/com/teamfillin/fillin/api/studio/InputKeywordRequest.java b/fillin/src/main/java/com/teamfillin/fillin/api/studio/InputKeywordRequest.java new file mode 100644 index 0000000..cee8399 --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/api/studio/InputKeywordRequest.java @@ -0,0 +1,23 @@ +package com.teamfillin.fillin.api.studio; + +import com.teamfillin.fillin.domain.studio.InputKeywordCommand; + +public class InputKeywordRequest { + + private String keyword; + + private InputKeywordRequest() { + } + + public InputKeywordRequest(String keyword) { + this.keyword = keyword; + } + + public String getKeyword() { + return keyword; + } + + public InputKeywordCommand toInputKeywordCommand() { + return InputKeywordCommand.from(keyword); + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/api/studio/StudioDetailResponse.java b/fillin/src/main/java/com/teamfillin/fillin/api/studio/StudioDetailResponse.java new file mode 100644 index 0000000..2b60a39 --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/api/studio/StudioDetailResponse.java @@ -0,0 +1,99 @@ +package com.teamfillin.fillin.api.studio; + +import com.teamfillin.fillin.domain.price.view.PriceView; +import com.teamfillin.fillin.domain.runningTime.view.RunningTimeView; +import com.teamfillin.fillin.domain.studio.Studio; +import com.teamfillin.fillin.domain.studio.StudioDetailResult; + +public class StudioDetailResponse { + + private final long id; + private final String name; + private final String address; + private final String price; + private final String time; + private final String tel; + private final double latitude; + private final double longitude; + private final String etc; + private final String site; + private final String runningStatus; + + private StudioDetailResponse(long id, String name, String address, String price, String time, String tel, + double latitude, double longitude, String etc, String site, String runningStatus) { + this.id = id; + this.name = name; + this.address = address; + this.price = price; + this.time = time; + this.tel = tel; + this.latitude = latitude; + this.longitude = longitude; + this.etc = etc; + this.site = site; + this.runningStatus = runningStatus; + } + + public static StudioDetailResponse from(StudioDetailResult detailResult) { + Studio studio = detailResult.getStudio(); + PriceView priceView = detailResult.getPriceView(); + RunningTimeView runningTimeView = detailResult.getRunningTimeView(); + return new StudioDetailResponse( + studio.getNo(), + studio.getName(), + studio.getAddress(), + priceView.getPriceInfo(), + runningTimeView.getRunningTimeInfo(), + studio.getTel(), + studio.getLatitude(), + studio.getLongitude(), + studio.getEtc(), + studio.getSite(), + studio.getRunningStatusValue() + ); + } + + public long getId() { + return id; + } + + public String getName() { + return name; + } + + public String getAddress() { + return address; + } + + public String getPrice() { + return price; + } + + public String getTime() { + return time; + } + + public String getTel() { + return tel; + } + + public double getLatitude() { + return latitude; + } + + public double getLongitude() { + return longitude; + } + + public String getEtc() { + return etc; + } + + public String getSite() { + return site; + } + + public String getRunningStatus() { + return runningStatus; + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/api/studio/StudioRetrieveAllResponse.java b/fillin/src/main/java/com/teamfillin/fillin/api/studio/StudioRetrieveAllResponse.java new file mode 100644 index 0000000..2d8ada2 --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/api/studio/StudioRetrieveAllResponse.java @@ -0,0 +1,18 @@ +package com.teamfillin.fillin.api.studio; + +import java.util.List; + +import com.teamfillin.fillin.domain.studio.StudioLocationResult; + +public class StudioRetrieveAllResponse { + + private final List studios; + + public StudioRetrieveAllResponse(List studios) { + this.studios = studios; + } + + public List getStudios() { + return studios; + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/api/studio/StudioRetrieveApi.java b/fillin/src/main/java/com/teamfillin/fillin/api/studio/StudioRetrieveApi.java new file mode 100644 index 0000000..4f8423b --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/api/studio/StudioRetrieveApi.java @@ -0,0 +1,57 @@ +package com.teamfillin.fillin.api.studio; + +import java.util.List; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import com.teamfillin.fillin.api.FillinApiResponse; +import com.teamfillin.fillin.domain.studio.StudioDetailResult; +import com.teamfillin.fillin.domain.studio.StudioDetailRetrieveService; +import com.teamfillin.fillin.domain.studio.StudioLocationResult; +import com.teamfillin.fillin.domain.studio.StudioRetrieverService; +import com.teamfillin.fillin.domain.studio.StudioSearchService; + +@RestController +@RequestMapping("/studio") +public class StudioRetrieveApi { + + private final StudioRetrieverService studioRetrieverService; + private final StudioDetailRetrieveService studioDetailRetrieveService; + private final StudioSearchService studioSearchService; + + public StudioRetrieveApi(StudioRetrieverService studioRetrieverService, + StudioDetailRetrieveService studioDetailRetrieveService, + StudioSearchService studioSearchService) { + this.studioRetrieverService = studioRetrieverService; + this.studioDetailRetrieveService = studioDetailRetrieveService; + this.studioSearchService = studioSearchService; + } + + /** + * studio 데이터가 많아지게 되면 클라이언트로 부터 현재 위치 값을 받아 + * 구역 내 studio search 하는 api 로 변경 + */ + @GetMapping("/maps") + public ResponseEntity retrieveAllStudio() { + List studioLocationResults = studioRetrieverService.retrieveAllStudio(); + return FillinApiResponse.success(HttpStatus.OK, new StudioRetrieveAllResponse(studioLocationResults)); + } + + @GetMapping("/{studioNo}") + public ResponseEntity retrieveStudioDetail(@PathVariable long studioNo) { + StudioDetailResult detailResult = studioDetailRetrieveService.retrieveDetail(studioNo); + StudioDetailResponse studioDetailResponse = StudioDetailResponse.from(detailResult); + return FillinApiResponse.success(HttpStatus.OK, studioDetailResponse); + } + + @GetMapping("/search") + public ResponseEntity searchStudio(@RequestParam InputKeywordRequest inputKeywordRequest) { + return FillinApiResponse.success(HttpStatus.OK, studioSearchService.searchStudio(inputKeywordRequest.toInputKeywordCommand())); + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/config/TimeConfiguration.java b/fillin/src/main/java/com/teamfillin/fillin/config/TimeConfiguration.java new file mode 100644 index 0000000..d143f71 --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/config/TimeConfiguration.java @@ -0,0 +1,18 @@ +package com.teamfillin.fillin.config; + +import java.time.Clock; +import java.time.ZoneId; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class TimeConfiguration { + + private static final ZoneId KOREA_TIME_ZONE = ZoneId.of("Asia/Seoul"); + + @Bean + public Clock koreaTimeClock() { + return Clock.system(KOREA_TIME_ZONE); + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/price/Price.java b/fillin/src/main/java/com/teamfillin/fillin/domain/price/Price.java new file mode 100644 index 0000000..f81d52a --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/price/Price.java @@ -0,0 +1,39 @@ +package com.teamfillin.fillin.domain.price; + +import java.util.Objects; + +public class Price { + + private final long studioNo; + private final String name; + private final int amount; + + public Price(long studioNo, String name, int amount) { + this.studioNo = studioNo; + this.name = name; + this.amount = amount; + } + + public String getName() { + return name; + } + + public int getAmount() { + return amount; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + Price price = (Price)o; + return studioNo == price.studioNo && amount == price.amount && Objects.equals(name, price.name); + } + + @Override + public int hashCode() { + return Objects.hash(studioNo, name, amount); + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/price/PriceEntity.java b/fillin/src/main/java/com/teamfillin/fillin/domain/price/PriceEntity.java index 413dfa4..00962d4 100644 --- a/fillin/src/main/java/com/teamfillin/fillin/domain/price/PriceEntity.java +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/price/PriceEntity.java @@ -25,4 +25,20 @@ public class PriceEntity { protected PriceEntity() { } + + public Price toPrice() { + return new Price(studioNo, name, amount); + } + + public Long getStudioNo() { + return studioNo; + } + + public String getName() { + return name; + } + + public Integer getAmount() { + return amount; + } } diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/price/PriceRepository.java b/fillin/src/main/java/com/teamfillin/fillin/domain/price/PriceRepository.java new file mode 100644 index 0000000..8adbcc3 --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/price/PriceRepository.java @@ -0,0 +1,12 @@ +package com.teamfillin.fillin.domain.price; + +import java.util.List; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface PriceRepository extends JpaRepository { + + List findByStudioNo(Long studioNo); +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/price/PriceRetriever.java b/fillin/src/main/java/com/teamfillin/fillin/domain/price/PriceRetriever.java new file mode 100644 index 0000000..b55bb5e --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/price/PriceRetriever.java @@ -0,0 +1,22 @@ +package com.teamfillin.fillin.domain.price; + +import java.util.List; + +import org.springframework.stereotype.Component; + +@Component +public class PriceRetriever { + + private final PriceRepository priceRepository; + + public PriceRetriever(PriceRepository priceRepository) { + this.priceRepository = priceRepository; + } + + public List retrieveByStudioNo(long studioNo) { + return priceRepository.findByStudioNo(studioNo) + .stream() + .map(PriceEntity::toPrice) + .toList(); + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/price/view/PriceView.java b/fillin/src/main/java/com/teamfillin/fillin/domain/price/view/PriceView.java new file mode 100644 index 0000000..a02bc71 --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/price/view/PriceView.java @@ -0,0 +1,45 @@ +package com.teamfillin.fillin.domain.price.view; + +import java.util.List; +import java.util.StringJoiner; + +import javax.annotation.Nullable; + +import com.teamfillin.fillin.domain.price.Price; + +public class PriceView { + + private static final String SPACE = " "; + private static final String LINE_BREAK = "\n"; + + private final List values; + + public PriceView(List values) { + this.values = values; + } + + /** + * 기존 price 가 TEXT 로 들어가 있었기 때문에 응답 형식이 너무 자유로움. + * 어느정도 틀을 잡아둘 필요 있음 + * @return "컬러 4000\n 흑백 8000" or "7500" 등 + */ + @Nullable + public String getPriceInfo() { + if (values.isEmpty()) { + return null; + } + if (values.size() == 1) { + Price price = values.get(0); + if (price.getName() == null) { + return String.valueOf(price.getAmount()); + } + return price.getName() + SPACE + price.getAmount(); + } + StringJoiner sj = new StringJoiner(LINE_BREAK); + for (Price value : values) { + String priceInfo = value.getName() + SPACE + value.getAmount(); + sj.add(priceInfo); + } + return sj.toString(); + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/runningTime/DayOfWeek.java b/fillin/src/main/java/com/teamfillin/fillin/domain/runningTime/DayOfWeek.java index c93c25f..8eb1ef4 100644 --- a/fillin/src/main/java/com/teamfillin/fillin/domain/runningTime/DayOfWeek.java +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/runningTime/DayOfWeek.java @@ -1,5 +1,26 @@ package com.teamfillin.fillin.domain.runningTime; public enum DayOfWeek { - MON, TUE, WED, THR, FRI, SAT, SUN; + + MON("월요일"), + TUE("화요일"), + WED("수요일"), + THR("목요일"), + FRI("금요일"), + SAT("토요일"), + SUN("일요일"), + EVERY_DAY("매일"), + WEEK_DAY("평일"), + WEEK_END("주말"), + ; + + private final String value; + + DayOfWeek(String value) { + this.value = value; + } + + public String getValue() { + return value; + } } diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/runningTime/OperationStatus.java b/fillin/src/main/java/com/teamfillin/fillin/domain/runningTime/OperationStatus.java new file mode 100644 index 0000000..0afbb8f --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/runningTime/OperationStatus.java @@ -0,0 +1,28 @@ +package com.teamfillin.fillin.domain.runningTime; + +public enum OperationStatus { + + OPEN("운영"), + CLOSED("휴무"), + BREAK_TIME("브레이크 타임"), + ONLY_ONLINE("온라인만 운영"), + ; + + private final String value; + + OperationStatus(String value) { + this.value = value; + } + + public boolean isBreakTime() { + return this == BREAK_TIME; + } + + public boolean isClosed() { + return this == CLOSED; + } + + public String getValue() { + return value; + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/runningTime/RunningTime.java b/fillin/src/main/java/com/teamfillin/fillin/domain/runningTime/RunningTime.java new file mode 100644 index 0000000..ae654f1 --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/runningTime/RunningTime.java @@ -0,0 +1,41 @@ +package com.teamfillin.fillin.domain.runningTime; + +import java.time.LocalTime; + +public class RunningTime { + + private final long studioNo; + private final DayOfWeek dayOfWeek; + private final OperationStatus operationStatus; + private final LocalTime startAt; + private final LocalTime endAt; + + public RunningTime(long studioNo, DayOfWeek dayOfWeek, OperationStatus operationStatus, LocalTime startAt, + LocalTime endAt) { + this.studioNo = studioNo; + this.dayOfWeek = dayOfWeek; + this.operationStatus = operationStatus; + this.startAt = startAt; + this.endAt = endAt; + } + + public boolean isBetween(LocalTime now) { + return startAt.isBefore(now) && endAt.isAfter(now); + } + + public String getDayOfWeek() { + return dayOfWeek.getValue(); + } + + public OperationStatus getOperationStatus() { + return operationStatus; + } + + public LocalTime getStartAt() { + return startAt; + } + + public LocalTime getEndAt() { + return endAt; + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/runningTime/RunningTimeEntity.java b/fillin/src/main/java/com/teamfillin/fillin/domain/runningTime/RunningTimeEntity.java index 46dda9b..69bdbf5 100644 --- a/fillin/src/main/java/com/teamfillin/fillin/domain/runningTime/RunningTimeEntity.java +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/runningTime/RunningTimeEntity.java @@ -26,10 +26,44 @@ public class RunningTimeEntity { @Column(nullable = false) private DayOfWeek dayOfWeek; + @Enumerated(EnumType.STRING) + @Column(nullable = false) + private OperationStatus operationStatus; + private LocalDateTime startAt; private LocalDateTime endAt; protected RunningTimeEntity() { } + + public RunningTime toRunningTime() { + return new RunningTime( + studioNo, + dayOfWeek, + operationStatus, + startAt.toLocalTime(), + endAt.toLocalTime() + ); + } + + public Long getStudioNo() { + return studioNo; + } + + public DayOfWeek getDayOfWeek() { + return dayOfWeek; + } + + public OperationStatus getOperationStatus() { + return operationStatus; + } + + public LocalDateTime getStartAt() { + return startAt; + } + + public LocalDateTime getEndAt() { + return endAt; + } } diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/runningTime/RunningTimeRepository.java b/fillin/src/main/java/com/teamfillin/fillin/domain/runningTime/RunningTimeRepository.java new file mode 100644 index 0000000..97383c0 --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/runningTime/RunningTimeRepository.java @@ -0,0 +1,12 @@ +package com.teamfillin.fillin.domain.runningTime; + +import java.util.List; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface RunningTimeRepository extends JpaRepository { + + List findByStudioNo(Long studioNo); +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/runningTime/RunningTimeRetriever.java b/fillin/src/main/java/com/teamfillin/fillin/domain/runningTime/RunningTimeRetriever.java new file mode 100644 index 0000000..3decd81 --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/runningTime/RunningTimeRetriever.java @@ -0,0 +1,22 @@ +package com.teamfillin.fillin.domain.runningTime; + +import java.util.List; + +import org.springframework.stereotype.Component; + +@Component +public class RunningTimeRetriever { + + private final RunningTimeRepository runningTimeRepository; + + public RunningTimeRetriever(RunningTimeRepository runningTimeRepository) { + this.runningTimeRepository = runningTimeRepository; + } + + public List retrieveByStudioNo(long studioNo) { + return runningTimeRepository.findByStudioNo(studioNo) + .stream() + .map(RunningTimeEntity::toRunningTime) + .toList(); + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/runningTime/view/BreakTimeRunningTimeInfoFormatter.java b/fillin/src/main/java/com/teamfillin/fillin/domain/runningTime/view/BreakTimeRunningTimeInfoFormatter.java new file mode 100644 index 0000000..8cdec20 --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/runningTime/view/BreakTimeRunningTimeInfoFormatter.java @@ -0,0 +1,19 @@ +package com.teamfillin.fillin.domain.runningTime.view; + +import com.teamfillin.fillin.domain.runningTime.RunningTime; + +class BreakTimeRunningTimeInfoFormatter extends RunningTimeInfoFormatter { + + BreakTimeRunningTimeInfoFormatter(RunningTime runningTime) { + super(runningTime); + } + + @Override + public String format() { + return runningTime.getOperationStatus().getValue() + + SPACE + + runningTime.getStartAt() + + DASH + + runningTime.getEndAt(); + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/runningTime/view/ClosedRunningTimeInfoFormatter.java b/fillin/src/main/java/com/teamfillin/fillin/domain/runningTime/view/ClosedRunningTimeInfoFormatter.java new file mode 100644 index 0000000..770ca24 --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/runningTime/view/ClosedRunningTimeInfoFormatter.java @@ -0,0 +1,15 @@ +package com.teamfillin.fillin.domain.runningTime.view; + +import com.teamfillin.fillin.domain.runningTime.RunningTime; + +class ClosedRunningTimeInfoFormatter extends RunningTimeInfoFormatter { + + ClosedRunningTimeInfoFormatter(RunningTime runningTime) { + super(runningTime); + } + + @Override + public String format() { + return runningTime.getDayOfWeek() + SPACE + runningTime.getOperationStatus().getValue(); + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/runningTime/view/OnlyOnlineRunningTimeInfoFormatter.java b/fillin/src/main/java/com/teamfillin/fillin/domain/runningTime/view/OnlyOnlineRunningTimeInfoFormatter.java new file mode 100644 index 0000000..5244a73 --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/runningTime/view/OnlyOnlineRunningTimeInfoFormatter.java @@ -0,0 +1,15 @@ +package com.teamfillin.fillin.domain.runningTime.view; + +import com.teamfillin.fillin.domain.runningTime.RunningTime; + +class OnlyOnlineRunningTimeInfoFormatter extends RunningTimeInfoFormatter { + + OnlyOnlineRunningTimeInfoFormatter(RunningTime runningTime) { + super(runningTime); + } + + @Override + public String format() { + return runningTime.getOperationStatus().getValue(); + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/runningTime/view/OpenRunningTimeInfoFormatter.java b/fillin/src/main/java/com/teamfillin/fillin/domain/runningTime/view/OpenRunningTimeInfoFormatter.java new file mode 100644 index 0000000..2ec47c7 --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/runningTime/view/OpenRunningTimeInfoFormatter.java @@ -0,0 +1,19 @@ +package com.teamfillin.fillin.domain.runningTime.view; + +import com.teamfillin.fillin.domain.runningTime.RunningTime; + +class OpenRunningTimeInfoFormatter extends RunningTimeInfoFormatter { + + OpenRunningTimeInfoFormatter(RunningTime runningTime) { + super(runningTime); + } + + @Override + public String format() { + return runningTime.getDayOfWeek() + + SPACE + + runningTime.getStartAt() + + DASH + + runningTime.getEndAt(); + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/runningTime/view/RunningTimeInfoFormatter.java b/fillin/src/main/java/com/teamfillin/fillin/domain/runningTime/view/RunningTimeInfoFormatter.java new file mode 100644 index 0000000..30cdfcc --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/runningTime/view/RunningTimeInfoFormatter.java @@ -0,0 +1,17 @@ +package com.teamfillin.fillin.domain.runningTime.view; + +import com.teamfillin.fillin.domain.runningTime.RunningTime; + +abstract class RunningTimeInfoFormatter { + + protected static final String SPACE = " "; + protected static final String DASH = " - "; + + protected final RunningTime runningTime; + + protected RunningTimeInfoFormatter(RunningTime runningTime) { + this.runningTime = runningTime; + } + + public abstract String format(); +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/runningTime/view/RunningTimeInfoFormatterFactory.java b/fillin/src/main/java/com/teamfillin/fillin/domain/runningTime/view/RunningTimeInfoFormatterFactory.java new file mode 100644 index 0000000..abbf7a3 --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/runningTime/view/RunningTimeInfoFormatterFactory.java @@ -0,0 +1,18 @@ +package com.teamfillin.fillin.domain.runningTime.view; + +import com.teamfillin.fillin.domain.runningTime.RunningTime; + +class RunningTimeInfoFormatterFactory { + + private RunningTimeInfoFormatterFactory() { + } + + public static RunningTimeInfoFormatter getBy(RunningTime runningTime) { + return switch (runningTime.getOperationStatus()) { + case OPEN -> new OpenRunningTimeInfoFormatter(runningTime); + case CLOSED -> new ClosedRunningTimeInfoFormatter(runningTime); + case BREAK_TIME -> new BreakTimeRunningTimeInfoFormatter(runningTime); + case ONLY_ONLINE -> new OnlyOnlineRunningTimeInfoFormatter(runningTime); + }; + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/runningTime/view/RunningTimeView.java b/fillin/src/main/java/com/teamfillin/fillin/domain/runningTime/view/RunningTimeView.java new file mode 100644 index 0000000..786c9ea --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/runningTime/view/RunningTimeView.java @@ -0,0 +1,47 @@ +package com.teamfillin.fillin.domain.runningTime.view; + +import java.time.LocalTime; +import java.util.List; +import java.util.StringJoiner; + +import javax.annotation.Nullable; + +import com.teamfillin.fillin.domain.runningTime.OperationStatus; +import com.teamfillin.fillin.domain.runningTime.RunningTime; + +public class RunningTimeView { + + private static final String LINE_BREAK = "\n"; + + private final List values; + + public RunningTimeView(List values) { + this.values = values; + } + + /** + * 기존 time 이 TEXT 로 들어가 있었기 때문에 응답 형식이 너무 자유로움. + * 어느정도 틀을 잡아둘 필요 있음 + * @return issue 첨부 파일 참조 + */ + @Nullable + public String getRunningTimeInfo() { + if (values.isEmpty()) { + return null; + } + StringJoiner sj = new StringJoiner(LINE_BREAK); + for (RunningTime value : values) { + RunningTimeInfoFormatter runningTimeInfoFormatter = RunningTimeInfoFormatterFactory.getBy(value); + sj.add(runningTimeInfoFormatter.format()); + } + return sj.toString(); + } + + public OperationStatus getCurrentOperationStatus(LocalTime now) { + return values.stream() + .filter(runningTime -> runningTime.isBetween(now)) + .map(RunningTime::getOperationStatus) + .findFirst() + .orElse(OperationStatus.CLOSED); + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/studio/InputKeywordCommand.java b/fillin/src/main/java/com/teamfillin/fillin/domain/studio/InputKeywordCommand.java new file mode 100644 index 0000000..784f587 --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/studio/InputKeywordCommand.java @@ -0,0 +1,48 @@ +package com.teamfillin.fillin.domain.studio; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +public class InputKeywordCommand { + + private static final String SPLITTER = " "; + + private final List values; + + private InputKeywordCommand(List values) { + this.values = values; + } + + public static InputKeywordCommand from(String keyword) { + if (keyword == null) { + return new InputKeywordCommand(Collections.emptyList()); + } + return new InputKeywordCommand( + Arrays.stream(keyword.split(SPLITTER)) + .collect(Collectors.toList()) + ); + } + + public List getOriginValues() { + return new ArrayList<>(values); + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + InputKeywordCommand that = (InputKeywordCommand)o; + return Objects.equals(values, that.values); + } + + @Override + public int hashCode() { + return Objects.hash(values); + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/studio/KeywordManager.java b/fillin/src/main/java/com/teamfillin/fillin/domain/studio/KeywordManager.java new file mode 100644 index 0000000..634848d --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/studio/KeywordManager.java @@ -0,0 +1,111 @@ +package com.teamfillin.fillin.domain.studio; + +import java.util.Arrays; +import java.util.EnumMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + +import org.jetbrains.annotations.NotNull; + +class KeywordManager { + + private static final String BLANK = " "; + private static final int TOKEN_SIZE = 2; + private static final String CHUNK_SPLIT_REGEX = "(?<=\\G.{" + TOKEN_SIZE + "})"; + private static final String CRITICAL_OPERATOR = "+"; + + private final Map> values; + + private KeywordManager(Map> values) { + this.values = values; + } + + String makeSearchKeyword() { + return makeCriticalSearchKeyword() + makeMinorSearchKeyword(); + } + + private String makeCriticalSearchKeyword() { + StringBuilder sb = new StringBuilder(); + for (String criticalKeyWord : getCriticalKeyWords()) { + sb.append(CRITICAL_OPERATOR).append(criticalKeyWord).append(BLANK); + } + return sb.toString(); + } + + private List getCriticalKeyWords() { + return values.get(Value.CRITICAL); + } + + private String makeMinorSearchKeyword() { + StringBuilder sb = new StringBuilder(); + for (String minorKeyWord : getMinorKeyWords()) { + sb.append(minorKeyWord).append(BLANK); + } + return sb.toString(); + } + + private List getMinorKeyWords() { + return values.get(Value.MINOR); + } + + static KeywordManager from(InputKeywordCommand inputKeywordCommand) { + Map> keywordsWithValue = new EnumMap<>(Value.class); + List inputKeywordTokens = inputKeywordCommand.getOriginValues(); + StopWord.removeStopWordIn(inputKeywordTokens); + keywordsWithValue.put(Value.CRITICAL, refineKeywordsForSearch(inputKeywordTokens)); + keywordsWithValue.put(Value.MINOR, StopWord.replaceStopWordToModifiedWord(inputKeywordCommand.getOriginValues())); + return new KeywordManager(keywordsWithValue); + } + + @NotNull + private static List refineKeywordsForSearch(List keywordTokens) { + return keywordTokens.stream() + .flatMap(token -> token.length() > TOKEN_SIZE + ? Arrays.stream(token.split(CHUNK_SPLIT_REGEX)) + : Stream.of(token)) + .filter(token -> token.length() == TOKEN_SIZE) + .toList(); + } + + enum StopWord { + + HYEONSANG_SO("현상소", "현상"), + STUDIO("스튜디오", "스튜"), + SAJIIN_GWAN("사진관", "사진"), + CAMERA("카메라", "카메"), + FILM("필름", "필름"), + PHOTO("포토", "포토"), + ; + + private final String origin; + private final String modified; + + StopWord(String origin, String modified) { + this.origin = origin; + this.modified = modified; + } + + static List replaceStopWordToModifiedWord(List keywordTokens) { + return Arrays.stream(values()) + .filter(stopWord -> + keywordTokens.stream().anyMatch(token -> + token.contains(stopWord.origin) || token.contains(stopWord.modified)) + ) + .map(stopWord -> stopWord.modified) + .toList(); + } + + static void removeStopWordIn(List keywordTokens) { + Arrays.stream(values()) + .forEach(stopWord -> + keywordTokens.removeIf(token -> + token.contains(stopWord.origin) || token.contains(stopWord.modified) + )); + } + } + + enum Value { + CRITICAL, MINOR; + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/studio/Studio.java b/fillin/src/main/java/com/teamfillin/fillin/domain/studio/Studio.java new file mode 100644 index 0000000..5dd6572 --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/studio/Studio.java @@ -0,0 +1,101 @@ +package com.teamfillin.fillin.domain.studio; + +import java.util.Objects; + +import org.jetbrains.annotations.NotNull; + +import com.teamfillin.fillin.domain.runningTime.OperationStatus; + +import lombok.AccessLevel; +import lombok.Builder; + +public class Studio { + + private final long no; + private final String name; + private final String address; + private final String tel; + private final StudioLocation location; + private final String etc; + private final String site; + private final StudioStatus status; + private StudioRunningStatus runningStatus; + + + @Builder(access = AccessLevel.PACKAGE) + private Studio(long no, String name, String address, String tel, @NotNull StudioLocation location, String etc, + String site, StudioStatus status) { + this.no = no; + this.name = name; + this.address = address; + this.tel = tel; + this.location = location; + this.etc = etc; + this.site = site; + this.status = status; + } + + public void setRunningStatus(OperationStatus operationStatus) { + if (operationStatus.isClosed()) { + runningStatus = StudioRunningStatus.CLOSED; + } else if (operationStatus.isBreakTime()) { + runningStatus = StudioRunningStatus.BREAK_TIME; + } else { + runningStatus = StudioRunningStatus.OPEN; + } + } + + public long getNo() { + return no; + } + + public String getName() { + return name; + } + + public String getAddress() { + return address; + } + + public String getTel() { + return tel; + } + + public String getEtc() { + return etc; + } + + public String getSite() { + return site; + } + + public double getLatitude() { + return location.getLatitude(); + } + + public double getLongitude() { + return location.getLongitude(); + } + + public String getRunningStatusValue() { + return runningStatus.getValue(); + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + Studio studio = (Studio)o; + return no == studio.no && Objects.equals(name, studio.name) && Objects.equals(address, + studio.address) && Objects.equals(tel, studio.tel) && Objects.equals(location, + studio.location) && Objects.equals(etc, studio.etc) && Objects.equals(site, studio.site) + && status == studio.status; + } + + @Override + public int hashCode() { + return Objects.hash(no, name, address, tel, location, etc, site, status); + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/studio/StudioDetailResult.java b/fillin/src/main/java/com/teamfillin/fillin/domain/studio/StudioDetailResult.java new file mode 100644 index 0000000..1570488 --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/studio/StudioDetailResult.java @@ -0,0 +1,29 @@ +package com.teamfillin.fillin.domain.studio; + +import com.teamfillin.fillin.domain.price.view.PriceView; +import com.teamfillin.fillin.domain.runningTime.view.RunningTimeView; + +public class StudioDetailResult { + + private final Studio studio; + private final PriceView priceView; + private final RunningTimeView runningTimeView; + + public StudioDetailResult(Studio studio, PriceView priceView, RunningTimeView runningTimeView) { + this.studio = studio; + this.priceView = priceView; + this.runningTimeView = runningTimeView; + } + + public Studio getStudio() { + return studio; + } + + public PriceView getPriceView() { + return priceView; + } + + public RunningTimeView getRunningTimeView() { + return runningTimeView; + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/studio/StudioDetailRetrieveService.java b/fillin/src/main/java/com/teamfillin/fillin/domain/studio/StudioDetailRetrieveService.java new file mode 100644 index 0000000..be79543 --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/studio/StudioDetailRetrieveService.java @@ -0,0 +1,38 @@ +package com.teamfillin.fillin.domain.studio; + +import java.time.Clock; +import java.time.LocalTime; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.teamfillin.fillin.domain.price.PriceRetriever; +import com.teamfillin.fillin.domain.price.view.PriceView; +import com.teamfillin.fillin.domain.runningTime.RunningTimeRetriever; +import com.teamfillin.fillin.domain.runningTime.view.RunningTimeView; + +@Service +@Transactional(readOnly = true) +public class StudioDetailRetrieveService { + + private final StudioRetriever studioRetriever; + private final PriceRetriever priceRetriever; + private final RunningTimeRetriever runningTimeRetriever; + private final Clock koreaTimeClock; + + public StudioDetailRetrieveService(StudioRetriever studioRetriever, PriceRetriever priceRetriever, + RunningTimeRetriever runningTimeRetriever, Clock koreaTimeClock) { + this.studioRetriever = studioRetriever; + this.priceRetriever = priceRetriever; + this.runningTimeRetriever = runningTimeRetriever; + this.koreaTimeClock = koreaTimeClock; + } + + public StudioDetailResult retrieveDetail(long studioNo) { + Studio studio = studioRetriever.retrieveOne(studioNo); + PriceView priceView = new PriceView(priceRetriever.retrieveByStudioNo(studioNo)); + RunningTimeView runningTimeView = new RunningTimeView(runningTimeRetriever.retrieveByStudioNo(studioNo)); + studio.setRunningStatus(runningTimeView.getCurrentOperationStatus(LocalTime.now(koreaTimeClock))); + return new StudioDetailResult(studio, priceView, runningTimeView); + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/studio/StudioEntity.java b/fillin/src/main/java/com/teamfillin/fillin/domain/studio/StudioEntity.java index 5e2f85a..f619849 100644 --- a/fillin/src/main/java/com/teamfillin/fillin/domain/studio/StudioEntity.java +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/studio/StudioEntity.java @@ -40,7 +40,7 @@ public class StudioEntity { @Enumerated(EnumType.STRING) @Column(nullable = false, length = 10) - private StudioStatus status; + private StudioStatus status; // studio 가 더 이상 장사를 하지 않는건지 판단하는 경우에만 필요할듯 protected StudioEntity() { } @@ -57,4 +57,57 @@ private StudioEntity(String name, String address, String tel, Double latitude, D this.site = site; this.status = status; } + + public Studio toStudio() { + StudioLocation studioLocation = StudioLocation.builder() + .longitude(longitude) + .latitude(latitude) + .build(); + return Studio.builder() + .no(no) + .name(name) + .address(address) + .tel(tel) + .location(studioLocation) + .etc(etc) + .site(site) + .status(status) + .build(); + } + + public Long getNo() { + return no; + } + + public String getName() { + return name; + } + + public String getAddress() { + return address; + } + + public String getTel() { + return tel; + } + + public Double getLatitude() { + return latitude; + } + + public Double getLongitude() { + return longitude; + } + + public String getEtc() { + return etc; + } + + public String getSite() { + return site; + } + + public StudioStatus getStatus() { + return status; + } } diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/studio/StudioErrorCode.java b/fillin/src/main/java/com/teamfillin/fillin/domain/studio/StudioErrorCode.java new file mode 100644 index 0000000..77fb2d5 --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/studio/StudioErrorCode.java @@ -0,0 +1,33 @@ +package com.teamfillin.fillin.domain.studio; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.springframework.http.HttpStatus; + +import com.teamfillin.fillin.FillinErrorCode; + +enum StudioErrorCode implements FillinErrorCode { + + E404_STUDIO_NOT_FOUND(HttpStatus.NOT_FOUND, "존재하지 않는 스튜디오입니다"), + ; + + private final HttpStatus status; + private final String defaultMessage; + + StudioErrorCode(HttpStatus status, String defaultMessage) { + this.status = status; + this.defaultMessage = defaultMessage; + } + + @NotNull + @Override + public HttpStatus status() { + return status; + } + + @Nullable + @Override + public String defaultMessage() { + return defaultMessage; + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/studio/StudioExceptionHandler.java b/fillin/src/main/java/com/teamfillin/fillin/domain/studio/StudioExceptionHandler.java new file mode 100644 index 0000000..1ac73f5 --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/studio/StudioExceptionHandler.java @@ -0,0 +1,13 @@ +package com.teamfillin.fillin.domain.studio; + +import com.teamfillin.fillin.FillinException; + +class StudioExceptionHandler { + + private StudioExceptionHandler() { + } + + public static FillinException notFound() { + throw FillinException.from(StudioErrorCode.E404_STUDIO_NOT_FOUND); + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/studio/StudioLocation.java b/fillin/src/main/java/com/teamfillin/fillin/domain/studio/StudioLocation.java new file mode 100644 index 0000000..cae3e17 --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/studio/StudioLocation.java @@ -0,0 +1,42 @@ +package com.teamfillin.fillin.domain.studio; + +import java.util.Objects; + +import lombok.AccessLevel; +import lombok.Builder; + +public class StudioLocation { + + private final double latitude; + private final double longitude; + + @Builder(access = AccessLevel.PACKAGE) + private StudioLocation(double latitude, double longitude) { + this.latitude = latitude; + this.longitude = longitude; + } + + public double getLatitude() { + return latitude; + } + + public double getLongitude() { + return longitude; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + StudioLocation that = (StudioLocation)o; + return Double.compare(that.latitude, latitude) == 0 + && Double.compare(that.longitude, longitude) == 0; + } + + @Override + public int hashCode() { + return Objects.hash(latitude, longitude); + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/studio/StudioLocationResult.java b/fillin/src/main/java/com/teamfillin/fillin/domain/studio/StudioLocationResult.java new file mode 100644 index 0000000..4f31635 --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/studio/StudioLocationResult.java @@ -0,0 +1,30 @@ +package com.teamfillin.fillin.domain.studio; + +public class StudioLocationResult { + + private final long id; + private final double latitude; + private final double longitude; + + private StudioLocationResult(long id, double latitude, double longitude) { + this.id = id; + this.latitude = latitude; + this.longitude = longitude; + } + + public static StudioLocationResult from(Studio studio) { + return new StudioLocationResult(studio.getNo(), studio.getLatitude(), studio.getLongitude()); + } + + public long getId() { + return id; + } + + public double getLatitude() { + return latitude; + } + + public double getLongitude() { + return longitude; + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/studio/StudioRepository.java b/fillin/src/main/java/com/teamfillin/fillin/domain/studio/StudioRepository.java new file mode 100644 index 0000000..6c8a0b9 --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/studio/StudioRepository.java @@ -0,0 +1,17 @@ +package com.teamfillin.fillin.domain.studio; + +import java.util.List; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; + +@Repository +public interface StudioRepository extends JpaRepository { + + @Query(value = + "SELECT s.* FROM studio s " + + "WHERE MATCH(s.name, s.address) " + + "AGAINST(:searchKeyword IN BOOLEAN MODE)", nativeQuery = true) + List searchByKeywordInBooleanMode(String searchKeyword); +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/studio/StudioRetriever.java b/fillin/src/main/java/com/teamfillin/fillin/domain/studio/StudioRetriever.java new file mode 100644 index 0000000..6dc3779 --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/studio/StudioRetriever.java @@ -0,0 +1,31 @@ +package com.teamfillin.fillin.domain.studio; + +import java.util.List; + +import org.springframework.stereotype.Component; + +@Component +public class StudioRetriever { + + private final StudioRepository studioRepository; + + public StudioRetriever(StudioRepository studioRepository) { + this.studioRepository = studioRepository; + } + + /** + * 주의 : 메서드 호출 시 full table scan 발생 + */ + public List retrieveAll() { + return studioRepository.findAll() + .stream() + .map(StudioEntity::toStudio) + .toList(); + } + + public Studio retrieveOne(long studioNo) { + return studioRepository.findById(studioNo) + .map(StudioEntity::toStudio) + .orElseThrow(StudioExceptionHandler::notFound); + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/studio/StudioRetrieverService.java b/fillin/src/main/java/com/teamfillin/fillin/domain/studio/StudioRetrieverService.java new file mode 100644 index 0000000..c209623 --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/studio/StudioRetrieverService.java @@ -0,0 +1,24 @@ +package com.teamfillin.fillin.domain.studio; + +import java.util.List; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@Transactional(readOnly = true) +public class StudioRetrieverService { + + private final StudioRetriever studioRetriever; + + public StudioRetrieverService(StudioRetriever studioRetriever) { + this.studioRetriever = studioRetriever; + } + + public List retrieveAllStudio() { + return studioRetriever.retrieveAll() + .stream() + .map(StudioLocationResult::from) + .toList(); + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/studio/StudioRunningStatus.java b/fillin/src/main/java/com/teamfillin/fillin/domain/studio/StudioRunningStatus.java new file mode 100644 index 0000000..a535605 --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/studio/StudioRunningStatus.java @@ -0,0 +1,19 @@ +package com.teamfillin.fillin.domain.studio; + +enum StudioRunningStatus { + + OPEN("운영중"), + CLOSED("영업 종료"), + BREAK_TIME("브레이크 타임"), + ; + + private final String value; + + StudioRunningStatus(String value) { + this.value = value; + } + + public String getValue() { + return value; + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/studio/StudioSearchResult.java b/fillin/src/main/java/com/teamfillin/fillin/domain/studio/StudioSearchResult.java new file mode 100644 index 0000000..0c420f2 --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/studio/StudioSearchResult.java @@ -0,0 +1,30 @@ +package com.teamfillin.fillin.domain.studio; + +public class StudioSearchResult { + + private final long id; + private final String name; + private final String address; + + private StudioSearchResult(long id, String name, String address) { + this.id = id; + this.name = name; + this.address = address; + } + + public static StudioSearchResult from(Studio studio) { + return new StudioSearchResult(studio.getNo(), studio.getName(), studio.getAddress()); + } + + public long getId() { + return id; + } + + public String getName() { + return name; + } + + public String getAddress() { + return address; + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/studio/StudioSearchService.java b/fillin/src/main/java/com/teamfillin/fillin/domain/studio/StudioSearchService.java new file mode 100644 index 0000000..7c16de3 --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/studio/StudioSearchService.java @@ -0,0 +1,24 @@ +package com.teamfillin.fillin.domain.studio; + +import java.util.List; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@Transactional(readOnly = true) +public class StudioSearchService { + + private final StudioSearcher studioSearcher; + + public StudioSearchService(StudioSearcher studioSearcher) { + this.studioSearcher = studioSearcher; + } + + public List searchStudio(InputKeywordCommand inputKeywordCommand) { + return studioSearcher.searchByKeyword(inputKeywordCommand) + .stream() + .map(StudioSearchResult::from) + .toList(); + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/studio/StudioSearcher.java b/fillin/src/main/java/com/teamfillin/fillin/domain/studio/StudioSearcher.java new file mode 100644 index 0000000..3bc5f7e --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/studio/StudioSearcher.java @@ -0,0 +1,24 @@ +package com.teamfillin.fillin.domain.studio; + +import java.util.List; + +import org.springframework.stereotype.Component; + +@Component +public class StudioSearcher { + + private final StudioRepository studioSearchRepository; + + public StudioSearcher(StudioRepository studioSearchRepository) { + this.studioSearchRepository = studioSearchRepository; + } + + public List searchByKeyword(InputKeywordCommand inputKeywordCommand) { + KeywordManager keywordManager = KeywordManager.from(inputKeywordCommand); + String searchKeyword = keywordManager.makeSearchKeyword(); + return studioSearchRepository.searchByKeywordInBooleanMode(searchKeyword) + .stream() + .map(StudioEntity::toStudio) + .toList(); + } +} diff --git a/fillin/src/test/java/com/teamfillin/fillin/domain/runningTime/view/RunningTimeInfoFormatterTest.java b/fillin/src/test/java/com/teamfillin/fillin/domain/runningTime/view/RunningTimeInfoFormatterTest.java new file mode 100644 index 0000000..7b49284 --- /dev/null +++ b/fillin/src/test/java/com/teamfillin/fillin/domain/runningTime/view/RunningTimeInfoFormatterTest.java @@ -0,0 +1,41 @@ +package com.teamfillin.fillin.domain.runningTime.view; + +import static com.teamfillin.fillin.domain.runningTime.DayOfWeek.*; +import static com.teamfillin.fillin.domain.runningTime.OperationStatus.*; +import static java.time.LocalTime.*; +import static org.assertj.core.api.AssertionsForInterfaceTypes.*; + +import java.util.stream.Stream; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import com.teamfillin.fillin.domain.runningTime.RunningTime; + +class RunningTimeInfoFormatterTest { + + @DisplayName("RunningTime 의 OPEN, CLOSED, BREAK_TIME, ONLY_ONLINE 여부에 따른 운영 정보 형식을 반환한다") + @ParameterizedTest + @MethodSource("provideRunningTimeAndExpected") + void format(RunningTime runningTime, String expected) { + // given + RunningTimeInfoFormatter formatter = RunningTimeInfoFormatterFactory.getBy(runningTime); + + // when + String actual = formatter.format(); + + // then + assertThat(actual).isEqualTo(expected); + } + + private static Stream provideRunningTimeAndExpected() { + return Stream.of( + Arguments.of(new RunningTime(1, WEEK_DAY, OPEN, NOON, MIDNIGHT), "평일 12:00 - 00:00"), + Arguments.of(new RunningTime(2, WEEK_DAY, CLOSED, NOON, MIDNIGHT), "평일 휴무"), + Arguments.of(new RunningTime(3, WEEK_DAY, BREAK_TIME, NOON, MIDNIGHT), "브레이크 타임 12:00 - 00:00"), + Arguments.of(new RunningTime(4, WEEK_DAY, ONLY_ONLINE, NOON, MIDNIGHT), "온라인만 운영") + ); + } +} diff --git a/fillin/src/test/java/com/teamfillin/fillin/domain/studio/InputKeywordCommandTest.java b/fillin/src/test/java/com/teamfillin/fillin/domain/studio/InputKeywordCommandTest.java new file mode 100644 index 0000000..15a6244 --- /dev/null +++ b/fillin/src/test/java/com/teamfillin/fillin/domain/studio/InputKeywordCommandTest.java @@ -0,0 +1,50 @@ +package com.teamfillin.fillin.domain.studio; + +import static org.assertj.core.api.AssertionsForInterfaceTypes.*; + +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class InputKeywordCommandTest { + + @DisplayName("InputKeyword 생성시 문자열을 공백 기준 개별 키워드로 분할한다") + @Test + void from() { + // given + String input = "필린 현상소"; + + // when + InputKeywordCommand actual = InputKeywordCommand.from(input); + + // then + List expected = List.of("필린", "현상소"); + assertThat(actual.getOriginValues()).containsAll(expected); + } + + @DisplayName("InputKeyword 생성시 null 이 들어오면 empty list 를 반환한다") + @Test + void fromNull() { + // given, when + InputKeywordCommand actual = InputKeywordCommand.from(null); + + // then + assertThat(actual.getOriginValues()).isEmpty(); + } + + @DisplayName("getOriginValues 로 가져온 값은 외부에서 변경해도 내부는 변경되지 않는다") + @Test + void getOriginValues() { + // given + String input = "필린 현상소"; + InputKeywordCommand inputKeywordCommand = InputKeywordCommand.from(input); + + // when + List expected = List.of("필린", "현상소"); + inputKeywordCommand.getOriginValues().removeAll(expected); + + // then + assertThat(inputKeywordCommand.getOriginValues()).containsAll(expected); + } +} diff --git a/fillin/src/test/java/com/teamfillin/fillin/domain/studio/KeywordManagerTest.java b/fillin/src/test/java/com/teamfillin/fillin/domain/studio/KeywordManagerTest.java new file mode 100644 index 0000000..41548e3 --- /dev/null +++ b/fillin/src/test/java/com/teamfillin/fillin/domain/studio/KeywordManagerTest.java @@ -0,0 +1,94 @@ +package com.teamfillin.fillin.domain.studio; + +import static org.assertj.core.api.AssertionsForInterfaceTypes.*; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.stream.Stream; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class KeywordManagerTest { + + @DisplayName("입력 키워드를 검색용 키워드로 변환한다") + @ParameterizedTest + @MethodSource("provideInputKeywordAndSearchKeywordExpected") + void makeSearchKeyword(String input, String expected) { + // given + InputKeywordCommand inputKeywordCommand = InputKeywordCommand.from(input); + KeywordManager keywordManager = KeywordManager.from(inputKeywordCommand); + + // when + String actual = keywordManager.makeSearchKeyword(); + + // then + assertThat(actual).isEqualTo(expected); + } + + private static Stream provideInputKeywordAndSearchKeywordExpected() { + return Stream.of( + Arguments.of("분당 대왕판교로 현상소", "+분당 +대왕 +판교 현상 "), + Arguments.of("논현동 황제 스튜디오", "+논현 +황제 스튜 "), + Arguments.of("부산광역시 간지 사진관", "+부산 +광역 +간지 사진 "), + Arguments.of("압구정 카메라", "+압구 카메 "), + Arguments.of("대구 중구 동성로 필름", "+대구 +중구 +동성 필름 "), + Arguments.of("인천 포토", "+인천 포토 ") + ); + } + + @DisplayName("입력 검색어 토큰에 StopWord 가 포함되어 있으면 수정된 StopWord 를 반환하고, 포함되어 있지 않다면 빈 리스트를 반환한다") + @ParameterizedTest + @MethodSource("provideTokensAndModifyExpected") + void replaceStopWordToModifiedWord(List tokens, List expected) { + // given + List keywordTokens = new ArrayList<>(tokens); + + // when + List actual = KeywordManager.StopWord.replaceStopWordToModifiedWord(keywordTokens); + + // then + assertThat(actual).containsAll(expected); + } + + private static Stream provideTokensAndModifyExpected() { + return Stream.of( + Arguments.of(List.of("분당", "대왕판교로현상소"), List.of("현상")), + Arguments.of(List.of("논현동", "황제", "스튜디오"), List.of("스튜")), + Arguments.of(List.of("부산광역시", "간지", "사진관"), List.of("사진")), + Arguments.of(List.of("압구정", "카메라"), List.of("카메")), + Arguments.of(List.of("대구", "중구", "동성로", "필름"), List.of("필름")), + Arguments.of(List.of("인천", "포토"), List.of("포토")), + Arguments.of(List.of("STOP", "WORD", "미포함"), Collections.emptyList()) + ); + } + + @DisplayName("입력 검색어 토큰에 StopWord 가 포함되어 있으면 헤당 토큰을 삭제하고, 포함되어 있지 않다면 삭제하지 않는다") + @ParameterizedTest + @MethodSource("provideTokensAndRemoveExpected") + void removeStopWordIn(List tokens, List expected) { + // given + List keywordTokens = new ArrayList<>(tokens); + + // when + KeywordManager.StopWord.removeStopWordIn(keywordTokens); + + // then + assertThat(keywordTokens).containsAll(expected); + } + + private static Stream provideTokensAndRemoveExpected() { + return Stream.of( + Arguments.of(List.of("분당", "대왕판교로", "현상소"), List.of("분당", "대왕판교로")), + Arguments.of(List.of("논현동", "황제", "스튜디오"), List.of("논현동", "황제")), + Arguments.of(List.of("부산광역시", "간지", "사진관"), List.of("부산광역시", "간지")), + Arguments.of(List.of("압구정", "카메라"), List.of("압구정")), + Arguments.of(List.of("대구", "중구", "동성로", "필름"), List.of("대구", "중구", "동성로")), + Arguments.of(List.of("인천", "포토"), List.of("인천")), + Arguments.of(List.of("STOP", "WORD", "미포함"), List.of("STOP", "WORD", "미포함")) + ); + } +} From 65ea9ea7e156f8ca8a25296b4d39452baf1f1fbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=A8=B8=ED=99=98=EC=9E=84?= <88091704+daehwan2da@users.noreply.github.com> Date: Fri, 1 Mar 2024 18:15:24 +0900 Subject: [PATCH 14/15] =?UTF-8?q?Account=20=EB=A1=9C=EC=A7=81=20=EA=B0=9C?= =?UTF-8?q?=EC=84=A0=20(#35)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * token package api 하위로 이동 * Account 로직 개선 --- .../fillin/api/account/AccountApi.java | 22 +++++++------- .../fillin/api/account/AccountResponse.java | 2 +- .../account => api}/token/AccessToken.java | 5 ++-- .../account => api}/token/RefreshToken.java | 2 +- .../token/TokenConfiguration.java | 12 +++++++- .../fillin/api/token/TokenHandler.java | 9 ++++++ .../fillin/api/token/TokenType.java | 5 ++++ .../token/jwt}/JwtTokenHandler.java | 11 ++++--- .../fillin/domain/account/Account.java | 18 +----------- .../domain/account/AccountAccessResult.java | 20 ++++++------- ...AndUserResult.java => AccountAndUser.java} | 8 ++--- .../domain/account/AccountCreateCommand.java | 21 ++++++++++++++ .../fillin/domain/account/AccountEntity.java | 2 +- .../domain/account/AccountRegister.java | 15 ++++++---- .../domain/account/AccountRetriever.java | 16 ++++++++-- .../fillin/domain/account/AccountService.java | 13 +++++---- .../domain/account/AccountUserFacade.java | 29 ++++++++----------- .../fillin/domain/account/UserBridge.java | 29 +++++++++++++++++++ .../domain/account/token/TokenType.java | 5 ---- .../fillin/domain/user/UserRegister.java | 3 +- .../fillin/domain/user/UserRetriever.java | 4 ++- 21 files changed, 160 insertions(+), 91 deletions(-) rename fillin/src/main/java/com/teamfillin/fillin/{domain/account => api}/token/AccessToken.java (85%) rename fillin/src/main/java/com/teamfillin/fillin/{domain/account => api}/token/RefreshToken.java (93%) rename fillin/src/main/java/com/teamfillin/fillin/{domain/account => api}/token/TokenConfiguration.java (78%) create mode 100644 fillin/src/main/java/com/teamfillin/fillin/api/token/TokenHandler.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/api/token/TokenType.java rename fillin/src/main/java/com/teamfillin/fillin/{domain/account/token => api/token/jwt}/JwtTokenHandler.java (91%) rename fillin/src/main/java/com/teamfillin/fillin/domain/account/{AccountAndUserResult.java => AccountAndUser.java} (71%) create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountCreateCommand.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/account/UserBridge.java delete mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/account/token/TokenType.java diff --git a/fillin/src/main/java/com/teamfillin/fillin/api/account/AccountApi.java b/fillin/src/main/java/com/teamfillin/fillin/api/account/AccountApi.java index 01375e1..a23f8c3 100644 --- a/fillin/src/main/java/com/teamfillin/fillin/api/account/AccountApi.java +++ b/fillin/src/main/java/com/teamfillin/fillin/api/account/AccountApi.java @@ -7,34 +7,36 @@ import org.springframework.web.bind.annotation.RestController; import com.teamfillin.fillin.api.FillinApiResponse; +import com.teamfillin.fillin.api.token.AccessToken; +import com.teamfillin.fillin.api.token.TokenHandler; import com.teamfillin.fillin.domain.account.AccountAccessResult; +import com.teamfillin.fillin.domain.account.AccountCreateCommand; import com.teamfillin.fillin.domain.account.AccountService; -import com.teamfillin.fillin.domain.account.SocialType; -import com.teamfillin.fillin.domain.account.token.AccessToken; -import com.teamfillin.fillin.domain.account.token.JwtTokenHandler; @RestController public class AccountApi { private final AccountService accountService; - private final JwtTokenHandler jwtTokenHandler; + private final TokenHandler tokenHandler; public AccountApi( AccountService accountService, - JwtTokenHandler jwtTokenHandler + TokenHandler tokenHandler ) { this.accountService = accountService; - this.jwtTokenHandler = jwtTokenHandler; + this.tokenHandler = tokenHandler; } @PostMapping("/auth") public ResponseEntity loginOrJoin( @RequestBody AccountRequest accountRequest ) { - final SocialType socialType = accountRequest.getSocial(); - final String idKey = accountRequest.getIdKey(); + final AccountCreateCommand command = new AccountCreateCommand( + accountRequest.getSocial(), + accountRequest.getIdKey() + ); - final AccountAccessResult accountAccessResult = accountService.loginOrJoin(socialType, idKey); - final AccessToken accessToken = jwtTokenHandler.generateAccessTokenFrom(accountAccessResult); + final AccountAccessResult accountAccessResult = accountService.loginOrJoin(command); + final AccessToken accessToken = tokenHandler.generateAccessTokenFrom(accountAccessResult); return switch (accountAccessResult.getProcedure()) { case JOIN -> diff --git a/fillin/src/main/java/com/teamfillin/fillin/api/account/AccountResponse.java b/fillin/src/main/java/com/teamfillin/fillin/api/account/AccountResponse.java index 90b4eea..bead9d3 100644 --- a/fillin/src/main/java/com/teamfillin/fillin/api/account/AccountResponse.java +++ b/fillin/src/main/java/com/teamfillin/fillin/api/account/AccountResponse.java @@ -6,7 +6,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.teamfillin.fillin.domain.account.AccountAccessResult; import com.teamfillin.fillin.domain.account.SocialType; -import com.teamfillin.fillin.domain.account.token.AccessToken; +import com.teamfillin.fillin.api.token.AccessToken; import lombok.AccessLevel; import lombok.Builder; diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/account/token/AccessToken.java b/fillin/src/main/java/com/teamfillin/fillin/api/token/AccessToken.java similarity index 85% rename from fillin/src/main/java/com/teamfillin/fillin/domain/account/token/AccessToken.java rename to fillin/src/main/java/com/teamfillin/fillin/api/token/AccessToken.java index d4b79c4..815c351 100644 --- a/fillin/src/main/java/com/teamfillin/fillin/domain/account/token/AccessToken.java +++ b/fillin/src/main/java/com/teamfillin/fillin/api/token/AccessToken.java @@ -1,10 +1,9 @@ -package com.teamfillin.fillin.domain.account.token; +package com.teamfillin.fillin.api.token; import java.util.Objects; import org.jetbrains.annotations.NotNull; -import lombok.AccessLevel; import lombok.Builder; public class AccessToken { @@ -13,7 +12,7 @@ public class AccessToken { @NotNull private final String value; - @Builder(access = AccessLevel.PACKAGE) + @Builder private AccessToken(@NotNull TokenType tokenType, @NotNull String value) { this.type = tokenType; this.value = value; diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/account/token/RefreshToken.java b/fillin/src/main/java/com/teamfillin/fillin/api/token/RefreshToken.java similarity index 93% rename from fillin/src/main/java/com/teamfillin/fillin/domain/account/token/RefreshToken.java rename to fillin/src/main/java/com/teamfillin/fillin/api/token/RefreshToken.java index 7ee189e..1693793 100644 --- a/fillin/src/main/java/com/teamfillin/fillin/domain/account/token/RefreshToken.java +++ b/fillin/src/main/java/com/teamfillin/fillin/api/token/RefreshToken.java @@ -1,4 +1,4 @@ -package com.teamfillin.fillin.domain.account.token; +package com.teamfillin.fillin.api.token; import java.util.Objects; diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/account/token/TokenConfiguration.java b/fillin/src/main/java/com/teamfillin/fillin/api/token/TokenConfiguration.java similarity index 78% rename from fillin/src/main/java/com/teamfillin/fillin/domain/account/token/TokenConfiguration.java rename to fillin/src/main/java/com/teamfillin/fillin/api/token/TokenConfiguration.java index d82d9a3..68d4950 100644 --- a/fillin/src/main/java/com/teamfillin/fillin/domain/account/token/TokenConfiguration.java +++ b/fillin/src/main/java/com/teamfillin/fillin/api/token/TokenConfiguration.java @@ -1,11 +1,15 @@ -package com.teamfillin.fillin.domain.account.token; +package com.teamfillin.fillin.api.token; import org.jetbrains.annotations.NotNull; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationPropertiesScan; import org.springframework.boot.context.properties.ConstructorBinding; import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; + +import com.teamfillin.fillin.api.token.jwt.JwtTokenHandler; @Configuration @EnableConfigurationProperties @@ -37,4 +41,10 @@ public long getRefreshTokenValidityInMilli() { return refreshTokenValidityInMilli; } } + + @Bean + @Primary + public TokenHandler tokenHandler(JwtTokenProperties jwtTokenProperties) { + return new JwtTokenHandler(jwtTokenProperties); + } } diff --git a/fillin/src/main/java/com/teamfillin/fillin/api/token/TokenHandler.java b/fillin/src/main/java/com/teamfillin/fillin/api/token/TokenHandler.java new file mode 100644 index 0000000..1735abb --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/api/token/TokenHandler.java @@ -0,0 +1,9 @@ +package com.teamfillin.fillin.api.token; + +import org.jetbrains.annotations.NotNull; + +import com.teamfillin.fillin.domain.account.AccountAccessResult; + +public interface TokenHandler { + public AccessToken generateAccessTokenFrom(@NotNull final AccountAccessResult accountAccessResult); +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/api/token/TokenType.java b/fillin/src/main/java/com/teamfillin/fillin/api/token/TokenType.java new file mode 100644 index 0000000..4fda322 --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/api/token/TokenType.java @@ -0,0 +1,5 @@ +package com.teamfillin.fillin.api.token; + +public enum TokenType { + JWT +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/account/token/JwtTokenHandler.java b/fillin/src/main/java/com/teamfillin/fillin/api/token/jwt/JwtTokenHandler.java similarity index 91% rename from fillin/src/main/java/com/teamfillin/fillin/domain/account/token/JwtTokenHandler.java rename to fillin/src/main/java/com/teamfillin/fillin/api/token/jwt/JwtTokenHandler.java index 7ae7b4b..81c8396 100644 --- a/fillin/src/main/java/com/teamfillin/fillin/domain/account/token/JwtTokenHandler.java +++ b/fillin/src/main/java/com/teamfillin/fillin/api/token/jwt/JwtTokenHandler.java @@ -1,4 +1,4 @@ -package com.teamfillin.fillin.domain.account.token; +package com.teamfillin.fillin.api.token.jwt; import java.time.Instant; import java.util.Date; @@ -8,9 +8,12 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.springframework.stereotype.Component; import com.google.common.collect.Maps; +import com.teamfillin.fillin.api.token.AccessToken; +import com.teamfillin.fillin.api.token.TokenConfiguration; +import com.teamfillin.fillin.api.token.TokenHandler; +import com.teamfillin.fillin.api.token.TokenType; import com.teamfillin.fillin.domain.account.AccountAccessResult; import com.teamfillin.fillin.domain.account.SocialType; @@ -20,8 +23,7 @@ import io.jsonwebtoken.io.Decoders; import io.jsonwebtoken.security.Keys; -@Component -public class JwtTokenHandler { +public class JwtTokenHandler implements TokenHandler { private static final String CLAIM_SOCIAL_TYPE = "socialType"; private static final String CLAIM_ACCOUNT_NO = "accountNo"; private static final String CLAIM_USER_NO = "userNo"; @@ -34,6 +36,7 @@ public JwtTokenHandler(TokenConfiguration.JwtTokenProperties jwtTokenProperties) this.key = Keys.hmacShaKeyFor(Decoders.BASE64.decode(jwtTokenProperties.getSecret())); } + @Override public AccessToken generateAccessTokenFrom(@NotNull AccountAccessResult accountAccessResult) { final SocialType socialType = accountAccessResult.getSocialType(); final long accountNo = accountAccessResult.getAccountNo(); diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/account/Account.java b/fillin/src/main/java/com/teamfillin/fillin/domain/account/Account.java index ca12fe9..677142b 100644 --- a/fillin/src/main/java/com/teamfillin/fillin/domain/account/Account.java +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/account/Account.java @@ -5,7 +5,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import lombok.AccessLevel; import lombok.Builder; public class Account { @@ -15,7 +14,7 @@ public class Account { private final String socialId; private final SocialType socialType; - @Builder(access = AccessLevel.PRIVATE) + @Builder private Account(long no, String refreshToken, long userNo, String socialId, SocialType socialType) { this.no = no; this.refreshToken = refreshToken; @@ -47,21 +46,6 @@ public SocialType getSocialType() { return socialType; } - public static Account from(@NotNull AccountEntity accountEntity) { - return Account.builder() - .no(accountEntity.getNo()) - .userNo(accountEntity.getUserNo()) - .socialType(accountEntity.getSocialInfo().getSocialType()) - .socialId(accountEntity.getSocialInfo().getSocialId()) - .refreshToken(accountEntity.getRefreshToken()) - .build(); - } - - @NotNull - public AccountEntity.SocialInfo getSocialInfo() { - return AccountEntity.SocialInfo.from(socialType, socialId); - } - @Override public int hashCode() { return Objects.hash(no, userNo, socialId, socialType); diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountAccessResult.java b/fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountAccessResult.java index 31278cd..8896a4b 100644 --- a/fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountAccessResult.java +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountAccessResult.java @@ -22,23 +22,23 @@ private AccountAccessResult(PROCEDURE procedure, SocialType socialType, String n this.userNo = userNo; } - public static AccountAccessResult login(@NotNull AccountAndUserResult accountAndUserResult) { + public static AccountAccessResult login(@NotNull AccountAndUser accountAndUser) { return AccountAccessResult.builder() .procedure(PROCEDURE.LOGIN) - .socialType(accountAndUserResult.getSocialType()) - .nickname(accountAndUserResult.getNickname()) - .accountNo(accountAndUserResult.getAccountNo()) - .userNo(accountAndUserResult.getUserNo()) + .socialType(accountAndUser.getSocialType()) + .nickname(accountAndUser.getNickname()) + .accountNo(accountAndUser.getAccountNo()) + .userNo(accountAndUser.getUserNo()) .build(); } - public static AccountAccessResult join(@NotNull AccountAndUserResult accountAndUserResult) { + public static AccountAccessResult join(@NotNull AccountAndUser accountAndUser) { return AccountAccessResult.builder() .procedure(PROCEDURE.JOIN) - .socialType(accountAndUserResult.getSocialType()) - .nickname(accountAndUserResult.getNickname()) - .accountNo(accountAndUserResult.getAccountNo()) - .userNo(accountAndUserResult.getUserNo()) + .socialType(accountAndUser.getSocialType()) + .nickname(accountAndUser.getNickname()) + .accountNo(accountAndUser.getAccountNo()) + .userNo(accountAndUser.getUserNo()) .build(); } diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountAndUserResult.java b/fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountAndUser.java similarity index 71% rename from fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountAndUserResult.java rename to fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountAndUser.java index 672a495..39e37de 100644 --- a/fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountAndUserResult.java +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountAndUser.java @@ -4,19 +4,15 @@ import com.teamfillin.fillin.domain.user.User; -public class AccountAndUserResult { +public class AccountAndUser { private final Account account; private final User user; - private AccountAndUserResult(Account account, User user) { + AccountAndUser(@NotNull final Account account, @NotNull final User user) { this.account = account; this.user = user; } - public static AccountAndUserResult of(@NotNull Account account, @NotNull User user) { - return new AccountAndUserResult(account, user); - } - @NotNull public SocialType getSocialType() { return account.getSocialType(); diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountCreateCommand.java b/fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountCreateCommand.java new file mode 100644 index 0000000..7f06a60 --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountCreateCommand.java @@ -0,0 +1,21 @@ +package com.teamfillin.fillin.domain.account; + +import org.jetbrains.annotations.NotNull; + +public class AccountCreateCommand { + private final SocialType socialType; + private final String idKey; + + public AccountCreateCommand(@NotNull final SocialType socialType, @NotNull final String idKey) { + this.socialType = socialType; + this.idKey = idKey; + } + + public SocialType getSocialType() { + return socialType; + } + + public String getIdKey() { + return idKey; + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountEntity.java b/fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountEntity.java index 843fbf6..545e06b 100644 --- a/fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountEntity.java +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountEntity.java @@ -71,7 +71,7 @@ public String getRefreshToken() { return refreshToken; } - public Long getUserNo() { + public long getUserNo() { return userNo; } diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountRegister.java b/fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountRegister.java index a9395dd..fc5d855 100644 --- a/fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountRegister.java +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountRegister.java @@ -1,8 +1,8 @@ package com.teamfillin.fillin.domain.account; import org.jetbrains.annotations.NotNull; +import org.springframework.dao.DataIntegrityViolationException; import org.springframework.stereotype.Component; -import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import com.teamfillin.fillin.domain.account.AccountEntity.SocialInfo; @@ -15,16 +15,21 @@ public AccountRegister(AccountRepository accountRepository) { this.accountRepository = accountRepository; } - // TODO : 중복 요청으로 인한 unique error catch 필요 - @Transactional(propagation = Propagation.MANDATORY) + @Transactional public Account registerAccount( @NotNull SocialType socialType, @NotNull String socialId, long userNo - ) { + ) throws DataIntegrityViolationException { final SocialInfo socialInfo = SocialInfo.from(socialType, socialId); final AccountEntity registeredAccountEntity = accountRepository.save(AccountEntity.from(socialInfo, userNo)); - return Account.from(registeredAccountEntity); + return Account.builder() + .no(registeredAccountEntity.getNo()) + .userNo(registeredAccountEntity.getUserNo()) + .socialType(registeredAccountEntity.getSocialInfo().getSocialType()) + .socialId(registeredAccountEntity.getSocialInfo().getSocialId()) + .refreshToken(registeredAccountEntity.getRefreshToken()) + .build(); } } diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountRetriever.java b/fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountRetriever.java index 2795ab0..7cff851 100644 --- a/fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountRetriever.java +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountRetriever.java @@ -3,6 +3,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; @Component public class AccountRetriever { @@ -13,16 +14,27 @@ public AccountRetriever(AccountRepository accountRepository) { } @NotNull + @Transactional(readOnly = true) public Account retrieve(@NotNull SocialType socialType, @NotNull String socialId) { return accountRepository.findAccountBySocialInfo(AccountEntity.SocialInfo.from(socialType, socialId)) - .map(Account::from) + .map(this::toAccount) .orElseThrow(AccountExceptionHandler::notFound); } @Nullable public Account retrieveOrNull(@NotNull SocialType socialType, @NotNull String socialId) { return accountRepository.findAccountBySocialInfo(AccountEntity.SocialInfo.from(socialType, socialId)) - .map(Account::from) + .map(this::toAccount) .orElse(null); } + + private Account toAccount(@NotNull final AccountEntity accountEntity) { + return Account.builder() + .no(accountEntity.getNo()) + .userNo(accountEntity.getUserNo()) + .socialId(accountEntity.getSocialInfo().getSocialId()) + .socialType(accountEntity.getSocialInfo().getSocialType()) + .refreshToken(accountEntity.getRefreshToken()) + .build(); + } } diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountService.java b/fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountService.java index d6c0c5b..9996dee 100644 --- a/fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountService.java +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountService.java @@ -14,23 +14,26 @@ public AccountService(AccountUserFacade accountUserFacade) { } @Transactional - public AccountAccessResult loginOrJoin(@NotNull SocialType socialType, @NotNull String idKey) { - final AccountAndUserResult accountAndUser = accountUserFacade.retrieveAccountAndUserOrNull(socialType, idKey); + public AccountAccessResult loginOrJoin(@NotNull AccountCreateCommand command) { + final SocialType socialType = command.getSocialType(); + final String idKey = command.getIdKey(); + + final AccountAndUser accountAndUser = accountUserFacade.retrieveOrNull(socialType, idKey); if (joined(accountAndUser)) { return AccountAccessResult.login(accountAndUser); } - final AccountAndUserResult joinResult; + final AccountAndUser joinResult; try { joinResult = accountUserFacade.registerAccountAndUser(socialType, idKey); } catch (DataIntegrityViolationException e) { // unique error 에 대한 catch - final AccountAndUserResult foundAccountAndUser = accountUserFacade.retrieve(socialType, idKey); + final AccountAndUser foundAccountAndUser = accountUserFacade.retrieve(socialType, idKey); return AccountAccessResult.login(foundAccountAndUser); } return AccountAccessResult.join(joinResult); } - private boolean joined(AccountAndUserResult accountAndUser) { + private boolean joined(AccountAndUser accountAndUser) { return accountAndUser != null; } } diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountUserFacade.java b/fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountUserFacade.java index 123e003..9cfa88d 100644 --- a/fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountUserFacade.java +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/account/AccountUserFacade.java @@ -7,45 +7,40 @@ import org.springframework.transaction.annotation.Transactional; import com.teamfillin.fillin.domain.user.User; -import com.teamfillin.fillin.domain.user.UserRegister; -import com.teamfillin.fillin.domain.user.UserRetriever; @Component public class AccountUserFacade { private final AccountRegister accountRegister; private final AccountRetriever accountRetriever; - private final UserRegister userRegister; - private final UserRetriever userRetriever; + private final UserBridge userBridge; public AccountUserFacade( AccountRegister accountRegister , AccountRetriever accountRetriever - , UserRegister userRegister - , UserRetriever userRetriever + , UserBridge userBridge ) { this.accountRegister = accountRegister; this.accountRetriever = accountRetriever; - this.userRegister = userRegister; - this.userRetriever = userRetriever; + this.userBridge = userBridge; } @NotNull @Transactional(readOnly = true) - public AccountAndUserResult retrieve(@NotNull SocialType socialType, @NotNull String idKey) { + public AccountAndUser retrieve(@NotNull SocialType socialType, @NotNull String idKey) { final Account foundAccount = accountRetriever.retrieve(socialType, idKey); - final User foundUser = userRetriever.retrieve(foundAccount.getUserNo()); + final User foundUser = userBridge.retrieveUser(foundAccount.getUserNo()); - return AccountAndUserResult.of(foundAccount, foundUser); + return new AccountAndUser(foundAccount, foundUser); } @Nullable @Transactional(readOnly = true) - public AccountAndUserResult retrieveAccountAndUserOrNull(@NotNull SocialType socialType, @NotNull String idKey) { + public AccountAndUser retrieveOrNull(@NotNull SocialType socialType, @NotNull String idKey) { final Account foundAccount = accountRetriever.retrieveOrNull(socialType, idKey); if (foundAccount != null) { - final User foundUser = userRetriever.retrieve(foundAccount.getUserNo()); - return AccountAndUserResult.of(foundAccount, foundUser); + final User foundUser = userBridge.retrieveUser(foundAccount.getUserNo()); + return new AccountAndUser(foundAccount, foundUser); } return null; @@ -53,15 +48,15 @@ public AccountAndUserResult retrieveAccountAndUserOrNull(@NotNull SocialType soc @NotNull @Transactional - public AccountAndUserResult registerAccountAndUser(@NotNull SocialType socialType, @NotNull String idKey) + public AccountAndUser registerAccountAndUser(@NotNull SocialType socialType, @NotNull String idKey) throws DataIntegrityViolationException { - final User registeredUser = userRegister.registerUser(); + final User registeredUser = userBridge.registerUser(); final Account registeredAccount = accountRegister.registerAccount( socialType, idKey, registeredUser.getNo() ); - return AccountAndUserResult.of(registeredAccount, registeredUser); + return new AccountAndUser(registeredAccount, registeredUser); } } diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/account/UserBridge.java b/fillin/src/main/java/com/teamfillin/fillin/domain/account/UserBridge.java new file mode 100644 index 0000000..bda6af4 --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/account/UserBridge.java @@ -0,0 +1,29 @@ +package com.teamfillin.fillin.domain.account; + +import org.jetbrains.annotations.NotNull; +import org.springframework.stereotype.Component; + +import com.teamfillin.fillin.domain.user.User; +import com.teamfillin.fillin.domain.user.UserRegister; +import com.teamfillin.fillin.domain.user.UserRetriever; + +@Component +public class UserBridge { + private final UserRegister userRegister; + private final UserRetriever userRetriever; + + public UserBridge(UserRegister userRegister, UserRetriever userRetriever) { + this.userRegister = userRegister; + this.userRetriever = userRetriever; + } + + @NotNull + public User retrieveUser(final long userNo) { + return userRetriever.retrieve(userNo); + } + + @NotNull + public User registerUser() { + return userRegister.registerUser(); + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/account/token/TokenType.java b/fillin/src/main/java/com/teamfillin/fillin/domain/account/token/TokenType.java deleted file mode 100644 index 1b479a8..0000000 --- a/fillin/src/main/java/com/teamfillin/fillin/domain/account/token/TokenType.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.teamfillin.fillin.domain.account.token; - -public enum TokenType { - JWT -} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/user/UserRegister.java b/fillin/src/main/java/com/teamfillin/fillin/domain/user/UserRegister.java index da8e512..4b49d8d 100644 --- a/fillin/src/main/java/com/teamfillin/fillin/domain/user/UserRegister.java +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/user/UserRegister.java @@ -2,7 +2,6 @@ import org.jetbrains.annotations.NotNull; import org.springframework.stereotype.Component; -import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; @Component @@ -18,7 +17,7 @@ public UserRegister( this.userRepository = userRepository; } - @Transactional(propagation = Propagation.MANDATORY) + @Transactional public User registerUser() { final String randomNickname = generateRandomNickname(); final UserEntity registeredUserEntity = userRepository.save(UserEntity.createActive(randomNickname)); diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/user/UserRetriever.java b/fillin/src/main/java/com/teamfillin/fillin/domain/user/UserRetriever.java index 1791865..481efbf 100644 --- a/fillin/src/main/java/com/teamfillin/fillin/domain/user/UserRetriever.java +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/user/UserRetriever.java @@ -2,6 +2,7 @@ import org.jetbrains.annotations.NotNull; import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; @Component public class UserRetriever { @@ -13,7 +14,8 @@ public UserRetriever(UserRepository userRepository) { } @NotNull - public User retrieve(long no) { + @Transactional(readOnly = true) + public User retrieve(final long no) { final UserEntity userEntity = userRepository.findById(no).orElseThrow(UserExceptionHandler::notFound); return User.from(userEntity); } From c75c3be44f8420467065d3c13d2a8f95dc52bab0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=B0=AC=EC=9A=B0?= <58971262+oownahcohc@users.noreply.github.com> Date: Fri, 1 Mar 2024 18:16:41 +0900 Subject: [PATCH 15/15] =?UTF-8?q?studio=20=EB=B6=81=EB=A7=88=ED=81=AC=20?= =?UTF-8?q?=EB=A6=AC=EC=95=A1=EC=85=98=20=EB=93=B1=EB=A1=9D=20=EB=B0=8F=20?= =?UTF-8?q?=EB=A6=AC=EC=95=A1=EC=85=98=20=EC=A1=B0=ED=9A=8C=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EA=B0=9C=EB=B0=9C=20(#37)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 북마크 리액션 생성 개발 * 북마크 리액션 조회 개발 * reaction api 개발 --- .../reaction/BookmarkReactionResponse.java | 18 ++++++++ .../fillin/api/reaction/ReactionApi.java | 44 +++++++++++++++++++ .../domain/reaction/BookmarkReaction.java | 41 +++++++++++++++++ .../reaction/BookmarkReactionProjection.java | 8 ++++ .../reaction/BookmarkReactionResult.java | 26 +++++++++++ .../reaction/ReactionCreateCommand.java | 20 +++++++++ .../reaction/ReactionHistoryRepository.java | 16 +++++++ .../fillin/domain/reaction/ReactionId.java | 4 ++ .../domain/reaction/ReactionRegister.java | 17 +++++++ .../reaction/ReactionRegisterService.java | 18 ++++++++ .../reaction/ReactionRetrieveService.java | 26 +++++++++++ .../domain/reaction/ReactionRetriever.java | 27 ++++++++++++ 12 files changed, 265 insertions(+) create mode 100644 fillin/src/main/java/com/teamfillin/fillin/api/reaction/BookmarkReactionResponse.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/api/reaction/ReactionApi.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/reaction/BookmarkReaction.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/reaction/BookmarkReactionProjection.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/reaction/BookmarkReactionResult.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/reaction/ReactionCreateCommand.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/reaction/ReactionHistoryRepository.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/reaction/ReactionRegister.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/reaction/ReactionRegisterService.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/reaction/ReactionRetrieveService.java create mode 100644 fillin/src/main/java/com/teamfillin/fillin/domain/reaction/ReactionRetriever.java diff --git a/fillin/src/main/java/com/teamfillin/fillin/api/reaction/BookmarkReactionResponse.java b/fillin/src/main/java/com/teamfillin/fillin/api/reaction/BookmarkReactionResponse.java new file mode 100644 index 0000000..21ff46c --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/api/reaction/BookmarkReactionResponse.java @@ -0,0 +1,18 @@ +package com.teamfillin.fillin.api.reaction; + +import java.util.List; + +import com.teamfillin.fillin.domain.reaction.BookmarkReactionResult; + +public class BookmarkReactionResponse { + + private final List bookmarks; + + public BookmarkReactionResponse(List bookmarks) { + this.bookmarks = bookmarks; + } + + public List getBookmarks() { + return bookmarks; + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/api/reaction/ReactionApi.java b/fillin/src/main/java/com/teamfillin/fillin/api/reaction/ReactionApi.java new file mode 100644 index 0000000..94d3900 --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/api/reaction/ReactionApi.java @@ -0,0 +1,44 @@ +package com.teamfillin.fillin.api.reaction; + +import java.util.List; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.teamfillin.fillin.api.FillinApiResponse; +import com.teamfillin.fillin.domain.reaction.BookmarkReactionResult; +import com.teamfillin.fillin.domain.reaction.ReactionCreateCommand; +import com.teamfillin.fillin.domain.reaction.ReactionRegisterService; +import com.teamfillin.fillin.domain.reaction.ReactionRetrieveService; + +@RestController +@RequestMapping("/reaction") +public class ReactionApi { + + private final ReactionRegisterService reactionRegisterService; + private final ReactionRetrieveService reactionRetrieveService; + + public ReactionApi(ReactionRegisterService reactionRegisterService, + ReactionRetrieveService reactionRetrieveService) { + this.reactionRegisterService = reactionRegisterService; + this.reactionRetrieveService = reactionRetrieveService; + } + + @PostMapping("/{studioNo}") + public ResponseEntity registerStudioReaction(@PathVariable long studioNo, long userNo) { + ReactionCreateCommand reactionCreateCommand = new ReactionCreateCommand(userNo, studioNo); + reactionRegisterService.registerStudioReaction(reactionCreateCommand); + return FillinApiResponse.success(HttpStatus.CREATED); + } + + @GetMapping("/studio") + public ResponseEntity retrieve(long userNo) { + List bookmarkReactionResults = reactionRetrieveService.retrieveBookmark(userNo); + return FillinApiResponse.success(HttpStatus.OK, new BookmarkReactionResponse(bookmarkReactionResults)); + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/reaction/BookmarkReaction.java b/fillin/src/main/java/com/teamfillin/fillin/domain/reaction/BookmarkReaction.java new file mode 100644 index 0000000..fed6e2f --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/reaction/BookmarkReaction.java @@ -0,0 +1,41 @@ +package com.teamfillin.fillin.domain.reaction; + +import org.jetbrains.annotations.NotNull; + +public class BookmarkReaction { + + private final ReactionId reactionId; + private final StudioInfo studioInfo; + + private BookmarkReaction(ReactionId reactionId, StudioInfo studioInfo) { + this.reactionId = reactionId; + this.studioInfo = studioInfo; + } + + public static BookmarkReaction of(@NotNull ReactionId reactionId, String name, String address) { + return new BookmarkReaction(reactionId, new StudioInfo(name, address)); + } + + private static class StudioInfo { + + private final String name; + private final String address; + + StudioInfo(String name, String address) { + this.name = name; + this.address = address; + } + } + + public long getTargetNo() { + return reactionId.getTargetNo(); + } + + public String getName() { + return studioInfo.name; + } + + public String getAddress() { + return studioInfo.address; + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/reaction/BookmarkReactionProjection.java b/fillin/src/main/java/com/teamfillin/fillin/domain/reaction/BookmarkReactionProjection.java new file mode 100644 index 0000000..69f2407 --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/reaction/BookmarkReactionProjection.java @@ -0,0 +1,8 @@ +package com.teamfillin.fillin.domain.reaction; + +public interface BookmarkReactionProjection { + TargetType getType(); + long getTargetNo(); + String getName(); + String getAddress(); +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/reaction/BookmarkReactionResult.java b/fillin/src/main/java/com/teamfillin/fillin/domain/reaction/BookmarkReactionResult.java new file mode 100644 index 0000000..618a163 --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/reaction/BookmarkReactionResult.java @@ -0,0 +1,26 @@ +package com.teamfillin.fillin.domain.reaction; + +public class BookmarkReactionResult { + + private final long studioNo; + private final String name; + private final String address; + + public BookmarkReactionResult(long studioNo, String name, String address) { + this.studioNo = studioNo; + this.name = name; + this.address = address; + } + + public long getStudioNo() { + return studioNo; + } + + public String getName() { + return name; + } + + public String getAddress() { + return address; + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/reaction/ReactionCreateCommand.java b/fillin/src/main/java/com/teamfillin/fillin/domain/reaction/ReactionCreateCommand.java new file mode 100644 index 0000000..266ceb7 --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/reaction/ReactionCreateCommand.java @@ -0,0 +1,20 @@ +package com.teamfillin.fillin.domain.reaction; + +public class ReactionCreateCommand { + + private final long userNo; + private final long targetNo; + + public ReactionCreateCommand(long userNo, long targetNo) { + this.userNo = userNo; + this.targetNo = targetNo; + } + + public long getUserNo() { + return userNo; + } + + public long getTargetNo() { + return targetNo; + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/reaction/ReactionHistoryRepository.java b/fillin/src/main/java/com/teamfillin/fillin/domain/reaction/ReactionHistoryRepository.java new file mode 100644 index 0000000..2b21b21 --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/reaction/ReactionHistoryRepository.java @@ -0,0 +1,16 @@ +package com.teamfillin.fillin.domain.reaction; + +import java.util.List; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; + +@Repository +public interface ReactionHistoryRepository extends JpaRepository { + + @Query("SELECT rh.targetType AS type, rh.targetNo AS targetNo, s.name AS name, s.address AS address " + + "FROM ReactionHistoryEntity rh " + + "INNER JOIN StudioEntity s ON rh.targetNo = s.no AND rh.userNo = :userNo") + List findBy(long userNo); +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/reaction/ReactionId.java b/fillin/src/main/java/com/teamfillin/fillin/domain/reaction/ReactionId.java index 6ea36ef..a82ce8f 100644 --- a/fillin/src/main/java/com/teamfillin/fillin/domain/reaction/ReactionId.java +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/reaction/ReactionId.java @@ -13,6 +13,10 @@ public ReactionId(@NotNull TargetType type, long targetNo) { this.targetNo = targetNo; } + public long getTargetNo() { + return targetNo; + } + @Override public int hashCode() { return Objects.hash(type, targetNo); diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/reaction/ReactionRegister.java b/fillin/src/main/java/com/teamfillin/fillin/domain/reaction/ReactionRegister.java new file mode 100644 index 0000000..dd18456 --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/reaction/ReactionRegister.java @@ -0,0 +1,17 @@ +package com.teamfillin.fillin.domain.reaction; + +import org.springframework.stereotype.Component; + +@Component +public class ReactionRegister { + + private final ReactionHistoryRepository reactionHistoryRepository; + + public ReactionRegister(ReactionHistoryRepository reactionHistoryRepository) { + this.reactionHistoryRepository = reactionHistoryRepository; + } + + public void registerStudio(long userNo, long studioNo) { + reactionHistoryRepository.save(new ReactionHistoryEntity(userNo, studioNo, TargetType.STUDIO)); + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/reaction/ReactionRegisterService.java b/fillin/src/main/java/com/teamfillin/fillin/domain/reaction/ReactionRegisterService.java new file mode 100644 index 0000000..ee42fcf --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/reaction/ReactionRegisterService.java @@ -0,0 +1,18 @@ +package com.teamfillin.fillin.domain.reaction; + +import org.jetbrains.annotations.NotNull; +import org.springframework.stereotype.Service; + +@Service +public class ReactionRegisterService { + + private final ReactionRegister reactionRegister; + + public ReactionRegisterService(ReactionRegister reactionRegister) { + this.reactionRegister = reactionRegister; + } + + public void registerStudioReaction(@NotNull ReactionCreateCommand command) { + reactionRegister.registerStudio(command.getUserNo(), command.getTargetNo()); + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/reaction/ReactionRetrieveService.java b/fillin/src/main/java/com/teamfillin/fillin/domain/reaction/ReactionRetrieveService.java new file mode 100644 index 0000000..3242367 --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/reaction/ReactionRetrieveService.java @@ -0,0 +1,26 @@ +package com.teamfillin.fillin.domain.reaction; + +import java.util.List; + +import org.springframework.stereotype.Service; + +@Service +public class ReactionRetrieveService { + + private final ReactionRetriever reactionRetriever; + + public ReactionRetrieveService(ReactionRetriever reactionRetriever) { + this.reactionRetriever = reactionRetriever; + } + + public List retrieveBookmark(long userNo) { + return reactionRetriever.retrieveBookmark(userNo) + .stream() + .map(bookmarkReaction -> new BookmarkReactionResult( + bookmarkReaction.getTargetNo(), + bookmarkReaction.getName(), + bookmarkReaction.getAddress() + )) + .toList(); + } +} diff --git a/fillin/src/main/java/com/teamfillin/fillin/domain/reaction/ReactionRetriever.java b/fillin/src/main/java/com/teamfillin/fillin/domain/reaction/ReactionRetriever.java new file mode 100644 index 0000000..8db7f80 --- /dev/null +++ b/fillin/src/main/java/com/teamfillin/fillin/domain/reaction/ReactionRetriever.java @@ -0,0 +1,27 @@ +package com.teamfillin.fillin.domain.reaction; + +import java.util.List; +import java.util.stream.Collectors; + +import org.springframework.stereotype.Component; + +@Component +public class ReactionRetriever { + + private final ReactionHistoryRepository reactionHistoryRepository; + + public ReactionRetriever(ReactionHistoryRepository reactionHistoryRepository) { + this.reactionHistoryRepository = reactionHistoryRepository; + } + + public List retrieveBookmark(long userNo) { + return reactionHistoryRepository.findBy(userNo) + .stream() + .map(projection -> BookmarkReaction.of( + new ReactionId(projection.getType(), projection.getTargetNo()), + projection.getName(), + projection.getAddress() + )) + .collect(Collectors.toList()); + } +}