diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..d908d94 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,26 @@ +name: Deploy to EC2 + +on: + push: + branches: + - main + +jobs: + deploy: + runs-on: ubuntu-latest + + steps: + - name: Check out repository + uses: actions/checkout@v3 + + - name: Set up SSH key + uses: webfactory/ssh-agent@v0.7.0 + with: + ssh-private-key: ${{ secrets.FINUT_SSH_KEY }} + + - name: Deploy to EC2 + env: + EC2_HOST: ${{ secrets.FINUT_EC2_HOST }} + EC2_USER: ${{ secrets.FINUT_EC2_USER }} + run: | + ssh -o StrictHostKeyChecking=no $EC2_USER@$EC2_HOST 'bash /home/ec2-user/app/deploy.sh' diff --git a/build.gradle b/build.gradle index 258d16a..8d2d6df 100644 --- a/build.gradle +++ b/build.gradle @@ -4,6 +4,15 @@ plugins { id 'io.spring.dependency-management' version '1.1.5' } +jar { + manifest { + attributes( + 'Main-Class': 'com.finut.finut_server.FinutServerApplication' + ) + } +} + + group = 'com.finut' version = '0.0.1-SNAPSHOT' @@ -28,7 +37,8 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-security' implementation 'org.springframework.boot:spring-boot-starter-oauth2-client' implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' - implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.2' + implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.2' // 스웨거 + implementation 'org.jsoup:jsoup:1.15.3' // 크롤링 implementation 'org.springframework.boot:spring-boot-starter-data-jpa' compileOnly 'org.projectlombok:lombok' @@ -36,8 +46,25 @@ dependencies { testImplementation 'org.springframework.boot:spring-boot-starter-test' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' + // Google Auth Library for OAuth 2.0 + implementation 'com.google.auth:google-auth-library-oauth2-http:1.8.0' + + // Google API Client Library for OAuth2 + implementation 'com.google.apis:google-api-services-oauth2:v2-rev151-1.25.0' + + // JSON Parsing (Gson) + implementation 'com.google.http-client:google-http-client-gson:1.41.6' + // mysql 설정 runtimeOnly 'com.mysql:mysql-connector-j' + implementation 'org.mariadb.jdbc:mariadb-java-client' + + //RSS 툴 + implementation 'com.rometools:rome:1.15.0' + + //MariaDB + implementation("org.mariadb.jdbc:mariadb-java-client") + } tasks.named('test') { diff --git a/src/main/java/com/finut/finut_server/apiPayload/ApiResponse.java b/src/main/java/com/finut/finut_server/apiPayload/ApiResponse.java index 02c95aa..d9f3522 100644 --- a/src/main/java/com/finut/finut_server/apiPayload/ApiResponse.java +++ b/src/main/java/com/finut/finut_server/apiPayload/ApiResponse.java @@ -33,4 +33,12 @@ public static ApiResponse of(BaseCode code, T result){ public static ApiResponse onFailure(String code, String message, T data){ return new ApiResponse<>(true, code, message, data); } + + public String getData() { + return message; + } + + public String getStatusCode() { + return code; + } } diff --git a/src/main/java/com/finut/finut_server/apiPayload/code/status/ErrorStatus.java b/src/main/java/com/finut/finut_server/apiPayload/code/status/ErrorStatus.java index f2fb0d1..0c087f5 100644 --- a/src/main/java/com/finut/finut_server/apiPayload/code/status/ErrorStatus.java +++ b/src/main/java/com/finut/finut_server/apiPayload/code/status/ErrorStatus.java @@ -24,7 +24,11 @@ public enum ErrorStatus implements BaseErrorCode { ARTICLE_NOT_FOUND(HttpStatus.NOT_FOUND, "ARTICLE4001", "게시글이 없습니다."), // 퀴즈 관련 에러 - INVALID_NUMBER(HttpStatus.BAD_REQUEST, "QUIZ400", "유효하지 않은 값입니다."); + INVALID_NUMBER(HttpStatus.BAD_REQUEST, "QUIZ400", "유효하지 않은 값입니다."), + NULL_DATA(HttpStatus.BAD_REQUEST, "QUIZ401", "데이터가 없습니다."), + + // 출석 관련 에러 + NO_DATA_FOUND(HttpStatus.BAD_REQUEST, "ATTEND400", "데이터가 없습니다"); private final HttpStatus httpStatus; private final String code; diff --git a/src/main/java/com/finut/finut_server/config/SecurityConfig.java b/src/main/java/com/finut/finut_server/config/SecurityConfig.java index cf00e8d..ccc1621 100644 --- a/src/main/java/com/finut/finut_server/config/SecurityConfig.java +++ b/src/main/java/com/finut/finut_server/config/SecurityConfig.java @@ -4,10 +4,12 @@ import com.finut.finut_server.apiPayload.exception.handler.CustomOAuth2AuthenticationSuccessHandler; import com.finut.finut_server.config.auth.CustomOAuth2UserService; import com.finut.finut_server.domain.user.UsersRepository; +import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpSession; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; @@ -44,7 +46,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http, CustomOAuth2Us logout .logoutSuccessUrl("/") // 임시 ) - .csrf(csrf -> csrf.ignoringRequestMatchers("/h2-console/**")) + .csrf(AbstractHttpConfigurer::disable) // post 요청을 위한 csrf disable .headers(headers -> headers.frameOptions(frameOptions -> frameOptions.sameOrigin())); return http.build(); } diff --git a/src/main/java/com/finut/finut_server/controller/ConsumptionController.java b/src/main/java/com/finut/finut_server/controller/ConsumptionController.java index 279bf1a..d883097 100644 --- a/src/main/java/com/finut/finut_server/controller/ConsumptionController.java +++ b/src/main/java/com/finut/finut_server/controller/ConsumptionController.java @@ -2,12 +2,21 @@ import com.finut.finut_server.apiPayload.ApiResponse; +import com.finut.finut_server.apiPayload.code.ErrorReasonDTO; import com.finut.finut_server.domain.product.Product; import com.finut.finut_server.domain.purchases.Purchases; +import com.finut.finut_server.domain.user.Users; import com.finut.finut_server.service.ConsumptionService; import com.finut.finut_server.service.PurchasesService; +import com.finut.finut_server.service.UsersService; import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; @@ -22,32 +31,72 @@ public class ConsumptionController { private final ConsumptionService consumptionService; private final PurchasesService purchasesService; + private UsersService usersService; + @Autowired public ConsumptionController(ConsumptionService consumptionService, PurchasesService purchasesService) { this.consumptionService = consumptionService; this.purchasesService = purchasesService; } @Operation(summary = "전체 물품 보기", description = "전체 물품을 보기 위한 api 입니다") + @ApiResponses(value = { + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "성공", content = @Content(mediaType = "application/json", + array = @ArraySchema(schema = @Schema(implementation = Product.class)))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "", + content = @Content), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "500", description = "서버 에러, 관리자에게 문의 바랍니다.", + content = @Content(mediaType = "application/json", + schema = @Schema(implementation = ErrorReasonDTO.class))) + }) @GetMapping("/products") public ApiResponse> readAllProducts() { return ApiResponse.onSuccess(ConsumptionService.getAllProducts()); } + @ApiResponses(value = { + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "성공", content = @Content(mediaType = "application/json", + array = @ArraySchema(schema = @Schema(implementation = Product.class)))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "", + content = @Content), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "500", description = "서버 에러, 관리자에게 문의 바랍니다.", + content = @Content(mediaType = "application/json", + schema = @Schema(implementation = ErrorReasonDTO.class))) + }) @Operation(summary = "물품 상세 정보 보기", description = "각 물품의 상세 정보를 보기 위한 api 입니다") @GetMapping("/products/{productId}") public ApiResponse> readProduct(@PathVariable Long productId) { return ApiResponse.onSuccess(ConsumptionService.getProductInfo(productId)); } + @ApiResponses(value = { + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "성공", content = @Content(mediaType = "application/json", + schema = @Schema(implementation = Purchases.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "", + content = @Content), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "500", description = "서버 에러, 관리자에게 문의 바랍니다.", + content = @Content(mediaType = "application/json", + schema = @Schema(implementation = ErrorReasonDTO.class))) + }) @Operation(summary = "물품 구매", description = "물품을 구매하기 위한 api 입니다") @GetMapping("/buy") - public ApiResponse buyProduct(@RequestParam Long userId, @RequestParam Long productId) { - return ApiResponse.onSuccess(PurchasesService.buyProduct(userId, productId)); + public ApiResponse buyProduct(@RequestParam Long productId, HttpServletRequest request, HttpServletResponse response) { + Users user = usersService.getUserIdByToken(request, response); + return ApiResponse.onSuccess(PurchasesService.buyProduct(user.getId(), productId)); } + @ApiResponses(value = { + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "성공", content = @Content(mediaType = "application/json", + array = @ArraySchema(schema = @Schema(implementation = Product.class)))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "", + content = @Content), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "500", description = "서버 에러, 관리자에게 문의 바랍니다.", + content = @Content(mediaType = "application/json", + schema = @Schema(implementation = ErrorReasonDTO.class))) + }) @Operation(summary = "내 물품 보기", description = "구매한 물품들을 모두 위한 api 입니다") @GetMapping("/my-purchases") - public ApiResponse> readMyPurchases(@RequestParam Long userId) { - return ApiResponse.onSuccess(PurchasesService.getMyProducts(userId)); + public ApiResponse> readMyPurchases(HttpServletRequest request, HttpServletResponse response) { + Users user = usersService.getUserIdByToken(request, response); + return ApiResponse.onSuccess(PurchasesService.getMyProducts(user.getId())); } } diff --git a/src/main/java/com/finut/finut_server/controller/HomeController.java b/src/main/java/com/finut/finut_server/controller/HomeController.java index 8b8daf2..de96968 100644 --- a/src/main/java/com/finut/finut_server/controller/HomeController.java +++ b/src/main/java/com/finut/finut_server/controller/HomeController.java @@ -1,10 +1,14 @@ package com.finut.finut_server.controller; import com.finut.finut_server.apiPayload.ApiResponse; +import com.finut.finut_server.apiPayload.code.ErrorReasonDTO; import com.finut.finut_server.config.auth.dto.SessionUser; import com.finut.finut_server.domain.user.UserResponseDTO; import com.finut.finut_server.service.UsersService; 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.ApiResponses; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.servlet.http.HttpSession; import org.springframework.beans.factory.annotation.Autowired; @@ -34,6 +38,15 @@ public HomeController(HttpSession httpSession) { @Autowired private UsersService userService; + @ApiResponses(value = { + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "성공", content = @Content(mediaType = "application/json", + schema = @Schema(implementation = UserResponseDTO.loginUserDTO.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "", + content = @Content), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "500", description = "서버 에러, 관리자에게 문의 바랍니다.", + content = @Content(mediaType = "application/json", + schema = @Schema(implementation = ErrorReasonDTO.class))) + }) @Operation(summary = "로그인 성공", description = "로그인 성공시 해당 api에 리다이렉션 되어 수행됩니다") @GetMapping("/success") @ResponseBody diff --git a/src/main/java/com/finut/finut_server/controller/InterestRateController.java b/src/main/java/com/finut/finut_server/controller/InterestRateController.java deleted file mode 100644 index c345362..0000000 --- a/src/main/java/com/finut/finut_server/controller/InterestRateController.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.finut.finut_server.controller; - -import com.fasterxml.jackson.databind.node.ArrayNode; -import com.fasterxml.jackson.databind.node.ObjectNode; -import com.finut.finut_server.apiPayload.ApiResponse; -import com.finut.finut_server.domain.quiz.QuizResponseDTO; -import com.finut.finut_server.service.InterestRateService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -import lombok.Getter; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.*; - -@Tag(name = "InterestRate Controller", description = "금리 데이터 api") -@RequestMapping("/interest-rate") -@RestController -public class InterestRateController { - @Autowired - private InterestRateService interestRateService; - - @Operation(summary = "오늘의 금리 정보", description = "오늘의 금리 정보를 가져오기 위한 api 입니다.") - @GetMapping("/today") - public ApiResponse getInterestRatesToday() { - return ApiResponse.onSuccess(interestRateService.getInterestRatesToady()); - } - - @Operation(summary = "3개년 금리 정보", description = "3년의 금리 정보를 가져오기 위한 api 입니다") - @GetMapping("/3") - public ApiResponse getInterestRates3Y() { - return ApiResponse.onSuccess(interestRateService.getInterestRates3Y()); - } - - @Operation(summary = "5개년 금리 정보", description = "5년의 금리 정보를 가져오기 위한 api 입니다") - @GetMapping("/5") - public ApiResponse getInterestRates5Y() { - return ApiResponse.onSuccess(interestRateService.getInterestRates5Y()); - } -} diff --git a/src/main/java/com/finut/finut_server/controller/QuestController.java b/src/main/java/com/finut/finut_server/controller/QuestController.java new file mode 100644 index 0000000..ec86452 --- /dev/null +++ b/src/main/java/com/finut/finut_server/controller/QuestController.java @@ -0,0 +1,101 @@ +package com.finut.finut_server.controller; + +import com.finut.finut_server.apiPayload.ApiResponse; +import com.finut.finut_server.apiPayload.code.ErrorReasonDTO; +import com.finut.finut_server.domain.quest.Quest; +import com.finut.finut_server.domain.quest.QuestDTO; +import com.finut.finut_server.domain.questDone.QuestDone; +import com.finut.finut_server.domain.questQuiz.QuestQuiz; +import com.finut.finut_server.domain.questQuiz.QuestQuizRepository; +import com.finut.finut_server.domain.quiz.Quiz; +import com.finut.finut_server.domain.user.Users; +import com.finut.finut_server.domain.user.UsersRepository; +import com.finut.finut_server.service.LevelService; +import com.finut.finut_server.service.QuestQuizService; +import com.finut.finut_server.service.QuestService; +import com.finut.finut_server.service.UsersService; +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.ApiResponses; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Optional; + +@RestController +@RequestMapping("/quest") +@Tag(name = "Quest Controller", description = "퀘스트 관련 api") +public class QuestController { + @Autowired + private UsersService usersService; + + @Autowired + private QuestQuizService questQuizService; + + @Autowired + private QuestService questService; + + @Autowired + private LevelService levelService; + + @Operation(summary = "전체 퀘스트 가져오기", description = "전체 퀘스트를 가져옵니다") + @ApiResponses(value = { + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "성공", content = @Content(mediaType = "application/json", + schema = @Schema(implementation = Quest.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "퀘스트 내용을 제대로 가지고 오지 못했습니다.", + content = @Content), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "500", description = "서버 에러, 관리자에게 문의 바랍니다.", + content = @Content(mediaType = "application/json", + schema = @Schema(implementation = ErrorReasonDTO.class))) + }) + @GetMapping("") + public ApiResponse> getQuests(){ + List quests = questService.getAllQuests(); + return ApiResponse.onSuccess(quests); + } + + + @Operation(summary = "해당 퀘스트 퀴즈들 가져오기", description = "해당 퀘스트의 퀴즈을 모두 가져옵니다.") + @ApiResponses(value = { + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "성공", content = @Content(mediaType = "application/json", + schema = @Schema(implementation = QuestQuiz.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "퀘스트 퀴즈 내용을 제대로 가지고오지 못했습니다.", + content = @Content), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "500", description = "서버 에러, 관리자에게 문의 바랍니다.", + content = @Content(mediaType = "application/json", + schema = @Schema(implementation = ErrorReasonDTO.class))) + }) + @GetMapping("/{questId}") + public ApiResponse> getQuestQuizes(@PathVariable Long questId){ + List questQuizzes = questQuizService.getQuestQuizzes(questId); + return ApiResponse.onSuccess(questQuizzes); + } + + + + @Operation(summary = "퀘스트 성공", description = "퀘스트 성공시 Level을 다음 레벨으로 바꿉니다.") + @ApiResponses(value = { + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "성공", content = @Content(mediaType = "application/json", + schema = @Schema(implementation = QuestDone.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "", + content = @Content), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "500", description = "서버 에러, 관리자에게 문의 바랍니다.", + content = @Content(mediaType = "application/json", + schema = @Schema(implementation = ErrorReasonDTO.class))) + }) + @GetMapping("/done-quest/{questId}") + public ApiResponse updateQuestDone(@PathVariable Long questId, HttpServletRequest request, HttpServletResponse response) { + Users user = usersService.getUserIdByToken(request, response); + questService.updateQuestDone(user, questId); + user = levelService.upgradeUserLevel(user.getId()); + return ApiResponse.onSuccess(user); + } + + +} diff --git a/src/main/java/com/finut/finut_server/controller/QuizController.java b/src/main/java/com/finut/finut_server/controller/QuizController.java index 67e9052..8f1a04c 100644 --- a/src/main/java/com/finut/finut_server/controller/QuizController.java +++ b/src/main/java/com/finut/finut_server/controller/QuizController.java @@ -2,70 +2,133 @@ import com.finut.finut_server.apiPayload.ApiResponse; import com.finut.finut_server.apiPayload.code.ErrorReasonDTO; -import com.finut.finut_server.converter.QuizConverter; import com.finut.finut_server.domain.quiz.Quiz; -import com.finut.finut_server.domain.quiz.QuizRequestDTO; import com.finut.finut_server.domain.quiz.QuizResponseDTO; +import com.finut.finut_server.domain.user.UserResponseDTO; +import com.finut.finut_server.domain.user.Users; +import com.finut.finut_server.service.GoogleAuthService; +import com.finut.finut_server.service.QuizDoneService; import com.finut.finut_server.service.QuizService; +import com.finut.finut_server.service.UsersService; +import com.google.api.services.oauth2.model.Userinfoplus; 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.ApiResponses; import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.validation.Valid; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*; -@Controller -@RequestMapping("/api/quiz") +import java.io.IOException; +import java.util.Optional; + +@RestController +@RequestMapping("/quiz") @Tag(name = "Quiz Controller", description = "퀴즈 관련 api") public class QuizController { @Autowired private QuizService quizService; - @Operation(summary = "퀴즈 내용 불러오기", description = "퀴즈를 보여줍니다.") + @Autowired + private final GoogleAuthService googleAuthService; + + @Autowired + private final UsersService usersService; + + @Autowired + private final QuizDoneService quizDoneService; + private QuizResponseDTO quizResponseDTO; + + @Autowired + public QuizController(GoogleAuthService googleAuthService, UsersService usersService, QuizDoneService quizDoneService) { + this.googleAuthService = googleAuthService; + this.usersService = usersService; + this.quizDoneService = quizDoneService; + } + + @Operation(summary = "랜덤으로 퀴즈 내용 불러오기", description = "유저가 풀지 않았던 문제 중에서 퀴즈를 랜덤으로 하나 가져옵니다.") @ApiResponses(value = { - @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "성공"), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "성공", content = @Content(mediaType = "application/json", + schema = @Schema(implementation = Quiz.class))), @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "퀴즈 내용을 제대로 가지고 오지 못했습니다.", content = @Content), @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "500", description = "서버 에러, 관리자에게 문의 바랍니다.", content = @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorReasonDTO.class))) }) - @GetMapping("/") - public ApiResponse getQuiz(@PathVariable(name="userId") Long userId){ - Quiz quiz = quizService.getQuiz(); - return ApiResponse.onSuccess(QuizConverter.toGetQuizDto(userId, quiz)); + @GetMapping("") + public ApiResponse> getQuiz(HttpServletRequest request, HttpServletResponse response){ + Users user = usersService.getUserIdByToken(request, response); + Optional quiz = quizService.getQuiz(user.getId()); + if(quiz.isPresent()) + return ApiResponse.onSuccess(quiz); + return ApiResponse.onFailure("400", "퀴즈 내용을 제대로 가져오지 못했습니다", quiz); } - @Operation(summary = "퀴즈 데이터 저장", description = "생성된 퀴즈 데이터를 DB에 저장합니다.") + + + @Operation(summary = "퀴즈를 맞췄을 때", description = "퀴즈를 맞췄을 때 QuizDone DB에 해당 내용을 저장하고, 난이도 상승에 필요한 퀴즈 개수와 레벨업에 필요한 XP를 증가시킵니다.") @ApiResponses(value = { - @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "성공"), - @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "올바른 날짜나 시간이 아닙니다.", + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "성공", content = @Content(mediaType = "application/json", + schema = @Schema(implementation = String.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "퀴즈 내용을 제대로 가지고 오지 못했습니다.", content = @Content), @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "500", description = "서버 에러, 관리자에게 문의 바랍니다.", content = @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorReasonDTO.class))) }) - @PostMapping("/") - public ApiResponse saveQuiz(@RequestBody @Valid QuizRequestDTO.saveQuiz request){ - Quiz quiz = quizService.saveQuiz(request); - return ApiResponse.onSuccess(QuizConverter.toSaveQuizDto(quiz)); + // 퀴즈 맞췄을 때 api/ db 생성(isCorrect = true), diffQuizCnt++, xp + 25 + @GetMapping("/correct/{quizId}") + public ApiResponse quizCorrect(@PathVariable Long quizId, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { + Users user = usersService.getUserIdByToken(request, response); + + UserResponseDTO.checkUserXP checkUserXP = null; + + if (user == null) { + return ApiResponse.onFailure("401", "User not authenticated", checkUserXP); + } + + Optional quiz = quizService.getQuizByQuizId(quizId); + + if (quiz.isPresent()) { + quizDoneService.saveQuizDone(user, quiz.get(), true); //db 생성(isCorrect = true) + checkUserXP = usersService.updateDiffLevelCnt(user.getId()); //diffQuizCnt++, xp + 25 + // 성공 응답 반환 + return ApiResponse.onSuccess(checkUserXP); + } + else { + return ApiResponse.onFailure("500", "No Quiz", checkUserXP); + } } - @Operation(summary = "퀴즈 정답 판독 후 돈 적립", description = "사용자가 입력한 답이 정답인지 판독한 후, 정답 여부에 따라 돈을 적립합니다.") + + // 퀴즈 틀렸을 때 api/ db 생성(isCorrect = false) + @Operation(summary = "퀴즈를 틀렸을 때", description = "퀴즈를 틀렸을 때, 틀렸다는 정보를 저장합니다.") @ApiResponses(value = { - @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "성공"), - @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "정답 여부를 전달 받지 못했습니다.", + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "성공", content = @Content(mediaType = "application/json", + schema = @Schema(implementation = String.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "퀴즈 내용을 제대로 가지고 오지 못했습니다.", content = @Content), @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "500", description = "서버 에러, 관리자에게 문의 바랍니다.", content = @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorReasonDTO.class))) }) - @PatchMapping("/money") - public ApiResponse updateMoney(@RequestBody @Valid QuizRequestDTO.updateMoney request){ - int moneyAmount = quizService.updateMoney(request); - return ApiResponse.onSuccess(QuizConverter.toUpdateMoneyDto(request.getUserId(), moneyAmount)); + @GetMapping("/wrong/{quizId}") + public ApiResponse quizWrong(@PathVariable Long quizId, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { + Users user = usersService.getUserIdByToken(request, response); + Optional quiz = quizService.getQuizByQuizId(quizId); + + if (quiz.isPresent()) { + quizDoneService.saveQuizDone(user, quiz.get(), false); //db 생성(isCorrect = false) + // 성공 응답 반환 + return ApiResponse.onSuccess("success"); + } else { + return ApiResponse.onFailure("500", "No Quiz", "data"); + } } -} + +} \ No newline at end of file diff --git a/src/main/java/com/finut/finut_server/controller/TodayNewsController.java b/src/main/java/com/finut/finut_server/controller/TodayNewsController.java new file mode 100644 index 0000000..d3131e4 --- /dev/null +++ b/src/main/java/com/finut/finut_server/controller/TodayNewsController.java @@ -0,0 +1,153 @@ +package com.finut.finut_server.controller; + +import com.finut.finut_server.apiPayload.ApiResponse; +import com.finut.finut_server.apiPayload.code.ErrorReasonDTO; +import com.finut.finut_server.domain.news.NewsItemDTO; +import com.finut.finut_server.service.TodayNewsService; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@RestController +@RequestMapping("/news") +public class TodayNewsController { + + @Autowired + private TodayNewsService todayNewsService; + + @Operation(summary = "오늘의 뉴스 - 경제", description = "오늘의 뉴스 중 경제 부분의 뉴스들을 보여줍니다") + @ApiResponses(value = { + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "성공", content = @Content(mediaType = "application/json", + array = @ArraySchema(schema = @Schema(implementation = NewsItemDTO.class)))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "뉴스 내용을 제대로 가지고 오지 못했습니다.", + content = @Content), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "500", description = "서버 에러, 관리자에게 문의 바랍니다.", + content = @Content(mediaType = "application/json", + schema = @Schema(implementation = ErrorReasonDTO.class))) + }) + @GetMapping("/economy") + public ApiResponse> newsEconomy() { + try { + return ApiResponse.onSuccess(todayNewsService.getNews(1)); // economy + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException("Fail to fetch RSS Economy", e); + } + } + + @Operation(summary = "오늘의 뉴스 - 부동산", description = "오늘의 뉴스 중 부동산 부분의 뉴스들을 보여줍니다") + @ApiResponses(value = { + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "성공", content = @Content(mediaType = "application/json", + array = @ArraySchema(schema = @Schema(implementation = NewsItemDTO.class)))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "뉴스 내용을 제대로 가지고 오지 못했습니다.", + content = @Content), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "500", description = "서버 에러, 관리자에게 문의 바랍니다.", + content = @Content(mediaType = "application/json", + schema = @Schema(implementation = ErrorReasonDTO.class))) + }) + @GetMapping("/realestate") + public ApiResponse> newsRealEstate() { + try { + return ApiResponse.onSuccess(todayNewsService.getNews(2)); // real estate + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException("Fail to fetch RSS Economy", e); + } + } + + @Operation(summary = "오늘의 뉴스 - 증권", description = "오늘의 뉴스 중 증권 부분의 뉴스들을 보여줍니다") + @ApiResponses(value = { + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "성공", content = @Content(mediaType = "application/json", + array = @ArraySchema(schema = @Schema(implementation = NewsItemDTO.class)))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "뉴스 내용을 제대로 가지고 오지 못했습니다.", + content = @Content), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "500", description = "서버 에러, 관리자에게 문의 바랍니다.", + content = @Content(mediaType = "application/json", + schema = @Schema(implementation = ErrorReasonDTO.class))) + }) + @GetMapping("/stock") + public ApiResponse> newsStock() { + try { + return ApiResponse.onSuccess(todayNewsService.getNews(3)); // stock + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException("Fail to fetch RSS Economy", e); + } + } + + @Operation(summary = "뉴스 본문 - 증권", description = "{number} 증권 뉴스의 본문을 보여줍니다") + @ApiResponses(value = { + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "성공", content = @Content(mediaType = "application/json", + schema = @Schema(implementation = Map.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "뉴스 내용을 제대로 가지고 오지 못했습니다.", + content = @Content), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "500", description = "서버 에러, 관리자에게 문의 바랍니다.", + content = @Content(mediaType = "application/json", + schema = @Schema(implementation = ErrorReasonDTO.class))) + }) + @GetMapping("/stock/{number}") + public ApiResponse> getStockContent(@PathVariable Long number) { + String url = "https://m.mk.co.kr/news/stock/" + number; + String content = TodayNewsService.getMainContent(url); + + Map response = new HashMap<>(); + response.put("content", content); + + return ApiResponse.onSuccess(response); + } + + @Operation(summary = "뉴스 본문 - 경제", description = "{number} 경제 뉴스의 본문을 보여줍니다") + @ApiResponses(value = { + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "성공", content = @Content(mediaType = "application/json", + schema = @Schema(implementation = Map.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "뉴스 내용을 제대로 가지고 오지 못했습니다.", + content = @Content), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "500", description = "서버 에러, 관리자에게 문의 바랍니다.", + content = @Content(mediaType = "application/json", + schema = @Schema(implementation = ErrorReasonDTO.class))) + }) + @GetMapping("/economy/{number}") + public ApiResponse> getEconomyContent(@PathVariable Long number) { + String url = "https://m.mk.co.kr/news/economy/" + number; + String content = TodayNewsService.getMainContent(url); + + Map response = new HashMap<>(); + response.put("content", content); + + return ApiResponse.onSuccess(response); + } + + @Operation(summary = "뉴스 본문 - 부동산", description = "{number} 부동산 뉴스의 본문을 보여줍니다") + @ApiResponses(value = { + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "성공", content = @Content(mediaType = "application/json", + schema = @Schema(implementation = Map.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "뉴스 내용을 제대로 가지고 오지 못했습니다.", + content = @Content), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "500", description = "서버 에러, 관리자에게 문의 바랍니다.", + content = @Content(mediaType = "application/json", + schema = @Schema(implementation = ErrorReasonDTO.class))) + }) + @GetMapping("/realestate/{number}") + public ApiResponse> getContent(@PathVariable Long number) { + String url = "https://m.mk.co.kr/news/realestate/" + number; + String content = TodayNewsService.getMainContent(url); + + Map response = new HashMap<>(); + response.put("content", content); + + return ApiResponse.onSuccess(response); + } + +} \ No newline at end of file diff --git a/src/main/java/com/finut/finut_server/controller/UserController.java b/src/main/java/com/finut/finut_server/controller/UserController.java new file mode 100644 index 0000000..15d612a --- /dev/null +++ b/src/main/java/com/finut/finut_server/controller/UserController.java @@ -0,0 +1,58 @@ +package com.finut.finut_server.controller; + +import com.finut.finut_server.apiPayload.ApiResponse; +import com.finut.finut_server.apiPayload.code.ErrorReasonDTO; +import com.finut.finut_server.domain.user.UserResponseDTO; +import com.finut.finut_server.domain.user.Users; +import com.finut.finut_server.service.UsersService; +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.ApiResponses; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +@Tag(name = "User Controller", description = "사용자 관련 api") +@RequestMapping("/user") +@RestController +public class UserController { + @Autowired + private UsersService usersService; + + @Operation(summary = "출석 체크", description = "출석체크 수가 5의 배수면 월급을 받는 API 입니다.") + @ApiResponses(value = { + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "성공", content = @Content(mediaType = "application/json", + schema = @Schema(implementation = UserResponseDTO.updateAttendance.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "사용자 정보를 제대로 가지고 오지 못했습니다.", + content = @Content), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "500", description = "서버 에러, 관리자에게 문의 바랍니다.", + content = @Content(mediaType = "application/json", + schema = @Schema(implementation = ErrorReasonDTO.class))) + }) + @PatchMapping("/attend") + public ApiResponse updateAttendance(HttpServletRequest request, HttpServletResponse response){ + Users user = usersService.getUserIdByToken(request, response); + UserResponseDTO.updateAttendance updateAttendance = usersService.updateAttendDate(user.getId()); + return ApiResponse.onSuccess(updateAttendance); + } + + @Operation(summary = "프로필 확인", description = "사용자가 본인의 정보를 확인하는 API 입니다.") + @ApiResponses(value = { + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "성공", content = @Content(mediaType = "application/json", + schema = @Schema(implementation = UserResponseDTO.viewUserInfo.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "사용자 정보를 제대로 가지고 오지 못했습니다.", + content = @Content), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "500", description = "서버 에러, 관리자에게 문의 바랍니다.", + content = @Content(mediaType = "application/json", + schema = @Schema(implementation = ErrorReasonDTO.class))) + }) + @GetMapping("/profile") + public ApiResponse viewUserInfo(HttpServletRequest request, HttpServletResponse response){ + Users user = usersService.getUserIdByToken(request, response); + UserResponseDTO.viewUserInfo viewUserInfo = usersService.viewUserInfo(user.getId()); + return ApiResponse.onSuccess(viewUserInfo); + } +} diff --git a/src/main/java/com/finut/finut_server/converter/QuizConverter.java b/src/main/java/com/finut/finut_server/converter/QuizConverter.java deleted file mode 100644 index 4e705a9..0000000 --- a/src/main/java/com/finut/finut_server/converter/QuizConverter.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.finut.finut_server.converter; - -import com.finut.finut_server.domain.quiz.Quiz; -import com.finut.finut_server.domain.quiz.QuizRequestDTO; -import com.finut.finut_server.domain.quiz.QuizResponseDTO; - -public class QuizConverter { - public static QuizResponseDTO.saveQuizDto toSaveQuizDto(Quiz quiz){ - return QuizResponseDTO.saveQuizDto.builder() - .quizId(quiz.getId()) - .quizContent(quiz.getContent()) - .quizAnswer(quiz.getAnswer()) - .quizReason(quiz.getReason()) - .build(); - } - - public static Quiz toQuiz(QuizRequestDTO.saveQuiz request){ - return com.finut.finut_server.domain.quiz.Quiz.builder() - .content(request.getContent()) - .answer(request.getAnswer()) - .reason(request.getReason()) - .correctMoney(request.getCorrectMoney()) - .wrongMoney(request.getWrongMoney()) - .build(); - } - - public static QuizResponseDTO.getQuizDto toGetQuizDto(Long userId, Quiz quiz){ - return QuizResponseDTO.getQuizDto.builder() - .userId(userId) - .quizContent(quiz.getContent()) - .quizAnswer(quiz.getAnswer()) - .quizReason(quiz.getReason()) - .correctMoney(quiz.getCorrectMoney()) - .wrongMoney(quiz.getWrongMoney()) - .build(); - } - - public static QuizResponseDTO.updateMoneyDto toUpdateMoneyDto(Long userId, int moneyAmount){ - return QuizResponseDTO.updateMoneyDto.builder() - .userId(userId) - .moneyAmount(moneyAmount) - .build(); - } -} diff --git a/src/main/java/com/finut/finut_server/domain/attend/Attend.java b/src/main/java/com/finut/finut_server/domain/attend/Attend.java new file mode 100644 index 0000000..6e3eb2d --- /dev/null +++ b/src/main/java/com/finut/finut_server/domain/attend/Attend.java @@ -0,0 +1,36 @@ +package com.finut.finut_server.domain.attend; + +import com.finut.finut_server.domain.BaseTimeEntity; +import com.finut.finut_server.domain.purchases.Purchases; +import com.finut.finut_server.domain.user.Users; +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.io.Serializable; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +@Entity +@IdClass(Attend.AttendId.class) +public class Attend extends BaseTimeEntity { + @Id + @ManyToOne + @JoinColumn(name = "userId", referencedColumnName = "id") + private Users user; + + @Id + @Column(nullable = false) + private String attendDate = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")); + + public static class AttendId implements Serializable { + private Long user; + private String attendDate; + } +} diff --git a/src/main/java/com/finut/finut_server/domain/attend/AttendRepository.java b/src/main/java/com/finut/finut_server/domain/attend/AttendRepository.java new file mode 100644 index 0000000..059bbad --- /dev/null +++ b/src/main/java/com/finut/finut_server/domain/attend/AttendRepository.java @@ -0,0 +1,16 @@ +package com.finut.finut_server.domain.attend; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; + +import java.time.LocalDate; +import java.util.List; +import java.util.Optional; + +public interface AttendRepository extends JpaRepository { + Optional findByUserIdAndAttendDate(Long userId, String attendDate); + + @Query("SELECT attendDate FROM Attend WHERE user = :userId AND attendDate <= :today ORDER BY attendDate DESC LIMIT 5") + List findTop5ByUserIdAndDateBeforeOrderByDateDesc(Long userId, LocalDate today); +} + diff --git a/src/main/java/com/finut/finut_server/domain/difficulty/Difficulty.java b/src/main/java/com/finut/finut_server/domain/difficulty/Difficulty.java new file mode 100644 index 0000000..b87d9dd --- /dev/null +++ b/src/main/java/com/finut/finut_server/domain/difficulty/Difficulty.java @@ -0,0 +1,22 @@ +package com.finut.finut_server.domain.difficulty; + +import com.finut.finut_server.domain.user.Users; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.persistence.*; +import lombok.*; + +@Entity +@Getter +@Setter +@Table(name = "Difficulty") +public class Difficulty { + + @Id + @Enumerated(EnumType.STRING) + private DifficultyType difficulty; + + @Column(nullable = false) + private int diffQuizCnt; +// @OneToOne(mappedBy = "difficulty") +// private Users user; +} diff --git a/src/main/java/com/finut/finut_server/domain/difficulty/DifficultyRepository.java b/src/main/java/com/finut/finut_server/domain/difficulty/DifficultyRepository.java new file mode 100644 index 0000000..de53b06 --- /dev/null +++ b/src/main/java/com/finut/finut_server/domain/difficulty/DifficultyRepository.java @@ -0,0 +1,7 @@ +package com.finut.finut_server.domain.difficulty; + +import com.finut.finut_server.domain.product.Product; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface DifficultyRepository extends JpaRepository { +} diff --git a/src/main/java/com/finut/finut_server/domain/difficulty/DifficultyType.java b/src/main/java/com/finut/finut_server/domain/difficulty/DifficultyType.java new file mode 100644 index 0000000..aa3b590 --- /dev/null +++ b/src/main/java/com/finut/finut_server/domain/difficulty/DifficultyType.java @@ -0,0 +1,7 @@ +package com.finut.finut_server.domain.difficulty; + +public enum DifficultyType { + HI, + MI, + LO +} diff --git a/src/main/java/com/finut/finut_server/domain/level/Level.java b/src/main/java/com/finut/finut_server/domain/level/Level.java new file mode 100644 index 0000000..a9a770f --- /dev/null +++ b/src/main/java/com/finut/finut_server/domain/level/Level.java @@ -0,0 +1,29 @@ +package com.finut.finut_server.domain.level; + +import com.finut.finut_server.domain.BaseTimeEntity; +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +@Entity +public class Level extends BaseTimeEntity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable = false) + @Enumerated(EnumType.STRING) + private LevelName levelName; // 직급 + + @Column(nullable = false) + private int salary; // 월급 액수 + + @Column(nullable = false) + private int levelQuizCnt = 5; // 승진을 위한 퀴즈 수 +} diff --git a/src/main/java/com/finut/finut_server/domain/level/LevelName.java b/src/main/java/com/finut/finut_server/domain/level/LevelName.java new file mode 100644 index 0000000..6a44263 --- /dev/null +++ b/src/main/java/com/finut/finut_server/domain/level/LevelName.java @@ -0,0 +1,21 @@ +package com.finut.finut_server.domain.level; + +public enum LevelName { + PARTTIMEJOB("아르바이트"), + INTERN("인턴"), + STAFF("사원"), + ASSOCIATEMANAGER("대리"), + MANAGER("과장"), + SENIORMANAGER("차장"), + DIRECTOR("부장"); + + private final String koreanName; + + LevelName(String koreanName) { + this.koreanName = koreanName; + } + + public String getKoreanName() { + return koreanName; + } +} diff --git a/src/main/java/com/finut/finut_server/domain/level/LevelRepository.java b/src/main/java/com/finut/finut_server/domain/level/LevelRepository.java new file mode 100644 index 0000000..7053995 --- /dev/null +++ b/src/main/java/com/finut/finut_server/domain/level/LevelRepository.java @@ -0,0 +1,10 @@ +package com.finut.finut_server.domain.level; + +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.Optional; + +public interface LevelRepository extends JpaRepository { + + Optional findById(Long id); +} diff --git a/src/main/java/com/finut/finut_server/domain/news/NewsItemDTO.java b/src/main/java/com/finut/finut_server/domain/news/NewsItemDTO.java new file mode 100644 index 0000000..f763a42 --- /dev/null +++ b/src/main/java/com/finut/finut_server/domain/news/NewsItemDTO.java @@ -0,0 +1,16 @@ +package com.finut.finut_server.domain.news; + +import lombok.Getter; +import lombok.Setter; + +import java.util.Date; + +@Getter +@Setter +public class NewsItemDTO { + private String title; + private String link; + private String description; + private int number; + private String ImageUrl; +} diff --git a/src/main/java/com/finut/finut_server/domain/quest/Quest.java b/src/main/java/com/finut/finut_server/domain/quest/Quest.java new file mode 100644 index 0000000..b6d4b1f --- /dev/null +++ b/src/main/java/com/finut/finut_server/domain/quest/Quest.java @@ -0,0 +1,32 @@ +package com.finut.finut_server.domain.quest; + +import com.fasterxml.jackson.annotation.JsonManagedReference; +import com.finut.finut_server.domain.BaseTimeEntity; +import com.finut.finut_server.domain.difficulty.DifficultyType; +import com.finut.finut_server.domain.level.LevelName; +import com.finut.finut_server.domain.questQuiz.QuestQuiz; +import jakarta.persistence.*; +import lombok.*; + +import java.util.List; + +@Entity +@Builder +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@ToString +public class Quest { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable = false) + @Enumerated(EnumType.STRING) + private LevelName nextLevel; + + @OneToMany(mappedBy = "quest", cascade = CascadeType.ALL, fetch = FetchType.LAZY) + @JsonManagedReference + private List quizzes; +} diff --git a/src/main/java/com/finut/finut_server/domain/quest/QuestDTO.java b/src/main/java/com/finut/finut_server/domain/quest/QuestDTO.java new file mode 100644 index 0000000..928536c --- /dev/null +++ b/src/main/java/com/finut/finut_server/domain/quest/QuestDTO.java @@ -0,0 +1,18 @@ +package com.finut.finut_server.domain.quest; + +import com.finut.finut_server.domain.level.LevelName; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +public class QuestDTO { + private Long id; + private LevelName nextLevel; + +} diff --git a/src/main/java/com/finut/finut_server/domain/quest/QuestRepository.java b/src/main/java/com/finut/finut_server/domain/quest/QuestRepository.java new file mode 100644 index 0000000..616d9af --- /dev/null +++ b/src/main/java/com/finut/finut_server/domain/quest/QuestRepository.java @@ -0,0 +1,7 @@ +package com.finut.finut_server.domain.quest; + +import org.springframework.data.jpa.repository.JpaRepository; + +public interface QuestRepository extends JpaRepository { + +} diff --git a/src/main/java/com/finut/finut_server/domain/questDone/QuestDone.java b/src/main/java/com/finut/finut_server/domain/questDone/QuestDone.java new file mode 100644 index 0000000..c2ea7ce --- /dev/null +++ b/src/main/java/com/finut/finut_server/domain/questDone/QuestDone.java @@ -0,0 +1,24 @@ +package com.finut.finut_server.domain.questDone; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.IdClass; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +@Entity +@IdClass(QuestDoneId.class) +public class QuestDone { + @Id + private Long questId; + + @Id + private Long userId; + +} diff --git a/src/main/java/com/finut/finut_server/domain/questDone/QuestDoneId.java b/src/main/java/com/finut/finut_server/domain/questDone/QuestDoneId.java new file mode 100644 index 0000000..77075e6 --- /dev/null +++ b/src/main/java/com/finut/finut_server/domain/questDone/QuestDoneId.java @@ -0,0 +1,16 @@ +package com.finut.finut_server.domain.questDone; + +import java.io.Serializable; + +public class QuestDoneId implements Serializable { + private Long questId; + private Long userId; + + public QuestDoneId() {} + + public QuestDoneId(Long questId, Long userId) { + this.questId = questId; + this.userId = userId; + } +} + diff --git a/src/main/java/com/finut/finut_server/domain/questDone/QuestDoneRepository.java b/src/main/java/com/finut/finut_server/domain/questDone/QuestDoneRepository.java new file mode 100644 index 0000000..5f4fb6d --- /dev/null +++ b/src/main/java/com/finut/finut_server/domain/questDone/QuestDoneRepository.java @@ -0,0 +1,10 @@ +package com.finut.finut_server.domain.questDone; + +import com.finut.finut_server.domain.quest.Quest; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.List; + +public interface QuestDoneRepository extends JpaRepository { + List findAllByUserId(Long userid); +} diff --git a/src/main/java/com/finut/finut_server/domain/questQuiz/QuestQuiz.java b/src/main/java/com/finut/finut_server/domain/questQuiz/QuestQuiz.java new file mode 100644 index 0000000..ab30005 --- /dev/null +++ b/src/main/java/com/finut/finut_server/domain/questQuiz/QuestQuiz.java @@ -0,0 +1,48 @@ +package com.finut.finut_server.domain.questQuiz; + +import com.fasterxml.jackson.annotation.JsonBackReference; +import com.finut.finut_server.domain.quest.Quest; +import jakarta.persistence.*; +import lombok.*; + +@Entity +@Builder +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@ToString +public class QuestQuiz { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; +// +// @Column(nullable = false) +// private Long quizId; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "quest_id", insertable = false, updatable = false) + @JsonBackReference + private Quest quest; + + @Column(nullable = false) + private String question; + + @Column(nullable = false) + private String option1; + + @Column(nullable = false) + private String option2; + + @Column(nullable = false) + private String option3; + + @Column(nullable = false) + private Integer correctOption; + + @Column(nullable = false, columnDefinition = "TEXT") + @Lob + private String description; + + +} diff --git a/src/main/java/com/finut/finut_server/domain/questQuiz/QuestQuizRepository.java b/src/main/java/com/finut/finut_server/domain/questQuiz/QuestQuizRepository.java new file mode 100644 index 0000000..6085353 --- /dev/null +++ b/src/main/java/com/finut/finut_server/domain/questQuiz/QuestQuizRepository.java @@ -0,0 +1,10 @@ +package com.finut.finut_server.domain.questQuiz; + + +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.List; + +public interface QuestQuizRepository extends JpaRepository { + List findByQuestId(Long questId); +} diff --git a/src/main/java/com/finut/finut_server/domain/quiz/AnswerType.java b/src/main/java/com/finut/finut_server/domain/quiz/AnswerType.java new file mode 100644 index 0000000..d95467a --- /dev/null +++ b/src/main/java/com/finut/finut_server/domain/quiz/AnswerType.java @@ -0,0 +1,6 @@ +package com.finut.finut_server.domain.quiz; + +public enum AnswerType { + TRUE, + FALSE +} diff --git a/src/main/java/com/finut/finut_server/domain/quiz/Quiz.java b/src/main/java/com/finut/finut_server/domain/quiz/Quiz.java index 6686a92..2fc385a 100644 --- a/src/main/java/com/finut/finut_server/domain/quiz/Quiz.java +++ b/src/main/java/com/finut/finut_server/domain/quiz/Quiz.java @@ -2,12 +2,18 @@ import com.finut.finut_server.domain.BaseTimeEntity; +import com.finut.finut_server.domain.difficulty.DifficultyType; +import com.finut.finut_server.domain.quizDone.QuizDone; import jakarta.persistence.*; import lombok.*; +import java.util.List; +import java.util.Set; + @Entity @Builder @Getter +@Setter @AllArgsConstructor @NoArgsConstructor(access = AccessLevel.PROTECTED) @ToString @@ -17,17 +23,24 @@ public class Quiz extends BaseTimeEntity { private Long id; @Column(nullable = false) - private String content; // 퀴즈 내용 + @Enumerated(EnumType.STRING) + private DifficultyType difficulty; // 난이도 @Column(nullable = false) - private String answer; // 퀴즈 정답 + @Lob + private String question; // 퀴즈 내용 @Column(nullable = false) - private String reason; // 정답 이유 + @Enumerated(EnumType.STRING) + private AnswerType answer; - @Column(nullable = false) - private int correctMoney; // 맞춘 경우 얻는 돈 + @Column(nullable = false, columnDefinition = "MEDIUMTEXT") + private String description; // 정답 설명 - @Column(nullable = false) - private int wrongMoney; // 틀린 경우 얻는 돈 + @OneToMany(mappedBy = "quiz", cascade = CascadeType.ALL, orphanRemoval = true) + private List quizDoneList; + + public Quiz(Long quizId, DifficultyType difficultyType, String question, AnswerType answerType) { + super(); + } } diff --git a/src/main/java/com/finut/finut_server/domain/quiz/QuizRepository.java b/src/main/java/com/finut/finut_server/domain/quiz/QuizRepository.java index eabe287..77c39b0 100644 --- a/src/main/java/com/finut/finut_server/domain/quiz/QuizRepository.java +++ b/src/main/java/com/finut/finut_server/domain/quiz/QuizRepository.java @@ -1,5 +1,6 @@ package com.finut.finut_server.domain.quiz; +import com.finut.finut_server.domain.difficulty.DifficultyType; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; @@ -11,11 +12,5 @@ public interface QuizRepository extends JpaRepository { - @Query("SELECT e FROM Quiz e WHERE DATE(e.createdDate) = :date") - Quiz findByDate(@Param("date") LocalDate date); - - @Modifying - @Transactional - @Query("UPDATE Users u SET u.money = u.money + :moneyAmount WHERE u.id = :userId") - void updateMoney(Long userId, int moneyAmount); + List findByDifficulty(DifficultyType quizDiff); } diff --git a/src/main/java/com/finut/finut_server/domain/quiz/QuizRequestDTO.java b/src/main/java/com/finut/finut_server/domain/quiz/QuizRequestDTO.java deleted file mode 100644 index 24be21e..0000000 --- a/src/main/java/com/finut/finut_server/domain/quiz/QuizRequestDTO.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.finut.finut_server.domain.quiz; - -import jakarta.validation.constraints.NotNull; -import lombok.Builder; -import lombok.Getter; -import lombok.Setter; - -@Getter -@Setter -@Builder -public class QuizRequestDTO { - @Getter - public static class saveQuiz{ - @NotNull - String content; - @NotNull - String answer; - @NotNull - String reason; - @NotNull - int correctMoney; // 맞춘 경우 얻는 돈 - @NotNull - int wrongMoney; // 틀린 경우 얻는 돈 - } - - @Getter - public static class updateMoney{ - @NotNull - Long userId; // 접속한 유저의 아이디 - @NotNull - boolean isCorrect; // 정답 여부 - 정답이면 true, 오답이면 false - @NotNull - int correctMoney; // 맞춘 경우 받는 돈 - @NotNull - int wrongMoney; // 틀린 경우 받는 돈 - } - -} diff --git a/src/main/java/com/finut/finut_server/domain/quiz/QuizResponseDTO.java b/src/main/java/com/finut/finut_server/domain/quiz/QuizResponseDTO.java index 8cef88b..74dd70f 100644 --- a/src/main/java/com/finut/finut_server/domain/quiz/QuizResponseDTO.java +++ b/src/main/java/com/finut/finut_server/domain/quiz/QuizResponseDTO.java @@ -1,45 +1,22 @@ package com.finut.finut_server.domain.quiz; +import com.finut.finut_server.domain.difficulty.DifficultyType; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; -import java.util.List; - public class QuizResponseDTO { - @Builder - @Getter - @NoArgsConstructor - @AllArgsConstructor - public static class saveQuizDto{ - Long quizId; - String quizContent; // 퀴즈 내용 - String quizAnswer; // 퀴즈 답 - String quizReason; // 답 풀이 - int correctMoney; // 맞춘 경우 얻는 돈 - int wrongMoney; // 틀린 경우 얻는 돈 - } - - @Builder - @Getter - @NoArgsConstructor - @AllArgsConstructor - public static class getQuizDto{ - Long userId; // 접속한 유저의 아이디 - String quizContent; // 퀴즈 내용 - String quizAnswer; // 퀴즈 답 - String quizReason; // 답 풀이 - int correctMoney; // 맞춘 경우 얻는 돈 - int wrongMoney; // 틀린 경우 얻는 돈 - } @Builder @Getter @NoArgsConstructor @AllArgsConstructor - public static class updateMoneyDto{ - Long userId; // 접속한 유저의 아이디 - int moneyAmount; // 적립된 금액 + public static class randomQuizResponseDTO { + Long id; + DifficultyType difficulty; + String question; + AnswerType answer; + String description; } } diff --git a/src/main/java/com/finut/finut_server/domain/quizDone/QuizDone.java b/src/main/java/com/finut/finut_server/domain/quizDone/QuizDone.java new file mode 100644 index 0000000..7f58e22 --- /dev/null +++ b/src/main/java/com/finut/finut_server/domain/quizDone/QuizDone.java @@ -0,0 +1,37 @@ +package com.finut.finut_server.domain.quizDone; + +import com.finut.finut_server.domain.quiz.Quiz; +import com.finut.finut_server.domain.user.Users; +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.io.Serializable; + +@Entity +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +@IdClass(QuizDoneId.class) +public class QuizDone { + @Id + private Long quizId; + + @Id + private Long userId; + + private Boolean isCorrect; + + @ManyToOne + @JoinColumn(name = "quizId", insertable = false, updatable = false) + private Quiz quiz; + + @ManyToOne + @JoinColumn(name = "userId", insertable = false, updatable = false) + private Users user; + +} + diff --git a/src/main/java/com/finut/finut_server/domain/quizDone/QuizDoneId.java b/src/main/java/com/finut/finut_server/domain/quizDone/QuizDoneId.java new file mode 100644 index 0000000..4609767 --- /dev/null +++ b/src/main/java/com/finut/finut_server/domain/quizDone/QuizDoneId.java @@ -0,0 +1,28 @@ +package com.finut.finut_server.domain.quizDone; + +import lombok.Getter; +import lombok.Setter; + +import java.io.Serializable; +import java.util.Objects; + +@Getter +@Setter +public class QuizDoneId implements Serializable { + private Long quizId; + private Long userId; + + // Default constructor, hashCode, equals + @Override + public int hashCode(){ + return Objects.hash(quizId, userId); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + QuizDoneId that = (QuizDoneId) o; + return Objects.equals(quizId, that.quizId) && Objects.equals(userId, that.userId); + } +} diff --git a/src/main/java/com/finut/finut_server/domain/quizDone/QuizDoneRepository.java b/src/main/java/com/finut/finut_server/domain/quizDone/QuizDoneRepository.java new file mode 100644 index 0000000..55fd171 --- /dev/null +++ b/src/main/java/com/finut/finut_server/domain/quizDone/QuizDoneRepository.java @@ -0,0 +1,18 @@ +package com.finut.finut_server.domain.quizDone; + +import com.finut.finut_server.domain.quiz.Quiz; +import com.finut.finut_server.domain.user.Users; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; + +import java.util.List; +import java.util.Optional; + +@EnableJpaRepositories +public interface QuizDoneRepository extends JpaRepository { + Optional findByQuizIdAndUserId(Long quizId, Long userId); + + Optional findByUserId(Long userId); + + List findAllByUserId(Long userId); +} diff --git a/src/main/java/com/finut/finut_server/domain/user/UserResponseDTO.java b/src/main/java/com/finut/finut_server/domain/user/UserResponseDTO.java index ff08bfc..6763d21 100644 --- a/src/main/java/com/finut/finut_server/domain/user/UserResponseDTO.java +++ b/src/main/java/com/finut/finut_server/domain/user/UserResponseDTO.java @@ -1,5 +1,7 @@ package com.finut.finut_server.domain.user; +import com.finut.finut_server.domain.level.Level; +import com.finut.finut_server.domain.level.LevelName; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; @@ -14,4 +16,39 @@ public static class loginUserDTO { String email; String accessToken; } + @Builder + @Getter + @NoArgsConstructor + @AllArgsConstructor + public static class updateAttendance { + Long userId; + String message; + } + + @Builder + @Getter + @NoArgsConstructor + @AllArgsConstructor + public static class viewUserInfo { + Long userId; + String name; + Long money; + String picture; + int xp; + String levelName; + } + + @Builder + @Getter + @NoArgsConstructor + @AllArgsConstructor + public static class checkUserXP { + Long userId; + String name; + Long money; + int xp; + Level levelName; + } + + } diff --git a/src/main/java/com/finut/finut_server/domain/user/Users.java b/src/main/java/com/finut/finut_server/domain/user/Users.java index 1686503..4577661 100644 --- a/src/main/java/com/finut/finut_server/domain/user/Users.java +++ b/src/main/java/com/finut/finut_server/domain/user/Users.java @@ -2,14 +2,22 @@ import com.finut.finut_server.domain.BaseTimeEntity; +import com.finut.finut_server.domain.difficulty.Difficulty; +import com.finut.finut_server.domain.difficulty.DifficultyType; +import com.finut.finut_server.domain.quizDone.QuizDone; +import com.finut.finut_server.domain.level.Level; +import com.finut.finut_server.domain.level.LevelName; import jakarta.persistence.*; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; +import lombok.*; +import org.hibernate.mapping.ToOne; + +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.util.Set; @Getter @Setter +@AllArgsConstructor @NoArgsConstructor @Entity public class Users extends BaseTimeEntity { @@ -36,6 +44,36 @@ public class Users extends BaseTimeEntity { @Column(nullable = false) private Long money = 100000L; + @ManyToOne + @JoinColumn(name = "levelId", referencedColumnName = "id") + private Level level; + + @Column(nullable = false) + private int attendCount = 0; + + @Column(nullable = false) + private int XP = 0; + + @Column(nullable = false) + private boolean todaySalary = false; + + @Column(nullable = false) + private int continuousCount = 0; // 연속 출석 횟수 구하기 + + @Column(nullable = false) + private int diffQuizCount = 0; + +// @Column(nullable = false) +// private int levelQuizCount = 0; + + @ManyToOne + @JoinColumn(name = "difficulty") + private Difficulty difficulty; + + @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true) + private Set quizDoneList; + + @Builder public Users(String name, String email, String picture, String refreshToken, Role role) { this.name = name; @@ -61,4 +99,17 @@ public Users setRefreshToken(String refreshToken) public String getRoleKey() { return this.role.getKey(); } -} + + @PrePersist + public void prePersist() { + if (this.level == null) { + this.level = new Level(); // 또는 LevelRepository를 사용해 ID가 1인 Level을 설정 + this.level.setId(1L); // 기본값으로 ID가 1인 Level 설정 + } + + if (this.difficulty == null) { + this.difficulty = new Difficulty(); + this.difficulty.setDifficulty(DifficultyType.LO); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/finut/finut_server/service/GoogleAuthService.java b/src/main/java/com/finut/finut_server/service/GoogleAuthService.java new file mode 100644 index 0000000..ef123e8 --- /dev/null +++ b/src/main/java/com/finut/finut_server/service/GoogleAuthService.java @@ -0,0 +1,36 @@ +package com.finut.finut_server.service; + +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.util.Collections; + +import com.google.api.client.googleapis.auth.oauth2.GoogleCredential; +import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport; +import com.google.api.client.http.javanet.NetHttpTransport; +import com.google.api.client.json.gson.GsonFactory; +import com.google.api.client.json.jackson2.JacksonFactory; +import com.google.api.services.oauth2.Oauth2; +import com.google.api.services.oauth2.model.Userinfoplus; +import com.google.auth.http.HttpCredentialsAdapter; +import com.google.auth.oauth2.AccessToken; +import com.google.auth.oauth2.GoogleCredentials; +import org.springframework.stereotype.Service; + +@Service +public class GoogleAuthService { + public Userinfoplus getUserInfo(String accessToken) throws IOException, GeneralSecurityException { + // GoogleCredentials로 Access Token 설정 + GoogleCredentials credentials = GoogleCredentials.create(new AccessToken(accessToken, null)); + + // Oauth2 서비스 생성 + Oauth2 oauth2 = new Oauth2.Builder( + GoogleNetHttpTransport.newTrustedTransport(), + GsonFactory.getDefaultInstance(), // GsonFactory 사용 + new HttpCredentialsAdapter(credentials)) + .setApplicationName("Finut") + .build(); + + // 사용자 정보 요청 + return oauth2.userinfo().get().execute(); + } +} diff --git a/src/main/java/com/finut/finut_server/service/InterestRateService.java b/src/main/java/com/finut/finut_server/service/InterestRateService.java deleted file mode 100644 index b14372a..0000000 --- a/src/main/java/com/finut/finut_server/service/InterestRateService.java +++ /dev/null @@ -1,197 +0,0 @@ -package com.finut.finut_server.service; - -import com.fasterxml.jackson.databind.node.ArrayNode; -import com.fasterxml.jackson.databind.node.ObjectNode; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Service; -import org.springframework.web.client.RestTemplate; -import org.springframework.web.util.UriComponentsBuilder; - - -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import java.net.URI; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.util.Date; - -@Service -public class InterestRateService { - @Value("${bank.api.base-url}") - private String baseUrl; - - @Value("${bank.api.key}") - private String apiKey; - - private final RestTemplate restTemplate; - - LocalDate today = LocalDate.now(); - - private int yearValue = today.getYear(); - private int monthValue = today.getMonthValue(); - - String startDate; - final String endDate = Integer.toString(yearValue) + String.format("%02d", monthValue); - - public InterestRateService () { - this.restTemplate = new RestTemplate(); - } - - public String getInterestRatesToady() { - if(monthValue == 1) { - startDate = Integer.toString(yearValue - 1) + "12"; - } - else - { - startDate = Integer.toString(yearValue) + "01"; - } - - URI uri = UriComponentsBuilder.fromHttpUrl(baseUrl) - .pathSegment("StatisticSearch") - .pathSegment(apiKey) - .pathSegment("JSON") - .pathSegment("kr") - .pathSegment("1") // 요청시작건수 - .pathSegment("12") // 요청종료건수 - .pathSegment("722Y001") // 통계표코드 - .pathSegment("M") // 주기 - .pathSegment(startDate) // 검색시작일자 - .pathSegment(endDate) // 검색종료일자 - .pathSegment("0101000") // 통계항목코드1 - .pathSegment("?") // 통계항목코드2 - .pathSegment("?") // 통계항목코드3 - .pathSegment("?") // 통계항목코드4 - .build() - .toUri(); - - String response = restTemplate.getForObject(uri, String.class); - - return extractLastDataValue(response); - } - - private String extractLastDataValue(String jsonResponse) { - try { - ObjectMapper mapper = new ObjectMapper(); - JsonNode rootNode = mapper.readTree(jsonResponse); - - JsonNode rows = rootNode.path("StatisticSearch").path("row"); - - String dataValue; - if (rows.isArray() && rows.size() > 0) { - JsonNode lastRow = rows.get(rows.size() - 1); - dataValue = lastRow.path("DATA_VALUE").asText(); - } else { - dataValue = "No data available"; - } - - return dataValue; - } catch (Exception e) { - e.printStackTrace(); - ObjectNode errorNode = new ObjectMapper().createObjectNode(); - errorNode.put("result", "Error processing data"); - return errorNode.toString(); - } - } - - public ArrayNode getInterestRates3Y() { - startDate = Integer.toString(yearValue - 3) + String.format("%02d", monthValue); - - URI uri = UriComponentsBuilder.fromHttpUrl(baseUrl) - .pathSegment("StatisticSearch") - .pathSegment(apiKey) - .pathSegment("JSON") - .pathSegment("kr") - .pathSegment("1") // 요청시작건수 - .pathSegment("36") // 요청종료건수 - .pathSegment("722Y001") // 통계표코드 - .pathSegment("M") // 주기 - .pathSegment(startDate) // 검색시작일자 - .pathSegment(endDate) // 검색종료일자 - .pathSegment("0101000") // 통계항목코드1 - .pathSegment("?") // 통계항목코드2 - .pathSegment("?") // 통계항목코드3 - .pathSegment("?") // 통계항목코드4 - .build() - .toUri(); - - String response = restTemplate.getForObject(uri, String.class); - - try { - ObjectMapper objectMapper = new ObjectMapper(); - JsonNode rootNode = objectMapper.readTree(response); - JsonNode rowNode = rootNode.path("StatisticSearch").path("row"); - - ArrayNode resultArray = objectMapper.createArrayNode(); - - if (rowNode.isArray()) { - for (JsonNode node : rowNode) { - String time = node.path("TIME").asText(); - String dataValue = node.path("DATA_VALUE").asText(); - - ObjectNode newNode = objectMapper.createObjectNode(); - newNode.put("TIME", time); - newNode.put("DATA_VALUE", dataValue); - - resultArray.add(newNode); - } - } - - return resultArray; - - } catch (Exception e) { - e.printStackTrace(); - return null; - } - } - - public ArrayNode getInterestRates5Y() { - startDate = Integer.toString(yearValue - 5) + String.format("%02d", monthValue); - - URI uri = UriComponentsBuilder.fromHttpUrl(baseUrl) - .pathSegment("StatisticSearch") - .pathSegment(apiKey) - .pathSegment("JSON") - .pathSegment("kr") - .pathSegment("1") // 요청시작건수 - .pathSegment("60") // 요청종료건수 - .pathSegment("722Y001") // 통계표코드 - .pathSegment("M") // 주기 - .pathSegment(startDate) // 검색시작일자 - .pathSegment(endDate) // 검색종료일자 - .pathSegment("0101000") // 통계항목코드1 - .pathSegment("?") // 통계항목코드2 - .pathSegment("?") // 통계항목코드3 - .pathSegment("?") // 통계항목코드4 - .build() - .toUri(); - - String response = restTemplate.getForObject(uri, String.class); - - try { - ObjectMapper objectMapper = new ObjectMapper(); - JsonNode rootNode = objectMapper.readTree(response); - JsonNode rowNode = rootNode.path("StatisticSearch").path("row"); - - ArrayNode resultArray = objectMapper.createArrayNode(); - - if (rowNode.isArray()) { - for (JsonNode node : rowNode) { - String time = node.path("TIME").asText(); - String dataValue = node.path("DATA_VALUE").asText(); - - ObjectNode newNode = objectMapper.createObjectNode(); - newNode.put("TIME", time); - newNode.put("DATA_VALUE", dataValue); - - resultArray.add(newNode); - } - } - - return resultArray; - - } catch (Exception e) { - e.printStackTrace(); - return null; - } - } -} diff --git a/src/main/java/com/finut/finut_server/service/LevelService.java b/src/main/java/com/finut/finut_server/service/LevelService.java new file mode 100644 index 0000000..f98bbd6 --- /dev/null +++ b/src/main/java/com/finut/finut_server/service/LevelService.java @@ -0,0 +1,38 @@ +package com.finut.finut_server.service; + +import com.finut.finut_server.domain.level.Level; +import com.finut.finut_server.domain.level.LevelRepository; +import com.finut.finut_server.domain.user.Users; +import com.finut.finut_server.domain.user.UsersRepository; +import jakarta.transaction.Transactional; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.Optional; + +@Service +public class LevelService { + @Autowired + private UsersRepository usersRepository; + + @Autowired + private LevelRepository levelRepository; + @Transactional + public Users upgradeUserLevel(Long userId) { + Users user = usersRepository.findById(userId) + .orElseThrow(() -> new IllegalArgumentException("User not found with id: " + userId)); + + Long currentLevelId = user.getLevel().getId(); + Optional nextLevelOpt = levelRepository.findById(currentLevelId + 1); + + if (nextLevelOpt.isPresent()) { + user.setLevel(nextLevelOpt.get()); + user.setXP(0); + usersRepository.save(user); + return user; + } else { + throw new IllegalStateException("No higher level available. User is already at the highest level."); + } + } + +} diff --git a/src/main/java/com/finut/finut_server/service/QuestQuizService.java b/src/main/java/com/finut/finut_server/service/QuestQuizService.java new file mode 100644 index 0000000..0cce8fc --- /dev/null +++ b/src/main/java/com/finut/finut_server/service/QuestQuizService.java @@ -0,0 +1,19 @@ +package com.finut.finut_server.service; + +import com.finut.finut_server.domain.questQuiz.QuestQuiz; +import com.finut.finut_server.domain.questQuiz.QuestQuizRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class QuestQuizService { + + @Autowired + private QuestQuizRepository questQuizRepository; + + public List getQuestQuizzes(Long questId) { + return questQuizRepository.findByQuestId(questId); + } +} diff --git a/src/main/java/com/finut/finut_server/service/QuestService.java b/src/main/java/com/finut/finut_server/service/QuestService.java new file mode 100644 index 0000000..64068c2 --- /dev/null +++ b/src/main/java/com/finut/finut_server/service/QuestService.java @@ -0,0 +1,36 @@ +package com.finut.finut_server.service; + +import com.finut.finut_server.domain.quest.Quest; +import com.finut.finut_server.domain.quest.QuestDTO; +import com.finut.finut_server.domain.quest.QuestRepository; +import com.finut.finut_server.domain.questDone.QuestDone; +import com.finut.finut_server.domain.questDone.QuestDoneId; +import com.finut.finut_server.domain.questDone.QuestDoneRepository; +import com.finut.finut_server.domain.user.Users; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.stream.Collectors; + +@Service +public class QuestService { + @Autowired + private QuestRepository questRepository; + + @Autowired + private QuestDoneRepository questDoneRepository; + + public List getAllQuests() { + List quests= questRepository.findAll(); + return quests.stream() + .map(quest -> new QuestDTO(quest.getId(), quest.getNextLevel())) + .collect(Collectors.toList()); + } + + public List updateQuestDone(Users users, Long questId) { + QuestDone questDone = new QuestDone(users.getId(), questId); + questDoneRepository.save(questDone); + return questDoneRepository.findAllByUserId(users.getId()); + } +} diff --git a/src/main/java/com/finut/finut_server/service/QuizDoneService.java b/src/main/java/com/finut/finut_server/service/QuizDoneService.java new file mode 100644 index 0000000..2269626 --- /dev/null +++ b/src/main/java/com/finut/finut_server/service/QuizDoneService.java @@ -0,0 +1,35 @@ +package com.finut.finut_server.service; + +import com.finut.finut_server.domain.quiz.Quiz; +import com.finut.finut_server.domain.quizDone.QuizDone; +import com.finut.finut_server.domain.quizDone.QuizDoneId; +import com.finut.finut_server.domain.quizDone.QuizDoneRepository; +import com.finut.finut_server.domain.user.Users; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class QuizDoneService { + + @Autowired + QuizDoneRepository quizDoneRepository; + + + + public void saveQuizDone(Users user, Quiz quiz, boolean isCorrect) { + QuizDone quizDone = new QuizDone(); + QuizDoneId quizDoneId = new QuizDoneId(); + + quizDoneId.setQuizId(quiz.getId()); + quizDoneId.setUserId(user.getId()); + + quizDone.setQuizId(quiz.getId()); + quizDone.setUserId(user.getId()); + + quizDone.setQuiz(quiz); + quizDone.setUser(user); + quizDone.setIsCorrect(isCorrect); + quizDoneRepository.save(quizDone); + } + +} diff --git a/src/main/java/com/finut/finut_server/service/QuizService.java b/src/main/java/com/finut/finut_server/service/QuizService.java index 83c1ea0..eb8895f 100644 --- a/src/main/java/com/finut/finut_server/service/QuizService.java +++ b/src/main/java/com/finut/finut_server/service/QuizService.java @@ -1,21 +1,22 @@ package com.finut.finut_server.service; -import com.finut.finut_server.apiPayload.code.status.ErrorStatus; -import com.finut.finut_server.apiPayload.exception.GeneralException; -import com.finut.finut_server.controller.QuizController; -import com.finut.finut_server.converter.QuizConverter; +import com.finut.finut_server.domain.difficulty.DifficultyType; import com.finut.finut_server.domain.quiz.Quiz; import com.finut.finut_server.domain.quiz.QuizRepository; -import com.finut.finut_server.domain.quiz.QuizRequestDTO; +import com.finut.finut_server.domain.quizDone.QuizDoneRepository; +import com.finut.finut_server.domain.user.Users; +import com.finut.finut_server.domain.user.UsersRepository; import jakarta.transaction.Transactional; import lombok.RequiredArgsConstructor; +import lombok.Setter; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import java.time.LocalDate; -import java.time.format.DateTimeFormatter; import java.util.List; import java.util.Optional; +import java.util.Random; +import java.util.Set; +import java.util.stream.Collectors; @Service @RequiredArgsConstructor @@ -24,31 +25,51 @@ public class QuizService { @Autowired private QuizRepository quizRepository; + @Autowired + private QuizDoneRepository quizDoneRepository; + + @Autowired + private UsersRepository usersRepository; + + private final Random random = new Random(); + @Transactional - public Quiz saveQuiz(QuizRequestDTO.saveQuiz request){ - if(request.getCorrectMoney() < 0 || request.getWrongMoney() < 0){ - throw new GeneralException(ErrorStatus.INVALID_NUMBER); + public Optional getQuiz(Long userId) { + Optional userOpt = usersRepository.findById(userId); + if (userOpt.isEmpty()) { + System.out.println("userOpt is empty"); + return Optional.empty(); } + Users user = userOpt.get(); - Quiz quiz = QuizConverter.toQuiz(request); - quiz = quizRepository.save(quiz); + DifficultyType quizDiff = user.getDifficulty().getDifficulty(); - return quiz; - } + // quizDiff에 해당하는 모든 퀴즈 찾기 + List allQuizzesByDiff = quizRepository.findByDifficulty(quizDiff); - @Transactional - public Quiz getQuiz(){ - LocalDate today = LocalDate.now(); - return quizRepository.findByDate(today); + // 해당 사용자가 완료한 퀴즈 ID 목록 가져오기 + Set completedQuizIds = quizDoneRepository.findAllByUserId(userId).stream() + .map(quizDone -> quizDone.getQuizId()) + .collect(Collectors.toSet()); + + // 완료한 퀴즈는 제외한 남은 퀴즈 목록 생성 + List uncompletedQuizzes = allQuizzesByDiff.stream() + .filter(quiz -> !completedQuizIds.contains(quiz.getId())) + .toList(); + + // 남은 퀴즈가 없으면 Optional.empty() 반환 + if (uncompletedQuizzes.isEmpty()) { + System.out.println("uncomplete Quizzes is empty"); + return Optional.empty(); + } + + // 남은 퀴즈 목록에서 랜덤하게 하나 선택하여 반환 + return Optional.of(uncompletedQuizzes.get(random.nextInt(uncompletedQuizzes.size()))); } - @Transactional - public int updateMoney(QuizRequestDTO.updateMoney request){ - int moneyAmount = 0; - if(request.isCorrect()) moneyAmount = request.getCorrectMoney(); - else moneyAmount = request.getWrongMoney(); - quizRepository.updateMoney(request.getUserId(), moneyAmount); - return moneyAmount; + public Optional getQuizByQuizId(Long quizId) { + return quizRepository.findById(quizId); + } } diff --git a/src/main/java/com/finut/finut_server/service/TodayNewsService.java b/src/main/java/com/finut/finut_server/service/TodayNewsService.java new file mode 100644 index 0000000..b214d4d --- /dev/null +++ b/src/main/java/com/finut/finut_server/service/TodayNewsService.java @@ -0,0 +1,105 @@ +package com.finut.finut_server.service; + +import com.finut.finut_server.domain.news.NewsItemDTO; +import com.rometools.rome.feed.synd.SyndEntry; +import com.rometools.rome.feed.synd.SyndFeed; +import com.rometools.rome.io.XmlReader; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.jsoup.select.Elements; +import org.springframework.stereotype.Service; +import com.rometools.rome.io.SyndFeedInput; + +import org.jdom2.Element; + +import java.io.IOException; +import java.net.URL; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.List; +import java.util.Locale; +import java.util.stream.Collectors; + +@Service +public class TodayNewsService { + private static final String economy_URL = "https://www.mk.co.kr/rss/30100041/"; + private static final String real_estate_URL = "https://www.mk.co.kr/rss/50300009/"; + private static final String stock_URL = "https://www.mk.co.kr/rss/50200011/"; + + public List getNews(int type) throws Exception { + URL feedSource; + if (type == 1) + { + feedSource = new URL(economy_URL); + } + else if (type == 2) + { + feedSource = new URL(real_estate_URL); + } + else { + feedSource = new URL(stock_URL); + } + + SyndFeedInput input = new SyndFeedInput(); + SyndFeed feed = input.build(new XmlReader(feedSource)); + + return feed.getEntries().stream().map(this::convertToNewsItem).limit(10).collect(Collectors.toList()); + } + + private NewsItemDTO convertToNewsItem(SyndEntry entry) { + NewsItemDTO news = new NewsItemDTO(); + + //URL 에서 number 추출 + String url = entry.getLink(); + String[] parts = url.split("/"); + news.setNumber(Integer.parseInt(parts[parts.length-1])); + + // 기본 정보 설정 + news.setTitle(entry.getTitle()); + news.setLink(url.replace("www.", "m.")); + news.setDescription(entry.getDescription().getValue()); + + + // 이미지 추출 + String imageUrl = null; + List foreignMarkup = entry.getForeignMarkup(); // 커스텀 태그 추출 + for (Element element : foreignMarkup) { + System.out.println(element); + if ("content".equals(element.getName()) && "image".equals(element.getAttributeValue("medium"))) { + imageUrl = element.getAttributeValue("url"); + break; + } + } + + news.setImageUrl(imageUrl); + + + return news; + } + + + public static String getMainContent(String url) { + try { + Document doc = Jsoup.connect(url).userAgent("Mozilla/5.0").get(); + + // 본문을 감싸고 있는 요소 선택 (div.news_cnt_detail_wrap) + org.jsoup.nodes.Element content = doc.selectFirst("div.news_cnt_detail_wrap[itemprop=articleBody]"); + + if (content != null) { + // 이미지, 광고, 비어있는 태그 등을 제거 + content.select("img, iframe, figure, figcaption, .ad_boxm1, .thumb_area, .mid_title").remove(); + + // 텍스트만 추출 + String text = content.text(); + return text.toString(); + } else { + return("본문을 찾을 수 없습니다."); + } + + } catch (IOException e) { + e.printStackTrace(); + return "본문을 가져오는 도중 오류가 발생했습니다."; + } + } +} diff --git a/src/main/java/com/finut/finut_server/service/UsersService.java b/src/main/java/com/finut/finut_server/service/UsersService.java index 5f2098a..cdbcca3 100644 --- a/src/main/java/com/finut/finut_server/service/UsersService.java +++ b/src/main/java/com/finut/finut_server/service/UsersService.java @@ -1,15 +1,26 @@ package com.finut.finut_server.service; +import com.finut.finut_server.apiPayload.code.status.ErrorStatus; +import com.finut.finut_server.apiPayload.exception.GeneralException; +import com.finut.finut_server.domain.attend.Attend; +import com.finut.finut_server.domain.attend.AttendRepository; +import com.finut.finut_server.domain.level.Level; +import com.finut.finut_server.domain.level.LevelRepository; +import com.finut.finut_server.domain.quiz.Quiz; +import com.finut.finut_server.domain.user.UserResponseDTO; import com.finut.finut_server.domain.user.Users; import com.finut.finut_server.domain.user.UsersRepository; +import com.google.api.services.oauth2.model.Userinfoplus; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; -import java.util.ArrayList; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; import java.util.Optional; @Service @@ -19,6 +30,14 @@ public class UsersService { @Autowired private UsersRepository usersRepository; + @Autowired + private AttendRepository attendRepository; + + @Autowired + private LevelRepository levelRepository; + + @Autowired + private GoogleAuthService googleAuthService; public void saveRefreshToken(String email, String refreshToken) { Optional optionalUser = usersRepository.findByEmail(email); @@ -30,5 +49,138 @@ public void saveRefreshToken(String email, String refreshToken) { throw new UsernameNotFoundException("User not found"); } } + + public UserResponseDTO.updateAttendance updateAttendDate(Long userId){ + String msg = "출석했습니다!"; + Users user = usersRepository.findById(userId) + .orElseThrow(() -> new GeneralException(ErrorStatus.MEMBER_NOT_FOUND)); + LocalDate today = LocalDate.now(); + String formattedDate = today.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")); + + Attend attend = attendRepository.findByUserIdAndAttendDate(userId, formattedDate).orElse(null); + + if(attend == null){ + // 오늘 처음 출석하는 경우 + attend = new Attend(); + attend.setUser(user); + attend.setAttendDate(formattedDate); + attendRepository.save(attend); + user.setTodaySalary(false); + user.setAttendCount(user.getAttendCount() + 1); + + // 처음 출석하는 경우만 연속 출석 일자 체크하는 변수에 값 추가 + user.setContinuousCount(user.getContinuousCount() + 1); + usersRepository.save(user); + + // 만약 연속 출석 5회인 경우, XP 올리기 + 연속 출석 횟수 0으로 초기화 + if(user.getContinuousCount() == 5){ + user.setXP(user.getXP() + 5); + user.setContinuousCount(0); + msg = "연속 5회 출석했습니다!"; + } + + // XP가 100이면 승진하기 + if(user.getXP() >= 100){ + user.setXP(user.getXP() - 100); + Long newLevelId = user.getLevel().getId() + 1L; // id에 1 증가 + + // 증가된 id를 가진 Level 엔티티를 데이터베이스에서 조회하여 user에 설정 + Level newLevel = levelRepository.findById(newLevelId) + .orElseThrow(() -> new IllegalArgumentException("존재하지 않는 레벨입니다.")); + user.setLevel(newLevel); // user에 새로운 Level 설정 + msg = "승진했습니다!"; + } + + usersRepository.save(user); + } + else{ + // 처음 출석하는 경우가 아니라면 + if(user.isTodaySalary()) { + // 오늘 이미 월급을 받았다면 + msg = "오늘은 이미 월급을 받았습니다!"; + } + else { + user.setAttendCount(user.getAttendCount() + 1); + usersRepository.save(user); + + if(user.getAttendCount() == 5){ + // attendCount가 5가 되었다면 + user.setTodaySalary(true); + user.setAttendCount(0); + + Level level = levelRepository.findById(user.getLevel().getId()) + .orElseThrow(() -> new IllegalArgumentException("존재하지 않는 레벨입니다."));; + user.setMoney(user.getMoney() + level.getSalary()); + + usersRepository.save(user); + msg = "월급을 받았습니다!"; + } + } + } + + UserResponseDTO.updateAttendance updateAttendance = UserResponseDTO.updateAttendance.builder() + .userId(userId) + .message(msg) + .build(); + + return updateAttendance; + } + + public UserResponseDTO.viewUserInfo viewUserInfo(Long userId) { + Users user = usersRepository.findById(userId) + .orElseThrow(() -> new GeneralException(ErrorStatus.MEMBER_NOT_FOUND)); + UserResponseDTO.viewUserInfo viewUserInfo = UserResponseDTO.viewUserInfo.builder() + .userId(userId) + .name(user.getName()) + .money(user.getMoney()) + .picture(user.getPicture()) + .xp(user.getXP()) + .levelName(user.getLevel().getLevelName().getKoreanName()) + .build(); + + return viewUserInfo; + } + + public Users getUserIdByEmail(String email) { + return usersRepository.findByEmail(email) + .orElseThrow(() -> new RuntimeException("User not found with email: " + email)); + } + + public UserResponseDTO.checkUserXP updateDiffLevelCnt(Long userId) { + Users user = usersRepository.findById(userId) + .orElseThrow(() -> new RuntimeException("User not found with id: " + userId)); + + user.setDiffQuizCount(user.getDiffQuizCount() + 1); // diffQuizCnt 증가 + user.setXP(user.getXP() + 25); // XP 증가 + usersRepository.save(user); // 변경사항 저장 + + return new UserResponseDTO.checkUserXP(user.getId(), user.getName(), user.getMoney(), user.getXP(), user.getLevel()); + } + + public Users getUserIdByToken(HttpServletRequest request, HttpServletResponse response) { + // Authorization 헤더에서 Access Token을 가져옵니다. + String header = request.getHeader("Authorization"); + Users user; + Optional quiz; + + System.out.println(header); + if (header != null && header.startsWith("Bearer ")) + { + String accessToken = header.substring(7); // "Bearer " 제거 + try { + // Access Token을 이용해 사용자 정보를 조회합니다. + Userinfoplus userInfo = googleAuthService.getUserInfo(accessToken); + user = getUserIdByEmail(userInfo.getEmail()); + return user; + + } catch (Exception e) { + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + return null; + } + } else { + response.setStatus(HttpServletResponse.SC_BAD_REQUEST); + return null; + } + } } diff --git a/src/main/resources/difficulty.sql b/src/main/resources/difficulty.sql new file mode 100644 index 0000000..225e244 --- /dev/null +++ b/src/main/resources/difficulty.sql @@ -0,0 +1,7 @@ +use finut; + +INSERT INTO difficulty (difficulty, diff_quiz_cnt) VALUES ('MI', 3); +INSERT INTO difficulty (difficulty, diff_quiz_cnt) VALUES ('HI', 3); +INSERT INTO difficulty (difficulty, diff_quiz_cnt) VALUES ('LO', 3); + +select * from difficulty; \ No newline at end of file diff --git a/src/main/resources/level.sql b/src/main/resources/level.sql new file mode 100644 index 0000000..151184e --- /dev/null +++ b/src/main/resources/level.sql @@ -0,0 +1,17 @@ +/* +-- Query: SELECT * FROM finut.level +LIMIT 0, 5000 + +-- Date: 2024-11-06 01:59 +*/ +Use finut; + +INSERT INTO level (`id`, `level_name`, `salary`, `level_quiz_cnt`) VALUES (1, 'PARTTIMEJOB', 400000, 5); +INSERT INTO level (`id`, `level_name`, `salary`, `level_quiz_cnt`) VALUES (2, 'INTERN', 1000000, 5); +INSERT INTO level (`id`, `level_name`, `salary`, `level_quiz_cnt`) VALUES (3, 'STAFF', 2000000, 5); +INSERT INTO level (`id`, `level_name`, `salary`, `level_quiz_cnt`) VALUES (4, 'ASSOCIATEMANAGER', 4000000, 5); +INSERT INTO level (`id`, `level_name`, `salary`, `level_quiz_cnt`) VALUES (5, 'MANAGER', 6000000, 5); +INSERT INTO level (`id`, `level_name`, `salary`, `level_quiz_cnt`) VALUES (6, 'SENIORMANAGER', 8000000, 5); +INSERT INTO level (`id`, `level_name`, `salary`, `level_quiz_cnt`) VALUES (7, 'DIRECTOR', 10000000, 5); + +select * from level; diff --git a/src/main/resources/quest.sql b/src/main/resources/quest.sql new file mode 100644 index 0000000..575d5bd --- /dev/null +++ b/src/main/resources/quest.sql @@ -0,0 +1,10 @@ +use finut; + +INSERT INTO quest (next_level) VALUES ('INTERN'); +INSERT INTO quest (next_level) VALUES ('STAFF'); +INSERT INTO quest (next_level) VALUES ('ASSOCIATEMANAGER'); +INSERT INTO quest (next_level) VALUES ('MANAGER'); +INSERT INTO quest (next_level) VALUES ('SENIORMANAGER'); +INSERT INTO quest (next_level) VALUES ('DIRECTOR'); + +select * from quest; \ No newline at end of file diff --git a/src/main/resources/quest_quiz.sql b/src/main/resources/quest_quiz.sql new file mode 100644 index 0000000..199411d --- /dev/null +++ b/src/main/resources/quest_quiz.sql @@ -0,0 +1,36 @@ +use finut; + +INSERT INTO quest_quiz (quest_id, question, option1, option2, option3, correct_option, description) VALUES (1,'근로계약서를 작성하는 가장 중요한 이유는 무엇인가요?','회사가 요구하는 예의와 규범을 알기 위해','근로 조건과 관련된 불필요한 분쟁을 예방하기 위해','회사 정책에 대한 전반적인 이해를 돕기 위해',2,'근로계약서는 근로기준법에 따라 필수적으로 작성해야 하며, 이는 근로자와 회사 간의 권리와 의무를 명확히 정리해줍니다. 예를 들어, 임금이 제때 지급되지 않는 문제가 발생할 경우, 근로계약서에 명시된 내용에 따라 법적으로 해결할 수 있습니다. 계약서를 통해 근로자는 자신의 권리를 보호받을 수 있으며, 이를 통해 회사와의 불필요한 갈등을 줄일 수 있습니다.'); +INSERT INTO quest_quiz (quest_id, question, option1, option2, option3, correct_option, description) VALUES (1,'근로계약서에 반드시 포함되어야 하는 항목이 아닌 것은 무엇인가요?','임금','근로시간','회사의 비전과 목표',3,'근로계약서에는 근로자가 받아야 할 기본적인 근로 조건이 포함되어야 하며, 대표적인 예로 임금, 근로시간, 휴가, 업무 내용 등이 있습니다. 반면, 회사의 비전과 목표는 근로계약서의 필수 항목이 아니며, 이는 주로 직원들이 회사의 방향성을 이해하도록 설명 자료나 교육 시간에 제공됩니다.'); +INSERT INTO quest_quiz (quest_id, question, option1, option2, option3, correct_option, description) VALUES (1,'근로계약서에 임금 지급일이 포함되어야 하는 이유는 무엇인가요','급여 관리에 대한 회사의 계획을 이해하기 위해','근로자가 급여 지급 일정을 명확히 인지하기 위해','임금 산정 방식을 안내하기 위해',2,'근로기준법 제36조에 따르면 임금은 일정한 지급일에 맞춰 지급되어야 합니다. 이를 명시하지 않으면 근로자는 임금이 언제 지급될지 모호해지고, 자칫 임금이 연체되는 경우도 발생할 수 있습니다. 예를 들어, 매월 10일에 급여가 지급되는 회사의 경우, 근로계약서에 이 날짜가 명시되어 있으면 근로자는 매달 임금이 언제 지급될지 확실히 알 수 있습니다. 이는 불필요한 불만과 법적 분쟁을 예방하는 데 도움이 됩니다.'); +INSERT INTO quest_quiz (quest_id, question, option1, option2, option3, correct_option, description) VALUES (1,'근로계약서에 포함해야 하는 필수 항목 중 하나는 무엇인가요?','근로자의 직무 수행에 필요한 자격 요건','근로 장소 및 업무의 내용','근로자의 업무 성과 평가 기준',2,'근로계약서에는 반드시 근로 장소와 담당 업무가 명시되어야 합니다. 이는 근로기준법에 따라 근로자가 어떤 업무를 어디서 수행할지를 명확히 이해하고 고용 조건을 수용할 수 있도록 하는 중요한 요소입니다. 예를 들어, 근로자는 계약서에 특정 근로 장소가 명시되어 있으면, 추후 다른 지점으로의 근무를 요구받더라도 원래 근로 장소를 근거로 협의할 수 있습니다. 반면, 자격 요건이나 업무 성과 평가 기준은 직무 설명서나 별도의 평가 지표로 제시될 수 있지만, 근로계약서의 필수 항목은 아닙니다.'); +INSERT INTO quest_quiz (quest_id, question, option1, option2, option3, correct_option, description) VALUES (1,'근로계약서 작성 시 계약 종료 조건을 명확히 하는 이유는 무엇인가요?','계약 기간을 연장하거나 조정할 가능성을 열어두기 위해','근로자가 계약이 끝나는 시점에 대해 정확히 알기 위해','근로자가 필요한 경우 자유롭게 그만둘 수 있도록 하기 위해',2,'계약 기간이나 종료 조건은 근로자가 근로 관계가 언제 끝나는지 알 수 있도록 계약서에 명시되어야 합니다. 예를 들어, 1년 계약직 근로자인 경우, 근로계약서에 근무 시작일과 종료일이 명시되어 있어야 근로자는 언제까지 고용될지 알 수 있습니다. 이 조건이 명확하지 않으면 근로자는 고용 상태의 불안정성을 느끼게 되고, 회사 측에서도 불필요한 분쟁을 초래할 수 있습니다.'); +INSERT INTO quest_quiz (quest_id, question, option1, option2, option3, correct_option, description) VALUES (2,'대출계약서에서 대출이자를 정확히 확인해야 하는 이유는 무엇인가요?','대출 기간 중 이자율이 변동할 수 있기 때문에','대출 이자는 원금 상환 시 자동으로 사라지기 때문에','이자율이 계약서에 표기되지 않으면 고정 이자로 간주되기 때문에',1,'대출 계약서에는 이자율과 이자율의 변동 조건이 명확히 표기되어야 합니다. 변동금리 대출의 경우, 시장 상황에 따라 이자율이 달라질 수 있어 이를 확인하지 않으면 갑작스럽게 이자가 증가할 위험이 있습니다. 예를 들어, 2년 후 기준금리가 상승하면서 이자율이 올라가면 대출자의 상환 부담이 증가할 수 있습니다.'); +INSERT INTO quest_quiz (quest_id, question, option1, option2, option3, correct_option, description) VALUES (2,'대출계약서에 표기된 "연체 이자"를 정확히 파악해야 하는 이유는 무엇인가요?','연체 이자는 1년에 한 번만 부과되기 때문에','연체 시 추가적인 비용 발생을 줄이기 위해','연체 이자율이 낮을수록 상환 부담이 늘어나기 때문에',1,'대출 계약서에는 연체 이자율이 명시되어 있어야 합니다. 연체 이자는 상환 기일을 초과할 경우 부과되는 추가 비용이므로 이를 정확히 확인해야 불필요한 비용을 피할 수 있습니다. 예를 들어, 연체 이자율이 원래 이자율보다 높다면, 연체 시 이자 비용이 급격히 증가할 수 있어 상환에 어려움을 겪게 됩니다.'); +INSERT INTO quest_quiz (quest_id, question, option1, option2, option3, correct_option, description) VALUES (2,'보험계약서에서 "면책 사항"을 확인해야 하는 이유는 무엇인가요?','보험료가 계약서에 명시된 금액 이상으로 청구될 수 있기 때문에','특정 상황에서는 보상을 받지 못할 수 있기 때문에','보험 혜택이 갱신될 때마다 변동할 수 있기 때문에',2,'보험계약서에는 보상이 이루어지지 않는 면책 사항이 명시되어 있습니다. 예를 들어, 자동차 보험의 경우, 음주 운전 시 발생한 사고에 대해서는 보상이 이루어지지 않는다는 조항이 있을 수 있습니다. 이를 사전에 이해하지 않으면 예상치 못한 사고 발생 시 보상을 받지 못해 큰 경제적 부담을 질 수 있습니다.'); +INSERT INTO quest_quiz (quest_id, question, option1, option2, option3, correct_option, description) VALUES (2,'보험계약서에서 "보험금 지급 기준"을 확인하는 이유는 무엇인가요?','보험금이 지급되는 최소 금액을 알아야 하기 때문에','사고나 질병 발생 시 보험금 지급 여부와 지급액이 결정되기 때문에','보험 가입자가 언제든지 계약을 취소할 수 있기 때문에',2,'보험계약서에 명시된 보험금 지급 기준을 통해, 특정 사고나 질병에 대해 보험금이 지급되는 조건과 지급 금액이 결정됩니다. 예를 들어, 생명보험의 경우 사망 시 보장금이 지급되지만, 사망 원인이 면책 사유에 해당되면 지급이 거부될 수 있습니다. 이를 명확히 파악하지 않으면 필요할 때 보험 혜택을 받지 못하는 상황이 발생할 수 있습니다.'); +INSERT INTO quest_quiz (quest_id, question, option1, option2, option3, correct_option, description) VALUES (2,'보험계약서에서 "해지 환급금" 조항을 확인해야 하는 이유는 무엇인가요?','계약 해지 시 일부 환급금이 지급될 수 있기 때문에','환급금이 있으면 보험료가 낮아지기 때문에','환급금을 받으면 보험이 자동으로 갱신되기 때문에',1,'보험계약서에 명시된 해지 환급금은 계약 해지 시 보험사가 가입자에게 지급하는 환급금입니다. 보험 기간 중간에 해지하게 되면 일정 비율의 보험료가 환급될 수 있습니다. 예를 들어, 장기 보험에 가입했다가 중도 해지할 경우, 해지 환급금이 얼마인지 정확히 알아야 손해를 최소화할 수 있습니다.'); +INSERT INTO quest_quiz (quest_id, question, option1, option2, option3, correct_option, description) VALUES (3,'임대차계약서에서 계약 기간을 명확히 설정해야 하는 이유는 무엇인가요?','임대인은 계약 기간 중 임차인을 임의로 퇴거시킬 수 있기 때문에','임차인은 계약 기간 중 임대료를 임의로 조정할 수 있기 때문에','계약 종료 후 갱신 여부나 임대 조건이 자동으로 변경될 수 있기 때문에',3,'임대차계약서에서 계약 기간을 명확히 설정하면 계약 종료 시점에 갱신 여부를 결정할 수 있으며, 임대료나 조건에 대한 협상이 가능합니다. 예를 들어, 계약 기간이 명확하지 않으면 임대인이 계약을 갱신하면서 임대료를 인상할 때 임차인에게 불리하게 작용할 수 있습니다.'); +INSERT INTO quest_quiz (quest_id, question, option1, option2, option3, correct_option, description) VALUES (3,'임대차계약서에서 보증금 반환에 대한 조항을 꼼꼼히 확인해야 하는 이유는 무엇인가요?','임대인은 보증금을 임의로 사용할 수 있기 때문에','임대인은 임차인 요청 시 보증금을 일부만 반환할 수 있기 때문에','계약 종료 시 보증금 반환 시점이나 조건이 명확하지 않으면 분쟁이 발생할 수 있기 때문에',3,'임대차계약서에는 보증금 반환 조건이 명시되어 있어야 합니다. 예를 들어, 계약이 종료된 후 임차인이 원상복구를 마친 상태에서 보증금을 반환받지 못하는 경우가 발생할 수 있습니다. 따라서 반환 시점과 조건을 명확히 기재해야 임대인과 임차인 간의 분쟁을 줄일 수 있습니다.'); +INSERT INTO quest_quiz (quest_id, question, option1, option2, option3, correct_option, description) VALUES (3,'보험계약서에서 "면책 기간"을 확인해야 하는 이유는 무엇인가요?','면책 기간 동안 보험금을 청구할 수 없기 때문에','면책 기간이 지나면 보험료가 인상되기 때문에','면책 기간 동안에는 보상 금액이 축소되기 때문에',1,'면책 기간은 보험 가입 후 일정 기간 동안 보험금 청구가 불가능한 기간을 의미합니다. 예를 들어, 암 보험의 경우 가입 후 초기 면책 기간 동안에는 암 진단을 받아도 보험금을 청구할 수 없습니다. 이를 미리 인지하지 못하면 예상치 못한 상황에서 보상을 받지 못할 수 있습니다.'); +INSERT INTO quest_quiz (quest_id, question, option1, option2, option3, correct_option, description) VALUES (3,'보험계약서의 "감액 지급" 조항이 적용될 수 있는 상황은 무엇인가요?','보험 가입 기간 중 추가 보험료를 납부하지 않았을 때','사고 원인이 면책 사유에 해당하지 않는 경우','보험 사고 발생 시점에 특정 조건이 충족되지 않았을 때',3,'감액 지급은 보험 사고가 발생했지만 계약에서 요구하는 특정 조건을 완전히 충족하지 못한 경우 보험금이 감액되어 지급되는 조항입니다. 예를 들어, 특정 질병 진단 시 병원 입원 일수가 기준에 미치지 못하면 보험금이 줄어들 수 있습니다. 이를 숙지하지 않으면 예상보다 적은 보험금을 받게 될 수 있습니다.'); +INSERT INTO quest_quiz (quest_id, question, option1, option2, option3, correct_option, description) VALUES (3,'"소멸 시효"에 따라 보험금 청구를 할 수 없는 경우가 발생할 수 있습니다. 소멸 시효가 중요한 이유는 무엇인가요?','소멸 시효가 지나면 보험계약 자체가 해지되기 때문에','소멸 시효가 지나면 보험료 납부가 중단되기 때문에','소멸 시효가 지나면 보험금을 청구할 권리가 소멸되기 때문에',3,'보험금 청구에는 법적으로 소멸 시효가 적용됩니다. 예를 들어, 생명보험의 경우 사고 발생일로부터 3년 이내에 보험금을 청구해야 하며, 이를 넘기면 보험금을 받을 권리가 사라집니다. 이를 미리 인지하고 있어야 시효가 지나 보험금을 청구하지 못하는 불이익을 방지할 수 있습니다.'); +INSERT INTO quest_quiz (quest_id, question, option1, option2, option3, correct_option, description) VALUES (4,'펀드 투자 계약서에서 "투자 원금 보장 여부"를 확인하는 이유는 무엇인가요?','펀드는 원금 보장이 기본적으로 포함되어 있기 때문에','일부 펀드는 원금 보장이 없을 수 있기 때문에','원금 보장이 없으면 투자 수익률이 자동으로 높아지기 때문에',2,'대부분의 펀드 상품은 원금을 보장하지 않으며, 이는 투자자에게 중요한 리스크 요소입니다. 예를 들어, 주식형 펀드는 주식 시장의 변동에 따라 원금 손실이 발생할 수 있으므로, 투자자는 계약서에서 원금 보장 여부를 반드시 확인해야 합니다.'); +INSERT INTO quest_quiz (quest_id, question, option1, option2, option3, correct_option, description) VALUES (4,'금융 투자 계약서에서 "수수료 및 비용" 항목을 확인해야 하는 이유는 무엇인가요?','수수료가 높으면 이익이 커지기 때문에','일부 펀드는 수수료를 청구하지 않기 때문에','수수료와 기타 비용이 투자 수익률에 큰 영향을 줄 수 있기 때문에',3,'펀드 및 주식 투자 시 발생하는 운용 수수료, 매매 수수료 등이 투자 수익률에 영향을 미칩니다. 예를 들어, 수익이 낮은 상황에서도 수수료는 정해진 비율로 차감되므로, 이를 미리 파악하지 않으면 기대 수익보다 낮은 수익을 얻게 될 수 있습니다.'); +INSERT INTO quest_quiz (quest_id, question, option1, option2, option3, correct_option, description) VALUES (4,'주식 투자 계약서에서 "매도 제한 조건"을 확인해야 하는 이유는 무엇인가요?','특정 주식은 일정 기간 동안 매도할 수 없을 수 있기 때문에','매도 제한 조건이 있으면 배당 수익이 보장되기 때문에','매도 제한이 있으면 매도 시점에 자동으로 수수료가 면제되기 때문에',1,'일부 주식은 상장 직후 일정 기간 동안 매도가 제한될 수 있으며, 이는 투자자의 유동성을 제한하는 요소가 됩니다. 예를 들어, IPO(기업 공개) 주식의 경우 상장 후 일정 기간 동안 대주주가 매도하지 못하도록 제한하는 ''락업(lock-up)'' 조항이 있을 수 있습니다. 이를 이해하지 못하면 예상치 못한 상황에서 주식을 처분하지 못하는 경우가 발생할 수 있습니다.'); +INSERT INTO quest_quiz (quest_id, question, option1, option2, option3, correct_option, description) VALUES (4,'청약 계약서에서 "청약 철회 가능 기간"을 반드시 확인해야 하는 이유는 무엇인가요?','청약 철회 가능 기간이 지나면 청약을 철회할 수 없기 때문에','청약 철회 가능 기간이 지나면 위약금이 발생하기 때문에','청약 철회 기간이 지나면 청약 자격을 잃게 되기 때문에',1,'청약 철회 가능 기간이 지나면 청약을 철회하고자 하더라도 법적으로 불가능하게 됩니다. 예를 들어, 주택 청약의 경우 일반적으로 계약 체결 후 3일 이내에 철회할 수 있으며, 이 기간이 지나면 위약금 없이 청약을 취소하는 것이 어려울 수 있습니다.'); +INSERT INTO quest_quiz (quest_id, question, option1, option2, option3, correct_option, description) VALUES (4,'청약 계약서에서 "청약 신청금"의 환불 조건을 확인하는 이유는 무엇인가요?','신청금은 청약 철회 시에도 환불되지 않기 때문에','환불 조건에 따라 신청금을 전액 돌려받지 못할 수 있기 때문에','환불이 되면 자동으로 계약이 해지되기 때문에',2,'청약 철회 시, 신청금 환불 조건을 미리 확인하지 않으면 일부만 환불되거나 환불이 불가능한 경우가 있을 수 있습니다. 예를 들어, 청약 신청 후 계약을 철회할 경우, 일부 청약금은 위약금으로 차감되거나, 환불 조건에 따라 반환 금액이 제한될 수 있습니다.'); +INSERT INTO quest_quiz (quest_id, question, option1, option2, option3, correct_option, description) VALUES (5,'자동차 매매 계약서에서 차량 상태 점검 내역을 확인해야 하는 중요한 이유는 무엇인가요?','차량 상태에 따라 계약 후 수리비가 발생할 수 있기 때문에','차량 상태가 양호하면 계약 취소가 어려워지기 때문에','점검 내역에 따라 차량 보험료가 자동으로 조정되기 때문에',1,'자동차 매매 계약서에는 차량 상태가 명확히 기재되어 있어야 하며, 이를 통해 계약 후 수리비 부담을 줄일 수 있습니다. 예를 들어, 사고 차량을 사게 되는 경우, 수리비가 예기치 않게 발생할 수 있으므로, 구매 전에 점검 내역을 철저히 확인하여 예상치 못한 비용 부담을 피해야 합니다.'); +INSERT INTO quest_quiz (quest_id, question, option1, option2, option3, correct_option, description) VALUES (5,'자동차 매매 계약서에서 "소유권 이전일"을 명확히 설정해야 하는 이유는 무엇인가요?','소유권 이전일이 없으면 자동차 등록이 자동으로 취소되기 때문에','소유권 이전일 이후부터 차량에 대한 책임이 구매자에게 있기 때문에','소유권 이전일이 늦어지면 자동차 보험 가입이 제한되기 때문에',2,'자동차 매매 계약서에 소유권 이전일을 명확히 기재해야 하는 이유는, 소유권 이전일 이후부터 차량에 대한 모든 법적, 금전적 책임이 구매자에게 전가되기 때문입니다. 예를 들어, 소유권 이전일 이후 발생한 사고나 벌금은 구매자가 책임을 져야 하므로, 이전일을 정확히 정해 책임 소재를 분명히 해야 합니다.'); +INSERT INTO quest_quiz (quest_id, question, option1, option2, option3, correct_option, description) VALUES (5,'자동차 매매 계약서에서 차량 등록세와 관련된 사항을 확인하는 이유는 무엇인가요?','등록세는 이전 소유자가 자동으로 납부하기 때문','등록세는 구매자가 납부해야 할 추가 비용이기 때문','등록세를 납부하지 않으면 차량 번호판이 발급되지 않기 때문',2,'자동차 등록세는 일반적으로 구매자가 부담하는 비용이며, 이는 계약 후 차량 소유권을 이전하기 위해 필수적으로 납부해야 합니다. 만약 계약 단계에서 이를 인지하지 못하면 예상하지 못한 추가 비용으로 인해 재정적인 부담이 생길 수 있습니다.'); +INSERT INTO quest_quiz (quest_id, question, option1, option2, option3, correct_option, description) VALUES (5,'펀드 투자 계약서에서 "투자 위험 등급"을 확인해야 하는 이유는 무엇인가요?','위험 등급이 높을수록 원금 보장이 되기 때문에','위험 등급에 따라 예상 수익률이 달라지기 때문에','위험 등급에 따라 추가 비용이 발생할 수 있기 때문에',2,'펀드 상품은 위험 등급에 따라 투자 성향이 달라지며, 이는 수익률에도 큰 영향을 미칩니다. 예를 들어, 고위험 등급의 펀드는 주식이나 파생상품에 많이 투자하여 수익이 높을 수 있지만, 반대로 원금 손실의 위험도 큽니다. 따라서 자신의 투자 성향에 맞는 위험 등급을 선택하는 것이 중요합니다.'); +INSERT INTO quest_quiz (quest_id, question, option1, option2, option3, correct_option, description) VALUES (5,'주식 투자 계약서에서 "배당금 지급 조건"을 확인해야 하는 이유는 무엇인가요?','배당금 지급 조건이 있으면 주식 매도 제한이 생기기 때문에','특정 조건을 충족해야만 배당금을 받을 수 있기 때문에','배당금 지급 조건에 따라 배당 수익이 자동으로 재투자되기 때문에',2,'주식 배당금은 일정 조건을 충족해야 받을 수 있으며, 이는 계약서에 명시되어야 합니다. 예를 들어, 특정 주식을 배당 기준일에 보유하고 있어야 배당금을 받을 수 있는 경우가 있습니다. 배당 조건을 이해하지 못하면 예상했던 배당 수익을 받지 못할 수 있습니다.'); +INSERT INTO quest_quiz (quest_id, question, option1, option2, option3, correct_option, description) VALUES (6,'펀드 투자 계약서에서 "성과보수" 항목을 확인해야 하는 이유는 무엇인가요?','펀드 성과가 좋을 때 추가적인 보수를 투자자가 지급해야 할 수 있기 때문에','성과보수는 펀드 운용사에게만 적용되는 항목이기 때문에','성과보수 조항이 있으면 투자자가 부담하는 기본 수수료가 사라지기 때문에',1,'일부 펀드는 성과보수를 부과하는데, 이는 펀드가 일정 기준 이상의 수익률을 달성할 경우 추가적으로 부과되는 수수료입니다. 예를 들어, 목표 수익률이 10%로 설정된 펀드가 15%의 성과를 거두면, 5% 초과 성과에 대해 일정 비율의 성과보수가 부과될 수 있습니다. 성과보수 조건을 이해하지 못하면 예상 수익보다 낮은 수익을 실현하게 될 수 있습니다.'); +INSERT INTO quest_quiz (quest_id, question, option1, option2, option3, correct_option, description) VALUES (6,'주식 투자 계약서에서 "콜옵션" 조항을 포함하는 경우, 이를 주의 깊게 확인해야 하는 이유는 무엇인가요?','콜옵션이 발동되면 주식을 일정 가격에 매도해야 할 수 있기 때문에','콜옵션은 주식 가격이 떨어질 때 자동으로 발동되기 때문에','콜옵션이 발동되면 배당금 지급 조건이 취소되기 때문에',1,'콜옵션 조항이 있는 경우, 특정 조건 하에서 투자자가 보유한 주식을 사전에 정해진 가격으로 매도해야 할 수 있습니다. 예를 들어, 기업이 투자자에게 주식을 콜옵션을 통해 되사들일 수 있는 권리를 가지면, 주가 상승 시에도 미리 정해진 가격에 매도해야 하는 상황이 발생할 수 있습니다. 이러한 조항을 이해하지 못하면 예상보다 낮은 가격에 주식을 팔아야 하는 상황이 올 수 있습니다.'); +INSERT INTO quest_quiz (quest_id, question, option1, option2, option3, correct_option, description) VALUES (6,'펀드 투자 계약서에서 "환매 수수료"를 확인해야 하는 이유는 무엇인가요?','환매 시점에 따라 수수료가 증가하기 때문에','환매 수수료는 투자자 보호 차원에서 무료로 제공되기 때문에','환매 수수료는 투자 시점과 환매 시점의 시장 상황에 따라 달라질 수 있기 때문에',1,'펀드의 환매 수수료는 펀드를 매도하는 시점에 따라 차등적으로 적용되며, 일반적으로 투자 기간이 짧을수록 수수료가 높아지는 경향이 있습니다. 예를 들어, 6개월 내 환매 시 2%의 수수료가 부과되지만, 1년 이상 보유하면 수수료가 없어지는 식으로 설정될 수 있습니다. 이를 잘못 이해하면 단기 투자에서 예상보다 높은 비용을 부담하게 될 수 있습니다.'); +INSERT INTO quest_quiz (quest_id, question, option1, option2, option3, correct_option, description) VALUES (6,'주식 투자 계약서에서 "우선 매수권" 조항을 확인해야 하는 이유는 무엇인가요?','우선 매수권 조항이 있으면 투자자가 원하는 시점에 추가 주식을 매수할 수 있기 때문에','기존 주주가 특정 주식 매도 시 우선적으로 해당 주식을 매수할 수 있기 때문에','우선 매수권 조항이 있는 경우, 추가 투자 시 발생하는 세금이 면제되기 때문에',2,'우선 매수권은 기존 주주가 보유한 주식을 제3자에게 매도하기 전에 다른 주주에게 먼저 매수할 기회를 제공하는 권리입니다. 예를 들어, 특정 주식의 대주주가 지분을 처분할 때, 기존 주주는 우선 매수권을 행사하여 지분율을 유지하거나 지분율을 높일 수 있습니다. 이를 이해하지 못하면 우선 매수권을 행사할 기회를 놓치거나 의도치 않게 주식 지분율이 낮아질 수 있습니다.'); +INSERT INTO quest_quiz (quest_id, question, option1, option2, option3, correct_option, description) VALUES (6,'펀드 계약서에 명시된 "헤지 전략"을 파악해야 하는 이유는 무엇인가요?','헤지 전략을 사용하면 펀드의 위험을 줄이기 위해 추가 비용이 발생할 수 있기 때문에','헤지 전략을 사용할 경우 일정 금액 이상의 원금이 보장되기 때문에','헤지 전략은 펀드가 특정 주식에 집중 투자하는 방식을 의미하기 때문에',1,'헤지 전략은 위험을 줄이기 위해 파생상품을 이용하거나 반대 포지션을 취하는 전략으로, 이러한 전략에는 추가 비용이 발생할 수 있습니다. 예를 들어, 해외 주식 펀드가 환율 변동 위험을 줄이기 위해 선물 계약을 사용하면 헤지 비용이 발생하며, 이는 전체 수익에 영향을 미칠 수 있습니다. 헤지 전략을 이해하지 못하면 예상보다 낮은 순이익을 얻게 될 수 있습니다.'); + + + +select * from quest_quiz; \ No newline at end of file diff --git a/src/main/resources/quiz.sql b/src/main/resources/quiz.sql new file mode 100644 index 0000000..bb7b36f --- /dev/null +++ b/src/main/resources/quiz.sql @@ -0,0 +1,28 @@ +use finut; + +insert into finut.quiz(difficulty, question, answer,description) values ("MI", "1금융권은 정부에서 만들어진 ‘상호신용금고법’으로부터 나타난 금융회사가 아니라는 것이다.", "TRUE", "은행 외에는 고금리의 사금융밖에 선택지가 없던 1970 년대, 정부에서 ‘상호신용금고법’을 만들어 사금융을 양성화하기 시작했습니다. 이때부터 나타난 회사들은 특정 그룹이나 분야에 전문성을 가진 회사였습니다. 이렇게 새로운 금융회사들이 만들어지던 시기에, 은행과 은행이 아닌 금융회사를 구별해서 부르기 시작했습니다. 예전부터 있었던 은행을 1 금융권으로, ‘은행이 아닌’ 금융회사를 2금융권으로 부르게 된 거죠. 1금융권인 은행과 2금융권에 속해있는 저축은행의 경우, 예금과 대출을 해준다는 측면에서 비슷한 기능을 하기 때문에 소비자 입장에서는 같은 선상에 두고 비교해볼 만합니다.안정성이 높지만 수익률(이자율)이 낮은 1금융권 예금을 택할지, 대출금리가 높지만 대출받기가 더 쉬운 2금융권 대출을 택할지 비교해보는 거예요."); +insert into finut.quiz(difficulty, question, answer, description) values ("HI", "추가경정예산안은 이미 1년 예산을 정해두었는데, 예산을 세운 뒤에 생긴 일 때문에 금액을 더 보태서 고친다는 걸 뜻하는 것이다.", "TRUE", "추경은 ‘추가경정예산’의 줄임말이고, 경정(更正)은 ‘바르게 고친다’라는 뜻이에요. 추가경정예산은 이미 1년 예산을 정해두었는데, 예산을 세운 뒤에 생긴 일 때문에 금액을 더 보태서 고친다는 걸 뜻하죠. 연초에 세운 계획보다 돈을 더 쓰겠다는 건데요. 추경안은 자연재해나 큰 사고가 나서 인프라와 사회를 복구해야 할 때 나옵니다. 그래서 지난 추경안 규모를 보면 굵직한 경제적 충격들을 읽을 수 있어요. 추경 규모가 10조 원을 넘는 곳(빨간색 그래프)은 리먼브라더스 사태가 촉발한 세계 금융위기, 브렉시트 등 우리나라뿐만 아니라 세계적인 경제 위기가 있었던 시기입니다. 그중에서도 2020년과 2021년의 추경 횟수와 금액은 ‘역대급’이니, 코로나19가 세계적으로 얼마나 커다란 경제적 충격을 가져왔는지 알 수 있어요. 사실 추경 자체가 엄청나게 특별한 일은 아닙니다. 우리나라뿐만 아니라 전 세계 국가 대부분이 매년 추경을 실시합니다. 돈 쓸 일이 너무 많다 보니 1년 동안 얼마가 필요할지 정확하게 예측하기가 어렵거든요. 돌발사태가 발생하기도 하고요. 국가가 사업을 집행하는 데는 국민의 세금이 사용됩니다. 그래서 국민의 대리인인 국회의원과 정당이 추가예산이 필요하다는 행정부를 상대로 견제하기 시작합니다. ‘추경을 하느냐, 마느냐’보다는 추경 횟수와 액수 등을 두고 다투죠. 일명 ‘감액심사’라고 해요. 정치적 이해관계에 따라 여당이 행정부 편을 많이 들어주기도 하는데요. 보통 매년 추경을 하기 때문에 ‘필요하다’라는 쪽으로 결론이 나긴 합니다. 하지만 모든 추경안이 실행되는 건 아니에요. 예를 들어 어떤 정당이 추경을 해야 할 만큼 꼭 필요한 사업이라고 생각하지 않고, 이들의 주장이 국회 다수결로 지지를 받는다면 추경안은 실행되지 않습니다."); +insert into finut.quiz(difficulty, question, answer, description) values ("HI", "2차전지는 충전과 방전을 통해 사용이 가능한 전지가 아니에요.", "FALSE", "2차전지는 충전과 방전을 통해 여러 번 사용이 가능한 전지예요. 대표적인 2 차전지로는 휴대폰 배터리가 있습니다. 종류는 납 축전지, 니켈카드뮴 전지, 니켈수소 전지, 리튬이온 전지 등이 있어요."); +insert into finut.quiz(difficulty, question, answer, description) values ("MI", "52주 신고가는 특정 주식이 지난 1년 동안 기록한 가장 높은 주가입니다.", "TRUE", "52주 신고가는 특정 주식이 지난 52주(1년) 동안 기록한 가장 높은 주가를 말해요."); +insert into finut.quiz(difficulty, question, answer, description) values ("LO", "52주 신저가는 특정 주식이 지난 52주(1년) 동안 기록한 가장 낮은 주가를 말합니다.", "TRUE", "52주 신저가는 특정 주식이 지난 52주(1년) 동안 기록한 가장 낮은 주가를 말해요."); +insert into finut.quiz(difficulty, question, answer, description) values ("HI", "bp(베이시스포인트)는 0.01%p의 움직임도 금융시장에 큰 영향을 미칩니다.", "TRUE", "기준금리 뉴스에 자주 등장하는 ‘bp’는 ‘basis point’의 약자예요. 금융에서 쓰이는 기본단위로, 금리나 수익률, 환율을 나타낼 때 사용합니다. 일반적으로 사용하는 백분율이 아닌 ‘만분율’ 단위의 움직임을 표현하는데요. 1bp는 0.01%p, 10bp는 0.1%p, 100bp는 1%p예요. 금융시장에서는 0.01%p의 움직임도 영향력이 커요. 그래서 0.01%p도 뉴스에 자주 언급됩니다. 백분율을 기준으로 ‘퍼센트포인트’라고 말하는 것보다 만분율을 기준으로 bp로 나타내는 것이 정보전달에 더 효율적이라서 사용한다는 얘기가 있어요."); +insert into finut.quiz(difficulty, question, answer, description) values ("MI", "DSR은 내 소득 대비 금융부채 원리금 상환비율로, 40%를 넘는 경우에는 대출을 해주지 않는다.", "TRUE", "DSR(Debt Service Ratio)은 내 소득 대비 금융부채 원리금 상환비율이에요. DSR 40%라면 내 연 소득이 1억 원일 때, 1년 동안의 원금과 이자 상환비율이 4천만 원을 넘지 않는 정도까지 대출을 해준다는 뜻이에요."); +insert into finut.quiz(difficulty, question, answer, description) values ("LO", "DTI(총부채상환비율)는 연 소득 대비 금융비용 부담률을 나타내는 지표입니다.", "TRUE", "DTI(Debt to Income : 총부채상환비율)는 연 소득 대비 금융비용 부담률을 의미합니다. 소득과 비교한 대출금 수준을 판단하는 지표예요. DTI는 ‘나의 연소 득’에서 ‘주택담보대출의 원금 상환과 이자, 다른 대출의 이자로 나가는 금액’이 차지하는 비중으로 구합니다. 내가 가진 모든 대출의 원리금 상환금액을 합쳐 따지는 DSR보다는 유한 기준입니다."); +insert into finut.quiz(difficulty, question, answer, description) values ("HI", "ESG는 환경, 사회, CORPORATE GOVERNANCE에 대한 고려를 의미합니다.", "TRUE", "ESG는 Environmental(환경), Social(사회), Governance(지배구조)의 첫 글자를 조합한 단어로 기업의 친환경 경영, 사회적 책임, 투명한 지배구조 등을 의미합니다."); +insert into finut.quiz(difficulty, question, answer, description) values ("MI", "ETF는 특정 주식에 대한 투자 상품이 아니다.", "TRUE", "ETF(Exchange Traded Fund)는 말 그대로 인덱스펀드를 거래소에 상장시켜 투자자들이 주식처럼 편리하게 거래할 수 있도록 만든 상품입니다."); +insert into finut.quiz(difficulty, question, answer, description) values ("LO", "G7은 매년 정기적으로 국제 사회 현안에 대한 회의를 개최하는 7개국을 의미합니다.", "TRUE", "G7. Group of Seven Summit. 주요 7개국 정상회담의 약자입니다. 미국, 캐나다, 영국, 독일, 프랑스, 이탈리아, 일본 7개국은 매년 정기적으로 국제 사회 현안에 대한 회의를 개최해요. 이 회담뿐만 아니라 이 회담에 참여하는 7개국을 G7이라고 줄여 부르기도 해요."); +insert into finut.quiz(difficulty, question, answer, description) values ("HI", "GNI는 일정 기간 내 ‘우리나라 국적을 가진 사람’이 벌어들인 모든 소득을 더한 것이다.", "TRUE", "GNI는 Gross National Income의 약자로, ‘국민총소득’입니다. GNI는 일정 기간 내 ‘우리나라 국적을 가진 사람’이 벌어들인 모든 소득을 더한 거예요. 우리나라에 거주하고 있는 외국인의 소득은 제외하고, 외국에 거주하는 우리나라 국민의 소득을 포함해요. GNI를 구하는 가장 큰 이유는 국가가 아니라 국민들의 생활 수준을 알아보기 위해서예요. GNI와 유사한 지표로는 GDP가 있어요."); +insert into finut.quiz(difficulty, question, answer, description) values ("MI", "GVC 후방참여율은 해외에서 수입한 중간재를 이용해 상품을 생산하고, 이를 다시 수출하는 방식으로 진행되는 것이다.", "TRUE", "GVC 후방참여율은 해외에서 수입한 중간재를 이용해 상품을 생산하고, 이를 다시 수출하는 비율을 말해요. 이 수치가 높은 나라일수록 전 세계 자원이 거쳐가는 글로벌 생산기지라 할 수 있어요."); +insert into finut.quiz(difficulty, question, answer, description) values ("LO", "LTV는 주택담보대출의 대출가능금액을 산출할 때 사용되는 공식입니다.", "TRUE", "LTV는 주택담보대출비율(Loan to Value ratio)입니다. 공식은 ‘(대출한 금액) / (아파트 가치)*100’이에요. 즉, 총 아파트 가치에 비교한 대출금의 비중을 보여줘요."); +insert into finut.quiz(difficulty, question, answer, description) values ("HI", "M&A(인수합병)는 기업이 다른 기업의 경영권을 획득하고자 하는 인수와 다른 기업의 소유권을 획득하고자 하는 합병을 합한 개념입니다.", "TRUE", "M&A는 ‘Merger(합병)&Accquisition(인수)’의 약자로, 인수와 합병을 통해 회사 규모를 키우거나 시장에서 빠르게 경쟁력을 키우는 것이 목적이에요."); +insert into finut.quiz(difficulty, question, answer, description) values ("LO", "PMI ‘ISM 제조업 구매관리자지수’는 생산 관련 지수가 아니고, 경제동향지표입니다.", "TRUE", "PMI 지수는 미국의 공급관리협회에서 매달 조사하는 경기동향지표로, 제조업 실무자에게 업황을 묻는 방식으로 업계 전망을 직접적으로 파악할 수 있어요."); +insert into finut.quiz(difficulty, question, answer, description) values ("MI", "가처분소득은 내가 번 소득에서 세금이나 보험료 등 필수로 나가는 지출을 제외하고, 정부의 혜택 등을 더한 금액입니다.", "TRUE", "가처분소득은 내가 번 소득 중 소비와 저축으로 쓸 수 있는 돈을 뜻해요. 구체적으로는 내가 번 돈에서 세금이나 보험료 등 필수로 나가는 지출을 제외하고, 정부의 혜택 등을 더한 금액입니다."); +insert into finut.quiz(difficulty, question, answer, description) values ("HI", "감사보고서는 외부 감사인이 회사의 재무제표를 보고, 제대로 작성되었는지 확인하고 의견을 표명하는 보고서입니다.", "TRUE", "감사보고서는 외부 감사인이 회사의 재무제표를 보고, 제대로 작성되었는지 확인하고 의견을 표명하는 보고서입니다."); +insert into finut.quiz(difficulty, question, answer, description) values ("LO", "개인소비지출(PCE)은 개인이 소유한 재산을 사용하여 구입하는 모든 물품과 서비스를 의미합니다.", "TRUE", "개인소비지출(Personal Consumption Expenditures, PCE)은 가계가 재화와 서비스를 구매하는 데 사용하는 지출을 의미합니다."); +insert into finut.quiz(difficulty, question, answer, description) values ("MI", "거래대금은 주식이 사고 팔린 총 금액으로, 매매된 주식의 수량을 나타내는 거래량과 함께 보는 지표입니다.", "TRUE", "거래대금은 주식이 사고 팔린 총 금액입니다. 매매된 주식의 수량을 나타내는 거래량과 함께 보는 지표입니다."); +insert into finut.quiz(difficulty, question, answer, description) values ("HI", "경기순환사이클은 상승과 하락을 반복하는 현상을 의미하며, 크게 회복기-활황기- 후퇴기-침체기가 있다.", "TRUE", "경기가 일정 주기를 두고 상승과 하락을 반복하는 현상을 경기순환이라고 합니다."); +insert into finut.quiz(difficulty, question, answer, description) values ("MI", "경상수지는 일정 기간 우리나라와 외국 사이 상품·서비스의 수출입과 노동·자본에 지급된 대가처럼 경제에서 ‘생산을 담당하는 요소’의 흐름을 금액으로 계산한 장부입니다.", "TRUE", "경상수지는 일정 기간 우리나라와 외국 사이 상품·서비스의 수출입과 노동· 자본에 지급된 대가처럼 경제에서 ‘생산을 담당하는 요소’의 흐름을 금액으로 계산한 장부라고 할 수 있어요."); +insert into finut.quiz(difficulty, question, answer, description) values ("LO", "경착륙은 호황이 끝나고 자산 가치가 폭락하고 실업자가 급증하는 사태를 말합니다.", "TRUE", "경착륙은 활발하게 잘 나가던 경기가 얼어붙으면서 자산 가치가 폭락하고 실업자가 급증하는 사태를 말합니다. 연착륙과 대비되는 개념입니다."); + + +select * from quiz; \ No newline at end of file diff --git a/src/test/java/com/finut/finut_server/controller/InterestRateControllerTest.java b/src/test/java/com/finut/finut_server/controller/InterestRateControllerTest.java deleted file mode 100644 index e4f454a..0000000 --- a/src/test/java/com/finut/finut_server/controller/InterestRateControllerTest.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.finut.finut_server.controller; - -import com.finut.finut_server.service.InterestRateService; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; -import org.springframework.web.context.WebApplicationContext; - -import static org.mockito.Mockito.*; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; - -@SpringBootTest -public class InterestRateControllerTest { - - @Autowired - private WebApplicationContext context; - - private MockMvc mockMvc; - - @MockBean - private InterestRateService interestRateService; - - @BeforeEach - public void setUp() { - mockMvc = MockMvcBuilders.webAppContextSetup(context).build(); - } - - @Test - public void testGetInterestRates() throws Exception { - String expectedResponse = "3.5"; - - when(interestRateService.getInterestRatesToady()).thenReturn(expectedResponse); - - mockMvc.perform(get("/interest-rate/today")) - .andExpect(status().isOk()) - .andExpect(content().string(expectedResponse)); - - verify(interestRateService, times(1)).getInterestRatesToady(); - } - - - -}