Skip to content

Commit

Permalink
feat: 벌레 조회 기능 구현 (#21)
Browse files Browse the repository at this point in the history
* feat: Bug 임베디드 타입 생성

* feat: 벌레 조회 API 구현

* docs: PR merge 시, Issue 자동 close로 수정

* refactor: 엔티티 생성자 id 포함으로 변경

* feat: 벌레 개수 검증 추가

* test: 벌레 조회 서비스 테스트

* style: dto 내 bug 패키지 제거

* test: Bug 도메인 테스트

* style: 테스트 메서드 네이밍 수정

* test: 벌레 조회 controller 테스트

* refactor: private 생성자 추가

* test: 멤버 fixture 생성 및 적용

* test: 벌레 fixture 생성 및 적용

* test: 멤버 엔티티 테스트에 Bug 추가

* fix: code smell 제거

* style: BugMapper 메서드 네이밍 수정

* style: return 전 줄바꿈 추가

* refactor: ResponseStatus + DTO 방식으로 변경

* test: 벌레 개수 검증 테스트에 ParameterizedTest 적용
  • Loading branch information
kmebin committed Nov 1, 2023
1 parent ec1bc6d commit 929acc5
Show file tree
Hide file tree
Showing 17 changed files with 345 additions and 19 deletions.
3 changes: 2 additions & 1 deletion .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
## 📋 Checklist

- [ ] 🔀 PR 제목의 형식을 잘 작성했나요? (e.g. `feat: 유저 조회 기능 구현`)
- [ ] 🏷️ 라벨, 프로젝트, 마일스톤은 등록했나요?
- [ ] 🧹 코드 스멜은 해결했나요?

## 🧩 이슈 번호 <!-- 이슈 번호를 작성해주세요 ex) #11 -->

- #이슈번호
- close #이슈번호

## 👩‍💻 공유 포인트 및 논의 사항
24 changes: 24 additions & 0 deletions src/main/java/com/moabam/api/application/BugService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.moabam.api.application;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.moabam.api.domain.entity.Member;
import com.moabam.api.dto.BugMapper;
import com.moabam.api.dto.BugResponse;

import lombok.RequiredArgsConstructor;

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

private final MemberService memberService;

public BugResponse getBug(Long memberId) {
Member member = memberService.getById(memberId);

return BugMapper.toBugResponse(member.getBug());
}
}
25 changes: 25 additions & 0 deletions src/main/java/com/moabam/api/application/MemberService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.moabam.api.application;

import static com.moabam.global.error.model.ErrorMessage.*;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.moabam.api.domain.entity.Member;
import com.moabam.api.domain.repository.MemberRepository;
import com.moabam.global.error.exception.NotFoundException;

import lombok.RequiredArgsConstructor;

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

private final MemberRepository memberRepository;

public Member getById(Long memberId) {
return memberRepository.findById(memberId)
.orElseThrow(() -> new NotFoundException(MEMBER_NOT_FOUND));
}
}
47 changes: 47 additions & 0 deletions src/main/java/com/moabam/api/domain/entity/Bug.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.moabam.api.domain.entity;

import static com.moabam.global.error.model.ErrorMessage.*;

import org.hibernate.annotations.ColumnDefault;

import com.moabam.global.error.exception.BadRequestException;

import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Embeddable
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Bug {

@Column(name = "morning_bug", nullable = false)
@ColumnDefault("0")
private int morningBug;

@Column(name = "night_bug", nullable = false)
@ColumnDefault("0")
private int nightBug;

@Column(name = "golden_bug", nullable = false)
@ColumnDefault("0")
private int goldenBug;

@Builder
private Bug(int morningBug, int nightBug, int goldenBug) {
this.morningBug = validateBugCount(morningBug);
this.nightBug = validateBugCount(nightBug);
this.goldenBug = validateBugCount(goldenBug);
}

private int validateBugCount(int bug) {
if (bug < 0) {
throw new BadRequestException(INVALID_BUG_COUNT);
}

return bug;
}
}
19 changes: 6 additions & 13 deletions src/main/java/com/moabam/api/domain/entity/Member.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import com.moabam.global.common.util.BaseImageUrl;

import jakarta.persistence.Column;
import jakarta.persistence.Embedded;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
Expand All @@ -25,8 +26,8 @@
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@Entity
@Getter
@Table(name = "member")
@SQLDelete(sql = "UPDATE member SET deleted_at = CURRENT_TIMESTAMP where participant_id = ?")
@Where(clause = "deleted_at IS NOT NULL")
Expand Down Expand Up @@ -66,17 +67,8 @@ public class Member extends BaseTimeEntity {
@ColumnDefault("0")
private int currentMorningCount;

@Column(name = "morning_bug", nullable = false)
@ColumnDefault("0")
private int morningBug;

@Column(name = "night_bug", nullable = false)
@ColumnDefault("0")
private int nightBug;

@Column(name = "golden_bug", nullable = false)
@ColumnDefault("0")
private int goldenBug;
@Embedded
private Bug bug;

@Enumerated(EnumType.STRING)
@Column(name = "role", nullable = false)
Expand All @@ -87,11 +79,12 @@ public class Member extends BaseTimeEntity {
private LocalDateTime deletedAt;

@Builder
private Member(Long id, String socialId, String nickname, String profileImage) {
private Member(Long id, String socialId, String nickname, String profileImage, Bug bug) {
this.id = id;
this.socialId = requireNonNull(socialId);
this.nickname = requireNonNull(nickname);
this.profileImage = requireNonNullElse(profileImage, BaseImageUrl.PROFILE_URL);
this.bug = requireNonNull(bug);
this.role = Role.USER;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.moabam.api.domain.repository;

import org.springframework.data.jpa.repository.JpaRepository;

import com.moabam.api.domain.entity.Member;

public interface MemberRepository extends JpaRepository<Member, Long> {

}
18 changes: 18 additions & 0 deletions src/main/java/com/moabam/api/dto/BugMapper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.moabam.api.dto;

import com.moabam.api.domain.entity.Bug;

import lombok.AccessLevel;
import lombok.NoArgsConstructor;

@NoArgsConstructor(access = AccessLevel.PROTECTED)
public final class BugMapper {

public static BugResponse toBugResponse(Bug bug) {
return BugResponse.builder()
.morningBug(bug.getMorningBug())
.nightBug(bug.getNightBug())
.goldenBug(bug.getGoldenBug())
.build();
}
}
12 changes: 12 additions & 0 deletions src/main/java/com/moabam/api/dto/BugResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.moabam.api.dto;

import lombok.Builder;

@Builder
public record BugResponse(
int morningBug,
int nightBug,
int goldenBug
) {

}
1 change: 0 additions & 1 deletion src/main/java/com/moabam/api/dto/OAuthMapper.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.moabam.api.dto;

import com.moabam.api.dto.AuthorizationCodeRequest;
import com.moabam.global.config.OAuthConfig;

import lombok.AccessLevel;
Expand Down
26 changes: 26 additions & 0 deletions src/main/java/com/moabam/api/presentation/BugController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.moabam.api.presentation;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

import com.moabam.api.application.BugService;
import com.moabam.api.dto.BugResponse;

import lombok.RequiredArgsConstructor;

@RestController
@RequestMapping("/bugs")
@RequiredArgsConstructor
public class BugController {

private final BugService bugService;

@GetMapping
@ResponseStatus(HttpStatus.OK)
public BugResponse getBug() {
return bugService.getBug(1L);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@ public enum ErrorMessage {
ROOM_MODIFY_UNAUTHORIZED_REQUEST("방장이 아닌 사용자는 방을 수정할 수 없습니다."),
PARTICIPANT_NOT_FOUND("방에 대한 참여자의 정보가 없습니다."),
LOGIN_FAILED("로그인에 실패했습니다."),
REQUEST_FAILD("네트우크 접근 실패입니다.");
REQUEST_FAILD("네트우크 접근 실패입니다."),

MEMBER_NOT_FOUND("존재하지 않는 회원입니다."),

INVALID_BUG_COUNT("벌레 개수는 0 이상이어야 합니다.");

private final String message;
}
44 changes: 44 additions & 0 deletions src/test/java/com/moabam/api/application/BugServiceTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.moabam.api.application;

import static org.assertj.core.api.Assertions.*;
import static org.mockito.BDDMockito.*;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import com.moabam.api.domain.entity.Bug;
import com.moabam.api.domain.entity.Member;
import com.moabam.api.dto.BugResponse;
import com.moabam.fixture.MemberFixture;

@ExtendWith(MockitoExtension.class)
class BugServiceTest {

@InjectMocks
BugService bugService;

@Mock
MemberService memberService;

@DisplayName("벌레를 조회한다.")
@Test
void get_bug_success() {
// given
Long memberId = 1L;
Member member = MemberFixture.member();
given(memberService.getById(memberId)).willReturn(member);

// when
BugResponse response = bugService.getBug(memberId);

// then
Bug bug = member.getBug();
assertThat(response.morningBug()).isEqualTo(bug.getMorningBug());
assertThat(response.nightBug()).isEqualTo(bug.getNightBug());
assertThat(response.goldenBug()).isEqualTo(bug.getGoldenBug());
}
}
30 changes: 30 additions & 0 deletions src/test/java/com/moabam/api/domain/entity/BugTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.moabam.api.domain.entity;

import static org.assertj.core.api.Assertions.*;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;

import com.moabam.global.error.exception.BadRequestException;

class BugTest {

@DisplayName("벌레 개수가 음수이면 예외가 발생한다.")
@ParameterizedTest
@CsvSource({
"-10, 10, 10",
"10, -10, 10",
"10, 10, -10",
})
void validate_bug_count_exception(int morningBug, int nightBug, int goldenBug) {
Bug.BugBuilder bugBuilder = Bug.builder()
.morningBug(morningBug)
.nightBug(nightBug)
.goldenBug(goldenBug);

assertThatThrownBy(bugBuilder::build)
.isInstanceOf(BadRequestException.class)
.hasMessage("벌레 개수는 0 이상이어야 합니다.");
}
}
8 changes: 5 additions & 3 deletions src/test/java/com/moabam/api/domain/entity/MemberTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ void create_member_success() {
.socialId(socialId)
.nickname(nickname)
.profileImage(profileImage)
.bug(Bug.builder().build())
.build());
}

Expand All @@ -35,14 +36,15 @@ void create_member_noImage_success() {
.socialId(socialId)
.nickname(nickname)
.profileImage(null)
.bug(Bug.builder().build())
.build();

assertAll(
() -> assertThat(member.getProfileImage()).isEqualTo(BaseImageUrl.PROFILE_URL),
() -> assertThat(member.getRole()).isEqualTo(Role.USER),
() -> assertThat(member.getNightBug()).isZero(),
() -> assertThat(member.getGoldenBug()).isZero(),
() -> assertThat(member.getMorningBug()).isZero(),
() -> assertThat(member.getBug().getNightBug()).isZero(),
() -> assertThat(member.getBug().getGoldenBug()).isZero(),
() -> assertThat(member.getBug().getMorningBug()).isZero(),
() -> assertThat(member.getTotalCertifyCount()).isZero(),
() -> assertThat(member.getReportCount()).isZero(),
() -> assertThat(member.getCurrentMorningCount()).isZero(),
Expand Down
Loading

0 comments on commit 929acc5

Please sign in to comment.