From 5c86e9f087d90ba2ca88986045aaa329eeb6cb5e Mon Sep 17 00:00:00 2001 From: inpink Date: Mon, 20 Jan 2025 22:11:40 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20Entity=EB=A5=BC=20=EC=83=9D=EC=84=B1?= =?UTF-8?q?=ED=95=9C=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 2 +- ...3\267\260_\354\203\235\354\204\261_API.md" | 33 +++++++++++++++++++ ...54\236\254\354\203\235\354\204\261_API.md" | 23 +++++++++++++ ...4\241\260_\354\241\260\355\232\214_API.md" | 25 ++++++++++++++ gradle.properties | 5 ++- gradle/db.gradle | 1 + .../{com => me}/misik/api/Application.kt | 2 +- src/main/kotlin/me/misik/api/api/.gitkeep | 0 src/main/kotlin/me/misik/api/app/.gitkeep | 0 .../kotlin/me/misik/api/core/AggregateRoot.kt | 5 +++ src/main/kotlin/me/misik/api/core/Clock.kt | 20 +++++++++++ .../kotlin/me/misik/api/core/IdGenerator.kt | 10 ++++++ .../me/misik/api/domain/AbstractTime.kt | 32 ++++++++++++++++++ .../me/misik/api/domain/RequestPrompt.kt | 20 +++++++++++ src/main/kotlin/me/misik/api/domain/Review.kt | 30 +++++++++++++++++ .../me/misik/api/domain/ReviewRepository.kt | 5 +++ .../me/misik/api/domain/ReviewService.kt | 11 +++++++ .../kotlin/me/misik/api/domain/ReviewStyle.kt | 5 +++ .../domain/converter/ListToStringConverter.kt | 23 +++++++++++++ .../me/misik/api/domain/query/Prompt.kt | 19 +++++++++++ src/main/kotlin/me/misik/api/infra/.gitkeep | 0 21 files changed, 268 insertions(+), 3 deletions(-) create mode 100644 "docs/api/\353\246\254\353\267\260_\354\203\235\354\204\261_API.md" create mode 100644 "docs/api/\353\246\254\353\267\260_\354\236\254\354\203\235\354\204\261_API.md" create mode 100644 "docs/api/\354\204\240\355\203\235\352\260\200\353\212\245\355\225\234_\352\260\200\353\212\245\355\225\234_\354\226\264\354\241\260_\354\241\260\355\232\214_API.md" rename src/main/kotlin/{com => me}/misik/api/Application.kt (96%) create mode 100644 src/main/kotlin/me/misik/api/api/.gitkeep create mode 100644 src/main/kotlin/me/misik/api/app/.gitkeep create mode 100644 src/main/kotlin/me/misik/api/core/AggregateRoot.kt create mode 100644 src/main/kotlin/me/misik/api/core/Clock.kt create mode 100644 src/main/kotlin/me/misik/api/core/IdGenerator.kt create mode 100644 src/main/kotlin/me/misik/api/domain/AbstractTime.kt create mode 100644 src/main/kotlin/me/misik/api/domain/RequestPrompt.kt create mode 100644 src/main/kotlin/me/misik/api/domain/Review.kt create mode 100644 src/main/kotlin/me/misik/api/domain/ReviewRepository.kt create mode 100644 src/main/kotlin/me/misik/api/domain/ReviewService.kt create mode 100644 src/main/kotlin/me/misik/api/domain/ReviewStyle.kt create mode 100644 src/main/kotlin/me/misik/api/domain/converter/ListToStringConverter.kt create mode 100644 src/main/kotlin/me/misik/api/domain/query/Prompt.kt create mode 100644 src/main/kotlin/me/misik/api/infra/.gitkeep diff --git a/build.gradle b/build.gradle index 7363333..33b74f6 100644 --- a/build.gradle +++ b/build.gradle @@ -19,7 +19,7 @@ test { } application { - mainClassName = 'com.misik.api.Application' + mainClassName = 'me.misik.api.Application' } apply from: "gradle/db.gradle" diff --git "a/docs/api/\353\246\254\353\267\260_\354\203\235\354\204\261_API.md" "b/docs/api/\353\246\254\353\267\260_\354\203\235\354\204\261_API.md" new file mode 100644 index 0000000..2e76b2e --- /dev/null +++ "b/docs/api/\353\246\254\353\267\260_\354\203\235\354\204\261_API.md" @@ -0,0 +1,33 @@ +# 리뷰 생성 + +리뷰를 생성합니다. + +## Request + +### HTTP METHOD : `POST` + +### url : `https://api.misik.me/reviews` +### Http Headers +- device-id: 식별할 수 있는 값 + `미래에도 변하지 않아야함 앱을 삭제했다 다시 깔아도 안변하는 값으로 줄 수 있는지` + +### RequestBody + +```json +{ + "ocrText": "", + "hashTag": ["특별한 메뉴가 있어요", "뷰가 좋아요", ..., "종류가 다양해요"], + "reviewStyle": "딱딱한 미식가" +} +``` + +### Response + +#### `Response Status 200 OK` + +```json +{ + "id": "123456789", + "review": "카야토스는 숨겨져 있는 카야잼과 버터가 확실히 가득합니다. 또한, 가게의 분위기는 아늑하고 편안하고 바깥쪽에 있고 사랑하는 시간을 보낼 수 있는 공간입니다. 무엇보다 가격에 비해 음식의 품질이 정말 훌륭해서, 마음에 들었습니다." +} +``` diff --git "a/docs/api/\353\246\254\353\267\260_\354\236\254\354\203\235\354\204\261_API.md" "b/docs/api/\353\246\254\353\267\260_\354\236\254\354\203\235\354\204\261_API.md" new file mode 100644 index 0000000..1dbdbd7 --- /dev/null +++ "b/docs/api/\353\246\254\353\267\260_\354\236\254\354\203\235\354\204\261_API.md" @@ -0,0 +1,23 @@ +# 리뷰 재생성 + +리뷰를 재생성합니다. + +## Request + +### HTTP METHOD : `POST` + +### url : `https://api.misik.me/reviews/{id}/re-create` +### Http Headers +- device-id: 식별할 수 있는 값 + `미래에도 변하지 않아야함 앱을 삭제했다 다시 깔아도 안변하는 값으로 줄 수 있는지` + +### Response + +#### `Response Status 200 OK` + +```json +{ + "id": "123456789", + "review": "가득합니다....." +} +``` diff --git "a/docs/api/\354\204\240\355\203\235\352\260\200\353\212\245\355\225\234_\352\260\200\353\212\245\355\225\234_\354\226\264\354\241\260_\354\241\260\355\232\214_API.md" "b/docs/api/\354\204\240\355\203\235\352\260\200\353\212\245\355\225\234_\352\260\200\353\212\245\355\225\234_\354\226\264\354\241\260_\354\241\260\355\232\214_API.md" new file mode 100644 index 0000000..bd3b00a --- /dev/null +++ "b/docs/api/\354\204\240\355\203\235\352\260\200\353\212\245\355\225\234_\352\260\200\353\212\245\355\225\234_\354\226\264\354\241\260_\354\241\260\355\232\214_API.md" @@ -0,0 +1,25 @@ +# 가능한 어조 조회 + +선택 가능한 어조를 조회합니다. + +## Request + +### HTTP METHOD : `GET` + +### url : `https://api.misik.me/reviews/styles` + +### Response + +#### `Response Status 200 OK` + +```json +{ + "reviewStyles": [ + { + "icon": "https://static.misik.me/", + "style": "딱딱한 미식가" + }, + ... + ] +} +``` diff --git a/gradle.properties b/gradle.properties index cbc12df..2e1d23a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,7 +1,7 @@ kotlin.code.style=official ### Project ### -group=com.misik.api +group=me.misik.api version=1.0 ### Spring ### @@ -28,3 +28,6 @@ netxVersion=0.4.8 ### H2version ### h2Version=1.4.200 + +### Snowflake ### +snowflakeVersion=5.2.5 diff --git a/gradle/db.gradle b/gradle/db.gradle index 61d4ab8..1750eef 100644 --- a/gradle/db.gradle +++ b/gradle/db.gradle @@ -1,5 +1,6 @@ dependencies { implementation "mysql:mysql-connector-java:${mysqlConnectorVersion}" + implementation "com.github.f4b6a3:tsid-creator:${snowflakeVersion}" testRuntimeOnly "com.h2database:h2:${h2Version}" } diff --git a/src/main/kotlin/com/misik/api/Application.kt b/src/main/kotlin/me/misik/api/Application.kt similarity index 96% rename from src/main/kotlin/com/misik/api/Application.kt rename to src/main/kotlin/me/misik/api/Application.kt index 24ebb17..af28bab 100644 --- a/src/main/kotlin/com/misik/api/Application.kt +++ b/src/main/kotlin/me/misik/api/Application.kt @@ -1,4 +1,4 @@ -package com.misik.api +package me.misik.api import org.rooftop.netx.meta.EnableSaga import org.springframework.boot.SpringApplication diff --git a/src/main/kotlin/me/misik/api/api/.gitkeep b/src/main/kotlin/me/misik/api/api/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/main/kotlin/me/misik/api/app/.gitkeep b/src/main/kotlin/me/misik/api/app/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/main/kotlin/me/misik/api/core/AggregateRoot.kt b/src/main/kotlin/me/misik/api/core/AggregateRoot.kt new file mode 100644 index 0000000..7923ed0 --- /dev/null +++ b/src/main/kotlin/me/misik/api/core/AggregateRoot.kt @@ -0,0 +1,5 @@ +package me.misik.api.core + +@Target(AnnotationTarget.CLASS) +@Retention(AnnotationRetention.RUNTIME) +annotation class AggregateRoot diff --git a/src/main/kotlin/me/misik/api/core/Clock.kt b/src/main/kotlin/me/misik/api/core/Clock.kt new file mode 100644 index 0000000..ea6eb82 --- /dev/null +++ b/src/main/kotlin/me/misik/api/core/Clock.kt @@ -0,0 +1,20 @@ +package me.misik.api.core + +import java.time.Clock +import java.time.Instant +import java.time.ZoneId +import java.time.ZonedDateTime + +object Clock { + + var clock: Clock = Clock.systemUTC() + + fun instant() = Instant.now(clock) + + fun Instant.toZonedDateTime() = ZonedDateTime.ofInstant(this, clock.zone) + + fun Instant.toZonedDateTime(zoneId: ZoneId) = ZonedDateTime.ofInstant(this, zoneId) + + fun Instant.toKr() = ZonedDateTime.ofInstant(this, ZoneId.of("Asia/Seoul")) + +} diff --git a/src/main/kotlin/me/misik/api/core/IdGenerator.kt b/src/main/kotlin/me/misik/api/core/IdGenerator.kt new file mode 100644 index 0000000..a734afc --- /dev/null +++ b/src/main/kotlin/me/misik/api/core/IdGenerator.kt @@ -0,0 +1,10 @@ +package me.misik.api.core + +import com.github.f4b6a3.tsid.TsidFactory + +object IdGenerator { + + private val tsidFactory = TsidFactory.newInstance256() + + fun generate(): Long = tsidFactory.create().toLong() +} diff --git a/src/main/kotlin/me/misik/api/domain/AbstractTime.kt b/src/main/kotlin/me/misik/api/domain/AbstractTime.kt new file mode 100644 index 0000000..b459756 --- /dev/null +++ b/src/main/kotlin/me/misik/api/domain/AbstractTime.kt @@ -0,0 +1,32 @@ +package me.misik.api.domain + +import jakarta.persistence.Column +import jakarta.persistence.EntityListeners +import jakarta.persistence.MappedSuperclass +import jakarta.persistence.PrePersist +import org.springframework.data.annotation.CreatedDate +import org.springframework.data.annotation.LastModifiedDate +import org.springframework.data.jpa.domain.support.AuditingEntityListener +import java.time.Instant + +@MappedSuperclass +@EntityListeners(AuditingEntityListener::class) +abstract class AbstractTime( + @CreatedDate + @Column(name = "created_at") + var createdAt: Instant = Instant.now(), + + @LastModifiedDate + @Column(name = "modified_at") + var modifiedAt: Instant? = null, +) { + + @PrePersist + fun prePersist() { + modifiedAt = when (modifiedAt == null) { + true -> createdAt + false -> return + } + } +} + diff --git a/src/main/kotlin/me/misik/api/domain/RequestPrompt.kt b/src/main/kotlin/me/misik/api/domain/RequestPrompt.kt new file mode 100644 index 0000000..c338ad5 --- /dev/null +++ b/src/main/kotlin/me/misik/api/domain/RequestPrompt.kt @@ -0,0 +1,20 @@ +package me.misik.api.domain + +import jakarta.persistence.* +import me.misik.api.domain.converter.ListToStringConverter + +@Embeddable +class RequestPrompt( + @Enumerated(EnumType.STRING) + @Column(name = "style", nullable = false, columnDefinition = "VARCHAR(20)") + val style: ReviewStyle, + + @Column(name = "text", columnDefinition = "TEXT", nullable = false) + val text: String, + + @Column(name = "hash_tags", columnDefinition = "TEXT", nullable = false) + @Convert(converter = ListToStringConverter::class) + val hashTags: List, +) { + +} diff --git a/src/main/kotlin/me/misik/api/domain/Review.kt b/src/main/kotlin/me/misik/api/domain/Review.kt new file mode 100644 index 0000000..d808d51 --- /dev/null +++ b/src/main/kotlin/me/misik/api/domain/Review.kt @@ -0,0 +1,30 @@ +package me.misik.api.domain + +import jakarta.persistence.Column +import jakarta.persistence.Embedded +import jakarta.persistence.Entity +import jakarta.persistence.Id +import jakarta.persistence.Table +import me.misik.api.core.AggregateRoot + + +@Entity(name = "review") +@Table(name = "review") +@AggregateRoot +class Review( + @Id + @Column(name = "id") + val id: Long, + + @Column(name = "text", length = 300, columnDefinition = "VARCHAR(300)", nullable = false) + val text: String, + + @Column(name = "device_id", nullable = false, columnDefinition = "VARCHAR(100)") + val deviceId: String, + + @Embedded + val requestPrompt: RequestPrompt, +): AbstractTime() { + + +} diff --git a/src/main/kotlin/me/misik/api/domain/ReviewRepository.kt b/src/main/kotlin/me/misik/api/domain/ReviewRepository.kt new file mode 100644 index 0000000..aabb286 --- /dev/null +++ b/src/main/kotlin/me/misik/api/domain/ReviewRepository.kt @@ -0,0 +1,5 @@ +package me.misik.api.domain + +import org.springframework.data.jpa.repository.JpaRepository + +interface ReviewRepository : JpaRepository diff --git a/src/main/kotlin/me/misik/api/domain/ReviewService.kt b/src/main/kotlin/me/misik/api/domain/ReviewService.kt new file mode 100644 index 0000000..657a0a0 --- /dev/null +++ b/src/main/kotlin/me/misik/api/domain/ReviewService.kt @@ -0,0 +1,11 @@ +package me.misik.api.domain + +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional + +@Service +@Transactional(readOnly = true) +class ReviewService( + private val reviewRepository: ReviewRepository, +) { +} diff --git a/src/main/kotlin/me/misik/api/domain/ReviewStyle.kt b/src/main/kotlin/me/misik/api/domain/ReviewStyle.kt new file mode 100644 index 0000000..0b0d187 --- /dev/null +++ b/src/main/kotlin/me/misik/api/domain/ReviewStyle.kt @@ -0,0 +1,5 @@ +package me.misik.api.domain + +enum class ReviewStyle { + DUMMY, +} diff --git a/src/main/kotlin/me/misik/api/domain/converter/ListToStringConverter.kt b/src/main/kotlin/me/misik/api/domain/converter/ListToStringConverter.kt new file mode 100644 index 0000000..7970b45 --- /dev/null +++ b/src/main/kotlin/me/misik/api/domain/converter/ListToStringConverter.kt @@ -0,0 +1,23 @@ +package me.misik.api.domain.converter + +import jakarta.persistence.AttributeConverter +import jakarta.persistence.Converter + +@Converter +class ListToStringConverter : AttributeConverter, String> { + + override fun convertToDatabaseColumn(attribute: List): String { + val seperatorDeletedAttribute = attribute.map { it.replace(SEPERATOR, "") } + + return seperatorDeletedAttribute.joinToString(SEPERATOR) + } + + override fun convertToEntityAttribute(dbData: String?): List { + return dbData?.split(SEPERATOR) ?: emptyList() + } + + private companion object { + private const val SEPERATOR = "|" + } +} + diff --git a/src/main/kotlin/me/misik/api/domain/query/Prompt.kt b/src/main/kotlin/me/misik/api/domain/query/Prompt.kt new file mode 100644 index 0000000..aaa4229 --- /dev/null +++ b/src/main/kotlin/me/misik/api/domain/query/Prompt.kt @@ -0,0 +1,19 @@ +package me.misik.api.domain.query + +import jakarta.persistence.* +import me.misik.api.domain.AbstractTime +import me.misik.api.domain.ReviewStyle + +@Entity +class Prompt( + @Id + @Column(name = "id") + val id: Long, + + @Column(name = "style", columnDefinition = "VARCHAR(20)", nullable = false) + @Enumerated(EnumType.STRING) + val style: ReviewStyle, + + @Column(name = "text", columnDefinition = "TEXT", nullable = false) + val text: String, +) : AbstractTime() diff --git a/src/main/kotlin/me/misik/api/infra/.gitkeep b/src/main/kotlin/me/misik/api/infra/.gitkeep new file mode 100644 index 0000000..e69de29