diff --git a/src/main/java/com/dnd/runus/auth/userdetails/UserDetailsServiceImpl.java b/src/main/java/com/dnd/runus/auth/userdetails/UserDetailsServiceImpl.java index 2bcb10ff..d1e893b2 100644 --- a/src/main/java/com/dnd/runus/auth/userdetails/UserDetailsServiceImpl.java +++ b/src/main/java/com/dnd/runus/auth/userdetails/UserDetailsServiceImpl.java @@ -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); } diff --git a/src/main/java/com/dnd/runus/domain/badge/Badge.java b/src/main/java/com/dnd/runus/domain/badge/Badge.java new file mode 100644 index 00000000..8641de26 --- /dev/null +++ b/src/main/java/com/dnd/runus/domain/badge/Badge.java @@ -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) {} diff --git a/src/main/java/com/dnd/runus/domain/badge/BadgeAchievement.java b/src/main/java/com/dnd/runus/domain/badge/BadgeAchievement.java new file mode 100644 index 00000000..94801b93 --- /dev/null +++ b/src/main/java/com/dnd/runus/domain/badge/BadgeAchievement.java @@ -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) {} diff --git a/src/main/java/com/dnd/runus/domain/badge/entity/BadgeAchievement.java b/src/main/java/com/dnd/runus/domain/badge/entity/BadgeAchievement.java deleted file mode 100644 index 2361f7e4..00000000 --- a/src/main/java/com/dnd/runus/domain/badge/entity/BadgeAchievement.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.dnd.runus.domain.badge.entity; - -import com.dnd.runus.domain.member.entity.Member; -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 -@NoArgsConstructor(access = PROTECTED) -public class BadgeAchievement { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - @NotNull - private Long badgeId; - - @NotNull - @ManyToOne(fetch = LAZY) - private Member member; - - public static BadgeAchievement of(Badge badge, Member member) { - BadgeAchievement badgeAchievement = new BadgeAchievement(); - badgeAchievement.badgeId = badge.getId(); - badgeAchievement.member = member; - return badgeAchievement; - } -} diff --git a/src/main/java/com/dnd/runus/domain/common/BaseTimeEntity.java b/src/main/java/com/dnd/runus/domain/common/BaseTimeEntity.java index af870e7c..dd5f8ef3 100644 --- a/src/main/java/com/dnd/runus/domain/common/BaseTimeEntity.java +++ b/src/main/java/com/dnd/runus/domain/common/BaseTimeEntity.java @@ -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(); + } } diff --git a/src/main/java/com/dnd/runus/domain/common/Coordinate.java b/src/main/java/com/dnd/runus/domain/common/Coordinate.java new file mode 100644 index 00000000..16d115d7 --- /dev/null +++ b/src/main/java/com/dnd/runus/domain/common/Coordinate.java @@ -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); + } +} diff --git a/src/main/java/com/dnd/runus/domain/level/Level.java b/src/main/java/com/dnd/runus/domain/level/Level.java new file mode 100644 index 00000000..2589027f --- /dev/null +++ b/src/main/java/com/dnd/runus/domain/level/Level.java @@ -0,0 +1,3 @@ +package com.dnd.runus.domain.level; + +public record Level(long levelId, int requiredExp) {} diff --git a/src/main/java/com/dnd/runus/domain/member/Member.java b/src/main/java/com/dnd/runus/domain/member/Member.java new file mode 100644 index 00000000..a24f3959 --- /dev/null +++ b/src/main/java/com/dnd/runus/domain/member/Member.java @@ -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) {} diff --git a/src/main/java/com/dnd/runus/domain/member/MemberRepository.java b/src/main/java/com/dnd/runus/domain/member/MemberRepository.java new file mode 100644 index 00000000..09db06ac --- /dev/null +++ b/src/main/java/com/dnd/runus/domain/member/MemberRepository.java @@ -0,0 +1,9 @@ +package com.dnd.runus.domain.member; + +import java.util.Optional; + +public interface MemberRepository { + Optional findById(long id); + + Member save(Member member); +} diff --git a/src/main/java/com/dnd/runus/domain/member/SocialProfile.java b/src/main/java/com/dnd/runus/domain/member/SocialProfile.java new file mode 100644 index 00000000..4feddc1a --- /dev/null +++ b/src/main/java/com/dnd/runus/domain/member/SocialProfile.java @@ -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) {} diff --git a/src/main/java/com/dnd/runus/domain/member/SocialProfileRepository.java b/src/main/java/com/dnd/runus/domain/member/SocialProfileRepository.java new file mode 100644 index 00000000..b4df8b45 --- /dev/null +++ b/src/main/java/com/dnd/runus/domain/member/SocialProfileRepository.java @@ -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 findById(Long socialProfileId); + + Optional findBySocialTypeAndOauthId(SocialType socialType, String oauthId); + + SocialProfile save(SocialProfile socialProfile); + + void updateOauthEmail(long socialProfileId, String oauthEmail); +} diff --git a/src/main/java/com/dnd/runus/domain/member/entity/Member.java b/src/main/java/com/dnd/runus/domain/member/entity/Member.java deleted file mode 100644 index f5739dd2..00000000 --- a/src/main/java/com/dnd/runus/domain/member/entity/Member.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.dnd.runus.domain.member.entity; - -import com.dnd.runus.domain.common.BaseTimeEntity; -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 lombok.experimental.Accessors; - -import static jakarta.persistence.EnumType.STRING; -import static lombok.AccessLevel.PROTECTED; - -@Getter -@Entity -@NoArgsConstructor(access = PROTECTED) -public class Member extends BaseTimeEntity { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - @NotNull - @Enumerated(STRING) - private MemberRole role; - - @NotNull - @Size(max = 20) - private String nickname; - - private Long mainBadgeId; - - @Embedded - @Accessors(fluent = true) - private PersonalProfile personal; - - public static Member of(MemberRole role, String nickname, PersonalProfile personal) { - Member member = new Member(); - member.role = role; - member.nickname = nickname; - member.personal = personal; - return member; - } -} diff --git a/src/main/java/com/dnd/runus/domain/member/entity/PersonalProfile.java b/src/main/java/com/dnd/runus/domain/member/entity/PersonalProfile.java deleted file mode 100644 index e99ac8a9..00000000 --- a/src/main/java/com/dnd/runus/domain/member/entity/PersonalProfile.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.dnd.runus.domain.member.entity; - -import jakarta.persistence.Embeddable; -import jakarta.persistence.Enumerated; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; - -import java.time.LocalDate; - -import static jakarta.persistence.EnumType.STRING; -import static lombok.AccessLevel.PRIVATE; -import static lombok.AccessLevel.PROTECTED; - -@Getter -@Builder -@NoArgsConstructor(access = PROTECTED) -@AllArgsConstructor(access = PRIVATE) -@Embeddable -public class PersonalProfile { - @Enumerated(STRING) - private Gender gender; - - private Integer heightCm; - - private Integer weightKg; - - private LocalDate birthDay; - - public enum Gender { - MALE, - FEMALE - } -} diff --git a/src/main/java/com/dnd/runus/domain/member/entity/SocialProfile.java b/src/main/java/com/dnd/runus/domain/member/entity/SocialProfile.java deleted file mode 100644 index d11f6570..00000000 --- a/src/main/java/com/dnd/runus/domain/member/entity/SocialProfile.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.dnd.runus.domain.member.entity; - -import com.dnd.runus.domain.common.BaseTimeEntity; -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 -@NoArgsConstructor(access = PROTECTED) -public class SocialProfile 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 SocialProfile of(SocialType socialType, String oauthId, String oauthEmail, Long memberId) { - SocialProfile socialProfile = new SocialProfile(); - socialProfile.socialType = socialType; - socialProfile.oauthId = oauthId; - socialProfile.oauthEmail = oauthEmail; - socialProfile.memberId = memberId; - return socialProfile; - } - - public void updateEmail(String email) { - this.oauthEmail = email; - } -} diff --git a/src/main/java/com/dnd/runus/domain/member/repository/MemberRepository.java b/src/main/java/com/dnd/runus/domain/member/repository/MemberRepository.java deleted file mode 100644 index a7e009e5..00000000 --- a/src/main/java/com/dnd/runus/domain/member/repository/MemberRepository.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.dnd.runus.domain.member.repository; - -import com.dnd.runus.domain.member.entity.Member; -import org.springframework.data.jpa.repository.JpaRepository; - -public interface MemberRepository extends JpaRepository {} diff --git a/src/main/java/com/dnd/runus/domain/member/repository/SocialProfileRepository.java b/src/main/java/com/dnd/runus/domain/member/repository/SocialProfileRepository.java deleted file mode 100644 index 8b18d031..00000000 --- a/src/main/java/com/dnd/runus/domain/member/repository/SocialProfileRepository.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.dnd.runus.domain.member.repository; - -import com.dnd.runus.domain.member.entity.SocialProfile; -import com.dnd.runus.global.constant.SocialType; -import org.springframework.data.jpa.repository.JpaRepository; - -import java.util.Optional; - -public interface SocialProfileRepository extends JpaRepository { - Optional findBySocialTypeAndOauthId(SocialType socialType, String oauthId); - - boolean existsByOauthEmail(String email); - - boolean existsBySocialTypeAndOauthId(SocialType socialType, String oauthId); -} diff --git a/src/main/java/com/dnd/runus/domain/oauth/controller/OauthController.java b/src/main/java/com/dnd/runus/domain/oauth/controller/OauthController.java index 0c0fbbfc..a9a7475b 100644 --- a/src/main/java/com/dnd/runus/domain/oauth/controller/OauthController.java +++ b/src/main/java/com/dnd/runus/domain/oauth/controller/OauthController.java @@ -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); } } diff --git a/src/main/java/com/dnd/runus/domain/oauth/service/OauthService.java b/src/main/java/com/dnd/runus/domain/oauth/service/OauthService.java index 4a61043f..6e87ea32 100644 --- a/src/main/java/com/dnd/runus/domain/oauth/service/OauthService.java +++ b/src/main/java/com/dnd/runus/domain/oauth/service/OauthService.java @@ -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()); } } diff --git a/src/main/java/com/dnd/runus/domain/runing/entity/RunningRecord.java b/src/main/java/com/dnd/runus/domain/runing/entity/RunningRecord.java deleted file mode 100644 index a1d789cb..00000000 --- a/src/main/java/com/dnd/runus/domain/runing/entity/RunningRecord.java +++ /dev/null @@ -1,85 +0,0 @@ -package com.dnd.runus.domain.runing.entity; - -import com.dnd.runus.domain.common.BaseTimeEntity; -import com.dnd.runus.domain.member.entity.Member; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.ManyToOne; -import jakarta.validation.constraints.NotNull; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; -import org.locationtech.jts.geom.LineString; - -import java.time.Instant; - -import static jakarta.persistence.FetchType.LAZY; -import static lombok.AccessLevel.PROTECTED; - -@Getter -@Entity -@NoArgsConstructor(access = PROTECTED) -public class RunningRecord extends BaseTimeEntity { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - @NotNull - @ManyToOne(fetch = LAZY) - private Member member; - - @NotNull - private Double distance; - - @NotNull - private Integer durationSeconds; - - @NotNull - private Double calorie; - - @NotNull - private Double averagePace; - - @NotNull - private Instant startAt; - - @NotNull - private Instant endAt; - - @Column(columnDefinition = "geometry(LineString, 4326)") - private LineString route; - - @NotNull - private String location; - - @NotNull - private Long emojiId; - - @Builder - private RunningRecord( - Member member, - Double distance, - Integer durationSeconds, - Double calorie, - Double averagePace, - Instant startAt, - Instant endAt, - LineString route, - String location, - Long emojiId) { - this.member = member; - this.distance = distance; - this.durationSeconds = durationSeconds; - this.calorie = calorie; - this.averagePace = averagePace; - this.startAt = startAt; - this.endAt = endAt; - this.route = route; - this.location = location; - this.emojiId = emojiId; - } -} diff --git a/src/main/java/com/dnd/runus/domain/runing/repository/RunningEmojiRepository.java b/src/main/java/com/dnd/runus/domain/runing/repository/RunningEmojiRepository.java deleted file mode 100644 index 14c158f3..00000000 --- a/src/main/java/com/dnd/runus/domain/runing/repository/RunningEmojiRepository.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.dnd.runus.domain.runing.repository; - -import com.dnd.runus.domain.runing.entity.RunningEmoji; -import org.springframework.data.jpa.repository.JpaRepository; - -public interface RunningEmojiRepository extends JpaRepository {} diff --git a/src/main/java/com/dnd/runus/domain/runing/repository/RunningRecordRepository.java b/src/main/java/com/dnd/runus/domain/runing/repository/RunningRecordRepository.java deleted file mode 100644 index 340c3b1d..00000000 --- a/src/main/java/com/dnd/runus/domain/runing/repository/RunningRecordRepository.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.dnd.runus.domain.runing.repository; - -import com.dnd.runus.domain.runing.entity.RunningRecord; -import org.springframework.data.jpa.repository.JpaRepository; - -public interface RunningRecordRepository extends JpaRepository {} diff --git a/src/main/java/com/dnd/runus/domain/running/RunningEmoji.java b/src/main/java/com/dnd/runus/domain/running/RunningEmoji.java new file mode 100644 index 00000000..51a7fbb2 --- /dev/null +++ b/src/main/java/com/dnd/runus/domain/running/RunningEmoji.java @@ -0,0 +1,3 @@ +package com.dnd.runus.domain.running; + +public record RunningEmoji(long emojiId, String emojiImageUrl) {} diff --git a/src/main/java/com/dnd/runus/domain/running/RunningRecord.java b/src/main/java/com/dnd/runus/domain/running/RunningRecord.java new file mode 100644 index 00000000..b039d91b --- /dev/null +++ b/src/main/java/com/dnd/runus/domain/running/RunningRecord.java @@ -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 route, + String location, + RunningEmoji emoji) {} diff --git a/src/main/java/com/dnd/runus/global/constant/GeometryConstant.java b/src/main/java/com/dnd/runus/global/constant/GeometryConstant.java new file mode 100644 index 00000000..59f447cc --- /dev/null +++ b/src/main/java/com/dnd/runus/global/constant/GeometryConstant.java @@ -0,0 +1,5 @@ +package com.dnd.runus.global.constant; + +public class GeometryConstant { + public static final int SRID = 4326; +} diff --git a/src/main/java/com/dnd/runus/infrastructure/persistence/InfrastructurePersistencePackage.java b/src/main/java/com/dnd/runus/infrastructure/persistence/InfrastructurePersistencePackage.java new file mode 100644 index 00000000..8439347e --- /dev/null +++ b/src/main/java/com/dnd/runus/infrastructure/persistence/InfrastructurePersistencePackage.java @@ -0,0 +1,3 @@ +package com.dnd.runus.infrastructure.persistence; + +public class InfrastructurePersistencePackage {} diff --git a/src/main/java/com/dnd/runus/infrastructure/persistence/domain/member/MemberRepositoryImpl.java b/src/main/java/com/dnd/runus/infrastructure/persistence/domain/member/MemberRepositoryImpl.java new file mode 100644 index 00000000..b91cae6c --- /dev/null +++ b/src/main/java/com/dnd/runus/infrastructure/persistence/domain/member/MemberRepositoryImpl.java @@ -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 findById(long id) { + return jpaMemberRepository.findById(id).map(MemberEntity::toDomain); + } + + @Override + public Member save(Member member) { + return jpaMemberRepository.save(MemberEntity.from(member)).toDomain(); + } +} diff --git a/src/main/java/com/dnd/runus/infrastructure/persistence/domain/member/SocialProfileRepositoryImpl.java b/src/main/java/com/dnd/runus/infrastructure/persistence/domain/member/SocialProfileRepositoryImpl.java new file mode 100644 index 00000000..8c5234f1 --- /dev/null +++ b/src/main/java/com/dnd/runus/infrastructure/persistence/domain/member/SocialProfileRepositoryImpl.java @@ -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 findById(Long socialProfileId) { + return jpaSocialProfileRepository.findById(socialProfileId).map(SocialProfileEntity::toDomain); + } + + @Override + public Optional 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)); + } +} diff --git a/src/main/java/com/dnd/runus/infrastructure/persistence/jooq/member/MemberJooqRepository.java b/src/main/java/com/dnd/runus/infrastructure/persistence/jooq/member/MemberJooqRepository.java new file mode 100644 index 00000000..34745faf --- /dev/null +++ b/src/main/java/com/dnd/runus/infrastructure/persistence/jooq/member/MemberJooqRepository.java @@ -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; + +} diff --git a/src/main/java/com/dnd/runus/infrastructure/persistence/jpa/badge/entity/BadgeAchievementEntity.java b/src/main/java/com/dnd/runus/infrastructure/persistence/jpa/badge/entity/BadgeAchievementEntity.java new file mode 100644 index 00000000..ef7476cd --- /dev/null +++ b/src/main/java/com/dnd/runus/infrastructure/persistence/jpa/badge/entity/BadgeAchievementEntity.java @@ -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()); + } +} diff --git a/src/main/java/com/dnd/runus/domain/badge/entity/Badge.java b/src/main/java/com/dnd/runus/infrastructure/persistence/jpa/badge/entity/BadgeEntity.java similarity index 56% rename from src/main/java/com/dnd/runus/domain/badge/entity/Badge.java rename to src/main/java/com/dnd/runus/infrastructure/persistence/jpa/badge/entity/BadgeEntity.java index b989ee5f..308da3e7 100644 --- a/src/main/java/com/dnd/runus/domain/badge/entity/Badge.java +++ b/src/main/java/com/dnd/runus/infrastructure/persistence/jpa/badge/entity/BadgeEntity.java @@ -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; } } diff --git a/src/main/java/com/dnd/runus/domain/level/entity/Level.java b/src/main/java/com/dnd/runus/infrastructure/persistence/jpa/level/entity/LevelEntity.java similarity index 81% rename from src/main/java/com/dnd/runus/domain/level/entity/Level.java rename to src/main/java/com/dnd/runus/infrastructure/persistence/jpa/level/entity/LevelEntity.java index d26df31e..22ffc6d2 100644 --- a/src/main/java/com/dnd/runus/domain/level/entity/Level.java +++ b/src/main/java/com/dnd/runus/infrastructure/persistence/jpa/level/entity/LevelEntity.java @@ -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; diff --git a/src/main/java/com/dnd/runus/infrastructure/persistence/jpa/member/JpaMemberRepository.java b/src/main/java/com/dnd/runus/infrastructure/persistence/jpa/member/JpaMemberRepository.java new file mode 100644 index 00000000..82c27dce --- /dev/null +++ b/src/main/java/com/dnd/runus/infrastructure/persistence/jpa/member/JpaMemberRepository.java @@ -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 {} diff --git a/src/main/java/com/dnd/runus/infrastructure/persistence/jpa/member/JpaSocialProfileRepository.java b/src/main/java/com/dnd/runus/infrastructure/persistence/jpa/member/JpaSocialProfileRepository.java new file mode 100644 index 00000000..9adf7df3 --- /dev/null +++ b/src/main/java/com/dnd/runus/infrastructure/persistence/jpa/member/JpaSocialProfileRepository.java @@ -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 { + Optional findBySocialTypeAndOauthId(SocialType socialType, String oauthId); +} diff --git a/src/main/java/com/dnd/runus/infrastructure/persistence/jpa/member/entity/MemberEntity.java b/src/main/java/com/dnd/runus/infrastructure/persistence/jpa/member/entity/MemberEntity.java new file mode 100644 index 00000000..5bfdfe31 --- /dev/null +++ b/src/main/java/com/dnd/runus/infrastructure/persistence/jpa/member/entity/MemberEntity.java @@ -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(); + } +} diff --git a/src/main/java/com/dnd/runus/domain/member/entity/MemberLevel.java b/src/main/java/com/dnd/runus/infrastructure/persistence/jpa/member/entity/MemberLevelEntity.java similarity index 50% rename from src/main/java/com/dnd/runus/domain/member/entity/MemberLevel.java rename to src/main/java/com/dnd/runus/infrastructure/persistence/jpa/member/entity/MemberLevelEntity.java index 1db9b27c..04a149f3 100644 --- a/src/main/java/com/dnd/runus/domain/member/entity/MemberLevel.java +++ b/src/main/java/com/dnd/runus/infrastructure/persistence/jpa/member/entity/MemberLevelEntity.java @@ -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); } } diff --git a/src/main/java/com/dnd/runus/infrastructure/persistence/jpa/member/entity/SocialProfileEntity.java b/src/main/java/com/dnd/runus/infrastructure/persistence/jpa/member/entity/SocialProfileEntity.java new file mode 100644 index 00000000..50c5abbe --- /dev/null +++ b/src/main/java/com/dnd/runus/infrastructure/persistence/jpa/member/entity/SocialProfileEntity.java @@ -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; + } +} diff --git a/src/main/java/com/dnd/runus/infrastructure/persistence/jpa/running/JpaRunningEmojiRepository.java b/src/main/java/com/dnd/runus/infrastructure/persistence/jpa/running/JpaRunningEmojiRepository.java new file mode 100644 index 00000000..37dd8d1b --- /dev/null +++ b/src/main/java/com/dnd/runus/infrastructure/persistence/jpa/running/JpaRunningEmojiRepository.java @@ -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 {} diff --git a/src/main/java/com/dnd/runus/infrastructure/persistence/jpa/running/JpaRunningRecordRepository.java b/src/main/java/com/dnd/runus/infrastructure/persistence/jpa/running/JpaRunningRecordRepository.java new file mode 100644 index 00000000..b6d66a44 --- /dev/null +++ b/src/main/java/com/dnd/runus/infrastructure/persistence/jpa/running/JpaRunningRecordRepository.java @@ -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 {} diff --git a/src/main/java/com/dnd/runus/domain/runing/entity/RunningEmoji.java b/src/main/java/com/dnd/runus/infrastructure/persistence/jpa/running/entity/RunningEmojiEntity.java similarity index 77% rename from src/main/java/com/dnd/runus/domain/runing/entity/RunningEmoji.java rename to src/main/java/com/dnd/runus/infrastructure/persistence/jpa/running/entity/RunningEmojiEntity.java index 41dfa241..6ed62d07 100644 --- a/src/main/java/com/dnd/runus/domain/runing/entity/RunningEmoji.java +++ b/src/main/java/com/dnd/runus/infrastructure/persistence/jpa/running/entity/RunningEmojiEntity.java @@ -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) diff --git a/src/main/java/com/dnd/runus/infrastructure/persistence/jpa/running/entity/RunningRecordEntity.java b/src/main/java/com/dnd/runus/infrastructure/persistence/jpa/running/entity/RunningRecordEntity.java new file mode 100644 index 00000000..49e99536 --- /dev/null +++ b/src/main/java/com/dnd/runus/infrastructure/persistence/jpa/running/entity/RunningRecordEntity.java @@ -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 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(); + } +} diff --git a/src/main/java/com/dnd/runus/infrastructure/persistence/jpa/scale/entity/ScaleEntity.java b/src/main/java/com/dnd/runus/infrastructure/persistence/jpa/scale/entity/ScaleEntity.java new file mode 100644 index 00000000..57333942 --- /dev/null +++ b/src/main/java/com/dnd/runus/infrastructure/persistence/jpa/scale/entity/ScaleEntity.java @@ -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; +} diff --git a/src/test/java/com/dnd/runus/infrastructure/persistence/annotation/RepositoryTest.java b/src/test/java/com/dnd/runus/infrastructure/persistence/annotation/RepositoryTest.java new file mode 100644 index 00000000..42de8aee --- /dev/null +++ b/src/test/java/com/dnd/runus/infrastructure/persistence/annotation/RepositoryTest.java @@ -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 {} diff --git a/src/test/java/com/dnd/runus/infrastructure/persistence/domain/member/MemberRepositoryImplTest.java b/src/test/java/com/dnd/runus/infrastructure/persistence/domain/member/MemberRepositoryImplTest.java new file mode 100644 index 00000000..c07818c4 --- /dev/null +++ b/src/test/java/com/dnd/runus/infrastructure/persistence/domain/member/MemberRepositoryImplTest.java @@ -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()); + } +} diff --git a/src/test/java/com/dnd/runus/infrastructure/persistence/jpa/member/entity/MemberEntityTest.java b/src/test/java/com/dnd/runus/infrastructure/persistence/jpa/member/entity/MemberEntityTest.java new file mode 100644 index 00000000..6069940a --- /dev/null +++ b/src/test/java/com/dnd/runus/infrastructure/persistence/jpa/member/entity/MemberEntityTest.java @@ -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()); + } +} diff --git a/src/test/java/com/dnd/runus/infrastructure/persistence/jpa/running/entity/RunningRecordEntityTest.java b/src/test/java/com/dnd/runus/infrastructure/persistence/jpa/running/entity/RunningRecordEntityTest.java new file mode 100644 index 00000000..3484b12e --- /dev/null +++ b/src/test/java/com/dnd/runus/infrastructure/persistence/jpa/running/entity/RunningRecordEntityTest.java @@ -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()); + } +}