Skip to content

Commit

Permalink
[BE] Topic 패키지 관련 코드 리팩토링 (#238)
Browse files Browse the repository at this point in the history
* [Docs] GitHub Issue 및 PR Template 설정 (#37)

* chore: .gitignore 추가

* chore: GitHub PR 및 Issue Template

* Revert "chore: GitHub PR 및 Issue Template"

This reverts commit 65915f7.

* Revert "chore: .gitignore 추가"

This reverts commit 1e1865a.

* chore: .gitignore 추가

* chore: GitHub Issue 및 PR Template 추가

* [Docs] GitHub Issue Template 파일명 오류 수정 (#39)

* chore: .gitignore 추가

* chore: GitHub PR 및 Issue Template

* Revert "chore: GitHub PR 및 Issue Template"

This reverts commit 65915f7.

* Revert "chore: .gitignore 추가"

This reverts commit 1e1865a.

* chore: .gitignore 추가

* chore: GitHub Issue 및 PR Template 추가

* chore: GitHub Issue 및 PR Template 추가

* feat: member 구현 중

Co-authored-by: jaeyeon kim <[email protected]>

* feat: 패키지 분리, AuthMember 구현

Co-authored-by: jaeyeon kim <[email protected]>

* feat: MemberArgumentResolver 구현

* feat: AuthTopic 구현

* refactor: API 명세 수정을 위한 임의 커밋

Co-authored-by: jaeyeon kim <[email protected]>

* feat: Publicity, Permission 정적 팩토리 및 Converter 추가

* refactor: Topic 에 Publicity, Permission 추가로 인한 테스트 수정

* refactor: 모든 테스트가 통과하도록 수정

* refactor: 모든 테스트가 통과하도록 수정

* feat: Topic 에 관한 CRUD 에 권한 적용 완료

* fix: Converter 반환값 이상으로 인한 오류 해결

* feat: Pin 기능에 권한 설정 추가

* style: 사용하지 않는 import 문 제거 및 접근 제어자 조정

* refactor: .. 지송;;^^

- 패키지 재분배
- AUTH 관련 기능 구현
- TopicController 및 LocationController 분리

Co-authored-by: jaeyeon kim <[email protected]>

* refactor: TopicStatus 로직 오류 수정 LocationController 오타 수정

* refactor: 불필요 상수 제거

* refactor: PinResponse 자료형 변경

* feat: Image 클래스 구현

* feat: LocationRepository에 하버사인을 이용한 find 메서드 구현

* refactor: LocationController의 메서드명 변경

* refactor: getTopicsWithPermission의 반환값 변경

* style: 불필요 공백 제거

* refactor: 로직 오류 수정 및 테스트 추가

* test: Topic 패키지 테스트 추가

* refactor: Auth 관련 리팩터링 및 TopicIntegrationTest

* refactor: auth 어노테이션 제거

* test: AddressTest 추가

* test: Pin 관련 테스트 추가

* refactor: Coordinate BigDecimal -> Double 로 수정

* refactor: 모든 테스트가 성공하도록 수정

* refactor: LocationRepositoryTest 수정

* refactor: AuthTopic 객체 제거

* refactor: RestDocs를 위한 Interceptor 조건문 추가

* refactor: DTO 변수 타입 수정 (primitive -> wrapper)

* refactor: Image 정적 팩토리 메서드 명 변경 (of -> from)

* refactor: Image 검증 메서드 부정 조건문 수정

* refactor: enum 클래스 변수명 변경 (title -> value)

* refactor: Topic 메서드 내 변수 분리

* refactor: TopicInfo 검증 메서드 수정

* refactor: TopicQueryService 메서드 명 수정

* refactor: 토픽 생성 및 병합 메서드 분리

* refactor: Topic 조회시 검증 로직 추가

* test: TopicRepository soft-deleting 테스트 구현

* test: Topic 권한에 따른 조회 테스트 구현

* test: Topic 권한에 따른 생성 및 병합 수정 삭제 테스트 구현

* test: TopicController 다중 조회 및 핀 병합 테스트 구현

* refactor: 토픽 다중 조회 검증 로직 추가

* refactor: 예외 메세지 수정 및 null 검증 방식 수정

* refactor: 요청에 따른 토픽 생성 분기 시점 변경

* refactor: 토픽 권한 수정 메서드 추가

* refactor: TopicController 변수 명 통일

* style: 예외 메세지 오탈자 수정

* refactor: TopicInfo 불변 객체로 수정

* refactor: TopicStatus 검증 로직 추가

* style: 주석 제거

* refactor: 핀 복사 메서드 수정

* refactor: 핀 조회 메서드 분리

---------

Co-authored-by: 준팍(junpak) <[email protected]>
Co-authored-by: jaeyeon kim <[email protected]>
Co-authored-by: junpakPark <[email protected]>
  • Loading branch information
4 people authored Aug 9, 2023
1 parent e0fed7e commit f0e2415
Show file tree
Hide file tree
Showing 32 changed files with 1,025 additions and 382 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,18 @@ private Image(String imageUrl) {
this.imageUrl = imageUrl;
}

public static Image of(String imageUrl) {
public static Image from(String imageUrl) {
validateUrl(imageUrl);

return new Image(imageUrl);
}

private static void validateUrl(String imageUrl) {
if (!RegexUtil.matches(VALID_IMAGE_URL_REGEX, imageUrl)) {
throw new IllegalArgumentException("잘못된 형식의 URL입니다.");
if (RegexUtil.matches(VALID_IMAGE_URL_REGEX, imageUrl)) {
return;
}

throw new IllegalArgumentException("잘못된 형식의 URL입니다.");
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.mapbefine.mapbefine.location.domain;


import static lombok.AccessLevel.PROTECTED;

import jakarta.persistence.Column;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public static MemberInfo of(
return new MemberInfo(
nickName,
email,
Image.of(imageUrl),
Image.from(imageUrl),
role
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,8 @@ private Member findMember(Long memberId) {
private Location findDuplicateOrCreatePinLocation(PinCreateRequest request) {
Coordinate coordinate = Coordinate.of(request.latitude(), request.longitude());

return locationRepository.findAllByCoordinateAndDistanceInMeters(coordinate, DUPLICATE_LOCATION_DISTANCE_METERS)
return locationRepository.findAllByCoordinateAndDistanceInMeters(coordinate,
DUPLICATE_LOCATION_DISTANCE_METERS)
.stream()
.filter(location -> location.isSameAddress(request.address()))
.findFirst()
Expand Down Expand Up @@ -138,7 +139,7 @@ public void removeImageById(AuthMember authMember, Long pinImageId) {
PinImage pinImage = findPinImage(pinImageId);
Pin pin = pinImage.getPin();
validatePinCreateOrUpdate(authMember, pin.getTopic());

pinImageRepository.deleteById(pinImageId);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,17 +90,16 @@ public void updatePinInfo(String name, String description) {
pinInfo = PinInfo.of(name, description);
}

public Pin copy(Topic topic, Member creator) {
Pin copy = Pin.createPinAssociatedWithLocationAndTopicAndMember(
public void copyToTopic(Member creator, Topic topic) {
Pin copiedPin = Pin.createPinAssociatedWithLocationAndTopicAndMember(
pinInfo.getName(),
pinInfo.getDescription(),
location,
topic,
creator
);
copyPinImages(copy);

return copy;
copyPinImages(copiedPin);
}

private void copyPinImages(Pin pin) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ private PinImage(Image image, Pin pin) {
}

public static PinImage createPinImageAssociatedWithPin(String imageUrl, Pin pin) {
PinImage pinImage = new PinImage(Image.of(imageUrl), pin);
PinImage pinImage = new PinImage(Image.from(imageUrl), pin);
pin.addPinImage(pinImage);

return pinImage;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import java.util.Collection;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Objects;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

Expand All @@ -34,116 +35,170 @@ public TopicCommandService(
this.memberRepository = memberRepository;
}

public long createNew(AuthMember authMember, TopicCreateRequest request) {
Topic topic = createNewTopic(authMember, request);
Member member = memberRepository.findById(authMember.getMemberId())
.orElseThrow(NoSuchElementException::new);

public Long saveTopic(AuthMember member, TopicCreateRequest request) {
Topic topic = convertToTopic(member, request);
List<Long> pinIds = request.pins();
List<Pin> original = pinRepository.findAllById(pinIds);

validateExist(pinIds.size(), original.size());
pinRepository.saveAll(copyPins(original, topic, member));
if (pinIds.size() > 0) {
copyPinsToTopic(member, topic, pinIds);
}

topicRepository.save(topic);

return topic.getId();
}

public long createMerge(AuthMember authMember, TopicMergeRequest request) {
List<Long> topicIds = request.topics();
List<Topic> topics = findTopicsByIds(authMember, topicIds);
Member member = memberRepository.findById(authMember.getMemberId())
.orElseThrow(NoSuchElementException::new);
private Topic convertToTopic(AuthMember member, TopicCreateRequest request) {
Member creator = findCreatorByAuthMember(member);

validateExist(topicIds.size(), topics.size());
Topic topic = createMergeTopic(authMember, request);
return Topic.createTopicAssociatedWithCreator(
request.name(),
request.description(),
request.image(),
request.publicity(),
request.permission(),
creator
);
}

List<Pin> original = getPinFromTopics(topics);
pinRepository.saveAll(copyPins(original, topic, member));
private Member findCreatorByAuthMember(AuthMember member) {
if (Objects.isNull(member.getMemberId())) {
throw new IllegalArgumentException("Guest는 토픽을 생성할 수 없습니다.");
}

return topic.getId();
return memberRepository.findById(member.getMemberId())
.orElseThrow(NoSuchElementException::new);
}

public void updateTopicInfo(
private void copyPinsToTopic(
AuthMember member,
Long id,
TopicUpdateRequest request
Topic topic,
List<Long> pinIds
) {
Topic topic = topicRepository.findById(id)
.orElseThrow(() -> new IllegalArgumentException("존재하지 않는 Topic입니다."));
member.canTopicUpdate(topic);
List<Pin> originalPins = findAllPins(pinIds);
validateCopyablePins(member, originalPins);

topic.updateTopicInfo(
request.name(),
request.description(),
request.image()
);
Member creator = findCreatorByAuthMember(member);

originalPins.forEach(pin -> pin.copyToTopic(creator, topic));
}

public void delete(AuthMember member, Long id) {
Topic topic = topicRepository.findById(id)
.orElseThrow(() -> new IllegalArgumentException("존재하지 않는 Topic입니다."));
member.canDelete(topic);
private List<Pin> findAllPins(List<Long> pinIds) {
List<Pin> findPins = pinRepository.findAllById(pinIds);

pinRepository.deleteAllByTopicId(id);
topicRepository.deleteById(id);
}
if (pinIds.size() != findPins.size()) {
throw new IllegalArgumentException("존재하지 않는 핀 Id가 존재합니다.");
}

private List<Topic> findTopicsByIds(AuthMember member, List<Long> topicIds) {
return topicRepository.findAllById(topicIds)
.stream()
.filter(member::canRead)
.toList();
return findPins;
}

private List<Pin> getPinFromTopics(List<Topic> topics) {
return topics.stream()
.map(Topic::getPins)
.flatMap(Collection::stream)
.toList();
private void validateCopyablePins(AuthMember member, List<Pin> originalPins) {
int copyablePinCount = (int) originalPins.stream()
.filter(pin -> member.canRead(pin.getTopic()))
.count();

if (copyablePinCount != originalPins.size()) {
throw new IllegalArgumentException("복사할 수 없는 pin이 존재합니다.");
}
}

private Topic createMergeTopic(AuthMember member, TopicMergeRequest request) {
public Long merge(AuthMember member, TopicMergeRequest request) {
Topic topic = convertToTopic(member, request);
List<Topic> originalTopics = findAllTopics(request.topics());

validateCopyableTopics(member, originalTopics);

Member creator = findCreatorByAuthMember(member);
Topic topic = Topic.createTopicAssociatedWithMember(
request.name(),
request.description(),
request.image(),
request.publicity(),
request.permission(),
creator
);
return topicRepository.save(topic);
List<Pin> originalPins = getAllPinsFromTopics(originalTopics);
originalPins.forEach(pin -> pin.copyToTopic(creator, topic));

topicRepository.save(topic);

return topic.getId();
}

private Topic createNewTopic(AuthMember member, TopicCreateRequest request) {
private Topic convertToTopic(AuthMember member, TopicMergeRequest request) {
Member creator = findCreatorByAuthMember(member);
Topic topic = Topic.createTopicAssociatedWithMember(

return Topic.createTopicAssociatedWithCreator(
request.name(),
request.description(),
request.image(),
request.publicity(),
request.permission(),
creator
);
return topicRepository.save(topic);
}

private Member findCreatorByAuthMember(AuthMember member) {
return memberRepository.findById(member.getMemberId())
.orElseThrow(NoSuchElementException::new);
private List<Topic> findAllTopics(List<Long> topicIds) {
List<Topic> findTopics = topicRepository.findAllById(topicIds);

if (topicIds.size() != findTopics.size()) {
throw new IllegalArgumentException("존재하지 않는 토픽 Id가 존재합니다.");
}

return findTopics;
}

private void validateExist(int idCount, int existCount) {
if (idCount != existCount) {
throw new IllegalArgumentException("찾을 수 없는 ID가 포함되어 있습니다.");
private void validateCopyableTopics(AuthMember member, List<Topic> originalTopics) {
int copyablePinCount = (int) originalTopics.stream()
.filter(member::canRead)
.count();

if (copyablePinCount != originalTopics.size()) {
throw new IllegalArgumentException("복사할 수 없는 토픽이 존재합니다.");
}
}

private List<Pin> copyPins(List<Pin> pins, Topic topic, Member member) {
return pins.stream()
.map(original -> original.copy(topic, member))
private List<Pin> getAllPinsFromTopics(List<Topic> topics) {
return topics.stream()
.map(Topic::getPins)
.flatMap(Collection::stream)
.toList();
}

public void updateTopicInfo(
AuthMember member,
Long topicId,
TopicUpdateRequest request
) {
Topic topic = findTopic(topicId);

validateUpdateAuth(member, topic);

topic.updateTopicInfo(request.name(), request.description(), request.image());
topic.updateTopicStatus(request.publicity(), request.permission());
}

private Topic findTopic(Long topicId) {
return topicRepository.findById(topicId)
.orElseThrow(() -> new IllegalArgumentException("존재하지 않는 Topic입니다."));
}

private void validateUpdateAuth(AuthMember member, Topic topic) {
if (member.canTopicUpdate(topic)) {
return;
}

throw new IllegalArgumentException("업데이트 권한이 없습니다.");
}

public void delete(AuthMember member, Long topicId) {
Topic topic = findTopic(topicId);

validateDeleteAuth(member, topic);

pinRepository.deleteAllByTopicId(topicId);
topicRepository.deleteById(topicId);
}

private void validateDeleteAuth(AuthMember member, Topic topic) {
if (member.canDelete(topic)) {
return;
}

throw new IllegalArgumentException("삭제 권한이 없습니다.");
}

}
Loading

0 comments on commit f0e2415

Please sign in to comment.