Skip to content

Commit

Permalink
feat: app be를 위한 스터디 수 조회 API 구현 (#489)
Browse files Browse the repository at this point in the history
* feat(InternalMeetingStatsController): orgId로 참여한 스터디 개수를 반환하는 API 구현

* feat(ApprovedStudyCountResponseDto): 플그 id와 승인된 스터디 수를 반환하는 DTO 구현

* feat(InternalMeetingStatsService): orgId로 승인된 스터디 개수 조회 기능 추가

* feat(ApprovedStudyCountProjection): orgId와 approvedStudyCount를 반환하는 프로젝션 인터페이스 작성

* feat(ApplyRepository): orgId를 기준으로 승인된 스터디 수 조회 쿼리 구현

* feat(InternalMeetingStatsApi): 유저의 승인된 스터디 수 조회 API 스웨거 명세 구현

* chore(ApplyRepository): 주석 제거

* refactor: 반환 로직 DTO 내부로 이동

* feat: Transaction 읽기 속성 추가

* del: 프로젝션 인터페이스 삭제

* refactor(ApprovedStudyCountResponseDto): 정적 팩토리 메서드 리팩토링

* refactor(ApplyRepository): JPQL 쿼리 변경

* refactor(InternalMeetingStatsService): 비즈니스 로직 변경

* refactor(InternalMeetingStatsService): orgId에 해당하는 유저가 없을 경우 승인된 스터디 신청 수를 0으로 반환하도록 변경

* refactor(InternalMeetingStatsService): 유저 객체로 부터 orgId를 가져와서 반환하도록 리팩토링
  • Loading branch information
hoonyworld authored Nov 21, 2024
1 parent defb1b3 commit 0363543
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package org.sopt.makers.crew.main.entity.apply;

import static org.sopt.makers.crew.main.global.exception.ErrorStatus.NOT_FOUND_APPLY;
import static org.sopt.makers.crew.main.global.exception.ErrorStatus.*;

import java.util.List;

import org.sopt.makers.crew.main.global.exception.BadRequestException;
import org.sopt.makers.crew.main.entity.apply.enums.EnApplyStatus;
import org.sopt.makers.crew.main.entity.meeting.enums.MeetingCategory;
import org.sopt.makers.crew.main.global.exception.BadRequestException;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
Expand Down Expand Up @@ -57,4 +58,12 @@ default Apply findByIdOrThrow(Integer applyId) {
@Query("DELETE FROM Apply a WHERE a.meetingId = :meetingId")
void deleteAllByMeetingIdQuery(Integer meetingId);

}
@Query("SELECT COALESCE(COUNT(a.id), 0) " +
"FROM Apply a JOIN a.meeting m " +
"JOIN a.user u " +
"WHERE m.category = :category AND a.status = :status AND u.orgId = :orgId")
Long findApprovedStudyCountByOrgId(
@Param("category") MeetingCategory category,
@Param("status") EnApplyStatus status,
@Param("orgId") Integer orgId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package org.sopt.makers.crew.main.internal;

import java.util.Map;

import org.springframework.http.ResponseEntity;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;

@Tag(name = "internal API - 개수(스터디, 행사 등등)")
public interface InternalMeetingStatsApi {

@Operation(summary = "유저의 승인된 스터디 수 조회", description = "특정 유저의 승인된 스터디 수를 조회하는 API입니다.", tags = {
"Internal Meeting Stats"})
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "APPROVE된 스터디 수 조회 성공", content = @Content(mediaType = "application/json", schema = @Schema(example = "{\"orgId\": 1, \"approvedStudyCount\": 5}"))),
@ApiResponse(responseCode = "404", description = "존재하지 않는 유저입니다.", content = @Content(mediaType = "application/json"))})
ResponseEntity<Map<String, Object>> getApprovedStudyCountByOrgId(
@Parameter(description = "플레이그라운드 유저 ID(orgId)", example = "1") Integer orgId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package org.sopt.makers.crew.main.internal;

import org.sopt.makers.crew.main.internal.dto.ApprovedStudyCountResponseDto;
import org.sopt.makers.crew.main.internal.service.InternalMeetingStatsService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import lombok.RequiredArgsConstructor;

@RestController
@RequestMapping("/internal/meeting/stats")
@RequiredArgsConstructor
public class InternalMeetingStatsController {
private final InternalMeetingStatsService internalMeetingStatsService;

@GetMapping("/approved-studies/{orgId}")
public ResponseEntity<ApprovedStudyCountResponseDto> getApprovedStudyCountByOrgId(
@PathVariable Integer orgId
) {
ApprovedStudyCountResponseDto response = internalMeetingStatsService.getApprovedStudyCountByOrgId(orgId);
return ResponseEntity.ok(response);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.sopt.makers.crew.main.internal.dto;

import io.swagger.v3.oas.annotations.media.Schema;

@Schema(description = "승인된 스터디 수를 나타내는 DTO")
public record ApprovedStudyCountResponseDto(
@Schema(description = "플레이그라운드 유저 ID(orgId)", example = "1")
Integer orgId,

@Schema(description = "승인된 스터디 수", example = "5")
Long approvedStudyCount
) {
public static ApprovedStudyCountResponseDto of(Integer orgId, Long approvedStudyCount) {
return new ApprovedStudyCountResponseDto(orgId, approvedStudyCount);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package org.sopt.makers.crew.main.internal.service;

import org.sopt.makers.crew.main.entity.apply.ApplyRepository;
import org.sopt.makers.crew.main.entity.apply.enums.EnApplyStatus;
import org.sopt.makers.crew.main.entity.meeting.enums.MeetingCategory;
import org.sopt.makers.crew.main.entity.user.User;
import org.sopt.makers.crew.main.entity.user.UserRepository;
import org.sopt.makers.crew.main.internal.dto.ApprovedStudyCountResponseDto;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import lombok.RequiredArgsConstructor;

@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class InternalMeetingStatsService {
private final ApplyRepository applyRepository;
private final UserRepository userRepository;

public ApprovedStudyCountResponseDto getApprovedStudyCountByOrgId(Integer orgId) {
User user = userRepository.findByOrgId(orgId).orElse(null);

if (user == null) {
return ApprovedStudyCountResponseDto.of(orgId, 0L);
}

Long approvedStudyCount = applyRepository.findApprovedStudyCountByOrgId(MeetingCategory.STUDY,
EnApplyStatus.APPROVE, user.getOrgId());

return ApprovedStudyCountResponseDto.of(user.getOrgId(), approvedStudyCount);
}
}

0 comments on commit 0363543

Please sign in to comment.