Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

46 feat 추천 문제 정보 추가하여 반환 #53

Merged
merged 4 commits into from
Jan 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
package com.api.TaveShot.domain.recommend.controller;

import com.api.TaveShot.domain.post.post.dto.response.PostResponse;
import com.api.TaveShot.domain.recommend.dto.RecProRequestDto;
import com.api.TaveShot.domain.recommend.dto.RecProResponseDto;
import com.api.TaveShot.domain.recommend.dto.RecResponseDto;
import com.api.TaveShot.domain.recommend.service.RecommendService;
import com.api.TaveShot.global.success.SuccessResponse;
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.parameters.RequestBody;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;
Expand All @@ -30,9 +33,7 @@ public class RecommendController {
@Operation(summary = "사용자 기반 문제 추천", description = "해당 유저가 푼 문제들을 바탕으로" +
" 같은 티어의 사용자가 푼 문제들을 관련성이 높은 순으로 추천합니다.")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "추천 성공",
content = @Content(mediaType = MediaType.MULTIPART_FORM_DATA_VALUE,
schema = @Schema(implementation = PostResponse.class)))
@ApiResponse(responseCode = "200", description = "추천 성공")
})
@GetMapping("/rival")
public SuccessResponse<RecResponseDto> getUserBasedProList() throws IOException {
Expand All @@ -42,13 +43,11 @@ public SuccessResponse<RecResponseDto> getUserBasedProList() throws IOException

@Operation(summary = "태그 관련 문제 추천", description = "원하는 문제와 태그가 같은 관련성이 높은 문제를 추천합니다.")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "추천 성공",
content = @Content(mediaType = MediaType.MULTIPART_FORM_DATA_VALUE,
schema = @Schema(implementation = PostResponse.class)))
@ApiResponse(responseCode = "200", description = "추천 성공")
})
@GetMapping("/problem")
public SuccessResponse<RecResponseDto> getSolvedProList() throws IOException {
RecResponseDto responseDto = recommendService.getListByProblem();
public SuccessResponse<RecResponseDto> getSolvedProList(@RequestParam(value = "solvedRecentId") int solvedNumber) throws IOException {
RecResponseDto responseDto = recommendService.getListByProblem(solvedNumber);
return new SuccessResponse<>(responseDto);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.api.TaveShot.domain.recommend.converter;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.persistence.AttributeConverter;
import jakarta.persistence.Converter;

import java.util.List;

@Converter
public class StringListConverter implements AttributeConverter<List<String>, String> {

private final ObjectMapper mapper = new ObjectMapper();

@Override
public String convertToDatabaseColumn(List<String> dataList){
try{
return mapper.writeValueAsString(dataList);
} catch(JsonProcessingException e){
throw new RuntimeException(e);
}
}

@Override
public List<String> convertToEntityAttribute(String data) {
try{
return mapper.readValue(data, List.class);
} catch (JsonProcessingException e){
throw new RuntimeException(e);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.api.TaveShot.domain.recommend.domain;

import com.api.TaveShot.domain.recommend.converter.StringListConverter;
import jakarta.persistence.*;
import lombok.*;

import java.util.ArrayList;
import java.util.List;

@Entity
@Table(name = "Problem")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
@AllArgsConstructor
@Builder
public class ProblemElement {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

private Integer problemId;

private String title;
private Integer acceptedUserCount;
private Integer bojLevel;

private String bojKey;
private String bojTagId;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.api.TaveShot.domain.recommend.dto;

import lombok.Builder;
import lombok.Getter;
import lombok.ToString;

import java.util.List;

@Getter
@ToString
@Builder
public class RecProDetailResponseDto {

private String title;
private String problemId;
private String tier;
private List<String> bojTag;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.api.TaveShot.domain.recommend.dto;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@Builder
public class RecProRequestDto {

private int solvedRecentId;
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
@Builder
public class RecResponseDto {

private List<String> result;
private List<RecProDetailResponseDto> result;

private String rightCnt;
private String wrongCnt;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.api.TaveShot.domain.recommend.dto;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Builder;
import lombok.Getter;
import lombok.ToString;
Expand All @@ -10,9 +11,19 @@
@Builder
@ToString
public class UserCrawlingDto {

@Schema(description = "유저 순위")
private String rank;

@Schema(description = "맞은 문제 수")
private String rightCnt;

@Schema(description = "틀린 문제 수")
private String wrongCnt;

@Schema(description = "유저 레벨")
private String tier;

@Schema(description = "맞은 문제 리스트")
private List<String> solvedProblemList;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.api.TaveShot.domain.recommend.repository;

import com.api.TaveShot.domain.recommend.domain.ProblemElement;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;

import java.util.Optional;

@Repository
public interface ProblemElementRepository extends JpaRepository<ProblemElement, Long> {

@Query("select p from ProblemElement as p where p.problemId=:problemId")
ProblemElement findByProblemId(Integer problemId);

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@

import com.api.TaveShot.domain.Member.domain.Member;
import com.api.TaveShot.domain.Member.repository.MemberRepository;
import com.api.TaveShot.domain.recommend.domain.TierCount;
import com.api.TaveShot.domain.recommend.dto.RecResponseDto;
import com.api.TaveShot.domain.recommend.dto.UserCrawlingDto;
import com.api.TaveShot.domain.recommend.dto.RecProResponseDto;
import com.api.TaveShot.domain.recommend.domain.ProblemElement;
import com.api.TaveShot.domain.recommend.dto.*;
import com.api.TaveShot.domain.recommend.repository.ProblemElementRepository;
import com.api.TaveShot.domain.recommend.repository.TierCountRepository;
import com.api.TaveShot.global.exception.ApiException;
import com.api.TaveShot.global.exception.ErrorType;
Expand All @@ -18,6 +17,10 @@
import org.springframework.web.reactive.function.client.WebClient;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Service
@Slf4j
Expand All @@ -27,6 +30,7 @@ public class RecommendService {
private final CrawlingService crawlingService;
private final MemberRepository memberRepository;
private final TierCountRepository tierCountRepository;
private final ProblemElementRepository problemElementRepository;

@Value("${lambda.secret.url1}")
private String lambda1;
Expand All @@ -36,10 +40,7 @@ public class RecommendService {

// 사용자 기반 추천 서비스
public RecResponseDto getListByUser() throws IOException {
Member currentMember1 = getCurrentMember();
String bojName = currentMember1.getBojName();

UserCrawlingDto dto = crawlingService.getUserInfo(bojName);
UserCrawlingDto dto = getUserInfo();

WebClient webClient = WebClient.builder()
.baseUrl(lambda1)
Expand All @@ -53,54 +54,128 @@ public RecResponseDto getListByUser() throws IOException {

Integer tierCount = tierCountRepository.findByTier(Integer.parseInt(dto.getTier()));

List<RecProDetailResponseDto> proDetailResponseDtos = getProblemDetail(proList);

RecResponseDto responseDto = RecResponseDto.builder()
.rivalCnt(Integer.toString(tierCount))
.rightCnt(dto.getRightCnt())
.wrongCnt(dto.getWrongCnt())
.userRank(dto.getRank())
.result(proList.getResult())
.result(proDetailResponseDtos)
.build();

return responseDto;
}

// 문제 기반 추천 서비스
public RecResponseDto getListByProblem() throws IOException {
Member currentMember2 = getCurrentMember();
String bojName = currentMember2.getBojName();
public RecResponseDto getListByProblem(int solvedRecentId) throws IOException {

UserCrawlingDto dto = crawlingService.getUserInfo(bojName);
UserCrawlingDto dto = getUserInfo();

WebClient webClient = WebClient.builder()
.baseUrl(lambda2)
.build();

RecProRequestDto requestDto = RecProRequestDto.builder()
.solvedRecentId(solvedRecentId)
.build();

RecProResponseDto proList = webClient.post()
.body(BodyInserters.fromValue(dto))
.body(BodyInserters.fromValue(requestDto))
.retrieve()
.bodyToMono(RecProResponseDto.class)
.block();

Integer tierCount = tierCountRepository.findByTier(Integer.parseInt(dto.getTier()));

List<RecProDetailResponseDto> proDetailResponseDtos = getProblemDetail(proList);

RecResponseDto responseDto = RecResponseDto.builder()
.rivalCnt(Integer.toString(tierCount))
.rightCnt(dto.getRightCnt())
.wrongCnt(dto.getWrongCnt())
.userRank(dto.getRank())
.result(proList.getResult())
.result(proDetailResponseDtos)
.build();

return responseDto;
}

private Member getCurrentMember(){
public UserCrawlingDto getUserInfo() throws IOException {
Member currentMember2 = getCurrentMember();
String bojName = currentMember2.getBojName();
// String bojName = "wjdrhs3473";

UserCrawlingDto dto = crawlingService.getUserInfo(bojName);

return dto;
}

private Member getCurrentMember(){
Member currentMember = SecurityUtil.getCurrentMember();
Long currentMemberId = currentMember.getId();
return memberRepository.findById(currentMemberId)
.orElseThrow(() -> new ApiException(ErrorType._USER_NOT_FOUND_DB));
}

private String getTierName(Integer bojLevel){
String[] tiers = {"BRONZE", "SILVER", "GOLD", "PLATINUM", "DIAMOND", "RUBY", "MASTER"};
if(bojLevel <= 5)
return tiers[0];
else if(bojLevel <= 10)
return tiers[1];
else if(bojLevel <= 15)
return tiers[2];
else if(bojLevel <= 20)
return tiers[3];
else if(bojLevel <= 25)
return tiers[4];
else if(bojLevel <= 30)
return tiers[5];

return tiers[6];
}

private List<String> extractWords(String bojTags){
List<String> tags = new ArrayList<>();

Pattern pattern = Pattern.compile("\\b\\w+\\b");
Matcher matcher = pattern.matcher(bojTags);

while(matcher.find()){
tags.add(matcher.group());
}

return tags;
}

private List<RecProDetailResponseDto> getProblemDetail(RecProResponseDto proList){
List<String> result = proList.getResult();
List<RecProDetailResponseDto> proDetailResponseDto = new ArrayList<>();

// 문제 세부 정보 찾기
for(int i=0;i<15;i++){
Integer num = Integer.parseInt(result.get(i));
log.info("num:{}", num);
try {
ProblemElement problemElement = problemElementRepository.findByProblemId(num);
log.info("{}, {}, {}", problemElement.getProblemId(), problemElement.getBojLevel(), problemElement.getBojKey());
String tierName = getTierName(problemElement.getBojLevel());
List<String> tags = extractWords(problemElement.getBojKey());
RecProDetailResponseDto detailResponseDto = RecProDetailResponseDto.builder()
.title(problemElement.getTitle())
.problemId(String.valueOf(num))
.tier(tierName)
.bojTag(tags)
.build();
proDetailResponseDto.add(detailResponseDto);
} catch(Exception e){
continue;
}

}

return proDetailResponseDto;
}

}
Loading