diff --git a/.DS_Store b/.DS_Store index e82ca7ed..1bea640e 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/src/.DS_Store b/src/.DS_Store index 83a4c15f..97314535 100644 Binary files a/src/.DS_Store and b/src/.DS_Store differ diff --git a/src/main/.DS_Store b/src/main/.DS_Store index a068fe57..b3d027d0 100644 Binary files a/src/main/.DS_Store and b/src/main/.DS_Store differ diff --git a/src/main/java/.DS_Store b/src/main/java/.DS_Store index 419e452f..ef52379d 100644 Binary files a/src/main/java/.DS_Store and b/src/main/java/.DS_Store differ diff --git a/src/main/java/org/.DS_Store b/src/main/java/org/.DS_Store index e6675bd3..fcb8f060 100644 Binary files a/src/main/java/org/.DS_Store and b/src/main/java/org/.DS_Store differ diff --git a/src/main/java/org/ezcode/codetest/application/usermanagement/user/service/UserService.java b/src/main/java/org/ezcode/codetest/application/usermanagement/user/service/UserService.java index 6a41b981..bf4ea115 100644 --- a/src/main/java/org/ezcode/codetest/application/usermanagement/user/service/UserService.java +++ b/src/main/java/org/ezcode/codetest/application/usermanagement/user/service/UserService.java @@ -1,5 +1,6 @@ package org.ezcode.codetest.application.usermanagement.user.service; +import java.time.LocalDate; import java.time.LocalDateTime; import java.time.temporal.ChronoUnit; import java.util.List; @@ -56,7 +57,7 @@ public class UserService { private final RedisTemplate redisTemplate; private final S3Uploader s3Uploader; - @Transactional(readOnly = true) + @Transactional public UserInfoResponse getUserInfo(AuthUser authUser) { log.info("authUserEmail: {}, authUserID : {}", authUser.getEmail(), authUser.getId()); User user = userDomainService.getUserById(authUser.getId()); @@ -64,6 +65,10 @@ public UserInfoResponse getUserInfo(AuthUser authUser) { List userAuthTypes = userDomainService.getUserAuthTypesByUser(user); List authTypes = userAuthTypes.stream() .map(UserAuthType::getAuthType).toList(); + if (user.getLanguage() == null) { + Language userLanguage = languageDomainService.getLanguage(1L); + user.setLanguage(userLanguage); + } return UserInfoResponse.builder() .username(user.getUsername()) @@ -87,6 +92,12 @@ public UserInfoResponse getUserInfo(AuthUser authUser) { public UserInfoResponse modifyUserInfo(AuthUser authUser, ModifyUserInfoRequest request, MultipartFile image) { User user = userDomainService.getUserById(authUser.getId()); Language findLangauge = languageDomainService.getLanguage(request.languageId()); + if (request.nickname() != null && !request.nickname().equals(user.getNickname())) { + if (userDomainService.existsByNickname(request.nickname())) { + log.info("중복 닉네임"); + throw new UserException(UserExceptionCode.ALREADY_EXIST_NICKNAME); + } + } user.modifyUserInfo( request.nickname(), diff --git a/src/main/java/org/ezcode/codetest/domain/submission/dto/DailyCorrectCount.java b/src/main/java/org/ezcode/codetest/domain/submission/dto/DailyCorrectCount.java index 957c3c7c..b30b8651 100644 --- a/src/main/java/org/ezcode/codetest/domain/submission/dto/DailyCorrectCount.java +++ b/src/main/java/org/ezcode/codetest/domain/submission/dto/DailyCorrectCount.java @@ -1,12 +1,10 @@ package org.ezcode.codetest.domain.submission.dto; import java.time.LocalDate; +import java.util.Set; public record DailyCorrectCount( LocalDate date, - int count -) { - public DailyCorrectCount(java.sql.Date date, int count) { - this(date.toLocalDate(), count); - } -} + Long count, + Set problemIds +) { } diff --git a/src/main/java/org/ezcode/codetest/domain/submission/service/SubmissionDomainService.java b/src/main/java/org/ezcode/codetest/domain/submission/service/SubmissionDomainService.java index 76069406..a41cfc38 100644 --- a/src/main/java/org/ezcode/codetest/domain/submission/service/SubmissionDomainService.java +++ b/src/main/java/org/ezcode/codetest/domain/submission/service/SubmissionDomainService.java @@ -1,5 +1,6 @@ package org.ezcode.codetest.domain.submission.service; +import java.time.LocalDate; import java.time.LocalDateTime; import java.util.List; import java.util.Optional; @@ -120,4 +121,5 @@ private void modifyUserProblemResult(UserProblemResult userProblemResult, boolea public int findSubmissionCountByUserId(Long userId) { return submissionRepository.findSubmissionCountByUserId(userId); } + } diff --git a/src/main/java/org/ezcode/codetest/domain/user/exception/code/UserExceptionCode.java b/src/main/java/org/ezcode/codetest/domain/user/exception/code/UserExceptionCode.java index d9638efb..fc2825e5 100644 --- a/src/main/java/org/ezcode/codetest/domain/user/exception/code/UserExceptionCode.java +++ b/src/main/java/org/ezcode/codetest/domain/user/exception/code/UserExceptionCode.java @@ -15,8 +15,7 @@ public enum UserExceptionCode implements ResponseCode { NO_GITHUB_INFO(false, HttpStatus.BAD_REQUEST, "깃허브 정보가 없습니다."), NO_GITHUB_REPO(false, HttpStatus.BAD_REQUEST, "해당하는 Repository를 찾을 수 없습니다."), - - ; + ALREADY_EXIST_NICKNAME(false, HttpStatus.BAD_REQUEST, "이미 존재하는 닉네임입니다"); private final boolean success; private final HttpStatus status; private final String message; diff --git a/src/main/java/org/ezcode/codetest/domain/user/model/entity/User.java b/src/main/java/org/ezcode/codetest/domain/user/model/entity/User.java index 34757119..f09709e1 100644 --- a/src/main/java/org/ezcode/codetest/domain/user/model/entity/User.java +++ b/src/main/java/org/ezcode/codetest/domain/user/model/entity/User.java @@ -216,4 +216,8 @@ public void modifyProfileImage(String profileImageUrl) { public void modifyUserRole(UserRole userRole) { this.role = userRole; } + + public void setLanguage(Language userLanguage) { + this.language = userLanguage; + } } diff --git a/src/main/java/org/ezcode/codetest/domain/user/repository/UserRepository.java b/src/main/java/org/ezcode/codetest/domain/user/repository/UserRepository.java index 9eea7b4d..6a2079bc 100644 --- a/src/main/java/org/ezcode/codetest/domain/user/repository/UserRepository.java +++ b/src/main/java/org/ezcode/codetest/domain/user/repository/UserRepository.java @@ -22,4 +22,5 @@ public interface UserRepository { void updateUserGithubAccessToken(User loginUser); + List getUserNicknames(); } diff --git a/src/main/java/org/ezcode/codetest/domain/user/service/UserDomainService.java b/src/main/java/org/ezcode/codetest/domain/user/service/UserDomainService.java index d3fcf3d4..e01ce696 100644 --- a/src/main/java/org/ezcode/codetest/domain/user/service/UserDomainService.java +++ b/src/main/java/org/ezcode/codetest/domain/user/service/UserDomainService.java @@ -132,4 +132,11 @@ public List getUserAuthTypesByUser(User user) { return userAuthTypeRepository.getUserAuthTypesByUser(user); } + public List getUserNicknames() { + return userRepository.getUserNicknames(); + } + + public boolean existsByNickname(String nickname) { + return userRepository.existsByNickname(nickname); + } } diff --git a/src/main/java/org/ezcode/codetest/infrastructure/persistence/repository/submission/query/UserProblemResultQueryRepositoryImpl.java b/src/main/java/org/ezcode/codetest/infrastructure/persistence/repository/submission/query/UserProblemResultQueryRepositoryImpl.java index 195c6f26..2bc42544 100644 --- a/src/main/java/org/ezcode/codetest/infrastructure/persistence/repository/submission/query/UserProblemResultQueryRepositoryImpl.java +++ b/src/main/java/org/ezcode/codetest/infrastructure/persistence/repository/submission/query/UserProblemResultQueryRepositoryImpl.java @@ -1,13 +1,17 @@ package org.ezcode.codetest.infrastructure.persistence.repository.submission.query; +import java.time.LocalDate; import java.util.List; +import java.util.Set; 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.group.GroupBy; import com.querydsl.core.types.Projections; +import com.querydsl.core.types.dsl.NumberExpression; import com.querydsl.core.types.dsl.Expressions; import com.querydsl.jpa.impl.JPAQueryFactory; @@ -24,20 +28,25 @@ public List countCorrectByUserGroupedByDate(Long userId) { QUserProblemResult upr = QUserProblemResult.userProblemResult; - var date = Expressions.dateTemplate(java.sql.Date.class, "DATE({0})", upr.modifiedAt); + var date = Expressions.dateTemplate(LocalDate.class, "DATE({0})", upr.modifiedAt); + NumberExpression countDistinctProblem = upr.problem.id.countDistinct(); 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(); + .transform( + GroupBy.groupBy(date).list( + Projections.constructor( + DailyCorrectCount.class, + date, + countDistinctProblem, + GroupBy.set(upr.problem.id) + ) + ) + ); } } diff --git a/src/main/java/org/ezcode/codetest/infrastructure/persistence/repository/user/UserJpaRepository.java b/src/main/java/org/ezcode/codetest/infrastructure/persistence/repository/user/UserJpaRepository.java index a7e559c2..533e92e9 100644 --- a/src/main/java/org/ezcode/codetest/infrastructure/persistence/repository/user/UserJpaRepository.java +++ b/src/main/java/org/ezcode/codetest/infrastructure/persistence/repository/user/UserJpaRepository.java @@ -24,4 +24,7 @@ void updateReviewTokens( @Param("ids") List ids, @Param("newToken") int newToken ); + + @Query("select u.nickname from User u ") + List findAllNicknames(); } diff --git a/src/main/java/org/ezcode/codetest/infrastructure/persistence/repository/user/UserRepositoryImpl.java b/src/main/java/org/ezcode/codetest/infrastructure/persistence/repository/user/UserRepositoryImpl.java index a9090b38..765cd5b6 100644 --- a/src/main/java/org/ezcode/codetest/infrastructure/persistence/repository/user/UserRepositoryImpl.java +++ b/src/main/java/org/ezcode/codetest/infrastructure/persistence/repository/user/UserRepositoryImpl.java @@ -53,6 +53,9 @@ public void updateUserGithubAccessToken(User loginUser) { userJpaRepository.save(loginUser); } - + @Override + public List getUserNicknames() { + return userJpaRepository.findAllNicknames(); + } } diff --git a/src/main/java/org/ezcode/codetest/presentation/usermanagement/UserController.java b/src/main/java/org/ezcode/codetest/presentation/usermanagement/UserController.java index 9dc7e60b..27f2c0f4 100644 --- a/src/main/java/org/ezcode/codetest/presentation/usermanagement/UserController.java +++ b/src/main/java/org/ezcode/codetest/presentation/usermanagement/UserController.java @@ -13,6 +13,7 @@ import org.ezcode.codetest.application.usermanagement.user.service.UserService; import org.ezcode.codetest.domain.user.model.entity.AuthUser; import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.DeleteMapping; @@ -46,7 +47,7 @@ public ResponseEntity getUserInfo(@AuthenticationPrincipal Aut } @Operation(summary = "내 정보 수정", description = "닉네임, 블로그, 깃허브, 소개 등 개인 정보를 추가하거나 수정합니다.") - @PutMapping("/users") + @PutMapping(value = "/users", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public ResponseEntity modifyUserInfo( @AuthenticationPrincipal AuthUser authUser, @Valid @RequestPart("request") ModifyUserInfoRequest request, @@ -90,6 +91,7 @@ public ResponseEntity getReviewToken( return ResponseEntity.status(HttpStatus.OK).body(userService.getReviewToken(authUser)); } + @Operation(summary = "회원의 푼 문제 수 조회", description = "날짜, 날짜마다 푼 문제 번호 리스트, 푼 문제 개수") @GetMapping("/users/daily-solved") public ResponseEntity getUserDailySolvedHistory( @AuthenticationPrincipal AuthUser authUser diff --git a/src/main/java/org/ezcode/codetest/presentation/usermanagement/UserVerifyController.java b/src/main/java/org/ezcode/codetest/presentation/usermanagement/UserVerifyController.java index 7b2b640a..89e94a09 100644 --- a/src/main/java/org/ezcode/codetest/presentation/usermanagement/UserVerifyController.java +++ b/src/main/java/org/ezcode/codetest/presentation/usermanagement/UserVerifyController.java @@ -1,5 +1,7 @@ package org.ezcode.codetest.presentation.usermanagement; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpHeaders; import org.ezcode.codetest.application.usermanagement.auth.dto.request.FindPasswordRequest; import org.ezcode.codetest.application.usermanagement.user.dto.request.ResetPasswordRequest; import org.ezcode.codetest.application.usermanagement.auth.dto.request.SendEmailRequest; diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index bf1c9786..3ac0c698 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -159,4 +159,12 @@ spring.datasource.hikari.minimum-idle=10 spring.datasource.hikari.idle-timeout=30000 spring.datasource.hikari.max-lifetime=600000 spring.datasource.hikari.connection-timeout=30000 -spring.datasource.hikari.validation-timeout=5000 \ No newline at end of file +spring.datasource.hikari.validation-timeout=5000 + +# ======================== +# App Redirects (Email Verify) +# ======================== +# 인증 성공 시 이동할 페이지 URL (예: 프론트의 성공 안내 페이지) +app.redirect.verify.success=${APP_REDIRECT_VERIFY_SUCCESS:https://your-frontend.example.com/verify/success} +# 인증 실패 시 이동할 페이지 URL (예: 프론트의 실패 안내 페이지) +app.redirect.verify.failure=${APP_REDIRECT_VERIFY_FAILURE:https://your-frontend.example.com/verify/failure} \ No newline at end of file