From ecc15de51e1c0fa6009fd3d0c4ff266a06f1d89a Mon Sep 17 00:00:00 2001 From: msk226 Date: Sun, 16 Mar 2025 17:18:20 +0900 Subject: [PATCH 01/13] =?UTF-8?q?[FEATURE]=20Attraction=20=EC=97=94?= =?UTF-8?q?=ED=8B=B0=ED=8B=B0=20=EB=82=B4=EC=9A=A9=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hyu/erica/capstone/domain/Attraction.java | 88 ++++++++++++++++++- 1 file changed, 87 insertions(+), 1 deletion(-) diff --git a/src/main/java/hyu/erica/capstone/domain/Attraction.java b/src/main/java/hyu/erica/capstone/domain/Attraction.java index 5a9b567..28f8c18 100644 --- a/src/main/java/hyu/erica/capstone/domain/Attraction.java +++ b/src/main/java/hyu/erica/capstone/domain/Attraction.java @@ -1,5 +1,91 @@ package hyu.erica.capstone.domain; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Lob; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.hibernate.annotations.DynamicInsert; +import org.hibernate.annotations.DynamicUpdate; + +@Entity +@Getter +@Builder +@DynamicUpdate +@DynamicInsert +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor public class Attraction { - // 추후 작성 예정 + + @Id + @Column(name = "UC_SEQ") + private Long ucSeq; + + @Column(name = "MAIN_TITLE") + private String mainTitle; + + @Column(name = "GUGUN_NM") + private String gugunNm; + + @Column(name = "LAT") + private Double lat; + + @Column(name = "LNG") + private Double lng; + + @Column(name = "PLACE") + private String place; + + @Column(name = "TITLE") + private String title; + + @Column(name = "SUBTITLE") + private String subtitle; + + @Column(name = "MAIN_PLACE") + private String mainPlace; + + @Column(name = "ADDR1") + private String addr1; + + @Column(name = "ADDR2") + private String addr2; + + @Column(name = "CNTCT_TEL") + private String cntctTel; + + @Column(name = "HOMEPAGE_URL") + private String homepageUrl; + + @Column(name = "TRFC_INFO") + private String trfcInfo; + + @Column(name = "USAGE_DAY") + private String usageDay; + + @Column(name = "HLDY_INFO") + private String hldyInfo; + + @Column(name = "USAGE_DAY_WEEK_AND_TIME") + private String usageDayWeekAndTime; + + @Column(name = "USAGE_AMOUNT") + private String usageAmount; + + @Column(name = "MIDDLE_SIZE_RM1") + private String middleSizeRm1; + + @Column(name = "MAIN_IMG_NORMAL") + private String mainImgNormal; + + @Column(name = "MAIN_IMG_THUMB") + private String mainImgThumb; + + @Lob + @Column(name = "ITEMCNTNTS") + private String itemcntnts; // ITEMCNTNTS (상세 설명) } From 6186751d292ce7719e8c537b13dc00368c7d26bf Mon Sep 17 00:00:00 2001 From: msk226 Date: Sun, 16 Mar 2025 17:44:57 +0900 Subject: [PATCH 02/13] =?UTF-8?q?[FEATURE]=20Attraction=20=ED=85=8C?= =?UTF-8?q?=EC=9D=B4=EB=B8=94=20=EB=82=B4=20=EC=BB=AC=EB=9F=BC=20=ED=83=80?= =?UTF-8?q?=EC=9E=85=20TEXT=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 --- src/main/java/hyu/erica/capstone/domain/Attraction.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/hyu/erica/capstone/domain/Attraction.java b/src/main/java/hyu/erica/capstone/domain/Attraction.java index 28f8c18..4675762 100644 --- a/src/main/java/hyu/erica/capstone/domain/Attraction.java +++ b/src/main/java/hyu/erica/capstone/domain/Attraction.java @@ -61,7 +61,7 @@ public class Attraction { @Column(name = "HOMEPAGE_URL") private String homepageUrl; - @Column(name = "TRFC_INFO") + @Column(name = "TRFC_INFO", columnDefinition = "TEXT") private String trfcInfo; @Column(name = "USAGE_DAY") @@ -70,7 +70,7 @@ public class Attraction { @Column(name = "HLDY_INFO") private String hldyInfo; - @Column(name = "USAGE_DAY_WEEK_AND_TIME") + @Column(name = "USAGE_DAY_WEEK_AND_TIME", columnDefinition = "TEXT") private String usageDayWeekAndTime; @Column(name = "USAGE_AMOUNT") From e475a6a2e99d69ea262b95d4a5a9ecea29fca54c Mon Sep 17 00:00:00 2001 From: msk226 Date: Sun, 16 Mar 2025 17:45:11 +0900 Subject: [PATCH 03/13] =?UTF-8?q?[FEATURE]=20CSV=20=ED=8C=8C=EC=9D=BC=20?= =?UTF-8?q?=ED=8C=8C=EC=8B=B1=20=EC=9C=84=ED=95=9C=20=EC=9D=98=EC=A1=B4?= =?UTF-8?q?=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 3 +++ 1 file changed, 3 insertions(+) diff --git a/build.gradle b/build.gradle index cd91719..889131a 100644 --- a/build.gradle +++ b/build.gradle @@ -45,6 +45,9 @@ dependencies { // spring security implementation 'org.springframework.boot:spring-boot-starter-security' + + // apache commons + implementation 'org.apache.commons:commons-csv:1.8' } From 4e01f028708eb8c137a5ce277144380f15ed7dae Mon Sep 17 00:00:00 2001 From: msk226 Date: Sun, 16 Mar 2025 17:45:23 +0900 Subject: [PATCH 04/13] =?UTF-8?q?[FEATURE]=20Attraction=20=EC=97=85?= =?UTF-8?q?=EB=A1=9C=EB=93=9C=20API=20=EC=A0=9C=EC=9E=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../capstone/api/code/status/ErrorStatus.java | 3 + .../repository/AttractionRepository.java | 9 +++ .../erica/capstone/web/CsvImportService.java | 77 +++++++++++++++++++ .../web/controller/DefaultController.java | 26 +++++++ 4 files changed, 115 insertions(+) create mode 100644 src/main/java/hyu/erica/capstone/repository/AttractionRepository.java create mode 100644 src/main/java/hyu/erica/capstone/web/CsvImportService.java diff --git a/src/main/java/hyu/erica/capstone/api/code/status/ErrorStatus.java b/src/main/java/hyu/erica/capstone/api/code/status/ErrorStatus.java index 0abda79..81339e6 100644 --- a/src/main/java/hyu/erica/capstone/api/code/status/ErrorStatus.java +++ b/src/main/java/hyu/erica/capstone/api/code/status/ErrorStatus.java @@ -29,6 +29,9 @@ public enum ErrorStatus implements BaseErrorCode { _MEMBER_EMAIL_EXIST(HttpStatus.BAD_REQUEST, "COMMON4014", "이미 가입 된 이메일입니다. 다른 로그인 방식을 이용해주세요."), _RSA_ERROR(HttpStatus.BAD_REQUEST, "COMMON4015", "RSA 에러가 발생했습니다."), + // 파일 에러 + _FILE_INPUT_ERROR(HttpStatus.BAD_REQUEST, "FILE4000", "파일 입력 중 에러가 발생했습니다."), + //유저 에러 _USER_NOT_FOUND(HttpStatus.NOT_FOUND, "USER4040", "사용자를 찾을 수 없습니다."), _INVALID_PASSWORD(HttpStatus.BAD_REQUEST, "USER4001", "비밀번호가 일치하지 않습니다."), diff --git a/src/main/java/hyu/erica/capstone/repository/AttractionRepository.java b/src/main/java/hyu/erica/capstone/repository/AttractionRepository.java new file mode 100644 index 0000000..73e34a3 --- /dev/null +++ b/src/main/java/hyu/erica/capstone/repository/AttractionRepository.java @@ -0,0 +1,9 @@ +package hyu.erica.capstone.repository; + +import hyu.erica.capstone.domain.Attraction; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface AttractionRepository extends JpaRepository { +} diff --git a/src/main/java/hyu/erica/capstone/web/CsvImportService.java b/src/main/java/hyu/erica/capstone/web/CsvImportService.java new file mode 100644 index 0000000..452d177 --- /dev/null +++ b/src/main/java/hyu/erica/capstone/web/CsvImportService.java @@ -0,0 +1,77 @@ +package hyu.erica.capstone.web; + +import static java.lang.Double.parseDouble; + +import hyu.erica.capstone.api.code.status.ErrorStatus; +import hyu.erica.capstone.api.exception.GeneralException; +import hyu.erica.capstone.domain.Attraction; +import hyu.erica.capstone.repository.AttractionRepository; +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.apache.commons.csv.CSVFormat; +import org.apache.commons.csv.CSVParser; +import org.apache.commons.csv.CSVRecord; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; + +@Service +@Transactional +@RequiredArgsConstructor +public class CsvImportService { + + private final AttractionRepository attractionRepository; + + + public void importAttraction(MultipartFile file) { + try (BufferedReader reader = new BufferedReader(new InputStreamReader(file.getInputStream(), StandardCharsets.UTF_8)); + CSVParser csvParser = new CSVParser(reader, CSVFormat.DEFAULT.withFirstRecordAsHeader())) { + + List touristSpots = new ArrayList<>(); + + for (CSVRecord record : csvParser) { + Attraction spot = Attraction.builder() + .ucSeq(Long.parseLong(record.get("UC_SEQ"))) + .mainTitle(record.get("MAIN_TITLE")) + .gugunNm(record.get("GUGUN_NM")) + .lat(parseDouble(record.get("LAT"))) + .lng(parseDouble(record.get("LNG"))) + .place(record.get("PLACE")) + .title(record.get("TITLE")) + .subtitle(record.get("SUBTITLE")) + .mainPlace(record.get("MAIN_PLACE")) + .addr1(record.get("ADDR1")) + .addr2(record.get("ADDR2")) + .cntctTel(record.get("CNTCT_TEL")) + .homepageUrl(record.get("HOMEPAGE_URL")) + .trfcInfo(trimString(record.get("TRFC_INFO"), 1000)) + .usageDay(record.get("USAGE_DAY")) + .hldyInfo(record.get("HLDY_INFO")) + .usageDayWeekAndTime(record.get("USAGE_DAY_WEEK_AND_TIME")) + .usageAmount(record.get("USAGE_AMOUNT")) + .middleSizeRm1(record.get("MIDDLE_SIZE_RM1")) + .mainImgNormal(record.get("MAIN_IMG_NORMAL")) + .mainImgThumb(record.get("MAIN_IMG_THUMB")) + .build(); + + touristSpots.add(spot); + } + attractionRepository.saveAll(touristSpots); + } catch (Exception e) { + e.printStackTrace(); + throw new GeneralException(ErrorStatus._FILE_INPUT_ERROR); + } + } + + private String trimString(String value, int maxLength) { + if (value == null) return null; + return value.length() > maxLength ? value.substring(0, maxLength) : value; + } + + + +} diff --git a/src/main/java/hyu/erica/capstone/web/controller/DefaultController.java b/src/main/java/hyu/erica/capstone/web/controller/DefaultController.java index 9e7ed2f..a2dd773 100644 --- a/src/main/java/hyu/erica/capstone/web/controller/DefaultController.java +++ b/src/main/java/hyu/erica/capstone/web/controller/DefaultController.java @@ -1,17 +1,43 @@ package hyu.erica.capstone.web.controller; +import hyu.erica.capstone.api.ApiResponse; +import hyu.erica.capstone.api.code.status.SuccessStatus; +import hyu.erica.capstone.web.CsvImportService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; +import java.io.File; +import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; @Tag(name = "기본", description = "기본 API") +@RequestMapping("/api") @RestController +@RequiredArgsConstructor public class DefaultController { + private final CsvImportService importService; + @Operation(summary = "Health Check", description = "서버 상태 확인") @GetMapping("/health-check") public String healthCheck() { return "Hello, World!"; } + + @Operation(summary = "[서버 개발 용] 어트랙션 파일 업로드", description = """ + ### 어트랙션 파일을 업로드합니다. + + ### Request Body + - file: 어트랙션 파일 + + """) + @PostMapping("/upload/attractions") + public ApiResponse uploadAttractionFile(@RequestParam MultipartFile file) { + importService.importAttraction(file); + return ApiResponse.onSuccess(SuccessStatus._OK); + } } From 6c74abc1d3b8f9a6642c9ad5767920f5885d9ee9 Mon Sep 17 00:00:00 2001 From: msk226 Date: Sun, 16 Mar 2025 17:48:40 +0900 Subject: [PATCH 05/13] =?UTF-8?q?[FEATURE]=20API=20=EB=AA=85=EC=84=B8?= =?UTF-8?q?=EC=84=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../capstone/web/controller/StyleController.java | 2 +- .../capstone/web/controller/UserController.java | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/java/hyu/erica/capstone/web/controller/StyleController.java b/src/main/java/hyu/erica/capstone/web/controller/StyleController.java index 0d1bcce..e651616 100644 --- a/src/main/java/hyu/erica/capstone/web/controller/StyleController.java +++ b/src/main/java/hyu/erica/capstone/web/controller/StyleController.java @@ -36,7 +36,7 @@ public class StyleController { - name : 회원 이름 - styleId : 스타일 ID (해당 스타일 ID로 선호 여행 스타일을 조회, 수정할 수 있습니다.) """) - @GetMapping("/") + @GetMapping("") public ApiResponse getUserPlanStyle() { return ApiResponse.of(SuccessStatus._OK, styleCommandService.initStyle(SecurityUtils.getCurrentUserId())); } diff --git a/src/main/java/hyu/erica/capstone/web/controller/UserController.java b/src/main/java/hyu/erica/capstone/web/controller/UserController.java index b99b483..327ffbd 100644 --- a/src/main/java/hyu/erica/capstone/web/controller/UserController.java +++ b/src/main/java/hyu/erica/capstone/web/controller/UserController.java @@ -29,7 +29,7 @@ public class UserController { private final UserCommandService userCommandService; // 회원 가입 - @Operation(summary = "[회원 가입]", description = """ + @Operation(summary = "[회원 관련] 회원 가입", description = """ ### 회원 가입을 진행합니다. ### Request Body @@ -50,7 +50,7 @@ public ApiResponse signUp( return ApiResponse.onSuccess(SuccessStatus._OK, userCommandService.signUp(request)); } - @Operation(summary = "[이메일 중복 확인]", description = """ + @Operation(summary = "[회원 관련] 이메일 중복 확인 ", description = """ ### 이메일 중복을 확인합니다. ### Request Body @@ -62,7 +62,7 @@ public ApiResponse checkEmail(@RequestBody String email) { } // 로그인 - @Operation(summary = "[로그인]", description = """ + @Operation(summary = "[회원 관련] 로그인", description = """ ### 로그인을 진행합니다. 로그인 성공 시 access-token을 헤더에 포함하여 반환합니다. ### Request Body @@ -76,7 +76,7 @@ public ApiResponse login(@RequestBody SignInRequestDTO request, HttpServletRe } // 토큰 재발급 - @Operation(summary = "[토큰 재발급]", description = """ + @Operation(summary = "[회원 관련] 토큰 재발급", description = """ ### 토큰을 재발급합니다. refresh-token을 이용하여 access-token을 재발급합니다. ### Request Header @@ -89,7 +89,7 @@ public ApiResponse reissueToken() { // 마이페이지 - @Operation(summary = "[마이 페이지]", description = """ + @Operation(summary = "[회원 관련] 토큰 재발급", description = """ ### 마이페이지를 조회합니다. """) @GetMapping("/my-page") @@ -99,7 +99,7 @@ public ApiResponse myPage() { // 프로필 수정 - @Operation(summary = "[프로필 수정]", description = """ + @Operation(summary = "[회원 관련] 프로필 수정", description = """ ### 프로필을 수정합니다. ### Request Body From ea33339cf4d85fe2bf40127c52a8771d5777f8f6 Mon Sep 17 00:00:00 2001 From: msk226 Date: Sun, 16 Mar 2025 17:58:13 +0900 Subject: [PATCH 06/13] =?UTF-8?q?[FEATURE]=20CSV=20=EC=84=9C=EB=B9=84?= =?UTF-8?q?=EC=8A=A4=20=EB=94=94=EB=A0=89=ED=86=A0=EB=A6=AC=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hyu/erica/capstone/{web => service}/CsvImportService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/main/java/hyu/erica/capstone/{web => service}/CsvImportService.java (98%) diff --git a/src/main/java/hyu/erica/capstone/web/CsvImportService.java b/src/main/java/hyu/erica/capstone/service/CsvImportService.java similarity index 98% rename from src/main/java/hyu/erica/capstone/web/CsvImportService.java rename to src/main/java/hyu/erica/capstone/service/CsvImportService.java index 452d177..89b3300 100644 --- a/src/main/java/hyu/erica/capstone/web/CsvImportService.java +++ b/src/main/java/hyu/erica/capstone/service/CsvImportService.java @@ -1,4 +1,4 @@ -package hyu.erica.capstone.web; +package hyu.erica.capstone.service; import static java.lang.Double.parseDouble; From 04ce9709ea5d0e2c98959ee0f0aecf958140bc3e Mon Sep 17 00:00:00 2001 From: msk226 Date: Sun, 16 Mar 2025 17:58:42 +0900 Subject: [PATCH 07/13] =?UTF-8?q?[FEATURE]=20=EC=99=B8=EB=B6=80=20API=20?= =?UTF-8?q?=ED=98=B8=EC=B6=9C=20=EC=9C=84=ED=95=9C=20=EC=9D=98=EC=A1=B4?= =?UTF-8?q?=EC=84=B1=20=EC=B6=94=EA=B0=80=20=EB=B0=8E=20=EC=BD=94=EB=93=9C?= =?UTF-8?q?=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 4 ++++ .../hyu/erica/capstone/client/PlanClient.java | 15 +++++++++++++++ .../web/controller/DefaultController.java | 3 +-- 3 files changed, 20 insertions(+), 2 deletions(-) create mode 100644 src/main/java/hyu/erica/capstone/client/PlanClient.java diff --git a/build.gradle b/build.gradle index 889131a..af71f94 100644 --- a/build.gradle +++ b/build.gradle @@ -48,6 +48,10 @@ dependencies { // apache commons implementation 'org.apache.commons:commons-csv:1.8' + + // feign + implementation "org.springframework.cloud:spring-cloud-starter-openfeign:4.1.3" + implementation 'io.github.openfeign:feign-jackson:12.1' } diff --git a/src/main/java/hyu/erica/capstone/client/PlanClient.java b/src/main/java/hyu/erica/capstone/client/PlanClient.java new file mode 100644 index 0000000..3bbed0f --- /dev/null +++ b/src/main/java/hyu/erica/capstone/client/PlanClient.java @@ -0,0 +1,15 @@ +package hyu.erica.capstone.client; + +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; + +@FeignClient(name = "planApiClient", url = "${plan.api.url}") +public interface PlanClient { + + @GetMapping("/restaurants/search") + String getRestaurants(@RequestParam String query); + + @GetMapping("/attractions/search") + String getAttractions(@RequestParam String query); +} diff --git a/src/main/java/hyu/erica/capstone/web/controller/DefaultController.java b/src/main/java/hyu/erica/capstone/web/controller/DefaultController.java index a2dd773..0c71c3f 100644 --- a/src/main/java/hyu/erica/capstone/web/controller/DefaultController.java +++ b/src/main/java/hyu/erica/capstone/web/controller/DefaultController.java @@ -2,10 +2,9 @@ import hyu.erica.capstone.api.ApiResponse; import hyu.erica.capstone.api.code.status.SuccessStatus; -import hyu.erica.capstone.web.CsvImportService; +import hyu.erica.capstone.service.CsvImportService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; -import java.io.File; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; From c5de911aeb571e73e8dfae358c0012e96e36ba42 Mon Sep 17 00:00:00 2001 From: msk226 Date: Sun, 16 Mar 2025 18:19:13 +0900 Subject: [PATCH 08/13] =?UTF-8?q?[FEATURE]=20=EC=99=B8=EB=B6=80=20API=20?= =?UTF-8?q?=ED=98=B8=EC=B6=9C=20=EC=9C=84=ED=95=9C=20DTO=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20=EB=B0=8F=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/hyu/erica/capstone/CapstoneApplication.java | 2 ++ src/main/java/hyu/erica/capstone/client/PlanClient.java | 6 ++++-- .../erica/capstone/web/dto/client/AttractionRequestDTO.java | 6 ++++++ .../erica/capstone/web/dto/client/RestaurantRequestDTO.java | 6 ++++++ 4 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 src/main/java/hyu/erica/capstone/web/dto/client/AttractionRequestDTO.java create mode 100644 src/main/java/hyu/erica/capstone/web/dto/client/RestaurantRequestDTO.java diff --git a/src/main/java/hyu/erica/capstone/CapstoneApplication.java b/src/main/java/hyu/erica/capstone/CapstoneApplication.java index 11508a5..0043d10 100644 --- a/src/main/java/hyu/erica/capstone/CapstoneApplication.java +++ b/src/main/java/hyu/erica/capstone/CapstoneApplication.java @@ -2,10 +2,12 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.data.jpa.repository.config.EnableJpaAuditing; @SpringBootApplication @EnableJpaAuditing +@EnableFeignClients public class CapstoneApplication { public static void main(String[] args) { diff --git a/src/main/java/hyu/erica/capstone/client/PlanClient.java b/src/main/java/hyu/erica/capstone/client/PlanClient.java index 3bbed0f..8d26c93 100644 --- a/src/main/java/hyu/erica/capstone/client/PlanClient.java +++ b/src/main/java/hyu/erica/capstone/client/PlanClient.java @@ -1,5 +1,7 @@ package hyu.erica.capstone.client; +import hyu.erica.capstone.web.dto.client.AttractionRequestDTO; +import hyu.erica.capstone.web.dto.client.RestaurantRequestDTO; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; @@ -8,8 +10,8 @@ public interface PlanClient { @GetMapping("/restaurants/search") - String getRestaurants(@RequestParam String query); + RestaurantRequestDTO getRestaurants(@RequestParam String query); @GetMapping("/attractions/search") - String getAttractions(@RequestParam String query); + AttractionRequestDTO getAttractions(@RequestParam String query); } diff --git a/src/main/java/hyu/erica/capstone/web/dto/client/AttractionRequestDTO.java b/src/main/java/hyu/erica/capstone/web/dto/client/AttractionRequestDTO.java new file mode 100644 index 0000000..a0e6c38 --- /dev/null +++ b/src/main/java/hyu/erica/capstone/web/dto/client/AttractionRequestDTO.java @@ -0,0 +1,6 @@ +package hyu.erica.capstone.web.dto.client; + +import java.util.List; + +public record AttractionRequestDTO (String answer, List attraction_ids) { +} diff --git a/src/main/java/hyu/erica/capstone/web/dto/client/RestaurantRequestDTO.java b/src/main/java/hyu/erica/capstone/web/dto/client/RestaurantRequestDTO.java new file mode 100644 index 0000000..544c74c --- /dev/null +++ b/src/main/java/hyu/erica/capstone/web/dto/client/RestaurantRequestDTO.java @@ -0,0 +1,6 @@ +package hyu.erica.capstone.web.dto.client; + +import java.util.List; + +public record RestaurantRequestDTO(String answer, List restaurant_ids) { +} From 40f3184f102e2548255c6990a59559f5fc180b83 Mon Sep 17 00:00:00 2001 From: msk226 Date: Sun, 16 Mar 2025 18:19:35 +0900 Subject: [PATCH 09/13] =?UTF-8?q?[FEATURE]=20=EB=A7=A4=ED=95=91=20?= =?UTF-8?q?=ED=85=8C=EC=9D=B4=EB=B8=94=20=EC=97=94=ED=8B=B0=ED=8B=B0=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20=EC=9E=91=EC=84=B1=20=EB=B0=8F=20=EB=A0=88?= =?UTF-8?q?=ED=8F=AC=EC=A7=80=ED=86=A0=EB=A6=AC=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../erica/capstone/domain/mapping/PreferAttraction.java | 4 +++- .../erica/capstone/domain/mapping/PreferRestaurant.java | 2 ++ .../capstone/repository/PreferAttractionRepository.java | 9 +++++++++ .../capstone/repository/PreferRestaurantRepository.java | 9 +++++++++ 4 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 src/main/java/hyu/erica/capstone/repository/PreferAttractionRepository.java create mode 100644 src/main/java/hyu/erica/capstone/repository/PreferRestaurantRepository.java diff --git a/src/main/java/hyu/erica/capstone/domain/mapping/PreferAttraction.java b/src/main/java/hyu/erica/capstone/domain/mapping/PreferAttraction.java index b62807f..4663bbd 100644 --- a/src/main/java/hyu/erica/capstone/domain/mapping/PreferAttraction.java +++ b/src/main/java/hyu/erica/capstone/domain/mapping/PreferAttraction.java @@ -32,7 +32,9 @@ public class PreferAttraction extends BaseEntity { @Id @GeneratedValue(strategy = IDENTITY) private Long id; - //private Attraction attraction; + @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) + @JoinColumn(name = "attraction_id") + private Attraction attraction; @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) @JoinColumn(name = "user_id") diff --git a/src/main/java/hyu/erica/capstone/domain/mapping/PreferRestaurant.java b/src/main/java/hyu/erica/capstone/domain/mapping/PreferRestaurant.java index 9afa8c5..a84ec7c 100644 --- a/src/main/java/hyu/erica/capstone/domain/mapping/PreferRestaurant.java +++ b/src/main/java/hyu/erica/capstone/domain/mapping/PreferRestaurant.java @@ -32,6 +32,8 @@ public class PreferRestaurant extends BaseEntity { @Id @GeneratedValue(strategy = IDENTITY) private Long id; +// @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) +// @JoinColumn(name = "restaurant_id") // private Restaurant restaurant; @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) diff --git a/src/main/java/hyu/erica/capstone/repository/PreferAttractionRepository.java b/src/main/java/hyu/erica/capstone/repository/PreferAttractionRepository.java new file mode 100644 index 0000000..74a6b88 --- /dev/null +++ b/src/main/java/hyu/erica/capstone/repository/PreferAttractionRepository.java @@ -0,0 +1,9 @@ +package hyu.erica.capstone.repository; + +import hyu.erica.capstone.domain.mapping.PreferAttraction; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface PreferAttractionRepository extends JpaRepository { +} diff --git a/src/main/java/hyu/erica/capstone/repository/PreferRestaurantRepository.java b/src/main/java/hyu/erica/capstone/repository/PreferRestaurantRepository.java new file mode 100644 index 0000000..c0663c7 --- /dev/null +++ b/src/main/java/hyu/erica/capstone/repository/PreferRestaurantRepository.java @@ -0,0 +1,9 @@ +package hyu.erica.capstone.repository; + +import hyu.erica.capstone.domain.mapping.PreferRestaurant; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface PreferRestaurantRepository extends JpaRepository { +} From d5b48e2e86911b296ae44aaea0be431b3661eb4f Mon Sep 17 00:00:00 2001 From: msk226 Date: Sun, 16 Mar 2025 18:20:04 +0900 Subject: [PATCH 10/13] =?UTF-8?q?[FEATURE]=20=EC=8A=A4=ED=83=80=EC=9D=BC?= =?UTF-8?q?=20=EC=B5=9C=EC=A2=85=20=EC=9E=91=EC=84=B1=20=ED=9B=84,=20?= =?UTF-8?q?=EC=B6=94=EC=B2=9C=20=EC=97=AC=ED=96=89=EC=A7=80=20=EB=B0=8F=20?= =?UTF-8?q?=EC=8B=9D=EB=8B=B9=20=EB=AA=A9=EB=A1=9D=20=EB=B0=9B=EC=95=84?= =?UTF-8?q?=EC=98=AC=20API=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/RestaurantRepository.java | 9 ++++ .../service/style/StyleCommandService.java | 3 +- .../style/StyleCommandServiceImpl.java | 41 +++++++++++++++++++ .../web/controller/StyleController.java | 13 ++++++ .../web/controller/TripPlanController.java | 2 - .../response/UserStyleFinalResponseDTO.java | 10 +++++ 6 files changed, 75 insertions(+), 3 deletions(-) create mode 100644 src/main/java/hyu/erica/capstone/repository/RestaurantRepository.java create mode 100644 src/main/java/hyu/erica/capstone/web/dto/style/response/UserStyleFinalResponseDTO.java diff --git a/src/main/java/hyu/erica/capstone/repository/RestaurantRepository.java b/src/main/java/hyu/erica/capstone/repository/RestaurantRepository.java new file mode 100644 index 0000000..ba652c6 --- /dev/null +++ b/src/main/java/hyu/erica/capstone/repository/RestaurantRepository.java @@ -0,0 +1,9 @@ +package hyu.erica.capstone.repository; + +import hyu.erica.capstone.domain.Restaurant; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface RestaurantRepository extends JpaRepository { +} diff --git a/src/main/java/hyu/erica/capstone/service/style/StyleCommandService.java b/src/main/java/hyu/erica/capstone/service/style/StyleCommandService.java index 831552a..875f25c 100644 --- a/src/main/java/hyu/erica/capstone/service/style/StyleCommandService.java +++ b/src/main/java/hyu/erica/capstone/service/style/StyleCommandService.java @@ -1,6 +1,7 @@ package hyu.erica.capstone.service.style; import hyu.erica.capstone.web.dto.style.request.UserStyleRequestDTO; +import hyu.erica.capstone.web.dto.style.response.UserStyleFinalResponseDTO; import hyu.erica.capstone.web.dto.style.response.UserStyleInitResponseDTO; import hyu.erica.capstone.web.dto.style.response.UserStyleResponseDTO; @@ -10,5 +11,5 @@ public interface StyleCommandService { UserStyleResponseDTO updateStyle(Long userId, Long styleId, UserStyleRequestDTO request); - + UserStyleFinalResponseDTO submitStyle(Long styleId); } diff --git a/src/main/java/hyu/erica/capstone/service/style/StyleCommandServiceImpl.java b/src/main/java/hyu/erica/capstone/service/style/StyleCommandServiceImpl.java index d375afa..104e4b8 100644 --- a/src/main/java/hyu/erica/capstone/service/style/StyleCommandServiceImpl.java +++ b/src/main/java/hyu/erica/capstone/service/style/StyleCommandServiceImpl.java @@ -2,14 +2,23 @@ import hyu.erica.capstone.api.code.status.ErrorStatus; import hyu.erica.capstone.api.exception.GeneralException; +import hyu.erica.capstone.client.PlanClient; import hyu.erica.capstone.domain.Style; import hyu.erica.capstone.domain.User; import hyu.erica.capstone.domain.enums.City; +import hyu.erica.capstone.repository.AttractionRepository; +import hyu.erica.capstone.repository.PreferAttractionRepository; +import hyu.erica.capstone.repository.PreferRestaurantRepository; +import hyu.erica.capstone.repository.RestaurantRepository; import hyu.erica.capstone.repository.StyleRepository; import hyu.erica.capstone.repository.UserRepository; +import hyu.erica.capstone.web.dto.client.AttractionRequestDTO; +import hyu.erica.capstone.web.dto.client.RestaurantRequestDTO; import hyu.erica.capstone.web.dto.style.request.UserStyleRequestDTO; +import hyu.erica.capstone.web.dto.style.response.UserStyleFinalResponseDTO; import hyu.erica.capstone.web.dto.style.response.UserStyleInitResponseDTO; import hyu.erica.capstone.web.dto.style.response.UserStyleResponseDTO; +import java.util.List; import java.util.Objects; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -22,6 +31,13 @@ public class StyleCommandServiceImpl implements StyleCommandService { private final UserRepository userRepository; private final StyleRepository styleRepository; + private final RestaurantRepository restaurantRepository; + private final AttractionRepository attractionRepository; + + private final PreferRestaurantRepository preferRestaurantRepository; + private final PreferAttractionRepository preferAttractionRepository; + + private final PlanClient planClient; @Override public UserStyleInitResponseDTO initStyle(Long userId) { @@ -49,4 +65,29 @@ public UserStyleResponseDTO updateStyle(Long userId, Long styleId, UserStyleRequ return UserStyleResponseDTO.of(save); } + + @Override + public UserStyleFinalResponseDTO submitStyle(Long styleId) { + Style style = styleRepository.findById(styleId).orElseThrow( () -> new GeneralException(ErrorStatus._STYLE_NOT_FOUND)); + + + StringBuilder sb = new StringBuilder(); + sb.append("여행 지역 : ").append(style.getCity().name()).append("\n") + .append("시작 날짜 : ").append(style.getStartDate()).append("\n") + .append("종료 날짜 : ").append(style.getEndDate()).append("\n") + .append("선호 활동 : ").append(style.getPreferActivity()).append("\n") + .append("추가 요구 사항 : ").append(style.getRequirement()); + + //AttractionRequestDTO attractions = planClient.getAttractions(sb.toString()); + RestaurantRequestDTO restaurants = planClient.getRestaurants(sb.toString()); + + // TODO 매핑 테이블에 데이터 저장하기 + + List restaurantIds = restaurants.restaurant_ids(); + + + + + return UserStyleFinalResponseDTO.of(restaurants.restaurant_ids(), List.of()); + } } diff --git a/src/main/java/hyu/erica/capstone/web/controller/StyleController.java b/src/main/java/hyu/erica/capstone/web/controller/StyleController.java index e651616..3189bef 100644 --- a/src/main/java/hyu/erica/capstone/web/controller/StyleController.java +++ b/src/main/java/hyu/erica/capstone/web/controller/StyleController.java @@ -77,5 +77,18 @@ public ApiResponse getUserPlanStyle(@PathVariable Long styleId) { return ApiResponse.of(SuccessStatus._OK, styleQueryService.getStyle(styleId, SecurityUtils.getCurrentUserId())); } + @Operation(summary = "[선호 여행 스타일 최종 제출]", description = """ + ### 선호 여행 스타일을 최종 제출합니다. \n + styleId를 통해 특정 선호 여행 스타일을 최종 제출할 수 있습니다. \n + + ### Response Body + - restaurantIds: 추천 식당 ID 리스트 + - attractionIds: 추천 관광지 ID 리스트 + """) + @PostMapping("/{styleId}/final") + public ApiResponse saveFinalUserPlanStyle(@PathVariable Long styleId) { + return ApiResponse.onSuccess(SuccessStatus._OK, styleCommandService.submitStyle(styleId)); + } + } diff --git a/src/main/java/hyu/erica/capstone/web/controller/TripPlanController.java b/src/main/java/hyu/erica/capstone/web/controller/TripPlanController.java index cd0f0c5..fdda9d4 100644 --- a/src/main/java/hyu/erica/capstone/web/controller/TripPlanController.java +++ b/src/main/java/hyu/erica/capstone/web/controller/TripPlanController.java @@ -20,8 +20,6 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; - -@Tag(name = "[개발 전] 여행", description = "여행 관련 API") @CrossOrigin @RestController @RequestMapping("/api/trip-plans/{tripPlansId}") diff --git a/src/main/java/hyu/erica/capstone/web/dto/style/response/UserStyleFinalResponseDTO.java b/src/main/java/hyu/erica/capstone/web/dto/style/response/UserStyleFinalResponseDTO.java new file mode 100644 index 0000000..f8fdadf --- /dev/null +++ b/src/main/java/hyu/erica/capstone/web/dto/style/response/UserStyleFinalResponseDTO.java @@ -0,0 +1,10 @@ +package hyu.erica.capstone.web.dto.style.response; + +import java.util.List; + +public record UserStyleFinalResponseDTO (List restaurant_ids, List attraction_ids) { + + public static UserStyleFinalResponseDTO of(List restaurant_ids, List attraction_ids) { + return new UserStyleFinalResponseDTO(restaurant_ids, attraction_ids); + } +} From 3dd7fe027f13c261afe0e013437c4960c29aa6a4 Mon Sep 17 00:00:00 2001 From: msk226 Date: Sun, 16 Mar 2025 18:34:19 +0900 Subject: [PATCH 11/13] =?UTF-8?q?[FEATURE]=20=EB=A0=88=EC=8A=A4=ED=86=A0?= =?UTF-8?q?=EB=9E=91=20=EC=A0=95=EB=B3=B4=20=EC=9E=85=EB=A0=A5=20API=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hyu/erica/capstone/domain/Restaurant.java | 151 +++++++++++++++++- .../domain/mapping/PreferRestaurant.java | 6 +- .../capstone/service/CsvImportService.java | 32 ++++ .../web/controller/DefaultController.java | 15 ++ 4 files changed, 200 insertions(+), 4 deletions(-) diff --git a/src/main/java/hyu/erica/capstone/domain/Restaurant.java b/src/main/java/hyu/erica/capstone/domain/Restaurant.java index a5cfd5b..b59ab6e 100644 --- a/src/main/java/hyu/erica/capstone/domain/Restaurant.java +++ b/src/main/java/hyu/erica/capstone/domain/Restaurant.java @@ -1,5 +1,154 @@ package hyu.erica.capstone.domain; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.hibernate.annotations.DynamicInsert; +import org.hibernate.annotations.DynamicUpdate; + + +@Entity +@Table(name = "restaurant") // 테이블명 설정 +@Getter +@NoArgsConstructor +@AllArgsConstructor +@Builder public class Restaurant { - // 추후 작성 예정 + + @Id + @Column(name = "RSTR_ID") + private Long rstrId; // 식당 고유 ID + + @Column(name = "RSTR_NM") + private String rstrNm; // 식당 이름 + + @Column(name = "RSTR_RDNMADR") + private String rstrRdnmAdr; // 도로명 주소 + + @Column(name = "RSTR_LNNO_ADRES") + private String rstrLnnoAdres; // 지번 주소 + + @Column(name = "RSTR_LA") + private Double rstrLa; // 위도 + + @Column(name = "RSTR_LO") + private Double rstrLo; // 경도 + + @Column(name = "RSTR_TELNO") + private String rstrTelNo; // 전화번호 + + @Column(name = "BSNS_STATM_BZCND_NM") + private String bsnsStatmBzcndNm; // 영업 상태 + + @Column(name = "BSNS_LCNC_NM") + private String bsnsLcncNm; // 업종명 + + @Column(name = "RSTR_INTRCN_CONT", columnDefinition = "TEXT") + private String rstrIntrcnCont; // 식당 소개 + + @Column(name = "AREA_NM") + private String areaNm; // 지역명 + + @Column(name = "PRDL_SEAT_CNT") + private Integer prdlSeatCnt; // 예상 좌석 수 + + @Column(name = "SEAT_CNT") + private Integer seatCnt; // 실제 좌석 수 + + @Column(name = "PRKG_POS_YN") + private String prkgPosYn; // 주차 가능 여부 (Y/N) + + @Column(name = "WIFI_OFR_YN") + private String wifiOfrYn; // 와이파이 제공 여부 (Y/N) + + @Column(name = "DCRN_YN") + private String dcrnYn; // 할인 여부 (Y/N) + + @Column(name = "PET_ENTRN_POSBL_YN") + private String petEntrnPosblYn; // 반려동물 동반 가능 여부 (Y/N) + + @Column(name = "FGGG_MENU_OFR_YN") + private String fgggMenuOfrYn; // 외국인 메뉴 제공 여부 (Y/N) + + @Column(name = "TLROM_INFO_CN") + private String tlromInfoCn; // 화장실 정보 + + @Column(name = "RESTDY_INFO_CN") + private String restdyInfoCn; // 휴무일 정보 + + @Column(name = "BSNS_TM_CN") + private String bsnsTmCn; // 영업시간 + + @Column(name = "HMDLV_SALE_YN") + private String hmdlvSaleYn; // 홈딜리버리 판매 여부 (Y/N) + + @Column(name = "DSBR_CVNTL_YN") + private String dsbrCvntlYn; // 장애인 편의시설 제공 여부 (Y/N) + + @Column(name = "DELV_SRVIC_YN") + private String delvSrvicYn; // 배달 서비스 제공 여부 (Y/N) + + @Column(name = "RSRV_MTHD_NM") + private String rsrvMthdNm; // 예약 방법 + + @Column(name = "ONLINE_RSRV_INFO_CN") + private String onlineRsrvInfoCn; // 온라인 예약 정보 + + @Column(name = "HMPG_URL") + private String hmpgUrl; // 홈페이지 URL + + @Column(name = "CRCMF_LDMARK_NM") + private String crcmfLdmarkNm; // 근처 랜드마크 이름 + + @Column(name = "CRCMF_LDMARK_LA") + private Double crcmfLdmarkLa; // 근처 랜드마크 위도 + + @Column(name = "CRCMF_LDMARK_LO") + private Double crcmfLdmarkLo; // 근처 랜드마크 경도 + + @Column(name = "CRCMF_LDMARK_DIST") + private Double crcmfLdmarkDist; // 근처 랜드마크 거리 + + @Column(name = "KIOSK_YN") + private String kioskYn; // 키오스크 여부 (Y/N) + + @Column(name = "MB_PMAMT_YN") + private String mbPmamtYn; // 모바일 결제 가능 여부 (Y/N) + + @Column(name = "SMORDER_YN") + private String smorderYn; // 스마트 오더 가능 여부 (Y/N) + + @Column(name = "REPRSNT_MENU_NM") + private String reprsntMenuNm; // 대표 메뉴 + + @Column(name = "AWARD_INFO_DSCRN") + private String awardInfoDscrn; // 수상 정보 + + @Column(name = "RTI_IDEX") + private Double rtiIdex; // RTI 지수 + + @Column(name = "ONLINE_CONV_PRGS_YN") + private String onlineConvPrgsYn; // 온라인 컨버전 진행 여부 (Y/N) + + @Column(name = "ACCPN_STTUS_IDEX") + private Double accpnSttusIdex; // 수용 상태 지수 + + @Column(name = "RATING_IDEX") + private Double ratingIdex; // 평점 지수 + + @Column(name = "TRPDVSR_GRAD") + private Double trpdvsrGrad; // 트립어드바이저 평점 + + @Column(name = "CTRIP_GRAD") + private Double ctripGrad; // 씨트립 평점 + + @Column(name = "NAVER_GRAD") + private Double naverGrad; // 네이버 평점 } diff --git a/src/main/java/hyu/erica/capstone/domain/mapping/PreferRestaurant.java b/src/main/java/hyu/erica/capstone/domain/mapping/PreferRestaurant.java index a84ec7c..4d9da9c 100644 --- a/src/main/java/hyu/erica/capstone/domain/mapping/PreferRestaurant.java +++ b/src/main/java/hyu/erica/capstone/domain/mapping/PreferRestaurant.java @@ -32,9 +32,9 @@ public class PreferRestaurant extends BaseEntity { @Id @GeneratedValue(strategy = IDENTITY) private Long id; -// @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) -// @JoinColumn(name = "restaurant_id") -// private Restaurant restaurant; + @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) + @JoinColumn(name = "restaurant_id") + private Restaurant restaurant; @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) @JoinColumn(name = "user_id") diff --git a/src/main/java/hyu/erica/capstone/service/CsvImportService.java b/src/main/java/hyu/erica/capstone/service/CsvImportService.java index 89b3300..b172280 100644 --- a/src/main/java/hyu/erica/capstone/service/CsvImportService.java +++ b/src/main/java/hyu/erica/capstone/service/CsvImportService.java @@ -5,7 +5,9 @@ import hyu.erica.capstone.api.code.status.ErrorStatus; import hyu.erica.capstone.api.exception.GeneralException; import hyu.erica.capstone.domain.Attraction; +import hyu.erica.capstone.domain.Restaurant; import hyu.erica.capstone.repository.AttractionRepository; +import hyu.erica.capstone.repository.RestaurantRepository; import java.io.BufferedReader; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; @@ -25,6 +27,7 @@ public class CsvImportService { private final AttractionRepository attractionRepository; + private final RestaurantRepository restaurantRepository; public void importAttraction(MultipartFile file) { @@ -67,6 +70,35 @@ public void importAttraction(MultipartFile file) { } } + public void importRestaurant(MultipartFile file) { + try (BufferedReader reader = new BufferedReader(new InputStreamReader(file.getInputStream(), StandardCharsets.UTF_8)); + CSVParser csvParser = new CSVParser(reader, CSVFormat.DEFAULT.withFirstRecordAsHeader())) { + + List batchList = new ArrayList<>(); + + for (CSVRecord record : csvParser) { + Restaurant restaurant = Restaurant.builder() + .rstrId(Long.parseLong(record.get("RSTR_ID"))) + .rstrNm(record.get("RSTR_NM")) + .rstrRdnmAdr(record.get("RSTR_RDNMADR")) + .rstrLnnoAdres(record.get("RSTR_LNNO_ADRES")) + .rstrLa(parseDouble(record.get("RSTR_LA"))) + .rstrLo(parseDouble(record.get("RSTR_LO"))) + .rstrTelNo(record.get("RSTR_TELNO")) + .bsnsStatmBzcndNm(record.get("BSNS_STATM_BZCND_NM")) + .bsnsLcncNm(record.get("BSNS_LCNC_NM")) + .rstrIntrcnCont(record.get("RSTR_INTRCN_CONT")) + .build(); + batchList.add(restaurant); + } + + restaurantRepository.saveAll(batchList); + + } catch (Exception e) { + throw new GeneralException(ErrorStatus._FILE_INPUT_ERROR); + } + } + private String trimString(String value, int maxLength) { if (value == null) return null; return value.length() > maxLength ? value.substring(0, maxLength) : value; diff --git a/src/main/java/hyu/erica/capstone/web/controller/DefaultController.java b/src/main/java/hyu/erica/capstone/web/controller/DefaultController.java index 0c71c3f..d1e1013 100644 --- a/src/main/java/hyu/erica/capstone/web/controller/DefaultController.java +++ b/src/main/java/hyu/erica/capstone/web/controller/DefaultController.java @@ -39,4 +39,19 @@ public ApiResponse uploadAttractionFile(@RequestParam MultipartFile file) { importService.importAttraction(file); return ApiResponse.onSuccess(SuccessStatus._OK); } + + @Operation(summary = "[서버 개발 용] 식당 파일 업로드", description = """ + ### 식당 파일을 업로드합니다. + + ### Request Body + - file: 식당 파일 + + """) + @PostMapping("/upload/restaurants") + public ApiResponse uploadRestaurantFile(@RequestParam MultipartFile file) { + importService.importRestaurant(file); + return ApiResponse.onSuccess(SuccessStatus._OK); + } + + } From c5f67ff4156e51a2ba3740b61003adf4b54551b8 Mon Sep 17 00:00:00 2001 From: msk226 Date: Sun, 16 Mar 2025 18:34:38 +0900 Subject: [PATCH 12/13] =?UTF-8?q?[FEATURE]=20=EC=97=AC=ED=96=89=20?= =?UTF-8?q?=EC=8A=A4=ED=83=80=EC=9D=BC=20=EC=B5=9C=EC=A2=85=20=EC=A0=9C?= =?UTF-8?q?=EC=B6=9C=20=EA=B4=80=EB=A0=A8=20API=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../capstone/api/code/status/ErrorStatus.java | 3 ++ .../service/style/StyleCommandService.java | 2 +- .../style/StyleCommandServiceImpl.java | 30 +++++++++++++++---- .../web/controller/StyleController.java | 2 +- 4 files changed, 29 insertions(+), 8 deletions(-) diff --git a/src/main/java/hyu/erica/capstone/api/code/status/ErrorStatus.java b/src/main/java/hyu/erica/capstone/api/code/status/ErrorStatus.java index 81339e6..1aa00a7 100644 --- a/src/main/java/hyu/erica/capstone/api/code/status/ErrorStatus.java +++ b/src/main/java/hyu/erica/capstone/api/code/status/ErrorStatus.java @@ -41,6 +41,9 @@ public enum ErrorStatus implements BaseErrorCode { _STYLE_NOT_FOUND(HttpStatus.NOT_FOUND, "STYLE4040", "입력하신 여행 스타일을 찾을 수 없습니다."), _UNAUTHORIZED_USER(HttpStatus.FORBIDDEN, "STYLE4001", "해당 사용자는 권한이 없습니다."), + // 식당 관련 + _RESTAURANT_NOT_FOUND(HttpStatus.NOT_FOUND, "RESTAURANT4040", "입력하신 식당을 찾을 수 없습니다."), + ; diff --git a/src/main/java/hyu/erica/capstone/service/style/StyleCommandService.java b/src/main/java/hyu/erica/capstone/service/style/StyleCommandService.java index 875f25c..98658c4 100644 --- a/src/main/java/hyu/erica/capstone/service/style/StyleCommandService.java +++ b/src/main/java/hyu/erica/capstone/service/style/StyleCommandService.java @@ -11,5 +11,5 @@ public interface StyleCommandService { UserStyleResponseDTO updateStyle(Long userId, Long styleId, UserStyleRequestDTO request); - UserStyleFinalResponseDTO submitStyle(Long styleId); + UserStyleFinalResponseDTO submitStyle(Long styleId, Long userId); } diff --git a/src/main/java/hyu/erica/capstone/service/style/StyleCommandServiceImpl.java b/src/main/java/hyu/erica/capstone/service/style/StyleCommandServiceImpl.java index 104e4b8..d7ca4ab 100644 --- a/src/main/java/hyu/erica/capstone/service/style/StyleCommandServiceImpl.java +++ b/src/main/java/hyu/erica/capstone/service/style/StyleCommandServiceImpl.java @@ -3,9 +3,11 @@ import hyu.erica.capstone.api.code.status.ErrorStatus; import hyu.erica.capstone.api.exception.GeneralException; import hyu.erica.capstone.client.PlanClient; +import hyu.erica.capstone.domain.Restaurant; import hyu.erica.capstone.domain.Style; import hyu.erica.capstone.domain.User; import hyu.erica.capstone.domain.enums.City; +import hyu.erica.capstone.domain.mapping.PreferRestaurant; import hyu.erica.capstone.repository.AttractionRepository; import hyu.erica.capstone.repository.PreferAttractionRepository; import hyu.erica.capstone.repository.PreferRestaurantRepository; @@ -67,9 +69,9 @@ public UserStyleResponseDTO updateStyle(Long userId, Long styleId, UserStyleRequ } @Override - public UserStyleFinalResponseDTO submitStyle(Long styleId) { + public UserStyleFinalResponseDTO submitStyle(Long styleId, Long userId) { Style style = styleRepository.findById(styleId).orElseThrow( () -> new GeneralException(ErrorStatus._STYLE_NOT_FOUND)); - + User user = userRepository.findById(userId).orElseThrow( () -> new GeneralException(ErrorStatus._USER_NOT_FOUND)); StringBuilder sb = new StringBuilder(); sb.append("여행 지역 : ").append(style.getCity().name()).append("\n") @@ -77,16 +79,32 @@ public UserStyleFinalResponseDTO submitStyle(Long styleId) { .append("종료 날짜 : ").append(style.getEndDate()).append("\n") .append("선호 활동 : ").append(style.getPreferActivity()).append("\n") .append("추가 요구 사항 : ").append(style.getRequirement()); - - //AttractionRequestDTO attractions = planClient.getAttractions(sb.toString()); +// +// AttractionRequestDTO attractions = planClient.getAttractions(sb.toString()); RestaurantRequestDTO restaurants = planClient.getRestaurants(sb.toString()); // TODO 매핑 테이블에 데이터 저장하기 List restaurantIds = restaurants.restaurant_ids(); - - + for (Long restaurantId : restaurantIds) { + Restaurant restaurant = restaurantRepository.findById(restaurantId).orElseThrow( + () -> new GeneralException(ErrorStatus._RESTAURANT_NOT_FOUND)); + preferRestaurantRepository.save(PreferRestaurant.builder() + .restaurant(restaurant) + .user(user) + .build()); + } + +// List attractionIds = attractions.attraction_ids(); +// for (Long attractionId : attractionIds) { +// Attraction attraction = attractionRepository.findById(attractionId).orElseThrow( +// () -> new GeneralException(ErrorStatus._ATTRACTION_NOT_FOUND)); +// preferAttractionRepository.save(PreferAttraction.builder() +// .attraction(attraction) +// .user(user) +// .build()); +// } return UserStyleFinalResponseDTO.of(restaurants.restaurant_ids(), List.of()); } diff --git a/src/main/java/hyu/erica/capstone/web/controller/StyleController.java b/src/main/java/hyu/erica/capstone/web/controller/StyleController.java index 3189bef..5e0c56d 100644 --- a/src/main/java/hyu/erica/capstone/web/controller/StyleController.java +++ b/src/main/java/hyu/erica/capstone/web/controller/StyleController.java @@ -87,7 +87,7 @@ public ApiResponse getUserPlanStyle(@PathVariable Long styleId) { """) @PostMapping("/{styleId}/final") public ApiResponse saveFinalUserPlanStyle(@PathVariable Long styleId) { - return ApiResponse.onSuccess(SuccessStatus._OK, styleCommandService.submitStyle(styleId)); + return ApiResponse.onSuccess(SuccessStatus._OK, styleCommandService.submitStyle(styleId, SecurityUtils.getCurrentUserId())); } From 6ad23e66c1f88fcc138df40d6ba25223aa528d15 Mon Sep 17 00:00:00 2001 From: msk226 Date: Sun, 16 Mar 2025 18:42:28 +0900 Subject: [PATCH 13/13] =?UTF-8?q?[FEATURE]=20=EB=A7=A4=ED=95=91=20?= =?UTF-8?q?=ED=85=8C=EC=9D=B4=EB=B8=94=20=EA=B4=80=EB=A0=A8=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=A3=BC=EC=84=9D=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../{ => impl}/StyleCommandServiceImpl.java | 24 +++++++++---------- .../{ => impl}/StyleQueryServiceImpl.java | 3 ++- 2 files changed, 14 insertions(+), 13 deletions(-) rename src/main/java/hyu/erica/capstone/service/style/{ => impl}/StyleCommandServiceImpl.java (88%) rename src/main/java/hyu/erica/capstone/service/style/{ => impl}/StyleQueryServiceImpl.java (92%) diff --git a/src/main/java/hyu/erica/capstone/service/style/StyleCommandServiceImpl.java b/src/main/java/hyu/erica/capstone/service/style/impl/StyleCommandServiceImpl.java similarity index 88% rename from src/main/java/hyu/erica/capstone/service/style/StyleCommandServiceImpl.java rename to src/main/java/hyu/erica/capstone/service/style/impl/StyleCommandServiceImpl.java index d7ca4ab..5de0ba6 100644 --- a/src/main/java/hyu/erica/capstone/service/style/StyleCommandServiceImpl.java +++ b/src/main/java/hyu/erica/capstone/service/style/impl/StyleCommandServiceImpl.java @@ -1,4 +1,4 @@ -package hyu.erica.capstone.service.style; +package hyu.erica.capstone.service.style.impl; import hyu.erica.capstone.api.code.status.ErrorStatus; import hyu.erica.capstone.api.exception.GeneralException; @@ -14,7 +14,7 @@ import hyu.erica.capstone.repository.RestaurantRepository; import hyu.erica.capstone.repository.StyleRepository; import hyu.erica.capstone.repository.UserRepository; -import hyu.erica.capstone.web.dto.client.AttractionRequestDTO; +import hyu.erica.capstone.service.style.StyleCommandService; import hyu.erica.capstone.web.dto.client.RestaurantRequestDTO; import hyu.erica.capstone.web.dto.style.request.UserStyleRequestDTO; import hyu.erica.capstone.web.dto.style.response.UserStyleFinalResponseDTO; @@ -85,16 +85,16 @@ public UserStyleFinalResponseDTO submitStyle(Long styleId, Long userId) { // TODO 매핑 테이블에 데이터 저장하기 - List restaurantIds = restaurants.restaurant_ids(); - - for (Long restaurantId : restaurantIds) { - Restaurant restaurant = restaurantRepository.findById(restaurantId).orElseThrow( - () -> new GeneralException(ErrorStatus._RESTAURANT_NOT_FOUND)); - preferRestaurantRepository.save(PreferRestaurant.builder() - .restaurant(restaurant) - .user(user) - .build()); - } +// List restaurantIds = restaurants.restaurant_ids(); +// +// for (Long restaurantId : restaurantIds) { +// Restaurant restaurant = restaurantRepository.findById(restaurantId).orElseThrow( +// () -> new GeneralException(ErrorStatus._RESTAURANT_NOT_FOUND)); +// preferRestaurantRepository.save(PreferRestaurant.builder() +// .restaurant(restaurant) +// .user(user) +// .build()); +// } // List attractionIds = attractions.attraction_ids(); // for (Long attractionId : attractionIds) { diff --git a/src/main/java/hyu/erica/capstone/service/style/StyleQueryServiceImpl.java b/src/main/java/hyu/erica/capstone/service/style/impl/StyleQueryServiceImpl.java similarity index 92% rename from src/main/java/hyu/erica/capstone/service/style/StyleQueryServiceImpl.java rename to src/main/java/hyu/erica/capstone/service/style/impl/StyleQueryServiceImpl.java index 42a1ad3..68a9155 100644 --- a/src/main/java/hyu/erica/capstone/service/style/StyleQueryServiceImpl.java +++ b/src/main/java/hyu/erica/capstone/service/style/impl/StyleQueryServiceImpl.java @@ -1,4 +1,4 @@ -package hyu.erica.capstone.service.style; +package hyu.erica.capstone.service.style.impl; import hyu.erica.capstone.api.code.status.ErrorStatus; import hyu.erica.capstone.api.exception.GeneralException; @@ -6,6 +6,7 @@ import hyu.erica.capstone.domain.User; import hyu.erica.capstone.repository.StyleRepository; import hyu.erica.capstone.repository.UserRepository; +import hyu.erica.capstone.service.style.StyleQueryService; import hyu.erica.capstone.web.dto.style.response.UserStyleResponseDTO; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service;