Skip to content

Commit

Permalink
Merge pull request #91 from yumzen/main
Browse files Browse the repository at this point in the history
Feat: 공연 예매/지원자 통계 조회 API
  • Loading branch information
yumzen authored Nov 16, 2024
2 parents d5043ae + 5e07e6a commit e04c257
Show file tree
Hide file tree
Showing 9 changed files with 250 additions and 14 deletions.
3 changes: 3 additions & 0 deletions src/main/java/kahlua/KahluaProject/config/SecurityConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
Expand Down Expand Up @@ -50,6 +51,8 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
);
http.authorizeHttpRequests((authorize) -> authorize
.requestMatchers( "/api-docs/**", "/swagger-ui/**", "/swagger-ui.html/**", "/v3/api-docs/**", "/swagger-ui/index.html#/**").permitAll()
.requestMatchers(HttpMethod.GET, "/v1/admin/tickets/info/**").permitAll()
.requestMatchers(HttpMethod.GET, "/v1/admin/apply/info/**").permitAll()
.requestMatchers("/v1/auth/sign-out/**", "v1/auth/recreate/**","/v1/user/**", "/v1/admin/**").authenticated()
.requestMatchers("v1/reservation/**").hasAnyAuthority("KAHLUA", "ADMIN")
.anyRequest().permitAll())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import kahlua.KahluaProject.dto.applyInfo.request.ApplyInfoRequest;
import kahlua.KahluaProject.dto.applyInfo.response.ApplyInfoResponse;
import kahlua.KahluaProject.dto.apply.response.ApplyListResponse;
import kahlua.KahluaProject.dto.apply.response.ApplyStatisticsResponse;
import kahlua.KahluaProject.exception.GeneralException;
import kahlua.KahluaProject.security.AuthDetails;
import kahlua.KahluaProject.service.ApplyService;
Expand Down Expand Up @@ -70,10 +71,22 @@ public ResponseEntity<InputStreamResource> applyListToExcel() throws IOException
.body(new InputStreamResource(in));
}

@PutMapping("/info/{apply_id}")
@PutMapping("/info/{apply_info_id}")
@Operation(summary = "지원 정보 수정", description = "지원 정보를 수정합니다")
public ApiResponse<ApplyInfoResponse> updateApplyInfo(@PathVariable("apply_id") Long applyId, @RequestBody ApplyInfoRequest applyInfoRequest, @AuthenticationPrincipal AuthDetails authDetails) {
public ApiResponse<ApplyInfoResponse> updateApplyInfo(@PathVariable("apply_info_id") Long applyId, @RequestBody ApplyInfoRequest applyInfoRequest, @AuthenticationPrincipal AuthDetails authDetails) {
return ApiResponse.onSuccess(applyService.updateApplyInfo(applyId, applyInfoRequest, authDetails.user()));
}

@GetMapping("/info/{apply_info_id}")
@Operation(summary = "지원 정보 조회", description = "지원 정보를 조회합니다")
public ApiResponse<ApplyInfoResponse> getApplyInfo(@PathVariable("apply_info_id") Long applyId) {
return ApiResponse.onSuccess(applyService.getApplyInfo(applyId));
}

@GetMapping("/statistics")
@Operation(summary = "지원자 통계 조회", description = "지원자 통계를 조회합니다")
public ApiResponse<ApplyStatisticsResponse> getApplyStatistics(@AuthenticationPrincipal AuthDetails authDetails) {
return ApiResponse.onSuccess(applyService.getApplyStatistics(authDetails.user()));
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import jakarta.servlet.http.HttpServletResponse;
import kahlua.KahluaProject.apipayload.ApiResponse;
import kahlua.KahluaProject.dto.ticket.response.TicketListResponse;
import kahlua.KahluaProject.dto.ticket.response.TicketStatisticsResponse;
import kahlua.KahluaProject.dto.ticket.response.TicketUpdateResponse;
import kahlua.KahluaProject.dto.ticketInfo.request.TicketInfoRequest;
import kahlua.KahluaProject.dto.ticketInfo.response.TicketInfoResponse;
Expand Down Expand Up @@ -104,4 +105,16 @@ public ResponseEntity<InputStreamResource> participantsListToExcel() throws IOEx
public ApiResponse<TicketInfoResponse> updateTicketInfo(@PathVariable("ticket_info_id") Long ticketInfoId, @RequestBody TicketInfoRequest ticketUpdateRequest, @AuthenticationPrincipal AuthDetails authDetails) {
return ApiResponse.onSuccess(ticketService.updateTicketInfo(ticketInfoId, ticketUpdateRequest, authDetails.user()));
}

@GetMapping("/{ticket_info_id}")
@Operation(summary = "티켓 정보 조회", description = "티켓 정보를 조회합니다")
public ApiResponse<TicketInfoResponse> getTicketInfo(@PathVariable("ticket_info_id") Long ticketInfoId) {
return ApiResponse.onSuccess(ticketService.getTicketInfo(ticketInfoId));
}

@GetMapping("/statistics")
@Operation(summary = "티켓 통계 조회", description = "티켓 통계를 조회합니다")
public ApiResponse<TicketStatisticsResponse> getTicketStatistics(@AuthenticationPrincipal AuthDetails authDetails) {
return ApiResponse.onSuccess(ticketService.getTicketStatistics(authDetails.user()));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package kahlua.KahluaProject.dto.apply.response;

import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import com.fasterxml.jackson.databind.annotation.JsonNaming;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Builder;

@Builder
@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
public record ApplyStatisticsResponse(
@Schema(description = "총 지원자 수", example = "100")
Long totalApplyCount,

@Schema(description = "보컬 지원자 수", example = "20")
Long vocalCount,

@Schema(description = "보컬 지원자 비율", example = "20")
Long vocalPercent,

@Schema(description = "드럼 지원자 수", example = "20")
Long drumCount,

@Schema(description = "드럼 지원자 비율", example = "20")
Long drumPercent,

@Schema(description = "기타 지원자 수", example = "20")
Long guitarCount,

@Schema(description = "기타 지원자 비율", example = "20")
Long guitarPercent,

@Schema(description = "베이스 지원자 수", example = "20")
Long bassCount,

@Schema(description = "베이스 지원자 비율", example = "20")
Long bassPercent,

@Schema(description = "신디사이저 지원자 수", example = "20")
Long synthesizerCount,

@Schema(description = "신디사이저 지원자 비율", example = "20")
Long synthesizerPercent
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package kahlua.KahluaProject.dto.ticket.response;

import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import com.fasterxml.jackson.databind.annotation.JsonNaming;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Builder;

import java.util.List;

@Builder
@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
public record TicketStatisticsResponse(
@Schema(description = "티켓 상태별 수")
TicketStatusResponse ticketStatusCount,
@Schema(description = "그래프 정보")
GraphResponse graph,
@Schema(description = "총 수입", example = "1000000")
Long totalIncome
) {
@Builder
@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
public record TicketStatusResponse(
@Schema(description = "총 티켓 수", example = "100")
Long totalTicketCount,
@Schema(description = "결제 대기 수", example = "20")
Long waitCount,
@Schema(description = "결제 완료 수", example = "20")
Long finishPaymentCount,
@Schema(description = "환불 요청 수", example = "20")
Long cancelRequestCount
) {
}
@Builder
@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
public record GraphResponse(
@Schema(description = "일반 티켓 수", example = "80")
Long generalCount,
@Schema(description = "일반 티켓 비율", example = "80")
Long generalPercent,
@Schema(description = "신입생 티켓 수", example = "20")
Long freshmanCount,
@Schema(description = "신입생 티켓 비율", example = "20")
Long freshmanPercent
) {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.List;
import java.util.Optional;

public interface ApplyRepository extends JpaRepository<Apply, Long> {

List<Apply> findAllByFirstPreference(Preference first_preference);
List<Apply> findAllBySecondPreference(Preference second_preference);
Boolean existsByPhoneNum(String phone_num);
Optional<Long> countByDeletedAtIsNull();
Optional<Long> countByFirstPreferenceAndDeletedAtIsNull(Preference first_preference);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package kahlua.KahluaProject.repository.ticket;

import kahlua.KahluaProject.domain.ticket.Status;
import kahlua.KahluaProject.domain.ticket.Ticket;
import kahlua.KahluaProject.domain.ticket.Type;
import org.springframework.data.jpa.repository.JpaRepository;
Expand All @@ -16,4 +17,9 @@ public interface TicketRepository extends JpaRepository<Ticket, Long>, TicketCus
Optional<Ticket> findByReservationId(String reservationId);
List<Ticket> findAllByOrderByBuyerAscIdDesc();
List<Ticket> findAllByTypeOrderByBuyerAscIdDesc(Type type);

Optional<Long> countByStatusAndDeletedAtIsNull(Status status);
Optional<Long> countByTypeAndDeletedAtIsNull(Type type);
Optional<Long> countAllByDeletedAtIsNull();
Optional<Long> countByTypeAndStatusAndDeletedAtIsNull(Type type, Status status);
}
55 changes: 55 additions & 0 deletions src/main/java/kahlua/KahluaProject/service/ApplyService.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import kahlua.KahluaProject.dto.apply.response.*;
import kahlua.KahluaProject.dto.applyInfo.request.ApplyInfoRequest;
import kahlua.KahluaProject.dto.applyInfo.response.ApplyInfoResponse;
import kahlua.KahluaProject.dto.apply.response.ApplyStatisticsResponse;
import kahlua.KahluaProject.exception.GeneralException;
import kahlua.KahluaProject.repository.ApplyInfoRepository;
import kahlua.KahluaProject.repository.ApplyRepository;
Expand All @@ -20,7 +21,10 @@
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
Expand Down Expand Up @@ -168,4 +172,55 @@ public ApplyInfoResponse updateApplyInfo(Long applyInfoId, ApplyInfoRequest appl
//return: ApplyInfoResponse 타입으로 변환 후 반환
return ApplyConverter.toApplyInfoResponse(applyInfo);
}

public ApplyInfoResponse getApplyInfo(Long applyId) {
//business logic: applyInfo 데이터 조회
ApplyInfo applyInfo = applyInfoRepository.findById(applyId)
.orElseThrow(() -> new GeneralException(ErrorStatus.APPLY_INFO_NOT_FOUND));

//return: ApplyInfoResponse 타입으로 변환 후 반환
return ApplyConverter.toApplyInfoResponse(applyInfo);
}

public ApplyStatisticsResponse getApplyStatistics(User user) {
//validation: 관리자 권한 확인
if (user.getUserType() != UserType.ADMIN) {
throw new GeneralException(ErrorStatus.UNAUTHORIZED);
}

//business logic: 전체 지원자 수, 각 세션별 지원자 수 조회
Long totalApplyCount = applyRepository.countByDeletedAtIsNull()
.orElseThrow(() -> new GeneralException(ErrorStatus.APPLICANT_NOT_FOUND));

Map<Preference, Long> counts = Arrays.stream(Preference.values())
.collect(Collectors.toMap(
preference -> preference,
this::getCount
));

//return: ApplyStatisticsResponse 타입으로 변환 후 반환
return ApplyStatisticsResponse.builder()
.totalApplyCount(totalApplyCount)
.vocalCount(counts.get(Preference.VOCAL))
.vocalPercent(calculatePercent(counts.get(Preference.VOCAL), totalApplyCount))
.drumCount(counts.get(Preference.DRUM))
.drumPercent(calculatePercent(counts.get(Preference.DRUM), totalApplyCount))
.guitarCount(counts.get(Preference.GUITAR))
.guitarPercent(calculatePercent(counts.get(Preference.GUITAR), totalApplyCount))
.bassCount(counts.get(Preference.BASS))
.bassPercent(calculatePercent(counts.get(Preference.BASS), totalApplyCount))
.synthesizerCount(counts.get(Preference.SYNTHESIZER))
.synthesizerPercent(calculatePercent(counts.get(Preference.SYNTHESIZER), totalApplyCount))
.build();
}

private Long getCount(Preference preference) {
return applyRepository.countByFirstPreferenceAndDeletedAtIsNull(preference)
.orElseThrow(() -> new GeneralException(ErrorStatus.APPLICANT_NOT_FOUND));
}

private Long calculatePercent(Long count, Long total) {
return total > 0 ? (count * 100) / total : 0;
}

}
Loading

0 comments on commit e04c257

Please sign in to comment.