Skip to content
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
Expand Up @@ -78,7 +78,7 @@ private List<ReadDailySummaryResponse.DailySummary> getPastDailySummary(User use

private List<ReadDailySummaryResponse.DailySummary> getTodayDailySummary(User user, LocalDate today) {
return solveHistoryStatisticsRepository
.findDailySummaryByUserAndDate(user, today)
.findFirstAttemptsDailySummaryByUserAndDate(user, today)
.stream()
.map(summary -> new ReadDailySummaryResponse.DailySummary(
summary.getDate(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ private Map<Integer, Integer> getPastHourlyDataMap(User user, LocalDate startDat
private Map<Integer, Integer> getTodayHourlyDataMap(User user, LocalDate today) {
Map<Integer, Integer> map = new HashMap<>();
List<SolveHistoryStatisticsRepository.HourlySummary> todayHourlyData =
solveHistoryStatisticsRepository.findHourlySummaryByUserAndDate(user, today);
solveHistoryStatisticsRepository.findFirstAttemptsHourlySummaryByUserAndDate(user, today);

for (SolveHistoryStatisticsRepository.HourlySummary summary : todayHourlyData) {
Integer hour = summary.getHourOfDay();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,18 @@
import io.swagger.v3.oas.annotations.tags.Tag;

import lombok.RequiredArgsConstructor;
import org.quizly.quizly.admin.dto.request.AdminReadInquiriesRequest;
import org.quizly.quizly.admin.dto.response.AdminReadInquiriesResponse;
import org.quizly.quizly.admin.service.AdminReadInquiriesService;
import org.quizly.quizly.configuration.swagger.ApiErrorCode;
import org.quizly.quizly.core.application.BaseResponse;
import org.quizly.quizly.core.domin.entity.Inquiry;
import org.quizly.quizly.core.exception.error.GlobalErrorCode;
import org.quizly.quizly.core.presentation.Pagination;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;
Expand All @@ -27,18 +29,21 @@ public class AdminReadInquiriesController {

@Operation(
summary = "관리자 문의 전체 조회 API",
description = "사용자가 등록한 문의를 전체 조회합니다.\n\n",
description = "사용자가 등록한 문의를 전체 조회합니다.\n\n"
+ "- `page`: 페이지 번호 (기본값 1)\n"
+ "- `pageSize`: 그룹 단위 페이지 크기 (기본값 10)\n",
operationId = "/admin/inquiries"
)
@GetMapping("/admin/inquiries")
@PreAuthorize("hasRole('ADMIN')")
@ApiErrorCode(errorCodes = {GlobalErrorCode.class, AdminReadInquiriesService.AdminReadInquiriesErrorCode.class})
public ResponseEntity<AdminReadInquiriesResponse> adminReadInquiries(
@RequestParam(required = false) Inquiry.Status status
){
@ModelAttribute AdminReadInquiriesRequest request)
{
Comment on lines +41 to +42
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

메서드 선언부의 여는 중괄호 { 위치가 파일 내 다른 메서드(toResponse)와 일관되지 않아 코드 가독성을 해칠 수 있습니다.

프로젝트 전반의 코드 스타일 일관성을 위해 여는 중괄호를 메서드 시그니처와 같은 줄에 배치하는 것을 권장합니다. 또한, 42번째 줄의 { 앞에는 불필요한 공백이 있습니다. 일관된 스타일은 코드를 더 쉽게 읽고 유지보수하는 데 도움이 됩니다.

        @ModelAttribute AdminReadInquiriesRequest request) {
References
  1. 프로젝트의 기존 코드 스타일과 일관성을 유지해야 합니다. 이 경우, 파일 내 다른 메서드와 중괄호 위치 스타일이 일치하지 않아 유지보수성을 저해할 수 있습니다. (link)

AdminReadInquiriesService.AdminReadInquiriesResponse serviceResponse = adminReadInquiriesService.execute(
AdminReadInquiriesService.AdminReadInquiriesRequest.builder()
.status(status)
.status(request.getStatus())
.pageRequest(request.toPageRequest())
.build()
);
if (serviceResponse == null || !serviceResponse.isSuccess()) {
Expand All @@ -51,10 +56,12 @@ public ResponseEntity<AdminReadInquiriesResponse> adminReadInquiries(
});
}

return ResponseEntity.ok(toResponse(serviceResponse.getInquiryList()));
return ResponseEntity.ok(toResponse(serviceResponse.getInquiryList(),serviceResponse.getPagination()));
}

private AdminReadInquiriesResponse toResponse(List<Inquiry> inquiryList){
private AdminReadInquiriesResponse toResponse(
List<Inquiry> inquiryList,
Pagination pagination){
List<AdminReadInquiriesResponse.AdminInquiryDetail> details = inquiryList.stream()
.map(inquiry -> new AdminReadInquiriesResponse.AdminInquiryDetail(
inquiry.getId(),
Expand All @@ -71,6 +78,7 @@ private AdminReadInquiriesResponse toResponse(List<Inquiry> inquiryList){

return AdminReadInquiriesResponse.builder()
.inquiryList(details)
.pagination(pagination)
.build();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.quizly.quizly.admin.dto.request;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
import org.quizly.quizly.core.domin.entity.Inquiry;
import org.quizly.quizly.core.presentation.BasePaginationRequest;
@Getter
@Setter
@Schema(description = "관리자 문의 조회 요청")
public class AdminReadInquiriesRequest extends BasePaginationRequest {

@Schema(description = "문의 답변 상태", example = "WAITING")
private Inquiry.Status status;
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import org.quizly.quizly.core.application.BaseResponse;
import org.quizly.quizly.core.domin.entity.Inquiry;
import org.quizly.quizly.core.exception.error.GlobalErrorCode;
import org.quizly.quizly.core.presentation.Pagination;

import java.time.LocalDateTime;
import java.util.List;
Expand All @@ -19,6 +20,9 @@ public class AdminReadInquiriesResponse extends BaseResponse<GlobalErrorCode> {
@Schema(description = "전체 문의 목록")
private List<AdminInquiryDetail> inquiryList;

@Schema(description = "페이지네이션 정보")
private Pagination pagination;

@Schema(description = "전체 문의 목록 상세")
public record AdminInquiryDetail(
@Schema(description = "문의 ID", example = "1")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
import org.quizly.quizly.core.domin.repository.InquiryRepository;
import org.quizly.quizly.core.exception.DomainException;
import org.quizly.quizly.core.exception.error.BaseErrorCode;
import org.quizly.quizly.core.presentation.Pagination;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
Expand All @@ -21,7 +25,7 @@
public class AdminReadInquiriesService implements BaseService<AdminReadInquiriesService.AdminReadInquiriesRequest, AdminReadInquiriesService.AdminReadInquiriesResponse> {

private final InquiryRepository inquiryRepository;

private static final String SORT_BY_LATEST = "createdAt";
@Override
public AdminReadInquiriesResponse execute(AdminReadInquiriesRequest request) {

Expand All @@ -31,20 +35,20 @@ public AdminReadInquiriesResponse execute(AdminReadInquiriesRequest request) {
.errorCode(AdminReadInquiriesErrorCode.NOT_EXIST_REQUIRED_PARAMETER)
.build();
}
Pageable pageRequest = request.getPageRequest().withSort(Sort.by(Sort.Direction.DESC, SORT_BY_LATEST));

Sort sort = Sort.by(Sort.Direction.DESC,"createdAt");

List<Inquiry>inquiryList;
Page<Inquiry> inquiryPage;

if(request.getStatus() == null){
inquiryList = inquiryRepository.findAllWithUser(sort);
inquiryPage = inquiryRepository.findAllWithUser(pageRequest);
}else{
inquiryList = inquiryRepository.findAllByStatusWithUser(request.getStatus(),sort);
inquiryPage = inquiryRepository.findAllByStatusWithUser(request.getStatus(),pageRequest);
}

return AdminReadInquiriesResponse.builder()
.success(true)
.inquiryList(inquiryList)
.inquiryList(inquiryPage.getContent())
.pagination(Pagination.getPaginationFromPage(inquiryPage))
.build();

}
Expand Down Expand Up @@ -73,11 +77,12 @@ public DomainException toException() {
public static class AdminReadInquiriesRequest implements BaseRequest {

private Inquiry.Status status;
private PageRequest pageRequest;


@Override
public boolean isValid() {
return true;
return pageRequest != null;
}
}

Expand All @@ -90,6 +95,7 @@ public boolean isValid() {
public static class AdminReadInquiriesResponse extends BaseResponse<AdminReadInquiriesService.AdminReadInquiriesErrorCode> {

private List<Inquiry> inquiryList;
private Pagination pagination;

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public ItemProcessor<User, List<SolveHourlySummary>> aggregateSolveHourlySummary
LocalDate targetDate = LocalDate.parse(targetDateStr);

List<HourlySummary> hourlySummaryList =
solveHistoryStatisticsRepository.findHourlySummaryByUserAndDate(user, targetDate);
solveHistoryStatisticsRepository.findFirstAttemptsHourlySummaryByUserAndDate(user, targetDate);

List<SolveHourlySummary> existSolveHourlySummaryList =
solveHourlySummaryRepository.findByUserAndDate(user, targetDate);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
Expand Down Expand Up @@ -44,5 +46,9 @@ public class SolveHistory extends BaseEntity {
@Column(nullable = true)
private LocalDateTime submittedAt;

@Column(nullable = false)
@Builder.Default
@Setter(AccessLevel.NONE)
private boolean isFirst = true;

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import org.quizly.quizly.core.domin.entity.Inquiry;
import org.quizly.quizly.core.domin.entity.User;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
Expand All @@ -12,9 +14,11 @@

public interface InquiryRepository extends JpaRepository<Inquiry,Long> {
List<Inquiry> findAllByUser(User user);
@Query("SELECT i FROM Inquiry i JOIN FETCH i.user ")
List<Inquiry> findAllWithUser(Sort sort);
@Query(value = "SELECT i FROM Inquiry i JOIN FETCH i.user",
countQuery = "SELECT count(i) FROM Inquiry i")
Page<Inquiry> findAllWithUser(Pageable pageable);

@Query("SELECT i FROM Inquiry i JOIN FETCH i.user WHERE i.status =:status")
List<Inquiry> findAllByStatusWithUser(@Param("status") Inquiry.Status status, Sort sort);
@Query(value = "SELECT i FROM Inquiry i JOIN FETCH i.user WHERE i.status = :status",
countQuery = "SELECT count(i) FROM Inquiry i WHERE i.status = :status")
Page<Inquiry> findAllByStatusWithUser(@Param("status") Inquiry.Status status, Pageable pageable);
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,13 @@ public interface SolveHistoryStatisticsRepository extends JpaRepository<SolveHis

@Query("SELECT q.quizType as quizType, " +
"COUNT(sh) as totalCount, " +
"SUM(CASE WHEN sh.isCorrect = true THEN 1 ELSE 0 END) as correctCount " +
"SUM(CASE WHEN sh.isCorrect = TRUE THEN 1 ELSE 0 END) as correctCount " +
"FROM SolveHistory sh " +
"JOIN sh.quiz q " +
"WHERE sh.user = :user " +
"AND sh.submittedAt >= :startDateTime " +
"AND sh.submittedAt < :endDateTime " +
"AND (sh.quiz.id, sh.createdAt) IN (" +
" SELECT sh2.quiz.id, MIN(sh2.createdAt) " +
" FROM SolveHistory sh2 " +
" WHERE sh2.user = :user " +
" AND sh2.submittedAt >= :startDateTime " +
" AND sh2.submittedAt < :endDateTime " +
" GROUP BY sh2.quiz.id" +
") " +
"AND sh.isFirst = TRUE " +
"GROUP BY q.quizType")
List<QuizTypeSummary> findFirstAttemptsByQuizTypeAndDateTimeRange(
@Param("user") User user,
Expand All @@ -51,20 +44,13 @@ interface QuizTypeSummary {
@Query("""
SELECT q.topic as topic,
COUNT(sh) as totalCount,
SUM(CASE WHEN sh.isCorrect = true THEN 1 ELSE 0 END) as correctCount
SUM(CASE WHEN sh.isCorrect = TRUE THEN 1 ELSE 0 END) as correctCount
FROM SolveHistory sh
JOIN sh.quiz q
WHERE sh.user = :user
AND sh.submittedAt >= :startDateTime
AND sh.submittedAt < :endDateTime
AND (sh.quiz.id, sh.createdAt) IN (
SELECT sh2.quiz.id, MIN(sh2.createdAt)
FROM SolveHistory sh2
WHERE sh2.user = :user
AND sh2.submittedAt >= :startDateTime
AND sh2.submittedAt < :endDateTime
GROUP BY sh2.quiz.id
)
AND sh.isFirst = TRUE
GROUP BY q.topic
""")
List<TopicSummary> findMonthlyTopicSummary(
Expand All @@ -86,18 +72,19 @@ interface TopicSummary {
"WHERE sh.user = :user " +
"AND sh.submittedAt >= :startDateTime " +
"AND sh.submittedAt < :endDateTime " +
"AND sh.isFirst = TRUE " +
"GROUP BY CAST(sh.submittedAt AS LocalDate) " +
"ORDER BY CAST(sh.submittedAt AS LocalDate)")
List<DailySummary> findDailySummaryByUserAndDateTimeRange(
List<DailySummary> findFirstAttemptsDailySummaryByUserAndDateTimeRange(
@Param("user") User user,
@Param("startDateTime") LocalDateTime startDateTime,
@Param("endDateTime") LocalDateTime endDateTime
);

default List<DailySummary> findDailySummaryByUserAndDate(User user, LocalDate date) {
default List<DailySummary> findFirstAttemptsDailySummaryByUserAndDate(User user, LocalDate date) {
LocalDateTime startDateTime = date.atStartOfDay();
LocalDateTime endDateTime = date.plusDays(1).atStartOfDay();
return findDailySummaryByUserAndDateTimeRange(user, startDateTime, endDateTime);
return findFirstAttemptsDailySummaryByUserAndDateTimeRange(user, startDateTime, endDateTime);
}

@Query("SELECT FUNCTION('HOUR', sh.submittedAt) as hourOfDay, " +
Expand All @@ -106,17 +93,18 @@ default List<DailySummary> findDailySummaryByUserAndDate(User user, LocalDate da
"WHERE sh.user = :user " +
"AND sh.submittedAt >= :startDateTime " +
"AND sh.submittedAt < :endDateTime " +
"AND sh.isFirst = TRUE " +
"GROUP BY FUNCTION('HOUR', sh.submittedAt)")
List<HourlySummary> findHourlySummaryByUserAndDateTimeRange(
List<HourlySummary> findFirstAttemptsHourlySummaryByUserAndDateTimeRange(
@Param("user") User user,
@Param("startDateTime") LocalDateTime startDateTime,
@Param("endDateTime") LocalDateTime endDateTime
);

default List<HourlySummary> findHourlySummaryByUserAndDate(User user, LocalDate date) {
default List<HourlySummary> findFirstAttemptsHourlySummaryByUserAndDate(User user, LocalDate date) {
LocalDateTime startDateTime = date.atStartOfDay();
LocalDateTime endDateTime = date.plusDays(1).atStartOfDay();
return findHourlySummaryByUserAndDateTimeRange(user, startDateTime, endDateTime);
return findFirstAttemptsHourlySummaryByUserAndDateTimeRange(user, startDateTime, endDateTime);
}

interface DailySummary {
Expand All @@ -128,4 +116,4 @@ interface HourlySummary {
Integer getHourOfDay();
Long getSolvedCount();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ private void saveSolveHistory(
.userAnswer(userAnswer)
.solveTime(solveTime)
.submittedAt(LocalDateTime.now())
.isFirst(false)
.build();

solveHistoryRepository.save(solveHistory);
Expand Down
Loading