Skip to content
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

[REFACTOR] 회원의 방문 이력 테이블 추가 #302

Merged
merged 6 commits into from
May 29, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.smeem.api.member.service;


import com.smeem.api.badge.service.dto.response.BadgeServiceResponse;
import com.smeem.api.goal.service.GoalService;
import com.smeem.api.goal.service.dto.request.GoalGetServiceRequest;
Expand All @@ -17,6 +16,9 @@
import com.smeem.domain.plan.adapter.PlanFinder;
import com.smeem.domain.training.model.DayType;
import com.smeem.domain.training.model.TrainingTime;
import com.smeem.domain.visit.adapter.VisitCounter;
import com.smeem.domain.visit.adapter.VisitFinder;
import com.smeem.domain.visit.adapter.VisitSaver;
import com.smeem.external.discord.AlarmService;
import lombok.RequiredArgsConstructor;
import lombok.val;
Expand Down Expand Up @@ -44,13 +46,17 @@ public class MemberService {
private final MemberFinder memberFinder;
private final MemberUpdater memberUpdater;
private final PlanFinder planFinder;
private final VisitSaver visitSaver;
private final VisitFinder visitFinder;
private final VisitCounter visitorCounter;

private final TrainingTimeService trainingTimeService;
private final GoalService goalService;
private final MemberBadgeService memberBadgeService;
private final AlarmService alarmService;
private final ValueConfig valueConfig;
private final MemberCounter memberCounter;
private final VisitCounter visitCounter;

@Transactional
public MemberUpdateServiceResponse updateUserProfile(final MemberServiceUpdateUserProfileRequest request) {
Expand Down Expand Up @@ -97,13 +103,16 @@ public MemberNameServiceResponse checkDuplicatedName(final String name) {

public MemberPerformanceGetServiceResponse getPerformanceSummary(final MemberPerformanceGetServiceRequest request) {
val member = memberFinder.findById(request.memberId());
return MemberPerformanceGetServiceResponse.of(member);
val memberVisitCount = visitCounter.countByMember(member);
return MemberPerformanceGetServiceResponse.of(member, memberVisitCount);
}

@Transactional
public void updateMemberVisit(final MemberVisitUpdateRequest request) {
val member = memberFinder.findById(request.memberId());
member.updateVisitInfoToday();
if (!visitFinder.isVisitedToday(member)) {
visitSaver.saveByMember(member);
}
}

public Optional<MemberPlanGetServiceResponse> getMemberPlan(final MemberPlanGetServiceRequest request) {
Expand Down Expand Up @@ -161,4 +170,4 @@ private void updateTermAccepted(final Member member, final MemberServiceUpdateUs
memberUpdater.updateTermAccepted(member, request.termAccepted());
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ public record MemberPerformanceGetServiceResponse(
int badgeCount
) {

public static MemberPerformanceGetServiceResponse of(Member member) {
public static MemberPerformanceGetServiceResponse of(Member member, int memberVisitCount) {
return MemberPerformanceGetServiceResponse.builder()
.visitDays(member.getVisitCount())
.visitDays(memberVisitCount)
.diaryCount(member.getDiaries().size())
.diaryComboCount(member.getDiaryComboInfo().getDiaryComboCount())
.badgeCount(member.getBadges().size())
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package com.smeem.member.service;

import com.smeem.api.member.service.MemberService;
import com.smeem.api.member.service.dto.request.MemberVisitUpdateRequest;
import com.smeem.domain.member.model.Member;
import com.smeem.domain.member.repository.MemberRepository;
import com.smeem.domain.visit.adapter.VisitCounter;
import com.smeem.domain.visit.repository.VisitRepository;
import com.smeem.support.ServiceIntegrationTest;
import com.smeem.support.fixture.MemberFixture;
import jakarta.transaction.Transactional;
import lombok.val;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;

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

public class MemberServiceIntegrationTest extends ServiceIntegrationTest {

@Autowired
private MemberService memberService;

@Autowired
private MemberRepository memberRepository;
@Autowired
private VisitRepository visitRepository;

@Autowired
private VisitCounter visitCounter;

@Nested
@DisplayName("회원 방문 체크")
class MemberVisitTest {

private Member member;

@BeforeEach
public void setUp() {
memberRepository.deleteAllInBatch();
visitRepository.deleteAllInBatch();
member = memberRepository.save(MemberFixture.member().build());
}

@Test
@Transactional
@DisplayName("[성공] 회원이 방문하면 하루에 한 번 이력이 남는다.")
void createMemberVisitedHistoryTodayAtOnce() {
// given
val request = new MemberVisitUpdateRequest(member.getId());

int initCount = visitCounter.countByMember(member);
assertThat(initCount).isEqualTo(0);

// when
memberService.updateMemberVisit(request);
memberService.updateMemberVisit(request);

// then
int visitCount = visitCounter.countByMember(member);
assertThat(visitCount).isEqualTo(1);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@
import java.util.List;
import java.util.Objects;

import static java.util.Objects.isNull;

@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
Expand Down Expand Up @@ -58,9 +56,6 @@ public class Member extends BaseTimeEntity {
@Embedded
private DiaryComboInfo diaryComboInfo;

@Embedded
private MemberVisitInfo visitInfo;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "training_plan_id")
private Plan plan;
Expand All @@ -85,7 +80,6 @@ public Member(SocialType social, String socialId, LangType targetLang, String fc
this.targetLang = targetLang;
this.fcmToken = fcmToken;
this.diaryComboInfo = new DiaryComboInfo();
this.visitInfo = new MemberVisitInfo();
}

public Member(Long id, SocialType social, String socialId, LangType targetLang) {
Expand All @@ -94,7 +88,6 @@ public Member(Long id, SocialType social, String socialId, LangType targetLang)
this.socialId = socialId;
this.targetLang = targetLang;
this.diaryComboInfo = new DiaryComboInfo();
this.visitInfo = new MemberVisitInfo();
}

public void updateUsername(String username) {
Expand Down Expand Up @@ -150,23 +143,10 @@ public boolean hasWriteDiaryToday() {
return this.diaries.stream().anyMatch(diary -> diary.getCreatedAt().toLocalDate().equals(today));
}

public int getVisitCount() {
val visitCount = Objects.nonNull(this.visitInfo) ? this.visitInfo.getVisitCount() : null;
return Objects.nonNull(visitCount) ? visitCount : 1;
}

public void updatePlan(Plan plan) {
this.plan = plan;
}

public void updateVisitInfoToday() {
if (isNull(this.visitInfo)) {
this.visitInfo = new MemberVisitInfo();
} else {
this.visitInfo.updateToday();
}
}

public int getDiaryCountInWeek() {
return this.diaries.stream()
.filter(diary -> isBetweenThisWeek(diary.getCreatedAt().toLocalDate()))
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.smeem.domain.visit.adapter;

import com.smeem.domain.member.model.Member;
import com.smeem.domain.support.RepositoryAdapter;
import com.smeem.domain.visit.repository.VisitRepository;
import lombok.RequiredArgsConstructor;

@RepositoryAdapter
@RequiredArgsConstructor
public class VisitCounter {

private final VisitRepository visitRepository;

public int countByMember(Member member) {
return visitRepository.countByMember(member);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.smeem.domain.visit.adapter;

import com.smeem.domain.member.model.Member;
import com.smeem.domain.support.RepositoryAdapter;
import com.smeem.domain.visit.repository.VisitRepository;
import lombok.RequiredArgsConstructor;
import lombok.val;

import java.time.LocalDate;

@RepositoryAdapter
@RequiredArgsConstructor
public class VisitFinder {

private final VisitRepository visitRepository;

public boolean isVisitedToday(Member member) {
val today = LocalDate.now().atStartOfDay();
val tomorrow = today.plusDays(1);
return visitRepository.existsByMemberAndVisitedAtBetween(member, today, tomorrow);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.smeem.domain.visit.adapter;

import com.smeem.domain.member.model.Member;
import com.smeem.domain.support.RepositoryAdapter;
import com.smeem.domain.visit.model.Visit;
import com.smeem.domain.visit.repository.VisitRepository;
import lombok.RequiredArgsConstructor;

@RepositoryAdapter
@RequiredArgsConstructor
public class VisitSaver {

private final VisitRepository visitRepository;

public void saveByMember(Member member) {
visitRepository.save(new Visit(member));
}
}
30 changes: 30 additions & 0 deletions smeem-domain/src/main/java/com/smeem/domain/visit/model/Visit.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.smeem.domain.visit.model;

import com.smeem.domain.member.model.Member;
import jakarta.persistence.*;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.time.LocalDateTime;

@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
public class Visit {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "member_id")
private Member member;

private LocalDateTime visitedAt;

public Visit(Member member) {
this.member = member;
this.visitedAt = LocalDateTime.now();
}
Comment on lines +28 to +31
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

로직에는 문제 없지만 생성자로 인스턴스 생성은 막아두고 static method나 builder로 인스턴스를 생성해도 괜찮을 것 같아요 ~ 큰 문제 없다고 판단되면 그대로 가셔도 좋습니다~

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.smeem.domain.visit.repository;

import com.smeem.domain.member.model.Member;
import com.smeem.domain.visit.model.Visit;
import org.springframework.data.jpa.repository.JpaRepository;

import java.time.LocalDateTime;

public interface VisitRepository extends JpaRepository<Visit, Long> {

boolean existsByMemberAndVisitedAtBetween(Member member, LocalDateTime start, LocalDateTime end);
int countByMember(Member member);
}
Loading