From 6f15fa65a4121f0ebf93e5f7ff6ddc69d00cef07 Mon Sep 17 00:00:00 2001 From: miiiinju1 Date: Tue, 24 Oct 2023 14:47:13 +0900 Subject: [PATCH] =?UTF-8?q?:sparkles:=20[FEAT]=20=EB=B0=B1=EC=A4=80=20?= =?UTF-8?q?=EC=A0=9C=EC=B6=9C=20=EC=8B=9C=20solution-id=EB=B0=98=ED=99=98?= =?UTF-8?q?=ED=95=98=EA=B2=8C=20=EC=84=A4=EC=A0=95=20#501?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit close #501 --- .../controller/CodeSubmitController.java | 3 +- .../domain/codeSubmit/dto/SolutionIdDto.java | 12 +++++ .../service/BaekjoonSubmitService.java | 54 ++++++++++++++++--- .../exception/errorcode/SubmitErrorCode.java | 3 +- 4 files changed, 63 insertions(+), 9 deletions(-) create mode 100644 src/main/java/swm_nm/morandi/domain/codeSubmit/dto/SolutionIdDto.java diff --git a/src/main/java/swm_nm/morandi/domain/codeSubmit/controller/CodeSubmitController.java b/src/main/java/swm_nm/morandi/domain/codeSubmit/controller/CodeSubmitController.java index fbc8fb52..2b376ff3 100644 --- a/src/main/java/swm_nm/morandi/domain/codeSubmit/controller/CodeSubmitController.java +++ b/src/main/java/swm_nm/morandi/domain/codeSubmit/controller/CodeSubmitController.java @@ -6,6 +6,7 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import swm_nm.morandi.domain.codeSubmit.dto.BaekjoonUserDto; +import swm_nm.morandi.domain.codeSubmit.dto.SolutionIdDto; import swm_nm.morandi.domain.codeSubmit.dto.SubmitCodeDto; import swm_nm.morandi.domain.codeSubmit.service.BaekjoonSubmitService; @@ -21,7 +22,7 @@ public class CodeSubmitController { @PostMapping("/baekjoon") @Operation(summary = "문제 번호, 언어이름, 소스코드를 백준에 제출하는 컨트롤러 ", description = "사용자가 테스트 중 코드를 제출하는 경우 백준에 제출하는 컨트롤러입니다.") - public ResponseEntity submit(@RequestBody @Valid SubmitCodeDto submitCodeDto) { + public ResponseEntity submit(@RequestBody @Valid SubmitCodeDto submitCodeDto) { return submitService.submit(submitCodeDto); } diff --git a/src/main/java/swm_nm/morandi/domain/codeSubmit/dto/SolutionIdDto.java b/src/main/java/swm_nm/morandi/domain/codeSubmit/dto/SolutionIdDto.java new file mode 100644 index 00000000..12b8fbfb --- /dev/null +++ b/src/main/java/swm_nm/morandi/domain/codeSubmit/dto/SolutionIdDto.java @@ -0,0 +1,12 @@ +package swm_nm.morandi.domain.codeSubmit.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; + +@Setter +@Getter +@AllArgsConstructor +public class SolutionIdDto { + public String solutionId; +} diff --git a/src/main/java/swm_nm/morandi/domain/codeSubmit/service/BaekjoonSubmitService.java b/src/main/java/swm_nm/morandi/domain/codeSubmit/service/BaekjoonSubmitService.java index c873227f..ca89c360 100644 --- a/src/main/java/swm_nm/morandi/domain/codeSubmit/service/BaekjoonSubmitService.java +++ b/src/main/java/swm_nm/morandi/domain/codeSubmit/service/BaekjoonSubmitService.java @@ -4,6 +4,7 @@ import lombok.extern.slf4j.Slf4j; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.http.*; import org.springframework.stereotype.Service; @@ -13,8 +14,8 @@ import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.RestTemplate; import swm_nm.morandi.domain.codeSubmit.constants.CodeVisuabilityConstants; -import swm_nm.morandi.domain.codeSubmit.constants.SubmitConstants; import swm_nm.morandi.domain.codeSubmit.dto.BaekjoonUserDto; +import swm_nm.morandi.domain.codeSubmit.dto.SolutionIdDto; import swm_nm.morandi.domain.member.entity.Member; import swm_nm.morandi.domain.member.repository.MemberRepository; import swm_nm.morandi.global.exception.MorandiException; @@ -86,15 +87,16 @@ private void updateMemberInfo(Member member, String bojId){ } - public ResponseEntity submit(SubmitCodeDto submitCodeDto) { + public ResponseEntity submit(SubmitCodeDto submitCodeDto) { validateBojProblemId(submitCodeDto.getBojProblemId()); Long memberId = SecurityUtils.getCurrentMemberId(); String cookie = getCookieFromRedis(generateKey(memberId)); String CSRFKey = getCSRFKey(cookie, submitCodeDto.getBojProblemId()); String submitResult = sendSubmitRequest(cookie, CSRFKey, submitCodeDto); - - return processSubmitResult(submitResult); + String acmicpcUrl = String.format("https://www.acmicpc.net/submit/%s", submitCodeDto.getBojProblemId()); + HttpHeaders headers = createHeaders(cookie); + return processSubmitResult(submitResult,acmicpcUrl,headers); } private void validateBojProblemId(String bojProblemId) { try { @@ -168,10 +170,48 @@ private MultiValueMap createParameters(SubmitCodeDto submitCodeD parameters.add("csrf_key", CSRFKey); return parameters; } - private ResponseEntity processSubmitResult(String submitResult) { - if (submitResult.contains("status")) { - return new ResponseEntity<>(HttpStatus.OK); + private ResponseEntity processSubmitResult(String location,String acmicpcUrl,HttpHeaders headers){ + + if (location.contains("status")) { + if (!location.startsWith("http")) + location = URI.create(acmicpcUrl).resolve(location).toString(); + ResponseEntity redirectedResponse = restTemplate.exchange(location, HttpMethod.GET, new HttpEntity<>(headers), String.class); + + // HTML에서 solution-id를 추출합니다. + SolutionIdDto solutionId = extractSolutionIdFromHtml(redirectedResponse.getBody()); + + if (solutionId.getSolutionId() != null) { + System.out.println("Extracted Solution ID: " + solutionId); + return ResponseEntity.status(redirectedResponse.getStatusCode()).body(solutionId); + + } else { + throw new MorandiException(SubmitErrorCode.CANT_FIND_SOLUTION_ID); + } + + } throw new MorandiException(SubmitErrorCode.BAEKJOON_LOGIN_ERROR); } + private SolutionIdDto extractSolutionIdFromHtml(String html) { + // HTML을 파싱합니다. + Document doc = Jsoup.parse(html); + + // 테이블을 선택합니다. + Element table = doc.getElementById("status-table"); + + // 첫 번째 행을 선택합니다. + Element firstRow = table.select("tbody tr").first(); + + // 첫 번째 행에서 solution-id를 추출합니다. + Element solutionIdElement = firstRow.select("td").first(); // 첫 번째 열에 있는 것이 solution-id 입니다. + + if (solutionIdElement != null) { + String solutionId = solutionIdElement.text(); + + return new SolutionIdDto(solutionId); + + } else { + throw new MorandiException(SubmitErrorCode.CANT_FIND_SOLUTION_ID); + } + } } diff --git a/src/main/java/swm_nm/morandi/global/exception/errorcode/SubmitErrorCode.java b/src/main/java/swm_nm/morandi/global/exception/errorcode/SubmitErrorCode.java index ec22eecf..ce1f86b1 100644 --- a/src/main/java/swm_nm/morandi/global/exception/errorcode/SubmitErrorCode.java +++ b/src/main/java/swm_nm/morandi/global/exception/errorcode/SubmitErrorCode.java @@ -13,7 +13,8 @@ public enum SubmitErrorCode implements ErrorCode { COOKIE_NOT_EXIST(HttpStatus.BAD_REQUEST,"쿠키가 존재하지 않습니다. 익스텐션을 실행해 주세요"), BAEKJOON_INVALID_ID(HttpStatus.BAD_REQUEST,"초기에 등록한 백준 ID로 로그인 해주세요"), BAEKJOON_UNKNOWN_ERROR(HttpStatus.INTERNAL_SERVER_ERROR,"알 수 없는 오류가 발생했습니다"), - INVALID_BOJPROBLEM_NUMBER(HttpStatus.BAD_REQUEST,"유효하지 않은 문제 번호입니다"); + INVALID_BOJPROBLEM_NUMBER(HttpStatus.BAD_REQUEST,"유효하지 않은 문제 번호입니다"), + CANT_FIND_SOLUTION_ID(HttpStatus.BAD_REQUEST,"Solution ID를 찾을 수 없습니다"); //TODO //크롬익스텐션에서 fetch요청 시 forbidden오면 저장된 쿠키 null로 변경하고, 다시 로그인하라는 메시지 띄우기