diff --git a/main/src/main/java/org/sopt/makers/crew/main/entity/apply/ApplyRepository.java b/main/src/main/java/org/sopt/makers/crew/main/entity/apply/ApplyRepository.java index 06350b91..b9dd9870 100644 --- a/main/src/main/java/org/sopt/makers/crew/main/entity/apply/ApplyRepository.java +++ b/main/src/main/java/org/sopt/makers/crew/main/entity/apply/ApplyRepository.java @@ -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; @@ -57,4 +58,12 @@ default Apply findByIdOrThrow(Integer applyId) { @Query("DELETE FROM Apply a WHERE a.meetingId = :meetingId") void deleteAllByMeetingIdQuery(Integer meetingId); -} \ No newline at end of file + @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); +} diff --git a/main/src/main/java/org/sopt/makers/crew/main/internal/InternalMeetingStatsApi.java b/main/src/main/java/org/sopt/makers/crew/main/internal/InternalMeetingStatsApi.java new file mode 100644 index 00000000..fecb694d --- /dev/null +++ b/main/src/main/java/org/sopt/makers/crew/main/internal/InternalMeetingStatsApi.java @@ -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> getApprovedStudyCountByOrgId( + @Parameter(description = "플레이그라운드 유저 ID(orgId)", example = "1") Integer orgId); +} diff --git a/main/src/main/java/org/sopt/makers/crew/main/internal/InternalMeetingStatsController.java b/main/src/main/java/org/sopt/makers/crew/main/internal/InternalMeetingStatsController.java new file mode 100644 index 00000000..488f543b --- /dev/null +++ b/main/src/main/java/org/sopt/makers/crew/main/internal/InternalMeetingStatsController.java @@ -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 getApprovedStudyCountByOrgId( + @PathVariable Integer orgId + ) { + ApprovedStudyCountResponseDto response = internalMeetingStatsService.getApprovedStudyCountByOrgId(orgId); + return ResponseEntity.ok(response); + } +} diff --git a/main/src/main/java/org/sopt/makers/crew/main/internal/dto/ApprovedStudyCountResponseDto.java b/main/src/main/java/org/sopt/makers/crew/main/internal/dto/ApprovedStudyCountResponseDto.java new file mode 100644 index 00000000..f69c85fa --- /dev/null +++ b/main/src/main/java/org/sopt/makers/crew/main/internal/dto/ApprovedStudyCountResponseDto.java @@ -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); + } +} diff --git a/main/src/main/java/org/sopt/makers/crew/main/internal/service/InternalMeetingStatsService.java b/main/src/main/java/org/sopt/makers/crew/main/internal/service/InternalMeetingStatsService.java new file mode 100644 index 00000000..5e4211ae --- /dev/null +++ b/main/src/main/java/org/sopt/makers/crew/main/internal/service/InternalMeetingStatsService.java @@ -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); + } +}