diff --git a/build.gradle b/build.gradle index 65520a1..0352f7f 100644 --- a/build.gradle +++ b/build.gradle @@ -3,6 +3,7 @@ buildscript { springBootVersion = '2.7.0' dependencyManagementVersion = '1.0.11.RELEASE' kotlinVersion = '1.6.21' + flywayVersion = '7.15.0' } repositories { mavenCentral() @@ -44,6 +45,7 @@ dependencies { implementation("org.jetbrains.kotlin:kotlin-stdlib:${kotlinVersion}") implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:${kotlinVersion}") implementation("org.jetbrains.kotlin:kotlin-reflect:${kotlinVersion}") + implementation("com.fasterxml.jackson.module:jackson-module-kotlin") implementation('io.github.microutils:kotlin-logging:2.1.21') @@ -60,6 +62,8 @@ dependencies { runtimeOnly 'mysql:mysql-connector-java' // MySQL runtimeOnly 'com.h2database:h2' // H2 + implementation "org.flywaydb:flyway-core:${flywayVersion}" // flyway + testImplementation 'org.springframework.boot:spring-boot-starter-test' } diff --git a/src/main/java/io/beaniejoy/dongnecafe/common/entity/BaseEntityAuditorAware.kt b/src/main/java/io/beaniejoy/dongnecafe/common/entity/BaseEntityAuditorAware.kt new file mode 100644 index 0000000..e253520 --- /dev/null +++ b/src/main/java/io/beaniejoy/dongnecafe/common/entity/BaseEntityAuditorAware.kt @@ -0,0 +1,13 @@ +package io.beaniejoy.dongnecafe.common.entity + +import org.springframework.data.domain.AuditorAware +import org.springframework.stereotype.Component +import java.util.* + +@Component +class BaseEntityAuditorAware: AuditorAware { + override fun getCurrentAuditor(): Optional { + // TODO 추후 사용자 로그인 기능 추가되면 실제 등록한 사용자를 DB에 저장하는 방향으로 수정 + return Optional.of("system") + } +} \ No newline at end of file diff --git a/src/main/java/io/beaniejoy/dongnecafe/common/domain/BaseTimeEntity.kt b/src/main/java/io/beaniejoy/dongnecafe/common/entity/BaseTimeEntity.kt similarity index 59% rename from src/main/java/io/beaniejoy/dongnecafe/common/domain/BaseTimeEntity.kt rename to src/main/java/io/beaniejoy/dongnecafe/common/entity/BaseTimeEntity.kt index 3bd74a6..633cad1 100644 --- a/src/main/java/io/beaniejoy/dongnecafe/common/domain/BaseTimeEntity.kt +++ b/src/main/java/io/beaniejoy/dongnecafe/common/entity/BaseTimeEntity.kt @@ -1,6 +1,8 @@ -package io.beaniejoy.dongnecafe.common.domain +package io.beaniejoy.dongnecafe.common.entity +import org.springframework.data.annotation.CreatedBy import org.springframework.data.annotation.CreatedDate +import org.springframework.data.annotation.LastModifiedBy import org.springframework.data.annotation.LastModifiedDate import org.springframework.data.jpa.domain.support.AuditingEntityListener import java.time.LocalDateTime @@ -9,10 +11,16 @@ import javax.persistence.MappedSuperclass @MappedSuperclass @EntityListeners(AuditingEntityListener::class) -open class BaseTimeEntity( +class BaseTimeEntity( @CreatedDate val createdAt: LocalDateTime = LocalDateTime.now(), + @CreatedBy + val createdBy: String = "", + @LastModifiedDate - val updatedAt: LocalDateTime? = null + val updatedAt: LocalDateTime? = null, + + @LastModifiedBy + val updatedBy: String? = null ) \ No newline at end of file diff --git a/src/main/java/io/beaniejoy/dongnecafe/domain/cafe/controller/CafeController.kt b/src/main/java/io/beaniejoy/dongnecafe/domain/cafe/controller/CafeController.kt index 22186b9..19d68f5 100644 --- a/src/main/java/io/beaniejoy/dongnecafe/domain/cafe/controller/CafeController.kt +++ b/src/main/java/io/beaniejoy/dongnecafe/domain/cafe/controller/CafeController.kt @@ -17,7 +17,7 @@ class CafeController( ) { @GetMapping fun searchCafeList( - @PageableDefault(sort = ["name"], direction = Sort.Direction.ASC, size = 10) pageable: Pageable + @PageableDefault(sort = ["name"], direction = Sort.Direction.ASC, page = 0, size = 10) pageable: Pageable ): Page { return cafeService.getCafeList(pageable) } diff --git a/src/main/java/io/beaniejoy/dongnecafe/domain/cafe/dto/menu/OptionDetailResponseDto.kt b/src/main/java/io/beaniejoy/dongnecafe/domain/cafe/dto/menu/OptionDetailResponseDto.kt index 31c23bb..756db31 100644 --- a/src/main/java/io/beaniejoy/dongnecafe/domain/cafe/dto/menu/OptionDetailResponseDto.kt +++ b/src/main/java/io/beaniejoy/dongnecafe/domain/cafe/dto/menu/OptionDetailResponseDto.kt @@ -13,7 +13,7 @@ data class OptionDetailResponseDto( return OptionDetailResponseDto( id = optionDetail.id, name = optionDetail.name, - extra = optionDetail.extra + extra = optionDetail.extraPrice ) } } diff --git a/src/main/java/io/beaniejoy/dongnecafe/domain/cafe/entity/Cafe.kt b/src/main/java/io/beaniejoy/dongnecafe/domain/cafe/entity/Cafe.kt index 9ae25e9..c5bd554 100644 --- a/src/main/java/io/beaniejoy/dongnecafe/domain/cafe/entity/Cafe.kt +++ b/src/main/java/io/beaniejoy/dongnecafe/domain/cafe/entity/Cafe.kt @@ -1,6 +1,6 @@ package io.beaniejoy.dongnecafe.domain.cafe.entity -import io.beaniejoy.dongnecafe.common.domain.BaseTimeEntity +import io.beaniejoy.dongnecafe.common.entity.BaseTimeEntity import javax.persistence.* @Entity diff --git a/src/main/java/io/beaniejoy/dongnecafe/domain/cafe/entity/CafeImage.kt b/src/main/java/io/beaniejoy/dongnecafe/domain/cafe/entity/CafeImage.kt index ab6b32e..716ed5f 100644 --- a/src/main/java/io/beaniejoy/dongnecafe/domain/cafe/entity/CafeImage.kt +++ b/src/main/java/io/beaniejoy/dongnecafe/domain/cafe/entity/CafeImage.kt @@ -1,6 +1,6 @@ package io.beaniejoy.dongnecafe.domain.cafe.entity -import io.beaniejoy.dongnecafe.common.domain.BaseTimeEntity +import io.beaniejoy.dongnecafe.common.entity.BaseTimeEntity import javax.persistence.* @Entity diff --git a/src/main/java/io/beaniejoy/dongnecafe/domain/cafe/entity/CafeMenu.kt b/src/main/java/io/beaniejoy/dongnecafe/domain/cafe/entity/CafeMenu.kt index bef74c4..b8f2872 100644 --- a/src/main/java/io/beaniejoy/dongnecafe/domain/cafe/entity/CafeMenu.kt +++ b/src/main/java/io/beaniejoy/dongnecafe/domain/cafe/entity/CafeMenu.kt @@ -1,6 +1,6 @@ package io.beaniejoy.dongnecafe.domain.cafe.entity -import io.beaniejoy.dongnecafe.common.domain.BaseTimeEntity +import io.beaniejoy.dongnecafe.common.entity.BaseTimeEntity import java.math.BigDecimal import javax.persistence.* diff --git a/src/main/java/io/beaniejoy/dongnecafe/domain/cafe/entity/MenuOption.kt b/src/main/java/io/beaniejoy/dongnecafe/domain/cafe/entity/MenuOption.kt index 266324b..65884ba 100644 --- a/src/main/java/io/beaniejoy/dongnecafe/domain/cafe/entity/MenuOption.kt +++ b/src/main/java/io/beaniejoy/dongnecafe/domain/cafe/entity/MenuOption.kt @@ -1,6 +1,6 @@ package io.beaniejoy.dongnecafe.domain.cafe.entity -import io.beaniejoy.dongnecafe.common.domain.BaseTimeEntity +import io.beaniejoy.dongnecafe.common.entity.BaseTimeEntity import javax.persistence.* @Entity diff --git a/src/main/java/io/beaniejoy/dongnecafe/domain/cafe/entity/OptionDetail.kt b/src/main/java/io/beaniejoy/dongnecafe/domain/cafe/entity/OptionDetail.kt index 4a6955d..68b8bf1 100644 --- a/src/main/java/io/beaniejoy/dongnecafe/domain/cafe/entity/OptionDetail.kt +++ b/src/main/java/io/beaniejoy/dongnecafe/domain/cafe/entity/OptionDetail.kt @@ -1,6 +1,6 @@ package io.beaniejoy.dongnecafe.domain.cafe.entity -import io.beaniejoy.dongnecafe.common.domain.BaseTimeEntity +import io.beaniejoy.dongnecafe.common.entity.BaseTimeEntity import java.math.BigDecimal import javax.persistence.* @@ -14,11 +14,10 @@ class OptionDetail( @Column(name = "name", nullable = false) val name: String, - @Column(name = "extra", nullable = false) - val extra: BigDecimal, + @Column(name = "extra_price", nullable = false) + val extraPrice: BigDecimal, @ManyToOne @JoinColumn(name = "option_id", nullable = false) val menuOption: MenuOption -): BaseTimeEntity() { -} \ No newline at end of file +): BaseTimeEntity() \ No newline at end of file diff --git a/src/main/java/io/beaniejoy/dongnecafe/domain/cafe/service/CafeService.kt b/src/main/java/io/beaniejoy/dongnecafe/domain/cafe/service/CafeService.kt index 43ba67a..adeffc1 100644 --- a/src/main/java/io/beaniejoy/dongnecafe/domain/cafe/service/CafeService.kt +++ b/src/main/java/io/beaniejoy/dongnecafe/domain/cafe/service/CafeService.kt @@ -2,8 +2,10 @@ package io.beaniejoy.dongnecafe.domain.cafe.service import io.beaniejoy.dongnecafe.domain.cafe.dto.cafe.CafeInfoResponseDto import io.beaniejoy.dongnecafe.domain.cafe.dto.cafe.CafeSearchResponseDto +import io.beaniejoy.dongnecafe.domain.cafe.entity.Cafe import io.beaniejoy.dongnecafe.domain.cafe.error.CafeNotFoundException import io.beaniejoy.dongnecafe.domain.cafe.repository.CafeRepository +import mu.KLogging import org.springframework.data.domain.Page import org.springframework.data.domain.Pageable import org.springframework.data.repository.findByIdOrNull @@ -15,12 +17,13 @@ import org.springframework.transaction.annotation.Transactional class CafeService( private val cafeRepository: CafeRepository ) { + companion object: KLogging() @Transactional(readOnly = true) fun getCafeList(pageable: Pageable): Page { - val cafeListWithPagination = cafeRepository.findAll(pageable) + val cafeList: Page = cafeRepository.findAll(pageable) - return cafeListWithPagination.map { CafeSearchResponseDto.of(it) } + return cafeList.map { CafeSearchResponseDto.of(it) } } @Transactional(readOnly = true) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 3482f2e..b923ae6 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,11 +1,18 @@ spring: datasource: - url: jdbc:h2:mem:testdb;MODE=MySQL + url: jdbc:mysql://localhost:3306/dongne?autoreconnect=true&characterEncoding=utf8&serverTimezone=Asia/Seoul + username: root + password: beaniejoy # TODO 추후 보안에 대해 생각해보기 + driver-class-name: com.mysql.cj.jdbc.Driver jpa: hibernate: - ddl-auto: update + ddl-auto: none # flyway migration 사용 properties: hibernate: dialect: org.hibernate.dialect.MySQL5InnoDBDialect format_sql: true show-sql: true + flyway: + baseline-on-migrate: true + locations: classpath:db/migration,classpath:db/seed +# baseline-version: 0 diff --git a/src/main/resources/db/migration/V001__Create_cafe.sql b/src/main/resources/db/migration/V001__Create_cafe.sql new file mode 100644 index 0000000..0b07ee1 --- /dev/null +++ b/src/main/resources/db/migration/V001__Create_cafe.sql @@ -0,0 +1,15 @@ +DROP TABLE IF EXISTS `cafe`; + +CREATE TABLE `cafe` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '카페 ID', + `name` varchar(20) NOT NULL COMMENT '카페명', + `address` varchar(100) NOT NULL COMMENT '카페 주소', + `phone_number` varchar(11) NOT NULL COMMENT '카페 전화번호', + `total_rate` float NOT NULL COMMENT '카페 종합 평가 점수', + `description` varchar(255) COMMENT '카페 상세설명', + `created_at` datetime NOT NULL COMMENT '카페 등록날짜', + `created_by` varchar(20) NOT NULL COMMENT '카페 등록자', + `updated_at` datetime NULL COMMENT '카페 변경날짜', + `updated_by` varchar(20) NULL COMMENT '카페 변경자', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; \ No newline at end of file diff --git a/src/main/resources/db/migration/V002__Create_cafe_menu.sql b/src/main/resources/db/migration/V002__Create_cafe_menu.sql new file mode 100644 index 0000000..a2e4b0d --- /dev/null +++ b/src/main/resources/db/migration/V002__Create_cafe_menu.sql @@ -0,0 +1,15 @@ +DROP TABLE IF EXISTS `cafe_menu`; + +CREATE TABLE `cafe_menu` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '카페 메뉴 ID', + `name` varchar(50) NOT NULL COMMENT '카페 메뉴명', + `price` decimal(10, 2) NOT NULL COMMENT '메뉴 가격', + `created_at` datetime NOT NULL COMMENT '메뉴 등록날짜', + `created_by` varchar(20) NOT NULL COMMENT '메뉴 등록자', + `updated_at` datetime COMMENT '메뉴 변경날짜', + `updated_by` varchar(20) NULL COMMENT '메뉴 변경자', + `cafe_id` bigint unsigned NOT NULL COMMENT '연관된 카페 ID', + PRIMARY KEY (`id`), + KEY `cafe_id` (`cafe_id`), + FOREIGN KEY (`cafe_id`) REFERENCES `cafe` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; \ No newline at end of file diff --git a/src/main/resources/db/migration/V003__Create_cafe_image.sql b/src/main/resources/db/migration/V003__Create_cafe_image.sql new file mode 100644 index 0000000..153e47d --- /dev/null +++ b/src/main/resources/db/migration/V003__Create_cafe_image.sql @@ -0,0 +1,14 @@ +DROP TABLE IF EXISTS `cafe_image`; + +CREATE TABLE `cafe_image` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '카페 이미지 ID', + `img_url` varchar(255) NOT NULL COMMENT '이미지 경로', + `created_at` datetime NOT NULL COMMENT '이미지 등록날짜', + `created_by` varchar(20) NOT NULL COMMENT '이미지 등록자', + `updated_at` datetime COMMENT '이미지 변경날짜', + `updated_by` varchar(20) NULL COMMENT '이미지 변경자', + `cafe_id` bigint unsigned NOT NULL COMMENT '연관된 카페 ID', + PRIMARY KEY (`id`), + KEY `cafe_id` (`cafe_id`), + FOREIGN KEY (`cafe_id`) REFERENCES `cafe` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; \ No newline at end of file diff --git a/src/main/resources/db/migration/V004__Create_menu_option.sql b/src/main/resources/db/migration/V004__Create_menu_option.sql new file mode 100644 index 0000000..f39d6b3 --- /dev/null +++ b/src/main/resources/db/migration/V004__Create_menu_option.sql @@ -0,0 +1,14 @@ +DROP TABLE IF EXISTS `menu_option`; + +CREATE TABLE `menu_option`( + `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '옵션 ID', + `title` varchar(50) NOT NULL COMMENT '메뉴 옵션 이름', + `created_at` datetime NOT NULL COMMENT '옵션 등록날짜', + `created_by` varchar(20) NOT NULL COMMENT '옵션 등록자', + `updated_at` datetime COMMENT '옵션 변경날짜', + `updated_by` varchar(20) NULL COMMENT '옵션 변경자', + `menu_id` bigint unsigned NOT NULL COMMENT '연관된 카페 메뉴 ID', + PRIMARY KEY (`id`), + KEY `menu_id` (`menu_id`), + FOREIGN KEY (`menu_id`) REFERENCES `cafe_menu` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; \ No newline at end of file diff --git a/src/main/resources/db/migration/V005__Create_option_detail.sql b/src/main/resources/db/migration/V005__Create_option_detail.sql new file mode 100644 index 0000000..5a84814 --- /dev/null +++ b/src/main/resources/db/migration/V005__Create_option_detail.sql @@ -0,0 +1,15 @@ +DROP TABLE IF EXISTS `option_detail`; + +CREATE TABLE `option_detail` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '옵션 상세 ID', + `name` varchar(50) NOT NULL COMMENT '옵션 상세명', + `extra_price` decimal(10, 2) NOT NULL COMMENT '옵션 추가 요금', + `created_at` datetime NOT NULL COMMENT '옵션 상세 등록날짜', + `created_by` varchar(20) NOT NULL COMMENT '옵션 상세 등록자', + `updated_at` datetime COMMENT '옵션 상세 변경날짜', + `updated_by` varchar(20) NULL COMMENT '옵션 상세 변경자', + `option_id` bigint unsigned NOT NULL COMMENT '연관된 옵션 ID', + PRIMARY KEY (`id`), + KEY `option_id` (`option_id`), + FOREIGN KEY (`option_id`) REFERENCES `menu_option` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; \ No newline at end of file diff --git a/src/main/resources/db/seed/R__Insert_Seed_cafe.sql b/src/main/resources/db/seed/R__Insert_Seed_cafe.sql new file mode 100644 index 0000000..362b3c3 --- /dev/null +++ b/src/main/resources/db/seed/R__Insert_Seed_cafe.sql @@ -0,0 +1,20 @@ +INSERT IGNORE INTO `cafe` (name, address, phone_number, total_rate, description, created_at, created_by, updated_at, updated_by) +VALUES ('비니카페', '서울시 동대문구 전농로', '01011112222', 3.98, '언제나 상쾌한 비니카페', now(), 'system', null, null); + +INSERT IGNORE INTO `cafe` (name, address, phone_number, total_rate, description, created_at, created_by, updated_at, updated_by) +VALUES ('조이카페', '서울시 영등포구', '01033334444', 4.67, '언제나 상쾌한 조이카페', now(), 'system', null, null); + +INSERT IGNORE INTO `cafe` (name, address, phone_number, total_rate, description, created_at, created_by, updated_at, updated_by) +VALUES ('abc카페', '서울시 서대문구', '01025341432', 4.89, '언제나 상쾌한 abc카페', now(), 'system', null, null); + +INSERT IGNORE INTO `cafe` (name, address, phone_number, total_rate, description, created_at, created_by, updated_at, updated_by) +VALUES ('동네주변카페', '서울시 송파구', '01022223333', 4.23, '언제나 상쾌한 동네주변카페', now(), 'system', null, null); + +INSERT IGNORE INTO `cafe` (name, address, phone_number, total_rate, description, created_at, created_by, updated_at, updated_by) +VALUES ('방긋카페', '서울시 광진구', '01099998888', 4.35, '언제나 상쾌한 방긋카페', now(), 'system', null, null); + +INSERT IGNORE INTO `cafe` (name, address, phone_number, total_rate, description, created_at, created_by, updated_at, updated_by) +VALUES ('example cafe1', '서울시 종로구', '01077779999', 2.85, '언제나 상쾌한 example cafe1', now(), 'system', null, null); + +INSERT IGNORE INTO `cafe` (name, address, phone_number, total_rate, description, created_at, created_by, updated_at, updated_by) +VALUES ('example cafe2', '서울시', '01044445555', 3.12, '언제나 상쾌한 example cafe2', now(), 'system', null, null); \ No newline at end of file diff --git a/src/main/resources/db/seed/R__Insert_Seed_cafe_image.sql b/src/main/resources/db/seed/R__Insert_Seed_cafe_image.sql new file mode 100644 index 0000000..6522029 --- /dev/null +++ b/src/main/resources/db/seed/R__Insert_Seed_cafe_image.sql @@ -0,0 +1,26 @@ +DROP PROCEDURE IF EXISTS insertCafeImages; + +DELIMITER $$ +CREATE PROCEDURE insertCafeImages() +BEGIN + DECLARE i INT DEFAULT 0; + DECLARE j INT; + DECLARE idx_img INT DEFAULT 1; + DECLARE var_cafe_id binary(16); + DECLARE count_cafe INT; + SET count_cafe = (SELECT COUNT(*) FROM `cafe`); + WHILE(i <= count_cafe) DO + SET j = 1; + SET var_cafe_id = (SELECT id FROM `cafe` LIMIT i, 1); + WHILE(j <= 3) DO + INSERT IGNORE INTO `cafe_image` (img_url, created_at, created_by, updated_at, updated_by, cafe_id) + VALUES (CONCAT('test_img_url_', idx_img), now(), 'system', null, null, var_cafe_id); + SET j = j + 1; + SET idx_img = idx_img + 1; + END WHILE; + SET i = i + 1; + END WHILE; +END$$ +DELIMITER ; + +CALL insertCafeImages(); \ No newline at end of file diff --git a/src/main/resources/db/seed/R__Insert_Seed_cafe_menu.sql b/src/main/resources/db/seed/R__Insert_Seed_cafe_menu.sql new file mode 100644 index 0000000..2151e40 --- /dev/null +++ b/src/main/resources/db/seed/R__Insert_Seed_cafe_menu.sql @@ -0,0 +1,22 @@ +DROP PROCEDURE IF EXISTS insertCafeMenus; + +DELIMITER $$ +CREATE PROCEDURE insertCafeMenus() +BEGIN + DECLARE i INT DEFAULT 0; + DECLARE j INT; + DECLARE var_cafe_id binary(16); + WHILE(i <= 4) DO + SET j = 1; + SET var_cafe_id = (SELECT id FROM `cafe` LIMIT i, 1); + WHILE(j <= 10) DO + INSERT IGNORE INTO `cafe_menu` (name, price, created_at, created_by, updated_at, updated_by, cafe_id) + VALUES (CONCAT('커피', j), FLOOR(RAND() * 10 + 1) * 1000, now(), 'system', null, null, var_cafe_id); + SET j = j + 1; + END WHILE; + SET i = i + 1; + END WHILE; +END$$ +DELIMITER ; + +CALL insertCafeMenus(); \ No newline at end of file