From 2048a63694160e39fad32c9a2cb2645f30d1f5e8 Mon Sep 17 00:00:00 2001 From: Jaewon Lee Date: Mon, 5 Aug 2024 23:27:42 +0900 Subject: [PATCH 1/3] =?UTF-8?q?Fix:=20ddl-auto=20validate=20=EC=86=8D?= =?UTF-8?q?=EC=84=B1=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jpa/badge/entity/BadgeAchievementEntity.java | 6 +++--- .../persistence/jpa/running/entity/RunningRecordEntity.java | 6 +++--- src/main/resources/application.yml | 2 +- src/test/resources/application.yml | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) 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 index ef7476cd..f2d49b58 100644 --- 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 @@ -25,16 +25,16 @@ public class BadgeAchievementEntity extends BaseTimeEntity { @NotNull @ManyToOne(fetch = LAZY) - private MemberEntity memberEntity; + private MemberEntity member; public static BadgeAchievementEntity from(BadgeAchievement badgeAchievement) { BadgeAchievementEntity badgeAchievementEntity = new BadgeAchievementEntity(); badgeAchievementEntity.badgeId = badgeAchievement.badge().badgeId(); - badgeAchievementEntity.memberEntity = MemberEntity.from(badgeAchievement.member()); + badgeAchievementEntity.member = MemberEntity.from(badgeAchievement.member()); return badgeAchievementEntity; } public BadgeAchievement toDomain(Badge badge) { - return new BadgeAchievement(badge, memberEntity.toDomain(), getCreatedAt(), getUpdatedAt()); + return new BadgeAchievement(badge, member.toDomain(), getCreatedAt(), getUpdatedAt()); } } 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 index c5e3d974..0359a470 100644 --- 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 @@ -39,7 +39,7 @@ public class RunningRecordEntity extends BaseTimeEntity { @NotNull @ManyToOne(fetch = LAZY) - private MemberEntity memberEntity; + private MemberEntity member; @NotNull private Integer distanceMeter; @@ -80,7 +80,7 @@ public static RunningRecordEntity from(RunningRecord runningRecord) { return RunningRecordEntity.builder() .id(runningRecord.runningId()) - .memberEntity(MemberEntity.from(runningRecord.member())) + .member(MemberEntity.from(runningRecord.member())) .distanceMeter(runningRecord.distanceMeter()) .durationSeconds(runningRecord.durationSeconds()) .calorie(runningRecord.calorie()) @@ -100,7 +100,7 @@ public RunningRecord toDomain() { return RunningRecord.builder() .runningId(id) - .member(memberEntity.toDomain()) + .member(member.toDomain()) .distanceMeter(distanceMeter) .durationSeconds(durationSeconds) .calorie(calorie) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index c3a9c593..472282c5 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -4,7 +4,7 @@ spring.profiles.active: local spring: jpa: hibernate: - ddl-auto: none + ddl-auto: validate show-sql: true open-in-view: false defer-datasource-initialization: false diff --git a/src/test/resources/application.yml b/src/test/resources/application.yml index 582bdcd0..2ec07225 100644 --- a/src/test/resources/application.yml +++ b/src/test/resources/application.yml @@ -1,7 +1,7 @@ spring: jpa: hibernate: - ddl-auto: none + ddl-auto: validate show-sql: true open-in-view: false defer-datasource-initialization: false From caaf16ef10ce0b5645db38370a61adf5f42d368b Mon Sep 17 00:00:00 2001 From: Jaewon Lee Date: Tue, 6 Aug 2024 00:21:22 +0900 Subject: [PATCH 2/3] =?UTF-8?q?Fix:=20flyway=20=EC=84=A4=EC=A0=95=20applic?= =?UTF-8?q?ation.yml=EC=97=90=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 472282c5..5baa7b51 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -15,6 +15,10 @@ spring: username: ${DATABASE_USER} password: ${DATABASE_PASSWORD} + flyway: + enabled: true + baseline-on-migrate: true + app: api: allow-origins: ${ALLOW_ORIGINS} From 78df8fb22a28021d64191e56587293b1ef7b57bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=9E=AC=EC=9B=90?= Date: Tue, 6 Aug 2024 17:40:17 +0900 Subject: [PATCH 3/3] =?UTF-8?q?Fix:=20running=20record=20=ED=85=8C?= =?UTF-8?q?=EC=9D=B4=EB=B8=94=20pace=20=EC=BB=AC=EB=9F=BC=20=ED=83=80?= =?UTF-8?q?=EC=9E=85=EC=9D=84=20=EC=A0=95=EC=88=98=ED=98=95=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD=20(#46)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dnd/runus/domain/common/Coordinate.java | 4 +- .../com/dnd/runus/domain/common/Pace.java | 34 +++++++++++++++++ .../runus/domain/running/RunningRecord.java | 6 ++- .../global/exception/NotFoundException.java | 4 ++ .../persistence/domain/GeometryMapper.java | 38 +++++++++++++++++++ .../running/entity/RunningRecordEntity.java | 35 +++++------------ .../V2__alter_running_record_avg_pace.sql | 2 + .../entity/RunningRecordEntityTest.java | 6 ++- 8 files changed, 97 insertions(+), 32 deletions(-) create mode 100644 src/main/java/com/dnd/runus/domain/common/Pace.java create mode 100644 src/main/java/com/dnd/runus/infrastructure/persistence/domain/GeometryMapper.java create mode 100644 src/main/resources/db/migration/V2__alter_running_record_avg_pace.sql diff --git a/src/main/java/com/dnd/runus/domain/common/Coordinate.java b/src/main/java/com/dnd/runus/domain/common/Coordinate.java index 16d115d7..9c885121 100644 --- a/src/main/java/com/dnd/runus/domain/common/Coordinate.java +++ b/src/main/java/com/dnd/runus/domain/common/Coordinate.java @@ -6,9 +6,7 @@ * @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); + this(longitude, latitude, Double.NaN); } } diff --git a/src/main/java/com/dnd/runus/domain/common/Pace.java b/src/main/java/com/dnd/runus/domain/common/Pace.java new file mode 100644 index 00000000..43c05ef7 --- /dev/null +++ b/src/main/java/com/dnd/runus/domain/common/Pace.java @@ -0,0 +1,34 @@ +package com.dnd.runus.domain.common; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +/** + * 러닝 페이스를 나타내는 클래스 + * + * @param minute + * @param second + */ +public record Pace(int minute, int second) { + public static Pace ofSeconds(int seconds) { + int minute = seconds / 60; + int second = seconds % 60; + return new Pace(minute, second); + } + + @JsonCreator + public static Pace from(String pace) { + // e.g. 5'30" + String[] paceArr = pace.split("'\""); + return new Pace(Integer.parseInt(paceArr[0]), Integer.parseInt(paceArr[1])); + } + + @JsonValue + public String getPace() { + return minute + "'" + second + "\""; + } + + public int toSeconds() { + return minute * 60 + second; + } +} diff --git a/src/main/java/com/dnd/runus/domain/running/RunningRecord.java b/src/main/java/com/dnd/runus/domain/running/RunningRecord.java index 4fd55dfa..67c8076b 100644 --- a/src/main/java/com/dnd/runus/domain/running/RunningRecord.java +++ b/src/main/java/com/dnd/runus/domain/running/RunningRecord.java @@ -1,10 +1,12 @@ package com.dnd.runus.domain.running; import com.dnd.runus.domain.common.Coordinate; +import com.dnd.runus.domain.common.Pace; import com.dnd.runus.domain.member.Member; import com.dnd.runus.global.constant.RunningEmoji; import lombok.Builder; +import java.time.Duration; import java.time.OffsetDateTime; import java.util.List; @@ -13,9 +15,9 @@ public record RunningRecord( long runningId, Member member, int distanceMeter, - int durationSeconds, + Duration duration, double calorie, - double averagePace, + Pace averagePace, OffsetDateTime startAt, OffsetDateTime endAt, List route, diff --git a/src/main/java/com/dnd/runus/global/exception/NotFoundException.java b/src/main/java/com/dnd/runus/global/exception/NotFoundException.java index c53c60c9..2e99bf13 100644 --- a/src/main/java/com/dnd/runus/global/exception/NotFoundException.java +++ b/src/main/java/com/dnd/runus/global/exception/NotFoundException.java @@ -6,4 +6,8 @@ public class NotFoundException extends BaseException { public NotFoundException(String message) { super(ErrorType.ENTITY_NOT_FOUND, message); } + + public NotFoundException(Class clazz, ID id) { + super(ErrorType.ENTITY_NOT_FOUND, clazz.getSimpleName() + ": id (" + id + ") is not found"); + } } diff --git a/src/main/java/com/dnd/runus/infrastructure/persistence/domain/GeometryMapper.java b/src/main/java/com/dnd/runus/infrastructure/persistence/domain/GeometryMapper.java new file mode 100644 index 00000000..60e1d0b0 --- /dev/null +++ b/src/main/java/com/dnd/runus/infrastructure/persistence/domain/GeometryMapper.java @@ -0,0 +1,38 @@ +package com.dnd.runus.infrastructure.persistence.domain; + +import com.dnd.runus.domain.common.Coordinate; +import org.locationtech.jts.geom.GeometryFactory; +import org.locationtech.jts.geom.LineString; +import org.locationtech.jts.geom.PrecisionModel; + +import java.util.List; +import java.util.stream.Stream; + +import static com.dnd.runus.global.constant.GeometryConstant.SRID; +import static org.locationtech.jts.geom.PrecisionModel.FLOATING; + +public final class GeometryMapper { + GeometryMapper() {} + + public static Coordinate toDomain(org.locationtech.jts.geom.Coordinate coordinate) { + return new Coordinate(coordinate.getX(), coordinate.getY(), coordinate.getZ()); + } + + public static List toDomain(LineString lineString) { + return Stream.of(lineString.getCoordinates()) + .map(GeometryMapper::toDomain) + .toList(); + } + + public static LineString toLineString(List coordinates) { + org.locationtech.jts.geom.Coordinate[] geoCoordinates = coordinates.stream() + .map(coordinate -> new org.locationtech.jts.geom.Coordinate( + coordinate.longitude(), coordinate.latitude(), coordinate.altitude())) + .toArray(org.locationtech.jts.geom.Coordinate[]::new); + return GeometryFactoryHolder.INSTANCE.createLineString(geoCoordinates); + } + + private static class GeometryFactoryHolder { + private static final GeometryFactory INSTANCE = new GeometryFactory(new PrecisionModel(FLOATING), SRID); + } +} 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 index 0359a470..3dd45793 100644 --- 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 @@ -1,9 +1,10 @@ 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.common.Pace; import com.dnd.runus.domain.running.RunningRecord; import com.dnd.runus.global.constant.RunningEmoji; +import com.dnd.runus.infrastructure.persistence.domain.GeometryMapper; import com.dnd.runus.infrastructure.persistence.jpa.member.entity.MemberEntity; import jakarta.persistence.*; import jakarta.validation.constraints.NotNull; @@ -11,20 +12,16 @@ 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.Duration; 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.EnumType.STRING; 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") @@ -51,7 +48,7 @@ public class RunningRecordEntity extends BaseTimeEntity { private Double calorie; @NotNull - private Double averagePace; + private Integer averagePace; @NotNull private OffsetDateTime startAt; @@ -70,44 +67,32 @@ public class RunningRecordEntity extends BaseTimeEntity { private RunningEmoji emoji; 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()) .member(MemberEntity.from(runningRecord.member())) .distanceMeter(runningRecord.distanceMeter()) - .durationSeconds(runningRecord.durationSeconds()) + .durationSeconds((int) runningRecord.duration().toSeconds()) .calorie(runningRecord.calorie()) - .averagePace(runningRecord.averagePace()) + .averagePace(runningRecord.averagePace().toSeconds()) .startAt(runningRecord.startAt()) .endAt(runningRecord.endAt()) - .route(route) + .route(GeometryMapper.toLineString(runningRecord.route())) .location(runningRecord.location()) .emoji(runningRecord.emoji()) .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(member.toDomain()) .distanceMeter(distanceMeter) - .durationSeconds(durationSeconds) + .duration(Duration.ofSeconds(durationSeconds)) .calorie(calorie) - .averagePace(averagePace) + .averagePace(Pace.ofSeconds(averagePace)) .startAt(startAt) .endAt(endAt) - .route(coordinates) + .route(GeometryMapper.toDomain(route)) .location(location) .emoji(emoji) .build(); diff --git a/src/main/resources/db/migration/V2__alter_running_record_avg_pace.sql b/src/main/resources/db/migration/V2__alter_running_record_avg_pace.sql new file mode 100644 index 00000000..01d7787c --- /dev/null +++ b/src/main/resources/db/migration/V2__alter_running_record_avg_pace.sql @@ -0,0 +1,2 @@ +ALTER TABLE running_record + ALTER COLUMN average_pace TYPE INTEGER USING average_pace::INTEGER; 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 index d470c39e..a640c526 100644 --- 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 @@ -1,6 +1,7 @@ package com.dnd.runus.infrastructure.persistence.jpa.running.entity; import com.dnd.runus.domain.common.Coordinate; +import com.dnd.runus.domain.common.Pace; import com.dnd.runus.domain.member.Member; import com.dnd.runus.domain.running.RunningRecord; import com.dnd.runus.global.constant.MemberRole; @@ -9,6 +10,7 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import java.time.Duration; import java.time.OffsetDateTime; import java.util.List; @@ -23,9 +25,9 @@ void setUp() { .runningId(1L) .member(new Member(MemberRole.USER, "nickname-1")) .distanceMeter(500) - .durationSeconds(1) + .duration(Duration.ofSeconds(100)) .calorie(1.0) - .averagePace(1.0) + .averagePace(Pace.ofSeconds(100)) .startAt(OffsetDateTime.now()) .endAt(OffsetDateTime.now()) .route(List.of(new Coordinate(128.0, 36.0), new Coordinate(128.0, 37.0)))