-
Notifications
You must be signed in to change notification settings - Fork 5
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: 유저가 선호하는 필터를 저장하는 기능을 구현한다 #453
Changes from all commits
6cfc5e5
ec8847a
1a6dae3
54a8b74
f3da5c7
6c32b43
ac2f7ce
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 |
---|---|---|
|
@@ -8,4 +8,4 @@ endif::[] | |
:sectlinks: | ||
|
||
include::station.adoc[] | ||
include::Filter.adoc[] | ||
include::filter.adoc[] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
= ADMIN API 문서 | ||
:doctype: book | ||
:icons: font | ||
:source-highlighter: highlightjs | ||
:toc: left | ||
:toclevels: 3 | ||
|
||
== 회원이 등록한 모든 필터를 조회한다. | ||
|
||
=== Request | ||
|
||
include::{snippets}/member-controller-test/find_all_filters/request-headers.adoc[] | ||
include::{snippets}/member-controller-test/find_all_filters/path-parameters.adoc[] | ||
include::{snippets}/member-controller-test/find_all_filters/request-body.adoc[] | ||
include::{snippets}/member-controller-test/find_all_filters/http-request.adoc[] | ||
|
||
=== Response | ||
|
||
include::{snippets}/member-controller-test/find_all_filters/response-fields.adoc[] | ||
include::{snippets}/member-controller-test/find_all_filters/http-response.adoc[] | ||
|
||
== 회원의 선호 필터를 등록한다 | ||
|
||
=== Request | ||
|
||
include::{snippets}/member-controller-test/add_member_filters/request-headers.adoc[] | ||
include::{snippets}/member-controller-test/add_member_filters/path-parameters.adoc[] | ||
include::{snippets}/member-controller-test/add_member_filters/request-fields.adoc[] | ||
include::{snippets}/member-controller-test/add_member_filters/http-request.adoc[] | ||
|
||
=== Response | ||
|
||
include::{snippets}/member-controller-test/add_member_filters/response-fields.adoc[] | ||
include::{snippets}/member-controller-test/add_member_filters/http-response.adoc[] | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
package com.carffeine.carffeine.member.controller; | ||
|
||
import com.carffeine.carffeine.auth.controller.AuthMember; | ||
import com.carffeine.carffeine.filter.controller.dto.FiltersResponse; | ||
import com.carffeine.carffeine.filter.domain.Filter; | ||
import com.carffeine.carffeine.filter.service.dto.FiltersRequest; | ||
import com.carffeine.carffeine.member.domain.MemberFilter; | ||
import com.carffeine.carffeine.member.service.MemberService; | ||
import lombok.RequiredArgsConstructor; | ||
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.PostMapping; | ||
import org.springframework.web.bind.annotation.RequestBody; | ||
import org.springframework.web.bind.annotation.RequestMapping; | ||
import org.springframework.web.bind.annotation.RestController; | ||
|
||
import java.util.List; | ||
|
||
@RequiredArgsConstructor | ||
@RequestMapping("/members") | ||
@RestController | ||
public class MemberController { | ||
|
||
private final MemberService memberService; | ||
|
||
@GetMapping("/{memberId}/filters") | ||
public ResponseEntity<FiltersResponse> findMemberFilters(@PathVariable Long memberId, | ||
@AuthMember Long loginMember) { | ||
List<Filter> memberFilters = memberService.findMemberFilters(memberId, loginMember); | ||
return ResponseEntity.ok(FiltersResponse.from(memberFilters)); | ||
} | ||
|
||
@PostMapping("/{memberId}/filters") | ||
public ResponseEntity<FiltersResponse> addMemberFilters(@PathVariable Long memberId, | ||
@AuthMember Long loginMember, | ||
@RequestBody FiltersRequest filtersRequest) { | ||
List<MemberFilter> memberFilters = memberService.addMemberFilters(memberId, loginMember, filtersRequest); | ||
return ResponseEntity.ok(FiltersResponse.fromMemberFilters(memberFilters)); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
package com.carffeine.carffeine.member.domain; | ||
|
||
import com.carffeine.carffeine.common.domain.BaseEntity; | ||
import com.carffeine.carffeine.filter.domain.Filter; | ||
import lombok.AccessLevel; | ||
import lombok.AllArgsConstructor; | ||
import lombok.Builder; | ||
import lombok.EqualsAndHashCode; | ||
import lombok.Getter; | ||
import lombok.NoArgsConstructor; | ||
import org.hibernate.annotations.OnDelete; | ||
import org.hibernate.annotations.OnDeleteAction; | ||
|
||
import javax.persistence.CascadeType; | ||
import javax.persistence.Entity; | ||
import javax.persistence.GeneratedValue; | ||
import javax.persistence.GenerationType; | ||
import javax.persistence.Id; | ||
import javax.persistence.JoinColumn; | ||
import javax.persistence.ManyToOne; | ||
import javax.persistence.Table; | ||
|
||
@Getter | ||
@Builder | ||
@AllArgsConstructor(access = AccessLevel.PRIVATE) | ||
@NoArgsConstructor(access = AccessLevel.PROTECTED) | ||
@Table(name = "member_filter") | ||
@EqualsAndHashCode(of = {"id"}, callSuper = false) | ||
@Entity | ||
public class MemberFilter extends BaseEntity { | ||
|
||
@Id | ||
@GeneratedValue(strategy = GenerationType.IDENTITY) | ||
private Long id; | ||
|
||
@ManyToOne | ||
@JoinColumn(name = "member_id", nullable = false) | ||
@OnDelete(action = OnDeleteAction.CASCADE) | ||
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. on delete를 사용하신 이유가 무엇인가요? 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. mebmer_filter 테이블은 (id, member_id, filter_id) 컬럼을 가지고, 멤버가 선호하는 필터를 나타내는 테이블입니다. 만약 member_filter테이블에서 member가 제거되거나, filter가 제거된다면 의미가 없는 column이 됩니다. 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. 제가 질문이 너무 짧았네요 cascade와 orphanremoval 속성을 사용하지 않고 on delete 옵션을 사용하신 이유가 있나요? 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. 아 지금 테이블 관계가 그래서 orphanremoval은 적용할 수 없었고, cacade = CASCADE.REMOVE 혹은 OnDelete를 적용할 수 있었습니다. 제가 알기로는 cascade로 삭제하는 것은 jpa 레벨에서 진행되는 것이고, 참조하는 레코드 수 만큼 쿼리가 나가고, OnDelete로 삭제하는 것은 db단에서 처리하기 때문에 삭제쿼리가 나가지 않고 삭제가 되는 것으로 알고 있습니다! 혹시 틀린 부분이 있다면 말씀해주세요~ |
||
private Member member; | ||
|
||
@ManyToOne(cascade = CascadeType.REMOVE) | ||
@JoinColumn(name = "filter_id", nullable = false) | ||
@OnDelete(action = OnDeleteAction.CASCADE) | ||
private Filter filter; | ||
|
||
public MemberFilter(Member member, Filter filter) { | ||
this.member = member; | ||
this.filter = filter; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package com.carffeine.carffeine.member.domain; | ||
|
||
import org.springframework.data.repository.Repository; | ||
|
||
import java.util.List; | ||
|
||
public interface MemberFilterRepository extends Repository<MemberFilter, Long> { | ||
|
||
List<MemberFilter> findAllByMember(Member member); | ||
|
||
void deleteAllByMember(Member member); | ||
|
||
<S extends MemberFilter> List<S> saveAll(Iterable<S> memberFilters); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
package com.carffeine.carffeine.member.service; | ||
|
||
import com.carffeine.carffeine.filter.domain.Filter; | ||
import com.carffeine.carffeine.filter.domain.FilterRepository; | ||
import com.carffeine.carffeine.filter.exception.FilterException; | ||
import com.carffeine.carffeine.filter.exception.FilterExceptionType; | ||
import com.carffeine.carffeine.filter.service.dto.FiltersRequest; | ||
import com.carffeine.carffeine.member.domain.Member; | ||
import com.carffeine.carffeine.member.domain.MemberFilter; | ||
import com.carffeine.carffeine.member.domain.MemberFilterRepository; | ||
import com.carffeine.carffeine.member.domain.MemberRepository; | ||
import com.carffeine.carffeine.member.exception.MemberException; | ||
import com.carffeine.carffeine.member.exception.MemberExceptionType; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.stereotype.Service; | ||
import org.springframework.transaction.annotation.Transactional; | ||
|
||
import java.util.List; | ||
import java.util.stream.Collectors; | ||
|
||
@RequiredArgsConstructor | ||
@Service | ||
public class MemberService { | ||
|
||
private final MemberRepository memberRepository; | ||
private final MemberFilterRepository memberFilterRepository; | ||
private final FilterRepository filterRepository; | ||
|
||
@Transactional(readOnly = true) | ||
public List<Filter> findMemberFilters(Long memberId, Long loginMember) { | ||
Member member = findMember(memberId, loginMember); | ||
|
||
return memberFilterRepository.findAllByMember(member).stream() | ||
.map(MemberFilter::getFilter) | ||
.collect(Collectors.toList()); | ||
} | ||
|
||
private Member findMember(Long memberId, Long loginMember) { | ||
Member member = memberRepository.findById(loginMember) | ||
.orElseThrow(() -> new MemberException(MemberExceptionType.NOT_FOUND)); | ||
|
||
validateMember(memberId, member); | ||
return member; | ||
} | ||
|
||
private static void validateMember(Long memberId, Member member) { | ||
if (!member.isSame(memberId)) { | ||
throw new MemberException(MemberExceptionType.INVALID_ACCESS); | ||
} | ||
} | ||
|
||
@Transactional | ||
public List<MemberFilter> addMemberFilters(Long memberId, Long loginMember, FiltersRequest filtersRequest) { | ||
Member member = findMember(memberId, loginMember); | ||
memberFilterRepository.deleteAllByMember(member); | ||
return memberFilterRepository.saveAll(makeMemberFilters(filtersRequest, member)); | ||
} | ||
|
||
private List<MemberFilter> makeMemberFilters(FiltersRequest filtersRequest, Member member) { | ||
List<Filter> filters = filtersRequest.filters() | ||
.stream() | ||
.map(it -> filterRepository.findByName(it.name()) | ||
.orElseThrow(() -> new FilterException(FilterExceptionType.FILTER_NOT_FOUND))) | ||
.toList(); | ||
|
||
return filters.stream() | ||
.map(it -> new MemberFilter(member, it)) | ||
.toList(); | ||
} | ||
} |
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.
filter response를 두 api에서 사용하는 것 같아보이는데요 이렇게 하신 특별한 이유가 있으실까요? 만약 하나를 위해 변경하면 둘 다 변경되는 일이 생길 것 같아서요
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.
Filter 등록 후 반환과, 멤버가 선호 Filter를 등록하고 반환하는 형식은 같습니다
멤버가 선호하는 MemberFilter 역시도 Filter 테이블 안에 속하기 때문입니다.
그래서 따로 반환될 일은 없다고 생각했습니다