-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: 어드민 멤버 조회하기 API 구현 #37
Changes from 8 commits
8eef013
fdfcce2
44c7744
e79e515
18b1e22
223a17e
9a0eea5
9d49cd9
1e034a3
94cd707
7f4cbb8
f090c40
ca1166b
939fe95
979d913
149c8b4
b0d5931
051e1e7
9ed344a
c17940c
e12f664
4435ad1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package com.gdschongik.gdsc.domain.member.api; | ||
|
||
import com.gdschongik.gdsc.domain.member.application.MemberService; | ||
import com.gdschongik.gdsc.domain.member.dto.response.AdminMemberFindAllResponse; | ||
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.RequestParam; | ||
import org.springframework.web.bind.annotation.RestController; | ||
|
||
@RestController | ||
@RequestMapping("/admin/members") | ||
@RequiredArgsConstructor | ||
public class AdminMemberController { | ||
|
||
private final MemberService memberService; | ||
|
||
@GetMapping | ||
public ResponseEntity<Page<AdminMemberFindAllResponse>> getMembers( | ||
@RequestParam(value = "keyword", required = false) String keyword, | ||
@RequestParam(value = "type", required = false) String type, | ||
Pageable pageable) { | ||
|
||
Page<AdminMemberFindAllResponse> response = memberService.findAll(keyword, type, pageable); | ||
return ResponseEntity.ok().body(response); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
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.response.AdminMemberFindAllResponse; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.data.domain.Page; | ||
import org.springframework.data.domain.Pageable; | ||
import org.springframework.stereotype.Service; | ||
|
||
@Service | ||
@RequiredArgsConstructor | ||
public class MemberService { | ||
|
||
private final MemberRepository memberRepository; | ||
|
||
public Page<AdminMemberFindAllResponse> findAll(String keyword, String type, Pageable pageable) { | ||
Page<Member> members = memberRepository.findAll(keyword, type, pageable); | ||
return members.map(AdminMemberFindAllResponse::of); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package com.gdschongik.gdsc.domain.member.dao; | ||
|
||
import com.gdschongik.gdsc.domain.member.domain.Member; | ||
import org.springframework.data.domain.Page; | ||
import org.springframework.data.domain.Pageable; | ||
|
||
public interface MemberCustomRepository { | ||
Page<Member> findAll(String keyword, String type, Pageable pageable); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
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.global.exception.CustomException; | ||
import com.gdschongik.gdsc.global.exception.ErrorCode; | ||
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(String keyword, String type, Pageable pageable) { | ||
List<Member> fetch = queryFactory | ||
.selectFrom(member) | ||
.where(queryOption(keyword, type)) | ||
.offset(pageable.getOffset()) | ||
.limit(pageable.getPageSize()) | ||
.fetch(); | ||
|
||
JPAQuery<Long> countQuery = | ||
queryFactory.select(member.count()).from(member).where(queryOption(keyword, type)); | ||
|
||
return PageableExecutionUtils.getPage(fetch, pageable, countQuery::fetchOne); | ||
} | ||
|
||
private BooleanExpression queryOption(String keyword, String type) { | ||
if (keyword != null && type != null) { | ||
return switch (type) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. type 파라미터 처리를 레포지터리 단에서 하게 된다면 멤버 조회하기 API 스펙이 변경된는 경우 레포지터리 레이어가 직접적으로 영향을 받게 됩니다. API 파라미터와 SQL 쿼리 파라미터를 독립적으로 유지할 수 있도록 개선해보세요. type 파라미터를 enum 타입으로 개선하는 방법을 고민해보거나, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. service 단에서 String을 enum으로 바꿔 repository에 전달하도록 수정해봤습니다 |
||
case "student-id" -> member.studentId.containsIgnoreCase(keyword); | ||
case "name" -> member.name.containsIgnoreCase(keyword); | ||
case "phone" -> member.phone.containsIgnoreCase(keyword); | ||
case "department" -> member.department.containsIgnoreCase(keyword); | ||
case "email" -> member.email.containsIgnoreCase(keyword); | ||
case "discord-username" -> member.discordUsername.containsIgnoreCase(keyword); | ||
case "discord-nickname" -> member.nickname.containsIgnoreCase(keyword); | ||
default -> throw new CustomException(ErrorCode.INVALID_QUERY_PARAMETER); | ||
}; | ||
} | ||
|
||
return null; | ||
} | ||
} |
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 AdminMemberFindAllResponse( | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
Long memberId, | ||||||
String studentId, | ||||||
String name, | ||||||
String phone, | ||||||
String department, | ||||||
String email, | ||||||
String discordUsername, | ||||||
String nickname) { | ||||||
|
||||||
public static AdminMemberFindAllResponse of(Member member) { | ||||||
return new AdminMemberFindAllResponse( | ||||||
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 |
---|---|---|
@@ -0,0 +1,29 @@ | ||
package com.gdschongik.gdsc.domain.member.dao; | ||
|
||
import static org.assertj.core.api.Assertions.*; | ||
|
||
import com.gdschongik.gdsc.config.TestQuerydslConfig; | ||
import com.gdschongik.gdsc.global.exception.CustomException; | ||
import org.junit.jupiter.api.Test; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; | ||
import org.springframework.context.annotation.Import; | ||
import org.springframework.data.domain.Pageable; | ||
|
||
@DataJpaTest | ||
@Import(TestQuerydslConfig.class) | ||
class MemberRepositoryTest { | ||
|
||
@Autowired | ||
private MemberRepository memberRepository; | ||
|
||
@Test | ||
void 유효하지_않은_type이면_예외_발생() { | ||
// given | ||
Pageable pageable = Pageable.ofSize(20); | ||
|
||
// when, then | ||
assertThatThrownBy(() -> memberRepository.findAll("keyword", "invalid type", pageable)) | ||
.isInstanceOf(CustomException.class); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Transactional(readOnly = true)
클래스 레벨