Skip to content

Commit

Permalink
Merge pull request #43 from Chapter-1/dev
Browse files Browse the repository at this point in the history
Deploy: 개발 1차 완료
  • Loading branch information
singingsandhill authored Nov 19, 2024
2 parents b1462a4 + 8fa283d commit b11cc91
Show file tree
Hide file tree
Showing 51 changed files with 2,529 additions and 209 deletions.
28 changes: 22 additions & 6 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -28,28 +28,44 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-devtools'
implementation 'org.springframework.boot:spring-boot-devtools' //캐시 삭제, 개발 test 용이

compileOnly 'org.projectlombok:lombok'
runtimeOnly 'com.mysql:mysql-connector-j'

annotationProcessor 'org.projectlombok:lombok'

providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'


//JUnit4 추가
testImplementation("org.junit.vintage:junit-vintage-engine") {
exclude group: "org.hamcrest", module: "hamcrest-core"
}


// Spring Boot Starter Security
implementation 'org.springframework.boot:spring-boot-starter-security'

// JWT
implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5'
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5'
implementation 'org.springframework.boot:spring-boot-starter-web'

// JSON 변환
implementation 'com.fasterxml.jackson.core:jackson-databind'
implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.15.2'
implementation 'org.springframework.boot:spring-boot-starter-mail'
implementation 'org.springframework.boot:spring-boot-starter-web'

// JavaMail
implementation 'org.springframework.boot:spring-boot-starter-mail' // JavaMail support in Spring Boot

implementation 'org.json:json:20230618'

// Spring Security 테스트 의존성
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.security:spring-security-test'
// Swagger 의존성 추가
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.3.0'
}

tasks.named('test') {
Expand Down
29 changes: 29 additions & 0 deletions src/main/java/com/chapter1/blueprint/PageSerializer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.chapter1.blueprint;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import org.springframework.data.domain.Page;

import java.io.IOException;

public class PageSerializer extends StdSerializer<Page<?>> {

@SuppressWarnings("unchecked")
public PageSerializer() {
super((Class<Page<?>>) (Class<?>) Page.class);
}

@Override
public void serialize(Page<?> page, JsonGenerator gen, SerializerProvider provider) throws IOException {
gen.writeStartObject();
gen.writeObjectField("content", page.getContent());
gen.writeNumberField("totalPages", page.getTotalPages());
gen.writeNumberField("totalElements", page.getTotalElements());
gen.writeNumberField("number", page.getNumber());
gen.writeNumberField("size", page.getSize());
gen.writeBooleanField("first", page.isFirst());
gen.writeBooleanField("last", page.isLast());
gen.writeEndObject();
}
}
55 changes: 55 additions & 0 deletions src/main/java/com/chapter1/blueprint/SwaggerConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package com.chapter1.blueprint;

import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Contact;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.info.License;
import io.swagger.v3.oas.models.security.SecurityRequirement;
import io.swagger.v3.oas.models.security.SecurityScheme;
import io.swagger.v3.oas.models.servers.Server;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.Arrays;

@Configuration
public class SwaggerConfig {

@Bean
public OpenAPI openAPI() {
Info info = new Info()
.title("Project API Documentation")
.version("v1.0.0")
.description("API 명세서")
.contact(new Contact()
.name("Chapter 1")
.email("[email protected]")
.url("https://github.com/Chapter-1"))
.license(new License()
.name("Apache License Version 2.0")
.url("http://www.apache.org/licenses/LICENSE-2.0"));

// Security 스키마 설정
SecurityScheme bearerAuth = new SecurityScheme()
.type(SecurityScheme.Type.HTTP)
.scheme("bearer")
.bearerFormat("JWT")
.in(SecurityScheme.In.HEADER)
.name("Authorization");

// Security 요청 설정
SecurityRequirement securityRequirement = new SecurityRequirement().addList("bearerAuth");

return new OpenAPI()
.openapi("3.0.1")
.info(info)
.servers(Arrays.asList(
new Server().url("http://localhost:8080").description("Local Server"),
new Server().url("http://localhost:5173/frontend").description("Production Server")
))
.components(new Components()
.addSecuritySchemes("bearerAuth", bearerAuth))
.addSecurityItem(securityRequirement);
}
}
29 changes: 29 additions & 0 deletions src/main/java/com/chapter1/blueprint/WebMvcConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.chapter1.blueprint;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.module.SimpleModule;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.domain.Page;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.List;

@Configuration
@EnableWebMvc
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.getObjectMapper().setSerializationInclusion(JsonInclude.Include.NON_NULL);

// Register the custom Page serializer
SimpleModule module = new SimpleModule();
module.addSerializer((Class<Page<?>>) (Class<?>) Page.class, new PageSerializer());
converter.getObjectMapper().registerModule(module);

converters.add(0, converter);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,29 @@ public enum ErrorCode {
// 서버(Server)
INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "SERVER_001", "서버 내부 에러가 발생했습니다."),

// Real Estamate Error
// 정책(Policy)
POLICY_NOT_FOUND(HttpStatus.NOT_FOUND, "POLICY_001", "해당 정책을 찾을 수 없습니다."),

// 부동산(Real Estate)
REAL_ESTATE_NOT_FOUND(HttpStatus.NOT_FOUND, "ESTATE_001", "부동산 정보를 찾을 수 없습니다."),
INVALID_REGION_PARAMETER(HttpStatus.BAD_REQUEST, "ESTATE_002", "잘못된 지역 정보가 입력되었습니다."),
REAL_ESTATE_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "ESTATE_003", "부동산 정보 조회 중 오류가 발생했습니다."),

// 요청(Request 관련 에러
// 요청(Request 관련 에러)
BAD_REQUEST_ERROR(HttpStatus.BAD_REQUEST, "G001", "잘못된 요청입니다."),

// 이메일(Email)
INVALID_VERIFICATION_CODE(HttpStatus.BAD_REQUEST, "EMAIL_001", "유효하지 않은 인증 코드입니다."),
VERIFICATION_CODE_EXPIRED(HttpStatus.BAD_REQUEST, "EMAIL_002", "인증 코드가 만료되었습니다."),
EMAIL_SENDING_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "EMAIL_003", "이메일 전송에 실패하였습니다.");
EMAIL_SENDING_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "EMAIL_003", "이메일 전송에 실패하였습니다."),
RECOMMENDED_POLICY_EMAIL_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "EMAIL_004", "추천된 정책 이메일 전송에 실패하였습니다."),

// 알림(Notification)
NOTIFICATION_UPDATE_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "NOTIFICATION_001", "알림 상태 업데이트에 실패했습니다."),
POLICY_ALARM_NOT_FOUND(HttpStatus.NOT_FOUND, "NOTIFICATION_002", "해당 알림 설정을 찾을 수 없습니다."),
NOTIFICATION_DELETION_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "NOTIFICATION_003", "알림 삭제에 실패했습니다."),
NOTIFICATION_NOT_FOUND(HttpStatus.NOT_FOUND, "NOTIFICATION_004", "알림을 찾을 수 없습니다.");


private final HttpStatus status;
private final String code;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,42 +1,146 @@
package com.chapter1.blueprint.finance.controller;

import com.chapter1.blueprint.exception.dto.SuccessResponse;
import com.chapter1.blueprint.finance.domain.LoanList;
import com.chapter1.blueprint.finance.domain.SavingsList;
import com.chapter1.blueprint.finance.service.FinanceService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;
import java.util.Map;

@Slf4j
@RequiredArgsConstructor
@RestController
@RequestMapping("/finance")
@Tag(name = "Finance", description = "금융 상품 관리 API")
public class FinanceController {
private final FinanceService financeService;

@Operation(summary = "예금 상품 업데이트", description = "예금 상품 정보를 최신 데이터로 업데이트합니다.")
@ApiResponse(responseCode = "200", description = "업데이트 성공")
@GetMapping(value = "/update/deposit")
public ResponseEntity<?> updateDeposit() {
String result = financeService.updateDeposit();
return ResponseEntity.ok(new SuccessResponse(result));
}

@Operation(summary = "적금 상품 업데이트", description = "적금 상품 정보를 최신 데이터로 업데이트합니다.")
@ApiResponse(responseCode = "200", description = "업데이트 성공")
@GetMapping(value = "/update/saving")
public ResponseEntity<?> updateSaving() {
String result = financeService.updateSaving();
return ResponseEntity.ok(new SuccessResponse(result));
}

@Operation(summary = "주택담보대출 상품 업데이트", description = "주택담보대출 상품 정보를 최신 데이터로 업데이트합니다.")
@ApiResponse(responseCode = "200", description = "업데이트 성공")
@GetMapping(value = "/update/mortgage")
public ResponseEntity<?> updateMortgage() {
String result = financeService.updateMortgageLoan();
return ResponseEntity.ok(new SuccessResponse(result));
}

@Operation(summary = "전세자금대출 상품 업데이트", description = "전세자금대출 상품 정보를 최신 데이터로 업데이트합니다.")
@ApiResponse(responseCode = "200", description = "업데이트 성공")
@GetMapping(value = "/update/rentHouse")
public ResponseEntity<?> updateRentHouse() {
String result = financeService.updateRenthouse();
return ResponseEntity.ok(new SuccessResponse(result));
}

@Operation(summary = "적금 상품 필터 조회", description = "적금 상품 필터 정보를 조회합니다.")
@ApiResponse(responseCode = "200", description = "조회 성공",
content = @Content(schema = @Schema(implementation = SavingsList.class)))
@GetMapping("/filter/savings")
public ResponseEntity<SuccessResponse> getSavingsFilter() {

SavingsList savingsList = financeService.getSavingsFilter();
return ResponseEntity.ok(new SuccessResponse(savingsList));
}

@Operation(summary = "대출 상품 필터 조회", description = "대출 상품 필터 정보를 조회합니다.")
@ApiResponse(responseCode = "200", description = "조회 성공",
content = @Content(schema = @Schema(implementation = LoanList.class)))
@GetMapping("/filter/loan")
public ResponseEntity<SuccessResponse> getLoanFilter() {

LoanList loanList = financeService.getLoanFilter();
return ResponseEntity.ok(new SuccessResponse(loanList));
}

@Operation(summary = "대출 상품 목록 조회", description = "페이지네이션과 필터를 적용하여 대출 상품 목록을 조회합니다.")
@ApiResponse(responseCode = "200", description = "조회 성공")
@GetMapping("/loans")
public ResponseEntity<?> getLoans(
@RequestParam int page,
@RequestParam int size,
@RequestParam(required = false, defaultValue = "") String mrtgTypeNm,
@RequestParam(required = false, defaultValue = "") String lendRateTypeNm,
@RequestParam(required = false, defaultValue = "lendRateMin") String sortBy,
@RequestParam(required = false, defaultValue = "asc") String direction
) {
// Sort 객체 생성
Sort sort = Sort.by(Sort.Direction.fromString(direction), sortBy);
Pageable pageable = PageRequest.of(page, size, sort);

// 서비스 호출
Page<LoanList> result = financeService.getFilteredLoans(pageable,
mrtgTypeNm.isEmpty() ? null : mrtgTypeNm,
lendRateTypeNm.isEmpty() ? null : lendRateTypeNm);

return ResponseEntity.ok(new SuccessResponse(result));
}

@Operation(summary = "저축 상품 목록 조회", description = "페이지네이션과 필터를 적용하여 저축 상품 목록을 조회합니다.")
@ApiResponse(responseCode = "200", description = "조회 성공")
@GetMapping("/savings")
public ResponseEntity<?> getSavings(
@RequestParam int page,
@RequestParam int size,
@RequestParam(required = false, defaultValue = "") String intrRateNm,
@RequestParam(required = false, defaultValue = "") String prdCategory,
@RequestParam(required = false, defaultValue = "intrRate") String sortBy,
@RequestParam(required = false, defaultValue = "asc") String direction
) {
// Sort 객체 생성
Sort sort = Sort.by(Sort.Direction.fromString(direction), sortBy);
Pageable pageable = PageRequest.of(page, size, sort);

// 서비스 호출
Page<SavingsList> result = financeService.getFilteredSavings(pageable,
intrRateNm.isEmpty() ? null : intrRateNm,
prdCategory.isEmpty() ? null : prdCategory);

return ResponseEntity.ok(new SuccessResponse(result));
}

@GetMapping("/getAllLoans")
public ResponseEntity<?> getAllLoans() {
List<LoanList> loanList = financeService.getAllLoans();
return ResponseEntity.ok(new SuccessResponse(loanList));
}

@GetMapping("/getAllSavings")
public ResponseEntity<?> getAllSavings() {
List<SavingsList> savingsList = financeService.getAllSavings();
return ResponseEntity.ok(new SuccessResponse(savingsList));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,7 @@ public class SavingsList {

@Column(name = "prd_category")
private String prdCategory;

@Column(name = "image_url")
private String imageUrl;
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,23 @@
package com.chapter1.blueprint.finance.repository;

import com.chapter1.blueprint.finance.domain.LoanList;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;

import java.util.Map;

@Repository
public interface LoanListRepository extends JpaRepository<LoanList, Long> {

@Query(value = "SELECT * FROM finance.loan_list ORDER BY lend_rate_avg LIMIT 1", nativeQuery = true)
LoanList getLoanFilter();

// @Query("SELECT l FROM LoanList l WHERE (:filter1 IS NULL OR l.mrtg_type_nm = :filter1) AND (:filter2 IS NULL OR l.lend_rate_type_nm = :filter2)")
@Query("SELECT l FROM LoanList l WHERE (:mrtgTypeNm is null or l.mrtgTypeNm = :mrtgTypeNm) AND (:lendRateTypeNm is null or l.lendRateTypeNm = :lendRateTypeNm)")
Page<LoanList> findLoansWithFilters(@Param("mrtgTypeNm") String mrtgTypeNm, @Param("lendRateTypeNm") String lendRateTypeNm, Pageable pageable);

}
Loading

0 comments on commit b11cc91

Please sign in to comment.