Skip to content
2 changes: 2 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ dependencies {
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
testRuntimeOnly 'com.h2database:h2'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
}

tasks.named('test') {
Expand Down
28 changes: 28 additions & 0 deletions src/main/java/org/one/global/pagination/RequestPagingDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package org.one.global.pagination;

import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;

@Setter
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class RequestPagingDto {
private Integer page = 0;
private Integer size = 10;
private String sort = "id";
private String direction = "DESC";

public Pageable toPageable() {
Sort.Direction dir = Sort.Direction.fromString(direction);
return PageRequest.of(page, size, Sort.by(dir, sort));
}
}
30 changes: 30 additions & 0 deletions src/main/java/org/one/global/pagination/ResponsePagingDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package org.one.global.pagination;

import lombok.Builder;
import lombok.Getter;
import org.springframework.data.domain.Page;

import java.util.List;

@Getter
@Builder
public class ResponsePagingDto<T> {

private List<T> content;
private Integer page;
private Integer size;
private Long totalElements;
private Integer totalPages;
private Boolean last;

public static <T> ResponsePagingDto<T> from(Page<T> page) {
return ResponsePagingDto.<T>builder()
.content(page.getContent())
.page(page.getNumber())
.size(page.getSize())
.totalElements(page.getTotalElements())
.totalPages(page.getTotalPages())
.last(page.isLast())
.build();
}
}
46 changes: 45 additions & 1 deletion src/main/java/org/one/member/controller/MemberController.java
Original file line number Diff line number Diff line change
@@ -1,4 +1,48 @@
package org.one.member.controller;
package org.one.domain.controller;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.one.global.annotation.ApiErrorExceptions;
import org.one.global.dto.ApiResponse;
import org.one.global.enums.ErrorCode;
import org.one.member.dto.MemberListRequestDto;
import org.one.member.dto.MemberListResponseDto;
import org.one.member.service.MemberService;
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.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@Tag(name = "Member", description = "부원 명부 관리 (관리자 전용)")
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/v1/members")
public class MemberController {
private final MemberService memberService;

/**
* 명부 전체 조회 API
* 요청 시, 선택적으로 page관련 설정(정렬 등)
*
* 예시 : GET /api/v1/members?page=1&sort=...
*
* 응답 데이터 : 전체 member의 명부리스트
*/
@ApiErrorExceptions({ErrorCode.INVALID_INPUT})
@Operation(summary = "명부 전체 조회", description = "관리자 권한(ADMIN)이 있는 계정만 전체 부원 명부를 조회할 수 있습니다.")
@PreAuthorize("hasRole('ADMIN')")
Comment thread
marianddi marked this conversation as resolved.
@GetMapping
public ResponseEntity<ApiResponse<List<MemberListResponseDto>>> getMemberList(
@ModelAttribute @Valid MemberListRequestDto requestDto){

List<MemberListResponseDto> response = memberService.getMemberListByAdmin(requestDto);

return ResponseEntity.ok(ApiResponse.success(response));
}
}
51 changes: 51 additions & 0 deletions src/main/java/org/one/member/dto/MemberListRequestDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package org.one.member.dto;

import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.Pattern;
import lombok.Getter;
import org.one.global.pagination.RequestPagingDto;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;

@Schema(description = "명부 리스트 조회 요청(페이지 설정)")
public class MemberListRequestDto extends RequestPagingDto {
public MemberListRequestDto(){
//기본값 세팅
this.setPage(0);
this.setSize(15);
this.setSort("createdAt");
this.setDirection("ASC");
}

@Override
@Schema(description = "페이지 번호 (0부터 시작)", example = "0")
@Min(value = 0, message = "페이지 번호는 0 이상이어야 합니다.")
public Integer getPage() {
return super.getPage();
}

@Override
@Schema(description = "한 페이지에 보여줄 부원 수 [15 고정]", example = "15")
@Min(value = 15, message = "명부 조회의 페이지 크기는 15로 고정되어 있습니다.")
@Max(value = 15, message = "명부 조회의 페이지 크기는 15로 고정되어 있습니다.")
public Integer getSize() {
return super.getSize();
}

@Override
@Schema(description = "정렬 기준 필드", example = "createdAt")
@Pattern(regexp = "^(createdAt|grade)$", message = "정렬 기준은 createdAt 또는 grade만 가능합니다.")
public String getSort() {
return super.getSort();
}

@Override
@Schema(description = "정렬 방향", example = "ASC")
@Pattern(regexp = "^(ASC|DESC)$", message = "정렬 방향은 ASC 또는 DESC만 가능합니다.")
public String getDirection() {
return super.getDirection();
}
}
42 changes: 42 additions & 0 deletions src/main/java/org/one/member/dto/MemberListResponseDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package org.one.member.dto;

import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import org.one.member.domain.Member;
import org.one.member.enums.MemberStatus;


@JsonPropertyOrder({"memberId", "name", "age", "studentId", "grade", "phoneNumber", "status"})
@Schema(description = "명부 리스트 조회 응답")
@Getter
public class MemberListResponseDto {
@Schema(description = "부원 id", example = "2")
private Long memberId;
Comment thread
marianddi marked this conversation as resolved.
@Schema(description = "부원 이름", example = "홍길동")
private String name;
@Schema(description = "부원 학번", example = "20991234")
private String studentId;
@Schema(description = "부원 학년", example = "4")
private Integer grade;
@Schema(description = "부원 나이", example = "22")
private Integer age;
@Schema(description = "부원 전화번호", example = "010-1234-5678")
private String phoneNumber;
@Schema(description = "부원 활동 상태", example = "ACTIVE")
private MemberStatus status;

private MemberListResponseDto(Member member){
this.memberId = member.getMemberId();
this.name = member.getName();
this.studentId = member.getStudentId();
this.grade = member.getGrade();
this.age = member.getAge();
this.phoneNumber = member.getPhoneNumber();
this.status = member.getStatus();
}

public static MemberListResponseDto from(Member member){
return new MemberListResponseDto(member);
}
}
7 changes: 7 additions & 0 deletions src/main/java/org/one/member/repository/MemberRepository.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
package org.one.member.repository;

import org.one.member.domain.Member;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;

import java.util.List;

/**
* 정규 부원 데이터 조회와 일괄 갱신을 담당하는 JPA Repository입니다.
*/
Expand All @@ -24,4 +27,8 @@ public interface MemberRepository extends JpaRepository<Member, Long> {
@Modifying
@Query("UPDATE Member m SET m.grade = m.grade + 1, m.age = m.age + 1")
void incrementGradeAndAge();

//동아리에 소속중인 부원들의 모든 정보를 가져와 리스트로 만듦.
@Query("SELECT m FROM Member m")
List<Member> findAllByAdmin(Pageable pageable);
}
37 changes: 37 additions & 0 deletions src/main/java/org/one/member/service/MemberService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package org.one.member.service;


import lombok.RequiredArgsConstructor;
import org.one.auth.repository.AdminRepository;
import org.one.global.enums.ErrorCode;
import org.one.global.exception.BusinessException;
import org.one.member.domain.Member;
import org.one.member.dto.MemberListRequestDto;
import org.one.member.dto.MemberListResponseDto;
import org.one.member.repository.MemberRepository;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class MemberService {
private final MemberRepository memberRepository;
private final AdminRepository adminRepository;

public List<MemberListResponseDto> getMemberListByAdmin(MemberListRequestDto requestDto) {
//requestDto로 설정한 sort, size 등을 바탕으로 Pageable객체를 만듦.
Pageable pageable = requestDto.toPageable();

//memberRepository를 이용해 모든 부원 리스트를 가져옴.
List<Member> members = memberRepository.findAllByAdmin(pageable);

//모든 부원 리스트를 Member(entity) -> MemberListResponseDto로 필요한 데이터만 빼서 리스트를 만듦.
return members.stream()
.map(MemberListResponseDto::from)
.toList();
}
}