From 9eb7c75903af65279e2d8b16862daf038bd324a4 Mon Sep 17 00:00:00 2001 From: Donghun Won Date: Tue, 24 Sep 2024 17:27:52 +0900 Subject: [PATCH 01/10] =?UTF-8?q?Fix=20:=20main=20ci=EC=8B=9C=20=EB=AC=B4?= =?UTF-8?q?=ED=95=9C=EB=A1=9C=EB=94=A9=20=EB=90=98=EB=8A=94=20=EB=AC=B8?= =?UTF-8?q?=EC=A0=9C=20=20(#209)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix : main ci시 jar 파일 문법 수정 --------- Co-authored-by: hyeonjaez Co-authored-by: Fiat_lux <50399586+hyeonjaez@users.noreply.github.com> Co-authored-by: SK\ssssk Co-authored-by: 노현진 --- .github/workflows/deploy.yml | 2 +- gradlew | 0 src/main/resources/category.sql | 22 ++ src/main/resources/gathering_category.sql | 3 + src/main/resources/schema.sql | 314 ++++++++++++++++++++++ src/main/resources/schema_test.sql | 255 ++++++++++++++++++ src/main/resources/zone_category.sql | 281 +++++++++++++++++++ 7 files changed, 876 insertions(+), 1 deletion(-) mode change 100644 => 100755 gradlew create mode 100644 src/main/resources/category.sql create mode 100644 src/main/resources/gathering_category.sql create mode 100644 src/main/resources/schema.sql create mode 100644 src/main/resources/schema_test.sql create mode 100644 src/main/resources/zone_category.sql diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index d29e776..05e2e3b 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -69,7 +69,7 @@ jobs: mv /home/ubuntu/solitour-server/tobe/project.jar /home/ubuntu/solitour-server/current/project.jar cd /home/ubuntu/solitour-server/current sudo fuser -k -n tcp 8080 || true - nohup java -jar -Dspring.profiles.active=prod project.jar & > ./output.log 2>&1 & + nohup java -jar -Dspring.profiles.active=prod project.jar > ./output.log 2>&1 & rm -rf /home/ubuntu/solitour-server/tobe - name: SSH로 EC2 접속 diff --git a/gradlew b/gradlew old mode 100644 new mode 100755 diff --git a/src/main/resources/category.sql b/src/main/resources/category.sql new file mode 100644 index 0000000..24ee1e9 --- /dev/null +++ b/src/main/resources/category.sql @@ -0,0 +1,22 @@ +INSERT INTO `category` (`parent_category_id`, `category_name`) +VALUES (NULL, '맛집'), + (NULL, '숙박'), + (NULL, '액티비티'); + +INSERT INTO `category` (`parent_category_id`, `category_name`) +VALUES (1, '혼카페'), + (1, '혼밥'), + (1, '혼술'); + +INSERT INTO `category` (`parent_category_id`, `category_name`) +VALUES (2, '호텔/펜션'), + (2, '게스트하우스'), + (2, '모텔'), + (2, '홈/빌라'), + (2, '한옥'); + +INSERT INTO `category` (`parent_category_id`, `category_name`) +VALUES (3, '수상레저'), + (3, '관광지'), + (3, '전시'), + (3, '편집/소품샵'); diff --git a/src/main/resources/gathering_category.sql b/src/main/resources/gathering_category.sql new file mode 100644 index 0000000..73bb723 --- /dev/null +++ b/src/main/resources/gathering_category.sql @@ -0,0 +1,3 @@ +INSERT INTO `gathering_category` (`gathering_category_name`) +VALUES ('취미'), + ('활동'); \ No newline at end of file diff --git a/src/main/resources/schema.sql b/src/main/resources/schema.sql new file mode 100644 index 0000000..80fd873 --- /dev/null +++ b/src/main/resources/schema.sql @@ -0,0 +1,314 @@ +DROP TABLE IF EXISTS `great_information`; +DROP TABLE IF EXISTS `great_gathering`; +DROP TABLE IF EXISTS `book_mark_information`; +DROP TABLE IF EXISTS `book_mark_gathering`; +DROP TABLE IF EXISTS `info_tag`; +DROP TABLE IF EXISTS `gathering_tag`; +DROP TABLE IF EXISTS `comment`; +DROP TABLE IF EXISTS `image`; +DROP TABLE IF EXISTS `information`; +DROP TABLE IF EXISTS `gathering_applicants`; +DROP TABLE IF EXISTS `gathering`; +DROP TABLE IF EXISTS `tag`; +DROP TABLE IF EXISTS `category`; +DROP TABLE IF EXISTS `gathering_category`; +DROP TABLE IF EXISTS `zone_category`; +DROP TABLE IF EXISTS `place`; +DROP TABLE IF EXISTS `token`; +DROP TABLE IF EXISTS `banner`; +DROP TABLE IF EXISTS `notice`; +DROP TABLE IF EXISTS `qna_message`; +DROP TABLE IF EXISTS `qna`; +DROP TABLE IF EXISTS `user`; +DROP TABLE IF EXISTS `user_image`; +DROP TABLE IF EXISTS `diary_day_content`; +DROP TABLE IF EXISTS `diary`; + +CREATE TABLE `user_image` +( + `user_image_id` BIGINT NOT NULL AUTO_INCREMENT, + `user_image_address` VARCHAR(200) NOT NULL, + `user_image_created_date` DATE NOT NULL, + CONSTRAINT PK_user_image PRIMARY KEY (`user_image_id`) +); + +CREATE TABLE `user` +( + `user_id` BIGINT NOT NULL AUTO_INCREMENT, + `user_image_id` BIGINT NOT NULL, + `user_status_id` VARCHAR(20) NOT NULL, + `user_oauth_id` VARCHAR(100) NULL UNIQUE, + `provider` VARCHAR(10) NULL, + `user_nickname` VARCHAR(30) NULL, + `user_name` VARCHAR(20) NULL, + `user_age` INT NULL, + `user_sex` VARCHAR(10) NULL, + `user_email` VARCHAR(30) NULL, + `user_phone_number` VARCHAR(13) NULL, + `is_admin` BOOLEAN NOT NULL, + `user_latest_login_at` DATETIME NULL, + `user_created_at` DATETIME NOT NULL, + `user_deleted_at` DATETIME NULL, + CONSTRAINT PK_USER PRIMARY KEY (`user_id`), + CONSTRAINT FK_USER_IMAGE_TO_USER FOREIGN KEY (`user_image_id`) REFERENCES `user_image` (`user_image_id`) +); + +CREATE TABLE `token` +( + `token_id` BIGINT NOT NULL AUTO_INCREMENT, + `user_id` BIGINT NOT NULL, + `refresh_token` VARCHAR(250) NULL, + `oauth_token` VARCHAR(250) NULL, + CONSTRAINT PK_TOKEN PRIMARY KEY (`token_id`), + CONSTRAINT FK_USER_TO_TOKEN FOREIGN KEY (`user_id`) REFERENCES `user` (`user_id`) +); + +CREATE TABLE `place` +( + `place_id` BIGINT NOT NULL AUTO_INCREMENT, + `place_search_id` VARCHAR(30) NULL, + `place_name` VARCHAR(30) NOT NULL, + `place_x_axis` DECIMAL(10, 7) NOT NULL, + `place_y_axis` DECIMAL(10, 7) NOT NULL, + `place_address` VARCHAR(50) NOT NULL, + CONSTRAINT PK_PLACE PRIMARY KEY (`place_id`) +); + +CREATE TABLE `zone_category` +( + `zone_category_id` BIGINT NOT NULL AUTO_INCREMENT, + `parent_zone_category_id` BIGINT NULL, + `zone_category_name` VARCHAR(20) NOT NULL, + CONSTRAINT PK_ZONE_CATEGORY PRIMARY KEY (`zone_category_id`), + CONSTRAINT FK_zone_category_TO_zone_category FOREIGN KEY (`parent_zone_category_id`) REFERENCES `zone_category` (`zone_category_id`) +); + +CREATE TABLE `category` +( + `category_id` BIGINT NOT NULL AUTO_INCREMENT, + `parent_category_id` BIGINT NULL, + `category_name` VARCHAR(20) NOT NULL, + CONSTRAINT PK_CATEGORY PRIMARY KEY (`category_id`), + CONSTRAINT FK_category_TO_category FOREIGN KEY (`parent_category_id`) REFERENCES `category` (`category_id`) +); + +CREATE TABLE `gathering_category` +( + `gathering_category_id` BIGINT NOT NULL AUTO_INCREMENT, + `gathering_category_name` VARCHAR(20) NOT NULL, + CONSTRAINT PK_GATHERING_CATEGORY PRIMARY KEY (`gathering_category_id`) +); + +CREATE TABLE `information` +( + `information_id` BIGINT NOT NULL AUTO_INCREMENT, + `category_id` BIGINT NOT NULL, + `zone_category_id` BIGINT NOT NULL, + `user_id` BIGINT NOT NULL, + `place_id` BIGINT NOT NULL, + `information_title` VARCHAR(50) NOT NULL, + `information_address` VARCHAR(50) NOT NULL, + `information_created_date` DATETIME NOT NULL, + `information_view_count` INT NOT NULL DEFAULT 0, + `information_content` TEXT NULL, + `information_tip` TEXT NULL, + CONSTRAINT PK_information PRIMARY KEY (`information_id`), + CONSTRAINT FK_category_TO_information FOREIGN KEY (`category_id`) REFERENCES `category` (`category_id`), + CONSTRAINT FK_zone_category_TO_information FOREIGN KEY (`zone_category_id`) REFERENCES `zone_category` (`zone_category_id`), + CONSTRAINT FK_user_TO_information FOREIGN KEY (`user_id`) REFERENCES `user` (`user_id`), + CONSTRAINT FK_place_TO_information FOREIGN KEY (`place_id`) REFERENCES `place` (`place_id`) +); + +CREATE TABLE `gathering` +( + `gathering_id` BIGINT NOT NULL AUTO_INCREMENT, + `user_id` BIGINT NOT NULL, + `zone_category_id` BIGINT NOT NULL, + `gathering_category_id` BIGINT NOT NULL, + `place_id` BIGINT NOT NULL, + `gathering_title` VARCHAR(50) NULL, + `gathering_content` TEXT NULL, + `gathering_person_count` INT NULL, + `gathering_view_count` INT NULL, + `gathering_created_at` DATETIME NULL, + `gathering_edited_at` DATETIME NULL, + `gathering_schedule_start_date` DATETIME NULL, + `gathering_schedule_end_date` DATETIME NULL, + `gathering_is_finish` BOOLEAN NULL, + `gathering_deadline` DATETIME NULL, + `gathering_allowed_sex` VARCHAR(30) NOT NULL, + `gathering_start_age` INT NOT NULL, + `gathering_end_age` INT NOT NULL, + `gathering_is_deleted` BOOLEAN NOT NULL DEFAULT FALSE, + `gathering_open_chatting_url` VARCHAR(255) NULL, + + CONSTRAINT PK_gathering PRIMARY KEY (`gathering_id`), + CONSTRAINT FK_user_TO_gathering FOREIGN KEY (`user_id`) REFERENCES `user` (`user_id`), + CONSTRAINT FK_gathering_category_TO_information FOREIGN KEY (`gathering_category_id`) REFERENCES `gathering_category` (`gathering_category_id`), + CONSTRAINT FK_zone_category_TO_gathering FOREIGN KEY (`zone_category_id`) REFERENCES `zone_category` (`zone_category_id`), + CONSTRAINT FK_place_TO_gathering FOREIGN KEY (`place_id`) REFERENCES `place` (`place_id`) +); + +CREATE TABLE `gathering_applicants` +( + `gathering_applicants_id` BIGINT NOT NULL AUTO_INCREMENT, + `gathering_id` BIGINT NOT NULL, + `user_id` BIGINT NOT NULL, + `gathering_applicants_state` VARCHAR(20) NOT NULL, + CONSTRAINT PK_GATHERING_APPLICANTS PRIMARY KEY (`gathering_applicants_id`), + CONSTRAINT FK_GATHERING_TO_GATHERING_APPLICANTS FOREIGN KEY (`gathering_id`) REFERENCES `gathering` (`gathering_id`), + CONSTRAINT FK_user_TO_GATHERING_APPLICANTS FOREIGN KEY (`user_id`) REFERENCES `user` (`user_id`) +); + +CREATE TABLE `image` +( + `image_id` BIGINT NOT NULL AUTO_INCREMENT, + `image_status_id` VARCHAR(20) NOT NULL, + `information_id` BIGINT NOT NULL, + `image_address` VARCHAR(200) NOT NULL, + `image_created_date` DATE NOT NULL, + CONSTRAINT PK_image PRIMARY KEY (`image_id`), + CONSTRAINT FK_information_id_TO_image FOREIGN KEY (`information_id`) REFERENCES `information` (`information_id`) +); + +CREATE TABLE `great_information` +( + `great_information_id` BIGINT NOT NULL AUTO_INCREMENT, + `user_id` BIGINT NOT NULL, + `information_id` BIGINT NOT NULL, + CONSTRAINT PK_great_information PRIMARY KEY (`great_information_id`), + CONSTRAINT FK_great_information_TO_user FOREIGN KEY (`user_id`) REFERENCES `user` (`user_id`), + CONSTRAINT FK_great_information_TO_information FOREIGN KEY (`information_id`) REFERENCES `information` (`information_id`), + CONSTRAINT UK_great_information UNIQUE (`user_id`, `information_id`) +); + +CREATE TABLE `great_gathering` +( + `great_gathering_id` BIGINT NOT NULL AUTO_INCREMENT, + `user_id` BIGINT NOT NULL, + `gathering_id` BIGINT NOT NULL, + CONSTRAINT PK_great_gathering PRIMARY KEY (`great_gathering_id`), + CONSTRAINT FK_great_gathering_TO_user FOREIGN KEY (`user_id`) REFERENCES `user` (`user_id`), + CONSTRAINT FK_great_gathering_TO_gathering FOREIGN KEY (`gathering_id`) REFERENCES `gathering` (`gathering_id`), + CONSTRAINT UK_great_gathering UNIQUE (`user_id`, `gathering_id`) +); + +CREATE TABLE `book_mark_information` +( + `book_mark_information_id` BIGINT NOT NULL AUTO_INCREMENT, + `user_id` BIGINT NOT NULL, + `information_id` BIGINT NOT NULL, + CONSTRAINT PK_book_mark_information PRIMARY KEY (`book_mark_information_id`), + CONSTRAINT FK_book_mark_information_TO_user FOREIGN KEY (`user_id`) REFERENCES `user` (`user_id`), + CONSTRAINT FK_book_mark_information_TO_information FOREIGN KEY (`information_id`) REFERENCES `information` (`information_id`), + CONSTRAINT UK_book_mark_information UNIQUE (`user_id`, `information_id`) +); + +CREATE TABLE `book_mark_gathering` +( + `book_mark_gathering_id` BIGINT NOT NULL AUTO_INCREMENT, + `user_id` BIGINT NOT NULL, + `gathering_id` BIGINT NOT NULL, + CONSTRAINT PK_book_mark_gathering PRIMARY KEY (`book_mark_gathering_id`), + CONSTRAINT FK_book_mark_gathering_TO_user FOREIGN KEY (`user_id`) REFERENCES `user` (`user_id`), + CONSTRAINT FK_book_mark_gathering_TO_gathering FOREIGN KEY (`gathering_id`) REFERENCES `gathering` (`gathering_id`), + CONSTRAINT UK_book_mark_gathering UNIQUE (`user_id`, `gathering_id`) +); + +CREATE TABLE `tag` +( + `tag_id` BIGINT NOT NULL AUTO_INCREMENT, + `tag_name` VARCHAR(16) NOT NULL, + CONSTRAINT PK_tag PRIMARY KEY (`tag_id`) +); + +CREATE TABLE `info_tag` +( + `info_tag_id` BIGINT NOT NULL AUTO_INCREMENT, + `tag_id` BIGINT NOT NULL, + `information_id` BIGINT NOT NULL, + CONSTRAINT PK_info_tag PRIMARY KEY (`info_tag_id`), + CONSTRAINT FK_info_tag_TO_tag FOREIGN KEY (`tag_id`) REFERENCES `tag` (`tag_id`), + CONSTRAINT FK_info_tag_TO_information FOREIGN KEY (`information_id`) REFERENCES `information` (`information_id`), + CONSTRAINT UK_info_tag UNIQUE (`tag_id`, `information_id`) +); + +CREATE TABLE `gathering_tag` +( + `gathering_tag_id` BIGINT NOT NULL AUTO_INCREMENT, + `tag_id` BIGINT NOT NULL, + `gathering_id` BIGINT NOT NULL, + CONSTRAINT PK_gathering_tag PRIMARY KEY (`gathering_tag_id`), + CONSTRAINT FK_gathering_tag_TO_tag FOREIGN KEY (`tag_id`) REFERENCES `tag` (`tag_id`), + CONSTRAINT FK_gathering_tag_TO_gathering FOREIGN KEY (`gathering_id`) REFERENCES `gathering` (`gathering_id`), + CONSTRAINT UK_gathering_tag UNIQUE (`tag_id`, `gathering_id`) +); + +CREATE TABLE `banner` +( + `id` BIGINT NOT NULL AUTO_INCREMENT, + `name` VARCHAR(255) NOT NULL, + `url` VARCHAR(255) NOT NULL, + PRIMARY KEY (`id`) +); + +CREATE TABLE `qna` +( + `qna_id` BIGINT NOT NULL AUTO_INCREMENT, + `qna_category_name` VARCHAR(255) DEFAULT NULL, + `qna_created_at` DATETIME DEFAULT NULL, + `qna_status` VARCHAR(255) DEFAULT NULL, + `qna_title` VARCHAR(255) DEFAULT NULL, + `qna_updated_at` DATETIME DEFAULT NULL, + `user_id` BIGINT DEFAULT NULL, + PRIMARY KEY (`qna_id`), + CONSTRAINT FK_qna_user FOREIGN KEY (`user_id`) REFERENCES `user` (`user_id`) +); + +CREATE TABLE `qna_message` +( + `id` BIGINT NOT NULL AUTO_INCREMENT, + `qna_message_content` TEXT DEFAULT NULL, + `qna_message_created_at` DATETIME DEFAULT NULL, + `qna_message_user_id` BIGINT DEFAULT NULL, + `qna_id` BIGINT DEFAULT NULL, + PRIMARY KEY (`id`), + CONSTRAINT FK_qna_message_qna FOREIGN KEY (`qna_id`) REFERENCES `qna` (`qna_id`), + CONSTRAINT FK_qna_message_user FOREIGN KEY (`qna_message_user_id`) REFERENCES `user` (`user_id`) +); + +CREATE TABLE `notice` +( + `notice_id` BIGINT NOT NULL AUTO_INCREMENT, + `notice_category_name` VARCHAR(255) DEFAULT NULL, + `notice_content` TEXT DEFAULT NULL, + `notice_created_at` DATETIME DEFAULT NULL, + `notice_is_deleted` BOOLEAN DEFAULT FALSE, + `notice_title` VARCHAR(255) DEFAULT NULL, + PRIMARY KEY (`notice_id`) +); + +CREATE TABLE `diary` +( + `diary_id` BIGINT NOT NULL AUTO_INCREMENT, + `user_id` BIGINT NOT NULL, + `diary_title` VARCHAR(50) NOT NULL, + `diary_title_image` VARCHAR(200) DEFAULT NULL, + `diary_start_date` DATETIME NOT NULL, + `diary_end_date` DATETIME NOT NULL, + `diary_created_date` DATETIME NOT NULL, + `diary_edited_date` DATETIME DEFAULT NULL, + PRIMARY KEY (`diary_id`) +); + +CREATE TABLE `diary_day_content` +( + `diary_day_content_id` BIGINT NOT NULL AUTO_INCREMENT, + `diary_id` BIGINT NOT NULL, + `diary_day_content_place` VARCHAR(50) NOT NULL, + `diary_day_content_content` TEXT NOT NULL, + `diary_day_content_feeling_status` VARCHAR(20) NOT NULL, + `diary_day_content_image` TEXT DEFAULT NULL, + PRIMARY KEY (`diary_day_content_id`), + CONSTRAINT `FK_diary_day_content_TO_diary` FOREIGN KEY (`diary_id`) REFERENCES `diary` (`diary_id`) +); \ No newline at end of file diff --git a/src/main/resources/schema_test.sql b/src/main/resources/schema_test.sql new file mode 100644 index 0000000..b1b3576 --- /dev/null +++ b/src/main/resources/schema_test.sql @@ -0,0 +1,255 @@ +-- 기존 테이블 삭제 +DROP TABLE IF EXISTS "user_image"; +DROP TABLE IF EXISTS "user"; +DROP TABLE IF EXISTS "token"; +DROP TABLE IF EXISTS "place"; +DROP TABLE IF EXISTS "zone_category"; +DROP TABLE IF EXISTS "category"; +DROP TABLE IF EXISTS "gathering_category"; +DROP TABLE IF EXISTS "information"; +DROP TABLE IF EXISTS "gathering"; +DROP TABLE IF EXISTS "gathering_applicants"; +DROP TABLE IF EXISTS "image"; +DROP TABLE IF EXISTS "great_information"; +DROP TABLE IF EXISTS "great_gathering"; +DROP TABLE IF EXISTS "book_mark_information"; +DROP TABLE IF EXISTS "book_mark_gathering"; +DROP TABLE IF EXISTS "tag"; +DROP TABLE IF EXISTS "info_tag"; +DROP TABLE IF EXISTS "gathering_tag"; +DROP TABLE IF EXISTS "banner"; +DROP TABLE IF EXISTS "qna"; +DROP TABLE IF EXISTS "qna_message"; +DROP TABLE IF EXISTS "notice"; +DROP TABLE IF EXISTS "diary"; +DROP TABLE IF EXISTS "diary_day_content"; + +-- 테이블 생성 +CREATE TABLE "user_image" ( + "user_image_id" BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY, + "user_image_address" VARCHAR(200) NOT NULL, + "user_image_created_date" DATE NOT NULL +); + +CREATE TABLE "user" ( + "user_id" BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY, + "user_image_id" BIGINT NOT NULL, + "user_status_id" VARCHAR(20) NOT NULL, + "user_oauth_id" VARCHAR(100), + "provider" VARCHAR(10), + "user_nickname" VARCHAR(30), + "user_name" VARCHAR(20), + "user_age" INT, + "user_sex" VARCHAR(10), + "user_email" VARCHAR(30), + "user_phone_number" VARCHAR(13), + "is_admin" BOOLEAN NOT NULL, + "user_latest_login_at" DATETIME, + "user_created_at" DATETIME NOT NULL, + "user_deleted_at" DATETIME, + FOREIGN KEY ("user_image_id") REFERENCES "user_image" ("user_image_id") +); + +CREATE TABLE "token" ( + "token_id" BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY, + "user_id" BIGINT NOT NULL, + "refresh_token" VARCHAR(250) NOT NULL, + FOREIGN KEY ("user_id") REFERENCES "user" ("user_id") +); + +CREATE TABLE "place" ( + "place_id" BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY, + "place_search_id" VARCHAR(30), + "place_name" VARCHAR(30) NOT NULL, + "place_x_axis" DECIMAL(10, 7) NOT NULL, + "place_y_axis" DECIMAL(10, 7) NOT NULL, + "place_address" VARCHAR(50) NOT NULL +); + +CREATE TABLE "zone_category" ( + "zone_category_id" BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY, + "parent_zone_category_id" BIGINT, + "zone_category_name" VARCHAR(20) NOT NULL, + FOREIGN KEY ("parent_zone_category_id") REFERENCES "zone_category" ("zone_category_id") +); + +CREATE TABLE "category" ( + "category_id" BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY, + "parent_category_id" BIGINT, + "category_name" VARCHAR(20) NOT NULL, + FOREIGN KEY ("parent_category_id") REFERENCES "category" ("category_id") +); + +CREATE TABLE "gathering_category" ( + "gathering_category_id" BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY, + "gathering_category_name" VARCHAR(20) NOT NULL +); + +CREATE TABLE "information" ( + "information_id" BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY, + "category_id" BIGINT NOT NULL, + "zone_category_id" BIGINT NOT NULL, + "user_id" BIGINT NOT NULL, + "place_id" BIGINT NOT NULL, + "information_title" VARCHAR(50) NOT NULL, + "information_address" VARCHAR(50) NOT NULL, + "information_created_date" DATETIME NOT NULL, + "information_view_count" INT NOT NULL DEFAULT 0, + "information_content" TEXT, + "information_tip" TEXT, + FOREIGN KEY ("category_id") REFERENCES "category" ("category_id"), + FOREIGN KEY ("zone_category_id") REFERENCES "zone_category" ("zone_category_id"), + FOREIGN KEY ("user_id") REFERENCES "user" ("user_id"), + FOREIGN KEY ("place_id") REFERENCES "place" ("place_id") +); + +CREATE TABLE "gathering" ( + "gathering_id" BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY, + "user_id" BIGINT NOT NULL, + "zone_category_id" BIGINT NOT NULL, + "gathering_category_id" BIGINT NOT NULL, + "place_id" BIGINT NOT NULL, + "gathering_title" VARCHAR(50), + "gathering_content" TEXT, + "gathering_person_count" INT, + "gathering_view_count" INT, + "gathering_created_at" DATETIME, + "gathering_edited_at" DATETIME, + "gathering_schedule_start_date" DATETIME, + "gathering_schedule_end_date" DATETIME, + "gathering_is_finish" BOOLEAN, + "gathering_deadline" DATETIME, + "gathering_allowed_sex" VARCHAR(30) NOT NULL, + "gathering_start_age" INT NOT NULL, + "gathering_end_age" INT NOT NULL, + "gathering_is_deleted" BOOLEAN NOT NULL DEFAULT FALSE, + "gathering_open_chatting_url" VARCHAR(255), + FOREIGN KEY ("user_id") REFERENCES "user" ("user_id"), + FOREIGN KEY ("gathering_category_id") REFERENCES "gathering_category" ("gathering_category_id"), + FOREIGN KEY ("zone_category_id") REFERENCES "zone_category" ("zone_category_id"), + FOREIGN KEY ("place_id") REFERENCES "place" ("place_id") +); + +CREATE TABLE "gathering_applicants" ( + "gathering_applicants_id" BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY, + "gathering_id" BIGINT NOT NULL, + "user_id" BIGINT NOT NULL, + "gathering_applicants_state" VARCHAR(20) NOT NULL, + FOREIGN KEY ("gathering_id") REFERENCES "gathering" ("gathering_id"), + FOREIGN KEY ("user_id") REFERENCES "user" ("user_id") +); + +CREATE TABLE "image" ( + "image_id" BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY, + "image_status_id" VARCHAR(20) NOT NULL, + "information_id" BIGINT NOT NULL, + "image_address" VARCHAR(200) NOT NULL, + "image_created_date" DATE NOT NULL, + FOREIGN KEY ("information_id") REFERENCES "information" ("information_id") +); + +CREATE TABLE "great_information" ( + "great_information_id" BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY, + "user_id" BIGINT NOT NULL, + "information_id" BIGINT NOT NULL, + FOREIGN KEY ("user_id") REFERENCES "user" ("user_id"), + FOREIGN KEY ("information_id") REFERENCES "information" ("information_id"), + UNIQUE ("user_id", "information_id") +); + +CREATE TABLE "great_gathering" ( + "great_gathering_id" BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY, + "user_id" BIGINT NOT NULL, + "gathering_id" BIGINT NOT NULL, + FOREIGN KEY ("user_id") REFERENCES "user" ("user_id"), + FOREIGN KEY ("gathering_id") REFERENCES "gathering" ("gathering_id"), + UNIQUE ("user_id", "gathering_id") +); + +CREATE TABLE "book_mark_information" ( + "book_mark_information_id" BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY, + "user_id" BIGINT NOT NULL, + "information_id" BIGINT NOT NULL, + FOREIGN KEY ("user_id") REFERENCES "user" ("user_id"), + FOREIGN KEY ("information_id") REFERENCES "information" ("information_id"), + UNIQUE ("user_id", "information_id") +); + +CREATE TABLE "book_mark_gathering" ( + "book_mark_gathering_id" BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY, + "user_id" BIGINT NOT NULL, + "gathering_id" BIGINT NOT NULL, + FOREIGN KEY ("user_id") REFERENCES "user" ("user_id"), + FOREIGN KEY ("gathering_id") REFERENCES "gathering" ("gathering_id"), + UNIQUE ("user_id", "gathering_id") +); + +CREATE TABLE "tag" ( + "tag_id" BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY, + "tag_name" VARCHAR(16) NOT NULL +); + +CREATE TABLE "info_tag" ( + "info_tag_id" BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY, + "tag_id" BIGINT NOT NULL, + "information_id" BIGINT NOT NULL, + FOREIGN KEY ("tag_id") REFERENCES "tag" ("tag_id"), + FOREIGN KEY ("information_id") REFERENCES "information" ("information_id"), + UNIQUE ("tag_id", "information_id") +); + +CREATE TABLE "gathering_tag" ( + "gathering_tag_id" BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY, + "tag_id" BIGINT NOT NULL, + "gathering_id" BIGINT NOT NULL, + FOREIGN KEY ("tag_id") REFERENCES "tag" ("tag_id"), + FOREIGN KEY ("gathering_id") REFERENCES "gathering" ("gathering_id"), + UNIQUE ("tag_id", "gathering_id") +); + +CREATE TABLE "banner" ( + "id" BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY, + "name" VARCHAR(255) NOT NULL, + "url" VARCHAR(255) NOT NULL +); + +CREATE TABLE "qna" ( + "qna_id" BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY, + "qna_category_name" VARCHAR(255), + "qna_created_at" DATETIME, + "qna_status" VARCHAR(255), + "qna_title" VARCHAR(255), + "qna_updated_at" DATETIME, + "user_id" BIGINT, + FOREIGN KEY ("user_id") REFERENCES "user" ("user_id") +); + +CREATE TABLE "qna_message" ( + "id" BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY, + "qna_message_content" TEXT, + "qna_message_created_at" DATETIME, + "qna_message_user_id" BIGINT, + "qna_id" BIGINT, + FOREIGN KEY ("qna_id") REFERENCES "qna" ("qna_id"), + FOREIGN KEY ("qna_message_user_id") REFERENCES "user" ("user_id") +); + +CREATE TABLE "notice" ( + "notice_id" BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY, + "notice_category_name" VARCHAR(255), + "notice_content" TEXT, + "notice_created_at" DATETIME, + "notice_is_deleted" BOOLEAN DEFAULT FALSE, + "notice_title" VARCHAR(255) +); + +CREATE TABLE "diary" ( + "diary_id" BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY, + "user_id" BIGINT NOT NULL, + "diary_title" VARCHAR(50) NOT NULL, + "diary_title_image" VARCHAR(200) DEFAULT NULL, + "diary_start_date" DATETIME NOT NULL, + "diary_end_date" DATETIME NOT NULL, + "diary_created_date" DATETIME NOT NULL, + "diary_edited_date" DATETIME DEFAULT NULL +); \ No newline at end of file diff --git a/src/main/resources/zone_category.sql b/src/main/resources/zone_category.sql new file mode 100644 index 0000000..ab22ac7 --- /dev/null +++ b/src/main/resources/zone_category.sql @@ -0,0 +1,281 @@ +INSERT INTO `zone_category` (`parent_zone_category_id`, `zone_category_name`) +VALUES (NULL, '서울'), + (NULL, '광주'), + (NULL, '인천'), + (NULL, '대전'), + (NULL, '대구'), + (NULL, '전남'), + (NULL, '경북'), + (NULL, '경남'), + (NULL, '부산'), + (NULL, '울산'), + (NULL, '제주'), + (NULL, '경기'), + (NULL, '강원'), + (NULL, '충북'), + (NULL, '충남'), + (NULL, '전북'); + +INSERT INTO `zone_category` (`parent_zone_category_id`, `zone_category_name`) +VALUES (1, '강남구'), + (1, '강동구'), + (1, '강북구'), + (1, '강서구'), + (1, '관악구'), + (1, '광진구'), + (1, '구로구'), + (1, '금천구'), + (1, '노원구'), + (1, '도봉구'), + (1, '동대문구'), + (1, '동작구'), + (1, '마포구'), + (1, '서대문구'), + (1, '서초구'), + (1, '성동구'), + (1, '성북구'), + (1, '송파구'), + (1, '양천구'), + (1, '영등포구'), + (1, '용산구'), + (1, '은평구'), + (1, '종로구'), + (1, '중구'), + (1, '중랑구'); + +INSERT INTO `zone_category` (`parent_zone_category_id`, `zone_category_name`) +VALUES (2, '동구'), + (2, '서구'), + (2, '남구'), + (2, '북구'), + (2, '광산구'); + +INSERT INTO `zone_category` (`parent_zone_category_id`, `zone_category_name`) +VALUES (3, '중구'), + (3, '동구'), + (3, '미추홀구'), + (3, '연수구'), + (3, '남동구'), + (3, '부평구'), + (3, '계양구'), + (3, '서구'), + (3, '강화군'), + (3, '옹진군'); + +INSERT INTO `zone_category` (`parent_zone_category_id`, `zone_category_name`) +VALUES (4, '동구'), + (4, '중구'), + (4, '서구'), + (4, '유성구'), + (4, '대덕구'); + +INSERT INTO `zone_category` (`parent_zone_category_id`, `zone_category_name`) +VALUES (5, '중구'), + (5, '동구'), + (5, '서구'), + (5, '남구'), + (5, '북구'), + (5, '수성구'), + (5, '달서구'), + (5, '달성군'), + (5, '군위군'); + +INSERT INTO `zone_category` (`parent_zone_category_id`, `zone_category_name`) +VALUES (6, '목포시'), + (6, '여수시'), + (6, '순천시'), + (6, '나주시'), + (6, '광양시'), + (6, '담양군'), + (6, '곡성군'), + (6, '구례군'), + (6, '고흥군'), + (6, '보성군'), + (6, '화순군'), + (6, '장흥군'), + (6, '강진군'), + (6, '해남군'), + (6, '영암군'), + (6, '무안군'), + (6, '함평군'), + (6, '영광군'), + (6, '장성군'), + (6, '완도군'), + (6, '진도군'), + (6, '신안군'); + +INSERT INTO `zone_category` (`parent_zone_category_id`, `zone_category_name`) +VALUES (7, '포항시'), + (7, '경주시'), + (7, '김천시'), + (7, '안동시'), + (7, '구미시'), + (7, '영주시'), + (7, '영천시'), + (7, '상주시'), + (7, '문경시'), + (7, '경산시'), + (7, '의성군'), + (7, '청송군'), + (7, '영양군'), + (7, '영덕군'), + (7, '청도군'), + (7, '고령군'), + (7, '성주군'), + (7, '칠곡군'), + (7, '예천군'), + (7, '봉화군'), + (7, '울진군'), + (7, '울릉군'); + +INSERT INTO `zone_category` (`parent_zone_category_id`, `zone_category_name`) +VALUES (8, '창원시'), + (8, '김해시'), + (8, '진주시'), + (8, '양산시'), + (8, '거제시'), + (8, '통영시'), + (8, '사천시'), + (8, '밀양시'), + (8, '함안군'), + (8, '거창군'), + (8, '창녕군'), + (8, '고성군'), + (8, '하동군'), + (8, '합천군'), + (8, '남해군'), + (8, '함양군'), + (8, '산청군'), + (8, '의령군'); + +INSERT INTO `zone_category` (`parent_zone_category_id`, `zone_category_name`) +VALUES (9, '중구'), + (9, '서구'), + (9, '동구'), + (9, '영도구'), + (9, '부산진구'), + (9, '동래구'), + (9, '남구'), + (9, '북구'), + (9, '해운대구'), + (9, '사하구'), + (9, '금정구'), + (9, '강서구'), + (9, '연제구'), + (9, '수영구'), + (9, '사상구'), + (9, '기장군'); + +INSERT INTO `zone_category` (`parent_zone_category_id`, `zone_category_name`) +VALUES (10, '중구'), + (10, '남구'), + (10, '동구'), + (10, '북구'), + (10, '울주군'); + +INSERT INTO `zone_category` (`parent_zone_category_id`, `zone_category_name`) +VALUES (11, '서귀포시'), + (11, '제주시'); + +INSERT INTO `zone_category` (`parent_zone_category_id`, `zone_category_name`) +VALUES (12, '수원시'), + (12, '성남시'), + (12, '용인시'), + (12, '안양시'), + (12, '안산시'), + (12, '과천시'), + (12, '광명시'), + (12, '광주시'), + (12, '군포시'), + (12, '부천시'), + (12, '시흥시'), + (12, '김포시'), + (12, '안성시'), + (12, '오산시'), + (12, '의왕시'), + (12, '이천시'), + (12, '평택시'), + (12, '하남시'), + (12, '화성시'), + (12, '여주시'), + (12, '양평군'), + (12, '고양시'), + (12, '구리시'), + (12, '남양주시'), + (12, '동두천시'), + (12, '양주시'), + (12, '의정부시'), + (12, '파주시'), + (12, '포천시'), + (12, '연천군'), + (12, '가평군'); + +INSERT INTO `zone_category` (`parent_zone_category_id`, `zone_category_name`) +VALUES (13, '원주시'), + (13, '춘천시'), + (13, '강릉시'), + (13, '동해시'), + (13, '속초시'), + (13, '삼척시'), + (13, '태백시'), + (13, '홍천군'), + (13, '철원군'), + (13, '횡성군'), + (13, '평창군'), + (13, '정선군'), + (13, '영월군'), + (13, '인제군'), + (13, '고성군'), + (13, '양양군'), + (13, '화천군'), + (13, '양구군'); + +INSERT INTO `zone_category` (`parent_zone_category_id`, `zone_category_name`) +VALUES (14, '청주시'), + (14, '충주시'), + (14, '제천시'), + (14, '보은군'), + (14, '옥천군'), + (14, '영동군'), + (14, '증평군'), + (14, '진천군'), + (14, '괴산군'), + (14, '음성군'), + (14, '단양군'); + +INSERT INTO `zone_category` (`parent_zone_category_id`, `zone_category_name`) +VALUES (15, '천안시'), + (15, '공주시'), + (15, '보령시'), + (15, '아산시'), + (15, '서산시'), + (15, '논산시'), + (15, '계룡시'), + (15, '당진시'), + (15, '금산군'), + (15, '부여군'), + (15, '서천군'), + (15, '청양군'), + (15, '홍성군'), + (15, '예산군'), + (15, '태안군'); + +INSERT INTO `zone_category` (`parent_zone_category_id`, `zone_category_name`) +VALUES (16, '전주시'), + (16, '익산시'), + (16, '군산시'), + (16, '정읍시'), + (16, '김제시'), + (16, '남원시'), + (16, '완주군'), + (16, '고창군'), + (16, '부안군'), + (16, '임실군'), + (16, '순창군'), + (16, '진안군'), + (16, '무주군'), + (16, '장수군'); + +INSERT INTO `zone_category` (`parent_zone_category_id`, `zone_category_name`) VALUES (NULL, '세종'); +INSERT INTO `zone_category` (`parent_zone_category_id`, `zone_category_name`) VALUES (245, '세종'); + From 605a32993c19ace14057ac7733da7f0fac9c8168 Mon Sep 17 00:00:00 2001 From: Donghun Won Date: Thu, 26 Sep 2024 17:22:16 +0900 Subject: [PATCH 02/10] =?UTF-8?q?Feat=20:=20=EB=84=A4=EC=9D=B4=EB=B2=84=20?= =?UTF-8?q?oauth=20=EA=B8=B0=EB=8A=A5,=20=EC=B9=B4=EC=B9=B4=EC=98=A4=20oau?= =?UTF-8?q?th=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80=20(#213)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix : 카카오 회원탈퇴 오류 수정 , 카카오 추가정보 있을때 회원가입 (#210) * Feat : 카카오 추가정보 있을때 회원가입 * Fix : 카카오 회원탈퇴 오류 수정 성별이 필드가 비어있을때 조회 사용하는 로직 수정 * Feat : 네이버 oauth (#211) 회원가입, 로그인, 로그아웃, 회원탈퇴 --------- Co-authored-by: hyeonjaez Co-authored-by: Fiat_lux <50399586+hyeonjaez@users.noreply.github.com> Co-authored-by: SK\ssssk Co-authored-by: 노현진 --- .../auth/controller/OauthController.java | 42 ++++- .../auth/exception/RevokeFailException.java | 7 + .../UnsupportedLoginTypeException.java | 7 + .../exception/UserRevokeErrorException.java | 7 + .../solitour/auth/service/OauthService.java | 174 ++++++++++++++---- .../solitour/auth/service/TokenService.java | 4 +- .../dto/request/CreateUserInfoRequest.java | 12 ++ .../auth/support/naver/NaverConnector.java | 118 ++++++++++++ .../auth/support/naver/NaverProvider.java | 79 ++++++++ .../naver/dto/NaverTokenAndUserResponse.java | 17 ++ .../support/naver/dto/NaverTokenResponse.java | 17 ++ .../support/naver/dto/NaverUserResponse.java | 36 ++++ .../error/GlobalControllerAdvice.java | 13 +- 13 files changed, 488 insertions(+), 45 deletions(-) create mode 100644 src/main/java/solitour_backend/solitour/auth/exception/RevokeFailException.java create mode 100644 src/main/java/solitour_backend/solitour/auth/exception/UnsupportedLoginTypeException.java create mode 100644 src/main/java/solitour_backend/solitour/auth/exception/UserRevokeErrorException.java create mode 100644 src/main/java/solitour_backend/solitour/auth/support/kakao/dto/request/CreateUserInfoRequest.java create mode 100644 src/main/java/solitour_backend/solitour/auth/support/naver/NaverConnector.java create mode 100644 src/main/java/solitour_backend/solitour/auth/support/naver/NaverProvider.java create mode 100644 src/main/java/solitour_backend/solitour/auth/support/naver/dto/NaverTokenAndUserResponse.java create mode 100644 src/main/java/solitour_backend/solitour/auth/support/naver/dto/NaverTokenResponse.java create mode 100644 src/main/java/solitour_backend/solitour/auth/support/naver/dto/NaverUserResponse.java diff --git a/src/main/java/solitour_backend/solitour/auth/controller/OauthController.java b/src/main/java/solitour_backend/solitour/auth/controller/OauthController.java index c745848..8bec21b 100644 --- a/src/main/java/solitour_backend/solitour/auth/controller/OauthController.java +++ b/src/main/java/solitour_backend/solitour/auth/controller/OauthController.java @@ -4,11 +4,11 @@ import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; -import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @@ -18,12 +18,16 @@ import solitour_backend.solitour.auth.entity.Token; import solitour_backend.solitour.auth.entity.TokenRepository; import solitour_backend.solitour.auth.exception.TokenNotExistsException; +import solitour_backend.solitour.auth.exception.UnsupportedLoginTypeException; +import solitour_backend.solitour.auth.exception.UserRevokeErrorException; import solitour_backend.solitour.auth.service.OauthService; import solitour_backend.solitour.auth.service.dto.response.AccessTokenResponse; import solitour_backend.solitour.auth.service.dto.response.LoginResponse; import solitour_backend.solitour.auth.service.dto.response.OauthLinkResponse; import solitour_backend.solitour.auth.support.google.GoogleConnector; import solitour_backend.solitour.auth.support.kakao.KakaoConnector; +import solitour_backend.solitour.auth.support.kakao.dto.request.CreateUserInfoRequest; +import solitour_backend.solitour.auth.support.naver.NaverConnector; import solitour_backend.solitour.user.user_status.UserStatus; @@ -35,6 +39,7 @@ public class OauthController { private final OauthService oauthService; private final KakaoConnector kakaoConnector; private final GoogleConnector googleConnector; + private final NaverConnector naverConnector; private final TokenRepository tokenRepository; @GetMapping(value = "/login", params = {"type", "redirectUrl"}) @@ -43,6 +48,22 @@ public ResponseEntity access(@RequestParam String type, @Requ return ResponseEntity.ok(response); } + @PostMapping(value = "/login/kakao", params = {"code", "redirectUrl"}) + public ResponseEntity kakaoLogin(HttpServletResponse response, + @RequestParam String code, @RequestParam String redirectUrl, + @RequestBody CreateUserInfoRequest createUserInfoRequest) { + LoginResponse loginResponse = oauthService.requestkakaoAccessToken(code, redirectUrl, createUserInfoRequest); + + String accessCookieHeader = setCookieHeader(loginResponse.getAccessToken()); + String refreshCookieHeader = setCookieHeader(loginResponse.getRefreshToken()); + + response.addHeader("Set-Cookie", accessCookieHeader); + response.addHeader("Set-Cookie", refreshCookieHeader); + + return ResponseEntity.ok(loginResponse.getLoginStatus()); + } + + @GetMapping(value = "/login", params = {"type", "code", "redirectUrl"}) public ResponseEntity login(HttpServletResponse response, @RequestParam String type, @RequestParam String code, @RequestParam String redirectUrl) { @@ -78,8 +99,8 @@ public ResponseEntity reissueAccessToken(HttpServletResponse response, @Authenticated @DeleteMapping() - public ResponseEntity deleteUser(HttpServletResponse response, @AuthenticationPrincipal Long id, - @RequestParam String type) { + public ResponseEntity deleteUser(HttpServletResponse response, @AuthenticationPrincipal Long id, + @RequestParam String type) { Token token = tokenRepository.findByUserId(id) .orElseThrow(() -> new TokenNotExistsException("토큰이 존재하지 않습니다")); String oauthRefreshToken = getOauthAccessToken(type, token.getOauthToken()); @@ -89,11 +110,11 @@ public ResponseEntity deleteUser(HttpServletResponse response, @Authenti oauthService.logout(response, id); oauthService.deleteUser(id); - - return ResponseEntity.ok("User deleted successfully"); } catch (Exception e) { - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("An error occurred"); + throw new UserRevokeErrorException("회원 탈퇴 중 오류가 발생했습니다"); } + + return ResponseEntity.noContent().build(); } private String setCookieHeader(Cookie cookie) { @@ -107,10 +128,13 @@ private String getOauthAccessToken(String type, String refreshToken) { case "kakao" -> { token = kakaoConnector.refreshToken(refreshToken); } - case "google" -> { - token = googleConnector.refreshToken(refreshToken); + case "naver" -> { + token = naverConnector.refreshToken(refreshToken); } - default -> throw new RuntimeException("Unsupported oauth type"); +// case "google" -> { +// token = googleConnector.refreshToken(refreshToken); +// } + default -> throw new UnsupportedLoginTypeException("지원하지 않는 로그인 타입입니다"); } return token; } diff --git a/src/main/java/solitour_backend/solitour/auth/exception/RevokeFailException.java b/src/main/java/solitour_backend/solitour/auth/exception/RevokeFailException.java new file mode 100644 index 0000000..6bd23ec --- /dev/null +++ b/src/main/java/solitour_backend/solitour/auth/exception/RevokeFailException.java @@ -0,0 +1,7 @@ +package solitour_backend.solitour.auth.exception; + +public class RevokeFailException extends RuntimeException { + public RevokeFailException(String message) { + super(message); + } +} diff --git a/src/main/java/solitour_backend/solitour/auth/exception/UnsupportedLoginTypeException.java b/src/main/java/solitour_backend/solitour/auth/exception/UnsupportedLoginTypeException.java new file mode 100644 index 0000000..4b10d35 --- /dev/null +++ b/src/main/java/solitour_backend/solitour/auth/exception/UnsupportedLoginTypeException.java @@ -0,0 +1,7 @@ +package solitour_backend.solitour.auth.exception; + +public class UnsupportedLoginTypeException extends RuntimeException { + public UnsupportedLoginTypeException(String message) { + super(message); + } +} diff --git a/src/main/java/solitour_backend/solitour/auth/exception/UserRevokeErrorException.java b/src/main/java/solitour_backend/solitour/auth/exception/UserRevokeErrorException.java new file mode 100644 index 0000000..d69a887 --- /dev/null +++ b/src/main/java/solitour_backend/solitour/auth/exception/UserRevokeErrorException.java @@ -0,0 +1,7 @@ +package solitour_backend.solitour.auth.exception; + +public class UserRevokeErrorException extends RuntimeException { + public UserRevokeErrorException(String message) { + super(message); + } +} diff --git a/src/main/java/solitour_backend/solitour/auth/service/OauthService.java b/src/main/java/solitour_backend/solitour/auth/service/OauthService.java index 413b16c..f507a2a 100644 --- a/src/main/java/solitour_backend/solitour/auth/service/OauthService.java +++ b/src/main/java/solitour_backend/solitour/auth/service/OauthService.java @@ -14,6 +14,8 @@ import org.springframework.transaction.annotation.Transactional; import solitour_backend.solitour.auth.entity.Token; import solitour_backend.solitour.auth.entity.TokenRepository; +import solitour_backend.solitour.auth.exception.RevokeFailException; +import solitour_backend.solitour.auth.exception.UnsupportedLoginTypeException; import solitour_backend.solitour.auth.service.dto.response.AccessTokenResponse; import solitour_backend.solitour.auth.service.dto.response.LoginResponse; import solitour_backend.solitour.auth.service.dto.response.OauthLinkResponse; @@ -27,6 +29,12 @@ import solitour_backend.solitour.auth.support.kakao.dto.KakaoTokenAndUserResponse; import solitour_backend.solitour.auth.support.kakao.dto.KakaoTokenResponse; import solitour_backend.solitour.auth.support.kakao.dto.KakaoUserResponse; +import solitour_backend.solitour.auth.support.kakao.dto.request.CreateUserInfoRequest; +import solitour_backend.solitour.auth.support.naver.NaverConnector; +import solitour_backend.solitour.auth.support.naver.NaverProvider; +import solitour_backend.solitour.auth.support.naver.dto.NaverTokenAndUserResponse; +import solitour_backend.solitour.auth.support.naver.dto.NaverTokenResponse; +import solitour_backend.solitour.auth.support.naver.dto.NaverUserResponse; import solitour_backend.solitour.image.s3.S3Uploader; import solitour_backend.solitour.user.entity.User; import solitour_backend.solitour.user.exception.BlockedUserException; @@ -49,6 +57,8 @@ public class OauthService { private final KakaoProvider kakaoProvider; private final GoogleConnector googleConnector; private final GoogleProvider googleProvider; + private final NaverConnector naverConnector; + private final NaverProvider naverProvider; private final UserImageService userImageService; private final TokenRepository tokenRepository; private final UserImageRepository userImageRepository; @@ -83,6 +93,25 @@ public LoginResponse requestAccessToken(String type, String code, String redirec return new LoginResponse(accessCookie, refreshCookie, user.getUserStatus()); } + @Transactional + public LoginResponse requestkakaoAccessToken(String code, String redirectUrl, + CreateUserInfoRequest createUserInfoRequest) { + User user = checkAndSaveKakaoUser(code, redirectUrl, createUserInfoRequest); + user.updateLoginTime(); + final int ACCESS_COOKIE_AGE = (int) TimeUnit.MINUTES.toSeconds(30); + final int REFRESH_COOKIE_AGE = (int) TimeUnit.DAYS.toSeconds(30); + + String token = jwtTokenProvider.createAccessToken(user.getId()); + String refreshToken = jwtTokenProvider.createRefreshToken(user.getId()); + + tokenService.synchronizeRefreshToken(user, refreshToken); + + Cookie accessCookie = createCookie("access_token", token, ACCESS_COOKIE_AGE); + Cookie refreshCookie = createCookie("refresh_token", refreshToken, REFRESH_COOKIE_AGE); + + return new LoginResponse(accessCookie, refreshCookie, user.getUserStatus()); + } + private Cookie createCookie(String name, String value, int maxAge) { Cookie cookie = new Cookie(name, value); cookie.setSecure(true); @@ -92,6 +121,23 @@ private Cookie createCookie(String name, String value, int maxAge) { return cookie; } + private User checkAndSaveKakaoUser(String code, String redirectUrl, CreateUserInfoRequest createUserInfoRequest) { + KakaoTokenAndUserResponse response = kakaoConnector.requestKakaoUserInfo(code, redirectUrl); + KakaoTokenResponse tokenResponse = response.getKakaoTokenResponse(); + KakaoUserResponse kakaoUserResponse = response.getKakaoUserResponse(); + + String id = kakaoUserResponse.getId().toString(); + User user = userRepository.findByOauthId(id) + .orElseGet(() -> saveActiveKakaoUser(kakaoUserResponse, createUserInfoRequest)); + + checkUserStatus(user); + + Token token = tokenRepository.findByUserId(user.getId()) + .orElseGet(() -> tokenService.saveToken(tokenResponse.getRefreshToken(), user)); + + return user; + } + private User checkAndSaveUser(String type, String code, String redirectUrl) { if (Objects.equals(type, "kakao")) { KakaoTokenAndUserResponse response = kakaoConnector.requestKakaoUserInfo(code, redirectUrl); @@ -105,7 +151,23 @@ private User checkAndSaveUser(String type, String code, String redirectUrl) { checkUserStatus(user); Token token = tokenRepository.findByUserId(user.getId()) - .orElseGet(() -> tokenService.saveToken(tokenResponse, user)); + .orElseGet(() -> tokenService.saveToken(tokenResponse.getRefreshToken(), user)); + + return user; + } + if (Objects.equals(type, "naver")) { + NaverTokenAndUserResponse response = naverConnector.requestNaverUserInfo(code); + NaverTokenResponse tokenResponse = response.getNaverTokenResponse(); + NaverUserResponse naverUserResponse = response.getNaverUserResponse(); + + String id = naverUserResponse.getResponse().getId().toString(); + User user = userRepository.findByOauthId(id) + .orElseGet(() -> saveNaverUser(naverUserResponse)); + + checkUserStatus(user); + + Token token = tokenRepository.findByUserId(user.getId()) + .orElseGet(() -> tokenService.saveToken(tokenResponse.getRefreshToken(), user)); return user; } @@ -116,7 +178,38 @@ private User checkAndSaveUser(String type, String code, String redirectUrl) { return userRepository.findByOauthId(id) .orElseGet(() -> saveGoogleUser(response)); } else { - throw new RuntimeException("지원하지 않는 oauth 타입입니다."); + throw new UnsupportedLoginTypeException("지원하지 않는 oauth 로그인입니다."); + } + } + + private User saveNaverUser(NaverUserResponse naverUserResponse) { + String convertedSex = convertSex(naverUserResponse.getResponse().getGender()); + String imageUrl = getDefaultUserImage(convertedSex); + UserImage savedUserImage = userImageService.saveUserImage(imageUrl); + + User user = User.builder() + .userStatus(UserStatus.ACTIVATE) + .oauthId(String.valueOf(naverUserResponse.getResponse().getId())) + .provider("naver") + .isAdmin(false) + .userImage(savedUserImage) + .nickname(RandomNickName.generateRandomNickname()) + .email(naverUserResponse.getResponse().getEmail()) + .name(naverUserResponse.getResponse().getName()) + .age(Integer.parseInt(naverUserResponse.getResponse().getBirthyear())) + .sex(convertedSex) + .createdAt(LocalDateTime.now()) + .build(); + return userRepository.save(user); + } + + private String convertSex(String gender) { + if (gender.equals("M")) { + return "male"; + } else if (gender.equals("F")) { + return "female"; + } else { + return "none"; } } @@ -129,15 +222,6 @@ private void checkUserStatus(User user) { } } - private void saveToken(KakaoTokenResponse tokenResponse, User user) { - Token token = Token.builder() - .user(user) - .oauthToken(tokenResponse.getRefreshToken()) - .build(); - - tokenRepository.save(token); - } - private User saveGoogleUser(GoogleUserResponse response) { String imageUrl = getGoogleUserImage(response); UserImage savedUserImage = userImageService.saveUserImage(imageUrl); @@ -169,8 +253,29 @@ private String getGoogleUserImage(GoogleUserResponse response) { return USER_PROFILE_NONE; } + private User saveActiveKakaoUser(KakaoUserResponse kakaoUserResponse, CreateUserInfoRequest createUserInfoRequest) { + String imageUrl = getDefaultUserImage(createUserInfoRequest.getSex()); + UserImage savedUserImage = userImageService.saveUserImage(imageUrl); + + User user = User.builder() + .userStatus(UserStatus.ACTIVATE) + .oauthId(String.valueOf(kakaoUserResponse.getId())) + .provider("kakao") + .isAdmin(false) + .userImage(savedUserImage) + .name(createUserInfoRequest.getName()) + .sex(createUserInfoRequest.getSex()) + .nickname(RandomNickName.generateRandomNickname()) + .email(kakaoUserResponse.getKakaoAccount().getEmail()) + .name(createUserInfoRequest.getName()) + .age(Integer.valueOf(createUserInfoRequest.getAge())) + .createdAt(LocalDateTime.now()) + .build(); + return userRepository.save(user); + } + private User saveKakaoUser(KakaoUserResponse response) { - String imageUrl = getKakaoUserImage(response); + String imageUrl = getDefaultUserImage(response.getKakaoAccount().getGender()); UserImage savedUserImage = userImageService.saveUserImage(imageUrl); User user = User.builder() @@ -186,8 +291,8 @@ private User saveKakaoUser(KakaoUserResponse response) { return userRepository.save(user); } - private String getKakaoUserImage(KakaoUserResponse response) { - String gender = response.getKakaoAccount().getGender(); + + private String getDefaultUserImage(String gender) { if (Objects.equals(gender, "male")) { return USER_PROFILE_MALE; } @@ -201,7 +306,8 @@ private String getAuthLink(String type, String redirectUrl) { return switch (type) { case "kakao" -> kakaoProvider.generateAuthUrl(redirectUrl); case "google" -> googleProvider.generateAuthUrl(redirectUrl); - default -> throw new RuntimeException("지원하지 않는 oauth 타입입니다."); + case "naver" -> naverProvider.generateAuthUrl(redirectUrl); + default -> throw new UnsupportedLoginTypeException("지원하지 않는 oauth 로그인입니다."); }; } @@ -231,19 +337,18 @@ private void deleteCookie(String name, String value, HttpServletResponse respons response.addCookie(cookie); } + public void revokeToken(String type, String token) throws IOException { HttpStatusCode responseCode; switch (type) { case "kakao" -> responseCode = kakaoConnector.requestRevoke(token); case "google" -> responseCode = googleConnector.requestRevoke(token); - default -> throw new RuntimeException("Unsupported oauth type"); + case "naver" -> responseCode = naverConnector.requestRevoke(token); + default -> throw new UnsupportedLoginTypeException("지원하지 않는 oauth 로그인입니다."); } - if (responseCode.is2xxSuccessful()) { - System.out.println("Token successfully revoked"); - } else { - System.out.println("Failed to revoke token, response code: " + responseCode); - throw new RuntimeException("Failed to revoke token"); + if (!responseCode.is2xxSuccessful()) { + throw new RevokeFailException("회원탈퇴에 실패하였습니다."); } } @@ -261,22 +366,25 @@ private void changeToDefaultProfile(User user, UserImage userImage) { } private String getDefaultProfile(User user) { - String sex = user.getSex(); - if (sex.equals("male")) { - { + if (user.getSex() != null) { + if (user.getSex().equals("male")) { return USER_PROFILE_MALE; + } else { + return USER_PROFILE_FEMALE; } - } else { - return USER_PROFILE_FEMALE; } + return USER_PROFILE_NONE; } - private void deleteUserProfileFromS3(UserImage userImage, String defaultImageUrl) { - String userImageUrl = userImage.getAddress(); - if (userImageUrl.equals(USER_PROFILE_MALE) || userImageUrl.equals(USER_PROFILE_FEMALE)) { - return; + private void deleteUserProfileFromS3 (UserImage userImage, String defaultImageUrl){ + String userImageUrl = userImage.getAddress(); + if (userImageUrl.equals(USER_PROFILE_MALE) || userImageUrl.equals(USER_PROFILE_FEMALE) + || userImageUrl.equals( + USER_PROFILE_NONE)) { + return; + } + s3Uploader.deleteImage(userImageUrl); + userImage.changeToDefaultProfile(defaultImageUrl); } - s3Uploader.deleteImage(userImageUrl); - userImage.changeToDefaultProfile(defaultImageUrl); + } -} diff --git a/src/main/java/solitour_backend/solitour/auth/service/TokenService.java b/src/main/java/solitour_backend/solitour/auth/service/TokenService.java index 99eebc6..b4c2758 100644 --- a/src/main/java/solitour_backend/solitour/auth/service/TokenService.java +++ b/src/main/java/solitour_backend/solitour/auth/service/TokenService.java @@ -30,10 +30,10 @@ public void deleteByMemberId(Long memberId) { } @Transactional - public Token saveToken(KakaoTokenResponse tokenResponse, User user) { + public Token saveToken(String refreshToken, User user) { Token token = Token.builder() .user(user) - .oauthToken(tokenResponse.getRefreshToken()) + .oauthToken(refreshToken) .build(); tokenRepository.save(token); diff --git a/src/main/java/solitour_backend/solitour/auth/support/kakao/dto/request/CreateUserInfoRequest.java b/src/main/java/solitour_backend/solitour/auth/support/kakao/dto/request/CreateUserInfoRequest.java new file mode 100644 index 0000000..ddba8c8 --- /dev/null +++ b/src/main/java/solitour_backend/solitour/auth/support/kakao/dto/request/CreateUserInfoRequest.java @@ -0,0 +1,12 @@ +package solitour_backend.solitour.auth.support.kakao.dto.request; + +import lombok.Getter; +import lombok.NoArgsConstructor; + +@NoArgsConstructor +@Getter +public class CreateUserInfoRequest { + private String name; + private String age; + private String sex; +} diff --git a/src/main/java/solitour_backend/solitour/auth/support/naver/NaverConnector.java b/src/main/java/solitour_backend/solitour/auth/support/naver/NaverConnector.java new file mode 100644 index 0000000..5d47e7b --- /dev/null +++ b/src/main/java/solitour_backend/solitour/auth/support/naver/NaverConnector.java @@ -0,0 +1,118 @@ +package solitour_backend.solitour.auth.support.naver; + + +import java.io.IOException; +import java.util.Collections; +import java.util.UUID; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatusCode; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Component; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestTemplate; +import solitour_backend.solitour.auth.support.kakao.dto.KakaoUserResponse; +import solitour_backend.solitour.auth.support.naver.dto.NaverTokenAndUserResponse; +import solitour_backend.solitour.auth.support.naver.dto.NaverTokenResponse; +import solitour_backend.solitour.auth.support.naver.dto.NaverUserResponse; + +@Getter +@RequiredArgsConstructor +@Component +public class NaverConnector { + + private static final String BEARER_TYPE = "Bearer"; + private static final RestTemplate REST_TEMPLATE = new RestTemplate(); + + private final NaverProvider provider; + + public NaverTokenAndUserResponse requestNaverUserInfo(String code) { + NaverTokenResponse naverTokenResponse = requestAccessToken(code); + + HttpHeaders headers = new HttpHeaders(); + headers.set("Authorization", String.join(" ", BEARER_TYPE, naverTokenResponse.getAccessToken())); + HttpEntity entity = new HttpEntity<>(headers); + + ResponseEntity responseEntity = REST_TEMPLATE.exchange(provider.getUserInfoUrl(), + HttpMethod.GET, entity, + NaverUserResponse.class); + + return new NaverTokenAndUserResponse(naverTokenResponse, responseEntity.getBody()); + + } + + public NaverTokenResponse requestAccessToken(String code) { + HttpEntity> entity = new HttpEntity<>( + createBody(code), createHeaders()); + + return REST_TEMPLATE.postForEntity( + provider.getAccessTokenUrl(), + entity, + NaverTokenResponse.class).getBody(); + } + + public String refreshToken(String refreshToken) { + HttpEntity> entity = new HttpEntity<>( + createRefreshBody(refreshToken), createHeaders()); + + return REST_TEMPLATE.postForEntity( + provider.getAccessTokenUrl(), + entity, NaverTokenResponse.class).getBody().getAccessToken(); + } + + private HttpHeaders createHeaders() { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); + return headers; + } + + private MultiValueMap createBody(String code) { + MultiValueMap body = new LinkedMultiValueMap<>(); + body.add("code", code); + body.add("grant_type", provider.getGrantType()); + body.add("client_id", provider.getClientId()); + body.add("client_secret", provider.getClientSecret()); + body.add("state", UUID.randomUUID().toString()); + return body; + } + + private MultiValueMap createRefreshBody(String refreshToken) { + MultiValueMap body = new LinkedMultiValueMap<>(); + body.add("grant_type", provider.getRefreshGrantType()); + body.add("client_id", provider.getClientId()); + body.add("client_secret", provider.getClientSecret()); + body.add("refresh_token", refreshToken); + return body; + } + + public HttpStatusCode requestRevoke(String token) throws IOException { + HttpEntity> entity = new HttpEntity<>(createRevokeBody(token),createRevokeHeaders(token)); + + ResponseEntity response = REST_TEMPLATE.postForEntity(provider.getAccessTokenUrl(), entity, String.class); + + return response.getStatusCode(); + } + + private MultiValueMap createRevokeBody(String accessToken) { + MultiValueMap body = new LinkedMultiValueMap<>(); + body.add("client_id", provider.getClientId()); + body.add("client_secret", provider.getClientSecret()); + body.add("grant_type", provider.getRevokeGrantType()); + body.add("access_token", accessToken); + body.add("service_provider", provider.getServiceProvider()); + return body; + } + + private HttpHeaders createRevokeHeaders(String token) { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + headers.set("Authorization", String.join(" ", BEARER_TYPE, token)); + return headers; + } +} diff --git a/src/main/java/solitour_backend/solitour/auth/support/naver/NaverProvider.java b/src/main/java/solitour_backend/solitour/auth/support/naver/NaverProvider.java new file mode 100644 index 0000000..3b2e3f8 --- /dev/null +++ b/src/main/java/solitour_backend/solitour/auth/support/naver/NaverProvider.java @@ -0,0 +1,79 @@ +package solitour_backend.solitour.auth.support.naver; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import java.util.stream.Collectors; +import lombok.Getter; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpEntity; +import org.springframework.stereotype.Component; +import org.springframework.util.MultiValueMap; +import solitour_backend.solitour.auth.support.naver.dto.NaverTokenResponse; + +@Getter +@Component +public class NaverProvider { + + private final String clientId; + private final String clientSecret; + private final String authUrl; + private final String accessTokenUrl; + private final String userInfoUrl; + private final String grantType; + private final String refreshGrantType; + private final String revokeGrantType; + private final String serviceProvider; + private final String state = UUID.randomUUID().toString(); + + + public NaverProvider(@Value("${oauth2.naver.client.id}") String clientId, + @Value("${oauth2.naver.client.secret}") String clientSecret, + @Value("${oauth2.naver.url.auth}") String authUrl, + @Value("${oauth2.naver.url.token}") String accessTokenUrl, + @Value("${oauth2.naver.url.userinfo}") String userInfoUrl, + @Value("${oauth2.naver.service-provider}") String serviceProvider, + @Value("${oauth2.naver.grant-type}") String grantType, + @Value("${oauth2.naver.refresh-grant-type}") String refreshGrantType, + @Value("${oauth2.naver.revoke-grant-type}") String revokeGrantType) { + this.clientId = clientId; + this.clientSecret = clientSecret; + this.authUrl = authUrl; + this.accessTokenUrl = accessTokenUrl; + this.userInfoUrl = userInfoUrl; + this.serviceProvider = serviceProvider; + this.grantType = grantType; + this.refreshGrantType = refreshGrantType; + this.revokeGrantType = revokeGrantType; + } + + public String generateTokenUrl(String grantType, String code) { + Map params = new HashMap<>(); + params.put("grant_type", grantType); + params.put("client_id", clientId); + params.put("client_secret", clientSecret); + params.put("code", code); + params.put("state", state); + return authUrl + "?" + concatParams(params); + } + + public String generateAuthUrl(String redirectUrl) { + Map params = new HashMap<>(); + params.put("response_type", "code"); + params.put("client_id", clientId); + params.put("redirect_uri", redirectUrl); + params.put("state", state); + return authUrl + "?" + concatParams(params); + } + + private String concatParams(Map params) { + return params.entrySet() + .stream() + .map(entry -> entry.getKey() + "=" + entry.getValue()) + .collect(Collectors.joining("&")); + } + + public String generateAccessTokenUrl(String code) { + return generateTokenUrl("authorization_code", code); + } +} diff --git a/src/main/java/solitour_backend/solitour/auth/support/naver/dto/NaverTokenAndUserResponse.java b/src/main/java/solitour_backend/solitour/auth/support/naver/dto/NaverTokenAndUserResponse.java new file mode 100644 index 0000000..fcdee93 --- /dev/null +++ b/src/main/java/solitour_backend/solitour/auth/support/naver/dto/NaverTokenAndUserResponse.java @@ -0,0 +1,17 @@ +package solitour_backend.solitour.auth.support.naver.dto; + +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +@Getter +@NoArgsConstructor +@AllArgsConstructor +public class NaverTokenAndUserResponse { + + private NaverTokenResponse naverTokenResponse; + private NaverUserResponse naverUserResponse; +} diff --git a/src/main/java/solitour_backend/solitour/auth/support/naver/dto/NaverTokenResponse.java b/src/main/java/solitour_backend/solitour/auth/support/naver/dto/NaverTokenResponse.java new file mode 100644 index 0000000..ccb38fb --- /dev/null +++ b/src/main/java/solitour_backend/solitour/auth/support/naver/dto/NaverTokenResponse.java @@ -0,0 +1,17 @@ +package solitour_backend.solitour.auth.support.naver.dto; + +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +@Getter +@NoArgsConstructor +@AllArgsConstructor +public class NaverTokenResponse { + + private String accessToken; + private String refreshToken; +} diff --git a/src/main/java/solitour_backend/solitour/auth/support/naver/dto/NaverUserResponse.java b/src/main/java/solitour_backend/solitour/auth/support/naver/dto/NaverUserResponse.java new file mode 100644 index 0000000..6b4329d --- /dev/null +++ b/src/main/java/solitour_backend/solitour/auth/support/naver/dto/NaverUserResponse.java @@ -0,0 +1,36 @@ +package solitour_backend.solitour.auth.support.naver.dto; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Date; +import java.util.HashMap; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import solitour_backend.solitour.auth.support.kakao.dto.KakaoUserResponse.Partner; + +@Getter +@NoArgsConstructor +@JsonIgnoreProperties(ignoreUnknown = true) +public class NaverUserResponse { + private String resultcode; + private String message; + private Response response; + + @Getter + @NoArgsConstructor + public static class Response { + private String id; + private String email; + private String name; + private String nickname; + private String gender; + private String age; + private String birthday; + private String birthyear; + private String mobile; + private String profileImage; + } + +} diff --git a/src/main/java/solitour_backend/solitour/error/GlobalControllerAdvice.java b/src/main/java/solitour_backend/solitour/error/GlobalControllerAdvice.java index d9f9bdf..a877ef3 100644 --- a/src/main/java/solitour_backend/solitour/error/GlobalControllerAdvice.java +++ b/src/main/java/solitour_backend/solitour/error/GlobalControllerAdvice.java @@ -5,6 +5,8 @@ import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; import solitour_backend.solitour.auth.exception.TokenNotExistsException; +import solitour_backend.solitour.auth.exception.UnsupportedLoginTypeException; +import solitour_backend.solitour.auth.exception.UserRevokeErrorException; import solitour_backend.solitour.book_mark_gathering.exception.GatheringBookMarkNotExistsException; import solitour_backend.solitour.book_mark_information.exception.InformationBookMarkNotExistsException; import solitour_backend.solitour.category.exception.CategoryNotExistsException; @@ -40,7 +42,8 @@ public class GlobalControllerAdvice { RequestValidationFailedException.class, ImageRequestValidationFailedException.class, GatheringApplicantsManagerException.class, - InformationNotManageException.class + InformationNotManageException.class, + UnsupportedLoginTypeException.class }) public ResponseEntity validationException(Exception exception) { return ResponseEntity @@ -111,5 +114,13 @@ public ResponseEntity unauthorizedException(Exception exception) { .status(HttpStatus.UNAUTHORIZED) .body(exception.getMessage()); } + @ExceptionHandler({ + UserRevokeErrorException.class + }) + public ResponseEntity serverErrorException(Exception exception) { + return ResponseEntity + .status(HttpStatus.INTERNAL_SERVER_ERROR) + .body(exception.getMessage()); + } } From 2d453e945fdc7f40b685a84e18eef98015527822 Mon Sep 17 00:00:00 2001 From: Donghun Won Date: Tue, 1 Oct 2024 17:38:06 +0900 Subject: [PATCH 03/10] =?UTF-8?q?Release=20:=20=EC=86=94=EB=A6=AC=ED=88=AC?= =?UTF-8?q?=EC=96=B4=20v1.1.0=20(#215)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: page 객체 직렬화 문제 해결 * Feat(#91) : 여행일기 페이지네이션 (#106) * Feat : 여행 일기 crud * Feat : 타이틀 이미지 필드 추가 * Feat : 개별 diary 조회, title 이미지 반환값에 추가 * Feat : 여행일기 조회 페이지네이션 * Style: 코드 포맷팅 * Feat(#95) : 정보 좋아요 (#107) * Style : 코드 import 포맷팅 * Feat : 정보 좋아요 기능 좋아요 개수 조회 좋아요 좋아요 취소 * Style : 코드 포맷핑 * Feat : 정보 좋아요 삭제 시 요청 parameter수정 * fix: @NotNull 추가 * fix: repository -> JpaRepository 수정 * feat: isDeleted 필드 추가 * feat: 정보 상세 response dto 필드 추가 * feat: 정보 메인 응답 response 필드 추가 * feat: 북마크와 좋아요 삭제되지 않은 것 where 추가 * feat: 정보 상세 조회 service dto 필드 수정으로 인해 추가 구현 * feat: 좋아요 정보 id, 유저 id, 삭제 되지 않은것 있는지 확인 * feat: 북마크 좋아요 삭제 되지 않은 것 where 절 추가 * feat: isDeleted 필드 삭제 * feat: gathering default false 추가로 인한 코드 삭제 * feat: isApplicants 이 모임에 참여 신청을 했는지 필드 추가 * feat: isDeleted 삭제로 인한 코드 수정 * style: 코드 스타일 수정 * refactor: isDeleted 칼럼 삭제로 인한 수정 * fix: 모임 등록할때 모임 작성자도 모임 신청 save * style: 코드 스타일 수정 * style: 코드 스타일 수정 * Feat(#110) : mypage 정보, 모임 조회 (#111) * Feat : 마이페이지 프로필 이미지 변경 * Feat : 마이페이지 내가 작성한 정보, 구독한 정보 조회 * Style : 코드 포맷팅 * Feat : 마이페이지 모임조회 내가 작성한 모임 조회 내가 신청한 모임 조회 내가 북마크한 모임 조회 * Feat : 유저 모임 가입 상태 필드 추가 * Style : 코드 포맷팅 * Refactor(#110) : group by 쿼리 간소화 (#112) * Refactor : group by 쿼리 간소화 * refactor: 모임 상태 return * refactor: user id 필드 추가 * refactor: * Feature(#114): 정보, 모임 해시태그 검색 (#115) * Feat : 태그 검색 * Refactor : 마감여부 필드 추가, 가입상태 서술 변경 * Feat : 정보 태그 검색 * Feat: 모임 태그 검색 * Style : 코드 포맷팅 * feat: 모임 상태를 완료로 변경 하거나 완료하지 않았다로 변경 할때 이미 그 상태일때 발생하는 exception * feat: 모임 상태를 완료로 변경 하거나 완료하지 않았다로 변경 service * feat: 모임 상태를 완료로 변경 하거나 완료하지 않았다로 변경 controller * fix: validation if 문 수정 * Feat(#114) : 태그 검색 필터 (#118) * Refactor : 태그 필터 추가 공백, 특수문자 제거 한글, 영어, 숫자만 허용 * Feature(#119): 회원가입 중복 검증 (#120) * Refactor : 유저 아이디 중복 검증 * Feature(#121) : 이미지 업로드 로직 수정 (#122) * Refactor : 이미지 업로드 로직 수정 * Refactor : imageStatus 추가 * feat: 정보 create request dto * fix: gathering 생성자에 isDeleted false로 지정 * fix: gatheringApplicants 상태값 consent인 유저들만 count * feat: 사용하지 않는 코드 제거 및 createdDate 칼럼 어노테이션 적용 * feat: S3 upload response dto * refactor: createdDate 칼럼 어노테이션 적용 * feat: create information api 수정 * refactor: s3 upload api 반환타입 수정 * refactor: S3FileResponse dto 반환 타입 수정 * refactor: 정보 등록 api 수정 * Feat(#121): 이미지 업로드 시 dev, prod 환경 구분+ 엔티티별로 구분해서 경로 설정 (#125) * Feat : 엔티티, 환경에 따라 저장 경로 변경 * Refactor : 유저 프로필 이미지 url 위치 yml 파일로 변경 * Feat(#95) : 모임 북마크 (#126) * Feat : 모임 북마크 * Refactor : 모임 북마크 생성자 추가 * Fix : imageStatus 필드 복구 * Feature(#121) : 이미지 엔티티 경로 분류 시에 enum사용하도록 변경 (#127) * Refactor : 이미지 엔티티 경로 분류 시에 enum사용하도록 변경 * Feat(#128) : 엑세스 토큰 만료시 401 코드 반환 (#129) * Feat : 엑세스 토큰 기간 연장, 로그아웃 시 쿠키 삭제 * Style : 코드 포맷팅 * Refactor : 유저 아이디 중복 검증 * Refactor : 엑세스 토큰 만료시 401 코드 반환 * feat: 정보에 대한 권한 이 없는경우 exception * feat: 정보 update request dto * feat: 정보에 대한 권한 이 없는경우 exception handler * feat: 자식 카테고리 찾는 메서드 * feat: 정보에서 썸네일 이미지가 존재하는지 * feat: 정보 수정 api version2, 정보 삭제할때 userId 가져오는거 수정 * feat: 정보 수정 version2 service * refactor: 사용하지 않는 코드 삭제 * fix: url mapping 오류 해결 * fix: category 로직 수정 * fix: createdDate 칼럼 auto 생성 설정 문제 해결 * feat: 공지사항 entity 생성 * feat: 공지사항 (공지사항 생성, 수정, 제거, 목록조회, 상세조회, 관리자인경우 조회시 제거된 공지사항도 조회가능) 기능 * feat: QnA (QnA페이지,상태,검색으로 관리자가 관리할 QnA목록 조회, QnA 생성, QnA 사용자 목록 조회, QnA 상세 조회, QnA 제거, QnA 질문 등록, QnA 답변 등록) 작업 * fix: 예외처리하려다가 남겨놓은 불필요한 코드 삭제 * feat: enum 클래스 해당하는 값이 없으면 throw exception * feat: user default image getter() * feat: user default image value 가져오고 getter * feat: user default image service * Fix : 유저 정보글 조회, 유저 정보 북마크 조회 안되는 오류 수정 (#137) * Feat : 마이페이지 프로필 이미지 변경 * Feat : 마이페이지 내가 작성한 정보, 구독한 정보 조회 * Style : 코드 포맷팅 * Feat : 마이페이지 모임조회 내가 작성한 모임 조회 내가 신청한 모임 조회 내가 북마크한 모임 조회 * Feat : 유저 모임 가입 상태 필드 추가 * Style : 코드 포맷팅 * Refactor : group by 쿼리 간소화 * Refactor : 불필요한 북마크 조회 로직 삭제 * Fix : 유저 정보글 조회, 유저 정보 북마크 조회 안되는 오류 수정 isLike 구하는 로직 해당 결과 필드에 추가 * Feat(#114) : 정보, 모임 태그 검색 (#138) * Feat : 태그 검색 * Refactor : 마감여부 필드 추가, 가입상태 서술 변경 * Feat : 정보 태그 검색 * Feat: 모임 태그 검색 * Style : 코드 포맷팅 * Refactor : 태그 필터 추가 공백, 특수문자 제거 한글, 영어, 숫자만 허용 * feat: image s3 객체 lifecycle 적용 * feat: information 등록시 s3 image lifecycle 제거 * fix: home 페이지에 gathering 현재 참가 확정 인원 수 수정 * fix: 모집완료 된 모임 제외 where 절 수정 * Feat (#142): 모임 좋아요 기능 / 모임,정보 북마크, 좋아요 중복 체크 / 모임,정보 북마크,좋아요 예외, 상태 코드 처리 (#143) * Refactor : 토큰 존재하지 않을때, 유효하지 않을떄 예외 추가 * Refactor : 모임, 정보 예외 처리, 상태코드 변경 * Feat : 모임 좋아요 * Fix : 내가 신청한 모임 조회시 내가 모집한 모임 제외하도록 수정 (#145) * feat: 모임 아직 안끝났다고 수정할때 마감일 변경 request dto * feat: 마감일 변경 * feat: Request body GatheringNotFinishRequest * Fix : 태그 검색 인코딩 방식 변경 (#149) url인코딩, 디코딩 방식으로 변경 * Refactor(#150) : 회원 탈퇴 기능 향상 (#151) * Refactor: 최근 로그인 시간 기록, 카카오 로그인 닉네임 랜덤하게 설정 * Feat : 회원 탈퇴 유저 이미지 디폴트 이미지로 변경 유저 정보 null로 변경 s3에 저장된 유저 이미지 삭제 * feat: 모임 테이블 오픈 채팅 링크 컬럼 추가로 인해 entity 필드 추가 * refactor: 마감일 수정 삭제 * feat: 모임 테이블 오픈 채팅 링크 컬럼 추가로 인해 request dto 필드 추가 * feat: 모임 테이블 오픈 채팅 링크 컬럼 추가로 인해 response dto 필드 추가 * refactor: 모임 테이블 오픈 채팅 링크 컬럼 추가로 인해 수정, 조회, 생성 로직 변경 * feat: image entity setter 추가 * feat: 이미지 주소로 이미지 찾는 메서드 repository * feat: InformationUpdateRequest image 관련 필드 추가 * feat: information update 수정 * style: import 문 정리 * fix: s3 object key 값 수정 * style: 사용하지 않는 dto 삭제 * refactor: image request dto * refactor: address 길이 제한 50으로 수정 및 image request dto 추가 * refactor: image request dto 추가로 인한 refactor, 상세 페이지 글 작성자에 대한 유저 프로필 이미지로 반환 * fix: user 수정 * feat: information entity class viewCount plus method * feat: gathering entity class viewCount plus method * feat: information 조회수 올리기 * feat: information detail 조회 api 요청시 조회수 올리기 위한 HttpServletResponse 파라미터에 추가 * feat: gathering 조회수 올리기 * feat: gathering detail 조회 api 요청시 조회수 올리기 위한 HttpServletResponse 파라미터에 추가 * feat: 한 브라우저에 a 라는 회원이 조회를 했어 그 다음에 로그아웃하고 b 회원이 조회를 했어 그 다음 로그아웃 하고 비회원이 조회를 했어 이렇게 하면 3이 증가 해야 한다 이런 시나리오를 위한 로직 추가 * Refactor : 다이어리 콘텐트 이미지 필드 추가에 따라 로직 변경 (#161) * Fix : qna 결과 얻을때 발생하는 no session 문제 해결 (#163) * feat: 마감일이 지난 모임들은 안보여주는 where 절 추가 * fix: binding result 제거 * Fix : 승인된 유저만 인원수에 포함하도록 쿼리 수정 (#168) * Refactor(#169) : 일기 예외처리 (#170) * Refactor : 다이어리 콘텐트 이미지 필드 추가에 따라 로직 변경 * Refactor : authenticated 위치 변경, 상태코드 변경 * Refactor : 일기 없을때, 일기 권한 없을때 예외처리 * fix: qna 상세정보 조회시 메시지들 안보이는 문제 수정 * fix: qna 리스트 조회시 mysql 쿼리가 잘못되어서 수정 * fix: 조회수 쿠키 * style: 필요없는 import 문 제거 * feat: 정보 상세 dto response category 필드 추가 * feat: information page request dto 검색 필드 추가 * feat: information page 검색 기능 추가 * feat: information detail 조회시 category 추가 * feat: gathering detail response userImage 필드 추가 * feat: gathering detail service userImage 필드 추가 * Feat(#150) : 회원 탈퇴 방법 변경(카카오) (#175) * Refactor: 최근 로그인 시간 기록, 카카오 로그인 닉네임 랜덤하게 설정 * Feat : 회원 탈퇴 유저 이미지 디폴트 이미지로 변경 유저 정보 null로 변경 s3에 저장된 유저 이미지 삭제 * Refactor : 회원 탈퇴 방법 변경 kakao 로 부터 받은 refresh token을 저장해서 access token 재발급하여 회원 탈퇴 요청 하는 방법으로 변경 * Fix : 정보, 모임 조회시 로그인 유저 토큰 검증 로직 추가 * fix: and -> or * fix: gathering 조회수 기능 수정 * fix: information 조회수 기능 수정 * feat: 해시 함수 암호화 * fix: 이미지 삭제 할때 이미지 url 파싱 수정 * feature: 모임 조회수 기능 개선 * feature: 정보 조회수 기능 개선 * fix: error 임시 해결 * docs: git ignore resources/ .yml 파일로 수정 * docs: schema.sql * docs: 초반 data.sql * docs: git actions yml파일 * docs: git actions yml 파일 변경 - 브랜치 별로 application.yml 파일 다르게 설정 * docs: git actions yml 파일 변경 - gradlew 실행 권한 부여 * docs: git actions yml 파일 변경 - gradlew 실행 gradle9.0 버전에 맞게 수정 * docs: git actions yml 파일 변경 - gradlew 실행 시 오류가 날 경우 어떤 오류인지 * docs: git actions yml 파일 변경 - gradlew 실행 시 오류가 날 경우 어떤 오류인지 * docs: git actions yml 파일 변경 * docs: git actions yml 파일 변경 * docs: git actions yml 파일 변경 * docs: git actions yml 파일 변경 * fix: 모집 중 체크 해제했을 때 마감 일이 지난 것이 보이지 않는 오류 수정하기 * fix: 좋아요 로직 수정 * fix: 작성자 이름이 아니라 닉네임으로 수정 * docs: ci/cd yml 파일 수정 * docs: ci/cd yml 파일 수정 * docs: ci/cd yml 파일 수정 * docs: ci/cd yml 파일 수정 * docs: ci/cd yml 파일 수정 * docs: 테스트 h2 데이터베이스 설정 * docs: 테스트 h2 데이터베이스 설정 * docs: 테스트 h2 데이터베이스 설정 * docs: test 와 build 분리 설정 * docs: test schema.sql * docs: yml * docs: yml * Fix : test schema 파일 추가 * Fix : 테스트 스키마 업로드 삭제 * docs: h2 schema.sql 수정 * fix: 충돌 해결 * docs: yml * fix: 충돌로 인한 문제 해결 * fix: gathering 페이지네이션 모임 숫자 오류, home 에서 좋아요 했는지 * Fix : oauth 로그인 수정, 마이페이지 모임 오류 수정 (#183) 마이페이지 모임 조회 안되는 문제 수정 마이페이지 모임 인원수 계산 안되는 문제 수정 * fix: left join 시 중복 문제 해결 -> 일부 서브 쿼리로 해결 * Fix: 모임 사용자 본명 대신 닉네임 전달 (#185) * refactor: zone_category에 "세종" 추가 * fix: 모임 작성자 본명이 아니라 닉네임 전달 * refactor: name -> nickname 필드 수정 * refactor: name -> nickname 수정 * feat: category name 데이터 추가 * refactor: 쿼리문 개선 * Feat : 유저 블락 기능 (#186) * Refactor : 로그인 로직 수정 (#187) * Refactor : 로그인 로직 수정 최초 로그인 시에 inactive 상태 부여 이후 이름,성별,나이가 입력되면 active 상태로 전환 * Feat : 유저 성별 없을때 디폴트 이미지로 프로필 설정 * Feat : 유저 프로필 이미지 삭제 기능 * Refactor : 유저 이미지 업데이트 시 s3에서 이미지 삭제되도록 수정 * Style: 코드 포맷팅 * Fix : 유저 추가 정보 입력시 활성화 상태로 수정 * Feat : 유저 프로필 삭제 기능 (#190) * Feat : 유저 프로필 이미지 삭제 기능 * Refactor : 유저 이미지 업데이트 시 s3에서 이미지 삭제되도록 수정 * Style: 코드 포맷팅 * Fix : 유저 추가 정보 입력시 활성화 상태로 수정 * Fix : 프로필 이미지 yml 경로 수정 * Revert "Fix : 프로필 이미지 yml 경로 수정" This reverts commit 645a4f6847e946a780ee7a26f231ec7c69aba167. * Feat : 프로필 이미지 yml 경로 수정 * docs: application-common 추가 * Fix : 토큰 예외처리 수정 (#193) * Fix : 다이어리 장소 글자수 수정 (#194) * Fix : 비활성화 상태에서도 로그인 되도록 수정 (#195) * fix: home information order 절 수정 * fix: 좋아요 수 구하는 절 수정 * fix : 로그인 유저 상태 확인 수정 * fix: 좋아요 수 구하는 절 수정 Long -> Integer 로 수정 * fix: bookMark 했는지 구하는 절 수정 (#200) * fix: join -> 서브쿼리로 처리후 group by 수정 * Fix : 정보 카테고리 이름 추가 * Fix : 카테고리 이름 필드 추가 * Refactor : 성별입력시 유저 프로필 변경 * Fix : 유저 프로필이 디폴트 프로필일때만 변경 (#205) * fix: applicatns 부분 join -> 서브쿼리로 수정 * Fix : 마이페이지 모임 조회 오류 수정 (#207) * Fix : 삭제된 모임 조회되지 않도록 수정 * Fix : 모임 인원수 오류 수정 자기자신을 인원에 포함하지 않는 문제 여러명의 인원을 1명으로 인식하는 문제 * Fix : main ci시 jar 파일 문법 수정 * Fix : 카카오 회원탈퇴 오류 수정 , 카카오 추가정보 있을때 회원가입 (#210) * Feat : 카카오 추가정보 있을때 회원가입 * Fix : 카카오 회원탈퇴 오류 수정 성별이 필드가 비어있을때 조회 사용하는 로직 수정 * Feat : 네이버 oauth (#211) 회원가입, 로그인, 로그아웃, 회원탈퇴 * Refactor : 테스트 ci , 배포 ci 분리 (#212) * Refactor : 테스트 ci , 배포 ci 분리 * Fix : 빌드 캐시 비활성화 * Fix : 로그 출력 추가 * Feat : gradle package, wrapper 캐시 추가 * Fix : 개발서버 포트 변경 * Fix : ci 이름 수정 (#214) * Refactor : 테스트 ci , 배포 ci 분리 * Fix : 빌드 캐시 비활성화 * Fix : 로그 출력 추가 * Feat : gradle package, wrapper 캐시 추가 * Fix : 개발서버 포트 변경 * Fix : ci 이름 수정 --------- Co-authored-by: hyeonjaez Co-authored-by: Fiat_lux <50399586+hyeonjaez@users.noreply.github.com> Co-authored-by: SK\ssssk Co-authored-by: 노현진 --- .github/workflows/deploy.yml | 17 +++++-------- .github/workflows/test.yml | 48 ++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 11 deletions(-) create mode 100644 .github/workflows/test.yml diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 05e2e3b..042bcc7 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -1,10 +1,8 @@ -name: Docker Image CI +name: deploy on: push: branches: [ "main", "develop" ] - pull_request: - branches: [ "main", "develop" ] jobs: build: @@ -27,21 +25,18 @@ jobs: echo "${{ secrets.APPLICATION_DEV }}" > ./src/main/resources/application-dev.yml echo "${{ secrets.APPLICATION_PROD }}" > ./src/main/resources/application-prod.yml echo "${{ secrets.APPLICATION_LOCAL }}" > ./src/main/resources/application-local.yml - echo "${{ secrets.APPLICATION_TEST }}" > ./src/main/resources/application-test.yml - - name: gradlew 실행 권한 부여 run: chmod +x ./gradlew - - name: 테스트 및 빌드하기 (main 브랜치) + - name: 빌드하기 (main 브랜치) if: github.ref == 'refs/heads/main' - run: ./gradlew clean build -PspringProfile=prod --warning-mode all --scan + run: ./gradlew clean build -x test -PspringProfile=prod --warning-mode all --scan - - name: 테스트 및 빌드하기 (develop 브랜치) + - name: 빌드하기 (develop 브랜치) if: github.ref == 'refs/heads/develop' run: | - ./gradlew clean test -PspringProfile=test - ./gradlew build -x test -PspringProfile=dev --warning-mode all --scan + ./gradlew clean build -x test -PspringProfile=dev --warning-mode all --scan - name: 빌드된 파일 이름 변경 run: mv ./build/libs/*SNAPSHOT.jar ./project.jar @@ -85,6 +80,6 @@ jobs: mkdir /home/ubuntu/solitour-server/current mv /home/ubuntu/solitour-server/tobe/project.jar /home/ubuntu/solitour-server/current/project.jar cd /home/ubuntu/solitour-server/current - sudo fuser -k -n tcp 8080 || true + sudo fuser -k -n tcp 8081 || true nohup java -jar -Dspring.profiles.active=dev project.jar > ./output.log 2>&1 & rm -rf /home/ubuntu/solitour-server/tobe diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..e466f8f --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,48 @@ +name: test + +on: + pull_request: + branches: [ "main", "develop" ] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Github Repository 에 올린 파일들을 볼러오기 + uses: actions/checkout@v4 + + - name: JDK 17 버전 설치 + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: 17 + + - name: application.yml 파일 만들기 + run: | + mkdir -p ./src/main/resources + echo "${{ secrets.APPLICATION_YML }}" > ./src/main/resources/application.yml + echo "${{ secrets.APPLICATION_COMMON }}" > ./src/main/resources/application-common.yml + echo "${{ secrets.APPLICATION_TEST }}" > ./src/main/resources/application-test.yml + + + - name: gradlew 실행 권한 부여 + run: chmod +x ./gradlew + + - name: Gradle packages 캐시 + uses: actions/cache@v3 + with: + path: ~/.gradle/caches + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} + restore-keys: | + ${{ runner.os }}-gradle + + - name: Gradle wrapper 캐시 + uses: actions/cache@v3 + with: + path: ~/.gradle/wrapper + key: ${{ runner.os }}-gradle-wrapper-${{ hashFiles('**/gradle/wrapper/gradle-wrapper.properties') }} + + - name: 테스트 + run : ./gradlew clean test -PspringProfile=test --info + + From e43d4dcd109defc86b34c525470b725415debf91 Mon Sep 17 00:00:00 2001 From: Donghun Won Date: Mon, 7 Oct 2024 16:14:41 +0900 Subject: [PATCH 04/10] =?UTF-8?q?Fix(#216)=20:=20=EC=9D=BC=EA=B8=B0=20?= =?UTF-8?q?=EC=9D=B4=EB=AF=B8=EC=A7=80=20=ED=95=98=EB=A3=A8=20=EC=A7=80?= =?UTF-8?q?=EB=82=98=EB=A9=B4=20=EC=82=AD=EC=A0=9C=EB=90=98=EB=8A=94=20?= =?UTF-8?q?=EB=AC=B8=EC=A0=9C=20=20(#224)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix : 유저, 일기 이미지 자동삭제 되지 않게 수정 * Fix : 일기 내 이미지 url 쉼표로 구분하여 영구화하도록 수정 --- .../solitour/diary/service/DiaryService.java | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/main/java/solitour_backend/solitour/diary/service/DiaryService.java b/src/main/java/solitour_backend/solitour/diary/service/DiaryService.java index 78c9775..7c90983 100644 --- a/src/main/java/solitour_backend/solitour/diary/service/DiaryService.java +++ b/src/main/java/solitour_backend/solitour/diary/service/DiaryService.java @@ -94,7 +94,8 @@ public void updateDiary(Long userId, Long diaryId, DiaryUpdateRequest request) { } private void updateDiary(Long diaryId, DiaryUpdateRequest request) { - Diary diary = diaryRepository.findById(diaryId).orElseThrow(() -> new DiaryNotExistsException("해당 일기가 존재하지 않습니다.")); + Diary diary = diaryRepository.findById(diaryId) + .orElseThrow(() -> new DiaryNotExistsException("해당 일기가 존재하지 않습니다.")); deleteDiaryImage(request); diary.getDiaryDayContent().clear(); diary.updateDiary(request); @@ -103,7 +104,7 @@ private void updateDiary(Long diaryId, DiaryUpdateRequest request) { private void saveDiaryDayContent(Diary savedDiary, DiaryCreateRequest request) { for (DiaryDayRequest dayRequest : request.getDiaryDayRequests()) { - s3Uploader.markImagePermanent(dayRequest.getDiaryDayContentImages()); + makeDiaryImagePermanent(dayRequest.getDiaryDayContentImages()); DiaryDayContent diaryDayContent = DiaryDayContent.builder() .diary(savedDiary) .content(dayRequest.getContent()) @@ -115,6 +116,15 @@ private void saveDiaryDayContent(Diary savedDiary, DiaryCreateRequest request) { } } + private void makeDiaryImagePermanent(String diaryDayContentImages) { + if (!diaryDayContentImages.isEmpty()) { + String[] contentImages = diaryDayContentImages.split(","); + for (String contentImage : contentImages) { + s3Uploader.markImagePermanent(contentImage); + } + } + } + private void updateDiaryDayContent(Diary savedDiary, DiaryUpdateRequest request) { diaryDayContentRepository.deleteById(savedDiary.getId()); for (DiaryUpdateDayRequest dayRequest : request.getDiaryDayRequests()) { From 42f64ba204992c1a18f03d34a313efa0913baaf2 Mon Sep 17 00:00:00 2001 From: hyeonjaez Date: Fri, 11 Oct 2024 11:55:19 +0900 Subject: [PATCH 05/10] =?UTF-8?q?feat:=20toString()=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../information/dto/response/InformationBriefResponse.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/solitour_backend/solitour/information/dto/response/InformationBriefResponse.java b/src/main/java/solitour_backend/solitour/information/dto/response/InformationBriefResponse.java index c833211..2478487 100644 --- a/src/main/java/solitour_backend/solitour/information/dto/response/InformationBriefResponse.java +++ b/src/main/java/solitour_backend/solitour/information/dto/response/InformationBriefResponse.java @@ -2,9 +2,11 @@ import lombok.AllArgsConstructor; import lombok.Getter; +import lombok.ToString; @Getter @AllArgsConstructor +@ToString public class InformationBriefResponse { private Long informationId; From 5f962dcc09762d47270516aeacf456455f0c242a Mon Sep 17 00:00:00 2001 From: hyeonjaez Date: Fri, 11 Oct 2024 11:55:40 +0900 Subject: [PATCH 06/10] =?UTF-8?q?fix:=20=ED=8E=98=EC=9D=B4=EC=A7=80?= =?UTF-8?q?=EB=84=A4=EC=9D=B4=EC=85=98=20=EB=A9=94=EC=84=9C=EB=93=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20=EA=B8=B0=EC=A1=B4=EC=9D=98=20?= =?UTF-8?q?=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=A3=BC=EC=84=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/InformationRepositoryCustom.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/solitour_backend/solitour/information/repository/InformationRepositoryCustom.java b/src/main/java/solitour_backend/solitour/information/repository/InformationRepositoryCustom.java index 4100ea6..0076f59 100644 --- a/src/main/java/solitour_backend/solitour/information/repository/InformationRepositoryCustom.java +++ b/src/main/java/solitour_backend/solitour/information/repository/InformationRepositoryCustom.java @@ -14,10 +14,11 @@ public interface InformationRepositoryCustom { String LIKE_COUNT_SORT = "likes"; String VIEW_COUNT_SORT = "views"; + Page getPageInformationFilterAndOrder(Pageable pageable, InformationPageRequest informationPageRequest, Long userId, Long parentCategoryId); - Page getInformationPageFilterAndOrder(Pageable pageable, - InformationPageRequest informationPageRequest, - Long userId, Long parentCategoryId); +// Page getInformationPageFilterAndOrder(Pageable pageable, +// InformationPageRequest informationPageRequest, +// Long userId, Long parentCategoryId); List getInformationRank(); From ce202600478b183ec42fced6073a98e9c95307b8 Mon Sep 17 00:00:00 2001 From: hyeonjaez Date: Fri, 11 Oct 2024 11:56:00 +0900 Subject: [PATCH 07/10] =?UTF-8?q?fix:=20=ED=8E=98=EC=9D=B4=EC=A7=80?= =?UTF-8?q?=EB=84=A4=EC=9D=B4=EC=85=98=20=EC=BF=BC=EB=A6=AC=EB=AC=B8=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=EC=9C=BC=EB=A1=9C=20=EC=9D=B8=ED=95=B4=20?= =?UTF-8?q?=EB=A9=94=EC=84=9C=EB=93=9C=20=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20?= =?UTF-8?q?=EA=B8=B0=EC=A1=B4=EC=9D=98=20=EB=A9=94=EC=84=9C=EB=93=9C=20?= =?UTF-8?q?=EC=A3=BC=EC=84=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/InformationRepositoryImpl.java | 171 ++++++++++++++---- 1 file changed, 131 insertions(+), 40 deletions(-) diff --git a/src/main/java/solitour_backend/solitour/information/repository/InformationRepositoryImpl.java b/src/main/java/solitour_backend/solitour/information/repository/InformationRepositoryImpl.java index 67a2657..c7799cb 100644 --- a/src/main/java/solitour_backend/solitour/information/repository/InformationRepositoryImpl.java +++ b/src/main/java/solitour_backend/solitour/information/repository/InformationRepositoryImpl.java @@ -11,6 +11,7 @@ import java.util.List; import java.util.Objects; +import lombok.extern.slf4j.Slf4j; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; @@ -29,6 +30,7 @@ import solitour_backend.solitour.information.entity.QInformation; import solitour_backend.solitour.zone_category.entity.QZoneCategory; +@Slf4j public class InformationRepositoryImpl extends QuerydslRepositorySupport implements InformationRepositoryCustom { public InformationRepositoryImpl() { @@ -42,47 +44,48 @@ public InformationRepositoryImpl() { QImage image = QImage.image; QGreatInformation greatInformation = QGreatInformation.greatInformation; QCategory category = QCategory.category; + QCategory categoryParent = new QCategory("categoryParent"); QInfoTag infoTag = QInfoTag.infoTag; - - @Override - public Page getInformationPageFilterAndOrder(Pageable pageable, - InformationPageRequest informationPageRequest, - Long userId, Long parentCategoryId) { + public Page getPageInformationFilterAndOrder(Pageable pageable, InformationPageRequest informationPageRequest, Long userId, Long parentCategoryId) { BooleanBuilder whereClause = new BooleanBuilder(); - if (Objects.nonNull(informationPageRequest.getZoneCategoryId())) { whereClause.and( - information.zoneCategory.parentZoneCategory.id.eq(informationPageRequest.getZoneCategoryId())); + zoneCategoryParent.id.eq(informationPageRequest.getZoneCategoryId()) + ); } - BooleanBuilder categoryCondition = new BooleanBuilder(); - if (Objects.nonNull(informationPageRequest.getChildCategoryId())) { - whereClause.and(information.category.id.eq(informationPageRequest.getChildCategoryId())); + whereClause.and(category.id.eq(informationPageRequest.getChildCategoryId())); } else { - categoryCondition.and(category.parentCategory.id.eq(parentCategoryId)); + whereClause.and(categoryParent.id.eq(parentCategoryId)); } - if (Objects.nonNull(informationPageRequest.getSearch())) { String searchKeyword = informationPageRequest.getSearch().trim().replace(" ", ""); whereClause.and(information.title.trim().containsIgnoreCase(searchKeyword)); } - long total = from(information) + .leftJoin(zoneCategoryChild).on(zoneCategoryChild.id.eq(information.zoneCategory.id)) + .leftJoin(zoneCategoryParent).on(zoneCategoryChild.parentZoneCategory.id.eq(zoneCategoryParent.id)) + .leftJoin(category).on(category.id.eq(information.category.id)) + .leftJoin(categoryParent).on(categoryParent.id.eq(category.parentCategory.id)) + .join(image).on(image.information.id.eq(information.id).and(image.imageStatus.eq(ImageStatus.THUMBNAIL))) .where(whereClause) - .select(information.id).fetchCount(); + .distinct() + .fetchCount(); List list = from(information) - .join(zoneCategoryChild).on(zoneCategoryChild.id.eq(information.zoneCategory.id)) - .leftJoin(zoneCategoryParent).on(zoneCategoryParent.id.eq(zoneCategoryChild.parentZoneCategory.id)) - .leftJoin(image) - .on(image.information.id.eq(information.id).and(image.imageStatus.eq(ImageStatus.THUMBNAIL))) - .join(category).on(category.id.eq(information.category.id).and(categoryCondition)) + .leftJoin(zoneCategoryChild).on(zoneCategoryChild.id.eq(information.zoneCategory.id)) + .leftJoin(zoneCategoryParent).on(zoneCategoryChild.parentZoneCategory.id.eq(zoneCategoryParent.id)) + .leftJoin(category).on(category.id.eq(information.category.id)) + .leftJoin(categoryParent).on(categoryParent.id.eq(category.parentCategory.id)) + .leftJoin(image).on(image.information.id.eq(information.id).and(image.imageStatus.eq(ImageStatus.THUMBNAIL))) + .leftJoin(bookMarkInformation).on(bookMarkInformation.information.id.eq(information.id).and(bookMarkInformation.user.id.eq(userId))) + .leftJoin(greatInformation).on(greatInformation.information.id.eq(information.id)) .where(whereClause) - .groupBy(information.id, zoneCategoryChild.id, zoneCategoryParent.id, image.id) - .orderBy(getOrderSpecifier(informationPageRequest.getSort(), information.id)) + .groupBy(information.id, information.createdDate, information.viewCount, zoneCategoryChild.name, bookMarkInformation.id, image.address) + .orderBy(getOrderSpecifiers(informationPageRequest.getSort())) .select(Projections.constructor( InformationBriefResponse.class, information.id, @@ -91,17 +94,104 @@ public Page getInformationPageFilterAndOrder(Pageable zoneCategoryChild.name, information.category.name, information.viewCount, - isInformationBookmark(userId, information.id), + isBookMarkBooleanExpression(bookMarkInformation), image.address, - countGreatInformationByInformationById(information.id), - isUserGreatInformation(userId) + countGreatInformation(greatInformation), + isGreatBooleanExpression(userId, greatInformation) )).offset(pageable.getOffset()) .limit(pageable.getPageSize()) .fetch(); - + log.info("정보 페이지 네이션 총 갯수 : " + total + "\n"); + log.info("정보 들 : "); + log.info(list.toString()); return new PageImpl<>(list, pageable, total); } + private OrderSpecifier getOrderSpecifiers(String sort) { + if (Objects.nonNull(sort)) { + if (Objects.equals(LIKE_COUNT_SORT, sort)) { + return countGreatInformation(greatInformation).desc(); + } else if (Objects.equals(VIEW_COUNT_SORT, sort)) { + return information.viewCount.desc(); + } + } + return information.createdDate.desc(); + } + + private BooleanExpression isBookMarkBooleanExpression(QBookMarkInformation bookMarkInformation) { + return new CaseBuilder() + .when(bookMarkInformation.id.isNotNull()) + .then(true) + .otherwise(false); + } + + private BooleanExpression isGreatBooleanExpression(Long userId, QGreatInformation greatInformation) { + return greatInformation.user.id.eq(userId).count().gt(0); + } + + private NumberExpression countGreatInformation(QGreatInformation greatInformation) { + return greatInformation.id.count().intValue(); + } + +// @Override +// public Page getInformationPageFilterAndOrder(Pageable pageable, +// InformationPageRequest informationPageRequest, +// Long userId, Long parentCategoryId) { +// BooleanBuilder whereClause = new BooleanBuilder(); +// +// if (Objects.nonNull(informationPageRequest.getZoneCategoryId())) { +// whereClause.and( +// information.zoneCategory.parentZoneCategory.id.eq(informationPageRequest.getZoneCategoryId())); +// } +// +// BooleanBuilder categoryCondition = new BooleanBuilder(); +// +// if (Objects.nonNull(informationPageRequest.getChildCategoryId())) { +// whereClause.and(information.category.id.eq(informationPageRequest.getChildCategoryId())); +// } else { +// categoryCondition.and(category.parentCategory.id.eq(parentCategoryId)); +// } +// +// if (Objects.nonNull(informationPageRequest.getSearch())) { +// String searchKeyword = informationPageRequest.getSearch().trim().replace(" ", ""); +// whereClause.and(information.title.trim().containsIgnoreCase(searchKeyword)); +// } +// +// +// long total = from(information) +// .where(whereClause) +// .select(information.id).fetchCount(); +// System.out.println("page 네이션 총 데이터 갯수 : " + total); +// List list = from(information) +// .join(zoneCategoryChild).on(zoneCategoryChild.id.eq(information.zoneCategory.id)) +// .leftJoin(zoneCategoryParent).on(zoneCategoryParent.id.eq(zoneCategoryChild.parentZoneCategory.id)) +// .leftJoin(image) +// .on(image.information.id.eq(information.id).and(image.imageStatus.eq(ImageStatus.THUMBNAIL))) +// .join(category).on(category.id.eq(information.category.id).and(categoryCondition)) +// .where(whereClause) +// .groupBy(information.id, zoneCategoryChild.id, zoneCategoryParent.id, image.id) +// .orderBy(getOrderSpecifier(informationPageRequest.getSort(), information.id)) +// .select(Projections.constructor( +// InformationBriefResponse.class, +// information.id, +// information.title, +// zoneCategoryParent.name, +// zoneCategoryChild.name, +// information.category.name, +// information.viewCount, +// isInformationBookmark(userId, information.id), +// image.address, +// countGreatInformationByInformationById(information.id), +// isUserGreatInformation(userId) +// )).offset(pageable.getOffset()) +// .limit(pageable.getPageSize()) +// .fetch(); +// System.out.println(list.size()); +// System.out.println(list); +// +// return new PageImpl<>(list, pageable, total); +// } + @Override public List getInformationLikeCountFromCreatedIn3(Long userId) { @@ -112,7 +202,7 @@ public List getInformationLikeCountFromCreatedIn3(Long .leftJoin(category).on(category.id.eq(information.category.id)) .where(information.createdDate.after(LocalDateTime.now().minusMonths(3))) .groupBy(information.id, zoneCategoryParent.name, zoneCategoryChild.name, image.address) - .orderBy(countGreatInformationByInformationById(information.id).desc()) + .orderBy(countGreatInformationByInformationByIdSubQuery(information.id).desc()) .select(Projections.constructor( InformationMainResponse.class, information.id, @@ -121,10 +211,10 @@ public List getInformationLikeCountFromCreatedIn3(Long zoneCategoryChild.name, category.parentCategory.name, information.viewCount, - isInformationBookmark(userId, information.id), + isInformationBookmarkSubQuery(userId, information.id), image.address, - countGreatInformationByInformationById(information.id), // 파라미터 전달 - isUserGreatInformation(userId) + countGreatInformationByInformationByIdSubQuery(information.id), // 파라미터 전달 + isUserGreatInformationSubQuery(userId) )).limit(6).fetch(); } @@ -147,17 +237,18 @@ public List getInformationRecommend(Long informationId zoneCategoryChild.name, information.category.name, information.viewCount, - isInformationBookmark(userId, information.id), + isInformationBookmarkSubQuery(userId, information.id), image.address, - countGreatInformationByInformationById(information.id), - isUserGreatInformation(userId) + countGreatInformationByInformationByIdSubQuery(information.id), + isUserGreatInformationSubQuery(userId) )) .limit(3L) .fetch(); } @Override - public Page getInformationPageByTag(Pageable pageable, Long userId, Long parentCategoryId, + public Page getInformationPageByTag(Pageable pageable, Long userId, Long + parentCategoryId, InformationPageRequest informationPageRequest, String decodedTag) { BooleanBuilder whereClause = new BooleanBuilder(); @@ -213,8 +304,8 @@ public Page getInformationPageByTag(Pageable pageable, information.viewCount, bookMarkInformation.user.id.isNotNull(), image.address, - countGreatInformationByInformationById(information.id), - isUserGreatInformation(userId) + countGreatInformationByInformationByIdSubQuery(information.id), + isUserGreatInformationSubQuery(userId) )).offset(pageable.getOffset()) .limit(pageable.getPageSize()) .fetch(); @@ -228,7 +319,7 @@ public List getInformationRank() { .leftJoin(greatInformation) .on(greatInformation.information.id.eq(information.id)) .groupBy(information.id, information.title) - .orderBy(countGreatInformationByInformationById(information.id).desc()) + .orderBy(countGreatInformationByInformationByIdSubQuery(information.id).desc()) .limit(5) .select(Projections.constructor( InformationRankResponse.class, @@ -240,7 +331,7 @@ public List getInformationRank() { private OrderSpecifier getOrderSpecifier(String sort, NumberPath informationId) { if (Objects.nonNull(sort)) { if (Objects.equals(LIKE_COUNT_SORT, sort)) { - return countGreatInformationByInformationById(informationId).desc(); + return countGreatInformationByInformationByIdSubQuery(informationId).desc(); } else if (Objects.equals(VIEW_COUNT_SORT, sort)) { return information.viewCount.desc(); } @@ -248,7 +339,7 @@ private OrderSpecifier getOrderSpecifier(String sort, NumberPath inform return information.createdDate.desc(); } - private NumberExpression countGreatInformationByInformationById(NumberPath informationId) { + private NumberExpression countGreatInformationByInformationByIdSubQuery(NumberPath informationId) { QGreatInformation greatInformationSub = QGreatInformation.greatInformation; JPQLQuery likeCountSubQuery = JPAExpressions .select(greatInformationSub.count()) @@ -260,7 +351,7 @@ private NumberExpression countGreatInformationByInformationById(NumberP .intValue(); } - private BooleanExpression isUserGreatInformation(Long userId) { + private BooleanExpression isUserGreatInformationSubQuery(Long userId) { return new CaseBuilder() .when(JPAExpressions.selectOne() .from(greatInformation) @@ -271,7 +362,7 @@ private BooleanExpression isUserGreatInformation(Long userId) { .otherwise(false); } - private BooleanExpression isInformationBookmark(Long userId, NumberPath informationId) { + private BooleanExpression isInformationBookmarkSubQuery(Long userId, NumberPath informationId) { return new CaseBuilder() .when(JPAExpressions.selectOne() .from(bookMarkInformation) From 70050e11c05529a35aac78c90f01b655fa3edc93 Mon Sep 17 00:00:00 2001 From: hyeonjaez Date: Fri, 11 Oct 2024 11:56:23 +0900 Subject: [PATCH 08/10] =?UTF-8?q?fix:=20=ED=8E=98=EC=9D=B4=EC=A7=80?= =?UTF-8?q?=EB=84=A4=EC=9D=B4=EC=85=98=20=EB=A9=94=EC=84=9C=EB=93=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=EC=9C=BC=EB=A1=9C=20=EC=9D=B8=ED=95=B4=20ser?= =?UTF-8?q?vice=EC=97=90=EC=84=9C=20=ED=98=B8=EC=B6=9C=ED=95=98=EB=8A=94?= =?UTF-8?q?=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../solitour/information/service/InformationService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/solitour_backend/solitour/information/service/InformationService.java b/src/main/java/solitour_backend/solitour/information/service/InformationService.java index 8cfef9e..f0b04ec 100644 --- a/src/main/java/solitour_backend/solitour/information/service/InformationService.java +++ b/src/main/java/solitour_backend/solitour/information/service/InformationService.java @@ -458,7 +458,7 @@ public Page getPageInformation(Pageable pageable, Long } } - return informationRepository.getInformationPageFilterAndOrder(pageable, informationPageRequest, userId, parentCategoryId); + return informationRepository.getPageInformationFilterAndOrder(pageable, informationPageRequest, userId, parentCategoryId); } public List getRankInformation() { From e07124ab06f57d65bcee0f33b57763326e4de694 Mon Sep 17 00:00:00 2001 From: hyeonjaez Date: Mon, 14 Oct 2024 02:31:31 +0900 Subject: [PATCH 09/10] =?UTF-8?q?style:=20=ED=95=84=EC=9A=94=EC=97=86?= =?UTF-8?q?=EB=8A=94=20=EC=A3=BC=EC=84=9D=EB=AC=B8=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../information/repository/InformationRepositoryCustom.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/java/solitour_backend/solitour/information/repository/InformationRepositoryCustom.java b/src/main/java/solitour_backend/solitour/information/repository/InformationRepositoryCustom.java index 0076f59..4ca7daa 100644 --- a/src/main/java/solitour_backend/solitour/information/repository/InformationRepositoryCustom.java +++ b/src/main/java/solitour_backend/solitour/information/repository/InformationRepositoryCustom.java @@ -16,10 +16,6 @@ public interface InformationRepositoryCustom { String VIEW_COUNT_SORT = "views"; Page getPageInformationFilterAndOrder(Pageable pageable, InformationPageRequest informationPageRequest, Long userId, Long parentCategoryId); -// Page getInformationPageFilterAndOrder(Pageable pageable, -// InformationPageRequest informationPageRequest, -// Long userId, Long parentCategoryId); - List getInformationRank(); List getInformationLikeCountFromCreatedIn3(Long userId); From f6ea5e703547284f584917384a3824159f03b753 Mon Sep 17 00:00:00 2001 From: hyeonjaez Date: Mon, 14 Oct 2024 02:32:26 +0900 Subject: [PATCH 10/10] =?UTF-8?q?refactor:=20=EB=8B=A4=EB=A5=B8=20?= =?UTF-8?q?=EC=BF=BC=EB=A6=AC=EB=93=A4=20subquery=20->=20join=20=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/InformationRepositoryImpl.java | 112 ++++-------------- 1 file changed, 24 insertions(+), 88 deletions(-) diff --git a/src/main/java/solitour_backend/solitour/information/repository/InformationRepositoryImpl.java b/src/main/java/solitour_backend/solitour/information/repository/InformationRepositoryImpl.java index c7799cb..5f844e2 100644 --- a/src/main/java/solitour_backend/solitour/information/repository/InformationRepositoryImpl.java +++ b/src/main/java/solitour_backend/solitour/information/repository/InformationRepositoryImpl.java @@ -133,88 +133,32 @@ private NumberExpression countGreatInformation(QGreatInformation greatI return greatInformation.id.count().intValue(); } -// @Override -// public Page getInformationPageFilterAndOrder(Pageable pageable, -// InformationPageRequest informationPageRequest, -// Long userId, Long parentCategoryId) { -// BooleanBuilder whereClause = new BooleanBuilder(); -// -// if (Objects.nonNull(informationPageRequest.getZoneCategoryId())) { -// whereClause.and( -// information.zoneCategory.parentZoneCategory.id.eq(informationPageRequest.getZoneCategoryId())); -// } -// -// BooleanBuilder categoryCondition = new BooleanBuilder(); -// -// if (Objects.nonNull(informationPageRequest.getChildCategoryId())) { -// whereClause.and(information.category.id.eq(informationPageRequest.getChildCategoryId())); -// } else { -// categoryCondition.and(category.parentCategory.id.eq(parentCategoryId)); -// } -// -// if (Objects.nonNull(informationPageRequest.getSearch())) { -// String searchKeyword = informationPageRequest.getSearch().trim().replace(" ", ""); -// whereClause.and(information.title.trim().containsIgnoreCase(searchKeyword)); -// } -// -// -// long total = from(information) -// .where(whereClause) -// .select(information.id).fetchCount(); -// System.out.println("page 네이션 총 데이터 갯수 : " + total); -// List list = from(information) -// .join(zoneCategoryChild).on(zoneCategoryChild.id.eq(information.zoneCategory.id)) -// .leftJoin(zoneCategoryParent).on(zoneCategoryParent.id.eq(zoneCategoryChild.parentZoneCategory.id)) -// .leftJoin(image) -// .on(image.information.id.eq(information.id).and(image.imageStatus.eq(ImageStatus.THUMBNAIL))) -// .join(category).on(category.id.eq(information.category.id).and(categoryCondition)) -// .where(whereClause) -// .groupBy(information.id, zoneCategoryChild.id, zoneCategoryParent.id, image.id) -// .orderBy(getOrderSpecifier(informationPageRequest.getSort(), information.id)) -// .select(Projections.constructor( -// InformationBriefResponse.class, -// information.id, -// information.title, -// zoneCategoryParent.name, -// zoneCategoryChild.name, -// information.category.name, -// information.viewCount, -// isInformationBookmark(userId, information.id), -// image.address, -// countGreatInformationByInformationById(information.id), -// isUserGreatInformation(userId) -// )).offset(pageable.getOffset()) -// .limit(pageable.getPageSize()) -// .fetch(); -// System.out.println(list.size()); -// System.out.println(list); -// -// return new PageImpl<>(list, pageable, total); -// } - @Override public List getInformationLikeCountFromCreatedIn3(Long userId) { return from(information) .leftJoin(zoneCategoryChild).on(zoneCategoryChild.id.eq(information.zoneCategory.id)) - .leftJoin(zoneCategoryParent).on(zoneCategoryParent.id.eq(zoneCategoryChild.parentZoneCategory.id)) - .leftJoin(image).on(image.information.id.eq(information.id).and(image.imageStatus.eq(ImageStatus.THUMBNAIL))) + .leftJoin(zoneCategoryParent).on(zoneCategoryChild.parentZoneCategory.id.eq(zoneCategoryParent.id)) .leftJoin(category).on(category.id.eq(information.category.id)) + .leftJoin(categoryParent).on(categoryParent.id.eq(category.parentCategory.id)) + .leftJoin(image).on(image.information.id.eq(information.id).and(image.imageStatus.eq(ImageStatus.THUMBNAIL))) + .leftJoin(bookMarkInformation).on(bookMarkInformation.information.id.eq(information.id).and(bookMarkInformation.user.id.eq(userId))) + .leftJoin(greatInformation).on(greatInformation.information.id.eq(information.id)) .where(information.createdDate.after(LocalDateTime.now().minusMonths(3))) - .groupBy(information.id, zoneCategoryParent.name, zoneCategoryChild.name, image.address) - .orderBy(countGreatInformationByInformationByIdSubQuery(information.id).desc()) + .groupBy(information.id, information.createdDate, information.viewCount, zoneCategoryChild.name, bookMarkInformation.id, image.address) + .orderBy(countGreatInformation(greatInformation).desc()) .select(Projections.constructor( InformationMainResponse.class, information.id, information.title, zoneCategoryParent.name, zoneCategoryChild.name, - category.parentCategory.name, + information.category.name, information.viewCount, - isInformationBookmarkSubQuery(userId, information.id), + isBookMarkBooleanExpression(bookMarkInformation), image.address, - countGreatInformationByInformationByIdSubQuery(information.id), // 파라미터 전달 - isUserGreatInformationSubQuery(userId) + countGreatInformation(greatInformation), + isGreatBooleanExpression(userId, greatInformation) )).limit(6).fetch(); } @@ -222,12 +166,15 @@ public List getInformationLikeCountFromCreatedIn3(Long public List getInformationRecommend(Long informationId, Long childCategoryId, Long userId) { return from(information) - .join(zoneCategoryChild).on(zoneCategoryChild.id.eq(information.zoneCategory.id)) - .leftJoin(zoneCategoryParent).on(zoneCategoryParent.id.eq(zoneCategoryChild.parentZoneCategory.id)) - .leftJoin(image).on(image.information.id.eq(information.id) - .and(image.imageStatus.eq(ImageStatus.THUMBNAIL))) + .leftJoin(zoneCategoryChild).on(zoneCategoryChild.id.eq(information.zoneCategory.id)) + .leftJoin(zoneCategoryParent).on(zoneCategoryChild.parentZoneCategory.id.eq(zoneCategoryParent.id)) + .leftJoin(category).on(category.id.eq(information.category.id)) + .leftJoin(categoryParent).on(categoryParent.id.eq(category.parentCategory.id)) + .leftJoin(image).on(image.information.id.eq(information.id).and(image.imageStatus.eq(ImageStatus.THUMBNAIL))) + .leftJoin(bookMarkInformation).on(bookMarkInformation.information.id.eq(information.id).and(bookMarkInformation.user.id.eq(userId))) + .leftJoin(greatInformation).on(greatInformation.information.id.eq(information.id)) .where(information.category.id.eq(childCategoryId).and(information.id.ne(informationId))) - .groupBy(information.id, zoneCategoryChild.id, zoneCategoryParent.id, image.id) + .groupBy(information.id, information.createdDate, information.viewCount, zoneCategoryChild.name, bookMarkInformation.id, image.address) .orderBy(information.createdDate.desc()) .select(Projections.constructor( InformationBriefResponse.class, @@ -237,10 +184,10 @@ public List getInformationRecommend(Long informationId zoneCategoryChild.name, information.category.name, information.viewCount, - isInformationBookmarkSubQuery(userId, information.id), + isBookMarkBooleanExpression(bookMarkInformation), image.address, - countGreatInformationByInformationByIdSubQuery(information.id), - isUserGreatInformationSubQuery(userId) + countGreatInformation(greatInformation), + isGreatBooleanExpression(userId, greatInformation) )) .limit(3L) .fetch(); @@ -319,7 +266,7 @@ public List getInformationRank() { .leftJoin(greatInformation) .on(greatInformation.information.id.eq(information.id)) .groupBy(information.id, information.title) - .orderBy(countGreatInformationByInformationByIdSubQuery(information.id).desc()) + .orderBy(countGreatInformation(greatInformation).desc()) .limit(5) .select(Projections.constructor( InformationRankResponse.class, @@ -344,7 +291,7 @@ private NumberExpression countGreatInformationByInformationByIdSubQuery JPQLQuery likeCountSubQuery = JPAExpressions .select(greatInformationSub.count()) .from(greatInformationSub) - .where(greatInformationSub.information.id.eq(informationId)); // 파라미터로 받은 NumberPath와 비교 + .where(greatInformationSub.information.id.eq(informationId)); return Expressions.numberTemplate(Long.class, "{0}", likeCountSubQuery) .coalesce(0L) @@ -362,16 +309,5 @@ private BooleanExpression isUserGreatInformationSubQuery(Long userId) { .otherwise(false); } - private BooleanExpression isInformationBookmarkSubQuery(Long userId, NumberPath informationId) { - return new CaseBuilder() - .when(JPAExpressions.selectOne() - .from(bookMarkInformation) - .where(bookMarkInformation.information.id.eq(informationId) - .and(bookMarkInformation.user.id.eq(userId))) - .exists()) - .then(true) - .otherwise(false); - } - }