Skip to content

Commit 47ad9f9

Browse files
authored
Merge pull request #5 from e-wha/feature/#4
Feature/#4
2 parents fd2509c + 4750b2b commit 47ad9f9

19 files changed

+749
-0
lines changed

build.gradle

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,44 @@ dependencies {
2727
implementation 'org.springframework.boot:spring-boot-starter-data-elasticsearch'
2828
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
2929
implementation 'org.springframework.boot:spring-boot-starter-web'
30+
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
31+
32+
//xlsx 처리
33+
implementation 'org.apache.poi:poi:5.2.3'
34+
implementation 'org.apache.poi:poi-ooxml:5.2.3'
35+
3036
compileOnly 'org.projectlombok:lombok'
3137
developmentOnly 'org.springframework.boot:spring-boot-devtools'
3238
runtimeOnly 'org.postgresql:postgresql'
3339
annotationProcessor 'org.projectlombok:lombok'
3440
testImplementation 'org.springframework.boot:spring-boot-starter-test'
3541
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
42+
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.2' // Swagger
3643
}
3744

3845
tasks.named('test') {
3946
useJUnitPlatform()
4047
}
48+
49+
//QueryDSL
50+
def querydslDir = "$buildDir/generated/querydsl"
51+
52+
sourceSets {
53+
main {
54+
java {
55+
srcDirs += querydslDir
56+
}
57+
}
58+
}
59+
60+
tasks.withType(JavaCompile).configureEach {
61+
options.annotationProcessorGeneratedSourcesDirectory = file(querydslDir)
62+
}
63+
64+
dependencies {
65+
// 이미 있더라도, 확실하게 다음을 포함해줘야 함
66+
annotationProcessor "com.querydsl:querydsl-apt:5.0.0:jakarta"
67+
implementation "com.querydsl:querydsl-jpa:5.0.0:jakarta"
68+
annotationProcessor "jakarta.persistence:jakarta.persistence-api:3.1.0"
69+
annotationProcessor "jakarta.annotation:jakarta.annotation-api:2.1.1"
70+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package com.elasticsearch.elasticsearchengine.common;
2+
3+
import lombok.Getter;
4+
import lombok.RequiredArgsConstructor;
5+
import org.springframework.http.HttpStatus;
6+
import org.springframework.http.HttpStatusCode;
7+
8+
@Getter
9+
@RequiredArgsConstructor
10+
public enum BaseResponseStatus {
11+
12+
/**
13+
* 200: 요청 성공
14+
**/
15+
SUCCESS(HttpStatus.OK, true, 200, "요청에 성공했습니다."),
16+
NONE_DATA(HttpStatus.OK, true, 200, "조회할 게시물이 없습니다."),
17+
18+
/**
19+
* 400 : security 에러
20+
*/
21+
WRONG_PAGE_NUM_MIN(HttpStatus.BAD_REQUEST, false, 400, "잘못된 페이지 번호입니다. (최소 1 이상)"),
22+
WRONG_PARAM(HttpStatus.BAD_REQUEST, false, 400, "잘못된 요청 (필수 값 누락 또는 잘못된 입력)"),
23+
WRONG_PAGE_NUM_MAX(HttpStatus.BAD_REQUEST, false, 400, "잘못된 size 값입니다. (1~100)"),
24+
/**
25+
* 500 : security 에러
26+
*/
27+
WRONG_SERVER(HttpStatus.INTERNAL_SERVER_ERROR, false, 500, "서버 내부 오류가 발생"),
28+
29+
/**
30+
* 900: 기타 에러
31+
*/
32+
INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, false, 900, "Internal server error"),
33+
NO_EXIST_IMAGE(HttpStatus.NOT_FOUND, false, 901, "존재하지 않는 이미지 입니다");
34+
35+
private final HttpStatusCode httpStatusCode;
36+
private final boolean isSuccess;
37+
private final int code;
38+
private final String message;
39+
40+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package com.elasticsearch.elasticsearchengine.common;
2+
3+
import lombok.AllArgsConstructor;
4+
import lombok.Builder;
5+
import lombok.Data;
6+
import lombok.Getter;
7+
8+
@Data
9+
@Builder
10+
public class CommonResponse<T> {
11+
private boolean success;
12+
private String message;
13+
private T data;
14+
private Error error;
15+
public static <T> CommonResponse<T> success(String message) {
16+
return new CommonResponse<>(true, message, null,null);
17+
}
18+
19+
public static <T> CommonResponse<T> success(String message, T data) {
20+
return new CommonResponse<>(true, message, data,null);
21+
}
22+
23+
public static <T> CommonResponse<T> success(T data) {
24+
return new CommonResponse<>(true, null, data,null);
25+
}
26+
27+
public static <T> CommonResponse<T> fail(BaseResponseStatus code, String message) {
28+
return new CommonResponse<>(false, message, null, new Error(code));
29+
}
30+
31+
public static <T> CommonResponse<T> fail(BaseResponseStatus code) {
32+
return new CommonResponse<>(false, code.getMessage(), null, new Error(code));
33+
}
34+
35+
public static <T> CommonResponse<T> fail(BaseResponseStatus code, T data, String message) {
36+
return new CommonResponse<>(false, message, data, new Error(code));
37+
}
38+
39+
@Getter
40+
@AllArgsConstructor
41+
static class Error {
42+
private BaseResponseStatus code;
43+
}
44+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.elasticsearch.elasticsearchengine.config;
2+
3+
import com.querydsl.jpa.impl.JPAQueryFactory;
4+
import jakarta.persistence.EntityManager;
5+
import org.springframework.context.annotation.Bean;
6+
import org.springframework.context.annotation.Configuration;
7+
8+
@Configuration
9+
public class QuerydslConfig {
10+
11+
@Bean
12+
public JPAQueryFactory jpaQueryFactory(EntityManager em) {
13+
return new JPAQueryFactory(em);
14+
}
15+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package com.elasticsearch.elasticsearchengine.config;
2+
3+
import io.swagger.v3.oas.models.Components;
4+
import io.swagger.v3.oas.models.OpenAPI;
5+
import io.swagger.v3.oas.models.info.Info;
6+
import org.springframework.context.annotation.Bean;
7+
import org.springframework.context.annotation.Configuration;
8+
9+
@Configuration
10+
public class SwaggerConfig {
11+
//http://localhost:8080/swagger-ui/index.html
12+
@Bean
13+
public OpenAPI openAPI() {
14+
return new OpenAPI()
15+
.components(new Components())
16+
.info(info());
17+
}
18+
19+
private Info info() {
20+
return new Info()
21+
.title("Postgresql 기반 RestAPI")
22+
.description("개발자들을 위한 문서입니다.")
23+
.version("1.0");
24+
}
25+
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package com.elasticsearch.elasticsearchengine.controller;
2+
3+
import com.elasticsearch.elasticsearchengine.common.CommonResponse;
4+
import com.elasticsearch.elasticsearchengine.dto.ExcelSaveTourListResponseDto;
5+
import com.elasticsearch.elasticsearchengine.dto.TourListResponseDto;
6+
import com.elasticsearch.elasticsearchengine.service.TourService;
7+
import com.elasticsearch.elasticsearchengine.vo.ExcelSaveTourListResponseVo;
8+
import com.elasticsearch.elasticsearchengine.vo.TourListResponseVo;
9+
import io.swagger.v3.oas.annotations.Operation;
10+
import io.swagger.v3.oas.annotations.Parameter;
11+
import io.swagger.v3.oas.annotations.Parameters;
12+
import io.swagger.v3.oas.annotations.tags.Tag;
13+
import lombok.RequiredArgsConstructor;
14+
import org.springframework.data.domain.Page;
15+
import org.springframework.web.bind.annotation.*;
16+
import org.springframework.web.multipart.MultipartFile;
17+
18+
import java.io.IOException;
19+
import java.util.List;
20+
21+
import static com.elasticsearch.elasticsearchengine.common.BaseResponseStatus.INTERNAL_SERVER_ERROR;
22+
23+
@RequiredArgsConstructor
24+
@RestController
25+
@RequestMapping("tour")
26+
@Tag(name = "tour_controller", description = "관광정보관리시스템")
27+
public class TourController {
28+
29+
private final TourService tourService;
30+
31+
@PostMapping(value = "/save", consumes = "multipart/form-data")
32+
@Operation(summary = "관광 정보 등록 (엑셀 삽입)", description = "엑셀에 등록된 관광 정보를 등록합니다.")
33+
public CommonResponse<ExcelSaveTourListResponseVo> excelSaveTourList(
34+
@RequestPart("file")
35+
@io.swagger.v3.oas.annotations.Parameter(
36+
description = "관광 정보가 삽입된 엑셀 파일",
37+
required = true
38+
) MultipartFile file
39+
) {
40+
try {
41+
ExcelSaveTourListResponseDto responseDto = tourService.excelSaveTourList(file);
42+
ExcelSaveTourListResponseVo responseVo = ExcelSaveTourListResponseVo.dtoToVo(responseDto);
43+
return CommonResponse.success("관광정보가 성공적으로 등록되었습니다.", responseVo);
44+
} catch (IOException e) {
45+
return CommonResponse.fail(INTERNAL_SERVER_ERROR, "파일 처리 중 오류가 발생했습니다.");
46+
}
47+
}
48+
49+
50+
@GetMapping(value = "/find")
51+
@Operation(summary = "관광 정보 조회", description = "페이지 번호, 관광 타입 번호, 시군구코드, 서브 카테고리, 태그를 기준으로 관광 정보를 조회합니다.")
52+
@Parameters({
53+
@Parameter(name = "page", description = "페이지 번호, 기본 값 : 1"),
54+
@Parameter(name = "contentTypeId", description = "관광 타입 번호"),
55+
@Parameter(name = "sigunguCode", description = "시군구코드"),
56+
@Parameter(name = "sideCategory", description = "서브 카테고리"),
57+
@Parameter(name = "tags", description = "태그"),
58+
})
59+
public CommonResponse<TourListResponseVo> findTourList(
60+
@RequestParam(name = "page", defaultValue = "0") int page,
61+
@RequestParam(name = "contentTypeId") String contentTypeId,
62+
@RequestParam(name = "sigunguCode", required = false) String sigunguCode,
63+
@RequestParam(name = "sideCategory", required = false) String sideCategory,
64+
@RequestParam(name = "tags", required = false) List<String> tags
65+
) {
66+
Page<TourListResponseDto> TourListPage = tourService.findByMultiCode(page, contentTypeId, sigunguCode, sideCategory, tags);
67+
68+
if (TourListPage.isEmpty()) {
69+
return CommonResponse.success("관광 정보", TourListResponseVo.builder()
70+
.totalList(0)
71+
.currentPage(page)
72+
.totalPages(0)
73+
.tourListDto(null)
74+
.build());
75+
}
76+
77+
return CommonResponse.success("관광 정보",
78+
TourListResponseVo.dtoToVo(TourListPage.getContent().get(0)));
79+
}
80+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package com.elasticsearch.elasticsearchengine.domain;
2+
3+
import jakarta.persistence.*;
4+
import lombok.*;
5+
import java.time.LocalDate;
6+
7+
@Builder
8+
@Data
9+
@AllArgsConstructor
10+
@NoArgsConstructor
11+
@Entity(name = "tour")
12+
public class Tour {
13+
14+
@Id
15+
@GeneratedValue(strategy = GenerationType.IDENTITY)
16+
private int TourId;
17+
18+
@Column(nullable = false, unique = true)
19+
private int contentId;
20+
21+
private String contentTypeId;
22+
23+
private String title;
24+
25+
private String addr;
26+
27+
private String zipCode;
28+
29+
private String areaCode;
30+
31+
private String sigunguCode;
32+
33+
private String category;
34+
35+
private String sideCategory;
36+
37+
private String tags;
38+
39+
private String thumbnail;
40+
41+
private double mapx;
42+
43+
private double mapy;
44+
45+
private LocalDate createdTime;
46+
47+
private LocalDate modifiedTime;
48+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.elasticsearch.elasticsearchengine.dto;
2+
3+
import lombok.Builder;
4+
import lombok.Data;
5+
6+
import java.util.List;
7+
8+
@Builder
9+
@Data
10+
public class ExcelSaveTourListResponseDto {
11+
12+
private int successCount;
13+
private int failedCount;
14+
private List<String> errors;
15+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package com.elasticsearch.elasticsearchengine.dto;
2+
3+
import lombok.AllArgsConstructor;
4+
import lombok.Builder;
5+
import lombok.Data;
6+
import lombok.NoArgsConstructor;
7+
8+
import java.util.List;
9+
10+
@Data
11+
@Builder
12+
@AllArgsConstructor
13+
@NoArgsConstructor
14+
public class TourListDto {
15+
private int contentId;
16+
private String title;
17+
private String thumbnail;
18+
private String tags;
19+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.elasticsearch.elasticsearchengine.dto;
2+
3+
import lombok.Builder;
4+
import lombok.Data;
5+
6+
import java.util.List;
7+
8+
@Data
9+
@Builder
10+
public class TourListResponseDto {
11+
private int totalList;
12+
private int currentPage;
13+
private int totalPages;
14+
private List<TourListDto> tourListDto;
15+
}

0 commit comments

Comments
 (0)