Skip to content
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package org.ezcode.codetest.application.usermanagement.user.dto.response;

import java.util.List;

import org.ezcode.codetest.domain.submission.dto.DailyCorrectCount;

public record UserDailySolvedHistoryResponse (
Long userId,
List<DailyCorrectCount> dailySolvedCounts
){}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package org.ezcode.codetest.application.usermanagement.user.dto.response;

import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public class UserReviewTokenResponse {
private final int reviewToken;
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@
import java.util.List;

import org.ezcode.codetest.application.usermanagement.user.dto.response.GrantAdminRoleResponse;
import org.ezcode.codetest.application.usermanagement.user.dto.response.UserDailySolvedHistoryResponse;
import org.ezcode.codetest.application.usermanagement.user.dto.response.UserProfileImageResponse;
import org.ezcode.codetest.application.usermanagement.user.dto.response.UserReviewTokenResponse;
import org.ezcode.codetest.application.usermanagement.user.model.UsersByWeek;
import org.ezcode.codetest.domain.submission.dto.DailyCorrectCount;
import org.ezcode.codetest.domain.submission.dto.WeeklySolveCount;
import org.ezcode.codetest.application.usermanagement.user.dto.request.ChangeUserPasswordRequest;
import org.ezcode.codetest.application.usermanagement.user.dto.request.ModifyUserInfoRequest;
Expand Down Expand Up @@ -176,4 +179,16 @@ public UserProfileImageResponse deleteUserProfileImage(AuthUser authUser) {

return new UserProfileImageResponse(null);
}

public UserReviewTokenResponse getReviewToken(AuthUser authUser) {
User user = userDomainService.getUserById(authUser.getId());

return new UserReviewTokenResponse(user.getReviewToken());
}

public UserDailySolvedHistoryResponse getUserDailySolvedHistory(AuthUser authUser) {
Long userId = authUser.getId();
List<DailyCorrectCount> solvedHistory = submissionDomainService.getSolvedHistoryByDate(authUser.getId());
return new UserDailySolvedHistoryResponse(userId, solvedHistory);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package org.ezcode.codetest.domain.submission.dto;

import java.time.LocalDate;

public record DailyCorrectCount(
LocalDate date,
int count
) {
public DailyCorrectCount(java.sql.Date date, int count) {
this(date.toLocalDate(), count);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import java.util.List;
import java.util.Optional;

import org.ezcode.codetest.domain.submission.dto.DailyCorrectCount;
import org.ezcode.codetest.domain.submission.model.entity.UserProblemResult;
import org.springframework.data.repository.query.Param;

Expand All @@ -18,4 +19,6 @@ public interface UserProblemResultRepository {
List<Object[]> findScoresBetween(LocalDateTime start, LocalDateTime end);

Optional<Integer> sumPointByUserId(@Param("userId") Long userId);

List<DailyCorrectCount> countCorrectByUserGroupedByDate(Long userId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import java.util.Optional;

import org.ezcode.codetest.application.submission.model.SubmissionContext;
import org.ezcode.codetest.domain.submission.dto.DailyCorrectCount;
import org.ezcode.codetest.domain.submission.dto.WeeklySolveCount;
import org.ezcode.codetest.domain.submission.model.SubmissionResult;
import org.ezcode.codetest.domain.submission.model.TestcaseEvaluationInput;
Expand All @@ -18,7 +19,9 @@
import org.springframework.stereotype.Service;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
@RequiredArgsConstructor
public class SubmissionDomainService {
Expand Down Expand Up @@ -85,6 +88,10 @@ public List<WeeklySolveCount> getWeeklySolveCounts(
return submissionRepository.fetchWeeklySolveCounts(startDateTime, endDateTime);
}

public List<DailyCorrectCount> getSolvedHistoryByDate(Long userId) {
return userProblemResultRepository.countCorrectByUserGroupedByDate(userId);
}

private boolean evaluate(TestcaseEvaluationInput input) {
boolean isCorrect = input.isCorrect();
boolean timeEfficient = input.timeEfficient();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ private String callChatApi(Map<String, Object> requestBody) {
.bodyValue(requestBody)
.retrieve()
.bodyToMono(OpenAIResponse.class)
.timeout(Duration.ofSeconds(10))
.timeout(Duration.ofSeconds(20))
.retryWhen(
Retry.backoff(3, Duration.ofSeconds(1))
.maxBackoff(Duration.ofSeconds(5))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
import java.util.List;
import java.util.Optional;

import org.ezcode.codetest.domain.submission.dto.DailyCorrectCount;
import org.ezcode.codetest.domain.submission.model.entity.UserProblemResult;
import org.ezcode.codetest.domain.submission.repository.UserProblemResultRepository;
import org.ezcode.codetest.infrastructure.persistence.repository.submission.jpa.UserProblemResultJpaRepository;
import org.ezcode.codetest.infrastructure.persistence.repository.submission.query.UserProblemResultQueryRepository;
import org.springframework.stereotype.Repository;

import lombok.RequiredArgsConstructor;
Expand All @@ -16,6 +18,7 @@
public class UserProblemResultRepositoryImpl implements UserProblemResultRepository {

private final UserProblemResultJpaRepository userProblemResultJpaRepository;
private final UserProblemResultQueryRepository userProblemResultQueryRepository;

@Override
public Optional<UserProblemResult> findUserProblemResultByUserIdAndProblemId(Long userId, Long problemId) {
Expand All @@ -42,4 +45,9 @@ public Optional<Integer> sumPointByUserId(Long userId) {
return userProblemResultJpaRepository.sumScoreByUserId(userId);
}

@Override
public List<DailyCorrectCount> countCorrectByUserGroupedByDate(Long userId) {
return userProblemResultQueryRepository.countCorrectByUserGroupedByDate(userId);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.ezcode.codetest.infrastructure.persistence.repository.submission.query;

import java.util.List;

import org.ezcode.codetest.domain.submission.dto.DailyCorrectCount;

public interface UserProblemResultQueryRepository {
List<DailyCorrectCount> countCorrectByUserGroupedByDate(Long userId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package org.ezcode.codetest.infrastructure.persistence.repository.submission.query;


import java.util.List;

import org.ezcode.codetest.domain.submission.dto.DailyCorrectCount;
import org.ezcode.codetest.domain.submission.model.entity.QUserProblemResult;
import org.springframework.stereotype.Repository;

import com.querydsl.core.types.Projections;
import com.querydsl.core.types.dsl.Expressions;
import com.querydsl.jpa.impl.JPAQueryFactory;

import lombok.RequiredArgsConstructor;

@Repository
@RequiredArgsConstructor
public class UserProblemResultQueryRepositoryImpl implements UserProblemResultQueryRepository{

private final JPAQueryFactory queryFactory;

@Override
public List<DailyCorrectCount> countCorrectByUserGroupedByDate(Long userId) {

QUserProblemResult upr = QUserProblemResult.userProblemResult;

var date = Expressions.dateTemplate(java.sql.Date.class, "DATE({0})", upr.modifiedAt);

return queryFactory
.select(Projections.constructor(DailyCorrectCount.class,
date,
upr.count().intValue()
))
.from(upr)
.where(
upr.user.id.eq(userId),
upr.isCorrect.eq(true)
)
.groupBy(date)
.orderBy(date.asc())
.fetch();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
import org.ezcode.codetest.application.usermanagement.user.dto.request.ChangeUserPasswordRequest;
import org.ezcode.codetest.application.usermanagement.user.dto.response.ChangeUserPasswordResponse;
import org.ezcode.codetest.application.usermanagement.user.dto.response.GrantAdminRoleResponse;
import org.ezcode.codetest.application.usermanagement.user.dto.response.UserDailySolvedHistoryResponse;
import org.ezcode.codetest.application.usermanagement.user.dto.response.UserInfoResponse;
import org.ezcode.codetest.application.usermanagement.user.dto.response.UserProfileImageResponse;
import org.ezcode.codetest.application.usermanagement.user.dto.response.UserReviewTokenResponse;
import org.ezcode.codetest.application.usermanagement.user.dto.response.WithdrawUserResponse;
import org.ezcode.codetest.application.usermanagement.user.service.UserService;
import org.ezcode.codetest.domain.user.model.entity.AuthUser;
Expand Down Expand Up @@ -94,4 +96,19 @@ public ResponseEntity<WithdrawUserResponse> withdraw(
return ResponseEntity.status(HttpStatus.OK).body(userService.withdrawUser(authUser));
}

@Operation(summary = "회원 AI 리뷰 토큰 조회", description = "회원의 남은 AI리뷰용 토큰의 개수를 조회합니다.")
@GetMapping("/users/review-token")
public ResponseEntity<UserReviewTokenResponse> getReviewToken(
@AuthenticationPrincipal AuthUser authUser
){
return ResponseEntity.status(HttpStatus.OK).body(userService.getReviewToken(authUser));
}

@GetMapping("/users/daily-solved")
public ResponseEntity<UserDailySolvedHistoryResponse> getUserDailySolvedHistory(
@AuthenticationPrincipal AuthUser authUser
){
return ResponseEntity.status(HttpStatus.OK).body(userService.getUserDailySolvedHistory(authUser));
}

}
2 changes: 1 addition & 1 deletion src/main/resources/templates/submit-test.html
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@
const d = document.getElementById(`slot-${data.seqId}`);
if (!d) return;
d.className = data.isPassed ? 'slot passed' : 'slot failed';
d.textContent = `[${data.seqId}] ${data.message} (${data.executionTime}s, ${data.memoryUsage}KB)`;
d.textContent = `[${data.seqId}] ${data.message} (${data.executionTime}ms, ${data.memoryUsage}KB)`;
}

function handleFinal(res) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
import org.ezcode.codetest.domain.user.repository.UserAuthTypeRepository;
import org.ezcode.codetest.domain.user.repository.UserRepository;
import org.ezcode.codetest.domain.user.service.UserDomainService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
Expand Down Expand Up @@ -58,7 +57,6 @@ public class UserDomainServiceTest {
) {
public Long getId() { return 1L; }
public int getReviewToken() { return 5; }
public int getZeroReviewToken() { return 0; }
};
private final UserAuthType testAuthType = new UserAuthType(testUser, AuthType.EMAIL);

Expand Down Expand Up @@ -173,4 +171,51 @@ void isDeletedUser_shouldPassWhenActive() {
assertDoesNotThrow(() -> userDomainService.isDeletedUser(testUser));
}

// 9. 닉네임 자동 생성 테스트
@Test
void generateUniqueNickname_shouldReturnNonExistingNickname() {
when(userRepository.existsByNickname(any())).thenReturn(false);
String nickname = userDomainService.generateUniqueNickname();
assertNotNull(nickname);
}

@Test
void generateUniqueNickname_shouldThrowWhenMaxAttempts() {
when(userRepository.existsByNickname(any())).thenReturn(true);
assertThrows(IllegalStateException.class, () -> userDomainService.generateUniqueNickname());
}

// 10. 리뷰 토큰 테스트
@Test
void decreaseReviewToken_shouldUpdateWhenTokensAvailable() {
testUser.setReviewToken(5);
assertDoesNotThrow(() -> userDomainService.decreaseReviewToken(testUser));
verify(userRepository).decreaseReviewToken(testUser);
}

// @Test
// void decreaseReviewToken_shouldThrowWhenNoTokens() {
// User zeroTokenUser = new User(
// "[email protected]", "pwd", "user", "nick",
// 28, Tier.NEWBIE, UserRole.USER,
// false, true, "https://github.com", false
// ) {
// public int getReviewToken() {
// return 0;
// }
// };
//
// UserException exception = assertThrows(UserException.class,
// () -> userDomainService.decreaseReviewToken(zeroTokenUser));
//
// assertEquals(UserExceptionCode.NOT_ENOUGH_TOKEN, exception.getResponseCode());
// }
//
//
// // 12. 이메일로 유저 찾기
// @Test
// void getUserByEmail_shouldReturnUser() {
// when(userRepository.getUserByEmail("[email protected]")).thenReturn(testUser);
// assertEquals(testUser, userDomainService.getUserByEmail("[email protected]"));
// }
}
Loading