Skip to content

Commit

Permalink
feat: 어드민 멤버 조회하기 API 구현 (#37)
Browse files Browse the repository at this point in the history
* feat: AdminMemberController 추가

* feat: 어드민 멤버 조회 기능 구현

* feat: 멤버 조회 서비스 구현

* feat: 멤버 조회 응답 dto 추가

* feat: 멤버 조회 레포지토리 구현

* feat: 쿼리 파라미터 예외 추가

* fix: 메서드 이름 수정

* test: 유효하지 않은 검색 조건에 대한 테스트 추가

* feat: 쿼리 조건을 처리하는 enum 타입 추가

* refactor: 서비스 레이어에서 검색 조건을 처리하도록 수정

* test: 로직 변경에 따른 테스트 수정

* refactor: 검색 조건의 형식을 수정

* remove: 사용하지 않는 Enum 타입 제거

* remove: 로직 변경에 따른 테스트 제거

* refactor: 검색 조건 로직 수정

* feat: 검색 조건 전달 dto 추가

* refactor: 불필요한 정적 팩토리 메서드 제거

* refactor: read-only 속성 부여

* refactor: read-only 속성 클래스 레벨로 변경

* rename: dto 이름 변경

* style: spotless apply
  • Loading branch information
Sangwook02 authored Feb 4, 2024
1 parent b9bce54 commit a2fc43b
Show file tree
Hide file tree
Showing 8 changed files with 178 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.gdschongik.gdsc.domain.member.api;

import com.gdschongik.gdsc.domain.member.application.MemberService;
import com.gdschongik.gdsc.domain.member.dto.request.MemberQueryRequest;
import com.gdschongik.gdsc.domain.member.dto.response.MemberFindAllResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/admin/members")
@RequiredArgsConstructor
public class AdminMemberController {

private final MemberService memberService;

@GetMapping
public ResponseEntity<Page<MemberFindAllResponse>> getMembers(MemberQueryRequest queryRequest, Pageable pageable) {
Page<MemberFindAllResponse> response = memberService.findAll(queryRequest, pageable);
return ResponseEntity.ok().body(response);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.gdschongik.gdsc.domain.member.application;

import com.gdschongik.gdsc.domain.member.dao.MemberRepository;
import com.gdschongik.gdsc.domain.member.domain.Member;
import com.gdschongik.gdsc.domain.member.dto.request.MemberQueryRequest;
import com.gdschongik.gdsc.domain.member.dto.response.MemberFindAllResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class MemberService {

private final MemberRepository memberRepository;

public Page<MemberFindAllResponse> findAll(MemberQueryRequest queryRequest, Pageable pageable) {
Page<Member> members = memberRepository.findAll(queryRequest, pageable);
return members.map(MemberFindAllResponse::of);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.gdschongik.gdsc.domain.member.dao;

import com.gdschongik.gdsc.domain.member.domain.Member;
import com.gdschongik.gdsc.domain.member.dto.request.MemberQueryRequest;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

public interface MemberCustomRepository {
Page<Member> findAll(MemberQueryRequest queryRequest, Pageable pageable);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package com.gdschongik.gdsc.domain.member.dao;

import static com.gdschongik.gdsc.domain.member.domain.QMember.*;

import com.gdschongik.gdsc.domain.member.domain.Member;
import com.gdschongik.gdsc.domain.member.dto.request.MemberQueryRequest;
import com.querydsl.core.BooleanBuilder;
import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.jpa.impl.JPAQuery;
import com.querydsl.jpa.impl.JPAQueryFactory;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.support.PageableExecutionUtils;

@RequiredArgsConstructor
public class MemberCustomRepositoryImpl implements MemberCustomRepository {

private final JPAQueryFactory queryFactory;

@Override
public Page<Member> findAll(MemberQueryRequest queryRequest, Pageable pageable) {
List<Member> fetch = queryFactory
.selectFrom(member)
.where(queryOption(queryRequest))
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
.fetch();

JPAQuery<Long> countQuery =
queryFactory.select(member.count()).from(member).where(queryOption(queryRequest));

return PageableExecutionUtils.getPage(fetch, pageable, countQuery::fetchOne);
}

private BooleanBuilder queryOption(MemberQueryRequest queryRequest) {
BooleanBuilder booleanBuilder = new BooleanBuilder();

return booleanBuilder
.and(eqStudentId(queryRequest.studentId()))
.and(eqName(queryRequest.name()))
.and(eqPhone(queryRequest.phone()))
.and(eqDepartment(queryRequest.department()))
.and(eqEmail(queryRequest.email()))
.and(eqDiscordUsername(queryRequest.discordUsername()))
.and(eqDiscordNickname(queryRequest.discordNickname()));
}

private BooleanExpression eqStudentId(String studentId) {
return studentId != null ? member.studentId.containsIgnoreCase(studentId) : null;
}

private BooleanExpression eqName(String name) {
return name != null ? member.name.containsIgnoreCase(name) : null;
}

private BooleanExpression eqPhone(String phone) {
return phone != null ? member.phone.containsIgnoreCase(phone) : null;
}

private BooleanExpression eqDepartment(String department) {
return department != null ? member.department.containsIgnoreCase(department) : null;
}

private BooleanExpression eqEmail(String email) {
return email != null ? member.email.containsIgnoreCase(email) : null;
}

private BooleanExpression eqDiscordUsername(String discordUsername) {
return discordUsername != null ? member.discordUsername.containsIgnoreCase(discordUsername) : null;
}

private BooleanExpression eqDiscordNickname(String discordNickname) {
return discordNickname != null ? member.nickname.containsIgnoreCase(discordNickname) : null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;

public interface MemberRepository extends JpaRepository<Member, Long> {
public interface MemberRepository extends JpaRepository<Member, Long>, MemberCustomRepository {

Optional<Member> findByOauthId(String oauthId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.gdschongik.gdsc.domain.member.dto.request;

public record MemberQueryRequest(
String studentId,
String name,
String phone,
String department,
String email,
String discordUsername,
String discordNickname) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.gdschongik.gdsc.domain.member.dto.response;

import com.gdschongik.gdsc.domain.member.domain.Member;

public record MemberFindAllResponse(
Long memberId,
String studentId,
String name,
String phone,
String department,
String email,
String discordUsername,
String nickname) {

public static MemberFindAllResponse of(Member member) {
return new MemberFindAllResponse(
member.getId(),
member.getStudentId(),
member.getName(),
member.getPhone(),
member.getDepartment(),
member.getEmail(),
member.getDiscordUsername(),
member.getNickname());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ public enum ErrorCode {

// Jwt
INVALID_JWT_TOKEN(HttpStatus.UNAUTHORIZED, "유효하지 않은 JWT 토큰입니다."),
EXPIRED_JWT_TOKEN(HttpStatus.UNAUTHORIZED, "만료된 JWT 토큰입니다.");
EXPIRED_JWT_TOKEN(HttpStatus.UNAUTHORIZED, "만료된 JWT 토큰입니다."),

// Parameter
INVALID_QUERY_PARAMETER(HttpStatus.BAD_REQUEST, "잘못된 쿼리 파라미터입니다.");

private final HttpStatus status;
private final String message;
Expand Down

0 comments on commit a2fc43b

Please sign in to comment.