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: domain entity 분리 #29

Merged
merged 7 commits into from
Aug 5, 2024
Merged
Changes from all 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,8 +1,8 @@
package com.dnd.runus.auth.userdetails;

import com.dnd.runus.auth.exception.AuthException;
import com.dnd.runus.domain.member.entity.Member;
import com.dnd.runus.domain.member.repository.MemberRepository;
import com.dnd.runus.domain.member.Member;
import com.dnd.runus.domain.member.MemberRepository;
import com.dnd.runus.global.exception.type.ErrorType;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.userdetails.UserDetails;
@@ -25,7 +25,7 @@ public UserDetails loadUserByUsername(String identity) throws UsernameNotFoundEx
.findById(memberId)
.orElseThrow(
() -> new AuthException(ErrorType.FAILED_AUTHENTICATION, "Member not found: " + memberId));
return AuthUserDetails.of(memberId, member.getRole());
return AuthUserDetails.of(memberId, member.role());
} catch (NumberFormatException exception) {
throw new UsernameNotFoundException(identity);
}
6 changes: 6 additions & 0 deletions src/main/java/com/dnd/runus/domain/badge/Badge.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.dnd.runus.domain.badge;

import com.dnd.runus.global.constant.BadgeType;

public record Badge(
long badgeId, String name, String description, String imageUrl, BadgeType type, int requiredValue) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.dnd.runus.domain.badge;

import com.dnd.runus.domain.member.Member;

import java.time.OffsetDateTime;

public record BadgeAchievement(Badge badge, Member member, OffsetDateTime createdAt, OffsetDateTime updatedAt) {}

This file was deleted.

24 changes: 14 additions & 10 deletions src/main/java/com/dnd/runus/domain/common/BaseTimeEntity.java
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
package com.dnd.runus.domain.common;

import jakarta.persistence.EntityListeners;
import jakarta.persistence.MappedSuperclass;
import jakarta.persistence.PrePersist;
import jakarta.persistence.PreUpdate;
import lombok.Getter;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import java.time.Instant;
import java.time.OffsetDateTime;

@Getter
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class BaseTimeEntity {
@CreatedDate
private Instant createdAt;
private OffsetDateTime createdAt;
private OffsetDateTime updatedAt;

@LastModifiedDate
private Instant updatedAt;
@PrePersist
public void prePersist() {
createdAt = updatedAt = OffsetDateTime.now();
}

@PreUpdate
public void preUpdate() {
updatedAt = OffsetDateTime.now();
}
}
14 changes: 14 additions & 0 deletions src/main/java/com/dnd/runus/domain/common/Coordinate.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.dnd.runus.domain.common;

/**
* @param longitude 경도
* @param latitude 위도
* @param altitude 고도
*/
public record Coordinate(double longitude, double latitude, double altitude) {
public static final double NULL_ORDINATE = Double.NaN;

public Coordinate(double longitude, double latitude) {
this(longitude, latitude, NULL_ORDINATE);
}
}
3 changes: 3 additions & 0 deletions src/main/java/com/dnd/runus/domain/level/Level.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package com.dnd.runus.domain.level;

public record Level(long levelId, int requiredExp) {}
20 changes: 20 additions & 0 deletions src/main/java/com/dnd/runus/domain/member/Member.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.dnd.runus.domain.member;

import com.dnd.runus.domain.badge.Badge;
import com.dnd.runus.domain.level.Level;
import com.dnd.runus.global.constant.MemberRole;
import lombok.Builder;

import java.time.OffsetDateTime;

@Builder
public record Member(
long memberId,
OffsetDateTime createdAt,
OffsetDateTime updatedAt,
MemberRole role,
String nickname,
Badge mainBadge,
int weightKg,
Level level,
int currentExp) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.dnd.runus.domain.member;

import java.util.Optional;

public interface MemberRepository {
Optional<Member> findById(long id);

Member save(Member member);
}
8 changes: 8 additions & 0 deletions src/main/java/com/dnd/runus/domain/member/SocialProfile.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.dnd.runus.domain.member;

import com.dnd.runus.global.constant.SocialType;
import lombok.Builder;

@Builder
public record SocialProfile(
long socialProfileId, SocialType socialType, String oauthId, String oauthEmail, long memberId) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.dnd.runus.domain.member;

import com.dnd.runus.global.constant.SocialType;

import java.util.Optional;

public interface SocialProfileRepository {
Optional<SocialProfile> findById(Long socialProfileId);

Optional<SocialProfile> findBySocialTypeAndOauthId(SocialType socialType, String oauthId);

SocialProfile save(SocialProfile socialProfile);

void updateOauthEmail(long socialProfileId, String oauthEmail);
}
44 changes: 0 additions & 44 deletions src/main/java/com/dnd/runus/domain/member/entity/Member.java

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -27,7 +27,7 @@ public class OauthController {
@ApiErrorType({ErrorType.UNSUPPORTED_SOCIAL_TYPE, ErrorType.MALFORMED_ACCESS_TOKEN, ErrorType.UNSUPPORTED_JWT_TOKEN
})
@PostMapping
public TokenResponse SignIn(@Valid @RequestBody OauthRequest request) {
return oauthService.SignIn(request);
public TokenResponse signIn(@Valid @RequestBody OauthRequest request) {
return oauthService.signIn(request);
}
}
58 changes: 33 additions & 25 deletions src/main/java/com/dnd/runus/domain/oauth/service/OauthService.java
Original file line number Diff line number Diff line change
@@ -1,24 +1,26 @@
package com.dnd.runus.domain.oauth.service;

import com.dnd.runus.auth.exception.AuthException;
import com.dnd.runus.auth.oidc.provider.OidcProviderFactory;
import com.dnd.runus.auth.token.TokenProviderModule;
import com.dnd.runus.auth.token.dto.AuthTokenDto;
import com.dnd.runus.domain.member.entity.Member;
import com.dnd.runus.domain.member.entity.PersonalProfile;
import com.dnd.runus.domain.member.entity.SocialProfile;
import com.dnd.runus.domain.member.repository.MemberRepository;
import com.dnd.runus.domain.member.repository.SocialProfileRepository;
import com.dnd.runus.domain.member.Member;
import com.dnd.runus.domain.member.MemberRepository;
import com.dnd.runus.domain.member.SocialProfile;
import com.dnd.runus.domain.member.SocialProfileRepository;
import com.dnd.runus.domain.oauth.dto.request.OauthRequest;
import com.dnd.runus.domain.oauth.dto.response.TokenResponse;
import com.dnd.runus.global.constant.MemberRole;
import com.dnd.runus.global.constant.SocialType;
import com.dnd.runus.global.exception.BusinessException;
import com.dnd.runus.global.exception.type.ErrorType;
import io.jsonwebtoken.Claims;
import io.micrometer.common.util.StringUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Slf4j
@Service
@RequiredArgsConstructor
public class OauthService {
@@ -36,41 +38,47 @@ public class OauthService {
* @return TokenResponse
*/
@Transactional
public TokenResponse SignIn(OauthRequest request) {
public TokenResponse signIn(OauthRequest request) {

Claims claim = oidcProviderFactory.getClaims(request.socialType(), request.idToken());
String oAuthId = claim.getSubject();
String oauthId = claim.getSubject();
String email = String.valueOf(claim.get("email"));
if (StringUtils.isBlank(email)) {
log.warn("Failed to get email from idToken! type: {}, claim: {}", request.socialType(), claim);
throw new AuthException(ErrorType.FAILED_AUTHENTICATION, "Failed to get email from idToken");
}

// 회원 가입 안되있으면 회원가입 진행
SocialProfile socialProfile = socialProfileRepository
.findBySocialTypeAndOauthId(request.socialType(), oAuthId)
.orElseGet(() -> crateMember(oAuthId, email, request.socialType(), request.nickName()));
.findBySocialTypeAndOauthId(request.socialType(), oauthId)
.orElseGet(() -> createMember(oauthId, email, request.socialType(), request.nickName()));

// 이메일 변경(사용자가 애플의 이메일을 변경한 후 로그인하면 해당 이메일 변경해줘야함. -> 리젝 사유 될 수 있음)
if (!email.equals(socialProfile.getOauthEmail())) {
socialProfile.updateEmail(email);
if (!email.equals(socialProfile.oauthEmail())) {
socialProfileRepository.updateOauthEmail(socialProfile.socialProfileId(), email);
}

AuthTokenDto tokenDto = tokenProviderModule.generate(String.valueOf(socialProfile.getMemberId()));
AuthTokenDto tokenDto = tokenProviderModule.generate(String.valueOf(socialProfile.memberId()));

return TokenResponse.from(tokenDto);
}

private SocialProfile crateMember(String authId, String email, SocialType socialType, String nickName) {
if (socialProfileRepository.existsByOauthEmail(email)) {
throw new BusinessException(ErrorType.VIOLATION_OCCURRED, "이미 존재하는 이메일");
}

private SocialProfile createMember(String oauthId, String email, SocialType socialType, String nickname) {
// todo 체중 디폴트는 온보딩으로
// 현재는 들어갈 때 임시로 70이 들어가도록 하드 코딩해둠
Long memberId = memberRepository
.save(Member.of(
MemberRole.USER,
nickName,
PersonalProfile.builder().weightKg(70).build()))
.getId();
long memberId = memberRepository
.save(Member.builder()
.nickname(nickname)
.weightKg(70)
.role(MemberRole.USER)
.build())
.memberId();

return socialProfileRepository.save(SocialProfile.of(socialType, authId, email, memberId));
return socialProfileRepository.save(SocialProfile.builder()
.socialType(socialType)
.oauthId(oauthId)
.oauthEmail(email)
.memberId(memberId)
.build());
}
}

This file was deleted.

This file was deleted.

This file was deleted.

3 changes: 3 additions & 0 deletions src/main/java/com/dnd/runus/domain/running/RunningEmoji.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package com.dnd.runus.domain.running;

public record RunningEmoji(long emojiId, String emojiImageUrl) {}
22 changes: 22 additions & 0 deletions src/main/java/com/dnd/runus/domain/running/RunningRecord.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.dnd.runus.domain.running;

import com.dnd.runus.domain.common.Coordinate;
import com.dnd.runus.domain.member.Member;
import lombok.Builder;

import java.time.OffsetDateTime;
import java.util.List;

@Builder
public record RunningRecord(
long runningId,
Member member,
int distanceMeter,
int durationSeconds,
double calorie,
double averagePace,
OffsetDateTime startAt,
OffsetDateTime endAt,
List<Coordinate> route,
String location,
RunningEmoji emoji) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.dnd.runus.global.constant;

public class GeometryConstant {
public static final int SRID = 4326;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package com.dnd.runus.infrastructure.persistence;

public class InfrastructurePersistencePackage {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.dnd.runus.infrastructure.persistence.domain.member;

import com.dnd.runus.domain.member.Member;
import com.dnd.runus.domain.member.MemberRepository;
import com.dnd.runus.infrastructure.persistence.jpa.member.JpaMemberRepository;
import com.dnd.runus.infrastructure.persistence.jpa.member.entity.MemberEntity;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;

import java.util.Optional;

@Repository
@RequiredArgsConstructor
public class MemberRepositoryImpl implements MemberRepository {
private final JpaMemberRepository jpaMemberRepository;

@Override
public Optional<Member> findById(long id) {
return jpaMemberRepository.findById(id).map(MemberEntity::toDomain);
}

@Override
public Member save(Member member) {
return jpaMemberRepository.save(MemberEntity.from(member)).toDomain();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.dnd.runus.infrastructure.persistence.domain.member;

import com.dnd.runus.domain.member.SocialProfile;
import com.dnd.runus.domain.member.SocialProfileRepository;
import com.dnd.runus.global.constant.SocialType;
import com.dnd.runus.infrastructure.persistence.jpa.member.JpaSocialProfileRepository;
import com.dnd.runus.infrastructure.persistence.jpa.member.entity.SocialProfileEntity;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;

import java.util.Optional;

@Repository
@RequiredArgsConstructor
public class SocialProfileRepositoryImpl implements SocialProfileRepository {
private final JpaSocialProfileRepository jpaSocialProfileRepository;

@Override
public Optional<SocialProfile> findById(Long socialProfileId) {
return jpaSocialProfileRepository.findById(socialProfileId).map(SocialProfileEntity::toDomain);
}

@Override
public Optional<SocialProfile> findBySocialTypeAndOauthId(SocialType socialType, String oauthId) {
return jpaSocialProfileRepository
.findBySocialTypeAndOauthId(socialType, oauthId)
.map(SocialProfileEntity::toDomain);
}

@Override
public SocialProfile save(SocialProfile socialProfile) {
SocialProfileEntity entity = jpaSocialProfileRepository.save(SocialProfileEntity.of(
socialProfile.socialType(),
socialProfile.oauthId(),
socialProfile.oauthEmail(),
socialProfile.memberId()));
return entity.toDomain();
}

@Override
public void updateOauthEmail(long socialProfileId, String oauthEmail) {
jpaSocialProfileRepository
.findById(socialProfileId)
.ifPresent(socialProfileEntity -> socialProfileEntity.updateOauthEmail(oauthEmail));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.dnd.runus.infrastructure.persistence.jooq.member;

import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;

@Repository
@RequiredArgsConstructor
public class MemberJooqRepository {
// private final MemberJooqDao memberJooqDao;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.dnd.runus.infrastructure.persistence.jpa.badge.entity;

import com.dnd.runus.domain.badge.Badge;
import com.dnd.runus.domain.badge.BadgeAchievement;
import com.dnd.runus.domain.common.BaseTimeEntity;
import com.dnd.runus.infrastructure.persistence.jpa.member.entity.MemberEntity;
import jakarta.persistence.*;
import jakarta.validation.constraints.NotNull;
import lombok.Getter;
import lombok.NoArgsConstructor;

import static jakarta.persistence.FetchType.LAZY;
import static lombok.AccessLevel.PROTECTED;

@Getter
@Entity(name = "badge_achievement")
@NoArgsConstructor(access = PROTECTED)
public class BadgeAchievementEntity extends BaseTimeEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@NotNull
private Long badgeId;

@NotNull
@ManyToOne(fetch = LAZY)
private MemberEntity memberEntity;

public static BadgeAchievementEntity from(BadgeAchievement badgeAchievement) {
BadgeAchievementEntity badgeAchievementEntity = new BadgeAchievementEntity();
badgeAchievementEntity.badgeId = badgeAchievement.badge().badgeId();
badgeAchievementEntity.memberEntity = MemberEntity.from(badgeAchievement.member());
return badgeAchievementEntity;
}

public BadgeAchievement toDomain(Badge badge) {
return new BadgeAchievement(badge, memberEntity.toDomain(), getCreatedAt(), getUpdatedAt());
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.dnd.runus.domain.badge.entity;
package com.dnd.runus.infrastructure.persistence.jpa.badge.entity;

import com.dnd.runus.domain.badge.Badge;
import com.dnd.runus.global.constant.BadgeType;
import jakarta.persistence.*;
import jakarta.validation.constraints.NotNull;
@@ -11,9 +12,9 @@
import static lombok.AccessLevel.PROTECTED;

@Getter
@Entity
@Entity(name = "badge")
@NoArgsConstructor(access = PROTECTED)
public class Badge {
public class BadgeEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@@ -36,13 +37,14 @@ public class Badge {
@NotNull
private Integer requiredValue;

public static Badge of(String name, String description, String imagePath, BadgeType type, Integer requiredValue) {
Badge badge = new Badge();
badge.name = name;
badge.description = description;
badge.imagePath = imagePath;
badge.type = type;
badge.requiredValue = requiredValue;
return badge;
public static BadgeEntity from(Badge badge) {
BadgeEntity badgeEntity = new BadgeEntity();
badgeEntity.id = badge.badgeId();
badgeEntity.name = badge.name();
badgeEntity.description = badge.description();
badgeEntity.imagePath = badge.imageUrl();
badgeEntity.type = badge.type();
badgeEntity.requiredValue = badge.requiredValue();
return badgeEntity;
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.dnd.runus.domain.level.entity;
package com.dnd.runus.infrastructure.persistence.jpa.level.entity;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
@@ -11,9 +11,9 @@
import static lombok.AccessLevel.PROTECTED;

@Getter
@Entity
@Entity(name = "level")
@NoArgsConstructor(access = PROTECTED)
public class Level {
public class LevelEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.dnd.runus.infrastructure.persistence.jpa.member;

import com.dnd.runus.infrastructure.persistence.jpa.member.entity.MemberEntity;
import org.springframework.data.jpa.repository.JpaRepository;

public interface JpaMemberRepository extends JpaRepository<MemberEntity, Long> {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.dnd.runus.infrastructure.persistence.jpa.member;

import com.dnd.runus.global.constant.SocialType;
import com.dnd.runus.infrastructure.persistence.jpa.member.entity.SocialProfileEntity;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.Optional;

public interface JpaSocialProfileRepository extends JpaRepository<SocialProfileEntity, Long> {
Optional<SocialProfileEntity> findBySocialTypeAndOauthId(SocialType socialType, String oauthId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package com.dnd.runus.infrastructure.persistence.jpa.member.entity;

import com.dnd.runus.domain.badge.Badge;
import com.dnd.runus.domain.common.BaseTimeEntity;
import com.dnd.runus.domain.level.Level;
import com.dnd.runus.domain.member.Member;
import com.dnd.runus.global.constant.MemberRole;
import jakarta.persistence.*;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.Getter;
import lombok.NoArgsConstructor;

import static jakarta.persistence.EnumType.STRING;
import static lombok.AccessLevel.PROTECTED;

@Getter
@Entity(name = "member")
@NoArgsConstructor(access = PROTECTED)
public class MemberEntity extends BaseTimeEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@NotNull
@Enumerated(STRING)
private MemberRole role;

@NotNull
@Size(max = 20)
private String nickname;

private Integer weightKg;

private Long mainBadgeId;

public static MemberEntity from(Member member) {
MemberEntity memberEntity = new MemberEntity();
memberEntity.id = member.memberId();
memberEntity.role = member.role();
memberEntity.nickname = member.nickname();
memberEntity.weightKg = member.weightKg();
memberEntity.mainBadgeId =
member.mainBadge() == null ? null : member.mainBadge().badgeId();
return memberEntity;
}

public Member toDomain() {
return toDomain(null, null, -1);
}

public Member toDomain(Badge mainBadge, Level level, int currentExp) {
return Member.builder()
.memberId(id)
.createdAt(getCreatedAt())
.updatedAt(getUpdatedAt())
.role(role)
.nickname(nickname)
.weightKg(weightKg)
.mainBadge(mainBadge)
.level(level)
.currentExp(currentExp)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.dnd.runus.domain.member.entity;
package com.dnd.runus.infrastructure.persistence.jpa.member.entity;

import com.dnd.runus.domain.common.BaseTimeEntity;
import com.dnd.runus.domain.level.Level;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
@@ -12,9 +13,9 @@
import static lombok.AccessLevel.PROTECTED;

@Getter
@Entity
@Entity(name = "member_level")
@NoArgsConstructor(access = PROTECTED)
public class MemberLevel extends BaseTimeEntity {
public class MemberLevelEntity extends BaseTimeEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@@ -28,11 +29,15 @@ public class MemberLevel extends BaseTimeEntity {
@NotNull
private Integer exp;

public static MemberLevel of(Long memberId, Long levelId, Integer exp) {
MemberLevel memberLevel = new MemberLevel();
memberLevel.memberId = memberId;
memberLevel.levelId = levelId;
memberLevel.exp = exp;
return memberLevel;
public static MemberLevelEntity of(Long memberId, Long levelId, Integer exp) {
MemberLevelEntity memberLevelEntity = new MemberLevelEntity();
memberLevelEntity.memberId = memberId;
memberLevelEntity.levelId = levelId;
memberLevelEntity.exp = exp;
return memberLevelEntity;
}

public Level toDomain() {
return new Level(levelId, exp);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package com.dnd.runus.infrastructure.persistence.jpa.member.entity;

import com.dnd.runus.domain.common.BaseTimeEntity;
import com.dnd.runus.domain.member.SocialProfile;
import com.dnd.runus.global.constant.SocialType;
import jakarta.persistence.*;
import jakarta.validation.constraints.NotNull;
import lombok.Getter;
import lombok.NoArgsConstructor;

import static jakarta.persistence.EnumType.STRING;
import static lombok.AccessLevel.PROTECTED;

@Getter
@Entity(name = "social_profile")
@NoArgsConstructor(access = PROTECTED)
public class SocialProfileEntity extends BaseTimeEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@NotNull
@Enumerated(STRING)
private SocialType socialType;

@NotNull
private String oauthId;

@NotNull
private String oauthEmail;

@NotNull
private Long memberId;

public static SocialProfileEntity of(SocialType socialType, String oauthId, String oauthEmail, Long memberId) {
SocialProfileEntity socialProfileEntity = new SocialProfileEntity();
socialProfileEntity.socialType = socialType;
socialProfileEntity.oauthId = oauthId;
socialProfileEntity.oauthEmail = oauthEmail;
socialProfileEntity.memberId = memberId;
return socialProfileEntity;
}

public SocialProfile toDomain() {
return new SocialProfile(id, socialType, oauthId, oauthEmail, memberId);
}

public void updateOauthEmail(String oauthEmail) {
this.oauthEmail = oauthEmail;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.dnd.runus.infrastructure.persistence.jpa.running;

import com.dnd.runus.infrastructure.persistence.jpa.running.entity.RunningEmojiEntity;
import org.springframework.data.jpa.repository.JpaRepository;

public interface JpaRunningEmojiRepository extends JpaRepository<RunningEmojiEntity, Long> {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.dnd.runus.infrastructure.persistence.jpa.running;

import com.dnd.runus.infrastructure.persistence.jpa.running.entity.RunningRecordEntity;
import org.springframework.data.jpa.repository.JpaRepository;

public interface JpaRunningRecordRepository extends JpaRepository<RunningRecordEntity, Long> {}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.dnd.runus.domain.runing.entity;
package com.dnd.runus.infrastructure.persistence.jpa.running.entity;

import com.dnd.runus.domain.common.BaseTimeEntity;
import jakarta.persistence.Entity;
@@ -12,9 +12,9 @@
import static lombok.AccessLevel.PROTECTED;

@Getter
@Entity
@Entity(name = "running_emoji")
@NoArgsConstructor(access = PROTECTED)
public class RunningEmoji extends BaseTimeEntity {
public class RunningEmojiEntity extends BaseTimeEntity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package com.dnd.runus.infrastructure.persistence.jpa.running.entity;

import com.dnd.runus.domain.common.BaseTimeEntity;
import com.dnd.runus.domain.common.Coordinate;
import com.dnd.runus.domain.running.RunningEmoji;
import com.dnd.runus.domain.running.RunningRecord;
import com.dnd.runus.infrastructure.persistence.jpa.member.entity.MemberEntity;
import jakarta.persistence.*;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.LineString;
import org.locationtech.jts.geom.PrecisionModel;

import java.time.OffsetDateTime;
import java.util.Arrays;
import java.util.List;

import static com.dnd.runus.global.constant.GeometryConstant.SRID;
import static jakarta.persistence.FetchType.LAZY;
import static lombok.AccessLevel.PRIVATE;
import static lombok.AccessLevel.PROTECTED;
import static org.locationtech.jts.geom.PrecisionModel.FLOATING;

@Getter
@Entity(name = "running_record")
@NoArgsConstructor(access = PROTECTED)
@Builder(access = PRIVATE)
@AllArgsConstructor(access = PRIVATE)
public class RunningRecordEntity extends BaseTimeEntity {

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

@NotNull
@ManyToOne(fetch = LAZY)
private MemberEntity memberEntity;

@NotNull
private Integer distanceMeter;

@NotNull
private Integer durationSeconds;

@NotNull
private Double calorie;

@NotNull
private Double averagePace;

@NotNull
private OffsetDateTime startAt;

@NotNull
private OffsetDateTime endAt;

@Column(columnDefinition = "geometry(LineString, " + SRID + ")")
private LineString route;

@NotNull
private String location;

@NotNull
private Long emojiId;

public static RunningRecordEntity from(RunningRecord runningRecord) {
org.locationtech.jts.geom.Coordinate[] coordinates = runningRecord.route().stream()
.map(coordinate ->
new org.locationtech.jts.geom.Coordinate(coordinate.longitude(), coordinate.latitude()))
.toArray(org.locationtech.jts.geom.Coordinate[]::new);

GeometryFactory geometryFactory = new GeometryFactory(new PrecisionModel(FLOATING), SRID);
LineString route = geometryFactory.createLineString(coordinates);

return RunningRecordEntity.builder()
.id(runningRecord.runningId())
.memberEntity(MemberEntity.from(runningRecord.member()))
.distanceMeter(runningRecord.distanceMeter())
.durationSeconds(runningRecord.durationSeconds())
.calorie(runningRecord.calorie())
.averagePace(runningRecord.averagePace())
.startAt(runningRecord.startAt())
.endAt(runningRecord.endAt())
.route(route)
.location(runningRecord.location())
.emojiId(runningRecord.emoji().emojiId())
.build();
}

public RunningRecord toDomain() {
List<Coordinate> coordinates = Arrays.stream(route.getCoordinates())
.map(coordinate -> new Coordinate(coordinate.x, coordinate.y, coordinate.z))
.toList();

return RunningRecord.builder()
.runningId(id)
.member(memberEntity.toDomain())
.distanceMeter(distanceMeter)
.durationSeconds(durationSeconds)
.calorie(calorie)
.averagePace(averagePace)
.startAt(startAt)
.endAt(endAt)
.route(coordinates)
.location(location)
.emoji(new RunningEmoji(emojiId, null))
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.dnd.runus.infrastructure.persistence.jpa.scale.entity;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.validation.constraints.NotNull;
import lombok.Getter;
import lombok.NoArgsConstructor;

import static lombok.AccessLevel.PROTECTED;

@Getter
@Entity(name = "scale")
@NoArgsConstructor(access = PROTECTED)
public class ScaleEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@NotNull
private String name;

@NotNull
private Integer sizeMeter;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.dnd.runus.infrastructure.persistence.annotation;

import com.dnd.runus.config.TestcontainersConfig;
import com.dnd.runus.global.config.JpaConfig;
import com.dnd.runus.infrastructure.persistence.InfrastructurePersistencePackage;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Import;
import org.testcontainers.junit.jupiter.Testcontainers;

import java.lang.annotation.*;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Testcontainers
@DataJpaTest
@ComponentScan(basePackageClasses = {InfrastructurePersistencePackage.class})
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) // H2 사용 금지
@Import({JpaConfig.class, TestcontainersConfig.class})
public @interface RepositoryTest {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.dnd.runus.infrastructure.persistence.domain.member;

import com.dnd.runus.domain.member.Member;
import com.dnd.runus.domain.member.MemberRepository;
import com.dnd.runus.global.constant.MemberRole;
import com.dnd.runus.infrastructure.persistence.annotation.RepositoryTest;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;

import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;

@RepositoryTest
class MemberRepositoryImplTest {
@Autowired
private MemberRepository memberRepository;

@Test
@DisplayName("findById 메서드는 Member를 반환한다.")
void findById() {
Member member = memberRepository.findById(1L).orElse(null);
System.out.println(member);
}

@Test
@DisplayName("save 메서드는 Member를 저장하고 id와 함께 반환한다.")
void save() {
Member member = Member.builder()
.role(MemberRole.USER)
.nickname("nickname")
.weightKg(60)
.mainBadge(null)
.createdAt(null)
.updatedAt(null)
.level(null)
.currentExp(0)
.build();
Member savedMember = memberRepository.save(member);
assertNotEquals(0, savedMember.memberId());
assertNotNull(savedMember.createdAt());
assertNotNull(savedMember.updatedAt());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.dnd.runus.infrastructure.persistence.jpa.member.entity;

import com.dnd.runus.domain.badge.Badge;
import com.dnd.runus.domain.level.Level;
import com.dnd.runus.domain.member.Member;
import com.dnd.runus.global.constant.BadgeType;
import com.dnd.runus.global.constant.MemberRole;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import java.time.OffsetDateTime;

import static org.junit.jupiter.api.Assertions.assertEquals;

class MemberEntityTest {
private Member member;

@BeforeEach
void setUp() {
Badge badge =
new Badge(1L, "badge name", "badge description", "https://badge.com", BadgeType.DISTANCE_METER, 1000);
member = Member.builder()
.memberId(1L)
.role(MemberRole.USER)
.nickname("nickname")
.weightKg(60)
.mainBadge(badge)
.createdAt(OffsetDateTime.now())
.updatedAt(OffsetDateTime.now())
.level(new Level(1L, 100))
.currentExp(0)
.build();
}

@Test
@DisplayName("올바른 Member가 주어질 때, MemberEntity.from() 메서드는 성공한다.")
void from() {
MemberEntity memberEntity = MemberEntity.from(member);
assertEquals(member.memberId(), memberEntity.getId());
}

@Test
@DisplayName("올바른 MemberEntity가 주어질 때, MemberEntity.toDomain() 메서드는 성공한다.")
void toDomain() {
MemberEntity memberEntity = MemberEntity.from(member);
Member member = memberEntity.toDomain();
assertEquals(memberEntity.getId(), member.memberId());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.dnd.runus.infrastructure.persistence.jpa.running.entity;

import com.dnd.runus.domain.common.Coordinate;
import com.dnd.runus.domain.member.Member;
import com.dnd.runus.domain.running.RunningEmoji;
import com.dnd.runus.domain.running.RunningRecord;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import java.time.OffsetDateTime;
import java.util.List;

import static org.junit.jupiter.api.Assertions.assertEquals;

class RunningRecordEntityTest {
private RunningRecord runningRecord;

@BeforeEach
void setUp() {
runningRecord = RunningRecord.builder()
.runningId(1L)
.member(Member.builder().memberId(1L).build())
.distanceMeter(500)
.durationSeconds(1)
.calorie(1.0)
.averagePace(1.0)
.startAt(OffsetDateTime.now())
.endAt(OffsetDateTime.now())
.route(List.of(new Coordinate(128.0, 36.0), new Coordinate(128.0, 37.0)))
.location("location 1")
.emoji(new RunningEmoji(1L, "https://emoji1.com"))
.build();
}

@Test
@DisplayName("올바른 RunningRecord가 주어질 때, RunningRecordEntity.from() 메서드는 성공한다.")
void from() {
RunningRecordEntity runningRecordEntity = RunningRecordEntity.from(runningRecord);
assertEquals(runningRecord.runningId(), runningRecordEntity.getId());
}

@Test
@DisplayName("올바른 RunningRecordEntity가 주어질 때, RunningRecordEntity.toDomain() 메서드는 성공한다.")
void toDomain() {
RunningRecordEntity runningRecordEntity = RunningRecordEntity.from(runningRecord);
RunningRecord runningRecord = runningRecordEntity.toDomain();
assertEquals(runningRecordEntity.getId(), runningRecord.runningId());
}
}